UNPKG

zigbee-herdsman-converters

Version:

Collection of device converters to be used with zigbee-herdsman

1,020 lines 84.7 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || (function () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); Object.defineProperty(exports, "__esModule", { value: true }); exports.definitions = void 0; const zigbee_herdsman_1 = require("zigbee-herdsman"); const fz = __importStar(require("../converters/fromZigbee")); const tz = __importStar(require("../converters/toZigbee")); const constants = __importStar(require("../lib/constants")); const exposes = __importStar(require("../lib/exposes")); const m = __importStar(require("../lib/modernExtend")); const reporting = __importStar(require("../lib/reporting")); const utils = __importStar(require("../lib/utils")); const utils_1 = require("../lib/utils"); const e = exposes.presets; const ea = exposes.access; const manuSinope = { manufacturerCode: zigbee_herdsman_1.Zcl.ManufacturerCode.SINOPE_TECHNOLOGIES }; const fzLocal = { ias_water_leak_alarm: { // RM3500ZB specific cluster: "ssIasZone", type: ["commandStatusChangeNotification", "attributeReport", "readResponse"], convert: (model, msg, publish, options, meta) => { const zoneStatus = msg.data.zoneStatus; return { water_leak: (zoneStatus & 1) > 0, tamper: (zoneStatus & (1 << 2)) > 0, }; }, }, thermostat: { cluster: "hvacThermostat", type: ["attributeReport", "readResponse"], convert: (model, msg, publish, options, meta) => { // @ts-expect-error ignore // biome-ignore lint/performance/noDelete: ignored using `--suppress` delete msg.running_state; const result = {}; const occupancyLookup = { 0: "unoccupied", 1: "occupied" }; const cycleOutputLookup = { 15: "15_sec", 300: "5_min", 600: "10_min", 900: "15_min", 1200: "20_min", 1800: "30_min", 65535: "off" }; if (msg.data["1024"] !== undefined) { result.thermostat_occupancy = utils.getFromLookup(msg.data["1024"], occupancyLookup); } if (msg.data.SinopeOccupancy !== undefined) { result.thermostat_occupancy = utils.getFromLookup(msg.data.SinopeOccupancy, occupancyLookup); } if (msg.data["1025"] !== undefined) { result.main_cycle_output = utils.getFromLookup(msg.data["1025"], cycleOutputLookup); } if (msg.data.SinopeMainCycleOutput !== undefined) { result.main_cycle_output = utils.getFromLookup(msg.data.SinopeMainCycleOutput, cycleOutputLookup); } if (msg.data["1026"] !== undefined) { const lookup = { 0: "on_demand", 1: "sensing", 2: "off" }; result.backlight_auto_dim = utils.getFromLookup(msg.data["1026"], lookup); } if (msg.data.SinopeBacklight !== undefined) { const lookup = { 0: "on_demand", 1: "sensing", 2: "off" }; result.backlight_auto_dim = utils.getFromLookup(msg.data.SinopeBacklight, lookup); } if (msg.data["1028"] !== undefined) { result.aux_cycle_output = utils.getFromLookup(msg.data["1028"], cycleOutputLookup); } if (msg.data.localTemp !== undefined) { result.local_temperature = (0, utils_1.precisionRound)(msg.data.localTemp, 2) / 100; } if (msg.data.localTemperatureCalibration !== undefined) { result.local_temperature_calibration = (0, utils_1.precisionRound)(msg.data.localTemperatureCalibration, 2) / 10; } if (msg.data.outdoorTemp !== undefined) { result.outdoor_temperature = (0, utils_1.precisionRound)(msg.data.outdoorTemp, 2) / 100; } if (msg.data.occupiedHeatingSetpoint !== undefined) { result.occupied_heating_setpoint = (0, utils_1.precisionRound)(msg.data.occupiedHeatingSetpoint, 2) / 100; } if (msg.data.unoccupiedHeatingSetpoint !== undefined) { result.unoccupied_heating_setpoint = (0, utils_1.precisionRound)(msg.data.unoccupiedHeatingSetpoint, 2) / 100; } if (msg.data.occupiedCoolingSetpoint !== undefined) { result.occupied_cooling_setpoint = (0, utils_1.precisionRound)(msg.data.occupiedCoolingSetpoint, 2) / 100; } if (msg.data.unoccupiedCoolingSetpoint !== undefined) { result.unoccupied_cooling_setpoint = (0, utils_1.precisionRound)(msg.data.unoccupiedCoolingSetpoint, 2) / 100; } if (msg.data.ctrlSeqeOfOper !== undefined) { result.control_sequence_of_operation = constants.thermostatControlSequenceOfOperations[msg.data.ctrlSeqeOfOper]; } if (msg.data.systemMode !== undefined) { result.system_mode = constants.thermostatSystemModes[msg.data.systemMode]; } if (msg.data.pIHeatingDemand !== undefined) { result.pi_heating_demand = (0, utils_1.precisionRound)(msg.data.pIHeatingDemand, 0); } if (msg.data.minHeatSetpointLimit !== undefined) { result.min_heat_setpoint_limit = (0, utils_1.precisionRound)(msg.data.minHeatSetpointLimit, 2) / 100; } if (msg.data.maxHeatSetpointLimit !== undefined) { result.max_heat_setpoint_limit = (0, utils_1.precisionRound)(msg.data.maxHeatSetpointLimit, 2) / 100; } if (msg.data.absMinHeatSetpointLimit !== undefined) { result.abs_min_heat_setpoint_limit = (0, utils_1.precisionRound)(msg.data.absMinHeatSetpointLimit, 2) / 100; } if (msg.data.absMaxHeatSetpointLimit !== undefined) { result.abs_max_heat_setpoint_limit = (0, utils_1.precisionRound)(msg.data.absMaxHeatSetpointLimit, 2) / 100; } if (msg.data.pIHeatingDemand !== undefined) { result.running_state = msg.data.pIHeatingDemand >= 10 ? "heat" : "idle"; } return result; }, }, tank_level: { cluster: "genAnalogInput", type: ["attributeReport", "readResponse"], convert: (model, msg, publish, options, meta) => { const result = {}; if (msg.data.presentValue !== undefined) { let x = msg.data.presentValue; if (x === -1) { result.tank_level = 0; } else { const xMin = 110; const xMax = 406; const delta = 46; if (delta <= x && x <= 70) { x = delta; } if (0 <= x && x <= delta) { x = x + 360; } const y = (x - xMin) / (xMax - xMin); const lowerLimit = 10; const upperLimit = 80; const valueRange = upperLimit - lowerLimit; const pct = y * valueRange + lowerLimit; result.tank_level = utils.precisionRound(pct, 2); } } return result; }, }, sinope: { cluster: "manuSpecificSinope", type: ["attributeReport", "readResponse"], convert: (model, msg, publish, options, meta) => { const result = {}; if (msg.data.GFCiStatus !== undefined) { const lookup = { 0: "off", 1: "on" }; result.gfci_status = utils.getFromLookup(msg.data.GFCiStatus, lookup); } if (msg.data.floorLimitStatus !== undefined) { const lookup = { 0: "off", 1: "on" }; result.floor_limit_status = utils.getFromLookup(msg.data.floorLimitStatus, lookup); } if (msg.data.secondScreenBehavior !== undefined) { const lookup = { 0: "auto", 1: "setpoint", 2: "outdoor temp" }; result.second_display_mode = utils.getFromLookup(msg.data.secondScreenBehavior, lookup); } if (msg.data.outdoorTempToDisplayTimeout !== undefined) { result.outdoor_temperature_timeout = msg.data.outdoorTempToDisplayTimeout; // DEPRECATED: Use Second Display Mode or control via set outdoorTempToDisplayTimeout result.enable_outdoor_temperature = msg.data.outdoorTempToDisplayTimeout === 12 ? "OFF" : "ON"; } if (msg.data.outdoorTempToDisplay !== undefined) { result.thermostat_outdoor_temperature = (0, utils_1.precisionRound)(msg.data.outdoorTempToDisplay, 2) / 100; } if (msg.data.currentTimeToDisplay !== undefined) { result.current_time_to_display = msg.data.currentTimeToDisplay; } if (msg.data.floorControlMode !== undefined) { const lookup = { 1: "ambiant", 2: "floor" }; result.floor_control_mode = utils.getFromLookup(msg.data.floorControlMode, lookup); } if (msg.data.ambiantMaxHeatSetpointLimit !== undefined) { result.ambiant_max_heat_setpoint = msg.data.ambiantMaxHeatSetpointLimit / 100.0; if (result.ambiant_max_heat_setpoint === -327.68) { result.ambiant_max_heat_setpoint = "off"; } } if (msg.data.floorMinHeatSetpointLimit !== undefined) { result.floor_min_heat_setpoint = msg.data.floorMinHeatSetpointLimit / 100.0; if (result.floor_min_heat_setpoint === -327.68) { result.floor_min_heat_setpoint = "off"; } } if (msg.data.floorMaxHeatSetpointLimit !== undefined) { result.floor_max_heat_setpoint = msg.data.floorMaxHeatSetpointLimit / 100.0; if (result.floor_max_heat_setpoint === -327.68) { result.floor_max_heat_setpoint = "off"; } } if (msg.data.temperatureSensor !== undefined) { const lookup = { 0: "10k", 1: "12k" }; result.floor_temperature_sensor = utils.getFromLookup(msg.data.temperatureSensor, lookup); } if (msg.data.timeFormatToDisplay !== undefined) { const lookup = { 0: "24h", 1: "12h" }; result.time_format = utils.getFromLookup(msg.data.timeFormatToDisplay, lookup); } if (msg.data.connectedLoad !== undefined) { result.connected_load = msg.data.connectedLoad; } if (msg.data.auxConnectedLoad !== undefined) { result.aux_connected_load = msg.data.auxConnectedLoad; if (result.aux_connected_load === 65535) { result.aux_connected_load = "disabled"; } } if (msg.data.pumpProtection !== undefined) { result.pump_protection = msg.data.pumpProtection === 1 ? "ON" : "OFF"; } if (msg.data.dimmerTimmer !== undefined) { result.timer_seconds = msg.data.dimmerTimmer; } if (msg.data.ledIntensityOn !== undefined) { result.led_intensity_on = msg.data.ledIntensityOn; } if (msg.data.ledIntensityOff !== undefined) { result.led_intensity_off = msg.data.ledIntensityOff; } if (msg.data.minimumBrightness !== undefined) { result.minimum_brightness = msg.data.minimumBrightness; } if (msg.data.actionReport !== undefined) { const lookup = { 1: "up_clickdown", 2: "up_single", 3: "up_hold", 4: "up_double", 17: "down_clickdown", 18: "down_single", 19: "down_hold", 20: "down_double", }; result.action = utils.getFromLookup(msg.data.actionReport, lookup); } if (msg.data.keypadLockout !== undefined) { const lookup = { 0: "unlock", 1: "lock" }; result.keypad_lockout = utils.getFromLookup(msg.data.keypadLockout, lookup); } if (msg.data.drConfigWaterTempMin !== undefined) { result.low_water_temp_protection = msg.data.drConfigWaterTempMin; } return result; }, }, }; const tzLocal = { thermostat_occupancy: { key: ["thermostat_occupancy"], convertSet: async (entity, key, value, meta) => { const sinopeOccupancy = { 0: "unoccupied", 1: "occupied" }; const SinopeOccupancy = utils.getKey(sinopeOccupancy, value, value, Number); await entity.write("hvacThermostat", { SinopeOccupancy }, manuSinope); return { state: { thermostat_occupancy: value } }; }, convertGet: async (entity, key, meta) => { await entity.read("hvacThermostat", ["SinopeOccupancy"], manuSinope); }, }, backlight_autodim: { key: ["backlight_auto_dim"], convertSet: async (entity, key, value, meta) => { const sinopeBacklightParam = { 0: "on_demand", 1: "sensing", 2: "off" }; const SinopeBacklight = utils.getKey(sinopeBacklightParam, value, value, Number); await entity.write("hvacThermostat", { SinopeBacklight }, manuSinope); return { state: { backlight_auto_dim: value } }; }, convertGet: async (entity, key, meta) => { await entity.read("hvacThermostat", ["SinopeBacklight"], manuSinope); }, }, main_cycle_output: { key: ["main_cycle_output"], convertSet: async (entity, key, value, meta) => { const lookup = { "15_sec": 15, "5_min": 300, "10_min": 600, "15_min": 900, "20_min": 1200, "30_min": 1800 }; await entity.write("hvacThermostat", { SinopeMainCycleOutput: utils.getFromLookup(value, lookup) }, manuSinope); return { state: { main_cycle_output: value } }; }, convertGet: async (entity, key, meta) => { await entity.read("hvacThermostat", ["SinopeMainCycleOutput"], manuSinope); }, }, aux_cycle_output: { // TH1400ZB specific key: ["aux_cycle_output"], convertSet: async (entity, key, value, meta) => { const lookup = { off: 65535, "15_sec": 15, "5_min": 300, "10_min": 600, "15_min": 900, "20_min": 1200, "30_min": 1800 }; await entity.write("hvacThermostat", { SinopeAuxCycleOutput: utils.getFromLookup(value, lookup) }); return { state: { aux_cycle_output: value } }; }, convertGet: async (entity, key, meta) => { await entity.read("hvacThermostat", ["SinopeAuxCycleOutput"]); }, }, enable_outdoor_temperature: { // DEPRECATED: Use Second Display Mode or control via the timeout key: ["enable_outdoor_temperature"], convertSet: async (entity, key, value, meta) => { utils.assertString(value); if (value.toLowerCase() === "on") { await entity.write("manuSpecificSinope", { outdoorTempToDisplayTimeout: 10800 }, manuSinope); } else if (value.toLowerCase() === "off") { // set timer to 12 sec in order to disable outdoor temperature await entity.write("manuSpecificSinope", { outdoorTempToDisplayTimeout: 12 }, manuSinope); } return { state: { enable_outdoor_temperature: value } }; }, convertGet: async (entity, key, meta) => { await entity.read("manuSpecificSinope", ["outdoorTempToDisplayTimeout"], manuSinope); }, }, second_display_mode: { key: ["second_display_mode"], convertSet: async (entity, key, value, meta) => { const lookup = { auto: 0, setpoint: 1, "outdoor temp": 2 }; await entity.write("manuSpecificSinope", { secondScreenBehavior: utils.getFromLookup(value, lookup) }); return { state: { second_display_mode: value } }; }, convertGet: async (entity, key, meta) => { await entity.read("manuSpecificSinope", ["secondScreenBehavior"]); }, }, thermostat_outdoor_temperature: { key: ["thermostat_outdoor_temperature"], convertSet: async (entity, key, value, meta) => { const number = utils.toNumber(value); if (number >= -99.5 && number <= 99.5) { await entity.write("manuSpecificSinope", { outdoorTempToDisplay: number * 100 }, manuSinope); } return { state: { thermostat_outdoor_temperature: number } }; }, convertGet: async (entity, key, meta) => { await entity.read("manuSpecificSinope", ["outdoorTempToDisplay"], manuSinope); }, }, outdoor_temperature_timeout: { key: ["outdoor_temperature_timeout"], convertSet: async (entity, key, value, meta) => { const number = utils.toNumber(value); if (number >= 30 && number <= 64800) { await entity.write("manuSpecificSinope", { outdoorTempToDisplayTimeout: number }); return { state: { outdoor_temperature_timeout: number } }; } }, convertGet: async (entity, key, meta) => { await entity.read("manuSpecificSinope", ["outdoorTempToDisplayTimeout"]); }, }, thermostat_time: { key: ["thermostat_time"], convertSet: async (entity, key, value, meta) => { if (value === "") { const thermostatDate = new Date(); const thermostatTimeSec = thermostatDate.getTime() / 1000; const thermostatTimezoneOffsetSec = thermostatDate.getTimezoneOffset() * 60; const currentTimeToDisplay = Math.round(thermostatTimeSec - thermostatTimezoneOffsetSec - 946684800); await entity.write("manuSpecificSinope", { currentTimeToDisplay }, manuSinope); } else if (value !== "") { await entity.write("manuSpecificSinope", { currentTimeToDisplay: value }, manuSinope); } }, }, floor_control_mode: { // TH1300ZB and TH1400ZB specific key: ["floor_control_mode"], convertSet: async (entity, key, value, meta) => { if (typeof value !== "string") { return; } const lookup = { ambiant: 1, floor: 2 }; // biome-ignore lint/style/noParameterAssign: ignored using `--suppress` value = value.toLowerCase(); // @ts-expect-error ignore if (lookup[value] !== undefined) { await entity.write("manuSpecificSinope", { floorControlMode: utils.getFromLookup(value, lookup) }); } return { state: { floor_control_mode: value } }; }, convertGet: async (entity, key, meta) => { await entity.read("manuSpecificSinope", ["floorControlMode"]); }, }, ambiant_max_heat_setpoint: { // TH1300ZB and TH1400ZB specific key: ["ambiant_max_heat_setpoint"], convertSet: async (entity, key, value, meta) => { // @ts-expect-error ignore if ((value >= 5 && value <= 36) || value === "off") { // @ts-expect-error ignore await entity.write("manuSpecificSinope", { ambiantMaxHeatSetpointLimit: value === "off" ? -32768 : value * 100 }); return { state: { ambiant_max_heat_setpoint: value } }; } }, convertGet: async (entity, key, meta) => { await entity.read("manuSpecificSinope", ["ambiantMaxHeatSetpointLimit"]); }, }, floor_min_heat_setpoint: { // TH1300ZB and TH1400ZB specific key: ["floor_min_heat_setpoint"], convertSet: async (entity, key, value, meta) => { // @ts-expect-error ignore if ((value >= 5 && value <= 34) || value === "off") { // @ts-expect-error ignore await entity.write("manuSpecificSinope", { floorMinHeatSetpointLimit: value === "off" ? -32768 : value * 100 }); return { state: { floor_min_heat_setpoint: value } }; } }, convertGet: async (entity, key, meta) => { await entity.read("manuSpecificSinope", ["floorMinHeatSetpointLimit"]); }, }, floor_max_heat_setpoint: { // TH1300ZB and TH1400ZB specific key: ["floor_max_heat_setpoint"], convertSet: async (entity, key, value, meta) => { // @ts-expect-error ignore if ((value >= 7 && value <= 36) || value === "off") { // @ts-expect-error ignore await entity.write("manuSpecificSinope", { floorMaxHeatSetpointLimit: value === "off" ? -32768 : value * 100 }); return { state: { floor_max_heat_setpoint: value } }; } }, convertGet: async (entity, key, meta) => { await entity.read("manuSpecificSinope", ["floorMaxHeatSetpointLimit"]); }, }, temperature_sensor: { // TH1300ZB and TH1400ZB specific key: ["floor_temperature_sensor"], convertSet: async (entity, key, value, meta) => { if (typeof value !== "string") { return; } const lookup = { "10k": 0, "12k": 1 }; // biome-ignore lint/style/noParameterAssign: ignored using `--suppress` value = value.toLowerCase(); // @ts-expect-error ignore if (lookup[value] !== undefined) { await entity.write("manuSpecificSinope", { temperatureSensor: utils.getFromLookup(value, lookup) }); } return { state: { floor_temperature_sensor: value } }; }, convertGet: async (entity, key, meta) => { await entity.read("manuSpecificSinope", ["temperatureSensor"]); }, }, time_format: { key: ["time_format"], convertSet: async (entity, key, value, meta) => { await entity.write("manuSpecificSinope", { timeFormatToDisplay: utils.getFromLookup(value, { "24h": 0, "12h": 1 }) }, manuSinope); return { state: { time_format: value } }; }, convertGet: async (entity, key, meta) => { await entity.read("manuSpecificSinope", ["timeFormatToDisplay"], manuSinope); }, }, connected_load: { // TH1400ZB and SW2500ZB key: ["connected_load"], convertSet: async (entity, key, value, meta) => { await entity.write("manuSpecificSinope", { connectedLoad: value }); return { state: { connected_load: value } }; }, convertGet: async (entity, key, meta) => { await entity.read("manuSpecificSinope", ["connectedLoad"]); }, }, aux_connected_load: { // TH1400ZB specific key: ["aux_connected_load"], convertSet: async (entity, key, value, meta) => { await entity.write("manuSpecificSinope", { auxConnectedLoad: value }); return { state: { aux_connected_load: value } }; }, convertGet: async (entity, key, meta) => { await entity.read("manuSpecificSinope", ["auxConnectedLoad"]); }, }, pump_protection: { // TH1400ZB specific key: ["pump_protection"], convertSet: async (entity, key, value, meta) => { utils.assertString(value); if (value.toLowerCase() === "on") { await entity.write("manuSpecificSinope", { pumpProtection: 1 }); } else if (value.toLowerCase() === "off") { await entity.write("manuSpecificSinope", { pumpProtection: 255 }); } return { state: { pump_protection: value } }; }, convertGet: async (entity, key, meta) => { await entity.read("manuSpecificSinope", ["pumpProtection"]); }, }, led_intensity_on: { // DM25x0ZB and SW2500ZB key: ["led_intensity_on"], convertSet: async (entity, key, value, meta) => { const number = utils.toNumber(value); if (number >= 0 && number <= 100) { await entity.write("manuSpecificSinope", { ledIntensityOn: number }); } return { state: { led_intensity_on: number } }; }, convertGet: async (entity, key, meta) => { await entity.read("manuSpecificSinope", ["ledIntensityOn"]); }, }, led_intensity_off: { // DM25x0ZB and SW2500ZB key: ["led_intensity_off"], convertSet: async (entity, key, value, meta) => { const number = utils.toNumber(value); if (number >= 0 && number <= 100) { await entity.write("manuSpecificSinope", { ledIntensityOff: number }); } return { state: { led_intensity_off: number } }; }, convertGet: async (entity, key, meta) => { await entity.read("manuSpecificSinope", ["ledIntensityOff"]); }, }, led_color_on: { // DM25x0ZB and SW2500ZB key: ["led_color_on"], convertSet: async (entity, key, value, meta) => { const r = value.r >= 0 && value.r <= 255 ? value.r : 0; const g = value.g >= 0 && value.g <= 255 ? value.g : 0; const b = value.b >= 0 && value.b <= 255 ? value.b : 0; const valueHex = r + g * 256 + b * 256 ** 2; await entity.write("manuSpecificSinope", { ledColorOn: valueHex }); }, }, led_color_off: { // DM25x0ZB and SW2500ZB key: ["led_color_off"], convertSet: async (entity, key, value, meta) => { const r = value.r >= 0 && value.r <= 255 ? value.r : 0; const g = value.g >= 0 && value.g <= 255 ? value.g : 0; const b = value.b >= 0 && value.b <= 255 ? value.b : 0; const valueHex = r + g * 256 + b * 256 ** 2; await entity.write("manuSpecificSinope", { ledColorOff: valueHex }); }, }, minimum_brightness: { // DM25x0ZB key: ["minimum_brightness"], convertSet: async (entity, key, value, meta) => { const number = utils.toNumber(value); if (number >= 0 && number <= 3000) { await entity.write("manuSpecificSinope", { minimumBrightness: number }); } return { state: { minimumBrightness: number } }; }, convertGet: async (entity, key, meta) => { await entity.read("manuSpecificSinope", ["minimumBrightness"]); }, }, timer_seconds: { // DM25x0ZB and SW2500ZB key: ["timer_seconds"], convertSet: async (entity, key, value, meta) => { const number = utils.toNumber(value); if (number >= 0 && number <= 65535) { await entity.write("manuSpecificSinope", { dimmerTimmer: number }); } return { state: { timer_seconds: number } }; }, convertGet: async (entity, key, meta) => { await entity.read("manuSpecificSinope", ["dimmerTimmer"]); }, }, keypad_lockout: { // SW2500ZB key: ["keypad_lockout"], convertSet: async (entity, key, value, meta) => { const lookup = { unlock: 0, lock: 1 }; await entity.write("manuSpecificSinope", { keypadLockout: utils.getFromLookup(value, lookup) }); return { state: { keypad_lockout: value } }; }, convertGet: async (entity, key, meta) => { await entity.read("manuSpecificSinope", ["keypadLockout"]); }, }, low_water_temp_protection: { // RM3500ZB specific key: ["low_water_temp_protection"], convertSet: async (entity, key, value, meta) => { await entity.write("manuSpecificSinope", { drConfigWaterTempMin: value }); return { state: { low_water_temp_protection: value } }; }, convertGet: async (entity, key, meta) => { await entity.read("manuSpecificSinope", ["drConfigWaterTempMin"]); }, }, }; exports.definitions = [ { zigbeeModel: ["TH1123ZB"], model: "TH1123ZB", vendor: "Sinopé", description: "Zigbee line volt thermostat", extend: [m.electricityMeter()], fromZigbee: [fzLocal.thermostat, fzLocal.sinope, fz.hvac_user_interface, fz.ignore_temperature_report], toZigbee: [ tz.thermostat_local_temperature, tz.thermostat_occupied_heating_setpoint, tz.thermostat_unoccupied_heating_setpoint, tz.thermostat_temperature_display_mode, tz.thermostat_keypad_lockout, tz.thermostat_system_mode, tzLocal.backlight_autodim, tzLocal.thermostat_time, tzLocal.time_format, tzLocal.enable_outdoor_temperature, tzLocal.second_display_mode, tzLocal.thermostat_outdoor_temperature, tzLocal.outdoor_temperature_timeout, tzLocal.thermostat_occupancy, tzLocal.main_cycle_output, ], exposes: [ e .climate() .withSetpoint("occupied_heating_setpoint", 5, 30, 0.5) .withSetpoint("unoccupied_heating_setpoint", 5, 30, 0.5) .withLocalTemperature() .withSystemMode(["off", "heat"], ea.ALL, "Mode of the thermostat") .withPiHeatingDemand() .withRunningState(["idle", "heat"], ea.STATE), e.enum("thermostat_occupancy", ea.ALL, ["unoccupied", "occupied"]).withDescription("Occupancy state of the thermostat"), e .enum("second_display_mode", ea.ALL, ["auto", "setpoint", "outdoor temp"]) .withDescription('Displays the outdoor temperature and then returns to the set point in "auto" mode, or clears ' + 'in "outdoor temp" mode when expired.'), e .numeric("thermostat_outdoor_temperature", ea.ALL) .withUnit("°C") .withValueMin(-99.5) .withValueMax(99.5) .withValueStep(0.5) .withDescription("Outdoor temperature for the secondary display"), e .numeric("outdoor_temperature_timeout", ea.ALL) .withUnit("s") .withValueMin(30) .withValueMax(64800) .withPreset("15 min", 900, "15 minutes") .withPreset("30 min", 1800, "30 minutes") .withPreset("1 hour", 3600, "1 hour") .withDescription("Time in seconds after which the outdoor temperature is considered to have expired"), e .binary("enable_outdoor_temperature", ea.ALL, "ON", "OFF") .withDescription("DEPRECATED: Use second_display_mode or control via outdoor_temperature_timeout"), e .enum("temperature_display_mode", ea.ALL, ["celsius", "fahrenheit"]) .withDescription("The temperature format displayed on the thermostat screen"), e.enum("time_format", ea.ALL, ["24h", "12h"]).withDescription("The time format featured on the thermostat display"), e.enum("backlight_auto_dim", ea.ALL, ["on_demand", "sensing"]).withDescription("Control backlight dimming behavior"), e.enum("keypad_lockout", ea.ALL, ["unlock", "lock1"]).withDescription("Enables or disables the device’s buttons"), e.enum("main_cycle_output", ea.ALL, ["15_sec", "15_min"]).withDescription("The length of the control cycle: 15_sec=normal 15_min=fan"), ], configure: async (device, coordinatorEndpoint) => { const endpoint = device.getEndpoint(1); const binds = [ "genBasic", "genIdentify", "genGroups", "hvacThermostat", "hvacUserInterfaceCfg", "msTemperatureMeasurement", "manuSpecificSinope", ]; await reporting.bind(endpoint, coordinatorEndpoint, binds); await reporting.thermostatTemperature(endpoint); await reporting.thermostatPIHeatingDemand(endpoint); await reporting.thermostatOccupiedHeatingSetpoint(endpoint); await reporting.temperature(endpoint, { min: 1, max: 0xffff }); // Disable default reporting await endpoint.configureReporting("msTemperatureMeasurement", [ { attribute: "tolerance", minimumReportInterval: 1, maximumReportInterval: 0xffff, reportableChange: 1, }, ]); try { await reporting.thermostatSystemMode(endpoint); } catch { /* Not all support this */ } }, }, { zigbeeModel: ["TH1124ZB"], model: "TH1124ZB", vendor: "Sinopé", description: "Zigbee line volt thermostat", extend: [m.electricityMeter()], fromZigbee: [fzLocal.thermostat, fzLocal.sinope, fz.hvac_user_interface, fz.ignore_temperature_report], toZigbee: [ tz.thermostat_local_temperature, tz.thermostat_occupied_heating_setpoint, tz.thermostat_unoccupied_heating_setpoint, tz.thermostat_temperature_display_mode, tz.thermostat_keypad_lockout, tz.thermostat_system_mode, tzLocal.backlight_autodim, tzLocal.thermostat_time, tzLocal.time_format, tzLocal.enable_outdoor_temperature, tzLocal.second_display_mode, tzLocal.thermostat_outdoor_temperature, tzLocal.outdoor_temperature_timeout, tzLocal.thermostat_occupancy, tzLocal.main_cycle_output, ], exposes: [ e .climate() .withSetpoint("occupied_heating_setpoint", 5, 30, 0.5) .withSetpoint("unoccupied_heating_setpoint", 5, 30, 0.5) .withLocalTemperature() .withSystemMode(["off", "heat"], ea.ALL, "Mode of the thermostat") .withPiHeatingDemand() .withRunningState(["idle", "heat"], ea.STATE), e.enum("thermostat_occupancy", ea.ALL, ["unoccupied", "occupied"]).withDescription("Occupancy state of the thermostat"), e .enum("second_display_mode", ea.ALL, ["auto", "setpoint", "outdoor temp"]) .withDescription('Displays the outdoor temperature and then returns to the set point in "auto" mode, or clears ' + 'in "outdoor temp" mode when expired.'), e .numeric("thermostat_outdoor_temperature", ea.ALL) .withUnit("°C") .withValueMin(-99.5) .withValueMax(99.5) .withValueStep(0.5) .withDescription("Outdoor temperature for the secondary display"), e .numeric("outdoor_temperature_timeout", ea.ALL) .withUnit("s") .withValueMin(30) .withValueMax(64800) .withPreset("15 min", 900, "15 minutes") .withPreset("30 min", 1800, "30 minutes") .withPreset("1 hour", 3600, "1 hour") .withDescription("Time in seconds after which the outdoor temperature is considered to have expired"), e .binary("enable_outdoor_temperature", ea.ALL, "ON", "OFF") .withDescription("DEPRECATED: Use second_display_mode or control via outdoor_temperature_timeout"), e .enum("temperature_display_mode", ea.ALL, ["celsius", "fahrenheit"]) .withDescription("The temperature format displayed on the thermostat screen"), e.enum("time_format", ea.ALL, ["24h", "12h"]).withDescription("The time format featured on the thermostat display"), e.enum("backlight_auto_dim", ea.ALL, ["on_demand", "sensing"]).withDescription("Control backlight dimming behavior"), e.enum("keypad_lockout", ea.ALL, ["unlock", "lock1"]).withDescription("Enables or disables the device’s buttons"), e.enum("main_cycle_output", ea.ALL, ["15_sec", "15_min"]).withDescription("The length of the control cycle: 15_sec=normal 15_min=fan"), ], configure: async (device, coordinatorEndpoint) => { const endpoint = device.getEndpoint(1); const binds = [ "genBasic", "genIdentify", "genGroups", "hvacThermostat", "hvacUserInterfaceCfg", "msTemperatureMeasurement", "manuSpecificSinope", ]; await reporting.bind(endpoint, coordinatorEndpoint, binds); await reporting.thermostatTemperature(endpoint); await reporting.thermostatPIHeatingDemand(endpoint); await reporting.thermostatOccupiedHeatingSetpoint(endpoint); await reporting.temperature(endpoint, { min: 1, max: 0xffff }); // Disable default reporting await endpoint.configureReporting("msTemperatureMeasurement", [ { attribute: "tolerance", minimumReportInterval: 1, maximumReportInterval: 0xffff, reportableChange: 1, }, ]); try { await reporting.thermostatSystemMode(endpoint); } catch { /* Not all support this */ } }, }, { zigbeeModel: ["TH1123ZB-G2"], model: "TH1123ZB-G2", vendor: "Sinopé", description: "Zigbee line volt thermostat", extend: [m.electricityMeter()], fromZigbee: [fzLocal.thermostat, fzLocal.sinope, fz.hvac_user_interface, fz.ignore_temperature_report], toZigbee: [ tz.thermostat_local_temperature, tz.thermostat_occupied_heating_setpoint, tz.thermostat_unoccupied_heating_setpoint, tz.thermostat_temperature_display_mode, tz.thermostat_keypad_lockout, tz.thermostat_system_mode, tzLocal.backlight_autodim, tzLocal.thermostat_time, tzLocal.time_format, tzLocal.enable_outdoor_temperature, tzLocal.second_display_mode, tzLocal.thermostat_outdoor_temperature, tzLocal.outdoor_temperature_timeout, tzLocal.thermostat_occupancy, tzLocal.main_cycle_output, ], exposes: [ e .climate() .withSetpoint("occupied_heating_setpoint", 5, 30, 0.5) .withSetpoint("unoccupied_heating_setpoint", 5, 30, 0.5) .withLocalTemperature() .withSystemMode(["off", "heat"], ea.ALL, "Mode of the thermostat") .withPiHeatingDemand() .withRunningState(["idle", "heat"], ea.STATE), e.enum("thermostat_occupancy", ea.ALL, ["unoccupied", "occupied"]).withDescription("Occupancy state of the thermostat"), e .enum("second_display_mode", ea.ALL, ["auto", "setpoint", "outdoor temp"]) .withDescription('Displays the outdoor temperature and then returns to the set point in "auto" mode, or clears ' + 'in "outdoor temp" mode when expired.'), e .numeric("thermostat_outdoor_temperature", ea.ALL) .withUnit("°C") .withValueMin(-99.5) .withValueMax(99.5) .withValueStep(0.5) .withDescription("Outdoor temperature for the secondary display"), e .numeric("outdoor_temperature_timeout", ea.ALL) .withUnit("s") .withValueMin(30) .withValueMax(64800) .withPreset("15 min", 900, "15 minutes") .withPreset("30 min", 1800, "30 minutes") .withPreset("1 hour", 3600, "1 hour") .withDescription("Time in seconds after which the outdoor temperature is considered to have expired"), e .binary("enable_outdoor_temperature", ea.ALL, "ON", "OFF") .withDescription("DEPRECATED: Use second_display_mode or control via outdoor_temperature_timeout"), e .enum("temperature_display_mode", ea.ALL, ["celsius", "fahrenheit"]) .withDescription("The temperature format displayed on the thermostat screen"), e.enum("time_format", ea.ALL, ["24h", "12h"]).withDescription("The time format featured on the thermostat display"), e.enum("backlight_auto_dim", ea.ALL, ["on_demand", "sensing", "off"]).withDescription("Control backlight dimming behavior"), e.enum("keypad_lockout", ea.ALL, ["unlock", "lock1"]).withDescription("Enables or disables the device’s buttons"), e.enum("main_cycle_output", ea.ALL, ["15_sec", "15_min"]).withDescription("The length of the control cycle: 15_sec=normal 15_min=fan"), ], configure: async (device, coordinatorEndpoint) => { const endpoint = device.getEndpoint(1); const binds = [ "genBasic", "genIdentify", "genGroups", "hvacThermostat", "hvacUserInterfaceCfg", "msTemperatureMeasurement", "manuSpecificSinope", ]; await reporting.bind(endpoint, coordinatorEndpoint, binds); // This G2 version has limited memory space const thermostatDate = new Date(); const thermostatTimeSec = thermostatDate.getTime() / 1000; const thermostatTimezoneOffsetSec = thermostatDate.getTimezoneOffset() * 60; const currentTimeToDisplay = Math.round(thermostatTimeSec - thermostatTimezoneOffsetSec - 946684800); await endpoint.write("manuSpecificSinope", { currentTimeToDisplay }, manuSinope); await endpoint.write("manuSpecificSinope", { secondScreenBehavior: 0 }, manuSinope); // Mode auto await reporting.thermostatTemperature(endpoint); await reporting.thermostatPIHeatingDemand(endpoint); await reporting.thermostatOccupiedHeatingSetpoint(endpoint); await reporting.thermostatSystemMode(endpoint); await reporting.temperature(endpoint, { min: 1, max: 0xffff }); // Disable default reporting await endpoint.configureReporting("msTemperatureMeasurement", [ { attribute: "tolerance", minimumReportInterval: 1, maximumReportInterval: 0xffff, reportableChange: 1, }, ]); // Disable default reporting (not used by Sinope) await reporting.thermostatRunningState(endpoint, { min: 1, max: 0xffff }); try { await reporting.thermostatUnoccupiedHeatingSetpoint(endpoint); } catch { /* Do nothing */ } }, }, { zigbeeModel: ["TH1124ZB-G2"], model: "TH1124ZB-G2", vendor: "Sinopé", description: "Zigbee line volt thermostat", extend: [m.electricityMeter()], fromZigbee: [fzLocal.thermostat, fzLocal.sinope, fz.hvac_user_interface, fz.ignore_temperature_report], toZigbee: [ tz.thermostat_local_temperature, tz.thermostat_occupied_heating_setpoint, tz.thermostat_unoccupied_heating_setpoint, tz.thermostat_temperature_display_mode, tz.thermostat_keypad_lockout, tz.thermostat_system_mode, tzLocal.backlight_autodim, tzLocal.thermostat_time, tzLocal.time_format, tzLocal.enable_outdoor_temperature, tzLocal.second_display_mode, tzLocal.thermostat_outdoor_temperature, tzLocal.outdoor_temperature_timeout, tzLocal.thermostat_occupancy, tzLocal.main_cycle_output, ], exposes: [ e .climate() .withSetpoint("occupied_heating_setpoint", 5, 30, 0.5) .withSetpoint("unoccupied_heating_setpoint", 5, 30, 0.5) .withLocalTemperature() .withSystemMode(["off", "heat"], ea.ALL, "Mode of the thermostat") .withPiHeatingDemand() .withRunningState(["idle", "heat"], ea.STATE), e.enum("thermostat_occupancy", ea.ALL, ["unoccupied", "occupied"]).withDescription("Occupancy state of the thermostat"), e .enum("second_display_mode", ea.ALL, ["auto", "setpoint", "outdoor temp"]) .withDescription('Displays the outdoor temperature and then returns to the set point in "auto" mode, or clears ' + 'in "outdoor temp" mode when expired.'), e .numeric("thermostat_outdoor_temperature", ea.ALL) .withUnit("°C") .withValueMin(-99.5) .withValueMax(99.5) .withValueStep(0.5) .withDescription("Outdoor temperature for the secondary display"), e .numeric("outdoor_temperature_timeout", ea.ALL) .withUnit("s") .withValueMin(30) .withValueMax(64800) .withPreset("15 min", 900, "15 minutes") .withPreset("30 min", 1800, "30 minutes") .withPreset("1 hour", 3600, "1 hour") .withDescription("Time in seconds after which the outdoor temperature is considered to have expired"), e .binary("enable_outdoor_temperature", ea.ALL, "ON", "OFF") .withDescription("DEPRECATED: Use second_display_mode or control via outdoor_temperature_timeout"), e .enum("temperature_display_mode", ea.ALL, ["celsius", "fahrenheit"]) .withDescription("The temperature format displayed on the thermostat screen"), e.enum("time_format", ea.ALL, ["24h", "12h"]).withDescription("The time format featured on the thermostat display"), e.enum("backlight_auto_dim", ea.ALL, ["on_demand", "sensing", "off"]).withDescription("Control backlight dimming behavior"), e.enum("keypad_lockout", ea.ALL, ["unlock", "lock1"]).withDescription("Enables or disables the device’s buttons"), e.enum("main_cycle_output", ea.ALL, ["15_sec", "15_min"]).withDescription("The length of the control cycle: 15_sec=normal 15_min=fan"), ], configure: async (device, coordinatorEndpoint) => { const endpoint = device.getEndpoint(1); const binds = [ "genBasic", "genIdentify", "genGroups", "hvacThermostat", "hvacUserInterfaceCfg", "msTemperatureMeasurement", "manuSpecificSinope", ]; await reporting.bind(endpoint, coordinatorEndpoint, binds); // This G2 version has limited memory space const thermostatDate = new Date(); const thermostatTimeSec = thermostatDate.getTime() / 1000; const thermostatTimezoneOffsetSec = thermostatDate.getTimezoneOffset() * 60; const currentTimeToDisplay = Math.round(thermostatTimeSec - thermostatTimezoneOffsetSec - 946684800); await endpoint.write("manuSpecificSinope", { currentTimeToDisplay }, manuSinope); await endpoint.write("manuSpecificSinope", { secondScreenBehavior: 0 }, manuSinope); // Mode auto await reporting.thermostatTemperature(endpoint); await reporting.thermostatPIHeatingDemand(endpoint); aw