diff --git a/lib/extension/publish.ts b/lib/extension/publish.ts index 9eb88e5b97..7bf055cfe5 100644 --- a/lib/extension/publish.ts +++ b/lib/extension/publish.ts @@ -40,7 +40,7 @@ const defaultGroupConverters = [ interface ParsedTopic { ID: string; - endpoint: string; + endpoint: string | undefined; attribute: string; type: 'get' | 'set'; } @@ -238,7 +238,9 @@ export default class Publish extends Extension { if (re instanceof Device && propertyEndpointMatch) { endpointName = propertyEndpointMatch[2]; key = propertyEndpointMatch[1]; - localTarget = re.endpoint(endpointName); + // endpointName is always matched to an existing endpoint of the device + // since `propertyEndpointRegex` only contains valid endpoints for this device. + localTarget = re.endpoint(endpointName)!; endpointOrGroupID = localTarget.ID; } @@ -247,7 +249,7 @@ export default class Publish extends Extension { // Match any key if the toZigbee converter defines no key. const converter = converters.find((c) => (!c.key || c.key.includes(key)) && (!c.endpoint || c.endpoint == endpointName)); - if (parsedTopic.type === 'set' && usedConverters[endpointOrGroupID].includes(converter)) { + if (parsedTopic.type === 'set' && converter && usedConverters[endpointOrGroupID].includes(converter)) { // Use a converter for set only once // (e.g. light_onoff_brightness converters can convert state and brightness) continue; @@ -269,11 +271,11 @@ export default class Publish extends Extension { endpoint_name: endpointName, options: entitySettingsKeyValue, message: {...message}, - logger, device, state: entityState, membersState, - mapped: definition, + // In case a converter is found, the device always has a definition + mapped: definition!, }; // Strip endpoint name from meta.message properties. @@ -311,7 +313,7 @@ export default class Publish extends Extension { if (result && result.membersState && optimistic) { for (const [ieeeAddr, state] of Object.entries(result.membersState)) { - addToToPublish(this.zigbee.resolveEntity(ieeeAddr), state); + addToToPublish(this.zigbee.resolveEntity(ieeeAddr)!, state); } } @@ -339,7 +341,7 @@ export default class Publish extends Extension { } } - const scenesChanged = Object.values(usedConverters).some((cl) => cl.some((c) => c.key.some((k) => sceneConverterKeys.includes(k)))); + const scenesChanged = Object.values(usedConverters).some((cl) => cl.some((c) => c.key?.some((k) => sceneConverterKeys.includes(k)))); if (scenesChanged) { this.eventBus.emitScenesChanged({entity: re}); diff --git a/lib/model/device.ts b/lib/model/device.ts index 47d0a455ed..5775325d55 100644 --- a/lib/model/device.ts +++ b/lib/model/device.ts @@ -17,7 +17,7 @@ export default class Device { return this.zh.ieeeAddr; } get options(): DeviceOptions { - const deviceOptions = settings.getDevice(this.ieeeAddr) ?? {ID: this.ieeeAddr, friendly_name: this.ieeeAddr}; + const deviceOptions = settings.getDevice(this.ieeeAddr) ?? {friendly_name: this.ieeeAddr}; return {...settings.get().device_options, ...deviceOptions}; } get name(): string { diff --git a/lib/types/types.d.ts b/lib/types/types.d.ts index 22b7c70b3d..c64adba270 100644 --- a/lib/types/types.d.ts +++ b/lib/types/types.d.ts @@ -226,7 +226,6 @@ declare global { } interface DeviceOptions { - ID: string; disabled?: boolean; retention?: number; availability?: boolean | {timeout: number}; @@ -245,6 +244,10 @@ declare global { qos?: 0 | 1 | 2; } + interface DeviceOptionsWithId extends DeviceOptions { + ID: string; + } + interface GroupOptions { devices: string[]; ID: number; diff --git a/lib/util/settings.ts b/lib/util/settings.ts index 891ad41b08..4b0a383737 100644 --- a/lib/util/settings.ts +++ b/lib/util/settings.ts @@ -14,17 +14,29 @@ objectAssignDeep(schema, schemaJson); // Remove legacy settings from schema { + // @ts-expect-error delete schema.properties.advanced.properties.homeassistant_discovery_topic; + // @ts-expect-error delete schema.properties.advanced.properties.homeassistant_legacy_entity_attributes; + // @ts-expect-error delete schema.properties.advanced.properties.homeassistant_legacy_triggers; + // @ts-expect-error delete schema.properties.advanced.properties.homeassistant_status_topic; + // @ts-expect-error delete schema.properties.advanced.properties.soft_reset_timeout; + // @ts-expect-error delete schema.properties.advanced.properties.report; + // @ts-expect-error delete schema.properties.advanced.properties.baudrate; + // @ts-expect-error delete schema.properties.advanced.properties.rtscts; + // @ts-expect-error delete schema.properties.advanced.properties.ikea_ota_use_test_url; + // @ts-expect-error delete schema.properties.experimental; + // @ts-expect-error delete schemaJson.properties.whitelist; + // @ts-expect-error delete schemaJson.properties.ban; } @@ -117,8 +129,8 @@ const defaults: RecursivePartial = { }, }; -let _settings: Partial; -let _settingsWithDefaults: Settings; +let _settings: Partial | undefined; +let _settingsWithDefaults: Settings | undefined; function loadSettingsWithDefaults(): void { _settingsWithDefaults = objectAssignDeep({}, defaults, getInternalSettings()) as Settings; @@ -564,7 +576,7 @@ function getGroupThrowIfNotExists(IDorName: string): GroupOptions { return group; } -export function getDevice(IDorName: string): DeviceOptions | undefined { +export function getDevice(IDorName: string): DeviceOptionsWithId | undefined { const settings = get(); const byID = settings.devices[IDorName]; @@ -581,7 +593,7 @@ export function getDevice(IDorName: string): DeviceOptions | undefined { return undefined; } -function getDeviceThrowIfNotExists(IDorName: string): DeviceOptions { +function getDeviceThrowIfNotExists(IDorName: string): DeviceOptionsWithId { const device = getDevice(IDorName); if (!device) { throw new Error(`Device '${IDorName}' does not exist`); @@ -590,7 +602,7 @@ function getDeviceThrowIfNotExists(IDorName: string): DeviceOptions { return device; } -export function addDevice(ID: string): DeviceOptions { +export function addDevice(ID: string): DeviceOptionsWithId { if (getDevice(ID)) { throw new Error(`Device '${ID}' already exists`); }