chore(readme.md eslintrc.js pre-commit prepare-commit-msg): update README.md add eslint commit rules

This commit is contained in:
Jatus 2024-01-30 17:53:08 +01:00
parent 031d729af0
commit 007d9c4f98
19 changed files with 3873 additions and 196 deletions

4
.husky/pre-commit Normal file
View File

@ -0,0 +1,4 @@
# ...
#!/bin/sh
. "$(dirname "$0")/_/husky.sh"
cd ./matter-bridge && npm run lint-fix && git add .

View File

@ -0,0 +1,3 @@
#!/bin/bash
cd matter-bridge
exec < /dev/tty && node_modules/.bin/cz --hook || true

View File

@ -1,5 +1,7 @@
# HA-Matter-Bridge # HA-Matter-Bridge
[![paypal](https://camo.githubusercontent.com/0283ea90498d8ea623c07906a5e07e9e6c2a5eaa6911d52033687c60cfa8d22f/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f446f6e6174652d50617950616c2d677265656e2e737667)](https://www.paypal.com/donate/?business=VHWQSZR2SS898&no_recurring=0&item_name=HA+Matter+Bridge&currency_code=EUR) [![Publish Docker image](https://github.com/Jatus93/ha-matter-bridge/actions/workflows/docker-image.yml/badge.svg)](https://github.com/Jatus93/ha-matter-bridge/actions/workflows/docker-image.yml)
## Purpose ## Purpose
This project serves as a proof of concept to connect HomeAssistant devices to Voice Assistants through the Matter Protocol. This project serves as a proof of concept to connect HomeAssistant devices to Voice Assistants through the Matter Protocol.
@ -31,6 +33,7 @@ This project serves as a proof of concept to connect HomeAssistant devices to Vo
1. Help organize the project and dependencies. 1. Help organize the project and dependencies.
2. Add integrations for other devices. 2. Add integrations for other devices.
3. Add some tests 3. Add some tests
4. Donation
## Known Issues and Limitations ## Known Issues and Limitations

View File

@ -0,0 +1,46 @@
module.exports = {
env: {
es2021: true,
node: true,
jest: true,
},
extends: [
'eslint:recommended',
'plugin:@typescript-eslint/recommended',
'plugin:prettier/recommended',
],
overrides: [
{
env: {
node: true,
},
files: ['.eslintrc.{js,cjs}'],
parserOptions: {
sourceType: 'script',
},
},
],
parser: '@typescript-eslint/parser',
parserOptions: {
ecmaVersion: 'latest',
sourceType: 'module',
ecmaFeatures: {
arrowFunctions: true,
},
},
plugins: ['@stylistic/ts', '@typescript-eslint', 'prettier'],
rules: {
'linebreak-style': ['error', 'unix'],
quotes: ['error', 'single'],
semi: ['error', 'always'],
curly: ['error'],
'no-empty-function': [
'off',
{
allow: null,
},
],
'require-await': ['error'],
},
globals: {},
};

View File

@ -1,15 +0,0 @@
{
"env": {
"es2021": true,
"node": true
},
"extends": ["eslint:recommended", "plugin:@typescript-eslint/recommended"],
"parser": "@typescript-eslint/parser",
"parserOptions": {
"ecmaVersion": "latest",
"sourceType": "module"
},
"plugins": ["@typescript-eslint"],
"rules": {
}
}

View File

@ -1,7 +1,7 @@
{ {
"trailingComma": "es5",
"tabWidth": 4, "tabWidth": 4,
"semi": true, "semi": true,
"singleQuote": true, "singleQuote": true,
"printWidth": 80 "printWidth": 70,
"useTabs": false
} }

View File

@ -1,33 +1,21 @@
ARG BUILD_FROM=ghcr.io/hassio-addons/base:15.0.3 ARG BUILD_FROM=ghcr.io/hassio-addons/base:15.0.3
FROM node:18.19.0-alpine as node
FROM node:18.19.0-alpine as buildbase
COPY ./package.json ./
COPY ./package-lock.json ./
COPY ./src ./src
COPY ./tsconfig.json ./
RUN npm install
RUN npm run build
FROM node:18.19.0-alpine as deps
COPY ./package.json ./
COPY ./package-lock.json ./
COPY ./src ./src
COPY ./tsconfig.json ./
RUN npm install --production
FROM ${BUILD_FROM} as runenv FROM ${BUILD_FROM} as runenv
COPY --from=buildbase /usr/lib /usr/lib COPY --from=node /usr/lib /usr/lib
COPY --from=buildbase /usr/local/lib /usr/local/lib COPY --from=node /usr/local/lib /usr/local/lib
COPY --from=buildbase /usr/local/include /usr/local/include COPY --from=node /usr/local/include /usr/local/include
COPY --from=buildbase /usr/local/bin /usr/local/bin COPY --from=node /usr/local/bin /usr/local/bin
COPY --from=buildbase ./build ./build
COPY --from=deps ./node_modules ./node_modules
COPY --from=deps ./package.json ./package.json
COPY ./package.json ./
COPY ./package-lock.json ./
COPY ./src ./src
COPY ./tsconfig.json ./
RUN HUSKY=0 && npm install
RUN npm run build
RUN rm -rf ./node_modules
RUN HUSKY=0 && npm install --production
COPY run.sh / COPY run.sh /
RUN chmod a+x /run.sh RUN chmod a+x /run.sh

File diff suppressed because it is too large Load Diff

View File

@ -1,11 +1,13 @@
{ {
"name": "ha-matter-bridge", "name": "ha-matter-bridge",
"version": "1.0.0",
"description": "",
"main": "index.js", "main": "index.js",
"scripts": { "scripts": {
"build": "tsc", "build": "tsc",
"start": "node ./build/index.js" "start": "node ./build/index.js",
"lint": "eslint .",
"lint-fix": "eslint --fix .",
"prepare": "if [ $HUSKY ]; then cd .. && husky install matter-bridge/.husky; fi",
"commit": "./node_modules/.bin/git-cz"
}, },
"author": "", "author": "",
"license": "GPL-3.0", "license": "GPL-3.0",
@ -18,11 +20,28 @@
"ws": "^8.16.0" "ws": "^8.16.0"
}, },
"devDependencies": { "devDependencies": {
"@stylistic/eslint-plugin": "^1.5.4",
"@stylistic/eslint-plugin-js": "^1.5.4",
"@stylistic/eslint-plugin-plus": "^1.5.4",
"@stylistic/eslint-plugin-ts": "^1.5.4",
"@types/crypto-js": "^4.2.2", "@types/crypto-js": "^4.2.2",
"@types/ws": "^8.5.10", "@types/ws": "^8.5.10",
"@typescript-eslint/eslint-plugin": "^6.19.1", "@typescript-eslint/eslint-plugin": "^6.20.0",
"@typescript-eslint/parser": "^6.19.1", "@typescript-eslint/parser": "^6.20.0",
"commitizen": "^4.3.0",
"cz-conventional-changelog": "^3.3.0",
"eslint": "^8.56.0", "eslint": "^8.56.0",
"eslint-config-prettier": "^9.1.0",
"eslint-plugin-prettier": "^5.1.3",
"husky": "^9.0.7",
"prettier": "^3.2.4",
"prettier-eslint": "^16.3.0",
"standard-version": "^9.5.0",
"typescript": "^5.3.3" "typescript": "^5.3.3"
},
"config": {
"commitizen": {
"path": "cz-conventional-changelog"
}
} }
} }

View File

@ -15,14 +15,19 @@ export class HAMiddleware {
this.hassClient.rawClient.ws.close(); this.hassClient.rawClient.ws.close();
} }
async callAService(domain: string, service: string, extraArgs?: unknown) { async callAService(
domain: string,
service: string,
extraArgs?: unknown,
) {
await this.hassClient.callService(domain, service, extraArgs); await this.hassClient.callService(domain, service, extraArgs);
} }
subscribe() { subscribe() {
this.hassClient.on('state_changed', (event) => { this.hassClient.on('state_changed', (event) => {
this.logger.debug(JSON.stringify(event)); this.logger.debug(JSON.stringify(event));
const toDo = this.functionsToCallOnChange[event.data.entity_id]; const toDo =
this.functionsToCallOnChange[event.data.entity_id];
if (toDo) { if (toDo) {
toDo(event); toDo(event);
} }
@ -31,7 +36,7 @@ export class HAMiddleware {
subscribeToDevice( subscribeToDevice(
deviceId: string, deviceId: string,
fn: (event: StateChangedEvent) => void fn: (event: StateChangedEvent) => void,
) { ) {
this.functionsToCallOnChange[deviceId] = fn; this.functionsToCallOnChange[deviceId] = fn;
this.logger.debug(this.functionsToCallOnChange); this.logger.debug(this.functionsToCallOnChange);
@ -39,19 +44,20 @@ export class HAMiddleware {
async getStates(): Promise<{ [k: string]: HassEntity }> { async getStates(): Promise<{ [k: string]: HassEntity }> {
const states = await this.hassClient.getStates(); const states = await this.hassClient.getStates();
const sorted = states.reduceRight<{ [k: string]: HassEntity }>( const sorted = states.reduceRight<{
(last, current) => { [k: string]: HassEntity;
last[current['entity_id']] = current; }>((last, current) => {
return last; last[current['entity_id']] = current;
}, return last;
{} }, {});
);
this.logger.debug(JSON.stringify({ getStates: sorted })); this.logger.debug(JSON.stringify({ getStates: sorted }));
this.entities = sorted; this.entities = sorted;
return this.entities; return this.entities;
} }
async getStatesPartitionedByType(): Promise<{ [k: string]: HassEntity[] }> { async getStatesPartitionedByType(): Promise<{
[k: string]: HassEntity[];
}> {
const states = await this.getStates(); const states = await this.getStates();
const toReturn = Object.keys(states).reduceRight<{ const toReturn = Object.keys(states).reduceRight<{
[k: string]: HassEntity[]; [k: string]: HassEntity[];
@ -64,7 +70,7 @@ export class HAMiddleware {
return prev; return prev;
}, {}); }, {});
this.logger.debug( this.logger.debug(
JSON.stringify({ getStatesPartitionedByType: toReturn }) JSON.stringify({ getStatesPartitionedByType: toReturn }),
); );
return toReturn; return toReturn;
} }
@ -79,7 +85,7 @@ export class HAMiddleware {
} }
public static async getInstance( public static async getInstance(
callerOptions?: Partial<HassWsOptions> | undefined callerOptions?: Partial<HassWsOptions> | undefined,
): Promise<HAMiddleware> { ): Promise<HAMiddleware> {
if (!HAMiddleware.instance) { if (!HAMiddleware.instance) {
const client = await hass(callerOptions); const client = await hass(callerOptions);

View File

@ -60,7 +60,12 @@ export type HassConfig = {
config_source: string; config_source: string;
recovery_mode: boolean; recovery_mode: boolean;
safe_mode: boolean; safe_mode: boolean;
state: 'NOT_RUNNING' | 'STARTING' | 'RUNNING' | 'STOPPING' | 'FINAL_WRITE'; state:
| 'NOT_RUNNING'
| 'STARTING'
| 'RUNNING'
| 'STOPPING'
| 'FINAL_WRITE';
external_url: string | null; external_url: string | null;
internal_url: string | null; internal_url: string | null;
currency: string; currency: string;

View File

@ -8,19 +8,23 @@ const LOGGER = new Logger('Mapper');
async function setHasEntities( async function setHasEntities(
haMiddleware: HAMiddleware, haMiddleware: HAMiddleware,
bridge: Bridge bridge: Bridge,
): Promise<void> { ): Promise<void> {
const entities = await haMiddleware.getStatesPartitionedByType(); const entities = await haMiddleware.getStatesPartitionedByType();
LOGGER.info({ entities }); LOGGER.info({ entities });
if (entities['light']) { if (entities['light']) {
LOGGER.info('adding ', entities['light'].length, 'light devices'); LOGGER.info(
'adding ',
entities['light'].length,
'light devices',
);
setLights(entities['light'], haMiddleware, bridge); setLights(entities['light'], haMiddleware, bridge);
} }
} }
export async function addAllDevicesToBridge( export async function addAllDevicesToBridge(
haMiddleware: HAMiddleware, haMiddleware: HAMiddleware,
bridge: Bridge bridge: Bridge,
): Promise<void> { ): Promise<void> {
await setHasEntities(haMiddleware, bridge); await setHasEntities(haMiddleware, bridge);
haMiddleware.subscribe(); haMiddleware.subscribe();

View File

@ -7,7 +7,7 @@ import { Bridge } from '../../matter';
export type AddHaDeviceToBridge = ( export type AddHaDeviceToBridge = (
haEntity: HassEntity, haEntity: HassEntity,
haMiddleware: HAMiddleware, haMiddleware: HAMiddleware,
bridge: Bridge bridge: Bridge,
) => Device; ) => Device;
export { HAMiddleware } from '../../home-assistant/HAmiddleware'; export { HAMiddleware } from '../../home-assistant/HAmiddleware';

View File

@ -7,54 +7,68 @@ import {
HassEntity, HassEntity,
StateChangedEvent, StateChangedEvent,
} from '../../../home-assistant/HAssTypes'; } from '../../../home-assistant/HAssTypes';
import { AddHaDeviceToBridge, Bridge, HAMiddleware } from '../MapperType'; import {
AddHaDeviceToBridge,
Bridge,
HAMiddleware,
} from '../MapperType';
import { Logger } from '@project-chip/matter-node.js/log'; import { Logger } from '@project-chip/matter-node.js/log';
const LOGGER = new Logger('DimmableLight'); const LOGGER = new Logger('DimmableLight');
export const addDimmableLightDevice: AddHaDeviceToBridge = ( export const addDimmableLightDevice: AddHaDeviceToBridge = (
haEntity: HassEntity, haEntity: HassEntity,
haMiddleware: HAMiddleware, haMiddleware: HAMiddleware,
bridge: Bridge bridge: Bridge,
): Device => { ): Device => {
LOGGER.debug( LOGGER.debug(
`Building device ${haEntity.entity_id} \n ${JSON.stringify({ `Building device ${haEntity.entity_id} \n ${JSON.stringify({
haEntity, haEntity,
})}` })}`,
); );
const device = new DimmableLightDevice( const device = new DimmableLightDevice(
{ onOff: haEntity.state === 'on' }, { onOff: haEntity.state === 'on' },
{ {
currentLevel: currentLevel:
Number(haEntity.attributes['brightness']) / 255 || null, Number(haEntity.attributes['brightness']) / 255 ||
null,
onLevel: 0.1, onLevel: 0.1,
options: { coupleColorTempToLevel: false, executeIfOff: false }, options: {
} coupleColorTempToLevel: false,
executeIfOff: false,
},
},
); );
const serialFromId = MD5(haEntity.entity_id).toString(); const serialFromId = MD5(haEntity.entity_id).toString();
device.addOnOffListener((value, oldValue) => { device.addOnOffListener((value, oldValue) => {
LOGGER.debug( LOGGER.debug(
`OnOff Event for device ${haEntity.entity_id}, ${JSON.stringify({ `OnOff Event for device ${haEntity.entity_id}, ${JSON.stringify(
value, {
oldValue, value,
})}` oldValue,
},
)}`,
); );
if (value !== oldValue) { if (value !== oldValue) {
haMiddleware.callAService('light', value ? 'turn_on' : 'turn_off', { haMiddleware.callAService(
entity_id: haEntity.entity_id, 'light',
}); value ? 'turn_on' : 'turn_off',
{
entity_id: haEntity.entity_id,
},
);
} }
}); });
device.addCommandHandler( device.addCommandHandler(
'identify', 'identify',
async ({ request: { identifyTime } }) => ({ request: { identifyTime } }) =>
LOGGER.info( LOGGER.info(
`Identify called for OnOffDevice ${haEntity.attributes['friendly_name']} with id: ${serialFromId} and identifyTime: ${identifyTime}` `Identify called for OnOffDevice ${haEntity.attributes['friendly_name']} with id: ${serialFromId} and identifyTime: ${identifyTime}`,
) ),
); );
device.addCurrentLevelListener((value) => { device.addCurrentLevelListener((value) => {
LOGGER.debug( LOGGER.debug(
`CurrentLevel Event for device ${haEntity.entity_id} value: ${value}` `CurrentLevel Event for device ${haEntity.entity_id} value: ${value}`,
); );
let extraArgs = { entity_id: haEntity.entity_id } as object; let extraArgs = { entity_id: haEntity.entity_id } as object;
if (Number(value) > 0) { if (Number(value) > 0) {
@ -63,7 +77,7 @@ export const addDimmableLightDevice: AddHaDeviceToBridge = (
haMiddleware.callAService( haMiddleware.callAService(
'light', 'light',
Number(value) > 0 ? 'turn_on' : 'turn_off', Number(value) > 0 ? 'turn_on' : 'turn_off',
extraArgs extraArgs,
); );
}); });
@ -74,9 +88,11 @@ export const addDimmableLightDevice: AddHaDeviceToBridge = (
LOGGER.debug(JSON.stringify(event)); LOGGER.debug(JSON.stringify(event));
device.setOnOff(event.data.new_state?.state === 'on'); device.setOnOff(event.data.new_state?.state === 'on');
device.setCurrentLevel( device.setCurrentLevel(
(event.data.new_state?.attributes as never)['brightness'] (event.data.new_state?.attributes as never)[
'brightness'
],
); );
} },
); );
bridge.addDevice(device, { bridge.addDevice(device, {

View File

@ -1,46 +1,59 @@
/* eslint-disable @typescript-eslint/no-explicit-any */ /* eslint-disable @typescript-eslint/no-explicit-any */
import { Device, OnOffLightDevice } from '@project-chip/matter-node.js/device'; import {
Device,
OnOffLightDevice,
} from '@project-chip/matter-node.js/device';
import { MD5 } from 'crypto-js'; import { MD5 } from 'crypto-js';
import { import {
HassEntity, HassEntity,
StateChangedEvent, StateChangedEvent,
} from '../../../home-assistant/HAssTypes'; } from '../../../home-assistant/HAssTypes';
import { AddHaDeviceToBridge, Bridge, HAMiddleware } from '../MapperType'; import {
AddHaDeviceToBridge,
Bridge,
HAMiddleware,
} from '../MapperType';
import { Logger } from '@project-chip/matter-node.js/log'; import { Logger } from '@project-chip/matter-node.js/log';
const LOGGER = new Logger('OnOffLight'); const LOGGER = new Logger('OnOffLight');
export const addOnOffLightDevice: AddHaDeviceToBridge = ( export const addOnOffLightDevice: AddHaDeviceToBridge = (
haEntity: HassEntity, haEntity: HassEntity,
haMiddleware: HAMiddleware, haMiddleware: HAMiddleware,
bridge: Bridge bridge: Bridge,
): Device => { ): Device => {
LOGGER.debug( LOGGER.debug(
`Building device ${haEntity.entity_id} \n ${JSON.stringify({ `Building device ${haEntity.entity_id} \n ${JSON.stringify({
haEntity, haEntity,
})}` })}`,
); );
const device = new OnOffLightDevice(); const device = new OnOffLightDevice();
const serialFromId = MD5(haEntity.entity_id).toString(); const serialFromId = MD5(haEntity.entity_id).toString();
device.addOnOffListener((value, oldValue) => { device.addOnOffListener((value, oldValue) => {
LOGGER.debug( LOGGER.debug(
`OnOff Event for device ${haEntity.entity_id}, ${JSON.stringify({ `OnOff Event for device ${haEntity.entity_id}, ${JSON.stringify(
value, {
oldValue, value,
})}` oldValue,
},
)}`,
); );
if (value !== oldValue) { if (value !== oldValue) {
haMiddleware.callAService('light', value ? 'turn_on' : 'turn_off', { haMiddleware.callAService(
entity_id: haEntity.entity_id, 'light',
}); value ? 'turn_on' : 'turn_off',
{
entity_id: haEntity.entity_id,
},
);
} }
}); });
device.addCommandHandler( device.addCommandHandler(
'identify', 'identify',
async ({ request: { identifyTime } }) => ({ request: { identifyTime } }) =>
LOGGER.info( LOGGER.info(
`Identify called for OnOffDevice ${haEntity.attributes['friendly_name']} with id: ${serialFromId} and identifyTime: ${identifyTime}` `Identify called for OnOffDevice ${haEntity.attributes['friendly_name']} with id: ${serialFromId} and identifyTime: ${identifyTime}`,
) ),
); );
haMiddleware.subscribeToDevice( haMiddleware.subscribeToDevice(
haEntity.entity_id, haEntity.entity_id,
@ -48,7 +61,7 @@ export const addOnOffLightDevice: AddHaDeviceToBridge = (
LOGGER.debug(`Event for device ${haEntity.entity_id}`); LOGGER.debug(`Event for device ${haEntity.entity_id}`);
LOGGER.debug(JSON.stringify(event)); LOGGER.debug(JSON.stringify(event));
device.setOnOff(event.data.new_state?.state === 'on'); device.setOnOff(event.data.new_state?.state === 'on');
} },
); );
bridge.addDevice(device, { bridge.addDevice(device, {
nodeLabel: haEntity.attributes['friendly_name'], nodeLabel: haEntity.attributes['friendly_name'],

View File

@ -11,25 +11,27 @@ export * from './OnOffLightDevice';
const LOGGER = new Logger('Lights'); const LOGGER = new Logger('Lights');
const LIGHTS_MAP_FUNCTIONS: Map<string, AddHaDeviceToBridge> = new Map< const LIGHTS_MAP_FUNCTIONS: Map<string, AddHaDeviceToBridge> =
string, new Map<string, AddHaDeviceToBridge>([
AddHaDeviceToBridge ['onoff', addOnOffLightDevice],
>([ ['rgb', addDimmableLightDevice],
['onoff', addOnOffLightDevice], ['brightness', addDimmableLightDevice],
['rgb', addDimmableLightDevice], ]);
['brightness', addDimmableLightDevice],
]);
const LIGHTS_MAP: Map<string, Device> = new Map<string, Device>(); const LIGHTS_MAP: Map<string, Device> = new Map<string, Device>();
export function setLights( export function setLights(
lights: HassEntity[], lights: HassEntity[],
haMiddleware: HAMiddleware, haMiddleware: HAMiddleware,
bridge: Bridge bridge: Bridge,
) { ) {
lights.forEach((entity) => { lights.forEach((entity) => {
LOGGER.info({ colormodes: entity.attributes['supported_color_modes'] }); LOGGER.info({
const key = (entity.attributes['supported_color_modes'] as string[])[0]; colormodes: entity.attributes['supported_color_modes'],
});
const key = (
entity.attributes['supported_color_modes'] as string[]
)[0];
LOGGER.info({ key }); LOGGER.info({ key });
const lightBuildFunction = LIGHTS_MAP_FUNCTIONS.get(key); const lightBuildFunction = LIGHTS_MAP_FUNCTIONS.get(key);
if (!lightBuildFunction) { if (!lightBuildFunction) {
@ -37,7 +39,7 @@ export function setLights(
} }
LIGHTS_MAP.set( LIGHTS_MAP.set(
entity.entity_id, entity.entity_id,
lightBuildFunction(entity, haMiddleware, bridge) lightBuildFunction(entity, haMiddleware, bridge),
); );
}); });
} }

View File

@ -23,10 +23,10 @@ export class Bridge {
private static readonly deviceName = private static readonly deviceName =
getParameter('name') || 'Matter Bridge'; getParameter('name') || 'Matter Bridge';
private static readonly deviceType = DeviceTypes.AGGREGATOR.code; private static readonly deviceType = DeviceTypes.AGGREGATOR.code;
private static readonly vendorName = getParameter('vendor') || 'Jatus'; private static readonly vendorName =
getParameter('vendor') || 'Jatus';
private static readonly productName = 'HomeAssistant'; private static readonly productName = 'HomeAssistant';
private static readonly port = getIntParameter('port') ?? 5540; private static readonly port = getIntParameter('port') ?? 5540;
private ready = false;
private matterServer: MatterServer; private matterServer: MatterServer;
private static instance: Bridge; private static instance: Bridge;
@ -37,7 +37,7 @@ export class Bridge {
private constructor( private constructor(
matterServer: MatterServer, matterServer: MatterServer,
storageManager: StorageManager storageManager: StorageManager,
) { ) {
this.matterServer = matterServer; this.matterServer = matterServer;
this.storageManager = storageManager; this.storageManager = storageManager;
@ -47,7 +47,7 @@ export class Bridge {
public static getInstance( public static getInstance(
matterServer: MatterServer, matterServer: MatterServer,
storageManager: StorageManager storageManager: StorageManager,
): Bridge { ): Bridge {
if (!Bridge.instance) { if (!Bridge.instance) {
this.instance = new Bridge(matterServer, storageManager); this.instance = new Bridge(matterServer, storageManager);
@ -106,12 +106,17 @@ export class Bridge {
device: Device | ComposedDevice, device: Device | ComposedDevice,
bridgedBasicInformation?: AttributeInitialValues< bridgedBasicInformation?: AttributeInitialValues<
typeof BridgedDeviceBasicInformationCluster.attributes typeof BridgedDeviceBasicInformationCluster.attributes
> >,
) { ) {
if (!this.commissioningServer?.isCommissioned()) { if (!this.commissioningServer?.isCommissioned()) {
this.logger.warn('System not initialized, may cause crashes'); this.logger.warn(
'System not initialized, may cause crashes',
);
} }
this.aggregator.addBridgedDevice(device, bridgedBasicInformation); this.aggregator.addBridgedDevice(
device,
bridgedBasicInformation,
);
} }
async start() { async start() {
@ -119,30 +124,32 @@ export class Bridge {
this.commissioningServer = this.commissioningServer =
await this.setupContextAndCommissioningServer(); await this.setupContextAndCommissioningServer();
this.commissioningServer.addDevice(this.aggregator); this.commissioningServer.addDevice(this.aggregator);
this.matterServer.addCommissioningServer(this.commissioningServer); this.matterServer.addCommissioningServer(
this.commissioningServer,
);
await this.matterServer.start(); await this.matterServer.start();
this.logger.info('Listening'); this.logger.info('Listening');
if (!this.commissioningServer.isCommissioned()) { if (!this.commissioningServer.isCommissioned()) {
const pairingData = this.commissioningServer.getPairingCode(); const pairingData =
this.commissioningServer.getPairingCode();
const { qrPairingCode, manualPairingCode } = pairingData; const { qrPairingCode, manualPairingCode } = pairingData;
console.log(QrCode.get(qrPairingCode)); console.log(QrCode.get(qrPairingCode));
this.logger.info( this.logger.info(
`QR Code URL: https://project-chip.github.io/connectedhomeip/qrcode.html?data=${qrPairingCode}` `QR Code URL: https://project-chip.github.io/connectedhomeip/qrcode.html?data=${qrPairingCode}`,
);
this.logger.info(
`Manual pairing code: ${manualPairingCode}`,
); );
this.logger.info(`Manual pairing code: ${manualPairingCode}`);
} else { } else {
this.logger.info( this.logger.info(
'Device is already commissioned. Waiting for controllers to connect ...' 'Device is already commissioned. Waiting for controllers to connect ...',
); );
} }
} }
async stop() { async stop() {
this.matterServer.close(); await this.matterServer.close();
this.storageManager await this.storageManager.close();
.close()
.then(() => process.exit(0))
.catch((err) => this.logger.error(err));
} }
} }

View File

@ -3,7 +3,10 @@ import {
StorageBackendDisk, StorageBackendDisk,
StorageManager, StorageManager,
} from '@project-chip/matter-node.js/storage'; } from '@project-chip/matter-node.js/storage';
import { getParameter, hasParameter } from '@project-chip/matter-node.js/util'; import {
getParameter,
hasParameter,
} from '@project-chip/matter-node.js/util';
export { Bridge } from './Bridge'; export { Bridge } from './Bridge';
import { Bridge } from './Bridge'; import { Bridge } from './Bridge';
@ -16,25 +19,29 @@ export function serverSetup(): {
storageManager: StorageManager; storageManager: StorageManager;
} { } {
if (!(MATTER_SERVER && STORAGE && STORAGE_MANAGER)) { if (!(MATTER_SERVER && STORAGE && STORAGE_MANAGER)) {
const storageLocation = getParameter('store') || '/config/deviceData'; const storageLocation =
getParameter('store') || '/config/deviceData';
STORAGE = new StorageBackendDisk( STORAGE = new StorageBackendDisk(
storageLocation, storageLocation,
hasParameter('clearstorage') hasParameter('clearstorage'),
); );
STORAGE_MANAGER = new StorageManager(STORAGE); STORAGE_MANAGER = new StorageManager(STORAGE);
MATTER_SERVER = new MatterServer(STORAGE_MANAGER, { MATTER_SERVER = new MatterServer(STORAGE_MANAGER, {
mdnsInterface: getParameter('netinterface'), mdnsInterface: getParameter('netinterface'),
}); });
} }
return { matterServer: MATTER_SERVER, storageManager: STORAGE_MANAGER }; return {
matterServer: MATTER_SERVER,
storageManager: STORAGE_MANAGER,
};
} }
export function getBridge(): Bridge { export function getBridge(): Bridge {
const serverData = serverSetup(); const serverData = serverSetup();
const bridge = Bridge.getInstance( const bridge = Bridge.getInstance(
serverData.matterServer, serverData.matterServer,
serverData.storageManager serverData.storageManager,
); );
return bridge; return bridge;
} }

View File

@ -14,20 +14,25 @@ export function hasParameter(name: string) {
export function getIntParameter(name: string) { export function getIntParameter(name: string) {
const value = getParameter(name); const value = getParameter(name);
if (value === undefined) return undefined; if (value === undefined) {
return undefined;
}
const intValue = parseInt(value, 10); const intValue = parseInt(value, 10);
if (isNaN(intValue)) if (isNaN(intValue)) {
throw new ValidationError( throw new ValidationError(
`Invalid value for parameter ${name}: ${value} is not a number` `Invalid value for parameter ${name}: ${value} is not a number`,
); );
}
return intValue; return intValue;
} }
export function commandExecutor(scriptParamName: string) { export function commandExecutor(scriptParamName: string) {
const script = getParameter(scriptParamName); const script = getParameter(scriptParamName);
if (script === undefined) return undefined; if (script === undefined) {
return undefined;
}
return () => return () =>
console.log( console.log(
`${scriptParamName}: ${execSync(script).toString().slice(0, -1)}` `${scriptParamName}: ${execSync(script).toString().slice(0, -1)}`,
); );
} }