@willieee802/zigbee-herdsman-converters 19.44.4 → 19.45.0

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 (591) hide show
  1. package/CHANGELOG.md +1101 -0
  2. package/dist/converters/actions.d.ts.map +1 -1
  3. package/dist/converters/actions.js +2 -0
  4. package/dist/converters/actions.js.map +1 -1
  5. package/dist/converters/fromZigbee.d.ts +1 -150
  6. package/dist/converters/fromZigbee.d.ts.map +1 -1
  7. package/dist/converters/fromZigbee.js +183 -3044
  8. package/dist/converters/fromZigbee.js.map +1 -1
  9. package/dist/converters/toZigbee.d.ts +0 -121
  10. package/dist/converters/toZigbee.d.ts.map +1 -1
  11. package/dist/converters/toZigbee.js +289 -2386
  12. package/dist/converters/toZigbee.js.map +1 -1
  13. package/dist/devices/ITCommander.d.ts.map +1 -1
  14. package/dist/devices/ITCommander.js +2 -1
  15. package/dist/devices/ITCommander.js.map +1 -1
  16. package/dist/devices/acova.d.ts.map +1 -1
  17. package/dist/devices/acova.js +69 -2
  18. package/dist/devices/acova.js.map +1 -1
  19. package/dist/devices/adeo.d.ts.map +1 -1
  20. package/dist/devices/adeo.js +35 -3
  21. package/dist/devices/adeo.js.map +1 -1
  22. package/dist/devices/adurosmart.js +1 -1
  23. package/dist/devices/adurosmart.js.map +1 -1
  24. package/dist/devices/amina.d.ts.map +1 -1
  25. package/dist/devices/amina.js +47 -10
  26. package/dist/devices/amina.js.map +1 -1
  27. package/dist/devices/aurora_lighting.d.ts.map +1 -1
  28. package/dist/devices/aurora_lighting.js +15 -0
  29. package/dist/devices/aurora_lighting.js.map +1 -1
  30. package/dist/devices/automaton.d.ts.map +1 -1
  31. package/dist/devices/automaton.js +1 -0
  32. package/dist/devices/automaton.js.map +1 -1
  33. package/dist/devices/avatto.d.ts.map +1 -1
  34. package/dist/devices/avatto.js +29 -24
  35. package/dist/devices/avatto.js.map +1 -1
  36. package/dist/devices/awox.d.ts.map +1 -1
  37. package/dist/devices/awox.js +41 -3
  38. package/dist/devices/awox.js.map +1 -1
  39. package/dist/devices/bacchus.d.ts.map +1 -1
  40. package/dist/devices/bacchus.js +75 -0
  41. package/dist/devices/bacchus.js.map +1 -1
  42. package/dist/devices/bitron.d.ts.map +1 -1
  43. package/dist/devices/bitron.js +29 -2
  44. package/dist/devices/bitron.js.map +1 -1
  45. package/dist/devices/bosch.d.ts.map +1 -1
  46. package/dist/devices/bosch.js +70 -50
  47. package/dist/devices/bosch.js.map +1 -1
  48. package/dist/devices/bticino.d.ts.map +1 -1
  49. package/dist/devices/bticino.js +11 -1
  50. package/dist/devices/bticino.js.map +1 -1
  51. package/dist/devices/byun.d.ts.map +1 -1
  52. package/dist/devices/byun.js +40 -3
  53. package/dist/devices/byun.js.map +1 -1
  54. package/dist/devices/candeo.d.ts.map +1 -1
  55. package/dist/devices/candeo.js +262 -9
  56. package/dist/devices/candeo.js.map +1 -1
  57. package/dist/devices/centralite.d.ts +9 -0
  58. package/dist/devices/centralite.d.ts.map +1 -1
  59. package/dist/devices/centralite.js +44 -5
  60. package/dist/devices/centralite.js.map +1 -1
  61. package/dist/devices/{aubess.d.ts → cigol.d.ts} +1 -1
  62. package/dist/devices/cigol.d.ts.map +1 -0
  63. package/dist/devices/cigol.js +260 -0
  64. package/dist/devices/cigol.js.map +1 -0
  65. package/dist/devices/cleverio.js +2 -2
  66. package/dist/devices/cleverio.js.map +1 -1
  67. package/dist/devices/climax.d.ts.map +1 -1
  68. package/dist/devices/climax.js +15 -1
  69. package/dist/devices/climax.js.map +1 -1
  70. package/dist/devices/ctm.d.ts.map +1 -1
  71. package/dist/devices/ctm.js +845 -496
  72. package/dist/devices/ctm.js.map +1 -1
  73. package/dist/devices/custom_devices_diy.d.ts +183 -1
  74. package/dist/devices/custom_devices_diy.d.ts.map +1 -1
  75. package/dist/devices/custom_devices_diy.js +390 -33
  76. package/dist/devices/custom_devices_diy.js.map +1 -1
  77. package/dist/devices/daewoo.d.ts +3 -0
  78. package/dist/devices/daewoo.d.ts.map +1 -0
  79. package/dist/devices/daewoo.js +198 -0
  80. package/dist/devices/daewoo.js.map +1 -0
  81. package/dist/devices/danfoss.d.ts.map +1 -1
  82. package/dist/devices/danfoss.js +1270 -296
  83. package/dist/devices/danfoss.js.map +1 -1
  84. package/dist/devices/databyte.d.ts.map +1 -1
  85. package/dist/devices/databyte.js +47 -3
  86. package/dist/devices/databyte.js.map +1 -1
  87. package/dist/devices/datek.d.ts.map +1 -1
  88. package/dist/devices/datek.js +202 -41
  89. package/dist/devices/datek.js.map +1 -1
  90. package/dist/devices/dawon_dns.d.ts.map +1 -1
  91. package/dist/devices/dawon_dns.js +14 -1
  92. package/dist/devices/dawon_dns.js.map +1 -1
  93. package/dist/devices/develco.d.ts.map +1 -1
  94. package/dist/devices/develco.js +145 -64
  95. package/dist/devices/develco.js.map +1 -1
  96. package/dist/devices/diyruz.d.ts.map +1 -1
  97. package/dist/devices/diyruz.js +444 -20
  98. package/dist/devices/diyruz.js.map +1 -1
  99. package/dist/devices/domraem.d.ts.map +1 -1
  100. package/dist/devices/domraem.js +7 -0
  101. package/dist/devices/domraem.js.map +1 -1
  102. package/dist/devices/easyaccess.d.ts.map +1 -1
  103. package/dist/devices/easyaccess.js +22 -2
  104. package/dist/devices/easyaccess.js.map +1 -1
  105. package/dist/devices/easyiot.d.ts.map +1 -1
  106. package/dist/devices/easyiot.js +767 -5
  107. package/dist/devices/easyiot.js.map +1 -1
  108. package/dist/devices/echostar.d.ts.map +1 -1
  109. package/dist/devices/echostar.js +25 -1
  110. package/dist/devices/echostar.js.map +1 -1
  111. package/dist/devices/ecodim.d.ts.map +1 -1
  112. package/dist/devices/ecodim.js +8 -0
  113. package/dist/devices/ecodim.js.map +1 -1
  114. package/dist/devices/ecolink.d.ts.map +1 -1
  115. package/dist/devices/ecolink.js +12 -0
  116. package/dist/devices/ecolink.js.map +1 -1
  117. package/dist/devices/efekta.d.ts.map +1 -1
  118. package/dist/devices/efekta.js +1615 -60
  119. package/dist/devices/efekta.js.map +1 -1
  120. package/dist/devices/eglo.d.ts.map +1 -1
  121. package/dist/devices/eglo.js +203 -17
  122. package/dist/devices/eglo.js.map +1 -1
  123. package/dist/devices/elko.d.ts.map +1 -1
  124. package/dist/devices/elko.js +20 -22
  125. package/dist/devices/elko.js.map +1 -1
  126. package/dist/devices/engo.d.ts.map +1 -1
  127. package/dist/devices/engo.js +201 -0
  128. package/dist/devices/engo.js.map +1 -1
  129. package/dist/devices/enocean.d.ts.map +1 -1
  130. package/dist/devices/enocean.js +132 -4
  131. package/dist/devices/enocean.js.map +1 -1
  132. package/dist/devices/ensystec.d.ts +3 -0
  133. package/dist/devices/ensystec.d.ts.map +1 -0
  134. package/dist/devices/ensystec.js +1200 -0
  135. package/dist/devices/ensystec.js.map +1 -0
  136. package/dist/devices/eurotronic.d.ts +78 -1
  137. package/dist/devices/eurotronic.d.ts.map +1 -1
  138. package/dist/devices/eurotronic.js +244 -40
  139. package/dist/devices/eurotronic.js.map +1 -1
  140. package/dist/devices/ewelink.d.ts.map +1 -1
  141. package/dist/devices/ewelink.js +154 -2
  142. package/dist/devices/ewelink.js.map +1 -1
  143. package/dist/devices/fantem.js +1 -1
  144. package/dist/devices/fantem.js.map +1 -1
  145. package/dist/devices/fireangel.d.ts.map +1 -1
  146. package/dist/devices/fireangel.js +12 -1
  147. package/dist/devices/fireangel.js.map +1 -1
  148. package/dist/devices/frankever.d.ts.map +1 -1
  149. package/dist/devices/frankever.js +96 -0
  150. package/dist/devices/frankever.js.map +1 -1
  151. package/dist/devices/frient.d.ts.map +1 -1
  152. package/dist/devices/frient.js +15 -0
  153. package/dist/devices/frient.js.map +1 -1
  154. package/dist/devices/girier.js +1 -1
  155. package/dist/devices/girier.js.map +1 -1
  156. package/dist/devices/gledopto.js +1 -1
  157. package/dist/devices/gledopto.js.map +1 -1
  158. package/dist/devices/gmmts.d.ts.map +1 -1
  159. package/dist/devices/gmmts.js +59 -55
  160. package/dist/devices/gmmts.js.map +1 -1
  161. package/dist/devices/halo_smart_labs.d.ts +3 -0
  162. package/dist/devices/halo_smart_labs.d.ts.map +1 -0
  163. package/dist/devices/halo_smart_labs.js +918 -0
  164. package/dist/devices/halo_smart_labs.js.map +1 -0
  165. package/dist/devices/handshake_finland.d.ts +3 -0
  166. package/dist/devices/handshake_finland.d.ts.map +1 -0
  167. package/dist/devices/handshake_finland.js +15 -0
  168. package/dist/devices/handshake_finland.js.map +1 -0
  169. package/dist/devices/heiman.d.ts.map +1 -1
  170. package/dist/devices/heiman.js +1361 -147
  171. package/dist/devices/heiman.js.map +1 -1
  172. package/dist/devices/heimgard_technologies.d.ts.map +1 -1
  173. package/dist/devices/heimgard_technologies.js +13 -0
  174. package/dist/devices/heimgard_technologies.js.map +1 -1
  175. package/dist/devices/hive.d.ts.map +1 -1
  176. package/dist/devices/hive.js +11 -0
  177. package/dist/devices/hive.js.map +1 -1
  178. package/dist/devices/ikea.d.ts.map +1 -1
  179. package/dist/devices/ikea.js +185 -135
  180. package/dist/devices/ikea.js.map +1 -1
  181. package/dist/devices/iluminize.d.ts.map +1 -1
  182. package/dist/devices/iluminize.js +8 -7
  183. package/dist/devices/iluminize.js.map +1 -1
  184. package/dist/devices/immax.d.ts.map +1 -1
  185. package/dist/devices/immax.js +3 -1
  186. package/dist/devices/immax.js.map +1 -1
  187. package/dist/devices/index.d.ts.map +1 -1
  188. package/dist/devices/index.js +20 -2
  189. package/dist/devices/index.js.map +1 -1
  190. package/dist/devices/innr.d.ts.map +1 -1
  191. package/dist/devices/innr.js +34 -2
  192. package/dist/devices/innr.js.map +1 -1
  193. package/dist/devices/inovelli.d.ts.map +1 -1
  194. package/dist/devices/inovelli.js +41 -2348
  195. package/dist/devices/inovelli.js.map +1 -1
  196. package/dist/devices/iris.d.ts.map +1 -1
  197. package/dist/devices/iris.js +13 -1
  198. package/dist/devices/iris.js.map +1 -1
  199. package/dist/devices/javis.d.ts.map +1 -1
  200. package/dist/devices/javis.js +39 -1
  201. package/dist/devices/javis.js.map +1 -1
  202. package/dist/devices/jxuan.d.ts.map +1 -1
  203. package/dist/devices/jxuan.js +37 -2
  204. package/dist/devices/jxuan.js.map +1 -1
  205. package/dist/devices/kami.d.ts.map +1 -1
  206. package/dist/devices/kami.js +21 -2
  207. package/dist/devices/kami.js.map +1 -1
  208. package/dist/devices/keen_home.d.ts.map +1 -1
  209. package/dist/devices/keen_home.js +13 -3
  210. package/dist/devices/keen_home.js.map +1 -1
  211. package/dist/devices/klikaanklikuit.d.ts.map +1 -1
  212. package/dist/devices/klikaanklikuit.js +7 -0
  213. package/dist/devices/klikaanklikuit.js.map +1 -1
  214. package/dist/devices/kmpcil.d.ts.map +1 -1
  215. package/dist/devices/kmpcil.js +44 -5
  216. package/dist/devices/kmpcil.js.map +1 -1
  217. package/dist/devices/konke.d.ts.map +1 -1
  218. package/dist/devices/konke.js +10 -1
  219. package/dist/devices/konke.js.map +1 -1
  220. package/dist/devices/lds.d.ts.map +1 -1
  221. package/dist/devices/lds.js +7 -0
  222. package/dist/devices/lds.js.map +1 -1
  223. package/dist/devices/led_trading.d.ts.map +1 -1
  224. package/dist/devices/led_trading.js +13 -13
  225. package/dist/devices/led_trading.js.map +1 -1
  226. package/dist/devices/leedarson.d.ts.map +1 -1
  227. package/dist/devices/leedarson.js +140 -1
  228. package/dist/devices/leedarson.js.map +1 -1
  229. package/dist/devices/legrand.d.ts.map +1 -1
  230. package/dist/devices/legrand.js +73 -42
  231. package/dist/devices/legrand.js.map +1 -1
  232. package/dist/devices/lellki.d.ts.map +1 -1
  233. package/dist/devices/lellki.js +3 -1
  234. package/dist/devices/lellki.js.map +1 -1
  235. package/dist/devices/letv.d.ts.map +1 -1
  236. package/dist/devices/letv.js +16 -2
  237. package/dist/devices/letv.js.map +1 -1
  238. package/dist/devices/lidl.d.ts.map +1 -1
  239. package/dist/devices/lidl.js +26 -6
  240. package/dist/devices/lidl.js.map +1 -1
  241. package/dist/devices/lincukoo.d.ts.map +1 -1
  242. package/dist/devices/lincukoo.js +92 -31
  243. package/dist/devices/lincukoo.js.map +1 -1
  244. package/dist/devices/linkind.d.ts.map +1 -1
  245. package/dist/devices/linkind.js +14 -1
  246. package/dist/devices/linkind.js.map +1 -1
  247. package/dist/devices/linptech.d.ts.map +1 -1
  248. package/dist/devices/linptech.js +3 -1
  249. package/dist/devices/linptech.js.map +1 -1
  250. package/dist/devices/livingwise.d.ts.map +1 -1
  251. package/dist/devices/livingwise.js +2 -1
  252. package/dist/devices/livingwise.js.map +1 -1
  253. package/dist/devices/livolo.d.ts.map +1 -1
  254. package/dist/devices/livolo.js +612 -20
  255. package/dist/devices/livolo.js.map +1 -1
  256. package/dist/devices/lixee.d.ts.map +1 -1
  257. package/dist/devices/lixee.js +46 -44
  258. package/dist/devices/lixee.js.map +1 -1
  259. package/dist/devices/lonsonho.d.ts.map +1 -1
  260. package/dist/devices/lonsonho.js +18 -11
  261. package/dist/devices/lonsonho.js.map +1 -1
  262. package/dist/devices/lumi.d.ts.map +1 -1
  263. package/dist/devices/lumi.js +877 -155
  264. package/dist/devices/lumi.js.map +1 -1
  265. package/dist/devices/lytko.d.ts.map +1 -1
  266. package/dist/devices/lytko.js +205 -556
  267. package/dist/devices/lytko.js.map +1 -1
  268. package/dist/devices/makegood.d.ts.map +1 -1
  269. package/dist/devices/makegood.js +5 -1
  270. package/dist/devices/makegood.js.map +1 -1
  271. package/dist/devices/mazda.js +7 -7
  272. package/dist/devices/mazda.js.map +1 -1
  273. package/dist/devices/meazon.d.ts.map +1 -1
  274. package/dist/devices/meazon.js +82 -20
  275. package/dist/devices/meazon.js.map +1 -1
  276. package/dist/devices/megaman.d.ts +4 -0
  277. package/dist/devices/megaman.d.ts.map +1 -0
  278. package/dist/devices/megaman.js +48 -0
  279. package/dist/devices/megaman.js.map +1 -0
  280. package/dist/devices/mercator.d.ts.map +1 -1
  281. package/dist/devices/mercator.js +20 -5
  282. package/dist/devices/mercator.js.map +1 -1
  283. package/dist/devices/miboxer.d.ts.map +1 -1
  284. package/dist/devices/miboxer.js +3 -1
  285. package/dist/devices/miboxer.js.map +1 -1
  286. package/dist/devices/mill.d.ts.map +1 -1
  287. package/dist/devices/mill.js +33 -7
  288. package/dist/devices/mill.js.map +1 -1
  289. package/dist/devices/moes.d.ts.map +1 -1
  290. package/dist/devices/moes.js +89 -15
  291. package/dist/devices/moes.js.map +1 -1
  292. package/dist/devices/msh.d.ts +3 -0
  293. package/dist/devices/msh.d.ts.map +1 -0
  294. package/dist/devices/msh.js +93 -0
  295. package/dist/devices/msh.js.map +1 -0
  296. package/dist/devices/muller_licht.d.ts.map +1 -1
  297. package/dist/devices/muller_licht.js +11 -0
  298. package/dist/devices/muller_licht.js.map +1 -1
  299. package/dist/devices/multir.d.ts.map +1 -1
  300. package/dist/devices/multir.js +12 -1
  301. package/dist/devices/multir.js.map +1 -1
  302. package/dist/devices/multiterm.js +2 -2
  303. package/dist/devices/multiterm.js.map +1 -1
  304. package/dist/devices/namron.d.ts.map +1 -1
  305. package/dist/devices/namron.js +488 -128
  306. package/dist/devices/namron.js.map +1 -1
  307. package/dist/devices/neo.d.ts.map +1 -1
  308. package/dist/devices/neo.js +151 -192
  309. package/dist/devices/neo.js.map +1 -1
  310. package/dist/devices/netica.d.ts +3 -0
  311. package/dist/devices/netica.d.ts.map +1 -0
  312. package/dist/devices/netica.js +153 -0
  313. package/dist/devices/netica.js.map +1 -0
  314. package/dist/devices/niko.d.ts.map +1 -1
  315. package/dist/devices/niko.js +10 -8
  316. package/dist/devices/niko.js.map +1 -1
  317. package/dist/devices/nodon.d.ts.map +1 -1
  318. package/dist/devices/nodon.js +6 -4
  319. package/dist/devices/nodon.js.map +1 -1
  320. package/dist/devices/nous.d.ts.map +1 -1
  321. package/dist/devices/nous.js +160 -61
  322. package/dist/devices/nous.js.map +1 -1
  323. package/dist/devices/nue_3a.d.ts.map +1 -1
  324. package/dist/devices/nue_3a.js +2 -0
  325. package/dist/devices/nue_3a.js.map +1 -1
  326. package/dist/devices/onesti.d.ts +11 -1
  327. package/dist/devices/onesti.d.ts.map +1 -1
  328. package/dist/devices/onesti.js +30 -6
  329. package/dist/devices/onesti.js.map +1 -1
  330. package/dist/devices/onokom.d.ts.map +1 -1
  331. package/dist/devices/onokom.js +1152 -2309
  332. package/dist/devices/onokom.js.map +1 -1
  333. package/dist/devices/orvibo.d.ts +17 -1
  334. package/dist/devices/orvibo.d.ts.map +1 -1
  335. package/dist/devices/orvibo.js +71 -4
  336. package/dist/devices/orvibo.js.map +1 -1
  337. package/dist/devices/owon.d.ts.map +1 -1
  338. package/dist/devices/owon.js +382 -62
  339. package/dist/devices/owon.js.map +1 -1
  340. package/dist/devices/paul_neuhaus.d.ts.map +1 -1
  341. package/dist/devices/paul_neuhaus.js +9 -0
  342. package/dist/devices/paul_neuhaus.js.map +1 -1
  343. package/dist/devices/paulmann.js +2 -2
  344. package/dist/devices/paulmann.js.map +1 -1
  345. package/dist/devices/perenio.d.ts.map +1 -1
  346. package/dist/devices/perenio.js +57 -39
  347. package/dist/devices/perenio.js.map +1 -1
  348. package/dist/devices/philips.d.ts.map +1 -1
  349. package/dist/devices/philips.js +194 -53
  350. package/dist/devices/philips.js.map +1 -1
  351. package/dist/devices/plaid.d.ts.map +1 -1
  352. package/dist/devices/plaid.js +18 -1
  353. package/dist/devices/plaid.js.map +1 -1
  354. package/dist/devices/plugwise.d.ts.map +1 -1
  355. package/dist/devices/plugwise.js +307 -26
  356. package/dist/devices/plugwise.js.map +1 -1
  357. package/dist/devices/profalux.d.ts.map +1 -1
  358. package/dist/devices/profalux.js +25 -1
  359. package/dist/devices/profalux.js.map +1 -1
  360. package/dist/devices/pushok.d.ts.map +1 -1
  361. package/dist/devices/pushok.js +44 -0
  362. package/dist/devices/pushok.js.map +1 -1
  363. package/dist/devices/qa.d.ts.map +1 -1
  364. package/dist/devices/qa.js +80 -32
  365. package/dist/devices/qa.js.map +1 -1
  366. package/dist/devices/repenic_ltd.d.ts +3 -0
  367. package/dist/devices/repenic_ltd.d.ts.map +1 -0
  368. package/dist/devices/repenic_ltd.js +97 -0
  369. package/dist/devices/repenic_ltd.js.map +1 -0
  370. package/dist/devices/robb.d.ts.map +1 -1
  371. package/dist/devices/robb.js +13 -1
  372. package/dist/devices/robb.js.map +1 -1
  373. package/dist/devices/rtx.js +1 -1
  374. package/dist/devices/rtx.js.map +1 -1
  375. package/dist/devices/salus_controls.d.ts.map +1 -1
  376. package/dist/devices/salus_controls.js +44 -17
  377. package/dist/devices/salus_controls.js.map +1 -1
  378. package/dist/devices/samotech.js +2 -2
  379. package/dist/devices/samotech.js.map +1 -1
  380. package/dist/devices/sber.d.ts.map +1 -1
  381. package/dist/devices/sber.js +435 -26
  382. package/dist/devices/sber.js.map +1 -1
  383. package/dist/devices/schneider_electric.d.ts.map +1 -1
  384. package/dist/devices/schneider_electric.js +808 -252
  385. package/dist/devices/schneider_electric.js.map +1 -1
  386. package/dist/devices/securifi.d.ts.map +1 -1
  387. package/dist/devices/securifi.js +24 -1
  388. package/dist/devices/securifi.js.map +1 -1
  389. package/dist/devices/sengled.d.ts.map +1 -1
  390. package/dist/devices/sengled.js +5 -4
  391. package/dist/devices/sengled.js.map +1 -1
  392. package/dist/devices/shada.d.ts +3 -0
  393. package/dist/devices/shada.d.ts.map +1 -0
  394. package/dist/devices/{aubess.js → shada.js} +7 -20
  395. package/dist/devices/shada.js.map +1 -0
  396. package/dist/devices/shelly.d.ts.map +1 -1
  397. package/dist/devices/shelly.js +646 -61
  398. package/dist/devices/shelly.js.map +1 -1
  399. package/dist/devices/shinasystem.d.ts.map +1 -1
  400. package/dist/devices/shinasystem.js +75 -16
  401. package/dist/devices/shinasystem.js.map +1 -1
  402. package/dist/devices/siglis.d.ts.map +1 -1
  403. package/dist/devices/siglis.js +26 -0
  404. package/dist/devices/siglis.js.map +1 -1
  405. package/dist/devices/sinope.d.ts.map +1 -1
  406. package/dist/devices/sinope.js +163 -83
  407. package/dist/devices/sinope.js.map +1 -1
  408. package/dist/devices/slacky_diy.d.ts.map +1 -1
  409. package/dist/devices/slacky_diy.js +3007 -69
  410. package/dist/devices/slacky_diy.js.map +1 -1
  411. package/dist/devices/smarli.d.ts.map +1 -1
  412. package/dist/devices/smarli.js +4 -5
  413. package/dist/devices/smarli.js.map +1 -1
  414. package/dist/devices/smartthings.d.ts +25 -0
  415. package/dist/devices/smartthings.d.ts.map +1 -1
  416. package/dist/devices/smartthings.js +62 -11
  417. package/dist/devices/smartthings.js.map +1 -1
  418. package/dist/devices/somfy.d.ts.map +1 -1
  419. package/dist/devices/somfy.js +14 -1
  420. package/dist/devices/somfy.js.map +1 -1
  421. package/dist/devices/sonoff.d.ts +42 -1
  422. package/dist/devices/sonoff.d.ts.map +1 -1
  423. package/dist/devices/sonoff.js +5161 -2007
  424. package/dist/devices/sonoff.js.map +1 -1
  425. package/dist/devices/sprut.d.ts.map +1 -1
  426. package/dist/devices/sprut.js +5 -4
  427. package/dist/devices/sprut.js.map +1 -1
  428. package/dist/devices/stello.d.ts.map +1 -1
  429. package/dist/devices/stello.js +6 -24
  430. package/dist/devices/stello.js.map +1 -1
  431. package/dist/devices/stelpro.d.ts +51 -1
  432. package/dist/devices/stelpro.d.ts.map +1 -1
  433. package/dist/devices/stelpro.js +86 -23
  434. package/dist/devices/stelpro.js.map +1 -1
  435. package/dist/devices/sunricher.d.ts.map +1 -1
  436. package/dist/devices/sunricher.js +215 -29
  437. package/dist/devices/sunricher.js.map +1 -1
  438. package/dist/devices/tech.d.ts.map +1 -1
  439. package/dist/devices/tech.js +35 -35
  440. package/dist/devices/tech.js.map +1 -1
  441. package/dist/devices/terncy.d.ts.map +1 -1
  442. package/dist/devices/terncy.js +398 -2
  443. package/dist/devices/terncy.js.map +1 -1
  444. package/dist/devices/third_reality.d.ts +42 -1
  445. package/dist/devices/third_reality.d.ts.map +1 -1
  446. package/dist/devices/third_reality.js +521 -110
  447. package/dist/devices/third_reality.js.map +1 -1
  448. package/dist/devices/tuya.d.ts.map +1 -1
  449. package/dist/devices/tuya.js +3139 -814
  450. package/dist/devices/tuya.js.map +1 -1
  451. package/dist/devices/ubisys.d.ts.map +1 -1
  452. package/dist/devices/ubisys.js +11 -8
  453. package/dist/devices/ubisys.js.map +1 -1
  454. package/dist/devices/vesternet.d.ts.map +1 -1
  455. package/dist/devices/vesternet.js +7 -0
  456. package/dist/devices/vesternet.js.map +1 -1
  457. package/dist/devices/viessmann.d.ts.map +1 -1
  458. package/dist/devices/viessmann.js +107 -4
  459. package/dist/devices/viessmann.js.map +1 -1
  460. package/dist/devices/vsmart.d.ts.map +1 -1
  461. package/dist/devices/vsmart.js +11 -0
  462. package/dist/devices/vsmart.js.map +1 -1
  463. package/dist/devices/weiser.d.ts.map +1 -1
  464. package/dist/devices/weiser.js +6 -0
  465. package/dist/devices/weiser.js.map +1 -1
  466. package/dist/devices/wirenboard.d.ts.map +1 -1
  467. package/dist/devices/wirenboard.js +124 -21
  468. package/dist/devices/wirenboard.js.map +1 -1
  469. package/dist/devices/wmun.d.ts.map +1 -1
  470. package/dist/devices/wmun.js +3 -2
  471. package/dist/devices/wmun.js.map +1 -1
  472. package/dist/devices/woolley.d.ts.map +1 -1
  473. package/dist/devices/woolley.js +6 -9
  474. package/dist/devices/woolley.js.map +1 -1
  475. package/dist/devices/woox.js +2 -2
  476. package/dist/devices/woox.js.map +1 -1
  477. package/dist/devices/xyzroe.d.ts.map +1 -1
  478. package/dist/devices/xyzroe.js +16 -4
  479. package/dist/devices/xyzroe.js.map +1 -1
  480. package/dist/devices/yale.d.ts.map +1 -1
  481. package/dist/devices/yale.js +138 -40
  482. package/dist/devices/yale.js.map +1 -1
  483. package/dist/devices/yandex.d.ts.map +1 -1
  484. package/dist/devices/yandex.js +93 -11
  485. package/dist/devices/yandex.js.map +1 -1
  486. package/dist/devices/yokis.d.ts.map +1 -1
  487. package/dist/devices/yokis.js +122 -85
  488. package/dist/devices/yokis.js.map +1 -1
  489. package/dist/devices/zbeacon.d.ts.map +1 -1
  490. package/dist/devices/zbeacon.js +0 -7
  491. package/dist/devices/zbeacon.js.map +1 -1
  492. package/dist/devices/zemismart.d.ts.map +1 -1
  493. package/dist/devices/zemismart.js +148 -11
  494. package/dist/devices/zemismart.js.map +1 -1
  495. package/dist/devices/zigbeetlc.js +1 -1
  496. package/dist/devices/zigbeetlc.js.map +1 -1
  497. package/dist/index.d.ts +1 -1
  498. package/dist/index.d.ts.map +1 -1
  499. package/dist/index.js +40 -35
  500. package/dist/index.js.map +1 -1
  501. package/dist/lib/bosch.d.ts.map +1 -1
  502. package/dist/lib/bosch.js +212 -42
  503. package/dist/lib/bosch.js.map +1 -1
  504. package/dist/lib/constants.d.ts +99 -17
  505. package/dist/lib/constants.d.ts.map +1 -1
  506. package/dist/lib/constants.js +15 -2
  507. package/dist/lib/constants.js.map +1 -1
  508. package/dist/lib/develco.d.ts +11 -0
  509. package/dist/lib/develco.d.ts.map +1 -1
  510. package/dist/lib/develco.js +78 -9
  511. package/dist/lib/develco.js.map +1 -1
  512. package/dist/lib/exposes.d.ts +11 -10
  513. package/dist/lib/exposes.d.ts.map +1 -1
  514. package/dist/lib/exposes.js +7 -48
  515. package/dist/lib/exposes.js.map +1 -1
  516. package/dist/lib/heiman.d.ts.map +1 -1
  517. package/dist/lib/heiman.js +34 -14
  518. package/dist/lib/heiman.js.map +1 -1
  519. package/dist/lib/ikea.d.ts +17 -0
  520. package/dist/lib/ikea.d.ts.map +1 -1
  521. package/dist/lib/ikea.js +46 -20
  522. package/dist/lib/ikea.js.map +1 -1
  523. package/dist/lib/inovelli.d.ts +86 -0
  524. package/dist/lib/inovelli.d.ts.map +1 -0
  525. package/dist/lib/inovelli.js +2580 -0
  526. package/dist/lib/inovelli.js.map +1 -0
  527. package/dist/lib/ledvance.d.ts +4 -4
  528. package/dist/lib/ledvance.d.ts.map +1 -1
  529. package/dist/lib/ledvance.js +23 -7
  530. package/dist/lib/ledvance.js.map +1 -1
  531. package/dist/lib/legacy.d.ts +21 -2
  532. package/dist/lib/legacy.d.ts.map +1 -1
  533. package/dist/lib/legacy.js +29 -2
  534. package/dist/lib/legacy.js.map +1 -1
  535. package/dist/lib/legrand.d.ts +65 -6
  536. package/dist/lib/legrand.d.ts.map +1 -1
  537. package/dist/lib/legrand.js +188 -9
  538. package/dist/lib/legrand.js.map +1 -1
  539. package/dist/lib/light.d.ts +1 -1
  540. package/dist/lib/light.d.ts.map +1 -1
  541. package/dist/lib/light.js +2 -2
  542. package/dist/lib/light.js.map +1 -1
  543. package/dist/lib/lumi.d.ts +88 -44
  544. package/dist/lib/lumi.d.ts.map +1 -1
  545. package/dist/lib/lumi.js +1813 -59
  546. package/dist/lib/lumi.js.map +1 -1
  547. package/dist/lib/modernExtend.d.ts +19 -14
  548. package/dist/lib/modernExtend.d.ts.map +1 -1
  549. package/dist/lib/modernExtend.js +37 -35
  550. package/dist/lib/modernExtend.js.map +1 -1
  551. package/dist/lib/namron.d.ts +131 -28
  552. package/dist/lib/namron.d.ts.map +1 -1
  553. package/dist/lib/namron.js +723 -42
  554. package/dist/lib/namron.js.map +1 -1
  555. package/dist/lib/nodon.d.ts.map +1 -1
  556. package/dist/lib/nodon.js +3 -1
  557. package/dist/lib/nodon.js.map +1 -1
  558. package/dist/lib/philips.d.ts +106 -2
  559. package/dist/lib/philips.d.ts.map +1 -1
  560. package/dist/lib/philips.js +945 -44
  561. package/dist/lib/philips.js.map +1 -1
  562. package/dist/lib/sonoff.d.ts +77 -10
  563. package/dist/lib/sonoff.d.ts.map +1 -1
  564. package/dist/lib/sonoff.js +165 -34
  565. package/dist/lib/sonoff.js.map +1 -1
  566. package/dist/lib/sunricher.d.ts +1 -1
  567. package/dist/lib/sunricher.d.ts.map +1 -1
  568. package/dist/lib/sunricher.js +6 -8
  569. package/dist/lib/sunricher.js.map +1 -1
  570. package/dist/lib/tuya.d.ts +558 -13
  571. package/dist/lib/tuya.d.ts.map +1 -1
  572. package/dist/lib/tuya.js +1518 -10
  573. package/dist/lib/tuya.js.map +1 -1
  574. package/dist/lib/types.d.ts +9 -6
  575. package/dist/lib/types.d.ts.map +1 -1
  576. package/dist/lib/ubisys.d.ts +1 -1
  577. package/dist/lib/ubisys.d.ts.map +1 -1
  578. package/dist/lib/ubisys.js +60 -5
  579. package/dist/lib/ubisys.js.map +1 -1
  580. package/dist/lib/utils.d.ts +5 -8
  581. package/dist/lib/utils.d.ts.map +1 -1
  582. package/dist/lib/utils.js +30 -17
  583. package/dist/lib/utils.js.map +1 -1
  584. package/dist/lib/zosung.d.ts +69 -6
  585. package/dist/lib/zosung.d.ts.map +1 -1
  586. package/dist/lib/zosung.js +113 -1
  587. package/dist/lib/zosung.js.map +1 -1
  588. package/dist/models-index.json +1 -1
  589. package/package.json +2 -2
  590. package/dist/devices/aubess.d.ts.map +0 -1
  591. package/dist/devices/aubess.js.map +0 -1
package/dist/lib/lumi.js CHANGED
@@ -36,7 +36,9 @@ Object.defineProperty(exports, "__esModule", { value: true });
36
36
  exports.toZigbee = exports.fromZigbee = exports.modernExtend = exports.lumiModernExtend = exports.manufacturerCode = exports.trv = exports.presence = exports.numericAttributes2Payload = exports.buffer2DataObject = void 0;
37
37
  exports.w100EnsureDefaults = w100EnsureDefaults;
38
38
  const node_buffer_1 = require("node:buffer");
39
+ const zigbee_herdsman_1 = require("@willieee802/zigbee-herdsman");
39
40
  const fz = __importStar(require("../converters/fromZigbee"));
41
+ const tz = __importStar(require("../converters/toZigbee"));
40
42
  const exposes = __importStar(require("./exposes"));
41
43
  const logger_1 = require("./logger");
42
44
  const modernExtend = __importStar(require("./modernExtend"));
@@ -45,6 +47,7 @@ const utils_1 = require("./utils");
45
47
  const NS = "zhc:lumi";
46
48
  const e = exposes.presets;
47
49
  const ea = exposes.access;
50
+ const ZNCLBL01LM_RUNNING_STORE_KEY = "ZNCLBL01LM_running";
48
51
  const buffer2DataObject = (model, buffer) => {
49
52
  const dataObject = {};
50
53
  if (buffer !== null && node_buffer_1.Buffer.isBuffer(buffer)) {
@@ -492,6 +495,15 @@ msg, meta, model, options, dataObject) => {
492
495
  payload.trigger_indicator = value === 1;
493
496
  }
494
497
  else if (["ZNCLBL01LM"].includes(model.model)) {
498
+ // https://github.com/Koenkk/zigbee-herdsman-converters/pull/11911
499
+ const value1057 = typeof dataObject["1057"] === "number" ? dataObject["1057"] : undefined;
500
+ const storedRunning = globalStore.getValue(msg.endpoint, ZNCLBL01LM_RUNNING_STORE_KEY, undefined);
501
+ const payloadRunning = value1057 !== undefined && value1057 < 2;
502
+ const shouldSuppressInStopPayload = value1057 === 2;
503
+ const shouldSuppressWhileStopped = storedRunning === false && !payloadRunning;
504
+ if (shouldSuppressInStopPayload || shouldSuppressWhileStopped) {
505
+ break;
506
+ }
495
507
  (0, utils_1.assertNumber)(value);
496
508
  const position = options.invert_cover ? 100 - value : value;
497
509
  payload.position = position;
@@ -509,7 +521,7 @@ msg, meta, model, options, dataObject) => {
509
521
  payload.consumption = payload.energy;
510
522
  break;
511
523
  case "150":
512
- if (["KD-R01D"].includes(model.model)) {
524
+ if (["KD-R01D", "WS-K05E"].includes(model.model)) {
513
525
  (0, utils_1.assertNumber)(value);
514
526
  payload.voltage = value * 0.01;
515
527
  }
@@ -862,9 +874,19 @@ msg, meta, model, options, dataObject) => {
862
874
  break;
863
875
  case "1057":
864
876
  if (["ZNCLBL01LM"].includes(model.model)) {
877
+ const previousRunning = globalStore.getValue(msg.endpoint, ZNCLBL01LM_RUNNING_STORE_KEY, undefined);
865
878
  payload.motor_state = (0, utils_1.getFromLookup)(value, options.invert_cover ? { 0: "opening", 1: "closing", 2: "stopped" } : { 0: "closing", 1: "opening", 2: "stopped" });
866
879
  (0, utils_1.assertNumber)(value);
867
880
  payload.running = value < 2;
881
+ globalStore.putValue(msg.endpoint, ZNCLBL01LM_RUNNING_STORE_KEY, payload.running);
882
+ // https://github.com/Koenkk/zigbee-herdsman-converters/pull/11911
883
+ if (!payload.running && previousRunning !== false) {
884
+ // After stop, read the final position.
885
+ // Attr 107 can still be stale near the end.
886
+ msg.endpoint
887
+ .read("closuresWindowCovering", ["currentPositionLiftPercentage"])
888
+ .catch((error) => logger_1.logger.error(`Failed to read position '${msg.device.ieeeAddr}' (${error})`, NS));
889
+ }
868
890
  }
869
891
  break;
870
892
  case "1061":
@@ -1590,6 +1612,23 @@ const manufacturerOptions = {
1590
1612
  lumi: { manufacturerCode: exports.manufacturerCode, disableDefaultResponse: true },
1591
1613
  };
1592
1614
  exports.lumiModernExtend = {
1615
+ addManuSpecificLumiCluster: () => modernExtend.deviceAddCustomCluster("manuSpecificLumi", {
1616
+ name: "manuSpecificLumi",
1617
+ ID: 0xfcc0,
1618
+ manufacturerCode: zigbee_herdsman_1.Zcl.ManufacturerCode.LUMI_UNITED_TECHOLOGY_LTD_SHENZHEN,
1619
+ attributes: {
1620
+ mode: { name: "mode", ID: 0x0009, type: zigbee_herdsman_1.Zcl.DataType.UINT8, write: true, max: 0xff },
1621
+ illuminance: { name: "illuminance", ID: 0x0112, type: zigbee_herdsman_1.Zcl.DataType.UINT32, write: true, max: 0xffffffff },
1622
+ displayUnit: { name: "displayUnit", ID: 0x0114, type: zigbee_herdsman_1.Zcl.DataType.UINT8, write: true, max: 0xff },
1623
+ movement: { name: "movement", ID: 0x0118, type: zigbee_herdsman_1.Zcl.DataType.UINT8 },
1624
+ airQuality: { name: "airQuality", ID: 0x0129, type: zigbee_herdsman_1.Zcl.DataType.UINT8, write: true, max: 0xff },
1625
+ curtainReverse: { name: "curtainReverse", ID: 0x0400, type: zigbee_herdsman_1.Zcl.DataType.BOOLEAN, write: true },
1626
+ curtainHandOpen: { name: "curtainHandOpen", ID: 0x0401, type: zigbee_herdsman_1.Zcl.DataType.BOOLEAN, write: true },
1627
+ curtainCalibrated: { name: "curtainCalibrated", ID: 0x0402, type: zigbee_herdsman_1.Zcl.DataType.BOOLEAN, write: true },
1628
+ },
1629
+ commands: {},
1630
+ commandsResponse: {},
1631
+ }),
1593
1632
  lumiLight: (args) => {
1594
1633
  args = { powerOutageCount: true, deviceTemperature: true, ...args };
1595
1634
  const colorTemp = args.colorTemp ? { startup: false, range: args.colorTempRange ?? [153, 370] } : undefined;
@@ -2147,6 +2186,18 @@ exports.lumiModernExtend = {
2147
2186
  attribute: "presentValue",
2148
2187
  ...args,
2149
2188
  }),
2189
+ lumiStaticStateAction: () => {
2190
+ const converter = {
2191
+ cluster: "manuSpecificLumi",
2192
+ type: ["attributeReport"],
2193
+ convert: (model, msg, publish, options, meta) => {
2194
+ if (msg.data["499"] === 1) {
2195
+ return { action: "static" };
2196
+ }
2197
+ },
2198
+ };
2199
+ return { fromZigbee: [converter], isModernExtend: true };
2200
+ },
2150
2201
  lumiVoc: (args) => modernExtend.numeric({
2151
2202
  name: "voc",
2152
2203
  cluster: "genAnalogInput",
@@ -2481,7 +2532,9 @@ exports.lumiModernExtend = {
2481
2532
  // 1 - 'event' mode. keys send events. useful for handling
2482
2533
  const configure = [
2483
2534
  async (device, coordinatorEndpoint, definition) => {
2484
- await device.getEndpoint(1).write("manuSpecificLumi", { mode: 1 }, { manufacturerCode: exports.manufacturerCode, disableResponse: true });
2535
+ await device
2536
+ .getEndpoint(1)
2537
+ .write("manuSpecificLumi", { mode: 1 }, { manufacturerCode: exports.manufacturerCode, disableResponse: true });
2485
2538
  },
2486
2539
  ];
2487
2540
  return { configure, isModernExtend: true };
@@ -2523,7 +2576,7 @@ exports.lumiModernExtend = {
2523
2576
  cluster: "manuSpecificLumi",
2524
2577
  type: ["attributeReport", "readResponse"],
2525
2578
  convert: (model, msg, publish, options, meta) => {
2526
- if (msg.data[0x0118] !== undefined && msg.data[0x0118] === 1) {
2579
+ if (msg.data.movement !== undefined && msg.data.movement === 1) {
2527
2580
  return { action: "movement" };
2528
2581
  }
2529
2582
  },
@@ -2639,7 +2692,9 @@ exports.lumiModernExtend = {
2639
2692
  },
2640
2693
  convertGet: async (entity, key, meta) => {
2641
2694
  const endpoint = meta.device.getEndpoint(1);
2642
- await endpoint.read("manuSpecificLumi", ["mode"], { manufacturerCode: manufacturerOptions.lumi.manufacturerCode });
2695
+ await endpoint.read("manuSpecificLumi", ["mode"], {
2696
+ manufacturerCode: manufacturerOptions.lumi.manufacturerCode,
2697
+ });
2643
2698
  },
2644
2699
  },
2645
2700
  ];
@@ -2652,7 +2707,7 @@ exports.lumiModernExtend = {
2652
2707
  lumiBattery: (args) => {
2653
2708
  args = {
2654
2709
  cluster: "manuSpecificLumi",
2655
- percentageAtrribute: 1,
2710
+ percentageAttribute: 1,
2656
2711
  voltageAttribute: 1,
2657
2712
  ...args,
2658
2713
  };
@@ -2664,8 +2719,8 @@ exports.lumiModernExtend = {
2664
2719
  convert: (model, msg, publish, options, meta) => {
2665
2720
  const payload = {};
2666
2721
  const lookup = numericAttributes2Lookup(model, msg.data);
2667
- if (lookup[args.percentageAtrribute.toString()]) {
2668
- const value = lookup[args.percentageAtrribute];
2722
+ if (lookup[args.percentageAttribute.toString()]) {
2723
+ const value = lookup[args.percentageAttribute];
2669
2724
  (0, utils_1.assertNumber)(value);
2670
2725
  if (!args.voltageToPercentage)
2671
2726
  payload.battery = value;
@@ -3088,7 +3143,9 @@ exports.lumiModernExtend = {
3088
3143
  await entity.write("manuSpecificLumi", { 65522: { value: val1, type: 0x41 } }, { manufacturerCode: exports.manufacturerCode });
3089
3144
  await entity.write("manuSpecificLumi", { 65522: { value: val2, type: 0x41 } }, { manufacturerCode: exports.manufacturerCode });
3090
3145
  }
3091
- await entity.read("manuSpecificLumi", [0x172], { manufacturerCode: exports.manufacturerCode });
3146
+ await entity.read("manuSpecificLumi", [0x172], {
3147
+ manufacturerCode: exports.manufacturerCode,
3148
+ });
3092
3149
  break;
3093
3150
  }
3094
3151
  case "external_temperature":
@@ -3140,63 +3197,1546 @@ exports.lumiModernExtend = {
3140
3197
  logger_1.logger.debug(`Unknown key ${key} = ${value}`, "zhc:lumi:externalSensor");
3141
3198
  }
3142
3199
  });
3143
- return result;
3144
- },
3200
+ return result;
3201
+ },
3202
+ },
3203
+ ],
3204
+ };
3205
+ },
3206
+ w600ExternalTempSensor: () => createW600ExternalTempSensor(),
3207
+ w600Heartbeat: () => createW600Heartbeat(),
3208
+ w600Thermostat: () => createW600Thermostat(),
3209
+ w600Schedule: () => createW600Schedule(),
3210
+ w600WeeklySchedule: () => createW600WeeklySchedule(),
3211
+ w600PresetTemperatureTable: () => createW600PresetTemperatureTable(),
3212
+ w600ValvePosition: () => createW600ValvePosition(),
3213
+ lumiReadPositionOnReport: (type) => {
3214
+ let converter;
3215
+ if (type === "genAnalogOutput") {
3216
+ converter = {
3217
+ cluster: "genAnalogOutput",
3218
+ type: ["attributeReport"],
3219
+ convert: (model, msg, publish, options, meta) => {
3220
+ // The position (genAnalogOutput.presentValue) reported via an attribute contains an invalid value
3221
+ // however when reading it will provide the correct value.
3222
+ msg.device.endpoints[0]
3223
+ .read("genAnalogOutput", ["presentValue"])
3224
+ .catch((error) => logger_1.logger.error(`Failed to read position '${msg.device.ieeeAddr}' (${error})`, NS));
3225
+ },
3226
+ };
3227
+ }
3228
+ else if (type === "genMultistateOutput") {
3229
+ converter = {
3230
+ cluster: "genMultistateOutput",
3231
+ type: ["attributeReport"],
3232
+ convert: (model, msg, publish, options, meta) => {
3233
+ if (msg.data.presentValue !== undefined && msg.data.presentValue > 1) {
3234
+ // Try to read the position after the motor stops, the device occasionally report wrong data right after stopping
3235
+ // Might need to add delay, seems to be working without one but needs more tests.
3236
+ msg.device
3237
+ .getEndpoint(1)
3238
+ .read("genAnalogOutput", ["presentValue"])
3239
+ .catch((error) => logger_1.logger.error(`Failed to read position '${msg.device.ieeeAddr}' (${error})`, NS));
3240
+ }
3241
+ },
3242
+ };
3243
+ }
3244
+ else if (type === "genBasic") {
3245
+ converter = {
3246
+ cluster: "genBasic",
3247
+ type: ["attributeReport"],
3248
+ convert: (model, msg, publish, options, meta) => {
3249
+ if (msg.data["1028"] === 0) {
3250
+ // Try to read the position after the motor stops, the device occasionally report wrong data right after stopping
3251
+ // Might need to add delay, seems to be working without one but needs more tests.
3252
+ msg.device
3253
+ .getEndpoint(1)
3254
+ .read("genAnalogOutput", ["presentValue"])
3255
+ .catch((error) => logger_1.logger.error(`Failed to read position '${msg.device.ieeeAddr}' (${error})`, NS));
3256
+ }
3257
+ },
3258
+ };
3259
+ }
3260
+ return { fromZigbee: [converter], isModernExtend: true };
3261
+ },
3262
+ };
3263
+ exports.modernExtend = exports.lumiModernExtend;
3264
+ const W600_NS = "zhc:aqara_w600";
3265
+ const W600_LUMI_CLUSTER = "manuSpecificLumi";
3266
+ const W600_THERMOSTAT_CLUSTER = "hvacThermostat";
3267
+ const W600_ATTR_TEMP_SETPOINT_HOLD_DURATION = 0x0024;
3268
+ const W600_ATTR_SYSTEM_MODE = 0x0271;
3269
+ const W600_ATTR_SCHEDULE = 0x027d;
3270
+ const W600_ATTR_PRESET = 0x0311;
3271
+ const W600_ATTR_PRESET_TEMPERATURE_TABLE = 0x0317;
3272
+ const W600_ATTR_SENSOR_SOURCE = 0x0280;
3273
+ const W600_ATTR_SENSOR_BINDING = 0xfff2;
3274
+ const W600_ATTR_HEARTBEAT = 0x00f7;
3275
+ const W600_ATTR_VALVE_POSITION = 0x0360;
3276
+ const W600_EXTERNAL_TEMP_SENSOR = node_buffer_1.Buffer.from("00158d00019d1b98", "hex");
3277
+ const W600_PRESET_TABLE_STORE_KEY = "w600PresetTemperatureTable";
3278
+ const W600_SENSOR_BINDING_COUNTER_STORE_KEY = "w600SensorBindingCounter";
3279
+ const W600_MANUAL_CUSTOM_PRESET_SUPPRESSION_UNTIL_STORE_KEY = "w600ManualCustomPresetSuppressionUntil";
3280
+ const W600_WEEKLY_SCHEDULE_DRAFT_STORE_KEY = "w600WeeklyScheduleDraft";
3281
+ const W600_WEEKLY_SCHEDULE_OTA_STAGE_STORE_KEY = "w600WeeklyScheduleOtaStage";
3282
+ const W600_WEEKLY_SCHEDULE_UPLOAD_STATE_STORE_KEY = "w600WeeklyScheduleUploadState";
3283
+ const W600_MANUAL_CUSTOM_PRESET_SUPPRESSION_MS = 15_000;
3284
+ const W600_WEEKLY_SCHEDULE_OTA_STAGE_TTL_MS = 5 * 60 * 1000;
3285
+ const W600_WEEKLY_SCHEDULE_UPLOAD_TIMEOUTS = new Map();
3286
+ const W600_SENSOR_BINDING_MARKER = node_buffer_1.Buffer.from([0x00, 0x01, 0x00, 0x55]);
3287
+ const W600_EXTERNAL_TEMP_SENSOR_DESCRIPTOR = node_buffer_1.Buffer.from([
3288
+ 0x15, 0x0a, 0x01, 0x00, 0x00, 0x01, 0x06, 0xe6, 0xb8, 0xa9, 0xe5, 0xba, 0xa6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x07, 0x65,
3289
+ ]);
3290
+ const W600_PRESET_ORDER = ["home", "away", "sleep", "vacation", "wind_down"];
3291
+ const W600_PRESET_BY_ID = {
3292
+ 1: "home",
3293
+ 2: "away",
3294
+ 3: "sleep",
3295
+ 5: "vacation",
3296
+ 6: "wind_down",
3297
+ 255: "none",
3298
+ };
3299
+ const W600_PRESET_ID_BY_NAME = {
3300
+ home: 1,
3301
+ away: 2,
3302
+ sleep: 3,
3303
+ vacation: 5,
3304
+ wind_down: 6,
3305
+ };
3306
+ const W600_PRESET_TEMPERATURE_DEFINITIONS = [
3307
+ { preset: "home", property: "preset_home_temperature", label: "Home temperature", description: "Home preset temperature" },
3308
+ { preset: "away", property: "preset_away_temperature", label: "Away temperature", description: "Away preset temperature" },
3309
+ { preset: "sleep", property: "preset_sleep_temperature", label: "Sleep temperature", description: "Sleep preset temperature" },
3310
+ { preset: "vacation", property: "preset_vacation_temperature", label: "Vacation temperature", description: "Vacation preset temperature" },
3311
+ { preset: "wind_down", property: "preset_wind_down_temperature", label: "Wind down temperature", description: "Wind-down preset temperature" },
3312
+ ];
3313
+ const W600_PRESET_NAME_BY_PROPERTY = Object.fromEntries(W600_PRESET_TEMPERATURE_DEFINITIONS.map((definition) => [definition.property, definition.preset]));
3314
+ const W600_PROPERTY_BY_PRESET_NAME = Object.fromEntries(W600_PRESET_TEMPERATURE_DEFINITIONS.map((definition) => [definition.preset, definition.property]));
3315
+ const W600_WEEKLY_SCHEDULE_DAY_DEFINITIONS = [
3316
+ { label: "Sunday", mask: 0x01, property: "weekly_schedule_sunday" },
3317
+ { label: "Monday", mask: 0x02, property: "weekly_schedule_monday" },
3318
+ { label: "Tuesday", mask: 0x04, property: "weekly_schedule_tuesday" },
3319
+ { label: "Wednesday", mask: 0x08, property: "weekly_schedule_wednesday" },
3320
+ { label: "Thursday", mask: 0x10, property: "weekly_schedule_thursday" },
3321
+ { label: "Friday", mask: 0x20, property: "weekly_schedule_friday" },
3322
+ { label: "Saturday", mask: 0x40, property: "weekly_schedule_saturday" },
3323
+ ];
3324
+ const W600_WEEKLY_SCHEDULE_DAY_PROPERTIES = W600_WEEKLY_SCHEDULE_DAY_DEFINITIONS.map(({ property }) => property);
3325
+ const W600_WEEKLY_SCHEDULE_HEADER_STRING = "ROUTERX-ENCRYPTEDO00";
3326
+ const W600_WEEKLY_SCHEDULE_IMAGE_TYPE = 0x1400;
3327
+ const W600_WEEKLY_SCHEDULE_FILE_VERSION = 0x00000100;
3328
+ const W600_WEEKLY_SCHEDULE_STACK_VERSION = 0x0002;
3329
+ const W600_WEEKLY_SCHEDULE_IMAGE_NOTIFY_QUERY_JITTER = 48;
3330
+ const W600_WEEKLY_SCHEDULE_UPLOAD_STATUSES = ["idle", "staged", "in_progress", "success", "failed"];
3331
+ function findW600ClimateExpose(extend) {
3332
+ return extend.exposes?.find((expose) => typeof expose !== "function" && expose.type === "climate");
3333
+ }
3334
+ function normalizeW600EnumKey(value) {
3335
+ return typeof value === "string"
3336
+ ? value
3337
+ .trim()
3338
+ .toLowerCase()
3339
+ .replace(/[\s-]+/g, "_")
3340
+ : undefined;
3341
+ }
3342
+ function parseW600EnumName(value, lookup, key) {
3343
+ const normalized = normalizeW600EnumKey(value);
3344
+ if (normalized != null && Object.hasOwn(lookup, normalized)) {
3345
+ return normalized;
3346
+ }
3347
+ throw new Error(`${key} must be one of: ${Object.keys(lookup).join(", ")}`);
3348
+ }
3349
+ function parseW600HalfDegreeTemperature(value, key, min, max) {
3350
+ const numeric = Number(value);
3351
+ if (!Number.isFinite(numeric)) {
3352
+ throw new Error(`${key} must be a number`);
3353
+ }
3354
+ if (numeric < min || numeric > max) {
3355
+ throw new Error(`${key} must be between ${min} and ${max}`);
3356
+ }
3357
+ const scaled = Math.round(numeric * 100);
3358
+ if (scaled % 50 !== 0) {
3359
+ throw new Error(`${key} must use 0.5 C steps`);
3360
+ }
3361
+ return scaled;
3362
+ }
3363
+ function getW600DeviceStoreKey(deviceOrEntity) {
3364
+ if (typeof deviceOrEntity === "string") {
3365
+ return deviceOrEntity;
3366
+ }
3367
+ if ("ieeeAddr" in deviceOrEntity && typeof deviceOrEntity.ieeeAddr === "string") {
3368
+ return deviceOrEntity.ieeeAddr;
3369
+ }
3370
+ if ("deviceIeeeAddress" in deviceOrEntity && typeof deviceOrEntity.deviceIeeeAddress === "string") {
3371
+ return deviceOrEntity.deviceIeeeAddress;
3372
+ }
3373
+ throw new Error("Unable to derive device store key");
3374
+ }
3375
+ function getW600DeviceBuffer(entity) {
3376
+ return node_buffer_1.Buffer.from(entity.deviceIeeeAddress.substring(2), "hex");
3377
+ }
3378
+ function parseW600SensorSelection(value, key) {
3379
+ if (typeof value === "string") {
3380
+ const normalized = value.trim().toLowerCase();
3381
+ if (normalized === "internal" || normalized === "external") {
3382
+ return normalized;
3383
+ }
3384
+ }
3385
+ throw new Error(`${key} must be one of: internal, external`);
3386
+ }
3387
+ function getW600SensorSelectionFromState(value) {
3388
+ if (typeof value !== "string") {
3389
+ return undefined;
3390
+ }
3391
+ const normalized = value.trim().toLowerCase();
3392
+ return normalized === "internal" || normalized === "external" ? normalized : undefined;
3393
+ }
3394
+ function parseW600ExternalTemperatureInput(value, key) {
3395
+ const numeric = Number(value);
3396
+ if (!Number.isFinite(numeric)) {
3397
+ throw new Error(`${key} must be a number`);
3398
+ }
3399
+ if (numeric < -40 || numeric > 125) {
3400
+ throw new Error(`${key} must be between -40 and 125`);
3401
+ }
3402
+ return Math.round(numeric * 100);
3403
+ }
3404
+ function decodeW600Heartbeat(buffer) {
3405
+ const heartbeat = {};
3406
+ let offset = 0;
3407
+ while (offset + 2 <= buffer.length) {
3408
+ const key = buffer.readUInt8(offset);
3409
+ const type = buffer.readUInt8(offset + 1);
3410
+ offset += 2;
3411
+ switch (type) {
3412
+ case zigbee_herdsman_1.Zcl.DataType.BOOLEAN:
3413
+ case zigbee_herdsman_1.Zcl.DataType.UINT8:
3414
+ case zigbee_herdsman_1.Zcl.DataType.ENUM8:
3415
+ if (offset + 1 > buffer.length)
3416
+ return heartbeat;
3417
+ heartbeat[key] = buffer.readUInt8(offset);
3418
+ offset += 1;
3419
+ break;
3420
+ case zigbee_herdsman_1.Zcl.DataType.INT8:
3421
+ if (offset + 1 > buffer.length)
3422
+ return heartbeat;
3423
+ heartbeat[key] = buffer.readInt8(offset);
3424
+ offset += 1;
3425
+ break;
3426
+ case zigbee_herdsman_1.Zcl.DataType.UINT16:
3427
+ case zigbee_herdsman_1.Zcl.DataType.ENUM16:
3428
+ if (offset + 2 > buffer.length)
3429
+ return heartbeat;
3430
+ heartbeat[key] = buffer.readUInt16LE(offset);
3431
+ offset += 2;
3432
+ break;
3433
+ case zigbee_herdsman_1.Zcl.DataType.INT16:
3434
+ if (offset + 2 > buffer.length)
3435
+ return heartbeat;
3436
+ heartbeat[key] = buffer.readInt16LE(offset);
3437
+ offset += 2;
3438
+ break;
3439
+ case zigbee_herdsman_1.Zcl.DataType.UINT32:
3440
+ if (offset + 4 > buffer.length)
3441
+ return heartbeat;
3442
+ heartbeat[key] = buffer.readUInt32LE(offset);
3443
+ offset += 4;
3444
+ break;
3445
+ case zigbee_herdsman_1.Zcl.DataType.OCTET_STR: {
3446
+ if (offset + 1 > buffer.length)
3447
+ return heartbeat;
3448
+ const length = buffer.readUInt8(offset);
3449
+ offset += 1;
3450
+ if (offset + length > buffer.length)
3451
+ return heartbeat;
3452
+ heartbeat[key] = buffer.subarray(offset, offset + length);
3453
+ offset += length;
3454
+ break;
3455
+ }
3456
+ default:
3457
+ logger_1.logger.debug(`Unsupported W600 heartbeat type 0x${type.toString(16)} for sub-key 0x${key.toString(16)}`, W600_NS);
3458
+ return heartbeat;
3459
+ }
3460
+ }
3461
+ return heartbeat;
3462
+ }
3463
+ function decodeW600Heartbeat9c(buffer) {
3464
+ if (buffer.length < 8) {
3465
+ return undefined;
3466
+ }
3467
+ const windowOpenStatus = buffer[4];
3468
+ const windowOpen = windowOpenStatus === 0x00 ? false : windowOpenStatus === 0x0d || windowOpenStatus === 0x0e ? true : undefined;
3469
+ const valveAlarm = windowOpenStatus === 0x10 ? true : windowOpenStatus === 0x00 || windowOpenStatus === 0x0d || windowOpenStatus === 0x0e ? false : undefined;
3470
+ return {
3471
+ valveAlarm,
3472
+ windowOpen,
3473
+ };
3474
+ }
3475
+ function parseW600BinaryEnabled(value) {
3476
+ if (value === 1 || value === true) {
3477
+ return true;
3478
+ }
3479
+ if (value === 0 || value === false) {
3480
+ return false;
3481
+ }
3482
+ if (typeof value === "string") {
3483
+ const normalized = value.trim().toLowerCase();
3484
+ if (normalized === "on" || normalized === "true") {
3485
+ return true;
3486
+ }
3487
+ if (normalized === "off" || normalized === "false") {
3488
+ return false;
3489
+ }
3490
+ }
3491
+ return undefined;
3492
+ }
3493
+ function parseRequiredW600BinaryEnabled(value, key) {
3494
+ const enabled = parseW600BinaryEnabled(value);
3495
+ if (enabled == null) {
3496
+ throw new Error(`${key} must be one of: ON, OFF`);
3497
+ }
3498
+ return enabled;
3499
+ }
3500
+ function parseW600ScheduleEnabled(value) {
3501
+ return parseW600BinaryEnabled(value);
3502
+ }
3503
+ function parseW600TemperatureSetpointHold(value) {
3504
+ if (typeof value === "boolean") {
3505
+ return value;
3506
+ }
3507
+ if (value === 1 || value === "true") {
3508
+ return true;
3509
+ }
3510
+ if (value === 0 || value === "false") {
3511
+ return false;
3512
+ }
3513
+ return undefined;
3514
+ }
3515
+ function getW600OverrideActiveState(state) {
3516
+ return parseW600TemperatureSetpointHold(state?.override_active) ?? parseW600TemperatureSetpointHold(state?.temperature_setpoint_hold);
3517
+ }
3518
+ function parseW600HeatingEnabled(value) {
3519
+ if (value === "off") {
3520
+ return false;
3521
+ }
3522
+ if (value === "heat" || value === "auto") {
3523
+ return true;
3524
+ }
3525
+ return undefined;
3526
+ }
3527
+ function getRequestedW600ScheduleEnabled(meta) {
3528
+ if (meta.message?.schedule != null) {
3529
+ return parseRequiredW600BinaryEnabled(meta.message.schedule, "schedule");
3530
+ }
3531
+ const requestedSystemMode = normalizeW600EnumKey(meta.message?.system_mode);
3532
+ if (requestedSystemMode === "auto") {
3533
+ return true;
3534
+ }
3535
+ if (requestedSystemMode === "heat" || requestedSystemMode === "off") {
3536
+ return false;
3537
+ }
3538
+ const scheduleEnabled = parseW600ScheduleEnabled(meta.state?.schedule);
3539
+ if (scheduleEnabled != null) {
3540
+ return scheduleEnabled;
3541
+ }
3542
+ if (meta.state?.system_mode === "auto") {
3543
+ return true;
3544
+ }
3545
+ if (meta.state?.system_mode === "heat" || meta.state?.system_mode === "off") {
3546
+ return false;
3547
+ }
3548
+ return undefined;
3549
+ }
3550
+ async function safeW600Read(endpoint, cluster, attributes, options) {
3551
+ try {
3552
+ await endpoint.read(cluster, attributes, options);
3553
+ }
3554
+ catch (error) {
3555
+ const details = error instanceof Error ? error.message : String(error);
3556
+ logger_1.logger.debug(`Safe read failed for ${endpoint.deviceIeeeAddress} on ${String(cluster)} [${attributes.join(", ")}]: ${details}`, W600_NS);
3557
+ }
3558
+ }
3559
+ function readW600LumiAttribute(entity, attribute) {
3560
+ return entity.read(W600_LUMI_CLUSTER, [attribute], { manufacturerCode: exports.manufacturerCode });
3561
+ }
3562
+ function writeW600LumiAttribute(entity, attribute, value, type = zigbee_herdsman_1.Zcl.DataType.UINT8) {
3563
+ return entity.write(W600_LUMI_CLUSTER, {
3564
+ [attribute]: { value, type },
3565
+ }, { manufacturerCode: exports.manufacturerCode });
3566
+ }
3567
+ function getNextW600SensorBindingCounter(entity) {
3568
+ const storeKey = getW600DeviceStoreKey(entity);
3569
+ const counter = globalStore.getValue(storeKey, W600_SENSOR_BINDING_COUNTER_STORE_KEY, 0x12);
3570
+ globalStore.putValue(storeKey, W600_SENSOR_BINDING_COUNTER_STORE_KEY, (counter + 1) & 0xff);
3571
+ return counter;
3572
+ }
3573
+ function buildW600SensorPayload(entity, action, payload) {
3574
+ const header = node_buffer_1.Buffer.from([0xaa, 0x71, payload.length + 3, 0x44, getNextW600SensorBindingCounter(entity)]);
3575
+ const checksum = (0x200 - header.reduce((sum, byte) => sum + byte, 0)) & 0xff;
3576
+ return node_buffer_1.Buffer.concat([header, node_buffer_1.Buffer.from([checksum, action, zigbee_herdsman_1.Zcl.DataType.OCTET_STR, payload.length]), payload]);
3577
+ }
3578
+ function getW600TimestampBuffer() {
3579
+ const timestamp = node_buffer_1.Buffer.alloc(4);
3580
+ timestamp.writeUInt32BE(Math.floor(Date.now() / 1000), 0);
3581
+ return timestamp;
3582
+ }
3583
+ function buildW600ExternalTempSensorBindPayload(entity) {
3584
+ const payload = node_buffer_1.Buffer.concat([
3585
+ getW600TimestampBuffer(),
3586
+ node_buffer_1.Buffer.from([0x14]),
3587
+ getW600DeviceBuffer(entity),
3588
+ W600_EXTERNAL_TEMP_SENSOR,
3589
+ W600_SENSOR_BINDING_MARKER,
3590
+ W600_EXTERNAL_TEMP_SENSOR_DESCRIPTOR,
3591
+ ]);
3592
+ return buildW600SensorPayload(entity, 0x02, payload);
3593
+ }
3594
+ function buildW600ExternalTempSensorUnbindPayload(entity) {
3595
+ const payload = node_buffer_1.Buffer.concat([getW600TimestampBuffer(), node_buffer_1.Buffer.from([0x14]), getW600DeviceBuffer(entity), node_buffer_1.Buffer.alloc(12)]);
3596
+ return buildW600SensorPayload(entity, 0x04, payload);
3597
+ }
3598
+ function buildW600ExternalTemperaturePayload(entity, centiDegrees) {
3599
+ const temperatureBuffer = node_buffer_1.Buffer.alloc(4);
3600
+ temperatureBuffer.writeFloatBE(centiDegrees, 0);
3601
+ return buildW600SensorPayload(entity, 0x05, node_buffer_1.Buffer.concat([W600_EXTERNAL_TEMP_SENSOR, W600_SENSOR_BINDING_MARKER, temperatureBuffer]));
3602
+ }
3603
+ function createW600Heartbeat() {
3604
+ return {
3605
+ exposes: [
3606
+ e.battery().withDescription("Battery percentage"),
3607
+ e.valve_alarm().withDescription("Indicates whether temperature control abnormal notification has reported an active alert"),
3608
+ e.binary("window_open", ea.STATE, true, false).withDescription("Indicates whether open window detection has reported an open window"),
3609
+ ],
3610
+ fromZigbee: [
3611
+ {
3612
+ cluster: W600_LUMI_CLUSTER,
3613
+ type: ["attributeReport", "readResponse"],
3614
+ convert: (model, msg, publish, options, meta) => {
3615
+ const value = msg.data[W600_ATTR_HEARTBEAT];
3616
+ if (!node_buffer_1.Buffer.isBuffer(value)) {
3617
+ return;
3618
+ }
3619
+ const heartbeat = decodeW600Heartbeat(value);
3620
+ const result = {};
3621
+ if (typeof heartbeat[0x0d] === "number" && Number.isFinite(heartbeat[0x0d])) {
3622
+ const device = meta.device ?? msg.device;
3623
+ device.softwareBuildID = exports.trv.decodeFirmwareVersionString(heartbeat[0x0d]);
3624
+ }
3625
+ if (typeof heartbeat[0x18] === "number" && Number.isFinite(heartbeat[0x18])) {
3626
+ result.battery = Math.max(0, Math.min(100, heartbeat[0x18]));
3627
+ }
3628
+ if (node_buffer_1.Buffer.isBuffer(heartbeat[0x9c])) {
3629
+ const heartbeat9c = decodeW600Heartbeat9c(heartbeat[0x9c]);
3630
+ if (heartbeat9c) {
3631
+ if (typeof heartbeat9c.valveAlarm === "boolean") {
3632
+ result.valve_alarm = heartbeat9c.valveAlarm;
3633
+ }
3634
+ if (typeof heartbeat9c.windowOpen === "boolean") {
3635
+ result.window_open = heartbeat9c.windowOpen;
3636
+ }
3637
+ }
3638
+ }
3639
+ return Object.keys(result).length > 0 ? result : undefined;
3640
+ },
3641
+ },
3642
+ ],
3643
+ isModernExtend: true,
3644
+ };
3645
+ }
3646
+ function deriveW600SystemMode(args) {
3647
+ if (args.heatingEnabled === false) {
3648
+ return "off";
3649
+ }
3650
+ if (args.heatingEnabled === true && args.scheduleEnabled === true) {
3651
+ return "auto";
3652
+ }
3653
+ if (args.heatingEnabled === true && args.scheduleEnabled === false) {
3654
+ return "heat";
3655
+ }
3656
+ return undefined;
3657
+ }
3658
+ function deriveW600RunningStateFromValvePosition(position) {
3659
+ if (typeof position !== "number" || !Number.isFinite(position)) {
3660
+ return undefined;
3661
+ }
3662
+ if (position > 0) {
3663
+ return "heat";
3664
+ }
3665
+ if (position === 0) {
3666
+ return "idle";
3667
+ }
3668
+ return undefined;
3669
+ }
3670
+ function buildW600ScheduleState(enabled) {
3671
+ return { schedule: enabled ? "ON" : "OFF" };
3672
+ }
3673
+ function isW600ManualCustomPresetSuppressionActive(deviceOrEntity) {
3674
+ const suppressUntil = globalStore.getValue(getW600DeviceStoreKey(deviceOrEntity), W600_MANUAL_CUSTOM_PRESET_SUPPRESSION_UNTIL_STORE_KEY, 0);
3675
+ return typeof suppressUntil === "number" && suppressUntil > Date.now();
3676
+ }
3677
+ function startW600ManualCustomPresetSuppression(deviceOrEntity, durationMs = W600_MANUAL_CUSTOM_PRESET_SUPPRESSION_MS) {
3678
+ globalStore.putValue(getW600DeviceStoreKey(deviceOrEntity), W600_MANUAL_CUSTOM_PRESET_SUPPRESSION_UNTIL_STORE_KEY, Date.now() + durationMs);
3679
+ }
3680
+ function clearW600ManualCustomPresetSuppression(deviceOrEntity) {
3681
+ globalStore.putValue(getW600DeviceStoreKey(deviceOrEntity), W600_MANUAL_CUSTOM_PRESET_SUPPRESSION_UNTIL_STORE_KEY, 0);
3682
+ }
3683
+ function getCachedW600PresetTemperatureTable(entity, meta) {
3684
+ const storeKey = getW600DeviceStoreKey(entity);
3685
+ const cached = globalStore.getValue(storeKey, W600_PRESET_TABLE_STORE_KEY);
3686
+ if (cached && typeof cached === "object") {
3687
+ return { ...cached };
3688
+ }
3689
+ const table = {};
3690
+ for (const { preset, property } of W600_PRESET_TEMPERATURE_DEFINITIONS) {
3691
+ const stateValue = meta.state?.[property];
3692
+ if (typeof stateValue !== "number" || !Number.isFinite(stateValue)) {
3693
+ return undefined;
3694
+ }
3695
+ table[preset] = Math.round(stateValue * 100);
3696
+ }
3697
+ return table;
3698
+ }
3699
+ function decodeW600PresetTemperatureTable(buffer) {
3700
+ if (buffer.length < 1) {
3701
+ return undefined;
3702
+ }
3703
+ const entryCount = buffer.readUInt8(0);
3704
+ const table = {};
3705
+ for (let index = 0; index < entryCount; index++) {
3706
+ const offset = 1 + index * 5;
3707
+ if (offset + 5 > buffer.length) {
3708
+ break;
3709
+ }
3710
+ const presetId = buffer.readUInt8(offset);
3711
+ const presetName = W600_PRESET_BY_ID[presetId];
3712
+ if (!presetName || presetName === "none") {
3713
+ continue;
3714
+ }
3715
+ table[presetName] = buffer.readUInt16LE(offset + 3);
3716
+ }
3717
+ return Object.keys(table).length === 0 ? undefined : table;
3718
+ }
3719
+ function encodeW600PresetTemperatureTable(table) {
3720
+ const buffer = node_buffer_1.Buffer.alloc(1 + W600_PRESET_ORDER.length * 5);
3721
+ buffer.writeUInt8(W600_PRESET_ORDER.length, 0);
3722
+ W600_PRESET_ORDER.forEach((presetName, index) => {
3723
+ const centiDegrees = table[presetName];
3724
+ if (!Number.isInteger(centiDegrees)) {
3725
+ throw new Error(`Missing cached value for ${presetName} preset temperature`);
3726
+ }
3727
+ const offset = 1 + index * 5;
3728
+ buffer.writeUInt8(W600_PRESET_ID_BY_NAME[presetName], offset);
3729
+ buffer.writeUInt8(0, offset + 1);
3730
+ buffer.writeUInt8(0, offset + 2);
3731
+ buffer.writeUInt16LE(centiDegrees, offset + 3);
3732
+ });
3733
+ return buffer;
3734
+ }
3735
+ function parseW600ScheduleTriggerValue(value, key) {
3736
+ if (value === true || value === 1) {
3737
+ return;
3738
+ }
3739
+ if (typeof value === "string") {
3740
+ const normalized = normalizeW600EnumKey(value);
3741
+ if (normalized && ["trigger", "press", "pressed", "start", "save", "clear", "true", "1"].includes(normalized)) {
3742
+ return;
3743
+ }
3744
+ }
3745
+ throw new Error(`${key} must be one of: trigger, press, start`);
3746
+ }
3747
+ function formatW600ScheduleTime(totalMinutes) {
3748
+ const hours = Math.floor(totalMinutes / 60)
3749
+ .toString()
3750
+ .padStart(2, "0");
3751
+ const minutes = (totalMinutes % 60).toString().padStart(2, "0");
3752
+ return `${hours}:${minutes}`;
3753
+ }
3754
+ function formatW600ScheduleDayTransitions(transitions) {
3755
+ return transitions.map(({ minutes, preset }) => `${formatW600ScheduleTime(minutes)}/${preset}`).join(", ");
3756
+ }
3757
+ function parseW600ScheduleDayTransitions(value, key) {
3758
+ if (typeof value !== "string") {
3759
+ throw new Error(`${key} must be a string`);
3760
+ }
3761
+ const compact = value.replace(/\s+/g, "");
3762
+ if (compact === "") {
3763
+ return [];
3764
+ }
3765
+ const parts = compact.split(",");
3766
+ if (parts.some((part) => part.length === 0)) {
3767
+ throw new Error(`${key} must use comma-delimited entries in the format HH:MM/preset`);
3768
+ }
3769
+ const transitions = [];
3770
+ const seenMinutes = new Set();
3771
+ for (const part of parts) {
3772
+ const match = part.match(/^([0-9]|[01]\d|2[0-3]):([0-5]\d)\/(.+)$/);
3773
+ if (!match) {
3774
+ throw new Error(`${key} entries must use the format H:MM/preset or HH:MM/preset, for example 08:00/home`);
3775
+ }
3776
+ const minutes = Number.parseInt(match[1], 10) * 60 + Number.parseInt(match[2], 10);
3777
+ if (seenMinutes.has(minutes)) {
3778
+ throw new Error(`${key} cannot contain multiple entries for ${formatW600ScheduleTime(minutes)}`);
3779
+ }
3780
+ seenMinutes.add(minutes);
3781
+ transitions.push({
3782
+ minutes,
3783
+ preset: parseW600EnumName(match[3], W600_PRESET_ID_BY_NAME, key),
3784
+ });
3785
+ }
3786
+ transitions.sort((left, right) => left.minutes - right.minutes);
3787
+ return transitions;
3788
+ }
3789
+ function createEmptyW600WeeklyScheduleDraft() {
3790
+ return Object.fromEntries(W600_WEEKLY_SCHEDULE_DAY_DEFINITIONS.map(({ property }) => [property, ""]));
3791
+ }
3792
+ function buildW600WeeklyScheduleStatePayload(draft) {
3793
+ return Object.fromEntries(W600_WEEKLY_SCHEDULE_DAY_DEFINITIONS.map(({ property }) => [property, draft[property] === "" ? null : draft[property]]));
3794
+ }
3795
+ function buildW600WeeklyScheduleUploadStatePayload(uploadState) {
3796
+ return {
3797
+ schedule_upload_status: uploadState.status,
3798
+ };
3799
+ }
3800
+ function normalizeW600WeeklyScheduleDraft(draft) {
3801
+ const normalizedDraft = createEmptyW600WeeklyScheduleDraft();
3802
+ const records = [];
3803
+ for (const { mask, property } of W600_WEEKLY_SCHEDULE_DAY_DEFINITIONS) {
3804
+ const transitions = parseW600ScheduleDayTransitions(draft[property] ?? "", property);
3805
+ normalizedDraft[property] = formatW600ScheduleDayTransitions(transitions);
3806
+ for (const transition of transitions) {
3807
+ records.push({ dayMask: mask, minutes: transition.minutes, presetId: W600_PRESET_ID_BY_NAME[transition.preset] });
3808
+ }
3809
+ }
3810
+ records.sort((left, right) => left.dayMask - right.dayMask || left.minutes - right.minutes || left.presetId - right.presetId);
3811
+ return { draft: normalizedDraft, records };
3812
+ }
3813
+ function encodeW600WeeklyScheduleSch2(records) {
3814
+ if (records.length > 0xff) {
3815
+ throw new Error("Weekly schedule contains too many entries");
3816
+ }
3817
+ const buffer = node_buffer_1.Buffer.alloc(5 + records.length * 12);
3818
+ buffer.write("SCH2", 0, "ascii");
3819
+ buffer.writeUInt8(records.length, 4);
3820
+ records.forEach((record, index) => {
3821
+ const offset = 5 + index * 12;
3822
+ buffer.writeUInt8(record.dayMask, offset);
3823
+ buffer.writeUInt16LE(record.minutes, offset + 1);
3824
+ buffer.writeUInt8(0x01, offset + 3);
3825
+ buffer.writeUInt8(record.presetId, offset + 4);
3826
+ });
3827
+ return buffer;
3828
+ }
3829
+ function buildW600WeeklyScheduleCrc32Table() {
3830
+ const table = new Uint32Array(256);
3831
+ for (let index = 0; index < 256; index++) {
3832
+ let value = index;
3833
+ for (let bit = 0; bit < 8; bit++) {
3834
+ value = (value & 1) === 1 ? (value >>> 1) ^ 0xedb88320 : value >>> 1;
3835
+ }
3836
+ table[index] = value >>> 0;
3837
+ }
3838
+ return table;
3839
+ }
3840
+ const W600_WEEKLY_SCHEDULE_CRC32_TABLE = buildW600WeeklyScheduleCrc32Table();
3841
+ function computeW600WeeklyScheduleCrc32(buffer) {
3842
+ let crc = 0xffffffff;
3843
+ for (const value of buffer) {
3844
+ crc = W600_WEEKLY_SCHEDULE_CRC32_TABLE[(crc ^ value) & 0xff] ^ (crc >>> 8);
3845
+ }
3846
+ return (crc ^ 0xffffffff) >>> 0;
3847
+ }
3848
+ function buildW600WeeklyScheduleSubelement(sch2Payload) {
3849
+ const subelement = node_buffer_1.Buffer.alloc(35 + sch2Payload.length);
3850
+ subelement.writeUInt32LE(0x014f, 0);
3851
+ subelement.writeUInt32LE(sch2Payload.length + 21, 4);
3852
+ subelement.writeUInt8(0x01, 22);
3853
+ subelement.writeUInt8(0x04, 23);
3854
+ subelement.writeUInt8(0x01, 24);
3855
+ subelement.writeUInt8(0x04, 34);
3856
+ sch2Payload.copy(subelement, 35);
3857
+ subelement.writeUInt32LE(computeW600WeeklyScheduleCrc32(subelement), 10);
3858
+ return subelement;
3859
+ }
3860
+ function buildW600WeeklyScheduleImage(records) {
3861
+ const sch2Payload = encodeW600WeeklyScheduleSch2(records);
3862
+ const subelement = buildW600WeeklyScheduleSubelement(sch2Payload);
3863
+ const header = node_buffer_1.Buffer.alloc(56);
3864
+ const headerString = node_buffer_1.Buffer.alloc(32);
3865
+ headerString.write(W600_WEEKLY_SCHEDULE_HEADER_STRING, 0, "ascii");
3866
+ header.writeUInt32LE(0x0beef11e, 0);
3867
+ header.writeUInt16LE(0x0100, 4);
3868
+ header.writeUInt16LE(56, 6);
3869
+ header.writeUInt16LE(0, 8);
3870
+ header.writeUInt16LE(exports.manufacturerCode, 10);
3871
+ header.writeUInt16LE(W600_WEEKLY_SCHEDULE_IMAGE_TYPE, 12);
3872
+ header.writeUInt32LE(W600_WEEKLY_SCHEDULE_FILE_VERSION, 14);
3873
+ header.writeUInt16LE(W600_WEEKLY_SCHEDULE_STACK_VERSION, 18);
3874
+ headerString.copy(header, 20);
3875
+ const subelementHeader = node_buffer_1.Buffer.alloc(6);
3876
+ subelementHeader.writeUInt16LE(0xf006, 0);
3877
+ subelementHeader.writeUInt32LE(subelement.length, 2);
3878
+ const image = node_buffer_1.Buffer.concat([header, subelementHeader, subelement]);
3879
+ image.writeUInt32LE(image.length, 52);
3880
+ return image;
3881
+ }
3882
+ function normalizeW600WeeklyScheduleUploadState(uploadState) {
3883
+ const normalizedStatus = W600_WEEKLY_SCHEDULE_UPLOAD_STATUSES.includes(uploadState?.status)
3884
+ ? uploadState?.status
3885
+ : "idle";
3886
+ return {
3887
+ status: normalizedStatus,
3888
+ error: typeof uploadState?.error === "string" ? uploadState.error : "",
3889
+ operation: uploadState?.operation === "clear_schedule" ? "clear_schedule" : "save_schedule",
3890
+ recordCount: Number.isInteger(uploadState?.recordCount) && (uploadState?.recordCount ?? 0) >= 0 ? uploadState?.recordCount : 0,
3891
+ uploadId: typeof uploadState?.uploadId === "string" ? uploadState.uploadId : undefined,
3892
+ updatedAt: typeof uploadState?.updatedAt === "number" ? uploadState.updatedAt : 0,
3893
+ };
3894
+ }
3895
+ function getW600WeeklyScheduleUploadStatePayload(deviceOrEntity) {
3896
+ const uploadState = normalizeW600WeeklyScheduleUploadState(globalStore.getValue(getW600DeviceStoreKey(deviceOrEntity), W600_WEEKLY_SCHEDULE_UPLOAD_STATE_STORE_KEY));
3897
+ return buildW600WeeklyScheduleUploadStatePayload(uploadState);
3898
+ }
3899
+ function updateW600WeeklyScheduleUploadState(deviceOrEntity, partialState, publish) {
3900
+ const storeKey = getW600DeviceStoreKey(deviceOrEntity);
3901
+ const currentState = normalizeW600WeeklyScheduleUploadState(globalStore.getValue(storeKey, W600_WEEKLY_SCHEDULE_UPLOAD_STATE_STORE_KEY));
3902
+ const nextState = normalizeW600WeeklyScheduleUploadState({
3903
+ ...currentState,
3904
+ ...partialState,
3905
+ updatedAt: Date.now(),
3906
+ });
3907
+ globalStore.putValue(storeKey, W600_WEEKLY_SCHEDULE_UPLOAD_STATE_STORE_KEY, nextState);
3908
+ const payload = buildW600WeeklyScheduleUploadStatePayload(nextState);
3909
+ if (typeof publish === "function") {
3910
+ publish(payload);
3911
+ }
3912
+ return { state: payload, uploadState: nextState };
3913
+ }
3914
+ function clearW600WeeklyScheduleUploadTimeout(deviceOrEntity) {
3915
+ const storeKey = getW600DeviceStoreKey(deviceOrEntity);
3916
+ const timeout = W600_WEEKLY_SCHEDULE_UPLOAD_TIMEOUTS.get(storeKey);
3917
+ if (timeout != null) {
3918
+ clearTimeout(timeout);
3919
+ W600_WEEKLY_SCHEDULE_UPLOAD_TIMEOUTS.delete(storeKey);
3920
+ }
3921
+ }
3922
+ function describeW600WeeklyScheduleOperation(operation) {
3923
+ return operation === "clear_schedule" ? "clear schedule upload" : "save schedule upload";
3924
+ }
3925
+ function failW600WeeklyScheduleUpload(deviceOrEntity, error, publish) {
3926
+ const storeKey = getW600DeviceStoreKey(deviceOrEntity);
3927
+ const uploadState = normalizeW600WeeklyScheduleUploadState(globalStore.getValue(storeKey, W600_WEEKLY_SCHEDULE_UPLOAD_STATE_STORE_KEY));
3928
+ const message = typeof error === "string" && error.trim() !== "" ? error : "Unknown weekly schedule upload failure";
3929
+ clearW600WeeklyScheduleUploadTimeout(storeKey);
3930
+ globalStore.clearValue(storeKey, W600_WEEKLY_SCHEDULE_OTA_STAGE_STORE_KEY);
3931
+ logger_1.logger.warning(`W600 ${describeW600WeeklyScheduleOperation(uploadState.operation)} failed for ${storeKey}: ${message}`, W600_NS);
3932
+ return updateW600WeeklyScheduleUploadState(storeKey, { status: "failed", error: message }, publish);
3933
+ }
3934
+ function armW600WeeklyScheduleUploadTimeout(deviceOrEntity, uploadId, publish) {
3935
+ const storeKey = getW600DeviceStoreKey(deviceOrEntity);
3936
+ clearW600WeeklyScheduleUploadTimeout(storeKey);
3937
+ const timeout = setTimeout(() => {
3938
+ const stage = globalStore.getValue(storeKey, W600_WEEKLY_SCHEDULE_OTA_STAGE_STORE_KEY);
3939
+ if (!stage || stage.uploadId !== uploadId) {
3940
+ return;
3941
+ }
3942
+ failW600WeeklyScheduleUpload(storeKey, "Timed out waiting for the device to finish the weekly schedule OTA transfer", publish);
3943
+ }, W600_WEEKLY_SCHEDULE_OTA_STAGE_TTL_MS);
3944
+ timeout.unref?.();
3945
+ W600_WEEKLY_SCHEDULE_UPLOAD_TIMEOUTS.set(storeKey, timeout);
3946
+ }
3947
+ function updateW600WeeklyScheduleOtaStage(deviceOrEntity, partialStage) {
3948
+ const storeKey = getW600DeviceStoreKey(deviceOrEntity);
3949
+ const stage = globalStore.getValue(storeKey, W600_WEEKLY_SCHEDULE_OTA_STAGE_STORE_KEY);
3950
+ if (!stage || !node_buffer_1.Buffer.isBuffer(stage.image)) {
3951
+ return undefined;
3952
+ }
3953
+ const nextStage = {
3954
+ ...stage,
3955
+ ...partialStage,
3956
+ lastActivityAt: Date.now(),
3957
+ };
3958
+ globalStore.putValue(storeKey, W600_WEEKLY_SCHEDULE_OTA_STAGE_STORE_KEY, nextStage);
3959
+ return nextStage;
3960
+ }
3961
+ function markW600WeeklyScheduleUploadStarted(deviceOrEntity, publish) {
3962
+ const storeKey = getW600DeviceStoreKey(deviceOrEntity);
3963
+ const currentState = normalizeW600WeeklyScheduleUploadState(globalStore.getValue(storeKey, W600_WEEKLY_SCHEDULE_UPLOAD_STATE_STORE_KEY));
3964
+ const stage = updateW600WeeklyScheduleOtaStage(storeKey, {});
3965
+ if (!stage) {
3966
+ return undefined;
3967
+ }
3968
+ const shouldPublish = currentState.status !== "in_progress" || currentState.uploadId !== stage.uploadId || currentState.error !== "";
3969
+ if (shouldPublish) {
3970
+ logger_1.logger.info(`W600 ${describeW600WeeklyScheduleOperation(stage.operation)} started for ${storeKey}; image size ${stage.image.length} bytes`, W600_NS);
3971
+ }
3972
+ armW600WeeklyScheduleUploadTimeout(storeKey, stage.uploadId, publish);
3973
+ return updateW600WeeklyScheduleUploadState(storeKey, {
3974
+ status: "in_progress",
3975
+ error: "",
3976
+ operation: stage.operation,
3977
+ recordCount: stage.recordCount,
3978
+ uploadId: stage.uploadId,
3979
+ }, shouldPublish ? publish : undefined);
3980
+ }
3981
+ function noteW600WeeklyScheduleUploadBlock(deviceOrEntity, publish) {
3982
+ const stage = updateW600WeeklyScheduleOtaStage(deviceOrEntity, {});
3983
+ if (!stage) {
3984
+ return undefined;
3985
+ }
3986
+ armW600WeeklyScheduleUploadTimeout(deviceOrEntity, stage.uploadId, publish);
3987
+ return stage;
3988
+ }
3989
+ function completeW600WeeklyScheduleUpload(deviceOrEntity, publish) {
3990
+ const storeKey = getW600DeviceStoreKey(deviceOrEntity);
3991
+ const stage = globalStore.getValue(storeKey, W600_WEEKLY_SCHEDULE_OTA_STAGE_STORE_KEY);
3992
+ const operation = stage?.operation === "clear_schedule" ? "clear_schedule" : "save_schedule";
3993
+ clearW600WeeklyScheduleUploadTimeout(storeKey);
3994
+ globalStore.clearValue(storeKey, W600_WEEKLY_SCHEDULE_OTA_STAGE_STORE_KEY);
3995
+ logger_1.logger.info(`W600 ${describeW600WeeklyScheduleOperation(operation)} completed for ${storeKey}`, W600_NS);
3996
+ return updateW600WeeklyScheduleUploadState(storeKey, { status: "success", error: "", operation }, publish);
3997
+ }
3998
+ function getActiveW600WeeklyScheduleOtaStage(deviceOrEntity) {
3999
+ const storeKey = getW600DeviceStoreKey(deviceOrEntity);
4000
+ const stage = globalStore.getValue(storeKey, W600_WEEKLY_SCHEDULE_OTA_STAGE_STORE_KEY);
4001
+ if (!stage || !node_buffer_1.Buffer.isBuffer(stage.image) || typeof stage.createdAt !== "number") {
4002
+ return undefined;
4003
+ }
4004
+ const lastActivityAt = typeof stage.lastActivityAt === "number" ? stage.lastActivityAt : stage.createdAt;
4005
+ if (Date.now() - lastActivityAt > W600_WEEKLY_SCHEDULE_OTA_STAGE_TTL_MS) {
4006
+ failW600WeeklyScheduleUpload(storeKey, "Weekly schedule OTA stage expired before the transfer completed");
4007
+ return undefined;
4008
+ }
4009
+ return stage;
4010
+ }
4011
+ function ensureNoActiveW600WeeklyScheduleUpload(deviceOrEntity) {
4012
+ const stage = getActiveW600WeeklyScheduleOtaStage(deviceOrEntity);
4013
+ if (!stage) {
4014
+ return;
4015
+ }
4016
+ const uploadState = normalizeW600WeeklyScheduleUploadState(globalStore.getValue(getW600DeviceStoreKey(deviceOrEntity), W600_WEEKLY_SCHEDULE_UPLOAD_STATE_STORE_KEY));
4017
+ throw new Error(`A weekly schedule upload is already active (${uploadState.status}) for ${describeW600WeeklyScheduleOperation(stage.operation)}. ` +
4018
+ "Wait for it to finish before starting another save or clear.");
4019
+ }
4020
+ function getCachedW600WeeklyScheduleDraft(entity, meta) {
4021
+ const draft = createEmptyW600WeeklyScheduleDraft();
4022
+ const storeKey = getW600DeviceStoreKey(entity);
4023
+ const cached = globalStore.getValue(storeKey, W600_WEEKLY_SCHEDULE_DRAFT_STORE_KEY);
4024
+ for (const { property } of W600_WEEKLY_SCHEDULE_DAY_DEFINITIONS) {
4025
+ if (typeof cached?.[property] === "string") {
4026
+ draft[property] = cached[property];
4027
+ }
4028
+ else if (typeof meta.state?.[property] === "string") {
4029
+ draft[property] = meta.state[property];
4030
+ }
4031
+ }
4032
+ return draft;
4033
+ }
4034
+ function seedW600WeeklyScheduleDraftState(deviceOrEntity, state) {
4035
+ const draft = getCachedW600WeeklyScheduleDraft(deviceOrEntity, { state });
4036
+ Object.assign(state, buildW600WeeklyScheduleStatePayload(draft));
4037
+ }
4038
+ function stageW600WeeklyScheduleUpload(entity, draft, image, operation, recordCount, publish) {
4039
+ const storeKey = getW600DeviceStoreKey(entity);
4040
+ const uploadId = `${Date.now().toString(36)}-${Math.random().toString(16).slice(2, 10)}`;
4041
+ globalStore.putValue(storeKey, W600_WEEKLY_SCHEDULE_DRAFT_STORE_KEY, draft);
4042
+ globalStore.putValue(storeKey, W600_WEEKLY_SCHEDULE_OTA_STAGE_STORE_KEY, {
4043
+ createdAt: Date.now(),
4044
+ lastActivityAt: Date.now(),
4045
+ image,
4046
+ operation,
4047
+ recordCount,
4048
+ uploadId,
4049
+ });
4050
+ armW600WeeklyScheduleUploadTimeout(storeKey, uploadId, publish);
4051
+ return updateW600WeeklyScheduleUploadState(storeKey, { status: "staged", error: "", operation, recordCount, uploadId });
4052
+ }
4053
+ function getNumericW600OtaRequestField(data, key) {
4054
+ const value = data?.[key];
4055
+ if (value == null) {
4056
+ return undefined;
4057
+ }
4058
+ const numeric = Number(value);
4059
+ return Number.isFinite(numeric) ? numeric : undefined;
4060
+ }
4061
+ function matchesW600WeeklyScheduleOtaRequest(data, requireFileVersion = false) {
4062
+ if (getNumericW600OtaRequestField(data, "manufacturerCode") !== exports.manufacturerCode ||
4063
+ getNumericW600OtaRequestField(data, "imageType") !== W600_WEEKLY_SCHEDULE_IMAGE_TYPE) {
4064
+ return false;
4065
+ }
4066
+ return !requireFileVersion || getNumericW600OtaRequestField(data, "fileVersion") === W600_WEEKLY_SCHEDULE_FILE_VERSION;
4067
+ }
4068
+ function createW600ExternalTempSensor() {
4069
+ const readSensorState = async (entity) => {
4070
+ await readW600LumiAttribute(entity, W600_ATTR_SENSOR_SOURCE);
4071
+ };
4072
+ return {
4073
+ exposes: [
4074
+ e
4075
+ .temperature_sensor_select(["internal", "external"])
4076
+ .withAccess(ea.ALL)
4077
+ .withLabel("Temperature source")
4078
+ .withDescription("Choose whether the thermostat uses its internal sensor or data provided via 'External Sensor Temperature'"),
4079
+ e
4080
+ .external_temperature_input()
4081
+ .withValueMin(-40)
4082
+ .withValueMax(125)
4083
+ .withValueStep(0.01)
4084
+ .withDescription("Manual external temperature forwarded to the W600 when temperature source is external")
4085
+ .withCategory("config"),
4086
+ ],
4087
+ fromZigbee: [
4088
+ {
4089
+ cluster: W600_LUMI_CLUSTER,
4090
+ type: ["attributeReport", "readResponse"],
4091
+ convert: (model, msg) => {
4092
+ const result = {};
4093
+ if (msg.data[W600_ATTR_SENSOR_SOURCE] === 0 || msg.data[W600_ATTR_SENSOR_SOURCE] === 1) {
4094
+ result.sensor = msg.data[W600_ATTR_SENSOR_SOURCE] === 1 ? "external" : "internal";
4095
+ }
4096
+ return Object.keys(result).length > 0 ? result : undefined;
4097
+ },
4098
+ },
4099
+ ],
4100
+ toZigbee: [
4101
+ {
4102
+ key: ["sensor"],
4103
+ convertSet: async (entity, key, value) => {
4104
+ (0, utils_1.assertEndpoint)(entity);
4105
+ const sensor = parseW600SensorSelection(value, key);
4106
+ if (sensor === "external") {
4107
+ await writeW600LumiAttribute(entity, W600_ATTR_SENSOR_BINDING, buildW600ExternalTempSensorBindPayload(entity), zigbee_herdsman_1.Zcl.DataType.OCTET_STR);
4108
+ await writeW600LumiAttribute(entity, W600_ATTR_SENSOR_SOURCE, 1);
4109
+ return { state: { sensor: "external" } };
4110
+ }
4111
+ await writeW600LumiAttribute(entity, W600_ATTR_SENSOR_SOURCE, 0);
4112
+ await writeW600LumiAttribute(entity, W600_ATTR_SENSOR_BINDING, buildW600ExternalTempSensorUnbindPayload(entity), zigbee_herdsman_1.Zcl.DataType.OCTET_STR);
4113
+ return { state: { sensor: "internal" } };
4114
+ },
4115
+ convertGet: async (entity) => {
4116
+ (0, utils_1.assertEndpoint)(entity);
4117
+ await readSensorState(entity);
4118
+ },
4119
+ },
4120
+ {
4121
+ key: ["external_temperature_input"],
4122
+ convertSet: async (entity, key, value, meta) => {
4123
+ (0, utils_1.assertEndpoint)(entity);
4124
+ const requestedSensor = meta.message?.sensor != null ? parseW600SensorSelection(meta.message.sensor, "sensor") : undefined;
4125
+ const currentSensor = getW600SensorSelectionFromState(meta.state?.sensor);
4126
+ const sensor = requestedSensor ?? currentSensor;
4127
+ if (sensor !== "external") {
4128
+ throw new Error("external_temperature_input can only be used when sensor is external");
4129
+ }
4130
+ const shouldRefreshBinding = currentSensor !== "external" || requestedSensor === "external";
4131
+ if (shouldRefreshBinding) {
4132
+ await writeW600LumiAttribute(entity, W600_ATTR_SENSOR_BINDING, buildW600ExternalTempSensorBindPayload(entity), zigbee_herdsman_1.Zcl.DataType.OCTET_STR);
4133
+ await writeW600LumiAttribute(entity, W600_ATTR_SENSOR_SOURCE, 1);
4134
+ }
4135
+ const centiDegrees = parseW600ExternalTemperatureInput(value, key);
4136
+ await writeW600LumiAttribute(entity, W600_ATTR_SENSOR_BINDING, buildW600ExternalTemperaturePayload(entity, centiDegrees), zigbee_herdsman_1.Zcl.DataType.OCTET_STR);
4137
+ return {
4138
+ state: {
4139
+ external_temperature_input: centiDegrees / 100,
4140
+ ...(shouldRefreshBinding ? { sensor: "external" } : {}),
4141
+ },
4142
+ };
4143
+ },
4144
+ convertGet: async (entity) => {
4145
+ (0, utils_1.assertEndpoint)(entity);
4146
+ await readSensorState(entity);
4147
+ },
4148
+ },
4149
+ ],
4150
+ configure: [
4151
+ async (device) => {
4152
+ const endpoint = device.getEndpoint(1);
4153
+ await safeW600Read(endpoint, W600_LUMI_CLUSTER, [W600_ATTR_SENSOR_SOURCE], { manufacturerCode: exports.manufacturerCode });
4154
+ },
4155
+ ],
4156
+ isModernExtend: true,
4157
+ };
4158
+ }
4159
+ function createW600Thermostat() {
4160
+ const extend = modernExtend.thermostat({
4161
+ setpoints: {
4162
+ values: { occupiedHeatingSetpoint: { min: 5, max: 30, step: 0.5 } },
4163
+ },
4164
+ localTemperatureCalibration: { values: { min: -5, max: 5, step: 0.1 } },
4165
+ temperatureSetpointHoldDuration: true,
4166
+ systemMode: { values: ["off", "heat", "auto"], configure: { skip: true } },
4167
+ runningState: { values: ["idle", "heat"], toZigbee: { skip: true }, configure: { skip: true } },
4168
+ });
4169
+ const climateExpose = findW600ClimateExpose(extend);
4170
+ climateExpose?.withPreset([...W600_PRESET_ORDER], "Selected preset scene");
4171
+ climateExpose?.setAccess("preset", ea.ALL);
4172
+ const holdDurationExpose = extend.exposes?.find((expose) => typeof expose !== "function" && expose.type === "numeric" && expose.property === "temperature_setpoint_hold_duration");
4173
+ holdDurationExpose
4174
+ ?.withLabel("Manual Override Duration")
4175
+ .withUnit("min")
4176
+ .withCategory("config")
4177
+ .withDescription("Duration in minutes for the current manual override. 0 means until next schedule event, 65535 means indefinitely.");
4178
+ extend.exposes?.push(e.binary("override_active", ea.STATE, true, false).withLabel("Manual Override").withDescription("Temporary manual override active"));
4179
+ const thermostatConverter = {
4180
+ cluster: W600_THERMOSTAT_CLUSTER,
4181
+ type: ["attributeReport", "readResponse"],
4182
+ convert: (model, msg, publish, options, meta) => {
4183
+ const result = fz.thermostat.convert(model, msg, publish, options, meta);
4184
+ if (result && msg.data.tempSetpointHold !== undefined) {
4185
+ const holdProperty = (0, utils_1.postfixWithEndpointName)("temperature_setpoint_hold", msg, model, meta);
4186
+ result.override_active = msg.data.tempSetpointHold === 1;
4187
+ delete result[holdProperty];
4188
+ }
4189
+ return result;
4190
+ },
4191
+ };
4192
+ const occupiedHeatingSetpointConverter = {
4193
+ key: ["occupied_heating_setpoint"],
4194
+ options: tz.thermostat_occupied_heating_setpoint.options,
4195
+ convertSet: async (entity, key, value, meta) => {
4196
+ (0, utils_1.assertEndpoint)(entity);
4197
+ const result = await tz.thermostat_occupied_heating_setpoint.convertSet(entity, key, value, meta);
4198
+ const resultState = result && "state" in result ? result.state : undefined;
4199
+ const shouldUseHold = getRequestedW600ScheduleEnabled(meta) !== false;
4200
+ if (shouldUseHold) {
4201
+ await entity.write(W600_THERMOSTAT_CLUSTER, { tempSetpointHold: 1 });
4202
+ }
4203
+ startW600ManualCustomPresetSuppression(entity);
4204
+ return {
4205
+ state: {
4206
+ ...(resultState ?? {}),
4207
+ ...(shouldUseHold ? { system_mode: "auto", schedule: "ON", override_active: true } : {}),
4208
+ preset: "none",
4209
+ },
4210
+ };
4211
+ },
4212
+ convertGet: async (entity, key, meta) => {
4213
+ (0, utils_1.assertEndpoint)(entity);
4214
+ await tz.thermostat_occupied_heating_setpoint.convertGet?.(entity, key, meta);
4215
+ },
4216
+ };
4217
+ const systemModeConverter = {
4218
+ key: ["system_mode"],
4219
+ convertSet: async (entity, key, value, meta) => {
4220
+ (0, utils_1.assertEndpoint)(entity);
4221
+ const normalized = parseW600EnumName(value, { off: 0, heat: 1, auto: 2 }, key);
4222
+ if (normalized === "off") {
4223
+ clearW600ManualCustomPresetSuppression(entity);
4224
+ await writeW600LumiAttribute(entity, W600_ATTR_SYSTEM_MODE, 0);
4225
+ await writeW600LumiAttribute(entity, W600_ATTR_SCHEDULE, 0);
4226
+ await entity.write(W600_THERMOSTAT_CLUSTER, { tempSetpointHold: 0 });
4227
+ return { state: { system_mode: "off", schedule: "OFF", override_active: false, running_state: "idle" } };
4228
+ }
4229
+ await writeW600LumiAttribute(entity, W600_ATTR_SYSTEM_MODE, 1);
4230
+ if (normalized === "auto") {
4231
+ clearW600ManualCustomPresetSuppression(entity);
4232
+ await writeW600LumiAttribute(entity, W600_ATTR_SCHEDULE, 1);
4233
+ await entity.write(W600_THERMOSTAT_CLUSTER, { tempSetpointHold: 0 });
4234
+ return { state: { system_mode: "auto", schedule: "ON", override_active: false } };
4235
+ }
4236
+ clearW600ManualCustomPresetSuppression(entity);
4237
+ await writeW600LumiAttribute(entity, W600_ATTR_SCHEDULE, 0);
4238
+ await entity.write(W600_THERMOSTAT_CLUSTER, { tempSetpointHold: 0 });
4239
+ return { state: { system_mode: "heat", schedule: "OFF", override_active: false } };
4240
+ },
4241
+ convertGet: async (entity) => {
4242
+ (0, utils_1.assertEndpoint)(entity);
4243
+ await readW600LumiAttribute(entity, W600_ATTR_SYSTEM_MODE);
4244
+ await readW600LumiAttribute(entity, W600_ATTR_SCHEDULE);
4245
+ await entity.read(W600_THERMOSTAT_CLUSTER, ["tempSetpointHold"]);
4246
+ },
4247
+ };
4248
+ const presetConverter = {
4249
+ key: ["preset"],
4250
+ convertSet: async (entity, key, value, meta) => {
4251
+ (0, utils_1.assertEndpoint)(entity);
4252
+ const normalized = parseW600EnumName(value, { none: 0, ...W600_PRESET_ID_BY_NAME }, key);
4253
+ const scheduleEnabled = getRequestedW600ScheduleEnabled(meta) !== false;
4254
+ if (normalized === "none") {
4255
+ startW600ManualCustomPresetSuppression(entity);
4256
+ if (scheduleEnabled) {
4257
+ await entity.write(W600_THERMOSTAT_CLUSTER, { tempSetpointHold: 1 });
4258
+ return { state: { system_mode: "auto", schedule: "ON", preset: "none", override_active: true } };
4259
+ }
4260
+ return { state: { preset: "none" } };
4261
+ }
4262
+ clearW600ManualCustomPresetSuppression(entity);
4263
+ await writeW600LumiAttribute(entity, W600_ATTR_PRESET, W600_PRESET_ID_BY_NAME[normalized]);
4264
+ if (scheduleEnabled) {
4265
+ await entity.write(W600_THERMOSTAT_CLUSTER, { tempSetpointHold: 1 });
4266
+ return { state: { system_mode: "auto", schedule: "ON", preset: normalized, override_active: true } };
4267
+ }
4268
+ return { state: { preset: normalized } };
4269
+ },
4270
+ convertGet: async (entity) => {
4271
+ (0, utils_1.assertEndpoint)(entity);
4272
+ await readW600LumiAttribute(entity, W600_ATTR_PRESET);
4273
+ },
4274
+ };
4275
+ const holdDurationConverter = {
4276
+ key: ["temperature_setpoint_hold_duration"],
4277
+ convertSet: async (entity, key, value) => {
4278
+ (0, utils_1.assertEndpoint)(entity);
4279
+ const duration = Number(value);
4280
+ if (!Number.isInteger(duration) || duration < 0 || duration > 65535) {
4281
+ throw new Error(`${key} must be an integer between 0 and 65535`);
4282
+ }
4283
+ await entity.write(W600_THERMOSTAT_CLUSTER, { [W600_ATTR_TEMP_SETPOINT_HOLD_DURATION]: { value: duration, type: zigbee_herdsman_1.Zcl.DataType.UINT16 } }, { writeUndiv: true });
4284
+ return { state: { temperature_setpoint_hold_duration: duration } };
4285
+ },
4286
+ convertGet: async (entity) => {
4287
+ (0, utils_1.assertEndpoint)(entity);
4288
+ await entity.read(W600_THERMOSTAT_CLUSTER, ["tempSetpointHoldDuration"]);
4289
+ },
4290
+ };
4291
+ const runningStateConverter = {
4292
+ key: ["running_state"],
4293
+ convertGet: async (entity) => {
4294
+ (0, utils_1.assertEndpoint)(entity);
4295
+ await readW600LumiAttribute(entity, W600_ATTR_VALVE_POSITION);
4296
+ },
4297
+ };
4298
+ extend.toZigbee = (0, utils_1.replaceToZigbeeConvertersInArray)(extend.toZigbee ?? [], [tz.thermostat_occupied_heating_setpoint, tz.thermostat_system_mode, tz.thermostat_temperature_setpoint_hold_duration], [occupiedHeatingSetpointConverter, systemModeConverter, holdDurationConverter]);
4299
+ extend.toZigbee ??= [];
4300
+ extend.toZigbee.push(presetConverter, runningStateConverter);
4301
+ extend.fromZigbee = (extend.fromZigbee ?? []).map((converter) => (converter === fz.thermostat ? thermostatConverter : converter));
4302
+ extend.fromZigbee.push({
4303
+ cluster: W600_THERMOSTAT_CLUSTER,
4304
+ type: ["attributeReport", "readResponse"],
4305
+ convert: (model, msg, publish, options, meta) => {
4306
+ const device = meta.device ?? msg.device;
4307
+ const result = {};
4308
+ const hold = msg.data.tempSetpointHold !== undefined ? msg.data.tempSetpointHold === 1 : getW600OverrideActiveState(meta.state);
4309
+ const heatingEnabled = parseW600HeatingEnabled(meta.state?.system_mode);
4310
+ const scheduleEnabled = parseW600ScheduleEnabled(meta.state?.schedule);
4311
+ if (msg.data.tempSetpointHold === 0) {
4312
+ clearW600ManualCustomPresetSuppression(device);
4313
+ }
4314
+ if (msg.data.tempSetpointHold !== undefined) {
4315
+ result.override_active = hold;
4316
+ const systemMode = deriveW600SystemMode({ heatingEnabled, scheduleEnabled });
4317
+ if (systemMode) {
4318
+ result.system_mode = systemMode;
4319
+ }
4320
+ }
4321
+ if (isW600ManualCustomPresetSuppressionActive(device) && (msg.data.occupiedHeatingSetpoint !== undefined || hold === true)) {
4322
+ result.preset = "none";
4323
+ }
4324
+ return Object.keys(result).length > 0 ? result : undefined;
4325
+ },
4326
+ }, {
4327
+ cluster: W600_LUMI_CLUSTER,
4328
+ type: ["attributeReport", "readResponse"],
4329
+ convert: (model, msg, publish, options, meta) => {
4330
+ const device = meta.device ?? msg.device;
4331
+ const result = {};
4332
+ const heatingEnabled = msg.data[W600_ATTR_SYSTEM_MODE] !== undefined
4333
+ ? msg.data[W600_ATTR_SYSTEM_MODE] === 1
4334
+ : parseW600HeatingEnabled(meta.state?.system_mode);
4335
+ const scheduleEnabled = msg.data[W600_ATTR_SCHEDULE] !== undefined ? msg.data[W600_ATTR_SCHEDULE] === 1 : parseW600ScheduleEnabled(meta.state?.schedule);
4336
+ const runningState = deriveW600RunningStateFromValvePosition(msg.data[W600_ATTR_VALVE_POSITION]);
4337
+ if (runningState) {
4338
+ result.running_state = runningState;
4339
+ }
4340
+ if (msg.data[W600_ATTR_SYSTEM_MODE] !== undefined || msg.data[W600_ATTR_SCHEDULE] !== undefined) {
4341
+ const systemMode = deriveW600SystemMode({ heatingEnabled, scheduleEnabled });
4342
+ if (systemMode) {
4343
+ result.system_mode = systemMode;
4344
+ }
4345
+ if (heatingEnabled === false) {
4346
+ clearW600ManualCustomPresetSuppression(device);
4347
+ result.schedule = "OFF";
4348
+ result.override_active = false;
4349
+ result.running_state = "idle";
4350
+ if (parseW600ScheduleEnabled(meta.state?.schedule) !== false) {
4351
+ writeW600LumiAttribute(msg.endpoint, W600_ATTR_SCHEDULE, 0).catch((error) => logger_1.logger.warning(`Failed to disable W600 schedule after heating was turned off for '${device.ieeeAddr}': ${error}`, W600_NS));
4352
+ msg.endpoint
4353
+ .write(W600_THERMOSTAT_CLUSTER, { tempSetpointHold: 0 })
4354
+ .catch((error) => logger_1.logger.warning(`Failed to clear W600 manual override after heating was turned off for '${device.ieeeAddr}': ${error}`, W600_NS));
4355
+ }
4356
+ }
4357
+ }
4358
+ const presetValue = msg.data[W600_ATTR_PRESET];
4359
+ if (typeof presetValue === "number" && Object.hasOwn(W600_PRESET_BY_ID, presetValue)) {
4360
+ if (presetValue === 255) {
4361
+ startW600ManualCustomPresetSuppression(device);
4362
+ result.preset = "none";
4363
+ }
4364
+ else if (isW600ManualCustomPresetSuppressionActive(device)) {
4365
+ result.preset = "none";
4366
+ }
4367
+ else {
4368
+ clearW600ManualCustomPresetSuppression(device);
4369
+ result.preset = W600_PRESET_BY_ID[presetValue];
4370
+ }
4371
+ }
4372
+ return Object.keys(result).length > 0 ? result : undefined;
4373
+ },
4374
+ });
4375
+ extend.configure ??= [];
4376
+ const configureOverrideActive = modernExtend.setupConfigureForReporting(W600_THERMOSTAT_CLUSTER, "tempSetpointHold", {
4377
+ config: { min: "MIN", max: "1_HOUR", change: 0 },
4378
+ access: ea.STATE_GET,
4379
+ });
4380
+ if (configureOverrideActive) {
4381
+ extend.configure.push(configureOverrideActive);
4382
+ }
4383
+ extend.configure.push(async (device) => {
4384
+ const endpoint = device.getEndpoint(1);
4385
+ await safeW600Read(endpoint, W600_LUMI_CLUSTER, [W600_ATTR_SYSTEM_MODE, W600_ATTR_SCHEDULE, W600_ATTR_PRESET], { manufacturerCode: exports.manufacturerCode });
4386
+ await safeW600Read(endpoint, W600_THERMOSTAT_CLUSTER, ["tempSetpointHold"]);
4387
+ });
4388
+ return extend;
4389
+ }
4390
+ function createW600ValvePosition() {
4391
+ return modernExtend.numeric({
4392
+ name: "position",
4393
+ valueMin: 0,
4394
+ valueMax: 100,
4395
+ scale: 1,
4396
+ precision: 2,
4397
+ unit: "%",
4398
+ access: "STATE_GET",
4399
+ cluster: W600_LUMI_CLUSTER,
4400
+ attribute: { ID: W600_ATTR_VALVE_POSITION, type: zigbee_herdsman_1.Zcl.DataType.SINGLE_PREC },
4401
+ description: "Position of the valve, 100% is fully open",
4402
+ label: "Valve position",
4403
+ zigbeeCommandOptions: { manufacturerCode: exports.manufacturerCode },
4404
+ });
4405
+ }
4406
+ function createW600Schedule() {
4407
+ return {
4408
+ fromZigbee: [
4409
+ {
4410
+ cluster: W600_LUMI_CLUSTER,
4411
+ type: ["attributeReport", "readResponse"],
4412
+ convert: (model, msg, publish, options, meta) => {
4413
+ if (msg.data[W600_ATTR_SCHEDULE] === undefined) {
4414
+ return;
4415
+ }
4416
+ const heatingEnabled = msg.data[W600_ATTR_SYSTEM_MODE] !== undefined
4417
+ ? msg.data[W600_ATTR_SYSTEM_MODE] === 1
4418
+ : parseW600HeatingEnabled(meta.state?.system_mode);
4419
+ if (heatingEnabled === false) {
4420
+ return buildW600ScheduleState(false);
4421
+ }
4422
+ return buildW600ScheduleState(msg.data[W600_ATTR_SCHEDULE] === 1);
4423
+ },
4424
+ },
4425
+ ],
4426
+ toZigbee: [
4427
+ {
4428
+ key: ["schedule"],
4429
+ convertSet: async (entity, key, value, meta) => {
4430
+ (0, utils_1.assertEndpoint)(entity);
4431
+ const enabled = parseRequiredW600BinaryEnabled(value, key);
4432
+ const heatingEnabled = parseW600HeatingEnabled(meta.state?.system_mode);
4433
+ if (enabled) {
4434
+ await writeW600LumiAttribute(entity, W600_ATTR_SCHEDULE, 1);
4435
+ const state = buildW600ScheduleState(true);
4436
+ const systemMode = deriveW600SystemMode({
4437
+ heatingEnabled,
4438
+ scheduleEnabled: true,
4439
+ });
4440
+ if (systemMode) {
4441
+ state.system_mode = systemMode;
4442
+ }
4443
+ return { state };
4444
+ }
4445
+ clearW600ManualCustomPresetSuppression(entity);
4446
+ await entity.write(W600_THERMOSTAT_CLUSTER, { tempSetpointHold: 0 });
4447
+ await writeW600LumiAttribute(entity, W600_ATTR_SCHEDULE, 0);
4448
+ const state = { ...buildW600ScheduleState(false), override_active: false };
4449
+ const systemMode = deriveW600SystemMode({ heatingEnabled, scheduleEnabled: false });
4450
+ if (systemMode) {
4451
+ state.system_mode = systemMode;
4452
+ }
4453
+ return { state };
3145
4454
  },
3146
- ],
3147
- };
3148
- },
3149
- lumiReadPositionOnReport: (type) => {
3150
- let converter;
3151
- if (type === "genAnalogOutput") {
3152
- converter = {
3153
- cluster: "genAnalogOutput",
3154
- type: ["attributeReport"],
3155
- convert: (model, msg, publish, options, meta) => {
3156
- // The position (genAnalogOutput.presentValue) reported via an attribute contains an invalid value
3157
- // however when reading it will provide the correct value.
3158
- msg.device.endpoints[0]
3159
- .read("genAnalogOutput", ["presentValue"])
3160
- .catch((error) => logger_1.logger.error(`Failed to read position '${msg.device.ieeeAddr}' (${error})`, NS));
4455
+ convertGet: async (entity) => {
4456
+ (0, utils_1.assertEndpoint)(entity);
4457
+ await readW600LumiAttribute(entity, W600_ATTR_SCHEDULE);
3161
4458
  },
3162
- };
3163
- }
3164
- else if (type === "genMultistateOutput") {
3165
- converter = {
3166
- cluster: "genMultistateOutput",
3167
- type: ["attributeReport"],
4459
+ },
4460
+ ],
4461
+ configure: [
4462
+ async (device) => {
4463
+ const endpoint = device.getEndpoint(1);
4464
+ await safeW600Read(endpoint, W600_LUMI_CLUSTER, [W600_ATTR_SCHEDULE], { manufacturerCode: exports.manufacturerCode });
4465
+ },
4466
+ ],
4467
+ isModernExtend: true,
4468
+ };
4469
+ }
4470
+ function createW600WeeklySchedule() {
4471
+ const dayDescription = "Staged weekly schedule for this day. Use comma-delimited entries in the format HH:MM/preset, for example '08:00/home, 19:00/vacation'. Editing the text fields does not upload anything until Save schedule is triggered.";
4472
+ const uploadStatusDescription = "Current state of the custom OTA transfer used to upload the weekly schedule to the thermostat.";
4473
+ const onEvent = [
4474
+ (event) => {
4475
+ const shouldSeedDraft = event.type === "start" ||
4476
+ event.type === "deviceJoined" ||
4477
+ (event.type === "deviceInterview" && (event.data.status === "started" || event.data.status === "successful"));
4478
+ if (!shouldSeedDraft) {
4479
+ return;
4480
+ }
4481
+ seedW600WeeklyScheduleDraftState(event.data.device, event.data.state);
4482
+ const uploadState = normalizeW600WeeklyScheduleUploadState(globalStore.getValue(getW600DeviceStoreKey(event.data.device), W600_WEEKLY_SCHEDULE_UPLOAD_STATE_STORE_KEY));
4483
+ globalStore.putValue(getW600DeviceStoreKey(event.data.device), W600_WEEKLY_SCHEDULE_UPLOAD_STATE_STORE_KEY, uploadState);
4484
+ event.data.state.schedule_upload_status = uploadState.status;
4485
+ if (event.type === "start") {
4486
+ const endpoint = event.data.device.getEndpoint(1);
4487
+ void safeW600Read(endpoint, W600_LUMI_CLUSTER, [W600_ATTR_SCHEDULE], { manufacturerCode: exports.manufacturerCode });
4488
+ }
4489
+ },
4490
+ ];
4491
+ return {
4492
+ exposes: [
4493
+ ...W600_WEEKLY_SCHEDULE_DAY_DEFINITIONS.map(({ label, property }) => e.text(property, ea.STATE_SET).withLabel(`${label} schedule`).withDescription(dayDescription).withCategory("config")),
4494
+ e
4495
+ .enum("schedule_upload_status", ea.STATE, [...W600_WEEKLY_SCHEDULE_UPLOAD_STATUSES])
4496
+ .withLabel("Schedule upload status")
4497
+ .withDescription(uploadStatusDescription)
4498
+ .withCategory("diagnostic"),
4499
+ e
4500
+ .enum("save_schedule", ea.SET, ["trigger"])
4501
+ .withLabel("Save schedule")
4502
+ .withDescription("Upload the weekly schedule to the thermostat")
4503
+ .withCategory("config"),
4504
+ e
4505
+ .enum("clear_schedule", ea.SET, ["trigger"])
4506
+ .withLabel("Clear schedule")
4507
+ .withDescription("Clear all weekly schedule inputs and upload an empty schedule to the thermostat")
4508
+ .withCategory("config"),
4509
+ ],
4510
+ fromZigbee: [
4511
+ {
4512
+ cluster: W600_LUMI_CLUSTER,
4513
+ type: ["attributeReport", "readResponse"],
3168
4514
  convert: (model, msg, publish, options, meta) => {
3169
- if (msg.data.presentValue !== undefined && msg.data.presentValue > 1) {
3170
- // Try to read the position after the motor stops, the device occasionally report wrong data right after stopping
3171
- // Might need to add delay, seems to be working without one but needs more tests.
3172
- msg.device
3173
- .getEndpoint(1)
3174
- .read("genAnalogOutput", ["presentValue"])
3175
- .catch((error) => logger_1.logger.error(`Failed to read position '${msg.device.ieeeAddr}' (${error})`, NS));
4515
+ if (msg.data[W600_ATTR_SCHEDULE] === undefined) {
4516
+ return;
3176
4517
  }
4518
+ const device = meta.device ?? msg.device;
4519
+ return getW600WeeklyScheduleUploadStatePayload(device);
3177
4520
  },
3178
- };
3179
- }
3180
- else if (type === "genBasic") {
3181
- converter = {
3182
- cluster: "genBasic",
3183
- type: ["attributeReport"],
4521
+ },
4522
+ {
4523
+ cluster: "genOta",
4524
+ type: ["commandQueryNextImageRequest"],
4525
+ convert: async (model, msg, publish, options, meta) => {
4526
+ const device = meta.device ?? msg.device;
4527
+ const stage = getActiveW600WeeklyScheduleOtaStage(device);
4528
+ if (!stage) {
4529
+ return;
4530
+ }
4531
+ if (!matchesW600WeeklyScheduleOtaRequest(msg.data)) {
4532
+ await msg.endpoint.commandResponse("genOta", "queryNextImageResponse", { status: zigbee_herdsman_1.Zcl.Status.NO_IMAGE_AVAILABLE }, undefined, msg.meta.zclTransactionSequenceNumber);
4533
+ return;
4534
+ }
4535
+ markW600WeeklyScheduleUploadStarted(device, publish);
4536
+ await msg.endpoint.commandResponse("genOta", "queryNextImageResponse", {
4537
+ status: zigbee_herdsman_1.Zcl.Status.SUCCESS,
4538
+ manufacturerCode: exports.manufacturerCode,
4539
+ imageType: W600_WEEKLY_SCHEDULE_IMAGE_TYPE,
4540
+ fileVersion: W600_WEEKLY_SCHEDULE_FILE_VERSION,
4541
+ imageSize: stage.image.length,
4542
+ }, undefined, msg.meta.zclTransactionSequenceNumber);
4543
+ },
4544
+ },
4545
+ {
4546
+ cluster: "genOta",
4547
+ type: ["commandImageBlockRequest"],
4548
+ convert: async (model, msg, publish, options, meta) => {
4549
+ const device = meta.device ?? msg.device;
4550
+ const stage = getActiveW600WeeklyScheduleOtaStage(device);
4551
+ if (!stage) {
4552
+ return;
4553
+ }
4554
+ if (!matchesW600WeeklyScheduleOtaRequest(msg.data, true)) {
4555
+ await msg.endpoint.commandResponse("genOta", "imageBlockResponse", { status: zigbee_herdsman_1.Zcl.Status.INVALID_IMAGE }, undefined, msg.meta.zclTransactionSequenceNumber);
4556
+ return;
4557
+ }
4558
+ markW600WeeklyScheduleUploadStarted(device, publish);
4559
+ const fileOffset = Number(msg.data.fileOffset);
4560
+ const maximumDataSize = Number(msg.data.maximumDataSize);
4561
+ if (!Number.isInteger(fileOffset) ||
4562
+ !Number.isInteger(maximumDataSize) ||
4563
+ fileOffset < 0 ||
4564
+ maximumDataSize <= 0 ||
4565
+ fileOffset >= stage.image.length) {
4566
+ failW600WeeklyScheduleUpload(device, `Received invalid image block request (offset=${String(msg.data.fileOffset)}, maximumDataSize=${String(msg.data.maximumDataSize)})`, publish);
4567
+ await msg.endpoint.commandResponse("genOta", "imageBlockResponse", { status: zigbee_herdsman_1.Zcl.Status.ABORT }, undefined, msg.meta.zclTransactionSequenceNumber);
4568
+ return;
4569
+ }
4570
+ const chunk = stage.image.subarray(fileOffset, Math.min(stage.image.length, fileOffset + maximumDataSize));
4571
+ noteW600WeeklyScheduleUploadBlock(device, publish);
4572
+ await msg.endpoint.commandResponse("genOta", "imageBlockResponse", {
4573
+ status: zigbee_herdsman_1.Zcl.Status.SUCCESS,
4574
+ manufacturerCode: exports.manufacturerCode,
4575
+ imageType: W600_WEEKLY_SCHEDULE_IMAGE_TYPE,
4576
+ fileVersion: W600_WEEKLY_SCHEDULE_FILE_VERSION,
4577
+ fileOffset,
4578
+ dataSize: chunk.length,
4579
+ data: chunk,
4580
+ }, undefined, msg.meta.zclTransactionSequenceNumber);
4581
+ },
4582
+ },
4583
+ {
4584
+ cluster: "genOta",
4585
+ type: ["commandUpgradeEndRequest"],
4586
+ convert: async (model, msg, publish, options, meta) => {
4587
+ const device = meta.device ?? msg.device;
4588
+ const stage = getActiveW600WeeklyScheduleOtaStage(device);
4589
+ if (!stage) {
4590
+ return;
4591
+ }
4592
+ if (!matchesW600WeeklyScheduleOtaRequest(msg.data, true)) {
4593
+ return;
4594
+ }
4595
+ const upgradeStatus = getNumericW600OtaRequestField(msg.data, "status");
4596
+ if (upgradeStatus != null && upgradeStatus !== 0) {
4597
+ failW600WeeklyScheduleUpload(device, `Device ended the OTA transfer with status ${String(msg.data.status)}`, publish);
4598
+ return;
4599
+ }
4600
+ await msg.endpoint.commandResponse("genOta", "upgradeEndResponse", {
4601
+ manufacturerCode: exports.manufacturerCode,
4602
+ imageType: W600_WEEKLY_SCHEDULE_IMAGE_TYPE,
4603
+ fileVersion: W600_WEEKLY_SCHEDULE_FILE_VERSION,
4604
+ currentTime: 0,
4605
+ upgradeTime: 0,
4606
+ }, undefined, msg.meta.zclTransactionSequenceNumber);
4607
+ completeW600WeeklyScheduleUpload(device, publish);
4608
+ },
4609
+ },
4610
+ ],
4611
+ toZigbee: [
4612
+ {
4613
+ key: [...W600_WEEKLY_SCHEDULE_DAY_PROPERTIES],
4614
+ convertSet: (entity, key, value, meta) => {
4615
+ (0, utils_1.assertEndpoint)(entity);
4616
+ const draft = getCachedW600WeeklyScheduleDraft(entity, meta);
4617
+ const transitions = parseW600ScheduleDayTransitions(value, key);
4618
+ draft[key] = formatW600ScheduleDayTransitions(transitions);
4619
+ globalStore.putValue(getW600DeviceStoreKey(entity), W600_WEEKLY_SCHEDULE_DRAFT_STORE_KEY, draft);
4620
+ return { state: { [key]: draft[key] === "" ? null : draft[key] } };
4621
+ },
4622
+ },
4623
+ {
4624
+ key: ["save_schedule"],
4625
+ convertSet: async (entity, key, value, meta) => {
4626
+ (0, utils_1.assertEndpoint)(entity);
4627
+ parseW600ScheduleTriggerValue(value, key);
4628
+ ensureNoActiveW600WeeklyScheduleUpload(entity);
4629
+ const draft = getCachedW600WeeklyScheduleDraft(entity, meta);
4630
+ for (const property of W600_WEEKLY_SCHEDULE_DAY_PROPERTIES) {
4631
+ if (meta.message?.[property] !== undefined) {
4632
+ draft[property] = meta.message[property];
4633
+ }
4634
+ }
4635
+ const normalized = normalizeW600WeeklyScheduleDraft(draft);
4636
+ const image = buildW600WeeklyScheduleImage(normalized.records);
4637
+ const uploadState = stageW600WeeklyScheduleUpload(entity, normalized.draft, image, "save_schedule", normalized.records.length, meta.publish);
4638
+ logger_1.logger.info(`Staged W600 save schedule upload for ${getW600DeviceStoreKey(entity)} with ${normalized.records.length} entries (${image.length} bytes)`, W600_NS);
4639
+ try {
4640
+ await entity.commandResponse("genOta", "imageNotify", { payloadType: 0, queryJitter: W600_WEEKLY_SCHEDULE_IMAGE_NOTIFY_QUERY_JITTER }, { sendPolicy: "immediate" });
4641
+ }
4642
+ catch (error) {
4643
+ const details = error instanceof Error ? error.message : String(error);
4644
+ failW600WeeklyScheduleUpload(entity, `Failed to send imageNotify: ${details}`, meta.publish);
4645
+ throw error;
4646
+ }
4647
+ return { state: { ...buildW600WeeklyScheduleStatePayload(normalized.draft), ...uploadState.state } };
4648
+ },
4649
+ },
4650
+ {
4651
+ key: ["clear_schedule"],
4652
+ convertSet: async (entity, key, value, meta) => {
4653
+ (0, utils_1.assertEndpoint)(entity);
4654
+ parseW600ScheduleTriggerValue(value, key);
4655
+ ensureNoActiveW600WeeklyScheduleUpload(entity);
4656
+ const draft = createEmptyW600WeeklyScheduleDraft();
4657
+ const image = buildW600WeeklyScheduleImage([]);
4658
+ const uploadState = stageW600WeeklyScheduleUpload(entity, draft, image, "clear_schedule", 0, meta.publish);
4659
+ logger_1.logger.info(`Staged W600 clear schedule upload for ${getW600DeviceStoreKey(entity)} (${image.length} bytes)`, W600_NS);
4660
+ try {
4661
+ await entity.commandResponse("genOta", "imageNotify", { payloadType: 0, queryJitter: W600_WEEKLY_SCHEDULE_IMAGE_NOTIFY_QUERY_JITTER }, { sendPolicy: "immediate" });
4662
+ }
4663
+ catch (error) {
4664
+ const details = error instanceof Error ? error.message : String(error);
4665
+ failW600WeeklyScheduleUpload(entity, `Failed to send imageNotify: ${details}`, meta.publish);
4666
+ throw error;
4667
+ }
4668
+ return { state: { ...buildW600WeeklyScheduleStatePayload(draft), ...uploadState.state } };
4669
+ },
4670
+ },
4671
+ ],
4672
+ onEvent,
4673
+ isModernExtend: true,
4674
+ };
4675
+ }
4676
+ function createW600PresetTemperatureTable() {
4677
+ return {
4678
+ exposes: W600_PRESET_TEMPERATURE_DEFINITIONS.map(({ property, label, description }) => e
4679
+ .numeric(property, ea.ALL)
4680
+ .withLabel(label)
4681
+ .withValueMin(5)
4682
+ .withValueMax(30)
4683
+ .withValueStep(0.5)
4684
+ .withUnit("°C")
4685
+ .withDescription(description)
4686
+ .withCategory("config")),
4687
+ fromZigbee: [
4688
+ {
4689
+ cluster: W600_LUMI_CLUSTER,
4690
+ type: ["attributeReport", "readResponse"],
3184
4691
  convert: (model, msg, publish, options, meta) => {
3185
- if (msg.data["1028"] === 0) {
3186
- // Try to read the position after the motor stops, the device occasionally report wrong data right after stopping
3187
- // Might need to add delay, seems to be working without one but needs more tests.
3188
- msg.device
3189
- .getEndpoint(1)
3190
- .read("genAnalogOutput", ["presentValue"])
3191
- .catch((error) => logger_1.logger.error(`Failed to read position '${msg.device.ieeeAddr}' (${error})`, NS));
4692
+ const value = msg.data[W600_ATTR_PRESET_TEMPERATURE_TABLE];
4693
+ if (!node_buffer_1.Buffer.isBuffer(value) || value.length === 0) {
4694
+ return;
4695
+ }
4696
+ const table = decodeW600PresetTemperatureTable(value);
4697
+ if (!table) {
4698
+ return;
3192
4699
  }
4700
+ const device = meta.device ?? msg.device;
4701
+ globalStore.putValue(getW600DeviceStoreKey(device), W600_PRESET_TABLE_STORE_KEY, table);
4702
+ const result = {};
4703
+ for (const [presetName, centiDegrees] of Object.entries(table)) {
4704
+ result[W600_PROPERTY_BY_PRESET_NAME[presetName]] = centiDegrees / 100;
4705
+ }
4706
+ return result;
3193
4707
  },
3194
- };
3195
- }
3196
- return { fromZigbee: [converter], isModernExtend: true };
3197
- },
3198
- };
3199
- exports.modernExtend = exports.lumiModernExtend;
4708
+ },
4709
+ ],
4710
+ toZigbee: [
4711
+ {
4712
+ key: W600_PRESET_TEMPERATURE_DEFINITIONS.map(({ property }) => property),
4713
+ convertSet: async (entity, key, value, meta) => {
4714
+ (0, utils_1.assertEndpoint)(entity);
4715
+ const presetName = W600_PRESET_NAME_BY_PROPERTY[key];
4716
+ const table = getCachedW600PresetTemperatureTable(entity, meta);
4717
+ if (!table) {
4718
+ throw new Error("Preset temperature table is unknown. Read the preset temperature properties first.");
4719
+ }
4720
+ table[presetName] = parseW600HalfDegreeTemperature(value, key, 5, 30);
4721
+ await writeW600LumiAttribute(entity, W600_ATTR_PRESET_TEMPERATURE_TABLE, encodeW600PresetTemperatureTable(table), zigbee_herdsman_1.Zcl.DataType.OCTET_STR);
4722
+ globalStore.putValue(getW600DeviceStoreKey(entity), W600_PRESET_TABLE_STORE_KEY, table);
4723
+ return { state: { [key]: table[presetName] / 100 } };
4724
+ },
4725
+ convertGet: async (entity) => {
4726
+ (0, utils_1.assertEndpoint)(entity);
4727
+ await readW600LumiAttribute(entity, W600_ATTR_PRESET_TEMPERATURE_TABLE);
4728
+ },
4729
+ },
4730
+ ],
4731
+ configure: [
4732
+ async (device) => {
4733
+ const endpoint = device.getEndpoint(1);
4734
+ await safeW600Read(endpoint, W600_LUMI_CLUSTER, [W600_ATTR_PRESET_TEMPERATURE_TABLE], { manufacturerCode: exports.manufacturerCode });
4735
+ },
4736
+ ],
4737
+ isModernExtend: true,
4738
+ };
4739
+ }
3200
4740
  const feederDaysLookup = {
3201
4741
  127: "everyday",
3202
4742
  31: "workdays",
@@ -4754,6 +6294,97 @@ exports.fromZigbee = {
4754
6294
  }
4755
6295
  },
4756
6296
  },
6297
+ lumi_toilet: {
6298
+ cluster: "manuSpecificLumi",
6299
+ type: ["attributeReport", "readResponse"],
6300
+ convert: (model, msg, publish, options, meta) => {
6301
+ const result = {};
6302
+ Object.entries(msg.data).forEach(([key, value]) => {
6303
+ switch (Number.parseInt(key, 10)) {
6304
+ case 0xfff1: {
6305
+ // @ts-expect-error ignore
6306
+ if (value.length < 8) {
6307
+ logger_1.logger.debug(`Cannot handle ${value}, frame too small`, "zhc:lumi:toilet");
6308
+ return;
6309
+ }
6310
+ // @ts-expect-error ignore
6311
+ const attr = value.slice(3, 7);
6312
+ // @ts-expect-error ignore
6313
+ const len = value.slice(7, 8).readUInt8();
6314
+ // @ts-expect-error ignore
6315
+ const val = value.slice(8, 8 + len);
6316
+ switch (attr.readInt32BE()) {
6317
+ case 0x04030055:
6318
+ result.lid_switch = val.readUInt8() === 1 ? "OPEN" : "CLOSE";
6319
+ break;
6320
+ case 0x04040055:
6321
+ result.seat_switch = val.readUInt8() === 1 ? "OPEN" : "CLOSE";
6322
+ break;
6323
+ case 0x04200055:
6324
+ result.night_light = val.readUInt8() === 1 ? "ON" : "OFF";
6325
+ break;
6326
+ case 0x0e2f0055:
6327
+ result.seat_temp = ["Off", "Temp_31C", "Temp_33C", "Temp_35C", "Temp_37C", "Temp_39C"][val.readUInt32BE()];
6328
+ break;
6329
+ case 0x0e300055:
6330
+ result.cleaning_mode = ["Stop", "Rear", "Rear_Moving", "Female", "Female_Moving", "Child"][val.readUInt32BE()];
6331
+ break;
6332
+ case 0x0e340055:
6333
+ result.nozzle_position = ["Back", "Slightly_Back", "Middle", "Slightly_Front", "Front"][val.readUInt32BE()];
6334
+ break;
6335
+ case 0x0e330055:
6336
+ result.water_pressure = ["Weak", "Slightly_Weak", "Middle", "Slightly_Strong", "Strong"][val.readUInt32BE()];
6337
+ break;
6338
+ case 0x0e320055:
6339
+ result.water_temp = ["Off", "Temp_31C", "Temp_33C", "Temp_35C", "Temp_37C", "Temp_39C"][val.readUInt32BE()];
6340
+ break;
6341
+ case 0x0e350055:
6342
+ result.dryer_temp = ["Off", "Normal", "Low", "Mid_Low", "Middle", "Mid_High", "High"][val.readUInt32BE()];
6343
+ break;
6344
+ case 0x0e270055:
6345
+ result.nozzle_clean = ["Off", "Auto", "Manual"][val.readUInt32BE()];
6346
+ break;
6347
+ case 0x03010055:
6348
+ result.occupancy_status = val.readUInt8() === 1;
6349
+ break;
6350
+ case 0x041a0055:
6351
+ result.foot_sensor_switch = val.readUInt8() === 1 ? "ON" : "OFF";
6352
+ break;
6353
+ case 0x041f0055:
6354
+ result.auto_flush_after_leave = val.readUInt8() === 0 ? "ON" : "OFF";
6355
+ break;
6356
+ case 0x04220055:
6357
+ result.beeper_switch = val.readUInt8() === 0 ? "ON" : "OFF";
6358
+ break;
6359
+ case 0x04240055:
6360
+ result.child_seat_mode = val.readUInt8() === 1 ? "ON" : "OFF";
6361
+ break;
6362
+ case 0x04250055:
6363
+ result.pre_mist_switch = val.readUInt8() === 1 ? "ON" : "OFF";
6364
+ break;
6365
+ case 0x04420055:
6366
+ result.auto_foam_on_sit = val.readUInt8() === 1 ? "ON" : "OFF";
6367
+ break;
6368
+ case 0x04430055:
6369
+ result.auto_foam_on_leave = val.readUInt8() === 1 ? "ON" : "OFF";
6370
+ break;
6371
+ default:
6372
+ logger_1.logger.debug(`Unknown attribute ${attr} = ${val}`, "zhc:lumi:toilet");
6373
+ }
6374
+ break;
6375
+ }
6376
+ case 0x00ff:
6377
+ case 0x0007:
6378
+ case 0x00f7:
6379
+ logger_1.logger.debug(`Unhandled key ${key} = ${value}`, "zhc:lumi:toilet");
6380
+ break;
6381
+ default:
6382
+ logger_1.logger.debug(`Unknown key ${key} = ${value}`, "zhc:lumi:toilet");
6383
+ }
6384
+ });
6385
+ return result;
6386
+ },
6387
+ },
4757
6388
  };
4758
6389
  exports.toZigbee = {
4759
6390
  // lumi generic
@@ -5143,7 +6774,9 @@ exports.toZigbee = {
5143
6774
  schedule: 0x027d,
5144
6775
  schedule_settings: 0x0276,
5145
6776
  };
5146
- await entity.read("manuSpecificLumi", [(0, utils_1.getFromLookup)(key, dict)], { manufacturerCode: exports.manufacturerCode });
6777
+ await entity.read("manuSpecificLumi", [(0, utils_1.getFromLookup)(key, dict)], {
6778
+ manufacturerCode: exports.manufacturerCode,
6779
+ });
5147
6780
  },
5148
6781
  },
5149
6782
  lumi_presence_region_upsert: {
@@ -5709,7 +7342,9 @@ exports.toZigbee = {
5709
7342
  },
5710
7343
  convertGet: async (entity, key, meta) => {
5711
7344
  const endpoint = meta.device.getEndpoint(1);
5712
- await endpoint.read("manuSpecificLumi", ["mode"], { manufacturerCode: manufacturerOptions.lumi.manufacturerCode });
7345
+ await endpoint.read("manuSpecificLumi", ["mode"], {
7346
+ manufacturerCode: manufacturerOptions.lumi.manufacturerCode,
7347
+ });
5713
7348
  },
5714
7349
  },
5715
7350
  lumi_vibration_sensitivity: {
@@ -6475,5 +8110,124 @@ exports.toZigbee = {
6475
8110
  return { state: { ...defaults, thermostat_mode: value } };
6476
8111
  },
6477
8112
  },
8113
+ lumi_toilet: {
8114
+ key: [
8115
+ "lid_switch",
8116
+ "seat_switch",
8117
+ "night_light",
8118
+ "seat_temp",
8119
+ "cleaning_mode",
8120
+ "nozzle_position",
8121
+ "water_pressure",
8122
+ "water_temp",
8123
+ "dryer_temp",
8124
+ "nozzle_clean",
8125
+ "stop_button",
8126
+ "flush_big",
8127
+ "flush_small",
8128
+ "foam_shield",
8129
+ "foot_sensor_switch",
8130
+ "auto_flush_after_leave",
8131
+ "beeper_switch",
8132
+ "child_seat_mode",
8133
+ "pre_mist_switch",
8134
+ "auto_foam_on_sit",
8135
+ "auto_foam_on_leave",
8136
+ ],
8137
+ convertSet: async (entity, key, value, meta) => {
8138
+ const sendAttr = async (attrCode, value, length) => {
8139
+ // @ts-expect-error ignore
8140
+ entity.sendSeq = ((entity.sendSeq || 0) + 1) % 256;
8141
+ // @ts-expect-error ignore
8142
+ const val = node_buffer_1.Buffer.from([0x00, 0x02, entity.sendSeq, 0, 0, 0, 0, 0]);
8143
+ // @ts-expect-error ignore
8144
+ entity.sendSeq += 1;
8145
+ val.writeInt32BE(attrCode, 3);
8146
+ val.writeUInt8(length, 7);
8147
+ let v = node_buffer_1.Buffer.alloc(length);
8148
+ switch (length) {
8149
+ case 1:
8150
+ v.writeUInt8(value);
8151
+ break;
8152
+ case 2:
8153
+ v.writeUInt16BE(value);
8154
+ break;
8155
+ case 4:
8156
+ v.writeUInt32BE(value);
8157
+ break;
8158
+ default:
8159
+ // @ts-expect-error ignore
8160
+ v = value;
8161
+ }
8162
+ await entity.write("manuSpecificLumi", { 65521: { value: node_buffer_1.Buffer.concat([val, v]), type: 0x41 } }, { manufacturerCode: exports.manufacturerCode });
8163
+ };
8164
+ switch (key) {
8165
+ case "lid_switch":
8166
+ await sendAttr(0x04030055, (0, utils_1.getFromLookup)(value, { CLOSE: 0, OPEN: 1 }), 1);
8167
+ break;
8168
+ case "seat_switch":
8169
+ await sendAttr(0x04040055, (0, utils_1.getFromLookup)(value, { CLOSE: 0, OPEN: 1 }), 1);
8170
+ break;
8171
+ case "night_light":
8172
+ await sendAttr(0x04200055, (0, utils_1.getFromLookup)(value, { OFF: 0, ON: 1 }), 1);
8173
+ break;
8174
+ case "seat_temp":
8175
+ await sendAttr(0x0e2f0055, (0, utils_1.getFromLookup)(value, { off: 0, temp_31c: 1, temp_33c: 2, temp_35c: 3, temp_37c: 4, temp_39c: 5 }), 4);
8176
+ break;
8177
+ case "cleaning_mode":
8178
+ await sendAttr(0x0e300055, (0, utils_1.getFromLookup)(value, { stop: 0, rear: 1, rear_moving: 2, female: 3, female_moving: 4, child: 5 }), 4);
8179
+ break;
8180
+ case "nozzle_position":
8181
+ await sendAttr(0x0e340055, (0, utils_1.getFromLookup)(value, { back: 0, slightly_back: 1, middle: 2, slightly_front: 3, front: 4 }), 4);
8182
+ break;
8183
+ case "water_pressure":
8184
+ await sendAttr(0x0e330055, (0, utils_1.getFromLookup)(value, { weak: 0, slightly_weak: 1, middle: 2, slightly_strong: 3, strong: 4 }), 4);
8185
+ break;
8186
+ case "water_temp":
8187
+ await sendAttr(0x0e320055, (0, utils_1.getFromLookup)(value, { off: 0, temp_31c: 1, temp_33c: 2, temp_35c: 3, temp_37c: 4, temp_39c: 5 }), 4);
8188
+ break;
8189
+ case "dryer_temp":
8190
+ await sendAttr(0x0e350055, (0, utils_1.getFromLookup)(value, { off: 0, normal: 1, low: 2, mid_low: 3, middle: 4, mid_high: 5, high: 6 }), 4);
8191
+ break;
8192
+ case "nozzle_clean":
8193
+ await sendAttr(0x0e270055, (0, utils_1.getFromLookup)(value, { off: 0, auto: 1, manual: 2 }), 4);
8194
+ break;
8195
+ case "stop_button":
8196
+ await sendAttr(0x04010055, 1, 1);
8197
+ break;
8198
+ case "flush_big":
8199
+ await sendAttr(0x04070055, 1, 1);
8200
+ break;
8201
+ case "flush_small":
8202
+ await sendAttr(0x04020055, 1, 1);
8203
+ break;
8204
+ case "foam_shield":
8205
+ await sendAttr(0x04190055, 0, 1);
8206
+ break;
8207
+ case "foot_sensor_switch":
8208
+ await sendAttr(0x041a0055, (0, utils_1.getFromLookup)(value, { OFF: 0, ON: 1 }), 1);
8209
+ break;
8210
+ case "auto_flush_after_leave":
8211
+ await sendAttr(0x041f0055, (0, utils_1.getFromLookup)(value, { ON: 0, OFF: 1 }), 1);
8212
+ break;
8213
+ case "beeper_switch":
8214
+ await sendAttr(0x04220055, (0, utils_1.getFromLookup)(value, { ON: 0, OFF: 1 }), 1);
8215
+ break;
8216
+ case "child_seat_mode":
8217
+ await sendAttr(0x04240055, (0, utils_1.getFromLookup)(value, { OFF: 0, ON: 1 }), 1);
8218
+ break;
8219
+ case "pre_mist_switch":
8220
+ await sendAttr(0x04250055, (0, utils_1.getFromLookup)(value, { OFF: 0, ON: 1 }), 1);
8221
+ break;
8222
+ case "auto_foam_on_sit":
8223
+ await sendAttr(0x04420055, (0, utils_1.getFromLookup)(value, { OFF: 0, ON: 1 }), 1);
8224
+ break;
8225
+ case "auto_foam_on_leave":
8226
+ await sendAttr(0x04430055, (0, utils_1.getFromLookup)(value, { OFF: 0, ON: 1 }), 1);
8227
+ break;
8228
+ }
8229
+ return { state: { [key]: value } };
8230
+ },
8231
+ },
6478
8232
  };
6479
8233
  //# sourceMappingURL=lumi.js.map