@matterbridge/core 3.10.5-dev-20260808-d361317 → 3.10.5-dev-20260809-30130bb

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.
@@ -6,15 +6,17 @@ declare const MatterbridgeThermostatServer_base: import("@matter/node").ClusterB
6
6
  events: false;
7
7
  heating: true;
8
8
  localTemperatureNotExposed: false;
9
- matterScheduleConfiguration: false;
9
+ matterScheduleConfiguration: true;
10
10
  occupancy: false;
11
11
  presets: true;
12
12
  setback: false;
13
- thermostatSuggestions: false;
13
+ thermostatSuggestions: true;
14
14
  }>, import("@matter/types").ClusterType.Concrete, typeof import("@matter/node/behaviors/thermostat").ThermostatBaseServer.Internal, "thermostat">;
15
15
  export declare class MatterbridgeThermostatServer extends MatterbridgeThermostatServer_base {
16
- initialize(): Promise<void>;
17
16
  setpointRaiseLower(request: Thermostat.SetpointRaiseLowerRequest): Promise<void>;
18
17
  setActivePresetRequest(request: Thermostat.SetActivePresetRequest): Promise<void>;
18
+ setActiveScheduleRequest(request: Thermostat.SetActiveScheduleRequest): Promise<void>;
19
+ addThermostatSuggestion(request: Thermostat.AddThermostatSuggestionRequest): Promise<Thermostat.AddThermostatSuggestionResponse>;
20
+ removeThermostatSuggestion(request: Thermostat.RemoveThermostatSuggestionRequest): Promise<void>;
19
21
  }
20
22
  export {};
@@ -1,18 +1,9 @@
1
+ import { Bytes } from '@matter/general';
1
2
  import { ThermostatServer } from '@matter/node/behaviors/thermostat';
3
+ import { StatusResponse } from '@matter/types';
2
4
  import { Thermostat } from '@matter/types/clusters/thermostat';
3
5
  import { MatterbridgeServer } from './matterbridgeServer.js';
4
- export class MatterbridgeThermostatServer extends ThermostatServer.with(Thermostat.Feature.Cooling, Thermostat.Feature.Heating, Thermostat.Feature.AutoMode, Thermostat.Feature.Presets) {
5
- async initialize() {
6
- await super.initialize();
7
- this.endpoint.construction.onSuccess(async () => {
8
- const device = this.endpoint.stateOf(MatterbridgeServer);
9
- device.log.debug(`Removing atomic commands (endpoint ${this.endpoint.maybeId}.${this.endpoint.maybeNumber})`);
10
- await this.endpoint.setStateOf(ThermostatServer, {
11
- acceptedCommandList: [0],
12
- generatedCommandList: [],
13
- });
14
- });
15
- }
6
+ export class MatterbridgeThermostatServer extends ThermostatServer.with(Thermostat.Feature.Cooling, Thermostat.Feature.Heating, Thermostat.Feature.AutoMode, Thermostat.Feature.Presets, Thermostat.Feature.MatterScheduleConfiguration, Thermostat.Feature.ThermostatSuggestions) {
16
7
  async setpointRaiseLower(request) {
17
8
  const device = this.endpoint.stateOf(MatterbridgeServer);
18
9
  device.log.info(`Setting setpoint by ${request.amount} in mode ${request.mode} (endpoint ${this.endpoint.maybeId}.${this.endpoint.maybeNumber})`);
@@ -45,4 +36,82 @@ export class MatterbridgeThermostatServer extends ThermostatServer.with(Thermost
45
36
  const activePresetHandle = this.state.activePresetHandle ? `0x${Buffer.from(this.state.activePresetHandle).toString('hex')}` : 'null';
46
37
  device.log.debug(`MatterbridgeThermostatServer: setActivePresetRequest completed with activePresetHandle: ${activePresetHandle} occupiedHeatingSetpoint: ${this.state.occupiedHeatingSetpoint} occupiedCoolingSetpoint: ${this.state.occupiedCoolingSetpoint}`);
47
38
  }
39
+ async setActiveScheduleRequest(request) {
40
+ const device = this.endpoint.stateOf(MatterbridgeServer);
41
+ const scheduleHandle = `0x${Buffer.from(request.scheduleHandle).toString('hex')}`;
42
+ device.log.info(`Setting schedule to ${scheduleHandle} (endpoint ${this.endpoint.maybeId}.${this.endpoint.maybeNumber})`);
43
+ await device.commandHandler.executeHandler('Thermostat.setActiveScheduleRequest', {
44
+ command: 'setActiveScheduleRequest',
45
+ request,
46
+ cluster: ThermostatServer.id,
47
+ attributes: this.state,
48
+ endpoint: this.endpoint,
49
+ context: this.context,
50
+ });
51
+ const schedule = this.state.schedules.find((s) => s.scheduleHandle !== null && Bytes.areEqual(s.scheduleHandle, request.scheduleHandle));
52
+ if (schedule === undefined) {
53
+ throw new StatusResponse.InvalidCommandError('Requested ScheduleHandle not found');
54
+ }
55
+ this.state.activeScheduleHandle = Uint8Array.from(request.scheduleHandle);
56
+ device.log.debug(`MatterbridgeThermostatServer: setActiveScheduleRequest completed with activeScheduleHandle: ${scheduleHandle}`);
57
+ }
58
+ async addThermostatSuggestion(request) {
59
+ const device = this.endpoint.stateOf(MatterbridgeServer);
60
+ const presetHandle = `0x${Buffer.from(request.presetHandle).toString('hex')}`;
61
+ device.log.info(`Adding thermostat suggestion for preset ${presetHandle} (endpoint ${this.endpoint.maybeId}.${this.endpoint.maybeNumber})`);
62
+ await device.commandHandler.executeHandler('Thermostat.addThermostatSuggestion', {
63
+ command: 'addThermostatSuggestion',
64
+ request,
65
+ cluster: ThermostatServer.id,
66
+ attributes: this.state,
67
+ endpoint: this.endpoint,
68
+ context: this.context,
69
+ });
70
+ if (this.state.presets.find((p) => p.presetHandle !== null && Bytes.areEqual(p.presetHandle, request.presetHandle)) === undefined) {
71
+ throw new StatusResponse.NotFoundError('Requested PresetHandle not found');
72
+ }
73
+ if (this.state.thermostatSuggestions.length >= this.state.maxThermostatSuggestions) {
74
+ throw new StatusResponse.ResourceExhaustedError('Maximum number of thermostat suggestions reached');
75
+ }
76
+ const currentTime = Math.floor(Date.now() / 1000);
77
+ const effectiveTime = request.effectiveTime ?? currentTime;
78
+ if (effectiveTime > currentTime + 24 * 60 * 60) {
79
+ throw new StatusResponse.InvalidCommandError('EffectiveTime cannot be more than 24 hours in the future');
80
+ }
81
+ const usedUniqueIds = new Set(this.state.thermostatSuggestions.map((s) => s.uniqueId));
82
+ let uniqueId = 0;
83
+ while (usedUniqueIds.has(uniqueId))
84
+ uniqueId++;
85
+ const suggestion = {
86
+ uniqueId,
87
+ presetHandle: Uint8Array.from(request.presetHandle),
88
+ effectiveTime,
89
+ expirationTime: effectiveTime + request.expirationInMinutes * 60,
90
+ };
91
+ this.state.thermostatSuggestions = [...this.state.thermostatSuggestions, suggestion];
92
+ device.log.debug(`MatterbridgeThermostatServer: addThermostatSuggestion completed with uniqueId: ${uniqueId}`);
93
+ return { uniqueId };
94
+ }
95
+ async removeThermostatSuggestion(request) {
96
+ const device = this.endpoint.stateOf(MatterbridgeServer);
97
+ device.log.info(`Removing thermostat suggestion ${request.uniqueId} (endpoint ${this.endpoint.maybeId}.${this.endpoint.maybeNumber})`);
98
+ await device.commandHandler.executeHandler('Thermostat.removeThermostatSuggestion', {
99
+ command: 'removeThermostatSuggestion',
100
+ request,
101
+ cluster: ThermostatServer.id,
102
+ attributes: this.state,
103
+ endpoint: this.endpoint,
104
+ context: this.context,
105
+ });
106
+ const suggestion = this.state.thermostatSuggestions.find((s) => s.uniqueId === request.uniqueId);
107
+ if (suggestion === undefined) {
108
+ throw new StatusResponse.NotFoundError('Requested UniqueID not found');
109
+ }
110
+ this.state.thermostatSuggestions = this.state.thermostatSuggestions.filter((s) => s.uniqueId !== request.uniqueId);
111
+ if (this.state.currentThermostatSuggestion?.uniqueId === request.uniqueId) {
112
+ this.state.currentThermostatSuggestion = null;
113
+ this.state.thermostatSuggestionNotFollowingReason = null;
114
+ }
115
+ device.log.debug(`MatterbridgeThermostatServer: removeThermostatSuggestion completed for uniqueId: ${request.uniqueId}`);
116
+ }
48
117
  }
@@ -2,7 +2,7 @@ import { ClosureControlServer } from '@matter/node/behaviors/closure-control';
2
2
  import { ClosureControl } from '@matter/types/clusters/closure-control';
3
3
  import type { Semtag } from '@matter/types/globals';
4
4
  import { MatterbridgeEndpoint } from '../matterbridgeEndpoint.js';
5
- import { type ClosurePanelOptions } from './closurePanel.js';
5
+ import { type ClosureDimensionType, type ClosurePanelOptions } from './closurePanel.js';
6
6
  declare const MatterbridgeClosureControlServer_base: import("@matter/node").ClusterBehavior.Type<typeof ClosureControlServer, import("@matter/types").ClusterType.WithSupportedFeatures<ClosureControl, {
7
7
  calibration: false;
8
8
  instantaneous: false;
@@ -30,6 +30,6 @@ export interface ClosureOptions {
30
30
  export declare class Closure extends MatterbridgeEndpoint {
31
31
  constructor(name: string, serial: string, options?: ClosureOptions);
32
32
  getMainState(): ClosureControl.MainState | undefined;
33
- addPanel(name: string, tagList: Semtag[], options?: Pick<ClosurePanelOptions, 'currentState' | 'targetState' | 'resolution' | 'stepValue' | 'latchControlModes'>): MatterbridgeEndpoint;
33
+ addPanel(name: string, tagList: Semtag[], dimensionType: ClosureDimensionType, options?: Omit<ClosurePanelOptions, 'tagList'>): MatterbridgeEndpoint;
34
34
  }
35
35
  export {};
@@ -4,7 +4,7 @@ import { ThreeLevelAuto } from '@matter/types/globals';
4
4
  import { MatterbridgeServer } from '../behaviors/matterbridgeServer.js';
5
5
  import { closure, closurePanel } from '../matterbridgeDeviceTypes.js';
6
6
  import { MatterbridgeEndpoint } from '../matterbridgeEndpoint.js';
7
- import { MatterbridgeClosureDimensionServer } from './closurePanel.js';
7
+ import { createClosureDimensionClusterServer } from './closurePanel.js';
8
8
  export class MatterbridgeClosureControlServer extends ClosureControlServer.with(ClosureControl.Feature.Positioning, ClosureControl.Feature.MotionLatching, ClosureControl.Feature.Speed) {
9
9
  moveTo = async (request) => {
10
10
  const device = this.endpoint.stateOf(MatterbridgeServer);
@@ -21,10 +21,15 @@ export class MatterbridgeClosureControlServer extends ClosureControlServer.with(
21
21
  ...previousTarget,
22
22
  ...(request?.position !== undefined ? { position: request.position } : null),
23
23
  ...(request?.latch !== undefined ? { latch: request.latch } : null),
24
- speed: request?.speed ?? previousTarget.speed ?? ThreeLevelAuto.Auto,
24
+ speed: request?.speed ?? ThreeLevelAuto.Auto,
25
25
  };
26
26
  this.state.overallTargetState = nextTarget;
27
- this.state.mainState = ClosureControl.MainState.Moving;
27
+ const currentState = this.state.overallCurrentState;
28
+ const isAtTarget = currentState !== null &&
29
+ (nextTarget.position === undefined || nextTarget.position === currentState.position) &&
30
+ (nextTarget.latch === undefined || nextTarget.latch === currentState.latch) &&
31
+ nextTarget.speed === currentState.speed;
32
+ this.state.mainState = isAtTarget ? ClosureControl.MainState.Stopped : ClosureControl.MainState.Moving;
28
33
  };
29
34
  stop = async () => {
30
35
  const device = this.endpoint.stateOf(MatterbridgeServer);
@@ -65,15 +70,9 @@ export class Closure extends MatterbridgeEndpoint {
65
70
  getMainState() {
66
71
  return this.getAttribute(ClosureControlServer, 'mainState');
67
72
  }
68
- addPanel(name, tagList, options = {}) {
73
+ addPanel(name, tagList, dimensionType, options = {}) {
69
74
  const panel = this.addChildDeviceType(name, closurePanel, { tagList });
70
- panel.behaviors.require(MatterbridgeClosureDimensionServer, {
71
- currentState: options.currentState ?? { position: 0, latch: true, speed: ThreeLevelAuto.Auto },
72
- targetState: options.targetState ?? { position: 0, latch: true, speed: ThreeLevelAuto.Auto },
73
- resolution: options.resolution ?? 1,
74
- stepValue: options.stepValue ?? 1,
75
- latchControlModes: options.latchControlModes ?? { remoteLatching: true, remoteUnlatching: true },
76
- });
75
+ createClosureDimensionClusterServer(panel, dimensionType, options);
77
76
  return panel;
78
77
  }
79
78
  }
@@ -2,6 +2,7 @@ import { ClosureDimensionServer } from '@matter/node/behaviors/closure-dimension
2
2
  import { ClosureDimension } from '@matter/types/clusters/closure-dimension';
3
3
  import type { Semtag } from '@matter/types/globals';
4
4
  import { MatterbridgeEndpoint } from '../matterbridgeEndpoint.js';
5
+ export type ClosureDimensionType = 'lift' | 'tilt' | 'modulation';
5
6
  declare const MatterbridgeClosureDimensionServer_base: import("@matter/node").ClusterBehavior.Type<typeof ClosureDimensionServer, import("@matter/types").ClusterType.WithSupportedFeatures<ClosureDimension, {
6
7
  limitation: false;
7
8
  modulation: false;
@@ -22,9 +23,14 @@ export interface ClosurePanelOptions {
22
23
  resolution?: number;
23
24
  stepValue?: number;
24
25
  latchControlModes?: ClosureDimension.LatchControlModes;
26
+ translationDirection?: ClosureDimension.TranslationDirection;
27
+ rotationAxis?: ClosureDimension.RotationAxis;
28
+ overflow?: ClosureDimension.Overflow;
29
+ modulationType?: ClosureDimension.ModulationType;
25
30
  tagList?: Semtag[];
26
31
  }
27
32
  export declare class ClosurePanel extends MatterbridgeEndpoint {
28
- constructor(name: string, serial: string, options?: ClosurePanelOptions);
33
+ constructor(name: string, serial: string, dimensionType: ClosureDimensionType, options?: ClosurePanelOptions);
29
34
  }
35
+ export declare function createClosureDimensionClusterServer(endpoint: MatterbridgeEndpoint, dimensionType: ClosureDimensionType, options: ClosurePanelOptions): MatterbridgeEndpoint;
30
36
  export {};
@@ -20,7 +20,7 @@ export class MatterbridgeClosureDimensionServer extends ClosureDimensionServer.w
20
20
  ...previousTarget,
21
21
  ...(request?.position !== undefined ? { position: request.position } : null),
22
22
  ...(request?.latch !== undefined ? { latch: request.latch } : null),
23
- speed: request?.speed ?? previousTarget.speed ?? ThreeLevelAuto.Auto,
23
+ speed: request?.speed ?? ThreeLevelAuto.Auto,
24
24
  };
25
25
  this.state.targetState = nextTarget;
26
26
  };
@@ -53,15 +53,38 @@ export class MatterbridgeClosureDimensionServer extends ClosureDimensionServer.w
53
53
  };
54
54
  }
55
55
  export class ClosurePanel extends MatterbridgeEndpoint {
56
- constructor(name, serial, options = {}) {
56
+ constructor(name, serial, dimensionType, options = {}) {
57
57
  super([closurePanel], { id: `${name.replaceAll(' ', '')}-${serial.replaceAll(' ', '')}`, tagList: options.tagList });
58
58
  this.createDefaultBasicInformationClusterServer(name, serial, 0xfff1, 'Matterbridge', 0x8000, 'Matterbridge Closure Panel');
59
- this.behaviors.require(MatterbridgeClosureDimensionServer, {
60
- currentState: options.currentState ?? { position: 0, latch: true, speed: ThreeLevelAuto.Auto },
61
- targetState: options.targetState ?? { position: 0, latch: true, speed: ThreeLevelAuto.Auto },
62
- resolution: options.resolution ?? 1,
63
- stepValue: options.stepValue ?? 1,
64
- latchControlModes: options.latchControlModes ?? { remoteLatching: true, remoteUnlatching: true },
59
+ createClosureDimensionClusterServer(this, dimensionType, options);
60
+ }
61
+ }
62
+ export function createClosureDimensionClusterServer(endpoint, dimensionType, options) {
63
+ const commonOptions = {
64
+ currentState: options.currentState ?? { position: 0, latch: true, speed: ThreeLevelAuto.Auto },
65
+ targetState: options.targetState ?? { position: 0, latch: true, speed: ThreeLevelAuto.Auto },
66
+ resolution: options.resolution ?? 1,
67
+ stepValue: options.stepValue ?? 1,
68
+ latchControlModes: options.latchControlModes ?? { remoteLatching: true, remoteUnlatching: true },
69
+ };
70
+ if (dimensionType === 'lift') {
71
+ endpoint.behaviors.require(MatterbridgeClosureDimensionServer.with(ClosureDimension.Feature.Positioning, ClosureDimension.Feature.MotionLatching, ClosureDimension.Feature.Speed, ClosureDimension.Feature.Translation), {
72
+ ...commonOptions,
73
+ translationDirection: options.translationDirection ?? ClosureDimension.TranslationDirection.Downward,
74
+ });
75
+ }
76
+ else if (dimensionType === 'tilt') {
77
+ endpoint.behaviors.require(MatterbridgeClosureDimensionServer.with(ClosureDimension.Feature.Positioning, ClosureDimension.Feature.MotionLatching, ClosureDimension.Feature.Speed, ClosureDimension.Feature.Rotation), {
78
+ ...commonOptions,
79
+ rotationAxis: options.rotationAxis ?? ClosureDimension.RotationAxis.CenteredHorizontal,
80
+ overflow: options.overflow ?? ClosureDimension.Overflow.NoOverflow,
81
+ });
82
+ }
83
+ else {
84
+ endpoint.behaviors.require(MatterbridgeClosureDimensionServer.with(ClosureDimension.Feature.Positioning, ClosureDimension.Feature.MotionLatching, ClosureDimension.Feature.Speed, ClosureDimension.Feature.Modulation), {
85
+ ...commonOptions,
86
+ modulationType: options.modulationType ?? ClosureDimension.ModulationType.SlatsOrientation,
65
87
  });
66
88
  }
89
+ return endpoint;
67
90
  }
@@ -2,7 +2,6 @@ import { OperationalState } from '@matter/types/clusters/operational-state';
2
2
  import type { Semtag } from '@matter/types/globals';
3
3
  import { MatterbridgeEndpoint } from '../matterbridgeEndpoint.js';
4
4
  export interface IrrigationSystemOptions {
5
- singleZone?: boolean;
6
5
  batteryPowered?: boolean;
7
6
  operationalState?: OperationalState.OperationalStateEnum;
8
7
  flowMeasuredValue?: number | null;
@@ -5,8 +5,7 @@ import { MatterbridgeEndpoint } from '../matterbridgeEndpoint.js';
5
5
  import { getSemtag } from '../matterbridgeEndpointHelpers.js';
6
6
  export class IrrigationSystem extends MatterbridgeEndpoint {
7
7
  constructor(name, serial, options = {}) {
8
- const deviceTypes = options.singleZone ? [irrigationSystem, waterValve, powerSource] : [irrigationSystem, powerSource];
9
- super(deviceTypes, { id: `${name.replaceAll(' ', '')}-${serial.replaceAll(' ', '')}` });
8
+ super([irrigationSystem, powerSource], { id: `${name.replaceAll(' ', '')}-${serial.replaceAll(' ', '')}` });
10
9
  this.createDefaultIdentifyClusterServer();
11
10
  this.createDefaultBasicInformationClusterServer(name, serial, 0xfff1, 'Matterbridge', 0x8000, 'Matterbridge Irrigation System');
12
11
  if (options.batteryPowered) {
@@ -18,9 +17,6 @@ export class IrrigationSystem extends MatterbridgeEndpoint {
18
17
  this.createDefaultOperationalStateClusterServer(options.operationalState ?? OperationalState.OperationalStateEnum.Stopped);
19
18
  if (options.flowMeasuredValue !== undefined)
20
19
  this.createDefaultFlowMeasurementClusterServer(options.flowMeasuredValue);
21
- if (options.singleZone) {
22
- this.createDefaultValveConfigurationAndControlClusterServer();
23
- }
24
20
  this.addRequiredClusterServers();
25
21
  }
26
22
  addZone(tag) {
@@ -3,14 +3,14 @@ import { MicrowaveOvenControl } from '@matter/types/clusters/microwave-oven-cont
3
3
  import { MicrowaveOvenMode } from '@matter/types/clusters/microwave-oven-mode';
4
4
  import { MatterbridgeEndpoint } from '../matterbridgeEndpoint.js';
5
5
  export declare class MicrowaveOven extends MatterbridgeEndpoint {
6
- constructor(name: string, serial: string, currentMode?: number, supportedModes?: MicrowaveOvenMode.ModeOption[], selectedWattIndex?: number, supportedWatts?: number[], cookTime?: number, maxCookTime?: number);
6
+ constructor(name: string, serial: string, currentMode?: number, supportedModes?: MicrowaveOvenMode.ModeOption[], powerSetting?: number, minPower?: number, maxPower?: number, powerStep?: number, cookTime?: number, maxCookTime?: number);
7
7
  createDefaultMicrowaveOvenModeClusterServer(currentMode: number, supportedModes: MicrowaveOvenMode.ModeOption[]): this;
8
- createDefaultMicrowaveOvenControlClusterServer(selectedWattIndex: number, supportedWatts: number[], cookTime: number, maxCookTime: number): this;
8
+ createDefaultMicrowaveOvenControlClusterServer(powerSetting: number, minPower: number, maxPower: number, powerStep: number, cookTime: number, maxCookTime: number): this;
9
9
  }
10
10
  declare const MatterbridgeMicrowaveOvenControlServer_base: import("@matter/node").ClusterBehavior.Type<typeof MicrowaveOvenControlServer, import("@matter/types").ClusterType.WithSupportedFeatures<MicrowaveOvenControl, {
11
- powerAsNumber: false;
12
- powerInWatts: true;
13
- powerNumberLimits: false;
11
+ powerAsNumber: true;
12
+ powerInWatts: false;
13
+ powerNumberLimits: true;
14
14
  }>, import("@matter/types").ClusterType.Concrete, new () => {}, "microwaveOvenControl">;
15
15
  export declare class MatterbridgeMicrowaveOvenControlServer extends MatterbridgeMicrowaveOvenControlServer_base {
16
16
  initialize(): Promise<void>;
@@ -16,14 +16,14 @@ export class MicrowaveOven extends MatterbridgeEndpoint {
16
16
  { label: 'Max', mode: 5, modeTags: [{ value: MicrowaveOvenMode.ModeTag.Max }] },
17
17
  { label: 'Normal', mode: 6, modeTags: [{ value: MicrowaveOvenMode.ModeTag.Normal }] },
18
18
  { label: 'Defrost', mode: 7, modeTags: [{ value: MicrowaveOvenMode.ModeTag.Defrost }] },
19
- ], selectedWattIndex = 5, supportedWatts = [100, 200, 300, 400, 500, 600, 700, 800, 900, 1000], cookTime = 60, maxCookTime = 3600) {
19
+ ], powerSetting = 100, minPower = 10, maxPower = 100, powerStep = 10, cookTime = 60, maxCookTime = 3600) {
20
20
  super([microwaveOven, powerSource], { id: `${name.replaceAll(' ', '')}-${serial.replaceAll(' ', '')}` });
21
21
  this.createDefaultIdentifyClusterServer();
22
22
  this.createDefaultBasicInformationClusterServer(name, serial, 0xfff1, 'Matterbridge', 0x8000, 'Microwave Oven');
23
23
  this.createDefaultPowerSourceWiredClusterServer();
24
24
  this.createDefaultOperationalStateClusterServer(OperationalState.OperationalStateEnum.Stopped);
25
25
  this.createDefaultMicrowaveOvenModeClusterServer(currentMode, supportedModes);
26
- this.createDefaultMicrowaveOvenControlClusterServer(selectedWattIndex, supportedWatts, cookTime, maxCookTime);
26
+ this.createDefaultMicrowaveOvenControlClusterServer(powerSetting, minPower, maxPower, powerStep, cookTime, maxCookTime);
27
27
  }
28
28
  createDefaultMicrowaveOvenModeClusterServer(currentMode, supportedModes) {
29
29
  this.behaviors.require(MicrowaveOvenModeServer, {
@@ -32,17 +32,19 @@ export class MicrowaveOven extends MatterbridgeEndpoint {
32
32
  });
33
33
  return this;
34
34
  }
35
- createDefaultMicrowaveOvenControlClusterServer(selectedWattIndex, supportedWatts, cookTime, maxCookTime) {
36
- this.behaviors.require(MatterbridgeMicrowaveOvenControlServer.with(MicrowaveOvenControl.Feature.PowerInWatts), {
37
- supportedWatts,
38
- selectedWattIndex,
35
+ createDefaultMicrowaveOvenControlClusterServer(powerSetting, minPower, maxPower, powerStep, cookTime, maxCookTime) {
36
+ this.behaviors.require(MatterbridgeMicrowaveOvenControlServer.with(MicrowaveOvenControl.Feature.PowerAsNumber, MicrowaveOvenControl.Feature.PowerNumberLimits), {
37
+ powerSetting,
38
+ minPower,
39
+ maxPower,
40
+ powerStep,
39
41
  cookTime,
40
42
  maxCookTime,
41
43
  });
42
44
  return this;
43
45
  }
44
46
  }
45
- export class MatterbridgeMicrowaveOvenControlServer extends MicrowaveOvenControlServer.with(MicrowaveOvenControl.Feature.PowerInWatts) {
47
+ export class MatterbridgeMicrowaveOvenControlServer extends MicrowaveOvenControlServer.with(MicrowaveOvenControl.Feature.PowerAsNumber, MicrowaveOvenControl.Feature.PowerNumberLimits) {
46
48
  async initialize() {
47
49
  await super.initialize();
48
50
  const device = this.endpoint.stateOf(MatterbridgeServer);
@@ -76,13 +78,13 @@ export class MatterbridgeMicrowaveOvenControlServer extends MicrowaveOvenControl
76
78
  device.log.info(`MatterbridgeMicrowaveOvenControlServer: setCookingParameters called with no cookTime so set to 30sec.`);
77
79
  this.state.cookTime = 30;
78
80
  }
79
- if (request.wattSettingIndex !== undefined && request.wattSettingIndex >= 0 && request.wattSettingIndex < this.state.supportedWatts.length) {
80
- device.log.info(`MatterbridgeMicrowaveOvenControlServer: setCookingParameters called setting selectedWattIndex to ${request.wattSettingIndex}`);
81
- this.state.selectedWattIndex = request.wattSettingIndex;
81
+ if (request.powerSetting !== undefined && request.powerSetting >= this.state.minPower && request.powerSetting <= this.state.maxPower) {
82
+ device.log.info(`MatterbridgeMicrowaveOvenControlServer: setCookingParameters called setting powerSetting to ${request.powerSetting}`);
83
+ this.state.powerSetting = request.powerSetting;
82
84
  }
83
85
  else {
84
- device.log.info(`MatterbridgeMicrowaveOvenControlServer: setCookingParameters called with no wattSettingIndex so set to the highest Watt setting for the selected CookMode`);
85
- this.state.selectedWattIndex = this.state.supportedWatts.length - 1;
86
+ device.log.info(`MatterbridgeMicrowaveOvenControlServer: setCookingParameters called with no powerSetting so set to maxPower`);
87
+ this.state.powerSetting = this.state.maxPower;
86
88
  }
87
89
  if (request.startAfterSetting === true) {
88
90
  device.log.info(`MatterbridgeMicrowaveOvenControlServer: setCookingParameters called setting startAfterSetting = true`);
@@ -174,7 +174,9 @@ export declare class MatterbridgeEndpoint extends Endpoint {
174
174
  setWindowCoveringTargetAndCurrentPosition(liftPosition: number, tiltPosition?: number): Promise<void>;
175
175
  createDefaultThermostatClusterServer(localTemperature?: number, occupiedHeatingSetpoint?: number, occupiedCoolingSetpoint?: number, minSetpointDeadBand?: number, minHeatSetpointLimit?: number, maxHeatSetpointLimit?: number, minCoolSetpointLimit?: number, maxCoolSetpointLimit?: number, unoccupiedHeatingSetpoint?: number, unoccupiedCoolingSetpoint?: number, occupied?: boolean, outdoorTemperature?: number | null): this;
176
176
  createDefaultHeatingThermostatClusterServer(localTemperature?: number, occupiedHeatingSetpoint?: number, minHeatSetpointLimit?: number, maxHeatSetpointLimit?: number, unoccupiedHeatingSetpoint?: number, occupied?: boolean, outdoorTemperature?: number | null): this;
177
- createDefaultPresetsThermostatClusterServer(localTemperature?: number, occupiedHeatingSetpoint?: number, occupiedCoolingSetpoint?: number, minSetpointDeadBand?: number, minHeatSetpointLimit?: number, maxHeatSetpointLimit?: number, minCoolSetpointLimit?: number, maxCoolSetpointLimit?: number, unoccupiedHeatingSetpoint?: number, unoccupiedCoolingSetpoint?: number, occupied?: boolean, outdoorTemperature?: number | null, activePresetHandle?: Uint8Array | null, presets?: Thermostat.Preset[], presetTypes?: Thermostat.PresetType[]): this;
177
+ createDefaultPresetsThermostatClusterServer(localTemperature?: number, occupiedHeatingSetpoint?: number, occupiedCoolingSetpoint?: number, minSetpointDeadBand?: number, minHeatSetpointLimit?: number, maxHeatSetpointLimit?: number, minCoolSetpointLimit?: number, maxCoolSetpointLimit?: number, unoccupiedHeatingSetpoint?: number, unoccupiedCoolingSetpoint?: number, occupied?: boolean, outdoorTemperature?: number | null, activePresetHandle?: Uint8Array | null, presets?: Thermostat.Preset[], presetTypes?: Thermostat.PresetType[] | null): this;
178
+ createDefaultSchedulesThermostatClusterServer(localTemperature?: number, occupiedHeatingSetpoint?: number, occupiedCoolingSetpoint?: number, minSetpointDeadBand?: number, minHeatSetpointLimit?: number, maxHeatSetpointLimit?: number, minCoolSetpointLimit?: number, maxCoolSetpointLimit?: number, unoccupiedHeatingSetpoint?: number, unoccupiedCoolingSetpoint?: number, occupied?: boolean, outdoorTemperature?: number | null, activeScheduleHandle?: Uint8Array | null, schedules?: Thermostat.Schedule[], scheduleTypes?: Thermostat.ScheduleType[], numberOfScheduleTransitions?: number, numberOfScheduleTransitionPerDay?: number | null): this;
179
+ createDefaultThermostatSuggestionsClusterServer(localTemperature?: number, occupiedHeatingSetpoint?: number, occupiedCoolingSetpoint?: number, minSetpointDeadBand?: number, minHeatSetpointLimit?: number, maxHeatSetpointLimit?: number, minCoolSetpointLimit?: number, maxCoolSetpointLimit?: number, unoccupiedHeatingSetpoint?: number, unoccupiedCoolingSetpoint?: number, occupied?: boolean, outdoorTemperature?: number | null, activePresetHandle?: Uint8Array | null, presets?: Thermostat.Preset[], presetTypes?: Thermostat.PresetType[] | null, thermostatSuggestions?: Thermostat.ThermostatSuggestion[], currentThermostatSuggestion?: Thermostat.ThermostatSuggestion | null, thermostatSuggestionNotFollowingReason?: Thermostat.ThermostatSuggestionNotFollowingReason | null): this;
178
180
  createDefaultCoolingThermostatClusterServer(localTemperature?: number, occupiedCoolingSetpoint?: number, minCoolSetpointLimit?: number, maxCoolSetpointLimit?: number, unoccupiedCoolingSetpoint?: number, occupied?: boolean, outdoorTemperature?: number | null): this;
179
181
  createDefaultThermostatUserInterfaceConfigurationClusterServer(temperatureDisplayMode?: ThermostatUserInterfaceConfiguration.TemperatureDisplayMode, keypadLockout?: ThermostatUserInterfaceConfiguration.KeypadLockout, scheduleProgrammingVisibility?: ThermostatUserInterfaceConfiguration.ScheduleProgrammingVisibility): this;
180
182
  createDefaultFanControlClusterServer(fanMode?: FanControl.FanMode, fanModeSequence?: FanControl.FanModeSequence, percentSetting?: number, percentCurrent?: number): this;
@@ -992,7 +992,7 @@ export class MatterbridgeEndpoint extends Endpoint {
992
992
  ...(occupied !== undefined ? { unoccupiedCoolingSetpoint: unoccupiedCoolingSetpoint !== undefined ? unoccupiedCoolingSetpoint * 100 : 2700 } : {}),
993
993
  ...(occupied !== undefined ? { occupancy: { occupied } } : {}),
994
994
  ...(occupied !== undefined ? { externallyMeasuredOccupancy: true } : {}),
995
- numberOfPresets: Math.max(Array.isArray(presets) ? presets.length : 0, 10),
995
+ numberOfPresets: Math.max(Array.isArray(presets) ? presets.length : 0, Array.isArray(presetTypes) ? Math.max(0, ...presetTypes.map((pt) => pt.numberOfPresets ?? 0)) : 0, 1),
996
996
  activePresetHandle: activePresetHandle ? Uint8Array.from([activePresetHandle]) : null,
997
997
  presets: (presets ?? []).map((p) => ({
998
998
  presetHandle: p.presetHandle ? Uint8Array.from(p.presetHandle) : null,
@@ -1010,6 +1010,115 @@ export class MatterbridgeEndpoint extends Endpoint {
1010
1010
  });
1011
1011
  return this;
1012
1012
  }
1013
+ createDefaultSchedulesThermostatClusterServer(localTemperature = 23, occupiedHeatingSetpoint = 21, occupiedCoolingSetpoint = 25, minSetpointDeadBand = 0, minHeatSetpointLimit = 0, maxHeatSetpointLimit = 50, minCoolSetpointLimit = 0, maxCoolSetpointLimit = 50, unoccupiedHeatingSetpoint, unoccupiedCoolingSetpoint, occupied, outdoorTemperature, activeScheduleHandle = null, schedules = [], scheduleTypes = [
1014
+ {
1015
+ systemMode: Thermostat.SystemMode.Auto,
1016
+ numberOfSchedules: 10,
1017
+ scheduleTypeFeatures: { supportsSetpoints: true, supportsNames: true, supportsPresets: false, supportsOff: false },
1018
+ },
1019
+ ], numberOfScheduleTransitions = 10, numberOfScheduleTransitionPerDay = null) {
1020
+ this.behaviors.require(MatterbridgeThermostatServer.with(Thermostat.Feature.Heating, Thermostat.Feature.Cooling, Thermostat.Feature.AutoMode, ...(occupied !== undefined ? [Thermostat.Feature.Occupancy] : []), Thermostat.Feature.MatterScheduleConfiguration), {
1021
+ localTemperature: localTemperature * 100,
1022
+ externalMeasuredIndoorTemperature: localTemperature * 100,
1023
+ ...(outdoorTemperature !== undefined ? { outdoorTemperature: outdoorTemperature !== null ? outdoorTemperature * 100 : outdoorTemperature } : {}),
1024
+ systemMode: Thermostat.SystemMode.Auto,
1025
+ controlSequenceOfOperation: Thermostat.ControlSequenceOfOperation.CoolingAndHeating,
1026
+ occupiedHeatingSetpoint: occupiedHeatingSetpoint * 100,
1027
+ minHeatSetpointLimit: minHeatSetpointLimit * 100,
1028
+ maxHeatSetpointLimit: maxHeatSetpointLimit * 100,
1029
+ absMinHeatSetpointLimit: minHeatSetpointLimit * 100,
1030
+ absMaxHeatSetpointLimit: maxHeatSetpointLimit * 100,
1031
+ occupiedCoolingSetpoint: occupiedCoolingSetpoint * 100,
1032
+ minCoolSetpointLimit: minCoolSetpointLimit * 100,
1033
+ maxCoolSetpointLimit: maxCoolSetpointLimit * 100,
1034
+ absMinCoolSetpointLimit: minCoolSetpointLimit * 100,
1035
+ absMaxCoolSetpointLimit: maxCoolSetpointLimit * 100,
1036
+ minSetpointDeadBand: minSetpointDeadBand * 10,
1037
+ thermostatRunningMode: Thermostat.ThermostatRunningMode.Off,
1038
+ ...(occupied !== undefined ? { unoccupiedHeatingSetpoint: unoccupiedHeatingSetpoint !== undefined ? unoccupiedHeatingSetpoint * 100 : 1900 } : {}),
1039
+ ...(occupied !== undefined ? { unoccupiedCoolingSetpoint: unoccupiedCoolingSetpoint !== undefined ? unoccupiedCoolingSetpoint * 100 : 2700 } : {}),
1040
+ ...(occupied !== undefined ? { occupancy: { occupied } } : {}),
1041
+ ...(occupied !== undefined ? { externallyMeasuredOccupancy: true } : {}),
1042
+ numberOfSchedules: Math.max(Array.isArray(schedules) ? schedules.length : 0, Array.isArray(scheduleTypes) ? Math.max(0, ...scheduleTypes.map((st) => st.numberOfSchedules ?? 0)) : 0, 1),
1043
+ numberOfScheduleTransitions,
1044
+ numberOfScheduleTransitionPerDay,
1045
+ activeScheduleHandle: activeScheduleHandle ? Uint8Array.from(activeScheduleHandle) : null,
1046
+ schedules: (schedules ?? []).map((s) => ({
1047
+ scheduleHandle: s.scheduleHandle ? Uint8Array.from(s.scheduleHandle) : null,
1048
+ systemMode: s.systemMode,
1049
+ name: s.name,
1050
+ presetHandle: s.presetHandle ? Uint8Array.from(s.presetHandle) : undefined,
1051
+ transitions: s.transitions ?? [],
1052
+ builtIn: s.builtIn ?? true,
1053
+ })),
1054
+ scheduleTypes: (scheduleTypes ?? []).map((st) => ({
1055
+ ...st,
1056
+ numberOfSchedules: st.numberOfSchedules ?? 0,
1057
+ scheduleTypeFeatures: st.scheduleTypeFeatures ?? { supportsSetpoints: true, supportsNames: true, supportsPresets: false, supportsOff: false },
1058
+ })),
1059
+ });
1060
+ return this;
1061
+ }
1062
+ createDefaultThermostatSuggestionsClusterServer(localTemperature = 23, occupiedHeatingSetpoint = 21, occupiedCoolingSetpoint = 25, minSetpointDeadBand = 0, minHeatSetpointLimit = 0, maxHeatSetpointLimit = 50, minCoolSetpointLimit = 0, maxCoolSetpointLimit = 50, unoccupiedHeatingSetpoint, unoccupiedCoolingSetpoint, occupied, outdoorTemperature, activePresetHandle = null, presets = [], presetTypes = [
1063
+ { presetScenario: Thermostat.PresetScenario.Occupied, numberOfPresets: 2, presetTypeFeatures: { automatic: false, supportsNames: true } },
1064
+ { presetScenario: Thermostat.PresetScenario.Unoccupied, numberOfPresets: 2, presetTypeFeatures: { automatic: false, supportsNames: true } },
1065
+ ], thermostatSuggestions = [], currentThermostatSuggestion = null, thermostatSuggestionNotFollowingReason = null) {
1066
+ this.behaviors.require(MatterbridgeThermostatServer.with(Thermostat.Feature.Heating, Thermostat.Feature.Cooling, Thermostat.Feature.AutoMode, ...(occupied !== undefined ? [Thermostat.Feature.Occupancy] : []), Thermostat.Feature.Presets, Thermostat.Feature.ThermostatSuggestions), {
1067
+ localTemperature: localTemperature * 100,
1068
+ externalMeasuredIndoorTemperature: localTemperature * 100,
1069
+ ...(outdoorTemperature !== undefined ? { outdoorTemperature: outdoorTemperature !== null ? outdoorTemperature * 100 : outdoorTemperature } : {}),
1070
+ systemMode: Thermostat.SystemMode.Auto,
1071
+ controlSequenceOfOperation: Thermostat.ControlSequenceOfOperation.CoolingAndHeating,
1072
+ occupiedHeatingSetpoint: occupiedHeatingSetpoint * 100,
1073
+ minHeatSetpointLimit: minHeatSetpointLimit * 100,
1074
+ maxHeatSetpointLimit: maxHeatSetpointLimit * 100,
1075
+ absMinHeatSetpointLimit: minHeatSetpointLimit * 100,
1076
+ absMaxHeatSetpointLimit: maxHeatSetpointLimit * 100,
1077
+ occupiedCoolingSetpoint: occupiedCoolingSetpoint * 100,
1078
+ minCoolSetpointLimit: minCoolSetpointLimit * 100,
1079
+ maxCoolSetpointLimit: maxCoolSetpointLimit * 100,
1080
+ absMinCoolSetpointLimit: minCoolSetpointLimit * 100,
1081
+ absMaxCoolSetpointLimit: maxCoolSetpointLimit * 100,
1082
+ minSetpointDeadBand: minSetpointDeadBand * 10,
1083
+ thermostatRunningMode: Thermostat.ThermostatRunningMode.Off,
1084
+ ...(occupied !== undefined ? { unoccupiedHeatingSetpoint: unoccupiedHeatingSetpoint !== undefined ? unoccupiedHeatingSetpoint * 100 : 1900 } : {}),
1085
+ ...(occupied !== undefined ? { unoccupiedCoolingSetpoint: unoccupiedCoolingSetpoint !== undefined ? unoccupiedCoolingSetpoint * 100 : 2700 } : {}),
1086
+ ...(occupied !== undefined ? { occupancy: { occupied } } : {}),
1087
+ ...(occupied !== undefined ? { externallyMeasuredOccupancy: true } : {}),
1088
+ numberOfPresets: Math.max(Array.isArray(presets) ? presets.length : 0, Array.isArray(presetTypes) ? Math.max(0, ...presetTypes.map((pt) => pt.numberOfPresets ?? 0)) : 0, 1),
1089
+ activePresetHandle: activePresetHandle ? Uint8Array.from(activePresetHandle) : null,
1090
+ presets: (presets ?? []).map((p) => ({
1091
+ presetHandle: p.presetHandle ? Uint8Array.from(p.presetHandle) : null,
1092
+ presetScenario: p.presetScenario,
1093
+ name: p.name,
1094
+ coolingSetpoint: p.coolingSetpoint,
1095
+ heatingSetpoint: p.heatingSetpoint,
1096
+ builtIn: p.builtIn ?? true,
1097
+ })),
1098
+ presetTypes: (presetTypes ?? []).map((pt) => ({
1099
+ ...pt,
1100
+ numberOfPresets: pt.numberOfPresets ?? 0,
1101
+ presetTypeFeatures: pt.presetTypeFeatures ?? { automatic: false, supportsNames: true },
1102
+ })),
1103
+ maxThermostatSuggestions: Math.max(Array.isArray(thermostatSuggestions) ? thermostatSuggestions.length : 0, 5),
1104
+ thermostatSuggestions: (thermostatSuggestions ?? []).map((s) => ({
1105
+ uniqueId: s.uniqueId,
1106
+ presetHandle: Uint8Array.from(s.presetHandle),
1107
+ effectiveTime: s.effectiveTime,
1108
+ expirationTime: s.expirationTime,
1109
+ })),
1110
+ currentThermostatSuggestion: currentThermostatSuggestion
1111
+ ? {
1112
+ uniqueId: currentThermostatSuggestion.uniqueId,
1113
+ presetHandle: Uint8Array.from(currentThermostatSuggestion.presetHandle),
1114
+ effectiveTime: currentThermostatSuggestion.effectiveTime,
1115
+ expirationTime: currentThermostatSuggestion.expirationTime,
1116
+ }
1117
+ : null,
1118
+ thermostatSuggestionNotFollowingReason: thermostatSuggestionNotFollowingReason ?? null,
1119
+ });
1120
+ return this;
1121
+ }
1013
1122
  createDefaultCoolingThermostatClusterServer(localTemperature = 23, occupiedCoolingSetpoint = 25, minCoolSetpointLimit = 0, maxCoolSetpointLimit = 50, unoccupiedCoolingSetpoint, occupied, outdoorTemperature) {
1014
1123
  this.behaviors.require(MatterbridgeThermostatServer.with(Thermostat.Feature.Cooling, ...(occupied !== undefined ? [Thermostat.Feature.Occupancy] : [])), {
1015
1124
  localTemperature: localTemperature * 100,
@@ -457,6 +457,9 @@ export type CommandHandlerDataMap = {
457
457
  };
458
458
  'setpointRaiseLower': CommandHandlerData<'Thermostat.setpointRaiseLower'>;
459
459
  'setActivePresetRequest': CommandHandlerData<'Thermostat.setActivePresetRequest'>;
460
+ 'setActiveScheduleRequest': CommandHandlerData<'Thermostat.setActiveScheduleRequest'>;
461
+ 'addThermostatSuggestion': CommandHandlerData<'Thermostat.addThermostatSuggestion'>;
462
+ 'removeThermostatSuggestion': CommandHandlerData<'Thermostat.removeThermostatSuggestion'>;
460
463
  'Thermostat.setpointRaiseLower': {
461
464
  command: 'setpointRaiseLower';
462
465
  request: Thermostat.SetpointRaiseLowerRequest;
@@ -471,6 +474,27 @@ export type CommandHandlerDataMap = {
471
474
  attributes: ClusterAttributeValues<(typeof Thermostat)['attributes']>;
472
475
  endpoint: MatterbridgeEndpoint;
473
476
  };
477
+ 'Thermostat.setActiveScheduleRequest': {
478
+ command: 'setActiveScheduleRequest';
479
+ request: Thermostat.SetActiveScheduleRequest;
480
+ cluster: 'thermostat';
481
+ attributes: ClusterAttributeValues<(typeof Thermostat)['attributes']>;
482
+ endpoint: MatterbridgeEndpoint;
483
+ };
484
+ 'Thermostat.addThermostatSuggestion': {
485
+ command: 'addThermostatSuggestion';
486
+ request: Thermostat.AddThermostatSuggestionRequest;
487
+ cluster: 'thermostat';
488
+ attributes: ClusterAttributeValues<(typeof Thermostat)['attributes']>;
489
+ endpoint: MatterbridgeEndpoint;
490
+ };
491
+ 'Thermostat.removeThermostatSuggestion': {
492
+ command: 'removeThermostatSuggestion';
493
+ request: Thermostat.RemoveThermostatSuggestionRequest;
494
+ cluster: 'thermostat';
495
+ attributes: ClusterAttributeValues<(typeof Thermostat)['attributes']>;
496
+ endpoint: MatterbridgeEndpoint;
497
+ };
474
498
  'step': CommandHandlerData<'FanControl.step'>;
475
499
  'FanControl.step': {
476
500
  command: 'step';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@matterbridge/core",
3
- "version": "3.10.5-dev-20260808-d361317",
3
+ "version": "3.10.5-dev-20260809-30130bb",
4
4
  "description": "Matterbridge core library",
5
5
  "author": "https://github.com/Luligu",
6
6
  "homepage": "https://matterbridge.io/",
@@ -132,10 +132,10 @@
132
132
  },
133
133
  "dependencies": {
134
134
  "@matter/main": "0.17.9",
135
- "@matterbridge/dgram": "3.10.5-dev-20260808-d361317",
136
- "@matterbridge/thread": "3.10.5-dev-20260808-d361317",
137
- "@matterbridge/types": "3.10.5-dev-20260808-d361317",
138
- "@matterbridge/utils": "3.10.5-dev-20260808-d361317",
135
+ "@matterbridge/dgram": "3.10.5-dev-20260809-30130bb",
136
+ "@matterbridge/thread": "3.10.5-dev-20260809-30130bb",
137
+ "@matterbridge/types": "3.10.5-dev-20260809-30130bb",
138
+ "@matterbridge/utils": "3.10.5-dev-20260809-30130bb",
139
139
  "escape-html": "1.0.3",
140
140
  "express": "5.2.1",
141
141
  "express-rate-limit": "8.6.2",