zigbee-herdsman-converters
Version:
Collection of device converters to be used with zigbee-herdsman
1,036 lines • 83.5 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 reporting = __importStar(require("../lib/reporting"));
const globalStore = __importStar(require("../lib/store"));
const utils = __importStar(require("../lib/utils"));
const NS = "zhc:slacky-diy";
const e = exposes.presets;
const ea = exposes.access;
const defaultReporting = { min: 0, max: 300, change: 0 };
const ppmReporting = { min: 10, max: 300, change: 0.000001 };
const batteryReporting = { min: 3600, max: 0, change: 0 };
const model_r01 = "Tuya_Thermostat_r01";
const model_r02 = "Tuya_Thermostat_r02";
const model_r03 = "Tuya_Thermostat_r03";
const model_r04 = "Tuya_Thermostat_r04";
const model_r05 = "Tuya_Thermostat_r05";
const model_r06 = "Tuya_Thermostat_r06";
const model_r07 = "Tuya_Thermostat_r07";
const model_r08 = "Tuya_Thermostat_r08";
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 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 fzLocal = {
thermostat_custom_fw: {
cluster: "hvacThermostat",
type: ["attributeReport", "readResponse"],
convert: (model, msg, publish, options, meta) => {
const result = {};
if (msg.data[attrThermSensorUser] !== undefined) {
const lookup = { 0: "Inner (IN)", 1: "All (AL)", 2: "Outer (OU)" };
result.sensor = utils.getFromLookup(msg.data[attrThermSensorUser], lookup);
}
if (msg.data.minSetpointDeadBand !== undefined) {
let data;
if (model.model === model_r06) {
data = Number.parseFloat(msg.data.minSetpointDeadBand) / 10;
result.histeresis_temperature = data;
}
else {
data = Number.parseInt(msg.data.minSetpointDeadBand);
result.deadzone_temperature = data;
}
//logger.info(`DeadBand: ${data}`, NS);
}
if (msg.data[attrThermFrostProtect] !== undefined) {
const data = Number.parseInt(msg.data[attrThermFrostProtect]) / 100;
result.frost_protect = data;
}
if (msg.data[attrThermHeatProtect] !== undefined) {
const data = Number.parseInt(msg.data[attrThermHeatProtect]) / 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]) / 100;
result.eco_mode_cool_temperature = data;
}
if (msg.data[attrThermEcoModeHeatTemperature] !== undefined) {
const data = Number.parseInt(msg.data[attrThermEcoModeHeatTemperature]) / 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) {
const lookup = { 0: "Off", 1: "Low", 2: "Medium", 3: "High" };
result.brightness_level = utils.getFromLookup(msg.data[attrThermLevel], lookup);
}
if (msg.data[attrThermSound] !== undefined) {
result.sound = msg.data[attrThermSound] === 1 ? "On" : "Off";
}
if (msg.data[attrThermInversion] !== undefined) {
result.inversion = msg.data[attrThermInversion] === 1 ? "On" : "Off";
}
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;
result.external_temperature_calibration = data;
}
return result;
},
},
thermostat_schedule: {
cluster: "hvacThermostat",
type: ["commandSetWeeklySchedule"],
convert: (model, msg, publish, options, meta) => {
const result = {};
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;
},
},
};
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 }, utils.getOptions(meta.mapped, entity));
return { state: { brightness: value } };
},
convertGet: async (entity, key, meta) => {
await entity.read("genLevelCtrl", ["currentLevel"]);
},
},
thermostat_sensor_used: {
key: ["sensor"],
convertSet: async (entity, key, value, meta) => {
const endpoint = meta.device.getEndpoint(1);
const lookup = { "Inner (IN)": 0, "All (AL)": 1, "Outer (OU)": 2 };
await endpoint.write("hvacThermostat", { [attrThermSensorUser]: { value: utils.getFromLookup(value, lookup), type: 0x30 } });
return {
state: { [key]: 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: ["histeresis_temperature"],
convertSet: async (entity, key, value, meta) => {
utils.assertNumber(value);
const minSetpointDeadBand = Number(value) * 10;
await entity.write("hvacThermostat", { minSetpointDeadBand });
return { readAfterWriteTime: 250, state: { histeresis_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_inversion: {
key: ["inversion"],
convertSet: async (entity, key, value, meta) => {
const inversion = Number(value === "On");
await entity.write("hvacThermostat", { [attrThermInversion]: { value: inversion, type: 0x10 } });
return { readAfterWriteTime: 250, state: { inversion: value } };
},
convertGet: async (entity, key, meta) => {
await entity.read("hvacThermostat", [attrThermInversion]);
},
},
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]);
},
},
};
const localFromZigbeeThermostat = [
fz.ignore_basic_report,
fz.thermostat,
fz.fan,
fz.namron_hvac_user_interface,
fz.thermostat_weekly_schedule,
fzLocal.thermostat_custom_fw,
fzLocal.thermostat_schedule,
fzLocal.display_brightness,
fzLocal.fancontrol_control,
];
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,
tz.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_sound,
tzLocal.thermostat_inversion,
tzLocal.thermostat_schedule_mode,
tzLocal.thermostat_settings_reset,
tzLocal.thermostat_ext_temperature_calibration,
tzLocal.fancontrol_control,
];
async function configureCommon(device, coordinatorEndpoint, definition) {
//logger.info(definition.model);
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]);
await endpoint1.read("hvacFanCtrl", ["fanMode"]);
await endpoint1.read("hvacFanCtrl", [attrFanCtrlControl]);
await reporting.bind(endpoint1, coordinatorEndpoint, ["hvacThermostat", "hvacUserInterfaceCfg", "hvacFanCtrl"]);
if (definition.model === model_r03 || definition.model === model_r04) {
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) {
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) {
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) {
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_r08) {
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 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 = [
{
key: ["energy_tier_1", "energy_tier_2", "energy_tier_3", "energy_tier_4"],
convertGet: async (entity, key, meta) => {
await entity.read("seMetering", [
"currentTier1SummDelivered",
"currentTier2SummDelivered",
"currentTier3SummDelivered",
"currentTier4SummDelivered",
]);
},
convertSet: async (entity, key, value, meta) => {
return await null;
},
},
{
key: ["model_name"],
convertGet: async (entity, key, meta) => {
await entity.read("seMetering", [attrElCityMeterModelName]);
},
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", [attrElCityMeterDateRelease]);
},
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", { [attrElCityMeterAddressPreset]: { value: device_address_preset, type: 0x23 } });
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", { [attrElCityMeterPasswordPreset]: { value: device_password_preset, type: 0x41 } });
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", { [attrElCityMeterMeasurementPreset]: { value: device_measurement_preset, type: 0x20 } });
return { readAfterWriteTime: 250, state: { device_measurement_preset: value } };
},
convertGet: async (entity, key, meta) => {
await entity.read("seMetering", [attrElCityMeterMeasurementPreset]);
},
},
];
const fromZigbee = [
{
cluster: "seMetering",
type: ["attributeReport", "readResponse"],
convert: (model, msg, publish, options, meta) => {
const result = {};
if (msg.data.divisor !== undefined) {
const energyDivisor = Number.parseInt(msg.data.divisor);
globalStore.putValue(meta.device, "energyDivisor", energyDivisor);
result.e_divisor = energyDivisor;
}
return result;
},
},
{
cluster: "seMetering",
type: ["attributeReport", "readResponse"],
convert: (model, msg, publish, options, meta) => {
const result = {};
if (msg.data.multiplier !== undefined) {
const energyMultiplier = Number.parseInt(msg.data.multiplier);
globalStore.putValue(meta.device, "energyMultiplier", energyMultiplier);
result.e_multiplier = energyMultiplier;
}
return result;
},
},
{
cluster: "seMetering",
type: ["attributeReport", "readResponse"],
convert: (model, msg, publish, options, meta) => {
const result = {};
if (msg.data.currentTier1SummDelivered !== undefined) {
let energyDivisor = globalStore.getValue(meta.device, "energyDivisor");
let energyMultiplier = globalStore.getValue(meta.device, "energyMultiplier");
if (energyDivisor === undefined) {
energyDivisor = 1;
}
if (energyMultiplier === undefined) {
energyMultiplier = 1;
}
const data = msg.data.currentTier1SummDelivered;
result.energy_tier_1 = (Number.parseInt(data) / energyDivisor) * energyMultiplier;
}
return result;
},
},
{
cluster: "seMetering",
type: ["attributeReport", "readResponse"],
convert: (model, msg, publish, options, meta) => {
const result = {};
if (msg.data.currentTier2SummDelivered !== undefined) {
let energyDivisor = globalStore.getValue(meta.device, "energyDivisor");
let energyMultiplier = globalStore.getValue(meta.device, "energyMultiplier");
if (energyDivisor === undefined) {
energyDivisor = 1;
}
if (energyMultiplier === undefined) {
energyMultiplier = 1;
}
const data = msg.data.currentTier2SummDelivered;
result.energy_tier_2 = (Number.parseInt(data) / energyDivisor) * energyMultiplier;
}
return result;
},
},
{
cluster: "seMetering",
type: ["attributeReport", "readResponse"],
convert: (model, msg, publish, options, meta) => {
const result = {};
if (msg.data.currentTier3SummDelivered !== undefined) {
let energyDivisor = globalStore.getValue(meta.device, "energyDivisor");
let energyMultiplier = globalStore.getValue(meta.device, "energyMultiplier");
if (energyDivisor === undefined) {
energyDivisor = 1;
}
if (energyMultiplier === undefined) {
energyMultiplier = 1;
}
const data = msg.data.currentTier3SummDelivered;
result.energy_tier_3 = (Number.parseInt(data) / energyDivisor) * energyMultiplier;
}
return result;
},
},
{
cluster: "seMetering",
type: ["attributeReport", "readResponse"],
convert: (model, msg, publish, options, meta) => {
const result = {};
if (msg.data.currentTier4SummDelivered !== undefined) {
let energyDivisor = globalStore.getValue(meta.device, "energyDivisor");
let energyMultiplier = globalStore.getValue(meta.device, "energyMultiplier");
if (energyDivisor === undefined) {
energyDivisor = 1;
}
if (energyMultiplier === undefined) {
energyMultiplier = 1;
}
const data = msg.data.currentTier4SummDelivered;
result.energy_tier_4 = (Number.parseInt(data) / energyDivisor) * energyMultiplier;
}
return result;
},
},
{
cluster: "seMetering",
type: ["attributeReport", "readResponse"],
convert: (model, msg, publish, options, meta) => {
const result = {};
if (msg.data[attrElCityMeterModelName] !== undefined) {
const data = msg.data[attrElCityMeterModelName];
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[attrElCityMeterDateRelease] !== undefined) {
const data = msg.data[attrElCityMeterDateRelease];
result.date_release = data.toString();
}
return result;
},
},
{
cluster: "seMetering",
type: ["attributeReport", "readResponse"],
convert: (model, msg, publish, options, meta) => {
const result = {};
if (msg.data.status !== undefined) {
const data = msg.data.status;
const value = Number.parseInt(data);
return {
battery_low: (value & (1 << 1)) > 0,
tamper: (value & (1 << 2)) > 0,
};
}
return result;
},
},
{
cluster: "seMetering",
type: ["attributeReport", "readResponse"],
convert: (model, msg, publish, options, meta) => {
const result = {};
if (msg.data.remainingBattLife !== undefined) {
const data = Number.parseInt(msg.data.remainingBattLife);
result.battery_life = data;
}
return result;
},
},
{
cluster: "seMetering",
type: ["readResponse"],
convert: (model, msg, publish, options, meta) => {
const result = {};
if (msg.data[attrElCityMeterMeasurementPreset] !== undefined) {
const data = Number.parseInt(msg.data[attrElCityMeterMeasurementPreset]);
result.device_measurement_preset = data;
}
return result;
},
},
];
return {
exposes,
fromZigbee,
toZigbee,
isModernExtend: true,
};
},
};
function waterPreset() {
const exposes = [
e
.composite("preset", "preset", ea.SET)
.withFeature(e
.numeric("hot_water_preset", ea.SET)
.withValueMin(0)
.withValueMax(99999999)
.withValueStep(1)
.withUnit("L")
.withDescription("Preset hot water"))
.withFeature(e
.numeric("cold_water_preset", ea.SET)
.withValueMin(0)
.withValueMax(99999999)
.withValueStep(1)
.withUnit("L")
.withDescription("Preset cold water"))
.withFeature(e
.numeric("step_water_preset", ea.SET)
.withValueMin(1)
.withValueMax(100)
.withValueStep(1)
.withUnit("L")
.withDescription("Preset step water")),
];
const toZigbee = [
{
key: ["preset"],
convertSet: async (entity, key, value, meta) => {
const endpoint = meta.device.getEndpoint(3);
const values = {
// biome-ignore lint/suspicious/noExplicitAny: ignored using `--suppress`
hot_water: value.hot_water_preset,
// biome-ignore lint/suspicious/noExplicitAny: ignored using `--suppress`
cold_water: value.cold_water_preset,
// biome-ignore lint/suspicious/noExplicitAny: ignored using `--suppress`
step_water: value.step_water_preset,
};
if (values.hot_water != null && values.hot_water >= 0) {
const hot_water_preset = Number.parseInt(values.hot_water);
await endpoint.write("seMetering", { 61440: { value: hot_water_preset, type: 0x23 } });
}
if (values.cold_water != null && values.cold_water >= 0) {
const cold_water_preset = Number.parseInt(values.cold_water);
await endpoint.write("seMetering", { 61441: { value: cold_water_preset, type: 0x23 } });
}
if (values.step_water != null && values.step_water >= 0) {
const step_water_preset = Number.parseInt(values.step_water);
await endpoint.write("seMetering", { 61442: { value: step_water_preset, type: 0x21 } });
}
},
},
];
return { toZigbee, exposes, isModernExtend: true };
}
exports.definitions = [
{
zigbeeModel: ["Tuya_CO2Sensor_r01"],
model: "SLACKY_DIY_CO2_SENSOR_R01",
vendor: "Slacky-DIY",
description: "Tuya CO2 sensor with custom Firmware",
extend: [m.co2({ reporting: ppmReporting })],
ota: true,
},
{
zigbeeModel: ["Tuya_CO2Sensor_r02"],
model: "SLACKY_DIY_CO2_SENSOR_R02",
vendor: "Slacky-DIY",
description: "Tuya CO2 sensor with custom Firmware",
extend: [
m.co2({ reporting: ppmReporting }),
m.numeric({
name: "formaldehyde",
access: "STATE_GET",
cluster: "msFormaldehyde",
attribute: "measuredValue",
reporting: ppmReporting,
unit: "ppm",
scale: 0.000001,
precision: 2,
description: "Measured Formaldehyde value",
}),
m.numeric({
name: "voc",
access: "STATE_GET",
cluster: "genAnalogInput",
attribute: "presentValue",
reporting: ppmReporting,
unit: "ppm",
scale: 0.000001,
precision: 2,
description: "Measured VOC value",
}),
m.temperature(),
m.humidity(),
],
ota: true,
},
{
zigbeeModel: ["Watermeter_TLSR8258"],
model: "Watermeter_TLSR8258",
vendor: "Slacky-DIY",
description: "Water Meter",
configure: async (device, coordinatorEndpoint, logger) => {
const thirdEndpoint = device.getEndpoint(3);
await thirdEndpoint.read("seMetering", [0xf000, 0xf001, 0xf002]);
},
extend: [
m.deviceEndpoints({
endpoints: {
"1": 1,
"2": 2,
"3": 3,
"4": 4,
"5": 5,
},
}),
m.iasZoneAlarm({ zoneType: "water_leak", zoneAttributes: ["alarm_1"] }),
m.battery({
voltage: true,
voltageReporting: true,
percentageReportingConfig: batteryReporting,
voltageReportingConfig: batteryReporting,
}),
m.enumLookup({
name: "switch_actions",
endpointName: "4",
lookup: { on_off: 0, off_on: 1, toggle: 2 },
cluster: "genOnOffSwitchCfg",
attribute: "switchActions",
description: "Actions switch 1",
}),
m.enumLookup({
name: "switch_actions",
endpointName: "5",
lookup: { on_off: 0, off_on: 1, toggle: 2 },
cluster: "genOnOffSwitchCfg",
attribute: "switchActions",
description: "Actions switch 2",
}),
m.numeric({
name: "volume",
endpointNames: ["1"],
access: "STATE_GET",
cluster: "seMetering",
attribute: "currentSummDelivered",
reporting: { min: 0, max: 300, change: 0 },
unit: "L",
description: "Hot water",
}),
m.numeric({
name: "volume",
endpointNames: ["2"],
access: "STATE_GET",
cluster: "seMetering"