zigbee-herdsman-converters
Version:
Collection of device converters to be used with zigbee-herdsman
999 lines (998 loc) • 268 kB
JavaScript
"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;
},