@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/philips.js
CHANGED
|
@@ -33,7 +33,9 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
33
33
|
};
|
|
34
34
|
})();
|
|
35
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
-
exports.fz = exports.hueEffects = exports.gradientScenes = exports.tz = exports.m = exports.knownEffects = void 0;
|
|
36
|
+
exports.HueGradientStyle = exports.HueEffectType = exports.fz = exports.hueEffects = exports.gradientScenes = exports.tz = exports.m = exports.manuSpecificPhilips2Fz = exports.knownEffects = void 0;
|
|
37
|
+
exports.DecodeManuSpecificPhilips2 = DecodeManuSpecificPhilips2;
|
|
38
|
+
exports.EncodeManuSpecificPhilips2 = EncodeManuSpecificPhilips2;
|
|
37
39
|
exports.decodeGradientColors = decodeGradientColors;
|
|
38
40
|
exports.encodeGradientColors = encodeGradientColors;
|
|
39
41
|
const zigbee_herdsman_1 = require("@willieee802/zigbee-herdsman");
|
|
@@ -43,6 +45,7 @@ const reporting = __importStar(require("../lib/reporting"));
|
|
|
43
45
|
const libColor = __importStar(require("./color"));
|
|
44
46
|
const color_1 = require("./color");
|
|
45
47
|
const exposes = __importStar(require("./exposes"));
|
|
48
|
+
const light = __importStar(require("./light"));
|
|
46
49
|
const logger_1 = require("./logger");
|
|
47
50
|
const modernExtend = __importStar(require("./modernExtend"));
|
|
48
51
|
const globalStore = __importStar(require("./store"));
|
|
@@ -52,10 +55,16 @@ const NS = "zhc:philips";
|
|
|
52
55
|
const ea = exposes.access;
|
|
53
56
|
const e = exposes.presets;
|
|
54
57
|
const eNumeric = exposes.Numeric;
|
|
58
|
+
// Gradient color XY scaling constants per Bifrost spec.
|
|
59
|
+
// MAX_X = 0.7347: maximum X inside the visible light spectrum / Wide Gamut red X.
|
|
60
|
+
// MAX_Y = 0.8264: outer bound of the Wide Gamut Y axis.
|
|
61
|
+
// NOTE: many older implementations incorrectly use 0.8431 for MAX_Y.
|
|
62
|
+
const GRADIENT_COLORS_MAX_X = 0.7347;
|
|
63
|
+
const GRADIENT_COLORS_MAX_Y = 0.8264;
|
|
55
64
|
const encodeRGBToScaledGradient = (hex) => {
|
|
56
65
|
const xy = color_1.ColorRGB.fromHex(hex).toXY();
|
|
57
|
-
const x = (xy.x * 4095) /
|
|
58
|
-
const y = (xy.y * 4095) /
|
|
66
|
+
const x = (xy.x * 4095) / GRADIENT_COLORS_MAX_X;
|
|
67
|
+
const y = (xy.y * 4095) / GRADIENT_COLORS_MAX_Y;
|
|
59
68
|
const xx = Math.round(x).toString(16).padStart(3, "0");
|
|
60
69
|
const yy = Math.round(y).toString(16).padStart(3, "0");
|
|
61
70
|
return [xx[1], xx[2], yy[2], xx[0], yy[0], yy[1]].join("");
|
|
@@ -63,8 +72,8 @@ const encodeRGBToScaledGradient = (hex) => {
|
|
|
63
72
|
const decodeScaledGradientToRGB = (p) => {
|
|
64
73
|
const x = p[3] + p[0] + p[1];
|
|
65
74
|
const y = p[4] + p[5] + p[2];
|
|
66
|
-
const xx = Number(((Number.parseInt(x, 16) *
|
|
67
|
-
const yy = Number(((Number.parseInt(y, 16) *
|
|
75
|
+
const xx = Number(((Number.parseInt(x, 16) * GRADIENT_COLORS_MAX_X) / 4095).toFixed(4));
|
|
76
|
+
const yy = Number(((Number.parseInt(y, 16) * GRADIENT_COLORS_MAX_Y) / 4095).toFixed(4));
|
|
68
77
|
return new color_1.ColorXY(xx, yy).toRGB().toHEX();
|
|
69
78
|
};
|
|
70
79
|
const COLOR_MODE_GRADIENT = "4b01";
|
|
@@ -80,33 +89,137 @@ exports.knownEffects = {
|
|
|
80
89
|
"0a80": "sparkle",
|
|
81
90
|
"0b80": "opal",
|
|
82
91
|
"0c80": "glisten",
|
|
83
|
-
"0d80": "
|
|
84
|
-
"0e80": "
|
|
85
|
-
"0f80": "
|
|
86
|
-
"1080": "
|
|
92
|
+
"0d80": "sunset",
|
|
93
|
+
"0e80": "underwater",
|
|
94
|
+
"0f80": "cosmos",
|
|
95
|
+
"1080": "sunbeam",
|
|
96
|
+
"1180": "enchant",
|
|
87
97
|
};
|
|
98
|
+
const HUE_TAP_LOOKUP = {
|
|
99
|
+
34: "press_1",
|
|
100
|
+
16: "press_2",
|
|
101
|
+
17: "press_3",
|
|
102
|
+
18: "press_4",
|
|
103
|
+
// Actions below are never generated by a Hue Tap but by a PMT 215Z
|
|
104
|
+
// https://github.com/Koenkk/zigbee2mqtt/issues/18088
|
|
105
|
+
98: "press_3_and_4",
|
|
106
|
+
99: "release_3_and_4",
|
|
107
|
+
100: "press_1_and_2",
|
|
108
|
+
101: "release_1_and_2",
|
|
109
|
+
};
|
|
110
|
+
exports.manuSpecificPhilips2Fz = {
|
|
111
|
+
cluster: "manuSpecificPhilips2",
|
|
112
|
+
type: ["attributeReport", "readResponse"],
|
|
113
|
+
convert: (model, msg, publish, options, meta) => {
|
|
114
|
+
const retval = {};
|
|
115
|
+
if (msg.data.state !== undefined) {
|
|
116
|
+
// Publish the raw, unaltered state blob so advanced clients (e.g. Bifrost)
|
|
117
|
+
// can perform their own decoding without depending on z2m's interpretation.
|
|
118
|
+
retval["philips_raw"] = msg.data.state.toString("hex");
|
|
119
|
+
const decoded = DecodeManuSpecificPhilips2(msg.data.state);
|
|
120
|
+
logger_1.logger.debug(`Decoded manuSpecificPhilips2 state: ${JSON.stringify(decoded)}`, NS);
|
|
121
|
+
if (decoded.onOff !== undefined) {
|
|
122
|
+
retval["state"] = decoded.onOff ? "ON" : "OFF";
|
|
123
|
+
}
|
|
124
|
+
if (decoded.brightness !== undefined) {
|
|
125
|
+
retval["brightness"] = decoded.brightness;
|
|
126
|
+
}
|
|
127
|
+
if (decoded.colorMirek !== undefined) {
|
|
128
|
+
retval["color_temp"] = decoded.colorMirek;
|
|
129
|
+
retval["color_mode"] = "color_temp";
|
|
130
|
+
}
|
|
131
|
+
if (decoded.colorXY !== undefined) {
|
|
132
|
+
retval["color"] = decoded.colorXY.toObject();
|
|
133
|
+
retval["color_mode"] = "xy";
|
|
134
|
+
}
|
|
135
|
+
if (decoded.fadeSpeed !== undefined) {
|
|
136
|
+
retval["transition"] = decoded.fadeSpeed / 10;
|
|
137
|
+
}
|
|
138
|
+
if (decoded.effectType !== undefined) {
|
|
139
|
+
retval["effect"] = effectNames[decoded.effectType] ?? `unknown_0x${decoded.effectType.toString(16)}`;
|
|
140
|
+
}
|
|
141
|
+
if (decoded.effectSpeed !== undefined) {
|
|
142
|
+
retval["effect_speed"] = decoded.effectSpeed;
|
|
143
|
+
}
|
|
144
|
+
if (decoded.gradientColors !== undefined) {
|
|
145
|
+
// RGB hex for backward compat with z2m frontend and existing automations
|
|
146
|
+
retval["gradient"] = decoded.gradientColors.colors.map((c) => c.toRGB().toHEX());
|
|
147
|
+
// Lossless XY coordinates for clients that need device-independent color
|
|
148
|
+
retval["gradient_xy"] = decoded.gradientColors.colors.map((c) => c.toObject());
|
|
149
|
+
retval["gradient_style"] = gradientStyleNames[decoded.gradientColors.style] ?? "unknown";
|
|
150
|
+
}
|
|
151
|
+
if (decoded.gradientParams !== undefined) {
|
|
152
|
+
retval["gradient_scale"] = decoded.gradientParams.scale;
|
|
153
|
+
retval["gradient_offset"] = decoded.gradientParams.offset;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
return retval;
|
|
157
|
+
},
|
|
158
|
+
};
|
|
159
|
+
// Keys for Philips2-specific features not handled by standard light converters.
|
|
160
|
+
const philips2Keys = ["effect_speed", "gradient_scale", "gradient_offset", "gradient_style", "effect_color"];
|
|
88
161
|
const philipsModernExtend = {
|
|
162
|
+
addPhilipsGenBasicCluster: () => modernExtend.deviceAddCustomCluster("genBasic", {
|
|
163
|
+
name: "genBasic",
|
|
164
|
+
ID: zigbee_herdsman_1.Zcl.Clusters.genBasic.ID,
|
|
165
|
+
attributes: {
|
|
166
|
+
ledIndication: {
|
|
167
|
+
name: "ledIndication",
|
|
168
|
+
ID: 0x0033, // 51
|
|
169
|
+
type: zigbee_herdsman_1.Zcl.DataType.BOOLEAN,
|
|
170
|
+
manufacturerCode: zigbee_herdsman_1.Zcl.ManufacturerCode.SIGNIFY_NETHERLANDS_B_V,
|
|
171
|
+
write: true,
|
|
172
|
+
},
|
|
173
|
+
deviceMode: {
|
|
174
|
+
name: "deviceMode",
|
|
175
|
+
ID: 0x0034, // 52
|
|
176
|
+
type: zigbee_herdsman_1.Zcl.DataType.ENUM8,
|
|
177
|
+
manufacturerCode: zigbee_herdsman_1.Zcl.ManufacturerCode.SIGNIFY_NETHERLANDS_B_V,
|
|
178
|
+
write: true,
|
|
179
|
+
},
|
|
180
|
+
},
|
|
181
|
+
commands: {},
|
|
182
|
+
commandsResponse: {},
|
|
183
|
+
}),
|
|
184
|
+
addPhilipsMsOccupancySensingCluster: () => modernExtend.deviceAddCustomCluster("msOccupancySensing", {
|
|
185
|
+
name: "msOccupancySensing",
|
|
186
|
+
ID: zigbee_herdsman_1.Zcl.Clusters.msOccupancySensing.ID,
|
|
187
|
+
attributes: {
|
|
188
|
+
motionSensitivity: {
|
|
189
|
+
name: "motionSensitivity",
|
|
190
|
+
ID: 0x0030,
|
|
191
|
+
type: zigbee_herdsman_1.Zcl.DataType.UINT8,
|
|
192
|
+
manufacturerCode: zigbee_herdsman_1.Zcl.ManufacturerCode.SIGNIFY_NETHERLANDS_B_V,
|
|
193
|
+
write: true,
|
|
194
|
+
},
|
|
195
|
+
},
|
|
196
|
+
commands: {},
|
|
197
|
+
commandsResponse: {},
|
|
198
|
+
}),
|
|
89
199
|
addCustomClusterManuSpecificPhilipsContact: () => modernExtend.deviceAddCustomCluster("manuSpecificPhilipsContact", {
|
|
200
|
+
name: "manuSpecificPhilipsContact",
|
|
90
201
|
ID: 0xfc06,
|
|
91
202
|
manufacturerCode: zigbee_herdsman_1.Zcl.ManufacturerCode.SIGNIFY_NETHERLANDS_B_V,
|
|
92
203
|
attributes: {
|
|
93
|
-
contact: { ID: 0x0100, type: zigbee_herdsman_1.Zcl.DataType.ENUM8, write: true, max: 0xff },
|
|
94
|
-
contactLastChange: { ID: 0x0101, type: zigbee_herdsman_1.Zcl.DataType.UINT32, write: true, max: 0xffffffff },
|
|
95
|
-
tamper: { ID: 0x0102, type: zigbee_herdsman_1.Zcl.DataType.ENUM8, write: true, max: 0xff },
|
|
96
|
-
tamperLastChange: { ID: 0x0103, type: zigbee_herdsman_1.Zcl.DataType.UINT32, write: true, max: 0xffffffff },
|
|
204
|
+
contact: { name: "contact", ID: 0x0100, type: zigbee_herdsman_1.Zcl.DataType.ENUM8, write: true, max: 0xff },
|
|
205
|
+
contactLastChange: { name: "contactLastChange", ID: 0x0101, type: zigbee_herdsman_1.Zcl.DataType.UINT32, write: true, max: 0xffffffff },
|
|
206
|
+
tamper: { name: "tamper", ID: 0x0102, type: zigbee_herdsman_1.Zcl.DataType.ENUM8, write: true, max: 0xff },
|
|
207
|
+
tamperLastChange: { name: "tamperLastChange", ID: 0x0103, type: zigbee_herdsman_1.Zcl.DataType.UINT32, write: true, max: 0xffffffff },
|
|
97
208
|
},
|
|
98
209
|
commands: {},
|
|
99
210
|
commandsResponse: {},
|
|
100
211
|
}),
|
|
101
212
|
addManuSpecificPhilipsCluster: () => modernExtend.deviceAddCustomCluster("manuSpecificPhilips", {
|
|
213
|
+
name: "manuSpecificPhilips",
|
|
102
214
|
ID: 0xfc00,
|
|
103
215
|
manufacturerCode: zigbee_herdsman_1.Zcl.ManufacturerCode.SIGNIFY_NETHERLANDS_B_V,
|
|
104
216
|
attributes: {
|
|
105
|
-
config: { ID: 0x0031, type: zigbee_herdsman_1.Zcl.DataType.BITMAP16, write: true },
|
|
217
|
+
config: { name: "config", ID: 0x0031, type: zigbee_herdsman_1.Zcl.DataType.BITMAP16, write: true },
|
|
106
218
|
},
|
|
107
219
|
commands: {},
|
|
108
220
|
commandsResponse: {
|
|
109
221
|
hueNotification: {
|
|
222
|
+
name: "hueNotification",
|
|
110
223
|
ID: 0x00,
|
|
111
224
|
parameters: [
|
|
112
225
|
{ name: "button", type: zigbee_herdsman_1.Zcl.DataType.UINT8, max: 0xff },
|
|
@@ -120,26 +233,28 @@ const philipsModernExtend = {
|
|
|
120
233
|
},
|
|
121
234
|
}),
|
|
122
235
|
addManuSpecificPhilips2Cluster: () => modernExtend.deviceAddCustomCluster("manuSpecificPhilips2", {
|
|
236
|
+
name: "manuSpecificPhilips2",
|
|
123
237
|
ID: 0xfc03,
|
|
124
238
|
manufacturerCode: zigbee_herdsman_1.Zcl.ManufacturerCode.SIGNIFY_NETHERLANDS_B_V,
|
|
125
239
|
attributes: {
|
|
126
|
-
state: { ID: 0x0002, type: zigbee_herdsman_1.Zcl.DataType.OCTET_STR, write: true },
|
|
240
|
+
state: { name: "state", ID: 0x0002, type: zigbee_herdsman_1.Zcl.DataType.OCTET_STR, write: true },
|
|
127
241
|
},
|
|
128
242
|
commands: {
|
|
129
|
-
multiColor: { ID: 0x00, parameters: [{ name: "data", type: zigbee_herdsman_1.Zcl.BuffaloZclDataType.BUFFER }] },
|
|
243
|
+
multiColor: { name: "multiColor", ID: 0x00, parameters: [{ name: "data", type: zigbee_herdsman_1.Zcl.BuffaloZclDataType.BUFFER }] },
|
|
130
244
|
},
|
|
131
245
|
commandsResponse: {},
|
|
132
246
|
}),
|
|
133
247
|
addManuSpecificPhilips3Cluster: () => modernExtend.deviceAddCustomCluster("manuSpecificPhilips3", {
|
|
248
|
+
name: "manuSpecificPhilips3",
|
|
134
249
|
ID: 0xfc01,
|
|
135
250
|
manufacturerCode: zigbee_herdsman_1.Zcl.ManufacturerCode.SIGNIFY_NETHERLANDS_B_V,
|
|
136
251
|
attributes: {},
|
|
137
252
|
commands: {
|
|
138
|
-
command1: { ID: 1, parameters: [{ name: "data", type: zigbee_herdsman_1.Zcl.BuffaloZclDataType.BUFFER }] },
|
|
139
|
-
command2: { ID: 2, parameters: [{ name: "data", type: zigbee_herdsman_1.Zcl.BuffaloZclDataType.BUFFER }] },
|
|
140
|
-
command3: { ID: 3, parameters: [{ name: "data", type: zigbee_herdsman_1.Zcl.BuffaloZclDataType.BUFFER }] },
|
|
141
|
-
command4: { ID: 4, parameters: [{ name: "data", type: zigbee_herdsman_1.Zcl.BuffaloZclDataType.BUFFER }] },
|
|
142
|
-
command7: { ID: 7, parameters: [{ name: "data", type: zigbee_herdsman_1.Zcl.BuffaloZclDataType.BUFFER }] },
|
|
253
|
+
command1: { name: "command1", ID: 1, parameters: [{ name: "data", type: zigbee_herdsman_1.Zcl.BuffaloZclDataType.BUFFER }] },
|
|
254
|
+
command2: { name: "command2", ID: 2, parameters: [{ name: "data", type: zigbee_herdsman_1.Zcl.BuffaloZclDataType.BUFFER }] },
|
|
255
|
+
command3: { name: "command3", ID: 3, parameters: [{ name: "data", type: zigbee_herdsman_1.Zcl.BuffaloZclDataType.BUFFER }] },
|
|
256
|
+
command4: { name: "command4", ID: 4, parameters: [{ name: "data", type: zigbee_herdsman_1.Zcl.BuffaloZclDataType.BUFFER }] },
|
|
257
|
+
command7: { name: "command7", ID: 7, parameters: [{ name: "data", type: zigbee_herdsman_1.Zcl.BuffaloZclDataType.BUFFER }] },
|
|
143
258
|
},
|
|
144
259
|
commandsResponse: {},
|
|
145
260
|
}),
|
|
@@ -148,9 +263,342 @@ const philipsModernExtend = {
|
|
|
148
263
|
if (args.hueEffect || args.gradient)
|
|
149
264
|
args.effect = false;
|
|
150
265
|
if (args.color)
|
|
151
|
-
args.color = { modes: ["xy", "hs"], ...((0, utils_1.isObject)(args.color) ? args.color : {}) };
|
|
266
|
+
args.color = { modes: ["xy", "hs"], enhancedHue: true, ...((0, utils_1.isObject)(args.color) ? args.color : {}) };
|
|
152
267
|
const result = modernExtend.light(args);
|
|
153
|
-
result.toZigbee
|
|
268
|
+
const toZigbee = result.toZigbee;
|
|
269
|
+
result.toZigbee = [];
|
|
270
|
+
// Keys we intercept for Hue native control: core light attributes
|
|
271
|
+
// that can be sent atomically via the manuSpecificPhilips2 cluster.
|
|
272
|
+
// Command-only keys (brightness_move, hue_step, etc.) have no Philips2
|
|
273
|
+
// equivalent and are left to their standard converters in the array.
|
|
274
|
+
const nativeKeys = ["state", "brightness", "brightness_percent", "color", "color_temp", "color_temp_percent", "transition"];
|
|
275
|
+
const keys = [...nativeKeys, ...philips2Keys];
|
|
276
|
+
const philipsLightTz = {
|
|
277
|
+
key: keys,
|
|
278
|
+
convertSet: async (entity, key, value, meta) => {
|
|
279
|
+
// Resolve control mode: explicit option wins; otherwise default to standard converters.
|
|
280
|
+
const nativeControl = meta.options.hue_native_control === true;
|
|
281
|
+
const { options } = meta;
|
|
282
|
+
// Check if device supports the manuSpecificPhilips2 cluster.
|
|
283
|
+
// Wrapped in try-catch because supportsInputCluster may throw for
|
|
284
|
+
// custom clusters not in the cluster registry (e.g. in test mocks).
|
|
285
|
+
let hasPhilips2Cluster = false;
|
|
286
|
+
if (utils.isEndpoint(entity)) {
|
|
287
|
+
try {
|
|
288
|
+
hasPhilips2Cluster = entity.supportsInputCluster("manuSpecificPhilips2");
|
|
289
|
+
}
|
|
290
|
+
catch {
|
|
291
|
+
hasPhilips2Cluster = false;
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
// Delegate to standard converters if:
|
|
295
|
+
// - Device doesn't support manuSpecificPhilips2 cluster (old bulbs), OR
|
|
296
|
+
// - User hasn't opted into native Philips2 control (default)
|
|
297
|
+
// This mimics Z2M's own per-key dispatch so a single message routes
|
|
298
|
+
// each key to the appropriate converter.
|
|
299
|
+
const mustDelegate = (utils.isEndpoint(entity) && !hasPhilips2Cluster) || !nativeControl;
|
|
300
|
+
if (mustDelegate) {
|
|
301
|
+
const used = new Set();
|
|
302
|
+
let mergedState = {};
|
|
303
|
+
// Replicate Z2M's key ordering (publish.ts): when turning off,
|
|
304
|
+
// state/brightness come first; otherwise they come last. This ensures
|
|
305
|
+
// standard converters are called in the same sequence as without our
|
|
306
|
+
// intercepting converter, so e.g. the light turns on before color is
|
|
307
|
+
// set, and color is set before the light turns off.
|
|
308
|
+
const messageEntries = Object.entries(meta.message);
|
|
309
|
+
const stateValue = typeof meta.message.state === "string" ? meta.message.state.toLowerCase() : undefined;
|
|
310
|
+
const sorter = stateValue === "off" ? 1 : -1;
|
|
311
|
+
messageEntries.sort((a) => (["state", "brightness", "brightness_percent"].includes(a[0]) ? sorter : sorter * -1));
|
|
312
|
+
for (const [msgKey, msgValue] of messageEntries) {
|
|
313
|
+
// Only delegate keys we claim. Command-only keys (brightness_move,
|
|
314
|
+
// hue_step, etc.) are NOT in our key list — Z2M routes them to
|
|
315
|
+
// their standard converters directly.
|
|
316
|
+
if (!keys.includes(msgKey))
|
|
317
|
+
continue;
|
|
318
|
+
// philips2Keys have no standard equivalent and are handled below.
|
|
319
|
+
if (philips2Keys.includes(msgKey))
|
|
320
|
+
continue;
|
|
321
|
+
for (const tz of toZigbee) {
|
|
322
|
+
if (!used.has(tz) && tz.key.includes(msgKey) && tz.convertSet) {
|
|
323
|
+
used.add(tz);
|
|
324
|
+
const result = await tz.convertSet(entity, msgKey, msgValue, meta);
|
|
325
|
+
if (result && "state" in result && result.state) {
|
|
326
|
+
mergedState = { ...mergedState, ...result.state };
|
|
327
|
+
}
|
|
328
|
+
break;
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
// In delegated mode, we still need to handle Philips2-specific keys (effect_color,
|
|
333
|
+
// effect_speed, gradient_scale, etc.) below. But if the current call is for a
|
|
334
|
+
// standard key and no Philips2-specific keys are in the message, we're done.
|
|
335
|
+
const hasPhilips2Keys = Object.keys(meta.message).some((k) => philips2Keys.includes(k));
|
|
336
|
+
if (!hasPhilips2Keys) {
|
|
337
|
+
return Object.keys(mergedState).length > 0 ? { state: mergedState } : undefined;
|
|
338
|
+
}
|
|
339
|
+
// Fall through: continue below to handle Philips2-specific fields only.
|
|
340
|
+
}
|
|
341
|
+
// Native control mode (or handling Philips2-specific keys in delegate mode):
|
|
342
|
+
// build a Philips2 payload from the message. In delegate mode, we filter out
|
|
343
|
+
// standard keys since the standard converters already sent them.
|
|
344
|
+
const message = nativeControl
|
|
345
|
+
? meta.message
|
|
346
|
+
: Object.fromEntries(Object.entries(meta.message).filter(([k]) => philips2Keys.includes(k)));
|
|
347
|
+
const newState = {};
|
|
348
|
+
const data = {};
|
|
349
|
+
if (message.state !== undefined && typeof message.state === "string") {
|
|
350
|
+
const msgState = message.state.toLowerCase();
|
|
351
|
+
if (["on", "off", "toggle"].includes(msgState) === true) {
|
|
352
|
+
const targetState = msgState === "toggle" ? (meta.state.state === "ON" ? "off" : "on") : msgState;
|
|
353
|
+
data.onOff = targetState === "on";
|
|
354
|
+
newState.state = data.onOff ? "ON" : "OFF";
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
if (message.brightness != null) {
|
|
358
|
+
// Bifrost spec: brightness values 0 and 255 are INVALID, valid range 1..254
|
|
359
|
+
data.brightness = clamp(Number(message.brightness), 1, 254);
|
|
360
|
+
}
|
|
361
|
+
else if (message.brightness_percent != null) {
|
|
362
|
+
data.brightness = clamp(utils.mapNumberRange(Number(message.brightness_percent), 0, 100, 0, 255), 1, 254);
|
|
363
|
+
}
|
|
364
|
+
if (data.brightness !== undefined) {
|
|
365
|
+
newState.brightness = data.brightness;
|
|
366
|
+
}
|
|
367
|
+
if (message.color != null) {
|
|
368
|
+
const newColor = libColor.Color.fromConverterArg(message.color);
|
|
369
|
+
if (newColor.isHSV()) {
|
|
370
|
+
// Convert HSV → RGB → XY instead of silently dropping
|
|
371
|
+
const xy = newColor.hsv.toRGB().gammaCorrected().toXY().rounded(4);
|
|
372
|
+
data.colorXY = xy;
|
|
373
|
+
}
|
|
374
|
+
else {
|
|
375
|
+
const xy = newColor.isRGB() ? newColor.rgb.gammaCorrected().toXY().rounded(4) : newColor.xy;
|
|
376
|
+
data.colorXY = xy;
|
|
377
|
+
}
|
|
378
|
+
newState.color_mode = "xy";
|
|
379
|
+
newState.color = data.colorXY.toObject();
|
|
380
|
+
}
|
|
381
|
+
if (message.color_temp != null || message.color_temp_percent != null) {
|
|
382
|
+
const [colorTempMin, colorTempMax] = light.findColorTempRange(entity);
|
|
383
|
+
const preset = { warmest: colorTempMax, warm: 454, neutral: 370, cool: 250, coolest: colorTempMin };
|
|
384
|
+
let colorTemp = message.color_temp;
|
|
385
|
+
if (message.color_temp_percent != null) {
|
|
386
|
+
colorTemp = utils
|
|
387
|
+
.mapNumberRange(Number(message.color_temp_percent), 0, 100, colorTempMin != null ? colorTempMin : 154, colorTempMax != null ? colorTempMax : 500)
|
|
388
|
+
.toString();
|
|
389
|
+
}
|
|
390
|
+
if (utils.isString(colorTemp) && colorTemp in preset) {
|
|
391
|
+
data.colorMirek = utils.getFromLookup(colorTemp, preset);
|
|
392
|
+
}
|
|
393
|
+
else {
|
|
394
|
+
data.colorMirek = Number(colorTemp);
|
|
395
|
+
}
|
|
396
|
+
newState.color_mode = "color_temp";
|
|
397
|
+
newState.color_temp = data.colorMirek;
|
|
398
|
+
}
|
|
399
|
+
// Map transition time to Philips2 fadeSpeed
|
|
400
|
+
// Bifrost spec: 0 = instant, practical range ~2..8, >0x100 = very slow
|
|
401
|
+
if (message.transition != null) {
|
|
402
|
+
data.fadeSpeed = Math.round(Number(message.transition) * 10);
|
|
403
|
+
}
|
|
404
|
+
else if (options.transition != null && options.transition !== "") {
|
|
405
|
+
const transition = (0, utils_1.toNumber)(options.transition, "transition");
|
|
406
|
+
data.fadeSpeed = Math.round(Number(transition) * 10);
|
|
407
|
+
}
|
|
408
|
+
// Effect speed: 0.0 = slowest, 1.0 = fastest (maps to 0..255 byte)
|
|
409
|
+
if (message.effect_speed != null) {
|
|
410
|
+
data.effectSpeed = clamp(Number(message.effect_speed), 0, 1);
|
|
411
|
+
}
|
|
412
|
+
// Gradient scale/offset: fixed-point 5.3 format, exposed as float
|
|
413
|
+
if (message.gradient_scale != null) {
|
|
414
|
+
if (data.gradientParams === undefined) {
|
|
415
|
+
data.gradientParams = { scale: Number(message.gradient_scale), offset: 0 };
|
|
416
|
+
}
|
|
417
|
+
else {
|
|
418
|
+
data.gradientParams.scale = Number(message.gradient_scale);
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
if (message.gradient_offset != null) {
|
|
422
|
+
if (data.gradientParams === undefined) {
|
|
423
|
+
data.gradientParams = { scale: 1.0, offset: Number(message.gradient_offset) };
|
|
424
|
+
}
|
|
425
|
+
else {
|
|
426
|
+
data.gradientParams.offset = Number(message.gradient_offset);
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
// Gradient style: stored in state for use with gradient color commands.
|
|
430
|
+
// When gradient colors are also being sent through this path, the style
|
|
431
|
+
// is applied directly to data.gradientColors.style.
|
|
432
|
+
if (message.gradient_style != null) {
|
|
433
|
+
const styleLookup = {
|
|
434
|
+
linear: HueGradientStyle.Linear,
|
|
435
|
+
scattered: HueGradientStyle.Scattered,
|
|
436
|
+
mirrored: HueGradientStyle.Mirrored,
|
|
437
|
+
};
|
|
438
|
+
const style = styleLookup[String(message.gradient_style).toLowerCase()];
|
|
439
|
+
if (style !== undefined) {
|
|
440
|
+
if (data.gradientColors !== undefined) {
|
|
441
|
+
data.gradientColors.style = style;
|
|
442
|
+
}
|
|
443
|
+
newState.gradient_style = message.gradient_style;
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
// When color/color_temp changes without an explicit effect command,
|
|
447
|
+
// behavior depends on the effect_color_mode option:
|
|
448
|
+
// - "stop" (default, matches Hue app): color change stops the effect
|
|
449
|
+
// - "update": color change re-sends the active effect with the new color
|
|
450
|
+
if ((data.colorXY !== undefined || data.colorMirek !== undefined) && message.effect === undefined) {
|
|
451
|
+
const effectColorMode = meta.options.effect_color_mode ?? "stop";
|
|
452
|
+
const activeEffect = meta.state?.effect;
|
|
453
|
+
if (effectColorMode === "update" && activeEffect && activeEffect !== "none" && activeEffect in effectLookupAll) {
|
|
454
|
+
// Re-send the active effect with the new color
|
|
455
|
+
data.effectType = effectLookupAll[activeEffect];
|
|
456
|
+
newState.effect = activeEffect;
|
|
457
|
+
}
|
|
458
|
+
else {
|
|
459
|
+
// Hue app behavior: color change stops effect, clear stale state
|
|
460
|
+
newState.effect = "none";
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
// Handle effect_color: explicitly set the active effect's base color
|
|
464
|
+
// without stopping it. If no effect is active, just sets the color.
|
|
465
|
+
if (message.effect_color != null) {
|
|
466
|
+
const newColor = libColor.Color.fromConverterArg(message.effect_color);
|
|
467
|
+
if (newColor.isHSV()) {
|
|
468
|
+
data.colorXY = newColor.hsv.toRGB().gammaCorrected().toXY().rounded(4);
|
|
469
|
+
}
|
|
470
|
+
else {
|
|
471
|
+
data.colorXY = newColor.isRGB() ? newColor.rgb.gammaCorrected().toXY().rounded(4) : newColor.xy;
|
|
472
|
+
}
|
|
473
|
+
newState.color_mode = "xy";
|
|
474
|
+
newState.color = data.colorXY.toObject();
|
|
475
|
+
// Re-send the active effect with the new color
|
|
476
|
+
const activeEffect = meta.state?.effect;
|
|
477
|
+
if (activeEffect && activeEffect !== "none" && activeEffect in effectLookupAll) {
|
|
478
|
+
data.effectType = effectLookupAll[activeEffect];
|
|
479
|
+
newState.effect = activeEffect;
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
// When re-sending an effect (via effect_color or "update" mode),
|
|
483
|
+
// the effect resets brightness on activation. To preserve the user's
|
|
484
|
+
// brightness, we send it as a separate command AFTER the effect.
|
|
485
|
+
// Extract brightness now and send it after the main payload.
|
|
486
|
+
let deferredBrightness;
|
|
487
|
+
if (data.effectType !== undefined && message.effect === undefined) {
|
|
488
|
+
if (data.brightness !== undefined) {
|
|
489
|
+
// User explicitly sent brightness alongside color — defer it
|
|
490
|
+
deferredBrightness = data.brightness;
|
|
491
|
+
delete data.brightness;
|
|
492
|
+
// Keep newState.brightness so state updates correctly
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
const encodedPayload = Buffer.from(EncodeManuSpecificPhilips2(data));
|
|
496
|
+
// An empty Philips2Data encodes as just 2 zero bytes (the flags header).
|
|
497
|
+
// Check length rather than all-zeros, since a valid payload could
|
|
498
|
+
// legitimately contain zero-valued fields.
|
|
499
|
+
if (encodedPayload.length <= 2) {
|
|
500
|
+
logger_1.logger.debug("No Philips2 fields to send, falling back to standard converters", NS);
|
|
501
|
+
// Delegate to the standard converter that handles this key.
|
|
502
|
+
// Z2M calls convertSet once per key, so exactly one converter matches.
|
|
503
|
+
for (const tz of toZigbee) {
|
|
504
|
+
if (tz.key.includes(key)) {
|
|
505
|
+
return await tz.convertSet(entity, key, value, meta);
|
|
506
|
+
}
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
else {
|
|
510
|
+
logger_1.logger.debug(`Sending manuSpecificPhilips2 payload: ${encodedPayload.toString("hex")}`, NS);
|
|
511
|
+
const payload = { data: encodedPayload };
|
|
512
|
+
await entity.command("manuSpecificPhilips2", "multiColor", payload);
|
|
513
|
+
// Send brightness as a separate command after effect activation,
|
|
514
|
+
// since the effect resets brightness on start.
|
|
515
|
+
if (deferredBrightness !== undefined) {
|
|
516
|
+
const brightnessData = { brightness: deferredBrightness };
|
|
517
|
+
const brightnessPayload = Buffer.from(EncodeManuSpecificPhilips2(brightnessData));
|
|
518
|
+
await entity.command("manuSpecificPhilips2", "multiColor", {
|
|
519
|
+
data: brightnessPayload,
|
|
520
|
+
});
|
|
521
|
+
newState.brightness = deferredBrightness;
|
|
522
|
+
}
|
|
523
|
+
// When an effect is active or being set, read state after a delay
|
|
524
|
+
// to sync brightness (effects modulate it internally).
|
|
525
|
+
if (data.effectType !== undefined) {
|
|
526
|
+
setTimeout(async () => {
|
|
527
|
+
try {
|
|
528
|
+
await entity.read("manuSpecificPhilips2", ["state"]);
|
|
529
|
+
}
|
|
530
|
+
catch (_e) {
|
|
531
|
+
// Best-effort sync
|
|
532
|
+
}
|
|
533
|
+
}, 1000);
|
|
534
|
+
}
|
|
535
|
+
// Merge syncColorState results into newState. syncColorState
|
|
536
|
+
// returns only color-related keys (color, color_mode, color_temp),
|
|
537
|
+
// so we spread it on top of newState to keep state, brightness,
|
|
538
|
+
// effect, etc. intact.
|
|
539
|
+
const colorState = libColor.syncColorState(newState, meta.state, entity, meta.options);
|
|
540
|
+
return { state: { ...newState, ...colorState } };
|
|
541
|
+
}
|
|
542
|
+
},
|
|
543
|
+
convertGet: async (entity, key, meta) => {
|
|
544
|
+
let hasPhilips2Cluster = false;
|
|
545
|
+
if (utils.isEndpoint(entity)) {
|
|
546
|
+
try {
|
|
547
|
+
hasPhilips2Cluster = entity.supportsInputCluster("manuSpecificPhilips2");
|
|
548
|
+
}
|
|
549
|
+
catch {
|
|
550
|
+
hasPhilips2Cluster = false;
|
|
551
|
+
}
|
|
552
|
+
}
|
|
553
|
+
if (utils.isEndpoint(entity) && !hasPhilips2Cluster) {
|
|
554
|
+
for (const tz of toZigbee) {
|
|
555
|
+
if (tz.key.includes(key) && tz.convertGet) {
|
|
556
|
+
return await tz.convertGet(entity, key, meta);
|
|
557
|
+
}
|
|
558
|
+
}
|
|
559
|
+
return;
|
|
560
|
+
}
|
|
561
|
+
try {
|
|
562
|
+
await entity.read("manuSpecificPhilips2", ["state"]);
|
|
563
|
+
}
|
|
564
|
+
catch (e) {
|
|
565
|
+
logger_1.logger.debug(`Reading manuSpecificPhilips2 state failed: ${e}`, NS);
|
|
566
|
+
}
|
|
567
|
+
},
|
|
568
|
+
options: [
|
|
569
|
+
new exposes.Binary("hue_native_control", ea.SET, true, false).withDescription("Control this light using a Philips-specific protocol instead of standard Zigbee commands. " +
|
|
570
|
+
"When enabled, on/off, brightness, color, and color temperature are combined into single atomic commands. " +
|
|
571
|
+
"This is required to use the Effect color update mode. " +
|
|
572
|
+
"When disabled (default), standard Zigbee commands are used, which preserves the usual behavior, " +
|
|
573
|
+
"including simulating on/off transitions."),
|
|
574
|
+
new exposes.Enum("effect_color_mode", ea.SET, ["stop", "update"]).withDescription("Controls what happens when color is changed while an effect is active (requires Hue native control). " +
|
|
575
|
+
"'stop' (default): color change stops the effect (Hue app behavior). " +
|
|
576
|
+
"'update': color change re-sends the effect with the new color."),
|
|
577
|
+
],
|
|
578
|
+
};
|
|
579
|
+
// philipsLightTz claims all standard light keys. Inside convertSet, it delegates
|
|
580
|
+
// back to the original standard converters by default (opt-out), or sends via
|
|
581
|
+
// manuSpecificPhilips2 when the user enables the hue_native_control option.
|
|
582
|
+
// The original standard converters are captured in the `toZigbee` closure above.
|
|
583
|
+
// Standard converters for keys we DON'T claim. For converters that handle
|
|
584
|
+
// both claimed and unclaimed keys (e.g. light_onoff_brightness handles "state"
|
|
585
|
+
// which we claim AND "on_time" which we don't), we create wrappers with only
|
|
586
|
+
// the unclaimed keys so Z2M doesn't double-dispatch.
|
|
587
|
+
const unclaimed = [];
|
|
588
|
+
for (const tz of toZigbee) {
|
|
589
|
+
const unclaimedKeys = tz.key.filter((k) => !keys.includes(k));
|
|
590
|
+
if (unclaimedKeys.length === 0)
|
|
591
|
+
continue; // We claim all keys of this converter
|
|
592
|
+
if (unclaimedKeys.length === tz.key.length) {
|
|
593
|
+
// No overlap — include as-is
|
|
594
|
+
unclaimed.push(tz);
|
|
595
|
+
}
|
|
596
|
+
else {
|
|
597
|
+
// Partial overlap — wrap with only unclaimed keys
|
|
598
|
+
unclaimed.push({ ...tz, key: unclaimedKeys });
|
|
599
|
+
}
|
|
600
|
+
}
|
|
601
|
+
result.toZigbee = [philipsLightTz, philipsTz.hue_power_on_behavior, philipsTz.hue_power_on_error, ...unclaimed];
|
|
154
602
|
if (args.hueEffect || args.gradient) {
|
|
155
603
|
result.toZigbee.push(philipsTz.effect);
|
|
156
604
|
const effects = ["blink", "breathe", "okay", "channel_change", "candle"];
|
|
@@ -158,8 +606,6 @@ const philipsModernExtend = {
|
|
|
158
606
|
effects.push("fireplace", "colorloop");
|
|
159
607
|
if (args.gradient) {
|
|
160
608
|
result.toZigbee.push(philipsTz.gradient_scene, philipsTz.gradient({ reverse: true }));
|
|
161
|
-
result.fromZigbee.push(philipsFz.gradient);
|
|
162
|
-
effects.push("sunrise");
|
|
163
609
|
if (args.gradient !== true) {
|
|
164
610
|
effects.push(...args.gradient.extraEffects);
|
|
165
611
|
}
|
|
@@ -170,19 +616,48 @@ const philipsModernExtend = {
|
|
|
170
616
|
.withLengthMin(1)
|
|
171
617
|
.withLengthMax(9)
|
|
172
618
|
.withDescription("List of RGB HEX colors"), args.endpointNames));
|
|
173
|
-
result.configure.push(async (device, coordinatorEndpoint, definition) => {
|
|
174
|
-
for (const ep of device.endpoints.filter((ep) => ep.supportsInputCluster("manuSpecificPhilips2"))) {
|
|
175
|
-
await ep.bind("manuSpecificPhilips2", coordinatorEndpoint);
|
|
176
|
-
}
|
|
177
|
-
});
|
|
178
619
|
}
|
|
179
|
-
|
|
180
|
-
result.
|
|
620
|
+
// Register the Fz converter for all devices (if user enables state reports later)
|
|
621
|
+
result.fromZigbee.push(exports.manuSpecificPhilips2Fz);
|
|
622
|
+
// Don't bind or configure reporting automatically - causes unwanted effects
|
|
623
|
+
// (reports in-between states, conflicting with optimistic states)
|
|
624
|
+
// https://github.com/Koenkk/zigbee2mqtt/issues/32050#issuecomment-4496461658
|
|
625
|
+
// All Hue-specific effects per Bifrost spec
|
|
626
|
+
effects.push("sunset", "sunrise", "sparkle", "opal", "glisten", "underwater", "cosmos", "sunbeam", "enchant");
|
|
627
|
+
effects.push("none", "finish_effect", "stop_effect", "stop_hue_effect");
|
|
628
|
+
result.exposes.push(...(0, utils_1.exposeEndpoints)(e.enum("effect", ea.STATE_SET, effects), args.endpointNames));
|
|
629
|
+
// Expose effect_speed as a numeric 0..1 (0=slowest, 1=fastest)
|
|
630
|
+
result.exposes.push(...(0, utils_1.exposeEndpoints)(new eNumeric("effect_speed", ea.STATE_SET)
|
|
631
|
+
.withValueMin(0)
|
|
632
|
+
.withValueMax(1)
|
|
633
|
+
.withValueStep(0.01)
|
|
634
|
+
.withDescription("Animation speed for the active effect (0=slowest, 1=fastest)"), args.endpointNames));
|
|
635
|
+
// Expose effect_color: sets the base color of the active effect
|
|
636
|
+
// without stopping it. Accepts same formats as color (hex, xy, hs).
|
|
637
|
+
result.exposes.push(...(0, utils_1.exposeEndpoints)(e
|
|
638
|
+
.text("effect_color", ea.SET)
|
|
639
|
+
.withDescription('Set the base color of the active effect without stopping it (hex e.g. #FF4400, or JSON {"x":0.6,"y":0.3})'), args.endpointNames));
|
|
640
|
+
if (args.gradient) {
|
|
641
|
+
// Expose gradient style as an enum (per Bifrost spec: Linear, Scattered, Mirrored)
|
|
642
|
+
result.exposes.push(...(0, utils_1.exposeEndpoints)(e
|
|
643
|
+
.enum("gradient_style", ea.ALL, ["linear", "scattered", "mirrored"])
|
|
644
|
+
.withDescription("Gradient rendering style: linear (smooth blend), scattered (color per segment), mirrored (symmetric from center)"), args.endpointNames));
|
|
645
|
+
// Expose gradient scale and offset as numerics (fixed-point 5.3 format)
|
|
646
|
+
result.exposes.push(...(0, utils_1.exposeEndpoints)(new eNumeric("gradient_scale", ea.SET)
|
|
647
|
+
.withValueMin(0)
|
|
648
|
+
.withValueMax(31)
|
|
649
|
+
.withValueStep(0.125)
|
|
650
|
+
.withDescription("Gradient scale (0=auto fit, 1.0+=number of colors visible)"), args.endpointNames), ...(0, utils_1.exposeEndpoints)(new eNumeric("gradient_offset", ea.SET)
|
|
651
|
+
.withValueMin(0)
|
|
652
|
+
.withValueMax(31)
|
|
653
|
+
.withValueStep(0.125)
|
|
654
|
+
.withDescription("Gradient color offset (0=start from first color)"), args.endpointNames));
|
|
655
|
+
}
|
|
181
656
|
}
|
|
182
657
|
const customCluster2 = philipsModernExtend.addManuSpecificPhilips2Cluster();
|
|
183
658
|
const customCluster3 = philipsModernExtend.addManuSpecificPhilips3Cluster();
|
|
184
|
-
result.onEvent = [...
|
|
185
|
-
result.configure = [...
|
|
659
|
+
result.onEvent = [...customCluster2.onEvent, ...customCluster3.onEvent, ...(result.onEvent ?? [])];
|
|
660
|
+
result.configure = [...customCluster2.configure, ...customCluster3.configure, ...(result.configure ?? [])];
|
|
186
661
|
return result;
|
|
187
662
|
},
|
|
188
663
|
onOff: (args) => {
|
|
@@ -324,15 +799,44 @@ const philipsTz = {
|
|
|
324
799
|
},
|
|
325
800
|
gradient: (opts = { reverse: false }) => {
|
|
326
801
|
return {
|
|
327
|
-
key: ["gradient"],
|
|
802
|
+
key: ["gradient", "gradient_style"],
|
|
328
803
|
convertSet: async (entity, key, value, meta) => {
|
|
804
|
+
// Merge gradient_style from the message into opts if present
|
|
805
|
+
const mergedOpts = { ...opts };
|
|
806
|
+
const { message } = meta;
|
|
807
|
+
if (message.gradient_style != null) {
|
|
808
|
+
const styleLookup = {
|
|
809
|
+
linear: HueGradientStyle.Linear,
|
|
810
|
+
scattered: HueGradientStyle.Scattered,
|
|
811
|
+
mirrored: HueGradientStyle.Mirrored,
|
|
812
|
+
};
|
|
813
|
+
const style = styleLookup[String(message.gradient_style).toLowerCase()];
|
|
814
|
+
if (style !== undefined) {
|
|
815
|
+
mergedOpts.style = style;
|
|
816
|
+
}
|
|
817
|
+
}
|
|
818
|
+
// If only gradient_style was sent (no gradient colors), re-send current
|
|
819
|
+
// gradient from state with the new style
|
|
820
|
+
let colors = key === "gradient" ? value : message.gradient;
|
|
821
|
+
if (colors == null && meta.state?.gradient != null) {
|
|
822
|
+
colors = meta.state.gradient;
|
|
823
|
+
}
|
|
824
|
+
if (colors == null || (Array.isArray(colors) && colors.length === 0)) {
|
|
825
|
+
return; // Nothing to send
|
|
826
|
+
}
|
|
329
827
|
// @ts-expect-error ignore
|
|
330
|
-
const scene = encodeGradientColors(
|
|
828
|
+
const scene = encodeGradientColors(colors, mergedOpts);
|
|
331
829
|
const payload = { data: Buffer.from(scene, "hex") };
|
|
332
830
|
await entity.command("manuSpecificPhilips2", "multiColor", payload);
|
|
831
|
+
return { state: { gradient_style: message.gradient_style } };
|
|
333
832
|
},
|
|
334
833
|
convertGet: async (entity, key, meta) => {
|
|
335
|
-
|
|
834
|
+
try {
|
|
835
|
+
await entity.read("manuSpecificPhilips2", ["state"]);
|
|
836
|
+
}
|
|
837
|
+
catch (e) {
|
|
838
|
+
logger_1.logger.debug(`Reading manuSpecificPhilips2 state for gradient failed: ${e}`, NS);
|
|
839
|
+
}
|
|
336
840
|
},
|
|
337
841
|
};
|
|
338
842
|
},
|
|
@@ -340,12 +844,68 @@ const philipsTz = {
|
|
|
340
844
|
key: ["effect"],
|
|
341
845
|
convertSet: async (entity, key, value, meta) => {
|
|
342
846
|
utils.assertString(value, "effect");
|
|
343
|
-
|
|
344
|
-
|
|
847
|
+
const lower = value.toLowerCase();
|
|
848
|
+
// Stop commands — handle before the generic hueEffects branch,
|
|
849
|
+
// since stop_hue_effect is in the hueEffects map but not in effectLookupAll.
|
|
850
|
+
// All three stop variants also send the Hue stop command so they work
|
|
851
|
+
// regardless of whether the active effect is a ZCL or Hue effect.
|
|
852
|
+
if (lower === "none" || lower === "stop_hue_effect" || lower === "finish_effect" || lower === "stop_effect") {
|
|
853
|
+
// Stop Hue-specific effects via manuSpecificPhilips2
|
|
854
|
+
await entity.command("manuSpecificPhilips2", "multiColor", {
|
|
855
|
+
data: Buffer.from(exports.hueEffects.stop_hue_effect, "hex"),
|
|
856
|
+
});
|
|
857
|
+
// Also send the ZCL effect stop for standard effects (blink, breathe, etc.)
|
|
858
|
+
if (lower === "finish_effect" || lower === "stop_effect") {
|
|
859
|
+
try {
|
|
860
|
+
await tz.effect.convertSet(entity, key, value, meta);
|
|
861
|
+
}
|
|
862
|
+
catch (_e) {
|
|
863
|
+
// Ignore — device may not support ZCL identify cluster
|
|
864
|
+
}
|
|
865
|
+
}
|
|
866
|
+
return { state: { effect: "none" } };
|
|
345
867
|
}
|
|
346
|
-
|
|
347
|
-
|
|
868
|
+
if (lower in effectLookupAll) {
|
|
869
|
+
// Build payload dynamically so we can include optional color
|
|
870
|
+
const data = {
|
|
871
|
+
onOff: true,
|
|
872
|
+
effectType: effectLookupAll[lower],
|
|
873
|
+
};
|
|
874
|
+
// If color is provided alongside effect, include it in the payload
|
|
875
|
+
const msg = meta.message;
|
|
876
|
+
if (msg.color !== undefined) {
|
|
877
|
+
const newColor = libColor.Color.fromConverterArg(msg.color);
|
|
878
|
+
if (newColor.isHSV()) {
|
|
879
|
+
data.colorXY = newColor.hsv.toRGB().gammaCorrected().toXY().rounded(4);
|
|
880
|
+
}
|
|
881
|
+
else {
|
|
882
|
+
data.colorXY = newColor.isRGB() ? newColor.rgb.gammaCorrected().toXY().rounded(4) : newColor.xy;
|
|
883
|
+
}
|
|
884
|
+
}
|
|
885
|
+
// Include effect_speed if provided alongside effect
|
|
886
|
+
if (msg.effect_speed !== undefined) {
|
|
887
|
+
data.effectSpeed = clamp(Number(msg.effect_speed), 0, 1);
|
|
888
|
+
}
|
|
889
|
+
const payload = { data: Buffer.from(EncodeManuSpecificPhilips2(data)) };
|
|
890
|
+
await entity.command("manuSpecificPhilips2", "multiColor", payload);
|
|
891
|
+
const state = { effect: lower };
|
|
892
|
+
if (data.effectSpeed !== undefined)
|
|
893
|
+
state.effect_speed = data.effectSpeed;
|
|
894
|
+
// Effects modulate brightness internally (e.g. candle dims to 30-60%).
|
|
895
|
+
// Read state after a short delay so the Fz converter picks up the
|
|
896
|
+
// actual brightness the device settled on.
|
|
897
|
+
setTimeout(async () => {
|
|
898
|
+
try {
|
|
899
|
+
await entity.read("manuSpecificPhilips2", ["state"]);
|
|
900
|
+
}
|
|
901
|
+
catch (_e) {
|
|
902
|
+
// Ignore read failures — best-effort sync
|
|
903
|
+
}
|
|
904
|
+
}, 1000);
|
|
905
|
+
return { state };
|
|
348
906
|
}
|
|
907
|
+
// Standard ZCL effects (blink, breathe, okay, channel_change)
|
|
908
|
+
return await tz.effect.convertSet(entity, key, value, meta);
|
|
349
909
|
},
|
|
350
910
|
},
|
|
351
911
|
hue_power_on_behavior: {
|
|
@@ -447,7 +1007,7 @@ const philipsTz = {
|
|
|
447
1007
|
return { state: { motion_sensitivity: value } };
|
|
448
1008
|
},
|
|
449
1009
|
convertGet: async (entity, key, meta) => {
|
|
450
|
-
await entity.read("msOccupancySensing", [
|
|
1010
|
+
await entity.read("msOccupancySensing", ["motionSensitivity"], manufacturerOptions);
|
|
451
1011
|
},
|
|
452
1012
|
},
|
|
453
1013
|
hue_motion_led_indication: {
|
|
@@ -458,7 +1018,19 @@ const philipsTz = {
|
|
|
458
1018
|
return { state: { led_indication: value } };
|
|
459
1019
|
},
|
|
460
1020
|
convertGet: async (entity, key, meta) => {
|
|
461
|
-
await entity.read("genBasic", [
|
|
1021
|
+
await entity.read("genBasic", ["ledIndication"], manufacturerOptions);
|
|
1022
|
+
},
|
|
1023
|
+
},
|
|
1024
|
+
hue_wall_switch_device_mode: {
|
|
1025
|
+
key: ["device_mode"],
|
|
1026
|
+
convertSet: async (entity, key, value, meta) => {
|
|
1027
|
+
utils.assertString(value);
|
|
1028
|
+
const values = ["single_rocker", "single_push_button", "dual_rocker", "dual_push_button"];
|
|
1029
|
+
utils.validateValue(value, values);
|
|
1030
|
+
await entity.write("genBasic", { 52: { value: values.indexOf(value), type: 48 } }, manufacturerOptions);
|
|
1031
|
+
},
|
|
1032
|
+
convertGet: async (entity, key, meta) => {
|
|
1033
|
+
await entity.read("genBasic", ["deviceMode"], manufacturerOptions);
|
|
462
1034
|
},
|
|
463
1035
|
},
|
|
464
1036
|
};
|
|
@@ -549,6 +1121,7 @@ exports.hueEffects = {
|
|
|
549
1121
|
sparkle: "2100010a",
|
|
550
1122
|
opal: "2100010b",
|
|
551
1123
|
glisten: "2100010c",
|
|
1124
|
+
sunset: "2100010d",
|
|
552
1125
|
underwater: "2100010e",
|
|
553
1126
|
cosmos: "2100010f",
|
|
554
1127
|
sunbeam: "21000110",
|
|
@@ -689,8 +1262,329 @@ const philipsFz = {
|
|
|
689
1262
|
return payload;
|
|
690
1263
|
},
|
|
691
1264
|
},
|
|
1265
|
+
hue_motion_led_indication: {
|
|
1266
|
+
cluster: "genBasic",
|
|
1267
|
+
type: ["attributeReport", "readResponse"],
|
|
1268
|
+
convert: (model, msg, publish, options, meta) => {
|
|
1269
|
+
if (msg.data.ledIndication !== undefined) {
|
|
1270
|
+
return { led_indication: msg.data.ledIndication === true };
|
|
1271
|
+
}
|
|
1272
|
+
},
|
|
1273
|
+
},
|
|
1274
|
+
hue_motion_sensitivity: {
|
|
1275
|
+
cluster: "msOccupancySensing",
|
|
1276
|
+
type: ["attributeReport", "readResponse"],
|
|
1277
|
+
convert: (model, msg, publish, options, meta) => {
|
|
1278
|
+
if (msg.data.motionSensitivity !== undefined) {
|
|
1279
|
+
const lookup = ["low", "medium", "high", "very_high", "max"];
|
|
1280
|
+
return { motion_sensitivity: lookup[msg.data.motionSensitivity] };
|
|
1281
|
+
}
|
|
1282
|
+
},
|
|
1283
|
+
},
|
|
1284
|
+
hue_wall_switch_device_mode: {
|
|
1285
|
+
cluster: "genBasic",
|
|
1286
|
+
type: ["attributeReport", "readResponse"],
|
|
1287
|
+
convert: (model, msg, publish, options, meta) => {
|
|
1288
|
+
if (msg.data.deviceMode !== undefined) {
|
|
1289
|
+
const values = ["single_rocker", "single_push_button", "dual_rocker", "dual_push_button"];
|
|
1290
|
+
return { device_mode: values[msg.data.deviceMode] };
|
|
1291
|
+
}
|
|
1292
|
+
},
|
|
1293
|
+
},
|
|
1294
|
+
hue_tap: {
|
|
1295
|
+
cluster: "greenPower",
|
|
1296
|
+
type: ["commandNotification", "commandCommissioningNotification"],
|
|
1297
|
+
convert: (model, msg, publish, options, meta) => {
|
|
1298
|
+
const commandID = msg.data.commandID;
|
|
1299
|
+
if ((0, utils_1.hasAlreadyProcessedMessage)(msg, model, msg.data.frameCounter, `${msg.device.ieeeAddr}_${commandID}`))
|
|
1300
|
+
return;
|
|
1301
|
+
if (commandID >= 0xe0)
|
|
1302
|
+
return; // Skip op commands
|
|
1303
|
+
if (HUE_TAP_LOOKUP[commandID] === undefined) {
|
|
1304
|
+
logger_1.logger.error(`Hue Tap: missing command '${commandID}'`, NS);
|
|
1305
|
+
}
|
|
1306
|
+
else {
|
|
1307
|
+
return { action: HUE_TAP_LOOKUP[commandID] };
|
|
1308
|
+
}
|
|
1309
|
+
},
|
|
1310
|
+
},
|
|
692
1311
|
};
|
|
693
1312
|
exports.fz = philipsFz;
|
|
1313
|
+
const clamp = (x, low, high) => Math.min(Math.max(low, x), high);
|
|
1314
|
+
const scaleFloatToIntPow2 = (x, bits) => clamp(x, 0.0, 1.0) * (2 ** bits - 1);
|
|
1315
|
+
const scaleIntPow2ToFloat = (x, bits) => clamp(x, 0, 2 ** bits - 1) / (2 ** bits - 1);
|
|
1316
|
+
var HueEffectType;
|
|
1317
|
+
(function (HueEffectType) {
|
|
1318
|
+
HueEffectType[HueEffectType["NoEffect"] = 0] = "NoEffect";
|
|
1319
|
+
HueEffectType[HueEffectType["Candle"] = 1] = "Candle";
|
|
1320
|
+
HueEffectType[HueEffectType["Fireplace"] = 2] = "Fireplace";
|
|
1321
|
+
HueEffectType[HueEffectType["Prism"] = 3] = "Prism";
|
|
1322
|
+
HueEffectType[HueEffectType["Sunrise"] = 9] = "Sunrise";
|
|
1323
|
+
HueEffectType[HueEffectType["Sparkle"] = 10] = "Sparkle";
|
|
1324
|
+
HueEffectType[HueEffectType["Opal"] = 11] = "Opal";
|
|
1325
|
+
HueEffectType[HueEffectType["Glisten"] = 12] = "Glisten";
|
|
1326
|
+
HueEffectType[HueEffectType["Sunset"] = 13] = "Sunset";
|
|
1327
|
+
HueEffectType[HueEffectType["Underwater"] = 14] = "Underwater";
|
|
1328
|
+
HueEffectType[HueEffectType["Cosmos"] = 15] = "Cosmos";
|
|
1329
|
+
HueEffectType[HueEffectType["Sunbeam"] = 16] = "Sunbeam";
|
|
1330
|
+
HueEffectType[HueEffectType["Enchant"] = 17] = "Enchant";
|
|
1331
|
+
})(HueEffectType || (exports.HueEffectType = HueEffectType = {}));
|
|
1332
|
+
var HueGradientStyle;
|
|
1333
|
+
(function (HueGradientStyle) {
|
|
1334
|
+
HueGradientStyle[HueGradientStyle["Linear"] = 0] = "Linear";
|
|
1335
|
+
HueGradientStyle[HueGradientStyle["Scattered"] = 2] = "Scattered";
|
|
1336
|
+
HueGradientStyle[HueGradientStyle["Mirrored"] = 4] = "Mirrored";
|
|
1337
|
+
})(HueGradientStyle || (exports.HueGradientStyle = HueGradientStyle = {}));
|
|
1338
|
+
const gradientStyleNames = {
|
|
1339
|
+
[HueGradientStyle.Linear]: "linear",
|
|
1340
|
+
[HueGradientStyle.Scattered]: "scattered",
|
|
1341
|
+
[HueGradientStyle.Mirrored]: "mirrored",
|
|
1342
|
+
};
|
|
1343
|
+
// Mapping from effect names to HueEffectType enum values.
|
|
1344
|
+
// Used by both the effect Tz converter and the philipsLightTz convertSet.
|
|
1345
|
+
const effectLookupAll = {
|
|
1346
|
+
candle: HueEffectType.Candle,
|
|
1347
|
+
fireplace: HueEffectType.Fireplace,
|
|
1348
|
+
colorloop: HueEffectType.Prism,
|
|
1349
|
+
sunrise: HueEffectType.Sunrise,
|
|
1350
|
+
sparkle: HueEffectType.Sparkle,
|
|
1351
|
+
opal: HueEffectType.Opal,
|
|
1352
|
+
glisten: HueEffectType.Glisten,
|
|
1353
|
+
sunset: HueEffectType.Sunset,
|
|
1354
|
+
underwater: HueEffectType.Underwater,
|
|
1355
|
+
cosmos: HueEffectType.Cosmos,
|
|
1356
|
+
sunbeam: HueEffectType.Sunbeam,
|
|
1357
|
+
enchant: HueEffectType.Enchant,
|
|
1358
|
+
};
|
|
1359
|
+
// Inverse mapping: HueEffectType → name. Derived from effectLookupAll to stay in sync.
|
|
1360
|
+
const effectNames = {
|
|
1361
|
+
[HueEffectType.NoEffect]: "none",
|
|
1362
|
+
...Object.fromEntries(Object.entries(effectLookupAll).map(([name, type]) => [type, name])),
|
|
1363
|
+
};
|
|
1364
|
+
var HueFlags;
|
|
1365
|
+
(function (HueFlags) {
|
|
1366
|
+
HueFlags[HueFlags["OnOff"] = 1] = "OnOff";
|
|
1367
|
+
HueFlags[HueFlags["Brightness"] = 2] = "Brightness";
|
|
1368
|
+
HueFlags[HueFlags["ColorMirek"] = 4] = "ColorMirek";
|
|
1369
|
+
HueFlags[HueFlags["ColorXY"] = 8] = "ColorXY";
|
|
1370
|
+
HueFlags[HueFlags["FadeSpeed"] = 16] = "FadeSpeed";
|
|
1371
|
+
HueFlags[HueFlags["EffectType"] = 32] = "EffectType";
|
|
1372
|
+
HueFlags[HueFlags["GradientParams"] = 64] = "GradientParams";
|
|
1373
|
+
HueFlags[HueFlags["EffectSpeed"] = 128] = "EffectSpeed";
|
|
1374
|
+
HueFlags[HueFlags["GradientColors"] = 256] = "GradientColors";
|
|
1375
|
+
// Reserved for future use per Bifrost spec
|
|
1376
|
+
HueFlags[HueFlags["Reserved9"] = 512] = "Reserved9";
|
|
1377
|
+
HueFlags[HueFlags["ReservedA"] = 1024] = "ReservedA";
|
|
1378
|
+
HueFlags[HueFlags["ReservedB"] = 2048] = "ReservedB";
|
|
1379
|
+
HueFlags[HueFlags["ReservedC"] = 4096] = "ReservedC";
|
|
1380
|
+
HueFlags[HueFlags["ReservedD"] = 8192] = "ReservedD";
|
|
1381
|
+
HueFlags[HueFlags["ReservedE"] = 16384] = "ReservedE";
|
|
1382
|
+
HueFlags[HueFlags["ReservedF"] = 32768] = "ReservedF";
|
|
1383
|
+
})(HueFlags || (HueFlags = {}));
|
|
1384
|
+
const ON_OFF_DETAILS = {
|
|
1385
|
+
name: "onOff",
|
|
1386
|
+
flag: HueFlags.OnOff,
|
|
1387
|
+
maxLength: 1,
|
|
1388
|
+
encode: (v, p, d) => {
|
|
1389
|
+
v.setUint8(p, d.onOff ? 1 : 0);
|
|
1390
|
+
return p + 1;
|
|
1391
|
+
},
|
|
1392
|
+
decode: (v, p, d) => {
|
|
1393
|
+
d.onOff = !!v.getUint8(p);
|
|
1394
|
+
return p + 1;
|
|
1395
|
+
},
|
|
1396
|
+
};
|
|
1397
|
+
const BRIGHTNESS_DETAILS = {
|
|
1398
|
+
name: "brightness",
|
|
1399
|
+
flag: HueFlags.Brightness,
|
|
1400
|
+
maxLength: 1,
|
|
1401
|
+
encode: (v, p, d) => {
|
|
1402
|
+
v.setUint8(p, clamp(d.brightness, 1, 254));
|
|
1403
|
+
return p + 1;
|
|
1404
|
+
},
|
|
1405
|
+
decode: (v, p, d) => {
|
|
1406
|
+
d.brightness = v.getUint8(p);
|
|
1407
|
+
return p + 1;
|
|
1408
|
+
},
|
|
1409
|
+
};
|
|
1410
|
+
const COLOR_MIREK_DETAILS = {
|
|
1411
|
+
name: "colorMirek",
|
|
1412
|
+
flag: HueFlags.ColorMirek,
|
|
1413
|
+
maxLength: 2,
|
|
1414
|
+
encode: (v, p, d) => {
|
|
1415
|
+
v.setUint16(p, d.colorMirek, true);
|
|
1416
|
+
return p + 2;
|
|
1417
|
+
},
|
|
1418
|
+
decode: (v, p, d) => {
|
|
1419
|
+
d.colorMirek = v.getUint16(p, true);
|
|
1420
|
+
return p + 2;
|
|
1421
|
+
},
|
|
1422
|
+
};
|
|
1423
|
+
const COLOR_XY_DETAILS = {
|
|
1424
|
+
name: "colorXY",
|
|
1425
|
+
flag: HueFlags.ColorXY,
|
|
1426
|
+
maxLength: 4,
|
|
1427
|
+
encode: (v, p, d) => {
|
|
1428
|
+
v.setUint16(p, scaleFloatToIntPow2(d.colorXY.x, 16), true);
|
|
1429
|
+
v.setUint16(p + 2, scaleFloatToIntPow2(d.colorXY.y, 16), true);
|
|
1430
|
+
return p + 4;
|
|
1431
|
+
},
|
|
1432
|
+
decode: (v, p, d) => {
|
|
1433
|
+
d.colorXY = color_1.ColorXY.fromObject({
|
|
1434
|
+
x: scaleIntPow2ToFloat(v.getUint16(p, true), 16),
|
|
1435
|
+
y: scaleIntPow2ToFloat(v.getUint16(p + 2, true), 16),
|
|
1436
|
+
});
|
|
1437
|
+
return p + 4;
|
|
1438
|
+
},
|
|
1439
|
+
};
|
|
1440
|
+
const FADE_SPEED_DETAILS = {
|
|
1441
|
+
name: "fadeSpeed",
|
|
1442
|
+
flag: HueFlags.FadeSpeed,
|
|
1443
|
+
maxLength: 2,
|
|
1444
|
+
encode: (v, p, d) => {
|
|
1445
|
+
v.setUint16(p, d.fadeSpeed, true);
|
|
1446
|
+
return p + 2;
|
|
1447
|
+
},
|
|
1448
|
+
decode: (v, p, d) => {
|
|
1449
|
+
d.fadeSpeed = v.getUint16(p, true);
|
|
1450
|
+
return p + 2;
|
|
1451
|
+
},
|
|
1452
|
+
};
|
|
1453
|
+
const EFFECT_TYPE_DETAILS = {
|
|
1454
|
+
name: "effectType",
|
|
1455
|
+
flag: HueFlags.EffectType,
|
|
1456
|
+
maxLength: 1,
|
|
1457
|
+
encode: (v, p, d) => {
|
|
1458
|
+
v.setUint8(p, d.effectType);
|
|
1459
|
+
return p + 1;
|
|
1460
|
+
},
|
|
1461
|
+
decode: (v, p, d) => {
|
|
1462
|
+
d.effectType = v.getUint8(p);
|
|
1463
|
+
return p + 1;
|
|
1464
|
+
},
|
|
1465
|
+
};
|
|
1466
|
+
const GRADIENT_PARAMS_DETAILS = {
|
|
1467
|
+
name: "gradientParams",
|
|
1468
|
+
flag: HueFlags.GradientParams,
|
|
1469
|
+
maxLength: 2,
|
|
1470
|
+
encode: (v, p, d) => {
|
|
1471
|
+
v.setUint8(p, Math.round(clamp(d.gradientParams.scale * 8, 0, 255)));
|
|
1472
|
+
v.setUint8(p + 1, Math.round(clamp(d.gradientParams.offset * 8, 0, 255)));
|
|
1473
|
+
return p + 2;
|
|
1474
|
+
},
|
|
1475
|
+
decode: (v, p, d) => {
|
|
1476
|
+
d.gradientParams = {
|
|
1477
|
+
scale: v.getUint8(p) / 8.0,
|
|
1478
|
+
offset: v.getUint8(p + 1) / 8.0,
|
|
1479
|
+
};
|
|
1480
|
+
return p + 2;
|
|
1481
|
+
},
|
|
1482
|
+
};
|
|
1483
|
+
const EFFECT_SPEED_DETAILS = {
|
|
1484
|
+
name: "effectSpeed",
|
|
1485
|
+
flag: HueFlags.EffectSpeed,
|
|
1486
|
+
maxLength: 1,
|
|
1487
|
+
encode: (v, p, d) => {
|
|
1488
|
+
v.setUint8(p, scaleFloatToIntPow2(d.effectSpeed, 8));
|
|
1489
|
+
return p + 1;
|
|
1490
|
+
},
|
|
1491
|
+
decode: (v, p, d) => {
|
|
1492
|
+
d.effectSpeed = scaleIntPow2ToFloat(v.getUint8(p), 8);
|
|
1493
|
+
return p + 1;
|
|
1494
|
+
},
|
|
1495
|
+
};
|
|
1496
|
+
// GRADIENT_COLORS_MAX_X and GRADIENT_COLORS_MAX_Y defined at top of file
|
|
1497
|
+
const GRADIENT_COLORS_DETAILS = {
|
|
1498
|
+
name: "gradientColors",
|
|
1499
|
+
flag: HueFlags.GradientColors,
|
|
1500
|
+
// Max: 1 byte size + 4 bytes header + 9 colors * 3 bytes = 32 bytes
|
|
1501
|
+
maxLength: 5 + 9 * 3,
|
|
1502
|
+
encode: (v, p, d) => {
|
|
1503
|
+
// Bifrost spec: max 9 colors; 10+ causes device to reject entire message
|
|
1504
|
+
const colorCount = clamp(d.gradientColors.colors.length, 0, 9);
|
|
1505
|
+
// Size byte: 4 bytes header (count + style + 2 reserved) + 3 bytes per color
|
|
1506
|
+
v.setUint8(p, 4 + colorCount * 3);
|
|
1507
|
+
v.setUint8(p + 1, colorCount << 4);
|
|
1508
|
+
v.setUint8(p + 2, d.gradientColors.style);
|
|
1509
|
+
v.setUint16(p + 3, 0, true);
|
|
1510
|
+
d.gradientColors.colors.slice(0, colorCount).forEach(({ x, y }, i) => {
|
|
1511
|
+
const scaledX = scaleFloatToIntPow2(x / GRADIENT_COLORS_MAX_X, 12);
|
|
1512
|
+
const scaledY = scaleFloatToIntPow2(y / GRADIENT_COLORS_MAX_Y, 12);
|
|
1513
|
+
// Byte packing per Bifrost spec:
|
|
1514
|
+
// byte 0: x[7:0]
|
|
1515
|
+
// byte 1: y[3:0] << 4 | x[11:8]
|
|
1516
|
+
// byte 2: y[11:4]
|
|
1517
|
+
v.setUint8(p + 5 + i * 3, scaledX & 0xff);
|
|
1518
|
+
v.setUint8(p + 6 + i * 3, ((scaledY & 0xf) << 4) | ((scaledX & 0xf00) >> 8));
|
|
1519
|
+
v.setUint8(p + 7 + i * 3, (scaledY >> 4) & 0xff);
|
|
1520
|
+
});
|
|
1521
|
+
// Header: 1 byte size + 4 bytes (count, style, 2 reserved) = 5 bytes + 3*N colors
|
|
1522
|
+
return p + 5 + 3 * colorCount;
|
|
1523
|
+
},
|
|
1524
|
+
decode: (v, p, d) => {
|
|
1525
|
+
const size = v.getUint8(p);
|
|
1526
|
+
const colorCount = v.getUint8(p + 1) >> 4;
|
|
1527
|
+
const rawStyle = v.getUint8(p + 2);
|
|
1528
|
+
// Validate gradient style per Bifrost spec: Linear=0x00, Scattered=0x02, Mirrored=0x04
|
|
1529
|
+
const validStyles = [HueGradientStyle.Linear, HueGradientStyle.Scattered, HueGradientStyle.Mirrored];
|
|
1530
|
+
const style = validStyles.includes(rawStyle) ? rawStyle : HueGradientStyle.Linear;
|
|
1531
|
+
const colors = Array.from({ length: colorCount }, (_, i) => {
|
|
1532
|
+
const a = v.getUint8(p + 5 + i * 3);
|
|
1533
|
+
const b = v.getUint8(p + 6 + i * 3);
|
|
1534
|
+
const c = v.getUint8(p + 7 + i * 3);
|
|
1535
|
+
return color_1.ColorXY.fromObject({
|
|
1536
|
+
x: scaleIntPow2ToFloat(((b & 0xf) << 8) | a, 12) * GRADIENT_COLORS_MAX_X,
|
|
1537
|
+
y: scaleIntPow2ToFloat((c << 4) | ((b & 0xf0) >> 4), 12) * GRADIENT_COLORS_MAX_Y,
|
|
1538
|
+
});
|
|
1539
|
+
});
|
|
1540
|
+
d.gradientColors = { style, colors };
|
|
1541
|
+
return p + size + 1;
|
|
1542
|
+
},
|
|
1543
|
+
};
|
|
1544
|
+
// Ordered as they appear in the wire format per Bifrost spec.
|
|
1545
|
+
// IMPORTANT: the wire order does NOT match the flag bit order.
|
|
1546
|
+
// Specifically, GRADIENT_COLORS (flag 0x0100) appears BEFORE
|
|
1547
|
+
// EFFECT_SPEED (flag 0x0080) and GRADIENT_PARAMS (flag 0x0040)
|
|
1548
|
+
// in the actual byte stream. Both encode and decode MUST iterate
|
|
1549
|
+
// in this order, not in flag-bit order.
|
|
1550
|
+
// Reference: https://github.com/chrivers/bifrost/blob/master/doc/hue-zigbee-format.md
|
|
1551
|
+
const HUE_DATA_TYPES = [
|
|
1552
|
+
ON_OFF_DETAILS,
|
|
1553
|
+
BRIGHTNESS_DETAILS,
|
|
1554
|
+
COLOR_MIREK_DETAILS,
|
|
1555
|
+
COLOR_XY_DETAILS,
|
|
1556
|
+
FADE_SPEED_DETAILS,
|
|
1557
|
+
EFFECT_TYPE_DETAILS,
|
|
1558
|
+
GRADIENT_COLORS_DETAILS,
|
|
1559
|
+
EFFECT_SPEED_DETAILS,
|
|
1560
|
+
GRADIENT_PARAMS_DETAILS,
|
|
1561
|
+
];
|
|
1562
|
+
function DecodeManuSpecificPhilips2(data) {
|
|
1563
|
+
const view = new DataView(data.buffer, data.byteOffset, data.byteLength);
|
|
1564
|
+
const flags = view.getUint16(0, true);
|
|
1565
|
+
const decoded = {};
|
|
1566
|
+
let position = 2;
|
|
1567
|
+
HUE_DATA_TYPES.forEach((htd) => {
|
|
1568
|
+
if (flags & htd.flag) {
|
|
1569
|
+
position = htd.decode(view, position, decoded);
|
|
1570
|
+
}
|
|
1571
|
+
});
|
|
1572
|
+
return decoded;
|
|
1573
|
+
}
|
|
1574
|
+
function EncodeManuSpecificPhilips2(data) {
|
|
1575
|
+
const buffer = new ArrayBuffer(2 + HUE_DATA_TYPES.map((htd) => htd.maxLength).reduce((pv, cv) => pv + cv, 0));
|
|
1576
|
+
const view = new DataView(buffer);
|
|
1577
|
+
let position = 2;
|
|
1578
|
+
let flags = 0;
|
|
1579
|
+
HUE_DATA_TYPES.forEach((htd) => {
|
|
1580
|
+
if (data[htd.name] !== undefined) {
|
|
1581
|
+
flags |= htd.flag;
|
|
1582
|
+
position = htd.encode(view, position, data);
|
|
1583
|
+
}
|
|
1584
|
+
});
|
|
1585
|
+
view.setUint16(0, flags, true);
|
|
1586
|
+
return buffer.slice(0, position);
|
|
1587
|
+
}
|
|
694
1588
|
// decoder for manuSpecificPhilips2.state
|
|
695
1589
|
function decodeGradientColors(input, opts) {
|
|
696
1590
|
// Gradient mode (4b01)
|
|
@@ -868,9 +1762,16 @@ function encodeGradientColors(value, opts) {
|
|
|
868
1762
|
const offsetPayload = (offset << 3).toString(16).padStart(2, "0");
|
|
869
1763
|
// Payload length
|
|
870
1764
|
const length = (1 + 3 * (value.length + 1)).toString(16).padStart(2, "0");
|
|
1765
|
+
// Gradient style: Linear=0x00, Scattered=0x02, Mirrored=0x04
|
|
1766
|
+
// Per Bifrost spec, only these three values are valid.
|
|
1767
|
+
let style = 0x00; // Default: Linear
|
|
1768
|
+
if (opts.style != null) {
|
|
1769
|
+
style = opts.style;
|
|
1770
|
+
}
|
|
1771
|
+
const stylePayload = style.toString(16).padStart(2, "0");
|
|
871
1772
|
// 5001 - mode? set gradient?
|
|
872
1773
|
// 0400 - unknown
|
|
873
|
-
const scene = `50010400${length}${nColors}
|
|
1774
|
+
const scene = `50010400${length}${nColors}${stylePayload}0000${colorsPayload}${segmentsPayload}${offsetPayload}`;
|
|
874
1775
|
return scene;
|
|
875
1776
|
}
|
|
876
1777
|
//# sourceMappingURL=philips.js.map
|