@smarthome.exchange/integrations 2.0.0 → 4.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 +46 -0
- package/dist_ts/00_commitinfo_data.js +1 -1
- package/dist_ts/core/classes.nativeintegrationowner.d.ts +2 -0
- package/dist_ts/core/classes.nativeintegrationowner.js +5 -1
- package/dist_ts/core/classes.runtimemanager.d.ts +2 -0
- package/dist_ts/core/classes.runtimemanager.js +11 -3
- package/dist_ts/core/types.d.ts +2 -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/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 +32 -4
- package/dist_ts/integrations/hue/hue.classes.client.js +331 -57
- package/dist_ts/integrations/hue/hue.classes.configflow.d.ts +6 -1
- package/dist_ts/integrations/hue/hue.classes.configflow.js +79 -27
- package/dist_ts/integrations/hue/hue.classes.integration.d.ts +3 -1
- package/dist_ts/integrations/hue/hue.classes.integration.js +83 -62
- package/dist_ts/integrations/hue/hue.mapper.d.ts +2 -0
- package/dist_ts/integrations/hue/hue.mapper.js +8 -6
- package/dist_ts/integrations/hue/hue.types.d.ts +32 -1
- package/dist_ts/integrations/hue/hue.validation.d.ts +9 -3
- package/dist_ts/integrations/hue/hue.validation.js +112 -11
- 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/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/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/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 +10 -10
- package/readme.md +25 -6
- package/ts/00_commitinfo_data.ts +1 -1
- package/ts/core/classes.nativeintegrationowner.ts +5 -0
- package/ts/core/classes.runtimemanager.ts +11 -2
- package/ts/core/types.ts +2 -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/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 +288 -55
- package/ts/integrations/hue/hue.classes.configflow.ts +68 -25
- package/ts/integrations/hue/hue.classes.integration.ts +75 -54
- package/ts/integrations/hue/hue.mapper.ts +8 -5
- package/ts/integrations/hue/hue.types.ts +37 -1
- package/ts/integrations/hue/hue.validation.ts +102 -13
- package/ts/integrations/hue/readme.md +49 -12
- 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/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/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/zigbee2mqtt/readme.md +22 -27
- package/ts/integrations/zigbee2mqtt/zigbee2mqtt.classes.configflow.ts +16 -7
- package/ts/integrations/zigbee2mqtt/zigbee2mqtt.classes.integration.ts +53 -6
- package/ts/integrations/zigbee2mqtt/zigbee2mqtt.classes.runtime.ts +62 -49
- package/ts/integrations/zigbee2mqtt/zigbee2mqtt.mapper.ts +17 -11
- package/ts/integrations/zigbee2mqtt/zigbee2mqtt.types.ts +35 -2
- package/ts/integrations/zigbee2mqtt/zigbee2mqtt.validation.ts +53 -7
- package/ts/integrations/zwave_js/readme.md +42 -9
- package/ts/integrations/zwave_js/zwave_js.classes.client.ts +155 -31
- package/ts/integrations/zwave_js/zwave_js.classes.configflow.ts +55 -31
- package/ts/integrations/zwave_js/zwave_js.classes.integration.ts +57 -35
- package/ts/integrations/zwave_js/zwave_js.discovery.ts +2 -2
- package/ts/integrations/zwave_js/zwave_js.mapper.ts +37 -23
- package/ts/integrations/zwave_js/zwave_js.types.ts +26 -3
- package/ts/integrations/zwave_js/zwave_js.validation.ts +52 -18
- package/ts/plugins.ts +2 -1
|
@@ -1,48 +1,76 @@
|
|
|
1
|
+
import { NativeIntegrationOwner, NativeIntegrationScope } from '../../core/classes.nativeintegrationowner.js';
|
|
1
2
|
import type { IConfigFlow, IConfigFlowContext, IConfigFlowStep, IDiscoveryCandidate } from '../../core/types.js';
|
|
2
|
-
import { DeconzClient, DeconzRequestError } from './deconz.classes.client.js';
|
|
3
|
-
import type { IDeconzConfig } from './deconz.types.js';
|
|
3
|
+
import { DeconzClient, DeconzRequestError, DeconzWriteError } from './deconz.classes.client.js';
|
|
4
|
+
import type { IDeconzConfig, IDeconzIntegrationOptions } from './deconz.types.js';
|
|
5
|
+
import { captureDeconzOptions, deconzBridgeId, deconzData, deconzText, normalizeDeconzConfig } from './deconz.validation.js';
|
|
4
6
|
|
|
5
7
|
export class DeconzConfigFlow implements IConfigFlow<IDeconzConfig> {
|
|
8
|
+
readonly #options: Readonly<IDeconzIntegrationOptions>;
|
|
9
|
+
readonly #owner: NativeIntegrationOwner;
|
|
10
|
+
readonly #scope: NativeIntegrationScope;
|
|
11
|
+
readonly #ownsOwner: boolean;
|
|
12
|
+
#terminal = false;
|
|
13
|
+
#submitted = false;
|
|
14
|
+
|
|
15
|
+
constructor(optionsArg: IDeconzIntegrationOptions = {}, ownerArg?: NativeIntegrationOwner) {
|
|
16
|
+
this.#options = captureDeconzOptions(optionsArg);
|
|
17
|
+
this.#ownsOwner = !ownerArg;
|
|
18
|
+
this.#owner = ownerArg ?? new NativeIntegrationOwner('deconz', { homeId: this.#options.homeId, capacity: this.#options.capacity });
|
|
19
|
+
this.#scope = this.#owner.createFlowScope();
|
|
20
|
+
}
|
|
21
|
+
|
|
6
22
|
public async start(candidateArg: IDiscoveryCandidate, contextArg: IConfigFlowContext): Promise<IConfigFlowStep<IDeconzConfig>> {
|
|
7
|
-
|
|
23
|
+
const context = this.#scope.admit(contextArg);
|
|
24
|
+
if (this.#terminal) throw new Error('deCONZ config flow is closed.');
|
|
25
|
+
const candidate = deconzData(candidateArg, ['id', 'source', 'host', 'port', 'name', 'integrationDomain', 'manufacturer', 'model', 'serialNumber', 'macAddress', 'metadata']);
|
|
26
|
+
// Discovery reports a selection hint; only current gateway reads can check that reported identity.
|
|
27
|
+
const bridgeId = deconzBridgeId(candidate.id);
|
|
28
|
+
const host = deconzText(candidate.host, 'discovered host');
|
|
29
|
+
const port = candidate.port;
|
|
30
|
+
if (port !== undefined && typeof port !== 'number') throw new TypeError('Invalid deCONZ discovered port.');
|
|
8
31
|
const submit = async (valuesArg: Record<string, unknown>): Promise<IConfigFlowStep<IDeconzConfig>> => {
|
|
9
|
-
|
|
32
|
+
if (this.#terminal || this.#submitted) return { kind: 'error', title: 'deCONZ flow closed', error: 'deCONZ config flow is closed or already submitting.' };
|
|
33
|
+
this.#submitted = true;
|
|
34
|
+
let effectAccepted = false;
|
|
35
|
+
let capturedValues: Record<string, unknown> | undefined;
|
|
10
36
|
try {
|
|
11
|
-
const
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
const
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
client
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
return { kind: 'done', title: 'deCONZ connected', config };
|
|
37
|
+
const values = deconzData(valuesArg, ['host', 'port', 'protocol', 'credentialReference', 'apiKey']);
|
|
38
|
+
capturedValues = Object.freeze(values);
|
|
39
|
+
const config = normalizeDeconzConfig({ homeId: context.homeId, host: values.host ?? host, ...(values.port !== undefined || port !== undefined ? { port: values.port ?? port } : {}), protocol: values.protocol ?? 'http', bridgeId, credentialReference: deconzText(values.credentialReference, 'credential reference') });
|
|
40
|
+
const suppliedKey = values.apiKey === undefined || values.apiKey === '' ? undefined : deconzText(values.apiKey, 'pairing credential', 256);
|
|
41
|
+
const result = await this.#scope.run(context, context.homeId, async (lease) => {
|
|
42
|
+
const client = new DeconzClient(config, lease.context, this.#options, this.#owner);
|
|
43
|
+
let cleanupStarted = false;
|
|
44
|
+
lease.own(() => { if (cleanupStarted) return client.retryCleanup(); cleanupStarted = true; return client.destroy(); });
|
|
45
|
+
if (!client.canCreateCredential) throw new Error('deCONZ flow requires credential creation by the selected provider or encrypted store.');
|
|
46
|
+
const key = suppliedKey ?? await client.acquireApiKey(undefined, lease.signal);
|
|
47
|
+
effectAccepted = !suppliedKey;
|
|
48
|
+
await client.createCredential(key, lease.signal);
|
|
49
|
+
lease.assertOpen();
|
|
50
|
+
return { kind: 'done', title: 'deCONZ connected', config } as IConfigFlowStep<IDeconzConfig>;
|
|
51
|
+
});
|
|
52
|
+
this.#terminal = true;
|
|
53
|
+
await this.#scope.close();
|
|
54
|
+
return result;
|
|
30
55
|
} catch (error) {
|
|
31
|
-
if (error instanceof DeconzRequestError && error.apiErrorType === 101) {
|
|
32
|
-
return { kind: 'wait', title: 'Unlock deCONZ', description: 'In Phoscon, open Gateway → Advanced → Authenticate app, then retry within 60 seconds.', submit: () => submit(
|
|
56
|
+
if (error instanceof DeconzRequestError && error.apiErrorType === 101 && !effectAccepted) {
|
|
57
|
+
return { kind: 'wait', title: 'Unlock deCONZ', description: 'In Phoscon, open Gateway → Advanced → Authenticate app, then retry within 60 seconds.', submit: () => submit(capturedValues!) };
|
|
33
58
|
}
|
|
34
|
-
|
|
35
|
-
|
|
59
|
+
if (effectAccepted || error instanceof DeconzWriteError) { this.#terminal = true; await this.#scope.close(); }
|
|
60
|
+
return { kind: 'error', title: 'deCONZ connection failed', error: effectAccepted || error instanceof DeconzWriteError ? 'deCONZ pairing was dispatched; do not replay pairing.' : 'deCONZ setup failed.' };
|
|
61
|
+
} finally { this.#submitted = false; }
|
|
36
62
|
};
|
|
37
63
|
return {
|
|
38
|
-
kind: 'form', title: 'Connect deCONZ Gateway',
|
|
39
|
-
description: 'Provide a gateway API key, or leave it empty to pair after unlocking Authenticate app in Phoscon.',
|
|
64
|
+
kind: 'form', title: 'Connect deCONZ Gateway', description: 'Select a credential reference. A new API key is validated and stored by the host credential provider or encrypted config store.',
|
|
40
65
|
fields: [
|
|
41
66
|
{ name: 'host', label: 'Host', type: 'text', required: true },
|
|
42
67
|
{ name: 'port', label: 'Port', type: 'number' },
|
|
43
68
|
{ name: 'protocol', label: 'Protocol', type: 'select', options: [{ label: 'HTTP', value: 'http' }, { label: 'HTTPS', value: 'https' }] },
|
|
69
|
+
{ name: 'credentialReference', label: 'Credential reference', type: 'text', required: true },
|
|
44
70
|
{ name: 'apiKey', label: 'API key', type: 'password' },
|
|
45
71
|
], submit,
|
|
46
72
|
};
|
|
47
73
|
}
|
|
74
|
+
public destroy(): Promise<void> { this.#terminal = true; return this.#ownsOwner ? this.#owner.close() : this.#scope.close(); }
|
|
75
|
+
public retryCleanup(): Promise<void> { return this.#ownsOwner ? this.#owner.retryCleanup() : this.#scope.retryCleanup(); }
|
|
48
76
|
}
|
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
import * as plugins from '../../plugins.js';
|
|
2
2
|
import { BaseIntegration } from '../../core/classes.baseintegration.js';
|
|
3
3
|
import type { IDeviceFeatureWriteRequest, IIntegrationEntity, IIntegrationRuntime, IIntegrationSetupContext, IServiceCallRequest, IServiceCallResult } from '../../core/types.js';
|
|
4
|
-
import { DeconzClient } from './deconz.classes.client.js';
|
|
4
|
+
import { DeconzClient, DeconzWriteError, type IDeconzClientOperation } from './deconz.classes.client.js';
|
|
5
|
+
import { NativeIntegrationOwner, type INativeSetupLease } from '../../core/classes.nativeintegrationowner.js';
|
|
6
|
+
import { captureDeconzOptions, deconzData, normalizeDeconzConfig } from './deconz.validation.js';
|
|
5
7
|
import { DeconzConfigFlow } from './deconz.classes.configflow.js';
|
|
6
8
|
import { createDeconzDiscoveryDescriptor } from './deconz.discovery.js';
|
|
7
9
|
import { DeconzMapper } from './deconz.mapper.js';
|
|
8
|
-
import type { IDeconzConfig, IDeconzGroupActionPatch, IDeconzLightStatePatch, IDeconzSensorConfigPatch, IDeconzSensorStatePatch, IDeconzSnapshot } from './deconz.types.js';
|
|
10
|
+
import type { IDeconzConfig, IDeconzGroupActionPatch, IDeconzIntegrationOptions, IDeconzLightStatePatch, IDeconzSensorConfigPatch, IDeconzSensorStatePatch, IDeconzSnapshot } from './deconz.types.js';
|
|
9
11
|
|
|
10
12
|
interface IDeconzServiceTarget {
|
|
11
13
|
entity: IIntegrationEntity;
|
|
@@ -17,13 +19,13 @@ interface IDeconzServiceTarget {
|
|
|
17
19
|
}
|
|
18
20
|
|
|
19
21
|
export class DeconzIntegration extends BaseIntegration<IDeconzConfig> {
|
|
20
|
-
|
|
21
|
-
|
|
22
|
+
readonly #owner: NativeIntegrationOwner;
|
|
23
|
+
readonly #options: Readonly<IDeconzIntegrationOptions>;
|
|
22
24
|
public readonly domain = 'deconz';
|
|
23
25
|
public readonly displayName = 'deCONZ';
|
|
24
26
|
public readonly status = 'control-runtime' as const;
|
|
25
27
|
public readonly discoveryDescriptor = createDeconzDiscoveryDescriptor();
|
|
26
|
-
public readonly configFlow
|
|
28
|
+
public readonly configFlow: DeconzConfigFlow;
|
|
27
29
|
public readonly metadata = {
|
|
28
30
|
source: 'home-assistant/core',
|
|
29
31
|
upstreamPath: 'homeassistant/components/deconz',
|
|
@@ -36,46 +38,80 @@ export class DeconzIntegration extends BaseIntegration<IDeconzConfig> {
|
|
|
36
38
|
restDocumentation: 'https://dresden-elektronik.github.io/deconz-rest-doc/',
|
|
37
39
|
};
|
|
38
40
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
this.
|
|
43
|
-
|
|
44
|
-
await client.getSnapshot(contextArg.abortSignal);
|
|
45
|
-
if (this.destroyed || !client.isHealthy) throw new Error('deCONZ setup cancelled.');
|
|
46
|
-
const runtime = new DeconzRuntime(client, () => this.clients.delete(client));
|
|
47
|
-
return runtime;
|
|
48
|
-
} catch (error) { await client.destroy(); this.clients.delete(client); throw error; }
|
|
41
|
+
constructor(optionsArg: IDeconzIntegrationOptions = {}) {
|
|
42
|
+
super();
|
|
43
|
+
this.#options = captureDeconzOptions(optionsArg);
|
|
44
|
+
this.#owner = new NativeIntegrationOwner('deconz', { homeId: this.#options.homeId, capacity: this.#options.capacity });
|
|
45
|
+
this.configFlow = new DeconzConfigFlow(this.#options, this.#owner);
|
|
49
46
|
}
|
|
50
47
|
|
|
51
|
-
public async
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
48
|
+
public async setup(configArg: IDeconzConfig, contextArg: IIntegrationSetupContext): Promise<IIntegrationRuntime> {
|
|
49
|
+
const config = normalizeDeconzConfig(configArg);
|
|
50
|
+
return this.#owner.runSetup(contextArg, config.homeId, async (lease) => {
|
|
51
|
+
const client = new DeconzClient(config, lease.context, this.#options, this.#owner);
|
|
52
|
+
let cleanupStarted = false;
|
|
53
|
+
lease.own(() => { if (cleanupStarted) return client.retryCleanup(); cleanupStarted = true; return client.destroy(); });
|
|
54
|
+
await client.getSnapshot(lease.signal);
|
|
55
|
+
lease.assertOpen();
|
|
56
|
+
if (!client.isHealthy) throw new Error('deCONZ setup cancelled.');
|
|
57
|
+
lease.promoteToRuntime();
|
|
58
|
+
return new DeconzRuntime(client, lease);
|
|
59
|
+
});
|
|
56
60
|
}
|
|
61
|
+
|
|
62
|
+
public destroy(): Promise<void> { return this.#owner.close(); }
|
|
63
|
+
public retryCleanup(): Promise<void> { return this.#owner.retryCleanup(); }
|
|
64
|
+
|
|
57
65
|
}
|
|
58
66
|
|
|
59
67
|
export class HomeAssistantDeconzIntegration extends DeconzIntegration {}
|
|
60
68
|
|
|
61
69
|
class DeconzRuntime implements IIntegrationRuntime {
|
|
62
|
-
public domain = 'deconz';
|
|
63
|
-
|
|
64
|
-
constructor(private readonly client: DeconzClient, private readonly onDestroyed: () => void) {}
|
|
65
|
-
|
|
70
|
+
public readonly domain = 'deconz';
|
|
71
|
+
constructor(private readonly client: DeconzClient, private readonly lease: INativeSetupLease) {}
|
|
66
72
|
public isHealthy(): boolean { return this.client.isHealthy; }
|
|
67
|
-
|
|
68
|
-
public async
|
|
69
|
-
|
|
73
|
+
public async devices(signalArg?: AbortSignal): Promise<plugins.shxInterfaces.data.IDeviceDefinition[]> { return DeconzMapper.toDevices(await this.client.getSnapshot(signalArg)); }
|
|
74
|
+
public async entities(): Promise<IIntegrationEntity[]> { return DeconzMapper.toEntities(await this.client.getSnapshot()); }
|
|
75
|
+
public async subscribe(handlerArg: Parameters<NonNullable<IIntegrationRuntime['subscribe']>>[0], signalArg?: AbortSignal): Promise<() => Promise<void>> {
|
|
76
|
+
return this.client.subscribeToEvents((eventArg) => handlerArg(DeconzMapper.toIntegrationEvent(eventArg)), signalArg, (errorArg) => handlerArg({ type: 'error', integrationDomain: 'deconz', data: { message: errorArg.message }, timestamp: Date.now() }));
|
|
70
77
|
}
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
78
|
+
public async writeFeature(requestArg: IDeviceFeatureWriteRequest, signalArg?: AbortSignal): Promise<IServiceCallResult> {
|
|
79
|
+
try {
|
|
80
|
+
const request = deconzData(requestArg, ['device', 'feature', 'value']);
|
|
81
|
+
const device = deconzData(request.device, ['id', 'integrationDomain', 'name', 'room', 'protocol', 'manufacturer', 'model', 'online', 'features', 'state', 'metadata']);
|
|
82
|
+
const feature = deconzData(request.feature, ['id', 'capability', 'name', 'readable', 'writable', 'unit']);
|
|
83
|
+
if (typeof device.id !== 'string' || typeof device.integrationDomain !== 'string' || typeof feature.id !== 'string' || typeof feature.capability !== 'string' || !['boolean', 'number', 'string'].includes(typeof request.value)) throw new Error('Invalid deCONZ feature request.');
|
|
84
|
+
const captured = { device: { id: device.id, integrationDomain: device.integrationDomain }, feature: { id: feature.id, capability: feature.capability }, value: request.value } as IDeviceFeatureWriteRequest;
|
|
85
|
+
const result = await this.client.execute((session) => new DeconzRuntimeCommands(session).writeFeature(captured, signalArg), signalArg);
|
|
86
|
+
return { ...result.result, ...(result.observation ? { data: result.observation } : {}) };
|
|
87
|
+
} catch (error) { return this.failure(error); }
|
|
74
88
|
}
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
89
|
+
public async callService(requestArg: IServiceCallRequest): Promise<IServiceCallResult> {
|
|
90
|
+
try {
|
|
91
|
+
const request = deconzData(requestArg, ['domain', 'service', 'target', 'data']);
|
|
92
|
+
if (typeof request.domain !== 'string' || typeof request.service !== 'string') throw new Error('Invalid deCONZ service request.');
|
|
93
|
+
const target = deconzData(request.target, ['entityId', 'deviceId']);
|
|
94
|
+
if (Object.values(target).some((value) => typeof value !== 'string' || !value)) throw new Error('Invalid deCONZ service target.');
|
|
95
|
+
const fields = request.domain === 'light' ? request.service === 'turn_on' ? ['brightness', 'brightness_pct', 'percentage', 'transition', 'color_temp', 'color_temp_mired', 'xy_color'] : request.service === 'set_percentage' ? ['percentage'] : [] : request.domain === 'cover' ? ['position', 'percentage'] : ['sensor', 'binary_sensor', 'climate'].includes(request.domain) ? ['field', 'attribute', 'value', 'temperature', 'target_temperature'] : [];
|
|
96
|
+
const data = request.data === undefined ? undefined : deconzData(request.data, fields);
|
|
97
|
+
if (data) for (const [key, value] of Object.entries(data)) {
|
|
98
|
+
if (key === 'xy_color') {
|
|
99
|
+
if (!Array.isArray(value) || value.length !== 2 || Reflect.ownKeys(value).some((entry) => !['0', '1', 'length'].includes(String(entry)))) throw new Error('Invalid deCONZ color request.');
|
|
100
|
+
data[key] = ['0', '1'].map((entry) => { const descriptor = Object.getOwnPropertyDescriptor(value, entry); if (!descriptor || !('value' in descriptor) || typeof descriptor.value !== 'number') throw new Error('Invalid deCONZ color request.'); return descriptor.value as number; });
|
|
101
|
+
} else if (!['string', 'number', 'boolean'].includes(typeof value)) throw new Error('Invalid deCONZ service data.');
|
|
102
|
+
}
|
|
103
|
+
const captured = { domain: request.domain, service: request.service, target, data } as IServiceCallRequest;
|
|
104
|
+
const result = await this.client.execute((session) => new DeconzRuntimeCommands(session).callService(captured));
|
|
105
|
+
return { ...result.result, ...(result.observation ? { data: result.observation } : {}) };
|
|
106
|
+
} catch (error) { return this.failure(error); }
|
|
78
107
|
}
|
|
108
|
+
public destroy(): Promise<void> { return this.lease.close(); }
|
|
109
|
+
private failure(errorArg: unknown): IServiceCallResult { return { success: false, error: 'deCONZ command failed.', data: { effectOutcome: errorArg instanceof DeconzWriteError ? 'indeterminate' : 'not-sent', causalityConfirmed: false, physicalCompletion: 'unconfirmed' } }; }
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
class DeconzRuntimeCommands {
|
|
113
|
+
public readonly domain = 'deconz';
|
|
114
|
+
constructor(private readonly client: IDeconzClientOperation) {}
|
|
79
115
|
|
|
80
116
|
public async writeFeature(requestArg: IDeviceFeatureWriteRequest, abortSignalArg?: AbortSignal): Promise<IServiceCallResult> {
|
|
81
117
|
if (!this.client.isHealthy) return { success: false, error: 'deCONZ runtime is not healthy.' };
|
|
@@ -140,10 +176,6 @@ class DeconzRuntime implements IIntegrationRuntime {
|
|
|
140
176
|
return { success: false, error: `Unsupported deCONZ service domain: ${requestArg.domain}` };
|
|
141
177
|
}
|
|
142
178
|
|
|
143
|
-
public async destroy(): Promise<void> {
|
|
144
|
-
await this.client.destroy();
|
|
145
|
-
this.onDestroyed();
|
|
146
|
-
}
|
|
147
179
|
|
|
148
180
|
private async callLightService(targetArg: IDeconzServiceTarget, requestArg: IServiceCallRequest): Promise<IServiceCallResult> {
|
|
149
181
|
if (targetArg.resource !== 'lights' && targetArg.resource !== 'groups') {
|
|
@@ -99,6 +99,11 @@ export class DeconzMapper {
|
|
|
99
99
|
devices.push(this.sensorToDevice(sensorId, sensor, updatedAt));
|
|
100
100
|
}
|
|
101
101
|
|
|
102
|
+
if (snapshotArg.homeId) for (const device of devices) {
|
|
103
|
+
const resource = typeof device.metadata?.deconzResource === 'string' ? device.metadata.deconzResource : 'gateway';
|
|
104
|
+
const id = typeof device.metadata?.deconzId === 'string' ? device.metadata.deconzId : bridgeId;
|
|
105
|
+
device.id = this.canonicalDeviceId(snapshotArg, resource, id);
|
|
106
|
+
}
|
|
102
107
|
if (snapshotArg.config?.rfconnected === false) {
|
|
103
108
|
return devices.map((deviceArg) => ({ ...deviceArg, online: false, state: deviceArg.state.map((stateArg) => stateArg.featureId === 'connectivity' ? { ...stateArg, value: 'offline' } : stateArg) }));
|
|
104
109
|
}
|
|
@@ -138,9 +143,21 @@ export class DeconzMapper {
|
|
|
138
143
|
}
|
|
139
144
|
}
|
|
140
145
|
|
|
146
|
+
if (snapshotArg.homeId) for (const entity of entities) {
|
|
147
|
+
const resource = String(entity.attributes?.deconzResource);
|
|
148
|
+
const id = String(entity.attributes?.deconzId);
|
|
149
|
+
entity.deviceId = this.canonicalDeviceId(snapshotArg, resource, id);
|
|
150
|
+
const field = entity.attributes?.deconzStateKey ?? entity.attributes?.deconzConfigKey ?? entity.platform;
|
|
151
|
+
entity.id = entity.uniqueId = `${entity.deviceId}:${entity.platform}:${encodeURIComponent(String(field))}`;
|
|
152
|
+
}
|
|
141
153
|
return snapshotArg.config?.rfconnected === false ? entities.map((entityArg) => ({ ...entityArg, available: false })) : entities;
|
|
142
154
|
}
|
|
143
155
|
|
|
156
|
+
private static canonicalDeviceId(snapshotArg: IDeconzSnapshot, resourceArg: string, idArg: string): string {
|
|
157
|
+
const item = resourceArg === 'lights' ? snapshotArg.lights[idArg] : resourceArg === 'sensors' ? snapshotArg.sensors[idArg] : undefined;
|
|
158
|
+
return ['deconz', snapshotArg.homeId!, this.bridgeId(snapshotArg).toLowerCase(), resourceArg, idArg, item?.uniqueid ?? ''].map((partArg) => encodeURIComponent(partArg)).join(':');
|
|
159
|
+
}
|
|
160
|
+
|
|
144
161
|
public static toIntegrationEvent(eventArg: IDeconzWebsocketEvent): IIntegrationEvent {
|
|
145
162
|
return {
|
|
146
163
|
type: eventArg.e === 'added' ? 'device_added' : eventArg.e === 'deleted' ? 'device_removed' : 'state_changed',
|
|
@@ -185,13 +202,13 @@ export class DeconzMapper {
|
|
|
185
202
|
];
|
|
186
203
|
|
|
187
204
|
if (this.isCoverLight(lightArg)) {
|
|
188
|
-
features.push({ id: 'position', capability: 'cover', name: 'Position', readable: true, writable:
|
|
205
|
+
features.push({ id: 'position', capability: 'cover', name: 'Position', readable: true, writable: typeof lightArg.state?.lift === 'number', unit: '%' });
|
|
189
206
|
this.pushDeviceState(state, 'position', this.coverPosition(lightArg), updatedAtArg);
|
|
190
207
|
} else if (this.isSwitchLight(lightArg)) {
|
|
191
|
-
features.push({ id: 'on', capability: 'switch', name: 'Power', readable: true, writable:
|
|
208
|
+
features.push({ id: 'on', capability: 'switch', name: 'Power', readable: true, writable: typeof lightArg.state?.on === 'boolean' });
|
|
192
209
|
this.pushDeviceState(state, 'on', lightArg.state?.on ?? false, updatedAtArg);
|
|
193
210
|
} else {
|
|
194
|
-
features.push({ id: 'on', capability: 'light', name: 'Power', readable: true, writable:
|
|
211
|
+
features.push({ id: 'on', capability: 'light', name: 'Power', readable: true, writable: typeof lightArg.state?.on === 'boolean' });
|
|
195
212
|
this.pushDeviceState(state, 'on', lightArg.state?.on ?? false, updatedAtArg);
|
|
196
213
|
if (typeof lightArg.state?.bri === 'number') {
|
|
197
214
|
features.push({ id: 'brightness', capability: 'light', name: 'Brightness', readable: true, writable: true, unit: '%' });
|
|
@@ -238,8 +255,8 @@ export class DeconzMapper {
|
|
|
238
255
|
online: true,
|
|
239
256
|
features: [
|
|
240
257
|
{ id: 'connectivity', capability: 'sensor', name: 'Connectivity', readable: true, writable: false },
|
|
241
|
-
{ id: 'on', capability: 'light', name: 'Power', readable: true, writable:
|
|
242
|
-
{ id: 'brightness', capability: 'light', name: 'Brightness', readable: true, writable: true, unit: '%' },
|
|
258
|
+
{ id: 'on', capability: 'light', name: 'Power', readable: true, writable: typeof groupArg.action?.on === 'boolean' },
|
|
259
|
+
...(typeof groupArg.action?.bri === 'number' ? [{ id: 'brightness', capability: 'light' as const, name: 'Brightness', readable: true, writable: true, unit: '%' }] : []),
|
|
243
260
|
],
|
|
244
261
|
state,
|
|
245
262
|
metadata: {
|
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import type * as plugins from '../../plugins.js';
|
|
2
|
+
import type { IConfigEncryptionKeyProvider } from '../../core/classes.configstore.js';
|
|
3
|
+
|
|
1
4
|
export type TDeconzProtocol = 'http' | 'https';
|
|
2
5
|
|
|
3
6
|
export type TDeconzResource = 'config' | 'groups' | 'lights' | 'scenes' | 'sensors';
|
|
@@ -9,15 +12,52 @@ export type TDeconzCommandMethod = 'GET' | 'POST' | 'PUT' | 'DELETE';
|
|
|
9
12
|
export type TDeconzStateValue = string | number | boolean | null | undefined | number[] | string[] | Record<string, unknown>;
|
|
10
13
|
|
|
11
14
|
export interface IDeconzConfig {
|
|
12
|
-
|
|
15
|
+
homeId: string;
|
|
16
|
+
host: string;
|
|
13
17
|
port?: number;
|
|
14
|
-
|
|
18
|
+
credentialReference: string;
|
|
15
19
|
protocol?: TDeconzProtocol;
|
|
16
|
-
bridgeId
|
|
17
|
-
websocketPort?: number;
|
|
20
|
+
bridgeId: string;
|
|
18
21
|
enableWebSocket?: boolean;
|
|
19
22
|
requestTimeoutMs?: number;
|
|
20
|
-
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export interface IDeconzCredentialRequest {
|
|
26
|
+
readonly homeId: string;
|
|
27
|
+
readonly origin: string;
|
|
28
|
+
readonly bridgeId: string;
|
|
29
|
+
readonly credentialReference: string;
|
|
30
|
+
readonly signal: AbortSignal;
|
|
31
|
+
/** Monotonic performance.now() deadline, shared by credential resolution and HTTP dispatch. */
|
|
32
|
+
readonly deadline: number;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export interface IDeconzCredentialProvider {
|
|
36
|
+
resolve(requestArg: IDeconzCredentialRequest): Promise<string>;
|
|
37
|
+
/** Create a reference without replacing an existing credential; the host owns this persistence contract. */
|
|
38
|
+
create?(requestArg: IDeconzCredentialRequest, apiKeyArg: string): Promise<void>;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export interface IDeconzControlPermission {
|
|
42
|
+
readonly resource: 'config' | 'lights' | 'groups' | 'sensors' | 'scenes';
|
|
43
|
+
readonly resourceId: string;
|
|
44
|
+
readonly fields: readonly string[];
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export interface IDeconzIntegrationOptions {
|
|
48
|
+
readonly homeId?: string;
|
|
49
|
+
readonly capacity?: number;
|
|
50
|
+
readonly credentialProvider?: IDeconzCredentialProvider;
|
|
51
|
+
readonly controlPermissions?: readonly IDeconzControlPermission[];
|
|
52
|
+
readonly db?: plugins.nosqldb.SmartdataDb;
|
|
53
|
+
readonly keyProvider?: IConfigEncryptionKeyProvider;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export interface IDeconzWriteResult {
|
|
57
|
+
readonly effectOutcome: 'accepted';
|
|
58
|
+
readonly observedState: IDeconzSnapshot;
|
|
59
|
+
readonly causalityConfirmed: false;
|
|
60
|
+
readonly physicalCompletion: 'unconfirmed';
|
|
21
61
|
}
|
|
22
62
|
|
|
23
63
|
export interface IDeconzGatewayConfig {
|
|
@@ -58,11 +98,13 @@ export interface IDeconzLightState {
|
|
|
58
98
|
|
|
59
99
|
export interface IDeconzLight {
|
|
60
100
|
id?: string;
|
|
61
|
-
colorcapabilities?: number;
|
|
101
|
+
colorcapabilities?: number | string[];
|
|
102
|
+
modes?: string[];
|
|
62
103
|
ctmax?: number;
|
|
63
104
|
ctmin?: number;
|
|
64
105
|
etag?: string;
|
|
65
106
|
hascolor?: boolean;
|
|
107
|
+
effects?: string[];
|
|
66
108
|
lastannounced?: string;
|
|
67
109
|
lastseen?: string;
|
|
68
110
|
manufacturername?: string;
|
|
@@ -192,6 +234,7 @@ export interface IDeconzScene {
|
|
|
192
234
|
}
|
|
193
235
|
|
|
194
236
|
export interface IDeconzSnapshot {
|
|
237
|
+
homeId?: string;
|
|
195
238
|
config?: IDeconzGatewayConfig;
|
|
196
239
|
groups: Record<string, IDeconzGroup>;
|
|
197
240
|
lights: Record<string, IDeconzLight>;
|