From 11b092f6d19fd59408ccb25f4be3678efbeed7de Mon Sep 17 00:00:00 2001 From: Jatus Date: Fri, 26 Jan 2024 17:26:02 +0100 Subject: [PATCH] base refactoring --- src/index.ts | 2 +- src/matter/{devices => }/Bridge.ts | 2 +- src/matter/{devices => }/HAmiddleware.ts | 14 +- src/matter/{devices => }/HAssTypes.ts | 0 src/matter/Mapper.ts | 57 ++++++ src/matter/devices/Mapper.ts | 179 ------------------ src/matter/devices/MapperType.ts | 7 + .../devices/lights/DimmerableLightDevice.ts | 49 +++++ src/matter/devices/lights/OnOffLightDevice.ts | 39 ++++ src/matter/devices/lights/index.ts | 2 + src/matter/{devices => }/index.ts | 0 11 files changed, 163 insertions(+), 188 deletions(-) rename src/matter/{devices => }/Bridge.ts (98%) rename src/matter/{devices => }/HAmiddleware.ts (87%) rename src/matter/{devices => }/HAssTypes.ts (100%) create mode 100644 src/matter/Mapper.ts delete mode 100644 src/matter/devices/Mapper.ts create mode 100644 src/matter/devices/MapperType.ts create mode 100644 src/matter/devices/lights/DimmerableLightDevice.ts create mode 100644 src/matter/devices/lights/OnOffLightDevice.ts create mode 100644 src/matter/devices/lights/index.ts rename src/matter/{devices => }/index.ts (100%) diff --git a/src/index.ts b/src/index.ts index bd9f2fd..b955a4b 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,4 +1,4 @@ -import { Bridge, HAMiddleware, addAllDevicesToBridge } from './matter/devices'; +import { Bridge, HAMiddleware, addAllDevicesToBridge } from './matter'; import { serverSetup } from './matter/server'; import { Logger } from '@project-chip/matter-node.js/log'; diff --git a/src/matter/devices/Bridge.ts b/src/matter/Bridge.ts similarity index 98% rename from src/matter/devices/Bridge.ts rename to src/matter/Bridge.ts index 0bc2f0a..8427d1f 100644 --- a/src/matter/devices/Bridge.ts +++ b/src/matter/Bridge.ts @@ -10,7 +10,7 @@ import { } from '@project-chip/matter-node.js/device'; import { Logger } from '@project-chip/matter-node.js/log'; import { StorageManager } from '@project-chip/matter-node.js/storage'; -import { getIntParameter, getParameter } from '../../utils/utils'; +import { getIntParameter, getParameter } from '../utils/utils'; import { Time } from '@project-chip/matter-node.js/time'; import { VendorId } from '@project-chip/matter-node.js/datatype'; import { QrCode } from '@project-chip/matter-node.js/schema'; diff --git a/src/matter/devices/HAmiddleware.ts b/src/matter/HAmiddleware.ts similarity index 87% rename from src/matter/devices/HAmiddleware.ts rename to src/matter/HAmiddleware.ts index 1e6a739..68255d3 100644 --- a/src/matter/devices/HAmiddleware.ts +++ b/src/matter/HAmiddleware.ts @@ -1,5 +1,5 @@ import { Logger } from '@project-chip/matter-node.js/log'; -import { HassEntity, HassEvent } from './HAssTypes'; +import { HassEntity, StateChangedEvent } from './HAssTypes'; import hass, { HassApi, HassWsOptions } from 'homeassistant-ws'; const sleep = (ms: number) => new Promise((r) => setTimeout(r, ms)); @@ -12,7 +12,7 @@ export class HAMiddleware { private requestFulfilled: boolean = true; private entities: { [k: string]: HassEntity } = {}; private functionsToCallOnChange: { - [k: string]: ((data: HassEvent['data']) => void) | undefined; + [k: string]: ((data: StateChangedEvent) => void) | undefined; } = {}; async waitCompletition(): Promise { @@ -34,17 +34,17 @@ export class HAMiddleware { } subscribe() { - this.hassClient.on('state_changed', (stateChangedEvent) => { - this.logger.debug(stateChangedEvent.data); + this.hassClient.on('state_changed', (event) => { + this.logger.debug(event); const toDo = - this.functionsToCallOnChange[stateChangedEvent.data.entity_id]; + this.functionsToCallOnChange[event.data.entity_id]; if (toDo) { - toDo(stateChangedEvent.data); + toDo(event.data); } }); } - subscrieToDevice(deviceId: string, fn: (data: HassEvent['data']) => void) { + subscrieToDevice(deviceId: string, fn: (event: StateChangedEvent) => void) { this.functionsToCallOnChange[deviceId] = fn; this.logger.debug(this.functionsToCallOnChange); } diff --git a/src/matter/devices/HAssTypes.ts b/src/matter/HAssTypes.ts similarity index 100% rename from src/matter/devices/HAssTypes.ts rename to src/matter/HAssTypes.ts diff --git a/src/matter/Mapper.ts b/src/matter/Mapper.ts new file mode 100644 index 0000000..c99d5ae --- /dev/null +++ b/src/matter/Mapper.ts @@ -0,0 +1,57 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ +import { addDimmerableLightDevice, addOnOffLightDevice } from './devices/lights'; +import { HassEntity } from './HAssTypes'; +import { Bridge, HAMiddleware } from '.'; +import { Logger } from '@project-chip/matter-node.js/log'; + +const LOGGER = new Logger('Mapper'); + + +const lightsMap: Map< + string, + (haEntity: HassEntity, haMiddleware: HAMiddleware, bridge: Bridge) => void +> = new Map< + string, + (haEntity: HassEntity, haMiddleware: HAMiddleware, bridge: Bridge) => void +>([ + ['onoff', addOnOffLightDevice], + ['rgb', addDimmerableLightDevice], + ['brightness', addDimmerableLightDevice], +]); + +function setLights( + lights: HassEntity[], + haMiddleware: HAMiddleware, + bridge: Bridge +) { + lights.forEach((entity) => { + LOGGER.info({ colormodes: entity.attributes['supported_color_modes'] }); + const key = (entity.attributes['supported_color_modes'] as string[])[0]; + LOGGER.info({ key }); + const lightBuildFunction = lightsMap.get(key); + if (!lightBuildFunction) { + throw new Error('Missing ' + key); + } + return lightBuildFunction(entity, haMiddleware, bridge); + }); +} + +async function setHasEnties( + haMiddleware: HAMiddleware, + bridge: Bridge +): Promise { + const entities = await haMiddleware.getStatesPartitionedByType(); + LOGGER.info({ entities }); + if (entities['light']) { + LOGGER.info('adding ', entities['light'].length, 'light devices'); + setLights(entities['light'], haMiddleware, bridge); + } +} + +export async function addAllDevicesToBridge( + haMiddleware: HAMiddleware, + bridge: Bridge +): Promise { + await setHasEnties(haMiddleware, bridge); + haMiddleware.subscribe(); +} diff --git a/src/matter/devices/Mapper.ts b/src/matter/devices/Mapper.ts deleted file mode 100644 index 4e0c799..0000000 --- a/src/matter/devices/Mapper.ts +++ /dev/null @@ -1,179 +0,0 @@ -/* eslint-disable @typescript-eslint/no-explicit-any */ -import { - Device, - DimmableLightDevice, - OnOffLightDevice, -} from '@project-chip/matter.js/device'; -import { HassEntity, HassEvent } from './HAssTypes'; -import { Bridge, HAMiddleware } from '.'; -import { MD5 } from 'crypto-js'; -import { Logger } from '@project-chip/matter-node.js/log'; - -const DEVICE_ENTITY_MAP: { - [k: string]: { haEntity: HassEntity; device: Device }; -} = {}; - -const LOGGER = new Logger('Mapper'); - -function addRGBLightDeviceToMap( - haEntity: HassEntity, - haMiddleware: HAMiddleware, - bridge: Bridge -): void { - const device = new DimmableLightDevice(); - const serialFromId = MD5(haEntity.entity_id).toString(); - - device.addOnOffListener((value, oldValue) => { - if (value !== oldValue) { - haMiddleware.callAService('light', value ? 'turn_on' : 'turn_off', { - entity_id: haEntity.entity_id, - }); - } - }); - device.addCurrentLevelListener((value) => { - haMiddleware.callAService( - 'light', - Number(value) > 0 ? 'turn_on' : 'turn_off', - { entity_id: haEntity.entity_id, brightness: Number(value) } - ); - }); - bridge.addDevice(device, { - nodeLabel: haEntity.attributes['friendly_name'], - reachable: true, - serialNumber: serialFromId, - }); - - DEVICE_ENTITY_MAP[haEntity.entity_id] = { haEntity, device }; -} - -function addimmerableLightDeviceToMap( - haEntity: HassEntity, - haMiddleware: HAMiddleware, - bridge: Bridge -): void { - const device = new DimmableLightDevice(); - const serialFromId = MD5(haEntity.entity_id).toString(); - device.addOnOffListener((value, oldValue) => { - if (value !== oldValue) { - haMiddleware.callAService('light', value ? 'turn_on' : 'turn_off', { - entity_id: haEntity.entity_id, - }); - } - }); - device.addCommandHandler( - 'identify', - async ({ request: { identifyTime } }) => - LOGGER.info( - `Identify called for OnOffDevice ${haEntity.attributes['friendly_name']} with id: ${serialFromId} and identifyTime: ${identifyTime}` - ) - ); - - device.addCurrentLevelListener((value) => { - haMiddleware.callAService( - 'light', - Number(value) > 0 ? 'turn_on' : 'turn_off', - { entity_id: haEntity.entity_id, brightness: Number(value) } - ); - }); - haMiddleware.subscrieToDevice( - haEntity.entity_id, - (data: HassEvent['data']) => { - device.setOnOff((data.new_state as any).state === 'on'); - device.setCurrentLevel( - (data.new_state as any)['attributes']['brightness'] - ); - } - ); - - bridge.addDevice(device, { - nodeLabel: haEntity.attributes['friendly_name'], - reachable: true, - serialNumber: serialFromId, - }); - - DEVICE_ENTITY_MAP[haEntity.entity_id] = { haEntity, device }; -} - -function addOnOffLightDeviceToMap( - haEntity: HassEntity, - haMiddleware: HAMiddleware, - bridge: Bridge -) { - const device = new OnOffLightDevice(); - const serialFromId = MD5(haEntity.entity_id).toString(); - device.addOnOffListener((value, oldValue) => { - if (value !== oldValue) { - haMiddleware.callAService('light', value ? 'turn_on' : 'turn_off', { - entity_id: haEntity.entity_id, - }); - } - }); - device.addCommandHandler( - 'identify', - async ({ request: { identifyTime } }) => - LOGGER.info( - `Identify called for OnOffDevice ${haEntity.attributes['friendly_name']} with id: ${serialFromId} and identifyTime: ${identifyTime}` - ) - ); - haMiddleware.subscrieToDevice( - haEntity.entity_id, - (data: HassEvent['data']) => { - device.setOnOff((data.new_state as any).state === 'on'); - } - ); - bridge.addDevice(device, { - nodeLabel: haEntity.attributes['friendly_name'], - reachable: true, - serialNumber: serialFromId, - }); - DEVICE_ENTITY_MAP[haEntity.entity_id] = { haEntity, device }; -} - -const lightsMap: Map< - string, - (haEntity: HassEntity, haMiddleware: HAMiddleware, bridge: Bridge) => void -> = new Map< - string, - (haEntity: HassEntity, haMiddleware: HAMiddleware, bridge: Bridge) => void ->([ - ['onoff', addOnOffLightDeviceToMap], - ['rgb', addRGBLightDeviceToMap], - ['brightness', addimmerableLightDeviceToMap], -]); - -function setLights( - lights: HassEntity[], - haMiddleware: HAMiddleware, - bridge: Bridge -) { - lights.forEach((entity) => { - LOGGER.info({ colormodes: entity.attributes['supported_color_modes'] }); - const key = (entity.attributes['supported_color_modes'] as string[])[0]; - LOGGER.info({ key }); - const lightBuildFunction = lightsMap.get(key); - if (!lightBuildFunction) { - throw new Error('Missing ' + key); - } - return lightBuildFunction(entity, haMiddleware, bridge); - }); -} - -async function setHasEnties( - haMiddleware: HAMiddleware, - bridge: Bridge -): Promise { - const entities = await haMiddleware.getStatesPartitionedByType(); - LOGGER.info({ entities }); - if (entities['light']) { - LOGGER.info('adding ', entities['light'].length, 'light devices'); - setLights(entities['light'], haMiddleware, bridge); - } -} - -export async function addAllDevicesToBridge( - haMiddleware: HAMiddleware, - bridge: Bridge -): Promise { - await setHasEnties(haMiddleware, bridge); - haMiddleware.subscribe(); -} diff --git a/src/matter/devices/MapperType.ts b/src/matter/devices/MapperType.ts new file mode 100644 index 0000000..b57f20d --- /dev/null +++ b/src/matter/devices/MapperType.ts @@ -0,0 +1,7 @@ +import { Device } from "@project-chip/matter-node.js/device"; +import { HAMiddleware, Bridge } from ".."; +import { HassEntity } from "../HAssTypes"; + +export type AddHaDeviceToBridge = (haEntity: HassEntity, + haMiddleware: HAMiddleware, + bridge: Bridge) => Device diff --git a/src/matter/devices/lights/DimmerableLightDevice.ts b/src/matter/devices/lights/DimmerableLightDevice.ts new file mode 100644 index 0000000..0bb7e2e --- /dev/null +++ b/src/matter/devices/lights/DimmerableLightDevice.ts @@ -0,0 +1,49 @@ +import { Device, DimmableLightDevice } from "@project-chip/matter-node.js/device"; +import { MD5 } from "crypto-js"; +import { HAMiddleware, Bridge } from "../.."; +import { HassEntity, StateChangedEvent } from "../../HAssTypes"; +import { AddHaDeviceToBridge } from "../MapperType"; +import { Logger } from "@project-chip/matter-node.js/log"; + +const LOGGER = new Logger('DimmableLight'); +export const addDimmerableLightDevice: AddHaDeviceToBridge = (haEntity: HassEntity, haMiddleware: HAMiddleware, bridge: Bridge): Device => { + const device = new DimmableLightDevice(); + const serialFromId = MD5(haEntity.entity_id).toString(); + device.addOnOffListener((value, oldValue) => { + if (value !== oldValue) { + haMiddleware.callAService('light', value ? 'turn_on' : 'turn_off', { + entity_id: haEntity.entity_id, + }); + } + }); + device.addCommandHandler( + 'identify', + async ({ request: { identifyTime } }) => + LOGGER.info( + `Identify called for OnOffDevice ${haEntity.attributes['friendly_name']} with id: ${serialFromId} and identifyTime: ${identifyTime}` + ) + ); + + device.addCurrentLevelListener((value) => { + haMiddleware.callAService( + 'light', + Number(value) > 0 ? 'turn_on' : 'turn_off', + { entity_id: haEntity.entity_id, brightness: Number(value) } + ); + }); + haMiddleware.subscrieToDevice( + haEntity.entity_id, + (event: StateChangedEvent) => { + device.setOnOff(event.data.new_state?.state === 'on'); + device.setCurrentLevel( + (event.data.new_state?.attributes as never)['brightness']); + } + ); + + bridge.addDevice(device, { + nodeLabel: haEntity.attributes['friendly_name'], + reachable: true, + serialNumber: serialFromId, + }); + return device; +} \ No newline at end of file diff --git a/src/matter/devices/lights/OnOffLightDevice.ts b/src/matter/devices/lights/OnOffLightDevice.ts new file mode 100644 index 0000000..6e3d3c2 --- /dev/null +++ b/src/matter/devices/lights/OnOffLightDevice.ts @@ -0,0 +1,39 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ +import { Device, OnOffLightDevice } from "@project-chip/matter-node.js/device"; +import { MD5 } from "crypto-js"; +import { HAMiddleware, Bridge } from "../.."; +import { HassEntity, StateChangedEvent } from "../../HAssTypes"; +import { AddHaDeviceToBridge } from "../MapperType"; +import { Logger } from "@project-chip/matter-node.js/log"; + +const LOGGER = new Logger('OnOfflIght'); +export const addOnOffLightDevice: AddHaDeviceToBridge = (haEntity: HassEntity, haMiddleware: HAMiddleware, bridge: Bridge): Device => { + const device = new OnOffLightDevice(); + const serialFromId = MD5(haEntity.entity_id).toString(); + device.addOnOffListener((value, oldValue) => { + if (value !== oldValue) { + haMiddleware.callAService('light', value ? 'turn_on' : 'turn_off', { + entity_id: haEntity.entity_id, + }); + } + }); + device.addCommandHandler( + 'identify', + async ({ request: { identifyTime } }) => + LOGGER.info( + `Identify called for OnOffDevice ${haEntity.attributes['friendly_name']} with id: ${serialFromId} and identifyTime: ${identifyTime}` + ) + ); + haMiddleware.subscrieToDevice( + haEntity.entity_id, + (event: StateChangedEvent) => { + device.setOnOff(event.data.new_state?.state === 'on'); + } + ); + bridge.addDevice(device, { + nodeLabel: haEntity.attributes['friendly_name'], + reachable: true, + serialNumber: serialFromId, + }); + return device; +} \ No newline at end of file diff --git a/src/matter/devices/lights/index.ts b/src/matter/devices/lights/index.ts new file mode 100644 index 0000000..9aade4f --- /dev/null +++ b/src/matter/devices/lights/index.ts @@ -0,0 +1,2 @@ +export * from './DimmerableLightDevice'; +export * from './OnOffLightDevice'; \ No newline at end of file diff --git a/src/matter/devices/index.ts b/src/matter/index.ts similarity index 100% rename from src/matter/devices/index.ts rename to src/matter/index.ts