@matterbridge/core 3.10.6-dev-20260816-34076cc → 3.10.6-dev-20260817-a8ee796
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/dist/behaviors/booleanStateConfigurationServer.d.ts +8 -4
- package/dist/behaviors/booleanStateConfigurationServer.js +72 -1
- package/dist/behaviors/export.d.ts +1 -0
- package/dist/behaviors/export.js +1 -0
- package/dist/behaviors/occupancySensingServer.d.ts +19 -0
- package/dist/behaviors/occupancySensingServer.js +29 -0
- package/dist/behaviors/smokeCoAlarmServer.d.ts +10 -0
- package/dist/behaviors/smokeCoAlarmServer.js +34 -0
- package/dist/behaviors/thermostatServer.d.ts +4 -0
- package/dist/behaviors/thermostatServer.js +59 -4
- package/dist/chipTest.d.ts +10 -0
- package/dist/chipTest.js +506 -0
- package/dist/cli.js +1 -0
- package/dist/frontend.js +63 -9
- package/dist/helpers.js +7 -1
- package/dist/matterbridge.js +9 -1
- package/dist/matterbridgeEndpoint.d.ts +3 -1
- package/dist/matterbridgeEndpoint.js +17 -9
- package/dist/matterbridgeEndpointHelpers.d.ts +17 -2
- package/dist/matterbridgeEndpointHelpers.js +32 -5
- package/package.json +5 -5
|
@@ -1,13 +1,17 @@
|
|
|
1
|
+
import type { MaybePromise } from '@matter/general';
|
|
1
2
|
import { BooleanStateConfigurationServer } from '@matter/node/behaviors/boolean-state-configuration';
|
|
2
3
|
import { BooleanStateConfiguration } from '@matter/types/clusters/boolean-state-configuration';
|
|
3
|
-
declare const
|
|
4
|
-
alarmSuppress:
|
|
4
|
+
declare const Base: import("@matter/node").ClusterBehavior.Type<typeof BooleanStateConfigurationServer, import("@matter/types").ClusterType.WithSupportedFeatures<BooleanStateConfiguration, {
|
|
5
|
+
alarmSuppress: true;
|
|
5
6
|
audible: true;
|
|
6
|
-
faultEvents:
|
|
7
|
+
faultEvents: true;
|
|
7
8
|
sensitivityLevel: true;
|
|
8
9
|
visual: true;
|
|
9
10
|
}>, import("@matter/types").ClusterType.Concrete, new () => {}, "booleanStateConfiguration">;
|
|
10
|
-
export declare class MatterbridgeBooleanStateConfigurationServer extends
|
|
11
|
+
export declare class MatterbridgeBooleanStateConfigurationServer extends Base {
|
|
12
|
+
#private;
|
|
13
|
+
initialize(): MaybePromise;
|
|
14
|
+
suppressAlarm(request: BooleanStateConfiguration.SuppressAlarmRequest): Promise<void>;
|
|
11
15
|
enableDisableAlarm(request: BooleanStateConfiguration.EnableDisableAlarmRequest): Promise<void>;
|
|
12
16
|
}
|
|
13
17
|
export {};
|
|
@@ -1,8 +1,77 @@
|
|
|
1
1
|
import { BooleanStateConfigurationServer } from '@matter/node/behaviors/boolean-state-configuration';
|
|
2
|
+
import { Status, StatusResponseError } from '@matter/types';
|
|
2
3
|
import { BooleanStateConfiguration } from '@matter/types/clusters/boolean-state-configuration';
|
|
3
4
|
import { debugStringify } from 'node-ansi-logger';
|
|
4
5
|
import { MatterbridgeServer } from './matterbridgeServer.js';
|
|
5
|
-
|
|
6
|
+
const Base = BooleanStateConfigurationServer.with(BooleanStateConfiguration.Feature.Visual, BooleanStateConfiguration.Feature.Audible, BooleanStateConfiguration.Feature.AlarmSuppress, BooleanStateConfiguration.Feature.SensitivityLevel, BooleanStateConfiguration.Feature.FaultEvents);
|
|
7
|
+
export class MatterbridgeBooleanStateConfigurationServer extends Base {
|
|
8
|
+
initialize() {
|
|
9
|
+
if (this.features.visual || this.features.audible) {
|
|
10
|
+
this.reactTo(this.events.alarmsActive$Changed, this.#emitAlarmsStateChanged);
|
|
11
|
+
this.reactTo(this.events.alarmsSuppressed$Changed, this.#emitAlarmsStateChanged);
|
|
12
|
+
}
|
|
13
|
+
if (this.features.faultEvents) {
|
|
14
|
+
const sensorFaultChanged = this.events.sensorFault$Changed;
|
|
15
|
+
if (sensorFaultChanged)
|
|
16
|
+
this.reactTo(sensorFaultChanged, this.#emitSensorFault);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
#emitAlarmsStateChanged() {
|
|
20
|
+
this.events.alarmsStateChanged?.emit({ alarmsActive: this.state.alarmsActive, alarmsSuppressed: this.state.alarmsSuppressed }, this.context);
|
|
21
|
+
}
|
|
22
|
+
#emitSensorFault(sensorFault) {
|
|
23
|
+
this.events.sensorFault?.emit({ sensorFault }, this.context);
|
|
24
|
+
}
|
|
25
|
+
#mergeAlarmsSuppressed(alarmsToSuppress) {
|
|
26
|
+
return {
|
|
27
|
+
visual: [this.state.alarmsSuppressed.visual, alarmsToSuppress.visual].some(Boolean),
|
|
28
|
+
audible: [this.state.alarmsSuppressed.audible, alarmsToSuppress.audible].some(Boolean),
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
#applyAlarmsEnabled(alarmsToEnableDisable) {
|
|
32
|
+
const alarmsEnabled = {
|
|
33
|
+
visual: Boolean(alarmsToEnableDisable.visual),
|
|
34
|
+
audible: Boolean(alarmsToEnableDisable.audible),
|
|
35
|
+
};
|
|
36
|
+
this.state.alarmsEnabled = alarmsEnabled;
|
|
37
|
+
this.state.alarmsActive = {
|
|
38
|
+
visual: Boolean(this.state.alarmsActive.visual && alarmsEnabled.visual),
|
|
39
|
+
audible: Boolean(this.state.alarmsActive.audible && alarmsEnabled.audible),
|
|
40
|
+
};
|
|
41
|
+
this.state.alarmsSuppressed = {
|
|
42
|
+
visual: Boolean(this.state.alarmsSuppressed.visual && alarmsEnabled.visual),
|
|
43
|
+
audible: Boolean(this.state.alarmsSuppressed.audible && alarmsEnabled.audible),
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
#assertAlarmModesSupported(alarms) {
|
|
47
|
+
if ([Boolean(alarms.visual && !this.state.alarmsSupported.visual), Boolean(alarms.audible && !this.state.alarmsSupported.audible)].some(Boolean)) {
|
|
48
|
+
throw new StatusResponseError('Requested alarm mode is not supported', Status.ConstraintError);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
#assertSuppressAlarmAllowed(alarmsToSuppress) {
|
|
52
|
+
if ([
|
|
53
|
+
Boolean(alarmsToSuppress.visual && (!this.state.alarmsActive.visual || !this.state.alarmsEnabled?.visual)),
|
|
54
|
+
Boolean(alarmsToSuppress.audible && (!this.state.alarmsActive.audible || !this.state.alarmsEnabled?.audible)),
|
|
55
|
+
].some(Boolean)) {
|
|
56
|
+
throw new StatusResponseError('Requested alarm mode is not active', Status.InvalidInState);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
async suppressAlarm(request) {
|
|
60
|
+
const device = this.endpoint.stateOf(MatterbridgeServer);
|
|
61
|
+
device.log.info(`Suppressing alarm ${debugStringify(request.alarmsToSuppress)} (endpoint ${this.endpoint.maybeId}.${this.endpoint.maybeNumber})`);
|
|
62
|
+
await device.commandHandler.executeHandler('BooleanStateConfiguration.suppressAlarm', {
|
|
63
|
+
command: 'suppressAlarm',
|
|
64
|
+
request,
|
|
65
|
+
cluster: BooleanStateConfigurationServer.id,
|
|
66
|
+
attributes: this.state,
|
|
67
|
+
endpoint: this.endpoint,
|
|
68
|
+
context: this.context,
|
|
69
|
+
});
|
|
70
|
+
this.#assertAlarmModesSupported(request.alarmsToSuppress);
|
|
71
|
+
this.#assertSuppressAlarmAllowed(request.alarmsToSuppress);
|
|
72
|
+
this.state.alarmsSuppressed = this.#mergeAlarmsSuppressed(request.alarmsToSuppress);
|
|
73
|
+
device.log.debug(`MatterbridgeBooleanStateConfigurationServer: suppressAlarm called`);
|
|
74
|
+
}
|
|
6
75
|
async enableDisableAlarm(request) {
|
|
7
76
|
const device = this.endpoint.stateOf(MatterbridgeServer);
|
|
8
77
|
device.log.info(`Enabling/disabling alarm ${debugStringify(request.alarmsToEnableDisable)} (endpoint ${this.endpoint.maybeId}.${this.endpoint.maybeNumber})`);
|
|
@@ -14,6 +83,8 @@ export class MatterbridgeBooleanStateConfigurationServer extends BooleanStateCon
|
|
|
14
83
|
endpoint: this.endpoint,
|
|
15
84
|
context: this.context,
|
|
16
85
|
});
|
|
86
|
+
this.#assertAlarmModesSupported(request.alarmsToEnableDisable);
|
|
87
|
+
this.#applyAlarmsEnabled(request.alarmsToEnableDisable);
|
|
17
88
|
device.log.debug(`MatterbridgeBooleanStateConfigurationServer: enableDisableAlarm called`);
|
|
18
89
|
}
|
|
19
90
|
}
|
|
@@ -12,6 +12,7 @@ export * from './levelControlServer.js';
|
|
|
12
12
|
export * from './matterbridgeServer.js';
|
|
13
13
|
export * from './modeSelectServer.js';
|
|
14
14
|
export * from './onOffServer.js';
|
|
15
|
+
export * from './occupancySensingServer.js';
|
|
15
16
|
export * from './operationalStateServer.js';
|
|
16
17
|
export * from './powerSourceServer.js';
|
|
17
18
|
export * from './serviceAreaServer.js';
|
package/dist/behaviors/export.js
CHANGED
|
@@ -12,6 +12,7 @@ export * from './levelControlServer.js';
|
|
|
12
12
|
export * from './matterbridgeServer.js';
|
|
13
13
|
export * from './modeSelectServer.js';
|
|
14
14
|
export * from './onOffServer.js';
|
|
15
|
+
export * from './occupancySensingServer.js';
|
|
15
16
|
export * from './operationalStateServer.js';
|
|
16
17
|
export * from './powerSourceServer.js';
|
|
17
18
|
export * from './serviceAreaServer.js';
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { MaybePromise } from '@matter/general';
|
|
2
|
+
import { OccupancySensingServer } from '@matter/node/behaviors/occupancy-sensing';
|
|
3
|
+
import { OccupancySensing } from '@matter/types/clusters/occupancy-sensing';
|
|
4
|
+
declare const MatterbridgeOccupancySensingServer_base: import("@matter/node").ClusterBehavior.Type<typeof OccupancySensingServer, import("@matter/types").ClusterType.WithSupportedFeatures<OccupancySensing, {
|
|
5
|
+
activeInfrared: false;
|
|
6
|
+
occupancyEvent: false;
|
|
7
|
+
other: false;
|
|
8
|
+
passiveInfrared: true;
|
|
9
|
+
physicalContact: false;
|
|
10
|
+
radar: false;
|
|
11
|
+
rfSensing: false;
|
|
12
|
+
ultrasonic: false;
|
|
13
|
+
vision: false;
|
|
14
|
+
}>, import("@matter/types").ClusterType.Concrete, new () => {}, "occupancySensing">;
|
|
15
|
+
export declare class MatterbridgeOccupancySensingServer extends MatterbridgeOccupancySensingServer_base {
|
|
16
|
+
#private;
|
|
17
|
+
initialize(): MaybePromise;
|
|
18
|
+
}
|
|
19
|
+
export {};
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { OccupancySensingServer } from '@matter/node/behaviors/occupancy-sensing';
|
|
2
|
+
import { OccupancySensing } from '@matter/types/clusters/occupancy-sensing';
|
|
3
|
+
export class MatterbridgeOccupancySensingServer extends OccupancySensingServer.with(OccupancySensing.Feature.PassiveInfrared) {
|
|
4
|
+
#syncingHoldTime = false;
|
|
5
|
+
initialize() {
|
|
6
|
+
const result = super.initialize();
|
|
7
|
+
if (this.events.holdTime$Changing)
|
|
8
|
+
this.reactTo(this.events.holdTime$Changing, this.#syncPirOccupiedToUnoccupiedDelay);
|
|
9
|
+
if (this.events.pirOccupiedToUnoccupiedDelay$Changing)
|
|
10
|
+
this.reactTo(this.events.pirOccupiedToUnoccupiedDelay$Changing, this.#syncHoldTime);
|
|
11
|
+
return result;
|
|
12
|
+
}
|
|
13
|
+
#syncPirOccupiedToUnoccupiedDelay = (holdTime, _oldHoldTime, context) => {
|
|
14
|
+
const state = this.endpoint.agentFor(context).get(MatterbridgeOccupancySensingServer).state;
|
|
15
|
+
if (this.#syncingHoldTime || holdTime === undefined || state.pirOccupiedToUnoccupiedDelay === holdTime)
|
|
16
|
+
return;
|
|
17
|
+
this.#syncingHoldTime = true;
|
|
18
|
+
state.pirOccupiedToUnoccupiedDelay = holdTime;
|
|
19
|
+
this.#syncingHoldTime = false;
|
|
20
|
+
};
|
|
21
|
+
#syncHoldTime = (pirOccupiedToUnoccupiedDelay, _oldPirOccupiedToUnoccupiedDelay, context) => {
|
|
22
|
+
const state = this.endpoint.agentFor(context).get(MatterbridgeOccupancySensingServer).state;
|
|
23
|
+
if (this.#syncingHoldTime || pirOccupiedToUnoccupiedDelay === undefined || state.holdTime === pirOccupiedToUnoccupiedDelay)
|
|
24
|
+
return;
|
|
25
|
+
this.#syncingHoldTime = true;
|
|
26
|
+
state.holdTime = pirOccupiedToUnoccupiedDelay;
|
|
27
|
+
this.#syncingHoldTime = false;
|
|
28
|
+
};
|
|
29
|
+
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type Timer } from '@matter/general';
|
|
1
2
|
import { SmokeCoAlarmServer } from '@matter/node/behaviors/smoke-co-alarm';
|
|
2
3
|
import { SmokeCoAlarm } from '@matter/types/clusters/smoke-co-alarm';
|
|
3
4
|
declare const MatterbridgeSmokeCoAlarmServer_base: import("@matter/node").ClusterBehavior.Type<typeof SmokeCoAlarmServer, import("@matter/types").ClusterType.WithSupportedFeatures<SmokeCoAlarm, {
|
|
@@ -5,6 +6,15 @@ declare const MatterbridgeSmokeCoAlarmServer_base: import("@matter/node").Cluste
|
|
|
5
6
|
smokeAlarm: true;
|
|
6
7
|
}>, import("@matter/types").ClusterType.Concrete, new () => {}, "smokeCoAlarm">;
|
|
7
8
|
export declare class MatterbridgeSmokeCoAlarmServer extends MatterbridgeSmokeCoAlarmServer_base {
|
|
9
|
+
#private;
|
|
10
|
+
protected internal: MatterbridgeSmokeCoAlarmServer.Internal;
|
|
11
|
+
static selfTestDurationSeconds: number;
|
|
8
12
|
selfTestRequest(): Promise<void>;
|
|
13
|
+
[Symbol.asyncDispose](): Promise<void>;
|
|
14
|
+
}
|
|
15
|
+
export declare namespace MatterbridgeSmokeCoAlarmServer {
|
|
16
|
+
class Internal {
|
|
17
|
+
selfTestTimer?: Timer;
|
|
18
|
+
}
|
|
9
19
|
}
|
|
10
20
|
export {};
|
|
@@ -1,7 +1,10 @@
|
|
|
1
|
+
import { Seconds, Time } from '@matter/general';
|
|
1
2
|
import { SmokeCoAlarmServer } from '@matter/node/behaviors/smoke-co-alarm';
|
|
3
|
+
import { Status, StatusResponseError } from '@matter/types';
|
|
2
4
|
import { SmokeCoAlarm } from '@matter/types/clusters/smoke-co-alarm';
|
|
3
5
|
import { MatterbridgeServer } from './matterbridgeServer.js';
|
|
4
6
|
export class MatterbridgeSmokeCoAlarmServer extends SmokeCoAlarmServer.with(SmokeCoAlarm.Feature.SmokeAlarm, SmokeCoAlarm.Feature.CoAlarm) {
|
|
7
|
+
static selfTestDurationSeconds = 5;
|
|
5
8
|
async selfTestRequest() {
|
|
6
9
|
const device = this.endpoint.stateOf(MatterbridgeServer);
|
|
7
10
|
device.log.info(`Testing SmokeCOAlarm (endpoint ${this.endpoint.maybeId}.${this.endpoint.maybeNumber})`);
|
|
@@ -13,6 +16,37 @@ export class MatterbridgeSmokeCoAlarmServer extends SmokeCoAlarmServer.with(Smok
|
|
|
13
16
|
endpoint: this.endpoint,
|
|
14
17
|
context: this.context,
|
|
15
18
|
});
|
|
19
|
+
if (this.state.expressedState !== SmokeCoAlarm.ExpressedState.Normal || this.state.testInProgress) {
|
|
20
|
+
throw new StatusResponseError('SmokeCOAlarm self-test is busy', Status.Busy);
|
|
21
|
+
}
|
|
22
|
+
this.state.testInProgress = true;
|
|
23
|
+
this.state.expressedState = SmokeCoAlarm.ExpressedState.Testing;
|
|
24
|
+
this.#scheduleSelfTestComplete();
|
|
16
25
|
device.log.debug(`MatterbridgeSmokeCoAlarmServer: selfTestRequest called`);
|
|
17
26
|
}
|
|
27
|
+
#stopSelfTestTimer() {
|
|
28
|
+
this.internal.selfTestTimer?.stop();
|
|
29
|
+
this.internal.selfTestTimer = undefined;
|
|
30
|
+
}
|
|
31
|
+
#scheduleSelfTestComplete() {
|
|
32
|
+
this.#stopSelfTestTimer();
|
|
33
|
+
this.internal.selfTestTimer = Time.getTimer('SmokeCOAlarm self-test complete', Seconds(MatterbridgeSmokeCoAlarmServer.selfTestDurationSeconds), this.callback(this.#completeSelfTest, { lock: true })).start();
|
|
34
|
+
}
|
|
35
|
+
#completeSelfTest() {
|
|
36
|
+
this.internal.selfTestTimer = undefined;
|
|
37
|
+
this.state.testInProgress = false;
|
|
38
|
+
this.state.expressedState = SmokeCoAlarm.ExpressedState.Normal;
|
|
39
|
+
this.events.selfTestComplete.emit(undefined, this.context);
|
|
40
|
+
this.events.allClear.emit(undefined, this.context);
|
|
41
|
+
}
|
|
42
|
+
async [Symbol.asyncDispose]() {
|
|
43
|
+
this.#stopSelfTestTimer();
|
|
44
|
+
await super[Symbol.asyncDispose]?.();
|
|
45
|
+
}
|
|
18
46
|
}
|
|
47
|
+
(function (MatterbridgeSmokeCoAlarmServer) {
|
|
48
|
+
class Internal {
|
|
49
|
+
selfTestTimer;
|
|
50
|
+
}
|
|
51
|
+
MatterbridgeSmokeCoAlarmServer.Internal = Internal;
|
|
52
|
+
})(MatterbridgeSmokeCoAlarmServer || (MatterbridgeSmokeCoAlarmServer = {}));
|
|
@@ -13,9 +13,13 @@ declare const MatterbridgeThermostatServer_base: import("@matter/node").ClusterB
|
|
|
13
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
|
+
private removeThermostatSuggestionsForRemovedPresets;
|
|
16
18
|
setpointRaiseLower(request: Thermostat.SetpointRaiseLowerRequest): Promise<void>;
|
|
17
19
|
setActivePresetRequest(request: Thermostat.SetActivePresetRequest): Promise<void>;
|
|
18
20
|
setActiveScheduleRequest(request: Thermostat.SetActiveScheduleRequest): Promise<void>;
|
|
21
|
+
private removeExpiredThermostatSuggestions;
|
|
22
|
+
private reEvaluateCurrentThermostatSuggestion;
|
|
19
23
|
addThermostatSuggestion(request: Thermostat.AddThermostatSuggestionRequest): Promise<Thermostat.AddThermostatSuggestionResponse>;
|
|
20
24
|
removeThermostatSuggestion(request: Thermostat.RemoveThermostatSuggestionRequest): Promise<void>;
|
|
21
25
|
}
|
|
@@ -4,6 +4,41 @@ import { StatusResponse } from '@matter/types';
|
|
|
4
4
|
import { Thermostat } from '@matter/types/clusters/thermostat';
|
|
5
5
|
import { MatterbridgeServer } from './matterbridgeServer.js';
|
|
6
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) {
|
|
7
|
+
async initialize() {
|
|
8
|
+
await super.initialize();
|
|
9
|
+
this.reactTo(this.events.presets$AtomicChanged, this.removeThermostatSuggestionsForRemovedPresets);
|
|
10
|
+
}
|
|
11
|
+
removeThermostatSuggestionsForRemovedPresets(newPresets, oldPresets) {
|
|
12
|
+
const remainingPresetHandles = new Set();
|
|
13
|
+
for (const preset of newPresets) {
|
|
14
|
+
if (preset.presetHandle !== null)
|
|
15
|
+
remainingPresetHandles.add(Bytes.toHex(preset.presetHandle));
|
|
16
|
+
}
|
|
17
|
+
const removedPresetHandles = new Set();
|
|
18
|
+
for (const preset of oldPresets) {
|
|
19
|
+
if (preset.presetHandle !== null && !remainingPresetHandles.has(Bytes.toHex(preset.presetHandle))) {
|
|
20
|
+
removedPresetHandles.add(Bytes.toHex(preset.presetHandle));
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
if (removedPresetHandles.size === 0)
|
|
24
|
+
return;
|
|
25
|
+
const currentSuggestions = [...this.state.thermostatSuggestions];
|
|
26
|
+
const remainingSuggestions = currentSuggestions.filter((s) => !removedPresetHandles.has(Bytes.toHex(s.presetHandle)));
|
|
27
|
+
const currentSuggestion = this.state.currentThermostatSuggestion;
|
|
28
|
+
const clearCurrentSuggestion = currentSuggestion !== null && removedPresetHandles.has(Bytes.toHex(currentSuggestion.presetHandle));
|
|
29
|
+
if (remainingSuggestions.length === currentSuggestions.length && !clearCurrentSuggestion)
|
|
30
|
+
return;
|
|
31
|
+
const device = this.endpoint.stateOf(MatterbridgeServer);
|
|
32
|
+
if (remainingSuggestions.length !== currentSuggestions.length) {
|
|
33
|
+
device.log.info(`Removing ${currentSuggestions.length - remainingSuggestions.length} thermostat suggestion(s) referencing removed preset(s) (endpoint ${this.endpoint.maybeId}.${this.endpoint.maybeNumber})`);
|
|
34
|
+
this.state.thermostatSuggestions = remainingSuggestions;
|
|
35
|
+
}
|
|
36
|
+
if (clearCurrentSuggestion) {
|
|
37
|
+
device.log.info(`Clearing current thermostat suggestion referencing a removed preset (endpoint ${this.endpoint.maybeId}.${this.endpoint.maybeNumber})`);
|
|
38
|
+
this.state.currentThermostatSuggestion = null;
|
|
39
|
+
this.state.thermostatSuggestionNotFollowingReason = null;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
7
42
|
async setpointRaiseLower(request) {
|
|
8
43
|
const device = this.endpoint.stateOf(MatterbridgeServer);
|
|
9
44
|
device.log.info(`Setting setpoint by ${request.amount} in mode ${request.mode} (endpoint ${this.endpoint.maybeId}.${this.endpoint.maybeNumber})`);
|
|
@@ -55,6 +90,25 @@ export class MatterbridgeThermostatServer extends ThermostatServer.with(Thermost
|
|
|
55
90
|
this.state.activeScheduleHandle = Uint8Array.from(request.scheduleHandle);
|
|
56
91
|
device.log.debug(`MatterbridgeThermostatServer: setActiveScheduleRequest completed with activeScheduleHandle: ${scheduleHandle}`);
|
|
57
92
|
}
|
|
93
|
+
removeExpiredThermostatSuggestions() {
|
|
94
|
+
const now = Math.floor(Date.now() / 1000);
|
|
95
|
+
this.state.thermostatSuggestions = this.state.thermostatSuggestions.filter((s) => s.expirationTime > now);
|
|
96
|
+
}
|
|
97
|
+
reEvaluateCurrentThermostatSuggestion() {
|
|
98
|
+
const now = Math.floor(Date.now() / 1000);
|
|
99
|
+
let current = null;
|
|
100
|
+
for (const s of this.state.thermostatSuggestions) {
|
|
101
|
+
if (s.effectiveTime <= now && (current === null || s.effectiveTime < current.effectiveTime))
|
|
102
|
+
current = s;
|
|
103
|
+
}
|
|
104
|
+
if (this.state.currentThermostatSuggestion?.uniqueId === current?.uniqueId)
|
|
105
|
+
return;
|
|
106
|
+
this.state.currentThermostatSuggestion = current;
|
|
107
|
+
this.state.thermostatSuggestionNotFollowingReason = null;
|
|
108
|
+
if (current !== null) {
|
|
109
|
+
this.state.activePresetHandle = Uint8Array.from(current.presetHandle);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
58
112
|
async addThermostatSuggestion(request) {
|
|
59
113
|
const device = this.endpoint.stateOf(MatterbridgeServer);
|
|
60
114
|
const presetHandle = `0x${Buffer.from(request.presetHandle).toString('hex')}`;
|
|
@@ -70,6 +124,8 @@ export class MatterbridgeThermostatServer extends ThermostatServer.with(Thermost
|
|
|
70
124
|
if (this.state.presets.find((p) => p.presetHandle !== null && Bytes.areEqual(p.presetHandle, request.presetHandle)) === undefined) {
|
|
71
125
|
throw new StatusResponse.NotFoundError('Requested PresetHandle not found');
|
|
72
126
|
}
|
|
127
|
+
this.removeExpiredThermostatSuggestions();
|
|
128
|
+
this.reEvaluateCurrentThermostatSuggestion();
|
|
73
129
|
if (this.state.thermostatSuggestions.length >= this.state.maxThermostatSuggestions) {
|
|
74
130
|
throw new StatusResponse.ResourceExhaustedError('Maximum number of thermostat suggestions reached');
|
|
75
131
|
}
|
|
@@ -89,6 +145,7 @@ export class MatterbridgeThermostatServer extends ThermostatServer.with(Thermost
|
|
|
89
145
|
expirationTime: effectiveTime + request.expirationInMinutes * 60,
|
|
90
146
|
};
|
|
91
147
|
this.state.thermostatSuggestions = [...this.state.thermostatSuggestions, suggestion];
|
|
148
|
+
this.reEvaluateCurrentThermostatSuggestion();
|
|
92
149
|
device.log.debug(`MatterbridgeThermostatServer: addThermostatSuggestion completed with uniqueId: ${uniqueId}`);
|
|
93
150
|
return { uniqueId };
|
|
94
151
|
}
|
|
@@ -108,10 +165,8 @@ export class MatterbridgeThermostatServer extends ThermostatServer.with(Thermost
|
|
|
108
165
|
throw new StatusResponse.NotFoundError('Requested UniqueID not found');
|
|
109
166
|
}
|
|
110
167
|
this.state.thermostatSuggestions = this.state.thermostatSuggestions.filter((s) => s.uniqueId !== request.uniqueId);
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
this.state.thermostatSuggestionNotFollowingReason = null;
|
|
114
|
-
}
|
|
168
|
+
this.removeExpiredThermostatSuggestions();
|
|
169
|
+
this.reEvaluateCurrentThermostatSuggestion();
|
|
115
170
|
device.log.debug(`MatterbridgeThermostatServer: removeThermostatSuggestion completed for uniqueId: ${request.uniqueId}`);
|
|
116
171
|
}
|
|
117
172
|
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { GeneralDiagnosticsServer } from '@matter/node/behaviors/general-diagnostics';
|
|
2
|
+
import type { GeneralDiagnostics } from '@matter/types/clusters/general-diagnostics';
|
|
3
|
+
import type { Matterbridge } from './matterbridge.js';
|
|
4
|
+
export declare const chipTestEnableKey: Uint8Array<ArrayBuffer>;
|
|
5
|
+
export declare class MatterbridgeGeneralDiagnosticsServer extends GeneralDiagnosticsServer {
|
|
6
|
+
initialize(): void;
|
|
7
|
+
testEventTrigger({ eventTrigger, enableKey }: GeneralDiagnostics.TestEventTriggerRequest): Promise<void>;
|
|
8
|
+
}
|
|
9
|
+
export declare function createChipTestDevices(matterbridge: Matterbridge): Promise<void>;
|
|
10
|
+
export declare function createChipTestAppPipe(matterbridge: Matterbridge): void;
|