matterbridge 3.3.3-dev-20251015-706d832 → 3.3.3-dev-20251017-2e6040a

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -33,6 +33,8 @@ Advantages:
33
33
  - [frontend]: Added icon to open the cpu and memory usage in System Information panel.
34
34
  - [thermostat]: Added thermostatRunningState attribute. Thanks Ludovic BOUÉ (https://github.com/Luligu/matterbridge/pull/410).
35
35
  - [ElectricalPowerMeasurement]: Added createApparentElectricalPowerMeasurementClusterServer cluster helper. Thanks Ludovic BOUÉ (https://github.com/Luligu/matterbridge/pull/411).
36
+ - [DeviceEnergyManagementMode]: Added logic to set optOutState. Thanks Ludovic BOUÉ (https://github.com/Luligu/matterbridge-example-dynamic-platform/issues/34).
37
+ - [Thermostat]: Added provisional support for setActivePresetRequest. Thanks Ludovic BOUÉ (https://github.com/Luligu/matterbridge-example-dynamic-platform/issues/38).
36
38
 
37
39
  ### Changed
38
40
 
@@ -1,12 +1,13 @@
1
1
  import { AnsiLogger, BLUE, CYAN, db, debugStringify, er, wr } from 'node-ansi-logger';
2
2
  import { dev } from './matterbridgeTypes.js';
3
3
  import { BroadcastServer } from './broadcastServer.js';
4
+ import { hasParameter } from './utils/commandLine.js';
4
5
  export class DeviceManager {
5
6
  _devices = new Map();
6
7
  log;
7
8
  server;
8
9
  constructor() {
9
- this.log = new AnsiLogger({ logName: 'DeviceManager', logTimestampFormat: 4, logLevel: "info" });
10
+ this.log = new AnsiLogger({ logName: 'DeviceManager', logTimestampFormat: 4, logLevel: hasParameter('debug') ? "debug" : "info" });
10
11
  this.log.debug('Matterbridge device manager starting...');
11
12
  this.server = new BroadcastServer('devices', this.log);
12
13
  this.server.on('broadcast_message', this.msgHandler.bind(this));
package/dist/frontend.js CHANGED
@@ -35,7 +35,7 @@ export class Frontend extends EventEmitter {
35
35
  this.matterbridge = matterbridge;
36
36
  this.log = new AnsiLogger({ logName: 'Frontend', logTimestampFormat: 4, logLevel: hasParameter('debug') ? "debug" : "info" });
37
37
  this.log.logNameColor = '\x1b[38;5;97m';
38
- this.server = new BroadcastServer('plugins', this.log);
38
+ this.server = new BroadcastServer('frontend', this.log);
39
39
  this.server.on('broadcast_message', this.msgHandler.bind(this));
40
40
  }
41
41
  destroy() {
@@ -14,7 +14,7 @@ import { BasicInformationServer } from '@matter/main/behaviors/basic-information
14
14
  import { getParameter, getIntParameter, hasParameter } from './utils/commandLine.js';
15
15
  import { copyDirectory } from './utils/copyDirectory.js';
16
16
  import { createDirectory } from './utils/createDirectory.js';
17
- import { isValidString, parseVersionString, isValidNumber } from './utils/isvalid.js';
17
+ import { isValidString, parseVersionString, isValidNumber, isValidObject } from './utils/isvalid.js';
18
18
  import { formatMemoryUsage, formatOsUpTime } from './utils/network.js';
19
19
  import { withTimeout, waiter, wait } from './utils/wait.js';
20
20
  import { dev, MATTER_LOGGER_FILE, MATTER_STORAGE_NAME, MATTERBRIDGE_LOGGER_FILE, NODE_STORAGE_DIR, plg, typ } from './matterbridgeTypes.js';
@@ -1133,8 +1133,12 @@ export class Matterbridge extends EventEmitter {
1133
1133
  this.emit('cleanup_completed');
1134
1134
  }
1135
1135
  else {
1136
- if (!this.initialized)
1136
+ if (!this.initialized) {
1137
1137
  this.log.debug('Cleanup with instance not initialized...');
1138
+ this.frontend.destroy();
1139
+ this.plugins.destroy();
1140
+ this.devices.destroy();
1141
+ }
1138
1142
  if (this.hasCleanupStarted)
1139
1143
  this.log.debug('Cleanup already started...');
1140
1144
  }
@@ -1737,6 +1741,8 @@ export class Matterbridge extends EventEmitter {
1737
1741
  { cluster: 'Thermostat', attribute: 'occupiedCoolingSetpoint' },
1738
1742
  { cluster: 'Thermostat', attribute: 'occupiedHeatingSetpoint' },
1739
1743
  { cluster: 'Thermostat', attribute: 'systemMode' },
1744
+ { cluster: 'Thermostat', attribute: 'thermostatRunningMode' },
1745
+ { cluster: 'Thermostat', attribute: 'thermostatRunningState' },
1740
1746
  { cluster: 'WindowCovering', attribute: 'operationalStatus' },
1741
1747
  { cluster: 'WindowCovering', attribute: 'currentPositionLiftPercent100ths' },
1742
1748
  { cluster: 'DoorLock', attribute: 'lockState' },
@@ -1766,7 +1772,7 @@ export class Matterbridge extends EventEmitter {
1766
1772
  if (device.hasAttributeServer(sub.cluster, sub.attribute)) {
1767
1773
  this.log.debug(`Subscribing to endpoint ${or}${device.id}${db}:${or}${device.number}${db} attribute ${dev}${sub.cluster}${db}.${dev}${sub.attribute}${db} changes...`);
1768
1774
  await device.subscribeAttribute(sub.cluster, sub.attribute, (value) => {
1769
- this.log.debug(`Bridged endpoint ${or}${device.id}${db}:${or}${device.number}${db} attribute ${dev}${sub.cluster}${db}.${dev}${sub.attribute}${db} changed to ${CYAN}${value}${db}`);
1775
+ this.log.debug(`Bridged endpoint ${or}${device.id}${db}:${or}${device.number}${db} attribute ${dev}${sub.cluster}${db}.${dev}${sub.attribute}${db} changed to ${CYAN}${isValidObject(value) ? debugStringify(value) : value}${db}`);
1770
1776
  this.frontend.wssSendAttributeChangedMessage(device.plugin, device.serialNumber, device.uniqueId, device.number, device.id, sub.cluster, sub.attribute, value);
1771
1777
  });
1772
1778
  }
@@ -1774,7 +1780,7 @@ export class Matterbridge extends EventEmitter {
1774
1780
  if (child.hasAttributeServer(sub.cluster, sub.attribute)) {
1775
1781
  this.log.debug(`Subscribing to child endpoint ${or}${child.id}${db}:${or}${child.number}${db} attribute ${dev}${sub.cluster}${db}.${dev}${sub.attribute}${db} changes...`);
1776
1782
  await child.subscribeAttribute(sub.cluster, sub.attribute, (value) => {
1777
- this.log.debug(`Bridged child endpoint ${or}${child.id}${db}:${or}${child.number}${db} attribute ${dev}${sub.cluster}${db}.${dev}${sub.attribute}${db} changed to ${CYAN}${value}${db}`);
1783
+ this.log.debug(`Bridged child endpoint ${or}${child.id}${db}:${or}${child.number}${db} attribute ${dev}${sub.cluster}${db}.${dev}${sub.attribute}${db} changed to ${CYAN}${isValidObject(value) ? debugStringify(value) : value}${db}`);
1778
1784
  this.frontend.wssSendAttributeChangedMessage(device.plugin, device.serialNumber, device.uniqueId, child.number, child.id, sub.cluster, sub.attribute, value);
1779
1785
  });
1780
1786
  }
@@ -1,21 +1,23 @@
1
1
  import { Behavior } from '@matter/main';
2
- import { BooleanStateConfiguration } from '@matter/main/clusters/boolean-state-configuration';
3
- import { ColorControl } from '@matter/main/clusters/color-control';
4
- import { FanControl } from '@matter/main/clusters/fan-control';
5
- import { WindowCovering } from '@matter/main/clusters/window-covering';
6
- import { Thermostat } from '@matter/main/clusters/thermostat';
7
- import { ValveConfigurationAndControl } from '@matter/main/clusters/valve-configuration-and-control';
8
- import { SmokeCoAlarm } from '@matter/main/clusters/smoke-co-alarm';
9
- import { BooleanStateConfigurationServer } from '@matter/main/behaviors/boolean-state-configuration';
10
- import { OperationalState } from '@matter/main/clusters/operational-state';
11
- import { ModeBase } from '@matter/main/clusters/mode-base';
12
- import { ServiceArea } from '@matter/main/clusters/service-area';
2
+ import { BooleanStateConfiguration } from '@matter/types/clusters/boolean-state-configuration';
3
+ import { ColorControl } from '@matter/types/clusters/color-control';
4
+ import { FanControl } from '@matter/types/clusters/fan-control';
5
+ import { WindowCovering } from '@matter/types/clusters/window-covering';
6
+ import { Thermostat } from '@matter/types/clusters/thermostat';
7
+ import { ValveConfigurationAndControl } from '@matter/types/clusters/valve-configuration-and-control';
8
+ import { SmokeCoAlarm } from '@matter/types/clusters/smoke-co-alarm';
9
+ import { OperationalState } from '@matter/types/clusters/operational-state';
10
+ import { ModeBase } from '@matter/types/clusters/mode-base';
11
+ import { ServiceArea } from '@matter/types/clusters/service-area';
12
+ import { DeviceEnergyManagement } from '@matter/types/clusters/device-energy-management';
13
13
  import { ResourceMonitoring } from '@matter/types/clusters/resource-monitoring';
14
+ import { DeviceEnergyManagementMode } from '@matter/types/clusters/device-energy-management-mode';
14
15
  import { IdentifyServer } from '@matter/main/behaviors/identify';
15
16
  import { OnOffServer } from '@matter/main/behaviors/on-off';
16
17
  import { LevelControlServer } from '@matter/main/behaviors/level-control';
17
18
  import { ColorControlServer } from '@matter/main/behaviors/color-control';
18
19
  import { WindowCoveringServer } from '@matter/main/behaviors/window-covering';
20
+ import { BooleanStateConfigurationServer } from '@matter/main/behaviors/boolean-state-configuration';
19
21
  import { DoorLockServer } from '@matter/main/behaviors/door-lock';
20
22
  import { FanControlServer } from '@matter/main/behaviors/fan-control';
21
23
  import { ThermostatServer } from '@matter/main/behaviors/thermostat';
@@ -321,6 +323,35 @@ export class MatterbridgeThermostatServer extends ThermostatServer.with(Thermost
321
323
  }
322
324
  }
323
325
  }
326
+ export class MatterbridgePresetThermostatServer extends ThermostatServer.with(Thermostat.Feature.Presets, Thermostat.Feature.Cooling, Thermostat.Feature.Heating, Thermostat.Feature.AutoMode) {
327
+ setpointRaiseLower(request) {
328
+ const device = this.endpoint.stateOf(MatterbridgeServer);
329
+ device.log.info(`Setting setpoint by ${request.amount} in mode ${request.mode} (endpoint ${this.endpoint.maybeId}.${this.endpoint.maybeNumber})`);
330
+ device.commandHandler.executeHandler('setpointRaiseLower', { request, cluster: ThermostatServer.id, attributes: this.state, endpoint: this.endpoint });
331
+ const lookupSetpointAdjustMode = ['Heat', 'Cool', 'Both'];
332
+ device.log.debug(`MatterbridgeThermostatServer: setpointRaiseLower called with mode: ${lookupSetpointAdjustMode[request.mode]} amount: ${request.amount / 10}`);
333
+ if (this.state.occupiedHeatingSetpoint !== undefined)
334
+ device.log.debug(`- current occupiedHeatingSetpoint: ${this.state.occupiedHeatingSetpoint / 100}`);
335
+ if (this.state.occupiedCoolingSetpoint !== undefined)
336
+ device.log.debug(`- current occupiedCoolingSetpoint: ${this.state.occupiedCoolingSetpoint / 100}`);
337
+ if ((request.mode === Thermostat.SetpointRaiseLowerMode.Heat || request.mode === Thermostat.SetpointRaiseLowerMode.Both) && this.state.occupiedHeatingSetpoint !== undefined) {
338
+ const setpoint = this.state.occupiedHeatingSetpoint / 100 + request.amount / 10;
339
+ this.state.occupiedHeatingSetpoint = setpoint * 100;
340
+ device.log.debug(`Set occupiedHeatingSetpoint to ${setpoint}`);
341
+ }
342
+ if ((request.mode === Thermostat.SetpointRaiseLowerMode.Cool || request.mode === Thermostat.SetpointRaiseLowerMode.Both) && this.state.occupiedCoolingSetpoint !== undefined) {
343
+ const setpoint = this.state.occupiedCoolingSetpoint / 100 + request.amount / 10;
344
+ this.state.occupiedCoolingSetpoint = setpoint * 100;
345
+ device.log.debug(`Set occupiedCoolingSetpoint to ${setpoint}`);
346
+ }
347
+ }
348
+ setActivePresetRequest(request) {
349
+ const device = this.endpoint.stateOf(MatterbridgeServer);
350
+ device.log.info(`Setting preset to ${request.presetHandle} (endpoint ${this.endpoint.maybeId}.${this.endpoint.maybeNumber})`);
351
+ device.commandHandler.executeHandler('setActivePresetRequest', { request, cluster: ThermostatServer.id, attributes: this.state, endpoint: this.endpoint });
352
+ device.log.debug(`MatterbridgePresetThermostatServer: setActivePresetRequest called with presetHandle: ${request.presetHandle}`);
353
+ }
354
+ }
324
355
  export class MatterbridgeValveConfigurationAndControlServer extends ValveConfigurationAndControlServer.with(ValveConfigurationAndControl.Feature.Level) {
325
356
  open(request) {
326
357
  const device = this.endpoint.stateOf(MatterbridgeServer);
@@ -467,7 +498,7 @@ export class MatterbridgeActivatedCarbonFilterMonitoringServer extends Activated
467
498
  device.log.debug(`MatterbridgeActivatedCarbonFilterMonitoringServer: resetCondition called`);
468
499
  }
469
500
  }
470
- export class MatterbridgeDeviceEnergyManagementServer extends DeviceEnergyManagementServer {
501
+ export class MatterbridgeDeviceEnergyManagementServer extends DeviceEnergyManagementServer.with(DeviceEnergyManagement.Feature.PowerForecastReporting, DeviceEnergyManagement.Feature.PowerAdjustment) {
471
502
  powerAdjustRequest(request) {
472
503
  const device = this.endpoint.stateOf(MatterbridgeServer);
473
504
  device.log.info(`Adjusting power to ${request.power} duration ${request.duration} cause ${request.cause} (endpoint ${this.endpoint.maybeId}.${this.endpoint.maybeNumber})`);
@@ -492,6 +523,14 @@ export class MatterbridgeDeviceEnergyManagementModeServer extends DeviceEnergyMa
492
523
  return { status: ModeBase.ModeChangeStatus.UnsupportedMode, statusText: 'Unsupported mode' };
493
524
  }
494
525
  this.state.currentMode = request.newMode;
526
+ if (supported.modeTags.find((tag) => tag.value === DeviceEnergyManagementMode.ModeTag.NoOptimization)) {
527
+ if (this.endpoint.behaviors.has(DeviceEnergyManagementServer))
528
+ this.endpoint.setStateOf(DeviceEnergyManagementServer.with(DeviceEnergyManagement.Feature.PowerForecastReporting, DeviceEnergyManagement.Feature.PowerAdjustment), { optOutState: DeviceEnergyManagement.OptOutState.OptOut });
529
+ }
530
+ else {
531
+ if (this.endpoint.behaviors.has(DeviceEnergyManagementServer))
532
+ this.endpoint.setStateOf(DeviceEnergyManagementServer.with(DeviceEnergyManagement.Feature.PowerForecastReporting, DeviceEnergyManagement.Feature.PowerAdjustment), { optOutState: DeviceEnergyManagement.OptOutState.NoOptOut });
533
+ }
495
534
  device.log.debug(`MatterbridgeDeviceEnergyManagementModeServer changeToMode called with newMode ${request.newMode} => ${supported.label}`);
496
535
  return super.changeToMode(request);
497
536
  }
@@ -1157,13 +1157,14 @@ export class MatterbridgeEndpoint extends Endpoint {
1157
1157
  }
1158
1158
  createDefaultDeviceEnergyManagementClusterServer(esaType = DeviceEnergyManagement.EsaType.Other, esaCanGenerate = false, esaState = DeviceEnergyManagement.EsaState.Online, absMinPower = 0, absMaxPower = 0) {
1159
1159
  this.behaviors.require(MatterbridgeDeviceEnergyManagementServer.with(DeviceEnergyManagement.Feature.PowerForecastReporting, DeviceEnergyManagement.Feature.PowerAdjustment), {
1160
- forecast: null,
1161
- powerAdjustmentCapability: null,
1162
1160
  esaType,
1163
1161
  esaCanGenerate,
1164
1162
  esaState,
1165
1163
  absMinPower,
1166
1164
  absMaxPower,
1165
+ powerAdjustmentCapability: null,
1166
+ optOutState: DeviceEnergyManagement.OptOutState.NoOptOut,
1167
+ forecast: null,
1167
1168
  });
1168
1169
  return this;
1169
1170
  }