UNPKG

zigbee-herdsman-converters

Version:

Collection of device converters to be used with zigbee-herdsman

999 lines (998 loc) 268 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 fz = __importStar(require("../converters/fromZigbee")); const tz = __importStar(require("../converters/toZigbee")); const exposes = __importStar(require("../lib/exposes")); const m = __importStar(require("../lib/modernExtend")); const namron = __importStar(require("../lib/namron")); const reporting = __importStar(require("../lib/reporting")); const utils = __importStar(require("../lib/utils")); //import {logger} from "../lib/logger"; const e = exposes.presets; const ea = exposes.access; //const NS = "zhc:slacky_diy"; const ppmReporting = { min: 10, max: 300, change: 0.000001 }; const batteryReporting = { min: 3600, max: 21600, change: 0 }; const temperatureReporting = { min: 10, max: 3600, change: 10 }; const humidityReporting = { min: 10, max: 3600, change: 10 }; const model_r01 = "THERM_SLACKY_DIY_R01"; //const model_r02 = "THERM_SLACKY_DIY_R02"; const model_r03 = "THERM_SLACKY_DIY_R03"; const model_r04 = "THERM_SLACKY_DIY_R04"; //const model_r05 = "THERM_SLACKY_DIY_R05"; const model_r06 = "THERM_SLACKY_DIY_R06"; const model_r07 = "THERM_SLACKY_DIY_R07"; const model_r08 = "THERM_SLACKY_DIY_R08"; const model_r09 = "THERM_SLACKY_DIY_R09"; //const model_r0a = "THERM_SLACKY_DIY_R0A"; const model_r0b = "THERM_SLACKY_DIY_R0B"; const modelR0c = "THERM_SLACKY_DIY_R0C"; const model_r0d = "THERM_SLACKY_DIY_R0D"; const attrThermSensorUser = 0xf000; const attrThermFrostProtect = 0xf001; const attrThermHeatProtect = 0xf002; const attrThermEcoMode = 0xf003; const attrThermEcoModeHeatTemperature = 0xf004; const attrThermFrostProtectOnOff = 0xf005; const attrThermSettingsReset = 0xf006; const attrThermScheduleMode = 0xf007; const attrThermSound = 0xf008; const attrThermLevel = 0xf009; const attrThermInversion = 0xf00a; const attrThermEcoModeCoolTemperature = 0xf00b; const attrThermExtTemperatureCalibration = 0xf00c; const attrThermModeKeyLock = 0xf00d; const attrThermManufName = 0xf00e; const attrThermScreenOffTime = 0xf00f; const attrThermLedIndicator = 0xf010; const attrFanCtrlControl = 0xf000; const switchSensorUsed = ["Inner (IN)", "All (AL)", "Outer (OU)"]; const attrElCityMeterModelPreset = 0xf000; const attrElCityMeterAddressPreset = 0xf001; const attrElCityMeterMeasurementPreset = 0xf002; const attrElCityMeterDateRelease = 0xf003; const attrElCityMeterModelName = 0xf004; const attrElCityMeterPasswordPreset = 0xf005; const attrSensorReadPeriod = 0xf000; const attrTemperatureOffset = 0xf001; const attrTemperatureOnOff = 0xf002; const attrTemperatureLow = 0xf003; const attrTemperatureHigh = 0xf004; const attrHumidityOffset = 0xf005; const attrHumidityOnOff = 0xf006; const attrHumidityLow = 0xf007; const attrHumidityHigh = 0xf008; const attrRepeatCommand = 0xf009; const attrCo2Calibration = 0xf008; const attrFeaturesSensors = 0xf009; const attrDisplayRotate = 0xf00a; const attrDisplayInversion = 0xf00b; const switchFeatures = ["nothing", "co2_forced_calibration", "co2_factory_reset", "bind_reset", ""]; const attrPlugKeyLock = 0xf000; const attrPlugLedCtrl = 0xf001; const attrPlugSwitchCurrentMax = 0xf002; const attrPlugSwitchPowerMax = 0xf003; const attrPlugSwitchTimeReload = 0xf004; const attrPlugSwitchProtectCtrl = 0xf005; const attrPlugSwitchAutoRestart = 0xf006; const attrSwitchType = 0xf000; const attrSwitchDecoupled = 0xf001; const attrDeviceModelNumber = 0xf002; const attrDoorDelayOn = 0xf003; const attrDoorDelayOff = 0xf004; const attrDoorOnCmdOff = 0xf005; const attrDoorOffCmdOff = 0xf006; const attrSceneId = 0xf000; const attrGroupId = 0xf001; const fzLocal = { thermostat_custom_fw: { cluster: "hvacThermostat", type: ["attributeReport", "readResponse"], convert: (model, msg, publish, options, meta) => { const result = {}; if (msg.data[attrThermSensorUser] !== undefined) { const lookup2 = { 0: "Inner (IN)", 1: "Outer (OU)" }; const lookup3 = { 0: "Inner (IN)", 1: "All (AL)", 2: "Outer (OU)" }; if (model.model === model_r0b) { result.sensor = utils.getFromLookup(msg.data[attrThermSensorUser], lookup2); } else { result.sensor = utils.getFromLookup(msg.data[attrThermSensorUser], lookup3); } } if (msg.data.minSetpointDeadBand !== undefined) { //logger.info(`Model: ${model.model}`, NS); let data; if (model.model === model_r06 || model.model === model_r09 || model.model === model_r0b || model.model === model_r0d) { data = msg.data.minSetpointDeadBand / 10; result.hysteresis_temperature = data; } else { data = msg.data.minSetpointDeadBand; result.deadzone_temperature = data; } //logger.info(`DeadBand: ${data}`, NS); } if (msg.data[attrThermFrostProtect] !== undefined) { const data = Number.parseInt(msg.data[attrThermFrostProtect], 10) / 100; result.frost_protect = data; } if (msg.data[attrThermHeatProtect] !== undefined) { const data = Number.parseInt(msg.data[attrThermHeatProtect], 10) / 100; result.heat_protect = data; } if (msg.data[attrThermEcoMode] !== undefined) { result.eco_mode = msg.data[attrThermEcoMode] === 1 ? "On" : "Off"; } if (msg.data[attrThermEcoModeCoolTemperature] !== undefined) { const data = Number.parseInt(msg.data[attrThermEcoModeCoolTemperature], 10) / 100; result.eco_mode_cool_temperature = data; } if (msg.data[attrThermEcoModeHeatTemperature] !== undefined) { const data = Number.parseInt(msg.data[attrThermEcoModeHeatTemperature], 10) / 100; result.eco_mode_heat_temperature = data; } if (msg.data[attrThermFrostProtectOnOff] !== undefined) { result.frost_protect_on_off = msg.data[attrThermFrostProtectOnOff] === 1 ? "On" : "Off"; } if (msg.data[attrThermLevel] !== undefined) { if (model.model === model_r0b) { const lookup_sleep = { 0: "Off", 1: "Dim", 2: "On" }; result.screen_sleep_mode = utils.getFromLookup(msg.data[attrThermLevel], lookup_sleep); } else { const lookup = { 0: "Off", 1: "Low", 2: "Medium", 3: "High" }; result.brightness_level = utils.getFromLookup(msg.data[attrThermLevel], lookup); } } if (msg.data[attrThermScreenOffTime] !== undefined) { if (model.model === model_r0d) { const lookup_time = { 0: "10s", 1: "20s", 2: "30s", 3: "40s", 4: "50s", 5: "60s" }; result.screen_sleep_time = utils.getFromLookup(msg.data[attrThermScreenOffTime], lookup_time); } } if (msg.data[attrThermLedIndicator] !== undefined) { result.led_indicator = msg.data[attrThermLedIndicator] === 1 ? "On" : "Off"; } if (msg.data[attrThermSound] !== undefined) { result.sound = msg.data[attrThermSound] === 1 ? "On" : "Off"; } if (msg.data[attrThermInversion] !== undefined) { result.relay_type = msg.data[attrThermInversion] === 1 ? "NO" : "NC"; } if (msg.data[attrThermModeKeyLock] !== undefined) { result.mode_child_lock = msg.data[attrThermModeKeyLock] === 1 ? "all" : "partial"; } if (msg.data[attrThermScheduleMode] !== undefined) { const lookup = { 0: "Off", 1: "5+2", 2: "6+1", 3: "7" }; result.schedule_mode = utils.getFromLookup(msg.data[attrThermScheduleMode], lookup); } if (msg.data[attrThermExtTemperatureCalibration] !== undefined) { const data = Number.parseInt(msg.data[attrThermExtTemperatureCalibration], 10) / 10; result.external_temperature_calibration = data; } return result; }, }, thermostat_schedule: { cluster: "hvacThermostat", type: ["commandSetWeeklySchedule", "commandGetWeeklyScheduleRsp"], convert: (model, msg, publish, options, meta) => { const { data } = msg; const daysOfWeekNums = [...Array.from(Array(7).keys()).filter((x) => (2 ** x) & data.dayofweek)]; // biome-ignore lint/suspicious/noExplicitAny: ignored using `--suppress` const schedule = `${data.transitions.map((t) => `${String(Math.floor(t.transitionTime / 60)).padStart(2, "0")}:${String(t.transitionTime % 60).padStart(2, "0")}/${t.heatSetpoint / 100.0}°C`).join(" ")}`; const daysOfWeek = ["sunday", "monday", "tuesday", "wednesday", "thursday", "friday", "saturday"]; return Object.fromEntries(daysOfWeekNums.map((d) => [`schedule_${daysOfWeek[d]}`, schedule])); }, }, fancontrol_control: { cluster: "hvacFanCtrl", type: ["attributeReport", "readResponse"], convert: (model, msg, publish, options, meta) => { const result = {}; if (msg.data[attrFanCtrlControl] !== undefined) { result.fan_control = msg.data[attrFanCtrlControl] === 1 ? "On" : "Off"; } return result; }, }, display_brightness: { cluster: "genLevelCtrl", type: ["attributeReport", "readResponse"], convert: (model, msg, publish, options, meta) => { const result = {}; if (msg.data.currentLevel !== undefined) { const property = `brightness_${utils.getEndpointName(msg, model, meta)}`; //logger.info('property: ' + property); return { [property]: msg.data.currentLevel }; } return result; }, }, thermostat_humidity_offset: { cluster: "msRelativeHumidity", type: ["attributeReport", "readResponse"], convert: (model, msg, publish, options, meta) => { const result = {}; if (msg.data[attrHumidityOffset] !== undefined) { const data = Number.parseInt(msg.data[attrHumidityOffset], 10) / 100; result.humidity_offset = data; } return result; }, }, }; const tzLocal = { display_brightness: { key: ["brightness", "brightness_day", "brightness_night"], options: [exposes.options.transition()], convertSet: async (entity, key, value, meta) => { await entity.command("genLevelCtrl", "moveToLevel", { level: value, transtime: 0, optionsMask: 0, optionsOverride: 0 }, utils.getOptions(meta.mapped, entity)); return { state: { brightness: value } }; }, convertGet: async (entity, key, meta) => { await entity.read("genLevelCtrl", ["currentLevel"]); }, }, thermostat_humidity_offset: { key: ["humidity_offset"], convertSet: async (entity, key, value, meta) => { utils.assertNumber(value); const humidity_offset = Number(Math.round(value)) * 100; await entity.write("msRelativeHumidity", { [attrHumidityOffset]: { value: humidity_offset, type: 0x29 } }); return { readAfterWriteTime: 250, state: { humidity_offset: value } }; }, convertGet: async (entity, key, meta) => { await entity.read("msRelativeHumidity", [attrHumidityOffset]); }, }, thermostat_sensor_used: { key: ["sensor"], convertSet: async (entity, key, value, meta) => { //const endpoint = meta.device.getEndpoint(1); const lookup2 = { "Inner (IN)": 0, "Outer (OU)": 1 }; const lookup3 = { "Inner (IN)": 0, "All (AL)": 1, "Outer (OU)": 2 }; if (meta.mapped.model === model_r0b) { await entity.write("hvacThermostat", { [attrThermSensorUser]: { value: utils.getFromLookup(value, lookup2), type: 0x30 } }); } else { await entity.write("hvacThermostat", { [attrThermSensorUser]: { value: utils.getFromLookup(value, lookup3), type: 0x30 } }); } return { readAfterWriteTime: 250, state: { sensor: value } }; }, convertGet: async (entity, key, meta) => { await entity.read("hvacThermostat", [attrThermSensorUser]); }, }, thermostat_deadzone: { key: ["deadzone_temperature"], convertSet: async (entity, key, value, meta) => { utils.assertNumber(value); const minSetpointDeadBand = Number(Math.round(value)); await entity.write("hvacThermostat", { minSetpointDeadBand }); return { readAfterWriteTime: 250, state: { minSetpointDeadBand: value } }; }, convertGet: async (entity, key, meta) => { await entity.read("hvacThermostat", ["minSetpointDeadBand"]); }, }, thermostat_deadzone_10: { key: ["hysteresis_temperature"], convertSet: async (entity, key, value, meta) => { utils.assertNumber(value); const minSetpointDeadBand = Number(value) * 10; await entity.write("hvacThermostat", { minSetpointDeadBand }); return { readAfterWriteTime: 250, state: { hysteresis_temperature: value } }; }, convertGet: async (entity, key, meta) => { await entity.read("hvacThermostat", ["minSetpointDeadBand"]); }, }, thermostat_frost_protect: { key: ["frost_protect"], convertSet: async (entity, key, value, meta) => { utils.assertNumber(value); //if (!utils.isInRange(0, 10, Number(value))) throw new Error(`Invalid value: ${value} (expected ${0} to ${10})`); const frost_protect = Number(Math.round(value)) * 100; await entity.write("hvacThermostat", { [attrThermFrostProtect]: { value: frost_protect, type: 0x29 } }); return { readAfterWriteTime: 250, state: { frost_protect: value } }; }, convertGet: async (entity, key, meta) => { await entity.read("hvacThermostat", [attrThermFrostProtect]); }, }, thermostat_heat_protect: { key: ["heat_protect"], convertSet: async (entity, key, value, meta) => { utils.assertNumber(value); //if (!utils.isInRange(25, 70, Number(value))) throw new Error(`Invalid value: ${value} (expected ${25} to ${70})`); const heat_protect = Number(Math.round(value)) * 100; await entity.write("hvacThermostat", { [attrThermHeatProtect]: { value: heat_protect, type: 0x29 } }); return { readAfterWriteTime: 250, state: { heat_protect: value } }; }, convertGet: async (entity, key, meta) => { await entity.read("hvacThermostat", [attrThermHeatProtect]); }, }, thermostat_setpoint_raise_lower: { key: ["setpoint_raise_lower"], convertSet: async (entity, key, value, meta) => { utils.assertNumber(value); //if (!utils.isInRange(-5, 5, Number(value))) throw new Error(`Invalid value: ${value} (expected ${-5} to ${5})`); const setpoint_raise_lower = Number(Math.fround(value)) * 10; //Step 0.1°C. 5°C - 50, 1°C - 10 etc. await entity.command("hvacThermostat", "setpointRaiseLower", { mode: 0, amount: setpoint_raise_lower }); return { readAfterWriteTime: 250, state: { setpoint_raise_lower: value } }; }, }, fancontrol_control: { key: ["fan_control"], convertSet: async (entity, key, value, meta) => { const fan_control = Number(value === "On"); await entity.write("hvacFanCtrl", { [attrFanCtrlControl]: { value: fan_control, type: 0x10 } }); return { readAfterWriteTime: 250, state: { fan_control: value } }; }, convertGet: async (entity, key, meta) => { await entity.read("hvacFanCtrl", [attrFanCtrlControl]); }, }, thermostat_eco_mode: { key: ["eco_mode"], convertSet: async (entity, key, value, meta) => { const eco_mode = Number(value === "On"); await entity.write("hvacThermostat", { [attrThermEcoMode]: { value: eco_mode, type: 0x30 } }); return { readAfterWriteTime: 250, state: { eco_mode: value } }; }, convertGet: async (entity, key, meta) => { await entity.read("hvacThermostat", [attrThermEcoMode]); }, }, thermostat_eco_mode_cool_temperature: { key: ["eco_mode_cool_temperature"], convertSet: async (entity, key, value, meta) => { utils.assertNumber(value); //if (!utils.isInRange(5, 45, Number(value))) throw new Error(`Invalid value: ${value} (expected ${5} to ${45})`); const eco_mode_cool_temperature = Number(Math.round(value)) * 100; await entity.write("hvacThermostat", { [attrThermEcoModeCoolTemperature]: { value: eco_mode_cool_temperature, type: 0x29 } }); return { readAfterWriteTime: 250, state: { eco_mode_cool_temperature: value } }; }, convertGet: async (entity, key, meta) => { await entity.read("hvacThermostat", [attrThermEcoModeCoolTemperature]); }, }, thermostat_eco_mode_heat_temperature: { key: ["eco_mode_heat_temperature"], convertSet: async (entity, key, value, meta) => { utils.assertNumber(value); //if (!utils.isInRange(5, 45, Number(value))) throw new Error(`Invalid value: ${value} (expected ${5} to ${45})`); const eco_mode_heat_temperature = Number(Math.round(value)) * 100; await entity.write("hvacThermostat", { [attrThermEcoModeHeatTemperature]: { value: eco_mode_heat_temperature, type: 0x29 } }); return { readAfterWriteTime: 250, state: { eco_mode_heat_temperature: value } }; }, convertGet: async (entity, key, meta) => { await entity.read("hvacThermostat", [attrThermEcoModeHeatTemperature]); }, }, thermostat_frost_protect_onoff: { key: ["frost_protect_on_off"], convertSet: async (entity, key, value, meta) => { const frost_protect_on_off = Number(value === "On"); await entity.write("hvacThermostat", { [attrThermFrostProtectOnOff]: { value: frost_protect_on_off, type: 0x10 } }); return { readAfterWriteTime: 250, state: { frost_protect_on_off: value } }; }, convertGet: async (entity, key, meta) => { await entity.read("hvacThermostat", [attrThermFrostProtectOnOff]); }, }, thermostat_sound: { key: ["sound"], convertSet: async (entity, key, value, meta) => { const sound = Number(value === "On"); await entity.write("hvacThermostat", { [attrThermSound]: { value: sound, type: 0x10 } }); return { readAfterWriteTime: 250, state: { sound: value } }; }, convertGet: async (entity, key, meta) => { await entity.read("hvacThermostat", [attrThermSound]); }, }, thermostat_brightness_level: { key: ["brightness_level"], convertSet: async (entity, key, value, meta) => { //utils.assertNumber(value); const lookup = { Off: 0, Low: 1, Medium: 2, High: 3 }; await entity.write("hvacThermostat", { [attrThermLevel]: { value: utils.getFromLookup(value, lookup), type: 0x30 } }); return { state: { brightness_level: value } }; }, convertGet: async (entity, key, meta) => { await entity.read("hvacThermostat", [attrThermLevel]); }, }, thermostat_screen_sleep_mode: { key: ["screen_sleep_mode"], convertSet: async (entity, key, value, meta) => { //utils.assertNumber(value); const lookup = { Off: 0, Dim: 1, On: 2 }; await entity.write("hvacThermostat", { [attrThermLevel]: { value: utils.getFromLookup(value, lookup), type: 0x30 } }); return { state: { screen_sleep_mode: value } }; }, convertGet: async (entity, key, meta) => { await entity.read("hvacThermostat", [attrThermLevel]); }, }, thermostat_screen_sleep_time: { key: ["screen_sleep_time"], convertSet: async (entity, key, value, meta) => { //utils.assertNumber(value); const lookup = { "10s": 0, "20s": 1, "30s": 2, "40s": 3, "50s": 4, "60s": 5 }; await entity.write("hvacThermostat", { [attrThermScreenOffTime]: { value: utils.getFromLookup(value, lookup), type: 0x30 } }); return { state: { screen_sleep_time: value } }; }, convertGet: async (entity, key, meta) => { await entity.read("hvacThermostat", [attrThermScreenOffTime]); }, }, thermostat_led_indicator: { key: ["led_indicator"], convertSet: async (entity, key, value, meta) => { const led_indicator = Number(value === "On"); await entity.write("hvacThermostat", { [attrThermLedIndicator]: { value: led_indicator, type: 0x10 } }); return { readAfterWriteTime: 250, state: { led_indicator: value } }; }, convertGet: async (entity, key, meta) => { await entity.read("hvacThermostat", [attrThermLedIndicator]); }, }, thermostat_inversion: { key: ["relay_type"], convertSet: async (entity, key, value, meta) => { const relay_type = Number(value === "NO"); await entity.write("hvacThermostat", { [attrThermInversion]: { value: relay_type, type: 0x10 } }); return { readAfterWriteTime: 250, state: { relay_type: value } }; }, convertGet: async (entity, key, meta) => { await entity.read("hvacThermostat", [attrThermInversion]); }, }, thermostat_mode_child_lock: { key: ["mode_child_lock"], convertSet: async (entity, key, value, meta) => { const mode_child_lock = Number(value === "all"); await entity.write("hvacThermostat", { [attrThermModeKeyLock]: { value: mode_child_lock, type: 0x10 } }); return { readAfterWriteTime: 250, state: { mode_child_lock: value } }; }, convertGet: async (entity, key, meta) => { await entity.read("hvacThermostat", [attrThermModeKeyLock]); }, }, thermostat_schedule_mode: { key: ["schedule_mode"], convertSet: async (entity, key, value, meta) => { //utils.assertNumber(value); const lookup = { off: 0, "5+2": 1, "6+1": 2, "7": 3 }; await entity.write("hvacThermostat", { [attrThermScheduleMode]: { value: utils.getFromLookup(value, lookup), type: 0x30 } }); return { state: { schedule_mode: value } }; }, convertGet: async (entity, key, meta) => { await entity.read("hvacThermostat", [attrThermScheduleMode]); }, }, thermostat_settings_reset: { key: ["settings_reset"], convertSet: async (entity, key, value, meta) => { const settings_reset = Number(value === "Default"); await entity.write("hvacThermostat", { [attrThermSettingsReset]: { value: settings_reset, type: 0x10 } }); return { readAfterWriteTime: 250, state: { settings_reset: value } }; }, }, thermostat_ext_temperature_calibration: { key: ["external_temperature_calibration"], convertSet: async (entity, key, value, meta) => { utils.assertNumber(value); //if (!utils.isInRange(-9, 9, Number(value))) throw new Error(`Invalid value: ${value} (expected ${-9} to ${9})`); const external_temperature_calibration = Number(Math.round(value)) * 10; await entity.write("hvacThermostat", { [attrThermExtTemperatureCalibration]: { value: external_temperature_calibration, type: 0x28 } }); return { readAfterWriteTime: 250, state: { external_temperature_calibration: value } }; }, convertGet: async (entity, key, meta) => { await entity.read("hvacThermostat", [attrThermExtTemperatureCalibration]); }, }, thermostat_manuf_name: { key: ["manuf_name"], convertSet: async (entity, key, value, meta) => { const lookup = { R00: 0, R01: 1, R02: 2, R03: 3, R04: 4, R05: 5, R06: 6, R07: 7, R08: 8, R09: 9, R0A: 10, R0B: 11, R0C: 12, R0D: 13 }; await entity.write("hvacThermostat", { [attrThermManufName]: { value: utils.getFromLookup(value, lookup), type: 0x30 } }); return { state: { manuf_name: value } }; }, convertGet: async (entity, key, meta) => { await entity.read("hvacThermostat", [attrThermManufName]); }, }, device_model_number: { key: ["door_device_number", "switch_device_number"], convertSet: async (entity, key, value, meta) => { const lookup = { model_0: 0, model_1: 1, model_2: 2, model_3: 3, model_4: 4, model_5: 5 }; await entity.write("genOnOffSwitchCfg", { [attrDeviceModelNumber]: { value: utils.getFromLookup(value, lookup), type: 0x30 } }); return { state: { [key]: value } }; }, convertGet: async (entity, key, meta) => { await entity.read("genOnOffSwitchCfg", [attrDeviceModelNumber]); }, }, }; const localFromZigbeeThermostat = [ fz.humidity, fz.thermostat, fz.fan, namron.fromZigbee.namron_hvac_user_interface, fz.thermostat_weekly_schedule, fzLocal.thermostat_custom_fw, fzLocal.thermostat_schedule, fzLocal.display_brightness, fzLocal.fancontrol_control, fzLocal.thermostat_humidity_offset, ]; const localToZigbeeThermostat = [ tz.thermostat_local_temperature, tz.thermostat_outdoor_temperature, tz.thermostat_system_mode, tz.thermostat_occupied_heating_setpoint, tz.thermostat_running_state, tz.thermostat_local_temperature_calibration, tz.thermostat_min_heat_setpoint_limit, tz.thermostat_max_heat_setpoint_limit, tz.thermostat_programming_operation_mode, namron.toZigbee.namron_thermostat_child_lock, tz.thermostat_weekly_schedule, tz.fan_mode, tzLocal.display_brightness, tzLocal.thermostat_sensor_used, tzLocal.thermostat_deadzone, tzLocal.thermostat_deadzone_10, tzLocal.thermostat_setpoint_raise_lower, tzLocal.thermostat_frost_protect, tzLocal.thermostat_heat_protect, tzLocal.thermostat_eco_mode, tzLocal.thermostat_eco_mode_cool_temperature, tzLocal.thermostat_eco_mode_heat_temperature, tzLocal.thermostat_frost_protect_onoff, tzLocal.thermostat_brightness_level, tzLocal.thermostat_screen_sleep_mode, tzLocal.thermostat_screen_sleep_time, tzLocal.thermostat_led_indicator, tzLocal.thermostat_sound, tzLocal.thermostat_inversion, tzLocal.thermostat_schedule_mode, tzLocal.thermostat_settings_reset, tzLocal.thermostat_ext_temperature_calibration, tzLocal.thermostat_mode_child_lock, tzLocal.thermostat_manuf_name, tzLocal.fancontrol_control, tzLocal.thermostat_humidity_offset, ]; function localActionExtend(args = {}) { const { localAction = ["hold", "single", "double", "triple", "quadruple", "quintuple", "release"], bind = true, reporting = true, reportingConfig = { min: 10, max: 0, change: 1 }, endpointNames = undefined, } = args; let actions = localAction; if (endpointNames) { actions = localAction.flatMap((c) => endpointNames.map((e) => `${c}_${e}`)); } const exposes = [e.enum("action", ea.STATE, actions)]; const actionPayloadLookup = { 0: "hold", 1: "single", 2: "double", 3: "triple", 4: "quadruple", 5: "quintuple", 255: "release", }; const fromZigbee = [ { cluster: "genMultistateInput", type: ["attributeReport", "readResponse"], convert: (model, msg, publish, options, meta) => { if (utils.hasAlreadyProcessedMessage(msg, model)) return; const value = msg.data.presentValue; //logger.logger.info('msg.data: ' + data[attribute]); if (value === 300) return { action: "N/A" }; const payload = { action: utils.postfixWithEndpointName(actionPayloadLookup[value], msg, model, meta) }; return payload; }, }, ]; const result = { exposes, fromZigbee, isModernExtend: true }; if (reporting) result.configure = [ m.setupConfigureForBinding("genMultistateInput", "input", endpointNames), m.setupConfigureForReporting("genMultistateInput", "presentValue", { config: reportingConfig, access: ea.GET, endpointNames }), ]; else if (bind) result.configure = [m.setupConfigureForBinding("genMultistateInput", "input", endpointNames)]; return result; } async function configureCommon(device, coordinatorEndpoint, definition) { //logger.info(definition.model, NS); const endpoint1 = device.getEndpoint(1); const endpoint2 = device.getEndpoint(2); await endpoint1.read("hvacUserInterfaceCfg", ["keypadLockout"]); await endpoint1.read("genLevelCtrl", ["currentLevel"]); await endpoint2.read("genLevelCtrl", ["currentLevel"]); await endpoint1.read("hvacThermostat", ["localTemp"]); await endpoint1.read("hvacThermostat", ["outdoorTemp"]); await endpoint1.read("hvacThermostat", ["absMinHeatSetpointLimit"]); await endpoint1.read("hvacThermostat", ["absMaxHeatSetpointLimit"]); await endpoint1.read("hvacThermostat", ["minHeatSetpointLimit"]); await endpoint1.read("hvacThermostat", ["maxHeatSetpointLimit"]); await endpoint1.read("hvacThermostat", ["localTemperatureCalibration"]); await endpoint1.read("hvacThermostat", ["occupiedHeatingSetpoint"]); await endpoint1.read("hvacThermostat", ["programingOperMode"]); await endpoint1.read("hvacThermostat", ["systemMode"]); await endpoint1.read("hvacThermostat", ["runningState"]); await endpoint1.read("hvacThermostat", ["minSetpointDeadBand"]); await endpoint1.read("hvacThermostat", [attrThermSensorUser]); await endpoint1.read("hvacThermostat", [attrThermFrostProtect]); await endpoint1.read("hvacThermostat", [attrThermHeatProtect]); await endpoint1.read("hvacThermostat", [attrThermEcoMode]); await endpoint1.read("hvacThermostat", [attrThermEcoModeCoolTemperature]); await endpoint1.read("hvacThermostat", [attrThermEcoModeHeatTemperature]); await endpoint1.read("hvacThermostat", [attrThermFrostProtectOnOff]); await endpoint1.read("hvacThermostat", [attrThermScheduleMode]); await endpoint1.read("hvacThermostat", [attrThermSound]); await endpoint1.read("hvacThermostat", [attrThermLevel]); await endpoint1.read("hvacThermostat", [attrThermInversion]); await endpoint1.read("hvacThermostat", [attrThermExtTemperatureCalibration]); if (definition.model === model_r09) { await endpoint1.read("hvacThermostat", [attrThermModeKeyLock]); } await endpoint1.read("hvacFanCtrl", ["fanMode"]); await endpoint1.read("hvacFanCtrl", [attrFanCtrlControl]); await reporting.bind(endpoint1, coordinatorEndpoint, ["hvacThermostat", "hvacUserInterfaceCfg", "hvacFanCtrl"]); if (definition.model === modelR0c) { await reporting.bind(endpoint1, coordinatorEndpoint, ["msRelativeHumidity"]); await endpoint1.read("msRelativeHumidity", ["measuredValue"]); await endpoint1.read("msRelativeHumidity", [attrHumidityOffset]); const payload_humidity = [ { attribute: { ID: 0x0000, type: 0x21 }, minimumReportInterval: 10, maximumReportInterval: 3600, reportableChange: 10 }, ]; await endpoint1.configureReporting("msRelativeHumidity", payload_humidity); const payload_humidity_offset = [ { attribute: { ID: attrHumidityOffset, type: 0x29 }, minimumReportInterval: 0, maximumReportInterval: 3600, reportableChange: 0 }, ]; await endpoint1.configureReporting("msRelativeHumidity", payload_humidity_offset); } if (definition.model === model_r03 || definition.model === model_r04 || definition.model === model_r09 || definition.model === modelR0c) { await reporting.bind(endpoint1, coordinatorEndpoint, ["genLevelCtrl"]); const payloadCurrentLevel = [ { attribute: { ID: 0x0000, type: 0x20 }, minimumReportInterval: 0, maximumReportInterval: 3600, reportableChange: 0 }, ]; await endpoint1.configureReporting("genLevelCtrl", payloadCurrentLevel); if (definition.model === model_r03 || definition.model === model_r09) { await reporting.bind(endpoint2, coordinatorEndpoint, ["genLevelCtrl"]); await endpoint2.configureReporting("genLevelCtrl", payloadCurrentLevel); } } await reporting.thermostatTemperature(endpoint1, { min: 0, max: 3600, change: 0 }); await reporting.thermostatOccupiedHeatingSetpoint(endpoint1, { min: 0, max: 3600, change: 0 }); await reporting.thermostatRunningState(endpoint1, { min: 0, max: 3600, change: 0 }); await reporting.thermostatSystemMode(endpoint1, { min: 0, max: 3600, change: 0 }); await reporting.thermostatTemperatureCalibration(endpoint1, { min: 0, max: 3600, change: 0 }); await reporting.thermostatKeypadLockMode(endpoint1, { min: 0, max: 3600, change: 0 }); const payload_oper_mode = [ { attribute: "programingOperMode", minimumReportInterval: 0, maximumReportInterval: 3600, reportableChange: 0 }, ]; await endpoint1.configureReporting("hvacThermostat", payload_oper_mode); const payload_sensor_used = [ { attribute: { ID: attrThermSensorUser, type: 0x30 }, minimumReportInterval: 0, maximumReportInterval: 3600, reportableChange: 0 }, ]; await endpoint1.configureReporting("hvacThermostat", payload_sensor_used); const payload_deadzone = [{ attribute: { ID: 0x0019, type: 0x28 }, minimumReportInterval: 0, maximumReportInterval: 3600, reportableChange: 0 }]; await endpoint1.configureReporting("hvacThermostat", payload_deadzone); const payload_min = [{ attribute: { ID: 0x0015, type: 0x29 }, minimumReportInterval: 0, maximumReportInterval: 3600, reportableChange: 0 }]; await endpoint1.configureReporting("hvacThermostat", payload_min); const payload_max = [{ attribute: { ID: 0x0016, type: 0x29 }, minimumReportInterval: 0, maximumReportInterval: 3600, reportableChange: 0 }]; await endpoint1.configureReporting("hvacThermostat", payload_max); if (definition.model !== model_r01 && definition.model !== model_r06 && definition.model !== model_r0b) { const payload_outdoor = [{ attribute: { ID: 0x0001, type: 0x29 }, minimumReportInterval: 0, maximumReportInterval: 3600, reportableChange: 0 }]; await endpoint1.configureReporting("hvacThermostat", payload_outdoor); } const payload_frost_protect = [ { attribute: { ID: attrThermFrostProtect, type: 0x29 }, minimumReportInterval: 0, maximumReportInterval: 3600, reportableChange: 0, }, ]; await endpoint1.configureReporting("hvacThermostat", payload_frost_protect); const payload_heat_protect = [ { attribute: { ID: attrThermHeatProtect, type: 0x29 }, minimumReportInterval: 0, maximumReportInterval: 3600, reportableChange: 0 }, ]; await endpoint1.configureReporting("hvacThermostat", payload_heat_protect); if (definition.model === model_r03 || definition.model === model_r04 || definition.model === model_r07 || definition.model === modelR0c) { const payload_eco_mode = [ { attribute: { ID: attrThermEcoMode, type: 0x30 }, minimumReportInterval: 0, maximumReportInterval: 3600, reportableChange: 0 }, ]; await endpoint1.configureReporting("hvacThermostat", payload_eco_mode); const payload_eco_mode_heat_temp = [ { attribute: { ID: attrThermEcoModeHeatTemperature, type: 0x29 }, minimumReportInterval: 0, maximumReportInterval: 3600, reportableChange: 0, }, ]; await endpoint1.configureReporting("hvacThermostat", payload_eco_mode_heat_temp); if (definition.model === model_r07) { const payload_eco_mode_cool_temp = [ { attribute: { ID: attrThermEcoModeCoolTemperature, type: 0x29 }, minimumReportInterval: 0, maximumReportInterval: 3600, reportableChange: 0, }, ]; await endpoint1.configureReporting("hvacThermostat", payload_eco_mode_cool_temp); const payload_fan_control = [ { attribute: { ID: attrFanCtrlControl, type: 0x10 }, minimumReportInterval: 0, maximumReportInterval: 3600, reportableChange: 0 }, ]; await endpoint1.configureReporting("hvacFanCtrl", payload_fan_control); await reporting.fanMode(endpoint1, { min: 0, max: 3600, change: 0 }); } } if (definition.model === model_r06) { const payload_frost_protect_onoff = [ { attribute: { ID: attrThermFrostProtectOnOff, type: 0x10 }, minimumReportInterval: 0, maximumReportInterval: 3600, reportableChange: 0 }, ]; await endpoint1.configureReporting("hvacThermostat", payload_frost_protect_onoff); const payload_sound = [ { attribute: { ID: attrThermSound, type: 0x10 }, minimumReportInterval: 0, maximumReportInterval: 3600, reportableChange: 0 }, ]; await endpoint1.configureReporting("hvacThermostat", payload_sound); const payload_inversion = [ { attribute: { ID: attrThermInversion, type: 0x10 }, minimumReportInterval: 0, maximumReportInterval: 3600, reportableChange: 0 }, ]; await endpoint1.configureReporting("hvacThermostat", payload_inversion); const payload_level = [ { attribute: { ID: attrThermLevel, type: 0x30 }, minimumReportInterval: 0, maximumReportInterval: 3600, reportableChange: 0 }, ]; await endpoint1.configureReporting("hvacThermostat", payload_level); const payload_schedule_mode = [ { attribute: { ID: attrThermScheduleMode, type: 0x30 }, minimumReportInterval: 0, maximumReportInterval: 3600, reportableChange: 0 }, ]; await endpoint1.configureReporting("hvacThermostat", payload_schedule_mode); } if (definition.model === model_r09) { const payload_inversion = [ { attribute: { ID: attrThermInversion, type: 0x10 }, minimumReportInterval: 0, maximumReportInterval: 3600, reportableChange: 0 }, ]; await endpoint1.configureReporting("hvacThermostat", payload_inversion); } if (definition.model === model_r08 || definition.model === model_r09) { const payload_eco_mode = [ { attribute: { ID: attrThermEcoMode, type: 0x30 }, minimumReportInterval: 0, maximumReportInterval: 3600, reportableChange: 0 }, ]; await endpoint1.configureReporting("hvacThermostat", payload_eco_mode); const payload_ext_temp_calibration = [ { attribute: { ID: attrThermExtTemperatureCalibration, type: 0x28 }, minimumReportInterval: 0, maximumReportInterval: 3600, reportableChange: 0, }, ]; await endpoint1.configureReporting("hvacThermostat", payload_ext_temp_calibration); } } const energyResetExtend = { energyReset: () => { const exposes = [e.enum("energy_reset", ea.SET, ["reset"]).withDescription("Reset of accumulated energy")]; const toZigbee = [ { key: ["energy_reset"], convertSet: async (entity, key, value, meta) => { await entity.command("seMetering", 0x80, {}, utils.getOptions(meta.mapped, entity)); }, }, ]; // const fromZigbee = []; return { exposes, fromZigbee: [], toZigbee, isModernExtend: true, }; }, }; function electricityDeviceModel(args) { const { name, lookup, cluster, attribute, zigbeeCommandOptions } = args; const attributeKey = utils.isString(attribute) ? attribute : attribute.ID; const access = ea[args.access ?? "ALL"]; const mExtend = m.enumLookup(args); const toZigbee = [ { key: [name], convertSet: access & ea.SET ? async (entity, key, value, meta) => { const payloadValue = utils.getFromLookup(value, lookup); const payload = utils.isString(attribute) ? { [attribute]: payloadValue } : { [attribute.ID]: { value: payloadValue, type: attribute.type } }; const ep = utils.determineEndpoint(entity, meta, cluster); await ep.write(cluster, payload, zigbeeCommandOptions); await ep.read("seMetering", ["divisor", "multiplier"]); await ep.read("haElectricalMeasurement", ["acPowerDivisor", "acPowerMultiplier"]); await ep.read("haElectricalMeasurement", ["acCurrentDivisor", "acCurrentMultiplier"]); await ep.read("haElectricalMeasurement", ["acVoltageDivisor", "acVoltageMultiplier"]); return { state: { [key]: value } }; } : undefined, convertGet: access & ea.GET ? async (entity, key, meta) => { await utils .determineEndpoint(entity, meta, cluster) .read(cluster, [attributeKey], zigbeeCommandOptions); } : undefined, }, ]; return { ...mExtend, toZigbee, isModernExtend: true }; } const electricityMeterExtend = { elMeter: () => { const exposes = [ e.numeric("energy_tier_1", ea.STATE_GET).withUnit("kWh").withDescription("Energy consumed at Tier 1"), e.numeric("energy_tier_2", ea.STATE_GET).withUnit("kWh").withDescription("Energy consumed at Tier 2"), e.numeric("energy_tier_3", ea.STATE_GET).withUnit("kWh").withDescription("Energy consumed at Tier 3"), e.numeric("energy_tier_4", ea.STATE_GET).withUnit("kWh").withDescription("Energy consumed at Tier 4"), e.text("model_name", ea.STATE_GET).withDescription("Meter Model Name"), e.text("serial_number", ea.STATE_GET).withDescription("Meter Serial Number"), e.text("date_release", ea.STATE_GET).withDescription("Meter Date Release"), e.numeric("battery_life", ea.STATE_GET).withUnit("%").withDescription("Battery Life"), e.binary("tamper", ea.STATE, true, false).withDescription("Tamper"), e.binary("battery_low", ea.STATE, true, false).withDescription("Battery Low"), e.numeric("device_address_preset", ea.STATE_SET).withDescription("Device Address").withValueMin(1).withValueMax(9999999), e.text("device_password_preset", ea.STATE_SET).withDescription("Meter Password"), e.numeric("device_measurement_preset", ea.ALL).withDescription("Measurement Period").withValueMin(1).withValueMax(255), ]; const toZigbee = [ tz.currenttier1summdelivered, tz.currenttier2summdelivered, tz.currenttier3summdelivered, tz.currenttier4summdelivered, { key: ["model_name"], convertGet: async (entity, key, meta) => { await entity.read("seMetering", ["deviceName"]); }, convertSet: async (entity, key, value, meta) => { return await null; }, }, { key: ["serial_number"], convertGet: async (entity, key, meta) => { await entity.read("seMetering", ["meterSerialNumber"]); }, convertSet: async (entity, key, value, meta) => { return await null; }, }, { key: ["date_release"], convertGet: async (entity, key, meta) => { await entity.read("seMetering", ["dateRelease"]); }, convertSet: async (entity, key, value, meta) => { return await null; }, }, { key: ["battery_life"], convertGet: async (entity, key, meta) => { await entity.read("seMetering", ["remainingBattLife"]); }, convertSet: async (entity, key, value, meta) => { return await null; }, }, { key: ["device_address_preset"], convertSet: async (entity, key, value, meta) => { const device_address_preset = value; await entity.write("seMetering", { deviceAddress: device_address_preset }); return { readAfterWriteTime: 250, state: { device_address_preset: value } }; }, }, { key: ["device_password_preset"], convertSet: async (entity, key, value, meta) => { const device_password_preset = value.toString(); await entity.write("seMetering", { devicePassword: device_password_preset }); return { readAfterWriteTime: 250, state: { device_password_preset: value } }; }, }, { key: ["device_measurement_preset"], convertSet: async (entity, key, value, meta) => { const device_measurement_preset = value; await entity.write("seMetering", { readPeriod: device_measurement_preset }); return { readAfterWriteTime: 250, state: { device_measurement_preset: value } }; }, convertGet: async (entity, key, meta) => { await entity.read("seMetering", ["readPeriod"]); }, }, ]; const fromZigbee = [ fz.metering, { cluster: "seMetering", type: ["attributeReport", "readResponse"], convert: (model, msg, publish, options, meta) => { const result = {}; if (msg.data.deviceName !== undefined) { const data = msg.data.deviceName; result.model_name = data.toString(); } return result; }, }, { cluster: "seMetering", type: ["attributeReport", "readResponse"], convert: (model, msg, publish, options, meta) => { const result = {}; if (msg.data.meterSerialNumber !== undefined) { const data = msg.data.meterSerialNumber; result.serial_number = data.toString(); } return result; }, }, { cluster: "seMetering", type: ["attributeReport", "readResponse"], convert: (model, msg, publish, options, meta) => { const result = {}; if (msg.data.dateRelease !== undefined) { const data = msg.data.dateRelease; result.date_release = data.toString(); } return result; },