import { Family } from '../Definitions/index.js'; import { ISY, writeDebugFile } from '../ISY.js'; import { ISYNode } from '../ISYNode.js'; import type { NodeDef } from '../Model/NodeDef.js'; import { type NodeInfo, isDynamic, isZWave } from '../Model/NodeInfo.js'; import type { Factory, StringKeys } from '../Utils.js'; import type { Constructor } from './Constructor.js'; import type { DynamicNode } from './DynamicNode.js'; import type { ISYDeviceNode } from './ISYDeviceNode.js'; export namespace NodeFactory { export type NodeClassFactory> = Factory & { implements: string[] } & { Commands; Drivers; }; export const registry: NodeClassRegistry = {}; export type NodeClass = T extends Family ? typeof ISYNode : T extends keyof typeof Family ? typeof ISYNode<(typeof Family)[T], any, any, any> : never; export const implementsRegistry: { [x in StringKeys]?: { [x: string]: string[] } } = {}; export function register(nodeClass: NodeClass, id: string = nodeClass.nodeDefId) { //let s; //FamilyNodeClassRegistry<(typeof Family)[F]>; let f = Family[nodeClass.family]; let s = (registry[f] ?? (registry[f] = {})) as FamilyNodeClassRegistry; s[id] = nodeClass; if (!implementsRegistry[f]) { implementsRegistry[f] = {}; } implementsRegistry[f][id] = nodeClass.implements; } function compare(a: typeof ISYNode, b: typeof ISYNode) { if (a.nodeDefId === b.nodeDefId) { return 0; } if (a.implements.includes(b.nodeDefId)) return 1; if (b.implements.includes(a.nodeDefId)) return -1; return b.nodeDefId.localeCompare(a.nodeDefId); } export function sortImplementsRegistry() { for (let f in implementsRegistry) { let reg = implementsRegistry[f]; for (let e in reg) { reg[e] = reg[e].sort((a, b) => compare(getForNodeDefId(f as keyof typeof Family, a), getForNodeDefId(f as keyof typeof Family, b)) ); } } } export function getImplements>( node: T | typeof ISYNode | Factory ): string[] { if (node instanceof ISYNode) return implementsRegistry[Family[node.family] as F][node.nodeDefId] ?? []; else if (typeof node === 'function') return implementsRegistry[Family[node.family] as F][node.nodeDefId] ?? []; else return ( implementsRegistry[Family[(node.Class as typeof ISYNode).family] as F][ (node.Class as typeof ISYNode).nodeDefId ] ?? [] ); } export function getForNode( family: F, node: NodeInfo<(typeof Family)[F]> ): NodeClass { if (isZWave(node)) { return getForNodeDefId(family, node.sgid ?? node.nodeDefId); } else if (isDynamic(node)) { return getForNodeDefId(family, node.nodeTypeId); } return getForNodeDefId(family, node.nodeDefId); } export function getForNodeDefId(family: F, nodeDefId: string): NodeClass { if (typeof family === 'string') { return registry[family as keyof typeof Family]?.[nodeDefId] as NodeClass; } else if (typeof family === 'number') { return registry[Family[family] as keyof typeof Family]?.[nodeDefId] as NodeClass; } } export async function get( node: NodeInfo<(typeof Family)[F]>, isy: ISY = ISY.instance ): Promise> { let nodeN = null; let family = Family.Insteon; if (typeof node.family === 'object') family = node.family._; else if (node.family !== undefined && node.family !== null) family = node.family; nodeN = getForNodeDefId(Family[family] as F, node.nodeDefId); if (nodeN) return Promise.resolve(nodeN); let cls = (await import('../Devices/GenericNode.js')).GenericNode; NodeFactory.register(cls, isDynamic(node) || isZWave(node) ? node.nodeTypeId : node.nodeDefId); return cls; } export async function create( nodeInfo: NodeInfo, isy: ISY = ISY.instance ): Promise> { const nodeClass = (await get(nodeInfo)) as unknown as Constructor>; let nodeInstance = null as ISYNode; if (nodeClass?.name !== 'ISYNode' && nodeClass !== undefined) { nodeInstance = new nodeClass(isy, nodeInfo); } if (isDynamic(nodeInfo) || isZWave(nodeInfo)) { let n = (await isy.sendRequest( `zmatter/${nodeInfo.family == Family.ZWave ? 'zwave' : 'zigbee'}/node/${nodeInfo.address}/def/get?full=true`, { trailingSlash: false } )) as NodeDef; if (n) { isy.logger.info( `NodeDef for ${nodeInfo.name} with nodeDef ${nodeInfo.nodeDefId} is dynamic. Saving nodeDef for debug purposes.` ); if (isy.isDebugEnabled) await writeDebugFile(JSON.stringify(n), `${nodeInfo.address}_NodeDef.json`, isy.logger, isy.storagePath); if (nodeInstance) { try { (nodeInstance as DynamicNode).applyNodeDef(n); } catch {} } } else { /*ISY.instance.logger.warn(`No built-in class found for ${Family[nodeInfo.family ?? 1]}.${nodeInfo.nodeDefId}`); return new GenericNode(isy, nodeInfo);*/ } } return nodeInstance; } } export type FamilyNodeClassRegistry = { [x: string]: NodeFactory.NodeClass }; type NodeClassRegistry = { [x in Extract]?: FamilyNodeClassRegistry<(typeof Family)[x]> };