@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
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
# Selected native Onkyo integration
|
|
2
|
+
|
|
3
|
+
`OnkyoIntegration` uses the published `OnkyoProtocol` from
|
|
4
|
+
`@ecobridge.xyz/devicemanager@4.5.0` for one selected Onkyo receiver main zone over
|
|
5
|
+
a persistent eISCP TCP connection. Device definitions use the canonical `tcp`
|
|
6
|
+
protocol from `@smarthome.exchange/interfaces@2.1.0`.
|
|
7
|
+
|
|
8
|
+
```typescript
|
|
9
|
+
import { OnkyoIntegration } from '@smarthome.exchange/integrations';
|
|
10
|
+
|
|
11
|
+
const integration = new OnkyoIntegration();
|
|
12
|
+
const runtime = await integration.setup({
|
|
13
|
+
homeId: 'home-1',
|
|
14
|
+
host: '192.168.1.40',
|
|
15
|
+
macAddress: '0009B0D24B75',
|
|
16
|
+
model: 'TX-NR609',
|
|
17
|
+
allowUnencryptedLan: true,
|
|
18
|
+
volumeLimitRaw: 30,
|
|
19
|
+
}, { homeId: 'home-1' });
|
|
20
|
+
try {
|
|
21
|
+
const device = (await runtime.devices())[0];
|
|
22
|
+
const result = await runtime.callService!({
|
|
23
|
+
domain: 'onkyo', service: 'set_power',
|
|
24
|
+
target: { deviceId: device.id }, data: { power: true },
|
|
25
|
+
});
|
|
26
|
+
console.log(result);
|
|
27
|
+
} finally {
|
|
28
|
+
await integration.destroy();
|
|
29
|
+
}
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
The embedding host must select `context.homeId` independently of configuration,
|
|
33
|
+
form values, discovery or store scope. Setup and config flow require that explicit
|
|
34
|
+
selection; configuration must match it. Construction without options is inert.
|
|
35
|
+
An optional constructor `{ homeId }` adds a consistency binding and still requires
|
|
36
|
+
the explicit context. One instance binds synchronously to its first validated host
|
|
37
|
+
home, including when later receiver setup fails. Flow and setup share that binding.
|
|
38
|
+
This trusted host contract is not authentication by a home-id string.
|
|
39
|
+
|
|
40
|
+
Configuration is captured plain data. It requires a matching `homeId`, one
|
|
41
|
+
private/link-local/loopback unicast IPv4 `host`, twelve hexadecimal MAC digits,
|
|
42
|
+
the exact selected `model`, `allowUnencryptedLan: true`, and an integer
|
|
43
|
+
`volumeLimitRaw` from 0 through 100. Optional `port` defaults to 60128;
|
|
44
|
+
`requestTimeoutMs` defaults to 5000 and accepts 1–60000; `maxPendingOperations`
|
|
45
|
+
defaults to 16 and accepts 1–128. `name` is optional. Unknown keys, accessors,
|
|
46
|
+
hidden properties, injected clients/executors/snapshots, hostname resolution,
|
|
47
|
+
and configurable source/volume conversion maps are rejected.
|
|
48
|
+
|
|
49
|
+
Setup checks live NRI identity and an enabled main zone. Every control checks
|
|
50
|
+
fresh identity and capabilities through the public protocol. MAC/model binding
|
|
51
|
+
selects the receiver; eISCP does not authenticate or encrypt the LAN peer.
|
|
52
|
+
|
|
53
|
+
Device ids are `onkyo:<encoded homeId>:<uppercase MAC>` and the media entity id
|
|
54
|
+
adds `:main`. Every service needs an exact owned device or entity target. If
|
|
55
|
+
both target ids are supplied, both must match. Feature writes validate the
|
|
56
|
+
owned home/device and a fresh canonical writable feature; caller-provided
|
|
57
|
+
`writable` flags cannot grant capability.
|
|
58
|
+
|
|
59
|
+
| Service | Exact data |
|
|
60
|
+
| --- | --- |
|
|
61
|
+
| `onkyo.status`, `onkyo.snapshot`, `onkyo.refresh` | Empty or absent data; live sequential inspection |
|
|
62
|
+
| `onkyo.set_power` | `{ power: boolean }` |
|
|
63
|
+
| `onkyo.set_mute`, `onkyo.volume_mute` | `{ muted: boolean }` |
|
|
64
|
+
| `onkyo.set_volume_raw` | `{ volumeRaw: integer }` |
|
|
65
|
+
| `onkyo.set_input`, `onkyo.select_source` | `{ inputId: '10' }`; advertised two-digit selector id |
|
|
66
|
+
|
|
67
|
+
The same control services accept the `media_player` domain. Feature ids are
|
|
68
|
+
`power`, `mute`, `volume_raw`, and `input`, when observed. Identity, connectivity,
|
|
69
|
+
and advertised input lists are read-only. Unavailable reports produce absent
|
|
70
|
+
state features. Power/mute need current boolean reports; input writes need
|
|
71
|
+
current selector availability and enabled advertised main-zone choices.
|
|
72
|
+
|
|
73
|
+
Volume values are absolute integer receiver wire units. Writes require an
|
|
74
|
+
advertised `volstep="1"`, a positive `volmax` at most 100, and an observed raw
|
|
75
|
+
level consistent with that contract. The configured ceiling and current
|
|
76
|
+
advertised maximum both apply. Unknown/half-step contracts stay read-only.
|
|
77
|
+
There is no normalized Home Assistant `volume_level`, percent conversion,
|
|
78
|
+
guessed model scaling, increment, or toggle control.
|
|
79
|
+
|
|
80
|
+
Success returns a matching receiver report after dispatch with
|
|
81
|
+
`observation: 'matching-receiver-state'`, `commandAcknowledged: false`, and
|
|
82
|
+
`causalityConfirmed: false`. eISCP has no request id; this report does not prove
|
|
83
|
+
the command caused a physical change. Failures retain `effectOutcome` from the
|
|
84
|
+
protocol: `not-sent` before dispatch or `indeterminate` after dispatch. A
|
|
85
|
+
different report never counts as success. Controls are never retried or
|
|
86
|
+
replayed. Inspection is sequential and `observedAt` records its completion.
|
|
87
|
+
|
|
88
|
+
`subscribe()` emits observations from explicit inspection/control calls, plus
|
|
89
|
+
operation errors. It opens no polling timer and provides no unsolicited push
|
|
90
|
+
subscription. Subscriber cancellation unregisters its listener; runtime,
|
|
91
|
+
integration, or caller lifetime cancellation joins active/queued operations
|
|
92
|
+
and terminal protocol/socket cleanup. Shutdown is idempotent. A terminal
|
|
93
|
+
protocol cannot reconnect; create a new authorized runtime explicitly.
|
|
94
|
+
Integration shutdown collects cleanup failures, waits for every admitted resource,
|
|
95
|
+
pending setup, config-flow submission and active client operation to settle, then
|
|
96
|
+
reports the failures together in an `AggregateError`. Repeated shutdown returns
|
|
97
|
+
the same terminal promise. Failed cleanup stays owned and consumes the shared
|
|
98
|
+
capacity (16 by default; constructor `capacity` is a positive integer).
|
|
99
|
+
`retryCleanup()` explicitly retries retained cleanup handles, never receiver effects.
|
|
100
|
+
Closing is terminal and cannot select a different home or reopen an old form.
|
|
101
|
+
|
|
102
|
+
Manual/custom discovery candidates must supply `integrationDomain: 'onkyo'`,
|
|
103
|
+
selected host, MAC and model. The descriptor does no automatic network scan.
|
|
104
|
+
`probeSelectedOnkyo({ host, port?, requestTimeoutMs?, signal? })` delegates one
|
|
105
|
+
selected-unicast UDP query to the public protocol and returns a custom candidate
|
|
106
|
+
with the advertised TCP port. Config flow fixes its endpoint and identity,
|
|
107
|
+
captures the host home and lifetime before effects, collects acknowledgement and
|
|
108
|
+
ceiling, then validates TCP readiness before completion. Home is not editable; a
|
|
109
|
+
submitted conflicting home fails before opening a connection. Standalone
|
|
110
|
+
`OnkyoConfigFlow` also requires explicit context home and supports `destroy()`.
|
|
111
|
+
|
|
112
|
+
A supplied `configStore` must be an actual public `NoSqlConfigStore` with scope
|
|
113
|
+
matching exactly this home and the `onkyo` domain. A store is optional for these
|
|
114
|
+
protocol paths. The caller owns the public NoSQL store, database, and persistence
|
|
115
|
+
lifetime. This integration does not create, close, read, write, or delete store
|
|
116
|
+
data and does not persist receiver observations.
|
|
117
|
+
|
|
118
|
+
Direct `OnkyoClient` construction likewise requires a separate native context as
|
|
119
|
+
its second argument, such as `{ homeId: 'home-1' }`. Its snapshot home comes from
|
|
120
|
+
that captured admission; an optional third signal carries an owning lease lifetime.
|
|
121
|
+
|
|
122
|
+
Tests use bounded real TCP/UDP fixtures. No physical receiver or firmware has
|
|
123
|
+
been certified. Other zones, media transport commands, HDMI routing, arbitrary
|
|
124
|
+
raw commands, broadcast discovery, and cryptographic LAN authentication are
|
|
125
|
+
outside the supported contract. `HomeAssistantOnkyoIntegration` remains a class
|
|
126
|
+
alias with the same native semantics.
|
|
@@ -1,426 +1,129 @@
|
|
|
1
1
|
import * as plugins from '../../plugins.js';
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
private
|
|
17
|
-
private
|
|
2
|
+
import { NativeHomeAdmission } from '../../core/classes.nativehomeadmission.js';
|
|
3
|
+
import type { IPanasonicVieraClientOptions, IPanasonicVieraConfig, IPanasonicVieraSnapshot, TPanasonicVieraKey, TPanasonicVieraKeyReceipt } from './panasonic_viera.types.js';
|
|
4
|
+
import { drainPanasonicVieraCleanup, normalizePanasonicVieraConfig, panasonicVieraRecord } from './panasonic_viera.validation.js';
|
|
5
|
+
|
|
6
|
+
export class PanasonicVieraClient {
|
|
7
|
+
private readonly config: Readonly<IPanasonicVieraConfig>;
|
|
8
|
+
private readonly protocol: plugins.ecobridgeDeviceManager.PanasonicProtocol;
|
|
9
|
+
private readonly lifetime = new AbortController();
|
|
10
|
+
private readonly operations = new Set<Promise<unknown>>();
|
|
11
|
+
private readonly ownerSignal?: AbortSignal;
|
|
12
|
+
private readonly onDestroyed?: () => void;
|
|
13
|
+
private state: 'new' | 'starting' | 'ready' | 'closed' = 'new';
|
|
14
|
+
private closePromise?: Promise<void>;
|
|
15
|
+
private retryPromise?: Promise<void>;
|
|
16
|
+
private cleanupComplete = false;
|
|
17
|
+
private readonly onOwnerAbort = (): void => {
|
|
18
|
+
// An event listener cannot return the close result. Retain it for destroy(), while observing its rejection.
|
|
19
|
+
void this.destroy().catch(() => undefined);
|
|
20
|
+
};
|
|
18
21
|
|
|
19
|
-
constructor(
|
|
20
|
-
|
|
22
|
+
public constructor(configArg: IPanasonicVieraConfig, optionsArg: IPanasonicVieraClientOptions) {
|
|
23
|
+
this.config = normalizePanasonicVieraConfig(configArg);
|
|
24
|
+
const options = panasonicVieraRecord(optionsArg, ['homeId', 'configStore', 'logger', 'abortSignal', 'onDestroyed']);
|
|
25
|
+
if (options.onDestroyed !== undefined && typeof options.onDestroyed !== 'function') {
|
|
26
|
+
throw new TypeError('Panasonic Viera client lifetime options are invalid.');
|
|
27
|
+
}
|
|
28
|
+
const { onDestroyed: _onDestroyed, ...contextOptions } = options;
|
|
29
|
+
const context = new NativeHomeAdmission('panasonic_viera').admit(contextOptions, this.config.homeId);
|
|
30
|
+
this.protocol = new plugins.ecobridgeDeviceManager.PanasonicProtocol({
|
|
31
|
+
address: this.config.host, descriptionUrl: this.config.descriptionUrl,
|
|
32
|
+
expectedUdn: this.config.expectedUdn, expectedModelName: this.config.expectedModelName,
|
|
33
|
+
timeoutMs: this.config.timeoutMs, maxResponseBytes: this.config.maxResponseBytes,
|
|
34
|
+
});
|
|
35
|
+
this.ownerSignal = context.abortSignal;
|
|
36
|
+
this.onDestroyed = optionsArg.onDestroyed;
|
|
37
|
+
if (this.ownerSignal?.aborted) this.onOwnerAbort();
|
|
38
|
+
else this.ownerSignal?.addEventListener('abort', this.onOwnerAbort, { once: true });
|
|
21
39
|
}
|
|
22
40
|
|
|
23
|
-
public
|
|
24
|
-
|
|
25
|
-
return clone(this.cachedSnapshot);
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
if (!forceRefreshArg && this.clientConfig.snapshot) {
|
|
29
|
-
this.cachedSnapshot = PanasonicVieraMapper.toSnapshot({ config: this.clientConfig, source: 'snapshot', online: this.clientConfig.snapshot.online });
|
|
30
|
-
return clone(this.cachedSnapshot);
|
|
31
|
-
}
|
|
41
|
+
public get isClosed(): boolean { return this.state === 'closed'; }
|
|
42
|
+
public get isHealthy(): boolean { return this.state === 'ready' && this.protocol.isStarted; }
|
|
32
43
|
|
|
33
|
-
|
|
44
|
+
public start(abortSignalArg?: AbortSignal): Promise<IPanasonicVieraSnapshot> {
|
|
45
|
+
this.assertOpen();
|
|
46
|
+
if (this.state !== 'new') return Promise.reject(new Error('Panasonic Viera startup is single-use.'));
|
|
47
|
+
this.state = 'starting';
|
|
48
|
+
return this.track(async () => {
|
|
34
49
|
try {
|
|
35
|
-
const
|
|
36
|
-
this.
|
|
50
|
+
const profile = await this.protocol.start({ signal: this.operationSignal(abortSignalArg) });
|
|
51
|
+
this.assertOpen();
|
|
52
|
+
this.state = 'ready';
|
|
53
|
+
return this.snapshot(profile);
|
|
37
54
|
} catch (errorArg) {
|
|
38
|
-
this
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
public async refresh(): Promise<IServiceCallResult> {
|
|
48
|
-
this.cachedSnapshot = undefined;
|
|
49
|
-
const snapshot = await this.getSnapshot(Boolean(this.endpoint()));
|
|
50
|
-
const success = snapshot.online && !snapshot.error;
|
|
51
|
-
return { success, error: success ? undefined : snapshot.error || 'Panasonic Viera refresh requires a live SOAP endpoint, injected client, snapshotProvider, snapshot, entities, or rawData.', data: snapshot };
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
public async execute(requestArg: IServiceCallRequest): Promise<IServiceCallResult> {
|
|
55
|
-
if (this.clientConfig.client?.execute || this.clientConfig.commandExecutor) {
|
|
56
|
-
return super.execute(requestArg);
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
if (!this.endpoint()) {
|
|
60
|
-
return super.execute(requestArg);
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
try {
|
|
64
|
-
const data = requestArg.data || {};
|
|
65
|
-
if (requestArg.service === 'volume_set' || requestArg.service === 'set_volume') {
|
|
66
|
-
const volume = numberValue(data.volume_level ?? data.volumeLevel ?? data.volume);
|
|
67
|
-
if (volume === undefined) {
|
|
68
|
-
throw new Error('Panasonic Viera volume_set requires data.volume_level or data.volume.');
|
|
69
|
-
}
|
|
70
|
-
await this.setVolume(volume <= 1 ? Math.round(volume * 100) : Math.round(volume));
|
|
71
|
-
this.cachedSnapshot = undefined;
|
|
72
|
-
return { success: true, data: { volume } };
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
if (requestArg.service === 'volume_mute' || requestArg.service === 'set_mute') {
|
|
76
|
-
const mute = booleanValue(data.is_volume_muted ?? data.mute ?? data.muted);
|
|
77
|
-
if (mute === undefined) {
|
|
78
|
-
await this.sendKey('NRC_MUTE-ONOFF');
|
|
79
|
-
this.cachedSnapshot = undefined;
|
|
80
|
-
return { success: true, data: { key: 'NRC_MUTE-ONOFF' } };
|
|
81
|
-
}
|
|
82
|
-
await this.setMute(mute);
|
|
83
|
-
this.cachedSnapshot = undefined;
|
|
84
|
-
return { success: true, data: { muted: mute } };
|
|
55
|
+
// Close the protocol here; destroy drains this wrapper after the rejection settles.
|
|
56
|
+
this.state = 'closed';
|
|
57
|
+
this.ownerSignal?.removeEventListener('abort', this.onOwnerAbort);
|
|
58
|
+
this.lifetime.abort(errorArg);
|
|
59
|
+
try { await this.protocol.close(); }
|
|
60
|
+
catch (cleanupErrorArg) { throw new AggregateError([errorArg, cleanupErrorArg], 'Panasonic Viera admission and cleanup failed.'); }
|
|
61
|
+
throw errorArg;
|
|
85
62
|
}
|
|
86
|
-
|
|
87
|
-
const key = keyForService(requestArg.service, data);
|
|
88
|
-
if (!key) {
|
|
89
|
-
return { success: false, error: `Unsupported Panasonic Viera service: ${requestArg.service}` };
|
|
90
|
-
}
|
|
91
|
-
await this.sendKey(key);
|
|
92
|
-
this.cachedSnapshot = undefined;
|
|
93
|
-
return { success: true, data: { key } };
|
|
94
|
-
} catch (errorArg) {
|
|
95
|
-
return { success: false, error: errorMessage(errorArg) };
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
public async destroy(): Promise<void> {
|
|
100
|
-
await this.clientConfig.client?.destroy?.();
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
private async fetchRawData(): Promise<IPanasonicVieraRawApiData> {
|
|
104
|
-
const deviceInfoXml = await this.requestText('/nrc/ddd.xml');
|
|
105
|
-
const deviceInfo = parseDeviceInfo(deviceInfoXml);
|
|
106
|
-
const rawData: IPanasonicVieraRawApiData = {
|
|
107
|
-
deviceInfo,
|
|
108
|
-
fetchedAt: new Date().toISOString(),
|
|
109
|
-
};
|
|
110
|
-
|
|
111
|
-
try {
|
|
112
|
-
rawData.volume = await this.getVolume();
|
|
113
|
-
} catch (errorArg) {
|
|
114
|
-
rawData.volumeError = errorMessage(errorArg);
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
try {
|
|
118
|
-
rawData.muted = await this.getMute();
|
|
119
|
-
} catch (errorArg) {
|
|
120
|
-
rawData.muteError = errorMessage(errorArg);
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
return rawData;
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
private async getVolume(): Promise<number> {
|
|
127
|
-
const text = await this.soapRequest('/dmr/control_0', 'schemas-upnp-org:service:RenderingControl:1', 'GetVolume', '<InstanceID>0</InstanceID><Channel>Master</Channel>');
|
|
128
|
-
const volume = numberValue(extractXmlValue(text, 'CurrentVolume'));
|
|
129
|
-
if (volume === undefined) {
|
|
130
|
-
throw new Error('Panasonic Viera GetVolume response did not include CurrentVolume.');
|
|
131
|
-
}
|
|
132
|
-
return volume;
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
private async setVolume(volumeArg: number): Promise<void> {
|
|
136
|
-
if (volumeArg < 0 || volumeArg > 100) {
|
|
137
|
-
throw new Error('Panasonic Viera volume must be between 0 and 100.');
|
|
138
|
-
}
|
|
139
|
-
await this.soapRequest('/dmr/control_0', 'schemas-upnp-org:service:RenderingControl:1', 'SetVolume', `<InstanceID>0</InstanceID><Channel>Master</Channel><DesiredVolume>${volumeArg}</DesiredVolume>`);
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
private async getMute(): Promise<boolean> {
|
|
143
|
-
const text = await this.soapRequest('/dmr/control_0', 'schemas-upnp-org:service:RenderingControl:1', 'GetMute', '<InstanceID>0</InstanceID><Channel>Master</Channel>');
|
|
144
|
-
return extractXmlValue(text, 'CurrentMute') !== '0';
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
private async setMute(muteArg: boolean): Promise<void> {
|
|
148
|
-
await this.soapRequest('/dmr/control_0', 'schemas-upnp-org:service:RenderingControl:1', 'SetMute', `<InstanceID>0</InstanceID><Channel>Master</Channel><DesiredMute>${muteArg ? '1' : '0'}</DesiredMute>`);
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
private async sendKey(keyArg: string): Promise<void> {
|
|
152
|
-
await this.soapRequest('/nrc/control_0', 'panasonic-com:service:p00NetworkControl:1', 'X_SendKey', `<X_KeyEvent>${escapeXml(keyArg)}</X_KeyEvent>`);
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
private async soapRequest(pathArg: string, urnArg: string, actionArg: string, paramsArg: string, bodyElementArg = 'm'): Promise<string> {
|
|
156
|
-
let action = actionArg;
|
|
157
|
-
let params = paramsArg;
|
|
158
|
-
let bodyElement = bodyElementArg;
|
|
159
|
-
let encrypted = false;
|
|
160
|
-
|
|
161
|
-
if (this.shouldEncrypt(urnArg, actionArg)) {
|
|
162
|
-
const session = await this.ensureEncryptedSession();
|
|
163
|
-
session.sequenceNumber = (session.sequenceNumber || 1) + 1;
|
|
164
|
-
const originalCommand = `<X_SessionId>${session.id}</X_SessionId><X_SequenceNumber>${String(session.sequenceNumber).padStart(8, '0')}</X_SequenceNumber><X_OriginalCommand><u:${actionArg} xmlns:u="urn:${urnArg}">${paramsArg}</u:${actionArg}></X_OriginalCommand>`;
|
|
165
|
-
const encryptedCommand = this.encryptSoapPayload(originalCommand, session);
|
|
166
|
-
action = 'X_EncryptedCommand';
|
|
167
|
-
params = `<X_ApplicationId>${escapeXml(this.clientConfig.appId || '')}</X_ApplicationId><X_EncInfo>${encryptedCommand}</X_EncInfo>`;
|
|
168
|
-
bodyElement = 'u';
|
|
169
|
-
encrypted = true;
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
const body = `<?xml version="1.0" encoding="utf-8"?><s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/" s:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"><s:Body><${bodyElement}:${action} xmlns:${bodyElement}="urn:${urnArg}">${params}</${bodyElement}:${action}></s:Body></s:Envelope>`;
|
|
173
|
-
const text = await this.requestText(pathArg, {
|
|
174
|
-
method: 'POST',
|
|
175
|
-
body,
|
|
176
|
-
headers: {
|
|
177
|
-
'Content-Type': 'text/xml; charset=utf-8',
|
|
178
|
-
SOAPAction: `"urn:${urnArg}#${action}"`,
|
|
179
|
-
},
|
|
180
63
|
});
|
|
181
|
-
|
|
182
|
-
if (!encrypted) {
|
|
183
|
-
return text;
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
const encResult = extractXmlValue(text, 'X_EncResult');
|
|
187
|
-
if (!encResult) {
|
|
188
|
-
throw new Error('Panasonic Viera encrypted SOAP response did not include X_EncResult.');
|
|
189
|
-
}
|
|
190
|
-
return this.decryptSoapPayload(encResult, await this.ensureEncryptedSession());
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
private shouldEncrypt(urnArg: string, actionArg: string): boolean {
|
|
194
|
-
return urnArg === 'panasonic-com:service:p00NetworkControl:1'
|
|
195
|
-
&& !['X_GetEncryptSessionId', 'X_DisplayPinCode', 'X_RequestAuth'].includes(actionArg)
|
|
196
|
-
&& Boolean(this.clientConfig.appId && this.clientConfig.encryptionKey);
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
private async ensureEncryptedSession(): Promise<IPanasonicVieraSession> {
|
|
200
|
-
if (!this.clientConfig.appId || !this.clientConfig.encryptionKey) {
|
|
201
|
-
throw new Error('Panasonic Viera encrypted commands require config.appId and config.encryptionKey.');
|
|
202
|
-
}
|
|
203
|
-
if (!this.encryptedSession) {
|
|
204
|
-
this.encryptedSession = deriveSession(this.clientConfig.encryptionKey);
|
|
205
|
-
}
|
|
206
|
-
if (this.encryptedSession.id) {
|
|
207
|
-
return this.encryptedSession;
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
const encInfo = this.encryptSoapPayload(`<X_ApplicationId>${escapeXml(this.clientConfig.appId)}</X_ApplicationId>`, this.encryptedSession);
|
|
211
|
-
const text = await this.soapRequest('/nrc/control_0', 'panasonic-com:service:p00NetworkControl:1', 'X_GetEncryptSessionId', `<X_ApplicationId>${escapeXml(this.clientConfig.appId)}</X_ApplicationId><X_EncInfo>${encInfo}</X_EncInfo>`, 'u');
|
|
212
|
-
const encResult = extractXmlValue(text, 'X_EncResult');
|
|
213
|
-
if (!encResult) {
|
|
214
|
-
throw new Error('Panasonic Viera session response did not include X_EncResult.');
|
|
215
|
-
}
|
|
216
|
-
const decrypted = this.decryptSoapPayload(encResult, this.encryptedSession);
|
|
217
|
-
const sessionId = extractXmlValue(decrypted, 'X_SessionId');
|
|
218
|
-
if (!sessionId) {
|
|
219
|
-
throw new Error('Panasonic Viera encrypted session response did not include X_SessionId.');
|
|
220
|
-
}
|
|
221
|
-
this.encryptedSession.id = sessionId;
|
|
222
|
-
this.encryptedSession.sequenceNumber = 1;
|
|
223
|
-
return this.encryptedSession;
|
|
224
64
|
}
|
|
225
65
|
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
const cipher = plugins.crypto.createCipheriv('aes-128-cbc', sessionArg.key, sessionArg.iv);
|
|
232
|
-
cipher.setAutoPadding(false);
|
|
233
|
-
const ciphertext = Buffer.concat([cipher.update(paddedPayload), cipher.final()]);
|
|
234
|
-
const signature = plugins.crypto.createHmac('sha256', sessionArg.hmacKey).update(ciphertext).digest();
|
|
235
|
-
return Buffer.concat([ciphertext, signature]).toString('base64');
|
|
66
|
+
public getSnapshot(abortSignalArg?: AbortSignal): Promise<IPanasonicVieraSnapshot> {
|
|
67
|
+
this.assertOpen();
|
|
68
|
+
if (this.state === 'new') return this.start(abortSignalArg);
|
|
69
|
+
if (this.state !== 'ready') return Promise.reject(new Error('Panasonic Viera startup is in progress.'));
|
|
70
|
+
return this.track(async () => this.snapshot(await this.protocol.readProfile({ signal: this.operationSignal(abortSignalArg) })));
|
|
236
71
|
}
|
|
237
72
|
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
const decrypted = Buffer.concat([decipher.update(encrypted), decipher.final()]).toString('latin1');
|
|
243
|
-
return decrypted.slice(16).split('\0')[0];
|
|
73
|
+
public sendKey(keyArg: TPanasonicVieraKey, abortSignalArg?: AbortSignal): Promise<TPanasonicVieraKeyReceipt> {
|
|
74
|
+
this.assertOpen();
|
|
75
|
+
if (this.state !== 'ready') return Promise.reject(new Error('Panasonic Viera has not completed live admission.'));
|
|
76
|
+
return this.track(() => this.protocol.sendKey(keyArg, { signal: this.operationSignal(abortSignalArg) }));
|
|
244
77
|
}
|
|
245
78
|
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
method: optionsArg.method || 'GET',
|
|
255
|
-
headers: { Accept: 'text/xml, application/xml;q=0.9, */*;q=0.8', ...(optionsArg.headers || {}) },
|
|
256
|
-
body: optionsArg.body,
|
|
257
|
-
signal: controller.signal,
|
|
258
|
-
});
|
|
259
|
-
const text = await response.text();
|
|
260
|
-
if (!response.ok) {
|
|
261
|
-
throw new Error(`Panasonic Viera request ${url.pathname} failed with ${response.status}: ${extractXmlValue(text, 'errorDescription') || text.slice(0, 160)}`);
|
|
262
|
-
}
|
|
263
|
-
return text;
|
|
264
|
-
} finally {
|
|
265
|
-
clearTimeout(timer);
|
|
79
|
+
public destroy(): Promise<void> {
|
|
80
|
+
if (!this.closePromise) {
|
|
81
|
+
this.state = 'closed';
|
|
82
|
+
this.ownerSignal?.removeEventListener('abort', this.onOwnerAbort);
|
|
83
|
+
const operations = [...this.operations];
|
|
84
|
+
this.closePromise = Promise.resolve().then(() => drainPanasonicVieraCleanup([this.protocol.close()], operations,
|
|
85
|
+
'Panasonic Viera client cleanup failed.')).then(() => { this.onDestroyed?.(); this.cleanupComplete = true; });
|
|
86
|
+
this.lifetime.abort(new Error('Panasonic Viera client is closed.'));
|
|
266
87
|
}
|
|
88
|
+
return this.closePromise;
|
|
267
89
|
}
|
|
268
90
|
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
if (!
|
|
272
|
-
|
|
91
|
+
/** Explicit cleanup retry only. The original destroy rejection remains observable; transport work is never replayed. */
|
|
92
|
+
public retryCleanup(): Promise<void> {
|
|
93
|
+
if (!this.closePromise) return Promise.reject(new Error('Panasonic Viera cleanup must first be closed.'));
|
|
94
|
+
if (this.cleanupComplete) return Promise.resolve();
|
|
95
|
+
if (!this.retryPromise) {
|
|
96
|
+
const closed = this.closePromise;
|
|
97
|
+
this.retryPromise = Promise.resolve().then(async () => {
|
|
98
|
+
await closed.catch(() => undefined);
|
|
99
|
+
await drainPanasonicVieraCleanup([this.protocol.close()], [...this.operations], 'Panasonic Viera explicit cleanup retry failed.');
|
|
100
|
+
this.onDestroyed?.();
|
|
101
|
+
this.cleanupComplete = true;
|
|
102
|
+
});
|
|
103
|
+
const settled = (): void => { this.retryPromise = undefined; };
|
|
104
|
+
void this.retryPromise.then(settled, settled);
|
|
273
105
|
}
|
|
274
|
-
return
|
|
106
|
+
return this.retryPromise;
|
|
275
107
|
}
|
|
276
108
|
|
|
277
|
-
private
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
return normalizeBaseUrl(configuredUrl, this.clientConfig.port, this.clientConfig.useTls);
|
|
281
|
-
}
|
|
282
|
-
const host = stringValue(this.clientConfig.host);
|
|
283
|
-
if (!host) {
|
|
284
|
-
return undefined;
|
|
285
|
-
}
|
|
286
|
-
return normalizeBaseUrl(host, this.clientConfig.port || panasonicVieraDefaultPort, this.clientConfig.useTls);
|
|
109
|
+
private snapshot(profileArg: plugins.ecobridgeDeviceManager.IPanasonicProfile): IPanasonicVieraSnapshot {
|
|
110
|
+
this.assertOpen();
|
|
111
|
+
return Object.freeze({ homeId: this.config.homeId, name: this.config.name ?? profileArg.identity.name, profile: profileArg });
|
|
287
112
|
}
|
|
288
113
|
|
|
289
|
-
private
|
|
290
|
-
|
|
114
|
+
private operationSignal(signalArg?: AbortSignal): AbortSignal {
|
|
115
|
+
if (signalArg !== undefined && !(signalArg instanceof AbortSignal)) throw new TypeError('Panasonic Viera cancellation must be an AbortSignal.');
|
|
116
|
+
return signalArg ? AbortSignal.any([this.lifetime.signal, signalArg]) : this.lifetime.signal;
|
|
291
117
|
}
|
|
292
|
-
}
|
|
293
|
-
|
|
294
|
-
const keyMap: Record<string, string> = {
|
|
295
|
-
media_play: 'NRC_PLAY-ONOFF',
|
|
296
|
-
media_pause: 'NRC_PAUSE-ONOFF',
|
|
297
|
-
media_stop: 'NRC_STOP-ONOFF',
|
|
298
|
-
media_next_track: 'NRC_FF-ONOFF',
|
|
299
|
-
media_previous_track: 'NRC_REW-ONOFF',
|
|
300
|
-
volume_up: 'NRC_VOLUP-ONOFF',
|
|
301
|
-
volume_down: 'NRC_VOLDOWN-ONOFF',
|
|
302
|
-
turn_on: 'NRC_POWER-ONOFF',
|
|
303
|
-
turn_off: 'NRC_POWER-ONOFF',
|
|
304
|
-
toggle: 'NRC_POWER-ONOFF',
|
|
305
|
-
};
|
|
306
118
|
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
if (serviceArg === 'press' && explicit) {
|
|
310
|
-
return normalizeKey(explicit);
|
|
119
|
+
private assertOpen(): void {
|
|
120
|
+
if (this.isClosed) throw new Error('Panasonic Viera client is closed.');
|
|
311
121
|
}
|
|
312
|
-
return keyMap[serviceArg];
|
|
313
|
-
};
|
|
314
122
|
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
123
|
+
private track<TResult>(executeArg: () => Promise<TResult>): Promise<TResult> {
|
|
124
|
+
if (this.operations.size !== 0) return Promise.reject(new Error('Panasonic Viera has an active operation.'));
|
|
125
|
+
const operation = Promise.resolve().then(executeArg).finally(() => this.operations.delete(operation));
|
|
126
|
+
this.operations.add(operation);
|
|
127
|
+
return operation;
|
|
318
128
|
}
|
|
319
|
-
|
|
320
|
-
const aliases: Record<string, string> = {
|
|
321
|
-
PLAY: 'NRC_PLAY-ONOFF',
|
|
322
|
-
PAUSE: 'NRC_PAUSE-ONOFF',
|
|
323
|
-
STOP: 'NRC_STOP-ONOFF',
|
|
324
|
-
POWER: 'NRC_POWER-ONOFF',
|
|
325
|
-
VOLUME_UP: 'NRC_VOLUP-ONOFF',
|
|
326
|
-
VOLUME_DOWN: 'NRC_VOLDOWN-ONOFF',
|
|
327
|
-
MUTE: 'NRC_MUTE-ONOFF',
|
|
328
|
-
ENTER: 'NRC_ENTER-ONOFF',
|
|
329
|
-
UP: 'NRC_UP-ONOFF',
|
|
330
|
-
DOWN: 'NRC_DOWN-ONOFF',
|
|
331
|
-
LEFT: 'NRC_LEFT-ONOFF',
|
|
332
|
-
RIGHT: 'NRC_RIGHT-ONOFF',
|
|
333
|
-
HOME: 'NRC_HOME-ONOFF',
|
|
334
|
-
MENU: 'NRC_MENU-ONOFF',
|
|
335
|
-
BACK: 'NRC_RETURN-ONOFF',
|
|
336
|
-
};
|
|
337
|
-
return aliases[normalized] || `NRC_${normalized}-ONOFF`;
|
|
338
|
-
};
|
|
339
|
-
|
|
340
|
-
const deriveSession = (encryptionKeyArg: string): IPanasonicVieraSession => {
|
|
341
|
-
const iv = Buffer.from(encryptionKeyArg, 'base64').subarray(0, 16);
|
|
342
|
-
if (iv.length !== 16) {
|
|
343
|
-
throw new Error('Panasonic Viera encryptionKey must decode to a 16 byte AES IV.');
|
|
344
|
-
}
|
|
345
|
-
const key = Buffer.alloc(16);
|
|
346
|
-
for (let index = 0; index < 16; index += 4) {
|
|
347
|
-
key[index] = iv[index + 2];
|
|
348
|
-
key[index + 1] = iv[index + 3];
|
|
349
|
-
key[index + 2] = iv[index];
|
|
350
|
-
key[index + 3] = iv[index + 1];
|
|
351
|
-
}
|
|
352
|
-
return { key, iv, hmacKey: Buffer.concat([iv, iv]) };
|
|
353
|
-
};
|
|
354
|
-
|
|
355
|
-
const uint32be = (valueArg: number): Buffer => {
|
|
356
|
-
const buffer = Buffer.alloc(4);
|
|
357
|
-
buffer.writeUInt32BE(valueArg, 0);
|
|
358
|
-
return buffer;
|
|
359
|
-
};
|
|
360
|
-
|
|
361
|
-
const parseDeviceInfo = (xmlArg: string): Record<string, unknown> => ({
|
|
362
|
-
UDN: extractXmlValue(xmlArg, 'UDN'),
|
|
363
|
-
friendlyName: extractXmlValue(xmlArg, 'friendlyName'),
|
|
364
|
-
manufacturer: extractXmlValue(xmlArg, 'manufacturer'),
|
|
365
|
-
modelName: extractXmlValue(xmlArg, 'modelName'),
|
|
366
|
-
modelNumber: extractXmlValue(xmlArg, 'modelNumber'),
|
|
367
|
-
});
|
|
368
|
-
|
|
369
|
-
const extractXmlValue = (xmlArg: string, tagArg: string): string | undefined => {
|
|
370
|
-
const pattern = new RegExp(`<(?:\\w+:)?${tagArg}[^>]*>([\\s\\S]*?)</(?:\\w+:)?${tagArg}>`, 'i');
|
|
371
|
-
const match = pattern.exec(xmlArg);
|
|
372
|
-
return match?.[1]?.trim();
|
|
373
|
-
};
|
|
374
|
-
|
|
375
|
-
const escapeXml = (valueArg: string): string => valueArg.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"').replace(/'/g, ''');
|
|
376
|
-
|
|
377
|
-
const normalizeBaseUrl = (valueArg: string, portArg: number | undefined, useTlsArg?: boolean): URL | undefined => {
|
|
378
|
-
try {
|
|
379
|
-
const parsed = new URL(valueArg.includes('://') ? valueArg : `${useTlsArg ? 'https' : 'http'}://${valueArg}`);
|
|
380
|
-
if (portArg) {
|
|
381
|
-
parsed.port = String(portArg);
|
|
382
|
-
}
|
|
383
|
-
parsed.pathname = '';
|
|
384
|
-
parsed.search = '';
|
|
385
|
-
parsed.hash = '';
|
|
386
|
-
return parsed;
|
|
387
|
-
} catch {
|
|
388
|
-
return undefined;
|
|
389
|
-
}
|
|
390
|
-
};
|
|
391
|
-
|
|
392
|
-
const stringValue = (valueArg: unknown): string | undefined => {
|
|
393
|
-
return typeof valueArg === 'string' && valueArg.trim() ? valueArg.trim() : undefined;
|
|
394
|
-
};
|
|
395
|
-
|
|
396
|
-
const numberValue = (valueArg: unknown): number | undefined => {
|
|
397
|
-
if (typeof valueArg === 'number' && Number.isFinite(valueArg)) {
|
|
398
|
-
return valueArg;
|
|
399
|
-
}
|
|
400
|
-
if (typeof valueArg === 'string' && valueArg.trim() && Number.isFinite(Number(valueArg))) {
|
|
401
|
-
return Number(valueArg);
|
|
402
|
-
}
|
|
403
|
-
return undefined;
|
|
404
|
-
};
|
|
405
|
-
|
|
406
|
-
const booleanValue = (valueArg: unknown): boolean | undefined => {
|
|
407
|
-
if (typeof valueArg === 'boolean') {
|
|
408
|
-
return valueArg;
|
|
409
|
-
}
|
|
410
|
-
if (typeof valueArg === 'number') {
|
|
411
|
-
return valueArg !== 0;
|
|
412
|
-
}
|
|
413
|
-
if (typeof valueArg === 'string') {
|
|
414
|
-
if (valueArg === '1' || valueArg.toLowerCase() === 'true') {
|
|
415
|
-
return true;
|
|
416
|
-
}
|
|
417
|
-
if (valueArg === '0' || valueArg.toLowerCase() === 'false') {
|
|
418
|
-
return false;
|
|
419
|
-
}
|
|
420
|
-
}
|
|
421
|
-
return undefined;
|
|
422
|
-
};
|
|
423
|
-
|
|
424
|
-
const clone = <TValue>(valueArg: TValue): TValue => JSON.parse(JSON.stringify(valueArg)) as TValue;
|
|
425
|
-
|
|
426
|
-
const errorMessage = (errorArg: unknown): string => errorArg instanceof Error ? errorArg.message : String(errorArg);
|
|
129
|
+
}
|