UNPKG

@pmouli/isy-matter-server

Version:

Service to expose an ISY device as a Matter Border router

137 lines (122 loc) 5.15 kB
import { Category as BaseCat } from '../../Definitions/Global/Categories.js'; import { NodeInfo } from '../../Model/NodeInfo.js'; import { Family } from '../../Definitions/index.js'; import type { ISYDevice } from '../../ISYDevice.js'; import { NodeFactory } from '../NodeFactory.js'; import { ColorControl, ColorControlCluster, LevelControl, OnOff } from '@matter/main/clusters'; import { ClusterId, type ClusterType } from '@matter/types'; import { ISY, writeDebugFile } from '../../ISY.js'; import type { ISYDeviceNode } from '../ISYDeviceNode.js'; import { writeFile } from 'fs'; import { ColorHueSat, ColorTemperatureLight, ColorXY, DimmableLight, ExtendedColorLight, OnOffLevel } from './index.js'; import { OnOffLight } from './OnOffLight.js'; import { hasBitFlag, type ZigBeeClusterData } from './ZigbeeClusterData.js'; export function parseZigbeeClusterData(input: string): { [x: string]: ZigBeeClusterData<any> } { const clusters: { [x: ClusterId]: ZigBeeClusterData<ClusterType> } = {}; const clusterRegex = /Cluster (\d+) 0x([0-9A-Fa-f]+) ([\w_]+)/g; const dataRegex = /(\w+)\s+DATA\s+devices\.\d+\.endpoints\.(\d+)\.clusters\.\d+\.data\.?([\w\.]*)\s*=\s*(.+)/gm; let currentCluster: ZigBeeClusterData<ClusterType> | null = null; input?.split('\n').forEach((line) => { clusterRegex.lastIndex = 0; // Reset lastIndex for clusterRegex const clusterMatch = clusterRegex.exec(line); if (clusterMatch) { if (currentCluster) clusters[currentCluster.clusterName] = currentCluster; currentCluster = { clusterId: ClusterId(parseInt(clusterMatch[1], 10)), clusterName: clusterMatch[3] as Uppercase<string>, endpoint: 0, // Placeholder, will be updated later data: {} }; return; } dataRegex.lastIndex = 0; // Reset lastIndex for dataRegex const dataMatch = dataRegex.exec(line); if (dataMatch && currentCluster) { const [, type, endpoint, key, value] = dataMatch; currentCluster.endpoint = parseInt(endpoint, 10); currentCluster.data[key] = parseValue(type, value); } }); if (currentCluster) { clusters[currentCluster?.clusterName] = currentCluster; } return clusters; } function parseValue(type: string, value: string): any { switch (type) { case 'Boolean': return value.trim() === 'True'; case 'Integer': return parseInt(value, 10); case 'Binary': return value.match(/\[([0-9A-Fa-f]+)\]/)?.[1] || value; case 'Empty': return null; default: return value.trim(); } } export class ZigBeeDeviceFactory { public static async create(isy: ISY, nodeInfo: NodeInfo<Family.ZigBee>): Promise<ISYDevice<Family.ZigBee, any, any, any>> { let clusterInfo = await isy.sendRequest(`zmatter/zigbee/dh/node/${nodeInfo.address}/cluster/0`, { trailingSlash: false }); if (clusterInfo) { if (isy.isDebugEnabled) { await writeDebugFile(clusterInfo, `${nodeInfo.address}_ClusterData.txt`, isy.logger, isy.storagePath); } let cd = parseZigbeeClusterData(clusterInfo); //@ts-expect-error let cc = cd['COLOR_CONTROL'] as ZigBeeClusterData<ColorControl.Cluster>; //@ts-expect-error let lc = cd['LEVEL_CONTROL'] as ZigBeeClusterData<LevelControl.Cluster>; //@ts-expect-error let os = cd['ON_OFF_SWITCH'] as ZigBeeClusterData<OnOff.Cluster>; if (cc) { if (hasBitFlag(cc.data.colorCapabilities, ColorControl.ColorCapabilities.xy)) { return new ExtendedColorLight(isy, nodeInfo, cd); } else if (hasBitFlag(cc.data.colorCapabilities, ColorControl.ColorCapabilities.colorTemperature)) { return new ColorTemperatureLight(isy, nodeInfo); } } else if (lc && os) return new DimmableLight(isy, nodeInfo); else if (os) return new OnOffLight(isy, nodeInfo); } } // public static buildDeviceMap() { // var fams = new Map<Family, FamilyDef<Family>>(); // s.forEach((item) => { // var id = item.id as Family; // fams.set(id, { id: item.id, description: item.description, name: item.name, categories: new Map<string, CategoryDef<typeof id>>() }); // var famDef = fams[id] as FamilyDef<Family>; // item.categories.forEach((element) => { // var catDef = { id: element.id, name: element.name, devices: new Map<string, DeviceDef<Family>> }; // element.devices.forEach( // device => { // const r = this.getDeviceDetails({ // family: item.id, // type: `${element.id}.${device.id}.0.0`, // address: '0 0 0 1', // nodeDefId: '', // enabled: undefined, // pnode: undefined, // name: '', // startDelay: 0, // hint: '', // endDelay: 0, // wattage: 0, // dcPeriod: 0 // }); // if (!r.unsupported) { // device.name = r.name; // device.modelNumber = r.modelNumber; // device.class = r.class.name; // } // catDef.devices.set(`${device.id}`, { id: device.id, modelNumber: device.modelNumber, name: device.name, class: r.class }); // } // ); // famDef.categories.set(element.name, catDef); // element.devices = element.devices.sort((a, b) => a.id - b.id); // }); // } // ); // writeFileSync("DeviceMapClean.json", JSON.stringify(fams)); // } }