Skip to content

Commit

Permalink
First implementation of Socket and Light
Browse files Browse the repository at this point in the history
  • Loading branch information
GermanBluefox committed Oct 20, 2023
1 parent 22d9107 commit 391216e
Show file tree
Hide file tree
Showing 6 changed files with 144 additions and 71 deletions.
2 changes: 2 additions & 0 deletions src-admin/src/components/Bridges.js
Original file line number Diff line number Diff line change
Expand Up @@ -126,13 +126,15 @@ class Bridges extends React.Component {
enabled: true,
productID: this.state.editDialog.productID,
vendorID: this.state.editDialog.vendorID,
noComposed: this.state.editDialog.noComposed,
list: [],
uuid: uuidv4(),
});
} else if (this.state.editDialog.type === 'bridge') {
matter.bridges[this.state.editDialog.bridge].name = this.state.editDialog.name;
matter.bridges[this.state.editDialog.bridge].productID = this.state.editDialog.productID;
matter.bridges[this.state.editDialog.bridge].vendorID = this.state.editDialog.vendorID;
matter.bridges[this.state.editDialog.bridge].noComposed = this.state.editDialog.noComposed;
} else if (this.state.editDialog.bridge !== undefined) {
matter.bridges[this.state.editDialog.bridge].list[this.state.editDialog.device].name = this.state.editDialog.name;
}
Expand Down
21 changes: 21 additions & 0 deletions src/ioBrokerStorageTypes.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
export interface MatterAdapterConfig extends ioBroker.AdapterConfig {
interface: string;
}

export interface DeviceDescription {
uuid: string;
name: string;
oid: string;
type: string;
enabled: boolean;
}

export interface BridgeDescription {
uuid: string;
enabled: boolean;
productID: string;
vendorID: string;
passcode: string;
name: string;
list: DeviceDescription[];
}
68 changes: 33 additions & 35 deletions src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,28 +9,8 @@ import { StorageIoBroker } from './matter/StorageIoBroker';
import { SubscribeManager, DeviceFabric, GenericDevice } from './lib';
import { DetectedDevice } from './lib/devices/GenericDevice';
import BridgedDevice from './matter/BridgedDevicesNode';

interface MatterAdapterConfig extends ioBroker.AdapterConfig {
interface: string;
}

interface DeviceDescription {
uuid: string;
name: string;
oid: string;
type: string;
enabled: boolean;
}

interface BridgeDescription {
uuid: string;
enabled: boolean;
productID: string;
vendorID: string;
passcode: string;
name: string;
list: DeviceDescription[];
}
import { MatterAdapterConfig, DeviceDescription, BridgeDescription } from './ioBrokerStorageTypes';
import { Level, Logger } from '@project-chip/matter-node.js/log';

export class MatterAdapter extends utils.Adapter {
private detector: ChannelDetectorType;
Expand All @@ -48,7 +28,7 @@ export class MatterAdapter extends utils.Adapter {
name: 'matter',
uiClientSubscribe: data => this.onClientSubscribe(data.clientId),
uiClientUnsubscribe: data => {
const { clientId, message, reason } = data;
const { clientId, reason } = data;
if (reason === 'client') {
this.log.debug(`GUI Client "${clientId} disconnected`);
} else {
Expand All @@ -66,7 +46,7 @@ export class MatterAdapter extends utils.Adapter {
this.detector = new ChannelDetector();
}

async onClientSubscribe(clientId: string): Promise<{error?: string, accepted: boolean, heartbeat?: number}> {
async onClientSubscribe(clientId: string): Promise<{ error?: string, accepted: boolean, heartbeat?: number }> {
this.log.debug(`Subscribe from ${clientId}`);
if (!this._guiSubscribes) {
return { error: `Adapter is still initializing`,accepted: false };
Expand All @@ -93,7 +73,7 @@ export class MatterAdapter extends utils.Adapter {
return { accepted: true, heartbeat: 120000 };
}

onClientUnsubscribe(clientId: string) {
onClientUnsubscribe(clientId: string): void {
this.log.debug(`Unsubscribe from ${clientId}`);
if (!this._guiSubscribes) {
return;
Expand All @@ -109,7 +89,7 @@ export class MatterAdapter extends utils.Adapter {
} while(deleted);
}

sendToGui = async (data: any): Promise<void> => {
sendToGui = async(data: any): Promise<void> => {
if (!this._guiSubscribes) {
return;
}
Expand All @@ -118,10 +98,30 @@ export class MatterAdapter extends utils.Adapter {
await this.sendToUI({ clientId: this._guiSubscribes[i].clientId, data });
}
}
}
};

async createMatterServer() {
async createMatterServer(): Promise<void> {
const config: MatterAdapterConfig = this.config as MatterAdapterConfig;
Logger.defaultLogLevel = Level.DEBUG;
Logger.log = (level: Level, formattedLog: string) => {
switch (level) {
case Level.DEBUG:
this.log.silly(formattedLog);
break;
case Level.INFO:
this.log.debug(formattedLog);
break;
case Level.WARN:
this.log.info(formattedLog);
break;
case Level.ERROR:
this.log.warn(formattedLog);
break;
case Level.FATAL:
this.log.error(formattedLog);
break;
}
};

/**
* Initialize the storage system.
Expand Down Expand Up @@ -161,7 +161,7 @@ export class MatterAdapter extends utils.Adapter {
}

async requestNodeStates(): Promise<void> {
for (let uuid in this.bridges) {
for (const uuid in this.bridges) {
const state = await this.bridges[uuid].getState();
this.log.debug(`State of ${uuid} is ${state}`);
}
Expand Down Expand Up @@ -279,11 +279,9 @@ export class MatterAdapter extends utils.Adapter {
async createBridge(uuid: string, options: BridgeDescription): Promise<BridgedDevice> {
if (this.matterServer) {
const devices = [];
for (let l = 0; l < options.list.length; l++) {
const device = options.list[l];
if (device.enabled === false) {
continue;
}
const optionsList = (options.list || []).filter(item => item.enabled !== false);
for (let l = 0; l < optionsList.length; l++) {
const device = optionsList[l];
const detectedDevice = await this.getDeviceStates(device.oid) as DetectedDevice;
if (detectedDevice) {
const deviceObject = await DeviceFabric(detectedDevice, this);
Expand All @@ -303,8 +301,8 @@ export class MatterAdapter extends utils.Adapter {
devicename: options.name,
productname: `Product ${options.name}`,
},
adapter: this,
devices,
devicesOptions: optionsList,
matterServer: this.matterServer,
sendToGui: this.sendToGui,
});
Expand Down
84 changes: 54 additions & 30 deletions src/matter/BridgedDevicesNode.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,19 @@
import { CommissioningServer, MatterServer } from '@project-chip/matter-node.js';

import { VendorId } from '@project-chip/matter-node.js/datatype';
import { Aggregator, DeviceTypes, OnOffPluginUnitDevice } from '@project-chip/matter-node.js/device';
import { toJson } from '@project-chip/matter.js/storage';
import { Aggregator, DeviceTypes } from '@project-chip/matter-node.js/device';

import { GenericDevice, Socket } from '../lib';
import { GenericDevice } from '../lib';
import { DeviceDescription } from '../ioBrokerStorageTypes';

import matterDeviceFabric from './matterFabric';

export interface BridgeCreateOptions {
adapter: ioBroker.Adapter;
parameters: BridgeOptions,
devices: GenericDevice[];
sendToGui: (data: any) => Promise<void>;
matterServer: MatterServer;
devicesOptions: DeviceDescription[];
}

export interface BridgeOptions {
Expand All @@ -30,23 +32,24 @@ export enum BridgeStates {
Commissioned = 'commissioned',
}


class BridgedDevice {
private matterServer: MatterServer | undefined;
private adapter: ioBroker.Adapter;
private parameters: BridgeOptions;
private devices: GenericDevice[];
private sendToGui: (data: any) => Promise<void> | undefined;
private commissioningServer: CommissioningServer | undefined;
private devicesOptions: DeviceDescription[];

constructor(options: BridgeCreateOptions) {
this.adapter = options.adapter;
this.parameters = options.parameters;
this.devices = options.devices;
this.sendToGui = options.sendToGui;
this.matterServer = options.matterServer;
this.devicesOptions = options.devicesOptions;
}

async init() {
async init(): Promise<void> {
/**
* Collect all needed data
*
Expand All @@ -57,9 +60,9 @@ class BridgedDevice {
* and easy reuse. When you also do that be careful to not overlap with Matter-Server own contexts
* (so maybe better not ;-)).
*/
const deviceName = this.parameters.devicename || "Matter Bridge device";
const deviceName = this.parameters.devicename || 'Matter Bridge device';
const deviceType = DeviceTypes.AGGREGATOR.code;
const vendorName = "ioBroker";
const vendorName = 'ioBroker';
const passcode = this.parameters.passcode; // 20202021;
const discriminator = this.parameters.discriminator; // 3840);

Expand Down Expand Up @@ -116,24 +119,30 @@ class BridgedDevice {
const aggregator = new Aggregator();

for (let i = 1; i <= this.devices.length; i++) {
const device = this.devices[i - 1] as Socket;
const onOffDevice = new OnOffPluginUnitDevice();

onOffDevice.addOnOffListener(on => device.setPower(on));
onOffDevice.addCommandHandler('identify', async ({request: {identifyTime}}) => {
console.log(
`Identify called for OnOffDevice ${onOffDevice.name} with id: ${i} and identifyTime: ${identifyTime}`,
);
});

const name = `OnOff Socket ${i}`;
aggregator.addBridgedDevice(onOffDevice, {
nodeLabel: name,
productName: name,
productLabel: name,
uniqueId: i.toString().padStart(4, '0') + uniqueId.substring(4),
reachable: true,
});
const ioBrokerDevice = this.devices[i - 1] as GenericDevice;
const mappingDevice = await matterDeviceFabric(ioBrokerDevice, this.devicesOptions[i - 1].name, this.devicesOptions[i - 1].uuid);
if (mappingDevice) {
const name = mappingDevice.getName();// `OnOff Socket ${i}`;
aggregator.addBridgedDevice(mappingDevice.getMatterDevice(), {
nodeLabel: name,
productName: name,
productLabel: name,
uniqueId: this.devicesOptions[i - 1].uuid[i - 1].replace(/-/g, ''),
reachable: true,
});
} else {
console.error(`ioBroker Device "${this.devices[i - 1].getDeviceType()}" is not supported`);
}

// const onOffDevice = new OnOffPluginUnitDevice();
//
// onOffDevice.setOnOff(true);
// onOffDevice.addOnOffListener(on => device.setPower(on));
// onOffDevice.addCommandHandler('identify', async ({request: {identifyTime}}) => {
// console.log(
// `Identify called for OnOffDevice ${onOffDevice.name} with id: ${i} and identifyTime: ${identifyTime}`,
// );
// });
}

this.commissioningServer.addDevice(aggregator);
Expand Down Expand Up @@ -178,19 +187,34 @@ class BridgedDevice {
const activeSession = this.commissioningServer.getActiveSessionInformation();
const fabric = this.commissioningServer.getCommissionedFabricInformation();

const connectionInfo: any = activeSession.map(session => ({
vendor: session?.fabric?.rootVendorId,
connected: !!session.numberOfActiveSubscriptions,
label: session?.fabric?.label,
}));

fabric.forEach(fabric => {
if (!activeSession.find(session => session.fabric?.fabricId === fabric.fabricId)) {
connectionInfo.push({
vendor: fabric?.rootVendorId,
connected: false,
label: fabric?.label,
});
}
});

this.sendToGui({
uuid: this.parameters.uuid,
command: 'status',
data: 'connecting',
activeSession: toJson(activeSession),
fabric: toJson(fabric),
connectionInfo,
});
console.log('Device is already commissioned. Waiting for controllers to connect ...');
return BridgeStates.Commissioned;
}
}

async stop() {
async stop(): Promise<void> {
for (let d = 0; d < this.devices.length; d++) {
await this.devices[d].destroy();
}
Expand Down
12 changes: 6 additions & 6 deletions src/matter/StorageIoBroker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,10 @@ export class StorageIoBroker implements Storage {
this.createdKeys = {};
}

async initialize() {
async initialize(): Promise<void> {
let object;
try {
object = await this.adapter.getForeignObjectAsync(this.oid);
object = await this.adapter.getForeignObjectAsync(this.oid);
} catch (error) {
// create object
object = {
Expand All @@ -46,14 +46,14 @@ export class StorageIoBroker implements Storage {

// read all keys
const states = await this.adapter.getForeignStatesAsync(`${this.oid}.*`);
const len = this.oid.length + 1
const len = this.oid.length + 1;
for (const key in states) {
this.createdKeys[key] = true;
this.data[key.substring(len)] = fromJson(states[key].val as string);
}
}

async close() {
async close(): Promise<void> {
const keys = Object.keys(this.savingPromises);
if (keys.length) {
await Promise.all(keys.map(key => this.savingPromises[key]));
Expand All @@ -75,7 +75,7 @@ export class StorageIoBroker implements Storage {
return value as T;
}

saveKey(oid: string, value: string) {
saveKey(oid: string, value: string): void {
const index = this.savingNumber++;
if (this.savingNumber >= 0xFFFFFFFF) {
this.savingNumber = 1;
Expand Down Expand Up @@ -110,7 +110,7 @@ export class StorageIoBroker implements Storage {
}
}

deleteKey(oid: string) {
deleteKey(oid: string): void {
const index = this.savingNumber++;
if (this.createdKeys[oid]) {
if (this.savingNumber >= 0xFFFFFFFF) {
Expand Down
Loading

0 comments on commit 391216e

Please sign in to comment.