Skip to content

Commit

Permalink
Release 2.1.0
Browse files Browse the repository at this point in the history
  • Loading branch information
Luligu committed Jun 19, 2024
1 parent bfe112b commit 621aeec
Show file tree
Hide file tree
Showing 6 changed files with 62 additions and 48 deletions.
17 changes: 11 additions & 6 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,24 @@

All notable changes to this project will be documented in this file.

## [2.0.18] - 2024-06-17
## [2.1.0] - 2024-06-19

### Added

- [dependencies]: Update dependencies
- [schema]: Added schema to the root directory of the plugin
- [properties]: Added soil_moisture property as humidity sensor
- [properties]: Added transition if the zigbee device supports it and the controller sends it. You can disable this globally adding transition to the featureBlackList or only for the single device adding transition to the deviceFeatureBlackList. (Thanks Stefan Schweiger)
- [dependencies]: Update dependencies.
- [schema]: Added schema to the root directory of the plugin.
- [z2m]: Added soil_moisture property as humidity sensor.
- [z2m]: Added transition if the zigbee device supports it and the controller sends it. You can disable this globally adding transition to the featureBlackList or only for the single device adding transition to the deviceFeatureBlackList. (Thanks Stefan Schweiger).

### Changed

- [matter]: Removed PowerSourceConfiguration cluster that is deprecated in Matter 1.3.

### Fixed

- [schema]: Username and password are no more required fields (Thanks Stefan Schweiger)
- [schema]: Username and password are no more required fields (Thanks Stefan Schweiger).
- [LevelControl]: Fixed the commandHandler for LevelControl in child endpoint (Thanks jpadie).
- [availability]: Fixed the issue that caused the availability event sent before the start to be ignored.

<a href="https://www.buymeacoffee.com/luligugithub">
<img src="./yellow-button.png" alt="Buy me a coffee" width="120">
Expand Down
20 changes: 10 additions & 10 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -85,14 +85,14 @@
},
"dependencies": {
"moment": "^2.30.1",
"mqtt": "^5.7.0",
"mqtt": "^5.7.1",
"node-ansi-logger": "^1.9.5"
},
"devDependencies": {
"@eslint/js": "^9.5.0",
"@types/eslint__js": "^8.42.3",
"@types/jest": "^29.5.12",
"@types/node": "^20.14.3",
"@types/node": "^20.14.5",
"eslint-config-prettier": "^9.1.0",
"eslint-plugin-jest": "^28.6.0",
"eslint-plugin-prettier": "^5.1.3",
Expand Down
26 changes: 15 additions & 11 deletions src/entity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ import {
DoorLockCluster,
AttributeInitialValues,
powerSource,
bridgedNode,
} from 'matterbridge';

import { AnsiLogger, TimestampFormat, gn, dn, ign, idn, rs, db, wr, debugStringify, hk, zb, or, nf } from 'node-ansi-logger';
Expand Down Expand Up @@ -393,7 +394,7 @@ export class ZigbeeGroup extends ZigbeeEntity {
maxColorTemperature = Math.max(maxColorTemperature, feature.value_max);
}
});
this.log.info(`Group: ${gn}${group.friendly_name}${rs} state: ${useState} brightness: ${useBrightness} color: ${useColor} color_temp: ${useColorTemperature}-${minColorTemperature}-${maxColorTemperature}`);
this.log.debug(`Group: ${gn}${group.friendly_name}${rs}${db} state: ${useState} brightness: ${useBrightness} color: ${useColor} color_temp: ${useColorTemperature}-${minColorTemperature}-${maxColorTemperature}`);
let deviceType = DeviceTypes.ON_OFF_LIGHT;
if (useBrightness) deviceType = DeviceTypes.ON_OFF_LIGHT;
if (useColorTemperature || useColor) deviceType = DeviceTypes.COLOR_TEMPERATURE_LIGHT;
Expand Down Expand Up @@ -541,7 +542,7 @@ export class ZigbeeDevice extends ZigbeeEntity {
this.bridgedDevice.isRouter = true;
}

const debugEnabled = this.platform.debugEnabled;
// const debugEnabled = this.platform.debugEnabled;
// this.log.setLogDebug(true);

// Get types and properties
Expand Down Expand Up @@ -629,11 +630,11 @@ export class ZigbeeDevice extends ZigbeeEntity {
if (z2m) {
this.log.debug(`Device ${this.ien}${device.friendly_name}${rs}${db} endpoint: ${zb}${endpoint}${db} type: ${zb}${type}${db} property: ${zb}${name}${db} => deviceType: ${z2m.deviceType?.name} cluster: ${z2m.cluster} attribute: ${z2m.attribute}`);
if (endpoint === '') {
if (!this.bridgedDevice) this.bridgedDevice = new BridgedBaseDevice(this, [DeviceTypes.BRIDGED_DEVICE_WITH_POWERSOURCE_INFO], [...z2m.deviceType.requiredServerClusters, ClusterId(z2m.cluster)]);
if (!this.bridgedDevice) this.bridgedDevice = new BridgedBaseDevice(this, [z2m.deviceType], [...z2m.deviceType.requiredServerClusters, ClusterId(z2m.cluster)]);
else this.bridgedDevice.addDeviceTypeAndClusterServer(z2m.deviceType, [...z2m.deviceType.requiredServerClusters, ClusterId(z2m.cluster)]);
if (type !== '') this.bridgedDevice.addFixedLabel('type', type);
} else {
if (!this.bridgedDevice) this.bridgedDevice = new BridgedBaseDevice(this, [DeviceTypes.BRIDGED_DEVICE_WITH_POWERSOURCE_INFO]);
if (!this.bridgedDevice) this.bridgedDevice = new BridgedBaseDevice(this, [bridgedNode]);
const childEndpoint = this.bridgedDevice.addChildDeviceTypeAndClusterServer(endpoint, z2m.deviceType, [...z2m.deviceType.requiredServerClusters, ClusterId(z2m.cluster)]);
if (type !== '') childEndpoint.addFixedLabel('type', type);
this.bridgedDevice.addFixedLabel('composed', type);
Expand All @@ -642,7 +643,7 @@ export class ZigbeeDevice extends ZigbeeEntity {
if (name === 'action' && this.actions.length) {
this.log.info(`Device ${this.ien}${device.friendly_name}${rs}${nf} has actions mapped to these switches on sub endpoints:`);
this.log.info(' controller events <=> zigbee2mqtt actions');
if (!this.bridgedDevice) this.bridgedDevice = new BridgedBaseDevice(this, [DeviceTypes.BRIDGED_DEVICE_WITH_POWERSOURCE_INFO], []);
if (!this.bridgedDevice) this.bridgedDevice = new BridgedBaseDevice(this, [bridgedNode]);
// Mapping actions
const switchMap = ['Single Press', 'Double Press', 'Long Press '];
let count = 1;
Expand All @@ -667,7 +668,7 @@ export class ZigbeeDevice extends ZigbeeEntity {
this.bridgedDevice.addFixedLabel('composed', 'button');
}
});
this.log.setLogDebug(debugEnabled);
// this.log.setLogDebug(debugEnabled);

/* Verify that all required server clusters are present in the main endpoint and in the child endpoints */
if (this.bridgedDevice) {
Expand Down Expand Up @@ -701,7 +702,7 @@ export class ZigbeeDevice extends ZigbeeEntity {

// Command handlers
this.bridgedDevice.addCommandHandler('identify', async (data) => {
this.log.warn(`Command identify called for ${this.ien}${device.friendly_name}${rs}${db} endpoint: ${data.endpoint.number} request identifyTime:${data.request.identifyTime} identifyTime:${data.attributes.identifyTime.getLocal()} identifyType:${data.attributes.identifyType.getLocal()} `);
this.log.debug(`Command identify called for ${this.ien}${device.friendly_name}${rs}${db} endpoint: ${data.endpoint.number} request identifyTime:${data.request.identifyTime} identifyTime:${data.attributes.identifyTime.getLocal()} identifyType:${data.attributes.identifyType.getLocal()} `);
// logEndpoint(this.bridgedDevice!);
});
if (this.bridgedDevice.hasClusterServer(OnOff.Complete) || this.bridgedDevice.hasEndpoints) {
Expand Down Expand Up @@ -906,8 +907,8 @@ export class BridgedBaseDevice extends MatterbridgeDevice {
});
this.addDeviceClusterServer(includeServerList);

// Add BridgedDevice with PowerSourceInformation device type
this.addDeviceType(DeviceTypes.BRIDGED_DEVICE_WITH_POWERSOURCE_INFO);
// Add BridgedDevice device type
this.addDeviceType(bridgedNode);

// Add BridgedDeviceBasicInformation cluster
if (entity.isDevice && entity.device && entity.device.friendly_name === 'Coordinator') {
Expand All @@ -918,16 +919,19 @@ export class BridgedBaseDevice extends MatterbridgeDevice {
this.addBridgedDeviceBasicInformationCluster(entity.group.friendly_name, 'zigbee2MQTT', 'Group', `group-${entity.group.id}`);
}

// Add BridgedDevice device type
this.addDeviceType(powerSource);

// Add PowerSource cluster
this.createDefaultPowerSourceConfigurationClusterServer(); // TODO remove this cause is deprecated in Matter 1.3
// this.createDefaultPowerSourceConfigurationClusterServer(); // TODO remove this cause is deprecated in Matter 1.3
if (entity.isDevice) {
if (entity.device?.power_source === 'Battery') this.createDefaultPowerSourceReplaceableBatteryClusterServer(100, PowerSource.BatChargeLevel.Ok);
else this.createDefaultPowerSourceWiredClusterServer();
} else if (entity.isGroup) {
this.createDefaultPowerSourceWiredClusterServer();
}

// Add all other client clusters in the includelist
// Add all other client clusters in the includelist (not supported by matter.js)
this.addDeviceClusterClient(includeClientList);
}

Expand Down
37 changes: 19 additions & 18 deletions src/platform.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
* @file platform.ts
* @author Luca Liguori
* @date 2023-12-29
* @version 2.0.4
* @version 2.1.1
*
* Copyright 2023, 2024 Luca Liguori.
*
Expand Down Expand Up @@ -67,6 +67,8 @@ export class ZigbeePlatform extends MatterbridgeDynamicPlatform {
private z2mBridgeInfo: BridgeInfo | undefined;
private z2mBridgeDevices: BridgeDevice[] | undefined;
private z2mBridgeGroups: BridgeGroup[] | undefined;
private z2mDeviceAvailability = new Map<string, boolean>();
private availabilityTimer: NodeJS.Timeout | undefined;

constructor(matterbridge: Matterbridge, log: AnsiLogger, config: PlatformConfig) {
super(matterbridge, log, config);
Expand Down Expand Up @@ -143,6 +145,12 @@ export class ZigbeePlatform extends MatterbridgeDynamicPlatform {
this.updateAvailability(false);
});

this.z2m.on('availability', (device: string, available: boolean) => {
this.z2mDeviceAvailability.set(device, available);
if (available) this.log.info(`zigbee2MQTT device ${device} is ${available ? 'online' : 'offline'}`);
else this.log.warn(`zigbee2MQTT device ${device} is ${available ? 'online' : 'offline'}`);
});

this.z2m.on('permit_join', async (device: string, time: number, status: boolean) => {
this.log.info(`zigbee2MQTT sent permit_join device: ${device} time: ${time} status: ${status}`);
for (const zigbeeEntity of this.zigbeeEntities) {
Expand Down Expand Up @@ -366,6 +374,13 @@ export class ZigbeePlatform extends MatterbridgeDynamicPlatform {
device.configure();
}

this.availabilityTimer = setTimeout(() => {
for (const [device, available] of this.z2mDeviceAvailability) {
if (available) this.z2m.emit('ONLINE-' + device);
else this.z2m.emit('OFFLINE-' + device);
}
}, 60 * 1000);

if (this.config.injectPayloads) {
this.injectTimer = setInterval(() => {
const data = this.z2m.readConfig(path.join(this.matterbridge.matterbridgeDirectory, this.config.injectPayloads as string));
Expand All @@ -380,6 +395,9 @@ export class ZigbeePlatform extends MatterbridgeDynamicPlatform {
override async onShutdown(reason?: string) {
this.log.debug('Shutting down zigbee2mqtt platform: ' + reason);
if (this.injectTimer) clearInterval(this.injectTimer);
this.injectTimer = undefined;
if (this.availabilityTimer) clearInterval(this.availabilityTimer);
this.availabilityTimer = undefined;
// this.updateAvailability(false);
if (this.config.unregisterOnShutdown === true) await this.unregisterAllDevices();
this.z2m.stop();
Expand Down Expand Up @@ -482,23 +500,6 @@ export class ZigbeePlatform extends MatterbridgeDynamicPlatform {
return matterGroup;
}

/*
public async unregisterAll() {
this.log.warn(`Unregistering ${this.bridgedEntities.length} accessories`);
for (const bridgedDevice of this.bridgedDevices) {
this.log.warn(`- ${bridgedDevice.deviceName} ${bridgedDevice.id} (${bridgedDevice.name})`);
this.matterAggregator?.removeBridgedDevice(bridgedDevice);
}
this.bridgedDevices.splice(0);
for (const bridgedEntity of this.bridgedEntities) {
this.log.warn(`- ${bridgedEntity.bridgedDevice?.deviceName} ${bridgedEntity.bridgedDevice?.id} (${bridgedEntity.bridgedDevice?.name})`);
await this.unregisterDevice(bridgedEntity.bridgedDevice as unknown as MatterbridgeDevice);
}
this.bridgedEntities.splice(0);
}
*/
private async unregisterZigbeeEntity(friendly_name: string) {
/*
for (const zigbeeEntity of this.zigbeeEntities) {
Expand Down
6 changes: 5 additions & 1 deletion src/zigbee2mqtt.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
* @file zigbee2mqtt.ts
* @author Luca Liguori
* @date 2023-06-30
* @version 2.2.27
* @version 2.2.28
*
* Copyright 2023, 2024 Luca Liguori.
*
Expand Down Expand Up @@ -866,10 +866,12 @@ export class Zigbee2MQTT extends EventEmitter {
this.z2mDevices[deviceIndex].isAvailabilityEnabled = true;
this.z2mDevices[deviceIndex].isOnline = true;
// this.log.warn('handleDeviceMessage availability payload: ', data);
this.emit('availability', entity, true);
this.emit('ONLINE-' + entity);
} else if (data.state === 'offline') {
this.z2mDevices[deviceIndex].isOnline = false;
// this.log.warn('handleDeviceMessage availability payload: ', data);
this.emit('availability', entity, false);
this.emit('OFFLINE-' + entity);
}
} else if (service === 'get') {
Expand Down Expand Up @@ -902,9 +904,11 @@ export class Zigbee2MQTT extends EventEmitter {
if (data.state === 'online') {
this.z2mGroups[groupIndex].isAvailabilityEnabled = true;
this.z2mGroups[groupIndex].isOnline = true;
this.emit('availability', entity, true);
this.emit('ONLINE-' + entity);
} else if (data.state === 'offline') {
this.z2mGroups[groupIndex].isOnline = false;
this.emit('availability', entity, false);
this.emit('OFFLINE-' + entity);
}
} else if (service === 'get') {
Expand Down

0 comments on commit 621aeec

Please sign in to comment.