node-red-contrib-huemagic 4.2.2 → 5.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (515) hide show
  1. package/CHANGELOG.md +94 -5
  2. package/README.md +525 -123
  3. package/examples/Deutsch/Hue Automation.json +176 -0
  4. package/examples/Deutsch/Hue Bridge.json +256 -0
  5. package/examples/Deutsch/Hue Brightness.json +150 -0
  6. package/examples/Deutsch/Hue Buttons.json +88 -0
  7. package/examples/Deutsch/Hue Contact.json +150 -0
  8. package/examples/Deutsch/Hue Group.json +658 -0
  9. package/examples/Deutsch/Hue Light.json +893 -0
  10. package/examples/Deutsch/Hue Magic.json +148 -0
  11. package/examples/Deutsch/Hue Motion.json +150 -0
  12. package/examples/Deutsch/Hue Rules.json +150 -0
  13. package/examples/Deutsch/Hue Scene.json +178 -0
  14. package/examples/Deutsch/Hue Speaker.json +274 -0
  15. package/examples/Deutsch/Hue Sync Box.json +378 -0
  16. package/examples/Deutsch/Hue Temperature.json +150 -0
  17. package/examples/English/Hue Automation.json +176 -0
  18. package/examples/{Hue Bridge.json → English/Hue Bridge.json } +1 -1
  19. package/examples/{Hue Brightness.json → English/Hue Brightness.json } +1 -1
  20. package/examples/{Hue Buttons.json → English/Hue Buttons.json } +1 -1
  21. package/examples/English/Hue Contact.json +150 -0
  22. package/examples/{Hue Group.json → English/Hue Group.json } +1 -1
  23. package/examples/{Hue Light.json → English/Hue Light.json } +1 -1
  24. package/examples/{Hue Magic.json → English/Hue Magic.json } +1 -1
  25. package/examples/{Hue Motion.json → English/Hue Motion.json } +1 -1
  26. package/examples/{Hue Rules.json → English/Hue Rules.json } +1 -1
  27. package/examples/{Hue Scene.json → English/Hue Scene.json } +1 -1
  28. package/examples/English/Hue Speaker.json +274 -0
  29. package/examples/English/Hue Sync Box.json +378 -0
  30. package/examples/{Hue Temperature.json → English/Hue Temperature.json } +1 -1
  31. package/examples/Espa/303/261ol/Hue Automation.json" +176 -0
  32. package/examples/Espa/303/261ol/Hue Bridge.json" +256 -0
  33. package/examples/Espa/303/261ol/Hue Brightness.json" +150 -0
  34. package/examples/Espa/303/261ol/Hue Buttons.json" +88 -0
  35. package/examples/Espa/303/261ol/Hue Contact.json" +150 -0
  36. package/examples/Espa/303/261ol/Hue Group.json" +658 -0
  37. package/examples/Espa/303/261ol/Hue Light.json" +893 -0
  38. package/examples/Espa/303/261ol/Hue Magic.json" +148 -0
  39. package/examples/Espa/303/261ol/Hue Motion.json" +150 -0
  40. package/examples/Espa/303/261ol/Hue Rules.json" +150 -0
  41. package/examples/Espa/303/261ol/Hue Scene.json" +178 -0
  42. package/examples/Espa/303/261ol/Hue Speaker.json" +274 -0
  43. package/examples/Espa/303/261ol/Hue Sync Box.json" +378 -0
  44. package/examples/Espa/303/261ol/Hue Temperature.json" +150 -0
  45. package/examples/Fran/303/247ais/Hue Automation.json" +176 -0
  46. package/examples/Fran/303/247ais/Hue Bridge.json" +256 -0
  47. package/examples/Fran/303/247ais/Hue Brightness.json" +150 -0
  48. package/examples/Fran/303/247ais/Hue Buttons.json" +88 -0
  49. package/examples/Fran/303/247ais/Hue Contact.json" +150 -0
  50. package/examples/Fran/303/247ais/Hue Group.json" +658 -0
  51. package/examples/Fran/303/247ais/Hue Light.json" +893 -0
  52. package/examples/Fran/303/247ais/Hue Magic.json" +148 -0
  53. package/examples/Fran/303/247ais/Hue Motion.json" +150 -0
  54. package/examples/Fran/303/247ais/Hue Rules.json" +150 -0
  55. package/examples/Fran/303/247ais/Hue Scene.json" +178 -0
  56. package/examples/Fran/303/247ais/Hue Speaker.json" +274 -0
  57. package/examples/Fran/303/247ais/Hue Sync Box.json" +378 -0
  58. package/examples/Fran/303/247ais/Hue Temperature.json" +150 -0
  59. package/examples/Italiano/Hue Automation.json +176 -0
  60. package/examples/Italiano/Hue Bridge.json +256 -0
  61. package/examples/Italiano/Hue Brightness.json +150 -0
  62. package/examples/Italiano/Hue Buttons.json +88 -0
  63. package/examples/Italiano/Hue Contact.json +150 -0
  64. package/examples/Italiano/Hue Group.json +658 -0
  65. package/examples/Italiano/Hue Light.json +893 -0
  66. package/examples/Italiano/Hue Magic.json +148 -0
  67. package/examples/Italiano/Hue Motion.json +150 -0
  68. package/examples/Italiano/Hue Rules.json +150 -0
  69. package/examples/Italiano/Hue Scene.json +178 -0
  70. package/examples/Italiano/Hue Speaker.json +274 -0
  71. package/examples/Italiano/Hue Sync Box.json +378 -0
  72. package/examples/Italiano/Hue Temperature.json +150 -0
  73. package/examples/Nederlands/Hue Automation.json +176 -0
  74. package/examples/Nederlands/Hue Bridge.json +256 -0
  75. package/examples/Nederlands/Hue Brightness.json +150 -0
  76. package/examples/Nederlands/Hue Buttons.json +88 -0
  77. package/examples/Nederlands/Hue Contact.json +150 -0
  78. package/examples/Nederlands/Hue Group.json +658 -0
  79. package/examples/Nederlands/Hue Light.json +893 -0
  80. package/examples/Nederlands/Hue Magic.json +148 -0
  81. package/examples/Nederlands/Hue Motion.json +150 -0
  82. package/examples/Nederlands/Hue Rules.json +150 -0
  83. package/examples/Nederlands/Hue Scene.json +178 -0
  84. package/examples/Nederlands/Hue Speaker.json +274 -0
  85. package/examples/Nederlands/Hue Sync Box.json +378 -0
  86. package/examples/Nederlands/Hue Temperature.json +150 -0
  87. package/examples/Polski/Hue Automation.json +176 -0
  88. package/examples/Polski/Hue Bridge.json +256 -0
  89. package/examples/Polski/Hue Brightness.json +150 -0
  90. package/examples/Polski/Hue Buttons.json +88 -0
  91. package/examples/Polski/Hue Contact.json +150 -0
  92. package/examples/Polski/Hue Group.json +658 -0
  93. package/examples/Polski/Hue Light.json +893 -0
  94. package/examples/Polski/Hue Magic.json +148 -0
  95. package/examples/Polski/Hue Motion.json +150 -0
  96. package/examples/Polski/Hue Rules.json +150 -0
  97. package/examples/Polski/Hue Scene.json +178 -0
  98. package/examples/Polski/Hue Speaker.json +274 -0
  99. package/examples/Polski/Hue Sync Box.json +378 -0
  100. package/examples/Polski/Hue Temperature.json +150 -0
  101. package/examples/Portugu/303/252s/Hue Automation.json" +176 -0
  102. package/examples/Portugu/303/252s/Hue Bridge.json" +256 -0
  103. package/examples/Portugu/303/252s/Hue Brightness.json" +150 -0
  104. package/examples/Portugu/303/252s/Hue Buttons.json" +88 -0
  105. package/examples/Portugu/303/252s/Hue Contact.json" +150 -0
  106. package/examples/Portugu/303/252s/Hue Group.json" +658 -0
  107. package/examples/Portugu/303/252s/Hue Light.json" +893 -0
  108. package/examples/Portugu/303/252s/Hue Magic.json" +148 -0
  109. package/examples/Portugu/303/252s/Hue Motion.json" +150 -0
  110. package/examples/Portugu/303/252s/Hue Rules.json" +150 -0
  111. package/examples/Portugu/303/252s/Hue Scene.json" +178 -0
  112. package/examples/Portugu/303/252s/Hue Speaker.json" +274 -0
  113. package/examples/Portugu/303/252s/Hue Sync Box.json" +378 -0
  114. package/examples/Portugu/303/252s/Hue Temperature.json" +150 -0
  115. package/examples/T/303/274rk/303/247e/Hue Automation.json" +176 -0
  116. package/examples/T/303/274rk/303/247e/Hue Bridge.json" +256 -0
  117. package/examples/T/303/274rk/303/247e/Hue Brightness.json" +150 -0
  118. package/examples/T/303/274rk/303/247e/Hue Buttons.json" +88 -0
  119. package/examples/T/303/274rk/303/247e/Hue Contact.json" +150 -0
  120. package/examples/T/303/274rk/303/247e/Hue Group.json" +658 -0
  121. package/examples/T/303/274rk/303/247e/Hue Light.json" +893 -0
  122. package/examples/T/303/274rk/303/247e/Hue Magic.json" +148 -0
  123. package/examples/T/303/274rk/303/247e/Hue Motion.json" +150 -0
  124. package/examples/T/303/274rk/303/247e/Hue Rules.json" +150 -0
  125. package/examples/T/303/274rk/303/247e/Hue Scene.json" +178 -0
  126. package/examples/T/303/274rk/303/247e/Hue Speaker.json" +274 -0
  127. package/examples/T/303/274rk/303/247e/Hue Sync Box.json" +378 -0
  128. package/examples/T/303/274rk/303/247e/Hue Temperature.json" +150 -0
  129. package/examples//316/225/316/273/316/273/316/267/316/275/316/271/316/272/316/254/Hue Automation.json" +176 -0
  130. package/examples//316/225/316/273/316/273/316/267/316/275/316/271/316/272/316/254/Hue Bridge.json" +256 -0
  131. package/examples//316/225/316/273/316/273/316/267/316/275/316/271/316/272/316/254/Hue Brightness.json" +150 -0
  132. package/examples//316/225/316/273/316/273/316/267/316/275/316/271/316/272/316/254/Hue Buttons.json" +88 -0
  133. package/examples//316/225/316/273/316/273/316/267/316/275/316/271/316/272/316/254/Hue Contact.json" +150 -0
  134. package/examples//316/225/316/273/316/273/316/267/316/275/316/271/316/272/316/254/Hue Group.json" +658 -0
  135. package/examples//316/225/316/273/316/273/316/267/316/275/316/271/316/272/316/254/Hue Light.json" +893 -0
  136. package/examples//316/225/316/273/316/273/316/267/316/275/316/271/316/272/316/254/Hue Magic.json" +148 -0
  137. package/examples//316/225/316/273/316/273/316/267/316/275/316/271/316/272/316/254/Hue Motion.json" +150 -0
  138. package/examples//316/225/316/273/316/273/316/267/316/275/316/271/316/272/316/254/Hue Rules.json" +150 -0
  139. package/examples//316/225/316/273/316/273/316/267/316/275/316/271/316/272/316/254/Hue Scene.json" +178 -0
  140. package/examples//316/225/316/273/316/273/316/267/316/275/316/271/316/272/316/254/Hue Speaker.json" +274 -0
  141. package/examples//316/225/316/273/316/273/316/267/316/275/316/271/316/272/316/254/Hue Sync Box.json" +378 -0
  142. package/examples//316/225/316/273/316/273/316/267/316/275/316/271/316/272/316/254/Hue Temperature.json" +150 -0
  143. package/huemagic/assets/huemagic-examples.gif +0 -0
  144. package/huemagic/hue-automation.html +218 -0
  145. package/huemagic/hue-automation.js +173 -0
  146. package/huemagic/hue-bridge-config.html +39 -32
  147. package/huemagic/hue-bridge-config.js +290 -99
  148. package/huemagic/hue-bridge.html +8 -8
  149. package/huemagic/hue-bridge.js +29 -4
  150. package/huemagic/hue-brightness.html +21 -9
  151. package/huemagic/hue-brightness.js +20 -10
  152. package/huemagic/hue-buttons.html +227 -11
  153. package/huemagic/hue-buttons.js +183 -28
  154. package/huemagic/hue-contact.html +218 -0
  155. package/huemagic/hue-contact.js +209 -0
  156. package/huemagic/hue-group.html +16 -9
  157. package/huemagic/hue-group.js +141 -179
  158. package/huemagic/hue-light.html +19 -12
  159. package/huemagic/hue-light.js +222 -167
  160. package/huemagic/hue-magic.html +3 -5
  161. package/huemagic/hue-motion.html +16 -9
  162. package/huemagic/hue-motion.js +37 -8
  163. package/huemagic/hue-rules.html +16 -9
  164. package/huemagic/hue-rules.js +16 -8
  165. package/huemagic/hue-scene.html +9 -5
  166. package/huemagic/hue-scene.js +52 -16
  167. package/huemagic/hue-speaker.html +218 -0
  168. package/huemagic/hue-speaker.js +257 -0
  169. package/huemagic/hue-syncbox-config.html +123 -0
  170. package/huemagic/hue-syncbox-config.js +179 -0
  171. package/huemagic/hue-syncbox.html +82 -0
  172. package/huemagic/hue-syncbox.js +274 -0
  173. package/huemagic/hue-temperature.html +21 -9
  174. package/huemagic/hue-temperature.js +18 -10
  175. package/huemagic/icons/hue-automation.png +0 -0
  176. package/huemagic/icons/hue-automation.svg +3 -0
  177. package/huemagic/icons/hue-contact.png +0 -0
  178. package/huemagic/icons/hue-contact.svg +4 -0
  179. package/huemagic/icons/hue-speaker.png +0 -0
  180. package/huemagic/icons/hue-speaker.svg +6 -0
  181. package/huemagic/icons/hue-syncbox.png +0 -0
  182. package/huemagic/icons/hue-syncbox.svg +7 -0
  183. package/huemagic/locales/de/hue-automation.html +59 -0
  184. package/huemagic/locales/de/hue-automation.json +33 -0
  185. package/huemagic/locales/de/hue-bridge-config.json +23 -12
  186. package/huemagic/locales/de/hue-bridge.html +7 -4
  187. package/huemagic/locales/de/hue-bridge.json +9 -10
  188. package/huemagic/locales/de/hue-brightness.html +9 -6
  189. package/huemagic/locales/de/hue-brightness.json +13 -9
  190. package/huemagic/locales/de/hue-buttons.html +7 -4
  191. package/huemagic/locales/de/hue-buttons.json +42 -8
  192. package/huemagic/locales/de/hue-contact.html +67 -0
  193. package/huemagic/locales/de/hue-contact.json +35 -0
  194. package/huemagic/locales/de/hue-group.html +17 -6
  195. package/huemagic/locales/de/hue-group.json +16 -9
  196. package/huemagic/locales/de/hue-light.html +6 -6
  197. package/huemagic/locales/de/hue-light.json +17 -9
  198. package/huemagic/locales/de/hue-magic.html +1 -1
  199. package/huemagic/locales/de/hue-magic.json +6 -8
  200. package/huemagic/locales/de/hue-motion.html +4 -4
  201. package/huemagic/locales/de/hue-motion.json +14 -9
  202. package/huemagic/locales/de/hue-rules.html +2 -2
  203. package/huemagic/locales/de/hue-rules.json +10 -9
  204. package/huemagic/locales/de/hue-scene.html +11 -3
  205. package/huemagic/locales/de/hue-scene.json +9 -9
  206. package/huemagic/locales/de/hue-speaker.html +80 -0
  207. package/huemagic/locales/de/hue-speaker.json +38 -0
  208. package/huemagic/locales/de/hue-syncbox-config.html +19 -0
  209. package/huemagic/locales/de/hue-syncbox-config.json +25 -0
  210. package/huemagic/locales/de/hue-syncbox.html +86 -0
  211. package/huemagic/locales/de/hue-syncbox.json +30 -0
  212. package/huemagic/locales/de/hue-temperature.html +7 -5
  213. package/huemagic/locales/de/hue-temperature.json +13 -9
  214. package/huemagic/locales/el/hue-automation.html +59 -0
  215. package/huemagic/locales/el/hue-automation.json +33 -0
  216. package/huemagic/locales/el/hue-bridge-config.html +31 -0
  217. package/huemagic/locales/el/hue-bridge-config.json +40 -0
  218. package/huemagic/locales/el/hue-bridge.html +96 -0
  219. package/huemagic/locales/el/hue-bridge.json +26 -0
  220. package/huemagic/locales/el/hue-brightness.html +70 -0
  221. package/huemagic/locales/el/hue-brightness.json +37 -0
  222. package/huemagic/locales/el/hue-buttons.html +61 -0
  223. package/huemagic/locales/el/hue-buttons.json +62 -0
  224. package/huemagic/locales/el/hue-contact.html +67 -0
  225. package/huemagic/locales/el/hue-contact.json +35 -0
  226. package/huemagic/locales/el/hue-group.html +87 -0
  227. package/huemagic/locales/el/hue-group.json +46 -0
  228. package/huemagic/locales/el/hue-light.html +97 -0
  229. package/huemagic/locales/el/hue-light.json +44 -0
  230. package/huemagic/locales/el/hue-magic.html +68 -0
  231. package/huemagic/locales/el/hue-magic.json +26 -0
  232. package/huemagic/locales/el/hue-motion.html +64 -0
  233. package/huemagic/locales/el/hue-motion.json +36 -0
  234. package/huemagic/locales/el/hue-rules.html +69 -0
  235. package/huemagic/locales/el/hue-rules.json +30 -0
  236. package/huemagic/locales/el/hue-scene.html +43 -0
  237. package/huemagic/locales/el/hue-scene.json +32 -0
  238. package/huemagic/locales/el/hue-speaker.html +80 -0
  239. package/huemagic/locales/el/hue-speaker.json +38 -0
  240. package/huemagic/locales/el/hue-syncbox-config.html +19 -0
  241. package/huemagic/locales/el/hue-syncbox-config.json +25 -0
  242. package/huemagic/locales/el/hue-syncbox.html +86 -0
  243. package/huemagic/locales/el/hue-syncbox.json +30 -0
  244. package/huemagic/locales/el/hue-temperature.html +69 -0
  245. package/huemagic/locales/el/hue-temperature.json +33 -0
  246. package/huemagic/locales/en/hue-automation.html +59 -0
  247. package/huemagic/locales/en/hue-automation.json +33 -0
  248. package/huemagic/locales/en/hue-bridge-config.json +40 -0
  249. package/huemagic/locales/{en-US → en}/hue-bridge.html +5 -2
  250. package/huemagic/locales/{en-US → en}/hue-bridge.json +8 -9
  251. package/huemagic/locales/{en-US → en}/hue-brightness.html +6 -3
  252. package/huemagic/locales/{en-US → en}/hue-brightness.json +11 -9
  253. package/huemagic/locales/{en-US → en}/hue-buttons.html +6 -3
  254. package/huemagic/locales/en/hue-buttons.json +62 -0
  255. package/huemagic/locales/en/hue-contact.html +67 -0
  256. package/huemagic/locales/en/hue-contact.json +35 -0
  257. package/huemagic/locales/{en-US → en}/hue-group.html +16 -5
  258. package/huemagic/locales/{en-US → en}/hue-group.json +15 -10
  259. package/huemagic/locales/{en-US → en}/hue-light.html +3 -3
  260. package/huemagic/locales/en/hue-light.json +44 -0
  261. package/huemagic/locales/{en-US → en}/hue-magic.html +1 -1
  262. package/huemagic/locales/{en-US → en}/hue-motion.html +2 -2
  263. package/huemagic/locales/{en-US → en}/hue-motion.json +12 -9
  264. package/huemagic/locales/{en-US → en}/hue-rules.html +2 -2
  265. package/huemagic/locales/{en-US → en}/hue-rules.json +10 -9
  266. package/huemagic/locales/{en-US → en}/hue-scene.html +11 -3
  267. package/huemagic/locales/{en-US → en}/hue-scene.json +9 -9
  268. package/huemagic/locales/en/hue-speaker.html +80 -0
  269. package/huemagic/locales/en/hue-speaker.json +38 -0
  270. package/huemagic/locales/en/hue-syncbox-config.html +19 -0
  271. package/huemagic/locales/en/hue-syncbox-config.json +25 -0
  272. package/huemagic/locales/en/hue-syncbox.html +86 -0
  273. package/huemagic/locales/en/hue-syncbox.json +30 -0
  274. package/huemagic/locales/{en-US → en}/hue-temperature.html +4 -2
  275. package/huemagic/locales/{en-US → en}/hue-temperature.json +11 -9
  276. package/huemagic/locales/es/hue-automation.html +59 -0
  277. package/huemagic/locales/es/hue-automation.json +33 -0
  278. package/huemagic/locales/es/hue-bridge-config.html +31 -0
  279. package/huemagic/locales/es/hue-bridge-config.json +40 -0
  280. package/huemagic/locales/es/hue-bridge.html +96 -0
  281. package/huemagic/locales/es/hue-bridge.json +26 -0
  282. package/huemagic/locales/es/hue-brightness.html +70 -0
  283. package/huemagic/locales/es/hue-brightness.json +37 -0
  284. package/huemagic/locales/es/hue-buttons.html +61 -0
  285. package/huemagic/locales/es/hue-buttons.json +62 -0
  286. package/huemagic/locales/es/hue-contact.html +67 -0
  287. package/huemagic/locales/es/hue-contact.json +35 -0
  288. package/huemagic/locales/es/hue-group.html +87 -0
  289. package/huemagic/locales/es/hue-group.json +46 -0
  290. package/huemagic/locales/es/hue-light.html +97 -0
  291. package/huemagic/locales/es/hue-light.json +44 -0
  292. package/huemagic/locales/es/hue-magic.html +68 -0
  293. package/huemagic/locales/es/hue-magic.json +26 -0
  294. package/huemagic/locales/es/hue-motion.html +64 -0
  295. package/huemagic/locales/es/hue-motion.json +36 -0
  296. package/huemagic/locales/es/hue-rules.html +69 -0
  297. package/huemagic/locales/es/hue-rules.json +30 -0
  298. package/huemagic/locales/es/hue-scene.html +43 -0
  299. package/huemagic/locales/es/hue-scene.json +32 -0
  300. package/huemagic/locales/es/hue-speaker.html +80 -0
  301. package/huemagic/locales/es/hue-speaker.json +38 -0
  302. package/huemagic/locales/es/hue-syncbox-config.html +19 -0
  303. package/huemagic/locales/es/hue-syncbox-config.json +25 -0
  304. package/huemagic/locales/es/hue-syncbox.html +86 -0
  305. package/huemagic/locales/es/hue-syncbox.json +30 -0
  306. package/huemagic/locales/es/hue-temperature.html +69 -0
  307. package/huemagic/locales/es/hue-temperature.json +33 -0
  308. package/huemagic/locales/fr/hue-automation.html +59 -0
  309. package/huemagic/locales/fr/hue-automation.json +33 -0
  310. package/huemagic/locales/fr/hue-bridge-config.html +31 -0
  311. package/huemagic/locales/fr/hue-bridge-config.json +40 -0
  312. package/huemagic/locales/fr/hue-bridge.html +96 -0
  313. package/huemagic/locales/fr/hue-bridge.json +26 -0
  314. package/huemagic/locales/fr/hue-brightness.html +70 -0
  315. package/huemagic/locales/fr/hue-brightness.json +37 -0
  316. package/huemagic/locales/fr/hue-buttons.html +61 -0
  317. package/huemagic/locales/fr/hue-buttons.json +62 -0
  318. package/huemagic/locales/fr/hue-contact.html +67 -0
  319. package/huemagic/locales/fr/hue-contact.json +35 -0
  320. package/huemagic/locales/fr/hue-group.html +87 -0
  321. package/huemagic/locales/fr/hue-group.json +46 -0
  322. package/huemagic/locales/fr/hue-light.html +97 -0
  323. package/huemagic/locales/fr/hue-light.json +44 -0
  324. package/huemagic/locales/fr/hue-magic.html +68 -0
  325. package/huemagic/locales/fr/hue-magic.json +26 -0
  326. package/huemagic/locales/fr/hue-motion.html +64 -0
  327. package/huemagic/locales/fr/hue-motion.json +36 -0
  328. package/huemagic/locales/fr/hue-rules.html +69 -0
  329. package/huemagic/locales/fr/hue-rules.json +30 -0
  330. package/huemagic/locales/fr/hue-scene.html +43 -0
  331. package/huemagic/locales/fr/hue-scene.json +32 -0
  332. package/huemagic/locales/fr/hue-speaker.html +80 -0
  333. package/huemagic/locales/fr/hue-speaker.json +38 -0
  334. package/huemagic/locales/fr/hue-syncbox-config.html +19 -0
  335. package/huemagic/locales/fr/hue-syncbox-config.json +25 -0
  336. package/huemagic/locales/fr/hue-syncbox.html +86 -0
  337. package/huemagic/locales/fr/hue-syncbox.json +30 -0
  338. package/huemagic/locales/fr/hue-temperature.html +69 -0
  339. package/huemagic/locales/fr/hue-temperature.json +33 -0
  340. package/huemagic/locales/it/hue-automation.html +59 -0
  341. package/huemagic/locales/it/hue-automation.json +33 -0
  342. package/huemagic/locales/it/hue-bridge-config.html +31 -0
  343. package/huemagic/locales/it/hue-bridge-config.json +40 -0
  344. package/huemagic/locales/it/hue-bridge.html +96 -0
  345. package/huemagic/locales/it/hue-bridge.json +26 -0
  346. package/huemagic/locales/it/hue-brightness.html +70 -0
  347. package/huemagic/locales/it/hue-brightness.json +37 -0
  348. package/huemagic/locales/it/hue-buttons.html +61 -0
  349. package/huemagic/locales/it/hue-buttons.json +62 -0
  350. package/huemagic/locales/it/hue-contact.html +67 -0
  351. package/huemagic/locales/it/hue-contact.json +35 -0
  352. package/huemagic/locales/it/hue-group.html +87 -0
  353. package/huemagic/locales/it/hue-group.json +46 -0
  354. package/huemagic/locales/it/hue-light.html +97 -0
  355. package/huemagic/locales/it/hue-light.json +44 -0
  356. package/huemagic/locales/it/hue-magic.html +68 -0
  357. package/huemagic/locales/it/hue-magic.json +26 -0
  358. package/huemagic/locales/it/hue-motion.html +64 -0
  359. package/huemagic/locales/it/hue-motion.json +36 -0
  360. package/huemagic/locales/it/hue-rules.html +69 -0
  361. package/huemagic/locales/it/hue-rules.json +30 -0
  362. package/huemagic/locales/it/hue-scene.html +43 -0
  363. package/huemagic/locales/it/hue-scene.json +32 -0
  364. package/huemagic/locales/it/hue-speaker.html +80 -0
  365. package/huemagic/locales/it/hue-speaker.json +38 -0
  366. package/huemagic/locales/it/hue-syncbox-config.html +19 -0
  367. package/huemagic/locales/it/hue-syncbox-config.json +25 -0
  368. package/huemagic/locales/it/hue-syncbox.html +86 -0
  369. package/huemagic/locales/it/hue-syncbox.json +30 -0
  370. package/huemagic/locales/it/hue-temperature.html +69 -0
  371. package/huemagic/locales/it/hue-temperature.json +33 -0
  372. package/huemagic/locales/nl/hue-automation.html +59 -0
  373. package/huemagic/locales/nl/hue-automation.json +33 -0
  374. package/huemagic/locales/nl/hue-bridge-config.html +31 -0
  375. package/huemagic/locales/nl/hue-bridge-config.json +40 -0
  376. package/huemagic/locales/nl/hue-bridge.html +96 -0
  377. package/huemagic/locales/nl/hue-bridge.json +26 -0
  378. package/huemagic/locales/nl/hue-brightness.html +70 -0
  379. package/huemagic/locales/nl/hue-brightness.json +37 -0
  380. package/huemagic/locales/nl/hue-buttons.html +61 -0
  381. package/huemagic/locales/nl/hue-buttons.json +62 -0
  382. package/huemagic/locales/nl/hue-contact.html +67 -0
  383. package/huemagic/locales/nl/hue-contact.json +35 -0
  384. package/huemagic/locales/nl/hue-group.html +87 -0
  385. package/huemagic/locales/nl/hue-group.json +46 -0
  386. package/huemagic/locales/nl/hue-light.html +97 -0
  387. package/huemagic/locales/nl/hue-light.json +44 -0
  388. package/huemagic/locales/nl/hue-magic.html +68 -0
  389. package/huemagic/locales/nl/hue-magic.json +26 -0
  390. package/huemagic/locales/nl/hue-motion.html +64 -0
  391. package/huemagic/locales/nl/hue-motion.json +36 -0
  392. package/huemagic/locales/nl/hue-rules.html +69 -0
  393. package/huemagic/locales/nl/hue-rules.json +30 -0
  394. package/huemagic/locales/nl/hue-scene.html +43 -0
  395. package/huemagic/locales/nl/hue-scene.json +32 -0
  396. package/huemagic/locales/nl/hue-speaker.html +80 -0
  397. package/huemagic/locales/nl/hue-speaker.json +38 -0
  398. package/huemagic/locales/nl/hue-syncbox-config.html +19 -0
  399. package/huemagic/locales/nl/hue-syncbox-config.json +25 -0
  400. package/huemagic/locales/nl/hue-syncbox.html +86 -0
  401. package/huemagic/locales/nl/hue-syncbox.json +30 -0
  402. package/huemagic/locales/nl/hue-temperature.html +69 -0
  403. package/huemagic/locales/nl/hue-temperature.json +33 -0
  404. package/huemagic/locales/pl/hue-automation.html +59 -0
  405. package/huemagic/locales/pl/hue-automation.json +33 -0
  406. package/huemagic/locales/pl/hue-bridge-config.html +31 -0
  407. package/huemagic/locales/pl/hue-bridge-config.json +40 -0
  408. package/huemagic/locales/pl/hue-bridge.html +96 -0
  409. package/huemagic/locales/pl/hue-bridge.json +26 -0
  410. package/huemagic/locales/pl/hue-brightness.html +70 -0
  411. package/huemagic/locales/pl/hue-brightness.json +37 -0
  412. package/huemagic/locales/pl/hue-buttons.html +61 -0
  413. package/huemagic/locales/pl/hue-buttons.json +62 -0
  414. package/huemagic/locales/pl/hue-contact.html +67 -0
  415. package/huemagic/locales/pl/hue-contact.json +35 -0
  416. package/huemagic/locales/pl/hue-group.html +87 -0
  417. package/huemagic/locales/pl/hue-group.json +46 -0
  418. package/huemagic/locales/pl/hue-light.html +97 -0
  419. package/huemagic/locales/pl/hue-light.json +44 -0
  420. package/huemagic/locales/pl/hue-magic.html +68 -0
  421. package/huemagic/locales/pl/hue-magic.json +26 -0
  422. package/huemagic/locales/pl/hue-motion.html +64 -0
  423. package/huemagic/locales/pl/hue-motion.json +36 -0
  424. package/huemagic/locales/pl/hue-rules.html +69 -0
  425. package/huemagic/locales/pl/hue-rules.json +30 -0
  426. package/huemagic/locales/pl/hue-scene.html +43 -0
  427. package/huemagic/locales/pl/hue-scene.json +32 -0
  428. package/huemagic/locales/pl/hue-speaker.html +80 -0
  429. package/huemagic/locales/pl/hue-speaker.json +38 -0
  430. package/huemagic/locales/pl/hue-syncbox-config.html +19 -0
  431. package/huemagic/locales/pl/hue-syncbox-config.json +25 -0
  432. package/huemagic/locales/pl/hue-syncbox.html +86 -0
  433. package/huemagic/locales/pl/hue-syncbox.json +30 -0
  434. package/huemagic/locales/pl/hue-temperature.html +69 -0
  435. package/huemagic/locales/pl/hue-temperature.json +33 -0
  436. package/huemagic/locales/pt/hue-automation.html +59 -0
  437. package/huemagic/locales/pt/hue-automation.json +33 -0
  438. package/huemagic/locales/pt/hue-bridge-config.html +31 -0
  439. package/huemagic/locales/pt/hue-bridge-config.json +40 -0
  440. package/huemagic/locales/pt/hue-bridge.html +96 -0
  441. package/huemagic/locales/pt/hue-bridge.json +26 -0
  442. package/huemagic/locales/pt/hue-brightness.html +70 -0
  443. package/huemagic/locales/pt/hue-brightness.json +37 -0
  444. package/huemagic/locales/pt/hue-buttons.html +61 -0
  445. package/huemagic/locales/pt/hue-buttons.json +62 -0
  446. package/huemagic/locales/pt/hue-contact.html +67 -0
  447. package/huemagic/locales/pt/hue-contact.json +35 -0
  448. package/huemagic/locales/pt/hue-group.html +87 -0
  449. package/huemagic/locales/pt/hue-group.json +46 -0
  450. package/huemagic/locales/pt/hue-light.html +97 -0
  451. package/huemagic/locales/pt/hue-light.json +44 -0
  452. package/huemagic/locales/pt/hue-magic.html +68 -0
  453. package/huemagic/locales/pt/hue-magic.json +26 -0
  454. package/huemagic/locales/pt/hue-motion.html +64 -0
  455. package/huemagic/locales/pt/hue-motion.json +36 -0
  456. package/huemagic/locales/pt/hue-rules.html +69 -0
  457. package/huemagic/locales/pt/hue-rules.json +30 -0
  458. package/huemagic/locales/pt/hue-scene.html +43 -0
  459. package/huemagic/locales/pt/hue-scene.json +32 -0
  460. package/huemagic/locales/pt/hue-speaker.html +80 -0
  461. package/huemagic/locales/pt/hue-speaker.json +38 -0
  462. package/huemagic/locales/pt/hue-syncbox-config.html +19 -0
  463. package/huemagic/locales/pt/hue-syncbox-config.json +25 -0
  464. package/huemagic/locales/pt/hue-syncbox.html +86 -0
  465. package/huemagic/locales/pt/hue-syncbox.json +30 -0
  466. package/huemagic/locales/pt/hue-temperature.html +69 -0
  467. package/huemagic/locales/pt/hue-temperature.json +33 -0
  468. package/huemagic/locales/tr/hue-automation.html +59 -0
  469. package/huemagic/locales/tr/hue-automation.json +33 -0
  470. package/huemagic/locales/tr/hue-bridge-config.html +31 -0
  471. package/huemagic/locales/tr/hue-bridge-config.json +40 -0
  472. package/huemagic/locales/tr/hue-bridge.html +96 -0
  473. package/huemagic/locales/tr/hue-bridge.json +26 -0
  474. package/huemagic/locales/tr/hue-brightness.html +70 -0
  475. package/huemagic/locales/tr/hue-brightness.json +37 -0
  476. package/huemagic/locales/tr/hue-buttons.html +61 -0
  477. package/huemagic/locales/tr/hue-buttons.json +62 -0
  478. package/huemagic/locales/tr/hue-contact.html +67 -0
  479. package/huemagic/locales/tr/hue-contact.json +35 -0
  480. package/huemagic/locales/tr/hue-group.html +87 -0
  481. package/huemagic/locales/tr/hue-group.json +46 -0
  482. package/huemagic/locales/tr/hue-light.html +97 -0
  483. package/huemagic/locales/tr/hue-light.json +44 -0
  484. package/huemagic/locales/tr/hue-magic.html +68 -0
  485. package/huemagic/locales/tr/hue-magic.json +26 -0
  486. package/huemagic/locales/tr/hue-motion.html +64 -0
  487. package/huemagic/locales/tr/hue-motion.json +36 -0
  488. package/huemagic/locales/tr/hue-rules.html +69 -0
  489. package/huemagic/locales/tr/hue-rules.json +30 -0
  490. package/huemagic/locales/tr/hue-scene.html +43 -0
  491. package/huemagic/locales/tr/hue-scene.json +32 -0
  492. package/huemagic/locales/tr/hue-speaker.html +80 -0
  493. package/huemagic/locales/tr/hue-speaker.json +38 -0
  494. package/huemagic/locales/tr/hue-syncbox-config.html +19 -0
  495. package/huemagic/locales/tr/hue-syncbox-config.json +25 -0
  496. package/huemagic/locales/tr/hue-syncbox.html +86 -0
  497. package/huemagic/locales/tr/hue-syncbox.json +30 -0
  498. package/huemagic/locales/tr/hue-temperature.html +69 -0
  499. package/huemagic/locales/tr/hue-temperature.json +33 -0
  500. package/huemagic/utils/api.js +158 -54
  501. package/huemagic/utils/color.js +20 -2
  502. package/huemagic/utils/merge.js +3 -2
  503. package/huemagic/utils/messages.js +506 -120
  504. package/huemagic/utils/sse.js +51 -0
  505. package/huemagic/utils/syncbox.js +84 -0
  506. package/package.json +19 -11
  507. package/.github/FUNDING.yml +0 -2
  508. package/.github/ISSUE_TEMPLATE/bug_report.md +0 -30
  509. package/.github/ISSUE_TEMPLATE/feature_request.md +0 -20
  510. package/.github/workflows/npm.yml +0 -18
  511. package/huemagic/locales/en-US/hue-bridge-config.json +0 -29
  512. package/huemagic/locales/en-US/hue-buttons.json +0 -34
  513. package/huemagic/locales/en-US/hue-light.json +0 -38
  514. /package/huemagic/locales/{en-US → en}/hue-bridge-config.html +0 -0
  515. /package/huemagic/locales/{en-US → en}/hue-magic.json +0 -0
@@ -0,0 +1,257 @@
1
+ module.exports = function(RED)
2
+ {
3
+ "use strict";
4
+
5
+ function HueSpeaker(config)
6
+ {
7
+ RED.nodes.createNode(this, config);
8
+
9
+ const scope = this;
10
+ const bridge = RED.nodes.getNode(config.bridge);
11
+ const async = require('async');
12
+
13
+ // SAVE LAST COMMAND
14
+ this.lastCommand = null;
15
+
16
+ //
17
+ // CHECK CONFIG
18
+ if(bridge == null)
19
+ {
20
+ this.status({fill: "red", shape: "ring", text: "hue-speaker.node.not-configured"});
21
+ return false;
22
+ }
23
+
24
+ //
25
+ // UNIVERSAL MODE?
26
+ if(!config.speakerid)
27
+ {
28
+ this.status({fill: "grey", shape: "dot", text: "hue-speaker.node.universal"});
29
+ }
30
+
31
+ //
32
+ // UPDATE STATE
33
+ if(config.speakerid)
34
+ {
35
+ this.status({fill: "grey", shape: "dot", text: "hue-speaker.node.silent"});
36
+ }
37
+
38
+ //
39
+ // SUBSCRIBE TO UPDATES FROM THE BRIDGE
40
+ this.unsubscribe = bridge.subscribe("speaker", config.speakerid, function(info)
41
+ {
42
+ let currentState = bridge.get("speaker", info.id);
43
+
44
+ // RESOURCE FOUND?
45
+ if(currentState !== false)
46
+ {
47
+ // SEND MESSAGE
48
+ if(!config.skipevents && (config.initevents || info.suppressMessage == false) && (!config.onlycommands || scope.lastCommand !== null))
49
+ {
50
+ // SET LAST COMMAND
51
+ if(scope.lastCommand !== null)
52
+ {
53
+ currentState.command = scope.lastCommand;
54
+ }
55
+
56
+ // SEND STATE
57
+ scope.send(currentState);
58
+
59
+ // RESET LAST COMMAND
60
+ scope.lastCommand = null;
61
+ }
62
+
63
+ // NOT IN UNIVERAL MODE? -> CHANGE UI STATES
64
+ if(config.speakerid)
65
+ {
66
+ if(currentState.payload.reachable == false)
67
+ {
68
+ scope.status({fill: "red", shape: "ring", text: "hue-speaker.node.not-reachable"});
69
+ }
70
+ else if(currentState.payload.muted === true)
71
+ {
72
+ scope.status({fill: "grey", shape: "ring", text: "hue-speaker.node.muted"});
73
+ }
74
+ else if(currentState.payload.alarm !== false)
75
+ {
76
+ scope.status({fill: "red", shape: "dot", text: RED._("hue-speaker.node.playing-alarm", { sound: currentState.payload.alarm })});
77
+ }
78
+ else if(currentState.payload.chime !== false)
79
+ {
80
+ scope.status({fill: "blue", shape: "dot", text: RED._("hue-speaker.node.playing-chime", { sound: currentState.payload.chime })});
81
+ }
82
+ else if(currentState.payload.alert !== false)
83
+ {
84
+ scope.status({fill: "yellow", shape: "dot", text: RED._("hue-speaker.node.playing-alert", { sound: currentState.payload.alert })});
85
+ }
86
+ else
87
+ {
88
+ scope.status({fill: "grey", shape: "dot", text: "hue-speaker.node.silent"});
89
+ }
90
+ }
91
+ }
92
+ });
93
+
94
+ //
95
+ // CONTROL THE SPEAKER
96
+ this.on('input', function(msg, send, done)
97
+ {
98
+ // REDEFINE SEND AND DONE IF NOT AVAILABLE
99
+ send = send || function() { scope.send.apply(scope,arguments); }
100
+ done = done || function() { scope.done.apply(scope,arguments); }
101
+
102
+ // SAVE LAST COMMAND
103
+ scope.lastCommand = RED.util.cloneMessage(msg);
104
+
105
+ // CREATE PATCH
106
+ let patchObject = {};
107
+
108
+ // DEFINE SPEAKER ID
109
+ const tempSpeakerID = (!config.speakerid && typeof msg.topic != 'undefined' && bridge.validResourceID.test(msg.topic) === true) ? msg.topic : config.speakerid;
110
+ if(!tempSpeakerID)
111
+ {
112
+ scope.error(RED._("hue-speaker.node.error-no-id"), msg);
113
+ return false;
114
+ }
115
+
116
+ // GET CURRENT STATE
117
+ let currentState = bridge.get("speaker", tempSpeakerID);
118
+ if(!currentState)
119
+ {
120
+ scope.error(RED._("hue-speaker.node.error-not-available"), msg);
121
+ return false;
122
+ }
123
+
124
+ // JUST GIVE BACK THE CURRENT STATE
125
+ if( (typeof msg.payload != 'undefined' && typeof msg.payload.status != 'undefined') || (typeof msg.__user_inject_props__ != 'undefined' && msg.__user_inject_props__ == "status") )
126
+ {
127
+ // SET LAST COMMAND
128
+ if(scope.lastCommand !== null)
129
+ {
130
+ currentState.command = scope.lastCommand;
131
+ }
132
+
133
+ // SEND STATE
134
+ scope.send(currentState);
135
+
136
+ // RESET LAST COMMAND
137
+ scope.lastCommand = null;
138
+
139
+ if(done) { done(); }
140
+ return true;
141
+ }
142
+
143
+ // MUTE / UNMUTE
144
+ if(typeof msg.payload != 'undefined' && typeof msg.payload.mute != 'undefined')
145
+ {
146
+ patchObject["mute"] = { mute: (msg.payload.mute === true) ? "mute" : "unmute" };
147
+ }
148
+
149
+ // PLAY A SOUND / STOP IT AGAIN
150
+ for(const channel of ["alarm", "chime", "alert"])
151
+ {
152
+ if(typeof msg.payload == 'undefined' || typeof msg.payload[channel] == 'undefined') { continue; }
153
+
154
+ const supported = currentState.info.sounds[channel];
155
+ if(!supported)
156
+ {
157
+ scope.error(RED._("hue-speaker.node.error-no-channel", { channel: channel }), msg);
158
+ return false;
159
+ }
160
+
161
+ // A PLAIN "false" STOPS WHATEVER IS PLAYING
162
+ const request = (msg.payload[channel] === false) ? { sound: "no_sound" } : ((typeof msg.payload[channel] == 'string') ? { sound: msg.payload[channel] } : msg.payload[channel]);
163
+
164
+ if(!request.sound || (request.sound !== "no_sound" && supported.indexOf(request.sound) === -1))
165
+ {
166
+ scope.error(RED._("hue-speaker.node.error-invalid-sound", { channel: channel, sounds: supported.join(", ") }), msg);
167
+ return false;
168
+ }
169
+
170
+ patchObject[channel] = { sound: request.sound };
171
+
172
+ // HOW LOUD AND HOW LONG
173
+ if(typeof request.volume != 'undefined')
174
+ {
175
+ const volume = parseInt(request.volume);
176
+ if(isNaN(volume) || volume < 0 || volume > 100)
177
+ {
178
+ scope.error(RED._("hue-speaker.node.error-invalid-volume"), msg);
179
+ return false;
180
+ }
181
+
182
+ patchObject[channel]["volume"] = { level: volume };
183
+ }
184
+
185
+ if(typeof request.duration != 'undefined')
186
+ {
187
+ let duration = Math.round(parseFloat(request.duration) * 1000);
188
+ duration = (duration < 0) ? 0 : duration;
189
+
190
+ patchObject[channel]["duration"] = duration;
191
+ }
192
+ }
193
+
194
+ //
195
+ // SHOULD PATCH?
196
+ if(Object.values(patchObject).length > 0)
197
+ {
198
+ // CHANGE NODE UI STATE
199
+ if(config.speakerid)
200
+ {
201
+ scope.status({fill: "grey", shape: "ring", text: "hue-speaker.node.command"});
202
+ }
203
+
204
+ // PATCH!
205
+ async.retry({
206
+ times: 5,
207
+ errorFilter: function(err) {
208
+ return (err.status == 503 || err.status == 429);
209
+ },
210
+ interval: function(retryCount) { return 750*retryCount; }
211
+ },
212
+ function(callback, results)
213
+ {
214
+ bridge.patch("speaker", tempSpeakerID, patchObject)
215
+ .then(function() { callback(null, true); })
216
+ .catch(function(errors) { callback(errors, null); });
217
+ },
218
+ function(errors, success)
219
+ {
220
+ if(errors)
221
+ {
222
+ scope.error(errors, msg);
223
+ }
224
+ else if(done)
225
+ {
226
+ done();
227
+ }
228
+ });
229
+ }
230
+ else
231
+ {
232
+ // SET LAST COMMAND
233
+ if(scope.lastCommand !== null)
234
+ {
235
+ currentState.command = scope.lastCommand;
236
+ }
237
+
238
+ // SEND STATE
239
+ scope.send(currentState);
240
+
241
+ // RESET LAST COMMAND
242
+ scope.lastCommand = null;
243
+
244
+ if(done) { done(); }
245
+ }
246
+ });
247
+
248
+ //
249
+ // CLOSE NODE / DETACH FROM THE BRIDGE
250
+ this.on('close', function()
251
+ {
252
+ if(scope.unsubscribe) { scope.unsubscribe(); }
253
+ });
254
+ }
255
+
256
+ RED.nodes.registerType("hue-speaker", HueSpeaker);
257
+ }
@@ -0,0 +1,123 @@
1
+ <script type="text/x-red" data-template-name="hue-syncbox-config">
2
+ <div class="form-row">
3
+ <label for="node-config-input-name"><i class="fa fa-tag"></i> <span data-i18n="hue-syncbox-config.config.name"></span></label>
4
+ <input type="text" id="node-config-input-name" style="width: calc(100% - 105px)">
5
+ </div>
6
+ <div class="form-row">
7
+ <label for="node-config-input-syncbox"><i class="fa fa-television"></i> <span data-i18n="hue-syncbox-config.config.syncbox-ip"></span></label>
8
+ <input type="text" id="node-config-input-syncbox" placeholder="X.X.X.X" style="width: calc(100% - 105px)">
9
+ </div>
10
+ <div class="form-row">
11
+ <label for="node-config-input-token"><i class="fa fa-key"></i> <span data-i18n="hue-syncbox-config.config.token"></span></label>
12
+ <div style="display: inline-flex; width: calc(100% - 105px)">
13
+ <div style="flex-grow: 1;">
14
+ <input type="text" id="node-config-input-token" style="width: 100%"/>
15
+ </div>
16
+ <button id="node-config-input-register-syncbox" type="button" class="red-ui-button" style="margin-left: 10px;">
17
+ <i class="fa fa-user-circle-o"></i>
18
+ </button>
19
+ </div>
20
+ </div>
21
+ <div class="form-row">
22
+ <label for="node-config-input-pollinterval"><i class="fa fa-refresh"></i> <span data-i18n="hue-syncbox-config.config.pollinterval"></span></label>
23
+ <input type="number" id="node-config-input-pollinterval" placeholder="10" min="2" style="width: calc(100% - 105px)">
24
+ </div>
25
+ </script>
26
+
27
+ <script type="text/javascript">
28
+ RED.nodes.registerType("hue-syncbox-config", {
29
+ category: "config",
30
+ color: '#c7d8d8',
31
+ defaults: {
32
+ name: { value:"Hue Sync Box", required: true },
33
+ syncbox: { value:"", required: true },
34
+ token: { value:"", required: true },
35
+ pollinterval: { value: 10, required: true, validate: function(v) {
36
+ return (!isNaN(v) && v >= 2);
37
+ }}
38
+ },
39
+ icon: "hue-syncbox.svg",
40
+ label: function() {
41
+ return this.name;
42
+ },
43
+ paletteLabel: function() {
44
+ return this._("hue-syncbox-config.node.title");
45
+ },
46
+ oneditprepare: function()
47
+ {
48
+ const scope = this;
49
+
50
+ // CHANGED IP? -> GET NAME (IF POSSIBLE)
51
+ $(document).off('change', '#node-config-input-syncbox');
52
+ $(document).on('change', '#node-config-input-syncbox', function(e)
53
+ {
54
+ let currentIP = $(e.currentTarget).val();
55
+ if(currentIP.length > 3)
56
+ {
57
+ var notification = RED.notify(scope._("hue-syncbox-config.config.connecting"), { type: "compact", modal: true, fixed: true });
58
+
59
+ $.ajax({
60
+ url: 'hue/syncbox/name',
61
+ type: "GET",
62
+ data: { ip: currentIP },
63
+ timeout: 5000
64
+ })
65
+ .done( function(data)
66
+ {
67
+ $('#node-config-input-name').val(data);
68
+ setTimeout(function() { notification.close(); }, 500);
69
+ })
70
+ .fail(function(err)
71
+ {
72
+ notification.close();
73
+ RED.notify(scope._("hue-syncbox-config.config.invalid") + err.statusText, "error");
74
+ });
75
+ }
76
+ });
77
+
78
+ $('#node-config-input-register-syncbox').click(function()
79
+ {
80
+ if(!$('#node-config-input-syncbox').val())
81
+ {
82
+ RED.notify(scope._("hue-syncbox-config.config.error-select-first"), "error");
83
+ return false;
84
+ }
85
+
86
+ $('#node-config-input-token').val("");
87
+ $('#node-config-input-token').attr("placeholder", scope._("hue-syncbox-config.config.waiting"));
88
+
89
+ RED.notify(scope._("hue-syncbox-config.config.new-user-press"));
90
+
91
+ // KEEP ASKING THE BOX UNTIL THE BUTTON HAS BEEN PRESSED
92
+ var attempt = 0;
93
+ var register = function()
94
+ {
95
+ attempt += 1;
96
+
97
+ $.get('hue/syncbox/register', { ip: $('#node-config-input-syncbox').val() } )
98
+ .done( function(data)
99
+ {
100
+ if(data == "error")
101
+ {
102
+ if(attempt < 30) { return setTimeout(register, 2000); }
103
+
104
+ RED.notify(scope._("hue-syncbox-config.config.retry"), "error");
105
+ $('#node-config-input-token').attr("placeholder", "");
106
+ }
107
+ else
108
+ {
109
+ var registration = JSON.parse(data);
110
+ $('#node-config-input-token').val(registration.accessToken);
111
+ RED.notify(scope._("hue-syncbox-config.config.success"));
112
+ }
113
+ }).fail(function(err)
114
+ {
115
+ RED.notify(err.responseText, "error");
116
+ });
117
+ }
118
+
119
+ register();
120
+ });
121
+ }
122
+ });
123
+ </script>
@@ -0,0 +1,179 @@
1
+ module.exports = function(RED)
2
+ {
3
+ "use strict";
4
+
5
+ const API = require('./utils/syncbox');
6
+ const events = require('events');
7
+ const dayjs = require('dayjs');
8
+ const diff = require("deep-object-diff").diff;
9
+
10
+ const { HueSyncBoxMessage } = require('./utils/messages');
11
+
12
+ function HueSyncBox(config)
13
+ {
14
+ const scope = this;
15
+
16
+ // STATES
17
+ this.nodeActive = true;
18
+ this.config = config;
19
+ this.state = false;
20
+ this.lastState = false;
21
+ this.events = new events.EventEmitter();
22
+ this.events.setMaxListeners(0);
23
+ this.pollTimeout = null;
24
+ this.failures = 0;
25
+
26
+ // RESOURCE ID PATTERN (NEVER GLOBAL, "test" WOULD BECOME STATEFUL)
27
+ this.validResourceID = /^[a-zA-Z0-9-]+$/i;
28
+
29
+ // CREATE NODE
30
+ RED.nodes.createNode(scope, config);
31
+
32
+ // THE BOX HAS NO EVENT STREAM, SO IT HAS TO BE ASKED
33
+ this.pollInterval = config.pollinterval ? parseInt(config.pollinterval) * 1000 : 10000;
34
+
35
+ //
36
+ // KEEP THE STATE UP-TO-DATE
37
+ this.poll = function(immediately = false)
38
+ {
39
+ if(scope.pollTimeout !== null) { clearTimeout(scope.pollTimeout); }
40
+ if(scope.nodeActive === false) { return false; }
41
+
42
+ scope.pollTimeout = setTimeout(function()
43
+ {
44
+ API.state(config)
45
+ .then(function(state)
46
+ {
47
+ if(scope.failures > 0) { scope.log(RED._("hue-syncbox-config.node.connected")); }
48
+ scope.failures = 0;
49
+
50
+ state.updated = dayjs().format();
51
+ scope.state = state;
52
+
53
+ scope.pushUpdatedState();
54
+ scope.poll();
55
+ })
56
+ .catch(function(error)
57
+ {
58
+ scope.failures += 1;
59
+
60
+ // ONLY COMPLAIN ONCE PER OUTAGE
61
+ if(scope.failures === 1)
62
+ {
63
+ scope.log(RED._("hue-syncbox-config.node.request-error", { error: JSON.stringify(error.errors ? error.errors : error) }));
64
+ }
65
+
66
+ scope.events.emit(config.id + "_unreachable", true);
67
+ scope.poll();
68
+ });
69
+ }, immediately ? 100 : scope.pollInterval);
70
+ }
71
+
72
+ //
73
+ // PUSH THE STATE TO THE NODES
74
+ this.pushUpdatedState = function()
75
+ {
76
+ const suppressMessage = (scope.lastState === false);
77
+ scope.events.emit(config.id + "_syncbox", { suppressMessage: suppressMessage });
78
+ }
79
+
80
+ //
81
+ // GET THE CURRENT STATE (FROM NODES)
82
+ this.get = function()
83
+ {
84
+ if(!scope.state) { return false; }
85
+
86
+ try {
87
+ const message = new HueSyncBoxMessage(scope.state);
88
+ const previous = scope.lastState ? structuredClone(scope.lastState) : false;
89
+
90
+ let currentState = message.msg;
91
+ scope.lastState = structuredClone(currentState);
92
+
93
+ currentState.updated = (previous === false) ? {} : diff(previous, currentState);
94
+ currentState.lastState = previous;
95
+
96
+ return currentState;
97
+ } catch (error) {
98
+ return false;
99
+ }
100
+ }
101
+
102
+ //
103
+ // CHANGE WHAT THE BOX IS DOING (FROM NODES)
104
+ this.execute = function(patch)
105
+ {
106
+ return API.execute(config, patch).then(function(response)
107
+ {
108
+ // THE BOX ANSWERS WITHOUT A BODY, SO ASK IT AGAIN RIGHT AWAY
109
+ scope.poll(true);
110
+ return response;
111
+ });
112
+ }
113
+
114
+ //
115
+ // SUBSCRIBE (FROM NODES)
116
+ this.subscribe = function(callback)
117
+ {
118
+ const eventName = config.id + "_syncbox";
119
+ scope.events.on(eventName, callback);
120
+
121
+ // THE CONFIG NODE OUTLIVES A REDEPLOY, SO EVERY NODE HAS TO DETACH ITSELF AGAIN
122
+ return function() { scope.events.removeListener(eventName, callback); };
123
+ }
124
+
125
+ //
126
+ // START POLLING
127
+ this.log(RED._("hue-syncbox-config.node.initializing", { syncbox: config.syncbox }));
128
+ this.poll(true);
129
+
130
+ //
131
+ // CLOSE NODE
132
+ this.on('close', function()
133
+ {
134
+ scope.nodeActive = false;
135
+ if(scope.pollTimeout !== null) { clearTimeout(scope.pollTimeout); }
136
+ scope.events.removeAllListeners();
137
+ });
138
+ }
139
+
140
+ RED.nodes.registerType("hue-syncbox-config", HueSyncBox);
141
+
142
+ //
143
+ // GET THE NAME OF A SYNC BOX
144
+ RED.httpAdmin.get('/hue/syncbox/name', RED.auth.needsPermission('hue-syncbox-config.read'), function(req, res, next)
145
+ {
146
+ if(!req.query.ip)
147
+ {
148
+ return res.status(500).send(RED._("hue-syncbox-config.config.missing-ip"));
149
+ }
150
+
151
+ // THE DEVICE ENDPOINT ANSWERS THE NAME EVEN WITHOUT A TOKEN
152
+ API.request({ config: { syncbox: req.query.ip }, resource: "/device", token: false })
153
+ .then(function(device) { res.end(device.name ? device.name : "Hue Sync Box"); })
154
+ .catch(function(error) { res.status(500).send(error.errors ? JSON.stringify(error.errors) : "error"); });
155
+ });
156
+
157
+ //
158
+ // REGISTER WITH A SYNC BOX
159
+ RED.httpAdmin.get('/hue/syncbox/register', RED.auth.needsPermission('hue-syncbox-config.read'), function(req, res, next)
160
+ {
161
+ if(!req.query.ip)
162
+ {
163
+ return res.status(500).send(RED._("hue-syncbox-config.config.missing-ip"));
164
+ }
165
+
166
+ API.register({ syncbox: req.query.ip })
167
+ .then(function(registration)
168
+ {
169
+ if(!registration || !registration.accessToken) { return res.end("error"); }
170
+ res.end(JSON.stringify(registration));
171
+ })
172
+ .catch(function(error)
173
+ {
174
+ // CODE 16 MEANS THE BUTTON ON THE BOX HAS NOT BEEN PRESSED YET
175
+ if(error.code === 16) { return res.end("error"); }
176
+ res.status(500).send(error.errors ? JSON.stringify(error.errors) : "error");
177
+ });
178
+ });
179
+ };
@@ -0,0 +1,82 @@
1
+ <script type="text/x-red" data-template-name="hue-syncbox">
2
+ <div class="form-row">
3
+ <label for="node-input-name"><i class="fa fa-tag"></i> <span data-i18n="hue-syncbox.config.name"></span></label>
4
+ <input type="text" id="node-input-name" data-i18n="[placeholder]hue-syncbox.config.input-name" style="width: calc(100% - 105px)">
5
+ </div>
6
+ <div class="form-row">
7
+ <label for="node-input-syncbox"><i class="fa fa-television"></i> <span data-i18n="hue-syncbox.config.syncbox"></span></label>
8
+ <input type="text" id="node-input-syncbox" style="width: calc(100% - 105px)">
9
+ </div>
10
+ <div class="form-row" style="margin-top: 30px">
11
+ <div style="display: inline-flex; align-items: center; width: calc(100% - 105px)">
12
+ <input type="checkbox" id="node-input-skipevents" style="flex: 0 0 auto; width: auto; margin: 0;">
13
+ <span data-i18n="hue-syncbox.config.skipevents-node" style="flex: 1; margin-left: 8px;"></span>
14
+ </div>
15
+ </div>
16
+ <div class="form-row">
17
+ <div style="display: inline-flex; align-items: center; width: calc(100% - 105px)">
18
+ <input type="checkbox" id="node-input-initevents" style="flex: 0 0 auto; width: auto; margin: 0;">
19
+ <span data-i18n="hue-syncbox.config.sendinitevents-node" style="flex: 1; margin-left: 8px;"></span>
20
+ </div>
21
+ </div>
22
+ <div class="form-row">
23
+ <div style="display: inline-flex; align-items: center; width: calc(100% - 105px)">
24
+ <input type="checkbox" id="node-input-onlycommands" style="flex: 0 0 auto; width: auto; margin: 0;">
25
+ <span data-i18n="hue-syncbox.config.onlycommands-node" style="flex: 1; margin-left: 8px;"></span>
26
+ </div>
27
+ </div>
28
+ </script>
29
+
30
+ <script type="text/javascript">
31
+ RED.nodes.registerType('hue-syncbox',{
32
+ category: 'HueMagic',
33
+ color: '#e0674d',
34
+ defaults: {
35
+ name: { value:"" },
36
+ syncbox: { type: "hue-syncbox-config", required: true },
37
+ skipevents: { value: false },
38
+ initevents: { value: false },
39
+ onlycommands: { value: false }
40
+ },
41
+ align: 'left',
42
+ icon: "hue-syncbox.svg",
43
+ inputs: 1,
44
+ outputs: 1,
45
+ label: function() {
46
+ return this.name || this._("hue-syncbox.node.title");
47
+ },
48
+ paletteLabel: function() {
49
+ return this._("hue-syncbox.node.title");
50
+ },
51
+ inputLabels: function() {
52
+ return this._("hue-syncbox.node.input");
53
+ },
54
+ outputLabels: function() {
55
+ return this._("hue-syncbox.node.output");
56
+ },
57
+ button: {
58
+ enabled: function() {
59
+ return !!this.syncbox
60
+ },
61
+ visible: function() {
62
+ return !!this.syncbox
63
+ },
64
+ onclick: function()
65
+ {
66
+ const node = this;
67
+ if(node.syncbox)
68
+ {
69
+ $.ajax({
70
+ url: "inject/" + node.id,
71
+ type: "POST",
72
+ data: JSON.stringify({ __user_inject_props__: "status"}),
73
+ contentType: "application/json; charset=utf-8",
74
+ success: function (resp) {
75
+ RED.notify(node.name + ": " + node._("hue-syncbox.node.statusmsg"), { type: "success", id: "status", timeout: 2000 });
76
+ }
77
+ });
78
+ }
79
+ }
80
+ }
81
+ });
82
+ </script>