@smarthome.exchange/integrations 1.0.0 → 3.0.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 +84 -0
- package/dist_ts/00_commitinfo_data.js +1 -1
- package/dist_ts/core/classes.configstore.d.ts +2 -0
- package/dist_ts/core/classes.configstore.js +84 -27
- package/dist_ts/core/classes.nativehomeadmission.d.ts +19 -0
- package/dist_ts/core/classes.nativehomeadmission.js +84 -0
- package/dist_ts/core/classes.nativeintegrationowner.d.ts +81 -0
- package/dist_ts/core/classes.nativeintegrationowner.js +273 -0
- package/dist_ts/core/classes.runtimemanager.d.ts +2 -0
- package/dist_ts/core/classes.runtimemanager.js +11 -3
- package/dist_ts/core/index.d.ts +2 -0
- package/dist_ts/core/index.js +3 -1
- package/dist_ts/core/types.d.ts +16 -0
- package/dist_ts/integrations/braviatv/braviatv.classes.client.d.ts +23 -72
- package/dist_ts/integrations/braviatv/braviatv.classes.client.js +265 -625
- package/dist_ts/integrations/braviatv/braviatv.classes.configflow.d.ts +7 -7
- package/dist_ts/integrations/braviatv/braviatv.classes.configflow.js +51 -68
- package/dist_ts/integrations/braviatv/braviatv.classes.integration.d.ts +8 -11
- package/dist_ts/integrations/braviatv/braviatv.classes.integration.js +186 -135
- package/dist_ts/integrations/braviatv/braviatv.discovery.js +12 -11
- package/dist_ts/integrations/braviatv/braviatv.mapper.d.ts +6 -14
- package/dist_ts/integrations/braviatv/braviatv.mapper.js +37 -131
- package/dist_ts/integrations/braviatv/braviatv.types.d.ts +55 -133
- package/dist_ts/integrations/braviatv/braviatv.validation.d.ts +7 -0
- package/dist_ts/integrations/braviatv/braviatv.validation.js +72 -0
- package/dist_ts/integrations/broadlink/broadlink.actions.d.ts +2 -0
- package/dist_ts/integrations/broadlink/broadlink.actions.js +36 -0
- package/dist_ts/integrations/broadlink/broadlink.classes.client.d.ts +21 -19
- package/dist_ts/integrations/broadlink/broadlink.classes.client.js +185 -124
- package/dist_ts/integrations/broadlink/broadlink.classes.configflow.d.ts +5 -4
- package/dist_ts/integrations/broadlink/broadlink.classes.configflow.js +64 -83
- package/dist_ts/integrations/broadlink/broadlink.classes.integration.d.ts +16 -19
- package/dist_ts/integrations/broadlink/broadlink.classes.integration.js +114 -59
- package/dist_ts/integrations/broadlink/broadlink.mapper.d.ts +3 -2
- package/dist_ts/integrations/broadlink/broadlink.mapper.js +2 -1
- package/dist_ts/integrations/broadlink/broadlink.selected.mapper.d.ts +10 -0
- package/dist_ts/integrations/broadlink/broadlink.selected.mapper.js +41 -0
- package/dist_ts/integrations/broadlink/broadlink.types.d.ts +43 -7
- package/dist_ts/integrations/broadlink/broadlink.validation.d.ts +7 -0
- package/dist_ts/integrations/broadlink/broadlink.validation.js +63 -0
- package/dist_ts/integrations/daikin/daikin.classes.client.d.ts +10 -52
- package/dist_ts/integrations/daikin/daikin.classes.client.js +55 -614
- package/dist_ts/integrations/daikin/daikin.classes.configflow.d.ts +8 -12
- package/dist_ts/integrations/daikin/daikin.classes.configflow.js +77 -138
- package/dist_ts/integrations/daikin/daikin.classes.integration.d.ts +7 -24
- package/dist_ts/integrations/daikin/daikin.classes.integration.js +175 -67
- package/dist_ts/integrations/daikin/daikin.discovery.d.ts +11 -24
- package/dist_ts/integrations/daikin/daikin.discovery.js +98 -305
- package/dist_ts/integrations/daikin/daikin.mapper.d.ts +6 -7
- package/dist_ts/integrations/daikin/daikin.mapper.js +95 -155
- package/dist_ts/integrations/daikin/daikin.types.d.ts +24 -44
- package/dist_ts/integrations/daikin/daikin.types.js +1 -1
- package/dist_ts/integrations/daikin/daikin.validation.d.ts +5 -0
- package/dist_ts/integrations/daikin/daikin.validation.js +53 -0
- package/dist_ts/integrations/deconz/deconz.classes.client.d.ts +37 -29
- package/dist_ts/integrations/deconz/deconz.classes.client.js +521 -294
- package/dist_ts/integrations/deconz/deconz.classes.configflow.d.ts +6 -1
- package/dist_ts/integrations/deconz/deconz.classes.configflow.js +63 -33
- package/dist_ts/integrations/deconz/deconz.classes.integration.d.ts +4 -3
- package/dist_ts/integrations/deconz/deconz.classes.integration.js +85 -40
- package/dist_ts/integrations/deconz/deconz.mapper.d.ts +1 -0
- package/dist_ts/integrations/deconz/deconz.mapper.js +24 -6
- package/dist_ts/integrations/deconz/deconz.types.d.ts +43 -6
- package/dist_ts/integrations/deconz/deconz.validation.d.ts +7 -1
- package/dist_ts/integrations/deconz/deconz.validation.js +215 -4
- package/dist_ts/integrations/denonavr/denonavr.classes.client.d.ts +31 -40
- package/dist_ts/integrations/denonavr/denonavr.classes.client.js +149 -404
- package/dist_ts/integrations/denonavr/denonavr.classes.configflow.d.ts +7 -2
- package/dist_ts/integrations/denonavr/denonavr.classes.configflow.js +44 -36
- package/dist_ts/integrations/denonavr/denonavr.classes.integration.d.ts +6 -11
- package/dist_ts/integrations/denonavr/denonavr.classes.integration.js +192 -113
- package/dist_ts/integrations/denonavr/denonavr.discovery.d.ts +10 -18
- package/dist_ts/integrations/denonavr/denonavr.discovery.js +71 -193
- package/dist_ts/integrations/denonavr/denonavr.mapper.d.ts +5 -13
- package/dist_ts/integrations/denonavr/denonavr.mapper.js +35 -159
- package/dist_ts/integrations/denonavr/denonavr.types.d.ts +55 -90
- package/dist_ts/integrations/denonavr/denonavr.validation.d.ts +10 -0
- package/dist_ts/integrations/denonavr/denonavr.validation.js +86 -0
- package/dist_ts/integrations/esphome/esphome.classes.client.d.ts +19 -7
- package/dist_ts/integrations/esphome/esphome.classes.client.js +183 -89
- package/dist_ts/integrations/esphome/esphome.classes.configflow.d.ts +5 -2
- package/dist_ts/integrations/esphome/esphome.classes.configflow.js +43 -33
- package/dist_ts/integrations/esphome/esphome.classes.integration.d.ts +8 -6
- package/dist_ts/integrations/esphome/esphome.classes.integration.js +71 -43
- package/dist_ts/integrations/esphome/esphome.discovery.js +4 -12
- package/dist_ts/integrations/esphome/esphome.mapper.d.ts +2 -2
- package/dist_ts/integrations/esphome/esphome.mapper.js +6 -4
- package/dist_ts/integrations/esphome/esphome.types.d.ts +31 -6
- package/dist_ts/integrations/esphome/esphome.types.js +1 -1
- package/dist_ts/integrations/esphome/esphome.validation.d.ts +7 -0
- package/dist_ts/integrations/esphome/esphome.validation.js +66 -0
- package/dist_ts/integrations/homeassistant/homeassistant.classes.client.d.ts +32 -4
- package/dist_ts/integrations/homeassistant/homeassistant.classes.client.js +345 -116
- package/dist_ts/integrations/homeassistant/homeassistant.classes.integration.d.ts +8 -5
- package/dist_ts/integrations/homeassistant/homeassistant.classes.integration.js +34 -8
- package/dist_ts/integrations/homeassistant/homeassistant.classes.runtime.d.ts +7 -4
- package/dist_ts/integrations/homeassistant/homeassistant.classes.runtime.js +44 -56
- package/dist_ts/integrations/homeassistant/homeassistant.types.d.ts +12 -1
- package/dist_ts/integrations/hue/hue.classes.client.d.ts +6 -1
- package/dist_ts/integrations/hue/hue.classes.client.js +120 -45
- package/dist_ts/integrations/hue/hue.classes.configflow.js +16 -7
- package/dist_ts/integrations/hue/hue.classes.integration.d.ts +2 -0
- package/dist_ts/integrations/hue/hue.classes.integration.js +103 -30
- package/dist_ts/integrations/hue/hue.validation.d.ts +1 -0
- package/dist_ts/integrations/hue/hue.validation.js +14 -4
- package/dist_ts/integrations/kodi/kodi.classes.client.d.ts +11 -45
- package/dist_ts/integrations/kodi/kodi.classes.client.js +84 -310
- package/dist_ts/integrations/kodi/kodi.classes.configflow.d.ts +9 -8
- package/dist_ts/integrations/kodi/kodi.classes.configflow.js +53 -48
- package/dist_ts/integrations/kodi/kodi.classes.integration.d.ts +11 -11
- package/dist_ts/integrations/kodi/kodi.classes.integration.js +114 -171
- package/dist_ts/integrations/kodi/kodi.discovery.js +53 -29
- package/dist_ts/integrations/kodi/kodi.mapper.d.ts +7 -2
- package/dist_ts/integrations/kodi/kodi.mapper.js +48 -10
- package/dist_ts/integrations/kodi/kodi.types.d.ts +32 -28
- package/dist_ts/integrations/kodi/kodi.validation.d.ts +9 -0
- package/dist_ts/integrations/kodi/kodi.validation.js +62 -0
- package/dist_ts/integrations/modbus/modbus.classes.client.d.ts +15 -61
- package/dist_ts/integrations/modbus/modbus.classes.client.js +117 -776
- package/dist_ts/integrations/modbus/modbus.classes.configflow.d.ts +9 -5
- package/dist_ts/integrations/modbus/modbus.classes.configflow.js +70 -38
- package/dist_ts/integrations/modbus/modbus.classes.integration.d.ts +6 -17
- package/dist_ts/integrations/modbus/modbus.classes.integration.js +133 -57
- package/dist_ts/integrations/modbus/modbus.discovery.d.ts +5 -8
- package/dist_ts/integrations/modbus/modbus.discovery.js +38 -93
- package/dist_ts/integrations/modbus/modbus.mapper.d.ts +8 -32
- package/dist_ts/integrations/modbus/modbus.mapper.js +71 -431
- package/dist_ts/integrations/modbus/modbus.types.d.ts +82 -332
- package/dist_ts/integrations/modbus/modbus.validation.d.ts +10 -0
- package/dist_ts/integrations/modbus/modbus.validation.js +128 -0
- package/dist_ts/integrations/mqtt/mqtt.classes.client.d.ts +15 -2
- package/dist_ts/integrations/mqtt/mqtt.classes.client.js +173 -23
- package/dist_ts/integrations/mqtt/mqtt.classes.configflow.d.ts +5 -1
- package/dist_ts/integrations/mqtt/mqtt.classes.configflow.js +53 -33
- package/dist_ts/integrations/mqtt/mqtt.classes.integration.d.ts +4 -1
- package/dist_ts/integrations/mqtt/mqtt.classes.integration.js +127 -56
- package/dist_ts/integrations/mqtt/mqtt.classes.session.d.ts +13 -0
- package/dist_ts/integrations/mqtt/mqtt.classes.session.js +130 -0
- package/dist_ts/integrations/mqtt/mqtt.mapper.d.ts +3 -0
- package/dist_ts/integrations/mqtt/mqtt.mapper.js +44 -1
- package/dist_ts/integrations/mqtt/mqtt.types.d.ts +46 -0
- package/dist_ts/integrations/mqtt/mqtt.validation.d.ts +7 -0
- package/dist_ts/integrations/mqtt/mqtt.validation.js +180 -0
- package/dist_ts/integrations/nanoleaf/nanoleaf.classes.client.d.ts +12 -34
- package/dist_ts/integrations/nanoleaf/nanoleaf.classes.client.js +143 -231
- package/dist_ts/integrations/nanoleaf/nanoleaf.classes.configflow.d.ts +6 -1
- package/dist_ts/integrations/nanoleaf/nanoleaf.classes.configflow.js +105 -18
- package/dist_ts/integrations/nanoleaf/nanoleaf.classes.integration.d.ts +8 -8
- package/dist_ts/integrations/nanoleaf/nanoleaf.classes.integration.js +141 -155
- package/dist_ts/integrations/nanoleaf/nanoleaf.credentials.d.ts +9 -0
- package/dist_ts/integrations/nanoleaf/nanoleaf.credentials.js +35 -0
- package/dist_ts/integrations/nanoleaf/nanoleaf.mapper.d.ts +6 -18
- package/dist_ts/integrations/nanoleaf/nanoleaf.mapper.js +45 -220
- package/dist_ts/integrations/nanoleaf/nanoleaf.types.d.ts +33 -147
- package/dist_ts/integrations/nanoleaf/nanoleaf.validation.d.ts +6 -0
- package/dist_ts/integrations/nanoleaf/nanoleaf.validation.js +82 -0
- package/dist_ts/integrations/onkyo/onkyo.classes.client.d.ts +15 -20
- package/dist_ts/integrations/onkyo/onkyo.classes.client.js +161 -404
- package/dist_ts/integrations/onkyo/onkyo.classes.configflow.d.ts +9 -4
- package/dist_ts/integrations/onkyo/onkyo.classes.configflow.js +51 -6
- package/dist_ts/integrations/onkyo/onkyo.classes.integration.d.ts +22 -6
- package/dist_ts/integrations/onkyo/onkyo.classes.integration.js +144 -11
- package/dist_ts/integrations/onkyo/onkyo.discovery.d.ts +6 -1
- package/dist_ts/integrations/onkyo/onkyo.discovery.js +40 -4
- package/dist_ts/integrations/onkyo/onkyo.mapper.d.ts +7 -8
- package/dist_ts/integrations/onkyo/onkyo.mapper.js +45 -12
- package/dist_ts/integrations/onkyo/onkyo.types.d.ts +58 -8
- package/dist_ts/integrations/onkyo/onkyo.types.js +13 -102
- package/dist_ts/integrations/onkyo/onkyo.validation.d.ts +16 -0
- package/dist_ts/integrations/onkyo/onkyo.validation.js +77 -0
- package/dist_ts/integrations/panasonic_viera/panasonic_viera.classes.client.d.ts +25 -25
- package/dist_ts/integrations/panasonic_viera/panasonic_viera.classes.client.js +116 -360
- package/dist_ts/integrations/panasonic_viera/panasonic_viera.classes.configflow.d.ts +11 -3
- package/dist_ts/integrations/panasonic_viera/panasonic_viera.classes.configflow.js +76 -6
- package/dist_ts/integrations/panasonic_viera/panasonic_viera.classes.integration.d.ts +26 -4
- package/dist_ts/integrations/panasonic_viera/panasonic_viera.classes.integration.js +142 -9
- package/dist_ts/integrations/panasonic_viera/panasonic_viera.discovery.d.ts +2 -1
- package/dist_ts/integrations/panasonic_viera/panasonic_viera.discovery.js +31 -4
- package/dist_ts/integrations/panasonic_viera/panasonic_viera.mapper.d.ts +9 -9
- package/dist_ts/integrations/panasonic_viera/panasonic_viera.mapper.js +31 -149
- package/dist_ts/integrations/panasonic_viera/panasonic_viera.types.d.ts +51 -11
- package/dist_ts/integrations/panasonic_viera/panasonic_viera.types.js +22 -87
- package/dist_ts/integrations/panasonic_viera/panasonic_viera.validation.d.ts +6 -0
- package/dist_ts/integrations/panasonic_viera/panasonic_viera.validation.js +56 -0
- package/dist_ts/integrations/pioneer/pioneer.classes.client.d.ts +31 -16
- package/dist_ts/integrations/pioneer/pioneer.classes.client.js +167 -409
- package/dist_ts/integrations/pioneer/pioneer.classes.configflow.d.ts +10 -3
- package/dist_ts/integrations/pioneer/pioneer.classes.configflow.js +66 -6
- package/dist_ts/integrations/pioneer/pioneer.classes.integration.d.ts +13 -5
- package/dist_ts/integrations/pioneer/pioneer.classes.integration.js +239 -11
- package/dist_ts/integrations/pioneer/pioneer.discovery.d.ts +14 -1
- package/dist_ts/integrations/pioneer/pioneer.discovery.js +51 -4
- package/dist_ts/integrations/pioneer/pioneer.mapper.d.ts +14 -8
- package/dist_ts/integrations/pioneer/pioneer.mapper.js +54 -12
- package/dist_ts/integrations/pioneer/pioneer.types.d.ts +79 -12
- package/dist_ts/integrations/pioneer/pioneer.types.js +12 -76
- package/dist_ts/integrations/pioneer/pioneer.validation.d.ts +10 -0
- package/dist_ts/integrations/pioneer/pioneer.validation.js +85 -0
- package/dist_ts/integrations/samsungtv/samsungtv.classes.client.d.ts +4 -2
- package/dist_ts/integrations/samsungtv/samsungtv.classes.client.js +41 -16
- package/dist_ts/integrations/samsungtv/samsungtv.classes.configflow.d.ts +5 -0
- package/dist_ts/integrations/samsungtv/samsungtv.classes.configflow.js +20 -7
- package/dist_ts/integrations/samsungtv/samsungtv.classes.integration.d.ts +1 -0
- package/dist_ts/integrations/samsungtv/samsungtv.classes.integration.js +53 -50
- package/dist_ts/integrations/samsungtv/samsungtv.types.d.ts +4 -1
- package/dist_ts/integrations/shelly/shelly.classes.client.d.ts +26 -30
- package/dist_ts/integrations/shelly/shelly.classes.client.js +504 -276
- package/dist_ts/integrations/shelly/shelly.classes.configflow.d.ts +8 -3
- package/dist_ts/integrations/shelly/shelly.classes.configflow.js +43 -38
- package/dist_ts/integrations/shelly/shelly.classes.integration.d.ts +6 -6
- package/dist_ts/integrations/shelly/shelly.classes.integration.js +103 -101
- package/dist_ts/integrations/shelly/shelly.classes.subscription.d.ts +4 -0
- package/dist_ts/integrations/shelly/shelly.classes.subscription.js +24 -2
- package/dist_ts/integrations/shelly/shelly.mapper.d.ts +1 -2
- package/dist_ts/integrations/shelly/shelly.mapper.js +21 -20
- package/dist_ts/integrations/shelly/shelly.rpc.js +4 -1
- package/dist_ts/integrations/shelly/shelly.types.d.ts +42 -2
- package/dist_ts/integrations/shelly/shelly.types.js +11 -2
- package/dist_ts/integrations/shelly/shelly.validation.d.ts +7 -0
- package/dist_ts/integrations/shelly/shelly.validation.js +79 -0
- package/dist_ts/integrations/sonos/sonos.classes.client.d.ts +79 -22
- package/dist_ts/integrations/sonos/sonos.classes.client.js +427 -139
- package/dist_ts/integrations/sonos/sonos.classes.configflow.d.ts +9 -2
- package/dist_ts/integrations/sonos/sonos.classes.configflow.js +26 -28
- package/dist_ts/integrations/sonos/sonos.classes.integration.d.ts +7 -6
- package/dist_ts/integrations/sonos/sonos.classes.integration.js +289 -224
- package/dist_ts/integrations/sonos/sonos.config.d.ts +6 -1
- package/dist_ts/integrations/sonos/sonos.config.js +23 -2
- package/dist_ts/integrations/sonos/sonos.mapper.d.ts +1 -1
- package/dist_ts/integrations/sonos/sonos.mapper.js +14 -9
- package/dist_ts/integrations/sonos/sonos.matchers.js +3 -3
- package/dist_ts/integrations/sonos/sonos.types.d.ts +26 -1
- package/dist_ts/integrations/soundtouch/soundtouch.classes.client.d.ts +13 -36
- package/dist_ts/integrations/soundtouch/soundtouch.classes.client.js +179 -502
- package/dist_ts/integrations/soundtouch/soundtouch.classes.configflow.d.ts +6 -1
- package/dist_ts/integrations/soundtouch/soundtouch.classes.configflow.js +50 -40
- package/dist_ts/integrations/soundtouch/soundtouch.classes.integration.d.ts +14 -15
- package/dist_ts/integrations/soundtouch/soundtouch.classes.integration.js +108 -128
- package/dist_ts/integrations/soundtouch/soundtouch.discovery.d.ts +2 -20
- package/dist_ts/integrations/soundtouch/soundtouch.discovery.js +23 -113
- package/dist_ts/integrations/soundtouch/soundtouch.mapper.d.ts +3 -12
- package/dist_ts/integrations/soundtouch/soundtouch.mapper.js +35 -189
- package/dist_ts/integrations/soundtouch/soundtouch.types.d.ts +57 -142
- package/dist_ts/integrations/soundtouch/soundtouch.types.js +18 -3
- package/dist_ts/integrations/soundtouch/soundtouch.validation.d.ts +22 -0
- package/dist_ts/integrations/soundtouch/soundtouch.validation.js +65 -0
- package/dist_ts/integrations/tasmota/tasmota.classes.integration.d.ts +5 -0
- package/dist_ts/integrations/tasmota/tasmota.classes.integration.js +23 -7
- package/dist_ts/integrations/tasmota/tasmota.classes.runtime.d.ts +19 -5
- package/dist_ts/integrations/tasmota/tasmota.classes.runtime.js +115 -41
- package/dist_ts/integrations/tasmota/tasmota.config.js +23 -1
- package/dist_ts/integrations/tasmota/tasmota.credentials.d.ts +11 -0
- package/dist_ts/integrations/tasmota/tasmota.credentials.js +97 -0
- package/dist_ts/integrations/tasmota/tasmota.discovery.d.ts +7 -1
- package/dist_ts/integrations/tasmota/tasmota.discovery.js +108 -30
- package/dist_ts/integrations/tasmota/tasmota.mapper.d.ts +2 -2
- package/dist_ts/integrations/tasmota/tasmota.mapper.js +7 -7
- package/dist_ts/integrations/tasmota/tasmota.types.d.ts +41 -2
- package/dist_ts/integrations/tuya/tuya.classes.integration.d.ts +4 -1
- package/dist_ts/integrations/tuya/tuya.classes.integration.js +79 -110
- package/dist_ts/integrations/tuya/tuya.classes.runtime.d.ts +5 -2
- package/dist_ts/integrations/tuya/tuya.classes.runtime.js +73 -26
- package/dist_ts/integrations/tuya/tuya.mapper.js +2 -2
- package/dist_ts/integrations/tuya/tuya.types.d.ts +3 -0
- package/dist_ts/integrations/tuya/tuya.validation.d.ts +2 -2
- package/dist_ts/integrations/tuya/tuya.validation.js +6 -11
- package/dist_ts/integrations/webostv/webostv.classes.client.d.ts +6 -5
- package/dist_ts/integrations/webostv/webostv.classes.client.js +147 -76
- package/dist_ts/integrations/webostv/webostv.classes.configflow.d.ts +7 -2
- package/dist_ts/integrations/webostv/webostv.classes.configflow.js +36 -20
- package/dist_ts/integrations/webostv/webostv.classes.integration.d.ts +4 -4
- package/dist_ts/integrations/webostv/webostv.classes.integration.js +46 -54
- package/dist_ts/integrations/webostv/webostv.mapper.js +5 -5
- package/dist_ts/integrations/webostv/webostv.types.d.ts +16 -3
- package/dist_ts/integrations/webostv/webostv.types.js +1 -1
- package/dist_ts/integrations/webostv/webostv.validation.d.ts +3 -1
- package/dist_ts/integrations/webostv/webostv.validation.js +23 -1
- package/dist_ts/integrations/wiz/index.d.ts +1 -0
- package/dist_ts/integrations/wiz/index.js +2 -1
- package/dist_ts/integrations/wiz/wiz.classes.client.d.ts +14 -28
- package/dist_ts/integrations/wiz/wiz.classes.client.js +93 -229
- package/dist_ts/integrations/wiz/wiz.classes.configflow.d.ts +6 -4
- package/dist_ts/integrations/wiz/wiz.classes.configflow.js +38 -67
- package/dist_ts/integrations/wiz/wiz.classes.integration.d.ts +10 -21
- package/dist_ts/integrations/wiz/wiz.classes.integration.js +204 -44
- package/dist_ts/integrations/wiz/wiz.discovery.d.ts +10 -31
- package/dist_ts/integrations/wiz/wiz.discovery.js +48 -249
- package/dist_ts/integrations/wiz/wiz.mapper.d.ts +7 -51
- package/dist_ts/integrations/wiz/wiz.mapper.js +82 -668
- package/dist_ts/integrations/wiz/wiz.types.d.ts +34 -208
- package/dist_ts/integrations/wiz/wiz.validation.d.ts +8 -0
- package/dist_ts/integrations/wiz/wiz.validation.js +72 -0
- package/dist_ts/integrations/yamaha/index.d.ts +1 -0
- package/dist_ts/integrations/yamaha/index.js +2 -1
- package/dist_ts/integrations/yamaha/yamaha.classes.client.d.ts +30 -4
- package/dist_ts/integrations/yamaha/yamaha.classes.client.js +162 -6
- package/dist_ts/integrations/yamaha/yamaha.classes.configflow.d.ts +10 -3
- package/dist_ts/integrations/yamaha/yamaha.classes.configflow.js +106 -6
- package/dist_ts/integrations/yamaha/yamaha.classes.integration.d.ts +22 -4
- package/dist_ts/integrations/yamaha/yamaha.classes.integration.js +243 -8
- package/dist_ts/integrations/yamaha/yamaha.discovery.d.ts +13 -1
- package/dist_ts/integrations/yamaha/yamaha.discovery.js +57 -4
- package/dist_ts/integrations/yamaha/yamaha.mapper.d.ts +16 -8
- package/dist_ts/integrations/yamaha/yamaha.mapper.js +58 -12
- package/dist_ts/integrations/yamaha/yamaha.types.d.ts +73 -6
- package/dist_ts/integrations/yamaha/yamaha.types.js +8 -94
- package/dist_ts/integrations/yamaha/yamaha.validation.d.ts +12 -0
- package/dist_ts/integrations/yamaha/yamaha.validation.js +89 -0
- package/dist_ts/integrations/zigbee2mqtt/zigbee2mqtt.classes.configflow.d.ts +6 -1
- package/dist_ts/integrations/zigbee2mqtt/zigbee2mqtt.classes.configflow.js +18 -8
- package/dist_ts/integrations/zigbee2mqtt/zigbee2mqtt.classes.integration.d.ts +4 -1
- package/dist_ts/integrations/zigbee2mqtt/zigbee2mqtt.classes.integration.js +54 -6
- package/dist_ts/integrations/zigbee2mqtt/zigbee2mqtt.classes.runtime.d.ts +12 -6
- package/dist_ts/integrations/zigbee2mqtt/zigbee2mqtt.classes.runtime.js +73 -59
- package/dist_ts/integrations/zigbee2mqtt/zigbee2mqtt.mapper.d.ts +3 -2
- package/dist_ts/integrations/zigbee2mqtt/zigbee2mqtt.mapper.js +17 -12
- package/dist_ts/integrations/zigbee2mqtt/zigbee2mqtt.types.d.ts +31 -2
- package/dist_ts/integrations/zigbee2mqtt/zigbee2mqtt.validation.d.ts +9 -1
- package/dist_ts/integrations/zigbee2mqtt/zigbee2mqtt.validation.js +57 -7
- package/dist_ts/integrations/zwave_js/zwave_js.classes.client.d.ts +10 -4
- package/dist_ts/integrations/zwave_js/zwave_js.classes.client.js +167 -31
- package/dist_ts/integrations/zwave_js/zwave_js.classes.configflow.d.ts +6 -1
- package/dist_ts/integrations/zwave_js/zwave_js.classes.configflow.js +57 -35
- package/dist_ts/integrations/zwave_js/zwave_js.classes.integration.d.ts +3 -1
- package/dist_ts/integrations/zwave_js/zwave_js.classes.integration.js +59 -37
- package/dist_ts/integrations/zwave_js/zwave_js.discovery.js +3 -3
- package/dist_ts/integrations/zwave_js/zwave_js.mapper.js +41 -24
- package/dist_ts/integrations/zwave_js/zwave_js.types.d.ts +25 -3
- package/dist_ts/integrations/zwave_js/zwave_js.validation.d.ts +6 -2
- package/dist_ts/integrations/zwave_js/zwave_js.validation.js +52 -17
- package/dist_ts/plugins.d.ts +2 -1
- package/dist_ts/plugins.js +3 -2
- package/package.json +9 -9
- package/readme.md +539 -8
- package/ts/00_commitinfo_data.ts +1 -1
- package/ts/core/classes.configstore.ts +84 -27
- package/ts/core/classes.nativehomeadmission.ts +96 -0
- package/ts/core/classes.nativeintegrationowner.ts +302 -0
- package/ts/core/classes.runtimemanager.ts +11 -2
- package/ts/core/index.ts +2 -0
- package/ts/core/types.ts +18 -0
- package/ts/integrations/braviatv/braviatv.classes.client.ts +245 -709
- package/ts/integrations/braviatv/braviatv.classes.configflow.ts +47 -76
- package/ts/integrations/braviatv/braviatv.classes.integration.ts +167 -147
- package/ts/integrations/braviatv/braviatv.discovery.ts +11 -10
- package/ts/integrations/braviatv/braviatv.mapper.ts +39 -142
- package/ts/integrations/braviatv/braviatv.types.ts +53 -173
- package/ts/integrations/braviatv/braviatv.validation.ts +68 -0
- package/ts/integrations/broadlink/broadlink.actions.ts +31 -0
- package/ts/integrations/broadlink/broadlink.classes.client.ts +170 -144
- package/ts/integrations/broadlink/broadlink.classes.configflow.ts +59 -89
- package/ts/integrations/broadlink/broadlink.classes.integration.ts +110 -64
- package/ts/integrations/broadlink/broadlink.mapper.ts +5 -4
- package/ts/integrations/broadlink/broadlink.selected.mapper.ts +46 -0
- package/ts/integrations/broadlink/broadlink.types.ts +48 -8
- package/ts/integrations/broadlink/broadlink.validation.ts +66 -0
- package/ts/integrations/daikin/daikin.classes.client.ts +52 -661
- package/ts/integrations/daikin/daikin.classes.configflow.ts +72 -152
- package/ts/integrations/daikin/daikin.classes.integration.ts +166 -71
- package/ts/integrations/daikin/daikin.discovery.ts +94 -346
- package/ts/integrations/daikin/daikin.mapper.ts +83 -166
- package/ts/integrations/daikin/daikin.types.ts +27 -48
- package/ts/integrations/daikin/daikin.validation.ts +51 -0
- package/ts/integrations/daikin/readme.md +83 -0
- package/ts/integrations/deconz/deconz.classes.client.ts +348 -252
- package/ts/integrations/deconz/deconz.classes.configflow.ts +57 -29
- package/ts/integrations/deconz/deconz.classes.integration.ts +69 -37
- package/ts/integrations/deconz/deconz.mapper.ts +22 -5
- package/ts/integrations/deconz/deconz.types.ts +49 -6
- package/ts/integrations/deconz/deconz.validation.ts +177 -4
- package/ts/integrations/deconz/readme.md +15 -1
- package/ts/integrations/denonavr/denonavr.classes.client.ts +132 -421
- package/ts/integrations/denonavr/denonavr.classes.configflow.ts +44 -37
- package/ts/integrations/denonavr/denonavr.classes.integration.ts +171 -117
- package/ts/integrations/denonavr/denonavr.discovery.ts +67 -220
- package/ts/integrations/denonavr/denonavr.mapper.ts +36 -182
- package/ts/integrations/denonavr/denonavr.types.ts +48 -111
- package/ts/integrations/denonavr/denonavr.validation.ts +87 -0
- package/ts/integrations/esphome/esphome.classes.client.ts +179 -94
- package/ts/integrations/esphome/esphome.classes.configflow.ts +40 -33
- package/ts/integrations/esphome/esphome.classes.integration.ts +63 -36
- package/ts/integrations/esphome/esphome.discovery.ts +3 -12
- package/ts/integrations/esphome/esphome.mapper.ts +9 -7
- package/ts/integrations/esphome/esphome.types.ts +35 -6
- package/ts/integrations/esphome/esphome.validation.ts +66 -0
- package/ts/integrations/esphome/readme.md +20 -6
- package/ts/integrations/homeassistant/homeassistant.classes.client.ts +353 -125
- package/ts/integrations/homeassistant/homeassistant.classes.integration.ts +35 -18
- package/ts/integrations/homeassistant/homeassistant.classes.runtime.ts +50 -63
- package/ts/integrations/homeassistant/homeassistant.types.ts +17 -1
- package/ts/integrations/homeassistant/readme.md +22 -6
- package/ts/integrations/hue/hue.classes.client.ts +110 -41
- package/ts/integrations/hue/hue.classes.configflow.ts +12 -6
- package/ts/integrations/hue/hue.classes.integration.ts +92 -26
- package/ts/integrations/hue/hue.validation.ts +13 -3
- package/ts/integrations/hue/readme.md +3 -3
- package/ts/integrations/kodi/kodi.classes.client.ts +72 -352
- package/ts/integrations/kodi/kodi.classes.configflow.ts +52 -55
- package/ts/integrations/kodi/kodi.classes.integration.ts +97 -178
- package/ts/integrations/kodi/kodi.discovery.ts +49 -28
- package/ts/integrations/kodi/kodi.mapper.ts +52 -10
- package/ts/integrations/kodi/kodi.types.ts +33 -58
- package/ts/integrations/kodi/kodi.validation.ts +60 -0
- package/ts/integrations/modbus/modbus.classes.client.ts +100 -842
- package/ts/integrations/modbus/modbus.classes.configflow.ts +70 -41
- package/ts/integrations/modbus/modbus.classes.integration.ts +115 -60
- package/ts/integrations/modbus/modbus.discovery.ts +38 -106
- package/ts/integrations/modbus/modbus.mapper.ts +64 -467
- package/ts/integrations/modbus/modbus.types.ts +64 -381
- package/ts/integrations/modbus/modbus.validation.ts +119 -0
- package/ts/integrations/mqtt/mqtt.classes.client.ts +136 -19
- package/ts/integrations/mqtt/mqtt.classes.configflow.ts +45 -32
- package/ts/integrations/mqtt/mqtt.classes.integration.ts +99 -38
- package/ts/integrations/mqtt/mqtt.classes.session.ts +118 -0
- package/ts/integrations/mqtt/mqtt.mapper.ts +43 -1
- package/ts/integrations/mqtt/mqtt.types.ts +52 -0
- package/ts/integrations/mqtt/mqtt.validation.ts +157 -0
- package/ts/integrations/mqtt/readme.md +12 -3
- package/ts/integrations/nanoleaf/nanoleaf.classes.client.ts +115 -249
- package/ts/integrations/nanoleaf/nanoleaf.classes.configflow.ts +90 -19
- package/ts/integrations/nanoleaf/nanoleaf.classes.integration.ts +130 -181
- package/ts/integrations/nanoleaf/nanoleaf.credentials.ts +39 -0
- package/ts/integrations/nanoleaf/nanoleaf.mapper.ts +47 -260
- package/ts/integrations/nanoleaf/nanoleaf.types.ts +29 -147
- package/ts/integrations/nanoleaf/nanoleaf.validation.ts +77 -0
- package/ts/integrations/onkyo/onkyo.classes.client.ts +148 -457
- package/ts/integrations/onkyo/onkyo.classes.configflow.ts +52 -6
- package/ts/integrations/onkyo/onkyo.classes.integration.ts +121 -12
- package/ts/integrations/onkyo/onkyo.discovery.ts +41 -3
- package/ts/integrations/onkyo/onkyo.mapper.ts +46 -19
- package/ts/integrations/onkyo/onkyo.types.ts +55 -110
- package/ts/integrations/onkyo/onkyo.validation.ts +85 -0
- package/ts/integrations/onkyo/readme.md +126 -0
- package/ts/integrations/panasonic_viera/panasonic_viera.classes.client.ts +101 -398
- package/ts/integrations/panasonic_viera/panasonic_viera.classes.configflow.ts +69 -5
- package/ts/integrations/panasonic_viera/panasonic_viera.classes.integration.ts +141 -9
- package/ts/integrations/panasonic_viera/panasonic_viera.discovery.ts +32 -3
- package/ts/integrations/panasonic_viera/panasonic_viera.mapper.ts +34 -161
- package/ts/integrations/panasonic_viera/panasonic_viera.types.ts +52 -96
- package/ts/integrations/panasonic_viera/panasonic_viera.validation.ts +56 -0
- package/ts/integrations/pioneer/pioneer.classes.client.ts +132 -428
- package/ts/integrations/pioneer/pioneer.classes.configflow.ts +67 -5
- package/ts/integrations/pioneer/pioneer.classes.integration.ts +225 -11
- package/ts/integrations/pioneer/pioneer.discovery.ts +45 -3
- package/ts/integrations/pioneer/pioneer.mapper.ts +57 -13
- package/ts/integrations/pioneer/pioneer.types.ts +87 -85
- package/ts/integrations/pioneer/pioneer.validation.ts +84 -0
- package/ts/integrations/samsungtv/readme.md +11 -5
- package/ts/integrations/samsungtv/samsungtv.classes.client.ts +40 -15
- package/ts/integrations/samsungtv/samsungtv.classes.configflow.ts +21 -6
- package/ts/integrations/samsungtv/samsungtv.classes.integration.ts +53 -38
- package/ts/integrations/samsungtv/samsungtv.types.ts +4 -1
- package/ts/integrations/shelly/readme.md +22 -7
- package/ts/integrations/shelly/shelly.classes.client.ts +386 -269
- package/ts/integrations/shelly/shelly.classes.configflow.ts +43 -38
- package/ts/integrations/shelly/shelly.classes.integration.ts +92 -111
- package/ts/integrations/shelly/shelly.classes.subscription.ts +24 -1
- package/ts/integrations/shelly/shelly.mapper.ts +19 -20
- package/ts/integrations/shelly/shelly.rpc.ts +2 -0
- package/ts/integrations/shelly/shelly.types.ts +50 -2
- package/ts/integrations/shelly/shelly.validation.ts +76 -0
- package/ts/integrations/sonos/readme.md +117 -38
- package/ts/integrations/sonos/sonos.classes.client.ts +392 -123
- package/ts/integrations/sonos/sonos.classes.configflow.ts +25 -28
- package/ts/integrations/sonos/sonos.classes.integration.ts +211 -186
- package/ts/integrations/sonos/sonos.config.ts +24 -2
- package/ts/integrations/sonos/sonos.mapper.ts +13 -8
- package/ts/integrations/sonos/sonos.matchers.ts +2 -2
- package/ts/integrations/sonos/sonos.types.ts +30 -1
- package/ts/integrations/soundtouch/soundtouch.classes.client.ts +152 -571
- package/ts/integrations/soundtouch/soundtouch.classes.configflow.ts +47 -42
- package/ts/integrations/soundtouch/soundtouch.classes.integration.ts +95 -150
- package/ts/integrations/soundtouch/soundtouch.discovery.ts +21 -133
- package/ts/integrations/soundtouch/soundtouch.mapper.ts +36 -207
- package/ts/integrations/soundtouch/soundtouch.types.ts +50 -195
- package/ts/integrations/soundtouch/soundtouch.validation.ts +60 -0
- package/ts/integrations/tasmota/readme.md +16 -5
- package/ts/integrations/tasmota/tasmota.classes.integration.ts +24 -6
- package/ts/integrations/tasmota/tasmota.classes.runtime.ts +112 -43
- package/ts/integrations/tasmota/tasmota.config.ts +17 -0
- package/ts/integrations/tasmota/tasmota.credentials.ts +92 -0
- package/ts/integrations/tasmota/tasmota.discovery.ts +87 -24
- package/ts/integrations/tasmota/tasmota.mapper.ts +6 -6
- package/ts/integrations/tasmota/tasmota.types.ts +43 -2
- package/ts/integrations/tuya/readme.md +52 -10
- package/ts/integrations/tuya/tuya.classes.integration.ts +69 -98
- package/ts/integrations/tuya/tuya.classes.runtime.ts +70 -24
- package/ts/integrations/tuya/tuya.mapper.ts +1 -1
- package/ts/integrations/tuya/tuya.types.ts +4 -0
- package/ts/integrations/tuya/tuya.validation.ts +5 -8
- package/ts/integrations/webostv/readme.md +115 -22
- package/ts/integrations/webostv/webostv.classes.client.ts +123 -50
- package/ts/integrations/webostv/webostv.classes.configflow.ts +35 -15
- package/ts/integrations/webostv/webostv.classes.integration.ts +44 -37
- package/ts/integrations/webostv/webostv.mapper.ts +4 -4
- package/ts/integrations/webostv/webostv.types.ts +18 -3
- package/ts/integrations/webostv/webostv.validation.ts +22 -1
- package/ts/integrations/wiz/index.ts +1 -0
- package/ts/integrations/wiz/wiz.classes.client.ts +84 -254
- package/ts/integrations/wiz/wiz.classes.configflow.ts +39 -70
- package/ts/integrations/wiz/wiz.classes.integration.ts +189 -45
- package/ts/integrations/wiz/wiz.discovery.ts +48 -291
- package/ts/integrations/wiz/wiz.mapper.ts +81 -754
- package/ts/integrations/wiz/wiz.types.ts +24 -224
- package/ts/integrations/wiz/wiz.validation.ts +73 -0
- package/ts/integrations/yamaha/index.ts +1 -0
- package/ts/integrations/yamaha/yamaha.classes.client.ts +150 -6
- package/ts/integrations/yamaha/yamaha.classes.configflow.ts +100 -5
- package/ts/integrations/yamaha/yamaha.classes.integration.ts +236 -8
- package/ts/integrations/yamaha/yamaha.discovery.ts +52 -3
- package/ts/integrations/yamaha/yamaha.mapper.ts +65 -13
- package/ts/integrations/yamaha/yamaha.types.ts +77 -97
- package/ts/integrations/yamaha/yamaha.validation.ts +86 -0
- package/ts/integrations/zigbee2mqtt/readme.md +22 -27
- package/ts/integrations/zigbee2mqtt/zigbee2mqtt.classes.configflow.ts +16 -7
- package/ts/integrations/zigbee2mqtt/zigbee2mqtt.classes.integration.ts +53 -6
- package/ts/integrations/zigbee2mqtt/zigbee2mqtt.classes.runtime.ts +62 -49
- package/ts/integrations/zigbee2mqtt/zigbee2mqtt.mapper.ts +17 -11
- package/ts/integrations/zigbee2mqtt/zigbee2mqtt.types.ts +35 -2
- package/ts/integrations/zigbee2mqtt/zigbee2mqtt.validation.ts +53 -7
- package/ts/integrations/zwave_js/readme.md +42 -9
- package/ts/integrations/zwave_js/zwave_js.classes.client.ts +155 -31
- package/ts/integrations/zwave_js/zwave_js.classes.configflow.ts +55 -31
- package/ts/integrations/zwave_js/zwave_js.classes.integration.ts +57 -35
- package/ts/integrations/zwave_js/zwave_js.discovery.ts +2 -2
- package/ts/integrations/zwave_js/zwave_js.mapper.ts +37 -23
- package/ts/integrations/zwave_js/zwave_js.types.ts +26 -3
- package/ts/integrations/zwave_js/zwave_js.validation.ts +52 -18
- package/ts/plugins.ts +2 -1
|
@@ -1,117 +1,57 @@
|
|
|
1
|
-
export type
|
|
2
|
-
|
|
3
|
-
export type TDenonavrPowerState = 'ON' | 'STANDBY' | 'OFF' | string;
|
|
4
|
-
|
|
5
|
-
export type TDenonavrMediaState = 'on' | 'off' | 'playing' | 'paused' | 'stopped' | 'idle' | string;
|
|
6
|
-
|
|
7
|
-
export type TDenonavrReceiverType = 'avr' | 'avr-x' | 'avr-x-2016' | string;
|
|
8
|
-
|
|
9
|
-
export type TDenonavrCommand =
|
|
10
|
-
| 'turn_on'
|
|
11
|
-
| 'turn_off'
|
|
12
|
-
| 'volume_up'
|
|
13
|
-
| 'volume_down'
|
|
14
|
-
| 'set_volume'
|
|
15
|
-
| 'mute'
|
|
16
|
-
| 'select_source'
|
|
17
|
-
| 'play'
|
|
18
|
-
| 'pause'
|
|
19
|
-
| 'stop'
|
|
20
|
-
| 'play_pause'
|
|
21
|
-
| 'previous_track'
|
|
22
|
-
| 'next_track'
|
|
23
|
-
| 'get_command';
|
|
1
|
+
export type TDenonavrModel = 'AVR-1913' | 'AVR-2113CI' | 'NR1605' | 'SR5009';
|
|
24
2
|
|
|
25
3
|
export interface IDenonavrConfig {
|
|
26
|
-
|
|
27
|
-
|
|
4
|
+
homeId: string;
|
|
5
|
+
host: string;
|
|
6
|
+
descriptionUrl: string;
|
|
7
|
+
expectedUdn: string;
|
|
8
|
+
model: TDenonavrModel;
|
|
9
|
+
maximumVolumeDb: number;
|
|
28
10
|
name?: string;
|
|
29
|
-
model?: string;
|
|
30
|
-
manufacturer?: string;
|
|
31
|
-
serialNumber?: string;
|
|
32
|
-
receiverType?: TDenonavrReceiverType;
|
|
33
|
-
descriptionPort?: number;
|
|
34
|
-
showAllSources?: boolean;
|
|
35
|
-
zone2?: boolean;
|
|
36
|
-
zone3?: boolean;
|
|
37
|
-
sourceMap?: Record<string, string>;
|
|
38
|
-
receiverInfo?: IDenonavrReceiverInfo;
|
|
39
|
-
zones?: IDenonavrZoneState[];
|
|
40
|
-
snapshot?: IDenonavrSnapshot;
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
export interface IHomeAssistantDenonavrConfig extends IDenonavrConfig {}
|
|
44
|
-
|
|
45
|
-
export interface IDenonavrReceiverInfo {
|
|
46
|
-
host?: string;
|
|
47
11
|
port?: number;
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
modelName?: string;
|
|
52
|
-
modelNumber?: string;
|
|
53
|
-
serialNumber?: string;
|
|
54
|
-
receiverType?: TDenonavrReceiverType;
|
|
55
|
-
receiverPort?: number;
|
|
56
|
-
presentationUrl?: string;
|
|
57
|
-
softwareVersion?: string;
|
|
12
|
+
timeoutMs?: number;
|
|
13
|
+
maxPendingOperations?: number;
|
|
14
|
+
maxDescriptionBytes?: number;
|
|
58
15
|
}
|
|
59
16
|
|
|
60
|
-
export interface
|
|
61
|
-
title?: string;
|
|
62
|
-
artist?: string;
|
|
63
|
-
album?: string;
|
|
64
|
-
band?: string;
|
|
65
|
-
frequency?: string;
|
|
66
|
-
station?: string;
|
|
67
|
-
imageUrl?: string;
|
|
68
|
-
contentType?: string;
|
|
69
|
-
}
|
|
17
|
+
export interface IHomeAssistantDenonavrConfig extends IDenonavrConfig {}
|
|
70
18
|
|
|
71
|
-
export interface
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
source?: string;
|
|
80
|
-
sourceList?: string[];
|
|
81
|
-
sourceMap?: Record<string, string>;
|
|
82
|
-
soundMode?: string;
|
|
83
|
-
soundModeRaw?: string;
|
|
84
|
-
soundModeList?: string[];
|
|
85
|
-
dynamicEq?: boolean;
|
|
86
|
-
ecoMode?: string;
|
|
87
|
-
media?: IDenonavrMediaInfo;
|
|
88
|
-
available?: boolean;
|
|
19
|
+
export interface IDenonavrIdentity {
|
|
20
|
+
readonly udn: string;
|
|
21
|
+
readonly manufacturer: 'Denon' | 'Marantz';
|
|
22
|
+
readonly model: TDenonavrModel;
|
|
23
|
+
readonly name: string;
|
|
24
|
+
readonly serialNumber?: string;
|
|
25
|
+
readonly source: 'selected-upnp-description';
|
|
26
|
+
readonly observedAt: string;
|
|
89
27
|
}
|
|
90
28
|
|
|
29
|
+
/** Receiver settings are observations, not proof of electrical or acoustic effects. */
|
|
91
30
|
export interface IDenonavrSnapshot {
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
31
|
+
readonly homeId: string;
|
|
32
|
+
readonly host: string;
|
|
33
|
+
readonly port: number;
|
|
34
|
+
readonly name: string;
|
|
35
|
+
readonly identity: IDenonavrIdentity;
|
|
36
|
+
readonly power: 'on' | 'standby';
|
|
37
|
+
readonly muted: boolean;
|
|
38
|
+
readonly volumeDb: number | null;
|
|
39
|
+
readonly input: string;
|
|
40
|
+
readonly observedInputs: readonly string[];
|
|
41
|
+
readonly maximumVolumeDb: number;
|
|
42
|
+
readonly observedAt: string;
|
|
96
43
|
}
|
|
97
44
|
|
|
98
|
-
export
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
volumeDb?: number;
|
|
104
|
-
muted?: boolean;
|
|
105
|
-
path?: string;
|
|
106
|
-
}
|
|
45
|
+
export type TDenonavrControl =
|
|
46
|
+
| { feature: 'power'; value: boolean }
|
|
47
|
+
| { feature: 'muted'; value: boolean }
|
|
48
|
+
| { feature: 'volume_db'; value: number }
|
|
49
|
+
| { feature: 'input'; value: string };
|
|
107
50
|
|
|
108
|
-
export interface
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
parameter?: string;
|
|
113
|
-
command?: TDenonavrCommand;
|
|
114
|
-
timestamp: number;
|
|
51
|
+
export interface IDenonavrEffectResult {
|
|
52
|
+
readonly dispatch: 'written';
|
|
53
|
+
readonly outcome: 'observed-target' | 'indeterminate';
|
|
54
|
+
readonly snapshot: IDenonavrSnapshot;
|
|
115
55
|
}
|
|
116
56
|
|
|
117
57
|
export interface IDenonavrSsdpRecord {
|
|
@@ -122,21 +62,18 @@ export interface IDenonavrSsdpRecord {
|
|
|
122
62
|
upnp?: Record<string, string | undefined>;
|
|
123
63
|
}
|
|
124
64
|
|
|
125
|
-
export interface IDenonavrMdnsRecord {
|
|
126
|
-
name?: string;
|
|
127
|
-
type?: string;
|
|
128
|
-
host?: string;
|
|
129
|
-
port?: number;
|
|
130
|
-
txt?: Record<string, string | undefined>;
|
|
131
|
-
}
|
|
132
|
-
|
|
133
65
|
export interface IDenonavrManualEntry {
|
|
134
66
|
host?: string;
|
|
135
|
-
|
|
67
|
+
descriptionUrl?: string;
|
|
68
|
+
expectedUdn?: string;
|
|
136
69
|
id?: string;
|
|
137
70
|
name?: string;
|
|
138
71
|
manufacturer?: string;
|
|
139
72
|
model?: string;
|
|
140
|
-
serialNumber?: string;
|
|
141
73
|
metadata?: Record<string, unknown>;
|
|
142
74
|
}
|
|
75
|
+
|
|
76
|
+
export interface IDenonavrIntegrationOptions {
|
|
77
|
+
readonly homeId?: string;
|
|
78
|
+
readonly capacity?: number;
|
|
79
|
+
}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import * as plugins from '../../plugins.js';
|
|
2
|
+
import type { IDenonavrConfig, TDenonavrControl, TDenonavrModel } from './denonavr.types.js';
|
|
3
|
+
|
|
4
|
+
export function denonavrRecord(value: unknown, allowed?: readonly string[]): Record<string, unknown> {
|
|
5
|
+
if (!value || typeof value !== 'object' || Array.isArray(value) || ![Object.prototype, null].includes(Object.getPrototypeOf(value))) {
|
|
6
|
+
throw new TypeError('DenonAVR input must be a plain data object.');
|
|
7
|
+
}
|
|
8
|
+
const result: Record<string, unknown> = {};
|
|
9
|
+
for (const key of Reflect.ownKeys(value)) {
|
|
10
|
+
const descriptor = Object.getOwnPropertyDescriptor(value, key);
|
|
11
|
+
if (typeof key !== 'string' || (allowed && !allowed.includes(key)) || !descriptor || !('value' in descriptor) || !descriptor.enumerable) {
|
|
12
|
+
throw new TypeError('DenonAVR input contains an unsupported field or accessor.');
|
|
13
|
+
}
|
|
14
|
+
Object.defineProperty(result, key, { value: descriptor.value, enumerable: true });
|
|
15
|
+
}
|
|
16
|
+
return result;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function denonavrText(value: unknown, label: string, maximum = 256): string {
|
|
20
|
+
if (typeof value !== 'string' || !value || value.length > maximum || value !== value.trim() || /[\p{Cc}\p{Cs}]/u.test(value)) {
|
|
21
|
+
throw new TypeError(`DenonAVR ${label} must be bounded text without control characters.`);
|
|
22
|
+
}
|
|
23
|
+
return value;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export function denonavrHost(value: unknown): string {
|
|
27
|
+
if (typeof value !== 'string' || plugins.net.isIP(value) !== 4) throw new TypeError('DenonAVR requires a selected private IPv4 address.');
|
|
28
|
+
const [first, second, , last] = value.split('.').map(Number);
|
|
29
|
+
if (!(first === 10 || first === 127 || (first === 172 && second >= 16 && second <= 31) || (first === 192 && second === 168)) || last === 0 || last === 255) {
|
|
30
|
+
throw new TypeError('DenonAVR requires a selected private or loopback host IPv4 address.');
|
|
31
|
+
}
|
|
32
|
+
return value;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export function denonavrModel(value: unknown): TDenonavrModel {
|
|
36
|
+
if (value !== 'AVR-1913' && value !== 'AVR-2113CI' && value !== 'NR1605' && value !== 'SR5009') throw new TypeError('DenonAVR model has no documented selected-receiver profile.');
|
|
37
|
+
return value;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export function denonavrUdn(value: unknown): string {
|
|
41
|
+
if (typeof value !== 'string' || !/^uuid:[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(value)) throw new TypeError('DenonAVR requires an exact UPnP UUID.');
|
|
42
|
+
return value.toLowerCase();
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export function denonavrDescription(value: unknown, host: string): string {
|
|
46
|
+
const text = denonavrText(value, 'description URL', 2048);
|
|
47
|
+
const url = new URL(text);
|
|
48
|
+
if (url.protocol !== 'http:' || url.hostname !== host || url.username || url.password || url.search || url.hash || url.pathname === '/') {
|
|
49
|
+
throw new TypeError('DenonAVR description URL must select an HTTP path at the receiver IP without credentials, query or fragment.');
|
|
50
|
+
}
|
|
51
|
+
return url.href;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export function denonavrVolume(value: unknown): number {
|
|
55
|
+
if (typeof value !== 'number' || !Number.isFinite(value) || value < -79.5 || value > 18 || !Number.isInteger(value * 2)) {
|
|
56
|
+
throw new TypeError('DenonAVR absolute volume must be -79.5 through 18 dB in half-decibel steps.');
|
|
57
|
+
}
|
|
58
|
+
return value;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function integer(value: unknown, fallback: number, minimum: number, maximum: number): number {
|
|
62
|
+
if (value === undefined) return fallback;
|
|
63
|
+
if (typeof value !== 'number' || !Number.isSafeInteger(value) || value < minimum || value > maximum) throw new RangeError('DenonAVR numeric bound is invalid.');
|
|
64
|
+
return value;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export function normalizeDenonavrConfig(value: unknown): Readonly<Required<Omit<IDenonavrConfig, 'name'>> & Pick<IDenonavrConfig, 'name'>> {
|
|
68
|
+
const config = denonavrRecord(value, ['homeId', 'host', 'descriptionUrl', 'expectedUdn', 'model', 'maximumVolumeDb', 'name', 'port', 'timeoutMs', 'maxPendingOperations', 'maxDescriptionBytes']);
|
|
69
|
+
const host = denonavrHost(config.host);
|
|
70
|
+
return Object.freeze({
|
|
71
|
+
homeId: denonavrText(config.homeId, 'home ID'), host,
|
|
72
|
+
descriptionUrl: denonavrDescription(config.descriptionUrl, host), expectedUdn: denonavrUdn(config.expectedUdn),
|
|
73
|
+
model: denonavrModel(config.model), maximumVolumeDb: denonavrVolume(config.maximumVolumeDb),
|
|
74
|
+
...(config.name === undefined ? {} : { name: denonavrText(config.name, 'name', 128) }),
|
|
75
|
+
port: integer(config.port, 23, 1, 65535), timeoutMs: integer(config.timeoutMs, 5000, 1, 60000),
|
|
76
|
+
maxPendingOperations: integer(config.maxPendingOperations, 16, 1, 128), maxDescriptionBytes: integer(config.maxDescriptionBytes, 65536, 256, 1048576),
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export function validateDenonavrControl(value: unknown): TDenonavrControl {
|
|
81
|
+
const control = denonavrRecord(value, ['feature', 'value']);
|
|
82
|
+
if (control.feature === 'power' && typeof control.value === 'boolean') return { feature: 'power', value: control.value };
|
|
83
|
+
if (control.feature === 'muted' && typeof control.value === 'boolean') return { feature: 'muted', value: control.value };
|
|
84
|
+
if (control.feature === 'volume_db') return { feature: 'volume_db', value: denonavrVolume(control.value) };
|
|
85
|
+
if (control.feature === 'input') return { feature: 'input', value: denonavrText(control.value, 'physical input', 25) };
|
|
86
|
+
throw new TypeError('DenonAVR supports one explicit power, mute, absolute volume or observed physical input control.');
|
|
87
|
+
}
|
|
@@ -1,10 +1,15 @@
|
|
|
1
1
|
import * as plugins from '../../plugins.js';
|
|
2
|
-
import type { IEsphomeClientCommand, IEsphomeCommandResult, IEsphomeConfig, IEsphomeEntityDescriptor, IEsphomeEntityState, IEsphomeEvent, IEsphomeSnapshot } from './esphome.types.js';
|
|
3
|
-
import {
|
|
2
|
+
import type { IEsphomeClientCommand, IEsphomeCommandResult, IEsphomeConfig, IEsphomeEntityDescriptor, IEsphomeEntityState, IEsphomeEvent, IEsphomeSnapshot, TEsphomeCredentials } from './esphome.types.js';
|
|
3
|
+
import { captureEsphomeConfig, captureEsphomeCredentials, esphomeMac } from './esphome.validation.js';
|
|
4
4
|
|
|
5
5
|
type TEsphomeEventHandler = (eventArg: IEsphomeEvent) => void;
|
|
6
6
|
type TNativeClient = plugins.esphomeNativeApi.EspHomeClient;
|
|
7
7
|
type TNativeEntity = plugins.esphomeNativeApi.Entity;
|
|
8
|
+
interface IEsphomeCleanup {
|
|
9
|
+
readonly cleanup: () => void;
|
|
10
|
+
failed: boolean;
|
|
11
|
+
promise?: Promise<void>;
|
|
12
|
+
}
|
|
8
13
|
|
|
9
14
|
const readablePlatforms = new Set(['light', 'switch', 'fan', 'cover', 'climate', 'number', 'select', 'text', 'sensor', 'binary_sensor', 'text_sensor']);
|
|
10
15
|
const writablePlatforms = new Set(['light', 'switch', 'fan', 'cover', 'number', 'select', 'text']);
|
|
@@ -12,12 +17,12 @@ const writablePlatforms = new Set(['light', 'switch', 'fan', 'cover', 'number',
|
|
|
12
17
|
export class EsphomeClient {
|
|
13
18
|
private readonly connection: EsphomeNativeApiConnection;
|
|
14
19
|
|
|
15
|
-
constructor(configArg: IEsphomeConfig, nativeClientArg?: TNativeClient) {
|
|
16
|
-
this.connection = new EsphomeNativeApiConnection(configArg, nativeClientArg);
|
|
20
|
+
constructor(configArg: IEsphomeConfig, nativeClientArg?: TNativeClient, credentialsArg: TEsphomeCredentials = {}) {
|
|
21
|
+
this.connection = new EsphomeNativeApiConnection(configArg, nativeClientArg, credentialsArg);
|
|
17
22
|
}
|
|
18
23
|
|
|
19
|
-
public
|
|
20
|
-
|
|
24
|
+
public connectLive(signalArg?: AbortSignal): Promise<void> {
|
|
25
|
+
return this.connection.connect(signalArg);
|
|
21
26
|
}
|
|
22
27
|
|
|
23
28
|
public isHealthy(): boolean {
|
|
@@ -28,16 +33,20 @@ export class EsphomeClient {
|
|
|
28
33
|
return this.connection.getSnapshot();
|
|
29
34
|
}
|
|
30
35
|
|
|
31
|
-
public onEvent(handlerArg: TEsphomeEventHandler): () => void {
|
|
32
|
-
return this.connection.onEvent(handlerArg);
|
|
36
|
+
public onEvent(handlerArg: TEsphomeEventHandler, signalArg?: AbortSignal): () => void {
|
|
37
|
+
return this.connection.onEvent(handlerArg, signalArg);
|
|
33
38
|
}
|
|
34
39
|
|
|
35
40
|
public async sendCommand(commandArg: IEsphomeClientCommand, signalArg?: AbortSignal): Promise<IEsphomeCommandResult> {
|
|
36
41
|
return this.connection.sendCommand(commandArg, signalArg);
|
|
37
42
|
}
|
|
38
43
|
|
|
39
|
-
public
|
|
40
|
-
|
|
44
|
+
public destroy(): Promise<void> {
|
|
45
|
+
return this.connection.destroy();
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
public retryCleanup(): Promise<void> {
|
|
49
|
+
return this.connection.retryCleanup();
|
|
41
50
|
}
|
|
42
51
|
}
|
|
43
52
|
|
|
@@ -48,83 +57,88 @@ export class EsphomeNativeApiConnection {
|
|
|
48
57
|
private readonly setupTimeoutMs: number;
|
|
49
58
|
private readonly commandTimeoutMs: number;
|
|
50
59
|
private readonly requireEncryption: boolean;
|
|
60
|
+
private readonly config: IEsphomeConfig;
|
|
51
61
|
private readonly handlers = new Set<TEsphomeEventHandler>();
|
|
52
|
-
private readonly
|
|
62
|
+
private readonly cleanup = new Set<IEsphomeCleanup>();
|
|
63
|
+
private readonly operations = new Set<Promise<unknown>>();
|
|
53
64
|
private readonly pendingCommands = new Set<string>();
|
|
54
65
|
private readonly lifetime = new AbortController();
|
|
55
66
|
private ready = false;
|
|
56
67
|
private connecting = false;
|
|
57
68
|
private destroyed = false;
|
|
69
|
+
private subscriptionsStarted = false;
|
|
70
|
+
private identityMismatch = false;
|
|
71
|
+
private closePromise?: Promise<void>;
|
|
72
|
+
private retryPromise?: Promise<void>;
|
|
58
73
|
|
|
59
|
-
constructor(configArg: IEsphomeConfig, nativeClientArg?: TNativeClient) {
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
if (configArg.manualEntries && configArg.manualEntries.length > 1) {
|
|
68
|
-
throw new Error('Configure each ESPHome native API device as a separate integration instance.');
|
|
69
|
-
}
|
|
70
|
-
this.host = (configArg.host || manual?.host || '').trim();
|
|
71
|
-
if (!this.host || /\s/.test(this.host)) {
|
|
72
|
-
throw new Error('ESPHome native API requires a valid host.');
|
|
73
|
-
}
|
|
74
|
-
this.port = configArg.port ?? manual?.port ?? esphomeDefaultNativeApiPort;
|
|
75
|
-
if (!Number.isInteger(this.port) || this.port < 1 || this.port > 65535) {
|
|
76
|
-
throw new Error('ESPHome native API port must be an integer from 1 through 65535.');
|
|
77
|
-
}
|
|
78
|
-
this.setupTimeoutMs = this.timeout(configArg.initialSnapshotTimeoutMs, 15000);
|
|
79
|
-
this.commandTimeoutMs = this.timeout(configArg.commandTimeoutMs, 5000);
|
|
80
|
-
const keys = [configArg.encryptionKey, configArg.noisePsk, manual?.encryptionKey, manual?.noisePsk].filter((valueArg): valueArg is string => valueArg !== undefined);
|
|
81
|
-
if (new Set(keys).size > 1) throw new Error('ESPHome encryption key configuration is conflicting.');
|
|
82
|
-
const key = keys[0];
|
|
83
|
-
if (key !== undefined && (!/^[A-Za-z0-9+/]{43}=$/.test(key) || Buffer.from(key, 'base64').length !== 32 || Buffer.from(key, 'base64').toString('base64') !== key)) {
|
|
84
|
-
throw new Error('ESPHome encryption key must be a canonical base64-encoded 32-byte key.');
|
|
85
|
-
}
|
|
86
|
-
this.requireEncryption = key !== undefined;
|
|
74
|
+
constructor(configArg: IEsphomeConfig, nativeClientArg?: TNativeClient, credentialsArg: TEsphomeCredentials = {}) {
|
|
75
|
+
this.config = captureEsphomeConfig(configArg);
|
|
76
|
+
const credentials = captureEsphomeCredentials(credentialsArg);
|
|
77
|
+
this.host = this.config.host;
|
|
78
|
+
this.port = this.config.port ?? 6053;
|
|
79
|
+
this.setupTimeoutMs = this.config.initialSnapshotTimeoutMs ?? 15000;
|
|
80
|
+
this.commandTimeoutMs = this.config.commandTimeoutMs ?? 5000;
|
|
81
|
+
this.requireEncryption = credentials.psk != null;
|
|
87
82
|
this.native = nativeClientArg ?? new plugins.esphomeNativeApi.EspHomeClient({
|
|
88
83
|
host: this.host,
|
|
89
84
|
port: this.port,
|
|
90
|
-
psk:
|
|
91
|
-
serverName:
|
|
85
|
+
psk: credentials.psk,
|
|
86
|
+
serverName: this.config.deviceName,
|
|
92
87
|
connectTimeoutMs: this.setupTimeoutMs,
|
|
93
88
|
});
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
89
|
+
// Construction is inert: own the native client before invoking any subscription callbacks.
|
|
90
|
+
this.cleanup.add({ cleanup: () => this.native.disconnect(), failed: false });
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
private startSubscriptions(): void {
|
|
94
|
+
if (this.subscriptionsStarted) return;
|
|
95
|
+
this.subscriptionsStarted = true;
|
|
96
|
+
const telemetry = this.native.on('telemetry', (eventArg) => {
|
|
97
|
+
this.refreshReadiness();
|
|
98
|
+
if (!this.identityMismatch) this.emit({ type: 'state_changed', entityId: this.eventEntityId(eventArg), data: eventArg });
|
|
99
|
+
});
|
|
100
|
+
this.cleanup.add({ cleanup: () => telemetry[Symbol.dispose](), failed: false });
|
|
101
|
+
this.lifetime.signal.throwIfAborted();
|
|
102
|
+
const health = this.native.onHealthChange((healthArg) => {
|
|
103
|
+
if (healthArg.state === 'connected' && this.requireEncryption && !healthArg.encrypted) {
|
|
104
|
+
this.ready = false;
|
|
105
|
+
this.native.disconnect();
|
|
106
|
+
this.emit({ type: 'error', data: { message: 'ESPHome encryption is required; the plaintext session was rejected.' } });
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
this.refreshReadiness();
|
|
110
|
+
this.emit({ type: 'availability_changed', data: { available: this.isHealthy() } });
|
|
111
|
+
});
|
|
112
|
+
this.cleanup.add({ cleanup: () => health[Symbol.dispose](), failed: false });
|
|
113
|
+
this.lifetime.signal.throwIfAborted();
|
|
110
114
|
}
|
|
111
115
|
|
|
112
|
-
public
|
|
113
|
-
if (this.destroyed || this.connecting || this.isHealthy())
|
|
116
|
+
public connect(signalArg?: AbortSignal): Promise<void> {
|
|
117
|
+
if (this.destroyed || this.connecting || this.isHealthy()) return Promise.reject(new Error('ESPHome connection is destroyed, already connecting, or already connected.'));
|
|
114
118
|
this.connecting = true;
|
|
115
|
-
const
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
119
|
+
const operation = this.track(async () => {
|
|
120
|
+
const signal = AbortSignal.any([this.lifetime.signal, AbortSignal.timeout(this.setupTimeoutMs), ...(signalArg ? [signalArg] : [])]);
|
|
121
|
+
try {
|
|
122
|
+
signal.throwIfAborted();
|
|
123
|
+
this.startSubscriptions();
|
|
124
|
+
await this.native.connect({ signal });
|
|
125
|
+
if (this.requireEncryption && !this.native.health().encrypted) throw new Error('ESPHome encryption is required; the plaintext session was rejected.');
|
|
126
|
+
if (this.native.deviceInfo()?.usesPassword) throw new Error('ESPHome legacy API password authentication is unsupported.');
|
|
127
|
+
this.assertIdentity();
|
|
128
|
+
await this.waitForInitialStates(signal);
|
|
129
|
+
} catch {
|
|
130
|
+
// SDK diagnostics may contain credentials; do not expose their messages or causes.
|
|
131
|
+
throw new Error('ESPHome native setup failed or was cancelled.');
|
|
132
|
+
} finally {
|
|
133
|
+
this.connecting = false;
|
|
134
|
+
}
|
|
135
|
+
});
|
|
136
|
+
// Join cleanup only after the tracked connect operation settles, so cleanup never awaits itself.
|
|
137
|
+
return operation.catch(async (errorArg: unknown) => {
|
|
138
|
+
try { await this.destroy(); }
|
|
139
|
+
catch (cleanupErrorArg) { throw new AggregateError([errorArg, cleanupErrorArg], 'ESPHome setup and owned cleanup failed.'); }
|
|
140
|
+
throw errorArg;
|
|
141
|
+
});
|
|
128
142
|
}
|
|
129
143
|
|
|
130
144
|
public isHealthy(): boolean {
|
|
@@ -132,9 +146,11 @@ export class EsphomeNativeApiConnection {
|
|
|
132
146
|
}
|
|
133
147
|
|
|
134
148
|
public getSnapshot(): IEsphomeSnapshot {
|
|
149
|
+
if (this.identityMismatch) throw new Error('ESPHome selected device identity changed; native observations are unavailable.');
|
|
135
150
|
const connected = this.isHealthy();
|
|
136
151
|
const entities = this.native.entitiesByDevice(undefined).filter((entityArg) => readablePlatforms.has(entityArg.type));
|
|
137
152
|
return {
|
|
153
|
+
homeId: this.config.homeId,
|
|
138
154
|
host: this.host,
|
|
139
155
|
port: this.port,
|
|
140
156
|
connected,
|
|
@@ -155,13 +171,31 @@ export class EsphomeNativeApiConnection {
|
|
|
155
171
|
};
|
|
156
172
|
}
|
|
157
173
|
|
|
158
|
-
public onEvent(handlerArg: TEsphomeEventHandler): () => void {
|
|
174
|
+
public onEvent(handlerArg: TEsphomeEventHandler, signalArg?: AbortSignal): () => void {
|
|
159
175
|
if (this.destroyed) throw new Error('ESPHome connection was destroyed.');
|
|
176
|
+
signalArg?.throwIfAborted();
|
|
177
|
+
let active = true;
|
|
178
|
+
const dispose = (): void => {
|
|
179
|
+
if (!active) return;
|
|
180
|
+
signalArg?.removeEventListener('abort', dispose);
|
|
181
|
+
this.handlers.delete(handlerArg);
|
|
182
|
+
active = false;
|
|
183
|
+
this.cleanup.delete(resource);
|
|
184
|
+
};
|
|
185
|
+
const resource: IEsphomeCleanup = { cleanup: dispose, failed: false };
|
|
186
|
+
// Own the handler and caller-signal listener before the foreign registration method.
|
|
187
|
+
this.cleanup.add(resource);
|
|
160
188
|
this.handlers.add(handlerArg);
|
|
161
|
-
|
|
189
|
+
signalArg?.addEventListener('abort', dispose, { once: true });
|
|
190
|
+
if (signalArg?.aborted) dispose();
|
|
191
|
+
return dispose;
|
|
162
192
|
}
|
|
163
193
|
|
|
164
|
-
public
|
|
194
|
+
public sendCommand(commandArg: IEsphomeClientCommand, signalArg?: AbortSignal): Promise<IEsphomeCommandResult> {
|
|
195
|
+
return this.track(() => this.executeTrackedCommand(commandArg, signalArg));
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
private async executeTrackedCommand(commandArg: IEsphomeClientCommand, signalArg?: AbortSignal): Promise<IEsphomeCommandResult> {
|
|
165
199
|
signalArg?.throwIfAborted();
|
|
166
200
|
if (!this.isHealthy()) return { success: false, error: 'ESPHome is unavailable or has not received its authoritative initial states.' };
|
|
167
201
|
const entity = this.native.entitiesByDevice(undefined).find((entityArg) => entityArg.key === commandArg.key && entityArg.type === commandArg.platform && (entityArg.deviceId ?? 0) === (commandArg.deviceId ?? 0));
|
|
@@ -174,22 +208,56 @@ export class EsphomeNativeApiConnection {
|
|
|
174
208
|
try {
|
|
175
209
|
const state = await this.executeCommand(entity, commandArg.payload, signalArg);
|
|
176
210
|
return { success: true, data: structuredClone(state) };
|
|
177
|
-
} catch
|
|
178
|
-
return { success: false, error:
|
|
211
|
+
} catch {
|
|
212
|
+
return { success: false, error: 'ESPHome command failed or was cancelled; its physical effect is unknown.' };
|
|
179
213
|
} finally {
|
|
180
214
|
this.pendingCommands.delete(plugins.esphomeNativeApi.entityId(entity.type, entity.objectId));
|
|
181
215
|
}
|
|
182
216
|
}
|
|
183
217
|
|
|
184
|
-
public
|
|
185
|
-
if (this.
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
this.
|
|
218
|
+
public destroy(): Promise<void> {
|
|
219
|
+
if (!this.closePromise) {
|
|
220
|
+
this.destroyed = true;
|
|
221
|
+
this.ready = false;
|
|
222
|
+
this.closePromise = Promise.resolve().then(() => this.drain(false));
|
|
223
|
+
this.lifetime.abort(new Error('ESPHome connection was destroyed.'));
|
|
224
|
+
}
|
|
225
|
+
return this.closePromise;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
public retryCleanup(): Promise<void> {
|
|
229
|
+
if (!this.closePromise) throw new Error('ESPHome cleanup retry requires a closed client.');
|
|
230
|
+
if (!this.retryPromise) {
|
|
231
|
+
this.retryPromise = Promise.resolve().then(() => this.drain(true));
|
|
232
|
+
const settled = () => { this.retryPromise = undefined; };
|
|
233
|
+
void this.retryPromise.then(settled, settled);
|
|
234
|
+
}
|
|
235
|
+
return this.retryPromise;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
private track<T>(workArg: () => Promise<T>): Promise<T> {
|
|
239
|
+
if (this.destroyed) return Promise.reject(new Error('ESPHome connection was destroyed.'));
|
|
240
|
+
// Store the actual operation before invoking native or consumer callbacks.
|
|
241
|
+
const operation = Promise.resolve().then(workArg);
|
|
242
|
+
this.operations.add(operation);
|
|
243
|
+
const settled = () => this.operations.delete(operation);
|
|
244
|
+
void operation.then(settled, settled);
|
|
245
|
+
return operation;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
private async drain(retryArg: boolean): Promise<void> {
|
|
249
|
+
const cleanup = [...this.cleanup].reverse().map((resourceArg) => {
|
|
250
|
+
if (retryArg && resourceArg.failed) { resourceArg.promise = undefined; resourceArg.failed = false; }
|
|
251
|
+
resourceArg.promise ??= Promise.resolve().then(() => resourceArg.cleanup()).then(() => undefined, () => {
|
|
252
|
+
resourceArg.failed = true;
|
|
253
|
+
throw new Error('ESPHome owned resource cleanup failed.');
|
|
254
|
+
});
|
|
255
|
+
return resourceArg.promise;
|
|
256
|
+
});
|
|
257
|
+
const [results] = await Promise.all([Promise.allSettled(cleanup), Promise.allSettled([...this.operations])]);
|
|
192
258
|
this.handlers.clear();
|
|
259
|
+
const failures = results.filter((resultArg) => resultArg.status === 'rejected');
|
|
260
|
+
if (failures.length) throw new AggregateError(failures.map(() => new Error('ESPHome owned resource cleanup failed.')), 'ESPHome cleanup failed; resources remain retained for explicit retry.');
|
|
193
261
|
}
|
|
194
262
|
|
|
195
263
|
private async executeCommand(entityArg: TNativeEntity, payloadArg: Record<string, unknown>, signalArg?: AbortSignal): Promise<unknown> {
|
|
@@ -253,14 +321,32 @@ export class EsphomeNativeApiConnection {
|
|
|
253
321
|
|
|
254
322
|
private refreshReadiness(): void {
|
|
255
323
|
const health = this.native.health();
|
|
256
|
-
|
|
324
|
+
if (health.state === 'connected') {
|
|
325
|
+
try { this.assertIdentity(); }
|
|
326
|
+
catch {
|
|
327
|
+
this.identityMismatch = true;
|
|
328
|
+
this.ready = false;
|
|
329
|
+
this.native.disconnect();
|
|
330
|
+
this.emit({ type: 'error', data: { message: 'ESPHome selected native device identity changed.' } });
|
|
331
|
+
return;
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
this.ready = !this.destroyed && !this.identityMismatch && health.state === 'connected' && (!this.requireEncryption || health.encrypted) && this.native.entitiesByDevice(undefined).filter((entityArg) => readablePlatforms.has(entityArg.type)).every((entityArg) => this.native.latest(plugins.esphomeNativeApi.entityId(entityArg.type, entityArg.objectId)) != null);
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
private assertIdentity(): void {
|
|
338
|
+
const info = this.native.deviceInfo();
|
|
339
|
+
if (!info || esphomeMac(info.macAddress) !== this.config.expectedMacAddress || info.name !== this.config.deviceName
|
|
340
|
+
|| (this.config.expectedModel !== undefined && info.model !== this.config.expectedModel)) {
|
|
341
|
+
throw new Error('ESPHome native device identity does not match the selected endpoint.');
|
|
342
|
+
}
|
|
257
343
|
}
|
|
258
344
|
|
|
259
345
|
private entityId(entityArg: TNativeEntity): string {
|
|
260
346
|
const macAddress = this.native.deviceInfo()?.macAddress?.toLowerCase().replace(/[^a-f0-9]/g, '');
|
|
261
347
|
const identity = macAddress?.length === 12 ? macAddress : `${this.host}:${this.port}`;
|
|
262
348
|
const digest = plugins.crypto.createHash('sha256').update(JSON.stringify([identity, entityArg.type, entityArg.key, entityArg.deviceId ?? 0])).digest('hex').slice(0, 24);
|
|
263
|
-
return
|
|
349
|
+
return `home.${encodeURIComponent(this.config.homeId)}.${entityArg.type}.esphome_${digest}`;
|
|
264
350
|
}
|
|
265
351
|
|
|
266
352
|
private eventEntityId(eventArg: plugins.esphomeNativeApi.TelemetryEvent): string | undefined {
|
|
@@ -285,12 +371,11 @@ export class EsphomeNativeApiConnection {
|
|
|
285
371
|
}
|
|
286
372
|
|
|
287
373
|
private emit(eventArg: IEsphomeEvent): void {
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
if (
|
|
294
|
-
return value;
|
|
374
|
+
const failures: unknown[] = [];
|
|
375
|
+
for (const handler of this.handlers) {
|
|
376
|
+
try { handler({ ...structuredClone(eventArg), timestamp: Date.now() }); }
|
|
377
|
+
catch (error) { failures.push(error); }
|
|
378
|
+
}
|
|
379
|
+
if (failures.length) throw new AggregateError(failures.map(() => new Error('ESPHome subscriber callback failed.')), 'ESPHome event callbacks failed.');
|
|
295
380
|
}
|
|
296
381
|
}
|