@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.
- package/CHANGELOG.md +1101 -0
- package/dist/converters/actions.d.ts.map +1 -1
- package/dist/converters/actions.js +2 -0
- package/dist/converters/actions.js.map +1 -1
- package/dist/converters/fromZigbee.d.ts +1 -150
- package/dist/converters/fromZigbee.d.ts.map +1 -1
- package/dist/converters/fromZigbee.js +183 -3044
- package/dist/converters/fromZigbee.js.map +1 -1
- package/dist/converters/toZigbee.d.ts +0 -121
- package/dist/converters/toZigbee.d.ts.map +1 -1
- package/dist/converters/toZigbee.js +289 -2386
- package/dist/converters/toZigbee.js.map +1 -1
- package/dist/devices/ITCommander.d.ts.map +1 -1
- package/dist/devices/ITCommander.js +2 -1
- package/dist/devices/ITCommander.js.map +1 -1
- package/dist/devices/acova.d.ts.map +1 -1
- package/dist/devices/acova.js +69 -2
- package/dist/devices/acova.js.map +1 -1
- package/dist/devices/adeo.d.ts.map +1 -1
- package/dist/devices/adeo.js +35 -3
- package/dist/devices/adeo.js.map +1 -1
- package/dist/devices/adurosmart.js +1 -1
- package/dist/devices/adurosmart.js.map +1 -1
- package/dist/devices/amina.d.ts.map +1 -1
- package/dist/devices/amina.js +47 -10
- package/dist/devices/amina.js.map +1 -1
- package/dist/devices/aurora_lighting.d.ts.map +1 -1
- package/dist/devices/aurora_lighting.js +15 -0
- package/dist/devices/aurora_lighting.js.map +1 -1
- package/dist/devices/automaton.d.ts.map +1 -1
- package/dist/devices/automaton.js +1 -0
- package/dist/devices/automaton.js.map +1 -1
- package/dist/devices/avatto.d.ts.map +1 -1
- package/dist/devices/avatto.js +29 -24
- package/dist/devices/avatto.js.map +1 -1
- package/dist/devices/awox.d.ts.map +1 -1
- package/dist/devices/awox.js +41 -3
- package/dist/devices/awox.js.map +1 -1
- package/dist/devices/bacchus.d.ts.map +1 -1
- package/dist/devices/bacchus.js +75 -0
- package/dist/devices/bacchus.js.map +1 -1
- package/dist/devices/bitron.d.ts.map +1 -1
- package/dist/devices/bitron.js +29 -2
- package/dist/devices/bitron.js.map +1 -1
- package/dist/devices/bosch.d.ts.map +1 -1
- package/dist/devices/bosch.js +70 -50
- package/dist/devices/bosch.js.map +1 -1
- package/dist/devices/bticino.d.ts.map +1 -1
- package/dist/devices/bticino.js +11 -1
- package/dist/devices/bticino.js.map +1 -1
- package/dist/devices/byun.d.ts.map +1 -1
- package/dist/devices/byun.js +40 -3
- package/dist/devices/byun.js.map +1 -1
- package/dist/devices/candeo.d.ts.map +1 -1
- package/dist/devices/candeo.js +262 -9
- package/dist/devices/candeo.js.map +1 -1
- package/dist/devices/centralite.d.ts +9 -0
- package/dist/devices/centralite.d.ts.map +1 -1
- package/dist/devices/centralite.js +44 -5
- package/dist/devices/centralite.js.map +1 -1
- package/dist/devices/{aubess.d.ts → cigol.d.ts} +1 -1
- package/dist/devices/cigol.d.ts.map +1 -0
- package/dist/devices/cigol.js +260 -0
- package/dist/devices/cigol.js.map +1 -0
- package/dist/devices/cleverio.js +2 -2
- package/dist/devices/cleverio.js.map +1 -1
- package/dist/devices/climax.d.ts.map +1 -1
- package/dist/devices/climax.js +15 -1
- package/dist/devices/climax.js.map +1 -1
- package/dist/devices/ctm.d.ts.map +1 -1
- package/dist/devices/ctm.js +845 -496
- package/dist/devices/ctm.js.map +1 -1
- package/dist/devices/custom_devices_diy.d.ts +183 -1
- package/dist/devices/custom_devices_diy.d.ts.map +1 -1
- package/dist/devices/custom_devices_diy.js +390 -33
- package/dist/devices/custom_devices_diy.js.map +1 -1
- package/dist/devices/daewoo.d.ts +3 -0
- package/dist/devices/daewoo.d.ts.map +1 -0
- package/dist/devices/daewoo.js +198 -0
- package/dist/devices/daewoo.js.map +1 -0
- package/dist/devices/danfoss.d.ts.map +1 -1
- package/dist/devices/danfoss.js +1270 -296
- package/dist/devices/danfoss.js.map +1 -1
- package/dist/devices/databyte.d.ts.map +1 -1
- package/dist/devices/databyte.js +47 -3
- package/dist/devices/databyte.js.map +1 -1
- package/dist/devices/datek.d.ts.map +1 -1
- package/dist/devices/datek.js +202 -41
- package/dist/devices/datek.js.map +1 -1
- package/dist/devices/dawon_dns.d.ts.map +1 -1
- package/dist/devices/dawon_dns.js +14 -1
- package/dist/devices/dawon_dns.js.map +1 -1
- package/dist/devices/develco.d.ts.map +1 -1
- package/dist/devices/develco.js +145 -64
- package/dist/devices/develco.js.map +1 -1
- package/dist/devices/diyruz.d.ts.map +1 -1
- package/dist/devices/diyruz.js +444 -20
- package/dist/devices/diyruz.js.map +1 -1
- package/dist/devices/domraem.d.ts.map +1 -1
- package/dist/devices/domraem.js +7 -0
- package/dist/devices/domraem.js.map +1 -1
- package/dist/devices/easyaccess.d.ts.map +1 -1
- package/dist/devices/easyaccess.js +22 -2
- package/dist/devices/easyaccess.js.map +1 -1
- package/dist/devices/easyiot.d.ts.map +1 -1
- package/dist/devices/easyiot.js +767 -5
- package/dist/devices/easyiot.js.map +1 -1
- package/dist/devices/echostar.d.ts.map +1 -1
- package/dist/devices/echostar.js +25 -1
- package/dist/devices/echostar.js.map +1 -1
- package/dist/devices/ecodim.d.ts.map +1 -1
- package/dist/devices/ecodim.js +8 -0
- package/dist/devices/ecodim.js.map +1 -1
- package/dist/devices/ecolink.d.ts.map +1 -1
- package/dist/devices/ecolink.js +12 -0
- package/dist/devices/ecolink.js.map +1 -1
- package/dist/devices/efekta.d.ts.map +1 -1
- package/dist/devices/efekta.js +1615 -60
- package/dist/devices/efekta.js.map +1 -1
- package/dist/devices/eglo.d.ts.map +1 -1
- package/dist/devices/eglo.js +203 -17
- package/dist/devices/eglo.js.map +1 -1
- package/dist/devices/elko.d.ts.map +1 -1
- package/dist/devices/elko.js +20 -22
- package/dist/devices/elko.js.map +1 -1
- package/dist/devices/engo.d.ts.map +1 -1
- package/dist/devices/engo.js +201 -0
- package/dist/devices/engo.js.map +1 -1
- package/dist/devices/enocean.d.ts.map +1 -1
- package/dist/devices/enocean.js +132 -4
- package/dist/devices/enocean.js.map +1 -1
- package/dist/devices/ensystec.d.ts +3 -0
- package/dist/devices/ensystec.d.ts.map +1 -0
- package/dist/devices/ensystec.js +1200 -0
- package/dist/devices/ensystec.js.map +1 -0
- package/dist/devices/eurotronic.d.ts +78 -1
- package/dist/devices/eurotronic.d.ts.map +1 -1
- package/dist/devices/eurotronic.js +244 -40
- package/dist/devices/eurotronic.js.map +1 -1
- package/dist/devices/ewelink.d.ts.map +1 -1
- package/dist/devices/ewelink.js +154 -2
- package/dist/devices/ewelink.js.map +1 -1
- package/dist/devices/fantem.js +1 -1
- package/dist/devices/fantem.js.map +1 -1
- package/dist/devices/fireangel.d.ts.map +1 -1
- package/dist/devices/fireangel.js +12 -1
- package/dist/devices/fireangel.js.map +1 -1
- package/dist/devices/frankever.d.ts.map +1 -1
- package/dist/devices/frankever.js +96 -0
- package/dist/devices/frankever.js.map +1 -1
- package/dist/devices/frient.d.ts.map +1 -1
- package/dist/devices/frient.js +15 -0
- package/dist/devices/frient.js.map +1 -1
- package/dist/devices/girier.js +1 -1
- package/dist/devices/girier.js.map +1 -1
- package/dist/devices/gledopto.js +1 -1
- package/dist/devices/gledopto.js.map +1 -1
- package/dist/devices/gmmts.d.ts.map +1 -1
- package/dist/devices/gmmts.js +59 -55
- package/dist/devices/gmmts.js.map +1 -1
- package/dist/devices/halo_smart_labs.d.ts +3 -0
- package/dist/devices/halo_smart_labs.d.ts.map +1 -0
- package/dist/devices/halo_smart_labs.js +918 -0
- package/dist/devices/halo_smart_labs.js.map +1 -0
- package/dist/devices/handshake_finland.d.ts +3 -0
- package/dist/devices/handshake_finland.d.ts.map +1 -0
- package/dist/devices/handshake_finland.js +15 -0
- package/dist/devices/handshake_finland.js.map +1 -0
- package/dist/devices/heiman.d.ts.map +1 -1
- package/dist/devices/heiman.js +1361 -147
- package/dist/devices/heiman.js.map +1 -1
- package/dist/devices/heimgard_technologies.d.ts.map +1 -1
- package/dist/devices/heimgard_technologies.js +13 -0
- package/dist/devices/heimgard_technologies.js.map +1 -1
- package/dist/devices/hive.d.ts.map +1 -1
- package/dist/devices/hive.js +11 -0
- package/dist/devices/hive.js.map +1 -1
- package/dist/devices/ikea.d.ts.map +1 -1
- package/dist/devices/ikea.js +185 -135
- package/dist/devices/ikea.js.map +1 -1
- package/dist/devices/iluminize.d.ts.map +1 -1
- package/dist/devices/iluminize.js +8 -7
- package/dist/devices/iluminize.js.map +1 -1
- package/dist/devices/immax.d.ts.map +1 -1
- package/dist/devices/immax.js +3 -1
- package/dist/devices/immax.js.map +1 -1
- package/dist/devices/index.d.ts.map +1 -1
- package/dist/devices/index.js +20 -2
- package/dist/devices/index.js.map +1 -1
- package/dist/devices/innr.d.ts.map +1 -1
- package/dist/devices/innr.js +34 -2
- package/dist/devices/innr.js.map +1 -1
- package/dist/devices/inovelli.d.ts.map +1 -1
- package/dist/devices/inovelli.js +41 -2348
- package/dist/devices/inovelli.js.map +1 -1
- package/dist/devices/iris.d.ts.map +1 -1
- package/dist/devices/iris.js +13 -1
- package/dist/devices/iris.js.map +1 -1
- package/dist/devices/javis.d.ts.map +1 -1
- package/dist/devices/javis.js +39 -1
- package/dist/devices/javis.js.map +1 -1
- package/dist/devices/jxuan.d.ts.map +1 -1
- package/dist/devices/jxuan.js +37 -2
- package/dist/devices/jxuan.js.map +1 -1
- package/dist/devices/kami.d.ts.map +1 -1
- package/dist/devices/kami.js +21 -2
- package/dist/devices/kami.js.map +1 -1
- package/dist/devices/keen_home.d.ts.map +1 -1
- package/dist/devices/keen_home.js +13 -3
- package/dist/devices/keen_home.js.map +1 -1
- package/dist/devices/klikaanklikuit.d.ts.map +1 -1
- package/dist/devices/klikaanklikuit.js +7 -0
- package/dist/devices/klikaanklikuit.js.map +1 -1
- package/dist/devices/kmpcil.d.ts.map +1 -1
- package/dist/devices/kmpcil.js +44 -5
- package/dist/devices/kmpcil.js.map +1 -1
- package/dist/devices/konke.d.ts.map +1 -1
- package/dist/devices/konke.js +10 -1
- package/dist/devices/konke.js.map +1 -1
- package/dist/devices/lds.d.ts.map +1 -1
- package/dist/devices/lds.js +7 -0
- package/dist/devices/lds.js.map +1 -1
- package/dist/devices/led_trading.d.ts.map +1 -1
- package/dist/devices/led_trading.js +13 -13
- package/dist/devices/led_trading.js.map +1 -1
- package/dist/devices/leedarson.d.ts.map +1 -1
- package/dist/devices/leedarson.js +140 -1
- package/dist/devices/leedarson.js.map +1 -1
- package/dist/devices/legrand.d.ts.map +1 -1
- package/dist/devices/legrand.js +73 -42
- package/dist/devices/legrand.js.map +1 -1
- package/dist/devices/lellki.d.ts.map +1 -1
- package/dist/devices/lellki.js +3 -1
- package/dist/devices/lellki.js.map +1 -1
- package/dist/devices/letv.d.ts.map +1 -1
- package/dist/devices/letv.js +16 -2
- package/dist/devices/letv.js.map +1 -1
- package/dist/devices/lidl.d.ts.map +1 -1
- package/dist/devices/lidl.js +26 -6
- package/dist/devices/lidl.js.map +1 -1
- package/dist/devices/lincukoo.d.ts.map +1 -1
- package/dist/devices/lincukoo.js +92 -31
- package/dist/devices/lincukoo.js.map +1 -1
- package/dist/devices/linkind.d.ts.map +1 -1
- package/dist/devices/linkind.js +14 -1
- package/dist/devices/linkind.js.map +1 -1
- package/dist/devices/linptech.d.ts.map +1 -1
- package/dist/devices/linptech.js +3 -1
- package/dist/devices/linptech.js.map +1 -1
- package/dist/devices/livingwise.d.ts.map +1 -1
- package/dist/devices/livingwise.js +2 -1
- package/dist/devices/livingwise.js.map +1 -1
- package/dist/devices/livolo.d.ts.map +1 -1
- package/dist/devices/livolo.js +612 -20
- package/dist/devices/livolo.js.map +1 -1
- package/dist/devices/lixee.d.ts.map +1 -1
- package/dist/devices/lixee.js +46 -44
- package/dist/devices/lixee.js.map +1 -1
- package/dist/devices/lonsonho.d.ts.map +1 -1
- package/dist/devices/lonsonho.js +18 -11
- package/dist/devices/lonsonho.js.map +1 -1
- package/dist/devices/lumi.d.ts.map +1 -1
- package/dist/devices/lumi.js +877 -155
- package/dist/devices/lumi.js.map +1 -1
- package/dist/devices/lytko.d.ts.map +1 -1
- package/dist/devices/lytko.js +205 -556
- package/dist/devices/lytko.js.map +1 -1
- package/dist/devices/makegood.d.ts.map +1 -1
- package/dist/devices/makegood.js +5 -1
- package/dist/devices/makegood.js.map +1 -1
- package/dist/devices/mazda.js +7 -7
- package/dist/devices/mazda.js.map +1 -1
- package/dist/devices/meazon.d.ts.map +1 -1
- package/dist/devices/meazon.js +82 -20
- package/dist/devices/meazon.js.map +1 -1
- package/dist/devices/megaman.d.ts +4 -0
- package/dist/devices/megaman.d.ts.map +1 -0
- package/dist/devices/megaman.js +48 -0
- package/dist/devices/megaman.js.map +1 -0
- package/dist/devices/mercator.d.ts.map +1 -1
- package/dist/devices/mercator.js +20 -5
- package/dist/devices/mercator.js.map +1 -1
- package/dist/devices/miboxer.d.ts.map +1 -1
- package/dist/devices/miboxer.js +3 -1
- package/dist/devices/miboxer.js.map +1 -1
- package/dist/devices/mill.d.ts.map +1 -1
- package/dist/devices/mill.js +33 -7
- package/dist/devices/mill.js.map +1 -1
- package/dist/devices/moes.d.ts.map +1 -1
- package/dist/devices/moes.js +89 -15
- package/dist/devices/moes.js.map +1 -1
- package/dist/devices/msh.d.ts +3 -0
- package/dist/devices/msh.d.ts.map +1 -0
- package/dist/devices/msh.js +93 -0
- package/dist/devices/msh.js.map +1 -0
- package/dist/devices/muller_licht.d.ts.map +1 -1
- package/dist/devices/muller_licht.js +11 -0
- package/dist/devices/muller_licht.js.map +1 -1
- package/dist/devices/multir.d.ts.map +1 -1
- package/dist/devices/multir.js +12 -1
- package/dist/devices/multir.js.map +1 -1
- package/dist/devices/multiterm.js +2 -2
- package/dist/devices/multiterm.js.map +1 -1
- package/dist/devices/namron.d.ts.map +1 -1
- package/dist/devices/namron.js +488 -128
- package/dist/devices/namron.js.map +1 -1
- package/dist/devices/neo.d.ts.map +1 -1
- package/dist/devices/neo.js +151 -192
- package/dist/devices/neo.js.map +1 -1
- package/dist/devices/netica.d.ts +3 -0
- package/dist/devices/netica.d.ts.map +1 -0
- package/dist/devices/netica.js +153 -0
- package/dist/devices/netica.js.map +1 -0
- package/dist/devices/niko.d.ts.map +1 -1
- package/dist/devices/niko.js +10 -8
- package/dist/devices/niko.js.map +1 -1
- package/dist/devices/nodon.d.ts.map +1 -1
- package/dist/devices/nodon.js +6 -4
- package/dist/devices/nodon.js.map +1 -1
- package/dist/devices/nous.d.ts.map +1 -1
- package/dist/devices/nous.js +160 -61
- package/dist/devices/nous.js.map +1 -1
- package/dist/devices/nue_3a.d.ts.map +1 -1
- package/dist/devices/nue_3a.js +2 -0
- package/dist/devices/nue_3a.js.map +1 -1
- package/dist/devices/onesti.d.ts +11 -1
- package/dist/devices/onesti.d.ts.map +1 -1
- package/dist/devices/onesti.js +30 -6
- package/dist/devices/onesti.js.map +1 -1
- package/dist/devices/onokom.d.ts.map +1 -1
- package/dist/devices/onokom.js +1152 -2309
- package/dist/devices/onokom.js.map +1 -1
- package/dist/devices/orvibo.d.ts +17 -1
- package/dist/devices/orvibo.d.ts.map +1 -1
- package/dist/devices/orvibo.js +71 -4
- package/dist/devices/orvibo.js.map +1 -1
- package/dist/devices/owon.d.ts.map +1 -1
- package/dist/devices/owon.js +382 -62
- package/dist/devices/owon.js.map +1 -1
- package/dist/devices/paul_neuhaus.d.ts.map +1 -1
- package/dist/devices/paul_neuhaus.js +9 -0
- package/dist/devices/paul_neuhaus.js.map +1 -1
- package/dist/devices/paulmann.js +2 -2
- package/dist/devices/paulmann.js.map +1 -1
- package/dist/devices/perenio.d.ts.map +1 -1
- package/dist/devices/perenio.js +57 -39
- package/dist/devices/perenio.js.map +1 -1
- package/dist/devices/philips.d.ts.map +1 -1
- package/dist/devices/philips.js +194 -53
- package/dist/devices/philips.js.map +1 -1
- package/dist/devices/plaid.d.ts.map +1 -1
- package/dist/devices/plaid.js +18 -1
- package/dist/devices/plaid.js.map +1 -1
- package/dist/devices/plugwise.d.ts.map +1 -1
- package/dist/devices/plugwise.js +307 -26
- package/dist/devices/plugwise.js.map +1 -1
- package/dist/devices/profalux.d.ts.map +1 -1
- package/dist/devices/profalux.js +25 -1
- package/dist/devices/profalux.js.map +1 -1
- package/dist/devices/pushok.d.ts.map +1 -1
- package/dist/devices/pushok.js +44 -0
- package/dist/devices/pushok.js.map +1 -1
- package/dist/devices/qa.d.ts.map +1 -1
- package/dist/devices/qa.js +80 -32
- package/dist/devices/qa.js.map +1 -1
- package/dist/devices/repenic_ltd.d.ts +3 -0
- package/dist/devices/repenic_ltd.d.ts.map +1 -0
- package/dist/devices/repenic_ltd.js +97 -0
- package/dist/devices/repenic_ltd.js.map +1 -0
- package/dist/devices/robb.d.ts.map +1 -1
- package/dist/devices/robb.js +13 -1
- package/dist/devices/robb.js.map +1 -1
- package/dist/devices/rtx.js +1 -1
- package/dist/devices/rtx.js.map +1 -1
- package/dist/devices/salus_controls.d.ts.map +1 -1
- package/dist/devices/salus_controls.js +44 -17
- package/dist/devices/salus_controls.js.map +1 -1
- package/dist/devices/samotech.js +2 -2
- package/dist/devices/samotech.js.map +1 -1
- package/dist/devices/sber.d.ts.map +1 -1
- package/dist/devices/sber.js +435 -26
- package/dist/devices/sber.js.map +1 -1
- package/dist/devices/schneider_electric.d.ts.map +1 -1
- package/dist/devices/schneider_electric.js +808 -252
- package/dist/devices/schneider_electric.js.map +1 -1
- package/dist/devices/securifi.d.ts.map +1 -1
- package/dist/devices/securifi.js +24 -1
- package/dist/devices/securifi.js.map +1 -1
- package/dist/devices/sengled.d.ts.map +1 -1
- package/dist/devices/sengled.js +5 -4
- package/dist/devices/sengled.js.map +1 -1
- package/dist/devices/shada.d.ts +3 -0
- package/dist/devices/shada.d.ts.map +1 -0
- package/dist/devices/{aubess.js → shada.js} +7 -20
- package/dist/devices/shada.js.map +1 -0
- package/dist/devices/shelly.d.ts.map +1 -1
- package/dist/devices/shelly.js +646 -61
- package/dist/devices/shelly.js.map +1 -1
- package/dist/devices/shinasystem.d.ts.map +1 -1
- package/dist/devices/shinasystem.js +75 -16
- package/dist/devices/shinasystem.js.map +1 -1
- package/dist/devices/siglis.d.ts.map +1 -1
- package/dist/devices/siglis.js +26 -0
- package/dist/devices/siglis.js.map +1 -1
- package/dist/devices/sinope.d.ts.map +1 -1
- package/dist/devices/sinope.js +163 -83
- package/dist/devices/sinope.js.map +1 -1
- package/dist/devices/slacky_diy.d.ts.map +1 -1
- package/dist/devices/slacky_diy.js +3007 -69
- package/dist/devices/slacky_diy.js.map +1 -1
- package/dist/devices/smarli.d.ts.map +1 -1
- package/dist/devices/smarli.js +4 -5
- package/dist/devices/smarli.js.map +1 -1
- package/dist/devices/smartthings.d.ts +25 -0
- package/dist/devices/smartthings.d.ts.map +1 -1
- package/dist/devices/smartthings.js +62 -11
- package/dist/devices/smartthings.js.map +1 -1
- package/dist/devices/somfy.d.ts.map +1 -1
- package/dist/devices/somfy.js +14 -1
- package/dist/devices/somfy.js.map +1 -1
- package/dist/devices/sonoff.d.ts +42 -1
- package/dist/devices/sonoff.d.ts.map +1 -1
- package/dist/devices/sonoff.js +5161 -2007
- package/dist/devices/sonoff.js.map +1 -1
- package/dist/devices/sprut.d.ts.map +1 -1
- package/dist/devices/sprut.js +5 -4
- package/dist/devices/sprut.js.map +1 -1
- package/dist/devices/stello.d.ts.map +1 -1
- package/dist/devices/stello.js +6 -24
- package/dist/devices/stello.js.map +1 -1
- package/dist/devices/stelpro.d.ts +51 -1
- package/dist/devices/stelpro.d.ts.map +1 -1
- package/dist/devices/stelpro.js +86 -23
- package/dist/devices/stelpro.js.map +1 -1
- package/dist/devices/sunricher.d.ts.map +1 -1
- package/dist/devices/sunricher.js +215 -29
- package/dist/devices/sunricher.js.map +1 -1
- package/dist/devices/tech.d.ts.map +1 -1
- package/dist/devices/tech.js +35 -35
- package/dist/devices/tech.js.map +1 -1
- package/dist/devices/terncy.d.ts.map +1 -1
- package/dist/devices/terncy.js +398 -2
- package/dist/devices/terncy.js.map +1 -1
- package/dist/devices/third_reality.d.ts +42 -1
- package/dist/devices/third_reality.d.ts.map +1 -1
- package/dist/devices/third_reality.js +521 -110
- package/dist/devices/third_reality.js.map +1 -1
- package/dist/devices/tuya.d.ts.map +1 -1
- package/dist/devices/tuya.js +3139 -814
- package/dist/devices/tuya.js.map +1 -1
- package/dist/devices/ubisys.d.ts.map +1 -1
- package/dist/devices/ubisys.js +11 -8
- package/dist/devices/ubisys.js.map +1 -1
- package/dist/devices/vesternet.d.ts.map +1 -1
- package/dist/devices/vesternet.js +7 -0
- package/dist/devices/vesternet.js.map +1 -1
- package/dist/devices/viessmann.d.ts.map +1 -1
- package/dist/devices/viessmann.js +107 -4
- package/dist/devices/viessmann.js.map +1 -1
- package/dist/devices/vsmart.d.ts.map +1 -1
- package/dist/devices/vsmart.js +11 -0
- package/dist/devices/vsmart.js.map +1 -1
- package/dist/devices/weiser.d.ts.map +1 -1
- package/dist/devices/weiser.js +6 -0
- package/dist/devices/weiser.js.map +1 -1
- package/dist/devices/wirenboard.d.ts.map +1 -1
- package/dist/devices/wirenboard.js +124 -21
- package/dist/devices/wirenboard.js.map +1 -1
- package/dist/devices/wmun.d.ts.map +1 -1
- package/dist/devices/wmun.js +3 -2
- package/dist/devices/wmun.js.map +1 -1
- package/dist/devices/woolley.d.ts.map +1 -1
- package/dist/devices/woolley.js +6 -9
- package/dist/devices/woolley.js.map +1 -1
- package/dist/devices/woox.js +2 -2
- package/dist/devices/woox.js.map +1 -1
- package/dist/devices/xyzroe.d.ts.map +1 -1
- package/dist/devices/xyzroe.js +16 -4
- package/dist/devices/xyzroe.js.map +1 -1
- package/dist/devices/yale.d.ts.map +1 -1
- package/dist/devices/yale.js +138 -40
- package/dist/devices/yale.js.map +1 -1
- package/dist/devices/yandex.d.ts.map +1 -1
- package/dist/devices/yandex.js +93 -11
- package/dist/devices/yandex.js.map +1 -1
- package/dist/devices/yokis.d.ts.map +1 -1
- package/dist/devices/yokis.js +122 -85
- package/dist/devices/yokis.js.map +1 -1
- package/dist/devices/zbeacon.d.ts.map +1 -1
- package/dist/devices/zbeacon.js +0 -7
- package/dist/devices/zbeacon.js.map +1 -1
- package/dist/devices/zemismart.d.ts.map +1 -1
- package/dist/devices/zemismart.js +148 -11
- package/dist/devices/zemismart.js.map +1 -1
- package/dist/devices/zigbeetlc.js +1 -1
- package/dist/devices/zigbeetlc.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +40 -35
- package/dist/index.js.map +1 -1
- package/dist/lib/bosch.d.ts.map +1 -1
- package/dist/lib/bosch.js +212 -42
- package/dist/lib/bosch.js.map +1 -1
- package/dist/lib/constants.d.ts +99 -17
- package/dist/lib/constants.d.ts.map +1 -1
- package/dist/lib/constants.js +15 -2
- package/dist/lib/constants.js.map +1 -1
- package/dist/lib/develco.d.ts +11 -0
- package/dist/lib/develco.d.ts.map +1 -1
- package/dist/lib/develco.js +78 -9
- package/dist/lib/develco.js.map +1 -1
- package/dist/lib/exposes.d.ts +11 -10
- package/dist/lib/exposes.d.ts.map +1 -1
- package/dist/lib/exposes.js +7 -48
- package/dist/lib/exposes.js.map +1 -1
- package/dist/lib/heiman.d.ts.map +1 -1
- package/dist/lib/heiman.js +34 -14
- package/dist/lib/heiman.js.map +1 -1
- package/dist/lib/ikea.d.ts +17 -0
- package/dist/lib/ikea.d.ts.map +1 -1
- package/dist/lib/ikea.js +46 -20
- package/dist/lib/ikea.js.map +1 -1
- package/dist/lib/inovelli.d.ts +86 -0
- package/dist/lib/inovelli.d.ts.map +1 -0
- package/dist/lib/inovelli.js +2580 -0
- package/dist/lib/inovelli.js.map +1 -0
- package/dist/lib/ledvance.d.ts +4 -4
- package/dist/lib/ledvance.d.ts.map +1 -1
- package/dist/lib/ledvance.js +23 -7
- package/dist/lib/ledvance.js.map +1 -1
- package/dist/lib/legacy.d.ts +21 -2
- package/dist/lib/legacy.d.ts.map +1 -1
- package/dist/lib/legacy.js +29 -2
- package/dist/lib/legacy.js.map +1 -1
- package/dist/lib/legrand.d.ts +65 -6
- package/dist/lib/legrand.d.ts.map +1 -1
- package/dist/lib/legrand.js +188 -9
- package/dist/lib/legrand.js.map +1 -1
- package/dist/lib/light.d.ts +1 -1
- package/dist/lib/light.d.ts.map +1 -1
- package/dist/lib/light.js +2 -2
- package/dist/lib/light.js.map +1 -1
- package/dist/lib/lumi.d.ts +88 -44
- package/dist/lib/lumi.d.ts.map +1 -1
- package/dist/lib/lumi.js +1813 -59
- package/dist/lib/lumi.js.map +1 -1
- package/dist/lib/modernExtend.d.ts +19 -14
- package/dist/lib/modernExtend.d.ts.map +1 -1
- package/dist/lib/modernExtend.js +37 -35
- package/dist/lib/modernExtend.js.map +1 -1
- package/dist/lib/namron.d.ts +131 -28
- package/dist/lib/namron.d.ts.map +1 -1
- package/dist/lib/namron.js +723 -42
- package/dist/lib/namron.js.map +1 -1
- package/dist/lib/nodon.d.ts.map +1 -1
- package/dist/lib/nodon.js +3 -1
- package/dist/lib/nodon.js.map +1 -1
- package/dist/lib/philips.d.ts +106 -2
- package/dist/lib/philips.d.ts.map +1 -1
- package/dist/lib/philips.js +945 -44
- package/dist/lib/philips.js.map +1 -1
- package/dist/lib/sonoff.d.ts +77 -10
- package/dist/lib/sonoff.d.ts.map +1 -1
- package/dist/lib/sonoff.js +165 -34
- package/dist/lib/sonoff.js.map +1 -1
- package/dist/lib/sunricher.d.ts +1 -1
- package/dist/lib/sunricher.d.ts.map +1 -1
- package/dist/lib/sunricher.js +6 -8
- package/dist/lib/sunricher.js.map +1 -1
- package/dist/lib/tuya.d.ts +558 -13
- package/dist/lib/tuya.d.ts.map +1 -1
- package/dist/lib/tuya.js +1518 -10
- package/dist/lib/tuya.js.map +1 -1
- package/dist/lib/types.d.ts +9 -6
- package/dist/lib/types.d.ts.map +1 -1
- package/dist/lib/ubisys.d.ts +1 -1
- package/dist/lib/ubisys.d.ts.map +1 -1
- package/dist/lib/ubisys.js +60 -5
- package/dist/lib/ubisys.js.map +1 -1
- package/dist/lib/utils.d.ts +5 -8
- package/dist/lib/utils.d.ts.map +1 -1
- package/dist/lib/utils.js +30 -17
- package/dist/lib/utils.js.map +1 -1
- package/dist/lib/zosung.d.ts +69 -6
- package/dist/lib/zosung.d.ts.map +1 -1
- package/dist/lib/zosung.js +113 -1
- package/dist/lib/zosung.js.map +1 -1
- package/dist/models-index.json +1 -1
- package/package.json +2 -2
- package/dist/devices/aubess.d.ts.map +0 -1
- 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
|
|
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
|
|
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"], {
|
|
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
|
-
|
|
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.
|
|
2668
|
-
const value = lookup[args.
|
|
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], {
|
|
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
|
-
|
|
3165
|
-
|
|
3166
|
-
|
|
3167
|
-
|
|
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
|
|
3170
|
-
|
|
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
|
-
|
|
3181
|
-
|
|
3182
|
-
|
|
3183
|
-
|
|
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
|
-
|
|
3186
|
-
|
|
3187
|
-
|
|
3188
|
-
|
|
3189
|
-
|
|
3190
|
-
|
|
3191
|
-
|
|
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
|
-
|
|
3197
|
-
|
|
3198
|
-
}
|
|
3199
|
-
|
|
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)], {
|
|
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"], {
|
|
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
|