fixlib.py 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749
  1. #!/usr/bin/python3
  2. import os
  3. import json
  4. import time
  5. import __main__ as MAIN
  6. from collections import OrderedDict
  7. from lib.cprint import *
  8. HOME = os.getenv('HOME')
  9. def _fixture_decode_sav_line(line):
  10. out = None
  11. out = [0,"none",{}]
  12. if line.count("\t") < 2:
  13. cprint("Error line.count('\\t') < 2 (is:{})".format(line.count("\t")),color="red",end=" ")
  14. cprint("file:{}".format(line),color="red")
  15. else:
  16. key,label,rdata = line.split("\t",2)
  17. jdata = json.loads(rdata,object_pairs_hook=OrderedDict)
  18. key = int(key)
  19. #label += " dsav"
  20. #label = label.replace(" dsav","")
  21. out = [key,label,jdata]
  22. #if not out:
  23. #print(line)
  24. #sys.exit()
  25. return out
  26. def _fixture_repair_nr0(jdata):
  27. nrnull = 0
  28. if "ATTRIBUT" in jdata: # translate old FIXTURES.fixtures start with 0 to 1
  29. if nrnull:
  30. cprint("DMX NR IS NULL",attr,"CHANGE +1")
  31. for attr in jdata["ATTRIBUT"]:
  32. if "NR" in jdata["ATTRIBUT"][attr]:
  33. nr = jdata["ATTRIBUT"][attr]["NR"]
  34. if nr >= 0:
  35. jdata["ATTRIBUT"][attr]["NR"] +=1
  36. #return jdata
  37. def FIXTURE_CHECK_SDATA(ID,sdata):
  38. print("FIXTURE_CHECK_SDATA",ID)
  39. new_f = OrderedDict()
  40. #print("++++")
  41. for k,j in sdata.items():
  42. overide=0 # only for repair
  43. if overide:
  44. if k in ["TYPE","VENDOR"]: #ignor
  45. continue
  46. new_f[k] = j
  47. if k =="NAME":
  48. #print("AAAADDDDDD")
  49. if "TYPE" not in sdata and not overide:
  50. if len( sdata["ATTRIBUT"]) == 1:
  51. new_f["TYPE"] = "DIMMER"
  52. elif "PAN" in sdata["ATTRIBUT"]:
  53. new_f["TYPE"] = "MOVER"
  54. elif "RED" in sdata["ATTRIBUT"] and len(sdata["ATTRIBUT"]) == 3:
  55. new_f["TYPE"] = "RGB"
  56. elif "RED" in sdata["ATTRIBUT"]:
  57. new_f["TYPE"] = "LED"
  58. elif "CYAN" in sdata["ATTRIBUT"]:
  59. new_f["TYPE"] = "COLOR"
  60. else:
  61. new_f["TYPE"] = ""
  62. if "VENDOR" not in sdata and not overide:
  63. new_f["VENDOR"] = ""
  64. #print(k,j)#,sdata)
  65. sdata = new_f
  66. if "ACTIVE" not in sdata:
  67. sdata["ACTIVE"] = 0
  68. sdata["ATTRIBUT"]["_ACTIVE"] = OrderedDict()
  69. sdata["ATTRIBUT"]["_ACTIVE"]["NR"] = 0
  70. sdata["ATTRIBUT"]["_ACTIVE"]["ACTIVE"] = 1
  71. sdata["ATTRIBUT"]["_ACTIVE"]["VALUE"] = 0
  72. sdata["ATTRIBUT"]["_ACTIVE"]["FX2"] = {}
  73. sdata["ATTRIBUT"]["_ACTIVE"]["FX"] = ""
  74. if "DIM" not in sdata["ATTRIBUT"]:
  75. _tmp = None
  76. #print(sdata)
  77. vdim_count = 0
  78. for a in ["RED","GREEN","BLUE"]:#,"WHITE","AMBER"]:
  79. if a in sdata["ATTRIBUT"]:
  80. vdim_count +=1
  81. if vdim_count == 3:
  82. _tmp = {"NR": 0, "MASTER": "0", "MODE": "F", "VALUE": 255, "ACTIVE": 0, "FX": "", "FX2": {}}
  83. _tmp = OrderedDict(_tmp)
  84. sdata["ATTRIBUT"]["DIM"] =_tmp
  85. print("ADD ---- VDIM",vdim_count,_tmp)
  86. #input("STOP")
  87. for attr in sdata["ATTRIBUT"]:
  88. row = sdata["ATTRIBUT"][attr]
  89. row["ACTIVE"] = 0
  90. if "FX" not in row:
  91. row["FX"] =""
  92. if "FX2" not in row:
  93. row["FX2"] = {}
  94. if "MASTER" not in row:
  95. row["MASTER"] = 0
  96. if "ID" not in sdata:
  97. sdata["ID"] = str(ID)
  98. return sdata
  99. def _parse_fixture_name(name):
  100. out = []
  101. #{"FIX","MAN","CH","PATH":""}
  102. if name.count(".") == 2:
  103. m,n,e = name.split(".")
  104. #out = [n,m,"0",name]
  105. out = {"name":n,"manufactor":m,"fname":name}
  106. elif name.count("_") == 2:
  107. name,e = name.split(".")
  108. m,n,c = name.split("_")
  109. out = {"name":n,"ch":c,"manufactor":m,"name":name}
  110. #out = [n,m,c,name]
  111. else:
  112. out = {"name":name}
  113. return out
  114. def online_help(page):
  115. print("INIT:online_help",page)
  116. try:
  117. page = page.replace("&","")
  118. page = page.replace("=","")
  119. page = page.replace("/","")
  120. import webbrowser
  121. def _cb():
  122. print("online_help",page)
  123. webbrowser.open("http://librelight.de/wiki/doku.php?id="+page )
  124. return _cb
  125. except Exception as e:
  126. print("online_help Exception",e)
  127. raise e
  128. def _cb():
  129. print("error online_help",page)
  130. return _cb
  131. def index_fixtures():
  132. p="/opt/LibreLight/Xdesk/fixtures/"
  133. ls = os.listdir(p )
  134. ls.sort()
  135. blist = []
  136. for l in ls:
  137. b = _parse_fixture_name(l)
  138. b.append(p)
  139. b.insert(0,"base")
  140. blist.append(b)
  141. return blist
  142. #def _fixture_create_import_list(path=None):
  143. def _fixture_load_import_list(path=None):
  144. if not path:
  145. path = "/home/user/LibreLight/show"
  146. blist = []
  147. lsd = os.listdir(path)
  148. lsd.sort()
  149. fname_buffer = []
  150. for sname in lsd:
  151. #print(" ",sname)
  152. ok = 0
  153. try:
  154. fname = path+"/"+sname+"/patch.sav"
  155. if os.path.isfile(fname):
  156. ok = 1
  157. else:
  158. fname = path+"/"+sname
  159. if os.path.isfile(fname):
  160. ok = 1
  161. #fname_buffer = []
  162. if not ok:
  163. continue
  164. f = open(fname)
  165. lines = f.readlines()
  166. f.close()
  167. for line in lines:
  168. ok2 = 0
  169. _key = ""
  170. line = line.split("\t")
  171. if len(line) < 2:
  172. continue
  173. jdata = json.loads(line[2])
  174. fixture = jdata
  175. _len = str(fixture_get_ch_count(fixture))
  176. if "ATTRIBUT" in jdata:
  177. #_len = len(jdata["ATTRIBUT"])
  178. #if "_ACTIVE" in jdata["ATTRIBUT"]:
  179. # _len -= 1
  180. _key = list(jdata["ATTRIBUT"].keys())
  181. _key.sort()
  182. _key = str(_key)
  183. if _key not in fname_buffer:
  184. fname_buffer.append(_key) # group same fixtures by ATTR
  185. ok2 = 1
  186. if ok2:
  187. name = jdata["NAME"]
  188. #row = [name,fname+":"+name,path])
  189. xfname = fname.replace(path,"")
  190. row = {"xfname":xfname ,"name":name,"ch":_len, "xpath":path,"d":_key} #,"b":jdata}
  191. blist.append(row)
  192. except Exception as e:
  193. print("exception",e)
  194. raise e
  195. return blist
  196. def fixture_get_ch_count(fixture):
  197. _len = [0,0]
  198. if "ATTRIBUT" not in fixture:
  199. return [-1,-1]
  200. for at in fixture["ATTRIBUT"]:
  201. #print(at,_len)
  202. #print(" ",fixture["ATTRIBUT"][at])
  203. if not at.startswith("_") and not at.startswith("EMPTY"):
  204. _len[1] += 1
  205. if "NR" in fixture["ATTRIBUT"][at]:
  206. NR = fixture["ATTRIBUT"][at]["NR"]
  207. if NR > _len[0]:
  208. _len[0] = NR
  209. #print("-",at,_len)
  210. return _len
  211. def fixture_get_attr_data(fixture,attr):
  212. if "ATTRIBUT" in fixture:
  213. if attr in fixture["ATTRIBUT"]:
  214. return fixture["ATTRIBUT"][attr]
  215. if "NAME" in fixture:
  216. print(" NO fixture_get_attr_data A",fixture["NAME"],attr)
  217. else:
  218. print(" NO fixture_get_attr_data B",fixture,attr)
  219. def fixture_order_attr_by_nr(fixture):
  220. out1 = []
  221. max_nr = 0
  222. if "ATTRIBUT" not in fixture:
  223. return []
  224. nrs = {}
  225. for at in fixture["ATTRIBUT"]:
  226. #print("+ ",at)
  227. atd = fixture_get_attr_data(fixture,at)
  228. #print("+ ",atd)
  229. if not atd:
  230. continue
  231. k = atd["NR"]
  232. v = at
  233. nrs[k] = v
  234. if k > max_nr:
  235. max_nr = k
  236. for i in range(1,max_nr+1):
  237. if i not in nrs:
  238. v = "EMPTY" #-{}".format(i)
  239. nrs[i] = v
  240. #print("-: ",v)
  241. nrs_key = list(nrs.keys())
  242. nrs_key.sort()
  243. #print(nrs_key)
  244. for k in nrs_key:
  245. v = nrs[k]
  246. #print("-: ",k,v)
  247. out1.append(v)
  248. #print()
  249. return out1
  250. def _load_fixture_list(mode="None"):
  251. blist = []
  252. if mode == "USER":
  253. path = HOME+"/LibreLight/fixtures/"
  254. elif mode == "GLOBAL":
  255. path="/opt/LibreLight/Xdesk/fixtures/"
  256. elif mode == "IMPORT":
  257. path=None
  258. _r = _fixture_load_import_list(path=path)
  259. blist.extend( _r )
  260. return blist
  261. import lib.baselib as baselib
  262. class Fixtures():
  263. def __init__(self):
  264. #super().__init__()
  265. self.base=baselib.Base()
  266. #self.load()
  267. self.fixtures = OrderedDict()
  268. self.gui = None # GUIHandler()
  269. def load_patch(self):
  270. filename="patch"
  271. #self.base._init()
  272. d,l = self.base._load(filename)
  273. self.fixtures = OrderedDict()
  274. for i in l:
  275. sdata = d[i]
  276. #sdata = self._repair_sdata(sdata)
  277. sdata = FIXTURE_CHECK_SDATA(i,sdata)
  278. self.fixtures[str(i)] = sdata
  279. #PRESETS.label_presets = l
  280. self._re_sort()
  281. self.fx_off("all")
  282. def _re_sort(self):
  283. keys = list(self.fixtures.keys())
  284. keys2=[]
  285. for k in keys:
  286. #k = "{:0>5}".format(k)
  287. k = int(k)
  288. keys2.append(k)
  289. keys2.sort()
  290. fixtures2 = OrderedDict()
  291. for k in keys2:
  292. k = str(k)
  293. fixtures2[k] = self.fixtures[k]
  294. self.fixtures = fixtures2
  295. def backup_patch(self,save_as="",new=0):
  296. filename = "patch"
  297. #self.fx_off("all")
  298. data = self.fixtures
  299. labels = {}
  300. for k in data:
  301. labels[k] = k
  302. if new:
  303. data = []
  304. labels = {}
  305. #self.base._init()
  306. self.base._backup(filename,data,labels,save_as)
  307. def fx_get(self,fix=None):
  308. out={}
  309. if not fix or fix == "all":
  310. #self.data.fx.elem[self.attr]["bg"] = "magenta"
  311. for fix in self.fixtures:
  312. data = self.fixtures[fix]
  313. for attr in data["ATTRIBUT"]:
  314. out[str(fix)+"."+str(attr)+".fx"] = data["ATTRIBUT"][attr]["FX"]
  315. out[str(fix)+"."+str(attr)+".fx"] = data["ATTRIBUT"][attr]["FX2"]
  316. return out
  317. def fx_off(self,fix=None):
  318. if not fix or fix == "all":
  319. #self.data.fx.elem[self.attr]["bg"] = "magenta"
  320. for fix in self.fixtures:
  321. data = self.fixtures[fix]
  322. for attr in data["ATTRIBUT"]:
  323. data["ATTRIBUT"][attr]["FX"] = ""
  324. data["ATTRIBUT"][attr]["FX2"] = OrderedDict()
  325. def get_attr(self,fix,attr):
  326. if fix in self.fixtures:
  327. data = self.fixtures[fix]
  328. if "ATTRIBUT" in data:
  329. if attr in data["ATTRIBUT"]:
  330. return data["ATTRIBUT"][attr]
  331. def get_max_dmx_nr(self,fix):
  332. max_dmx = 0
  333. used_dmx = 0
  334. if fix not in self.fixtures:
  335. return (used_dmx,max_dmx)
  336. data = self.fixtures[fix]
  337. used_dmx = len(data["ATTRIBUT"])
  338. for a in data["ATTRIBUT"]:
  339. attr = data["ATTRIBUT"][a]
  340. if "NR" in attr:
  341. try:
  342. _n = int(attr["NR"])
  343. if _n > max_dmx:
  344. max_dmx=_n
  345. except ValueError:pass
  346. return (used_dmx,max_dmx)
  347. def get_dmx(self,fix,attr):
  348. #cprint("get_dmx",[fix,attr], fix in self.fixtures)
  349. DMX = -99
  350. if attr.startswith("_"):
  351. return -88
  352. if fix in self.fixtures:
  353. data = self.fixtures[fix]
  354. if "DMX" in data:
  355. DMX = int(data["DMX"])
  356. if DMX <= 0:
  357. return DMX # VIRTUAL FIX
  358. if "UNIVERS" in data:
  359. DMX += int(data["UNIVERS"])*512
  360. adata = self.get_attr(fix,attr)
  361. if adata:
  362. if "NR" in adata:
  363. NR = adata["NR"]
  364. if NR <= 0:
  365. return -12 # not a VIRTUAL ATTR
  366. else:
  367. DMX+=NR-1
  368. return DMX
  369. return -199
  370. def update_raw(self,rdata,update=1):
  371. #cprint("update_raw",len(rdata))
  372. cmd = []
  373. for i,d in enumerate(rdata):
  374. xcmd = {"DMX":""}
  375. fix = d["FIX"]
  376. attr = d["ATTR"]
  377. v2 = d["VALUE"]
  378. v2_fx = d["FX"]
  379. if fix not in self.fixtures:
  380. continue
  381. sdata = self.fixtures[fix] #shortcat
  382. ATTR = sdata["ATTRIBUT"]
  383. if attr not in ATTR:
  384. continue
  385. #print(sdata)
  386. #print("FIX",fix,attr)
  387. sDMX = MAIN.FIXTURES.get_dmx(fix,attr)
  388. #print(sDMX)
  389. xcmd["DMX"] = str(sDMX)
  390. cmd.append(xcmd)
  391. v=ATTR[attr]["VALUE"]
  392. if v2 is not None and update:
  393. ATTR[attr]["VALUE"] = v2
  394. if d["FX2"] and update:
  395. ATTR[attr]["FX2"] = d["FX2"]
  396. text = str(attr)+' '+str(round(v,2))
  397. return cmd
  398. def encoder(self,fix,attr,xval="",xfade=0,xdelay=0,blind=0):
  399. _blind = 0
  400. if MAIN.modes.val("BLIND"):
  401. _blind = 1
  402. if blind:
  403. _blind = 1
  404. if not _blind:
  405. cprint("FIXTURES.encoder",fix,attr,xval,xfade,color="yellow")
  406. if attr == "CLEAR":
  407. self.clear()
  408. return 0
  409. if attr == "ALL":
  410. x=self.select(fix,attr,mode="toggle")
  411. return x
  412. if attr == "INV-ATTR":
  413. cprint("-x-x-x-x-x-x-x-X-")
  414. x=self.select(fix,attr,mode="swap")
  415. #x=self.select(fix,"ALL",mode="swap")
  416. master.refresh_fix()
  417. return x
  418. if attr == "INV-FIX":
  419. cprint("-x-x-x-x-x-x-x-x-")
  420. x=self.select(fix,attr,mode="swap")
  421. #x=self.select(fix,"ALL",mode="swap")
  422. return x
  423. out = []
  424. #cprint("Fixture.Encoder(...)",fix,attr)
  425. if fix not in self.fixtures:
  426. #cprint(" activate Fixture in fixture list on encoder click ")
  427. ii =0
  428. delay=0
  429. sstart = time.time()
  430. #cprint(" encoder fix <--")
  431. sub_data = []
  432. for _fix in self.fixtures:
  433. ii+=1
  434. data = self.fixtures[_fix]
  435. if "-FINE" in attr.upper():
  436. continue
  437. elif (attr in data["ATTRIBUT"] ) and "-FINE" not in attr.upper() :
  438. if xval == "click":
  439. self.select(_fix,attr,mode="on")
  440. elif data["ATTRIBUT"][attr]["ACTIVE"]:
  441. if _fix:
  442. sub_data.append([_fix,attr,xval,xfade,delay])
  443. if MAIN.DELAY._is():
  444. delay += MAIN.DELAY.val()/100
  445. sub_jdata = []
  446. for dd in sub_data:
  447. #print("---",len(sub_data),end="")
  448. #self.encoder(fix,attr,xval,xfade,delay)
  449. _x123 = self.encoder(dd[0],dd[1],dd[2],dd[3],dd[4],blind=1)
  450. sub_jdata.append(_x123)
  451. if sub_jdata:
  452. cprint(" SEND MASTER ENCODER:",len(sub_data),sub_data[0],"... _blind:",_blind)#,end="")
  453. if not _blind:
  454. MAIN.jclient_send(sub_jdata)
  455. jdata=[{"MODE":ii}]
  456. #cprint(" ENCODER j send <--")
  457. if not _blind:
  458. MAIN.jclient_send(jdata)
  459. return sub_jdata #len(sub_data)
  460. data = self.fixtures[fix]
  461. if xval == "click":
  462. #cprint(data)
  463. return self.select(fix,attr,mode="toggle")
  464. v2=data["ATTRIBUT"][attr]["VALUE"]
  465. change=0
  466. increment = 5 #4.11
  467. jdata = {"MODE":"ENC"}
  468. if xval == "++":
  469. v2+= increment
  470. jdata["INC"] = increment
  471. change=1
  472. elif xval == "--":
  473. jdata["INC"] = increment*-1
  474. v2-= increment
  475. change=1
  476. elif xval == "+":
  477. increment = 0.25 #.5
  478. v2+= increment
  479. jdata["INC"] = increment
  480. change=1
  481. elif xval == "-":
  482. increment = 0.25 #.5
  483. jdata["INC"] = increment*-1
  484. v2-= increment
  485. change=1
  486. elif type(xval) is int or type(xval) is float:
  487. v2 = xval
  488. change=1
  489. if v2 < 0:
  490. v2=0
  491. elif v2 > 256:
  492. v2=256
  493. jdata["VALUE"] = round(v2,4)
  494. jdata["FIX"] = fix
  495. jdata["FADE"] = 0
  496. jdata["DELAY"] = 0
  497. jdata["ATTR"] = attr
  498. dmx = MAIN.FIXTURES.get_dmx(fix,attr)
  499. jdata["DMX"] = dmx
  500. dmx_fine = MAIN.FIXTURES.get_dmx(fix,attr+"-FINE")
  501. if dmx_fine != jdata["DMX"] and dmx > 0:
  502. jdata["DMX-FINE"] = dmx_fine
  503. out = {}
  504. if 1: #change:
  505. data["ATTRIBUT"][attr]["ACTIVE"] = 1
  506. data["ATTRIBUT"]["_ACTIVE"]["ACTIVE"] = 1
  507. data["ATTRIBUT"][attr]["VALUE"] = round(v2,4)
  508. if xfade:
  509. jdata["FADE"] = xfade
  510. if xdelay:
  511. #if attr not in ["PAN","TILT"] and 1:
  512. jdata["DELAY"] = xdelay
  513. if not _blind:
  514. jdata = [jdata]
  515. MAIN.jclient_send(jdata)
  516. time.sleep(0.001)
  517. return jdata
  518. def get_active(self):
  519. cprint("get_active",self)
  520. CFG = OrderedDict()
  521. sdata = OrderedDict()
  522. sdata["CFG"] = CFG # OrderedDict()
  523. sdata["CFG"]["FADE"] = MAIN.FADE.val()
  524. sdata["CFG"]["DEALY"] = 0
  525. for fix in self.fixtures:
  526. data = self.fixtures[fix]
  527. for attr in data["ATTRIBUT"]:
  528. if not data["ATTRIBUT"][attr]["ACTIVE"]:
  529. continue
  530. if fix not in sdata:
  531. sdata[fix] = {}
  532. if attr not in sdata[fix]:
  533. sdata[fix][attr] = OrderedDict()
  534. if not MAIN.modes.val("REC-FX"):
  535. sdata[fix][attr]["VALUE"] = data["ATTRIBUT"][attr]["VALUE"]
  536. else:
  537. sdata[fix][attr]["VALUE"] = None
  538. if "FX" not in data["ATTRIBUT"][attr]:
  539. data["ATTRIBUT"][attr]["FX"] = ""
  540. if "FX2" not in data["ATTRIBUT"][attr]:
  541. data["ATTRIBUT"][attr]["FX2"] = {}
  542. sdata[fix][attr]["FX"] = data["ATTRIBUT"][attr]["FX"]
  543. sdata[fix][attr]["FX2"] = data["ATTRIBUT"][attr]["FX2"]
  544. return sdata
  545. def _deselect_all(self,fix=None):
  546. cprint("FIXTURES._deselect_all()",fix,"ALL",color="yellow")
  547. c=0
  548. if fix in self.fixtures:
  549. data = self.fixtures[fix]
  550. for attr in data["ATTRIBUT"]:
  551. #print("SELECT ALL",fix,attr)
  552. if "-FINE" in attr.upper():
  553. pass
  554. else:
  555. c+=self.select(fix,attr,mode="off",mute=1)
  556. return c
  557. def _select_all(self,fix=None,mode="toggle",mute=0):
  558. if not mute:
  559. cprint("FIXTURES._select_all()",fix,"ALL",mode,color="yellow")
  560. c=0
  561. if fix in self.fixtures:
  562. data = self.fixtures[fix]
  563. for attr in data["ATTRIBUT"]:
  564. #print("SELECT ALL",fix,attr)
  565. if "-FINE" in attr.upper():
  566. continue
  567. if mode == "toggle":
  568. c+=self.select(fix,attr,mode="on",mute=mute)
  569. elif mode == "swap":
  570. if not attr.startswith("_"):
  571. c+=self.select(fix,attr,mode="toggle",mute=mute)
  572. if not c and mode == "toggle": # unselect all
  573. c= self._deselect_all(fix=fix)
  574. return c
  575. def select(self,fix=None,attr=None,mode="on",mute=0):
  576. if not mute:
  577. cprint("FIXTURES.select() >>",fix,attr,mode,color="yellow")
  578. out = 0
  579. if fix == "SEL":
  580. if attr.upper() == "INV-ATTR":
  581. fixs = self.get_active()
  582. cprint("selected:",len(fixs))
  583. for fix in fixs:
  584. x=self._select_all(fix=fix,mode=mode,mute=1)
  585. return None
  586. if fix in self.fixtures:
  587. if attr.upper() == "ALL":
  588. x=self._select_all(fix=fix,mode=mode)
  589. return x
  590. data = self.fixtures[fix]
  591. if attr in data["ATTRIBUT"]:
  592. if mode == "on":
  593. if not data["ATTRIBUT"][attr]["ACTIVE"]:
  594. data["ATTRIBUT"][attr]["ACTIVE"] = 1
  595. data["ATTRIBUT"]["_ACTIVE"]["ACTIVE"] = 1
  596. out = 1
  597. elif mode == "off":
  598. if data["ATTRIBUT"][attr]["ACTIVE"]:
  599. data["ATTRIBUT"][attr]["ACTIVE"] = 0
  600. out = 1
  601. elif mode == "toggle":
  602. if data["ATTRIBUT"][attr]["ACTIVE"]:
  603. data["ATTRIBUT"][attr]["ACTIVE"] = 0
  604. else:
  605. data["ATTRIBUT"][attr]["ACTIVE"] = 1
  606. data["ATTRIBUT"]["_ACTIVE"]["ACTIVE"] = 1
  607. out = 1
  608. return out
  609. def clear(self):
  610. out = 0
  611. for fix in self.fixtures:
  612. data = self.fixtures[fix]
  613. for attr in data["ATTRIBUT"]:
  614. #if attr.endswith("-FINE"):
  615. # continue
  616. if data["ATTRIBUT"][attr]["ACTIVE"]:
  617. out +=1
  618. data["ATTRIBUT"][attr]["ACTIVE"] = 0
  619. return out