|
| 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