iobroker.iot 5.0.13 → 6.0.1
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/README.md +6 -6
- package/admin/assets/index-39e7CNHj.js +705 -0
- package/admin/index_m.html +1 -1
- package/admin/rules/@mf-types/compiled-types/ActionVisu.d.ts +1 -1
- package/admin/rules/@mf-types.zip +0 -0
- package/admin/rules/assets/{ActionVisu-BN0Kjfo9.js → ActionVisu-BCZFwf_Y.js} +1 -1
- package/admin/rules/assets/{index-FrXvyqvW.js → index-DYx5xsZT.js} +76 -76
- package/admin/rules/assets/{localSharedImportMap-8wECk2hR.js → localSharedImportMap-CNn4MUou.js} +1 -1
- package/admin/rules/assets/{virtualExposes-b6CxLzZb.js → virtualExposes-B2aPQUg7.js} +1 -1
- package/admin/rules/customRuleBlocks.js +2 -2
- package/admin/rules/mf-stats.json +1 -1
- package/build/lib/AlexaSmartHomeV3/Alexa/Directives/Discovery.js +2 -1
- package/build/lib/AlexaSmartHomeV3/Alexa/Directives/Discovery.js.map +1 -1
- package/build/lib/AlexaSmartHomeV3/Controls/AirCondition.js +2 -2
- package/build/lib/AlexaSmartHomeV3/Controls/AirCondition.js.map +1 -1
- package/build/lib/AlexaSmartHomeV3/Controls/Ct.js +1 -1
- package/build/lib/AlexaSmartHomeV3/Controls/Ct.js.map +1 -1
- package/build/lib/AlexaSmartHomeV3/Controls/Rgb.js +1 -1
- package/build/lib/AlexaSmartHomeV3/Controls/Rgb.js.map +1 -1
- package/build/lib/AlexaSmartHomeV3/Controls/RgbSingle.js +1 -1
- package/build/lib/AlexaSmartHomeV3/Controls/RgbSingle.js.map +1 -1
- package/build/lib/AlexaSmartHomeV3/Controls/RgbwSingle.js +1 -1
- package/build/lib/AlexaSmartHomeV3/Controls/RgbwSingle.js.map +1 -1
- package/build/lib/AlexaSmartHomeV3/Controls/Thermostat.js +1 -1
- package/build/lib/AlexaSmartHomeV3/Controls/Thermostat.js.map +1 -1
- package/build/lib/AlexaSmartHomeV3/Controls/Volume.js +2 -2
- package/build/lib/AlexaSmartHomeV3/Controls/Volume.js.map +1 -1
- package/build/lib/AlexaSmartHomeV3/DeviceManager.js +18 -9
- package/build/lib/AlexaSmartHomeV3/DeviceManager.js.map +1 -1
- package/build/lib/AlexaSmartHomeV3/Helpers/DiscoveryValidator.js +283 -0
- package/build/lib/AlexaSmartHomeV3/Helpers/DiscoveryValidator.js.map +1 -0
- package/build/lib/AlexaSmartHomeV3/Helpers/Utils.js +4 -0
- package/build/lib/AlexaSmartHomeV3/Helpers/Utils.js.map +1 -1
- package/build/lib/alisa.js +15 -14
- package/build/lib/googleHome.js +151 -207
- package/io-package.json +14 -14
- package/package.json +1 -1
- package/admin/assets/index-DCr4hGKK.js +0 -705
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Volume.js","sourceRoot":"","sources":["../../../../src/lib/AlexaSmartHomeV3/Controls/Volume.ts"],"names":[],"mappings":";;;;;AAAA,4EAAoD;AACpD,qEAA6C;AAE7C,uDAAwE;AACxE,iFAAyD;AACzD,4CAAsE;AACtE,4EAAoD;AAEpD,0FAAkE;AAElE,MAAqB,MAAO,SAAQ,2BAAiB;IAChC,QAAQ,CAAU;IAC3B,WAAW,CAAkC;IAErD,YAAY,eAA0C;QAClD,KAAK,CAAC,eAAe,CAAC,CAAC;QAEvB,IAAI,CAAC,QAAQ,GAAG,IAAI,iBAAO,CAAC,IAAI,CAAC,uBAAuB,EAAE,EAAE,IAAI,CAAC,sBAAsB,EAAE,CAAC,CAAC;QAC3F,IAAI,CAAC,UAAU,GAAG,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QAElC,MAAM,MAAM,GAAG,IAAI,CAAC,sBAAsB,EAAE,CAAC;QAC7C,IAAI,MAAM,EAAE,CAAC;YACT,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,wBAAc,CAAC,MAAM,CAAC,CAAC,CAAC;QACrD,CAAC;IACL,CAAC;IAED,IAAI,UAAU;QACV,OAAO,CAAC,SAAS,CAAC,CAAC;IACvB,CAAC;IAED,oBAAoB;QAChB,OAAO,CAAC,eAAgB,CAAC,CAAC;IAC9B,CAAC;IAED,KAAK,CAAC,QAAQ,CAAC,QAAwB,EAAE,KAAsC;QAC3E,0BAA0B;QAC1B,MAAM,yBAAe,CAAC,QAAQ,CAAC,QAAQ,CAAC,KAAK,EAAE,KAAM,CAAC,CAAC;QACvD,QAAQ,CAAC,YAAY,GAAG,KAAK,CAAC;QAE9B,IAAI,QAAQ,CAAC,YAAY,KAAK,oBAAU,CAAC,KAAK,CAAC,YAAY,EAAE,CAAC;YAC1D,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC;gBACjB,MAAM,IAAI,KAAK,CAAC,gCAAgC,CAAC,CAAC;YACtD,CAAC;YACD,aAAa;YACb,IAAI,KAAK,EAAE,CAAC;gBACR,gCAAgC;gBAChC,
|
|
1
|
+
{"version":3,"file":"Volume.js","sourceRoot":"","sources":["../../../../src/lib/AlexaSmartHomeV3/Controls/Volume.ts"],"names":[],"mappings":"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","sourcesContent":["import Speaker from '../Alexa/Capabilities/Speaker';\nimport Properties from '../Alexa/Properties';\nimport type { Base as PropertiesBase, ControlStateInitObject } from '../Alexa/Properties/Base';\nimport { Volume as PropertiesVolume } from '../Alexa/Properties/Volume';\nimport AdapterProvider from '../Helpers/AdapterProvider';\nimport { denormalize_0_100, normalize_0_100 } from '../Helpers/Utils';\nimport AdjustableControl from './AdjustableControl';\nimport type { AlexaV3Category, AlexaV3DirectiveValue, IotExternalPatternControl } from '../types';\nimport EndpointHealth from '../Alexa/Capabilities/EndpointHealth';\n\nexport default class Volume extends AdjustableControl {\n private readonly _speaker: Speaker;\n private _lastVolume: ioBroker.StateValue | undefined;\n\n constructor(detectedControl: IotExternalPatternControl) {\n super(detectedControl);\n\n this._speaker = new Speaker(this.composeInitObjectVolume(), this.composeInitObjectMuted());\n this._supported = [this._speaker];\n\n const health = this.connectivityInitObject();\n if (health) {\n this._supported.push(new EndpointHealth(health));\n }\n }\n\n get categories(): AlexaV3Category[] {\n return ['SPEAKER'];\n }\n\n adjustableProperties(): (typeof PropertiesBase)[] {\n return [PropertiesVolume];\n }\n\n async setState(property: PropertiesBase, value: ioBroker.StateValue | undefined): Promise<void> {\n // set the property itself\n await AdapterProvider.setState(property.setId, value!);\n property.currentValue = value;\n\n if (property.propertyName === Properties.Muted.propertyName) {\n if (!this._speaker) {\n throw new Error('Muted property name is missing');\n }\n // set volume\n if (value) {\n // set volume to 0 on MUTED true\n this._lastVolume = this._speaker.volume.currentValue;\n await AdapterProvider.setState(this._speaker.volume.setId, 0);\n this._speaker.volume.currentValue = 0;\n } else {\n // set volume to the last known, configured or 20 otherwise on MUTED false\n if (this._lastVolume === undefined) {\n const smartName = this.states[this.statesMap.set]!.smartName;\n if (typeof smartName === 'object' && smartName && smartName.byON !== undefined) {\n this._lastVolume = smartName.byON;\n }\n this._lastVolume =\n this._lastVolume === undefined || isNaN(this._lastVolume as unknown as number)\n ? denormalize_0_100(\n 20,\n this._speaker.volume.valuesRangeMin as number,\n this._speaker.volume.valuesRangeMax as number,\n )\n : parseInt(this._lastVolume as string, 10);\n }\n await AdapterProvider.setState(this._speaker.volume.setId, this._lastVolume as number);\n this._speaker.volume.currentValue = this._lastVolume;\n }\n } else {\n // set muted\n const muted = value === 0;\n // only on different IDs for volume and muted\n if (this._speaker.volume.setId !== this._speaker.muted.setId) {\n await AdapterProvider.setState(this._speaker.muted.setId, muted);\n }\n this._speaker.muted.currentValue = muted;\n }\n }\n\n protected async getOrRetrieveCurrentValue(property: PropertiesBase): Promise<ioBroker.StateValue> {\n if (property.currentValue === undefined) {\n property.currentValue = await AdapterProvider.getState(property.getId);\n\n // convert non-zero volumes to muted = false\n if (\n property.propertyName === this._speaker.muted.propertyName &&\n property.getId === this._speaker.volume.getId\n ) {\n property.currentValue = property.currentValue === 0;\n }\n }\n\n if (property.currentValue === undefined) {\n throw new Error(`unable to retrieve ${property.getId}`);\n }\n\n return property.currentValue;\n }\n\n private composeInitObjectVolume(): ControlStateInitObject {\n const map = this.statesMap;\n\n return {\n setState: this.states[map.set]!,\n getState: this.states[map.actual] || this.states[map.set]!,\n alexaSetter: function (\n this: PropertiesBase,\n alexaValue: AlexaV3DirectiveValue,\n ): ioBroker.StateValue | undefined {\n return denormalize_0_100(\n alexaValue as number,\n this.valuesRangeMin as number,\n this.valuesRangeMax as number,\n );\n },\n alexaGetter: function (\n this: PropertiesBase,\n value: ioBroker.StateValue | undefined,\n ): AlexaV3DirectiveValue {\n return normalize_0_100(value as number, this.valuesRangeMin as number, this.valuesRangeMax as number);\n },\n };\n }\n\n private composeInitObjectMuted(): ControlStateInitObject {\n const map = this.statesMap;\n\n return {\n setState: this.states[map.mute] || this.states[map.set]!,\n getState: this.states[map.mute] || this.states[map.set]!,\n alexaSetter: function (alexaValue: AlexaV3DirectiveValue): ioBroker.StateValue | undefined {\n return alexaValue as boolean;\n },\n alexaGetter: function (value: ioBroker.StateValue | undefined): AlexaV3DirectiveValue {\n return value as boolean;\n },\n };\n }\n}\n"]}
|
|
@@ -178,14 +178,14 @@ class DeviceManager {
|
|
|
178
178
|
// detectedControls = detectedControls.filter(c => ['light', 'dimmer'].includes(c.type));
|
|
179
179
|
const createdGroups = [];
|
|
180
180
|
let iteration = 0;
|
|
181
|
-
// First collect all custom
|
|
181
|
+
// First, collect all custom-friendly names assigned by user
|
|
182
182
|
const customNamedControls = [];
|
|
183
183
|
for (const control of detectedControls) {
|
|
184
184
|
if (control.groupNames) {
|
|
185
185
|
for (const groupName of control.groupNames) {
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
186
|
+
const name = this.getName(groupName);
|
|
187
|
+
if (!customNamedControls.includes(name)) {
|
|
188
|
+
customNamedControls.push(name);
|
|
189
189
|
}
|
|
190
190
|
}
|
|
191
191
|
}
|
|
@@ -227,8 +227,17 @@ class DeviceManager {
|
|
|
227
227
|
else {
|
|
228
228
|
this.log.debug(`Control of type [${control.type}] assigned to room [${this.getName(control.room.common.name)}] has no function. Skipped.`);
|
|
229
229
|
}
|
|
230
|
-
// delete
|
|
231
|
-
|
|
230
|
+
// delete all processed controls from detected controls
|
|
231
|
+
if (processedControls.length > 1) {
|
|
232
|
+
for (let i = detectedControls.length - 1; i >= 0; i--) {
|
|
233
|
+
if (processedControls.includes(detectedControls[i])) {
|
|
234
|
+
detectedControls.splice(i, 1);
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
else {
|
|
239
|
+
detectedControls.splice(0, 1);
|
|
240
|
+
}
|
|
232
241
|
}
|
|
233
242
|
else if (control.groupNames?.length) {
|
|
234
243
|
// no room, but smart name (not only one)
|
|
@@ -259,7 +268,7 @@ class DeviceManager {
|
|
|
259
268
|
}
|
|
260
269
|
}
|
|
261
270
|
});
|
|
262
|
-
// Be sure
|
|
271
|
+
// Be sure that the processed control is removed
|
|
263
272
|
const index = detectedControls.indexOf(control);
|
|
264
273
|
if (index !== -1) {
|
|
265
274
|
detectedControls.splice(index, 1);
|
|
@@ -281,7 +290,7 @@ class DeviceManager {
|
|
|
281
290
|
for (const device of this.devices) {
|
|
282
291
|
this.log.debug(`${device.toString()}`);
|
|
283
292
|
}
|
|
284
|
-
// a new discovery process is needed in case we had
|
|
293
|
+
// a new discovery process is needed in case we already had devices and device collection was
|
|
285
294
|
// triggered again by, e.g., a change in room/function enums
|
|
286
295
|
// if (discoveryNeeded) {
|
|
287
296
|
// this.log.info(`Please delete all managed by ioBroker devices in your Alexa app and then start discovery`);
|
|
@@ -343,7 +352,7 @@ class DeviceManager {
|
|
|
343
352
|
const promises = [];
|
|
344
353
|
for (const id of this.subscribed) {
|
|
345
354
|
this.log.silly(`unsubscribing from updates of ${id}`);
|
|
346
|
-
promises.push(AdapterProvider_1.default.
|
|
355
|
+
promises.push(AdapterProvider_1.default.unsubscribe(id));
|
|
347
356
|
}
|
|
348
357
|
await Promise.allSettled(promises);
|
|
349
358
|
this.subscribed = [];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DeviceManager.js","sourceRoot":"","sources":["../../../src/lib/AlexaSmartHomeV3/DeviceManager.ts"],"names":[],"mappings":"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","sourcesContent":["import Device from './Device';\nimport * as Utils from './Helpers/Utils';\nimport Directives from './Alexa/Directives';\nimport Controls from './Controls';\nimport type Control from './Controls/Control';\nimport Logger from './Helpers/Logger';\nimport AdapterProvider from './Helpers/AdapterProvider';\nimport AlexaResponse from './Alexa/AlexaResponse';\nimport IotProxy from './Helpers/IotProxy';\nimport RateLimiter from './Helpers/RateLimiter';\nimport OverallDailyRateLimitExceeded from './Exceptions/OverallDailyRateLimitExceeded';\nimport HourlyDeviceRateLimitExceeded from './Exceptions/HourlyDeviceRateLimitExceeded';\nimport type { AlexaV3EndpointID, AlexaV3Request, IotExternalPatternControl } from './types';\nimport ChangeReport from './Alexa/Directives/ChangeReport';\nimport Discovery from './Alexa/Directives/Discovery';\nimport ReportState from './Alexa/Directives/ReportState';\nimport { randomUUID } from 'node:crypto';\nimport type { Types } from '@iobroker/type-detector';\n\nconst GroupWord: { [lang: string]: string } = {\n en: 'Group',\n de: 'Gruppe',\n it: 'Gruppo',\n fr: 'Groupe',\n ru: 'Группа',\n es: 'Grupo',\n pl: 'Grupa',\n nl: 'Groep',\n};\n// const DeviceWord: { [lang: string]: string } = {\n// en: 'Device',\n// de: 'Gerät',\n// it: 'Dispositivo',\n// fr: 'Appareil',\n// ru: 'Устройство',\n// es: 'Dispositivo',\n// pl: 'Urządzenie',\n// nl: 'Dispositivo',\n// };\n\nexport default class DeviceManager {\n private lang: ioBroker.Languages = 'en';\n private devices: Device[] = [];\n private subscribed: string[] = [];\n private log: Logger;\n private eventsPausedTill = 0; // timestamp\n\n private collecting = false;\n private recollect = false;\n public validTill: number = 0;\n\n /** Creates a Device Manager */\n constructor() {\n this.log = new Logger(this);\n }\n\n async informAboutStatesChange(): Promise<void> {\n await AdapterProvider.get().setStateAsync('smart.updates3', 2, true);\n }\n\n get language(): ioBroker.Languages {\n return this.lang;\n }\n\n set language(value: ioBroker.Languages) {\n this.lang = value;\n }\n\n public setValidTill(validTill: number): void {\n this.validTill = validTill;\n }\n\n matchDirective(event: AlexaV3Request): ChangeReport | Discovery | ReportState | null {\n if (ChangeReport.matches(event)) {\n return new ChangeReport();\n }\n if (Discovery.matches(event)) {\n return new Discovery();\n }\n if (ReportState.matches(event)) {\n return new ReportState();\n }\n\n return null;\n }\n\n get endpoints(): Device[] {\n return this.devices;\n }\n\n endpointById(id: AlexaV3EndpointID): Device | undefined {\n return this.devices.find(device => device.id === id);\n }\n\n addDevice(device: Device): void {\n this.devices.push(device);\n }\n\n toDevice(options: {\n detectedControls: IotExternalPatternControl[];\n friendlyName: string;\n autoDetected: boolean;\n roomName?: string;\n funcName?: string;\n toggle?: boolean;\n possibleTypes: Types[];\n typeWasDetected: boolean;\n }): Device | undefined {\n const controls: Control[] = [];\n\n this.log.debug(`merging controls to a device with name ${options.friendlyName}`);\n\n options.detectedControls.forEach(item => {\n this.log.silly(`processing control: ${JSON.stringify(item)}`);\n const control = Controls.factory(item);\n\n if (control) {\n this.log.debug(`\"${item.type}\" added to \"${options.friendlyName}\"`);\n controls.push(control);\n } else {\n this.log.debug(`control of type \"${item.type}\" not supported yet. Skipped.`);\n }\n });\n\n if (!controls.length) {\n // the controls are not supported yet...\n return;\n }\n\n // create and add a new device to the collected devices\n this.addDevice(\n new Device({\n id: options.friendlyName,\n friendlyName: options.friendlyName,\n controls,\n autoDetected: options.autoDetected,\n roomName: options.roomName,\n funcName: options.funcName,\n toggle: options.toggle,\n possibleTypes: options.possibleTypes,\n typeWasDetected: options.typeWasDetected,\n }),\n );\n }\n\n getName(name: ioBroker.StringOrTranslated | undefined): string {\n if (!name) {\n return '';\n }\n if (typeof name === 'object') {\n return name[this.lang] || name.en || '';\n }\n return name;\n }\n\n async collectEndpoints(): Promise<void> {\n if (this.collecting) {\n this.log.debug(`collecting devices already in progress. Skipping...`);\n this.recollect = true;\n return;\n }\n this.collecting = true;\n this.log.debug(`(re)collecting devices...`);\n try {\n // const discoveryNeeded = this.devices.length > 0;\n\n this.devices = [];\n const defaultToggle = AdapterProvider.get().config.defaultToggle || false;\n\n // collect all iobroker controls in terms of iobroker type detector (https://github.com/ioBroker/ioBroker.type-detector)\n const detectedControls = await Utils.controls(AdapterProvider.get(), this.lang);\n\n this.log.debug(`type detector found ${detectedControls.length} controls`);\n\n // Normally, every control is a smart device. But due to the iobroker concept of 'rooms and functions'\n // multiple controls might be merged to a single device.\n\n // as long as not all controls mapped to a device...\n\n // detectedControls = detectedControls.filter(c => ['light', 'dimmer'].includes(c.type));\n const createdGroups: string[] = [];\n let iteration = 0;\n\n // First collect all custom friendly names assigned by user\n const customNamedControls: string[] = [];\n for (const control of detectedControls) {\n if (control.groupNames) {\n for (const groupName of control.groupNames) {\n if (!customNamedControls.includes(this.getName(groupName))) {\n customNamedControls.push(this.getName(groupName));\n break;\n }\n }\n }\n }\n\n while (detectedControls.length) {\n iteration++;\n // take the next control\n const control = detectedControls[0];\n let processedControls: IotExternalPatternControl[] = [];\n\n if (control.room?.common?.name) {\n if (control.functionality?.common?.name) {\n // controls in the same room with the same functionality\n processedControls = detectedControls.filter(\n item =>\n control.room &&\n item.room?.id === control.room.id &&\n control.functionality &&\n item.functionality?.id === control.functionality.id,\n );\n let friendlyName = Utils.friendlyNameByRoomAndFunctionName(control, this.lang);\n // If a friendly name is already used, append Gruppe, Group, Gruppo, Groupe, etc.\n if (customNamedControls.includes(friendlyName)) {\n let index = 0;\n let newFriendlyName = '';\n do {\n newFriendlyName = `${friendlyName} ${GroupWord[this.lang] || GroupWord.en}${index ? ` ${index}` : ''}`;\n index++;\n } while (customNamedControls.includes(newFriendlyName));\n friendlyName = newFriendlyName;\n }\n\n this.toDevice({\n detectedControls: processedControls,\n friendlyName,\n autoDetected: true,\n roomName: this.getName(control.room?.common?.name),\n funcName: this.getName(control.functionality?.common?.name),\n toggle: processedControls[0].object?.toggle ?? defaultToggle,\n possibleTypes: processedControls[0].object?.possibleTypes || [],\n typeWasDetected: processedControls[0].object?.typeWasDetected || false,\n });\n } else {\n this.log.debug(\n `Control of type [${control.type}] assigned to room [${this.getName(control.room.common.name)}] has no function. Skipped.`,\n );\n }\n // delete it from detected controls to avoid endless loop\n detectedControls.splice(0, 1);\n } else if (control.groupNames?.length) {\n // no room, but smart name (not only one)\n const names = [...control.groupNames];\n names.forEach(groupName => {\n if (!createdGroups.includes(groupName)) {\n createdGroups.push(groupName);\n processedControls = detectedControls.filter(item => item.groupNames?.includes(groupName));\n this.toDevice({\n detectedControls: JSON.parse(JSON.stringify(processedControls)),\n friendlyName: this.getName(groupName),\n autoDetected: false,\n toggle: processedControls[0].object?.toggle ?? defaultToggle,\n possibleTypes: processedControls[0].object?.possibleTypes || [],\n typeWasDetected: processedControls[0].object?.typeWasDetected || false,\n });\n\n // Remove groupName from all controls to avoid processing it again\n for (let c = detectedControls.length - 1; c >= 0; c--) {\n if (detectedControls[c].groupNames) {\n const pos = detectedControls[c].groupNames?.indexOf(groupName);\n if (pos !== -1) {\n detectedControls[c].groupNames.splice(pos, 1);\n }\n if (!detectedControls[c].groupNames.length) {\n detectedControls.splice(c, 1);\n }\n }\n }\n }\n });\n // Be sure, that the processed control is removed\n const index = detectedControls.indexOf(control);\n if (index !== -1) {\n detectedControls.splice(index, 1);\n }\n } else {\n // delete it from detected controls to avoid endless loop\n detectedControls.splice(0, 1);\n // neither room nor smart name\n this.log.debug(`Control of type [${control.type}] has neither room no smart name. Skipped.`);\n }\n\n if (iteration > 1000) {\n this.log.error(\n `too many iterations while collecting devices. Stopping to avoid endless loop: ${JSON.stringify(detectedControls)}`,\n );\n break;\n }\n }\n\n // done\n this.log.debug(`finished collecting devices. there is/are ${this.devices.length} device(s) in total`);\n for (const device of this.devices) {\n this.log.debug(`${device.toString()}`);\n }\n\n // a new discovery process is needed in case we had already devices and device collection was\n // triggered again by, e.g., a change in room/function enums\n // if (discoveryNeeded) {\n // this.log.info(`Please delete all managed by ioBroker devices in your Alexa app and then start discovery`);\n // }\n\n // collect all relevant states to subscribe to updates\n const stateIds = new Set(\n this.devices\n .flatMap(d => d.controls)\n .flatMap(item => item.supported)\n .flatMap(item => item.properties)\n .map(item => item.getId)\n .filter(id => id),\n );\n this.log.debug(`registering for updates of total ${stateIds.size} states`);\n const newSubscribed = Array.from(stateIds);\n const subscribe: string[] = [];\n for (const id of newSubscribed) {\n this.log.silly(`subscribing to updates of ${id}`);\n if (!this.subscribed.includes(id)) {\n this.subscribed.push(id);\n if (!subscribe.includes(id)) {\n subscribe.push(id);\n }\n }\n }\n\n this.subscribed.sort();\n\n // wait till all promises are settled\n if (subscribe.length) {\n await AdapterProvider.subscribe(subscribe);\n }\n\n // unsubscribe from unused states\n const unsubscribe: string[] = [];\n for (let i = this.subscribed.length - 1; i >= 0; i--) {\n const id = this.subscribed[i];\n if (!newSubscribed.includes(id)) {\n this.log.silly(`unsubscribing from updates of ${id}`);\n this.subscribed.splice(i, 1);\n if (!unsubscribe.includes(id)) {\n unsubscribe.push(id);\n }\n }\n }\n if (unsubscribe.length) {\n await AdapterProvider.unsubscribe(unsubscribe);\n }\n } catch (e) {\n this.log.error(`failed to collect devices: ${e}`);\n if (e.stack) {\n this.log.error(e.stack);\n }\n }\n\n this.collecting = false;\n\n // if during the collection a new collection was triggered, start collecting again\n if (this.recollect) {\n this.recollect = false;\n setTimeout(() => this.collectEndpoints(), 1000);\n }\n }\n\n async destroy(): Promise<void> {\n const promises = [];\n for (const id of this.subscribed) {\n this.log.silly(`unsubscribing from updates of ${id}`);\n promises.push(AdapterProvider.subscribe(id));\n }\n await Promise.allSettled(promises);\n\n this.subscribed = [];\n }\n\n publishStateChange(stateChange: AlexaResponse): void {\n if (this.eventsPausedTill < Date.now()) {\n this.log.silly(`publishing ${JSON.stringify(stateChange)}`);\n IotProxy.publishStateChange(stateChange);\n }\n }\n\n pauseEvents(): void {\n this.eventsPausedTill = Date.now() + 30 * 60 * 1000; // 30 minutes\n }\n\n async executeWithinRateLimits(\n endpointId: AlexaV3EndpointID,\n awaitable: () => Promise<AlexaResponse>,\n errorResponse: AlexaResponse,\n ): Promise<AlexaResponse> {\n try {\n await RateLimiter.incrementAndGet(endpointId);\n return await awaitable();\n } catch (error) {\n if (error instanceof OverallDailyRateLimitExceeded || error instanceof HourlyDeviceRateLimitExceeded) {\n this.log.warn(error.message);\n } else {\n this.log.error(error.message);\n }\n\n return errorResponse;\n }\n }\n\n async handleAlexaEvent(event: AlexaV3Request): Promise<AlexaResponse> {\n this.log.debug(`incoming Alexa event`);\n this.log.silly(`${JSON.stringify(event)}`);\n if (!event?.directive?.header) {\n throw new Error('Alexa event header is missing');\n }\n\n let response: AlexaResponse;\n const directive = this.matchDirective(event);\n if (directive) {\n response = await directive.handle(event, this);\n } else {\n const endpointId = event?.directive?.endpoint?.endpointId;\n const device = endpointId ? this.endpointById(endpointId) : undefined;\n\n if (device) {\n if (device.supports(event)) {\n response = await this.executeWithinRateLimits(\n endpointId!,\n async () => {\n response = await device.handle(event);\n if (!AlexaResponse.isErrorResponse(response)) {\n // report state change via voice interaction\n const propertyName = response.context?.properties[0].name;\n const responseEvent = ChangeReport.get(\n device.id,\n propertyName || '',\n false,\n event.directive.header.messageId,\n );\n const directive = this.matchDirective(responseEvent);\n if (directive) {\n // BF[2024.02.04]: temporarily disabled as produced a huge number of events\n const stateChange = await directive.handle(responseEvent, this);\n // get device state (not just one control)\n const deviceState = await device.reportState();\n if (JSON.stringify(device.lastReportedState) !== JSON.stringify(deviceState)) {\n device.lastReportedState = deviceState;\n // fire state change report to Alexa\n if (this.validTill > Date.now() || process.env.TESTS_EXECUTION) {\n this.publishStateChange(stateChange);\n }\n await this.informAboutStatesChange();\n } else {\n this.log.debug(\n `ignoring state change event for ${endpointId} due to the same_ value [${JSON.stringify(deviceState)}]`,\n );\n }\n }\n }\n return response;\n },\n AlexaResponse.throttlingException(event.directive.header.messageId).get(),\n );\n } else {\n response = AlexaResponse.directiveNotSupportedByDevice(\n device.friendlyName,\n event.directive.header.namespace,\n event.directive.header.messageId,\n event.directive.header.payloadVersion,\n ).get();\n }\n } else {\n response = AlexaResponse.endpointUnreachable(event.directive.header.messageId).get();\n }\n }\n\n this.log.silly(`response: ${JSON.stringify(response)}`);\n return response;\n }\n\n async handleStateUpdate(id: string, state: ioBroker.State | null | undefined): Promise<void> {\n // ignore updates not confirmed by a corresponding device\n if (!state?.ack) {\n this.log.silly(`ignoring state change event for ${id} due to state.ack == ${state?.ack}`);\n return;\n }\n if (id.startsWith(`${AdapterProvider.get().namespace}.`)) {\n // nop, this is just to inform Alexa app about changes\n return;\n }\n\n let notFound = true;\n\n for (const device of this.devices) {\n const property = device.controls\n .flatMap(item => item.supported)\n .flatMap(item => item.properties)\n .find(item => item.getId === id);\n\n if (property) {\n notFound = false;\n if (property.currentValue === state.val) {\n this.log.debug(`ignoring state change event for ${id} due to the same value [${state.val}]`);\n } else {\n property.currentValue = state.val;\n\n const responseEvent = Directives.ChangeReport.get(\n device.id,\n property.propertyName,\n true,\n randomUUID(),\n );\n const directive = this.matchDirective(responseEvent);\n if (directive) {\n // BF[2024.02.04]: temporarily disabled as produced a huge number of events\n const stateChange = await directive.handle(responseEvent, this);\n\n // get device state (not just one control)\n const deviceState = await device.reportState();\n\n if (JSON.stringify(device.lastReportedState) !== JSON.stringify(deviceState)) {\n device.lastReportedState = deviceState;\n await this.informAboutStatesChange();\n\n // fire state change report to Alexa\n if (this.validTill > Date.now() || process.env.TESTS_EXECUTION) {\n await this.executeWithinRateLimits(\n device.id,\n () => {\n this.publishStateChange(stateChange);\n return Promise.resolve(stateChange);\n },\n AlexaResponse.throttlingException(responseEvent.directive.header.messageId).get(),\n );\n }\n } else {\n this.log.debug(\n `ignoring state change event for ${id} due to the same_ value [${JSON.stringify(deviceState)}]`,\n );\n }\n }\n }\n\n // should be the only device having the id => stop processing here\n break;\n }\n }\n\n // this should never happen\n if (notFound) {\n this.log.debug(`state id ${id} doesn't belong to any device`);\n }\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"DeviceManager.js","sourceRoot":"","sources":["../../../src/lib/AlexaSmartHomeV3/DeviceManager.ts"],"names":[],"mappings":"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","sourcesContent":["import Device from './Device';\nimport * as Utils from './Helpers/Utils';\nimport Directives from './Alexa/Directives';\nimport Controls from './Controls';\nimport type Control from './Controls/Control';\nimport Logger from './Helpers/Logger';\nimport AdapterProvider from './Helpers/AdapterProvider';\nimport AlexaResponse from './Alexa/AlexaResponse';\nimport IotProxy from './Helpers/IotProxy';\nimport RateLimiter from './Helpers/RateLimiter';\nimport OverallDailyRateLimitExceeded from './Exceptions/OverallDailyRateLimitExceeded';\nimport HourlyDeviceRateLimitExceeded from './Exceptions/HourlyDeviceRateLimitExceeded';\nimport type { AlexaV3EndpointID, AlexaV3Request, IotExternalPatternControl } from './types';\nimport ChangeReport from './Alexa/Directives/ChangeReport';\nimport Discovery from './Alexa/Directives/Discovery';\nimport ReportState from './Alexa/Directives/ReportState';\nimport { randomUUID } from 'node:crypto';\nimport type { Types } from '@iobroker/type-detector';\n\nconst GroupWord: { [lang: string]: string } = {\n en: 'Group',\n de: 'Gruppe',\n it: 'Gruppo',\n fr: 'Groupe',\n ru: 'Группа',\n es: 'Grupo',\n pl: 'Grupa',\n nl: 'Groep',\n};\n// const DeviceWord: { [lang: string]: string } = {\n// en: 'Device',\n// de: 'Gerät',\n// it: 'Dispositivo',\n// fr: 'Appareil',\n// ru: 'Устройство',\n// es: 'Dispositivo',\n// pl: 'Urządzenie',\n// nl: 'Dispositivo',\n// };\n\nexport default class DeviceManager {\n private lang: ioBroker.Languages = 'en';\n private devices: Device[] = [];\n private subscribed: string[] = [];\n private log: Logger;\n private eventsPausedTill = 0; // timestamp\n\n private collecting = false;\n private recollect = false;\n public validTill: number = 0;\n\n /** Creates a Device Manager */\n constructor() {\n this.log = new Logger(this);\n }\n\n async informAboutStatesChange(): Promise<void> {\n await AdapterProvider.get().setStateAsync('smart.updates3', 2, true);\n }\n\n get language(): ioBroker.Languages {\n return this.lang;\n }\n\n set language(value: ioBroker.Languages) {\n this.lang = value;\n }\n\n public setValidTill(validTill: number): void {\n this.validTill = validTill;\n }\n\n matchDirective(event: AlexaV3Request): ChangeReport | Discovery | ReportState | null {\n if (ChangeReport.matches(event)) {\n return new ChangeReport();\n }\n if (Discovery.matches(event)) {\n return new Discovery();\n }\n if (ReportState.matches(event)) {\n return new ReportState();\n }\n\n return null;\n }\n\n get endpoints(): Device[] {\n return this.devices;\n }\n\n endpointById(id: AlexaV3EndpointID): Device | undefined {\n return this.devices.find(device => device.id === id);\n }\n\n addDevice(device: Device): void {\n this.devices.push(device);\n }\n\n toDevice(options: {\n detectedControls: IotExternalPatternControl[];\n friendlyName: string;\n autoDetected: boolean;\n roomName?: string;\n funcName?: string;\n toggle?: boolean;\n possibleTypes: Types[];\n typeWasDetected: boolean;\n }): Device | undefined {\n const controls: Control[] = [];\n\n this.log.debug(`merging controls to a device with name ${options.friendlyName}`);\n\n options.detectedControls.forEach(item => {\n this.log.silly(`processing control: ${JSON.stringify(item)}`);\n const control = Controls.factory(item);\n\n if (control) {\n this.log.debug(`\"${item.type}\" added to \"${options.friendlyName}\"`);\n controls.push(control);\n } else {\n this.log.debug(`control of type \"${item.type}\" not supported yet. Skipped.`);\n }\n });\n\n if (!controls.length) {\n // the controls are not supported yet...\n return;\n }\n\n // create and add a new device to the collected devices\n this.addDevice(\n new Device({\n id: options.friendlyName,\n friendlyName: options.friendlyName,\n controls,\n autoDetected: options.autoDetected,\n roomName: options.roomName,\n funcName: options.funcName,\n toggle: options.toggle,\n possibleTypes: options.possibleTypes,\n typeWasDetected: options.typeWasDetected,\n }),\n );\n }\n\n getName(name: ioBroker.StringOrTranslated | undefined): string {\n if (!name) {\n return '';\n }\n if (typeof name === 'object') {\n return name[this.lang] || name.en || '';\n }\n return name;\n }\n\n async collectEndpoints(): Promise<void> {\n if (this.collecting) {\n this.log.debug(`collecting devices already in progress. Skipping...`);\n this.recollect = true;\n return;\n }\n this.collecting = true;\n this.log.debug(`(re)collecting devices...`);\n try {\n // const discoveryNeeded = this.devices.length > 0;\n\n this.devices = [];\n const defaultToggle = AdapterProvider.get().config.defaultToggle || false;\n\n // collect all iobroker controls in terms of iobroker type detector (https://github.com/ioBroker/ioBroker.type-detector)\n const detectedControls = await Utils.controls(AdapterProvider.get(), this.lang);\n\n this.log.debug(`type detector found ${detectedControls.length} controls`);\n\n // Normally, every control is a smart device. But due to the iobroker concept of 'rooms and functions'\n // multiple controls might be merged to a single device.\n\n // as long as not all controls mapped to a device...\n\n // detectedControls = detectedControls.filter(c => ['light', 'dimmer'].includes(c.type));\n const createdGroups: string[] = [];\n let iteration = 0;\n\n // First, collect all custom-friendly names assigned by user\n const customNamedControls: string[] = [];\n for (const control of detectedControls) {\n if (control.groupNames) {\n for (const groupName of control.groupNames) {\n const name = this.getName(groupName);\n if (!customNamedControls.includes(name)) {\n customNamedControls.push(name);\n }\n }\n }\n }\n\n while (detectedControls.length) {\n iteration++;\n // take the next control\n const control = detectedControls[0];\n let processedControls: IotExternalPatternControl[] = [];\n\n if (control.room?.common?.name) {\n if (control.functionality?.common?.name) {\n // controls in the same room with the same functionality\n processedControls = detectedControls.filter(\n item =>\n control.room &&\n item.room?.id === control.room.id &&\n control.functionality &&\n item.functionality?.id === control.functionality.id,\n );\n let friendlyName = Utils.friendlyNameByRoomAndFunctionName(control, this.lang);\n // If a friendly name is already used, append Gruppe, Group, Gruppo, Groupe, etc.\n if (customNamedControls.includes(friendlyName)) {\n let index = 0;\n let newFriendlyName = '';\n do {\n newFriendlyName = `${friendlyName} ${GroupWord[this.lang] || GroupWord.en}${index ? ` ${index}` : ''}`;\n index++;\n } while (customNamedControls.includes(newFriendlyName));\n friendlyName = newFriendlyName;\n }\n\n this.toDevice({\n detectedControls: processedControls,\n friendlyName,\n autoDetected: true,\n roomName: this.getName(control.room?.common?.name),\n funcName: this.getName(control.functionality?.common?.name),\n toggle: processedControls[0].object?.toggle ?? defaultToggle,\n possibleTypes: processedControls[0].object?.possibleTypes || [],\n typeWasDetected: processedControls[0].object?.typeWasDetected || false,\n });\n } else {\n this.log.debug(\n `Control of type [${control.type}] assigned to room [${this.getName(control.room.common.name)}] has no function. Skipped.`,\n );\n }\n // delete all processed controls from detected controls\n if (processedControls.length > 1) {\n for (let i = detectedControls.length - 1; i >= 0; i--) {\n if (processedControls.includes(detectedControls[i])) {\n detectedControls.splice(i, 1);\n }\n }\n } else {\n detectedControls.splice(0, 1);\n }\n } else if (control.groupNames?.length) {\n // no room, but smart name (not only one)\n const names = [...control.groupNames];\n names.forEach(groupName => {\n if (!createdGroups.includes(groupName)) {\n createdGroups.push(groupName);\n processedControls = detectedControls.filter(item => item.groupNames?.includes(groupName));\n this.toDevice({\n detectedControls: JSON.parse(JSON.stringify(processedControls)),\n friendlyName: this.getName(groupName),\n autoDetected: false,\n toggle: processedControls[0].object?.toggle ?? defaultToggle,\n possibleTypes: processedControls[0].object?.possibleTypes || [],\n typeWasDetected: processedControls[0].object?.typeWasDetected || false,\n });\n\n // Remove groupName from all controls to avoid processing it again\n for (let c = detectedControls.length - 1; c >= 0; c--) {\n if (detectedControls[c].groupNames) {\n const pos = detectedControls[c].groupNames?.indexOf(groupName);\n if (pos !== -1) {\n detectedControls[c].groupNames.splice(pos, 1);\n }\n if (!detectedControls[c].groupNames.length) {\n detectedControls.splice(c, 1);\n }\n }\n }\n }\n });\n // Be sure that the processed control is removed\n const index = detectedControls.indexOf(control);\n if (index !== -1) {\n detectedControls.splice(index, 1);\n }\n } else {\n // delete it from detected controls to avoid endless loop\n detectedControls.splice(0, 1);\n // neither room nor smart name\n this.log.debug(`Control of type [${control.type}] has neither room no smart name. Skipped.`);\n }\n\n if (iteration > 1000) {\n this.log.error(\n `too many iterations while collecting devices. Stopping to avoid endless loop: ${JSON.stringify(detectedControls)}`,\n );\n break;\n }\n }\n\n // done\n this.log.debug(`finished collecting devices. there is/are ${this.devices.length} device(s) in total`);\n for (const device of this.devices) {\n this.log.debug(`${device.toString()}`);\n }\n\n // a new discovery process is needed in case we already had devices and device collection was\n // triggered again by, e.g., a change in room/function enums\n // if (discoveryNeeded) {\n // this.log.info(`Please delete all managed by ioBroker devices in your Alexa app and then start discovery`);\n // }\n\n // collect all relevant states to subscribe to updates\n const stateIds = new Set(\n this.devices\n .flatMap(d => d.controls)\n .flatMap(item => item.supported)\n .flatMap(item => item.properties)\n .map(item => item.getId)\n .filter(id => id),\n );\n this.log.debug(`registering for updates of total ${stateIds.size} states`);\n const newSubscribed = Array.from(stateIds);\n const subscribe: string[] = [];\n for (const id of newSubscribed) {\n this.log.silly(`subscribing to updates of ${id}`);\n if (!this.subscribed.includes(id)) {\n this.subscribed.push(id);\n if (!subscribe.includes(id)) {\n subscribe.push(id);\n }\n }\n }\n\n this.subscribed.sort();\n\n // wait till all promises are settled\n if (subscribe.length) {\n await AdapterProvider.subscribe(subscribe);\n }\n\n // unsubscribe from unused states\n const unsubscribe: string[] = [];\n for (let i = this.subscribed.length - 1; i >= 0; i--) {\n const id = this.subscribed[i];\n if (!newSubscribed.includes(id)) {\n this.log.silly(`unsubscribing from updates of ${id}`);\n this.subscribed.splice(i, 1);\n if (!unsubscribe.includes(id)) {\n unsubscribe.push(id);\n }\n }\n }\n if (unsubscribe.length) {\n await AdapterProvider.unsubscribe(unsubscribe);\n }\n } catch (e) {\n this.log.error(`failed to collect devices: ${e}`);\n if (e.stack) {\n this.log.error(e.stack);\n }\n }\n\n this.collecting = false;\n\n // if during the collection a new collection was triggered, start collecting again\n if (this.recollect) {\n this.recollect = false;\n setTimeout(() => this.collectEndpoints(), 1000);\n }\n }\n\n async destroy(): Promise<void> {\n const promises = [];\n for (const id of this.subscribed) {\n this.log.silly(`unsubscribing from updates of ${id}`);\n promises.push(AdapterProvider.unsubscribe(id));\n }\n await Promise.allSettled(promises);\n\n this.subscribed = [];\n }\n\n publishStateChange(stateChange: AlexaResponse): void {\n if (this.eventsPausedTill < Date.now()) {\n this.log.silly(`publishing ${JSON.stringify(stateChange)}`);\n IotProxy.publishStateChange(stateChange);\n }\n }\n\n pauseEvents(): void {\n this.eventsPausedTill = Date.now() + 30 * 60 * 1000; // 30 minutes\n }\n\n async executeWithinRateLimits(\n endpointId: AlexaV3EndpointID,\n awaitable: () => Promise<AlexaResponse>,\n errorResponse: AlexaResponse,\n ): Promise<AlexaResponse> {\n try {\n await RateLimiter.incrementAndGet(endpointId);\n return await awaitable();\n } catch (error) {\n if (error instanceof OverallDailyRateLimitExceeded || error instanceof HourlyDeviceRateLimitExceeded) {\n this.log.warn(error.message);\n } else {\n this.log.error(error.message);\n }\n\n return errorResponse;\n }\n }\n\n async handleAlexaEvent(event: AlexaV3Request): Promise<AlexaResponse> {\n this.log.debug(`incoming Alexa event`);\n this.log.silly(`${JSON.stringify(event)}`);\n if (!event?.directive?.header) {\n throw new Error('Alexa event header is missing');\n }\n\n let response: AlexaResponse;\n const directive = this.matchDirective(event);\n if (directive) {\n response = await directive.handle(event, this);\n } else {\n const endpointId = event?.directive?.endpoint?.endpointId;\n const device = endpointId ? this.endpointById(endpointId) : undefined;\n\n if (device) {\n if (device.supports(event)) {\n response = await this.executeWithinRateLimits(\n endpointId!,\n async () => {\n response = await device.handle(event);\n if (!AlexaResponse.isErrorResponse(response)) {\n // report state change via voice interaction\n const propertyName = response.context?.properties[0].name;\n const responseEvent = ChangeReport.get(\n device.id,\n propertyName || '',\n false,\n event.directive.header.messageId,\n );\n const directive = this.matchDirective(responseEvent);\n if (directive) {\n // BF[2024.02.04]: temporarily disabled as produced a huge number of events\n const stateChange = await directive.handle(responseEvent, this);\n // get device state (not just one control)\n const deviceState = await device.reportState();\n if (JSON.stringify(device.lastReportedState) !== JSON.stringify(deviceState)) {\n device.lastReportedState = deviceState;\n // fire state change report to Alexa\n if (this.validTill > Date.now() || process.env.TESTS_EXECUTION) {\n this.publishStateChange(stateChange);\n }\n await this.informAboutStatesChange();\n } else {\n this.log.debug(\n `ignoring state change event for ${endpointId} due to the same_ value [${JSON.stringify(deviceState)}]`,\n );\n }\n }\n }\n return response;\n },\n AlexaResponse.throttlingException(event.directive.header.messageId).get(),\n );\n } else {\n response = AlexaResponse.directiveNotSupportedByDevice(\n device.friendlyName,\n event.directive.header.namespace,\n event.directive.header.messageId,\n event.directive.header.payloadVersion,\n ).get();\n }\n } else {\n response = AlexaResponse.endpointUnreachable(event.directive.header.messageId).get();\n }\n }\n\n this.log.silly(`response: ${JSON.stringify(response)}`);\n return response;\n }\n\n async handleStateUpdate(id: string, state: ioBroker.State | null | undefined): Promise<void> {\n // ignore updates not confirmed by a corresponding device\n if (!state?.ack) {\n this.log.silly(`ignoring state change event for ${id} due to state.ack == ${state?.ack}`);\n return;\n }\n if (id.startsWith(`${AdapterProvider.get().namespace}.`)) {\n // nop, this is just to inform Alexa app about changes\n return;\n }\n\n let notFound = true;\n\n for (const device of this.devices) {\n const property = device.controls\n .flatMap(item => item.supported)\n .flatMap(item => item.properties)\n .find(item => item.getId === id);\n\n if (property) {\n notFound = false;\n if (property.currentValue === state.val) {\n this.log.debug(`ignoring state change event for ${id} due to the same value [${state.val}]`);\n } else {\n property.currentValue = state.val;\n\n const responseEvent = Directives.ChangeReport.get(\n device.id,\n property.propertyName,\n true,\n randomUUID(),\n );\n const directive = this.matchDirective(responseEvent);\n if (directive) {\n // BF[2024.02.04]: temporarily disabled as produced a huge number of events\n const stateChange = await directive.handle(responseEvent, this);\n\n // get device state (not just one control)\n const deviceState = await device.reportState();\n\n if (JSON.stringify(device.lastReportedState) !== JSON.stringify(deviceState)) {\n device.lastReportedState = deviceState;\n await this.informAboutStatesChange();\n\n // fire state change report to Alexa\n if (this.validTill > Date.now() || process.env.TESTS_EXECUTION) {\n await this.executeWithinRateLimits(\n device.id,\n () => {\n this.publishStateChange(stateChange);\n return Promise.resolve(stateChange);\n },\n AlexaResponse.throttlingException(responseEvent.directive.header.messageId).get(),\n );\n }\n } else {\n this.log.debug(\n `ignoring state change event for ${id} due to the same_ value [${JSON.stringify(deviceState)}]`,\n );\n }\n }\n }\n\n // should be the only device having the id => stop processing here\n break;\n }\n }\n\n // this should never happen\n if (notFound) {\n this.log.debug(`state id ${id} doesn't belong to any device`);\n }\n }\n}\n"]}
|
|
@@ -0,0 +1,283 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.validateDiscoveryResponse = validateDiscoveryResponse;
|
|
4
|
+
// Alexa limits: https://developer.amazon.com/en-US/docs/alexa/device-apis/alexa-discovery.html
|
|
5
|
+
const MAX_ENDPOINTS = 300;
|
|
6
|
+
const MAX_ENDPOINT_ID_LENGTH = 256;
|
|
7
|
+
const MAX_FRIENDLY_NAME_LENGTH = 128;
|
|
8
|
+
const MAX_DESCRIPTION_LENGTH = 128;
|
|
9
|
+
const MAX_MANUFACTURER_NAME_LENGTH = 128;
|
|
10
|
+
const MAX_CAPABILITIES_PER_ENDPOINT = 100;
|
|
11
|
+
// AWS IoT MQTT message size limit (128 KB minus overhead)
|
|
12
|
+
const MAX_RESPONSE_SIZE_BYTES = 127 * 1024;
|
|
13
|
+
const RESPONSE_SIZE_WARNING_BYTES = 100 * 1024;
|
|
14
|
+
// https://developer.amazon.com/en-US/docs/alexa/device-apis/alexa-discovery.html#display-categories
|
|
15
|
+
const VALID_DISPLAY_CATEGORIES = new Set([
|
|
16
|
+
'ACTIVITY_TRIGGER',
|
|
17
|
+
'AIR_CONDITIONER',
|
|
18
|
+
'AIR_FRESHENER',
|
|
19
|
+
'AIR_PURIFIER',
|
|
20
|
+
'AIR_QUALITY_MONITOR',
|
|
21
|
+
'ALEXA_VOICE_ENABLED',
|
|
22
|
+
'AUTO_ACCESSORY',
|
|
23
|
+
'BLUETOOTH_SPEAKER',
|
|
24
|
+
'CAMERA',
|
|
25
|
+
'CHRISTMAS_TREE',
|
|
26
|
+
'COFFEE_MAKER',
|
|
27
|
+
'COMPUTER',
|
|
28
|
+
'CONTACT_SENSOR',
|
|
29
|
+
'DISHWASHER',
|
|
30
|
+
'DOOR',
|
|
31
|
+
'DOORBELL',
|
|
32
|
+
'DRYER',
|
|
33
|
+
'EXTERIOR_BLIND',
|
|
34
|
+
'FAN',
|
|
35
|
+
'GAME_CONSOLE',
|
|
36
|
+
'GARAGE_DOOR',
|
|
37
|
+
'HEADPHONES',
|
|
38
|
+
'HUB',
|
|
39
|
+
'INTERIOR_BLIND',
|
|
40
|
+
'LAPTOP',
|
|
41
|
+
'LIGHT',
|
|
42
|
+
'MICROWAVE',
|
|
43
|
+
'MOBILE_PHONE',
|
|
44
|
+
'MOTION_SENSOR',
|
|
45
|
+
'MUSIC_SYSTEM',
|
|
46
|
+
'NETWORK_HARDWARE',
|
|
47
|
+
'OTHER',
|
|
48
|
+
'OVEN',
|
|
49
|
+
'PHONE',
|
|
50
|
+
'PRINTER',
|
|
51
|
+
'REMOTE',
|
|
52
|
+
'ROUTER',
|
|
53
|
+
'SCENE_TRIGGER',
|
|
54
|
+
'SCREEN',
|
|
55
|
+
'SECURITY_PANEL',
|
|
56
|
+
'SECURITY_SYSTEM',
|
|
57
|
+
'SLOW_COOKER',
|
|
58
|
+
'SMARTLOCK',
|
|
59
|
+
'SMARTPLUG',
|
|
60
|
+
'SPEAKER',
|
|
61
|
+
'STREAMING_DEVICE',
|
|
62
|
+
'SWITCH',
|
|
63
|
+
'TABLET',
|
|
64
|
+
'TEMPERATURE_SENSOR',
|
|
65
|
+
'THERMOSTAT',
|
|
66
|
+
'TV',
|
|
67
|
+
'VACUUM_CLEANER',
|
|
68
|
+
'VACUUM',
|
|
69
|
+
'VEHICLE',
|
|
70
|
+
'WASHER',
|
|
71
|
+
'WATER_HEATER',
|
|
72
|
+
'WEARABLE',
|
|
73
|
+
]);
|
|
74
|
+
const VALID_NAMESPACES = new Set([
|
|
75
|
+
'Alexa',
|
|
76
|
+
'Alexa.BrightnessController',
|
|
77
|
+
'Alexa.ColorController',
|
|
78
|
+
'Alexa.ColorTemperatureController',
|
|
79
|
+
'Alexa.ContactSensor',
|
|
80
|
+
'Alexa.EndpointHealth',
|
|
81
|
+
'Alexa.HumiditySensor',
|
|
82
|
+
'Alexa.LockController',
|
|
83
|
+
'Alexa.ModeController',
|
|
84
|
+
'Alexa.MotionSensor',
|
|
85
|
+
'Alexa.PercentageController',
|
|
86
|
+
'Alexa.PowerController',
|
|
87
|
+
'Alexa.RangeController',
|
|
88
|
+
'Alexa.SceneController',
|
|
89
|
+
'Alexa.Speaker',
|
|
90
|
+
'Alexa.TemperatureSensor',
|
|
91
|
+
'Alexa.ThermostatController',
|
|
92
|
+
]);
|
|
93
|
+
// Multi-instance capabilities that require an instance property
|
|
94
|
+
const REQUIRES_INSTANCE = new Set(['Alexa.ModeController', 'Alexa.RangeController', 'Alexa.ToggleController']);
|
|
95
|
+
// Alexa wake words — devices named like this can't be controlled
|
|
96
|
+
const WAKE_WORDS = ['alexa', 'echo', 'amazon', 'computer', 'ziggy'];
|
|
97
|
+
/**
|
|
98
|
+
* Validates the Alexa Discovery response and removes invalid endpoints.
|
|
99
|
+
* Returns the sanitized response.
|
|
100
|
+
*/
|
|
101
|
+
function validateDiscoveryResponse(response, log) {
|
|
102
|
+
// Validate response header structure
|
|
103
|
+
const header = response?.event?.header;
|
|
104
|
+
if (!header) {
|
|
105
|
+
log.error('Discovery: response has no event.header');
|
|
106
|
+
return response;
|
|
107
|
+
}
|
|
108
|
+
// @ts-expect-error
|
|
109
|
+
if (header.namespace !== 'Alexa.Discovery') {
|
|
110
|
+
log.error(`Discovery: unexpected namespace "${header.namespace}", expected "Alexa.Discovery"`);
|
|
111
|
+
}
|
|
112
|
+
if (header.name !== 'Discover.Response') {
|
|
113
|
+
log.error(`Discovery: unexpected name "${header.name}", expected "Discover.Response"`);
|
|
114
|
+
}
|
|
115
|
+
if (header.payloadVersion !== '3') {
|
|
116
|
+
log.error(`Discovery: unexpected payloadVersion "${header.payloadVersion}", expected "3"`);
|
|
117
|
+
}
|
|
118
|
+
const endpoints = response?.event?.payload?.endpoints;
|
|
119
|
+
if (!endpoints || !Array.isArray(endpoints)) {
|
|
120
|
+
return response;
|
|
121
|
+
}
|
|
122
|
+
const errors = [];
|
|
123
|
+
const warnings = [];
|
|
124
|
+
const seenIds = new Set();
|
|
125
|
+
const seenNames = new Set();
|
|
126
|
+
// Validate and filter endpoints in place (backwards to safely splice)
|
|
127
|
+
for (let i = endpoints.length - 1; i >= 0; i--) {
|
|
128
|
+
const ep = endpoints[i];
|
|
129
|
+
const epIssues = validateEndpoint(ep, seenIds, seenNames);
|
|
130
|
+
const epErrors = epIssues.filter(e => e.severity === 'error');
|
|
131
|
+
const epWarnings = epIssues.filter(e => e.severity === 'warning');
|
|
132
|
+
warnings.push(...epWarnings);
|
|
133
|
+
if (epErrors.length) {
|
|
134
|
+
errors.push(...epErrors);
|
|
135
|
+
endpoints.splice(i, 1);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
// Enforce 300 endpoint limit
|
|
139
|
+
if (endpoints.length > MAX_ENDPOINTS) {
|
|
140
|
+
log.warn(`Discovery: ${endpoints.length} endpoints exceeds Alexa limit of ${MAX_ENDPOINTS}, truncating`);
|
|
141
|
+
endpoints.length = MAX_ENDPOINTS;
|
|
142
|
+
}
|
|
143
|
+
// Check total response size against AWS IoT MQTT limit
|
|
144
|
+
const responseSize = JSON.stringify(response).length;
|
|
145
|
+
if (responseSize > MAX_RESPONSE_SIZE_BYTES) {
|
|
146
|
+
const originalCount = endpoints.length;
|
|
147
|
+
while (endpoints.length > 1 && JSON.stringify(response).length > MAX_RESPONSE_SIZE_BYTES) {
|
|
148
|
+
const removed = endpoints.pop();
|
|
149
|
+
log.warn(`Discovery: removed "${removed.friendlyName}" to fit AWS IoT message size limit`);
|
|
150
|
+
}
|
|
151
|
+
log.warn(`Discovery: response was ${Math.round(responseSize / 1024)} KB (limit: ${Math.round(MAX_RESPONSE_SIZE_BYTES / 1024)} KB), reduced from ${originalCount} to ${endpoints.length} endpoints`);
|
|
152
|
+
}
|
|
153
|
+
else if (responseSize > RESPONSE_SIZE_WARNING_BYTES) {
|
|
154
|
+
log.warn(`Discovery: response is ${Math.round(responseSize / 1024)} KB with ${endpoints.length} endpoints — approaching AWS IoT limit of ${Math.round(MAX_RESPONSE_SIZE_BYTES / 1024)} KB`);
|
|
155
|
+
}
|
|
156
|
+
// Log warnings (non-fatal)
|
|
157
|
+
for (const w of warnings) {
|
|
158
|
+
log.warn(`Discovery: "${w.friendlyName}" (${w.endpointId}): ${w.field} — ${w.message}`);
|
|
159
|
+
}
|
|
160
|
+
// Log errors (endpoint was removed)
|
|
161
|
+
for (const err of errors) {
|
|
162
|
+
log.warn(`Discovery: removed "${err.friendlyName}" (${err.endpointId}): ${err.field} — ${err.message}`);
|
|
163
|
+
}
|
|
164
|
+
if (errors.length || warnings.length) {
|
|
165
|
+
log.info(`Discovery: ${endpoints.length} endpoint(s) valid, ${errors.length} removed, ${warnings.length} warning(s)`);
|
|
166
|
+
}
|
|
167
|
+
return response;
|
|
168
|
+
}
|
|
169
|
+
function validateEndpoint(ep, seenIds, seenNames) {
|
|
170
|
+
const issues = [];
|
|
171
|
+
const id = ep.endpointId || '(empty)';
|
|
172
|
+
const name = ep.friendlyName || '(empty)';
|
|
173
|
+
const error = (field, message) => ({
|
|
174
|
+
endpointId: id,
|
|
175
|
+
friendlyName: name,
|
|
176
|
+
field,
|
|
177
|
+
message,
|
|
178
|
+
severity: 'error',
|
|
179
|
+
});
|
|
180
|
+
const warning = (field, message) => ({
|
|
181
|
+
endpointId: id,
|
|
182
|
+
friendlyName: name,
|
|
183
|
+
field,
|
|
184
|
+
message,
|
|
185
|
+
severity: 'warning',
|
|
186
|
+
});
|
|
187
|
+
// --- endpointId ---
|
|
188
|
+
if (!ep.endpointId) {
|
|
189
|
+
issues.push(error('endpointId', 'missing'));
|
|
190
|
+
}
|
|
191
|
+
else if (ep.endpointId.length > MAX_ENDPOINT_ID_LENGTH) {
|
|
192
|
+
issues.push(error('endpointId', `exceeds ${MAX_ENDPOINT_ID_LENGTH} chars`));
|
|
193
|
+
}
|
|
194
|
+
else if (!/^[\w#;:!@"$%&'()*+,\-./>=<?[\\\]^`{|}~ ]+$/.test(ep.endpointId)) {
|
|
195
|
+
issues.push(error('endpointId', 'contains invalid characters'));
|
|
196
|
+
}
|
|
197
|
+
else if (seenIds.has(ep.endpointId)) {
|
|
198
|
+
issues.push(error('endpointId', 'duplicate'));
|
|
199
|
+
}
|
|
200
|
+
seenIds.add(ep.endpointId);
|
|
201
|
+
// --- friendlyName ---
|
|
202
|
+
if (!ep.friendlyName || !ep.friendlyName.trim()) {
|
|
203
|
+
issues.push(error('friendlyName', 'missing or empty'));
|
|
204
|
+
}
|
|
205
|
+
else if (ep.friendlyName.length > MAX_FRIENDLY_NAME_LENGTH) {
|
|
206
|
+
issues.push(error('friendlyName', `exceeds ${MAX_FRIENDLY_NAME_LENGTH} chars`));
|
|
207
|
+
}
|
|
208
|
+
else {
|
|
209
|
+
// Alexa rejects names that are only digits
|
|
210
|
+
if (/^\d+$/.test(ep.friendlyName.trim())) {
|
|
211
|
+
issues.push(error('friendlyName', 'must not be only digits'));
|
|
212
|
+
}
|
|
213
|
+
// Wake words as device name make the device unusable
|
|
214
|
+
const lower = ep.friendlyName.toLowerCase().trim();
|
|
215
|
+
if (WAKE_WORDS.includes(lower)) {
|
|
216
|
+
issues.push(warning('friendlyName', `"${ep.friendlyName}" is an Alexa wake word — device may not be controllable`));
|
|
217
|
+
}
|
|
218
|
+
// Duplicate names confuse Alexa ("which one did you mean?")
|
|
219
|
+
if (seenNames.has(lower)) {
|
|
220
|
+
issues.push(warning('friendlyName', 'duplicate name — Alexa will ask "which one did you mean?"'));
|
|
221
|
+
}
|
|
222
|
+
seenNames.add(lower);
|
|
223
|
+
}
|
|
224
|
+
// --- description ---
|
|
225
|
+
if (ep.description && ep.description.length > MAX_DESCRIPTION_LENGTH) {
|
|
226
|
+
issues.push(warning('description', `exceeds ${MAX_DESCRIPTION_LENGTH} chars, will be truncated`));
|
|
227
|
+
}
|
|
228
|
+
// --- manufacturerName ---
|
|
229
|
+
if (ep.manufacturerName && ep.manufacturerName.length > MAX_MANUFACTURER_NAME_LENGTH) {
|
|
230
|
+
issues.push(warning('manufacturerName', `exceeds ${MAX_MANUFACTURER_NAME_LENGTH} chars`));
|
|
231
|
+
}
|
|
232
|
+
// --- displayCategories ---
|
|
233
|
+
if (!ep.displayCategories || !ep.displayCategories.length) {
|
|
234
|
+
issues.push(error('displayCategories', 'missing or empty'));
|
|
235
|
+
}
|
|
236
|
+
else {
|
|
237
|
+
for (const cat of ep.displayCategories) {
|
|
238
|
+
if (!VALID_DISPLAY_CATEGORIES.has(cat)) {
|
|
239
|
+
issues.push(error('displayCategories', `unknown category "${cat}"`));
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
// --- capabilities ---
|
|
244
|
+
if (!ep.capabilities || !ep.capabilities.length) {
|
|
245
|
+
issues.push(error('capabilities', 'missing or empty'));
|
|
246
|
+
}
|
|
247
|
+
else {
|
|
248
|
+
if (ep.capabilities.length > MAX_CAPABILITIES_PER_ENDPOINT) {
|
|
249
|
+
issues.push(error('capabilities', `exceeds ${MAX_CAPABILITIES_PER_ENDPOINT} capabilities`));
|
|
250
|
+
}
|
|
251
|
+
// Alexa interface is required on every endpoint
|
|
252
|
+
const hasAlexa = ep.capabilities.some(c => c.interface === 'Alexa');
|
|
253
|
+
if (!hasAlexa) {
|
|
254
|
+
issues.push(error('capabilities', 'missing required Alexa interface'));
|
|
255
|
+
}
|
|
256
|
+
// Check for duplicate capabilities (same interface+instance)
|
|
257
|
+
const capKeys = new Set();
|
|
258
|
+
for (const cap of ep.capabilities) {
|
|
259
|
+
const capKey = `${cap.interface || ''}::${cap.instance || ''}`;
|
|
260
|
+
if (cap.interface !== 'Alexa' && capKeys.has(capKey)) {
|
|
261
|
+
issues.push(error('capabilities', `duplicate capability ${cap.interface}${cap.instance ? ` (${cap.instance})` : ''}`));
|
|
262
|
+
}
|
|
263
|
+
capKeys.add(capKey);
|
|
264
|
+
// Validate individual capability
|
|
265
|
+
if (cap.interface && !VALID_NAMESPACES.has(cap.interface)) {
|
|
266
|
+
issues.push(warning('capabilities', `unknown interface "${cap.interface}"`));
|
|
267
|
+
}
|
|
268
|
+
if (cap.version && cap.version !== '3' && cap.version !== '3.2') {
|
|
269
|
+
issues.push(error('capabilities', `unexpected version "${cap.version}" for ${cap.interface}`));
|
|
270
|
+
}
|
|
271
|
+
// ModeController and RangeController require instance
|
|
272
|
+
if (cap.interface && REQUIRES_INSTANCE.has(cap.interface) && !cap.instance) {
|
|
273
|
+
issues.push(error('capabilities', `${cap.interface} requires an instance property`));
|
|
274
|
+
}
|
|
275
|
+
// properties.supported must not be empty if defined
|
|
276
|
+
if (cap.properties && Array.isArray(cap.properties.supported) && cap.properties.supported.length === 0) {
|
|
277
|
+
issues.push(warning('capabilities', `${cap.interface} has empty properties.supported array`));
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
return issues;
|
|
282
|
+
}
|
|
283
|
+
//# sourceMappingURL=DiscoveryValidator.js.map
|