Skip to content

Commit a1238f3

Browse files
committed
Automate Thermostat Mode CC
1 parent 6fb738d commit a1238f3

2 files changed

Lines changed: 77 additions & 1 deletion

File tree

dut/zwave-js/handlers/behaviors/capabilities.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { registerHandler, type PromptResponse } from "../../prompt-handlers.ts";
22

33
const questions: { pattern: RegExp; answer: PromptResponse }[] = [
44
{ pattern: /allows the end user to establish association/i, answer: "No" },
5-
{ pattern: /capable to display the last known state/i, answer: "Yes" },
5+
{ pattern: /(capable|able) to display the last known state/i, answer: "Yes" },
66
{
77
pattern: /(current state|visualization|visualisation).+is visible/i,
88
answer: "Ok",
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
import { ThermostatMode, ThermostatModeCCValues } from "zwave-js";
2+
import { Bytes } from "@zwave-js/shared";
3+
import { registerHandler } from "../../prompt-handlers.ts";
4+
5+
// Map UPPER_CASE mode names from logs to ThermostatMode enum values
6+
const modeNameToEnum: Record<string, ThermostatMode> = {
7+
OFF: ThermostatMode["Off"],
8+
HEAT: ThermostatMode["Heat"],
9+
COOL: ThermostatMode["Cool"],
10+
AUTO: ThermostatMode["Auto"],
11+
AUXILIARY: ThermostatMode["Auxiliary"],
12+
RESUME: ThermostatMode["Resume (on)"],
13+
FAN: ThermostatMode["Fan"],
14+
FURNACE: ThermostatMode["Furnace"],
15+
DRY: ThermostatMode["Dry"],
16+
MOIST: ThermostatMode["Moist"],
17+
AUTO_CHANGEOVER: ThermostatMode["Auto changeover"],
18+
ENERGY_HEAT: ThermostatMode["Energy heat"],
19+
ENERGY_COOL: ThermostatMode["Energy cool"],
20+
AWAY: ThermostatMode["Away"],
21+
FULL_POWER: ThermostatMode["Full power"],
22+
MANUFACTURER_SPECIFIC: ThermostatMode["Manufacturer specific"],
23+
};
24+
25+
registerHandler("CCR_ThermostatModeCC_Rev02", {
26+
async onLog(ctx) {
27+
const node = ctx.includedNodes.at(-1);
28+
if (!node) return;
29+
30+
// Handle: THERMOSTAT_MODE_SET to mode = 'HEAT'
31+
// Handle: THERMOSTAT_MODE_SET to mode = 'MANUFACTURER_SPECIFIC' with manufacturer specific data = [0x01, 0x02, 0x03]
32+
const modeMatch =
33+
/THERMOSTAT_MODE_SET to mode = '(?<mode>[^']+)'(?:\s+with manufacturer specific data = \[(?<data>[^\]]+)\])?/i.exec(
34+
ctx.logText
35+
);
36+
37+
if (modeMatch?.groups?.mode) {
38+
const mode = modeNameToEnum[modeMatch.groups.mode];
39+
40+
if (mode === undefined) return;
41+
42+
if (mode === ThermostatMode["Manufacturer specific"]) {
43+
// Parse manufacturer data: "0x01, 0x02, 0x03" -> Uint8Array
44+
const dataStr = modeMatch.groups.data;
45+
if (dataStr) {
46+
const bytes = dataStr.split(",").map((s) => parseInt(s.trim(), 16));
47+
await node.commandClasses["Thermostat Mode"].set(
48+
mode,
49+
Bytes.from(bytes)
50+
);
51+
}
52+
} else {
53+
await node.commandClasses["Thermostat Mode"].set(mode);
54+
}
55+
return true;
56+
}
57+
},
58+
59+
async onPrompt(ctx) {
60+
const node = ctx.includedNodes.at(-1);
61+
if (!node) return;
62+
63+
// Handle: Confirm that last known mode of thermostat is 'OFF' (0x00) in the DUT UI!
64+
const modeCheckMatch =
65+
/last known mode of thermostat is '[^']+' \((?<value>0x[0-9a-fA-F]+)\)/i.exec(
66+
ctx.promptText
67+
);
68+
69+
if (modeCheckMatch?.groups?.value) {
70+
const expected = parseInt(modeCheckMatch.groups.value, 16);
71+
const actual = node.getValue(ThermostatModeCCValues.thermostatMode.id);
72+
73+
return actual === expected ? "Yes" : "No";
74+
}
75+
},
76+
});

0 commit comments

Comments
 (0)