@smarthome.exchange/integrations 1.0.0 → 2.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 +49 -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 +79 -0
- package/dist_ts/core/classes.nativeintegrationowner.js +269 -0
- package/dist_ts/core/index.d.ts +2 -0
- package/dist_ts/core/index.js +3 -1
- package/dist_ts/core/types.d.ts +14 -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/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/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/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/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/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/package.json +3 -3
- package/readme.md +514 -2
- 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 +297 -0
- package/ts/core/index.ts +2 -0
- package/ts/core/types.ts +16 -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/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/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/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/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/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
|
@@ -1,9 +1,55 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import type {
|
|
3
|
-
import {
|
|
1
|
+
import { NativeIntegrationOwner, type NativeIntegrationScope } from '../../core/classes.nativeintegrationowner.js';
|
|
2
|
+
import type { IConfigFlow, IConfigFlowStep, IDiscoveryCandidate, INativeConfigFlowContext } from '../../core/types.js';
|
|
3
|
+
import { OnkyoClient } from './onkyo.classes.client.js';
|
|
4
|
+
import { createOnkyoDiscoveryDescriptor } from './onkyo.discovery.js';
|
|
5
|
+
import type { IOnkyoConfig, IOnkyoIntegrationOptions } from './onkyo.types.js';
|
|
6
|
+
import { normalizeOnkyoConfig, onkyoKeys, onkyoRecord } from './onkyo.validation.js';
|
|
4
7
|
|
|
5
|
-
export class OnkyoConfigFlow
|
|
6
|
-
|
|
7
|
-
|
|
8
|
+
export class OnkyoConfigFlow implements IConfigFlow<IOnkyoConfig> {
|
|
9
|
+
readonly #scope: NativeIntegrationScope;
|
|
10
|
+
|
|
11
|
+
constructor(optionsArg: IOnkyoIntegrationOptions = {}, scopeArg?: NativeIntegrationScope) {
|
|
12
|
+
this.#scope = scopeArg ?? new NativeIntegrationOwner('onkyo', optionsArg).createFlowScope();
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
public async start(candidateArg: IDiscoveryCandidate, contextArg: INativeConfigFlowContext): Promise<IConfigFlowStep<IOnkyoConfig>> {
|
|
16
|
+
const context = this.#scope.admit(contextArg);
|
|
17
|
+
// The owned pure validator captures the selected candidate before yielding.
|
|
18
|
+
const validation = createOnkyoDiscoveryDescriptor().getValidators()[0].validate(candidateArg, {});
|
|
19
|
+
return this.#scope.run<IConfigFlowStep<IOnkyoConfig>>(context, undefined, async (leaseArg) => {
|
|
20
|
+
const matched = await validation;
|
|
21
|
+
leaseArg.assertOpen();
|
|
22
|
+
if (!matched.matched || !matched.candidate) return { kind: 'error', error: matched.reason };
|
|
23
|
+
const candidate = matched.candidate;
|
|
24
|
+
return {
|
|
25
|
+
kind: 'form', title: 'Connect selected Onkyo main zone',
|
|
26
|
+
description: 'Acknowledge the cleartext LAN protocol and set an absolute raw-volume ceiling. Home, endpoint, MAC and model are fixed by the host and selected candidate.',
|
|
27
|
+
fields: [
|
|
28
|
+
{ name: 'allowUnencryptedLan', label: 'Acknowledge unauthenticated cleartext LAN', type: 'boolean', required: true },
|
|
29
|
+
{ name: 'volumeLimitRaw', label: 'Maximum integer raw volume (0–100)', type: 'number', required: true },
|
|
30
|
+
{ name: 'name', label: 'Name', type: 'text' },
|
|
31
|
+
],
|
|
32
|
+
submit: async (valuesArg) => {
|
|
33
|
+
try {
|
|
34
|
+
const values = onkyoRecord(valuesArg, 'config flow values');
|
|
35
|
+
onkyoKeys(values, ['homeId', 'allowUnencryptedLan', 'volumeLimitRaw', 'name', 'requestTimeoutMs', 'maxPendingOperations']);
|
|
36
|
+
if (values.homeId !== undefined && values.homeId !== context.homeId) throw new TypeError('Onkyo submitted home must match the host-selected home.');
|
|
37
|
+
const config = normalizeOnkyoConfig({ ...values, homeId: context.homeId, host: candidate.host, port: candidate.port, macAddress: candidate.macAddress, model: candidate.model });
|
|
38
|
+
return await this.#scope.run<IConfigFlowStep<IOnkyoConfig>>(context, config.homeId, async (operationArg) => {
|
|
39
|
+
const client = new OnkyoClient(config, operationArg.context, operationArg.signal);
|
|
40
|
+
operationArg.own(() => client.destroy());
|
|
41
|
+
await client.getSnapshot(operationArg.signal);
|
|
42
|
+
operationArg.assertOpen();
|
|
43
|
+
return { kind: 'done', title: 'Onkyo receiver validated', config };
|
|
44
|
+
});
|
|
45
|
+
} catch (errorArg) {
|
|
46
|
+
return { kind: 'error', error: errorArg instanceof Error ? errorArg.message : 'Onkyo setup validation failed.' };
|
|
47
|
+
}
|
|
48
|
+
},
|
|
49
|
+
};
|
|
50
|
+
});
|
|
8
51
|
}
|
|
52
|
+
|
|
53
|
+
public destroy(): Promise<void> { return this.#scope.close(); }
|
|
54
|
+
public retryCleanup(): Promise<void> { return this.#scope.retryCleanup(); }
|
|
9
55
|
}
|
|
@@ -1,23 +1,132 @@
|
|
|
1
|
-
import
|
|
1
|
+
import * as plugins from '../../plugins.js';
|
|
2
|
+
import { BaseIntegration } from '../../core/classes.baseintegration.js';
|
|
3
|
+
import { NativeIntegrationOwner } from '../../core/classes.nativeintegrationowner.js';
|
|
4
|
+
import type { IDeviceFeatureWriteRequest, IIntegrationEntity, IIntegrationEvent, IIntegrationRuntime, INativeIntegrationSetupContext, IServiceCallRequest, IServiceCallResult } from '../../core/types.js';
|
|
2
5
|
import { OnkyoClient } from './onkyo.classes.client.js';
|
|
3
6
|
import { OnkyoConfigFlow } from './onkyo.classes.configflow.js';
|
|
4
7
|
import { createOnkyoDiscoveryDescriptor } from './onkyo.discovery.js';
|
|
5
|
-
import
|
|
6
|
-
import {
|
|
8
|
+
import { OnkyoMapper } from './onkyo.mapper.js';
|
|
9
|
+
import type { IOnkyoConfig, IOnkyoIntegrationOptions, IOnkyoSnapshot } from './onkyo.types.js';
|
|
10
|
+
import { onkyoProfile } from './onkyo.types.js';
|
|
11
|
+
import { normalizeOnkyoConfig, onkyoBoolean, onkyoInputId, onkyoInteger, onkyoKeys, onkyoRecord } from './onkyo.validation.js';
|
|
7
12
|
|
|
8
|
-
export class OnkyoIntegration extends
|
|
9
|
-
public readonly domain =
|
|
13
|
+
export class OnkyoIntegration extends BaseIntegration<IOnkyoConfig> {
|
|
14
|
+
public readonly domain = 'onkyo';
|
|
15
|
+
public readonly displayName = 'Onkyo';
|
|
16
|
+
public readonly status = 'control-runtime' as const;
|
|
17
|
+
public readonly metadata = onkyoProfile.metadata;
|
|
10
18
|
public readonly discoveryDescriptor = createOnkyoDiscoveryDescriptor();
|
|
11
|
-
public readonly configFlow
|
|
19
|
+
public readonly configFlow: OnkyoConfigFlow;
|
|
20
|
+
readonly #owner: NativeIntegrationOwner;
|
|
12
21
|
|
|
13
|
-
constructor() {
|
|
14
|
-
super(
|
|
22
|
+
constructor(optionsArg: IOnkyoIntegrationOptions = {}) {
|
|
23
|
+
super();
|
|
24
|
+
this.#owner = new NativeIntegrationOwner('onkyo', optionsArg);
|
|
25
|
+
this.configFlow = new OnkyoConfigFlow(optionsArg, this.#owner.createFlowScope());
|
|
15
26
|
}
|
|
16
27
|
|
|
17
|
-
public
|
|
18
|
-
|
|
19
|
-
|
|
28
|
+
public setup(configArg: IOnkyoConfig, contextArg: INativeIntegrationSetupContext): Promise<IIntegrationRuntime> {
|
|
29
|
+
let config: ReturnType<typeof normalizeOnkyoConfig>;
|
|
30
|
+
try {
|
|
31
|
+
config = normalizeOnkyoConfig(configArg);
|
|
32
|
+
} catch (errorArg) { return Promise.reject(errorArg); }
|
|
33
|
+
return this.#owner.runSetup(contextArg, config.homeId, async (leaseArg) => {
|
|
34
|
+
const client = new OnkyoClient(config, leaseArg.context, leaseArg.signal);
|
|
35
|
+
leaseArg.own(() => client.destroy());
|
|
36
|
+
const snapshot = await client.getSnapshot(leaseArg.signal);
|
|
37
|
+
leaseArg.assertOpen();
|
|
38
|
+
const runtime = new OnkyoRuntime(client, snapshot, () => leaseArg.close());
|
|
39
|
+
leaseArg.promoteToRuntime();
|
|
40
|
+
return runtime;
|
|
41
|
+
});
|
|
20
42
|
}
|
|
43
|
+
public destroy(): Promise<void> { return this.#owner.close(); }
|
|
44
|
+
public retryCleanup(): Promise<void> { return this.#owner.retryCleanup(); }
|
|
21
45
|
}
|
|
22
|
-
|
|
23
46
|
export class HomeAssistantOnkyoIntegration extends OnkyoIntegration {}
|
|
47
|
+
|
|
48
|
+
class OnkyoRuntime implements IIntegrationRuntime {
|
|
49
|
+
public readonly domain = 'onkyo';
|
|
50
|
+
readonly #deviceId: string;
|
|
51
|
+
readonly #entityId: string;
|
|
52
|
+
#closePromise?: Promise<void>;
|
|
53
|
+
constructor(private readonly client: OnkyoClient, snapshotArg: IOnkyoSnapshot, private readonly closeLease: () => Promise<void>) {
|
|
54
|
+
this.#deviceId = OnkyoMapper.deviceId(snapshotArg);
|
|
55
|
+
this.#entityId = OnkyoMapper.mediaEntityId(snapshotArg);
|
|
56
|
+
}
|
|
57
|
+
public isHealthy(): boolean { return this.client.isHealthy(); }
|
|
58
|
+
public async devices(signalArg?: AbortSignal): Promise<plugins.shxInterfaces.data.IDeviceDefinition[]> { return OnkyoMapper.toDevices(await this.client.getSnapshot(signalArg)); }
|
|
59
|
+
public async entities(): Promise<IIntegrationEntity[]> { return OnkyoMapper.toEntities(await this.client.getSnapshot()); }
|
|
60
|
+
public async subscribe(handlerArg: (eventArg: IIntegrationEvent) => void, signalArg?: AbortSignal): Promise<() => Promise<void>> {
|
|
61
|
+
if (typeof handlerArg !== 'function') throw new TypeError('Onkyo runtime subscriber must be a function.');
|
|
62
|
+
const unsubscribe = this.client.subscribe((eventArg) => {
|
|
63
|
+
const event: IIntegrationEvent = {
|
|
64
|
+
type: eventArg.type === 'error' ? 'error' : 'state_changed', integrationDomain: 'onkyo', deviceId: this.#deviceId,
|
|
65
|
+
entityId: this.#entityId, timestamp: Date.now(),
|
|
66
|
+
data: eventArg.type === 'snapshot' ? { snapshot: eventArg.snapshot, entity: OnkyoMapper.toEntities(eventArg.snapshot)[0] } :
|
|
67
|
+
eventArg.type === 'control' ? { observation: eventArg.observation } : { message: eventArg.error.message },
|
|
68
|
+
};
|
|
69
|
+
handlerArg(event);
|
|
70
|
+
}, signalArg);
|
|
71
|
+
return async () => unsubscribe();
|
|
72
|
+
}
|
|
73
|
+
public async callService(requestArg: IServiceCallRequest): Promise<IServiceCallResult> {
|
|
74
|
+
try {
|
|
75
|
+
const request = onkyoRecord(requestArg, 'service request');
|
|
76
|
+
onkyoKeys(request, ['domain', 'service', 'target', 'data']);
|
|
77
|
+
if (typeof request.domain !== 'string' || typeof request.service !== 'string') throw new TypeError('Onkyo service domain and service must be strings.');
|
|
78
|
+
this.#assertTarget(request.target);
|
|
79
|
+
const data = request.data === undefined ? {} : onkyoRecord(request.data, 'service data');
|
|
80
|
+
if (request.domain === 'onkyo' && ['status', 'snapshot', 'refresh'].includes(request.service)) {
|
|
81
|
+
onkyoKeys(data, []); return { success: true, data: await this.client.getSnapshot() };
|
|
82
|
+
}
|
|
83
|
+
if (request.domain !== 'onkyo' && request.domain !== 'media_player') throw new TypeError('Onkyo service domain is unsupported.');
|
|
84
|
+
if (request.service === 'set_power') {
|
|
85
|
+
onkyoKeys(data, ['power']); return observed(await this.client.setPower(onkyoBoolean(data.power, 'power')));
|
|
86
|
+
}
|
|
87
|
+
if (request.service === 'set_mute' || request.service === 'volume_mute') {
|
|
88
|
+
onkyoKeys(data, ['muted']); return observed(await this.client.setMuted(onkyoBoolean(data.muted, 'mute')));
|
|
89
|
+
}
|
|
90
|
+
if (request.service === 'set_volume_raw') {
|
|
91
|
+
onkyoKeys(data, ['volumeRaw']); return observed(await this.client.setVolume(onkyoInteger(data.volumeRaw, 0, 100, 'raw volume')));
|
|
92
|
+
}
|
|
93
|
+
if (request.service === 'set_input' || request.service === 'select_source') {
|
|
94
|
+
onkyoKeys(data, ['inputId']); return observed(await this.client.setInput(onkyoInputId(data.inputId)));
|
|
95
|
+
}
|
|
96
|
+
throw new TypeError('Onkyo service is unsupported.');
|
|
97
|
+
} catch (errorArg) { return failed(errorArg); }
|
|
98
|
+
}
|
|
99
|
+
public async writeFeature(requestArg: IDeviceFeatureWriteRequest, signalArg?: AbortSignal): Promise<IServiceCallResult> {
|
|
100
|
+
try {
|
|
101
|
+
const request = onkyoRecord(requestArg, 'feature write');
|
|
102
|
+
onkyoKeys(request, ['device', 'feature', 'value']);
|
|
103
|
+
const device = onkyoRecord(request.device, 'feature device');
|
|
104
|
+
const feature = onkyoRecord(request.feature, 'feature');
|
|
105
|
+
if (device.id !== this.#deviceId || device.integrationDomain !== 'onkyo') throw new TypeError('Onkyo feature device is not owned by this home runtime.');
|
|
106
|
+
if (typeof feature.id !== 'string') throw new TypeError('Onkyo feature id must be a string.');
|
|
107
|
+
const value = request.value;
|
|
108
|
+
if (typeof value !== 'boolean' && typeof value !== 'number' && typeof value !== 'string') throw new TypeError('Onkyo control requires an explicit primitive value.');
|
|
109
|
+
return observed(await this.client.writeFeature(feature.id, value, signalArg));
|
|
110
|
+
} catch (errorArg) { return failed(errorArg); }
|
|
111
|
+
}
|
|
112
|
+
public destroy(): Promise<void> {
|
|
113
|
+
this.#closePromise ??= Promise.resolve().then(() => this.closeLease());
|
|
114
|
+
return this.#closePromise;
|
|
115
|
+
}
|
|
116
|
+
#assertTarget(valueArg: unknown): void {
|
|
117
|
+
const target = onkyoRecord(valueArg, 'service target');
|
|
118
|
+
onkyoKeys(target, ['deviceId', 'entityId']);
|
|
119
|
+
if (target.deviceId === undefined && target.entityId === undefined ||
|
|
120
|
+
target.deviceId !== undefined && target.deviceId !== this.#deviceId ||
|
|
121
|
+
target.entityId !== undefined && target.entityId !== this.#entityId) throw new TypeError('Onkyo services require an exact owned device or main-zone entity target.');
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
function observed(observationArg: plugins.ecobridgeDeviceManager.IOnkyoControlObservation): IServiceCallResult {
|
|
125
|
+
return { success: true, data: { ...observationArg } };
|
|
126
|
+
}
|
|
127
|
+
function failed(errorArg: unknown): IServiceCallResult {
|
|
128
|
+
return { success: false, error: errorArg instanceof Error ? errorArg.message : 'Onkyo operation failed.', data: {
|
|
129
|
+
effectOutcome: errorArg instanceof plugins.ecobridgeDeviceManager.OnkyoProtocolError ? errorArg.effectOutcome : 'not-sent',
|
|
130
|
+
code: errorArg instanceof plugins.ecobridgeDeviceManager.OnkyoProtocolError ? errorArg.code : 'INVALID_OPERATION',
|
|
131
|
+
} };
|
|
132
|
+
}
|
|
@@ -1,4 +1,42 @@
|
|
|
1
|
-
import
|
|
2
|
-
import {
|
|
1
|
+
import * as plugins from '../../plugins.js';
|
|
2
|
+
import { DiscoveryDescriptor } from '../../core/classes.discoverydescriptor.js';
|
|
3
|
+
import type { IDiscoveryCandidate, IDiscoveryMatch } from '../../core/types.js';
|
|
4
|
+
import type { IOnkyoSelectedProbeOptions } from './onkyo.types.js';
|
|
5
|
+
import { onkyoHost, onkyoInteger, onkyoKeys, onkyoName, onkyoRecord, readOnkyoEndpoint } from './onkyo.validation.js';
|
|
3
6
|
|
|
4
|
-
|
|
7
|
+
function matchCandidate(valueArg: unknown): IDiscoveryMatch {
|
|
8
|
+
try {
|
|
9
|
+
const input = onkyoRecord(valueArg, 'discovery candidate');
|
|
10
|
+
if (input.source !== 'manual' && input.source !== 'custom' || input.integrationDomain !== 'onkyo') {
|
|
11
|
+
return { matched: false, confidence: 'low', reason: 'An explicit manual/custom Onkyo candidate is required.' };
|
|
12
|
+
}
|
|
13
|
+
const candidate: IDiscoveryCandidate = {
|
|
14
|
+
source: input.source, integrationDomain: 'onkyo', ...readOnkyoEndpoint(input.host, input.macAddress, input.model, input.port),
|
|
15
|
+
name: input.name === undefined ? undefined : onkyoName(input.name, 'candidate name'),
|
|
16
|
+
};
|
|
17
|
+
return { matched: true, confidence: 'medium', reason: 'Explicit endpoint/MAC/model binding; live identity and main zone are checked during setup.', candidate };
|
|
18
|
+
} catch (errorArg) {
|
|
19
|
+
return { matched: false, confidence: 'low', reason: errorArg instanceof Error ? errorArg.message : 'Invalid Onkyo candidate.' };
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function createOnkyoDiscoveryDescriptor(): DiscoveryDescriptor {
|
|
24
|
+
return new DiscoveryDescriptor({ integrationDomain: 'onkyo', displayName: 'Onkyo' })
|
|
25
|
+
.addMatcher({ id: 'onkyo-manual-match', source: 'manual', matches: async (inputArg) => matchCandidate(inputArg) })
|
|
26
|
+
.addMatcher({ id: 'onkyo-custom-match', source: 'custom', matches: async (inputArg) => matchCandidate(inputArg) })
|
|
27
|
+
.addValidator({ id: 'onkyo-explicit-endpoint', validate: async (candidateArg) => matchCandidate(candidateArg) });
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/** One explicitly selected unicast UDP probe; TCP setup still validates the chosen identity. */
|
|
31
|
+
export async function probeSelectedOnkyo(optionsArg: IOnkyoSelectedProbeOptions): Promise<IDiscoveryCandidate> {
|
|
32
|
+
const options = onkyoRecord(optionsArg, 'selected probe options');
|
|
33
|
+
onkyoKeys(options, ['host', 'port', 'requestTimeoutMs', 'signal']);
|
|
34
|
+
if (options.signal !== undefined && !(options.signal instanceof AbortSignal)) throw new TypeError('Onkyo probe signal must be an AbortSignal.');
|
|
35
|
+
const advertisement = await plugins.ecobridgeDeviceManager.probeOnkyo({
|
|
36
|
+
address: onkyoHost(options.host), port: options.port === undefined ? undefined : onkyoInteger(options.port, 1, 65535, 'probe port'),
|
|
37
|
+
timeoutMs: options.requestTimeoutMs === undefined ? undefined : onkyoInteger(options.requestTimeoutMs, 1, 60000, 'probe deadline'),
|
|
38
|
+
signal: options.signal,
|
|
39
|
+
});
|
|
40
|
+
return { source: 'custom', integrationDomain: 'onkyo', host: advertisement.address, port: advertisement.port,
|
|
41
|
+
model: advertisement.model, macAddress: advertisement.macAddress, manufacturer: 'Onkyo', metadata: { area: advertisement.area } };
|
|
42
|
+
}
|
|
@@ -1,26 +1,53 @@
|
|
|
1
|
-
import type * as
|
|
2
|
-
import {
|
|
3
|
-
import type {
|
|
4
|
-
import { onkyoProfile } from './onkyo.types.js';
|
|
1
|
+
import type * as plugins from '../../plugins.js';
|
|
2
|
+
import type { IIntegrationEntity } from '../../core/types.js';
|
|
3
|
+
import type { IOnkyoSnapshot } from './onkyo.types.js';
|
|
5
4
|
|
|
6
5
|
export class OnkyoMapper {
|
|
7
|
-
public static
|
|
8
|
-
return
|
|
6
|
+
public static deviceId(snapshotArg: Pick<IOnkyoSnapshot, 'homeId' | 'receiver'>): string {
|
|
7
|
+
return `onkyo:${encodeURIComponent(snapshotArg.homeId)}:${snapshotArg.receiver.macAddress}`;
|
|
9
8
|
}
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
return SimpleLocalMapper.toSnapshot({ profile: onkyoProfile, config: configArg, rawData: rawDataArg, online: true, source: 'manual' });
|
|
9
|
+
public static mediaEntityId(snapshotArg: Pick<IOnkyoSnapshot, 'homeId' | 'receiver'>): string {
|
|
10
|
+
return `${this.deviceId(snapshotArg)}:main`;
|
|
13
11
|
}
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
12
|
+
public static toDevices(snapshotArg: IOnkyoSnapshot): plugins.shxInterfaces.data.IDeviceDefinition[] {
|
|
13
|
+
const receiver = snapshotArg.receiver;
|
|
14
|
+
const features: plugins.shxInterfaces.data.IDeviceFeature[] = [
|
|
15
|
+
{ id: 'connectivity', capability: 'sensor', name: 'Connectivity', readable: true, writable: false },
|
|
16
|
+
{ id: 'identity', capability: 'sensor', name: 'Receiver identity', readable: true, writable: false },
|
|
17
|
+
{ id: 'inputs', capability: 'sensor', name: 'Advertised main-zone inputs', readable: true, writable: false },
|
|
18
|
+
];
|
|
19
|
+
const state: plugins.shxInterfaces.data.IDeviceState[] = [
|
|
20
|
+
{ featureId: 'connectivity', value: 'online', updatedAt: snapshotArg.observedAt },
|
|
21
|
+
{ featureId: 'identity', value: { macAddress: receiver.macAddress, model: receiver.model, brand: receiver.brand }, updatedAt: snapshotArg.observedAt },
|
|
22
|
+
{ featureId: 'inputs', value: { inputs: receiver.inputs.map((inputArg) => ({ ...inputArg })) }, updatedAt: snapshotArg.observedAt },
|
|
23
|
+
];
|
|
24
|
+
const add = (idArg: string, nameArg: string, valueArg: boolean | number | string, writableArg: boolean, unitArg?: string): void => {
|
|
25
|
+
features.push({ id: idArg, capability: 'media', name: nameArg, readable: true, writable: writableArg, ...(unitArg ? { unit: unitArg } : {}) });
|
|
26
|
+
state.push({ featureId: idArg, value: valueArg, updatedAt: snapshotArg.observedAt });
|
|
27
|
+
};
|
|
28
|
+
if (snapshotArg.power !== undefined) add('power', 'Power', snapshotArg.power, true);
|
|
29
|
+
if (snapshotArg.muted !== undefined) add('mute', 'Mute', snapshotArg.muted, true);
|
|
30
|
+
if (snapshotArg.volumeRaw !== undefined) add('volume_raw', 'Absolute raw volume', snapshotArg.volumeRaw,
|
|
31
|
+
receiver.integerVolumeMaximum !== undefined && snapshotArg.volumeRaw <= receiver.integerVolumeMaximum, 'receiver raw units');
|
|
32
|
+
if (snapshotArg.inputId !== undefined) add('input', 'Input selector', snapshotArg.inputId, receiver.inputs.length > 0);
|
|
33
|
+
return [{
|
|
34
|
+
id: this.deviceId(snapshotArg), integrationDomain: 'onkyo', name: snapshotArg.name, manufacturer: 'Onkyo',
|
|
35
|
+
model: receiver.model, protocol: 'tcp', online: snapshotArg.online, features, state,
|
|
36
|
+
metadata: { homeId: snapshotArg.homeId, macAddress: receiver.macAddress, zone: 'main', wireProtocol: 'eiscp', lanAuthenticated: false,
|
|
37
|
+
inputs: receiver.inputs.map((inputArg) => ({ ...inputArg })), integerVolumeMaximum: receiver.integerVolumeMaximum,
|
|
38
|
+
volumeLimitRaw: snapshotArg.volumeLimitRaw,
|
|
39
|
+
...(receiver.integerVolumeMaximum === undefined ? {} : { volumeRange: { minimumRaw: 0, maximumRaw: Math.min(receiver.integerVolumeMaximum, snapshotArg.volumeLimitRaw) } }),
|
|
40
|
+
observedAt: snapshotArg.observedAt },
|
|
41
|
+
}];
|
|
17
42
|
}
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
43
|
+
public static toEntities(snapshotArg: IOnkyoSnapshot): IIntegrationEntity[] {
|
|
44
|
+
return [{
|
|
45
|
+
id: this.mediaEntityId(snapshotArg), uniqueId: this.mediaEntityId(snapshotArg), deviceId: this.deviceId(snapshotArg),
|
|
46
|
+
integrationDomain: 'onkyo', platform: 'media_player', name: snapshotArg.name,
|
|
47
|
+
state: snapshotArg.power === undefined ? 'unknown' : snapshotArg.power ? 'on' : 'off', available: snapshotArg.online,
|
|
48
|
+
attributes: { power: snapshotArg.power, muted: snapshotArg.muted, volumeRaw: snapshotArg.volumeRaw, inputId: snapshotArg.inputId,
|
|
49
|
+
inputs: snapshotArg.receiver.inputs.map((inputArg) => ({ ...inputArg })), integerVolumeMaximum: snapshotArg.receiver.integerVolumeMaximum,
|
|
50
|
+
volumeLimitRaw: snapshotArg.volumeLimitRaw, zone: 'main', observedAt: snapshotArg.observedAt },
|
|
51
|
+
}];
|
|
25
52
|
}
|
|
26
53
|
}
|
|
@@ -1,120 +1,65 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import type * as plugins from '../../plugins.js';
|
|
2
2
|
|
|
3
|
-
export const onkyoDomain =
|
|
4
|
-
export const onkyoDefaultName =
|
|
3
|
+
export const onkyoDomain = 'onkyo';
|
|
4
|
+
export const onkyoDefaultName = 'Onkyo';
|
|
5
5
|
export const onkyoDefaultPort = 60128;
|
|
6
6
|
export const onkyoDefaultTimeoutMs = 5000;
|
|
7
7
|
|
|
8
|
-
export
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
8
|
+
export interface IOnkyoIntegrationOptions {
|
|
9
|
+
/** A consistency binding; setup and flow still require explicit host home. */
|
|
10
|
+
readonly homeId?: string;
|
|
11
|
+
/** Shared pending/runtime/flow/retained-cleanup capacity; defaults to 16. */
|
|
12
|
+
readonly capacity?: number;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export interface IOnkyoConfig {
|
|
16
|
+
homeId: string;
|
|
17
|
+
host: string;
|
|
18
|
+
macAddress: string;
|
|
19
|
+
model: string;
|
|
20
|
+
/** The selected identity does not authenticate the cleartext LAN peer. */
|
|
21
|
+
allowUnencryptedLan: true;
|
|
22
|
+
/** Absolute integer receiver units; no percent or half-step conversion. */
|
|
23
|
+
volumeLimitRaw: number;
|
|
24
|
+
port?: number;
|
|
25
|
+
name?: string;
|
|
26
|
+
requestTimeoutMs?: number;
|
|
27
|
+
maxPendingOperations?: number;
|
|
14
28
|
}
|
|
15
29
|
export interface IHomeAssistantOnkyoConfig extends IOnkyoConfig {}
|
|
16
30
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
"toggle",
|
|
40
|
-
"turn_off",
|
|
41
|
-
"turn_on",
|
|
42
|
-
"volume_down",
|
|
43
|
-
"volume_mute",
|
|
44
|
-
"volume_set",
|
|
45
|
-
"volume_up",
|
|
46
|
-
"onkyo_select_hdmi_output",
|
|
47
|
-
],
|
|
48
|
-
discoverySources: [
|
|
49
|
-
'manual',
|
|
50
|
-
'custom',
|
|
51
|
-
],
|
|
52
|
-
discoveryKeywords: [
|
|
53
|
-
"onkyo",
|
|
54
|
-
"Onkyo",
|
|
55
|
-
],
|
|
31
|
+
/** Sequential live observations, never an injectable production data source. */
|
|
32
|
+
export interface IOnkyoSnapshot extends plugins.ecobridgeDeviceManager.IOnkyoState {
|
|
33
|
+
readonly homeId: string;
|
|
34
|
+
readonly name: string;
|
|
35
|
+
readonly volumeLimitRaw: number;
|
|
36
|
+
readonly online: true;
|
|
37
|
+
}
|
|
38
|
+
export type TOnkyoRawData = IOnkyoSnapshot;
|
|
39
|
+
export type TOnkyoClientEvent =
|
|
40
|
+
| { type: 'snapshot'; snapshot: IOnkyoSnapshot }
|
|
41
|
+
| { type: 'control'; observation: plugins.ecobridgeDeviceManager.IOnkyoControlObservation }
|
|
42
|
+
| { type: 'error'; error: Error };
|
|
43
|
+
|
|
44
|
+
export interface IOnkyoSelectedProbeOptions {
|
|
45
|
+
host: string;
|
|
46
|
+
port?: number;
|
|
47
|
+
requestTimeoutMs?: number;
|
|
48
|
+
signal?: AbortSignal;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export const onkyoProfile = {
|
|
52
|
+
domain: onkyoDomain, displayName: onkyoDefaultName, status: 'control-runtime',
|
|
56
53
|
metadata: {
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
"dependencies": [
|
|
67
|
-
"network",
|
|
68
|
-
],
|
|
69
|
-
"codeowners": [
|
|
70
|
-
"@arturpragacz",
|
|
71
|
-
"@eclair4151",
|
|
72
|
-
],
|
|
73
|
-
"configFlow": true,
|
|
74
|
-
"documentation": "https://www.home-assistant.io/integrations/onkyo",
|
|
75
|
-
"loggers": [
|
|
76
|
-
"aioonkyo",
|
|
77
|
-
],
|
|
78
|
-
"runtime": {
|
|
79
|
-
"type": "control-runtime",
|
|
80
|
-
"services": [
|
|
81
|
-
"snapshot",
|
|
82
|
-
"status",
|
|
83
|
-
"refresh",
|
|
84
|
-
"media_pause",
|
|
85
|
-
"media_play",
|
|
86
|
-
"media_stop",
|
|
87
|
-
"select_source",
|
|
88
|
-
"toggle",
|
|
89
|
-
"turn_off",
|
|
90
|
-
"turn_on",
|
|
91
|
-
"volume_down",
|
|
92
|
-
"volume_mute",
|
|
93
|
-
"volume_set",
|
|
94
|
-
"volume_up",
|
|
95
|
-
"onkyo_select_hdmi_output",
|
|
96
|
-
],
|
|
97
|
-
"platforms": [
|
|
98
|
-
"media_player",
|
|
99
|
-
"switch",
|
|
100
|
-
],
|
|
101
|
-
"controls": "native eISCP TCP commands for main-zone media controls; injected client.execute or commandExecutor remains supported",
|
|
102
|
-
},
|
|
103
|
-
"localApi": {
|
|
104
|
-
"status": "Native Onkyo/Pioneer eISCP TCP polling and main-zone control are implemented for manual host/port configurations; UDP discovery/interview and persistent push subscriptions are not implemented.",
|
|
105
|
-
"implemented": [
|
|
106
|
-
"native eISCP TCP polling on port 60128 for main-zone power, volume, mute, input source, and listening mode status",
|
|
107
|
-
"native eISCP TCP control for power, volume, mute, source selection, HDMI output, and NET play/pause/stop commands",
|
|
108
|
-
"manual local setup for Onkyo endpoints, snapshots, rawData, snapshotProvider, and injected native clients",
|
|
109
|
-
"runtime mapping for Onkyo device snapshots and Home Assistant platform metadata",
|
|
110
|
-
"safe command delegation to injected client.execute or commandExecutor when no live eISCP host is configured",
|
|
111
|
-
],
|
|
112
|
-
"explicitUnsupported": [
|
|
113
|
-
"UDP eISCP discovery/interview broadcast semantics; use manual host configuration or external discovery metadata",
|
|
114
|
-
"persistent local_push subscription and unsolicited status update processing; this client performs focused request/response TCP polling and commands",
|
|
115
|
-
"multi-zone and channel-muting switch discovery beyond main-zone eISCP status/control",
|
|
116
|
-
"Home Assistant entity registry migrations and repair flows",
|
|
117
|
-
],
|
|
54
|
+
source: 'home-assistant/core', upstreamDomain: onkyoDomain, configFlow: true,
|
|
55
|
+
localApi: {
|
|
56
|
+
status: 'Selected native Onkyo main-zone eISCP TCP through devicemanager.',
|
|
57
|
+
implemented: ['selected unicast UDP advertisement', 'live MAC/model and enabled main-zone admission',
|
|
58
|
+
'explicit power/mute, advertised input selectors and bounded absolute integer raw volume',
|
|
59
|
+
'explicit captured host home and optional actual caller-owned NoSQL store', 'events from explicit observations and joined terminal shutdown'],
|
|
60
|
+
explicitUnsupported: ['broadcast discovery', 'cloud access', 'injected clients and snapshots', 'other zones',
|
|
61
|
+
'percentage or half-step volume conversion', 'toggle/increment/media transport/arbitrary commands',
|
|
62
|
+
'automatic reconnect, effect retry or replay', 'unsolicited push subscription', 'cryptographic LAN peer authentication'],
|
|
118
63
|
},
|
|
119
64
|
},
|
|
120
|
-
};
|
|
65
|
+
} as const;
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import * as plugins from '../../plugins.js';
|
|
2
|
+
import type { IOnkyoConfig } from './onkyo.types.js';
|
|
3
|
+
import { onkyoDefaultPort, onkyoDefaultTimeoutMs } from './onkyo.types.js';
|
|
4
|
+
|
|
5
|
+
interface IOnkyoNormalizedConfig extends IOnkyoConfig {
|
|
6
|
+
port: number;
|
|
7
|
+
requestTimeoutMs: number;
|
|
8
|
+
maxPendingOperations: number;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export function onkyoRecord(valueArg: unknown, nameArg: string): Record<string, unknown> {
|
|
12
|
+
if (!valueArg || typeof valueArg !== 'object' || Array.isArray(valueArg) ||
|
|
13
|
+
![Object.prototype, null].includes(Object.getPrototypeOf(valueArg))) {
|
|
14
|
+
throw new TypeError(`Onkyo ${nameArg} must be a plain data object.`);
|
|
15
|
+
}
|
|
16
|
+
const result: Record<string, unknown> = {};
|
|
17
|
+
for (const key of Reflect.ownKeys(valueArg)) {
|
|
18
|
+
const descriptor = Object.getOwnPropertyDescriptor(valueArg, key);
|
|
19
|
+
if (typeof key !== 'string' || !descriptor || !('value' in descriptor) || !descriptor.enumerable) {
|
|
20
|
+
throw new TypeError(`Onkyo ${nameArg} may not contain symbols, accessors or hidden fields.`);
|
|
21
|
+
}
|
|
22
|
+
Object.defineProperty(result, key, { value: descriptor.value, enumerable: true });
|
|
23
|
+
}
|
|
24
|
+
return result;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export function onkyoKeys(recordArg: Record<string, unknown>, keysArg: readonly string[]): void {
|
|
28
|
+
if (Object.keys(recordArg).some((keyArg) => !keysArg.includes(keyArg))) throw new TypeError('Onkyo data contains unsupported fields.');
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export function onkyoInteger(valueArg: unknown, minimumArg: number, maximumArg: number, nameArg: string): number {
|
|
32
|
+
if (typeof valueArg !== 'number' || !Number.isSafeInteger(valueArg) || valueArg < minimumArg || valueArg > maximumArg) {
|
|
33
|
+
throw new RangeError(`Onkyo ${nameArg} must be an integer from ${minimumArg} through ${maximumArg}.`);
|
|
34
|
+
}
|
|
35
|
+
return valueArg;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export function onkyoName(valueArg: unknown, nameArg: string): string {
|
|
39
|
+
if (typeof valueArg !== 'string' || !valueArg.trim() || valueArg.length > 256 || /[\p{Cc}\p{Cs}]/u.test(valueArg)) {
|
|
40
|
+
throw new TypeError(`Onkyo ${nameArg} must be a non-empty string without control characters, at most 256 characters.`);
|
|
41
|
+
}
|
|
42
|
+
return valueArg;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export function onkyoHost(valueArg: unknown): string {
|
|
46
|
+
if (typeof valueArg !== 'string' || plugins.net.isIP(valueArg) !== 4) throw new TypeError('Onkyo host must be a selected unicast IPv4 address.');
|
|
47
|
+
const octets = valueArg.split('.').map(Number);
|
|
48
|
+
const local = octets[0] === 10 || octets[0] === 127 || octets[0] === 192 && octets[1] === 168 ||
|
|
49
|
+
octets[0] === 172 && octets[1] >= 16 && octets[1] <= 31 || octets[0] === 169 && octets[1] === 254;
|
|
50
|
+
if (!local || octets[3] === 255) throw new TypeError('Onkyo host must be a private, loopback or link-local unicast IPv4 address.');
|
|
51
|
+
return valueArg;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export function readOnkyoEndpoint(hostArg: unknown, macArg: unknown, modelArg: unknown, portArg?: unknown): Pick<IOnkyoNormalizedConfig, 'host' | 'macAddress' | 'model' | 'port'> {
|
|
55
|
+
const host = onkyoHost(hostArg);
|
|
56
|
+
if (typeof macArg !== 'string' || !/^[0-9a-f]{12}$/i.test(macArg) || /^(0{12}|f{12})$/i.test(macArg)) {
|
|
57
|
+
throw new TypeError('Onkyo macAddress must be a nonempty twelve-digit receiver MAC.');
|
|
58
|
+
}
|
|
59
|
+
if (typeof modelArg !== 'string' || !/^[A-Za-z0-9][A-Za-z0-9 ._-]{0,63}$/.test(modelArg)) throw new TypeError('Onkyo model must be the exact selected receiver model.');
|
|
60
|
+
return { host, macAddress: macArg.toUpperCase(), model: modelArg,
|
|
61
|
+
port: onkyoInteger(portArg === undefined ? onkyoDefaultPort : portArg, 1, 65535, 'port') };
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export function normalizeOnkyoConfig(valueArg: unknown): Readonly<IOnkyoNormalizedConfig> {
|
|
65
|
+
const config = onkyoRecord(valueArg, 'configuration');
|
|
66
|
+
onkyoKeys(config, ['homeId', 'host', 'macAddress', 'model', 'allowUnencryptedLan', 'volumeLimitRaw', 'port', 'name', 'requestTimeoutMs', 'maxPendingOperations']);
|
|
67
|
+
if (config.allowUnencryptedLan !== true) throw new TypeError('Onkyo requires an explicit allowUnencryptedLan: true acknowledgement.');
|
|
68
|
+
return Object.freeze({
|
|
69
|
+
homeId: onkyoName(config.homeId, 'homeId'), ...readOnkyoEndpoint(config.host, config.macAddress, config.model, config.port),
|
|
70
|
+
allowUnencryptedLan: true, volumeLimitRaw: onkyoInteger(config.volumeLimitRaw, 0, 100, 'volumeLimitRaw'),
|
|
71
|
+
name: config.name === undefined ? undefined : onkyoName(config.name, 'name'),
|
|
72
|
+
requestTimeoutMs: onkyoInteger(config.requestTimeoutMs === undefined ? onkyoDefaultTimeoutMs : config.requestTimeoutMs, 1, 60000, 'requestTimeoutMs'),
|
|
73
|
+
maxPendingOperations: onkyoInteger(config.maxPendingOperations === undefined ? 16 : config.maxPendingOperations, 1, 128, 'maxPendingOperations'),
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export function onkyoBoolean(valueArg: unknown, nameArg: string): boolean {
|
|
78
|
+
if (typeof valueArg !== 'boolean') throw new TypeError(`Onkyo ${nameArg} requires an explicit boolean.`);
|
|
79
|
+
return valueArg;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export function onkyoInputId(valueArg: unknown): string {
|
|
83
|
+
if (typeof valueArg !== 'string' || !/^[0-9a-f]{2}$/i.test(valueArg)) throw new TypeError('Onkyo input requires an advertised two-digit selector ID.');
|
|
84
|
+
return valueArg.toUpperCase();
|
|
85
|
+
}
|