zigbee-herdsman-converters
Version:
Collection of device converters to be used with zigbee-herdsman
1,409 lines (1,408 loc) • 320 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;
};
})();
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.moesSwitch = exports.dataPoints = exports.ZMLookups = exports.msLookups = exports.giexWaterValve = exports.thermostatPresets = exports.thermostatSystemModes4 = exports.thermostatSystemModes3 = exports.thermostatSystemModes2 = exports.tuyaHPSCheckingResult = exports.thermostatSystemModes = exports.toZigbee = exports.tz = exports.fromZigbee = exports.fz = void 0;
const node_fs_1 = __importDefault(require("node:fs"));
const fromZigbee_1 = require("../converters/fromZigbee");
const constants = __importStar(require("./constants"));
const exposes = __importStar(require("./exposes"));
const light = __importStar(require("./light"));
const logger_1 = require("./logger");
const globalStore = __importStar(require("./store"));
const utils = __importStar(require("./utils"));
const dataTypes = {
raw: 0, // [ bytes ]
bool: 1, // [0/1]
value: 2, // [ 4 byte value ]
string: 3, // [ N byte string ]
enum: 4, // [ 0-255 ]
bitmap: 5, // [ 1,2,4 bytes ] as bits
};
// biome-ignore lint/suspicious/noExplicitAny: ignored using `--suppress`
const convertMultiByteNumberPayloadToSingleDecimalNumber = (chunks) => {
// Destructuring "chunks" is needed because it's a Buffer
// and we need a simple array.
let value = 0;
for (let i = 0; i < chunks.length; i++) {
value = value << 8;
value += chunks[i];
}
return value;
};
// biome-ignore lint/suspicious/noExplicitAny: ignored using `--suppress`
function getDataValue(dpValue) {
let dataString = "";
switch (dpValue.datatype) {
case dataTypes.raw:
return dpValue.data;
case dataTypes.bool:
return dpValue.data[0] === 1;
case dataTypes.value:
return convertMultiByteNumberPayloadToSingleDecimalNumber(dpValue.data);
case dataTypes.string:
// Don't use .map here, doesn't work: https://github.com/Koenkk/zigbee-herdsman-converters/pull/1799/files#r530377091
for (let i = 0; i < dpValue.data.length; ++i) {
dataString += String.fromCharCode(dpValue.data[i]);
}
return dataString;
case dataTypes.enum:
return dpValue.data[0];
case dataTypes.bitmap:
return convertMultiByteNumberPayloadToSingleDecimalNumber(dpValue.data);
}
}
// biome-ignore lint/suspicious/noExplicitAny: ignored using `--suppress`
function getTypeName(dpValue) {
const entry = Object.entries(dataTypes).find(([typeName, typeId]) => typeId === dpValue.datatype);
return entry ? entry[0] : "unknown";
}
// biome-ignore lint/suspicious/noExplicitAny: ignored using `--suppress`
function logUnexpectedDataPoint(where, msg, dpValue, meta) {
logger_1.logger.debug(`Received unexpected Tuya DataPoint #${dpValue.dp} from ${meta.device.ieeeAddr} with raw data '${JSON.stringify(dpValue)}': \
type='${msg.type}', datatype='${getTypeName(dpValue)}', value='${getDataValue(dpValue)}', known DP# usage: \
${JSON.stringify(getDataPointNames(dpValue))}`, `zhc:${where}`);
}
// biome-ignore lint/suspicious/noExplicitAny: ignored using `--suppress`
function logUnexpectedDataType(where, msg, dpValue, meta, expectedDataType) {
logger_1.logger.debug(`Received Tuya DataPoint #${dpValue.dp} with unexpected datatype from ${meta.device.ieeeAddr} with raw data \
'${JSON.stringify(dpValue)}': type='${msg.type}', datatype='${getTypeName(dpValue)}' (instead of '${expectedDataType}'), \
value='${getDataValue(dpValue)}', known DP# usage: ${JSON.stringify(getDataPointNames(dpValue))}`, `zhc:${where}`);
}
// biome-ignore lint/suspicious/noExplicitAny: ignored using `--suppress`
function getDataPointNames(dpValue) {
const entries = Object.entries(dataPoints).filter(([dpName, dpId]) => dpId === dpValue.dp);
return entries.map(([dpName, dpId]) => dpName);
}
const coverStateOverride = {
// Contains all covers which differentiate from the default enum states
// Use manufacturerName to identify device!
// https://github.com/Koenkk/zigbee2mqtt/issues/5596#issuecomment-759408189
// biome-ignore lint/style/useNamingConvention: ignored using `--suppress`
_TZE200_rddyvrci: { close: 1, open: 2, stop: 0 },
// biome-ignore lint/style/useNamingConvention: ignored using `--suppress`
_TZE200_wmcdj3aq: { close: 0, open: 2, stop: 1 },
// biome-ignore lint/style/useNamingConvention: ignored using `--suppress`
_TZE200_cowvfni3: { close: 0, open: 2, stop: 1 },
// biome-ignore lint/style/useNamingConvention: ignored using `--suppress`
_TYST11_cowvfni3: { close: 0, open: 2, stop: 1 },
};
// Gets an array containing which enums have to be used in order for the correct close/open/stop commands to be sent
function getCoverStateEnums(manufacturerName) {
if (manufacturerName in coverStateOverride) {
return coverStateOverride[manufacturerName];
}
return { close: 2, open: 0, stop: 1 }; // defaults
}
function convertDecimalValueTo4ByteHexArray(value) {
const hexValue = Number(value).toString(16).padStart(8, "0");
const chunk1 = hexValue.substring(0, 2);
const chunk2 = hexValue.substring(2, 4);
const chunk3 = hexValue.substring(4, 6);
const chunk4 = hexValue.substring(6);
return [chunk1, chunk2, chunk3, chunk4].map((hexVal) => Number.parseInt(hexVal, 16));
}
let gSec = undefined;
// biome-ignore lint/suspicious/noExplicitAny: ignored using `--suppress`
async function sendDataPoints(entity, dpValues, cmd = "dataRequest", seq = undefined) {
if (seq === undefined) {
if (gSec === undefined) {
gSec = 0;
}
else {
gSec++;
gSec %= 0xffff;
}
// biome-ignore lint/style/noParameterAssign: ignored using `--suppress`
seq = gSec;
}
await entity.command("manuSpecificTuya", cmd || "dataRequest", {
seq,
dpValues,
}, { disableDefaultResponse: true });
return seq;
}
function convertStringToHexArray(value) {
const asciiKeys = [];
for (let i = 0; i < value.length; i++) {
asciiKeys.push(value[i].charCodeAt(0));
}
return asciiKeys;
}
function dpValueFromIntValue(dp, value) {
return { dp, datatype: dataTypes.value, data: convertDecimalValueTo4ByteHexArray(value) };
}
function dpValueFromBool(dp, value) {
return { dp, datatype: dataTypes.bool, data: [value ? 1 : 0] };
}
function dpValueFromEnum(dp, value) {
return { dp, datatype: dataTypes.enum, data: [value] };
}
function dpValueFromStringBuffer(dp, stringBuffer) {
return { dp, datatype: dataTypes.string, data: stringBuffer };
}
// biome-ignore lint/suspicious/noExplicitAny: ignored using `--suppress`
function dpValueFromRaw(dp, rawBuffer) {
return { dp, datatype: dataTypes.raw, data: rawBuffer };
}
// biome-ignore lint/suspicious/noExplicitAny: ignored using `--suppress`
function dpValueFromBitmap(dp, bitmapBuffer) {
return { dp, datatype: dataTypes.bitmap, data: bitmapBuffer };
}
// Return `seq` - transaction ID for handling concrete response
// biome-ignore lint/suspicious/noExplicitAny: ignored using `--suppress`
async function sendDataPoint(entity, dpValue, cmd, seq = undefined) {
return await sendDataPoints(entity, [dpValue], cmd, seq);
}
// biome-ignore lint/suspicious/noExplicitAny: ignored using `--suppress`
async function sendDataPointValue(entity, dp, value, cmd, seq = undefined) {
return await sendDataPoints(entity, [dpValueFromIntValue(dp, value)], cmd, seq);
}
async function sendDataPointBool(entity, dp, value, cmd, seq = undefined) {
return await sendDataPoints(entity, [dpValueFromBool(dp, value)], cmd, seq);
}
async function sendDataPointEnum(entity, dp, value, cmd, seq = undefined) {
return await sendDataPoints(entity, [dpValueFromEnum(dp, value)], cmd, seq);
}
// biome-ignore lint/suspicious/noExplicitAny: ignored using `--suppress`
async function sendDataPointRaw(entity, dp, value, cmd, seq = undefined) {
return await sendDataPoints(entity, [dpValueFromRaw(dp, value)], cmd, seq);
}
// biome-ignore lint/suspicious/noExplicitAny: ignored using `--suppress`
async function sendDataPointBitmap(entity, dp, value, cmd, seq = undefined) {
return await sendDataPoints(entity, [dpValueFromBitmap(dp, value)], cmd, seq);
}
// biome-ignore lint/suspicious/noExplicitAny: ignored using `--suppress`
async function sendDataPointStringBuffer(entity, dp, value, cmd, seq = undefined) {
return await sendDataPoints(entity, [dpValueFromStringBuffer(dp, value)], cmd, seq);
}
// biome-ignore lint/suspicious/noExplicitAny: ignored using `--suppress`
function convertRawToCycleTimer(value) {
let timernr = 0;
let starttime = "00:00";
let endtime = "00:00";
let irrigationDuration = 0;
let pauseDuration = 0;
let weekdays = "once";
let timeractive = 0;
if (value.length > 11) {
timernr = value[1];
timeractive = value[2];
if (value[3] > 0) {
weekdays =
(value[3] & 0x01 ? "Su" : "") +
(value[3] & 0x02 ? "Mo" : "") +
(value[3] & 0x04 ? "Tu" : "") +
(value[3] & 0x08 ? "We" : "") +
(value[3] & 0x10 ? "Th" : "") +
(value[3] & 0x20 ? "Fr" : "") +
(value[3] & 0x40 ? "Sa" : "");
}
else {
weekdays = "once";
}
// biome-ignore lint/suspicious/noExplicitAny: ignored using `--suppress`
let minsincemidnight = value[4] * 256 + value[5];
// @ts-expect-error ignore
starttime = `${String(Number.parseInt(minsincemidnight / 60)).padStart(2, "0")}:${String(minsincemidnight % 60).padStart(2, "0")}`;
minsincemidnight = value[6] * 256 + value[7];
// @ts-expect-error ignore
endtime = `${String(Number.parseInt(minsincemidnight / 60)).padStart(2, "0")}:${String(minsincemidnight % 60).padStart(2, "0")}`;
irrigationDuration = value[8] * 256 + value[9];
pauseDuration = value[10] * 256 + value[11];
}
return {
timernr: timernr,
starttime: starttime,
endtime: endtime,
irrigationDuration: irrigationDuration,
pauseDuration: pauseDuration,
weekdays: weekdays,
active: timeractive,
};
}
// biome-ignore lint/suspicious/noExplicitAny: ignored using `--suppress`
function logDataPoint(where, msg, dpValue, meta) {
logger_1.logger.info(`Received Tuya DataPoint #${dpValue.dp} from ${meta.device.ieeeAddr} with raw data '${JSON.stringify(dpValue)}': \
type='${msg.type}', datatype='${getTypeName(dpValue)}', value='${getDataValue(dpValue)}', known DP# usage: \
${JSON.stringify(getDataPointNames(dpValue))}`, `zhc:${where}`);
}
const thermostatSystemModes2 = {
0: "auto",
1: "cool",
2: "heat",
3: "dry",
4: "fan",
};
exports.thermostatSystemModes2 = thermostatSystemModes2;
const thermostatSystemModes3 = {
0: "auto",
1: "heat",
2: "off",
};
exports.thermostatSystemModes3 = thermostatSystemModes3;
const thermostatSystemModes4 = {
0: "off",
1: "auto",
2: "heat",
};
exports.thermostatSystemModes4 = thermostatSystemModes4;
const thermostatWeekFormat = {
0: "5+2",
1: "6+1",
2: "7",
};
const thermostatForceMode = {
0: "normal",
1: "open",
2: "close",
};
const thermostatPresets = {
0: "away",
1: "schedule",
2: "manual",
3: "comfort",
4: "eco",
5: "boost",
6: "complex",
};
exports.thermostatPresets = thermostatPresets;
const thermostatScheduleMode = {
1: "single", // One schedule for all days
2: "weekday/weekend", // Weekdays(2-5) and Holidays(6-1)
3: "weekday/sat/sun", // Weekdays(2-6), Saturday(7), Sunday(1)
4: "7day", // 7 day schedule
};
const silvercrestModes = {
white: 0,
color: 1,
effect: 2,
};
const silvercrestEffects = {
steady: "00",
snow: "01",
rainbow: "02",
snake: "03",
twinkle: "04",
firework: "05",
horizontal_flag: "06",
waves: "07",
updown: "08",
vintage: "09",
fading: "0a",
collide: "0b",
strobe: "0c",
sparkles: "0d",
carnaval: "0e",
glow: "0f",
};
const fanModes = {
0: "low",
1: "medium",
2: "high",
3: "auto",
};
// Motion sensor lookups
const msLookups = {
OSensitivity: {
0: "sensitive",
1: "normal",
2: "cautious",
},
VSensitivity: {
0: "speed_priority",
1: "normal_priority",
2: "accuracy_priority",
},
Mode: {
0: "general_model",
1: "temporaty_stay",
2: "basic_detection",
3: "sensor_test",
},
};
exports.msLookups = msLookups;
const tvThermostatMode = {
0: "off",
1: "heat",
2: "auto",
};
const tvThermostatPreset = {
0: "auto",
1: "manual",
2: "holiday",
3: "holiday",
};
// Zemismart ZM_AM02 Roller Shade Converter
const ZMLookups = {
AM02Mode: {
0: "morning",
1: "night",
},
AM02Control: {
0: "open",
1: "stop",
2: "close",
3: "continue",
},
AM02Direction: {
0: "forward",
1: "back",
},
AM02WorkState: {
0: "opening",
1: "closing",
},
AM02Border: {
0: "up",
1: "down",
2: "down_delete",
},
AM02Situation: {
0: "fully_open",
1: "fully_close",
},
AM02MotorWorkingMode: {
0: "continuous",
1: "intermittently",
},
};
exports.ZMLookups = ZMLookups;
const moesSwitch = {
powerOnBehavior: {
0: "off",
1: "on",
2: "previous",
},
indicateLight: {
0: "off",
1: "switch",
2: "position",
3: "freeze",
},
};
exports.moesSwitch = moesSwitch;
const tuyaHPSCheckingResult = {
0: "checking",
1: "check_success",
2: "check_failure",
3: "others",
4: "comm_fault",
5: "radar_fault",
};
exports.tuyaHPSCheckingResult = tuyaHPSCheckingResult;
function convertWeekdaysTo1ByteHexArray(weekdays) {
let nr = 0;
if (weekdays === "once") {
return nr;
}
if (weekdays.includes("Mo")) {
nr |= 0x40;
}
if (weekdays.includes("Tu")) {
nr |= 0x20;
}
if (weekdays.includes("We")) {
nr |= 0x10;
}
if (weekdays.includes("Th")) {
nr |= 0x08;
}
if (weekdays.includes("Fr")) {
nr |= 0x04;
}
if (weekdays.includes("Sa")) {
nr |= 0x02;
}
if (weekdays.includes("Su")) {
nr |= 0x01;
}
return [nr];
}
// biome-ignore lint/suspicious/noExplicitAny: ignored using `--suppress`
function convertRawToTimer(value) {
let timernr = 0;
let starttime = "00:00";
let duration = 0;
let weekdays = "once";
let timeractive = "";
if (value.length > 12) {
timernr = value[1];
const minsincemidnight = value[2] * 256 + value[3];
// @ts-expect-error ignore
starttime = `${String(Number.parseInt(minsincemidnight / 60)).padStart(2, "0")}:${String(minsincemidnight % 60).padStart(2, "0")}`;
duration = value[4] * 256 + value[5];
if (value[6] > 0) {
weekdays =
(value[6] & 0x01 ? "Su" : "") +
(value[6] & 0x02 ? "Mo" : "") +
(value[6] & 0x04 ? "Tu" : "") +
(value[6] & 0x08 ? "We" : "") +
(value[6] & 0x10 ? "Th" : "") +
(value[6] & 0x20 ? "Fr" : "") +
(value[6] & 0x40 ? "Sa" : "");
}
else {
weekdays = "once";
}
timeractive = value[8];
}
return { timernr: timernr, time: starttime, duration: duration, weekdays: weekdays, active: timeractive };
}
function logUnexpectedDataValue(where, msg,
// biome-ignore lint/suspicious/noExplicitAny: ignored using `--suppress`
dpValue, meta,
// biome-ignore lint/suspicious/noExplicitAny: ignored using `--suppress`
valueKind,
// biome-ignore lint/suspicious/noExplicitAny: ignored using `--suppress`
expectedMinValue = null,
// biome-ignore lint/suspicious/noExplicitAny: ignored using `--suppress`
expectedMaxValue = null) {
if (expectedMinValue === null) {
if (expectedMaxValue === null) {
logger_1.logger.debug(`Received Tuya DataPoint #${dpValue.dp} with invalid value ${getDataValue(dpValue)} for ${valueKind} \
from ${meta.device.ieeeAddr}`, `zhc:${where}`);
}
else {
logger_1.logger.debug(`Received Tuya DataPoint #${dpValue.dp} with invalid value ${getDataValue(dpValue)} for ${valueKind} \
from ${meta.device.ieeeAddr} which is higher than the expected maximum of ${expectedMaxValue}`, `zhc:${where}`);
}
}
else {
if (expectedMaxValue === null) {
logger_1.logger.debug(`Received Tuya DataPoint #${dpValue.dp} with invalid value ${getDataValue(dpValue)} for ${valueKind} \
from ${meta.device.ieeeAddr} which is lower than the expected minimum of ${expectedMinValue}`, `zhc:${where}`);
}
else {
logger_1.logger.debug(`Received Tuya DataPoint #${dpValue.dp} with invalid value ${getDataValue(dpValue)} for ${valueKind} \
from ${meta.device.ieeeAddr} which is outside the expected range from ${expectedMinValue} to ${expectedMaxValue}`, `zhc:${where}`);
}
}
}
// Contains all covers which need their position inverted by default
// Default is 100 = open, 0 = closed; Devices listed here will use 0 = open, 100 = closed instead
// Use manufacturerName to identify device!
// Don't invert _TZE200_cowvfni3: https://github.com/Koenkk/zigbee2mqtt/issues/6043
const coverPositionInvert = [
"_TZE200_wmcdj3aq",
"_TZE200_nogaemzt",
"_TZE200_xuzcvlku",
"_TZE200_xaabybja",
"_TZE200_rmymn92d",
"_TZE200_gubdgai2",
"_TZE200_r0jdjrvi",
];
// Gets a boolean indicating whether the cover by this manufacturerName needs reversed positions
function isCoverInverted(manufacturerName) {
// Return true if cover is listed in coverPositionInvert
// Return false by default, not inverted
return coverPositionInvert.includes(manufacturerName);
}
// biome-ignore lint/suspicious/noExplicitAny: ignored using `--suppress`
function convertDecimalValueTo2ByteHexArray(value) {
const hexValue = Number(value).toString(16).padStart(4, "0");
const chunk1 = hexValue.substr(0, 2);
const chunk2 = hexValue.substr(2);
return [chunk1, chunk2].map((hexVal) => Number.parseInt(hexVal, 16));
}
function convertTimeTo2ByteHexArray(time) {
const timeArray = time.split(":");
if (timeArray.length !== 2) {
throw new Error("Time format incorrect");
}
const timeHour = Number.parseInt(timeArray[0]);
const timeMinute = Number.parseInt(timeArray[1]);
if (timeHour > 23 || timeMinute > 59) {
throw new Error("Time incorrect");
}
return convertDecimalValueTo2ByteHexArray(timeHour * 60 + timeMinute);
}
const dataPoints = {
wateringTimer: {
valve_state_auto_shutdown: 2,
water_flow: 3,
shutdown_timer: 11,
remaining_watering_time: 101,
valve_state: 102,
last_watering_duration: 107,
battery: 110,
},
// Common data points
// Below data points are usually shared between devices
state: 1,
heatingSetpoint: 2,
coverPosition: 2,
dimmerLevel: 3,
dimmerMinLevel: 3,
localTemp: 3,
coverArrived: 3,
occupancy: 3,
mode: 4,
fanMode: 5,
dimmerMaxLevel: 5,
motorDirection: 5,
config: 5,
childLock: 7,
coverChange: 7,
runningState: 14,
valveDetection: 20,
battery: 21,
tempCalibration: 44,
// Data points above 100 are usually custom function data points
waterLeak: 101,
minTemp: 102,
maxTemp: 103,
windowDetection: 104,
boostTime: 105,
coverSpeed: 105,
forceMode: 106,
comfortTemp: 107,
ecoTemp: 108,
valvePos: 109,
batteryLow: 110,
weekFormat: 111,
scheduleWorkday: 112,
scheduleHoliday: 113,
awayTemp: 114,
windowOpen: 115,
autoLock: 116,
awayDays: 117,
// Manufacturer specific
// Earda
eardaDimmerLevel: 2,
// Siterwell Thermostat
siterwellWindowDetection: 18,
// Moes Thermostat
moesHold: 2,
moesScheduleEnable: 3,
moesHeatingSetpoint: 16,
moesMaxTempLimit: 18,
moesMaxTemp: 19,
moesDeadZoneTemp: 20,
moesLocalTemp: 24,
moesMinTempLimit: 26,
moesTempCalibration: 27,
moesValve: 36,
moesChildLock: 40,
moesSensor: 43,
moesSchedule: 101,
etopErrorStatus: 13,
// MoesS Thermostat
moesSsystemMode: 1,
moesSheatingSetpoint: 2,
moesSlocalTemp: 3,
moesSboostHeating: 4,
moesSboostHeatingCountdown: 5,
moesSreset: 7,
// biome-ignore lint/style/useNamingConvention: ignored using `--suppress`
moesSwindowDetectionFunktion_A2: 8,
moesSwindowDetection: 9,
moesSchildLock: 13,
moesSbattery: 14,
moesSschedule: 101,
moesSvalvePosition: 104,
moesSboostHeatingCountdownTimeSet: 103,
moesScompensationTempSet: 105,
moesSecoMode: 106,
moesSecoModeTempSet: 107,
moesSmaxTempSet: 108,
moesSminTempSet: 109,
moesCoverCalibration: 3,
moesCoverBacklight: 7,
moesCoverMotorReversal: 8,
// Neo T&H
neoOccupancy: 101,
neoPowerType: 101,
neoMelody: 102,
neoDuration: 103,
neoTamper: 103,
neoAlarm: 104,
neoTemp: 105,
neoTempScale: 106,
neoHumidity: 106,
neoMinTemp: 107,
neoMaxTemp: 108,
neoMinHumidity: 109,
neoMaxHumidity: 110,
neoUnknown2: 112,
neoTempAlarm: 113,
neoTempHumidityAlarm: 113,
neoHumidityAlarm: 114,
neoUnknown3: 115,
neoVolume: 116,
// Neo AlarmOnly
neoAOBattPerc: 15,
neoAOMelody: 21,
neoAODuration: 7,
neoAOAlarm: 13,
neoAOVolume: 5,
// Saswell TRV
saswellHeating: 3,
saswellWindowDetection: 8,
saswellFrostDetection: 10,
saswellTempCalibration: 27,
saswellChildLock: 40,
saswellState: 101,
saswellLocalTemp: 102,
saswellHeatingSetpoint: 103,
saswellValvePos: 104,
saswellBatteryLow: 105,
saswellAwayMode: 106,
saswellScheduleMode: 107,
saswellScheduleEnable: 108,
saswellScheduleSet: 109,
saswellSetpointHistoryDay: 110,
saswellTimeSync: 111,
saswellSetpointHistoryWeek: 112,
saswellSetpointHistoryMonth: 113,
saswellSetpointHistoryYear: 114,
saswellLocalHistoryDay: 115,
saswellLocalHistoryWeek: 116,
saswellLocalHistoryMonth: 117,
saswellLocalHistoryYear: 118,
saswellMotorHistoryDay: 119,
saswellMotorHistoryWeek: 120,
saswellMotorHistoryMonth: 121,
saswellMotorHistoryYear: 122,
saswellScheduleSunday: 123,
saswellScheduleMonday: 124,
saswellScheduleTuesday: 125,
saswellScheduleWednesday: 126,
saswellScheduleThursday: 127,
saswellScheduleFriday: 128,
saswellScheduleSaturday: 129,
saswellAntiScaling: 130,
// HY thermostat
hyHeating: 102,
hyExternalTemp: 103,
hyAwayDays: 104,
hyAwayTemp: 105,
hyMaxTempProtection: 106,
hyMinTempProtection: 107,
hyTempCalibration: 109,
hyHysteresis: 110,
hyProtectionHysteresis: 111,
hyProtectionMaxTemp: 112,
hyProtectionMinTemp: 113,
hyMaxTemp: 114,
hyMinTemp: 115,
hySensor: 116,
hyPowerOnBehavior: 117,
hyWeekFormat: 118,
hyWorkdaySchedule1: 119,
hyWorkdaySchedule2: 120,
hyHolidaySchedule1: 121,
hyHolidaySchedule2: 122,
hyState: 125,
hyHeatingSetpoint: 126,
hyLocalTemp: 127,
hyMode: 128,
hyChildLock: 129,
hyAlarm: 130,
// Silvercrest
silvercrestChangeMode: 2,
silvercrestSetBrightness: 3,
silvercrestSetColorTemp: 4,
silvercrestSetColor: 5,
silvercrestSetEffect: 6,
// Fantem
fantemPowerSupplyMode: 101,
fantemReportingTime: 102,
fantemExtSwitchType: 103,
fantemTempCalibration: 104,
fantemHumidityCalibration: 105,
fantemLoadDetectionMode: 105,
fantemLuxCalibration: 106,
fantemExtSwitchStatus: 106,
fantemTemp: 107,
fantemHumidity: 108,
fantemMotionEnable: 109,
fantemControlMode: 109,
fantemBattery: 110,
fantemLedEnable: 111,
fantemReportingEnable: 112,
fantemLoadType: 112,
fantemLoadDimmable: 113,
// Woox
wooxSwitch: 102,
wooxBattery: 14,
wooxSmokeTest: 8,
// Woox thermostat
wooxDormancy: 108, // ???
wooxRefresh: 120, // ???
wooxControlTemperature: 119, // map auto and manual temperature setpoint.
wooxManualTemperatureSetpoint: 16, //RW
wooxAutomaticTemperatureSetpoint: 105, //RW
wooxMode: 2, //RW
wooxLocalTemperature: 24, //R
wooxTemperatureCalibration: 104, //RW
wooxWindowStatus: 107, //R open,close
wooxWindowTemperature: 116, //RW
wooxWindowTime: 117, //RW
wooxChildLock: 30, //RW
wooxBatteryCapacity: 34, //R
wooxEnergySavingTemperature: 102, //RW
wooxComfortTemperature: 101, //RW
wooxHolidayModeSettings: 103, //RW
wooxProgrammingMonday: 109, //RW
wooxProgrammingTuesday: 110, //RW
wooxProgrammingWednesday: 111, //RW
wooxProgrammingThursday: 112, //RW
wooxProgrammingFriday: 113, //RW
wooxProgrammingSaturday: 114, //RW
wooxProgrammingSunday: 115, //RW
wooxBoostHeating: 106, //RW
wooxFaultAlarm: 45, // R
wooxBoostHeatingCountdown: 118, //R
// FrankEver
frankEverTimer: 9,
frankEverTreshold: 101,
// Dinrail power meter switch
dinrailPowerMeterTotalEnergy: 17,
dinrailPowerMeterCurrent: 18,
dinrailPowerMeterPower: 19,
dinrailPowerMeterVoltage: 20,
dinrailPowerMeterTotalEnergy2: 101,
dinrailPowerMeterPower2: 103,
// tuya smart air box
tuyaSabCO2: 2,
tuyaSabTemp: 18,
tuyaSabHumidity: 19,
tuyaSabVOC: 21,
tuyaSabFormaldehyd: 22,
// tuya Smart Air House Keeper, Multifunctionale air quality detector.
// CO2, Temp, Humidity, VOC and Formaldehyd same as Smart Air Box
tuyaSahkMP25: 2,
tuyaSahkCO2: 22,
tuyaSahkFormaldehyd: 20,
// Tuya CO (carbon monoxide) smart air box
tuyaSabCOalarm: 1,
tuyaSabCO: 2,
// Moes MS-105 Dimmer
moes105DimmerState1: 1,
moes105DimmerLevel1: 2,
moes105DimmerState2: 7,
moes105DimmerLevel2: 8,
// Tuya Radar Sensor
trsPresenceState: 1,
trsSensitivity: 2,
trsMotionState: 102,
trsIlluminanceLux: 103,
trsDetectionData: 104,
trsScene: 112,
trsMotionDirection: 114,
trsMotionSpeed: 115,
// Tuya Radar Sensor with fall function
trsfPresenceState: 1,
trsfSensitivity: 2,
trsfMotionState: 102,
trsfIlluminanceLux: 103,
trsfTumbleSwitch: 105,
trsfTumbleAlarmTime: 106,
trsfScene: 112,
trsfMotionDirection: 114,
trsfMotionSpeed: 115,
trsfFallDownStatus: 116,
trsfStaticDwellAlarm: 117,
trsfFallSensitivity: 118,
// Human Presence Sensor AIR
msVSensitivity: 101,
msOSensitivity: 102,
msVacancyDelay: 103,
msMode: 104,
msVacantConfirmTime: 105,
msReferenceLuminance: 106,
msLightOnLuminancePrefer: 107,
msLightOffLuminancePrefer: 108,
msLuminanceLevel: 109,
msLedStatus: 110,
// TV01 Moes Thermostat
tvMode: 2,
tvWindowDetection: 8,
tvFrostDetection: 10,
tvHeatingSetpoint: 16,
tvLocalTemp: 24,
tvTempCalibration: 27,
tvWorkingDay: 31,
tvHolidayTemp: 32,
tvBattery: 35,
tvChildLock: 40,
tvErrorStatus: 45,
tvHolidayMode: 46,
tvBoostTime: 101,
tvOpenWindowTemp: 102,
tvComfortTemp: 104,
tvEcoTemp: 105,
tvWeekSchedule: 106,
tvHeatingStop: 107,
tvMondaySchedule: 108,
tvWednesdaySchedule: 109,
tvFridaySchedule: 110,
tvSundaySchedule: 111,
tvTuesdaySchedule: 112,
tvThursdaySchedule: 113,
tvSaturdaySchedule: 114,
tvBoostMode: 115,
// HOCH / WDYK DIN Rail
hochCountdownTimer: 9,
hochFaultCode: 26,
hochRelayStatus: 27,
hochChildLock: 29,
hochVoltage: 101,
hochCurrent: 102,
hochActivePower: 103,
hochLeakageCurrent: 104,
hochTemperature: 105,
hochRemainingEnergy: 106,
hochRechargeEnergy: 107,
hochCostParameters: 108,
hochLeakageParameters: 109,
hochVoltageThreshold: 110,
hochCurrentThreshold: 111,
hochTemperatureThreshold: 112,
hochTotalActivePower: 113,
hochEquipmentNumberType: 114,
hochClearEnergy: 115,
hochLocking: 116,
hochTotalReverseActivePower: 117,
hochHistoricalVoltage: 118,
hochHistoricalCurrent: 119,
// NOUS SMart LCD Temperature and Humidity Sensor E6
nousTemperature: 1,
nousHumidity: 2,
nousBattery: 4,
nousTempUnitConvert: 9,
nousMaxTemp: 10,
nousMinTemp: 11,
nousMaxHumi: 12,
nousMinHumi: 13,
nousTempAlarm: 14,
nousHumiAlarm: 15,
nousHumiSensitivity: 20,
nousTempSensitivity: 19,
nousTempReportInterval: 17,
nousHumiReportInterval: 18,
// TUYA Temperature and Humidity Sensor
tthTemperature: 1,
tthHumidity: 2,
tthBatteryLevel: 3,
tthBattery: 4,
// TUYA / HUMIDITY/ILLUMINANCE/TEMPERATURE SENSOR
thitBatteryPercentage: 3,
thitIlluminanceLux: 7,
tIlluminanceLux: 2,
thitHumidity: 9,
thitTemperature: 8,
// TUYA SMART VIBRATION SENSOR
tuyaVibration: 10,
// TUYA WLS-100z Water Leak Sensor
wlsWaterLeak: 1,
wlsBatteryPercentage: 4,
// Evanell
evanellMode: 2,
evanellHeatingSetpoint: 4,
evanellLocalTemp: 5,
evanellBattery: 6,
evanellChildLock: 8,
// ZMAM02 Zemismart RF Courtain Converter
AM02Control: 1,
AM02PercentControl: 2,
AM02PercentState: 3,
AM02Mode: 4,
AM02Direction: 5,
AM02WorkState: 7,
AM02CountdownLeft: 9,
AM02TimeTotal: 10,
AM02SituationSet: 11,
AM02Fault: 12,
AM02Border: 16,
AM02MotorWorkingMode: 20,
AM02AddRemoter: 101,
// Matsee Tuya Garage Door Opener
garageDoorTrigger: 1,
garageDoorContact: 3,
garageDoorStatus: 12,
// Moes switch with optional neutral
moesSwitchPowerOnBehavior: 14,
moesSwitchIndicateLight: 15,
// X5H thermostat
x5hState: 1,
x5hMode: 2,
x5hWorkingStatus: 3,
x5hSound: 7,
x5hFrostProtection: 10,
x5hSetTemp: 16,
x5hSetTempCeiling: 19,
x5hCurrentTemp: 24,
x5hTempCorrection: 27,
x5hWeeklyProcedure: 30,
x5hWorkingDaySetting: 31,
x5hFactoryReset: 39,
x5hChildLock: 40,
x5hSensorSelection: 43,
x5hFaultAlarm: 45,
x5hTempDiff: 101,
x5hProtectionTempLimit: 102,
x5hOutputReverse: 103,
x5hBackplaneBrightness: 104,
// Connected thermostat
connecteState: 1,
connecteMode: 2,
connecteHeatingSetpoint: 16,
connecteLocalTemp: 24,
connecteTempCalibration: 28,
connecteChildLock: 30,
connecteTempFloor: 101,
connecteSensorType: 102,
connecteHysteresis: 103,
connecteRunningState: 104,
connecteTempProgram: 105,
connecteOpenWindow: 106,
connecteMaxProtectTemp: 107,
// Tuya Smart Human Presence Sensor
tshpsPresenceState: 1,
tshpscSensitivity: 2,
tshpsMinimumRange: 3,
tshpsMaximumRange: 4,
tshpsTargetDistance: 9,
tshpsDetectionDelay: 101,
tshpsFadingTime: 102,
tshpsIlluminanceLux: 104,
tshpsCLI: 103, // not recognize
tshpsSelfTest: 6, // not recognize
// Tuya Luminance Motion sensor
lmsState: 1,
lmsBattery: 4,
lmsSensitivity: 9,
lmsKeepTime: 10,
lmsIlluminance: 12,
// Alecto SMART-SMOKE10
alectoSmokeState: 1,
alectoSmokeValue: 2,
alectoSelfChecking: 8,
alectoCheckingResult: 9,
alectoSmokeTest: 11,
alectoLifecycle: 12,
alectoBatteryState: 14,
alectoBatteryPercentage: 15,
alectoSilence: 16,
// BAC-002-ALZB - Moes like thermostat with Fan control
bacFanMode: 28,
// Human Presence Sensor Zigbee Radiowave Tuya
HPSZInductionState: 1,
HPSZPresenceTime: 101,
HPSZLeavingTime: 102,
HPSZLEDState: 103,
giexWaterValve: {
battery: 108,
currentTemperature: 106,
cycleIrrigationInterval: 105,
cycleIrrigationNumTimes: 103,
irrigationEndTime: 102,
irrigationStartTime: 101,
irrigationTarget: 104,
lastIrrigationDuration: 114,
mode: 1,
state: 2,
waterConsumed: 111,
},
zsHeatingSetpoint: 16,
zsChildLock: 40,
zsTempCalibration: 104,
zsLocalTemp: 24,
zsBatteryVoltage: 35,
zsComfortTemp: 101,
zsEcoTemp: 102,
zsHeatingSetpointAuto: 105,
zsOpenwindowTemp: 116,
zsOpenwindowTime: 117,
zsErrorStatus: 45,
zsMode: 2,
zsAwaySetting: 103,
zsBinaryOne: 106,
zsBinaryTwo: 107,
zsScheduleMonday: 109,
zsScheduleTuesday: 110,
zsScheduleWednesday: 111,
zsScheduleThursday: 112,
zsScheduleFriday: 113,
zsScheduleSaturday: 114,
zsScheduleSunday: 115,
};
exports.dataPoints = dataPoints;
// biome-ignore lint/suspicious/noExplicitAny: ignored using `--suppress`
function firstDpValue(msg, meta, converterName) {
const dpValues = msg.data.dpValues;
for (let index = 1; index < dpValues.length; index++) {
logger_1.logger.debug(`Additional DP #${dpValues[index].dp} with data ${JSON.stringify(dpValues[index])} will be ignored! \
Use a for loop in the fromZigbee converter (see \
https://www.zigbee2mqtt.io/advanced/support-new-devices/02_support_new_tuya_devices.html)`, `zhc:${converterName}`);
}
return dpValues[0];
}
// biome-ignore lint/suspicious/noExplicitAny: ignored using `--suppress`
function getMetaValue(entity, definition, key, groupStrategy = "first") {
if (entity.constructor.name === "Group" && entity.members.length > 0) {
const values = [];
for (const memberMeta of definition) {
if (memberMeta.meta && memberMeta.meta[key] !== undefined) {
if (groupStrategy === "first") {
return memberMeta.meta[key];
}
values.push(memberMeta.meta[key]);
}
else {
values.push(undefined);
}
}
if (groupStrategy === "allEqual" && new Set(values).size === 1) {
return values[0];
}
}
else if (definition && definition.meta && definition.meta[key] !== undefined) {
return definition.meta[key];
}
return undefined;
}
const SAFETY_MIN_SECS = 10;
const CAPACITY = "capacity";
const DURATION = "duration";
const OFF = "OFF";
const ON = "ON";
// biome-ignore lint/suspicious/noExplicitAny: ignored using `--suppress`
const toLocalTime = (time, timezone) => {
if (time === "--:--:--") {
return time;
}
const local = new Date(`2000-01-01T${time}.000${timezone}`); // Using 1970 instead produces edge cases
return local.toTimeString().split(" ").shift();
};
const giexFzModelConverters = {
QT06_1: {
// _TZE200_sh1btabb timezone is GMT+8
// biome-ignore lint/suspicious/noExplicitAny: ignored using `--suppress`
time: (value) => toLocalTime(value, "+08:00"),
},
};
const giexTzModelConverters = {
QT06_2: {
// _TZE200_a7sghmms irrigation time should not be less than 10 secs as per GiEX advice
// biome-ignore lint/suspicious/noExplicitAny: ignored using `--suppress`
irrigationTarget: (value, mode) => (value > 0 && value < SAFETY_MIN_SECS && mode === DURATION ? SAFETY_MIN_SECS : value),
},
};
const giexWaterValve = {
battery: "battery",
currentTemperature: "current_temperature",
cycleIrrigationInterval: "cycle_irrigation_interval",
cycleIrrigationNumTimes: "cycle_irrigation_num_times",
irrigationEndTime: "irrigation_end_time",
irrigationStartTime: "irrigation_start_time",
irrigationTarget: "irrigation_target",
lastIrrigationDuration: "last_irrigation_duration",
mode: "mode",
state: "state",
waterConsumed: "water_consumed",
};
exports.giexWaterValve = giexWaterValve;
const fromZigbee = {
TS0222: {
cluster: "manuSpecificTuya",
type: ["commandDataResponse", "commandDataReport"],
convert: (model, msg, publish, options, meta) => {
const result = {};
for (const dpValue of msg.data.dpValues) {
const dp = dpValue.dp;
const value = getDataValue(dpValue);
switch (dp) {
case 2:
result.illuminance = value;
break;
case 4:
result.battery = value;
break;
default:
logger_1.logger.debug(`Unrecognized DP #${dp} with data ${JSON.stringify(dpValue)}`, "zhc:legacy:fz:ts0222");
}
}
return result;
},
},
watering_timer: {
cluster: "manuSpecificTuya",
type: ["commandDataReport"],
convert: (model, msg, publish, options, meta) => {
const result = {};
for (const dpValue of msg.data.dpValues) {
const dp = dpValue.dp; // First we get the data point ID
const value = getDataValue(dpValue); // This function will take care of converting the data to proper JS type
switch (dp) {
case dataPoints.wateringTimer.water_flow: {
result.water_flow = value;
break;
}
case dataPoints.wateringTimer.remaining_watering_time: {
result.remaining_watering_time = value;
break;
}
case dataPoints.wateringTimer.last_watering_duration: {
result.last_watering_duration = value;
break;
}
case dataPoints.wateringTimer.valve_state: {
result.valve_state = value;
break;
}
case dataPoints.wateringTimer.shutdown_timer: {
result.shutdown_timer = value;
break;
}
case dataPoints.wateringTimer.valve_state_auto_shutdown: {
result.valve_state_auto_shutdown = value;
result.valve_state = value;
break;
}
case dataPoints.wateringTimer.battery: {
result.battery = value;
break;
}
default: {
logger_1.logger.debug(`>>> UNKNOWN DP #${dp} with data "${JSON.stringify(dpValue)}"`, "zhc:legacy:fz:watering_timer");
}
}
}
return result;
},
},
// biome-ignore lint/style/useNamingConvention: ignored using `--suppress`
ZM35HQ_battery: {
cluster: "manuSpecificTuya",
type: ["commandDataReport"],
convert: (model, msg, publish, options, meta) => {
const dpValue = firstDpValue(msg, meta, "ZM35HQ");
const dp = dpValue.dp;
const value = getDataValue(dpValue);
if (dp === 4)
return { battery: value };
logger_1.logger.debug(`Unrecognized DP #${dp} with data ${JSON.stringify(dpValue)}`, "zhc:legacy:fz:zm35hq");
},
},
ZMRM02: {
cluster: "manuSpecificTuya",
type: ["commandGetData", "commandSetDataResponse", "commandDataResponse"],
convert: (model, msg, publish, options, meta) => {
const dpValue = firstDpValue(msg, meta, "ZMRM02");
if (dpValue.dp === 10) {
return { battery: getDataValue(dpValue) };
}
const button = dpValue.dp;
const actionValue = getDataValue(dpValue);
const lookup = { 0: "single", 1: "double", 2: "hold" };
const action = lookup[actionValue];
return { action: `button_${button}_${action}` };
},
},
SA12IZL: {
cluster: "manuSpecificTuya",
type: ["commandDataResponse", "commandDataReport"],
convert: (model, msg, publish, options, meta) => {
const result = {};
for (const dpValue of msg.data.dpValues) {
const dp = dpValue.dp;
const value = getDataValue(dpValue);
switch (dp) {
case dataPoints.state:
result.smoke = value === 0;
break;
case 15:
result.battery = value;
break;
case 16:
result.silence_siren = value;
break;
case 20: {
const alarm = { 0: true, 1: false };
result.alarm = alarm[value];
break;
}
default:
logger_1.logger.debug(`Unrecognized DP #${dp} with data ${JSON.stringify(dpValue)}`, "zhc:legacy:fz:sa12izl");
}
}
return result;
},
},
// biome-ignore lint/style/useNamingConvention: ignored using `--suppress`
R7049_status: {
cluster: "manuSpecificTuya",
type: ["commandDataResponse", "commandDataReport"],
convert: (model, msg, publish, options, meta) => {
const result = {};
for (const dpValue of msg.data.dpValues) {
const dp = dpValue.dp; // First we get the data point ID
const value = getDataValue(dpValue); // This function will take care of converting the data to proper JS type
switch (dp) {
case 1:
result.smoke = Boolean(!value);
break;
case 8:
result.test_alarm = value;
break;
case 9: {
const testAlarmResult = { 0: "checking", 1: "check_success", 2: "check_failure", 3: "others" };
result.test_alarm_result = testAlarmResult[value];
break;
}
case 11:
result.fault_alarm = Boolean(value);
break;
case 14: {
const batteryLevels = { 0: "low", 1: "middle", 2: "high" };
result.battery_level = batteryLevels[value];
result.battery_low = value === 0;
break;
}
case 16:
result.silence_siren = value;
break;
case 20: {
const alarm = { 0: true, 1: false };
result.alarm = alarm[value];
break;
}
default:
logger_1.logger.debug(`Unrecognized DP #${dp} with data ${JSON.stringify(dpValue)}`, "zhc:legacy:fz:r7049_status");
}
}
return result;
},
},
// biome-ignore lint/style/useNamingConvention: ignored using `--suppress`
woox_R7060: {
cluster: "manuSpecificTuya",
type: ["commandActiveStatusReport"],
convert: (model, msg, publish, options, meta) => {
const dpValue = firstDpValue(msg, meta, "woox_R7060");
const dp = dpValue.dp;
const value = getDataValue(dpValue);
switch (dp) {
case dataPoints.wooxSwitch:
return { state: value === 2 ? "OFF" : "ON" };
case 101:
return { battery: value };
default:
logger_1.logger.debug(`Unrecognized DP #${dp} with data ${JSON.stringify(dpValue)}`, "zhc:legacy:fz:woox_r7060");
}
},
},
woox_thermostat: {
cluster: "manuSpecificTuya",
type: ["commandDataResponse", "commandDataReport"],
convert: (model, msg, publish, options, meta) => {
const result = {};
for (const dpValue of msg.data.dpValues) {
const dp = dpValue.dp;
const value = getDataValue(dpValue);
switch (dp) {
case dataPoints.wooxMode:
if (value === 0) {
result.system_mode = "auto";
result.away_mode = "OFF";
}
else if (value === 1) {
result.system_mode = "heat";
result.away_mode = "OFF";
}
else if (value === 2) {
result.away_mode = "ON";
result.system_mode = "auto";
}
else {
result.away_mode = "OFF";
result.system_mode = "off";
}
break;
case dataPoints.wooxManualTemperatureSetpoint:
result.current_heating_setpoint = Number.parseFloat((value / 2).toFixed(1));
result.manual_heating_setpoint = Number.parseFloat((value / 2).toFixed(1));
break;
case dataPoints.wooxAutomaticTemperatureSetpoint:
result.current_heating_setpoint = Number.parseFloat((value / 2).toFixed(1));
result.auto_heating_setpoint = Number.parseFloat((value / 2).toFixed(1));
break;
case dataPoints.wooxLocalTemperature:
result.local_temperature = Number.parseFloat((value / 10).toFixed(1));
break;
case dataPoints.wooxTemperatureCalibration:
result.local_temperature_calibration = Number.parseFloat((value / 10).toFixed(1));
break;
case dataPoints.wooxWindowStatus:
result.window_detection = value[0] ? "OPEN" : "CLOSED";
break;
case dataPoints.wooxWindowTemperature:
result.window_detection_temperature = Number.parseFloat((value / 2).toFixed(1));
break;
case dataPoints.wooxWindowTime:
result.window_detection_time = value;
break;
case dataPoints.wooxChildLock:
result.child_lock = value ? "LOCK" : "UNLOCK";
break;
case dataPoints.wooxBatteryCapacity:
result.battery = value;
result.battery_low = value < 30 ? 1 : 0;
break;
case dataPoints.wooxBoostHeatingCountdown:
result.boost_time = value;
break;
case dataPoints.wooxEnergySavingTemperature:
result.eco_temperature = Number.parseFloat((value / 2).toFixed(1));
break;
case dataPoints.wooxComfortTemperature:
result.comfort_temperature = Number.parseFloat((value / 2).toFixed(1));
break;
case dataPoints.wooxBoostHeating:
result.boost_heating = value ? "ON" : "OFF";
break;
case dataPoints.wooxFaultAlarm:
result.error_status = value;
break;
case dataPoints.wooxProgrammingMonday:
result.monday_schedule = value.join();
break;
case dataPoints.wooxProgrammingTuesday:
result.tuesday_schedule = value.join();