mirror of
https://github.com/formtapez/ZigUP.git
synced 2025-02-23 09:34:50 +01:00
1851 lines
66 KiB
JavaScript
1851 lines
66 KiB
JavaScript
'use strict';
|
||
|
||
const utils = require('./utils');
|
||
const common = require('./common');
|
||
const Zcl = require('zigbee-herdsman/dist/zcl');
|
||
|
||
const cfg = {
|
||
default: {
|
||
manufSpec: 0,
|
||
disDefaultRsp: 0,
|
||
},
|
||
defaultdisFeedbackRsp: {
|
||
manufSpec: 0,
|
||
disDefaultRsp: 0,
|
||
disFeedbackRsp: true,
|
||
},
|
||
xiaomi: {
|
||
manufSpec: 1,
|
||
disDefaultRsp: 1,
|
||
manufCode: 0x115F,
|
||
},
|
||
eurotronic: {
|
||
manufSpec: 1,
|
||
manufCode: 4151,
|
||
},
|
||
hue: {
|
||
manufSpec: 1,
|
||
manufCode: 4107,
|
||
},
|
||
osram: {
|
||
manufSpec: 1,
|
||
manufCode: 0x110c,
|
||
},
|
||
sinope: {
|
||
manufSpec: 1,
|
||
manufCode: 0x119C,
|
||
},
|
||
};
|
||
|
||
function getTransition(message, options) {
|
||
if (message.hasOwnProperty('transition')) {
|
||
return message.transition;
|
||
} else if (options.hasOwnProperty('transition')) {
|
||
return options.transition;
|
||
} else {
|
||
return 0;
|
||
}
|
||
}
|
||
|
||
const converters = {
|
||
/**
|
||
* Generic
|
||
*/
|
||
factory_reset: {
|
||
key: ['reset'],
|
||
convert: (key, value, message, type, postfix, options) => {
|
||
if (type === 'set') {
|
||
return [{
|
||
cid: 'genBasic',
|
||
cmd: 'resetFactDefault',
|
||
cmdType: 'functional',
|
||
zclData: {},
|
||
cfg: cfg.default,
|
||
}];
|
||
}
|
||
},
|
||
},
|
||
on_off: {
|
||
key: ['state'],
|
||
convert: (key, value, message, type, postfix, options) => {
|
||
const cid = 'genOnOff';
|
||
const attrId = 'onOff';
|
||
|
||
if (type === 'set') {
|
||
if (typeof value !== 'string') {
|
||
return;
|
||
}
|
||
|
||
return [{
|
||
cid: cid,
|
||
cmd: value.toLowerCase(),
|
||
cmdType: 'functional',
|
||
zclData: {},
|
||
cfg: options.disFeedbackRsp ? cfg.defaultdisFeedbackRsp : cfg.default,
|
||
newState: {state: value.toUpperCase()},
|
||
}];
|
||
} else if (type === 'get') {
|
||
return [{
|
||
cid: cid,
|
||
cmd: 'read',
|
||
cmdType: 'foundation',
|
||
zclData: [{attrId: Zcl.getAttributeLegacy(cid, attrId).value}],
|
||
cfg: cfg.default,
|
||
}];
|
||
}
|
||
},
|
||
},
|
||
cover_open_close: {
|
||
key: ['state'],
|
||
convert: (key, value, message, type, postfix, options) => {
|
||
if (type === 'set') {
|
||
if (typeof value !== 'string') {
|
||
return;
|
||
}
|
||
|
||
const positionByState = {
|
||
'open': 100,
|
||
'close': 0,
|
||
};
|
||
|
||
value = positionByState[value.toLowerCase()];
|
||
}
|
||
|
||
return converters.cover_position.convert(key, value, message, type, postfix, options);
|
||
},
|
||
},
|
||
cover_position: {
|
||
key: ['position'],
|
||
convert: (key, value, message, type, postfix, options) => {
|
||
const cid = 'genLevelCtrl';
|
||
const attrId = 'currentLevel';
|
||
|
||
if (type === 'set') {
|
||
return [{
|
||
cid: cid,
|
||
cmd: 'moveToLevelWithOnOff',
|
||
cmdType: 'functional',
|
||
zclData: {
|
||
level: Math.round(Number(value) * 2.55).toString(),
|
||
transtime: 0,
|
||
},
|
||
cfg: cfg.default,
|
||
newState: {position: value},
|
||
readAfterWriteTime: 0,
|
||
}];
|
||
} else if (type === 'get') {
|
||
return [{
|
||
cid: cid,
|
||
cmd: 'read',
|
||
cmdType: 'foundation',
|
||
zclData: [{attrId: Zcl.getAttributeLegacy(cid, attrId).value}],
|
||
cfg: cfg.default,
|
||
}];
|
||
}
|
||
},
|
||
},
|
||
warning: {
|
||
key: ['warning'],
|
||
convert: (key, value, message, type, postfix, options) => {
|
||
if (type === 'set') {
|
||
const mode = {
|
||
'stop': 0,
|
||
'burglar': 1,
|
||
'fire': 2,
|
||
'emergency': 3,
|
||
'police_panic': 4,
|
||
'fire_panic': 5,
|
||
'emergency_panic': 6,
|
||
};
|
||
|
||
const level = {
|
||
'low': 0,
|
||
'medium': 1,
|
||
'high': 2,
|
||
'very_high': 3,
|
||
};
|
||
|
||
const values = {
|
||
mode: value.mode || 'emergency',
|
||
level: value.level || 'medium',
|
||
strobe: value.hasOwnProperty('strobe') ? value.strobe : true,
|
||
duration: value.hasOwnProperty('duration') ? value.duration : 10,
|
||
};
|
||
|
||
const info = (mode[values.mode] << 4) + ((values.strobe ? 1 : 0) << 2) + (level[values.level]);
|
||
|
||
return [{
|
||
cid: 'ssIasWd',
|
||
cmd: 'startWarning',
|
||
cmdType: 'functional',
|
||
zclData: {startwarninginfo: info, warningduration: values.duration},
|
||
cfg: cfg.default,
|
||
}];
|
||
}
|
||
},
|
||
},
|
||
cover_control: {
|
||
key: ['state'],
|
||
convert: (key, value, message, type, postfix, options) => {
|
||
const zclCmdLookup = {
|
||
'open': 'upOpen',
|
||
'close': 'downClose',
|
||
'stop': 'stop',
|
||
'on': 'upOpen',
|
||
'off': 'downClose',
|
||
};
|
||
|
||
const zclCmd = zclCmdLookup[value.toLowerCase()];
|
||
if (zclCmd) {
|
||
return [{
|
||
cid: 'closuresWindowCovering',
|
||
cmdType: 'functional',
|
||
cmd: zclCmd,
|
||
zclData: {},
|
||
cfg: cfg.default,
|
||
}];
|
||
}
|
||
},
|
||
},
|
||
cover_gotopercentage: {
|
||
key: ['position', 'tilt'],
|
||
convert: (key, value, message, type, postfix, options) => {
|
||
const isPosition = (key === 'position');
|
||
const cid = 'closuresWindowCovering';
|
||
const attrId = isPosition ? 'currentPositionLiftPercentage' : 'currentPositionTiltPercentage';
|
||
// ZigBee officially expects "open" to be 0 and "closed" to be 100 whereas
|
||
// HomeAssistant etc. work the other way round.
|
||
value = 100 - value;
|
||
|
||
if (type === 'set') {
|
||
return [{
|
||
cid: cid,
|
||
cmdType: 'functional',
|
||
cmd: isPosition ? 'goToLiftPercentage' : 'goToTiltPercentage',
|
||
zclData: isPosition ? {percentageliftvalue: value} : {percentagetiltvalue: value},
|
||
cfg: cfg.default,
|
||
}];
|
||
} else if (type === 'get') {
|
||
return [{
|
||
cid: cid,
|
||
cmdType: 'foundation',
|
||
cmd: 'read',
|
||
zclData: [{attrId: Zcl.getAttributeLegacy(cid, attrId).value}],
|
||
cfg: cfg.default,
|
||
}];
|
||
}
|
||
},
|
||
},
|
||
occupancy_timeout: {
|
||
// set delay after motion detector changes from occupied to unoccupied
|
||
key: ['occupancy_timeout'],
|
||
convert: (key, value, message, type, postfix, options) => {
|
||
const cid = 'msOccupancySensing'; // 1030
|
||
const attrId = Zcl.getAttributeLegacy(cid, 'pirOToUDelay').value; // = 16
|
||
|
||
if (type === 'set') {
|
||
return [{
|
||
cid: cid,
|
||
cmd: 'write',
|
||
cmdType: 'foundation',
|
||
zclData: [{
|
||
attrId: attrId,
|
||
dataType: 33, // uint16
|
||
// hue_sml001:
|
||
// in seconds, minimum 10 seconds, <10 values result
|
||
// in 10 seconds delay
|
||
// make sure you write to second endpoint!
|
||
attrData: value,
|
||
}],
|
||
cfg: cfg.default,
|
||
}];
|
||
} else if (type === 'get') {
|
||
return [{
|
||
cid: cid,
|
||
cmd: 'read',
|
||
cmdType: 'foundation',
|
||
zclData: [{
|
||
attrId: attrId,
|
||
}],
|
||
cfg: cfg.default,
|
||
}];
|
||
}
|
||
},
|
||
},
|
||
light_brightness: {
|
||
key: ['brightness', 'brightness_percent'],
|
||
convert: (key, value, message, type, postfix, options) => {
|
||
const cid = 'genLevelCtrl';
|
||
const attrId = 'currentLevel';
|
||
|
||
if (type === 'set') {
|
||
if (key === 'brightness_percent') {
|
||
value = Math.round(Number(value) * 2.55).toString();
|
||
}
|
||
|
||
if (Number(value) === 0) {
|
||
const converted = converters.on_off.convert('state', 'off', message, 'set', postfix, options);
|
||
converted[0].newState.brightness = 0;
|
||
return converted;
|
||
} else {
|
||
return [{
|
||
cid: cid,
|
||
cmd: 'moveToLevel',
|
||
cmdType: 'functional',
|
||
zclData: {
|
||
level: Number(value),
|
||
transtime: getTransition(message, options) * 10,
|
||
},
|
||
cfg: cfg.default,
|
||
newState: {brightness: Number(value)},
|
||
readAfterWriteTime: message.hasOwnProperty('transition') ? message.transition * 1000 : 0,
|
||
}];
|
||
}
|
||
} else if (type === 'get') {
|
||
return [{
|
||
cid: cid,
|
||
cmd: 'read',
|
||
cmdType: 'foundation',
|
||
zclData: [{attrId: Zcl.getAttributeLegacy(cid, attrId).value}],
|
||
cfg: cfg.default,
|
||
}];
|
||
}
|
||
},
|
||
},
|
||
light_onoff_brightness: {
|
||
key: ['state', 'brightness', 'brightness_percent'],
|
||
convert: (key, value, message, type, postfix, options) => {
|
||
if (type === 'set') {
|
||
const hasBrightness = message.hasOwnProperty('brightness') ||
|
||
message.hasOwnProperty('brightness_percent');
|
||
const brightnessValue = message.hasOwnProperty('brightness') ?
|
||
message.brightness : message.brightness_percent;
|
||
const hasState = message.hasOwnProperty('state');
|
||
const hasTrasition = message.hasOwnProperty('transition') || options.hasOwnProperty('transition');
|
||
const state = hasState ? message.state.toLowerCase() : null;
|
||
|
||
if (hasState && (state === 'off' || !hasBrightness) && !hasTrasition) {
|
||
const converted = converters.on_off.convert('state', state, message, 'set', postfix, options);
|
||
if (state === 'on') {
|
||
converted[0].readAfterWriteTime = 0;
|
||
}
|
||
return converted;
|
||
} else if (!hasState && hasBrightness && Number(brightnessValue) === 0) {
|
||
const converted = converters.on_off.convert('state', 'off', message, 'set', postfix, options);
|
||
converted[0].newState.brightness = 0;
|
||
return converted;
|
||
} else {
|
||
let brightness = 0;
|
||
|
||
if (hasState && !hasBrightness && state == 'on') {
|
||
brightness = 255;
|
||
} else if (message.hasOwnProperty('brightness')) {
|
||
brightness = message.brightness;
|
||
} else if (message.hasOwnProperty('brightness_percent')) {
|
||
brightness = Math.round(Number(message.brightness_percent) * 2.55).toString();
|
||
}
|
||
|
||
const transition = getTransition(message, options);
|
||
|
||
return [{
|
||
cid: 'genLevelCtrl',
|
||
cmd: 'moveToLevelWithOnOff',
|
||
cmdType: 'functional',
|
||
zclData: {
|
||
level: Number(brightness),
|
||
transtime: transition * 10,
|
||
},
|
||
cfg: options.disFeedbackRsp ? cfg.defaultdisFeedbackRsp : cfg.default,
|
||
newState: {state: brightness === 0 ? 'OFF' : 'ON', brightness: Number(brightness)},
|
||
readAfterWriteTime: transition * 1000,
|
||
}];
|
||
}
|
||
} else if (type === 'get') {
|
||
return [
|
||
{
|
||
cid: 'genOnOff',
|
||
cmd: 'read',
|
||
cmdType: 'foundation',
|
||
zclData: [{attrId: Zcl.getAttributeLegacy('genOnOff', 'onOff').value}],
|
||
cfg: cfg.default,
|
||
},
|
||
{
|
||
cid: 'genLevelCtrl',
|
||
cmd: 'read',
|
||
cmdType: 'foundation',
|
||
zclData: [{attrId: Zcl.getAttributeLegacy('genLevelCtrl', 'currentLevel').value}],
|
||
cfg: cfg.default,
|
||
},
|
||
];
|
||
}
|
||
},
|
||
},
|
||
light_colortemp: {
|
||
key: ['color_temp', 'color_temp_percent'],
|
||
convert: (key, value, message, type, postfix, options) => {
|
||
const cid = 'lightingColorCtrl';
|
||
const attrId = 'colorTemperature';
|
||
|
||
if (type === 'set') {
|
||
if (key === 'color_temp_percent') {
|
||
value = Number(value) * 3.46;
|
||
value = Math.round(value + 154).toString();
|
||
}
|
||
|
||
value = Number(value);
|
||
|
||
return [{
|
||
cid: cid,
|
||
cmd: 'moveToColorTemp',
|
||
cmdType: 'functional',
|
||
zclData: {
|
||
colortemp: value,
|
||
transtime: getTransition(message, options) * 10,
|
||
},
|
||
cfg: cfg.default,
|
||
newState: {color_temp: value},
|
||
readAfterWriteTime: message.hasOwnProperty('transition') ? message.transition * 1000 : 0,
|
||
}];
|
||
} else if (type === 'get') {
|
||
return [{
|
||
cid: cid,
|
||
cmd: 'read',
|
||
cmdType: 'foundation',
|
||
zclData: [{attrId: Zcl.getAttributeLegacy(cid, attrId).value}],
|
||
cfg: cfg.default,
|
||
}];
|
||
}
|
||
},
|
||
},
|
||
light_color: {
|
||
key: ['color'],
|
||
convert: (key, value, message, type, postfix, options) => {
|
||
const cid = 'lightingColorCtrl';
|
||
|
||
if (type === 'set') {
|
||
// Check if we need to convert from RGB to XY and which cmd to use
|
||
let cmd;
|
||
if (value.hasOwnProperty('r') && value.hasOwnProperty('g') && value.hasOwnProperty('b')) {
|
||
const xy = utils.rgbToXY(value.r, value.g, value.b);
|
||
value.x = xy.x;
|
||
value.y = xy.y;
|
||
} else if (value.hasOwnProperty('rgb')) {
|
||
const rgb = value.rgb.split(',').map((i) => parseInt(i));
|
||
const xy = utils.rgbToXY(rgb[0], rgb[1], rgb[2]);
|
||
value.x = xy.x;
|
||
value.y = xy.y;
|
||
} else if (value.hasOwnProperty('hex')) {
|
||
const xy = utils.hexToXY(value.hex);
|
||
value.x = xy.x;
|
||
value.y = xy.y;
|
||
} else if (value.hasOwnProperty('hue') && value.hasOwnProperty('saturation')) {
|
||
value.hue = value.hue * (65535 / 360);
|
||
value.saturation = value.saturation * (2.54);
|
||
cmd = 'enhancedMoveToHueAndSaturation';
|
||
} else if (value.hasOwnProperty('hue')) {
|
||
value.hue = value.hue * (65535 / 360);
|
||
cmd = 'enhancedMoveToHue';
|
||
} else if (value.hasOwnProperty('saturation')) {
|
||
value.saturation = value.saturation * (2.54);
|
||
cmd = 'moveToSaturation';
|
||
}
|
||
|
||
const zclData = {
|
||
transtime: getTransition(message, options) * 10,
|
||
};
|
||
|
||
let newState = null;
|
||
switch (cmd) {
|
||
case 'enhancedMoveToHueAndSaturation':
|
||
zclData.enhancehue = value.hue;
|
||
zclData.saturation = value.saturation;
|
||
zclData.direction = value.direction || 0;
|
||
break;
|
||
case 'enhancedMoveToHue':
|
||
zclData.enhancehue = value.hue;
|
||
zclData.direction = value.direction || 0;
|
||
break;
|
||
|
||
case 'moveToSaturation':
|
||
zclData.saturation = value.saturation;
|
||
break;
|
||
|
||
default:
|
||
cmd = 'moveToColor';
|
||
newState = {color: {x: value.x, y: value.y}};
|
||
zclData.colorx = Math.round(value.x * 65535);
|
||
zclData.colory = Math.round(value.y * 65535);
|
||
}
|
||
|
||
return [{
|
||
cid: cid,
|
||
cmd: cmd,
|
||
cmdType: 'functional',
|
||
zclData: zclData,
|
||
cfg: cfg.default,
|
||
newState: newState,
|
||
readAfterWriteTime: message.hasOwnProperty('transition') ? message.transition * 1000 : 0,
|
||
}];
|
||
} else if (type === 'get') {
|
||
return [{
|
||
cid: cid,
|
||
cmd: 'read',
|
||
cmdType: 'foundation',
|
||
zclData: [
|
||
{attrId: Zcl.getAttributeLegacy(cid, 'currentX').value},
|
||
{attrId: Zcl.getAttributeLegacy(cid, 'currentY').value},
|
||
],
|
||
cfg: cfg.default,
|
||
}];
|
||
}
|
||
},
|
||
},
|
||
light_color_colortemp: {
|
||
/**
|
||
* This converter is a combination of light_color and light_colortemp and
|
||
* can be used instead of the two individual converters. When used to set,
|
||
* it actually calls out to light_color or light_colortemp to get the
|
||
* return value. When used to get, it gets both color and colorTemp in
|
||
* one call.
|
||
* The reason for the existence of this somewhat peculiar converter is
|
||
* that some lights don't report their state when changed. To fix this,
|
||
* we query the state after we set it. We want to query color and colorTemp
|
||
* both when setting either, because both change when setting one. This
|
||
* converter is used to do just that.
|
||
*/
|
||
key: ['color', 'color_temp', 'color_temp_percent'],
|
||
convert: (key, value, message, type, postfix, options) => {
|
||
const cid = 'lightingColorCtrl';
|
||
if (type === 'set') {
|
||
if (key == 'color') {
|
||
return converters.light_color.convert(key, value, message, type, postfix, options);
|
||
} else if (key == 'color_temp' || key == 'color_temp_percent') {
|
||
return converters.light_colortemp.convert(key, value, message, type, postfix, options);
|
||
}
|
||
} else if (type == 'get') {
|
||
return [{
|
||
cid: cid,
|
||
cmd: 'read',
|
||
cmdType: 'foundation',
|
||
zclData: [
|
||
{attrId: Zcl.getAttributeLegacy(cid, 'currentX').value},
|
||
{attrId: Zcl.getAttributeLegacy(cid, 'currentY').value},
|
||
{attrId: Zcl.getAttributeLegacy(cid, 'colorTemperature').value},
|
||
],
|
||
cfg: cfg.default,
|
||
}];
|
||
}
|
||
},
|
||
},
|
||
light_alert: {
|
||
key: ['alert', 'flash'],
|
||
convert: (key, value, message, type, postfix, options) => {
|
||
const cid = 'genIdentify';
|
||
if (type === 'set') {
|
||
const lookup = {
|
||
'select': 0x00,
|
||
'lselect': 0x01,
|
||
'none': 0xFF,
|
||
};
|
||
if (key === 'flash') {
|
||
if (value === 2) {
|
||
value = 'select';
|
||
} else if (value === 10) {
|
||
value = 'lselect';
|
||
}
|
||
}
|
||
return [{
|
||
cid: cid,
|
||
cmd: 'triggerEffect',
|
||
cmdType: 'functional',
|
||
zclData: {
|
||
effectid: lookup[value.toLowerCase()],
|
||
effectvariant: 0x01,
|
||
},
|
||
cfg: cfg.default,
|
||
}];
|
||
}
|
||
},
|
||
},
|
||
thermostat_local_temperature: {
|
||
key: 'local_temperature',
|
||
convert: (key, value, message, type, postfix, options) => {
|
||
const cid = 'hvacThermostat';
|
||
const attrId = 'localTemp';
|
||
if (type === 'get') {
|
||
return [{
|
||
cid: cid,
|
||
cmd: 'read',
|
||
cmdType: 'foundation',
|
||
zclData: [{attrId: Zcl.getAttributeLegacy(cid, attrId).value}],
|
||
cfg: cfg.default,
|
||
}];
|
||
}
|
||
},
|
||
},
|
||
thermostat_local_temperature_calibration: {
|
||
key: 'local_temperature_calibration',
|
||
convert: (key, value, message, type, postfix, options) => {
|
||
const cid = 'hvacThermostat';
|
||
const attrId = 'localTemperatureCalibration';
|
||
if (type === 'set') {
|
||
return [{
|
||
cid: cid,
|
||
cmd: 'write',
|
||
cmdType: 'foundation',
|
||
zclData: [{
|
||
attrId: Zcl.getAttributeLegacy(cid, attrId).value,
|
||
dataType: Zcl.getAttributeTypeLegacy(cid, attrId).value,
|
||
attrData: Math.round(value * 10),
|
||
}],
|
||
cfg: cfg.default,
|
||
}];
|
||
} else if (type === 'get') {
|
||
return [{
|
||
cid: cid,
|
||
cmd: 'read',
|
||
cmdType: 'foundation',
|
||
zclData: [{attrId: Zcl.getAttributeLegacy(cid, attrId).value}],
|
||
cfg: cfg.default,
|
||
}];
|
||
}
|
||
},
|
||
},
|
||
thermostat_occupancy: {
|
||
key: 'occupancy',
|
||
convert: (key, value, message, type, postfix, options) => {
|
||
const cid = 'hvacThermostat';
|
||
const attrId = 'ocupancy';
|
||
if (type === 'get') {
|
||
return [{
|
||
cid: cid,
|
||
cmd: 'read',
|
||
cmdType: 'foundation',
|
||
zclData: [{attrId: Zcl.getAttributeLegacy(cid, attrId).value}],
|
||
cfg: cfg.default,
|
||
}];
|
||
}
|
||
},
|
||
},
|
||
thermostat_occupied_heating_setpoint: {
|
||
key: 'occupied_heating_setpoint',
|
||
convert: (key, value, message, type, postfix, options) => {
|
||
const cid = 'hvacThermostat';
|
||
const attrId = 'occupiedHeatingSetpoint';
|
||
if (type === 'set') {
|
||
return [{
|
||
cid: cid,
|
||
cmd: 'write',
|
||
cmdType: 'foundation',
|
||
zclData: [{
|
||
attrId: Zcl.getAttributeLegacy(cid, attrId).value,
|
||
dataType: Zcl.getAttributeTypeLegacy(cid, attrId).value,
|
||
attrData: (Math.round((value * 2).toFixed(1))/2).toFixed(1) * 100,
|
||
}],
|
||
cfg: cfg.default,
|
||
}];
|
||
} else if (type === 'get') {
|
||
return [{
|
||
cid: cid,
|
||
cmd: 'read',
|
||
cmdType: 'foundation',
|
||
zclData: [{attrId: Zcl.getAttributeLegacy(cid, attrId).value}],
|
||
cfg: cfg.default,
|
||
}];
|
||
}
|
||
},
|
||
},
|
||
thermostat_unoccupied_heating_setpoint: {
|
||
key: 'unoccupied_heating_setpoint',
|
||
convert: (key, value, message, type, postfix, options) => {
|
||
const cid = 'hvacThermostat';
|
||
const attrId = 'unoccupiedHeatingSetpoint';
|
||
if (type === 'set') {
|
||
return [{
|
||
cid: cid,
|
||
cmd: 'write',
|
||
cmdType: 'foundation',
|
||
zclData: [{
|
||
attrId: Zcl.getAttributeLegacy(cid, attrId).value,
|
||
dataType: Zcl.getAttributeTypeLegacy(cid, attrId).value,
|
||
attrData: (Math.round((value * 2).toFixed(1))/2).toFixed(1) * 100,
|
||
}],
|
||
cfg: cfg.default,
|
||
}];
|
||
} else if (type === 'get') {
|
||
return [{
|
||
cid: cid,
|
||
cmd: 'read',
|
||
cmdType: 'foundation',
|
||
zclData: [{attrId: Zcl.getAttributeLegacy(cid, attrId).value}],
|
||
cfg: cfg.default,
|
||
}];
|
||
}
|
||
},
|
||
},
|
||
thermostat_occupied_cooling_setpoint: {
|
||
key: 'occupied_cooling_setpoint',
|
||
convert: (key, value, message, type, postfix, options) => {
|
||
const cid = 'hvacThermostat';
|
||
const attrId = 'occupiedCoolingSetpoint';
|
||
if (type === 'set') {
|
||
return [{
|
||
cid: cid,
|
||
cmd: 'write',
|
||
cmdType: 'foundation',
|
||
zclData: [{
|
||
attrId: Zcl.getAttributeLegacy(cid, attrId).value,
|
||
dataType: Zcl.getAttributeTypeLegacy(cid, attrId).value,
|
||
attrData: (Math.round((value * 2).toFixed(1))/2).toFixed(1) * 100,
|
||
}],
|
||
cfg: cfg.default,
|
||
}];
|
||
} else if (type === 'get') {
|
||
return [{
|
||
cid: cid,
|
||
cmd: 'read',
|
||
cmdType: 'foundation',
|
||
zclData: [{attrId: Zcl.getAttributeLegacy(cid, attrId).value}],
|
||
cfg: cfg.default,
|
||
}];
|
||
}
|
||
},
|
||
},
|
||
thermostat_remote_sensing: {
|
||
key: 'remote_sensing',
|
||
convert: (key, value, message, type, postfix, options) => {
|
||
const cid = 'hvacThermostat';
|
||
const attrId = 'remoteSensing';
|
||
if (type === 'set') {
|
||
return [{
|
||
cid: cid,
|
||
cmd: 'write',
|
||
cmdType: 'foundation',
|
||
zclData: [{
|
||
// Bit 0 = 0 – local temperature sensed internally
|
||
// Bit 0 = 1 – local temperature sensed remotely
|
||
// Bit 1 = 0 – outdoor temperature sensed internally
|
||
// Bit 1 = 1 – outdoor temperature sensed remotely
|
||
// Bit 2 = 0 – occupancy sensed internally
|
||
// Bit 2 = 1 – occupancy sensed remotely
|
||
attrId: Zcl.getAttributeLegacy(cid, attrId).value,
|
||
dataType: Zcl.getAttributeTypeLegacy(cid, attrId).value,
|
||
attrData: value, // TODO: Lookup in Zigbee documentation
|
||
}],
|
||
cfg: cfg.default,
|
||
}];
|
||
} else if (type === 'get') {
|
||
return [{
|
||
cid: cid,
|
||
cmd: 'read',
|
||
cmdType: 'foundation',
|
||
zclData: [{attrId: Zcl.getAttributeLegacy(cid, attrId).value}],
|
||
cfg: cfg.default,
|
||
}];
|
||
}
|
||
},
|
||
},
|
||
thermostat_control_sequence_of_operation: {
|
||
key: 'control_sequence_of_operation',
|
||
convert: (key, value, message, type, postfix, options) => {
|
||
const cid = 'hvacThermostat';
|
||
const attrId = 'ctrlSeqeOfOper';
|
||
if (type === 'set') {
|
||
return [{
|
||
cid: cid,
|
||
cmd: 'write',
|
||
cmdType: 'foundation',
|
||
zclData: [{
|
||
attrId: Zcl.getAttributeLegacy(cid, attrId).value,
|
||
dataType: Zcl.getAttributeTypeLegacy(cid, attrId).value,
|
||
attrData: utils.getKeyByValue(common.thermostatControlSequenceOfOperations, value, value),
|
||
}],
|
||
cfg: cfg.default,
|
||
}];
|
||
} else if (type === 'get') {
|
||
return [{
|
||
cid: cid,
|
||
cmd: 'read',
|
||
cmdType: 'foundation',
|
||
zclData: [{attrId: Zcl.getAttributeLegacy(cid, attrId).value}],
|
||
cfg: cfg.default,
|
||
}];
|
||
}
|
||
},
|
||
},
|
||
thermostat_system_mode: {
|
||
key: 'system_mode',
|
||
convert: (key, value, message, type, postfix, options) => {
|
||
const cid = 'hvacThermostat';
|
||
const attrId = 'systemMode';
|
||
if (type === 'set') {
|
||
return [{
|
||
cid: cid,
|
||
cmd: 'write',
|
||
cmdType: 'foundation',
|
||
zclData: [{
|
||
attrId: Zcl.getAttributeLegacy(cid, attrId).value,
|
||
dataType: Zcl.getAttributeTypeLegacy(cid, attrId).value,
|
||
attrData: utils.getKeyByValue(common.thermostatSystemModes, value, value),
|
||
}],
|
||
cfg: cfg.default,
|
||
readAfterWriteTime: 250,
|
||
}];
|
||
} else if (type === 'get') {
|
||
return [{
|
||
cid: cid,
|
||
cmd: 'read',
|
||
cmdType: 'foundation',
|
||
zclData: [{attrId: Zcl.getAttributeLegacy(cid, attrId).value}],
|
||
cfg: cfg.default,
|
||
}];
|
||
}
|
||
},
|
||
},
|
||
thermostat_setpoint_raise_lower: {
|
||
key: 'setpoint_raise_lower',
|
||
convert: (key, value, message, type, postfix, options) => {
|
||
const cid = 'hvacThermostat';
|
||
if (type === 'set') {
|
||
return [{
|
||
cid: cid,
|
||
cmd: 'setpointRaiseLower',
|
||
cmdType: 'functional',
|
||
zclData: {
|
||
mode: value.mode,
|
||
amount: Math.round(value.amount) * 100,
|
||
},
|
||
cfg: cfg.default,
|
||
}];
|
||
}
|
||
},
|
||
},
|
||
thermostat_weekly_schedule: {
|
||
key: 'weekly_schedule',
|
||
convert: (key, value, message, type, postfix, options) => {
|
||
const cid = 'hvacThermostat';
|
||
const attrId = 'weeklySchedule';
|
||
if (type === 'set') {
|
||
return [{
|
||
cid: cid,
|
||
cmd: 'setWeeklySchedule',
|
||
cmdType: 'functional',
|
||
zclData: {
|
||
dataType: Zcl.getAttributeTypeLegacy(cid, attrId).value,
|
||
attrData: value, // TODO: Combine attributes in attrData?
|
||
temperature_setpoint_hold: value.temperature_setpoint_hold,
|
||
temperature_setpoint_hold_duration: value.temperature_setpoint_hold_duration,
|
||
thermostat_programming_operation_mode: value.thermostat_programming_operation_mode,
|
||
thermostat_running_state: value.thermostat_running_state,
|
||
},
|
||
cfg: cfg.default,
|
||
}];
|
||
} else if (type === 'get') {
|
||
return [{
|
||
cid: cid,
|
||
cmd: 'getWeeklySchedule',
|
||
cmdType: 'functional',
|
||
zclData: {},
|
||
cfg: cfg.default,
|
||
}];
|
||
}
|
||
},
|
||
},
|
||
thermostat_clear_weekly_schedule: {
|
||
key: 'clear_weekly_schedule',
|
||
attr: [],
|
||
convert: (key, value, message, type, postfix, options) => {
|
||
return [{
|
||
cid: 'hvacThermostat',
|
||
cmd: 'clearWeeklySchedule',
|
||
type: 'functional',
|
||
zclData: {},
|
||
}];
|
||
},
|
||
},
|
||
thermostat_relay_status_log: {
|
||
key: 'relay_status_log',
|
||
attr: [],
|
||
convert: (key, value, message, type, postfix, options) => {
|
||
return [{
|
||
cid: 'hvacThermostat',
|
||
cmd: 'getRelayStatusLog',
|
||
type: 'functional',
|
||
zclData: {},
|
||
}];
|
||
},
|
||
},
|
||
thermostat_weekly_schedule_rsp: {
|
||
key: 'weekly_schedule_rsp',
|
||
attr: [],
|
||
convert: (key, value, message, type, postfix, options) => {
|
||
return [{
|
||
cid: 'hvacThermostat',
|
||
cmd: 'getWeeklyScheduleRsp',
|
||
type: 'functional',
|
||
zclData: {
|
||
number_of_transitions: value.numoftrans, // TODO: Lookup in Zigbee documentation
|
||
day_of_week: value.dayofweek,
|
||
mode: value.mode,
|
||
thermoseqmode: value.thermoseqmode,
|
||
},
|
||
}];
|
||
},
|
||
},
|
||
thermostat_relay_status_log_rsp: {
|
||
key: 'relay_status_log_rsp',
|
||
attr: [],
|
||
convert: (key, value, message, type, postfix, options) => {
|
||
return [{
|
||
cid: 'hvacThermostat',
|
||
cmd: 'getRelayStatusLogRsp',
|
||
type: 'functional',
|
||
zclData: {
|
||
time_of_day: value.timeofday, // TODO: Lookup in Zigbee documentation
|
||
relay_status: value.relaystatus,
|
||
local_temperature: value.localtemp,
|
||
humidity: value.humidity,
|
||
setpoint: value.setpoint,
|
||
unread_entries: value.unreadentries,
|
||
},
|
||
}];
|
||
},
|
||
},
|
||
thermostat_running_mode: {
|
||
key: 'running_mode',
|
||
convert: (key, value, message, type, postfix, options) => {
|
||
const cid = 'hvacThermostat';
|
||
const attrId = 'runningMode';
|
||
if (type === 'get') {
|
||
return [{
|
||
cid: cid,
|
||
cmd: 'read',
|
||
cmdType: 'foundation',
|
||
zclData: [{attrId: Zcl.getAttributeLegacy(cid, attrId).value}],
|
||
cfg: cfg.default,
|
||
}];
|
||
}
|
||
},
|
||
},
|
||
thermostat_running_state: {
|
||
key: 'running_state',
|
||
convert: (key, value, message, type, postfix, options) => {
|
||
const cid = 'hvacThermostat';
|
||
const attrId = 'runningState';
|
||
if (type === 'get') {
|
||
return [{
|
||
cid: cid,
|
||
cmd: 'read',
|
||
cmdType: 'foundation',
|
||
zclData: [{attrId: Zcl.getAttributeLegacy(cid, attrId).value}],
|
||
cfg: cfg.default,
|
||
}];
|
||
}
|
||
},
|
||
},
|
||
thermostat_temperature_display_mode: {
|
||
key: 'temperature_display_mode',
|
||
convert: (key, value, message, type, postfix, options) => {
|
||
const cid = 'hvacUserInterfaceCfg';
|
||
const attrId = 'tempDisplayMode';
|
||
if (type === 'set') {
|
||
return [{
|
||
cid: cid,
|
||
cmd: 'write',
|
||
cmdType: 'foundation',
|
||
zclData: [{
|
||
attrId: Zcl.getAttributeLegacy(cid, attrId).value,
|
||
dataType: Zcl.getAttributeTypeLegacy(cid, attrId).value,
|
||
attrData: utils.getKeyByValue(common.temperatureDisplayMode, value, value),
|
||
}],
|
||
cfg: cfg.default,
|
||
}];
|
||
}
|
||
},
|
||
},
|
||
thermostat_keypad_lockout: {
|
||
key: 'keypad_lockout',
|
||
convert: (key, value, message, type, postfix, options) => {
|
||
const cid = 'hvacUserInterfaceCfg';
|
||
const attrId = 'keypadLockout';
|
||
if (type === 'set') {
|
||
return [{
|
||
cid: cid,
|
||
cmd: 'write',
|
||
cmdType: 'foundation',
|
||
zclData: [{
|
||
attrId: Zcl.getAttributeLegacy(cid, attrId).value,
|
||
dataType: Zcl.getAttributeTypeLegacy(cid, attrId).value,
|
||
attrData: utils.getKeyByValue(common.keypadLockoutMode, value, value),
|
||
}],
|
||
cfg: cfg.default,
|
||
}];
|
||
}
|
||
},
|
||
},
|
||
fan_mode: {
|
||
key: ['fan_mode', 'fan_state'],
|
||
convert: (key, value, message, type, postfix, options) => {
|
||
const cid = 'hvacFanCtrl';
|
||
const attrId = 'fanMode';
|
||
|
||
if (type === 'set') {
|
||
value = value.toLowerCase();
|
||
const attrData = common.fanMode[value];
|
||
|
||
return [{
|
||
cid: cid,
|
||
cmd: 'write',
|
||
cmdType: 'foundation',
|
||
zclData: [{attrId: Zcl.getAttributeLegacy(cid, attrId).value, attrData: attrData, dataType: 48}],
|
||
cfg: cfg.default,
|
||
newState: {fan_mode: value, fan_state: value === 'off' ? 'OFF' : 'ON'},
|
||
}];
|
||
} else if (type === 'get') {
|
||
return [{
|
||
cid: cid,
|
||
cmd: 'read',
|
||
cmdType: 'foundation',
|
||
zclData: [{attrId: Zcl.getAttributeLegacy(cid, attrId).value}],
|
||
cfg: cfg.default,
|
||
}];
|
||
}
|
||
},
|
||
},
|
||
|
||
/**
|
||
* Device specific
|
||
*/
|
||
DJT11LM_vibration_sensitivity: {
|
||
key: ['sensitivity'],
|
||
convert: (key, value, message, type, postfix, options) => {
|
||
const cid = 'genBasic';
|
||
const attrId = 0xFF0D;
|
||
|
||
if (type === 'set') {
|
||
const lookup = {
|
||
'low': 0x15,
|
||
'medium': 0x0B,
|
||
'high': 0x01,
|
||
};
|
||
|
||
if (lookup.hasOwnProperty(value)) {
|
||
return [{
|
||
cid: cid,
|
||
cmd: 'write',
|
||
cmdType: 'foundation',
|
||
zclData: [{
|
||
attrId: attrId,
|
||
dataType: 0x20,
|
||
attrData: lookup[value],
|
||
}],
|
||
cfg: cfg.xiaomi,
|
||
}];
|
||
}
|
||
} else if (type === 'get') {
|
||
return [{
|
||
cid: cid,
|
||
cmd: 'read',
|
||
cmdType: 'foundation',
|
||
zclData: [{attrId: attrId}],
|
||
cfg: cfg.xiaomi,
|
||
}];
|
||
}
|
||
},
|
||
},
|
||
JTQJBF01LMBW_sensitivity: {
|
||
key: ['sensitivity'],
|
||
convert: (key, value, message, type, postfix, options) => {
|
||
const cid = 'ssIasZone';
|
||
|
||
if (type === 'set') {
|
||
const lookup = {
|
||
'low': 0x04010000,
|
||
'medium': 0x04020000,
|
||
'high': 0x04030000,
|
||
};
|
||
|
||
if (lookup.hasOwnProperty(value)) {
|
||
return [{
|
||
cid: cid,
|
||
cmd: 'write',
|
||
cmdType: 'foundation',
|
||
zclData: [{
|
||
attrId: 0xFFF1, // presentValue
|
||
dataType: 0x23, // dataType
|
||
attrData: lookup[value],
|
||
}],
|
||
cfg: cfg.xiaomi,
|
||
}];
|
||
}
|
||
} else if (type === 'get') {
|
||
return [{
|
||
cid: cid,
|
||
cmd: 'read',
|
||
cmdType: 'foundation',
|
||
zclData: [{
|
||
attrId: 0xFFF0, // presentValue
|
||
dataType: 0x39, // dataType
|
||
}],
|
||
cfg: cfg.xiaomi,
|
||
}];
|
||
}
|
||
},
|
||
},
|
||
JTQJBF01LMBW_selfest: {
|
||
key: ['selftest'],
|
||
convert: (key, value, message, type, postfix, options) => {
|
||
if (type === 'set') {
|
||
return [{
|
||
cid: 'ssIasZone',
|
||
cmd: 'write',
|
||
cmdType: 'foundation',
|
||
zclData: [{
|
||
attrId: 0xFFF1, // presentValue
|
||
dataType: 0x23, // dataType
|
||
attrData: 0x03010000,
|
||
}],
|
||
cfg: cfg.xiaomi,
|
||
}];
|
||
}
|
||
},
|
||
},
|
||
xiaomi_switch_operation_mode: {
|
||
key: ['operation_mode'],
|
||
convert: (key, value, message, type, postfix, options) => {
|
||
const cid = 'genBasic';
|
||
const lookupAttrId = {
|
||
'single': 0xFF22,
|
||
'left': 0xFF22,
|
||
'right': 0xFF23,
|
||
};
|
||
const lookupState = {
|
||
'control_relay': 0x12,
|
||
'control_left_relay': 0x12,
|
||
'control_right_relay': 0x22,
|
||
'decoupled': 0xFE,
|
||
};
|
||
let button;
|
||
if (value.hasOwnProperty('button')) {
|
||
button = value.button;
|
||
} else {
|
||
button = 'single';
|
||
}
|
||
if (type === 'set') {
|
||
return [{
|
||
cid: cid,
|
||
cmd: 'write',
|
||
cmdType: 'foundation',
|
||
zclData: [{
|
||
attrId: lookupAttrId[button],
|
||
dataType: 0x20,
|
||
attrData: lookupState[value.state],
|
||
}],
|
||
cfg: cfg.xiaomi,
|
||
}];
|
||
} else if (type === 'get') {
|
||
return [{
|
||
cid: cid,
|
||
cmd: 'read',
|
||
cmdType: 'foundation',
|
||
zclData: [{
|
||
attrId: lookupAttrId[button],
|
||
dataType: 0x20,
|
||
}],
|
||
cfg: cfg.xiaomi,
|
||
}];
|
||
}
|
||
},
|
||
},
|
||
STS_PRS_251_beep: {
|
||
key: ['beep'],
|
||
convert: (key, value, message, type, postfix, options) => {
|
||
const cid = 'genIdentify';
|
||
const attrId = 'identifyTime';
|
||
|
||
if (type === 'set') {
|
||
return [{
|
||
cid: cid,
|
||
cmd: 'identify',
|
||
cmdType: 'functional',
|
||
zclData: {
|
||
identifytime: value,
|
||
},
|
||
cfg: cfg.default,
|
||
}];
|
||
} else if (type === 'get') {
|
||
return [{
|
||
cid: cid,
|
||
cmd: 'read',
|
||
cmdType: 'foundation',
|
||
zclData: [{attrId: Zcl.getAttributeLegacy(cid, attrId).value}],
|
||
cfg: cfg.default,
|
||
}];
|
||
}
|
||
},
|
||
},
|
||
ZNCLDJ11LM_control: {
|
||
key: ['state', 'position'],
|
||
convert: (key, value, message, type, postfix, options) => {
|
||
if (key === 'state' && value.toLowerCase() === 'stop') {
|
||
return [{
|
||
cid: 'closuresWindowCovering',
|
||
cmd: 'stop',
|
||
cmdType: 'functional',
|
||
zclData: {},
|
||
cfg: cfg.default,
|
||
}];
|
||
} else {
|
||
const lookup = {
|
||
'open': 100,
|
||
'close': 0,
|
||
'on': 100,
|
||
'off': 0,
|
||
};
|
||
|
||
value = typeof value === 'string' ? value.toLowerCase() : value;
|
||
value = lookup.hasOwnProperty(value) ? lookup[value] : value;
|
||
|
||
return [{
|
||
cid: 'genAnalogOutput',
|
||
cmd: 'write',
|
||
cmdType: 'foundation',
|
||
zclData: [{
|
||
attrId: 0x0055,
|
||
dataType: 0x39,
|
||
attrData: value,
|
||
}],
|
||
cfg: cfg.default,
|
||
}];
|
||
}
|
||
},
|
||
},
|
||
osram_cmds: {
|
||
key: ['osram_set_transition', 'osram_remember_state'],
|
||
convert: (key, value, message, type, postfix, options) => {
|
||
if (type === 'set') {
|
||
if ( key === 'osram_set_transition' ) {
|
||
if (value) {
|
||
const transition = ( value > 1 ) ? (Math.round((value * 2).toFixed(1))/2).toFixed(1) * 10 : 1;
|
||
return [{
|
||
cid: 'genLevelCtrl',
|
||
cmd: 'write',
|
||
cmdType: 'foundation',
|
||
zclData: [{
|
||
attrId: 0x0012,
|
||
dataType: 0x21,
|
||
attrData: transition,
|
||
}, {attrId: 0x0013,
|
||
dataType: 0x21,
|
||
attrData: transition,
|
||
}],
|
||
cfg: cfg.default,
|
||
}];
|
||
}
|
||
} else if ( key == 'osram_remember_state' ) {
|
||
if (value === true) {
|
||
return [{
|
||
cid: 'manuSpecificOsram',
|
||
cmd: 'saveStartupParams',
|
||
cmdType: 'functional',
|
||
zclData: {},
|
||
cfg: cfg.osram,
|
||
}];
|
||
} else if ( value === false ) {
|
||
return [{
|
||
cid: 'manuSpecificOsram',
|
||
cmd: 'resetStartupParams',
|
||
cmdType: 'functional',
|
||
zclData: {},
|
||
cfg: cfg.osram,
|
||
}];
|
||
}
|
||
}
|
||
}
|
||
},
|
||
},
|
||
eurotronic_system_mode: {
|
||
key: 'eurotronic_system_mode',
|
||
convert: (key, value, message, type, postfix, options) => {
|
||
const cid = 'hvacThermostat';
|
||
const attrId = 0x4008;
|
||
if (type === 'set') {
|
||
return [{
|
||
cid: cid,
|
||
cmd: 'write',
|
||
cmdType: 'foundation',
|
||
zclData: [{
|
||
// Bit 0 = ? (default 1)
|
||
// Bit 2 = Boost
|
||
// Bit 4 = Window open
|
||
// Bit 7 = Child protection
|
||
attrId: attrId,
|
||
dataType: 0x22,
|
||
attrData: value,
|
||
}],
|
||
cfg: cfg.eurotronic,
|
||
}];
|
||
} else if (type === 'get') {
|
||
return [{
|
||
cid: cid,
|
||
cmd: 'read',
|
||
cmdType: 'foundation',
|
||
zclData: [{attrId: attrId}],
|
||
cfg: cfg.eurotronic,
|
||
}];
|
||
}
|
||
},
|
||
},
|
||
eurotronic_error_status: {
|
||
key: 'eurotronic_error_status',
|
||
convert: (key, value, message, type, postfix, options) => {
|
||
const cid = 'hvacThermostat';
|
||
const attrId = 0x4002;
|
||
if (type === 'get') {
|
||
return [{
|
||
cid: cid,
|
||
cmd: 'read',
|
||
cmdType: 'foundation',
|
||
zclData: [{attrId: attrId}],
|
||
cfg: cfg.eurotronic,
|
||
}];
|
||
}
|
||
},
|
||
},
|
||
eurotronic_current_heating_setpoint: {
|
||
key: 'current_heating_setpoint',
|
||
convert: (key, value, message, type, postfix, options) => {
|
||
const cid = 'hvacThermostat';
|
||
const attrId = 0x4003;
|
||
if (type === 'set') {
|
||
return [{
|
||
cid: cid,
|
||
cmd: 'write',
|
||
cmdType: 'foundation',
|
||
zclData: [{
|
||
attrId: attrId,
|
||
dataType: 0x29,
|
||
attrData: (Math.round((value * 2).toFixed(1))/2).toFixed(1) * 100,
|
||
}],
|
||
cfg: cfg.eurotronic,
|
||
}];
|
||
} else if (type === 'get') {
|
||
return [{
|
||
cid: cid,
|
||
cmd: 'read',
|
||
cmdType: 'foundation',
|
||
zclData: [{attrId: attrId}],
|
||
cfg: cfg.eurotronic,
|
||
}];
|
||
}
|
||
},
|
||
},
|
||
eurotronic_valve_position: {
|
||
key: 'eurotronic_valve_position',
|
||
convert: (key, value, message, type, postfix, options) => {
|
||
const cid = 'hvacThermostat';
|
||
const attrId = 0x4001;
|
||
if (type === 'set') {
|
||
return [{
|
||
cid: cid,
|
||
cmd: 'write',
|
||
cmdType: 'foundation',
|
||
zclData: [{
|
||
attrId: attrId,
|
||
dataType: 0x20,
|
||
attrData: value,
|
||
}],
|
||
cfg: cfg.eurotronic,
|
||
}];
|
||
} else if (type === 'get') {
|
||
return [{
|
||
cid: cid,
|
||
cmd: 'read',
|
||
cmdType: 'foundation',
|
||
zclData: [{attrId: attrId}],
|
||
cfg: cfg.eurotronic,
|
||
}];
|
||
}
|
||
},
|
||
},
|
||
eurotronic_trv_mode: {
|
||
key: 'eurotronic_trv_mode',
|
||
convert: (key, value, message, type, postfix, options) => {
|
||
const cid = 'hvacThermostat';
|
||
const attrId = 0x4000;
|
||
if (type === 'set') {
|
||
return [{
|
||
cid: cid,
|
||
cmd: 'write',
|
||
cmdType: 'foundation',
|
||
zclData: [{
|
||
attrId: attrId,
|
||
dataType: 0x30,
|
||
attrData: value,
|
||
}],
|
||
cfg: cfg.eurotronic,
|
||
}];
|
||
} else if (type === 'get') {
|
||
return [{
|
||
cid: cid,
|
||
cmd: 'read',
|
||
cmdType: 'foundation',
|
||
zclData: [{attrId: attrId}],
|
||
cfg: cfg.eurotronic,
|
||
}];
|
||
}
|
||
},
|
||
},
|
||
livolo_switch_on_off: {
|
||
key: ['state'],
|
||
convert: (key, value, message, type, postfix, options) => {
|
||
if (type === 'set') {
|
||
if (typeof value !== 'string') {
|
||
return;
|
||
}
|
||
|
||
postfix = postfix || 'left';
|
||
|
||
const cid = 'genLevelCtrl';
|
||
let state = value.toLowerCase();
|
||
let channel = 1;
|
||
|
||
if (state === 'on') {
|
||
state = 108;
|
||
} else if (state === 'off') {
|
||
state = 1;
|
||
} else {
|
||
return;
|
||
}
|
||
|
||
if (postfix === 'left') {
|
||
channel = 1.0;
|
||
} else if (postfix === 'right') {
|
||
channel = 2.0;
|
||
} else {
|
||
return;
|
||
}
|
||
|
||
return [{
|
||
cid: cid,
|
||
cmd: 'moveToLevelWithOnOff',
|
||
cmdType: 'functional',
|
||
zclData: {
|
||
level: state,
|
||
transtime: channel,
|
||
},
|
||
cfg: cfg.default,
|
||
newState: {state: value.toUpperCase()},
|
||
readAfterWriteTime: 250,
|
||
}];
|
||
} else if (type === 'get') {
|
||
const cid = 'genOnOff';
|
||
const attrId = 'onOff';
|
||
return [{
|
||
cid: cid,
|
||
cmd: 'read',
|
||
cmdType: 'foundation',
|
||
zclData: [{attrId: Zcl.getAttributeLegacy(cid, attrId).value}],
|
||
cfg: cfg.default,
|
||
}];
|
||
}
|
||
},
|
||
},
|
||
generic_lock: {
|
||
key: ['state'],
|
||
convert: (key, value, message, type, postfix, options) => {
|
||
const cid = 'closuresDoorLock';
|
||
const attrId = 'lockState';
|
||
|
||
if (type === 'set') {
|
||
if (typeof value !== 'string') {
|
||
return;
|
||
}
|
||
|
||
return [{
|
||
cid: cid,
|
||
cmd: `${value.toLowerCase()}Door`,
|
||
cmdType: 'functional',
|
||
zclData: {
|
||
'pincodevalue': '',
|
||
},
|
||
cfg: cfg.default,
|
||
readAfterWriteTime: 200,
|
||
}];
|
||
} else if (type === 'get') {
|
||
return [{
|
||
cid: cid,
|
||
cmd: 'read',
|
||
cmdType: 'foundation',
|
||
zclData: [{attrId: Zcl.getAttributeLegacy(cid, attrId).value}],
|
||
cfg: cfg.default,
|
||
}];
|
||
}
|
||
},
|
||
},
|
||
gledopto_light_onoff_brightness: {
|
||
key: ['state', 'brightness', 'brightness_percent'],
|
||
convert: (key, value, message, type, postfix, options) => {
|
||
if (message && message.hasOwnProperty('transition')) {
|
||
message.transition = message.transition * 3.3;
|
||
}
|
||
|
||
return converters.light_onoff_brightness.convert(key, value, message, type, postfix, options);
|
||
},
|
||
},
|
||
gledopto_light_color_colortemp: {
|
||
key: ['color', 'color_temp', 'color_temp_percent'],
|
||
convert: (key, value, message, type, postfix, options) => {
|
||
if (message && message.hasOwnProperty('transition')) {
|
||
message.transition = message.transition * 3.3;
|
||
}
|
||
|
||
return converters.light_color_colortemp.convert(key, value, message, type, postfix, options);
|
||
},
|
||
},
|
||
gledopto_light_colortemp: {
|
||
key: ['color_temp', 'color_temp_percent'],
|
||
convert: (key, value, message, type, postfix, options) => {
|
||
if (message && message.hasOwnProperty('transition')) {
|
||
message.transition = message.transition * 3.3;
|
||
}
|
||
|
||
return converters.light_colortemp.convert(key, value, message, type, postfix, options);
|
||
},
|
||
},
|
||
hue_power_on_behavior: {
|
||
key: ['hue_power_on_behavior'],
|
||
convert: (key, value, message, type, postfix, options) => {
|
||
const lookup = {
|
||
'default': 0x01,
|
||
'on': 0x01,
|
||
'off': 0x00,
|
||
'recover': 0xff,
|
||
};
|
||
|
||
if (type === 'set') {
|
||
return [{
|
||
cid: 'genOnOff',
|
||
cmd: 'write',
|
||
cmdType: 'foundation',
|
||
zclData: [{
|
||
attrId: 0x4003,
|
||
dataType: 0x30,
|
||
attrData: lookup[value],
|
||
}],
|
||
cfg: cfg.default,
|
||
}];
|
||
}
|
||
},
|
||
},
|
||
hue_power_on_brightness: {
|
||
key: ['hue_power_on_brightness'],
|
||
convert: (key, value, message, type, postfix, options) => {
|
||
if (type === 'set') {
|
||
if (value === 'default') {
|
||
value = 255;
|
||
}
|
||
return [{
|
||
cid: 'genLevelCtrl',
|
||
cmd: 'write',
|
||
cmdType: 'foundation',
|
||
zclData: [{
|
||
attrId: 0x4000,
|
||
dataType: 0x20,
|
||
attrData: value,
|
||
}],
|
||
cfg: cfg.default,
|
||
}];
|
||
}
|
||
},
|
||
},
|
||
hue_power_on_color_temperature: {
|
||
key: ['hue_power_on_color_temperature'],
|
||
convert: (key, value, message, type, postfix, options) => {
|
||
if (type === 'set') {
|
||
if (value === 'default') {
|
||
value = 366;
|
||
}
|
||
return [{
|
||
cid: 'lightingColorCtrl',
|
||
cmd: 'write',
|
||
cmdType: 'foundation',
|
||
zclData: [{
|
||
attrId: 0x4010,
|
||
dataType: 0x21,
|
||
attrData: value,
|
||
}],
|
||
cfg: cfg.default,
|
||
}];
|
||
}
|
||
},
|
||
},
|
||
hue_motion_sensitivity: {
|
||
// motion detect sensitivity, philips specific
|
||
key: ['motion_sensitivity'],
|
||
convert: (key, value, message, type, postfix, options) => {
|
||
const cid = 'msOccupancySensing'; // 1030
|
||
const attrId = 48;
|
||
if (type === 'set') {
|
||
// hue_sml:
|
||
// 0: low, 1: medium, 2: high (default)
|
||
// make sure you write to second endpoint!
|
||
const lookup = {
|
||
'low': 0,
|
||
'medium': 1,
|
||
'high': 2,
|
||
};
|
||
|
||
return [{
|
||
cid: cid,
|
||
cmd: 'write',
|
||
cmdType: 'foundation',
|
||
zclData: [{
|
||
attrId: attrId,
|
||
dataType: 32,
|
||
attrData: lookup[value],
|
||
}],
|
||
cfg: cfg.hue,
|
||
}];
|
||
} else if (type === 'get') {
|
||
return [{
|
||
cid: cid,
|
||
cmd: 'read',
|
||
cmdType: 'foundation',
|
||
zclData: [{
|
||
attrId: attrId,
|
||
}],
|
||
cfg: cfg.hue,
|
||
}];
|
||
}
|
||
},
|
||
},
|
||
ZigUP_lock: {
|
||
key: ['led'],
|
||
convert: (key, value, message, type, postfix, options) => {
|
||
const lookup = {
|
||
'off': 'lockDoor',
|
||
'on': 'unlockDoor',
|
||
'toggle': 'toggleDoor',
|
||
};
|
||
const cid = 'closuresDoorLock';
|
||
|
||
if (type === 'set') {
|
||
if (typeof value !== 'string') {
|
||
return;
|
||
}
|
||
|
||
return [{
|
||
cid: cid,
|
||
cmd: lookup[value],
|
||
cmdType: 'functional',
|
||
zclData: {
|
||
'pincodevalue': '',
|
||
},
|
||
cfg: cfg.default,
|
||
}];
|
||
}
|
||
},
|
||
},
|
||
|
||
// Sinope
|
||
sinope_thermostat_backlight_autodim_param: {
|
||
key: 'backlight_auto_dim',
|
||
convert: (key, value, message, type, postfix, options) => {
|
||
const cid = 'hvacThermostat';
|
||
const attrId = 0x0402;
|
||
if (type === 'set') {
|
||
const sinopeBacklightAutoDimParam = {
|
||
0: 'on demand',
|
||
1: 'sensing',
|
||
};
|
||
return [{
|
||
cid: cid,
|
||
cmd: 'write',
|
||
cmdType: 'foundation',
|
||
zclData: [{
|
||
attrId: attrId,
|
||
dataType: 0x30,
|
||
attrData: utils.getKeyByValue(sinopeBacklightAutoDimParam, value, value),
|
||
}],
|
||
cfg: cfg.default,
|
||
}];
|
||
}
|
||
},
|
||
},
|
||
sinope_thermostat_enable_outdoor_temperature: {
|
||
key: 'enable_outdoor_temperature',
|
||
convert: (key, value, message, type, postfix, options) => {
|
||
const cid = 0xFF01;
|
||
const attrId = 0x0011;
|
||
if (type === 'set' && value.toLowerCase()=='on') {
|
||
return [{
|
||
cid: cid,
|
||
cmd: 'write',
|
||
cmdType: 'foundation',
|
||
zclData: [{
|
||
attrId: attrId,
|
||
dataType: 0x21,
|
||
// set outdoor temperature timer to 3 hours
|
||
attrData: 10800,
|
||
}],
|
||
cfg: cfg.sinope,
|
||
}];
|
||
} else if (type === 'set' && value.toLowerCase()=='off') {
|
||
return [{
|
||
cid: cid,
|
||
cmd: 'write',
|
||
cmdType: 'foundation',
|
||
zclData: [{
|
||
attrId: attrId,
|
||
dataType: 0x21,
|
||
// set timer to 30sec in order to disable outdoor temperature
|
||
attrData: 30,
|
||
}],
|
||
cfg: cfg.sinope,
|
||
}];
|
||
}
|
||
},
|
||
},
|
||
sinope_thermostat_outdoor_temperature: {
|
||
key: 'thermostat_outdoor_temperature',
|
||
convert: (key, value, message, type, postfix, options) => {
|
||
const cid = 0xFF01;
|
||
const attrId = 0x0010;
|
||
if (type === 'set' && value > -100 && value < 100) {
|
||
return [{
|
||
cid: cid,
|
||
cmd: 'write',
|
||
cmdType: 'foundation',
|
||
zclData: [{
|
||
attrId: attrId,
|
||
dataType: 0x29,
|
||
attrData: value * 100,
|
||
}],
|
||
cfg: cfg.sinope,
|
||
}];
|
||
}
|
||
},
|
||
},
|
||
sinope_thermostat_time: {
|
||
key: 'thermostat_time',
|
||
convert: (key, value, message, type, postfix, options) => {
|
||
const cid = 0xFF01;
|
||
const attrId = 0x0020;
|
||
if (type === 'set' && value === '' ) {
|
||
const thermostatDate = new Date();
|
||
const thermostatTimeSec = thermostatDate.getTime() / 1000;
|
||
const thermostatTimezoneOffsetSec = thermostatDate.getTimezoneOffset() * 60;
|
||
return [{
|
||
cid: cid,
|
||
cmd: 'write',
|
||
cmdType: 'foundation',
|
||
zclData: [{
|
||
attrId: attrId,
|
||
dataType: 0x23,
|
||
// Current time in second since 2000-01-01T00:00 in the current time zone
|
||
attrData: Math.round(thermostatTimeSec - thermostatTimezoneOffsetSec - 946684800),
|
||
}],
|
||
cfg: cfg.sinope,
|
||
}];
|
||
} else if (type === 'set' && value !== '' ) {
|
||
return [{
|
||
cid: cid,
|
||
cmd: 'write',
|
||
cmdType: 'foundation',
|
||
zclData: [{
|
||
attrId: attrId,
|
||
dataType: 0x23,
|
||
attrData: value,
|
||
}],
|
||
cfg: cfg.sinope,
|
||
}];
|
||
}
|
||
},
|
||
},
|
||
DTB190502A1_LED: {
|
||
key: ['LED'],
|
||
convert: (key, value, message, type, postfix, options) => {
|
||
if (type === 'set') {
|
||
if (value === 'default') {
|
||
value = 1;
|
||
}
|
||
const lookup = {
|
||
'OFF': '0',
|
||
'ON': '1',
|
||
};
|
||
value = lookup[value];
|
||
// Check for valid data
|
||
if ( ((value >= 0) && value < 2) == false ) value = 0;
|
||
return [{
|
||
cid: 'genBasic',
|
||
cmd: 'write',
|
||
cmdType: 'foundation',
|
||
zclData: [{
|
||
attrId: 0x4010,
|
||
dataType: 0x21,
|
||
attrData: value,
|
||
}],
|
||
cfg: cfg.default,
|
||
}];
|
||
}
|
||
},
|
||
},
|
||
ptvo_switch_trigger: {
|
||
key: ['trigger', 'interval'],
|
||
convert: (key, value, message, type, postfix, options) => {
|
||
console.log('ptvo_switch_trigger:', key, value, message, type, postfix);
|
||
if (type === 'set') {
|
||
value = parseInt(value);
|
||
if (!value) {
|
||
return;
|
||
}
|
||
|
||
const cid = 'genOnOff';
|
||
|
||
if (key === 'trigger') {
|
||
return [{
|
||
cid: cid,
|
||
cmd: 'onWithTimedOff',
|
||
cmdType: 'functional',
|
||
zclData: {
|
||
ctrlbits: 0,
|
||
ontime: value,
|
||
offwaittime: 0,
|
||
},
|
||
cfg: cfg.default,
|
||
}];
|
||
} else if (key === 'interval') {
|
||
const attrId = 'onOff';
|
||
return [{
|
||
cid: cid,
|
||
cmd: 'configReport',
|
||
cmdType: 'foundation',
|
||
zclData: [{
|
||
direction: 0,
|
||
attrId: Zcl.getAttributeLegacy(cid, attrId).value,
|
||
dataType: Zcl.getAttributeTypeLegacy(cid, attrId).value,
|
||
attrData: value,
|
||
minRepIntval: value,
|
||
maxRepIntval: value,
|
||
}],
|
||
cfg: cfg.default,
|
||
}];
|
||
}
|
||
}
|
||
return;
|
||
},
|
||
},
|
||
|
||
/**
|
||
* Ignore converters
|
||
*/
|
||
ignore_transition: {
|
||
key: ['transition'],
|
||
attr: [],
|
||
convert: (key, value, message, type, postfix, options) => null,
|
||
},
|
||
};
|
||
|
||
module.exports = converters;
|