fetched version from zigbee2mqtt-pullrequest

This commit is contained in:
formtapez
2019-09-02 21:18:36 +02:00
parent d0f9c3b111
commit c14e12ebba
3 changed files with 1443 additions and 127 deletions

View File

@@ -30,6 +30,17 @@ const precisionRound = (number, precision) => {
return Math.round(number * factor) / factor;
};
const calibrateOptions = (number, options, type) => {
const key = `${type}_calibration`;
let calibrationOffset = options && options.hasOwnProperty(key) ? options[key] : 0;
if (type == 'illuminance') {
// linear calibration because measured value is zero based
// +/- percent
calibrationOffset = Math.round(number * calibrationOffset / 100);
}
return number + calibrationOffset;
};
const toPercentage = (value, min, max) => {
if (value > max) {
value = max;
@@ -447,7 +458,8 @@ const converters = {
type: ['attReport', 'readRsp'],
convert: (model, msg, publish, options) => {
const temperature = parseFloat(msg.data.data['measuredValue']) / 100.0;
return {temperature: precisionRoundOptions(temperature, options, 'temperature')};
const calTemperature = calibrateOptions(temperature, options, 'temperature');
return {temperature: precisionRoundOptions(calTemperature, options, 'temperature')};
},
},
generic_temperature_change: {
@@ -455,7 +467,8 @@ const converters = {
type: 'devChange',
convert: (model, msg, publish, options) => {
const temperature = parseFloat(msg.data.data['measuredValue']) / 100.0;
return {temperature: precisionRoundOptions(temperature, options, 'temperature')};
const calTemperature = calibrateOptions(temperature, options, 'temperature');
return {temperature: precisionRoundOptions(calTemperature, options, 'temperature')};
},
},
xiaomi_temperature: {
@@ -558,7 +571,7 @@ const converters = {
return lookup[value] ? lookup[value] : null;
},
},
xiaomi_humidity: {
generic_humidity: {
cid: 'msRelativeHumidity',
type: ['attReport', 'readRsp'],
convert: (model, msg, publish, options) => {
@@ -671,7 +684,7 @@ const converters = {
cid: 'genBasic',
type: ['attReport', 'readRsp'],
convert: (model, msg, publish, options) => {
if (msg.data.data.hasOwnProperty('65281')) {
if (msg.data.data.hasOwnProperty('65281') && msg.data.data['65281'].hasOwnProperty('100')) {
return {contact: msg.data.data['65281']['100'] === 0};
}
},
@@ -708,11 +721,11 @@ const converters = {
result.color = {};
if (msg.data.data['currentX']) {
result.color.x = precisionRound(msg.data.data['currentX'] / 65535, 3);
result.color.x = precisionRound(msg.data.data['currentX'] / 65535, 4);
}
if (msg.data.data['currentY']) {
result.color.y = precisionRound(msg.data.data['currentY'] / 65535, 3);
result.color.y = precisionRound(msg.data.data['currentY'] / 65535, 4);
}
if (msg.data.data['currentSaturation']) {
@@ -750,11 +763,11 @@ const converters = {
result.color = {};
if (msg.data.data['currentX']) {
result.color.x = precisionRound(msg.data.data['currentX'] / 65535, 3);
result.color.x = precisionRound(msg.data.data['currentX'] / 65535, 4);
}
if (msg.data.data['currentY']) {
result.color.y = precisionRound(msg.data.data['currentY'] / 65535, 3);
result.color.y = precisionRound(msg.data.data['currentY'] / 65535, 4);
}
if (msg.data.data['currentSaturation']) {
@@ -791,7 +804,10 @@ const converters = {
cid: 'msIlluminanceMeasurement',
type: ['attReport', 'readRsp'],
convert: (model, msg, publish, options) => {
return {illuminance: msg.data.data['measuredValue']};
const illuminance = msg.data.data['measuredValue'];
const calIlluminance = calibrateOptions(illuminance, options, 'illuminance');
// calibration value in +/- percent!
return {illuminance: calIlluminance};
},
},
generic_pressure: {
@@ -799,7 +815,8 @@ const converters = {
type: ['attReport', 'readRsp'],
convert: (model, msg, publish, options) => {
const pressure = parseFloat(msg.data.data['measuredValue']);
return {pressure: precisionRoundOptions(pressure, options, 'pressure')};
const calPressure = calibrateOptions(pressure, options, 'pressure');
return {pressure: precisionRoundOptions(calPressure, options, 'pressure')};
},
},
WXKG02LM_click: {
@@ -867,6 +884,15 @@ const converters = {
return {water_leak: msg.data.zoneStatus === 1};
},
},
SJCGQ11LM_water_leak_interval: {
cid: 'genBasic',
type: ['attReport', 'readRsp'],
convert: (model, msg, publish, options) => {
if (msg.data.data.hasOwnProperty('65281') && msg.data.data['65281'].hasOwnProperty('100')) {
return {water_leak: msg.data.data['65281']['100'] === 1};
}
},
},
state: {
cid: 'genOnOff',
type: ['attReport', 'readRsp'],
@@ -1190,6 +1216,7 @@ const converters = {
convert: (model, msg, publish, options) => {
const batt = msg.data.data.batteryPercentageRemaining;
const battLow = msg.data.data.batteryAlarmState;
const voltage = msg.data.data.batteryVoltage;
const results = {};
if (batt != null) {
const value = Math.round(batt/200.0*10000)/100; // Out of 200
@@ -1202,6 +1229,9 @@ const converters = {
results['battery_low'] = false;
}
}
if (voltage != null) {
results['voltage'] = voltage * 100;
}
return results;
},
},
@@ -1449,12 +1479,12 @@ const converters = {
'2': 'dig-in',
};
var ds18b20_id = null;
var ds18b20_value = null;
if (msg.data.data['41368']) {
ds18b20_id = msg.data.data['41368'].split(':')[0];
ds18b20_value = precisionRound(msg.data.data['41368'].split(':')[1], 2);
}
let ds18b20Id = null;
let ds18b20Value = null;
if (msg.data.data['41368']) {
ds18b20Id = msg.data.data['41368'].split(':')[0];
ds18b20Value = precisionRound(msg.data.data['41368'].split(':')[1], 2);
}
return {
state: msg.data.data['onOff'] === 1 ? 'ON' : 'OFF',
@@ -1465,7 +1495,7 @@ const converters = {
adc_volt: precisionRound(msg.data.data['41365'], 3),
dig_input: msg.data.data['41366'],
reason: lookup[msg.data.data['41367']],
[`${ds18b20_id}`]: ds18b20_value,
[`${ds18b20Id}`]: ds18b20Value,
};
},
},
@@ -1889,6 +1919,34 @@ const converters = {
};
},
},
generic_ias_zone_occupancy_status_change_no_off_msg: {
cid: 'ssIasZone',
type: 'statusChange',
convert: (model, msg, publish, options) => {
const zoneStatus = msg.data.zoneStatus;
const useOptionsTimeout = options && options.hasOwnProperty('occupancy_timeout');
const timeout = useOptionsTimeout ? options.occupancy_timeout : occupancyTimeout;
const deviceID = msg.endpoints[0].device.ieeeAddr;
if (store[deviceID]) {
clearTimeout(store[deviceID]);
store[deviceID] = null;
}
if (timeout !== 0) {
store[deviceID] = setTimeout(() => {
publish({occupancy: false});
store[deviceID] = null;
}, timeout * 1000);
}
return {
occupancy: (zoneStatus & 1) > 0, // Bit 0 = Alarm 1: Presence Indication
tamper: (zoneStatus & 1<<2) > 0, // Bit 2 = Tamper status
battery_low: (zoneStatus & 1<<3) > 0, // Bit 3 = Battery LOW indicator (trips around 2.4V)
};
},
},
generic_ias_zone_motion_dev_change: {
cid: 'ssIasZone',
type: 'devChange',
@@ -2076,6 +2134,10 @@ const converters = {
result.unoccupied_heating_setpoint =
precisionRound(msg.data.data['unoccupiedHeatingSetpoint'], 2) / 100;
}
if (typeof msg.data.data['occupiedCoolingSetpoint'] == 'number') {
result.occupied_cooling_setpoint =
precisionRound(msg.data.data['occupiedCoolingSetpoint'], 2) / 100;
}
if (typeof msg.data.data['weeklySchedule'] == 'number') {
result.weekly_schedule = msg.data.data['weeklySchedule'];
}
@@ -2136,6 +2198,10 @@ const converters = {
result.unoccupied_heating_setpoint =
precisionRound(msg.data.data['unoccupiedHeatingSetpoint'], 2) / 100;
}
if (typeof msg.data.data['occupiedCoolingSetpoint'] == 'number') {
result.occupied_cooling_setpoint =
precisionRound(msg.data.data['occupiedCoolingSetpoint'], 2) / 100;
}
if (typeof msg.data.data['weeklySchedule'] == 'number') {
result.weekly_schedule = msg.data.data['weeklySchedule'];
}
@@ -2606,6 +2672,25 @@ const converters = {
return {position: msg.data.data.currentPositionLiftPercentage};
},
},
closuresWindowCovering_report_pos_and_tilt: {
cid: 'closuresWindowCovering',
type: ['attReport', 'readRsp'],
convert: (model, msg, publish, options) => {
const result = {};
// ZigBee officially expects "open" to be 0 and "closed" to be 100 whereas
// HomeAssistant etc. work the other way round.
// ubisys J1 will report 255 if lift or tilt positions are not known.
if (msg.data.data.hasOwnProperty('currentPositionLiftPercentage')) {
const liftPercentage = msg.data.data['currentPositionLiftPercentage'];
result.position = liftPercentage <= 100 ? (100 - liftPercentage) : null;
}
if (msg.data.data.hasOwnProperty('currentPositionTiltPercentage')) {
const tiltPercentage = msg.data.data['currentPositionTiltPercentage'];
result.tilt = tiltPercentage <= 100 ? (100 - tiltPercentage) : null;
}
return result;
},
},
generic_fan_mode: {
cid: 'hvacFanCtrl',
type: 'attReport',
@@ -2799,7 +2884,7 @@ const converters = {
return result;
},
},
ZNMS12LM_closuresDoorLock_report: {
ZNMS12LM_ZNMS13LM_closuresDoorLock_report: {
cid: 'closuresDoorLock',
type: 'attReport',
convert: (model, msg, publish, options) => {
@@ -2821,6 +2906,7 @@ const converters = {
14: 'change_language_to',
15: 'finger_open',
16: 'password_open',
17: 'door_closed',
};
result.user = null;
result.repeat = null;
@@ -2828,34 +2914,68 @@ const converters = {
// Convert data back to hex to decode
const data = Buffer.from(msg.data.data['65526'], 'ascii').toString('hex');
const command = data.substr(6, 4);
if (command === '0301') {
if (
command === '0301' // ZNMS12LM
|| command === '0341' // ZNMS13LM
) {
result.action = lockStatusLookup[4];
result.state = 'UNLOCK';
result.reverse = 'UNLOCK';
} else if (command === '0311') {
} else if (
command === '0311' // ZNMS12LM
|| command === '0351' // ZNMS13LM
) {
result.action = lockStatusLookup[4];
result.state = 'LOCK';
result.reverse = 'UNLOCK';
} else if (command === '0205') {
} else if (
command === '0205' // ZNMS12LM
|| command === '0245' // ZNMS13LM
) {
result.action = lockStatusLookup[3];
result.state = 'UNLOCK';
result.reverse = 'LOCK';
} else if (command === '0215') {
} else if (
command === '0215' // ZNMS12LM
|| command === '0255' // ZNMS13LM
|| command === '1355' // ZNMS13LM
) {
result.action = lockStatusLookup[3];
result.state = 'LOCK';
result.reverse = 'LOCK';
} else if (command === '0111') {
} else if (
command === '0111' // ZNMS12LM
|| command === '1351' // ZNMS13LM locked from inside
|| command === '1451' // ZNMS13LM locked from outside
) {
result.action = lockStatusLookup[5];
result.state = 'LOCK';
result.reverse = 'UNLOCK';
} else if (command === '0b00') {
} else if (
command === '0b00' // ZNMS12LM
|| command === '0640' // ZNMS13LM
||command === '0600' // ZNMS13LM
) {
result.action = lockStatusLookup[12];
result.state = 'UNLOCK';
result.reverse = 'UNLOCK';
} else if (command === '0c00') {
} else if (
command === '0c00' // ZNMS12LM
|| command === '2300' // ZNMS13LM
|| command === '0540' // ZNMS13LM
|| command === '0440' // ZNMS13LM
) {
result.action = lockStatusLookup[11];
result.state = 'UNLOCK';
result.reverse = 'UNLOCK';
} else if (
command === '2400' // ZNMS13LM door closed from insed
|| command === '2401' // ZNMS13LM door closed from outside
) {
result.action = lockStatusLookup[17];
result.state = 'UNLOCK';
result.reverse = 'UNLOCK';
}
} else if (msg.data.data['65296']) { // finger/password success
const data = Buffer.from(msg.data.data['65296'], 'ascii').toString('hex');
@@ -2896,6 +3016,126 @@ const converters = {
return result;
},
},
DTB190502A1_parse: {
cid: 'genOnOff',
type: ['attReport', 'readRsp'],
convert: (model, msg, publish, options) => {
const lookupKEY = {
'0': 'KEY_SYS',
'1': 'KEY_UP',
'2': 'KEY_DOWN',
'3': 'KEY_NONE',
};
const lookupLED = {
'0': 'OFF',
'1': 'ON',
};
return {
cpu_temperature: precisionRound(msg.data.data['41361'], 2),
key_state: lookupKEY[msg.data.data['41362']],
led_state: lookupLED[msg.data.data['41363']],
};
},
},
konke_click: {
cid: 'genOnOff',
type: ['attReport', 'readRsp'],
convert: (model, msg, publish, options) => {
const value = msg.data.data['onOff'];
const lookup = {
128: {click: 'single'}, // single click
129: {click: 'double'}, // double and many click
130: {click: 'long'}, // hold
};
return lookup[value] ? lookup[value] : null;
},
},
E1746_linkquality: {
cid: 'genBasic',
type: ['attReport', 'readRsp'],
convert: (model, msg, publish, options) => {
return {linkquality: msg.linkquality};
},
},
generic_change_batteryvoltage_3000_2500: {
cid: 'genPowerCfg',
type: ['devChange'],
convert: (model, msg, publish, options) => {
const battery = {max: 3000, min: 2500};
const voltage = msg.data.data['batteryVoltage'] * 100;
return {
battery: toPercentage(voltage, battery.min, battery.max),
voltage: voltage,
};
},
},
generic_device_temperature: {
cid: 'genDeviceTempCfg',
type: ['devChange'],
convert: (model, msg, publish, options) => {
if (msg.data.data.hasOwnProperty('currentTemperature')) {
return {temperature: msg.data.data.currentTemperature};
}
},
},
ptvo_switch_state: {
cid: 'genOnOff',
type: 'attReport',
convert: (model, msg, publish, options) => {
const ep = msg.endpoints[0];
const key = `state_${getKey(model.ep(ep.device), ep.epId)}`;
const payload = {};
payload[key] = msg.data.data['onOff'] === 1 ? 'ON' : 'OFF';
return payload;
},
},
ptvo_switch_buttons: {
cid: 'genMultistateInput',
type: 'attReport',
convert: (model, msg, publish, options) => {
const ep = msg.endpoints[0];
const button = getKey(model.ep(ep.device), ep.epId);
const value = msg.data.data['presentValue'];
const actionLookup = {
1: 'single',
2: 'double',
3: 'tripple',
4: 'hold',
};
const action = actionLookup[value];
if (button) {
return {click: button + (action ? `_${action}` : '')};
}
},
},
keypad20states: {
cid: 'genOnOff',
type: ['devChange', 'attReport'],
convert: (model, msg, publish, options) => {
const ep = msg.endpoints[0];
const button = getKey(model.ep(ep.device), ep.epId);
const state = msg.data.data['onOff'] === 1 ? true : false;
if (button) {
return {[button]: state};
}
},
},
keypad20_battery: {
cid: 'genPowerCfg',
type: ['devChange', 'attReport'],
convert: (model, msg, publish, options) => {
const battery = {max: 3000, min: 2100};
const voltage = msg.data.data['mainsVoltage'] /10;
return {
battery: toPercentage(voltage, battery.min, battery.max),
voltage: voltage,
};
},
},
// Ignore converters (these message dont need parsing).
ignore_fan_change: {
@@ -3015,7 +3255,7 @@ const converters = {
},
ignore_metering_change: {
cid: 'seMetering',
type: 'devChange',
type: ['devChange', 'attReport'],
convert: (model, msg, publish, options) => null,
},
ignore_electrical_change: {

View File

@@ -2,7 +2,7 @@
const utils = require('./utils');
const common = require('./common');
const zclId = require('zigbee-herdsman/dist/zcl-id');
const Zcl = require('zigbee-herdsman/dist/zcl');
const cfg = {
default: {
@@ -31,8 +31,22 @@ const cfg = {
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
@@ -75,7 +89,7 @@ const converters = {
cid: cid,
cmd: 'read',
cmdType: 'foundation',
zclData: [{attrId: zclId.attr(cid, attrId).value}],
zclData: [{attrId: Zcl.getAttributeLegacy(cid, attrId).value}],
cfg: cfg.default,
}];
}
@@ -124,7 +138,7 @@ const converters = {
cid: cid,
cmd: 'read',
cmdType: 'foundation',
zclData: [{attrId: zclId.attr(cid, attrId).value}],
zclData: [{attrId: Zcl.getAttributeLegacy(cid, attrId).value}],
cfg: cfg.default,
}];
}
@@ -170,12 +184,64 @@ const converters = {
}
},
},
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 = zclId.attr(cid, 'pirOToUDelay').value; // = 16
const attrId = Zcl.getAttributeLegacy(cid, 'pirOToUDelay').value; // = 16
if (type === 'set') {
return [{
@@ -228,7 +294,7 @@ const converters = {
cmdType: 'functional',
zclData: {
level: Number(value),
transtime: message.hasOwnProperty('transition') ? message.transition * 10 : 0,
transtime: getTransition(message, options) * 10,
},
cfg: cfg.default,
newState: {brightness: Number(value)},
@@ -240,7 +306,7 @@ const converters = {
cid: cid,
cmd: 'read',
cmdType: 'foundation',
zclData: [{attrId: zclId.attr(cid, attrId).value}],
zclData: [{attrId: Zcl.getAttributeLegacy(cid, attrId).value}],
cfg: cfg.default,
}];
}
@@ -255,7 +321,7 @@ const converters = {
const brightnessValue = message.hasOwnProperty('brightness') ?
message.brightness : message.brightness_percent;
const hasState = message.hasOwnProperty('state');
const hasTrasition = message.hasOwnProperty('transition');
const hasTrasition = message.hasOwnProperty('transition') || options.hasOwnProperty('transition');
const state = hasState ? message.state.toLowerCase() : null;
if (hasState && (state === 'off' || !hasBrightness) && !hasTrasition) {
@@ -279,17 +345,19 @@ const converters = {
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: message.hasOwnProperty('transition') ? message.transition * 10 : 0,
transtime: transition * 10,
},
cfg: options.disFeedbackRsp ? cfg.defaultdisFeedbackRsp : cfg.default,
newState: {state: brightness === 0 ? 'OFF' : 'ON', brightness: Number(brightness)},
readAfterWriteTime: message.hasOwnProperty('transition') ? message.transition * 1000 : 0,
readAfterWriteTime: transition * 1000,
}];
}
} else if (type === 'get') {
@@ -298,14 +366,14 @@ const converters = {
cid: 'genOnOff',
cmd: 'read',
cmdType: 'foundation',
zclData: [{attrId: zclId.attr('genOnOff', 'onOff').value}],
zclData: [{attrId: Zcl.getAttributeLegacy('genOnOff', 'onOff').value}],
cfg: cfg.default,
},
{
cid: 'genLevelCtrl',
cmd: 'read',
cmdType: 'foundation',
zclData: [{attrId: zclId.attr('genLevelCtrl', 'currentLevel').value}],
zclData: [{attrId: Zcl.getAttributeLegacy('genLevelCtrl', 'currentLevel').value}],
cfg: cfg.default,
},
];
@@ -332,7 +400,7 @@ const converters = {
cmdType: 'functional',
zclData: {
colortemp: value,
transtime: message.hasOwnProperty('transition') ? message.transition * 10 : 0,
transtime: getTransition(message, options) * 10,
},
cfg: cfg.default,
newState: {color_temp: value},
@@ -343,7 +411,7 @@ const converters = {
cid: cid,
cmd: 'read',
cmdType: 'foundation',
zclData: [{attrId: zclId.attr(cid, attrId).value}],
zclData: [{attrId: Zcl.getAttributeLegacy(cid, attrId).value}],
cfg: cfg.default,
}];
}
@@ -383,7 +451,7 @@ const converters = {
}
const zclData = {
transtime: message.hasOwnProperty('transition') ? message.transition * 10 : 0,
transtime: getTransition(message, options) * 10,
};
let newState = null;
@@ -424,8 +492,8 @@ const converters = {
cmd: 'read',
cmdType: 'foundation',
zclData: [
{attrId: zclId.attr(cid, 'currentX').value},
{attrId: zclId.attr(cid, 'currentY').value},
{attrId: Zcl.getAttributeLegacy(cid, 'currentX').value},
{attrId: Zcl.getAttributeLegacy(cid, 'currentY').value},
],
cfg: cfg.default,
}];
@@ -460,9 +528,9 @@ const converters = {
cmd: 'read',
cmdType: 'foundation',
zclData: [
{attrId: zclId.attr(cid, 'currentX').value},
{attrId: zclId.attr(cid, 'currentY').value},
{attrId: zclId.attr(cid, 'colorTemperature').value},
{attrId: Zcl.getAttributeLegacy(cid, 'currentX').value},
{attrId: Zcl.getAttributeLegacy(cid, 'currentY').value},
{attrId: Zcl.getAttributeLegacy(cid, 'colorTemperature').value},
],
cfg: cfg.default,
}];
@@ -509,7 +577,7 @@ const converters = {
cid: cid,
cmd: 'read',
cmdType: 'foundation',
zclData: [{attrId: zclId.attr(cid, attrId).value}],
zclData: [{attrId: Zcl.getAttributeLegacy(cid, attrId).value}],
cfg: cfg.default,
}];
}
@@ -526,8 +594,8 @@ const converters = {
cmd: 'write',
cmdType: 'foundation',
zclData: [{
attrId: zclId.attr(cid, attrId).value,
dataType: zclId.attrType(cid, attrId).value,
attrId: Zcl.getAttributeLegacy(cid, attrId).value,
dataType: Zcl.getAttributeTypeLegacy(cid, attrId).value,
attrData: Math.round(value * 10),
}],
cfg: cfg.default,
@@ -537,7 +605,7 @@ const converters = {
cid: cid,
cmd: 'read',
cmdType: 'foundation',
zclData: [{attrId: zclId.attr(cid, attrId).value}],
zclData: [{attrId: Zcl.getAttributeLegacy(cid, attrId).value}],
cfg: cfg.default,
}];
}
@@ -553,7 +621,7 @@ const converters = {
cid: cid,
cmd: 'read',
cmdType: 'foundation',
zclData: [{attrId: zclId.attr(cid, attrId).value}],
zclData: [{attrId: Zcl.getAttributeLegacy(cid, attrId).value}],
cfg: cfg.default,
}];
}
@@ -570,8 +638,8 @@ const converters = {
cmd: 'write',
cmdType: 'foundation',
zclData: [{
attrId: zclId.attr(cid, attrId).value,
dataType: zclId.attrType(cid, attrId).value,
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,
@@ -581,7 +649,7 @@ const converters = {
cid: cid,
cmd: 'read',
cmdType: 'foundation',
zclData: [{attrId: zclId.attr(cid, attrId).value}],
zclData: [{attrId: Zcl.getAttributeLegacy(cid, attrId).value}],
cfg: cfg.default,
}];
}
@@ -598,8 +666,8 @@ const converters = {
cmd: 'write',
cmdType: 'foundation',
zclData: [{
attrId: zclId.attr(cid, attrId).value,
dataType: zclId.attrType(cid, attrId).value,
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,
@@ -609,7 +677,35 @@ const converters = {
cid: cid,
cmd: 'read',
cmdType: 'foundation',
zclData: [{attrId: zclId.attr(cid, attrId).value}],
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,
}];
}
@@ -632,8 +728,8 @@ const converters = {
// Bit 1 = 1 outdoor temperature sensed remotely
// Bit 2 = 0 occupancy sensed internally
// Bit 2 = 1 occupancy sensed remotely
attrId: zclId.attr(cid, attrId).value,
dataType: zclId.attrType(cid, attrId).value,
attrId: Zcl.getAttributeLegacy(cid, attrId).value,
dataType: Zcl.getAttributeTypeLegacy(cid, attrId).value,
attrData: value, // TODO: Lookup in Zigbee documentation
}],
cfg: cfg.default,
@@ -643,7 +739,7 @@ const converters = {
cid: cid,
cmd: 'read',
cmdType: 'foundation',
zclData: [{attrId: zclId.attr(cid, attrId).value}],
zclData: [{attrId: Zcl.getAttributeLegacy(cid, attrId).value}],
cfg: cfg.default,
}];
}
@@ -660,8 +756,8 @@ const converters = {
cmd: 'write',
cmdType: 'foundation',
zclData: [{
attrId: zclId.attr(cid, attrId).value,
dataType: zclId.attrType(cid, attrId).value,
attrId: Zcl.getAttributeLegacy(cid, attrId).value,
dataType: Zcl.getAttributeTypeLegacy(cid, attrId).value,
attrData: utils.getKeyByValue(common.thermostatControlSequenceOfOperations, value, value),
}],
cfg: cfg.default,
@@ -671,7 +767,7 @@ const converters = {
cid: cid,
cmd: 'read',
cmdType: 'foundation',
zclData: [{attrId: zclId.attr(cid, attrId).value}],
zclData: [{attrId: Zcl.getAttributeLegacy(cid, attrId).value}],
cfg: cfg.default,
}];
}
@@ -688,8 +784,8 @@ const converters = {
cmd: 'write',
cmdType: 'foundation',
zclData: [{
attrId: zclId.attr(cid, attrId).value,
dataType: zclId.attrType(cid, attrId).value,
attrId: Zcl.getAttributeLegacy(cid, attrId).value,
dataType: Zcl.getAttributeTypeLegacy(cid, attrId).value,
attrData: utils.getKeyByValue(common.thermostatSystemModes, value, value),
}],
cfg: cfg.default,
@@ -700,7 +796,7 @@ const converters = {
cid: cid,
cmd: 'read',
cmdType: 'foundation',
zclData: [{attrId: zclId.attr(cid, attrId).value}],
zclData: [{attrId: Zcl.getAttributeLegacy(cid, attrId).value}],
cfg: cfg.default,
}];
}
@@ -735,7 +831,7 @@ const converters = {
cmd: 'setWeeklySchedule',
cmdType: 'functional',
zclData: {
dataType: zclId.attrType(cid, attrId).value,
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,
@@ -825,7 +921,7 @@ const converters = {
cid: cid,
cmd: 'read',
cmdType: 'foundation',
zclData: [{attrId: zclId.attr(cid, attrId).value}],
zclData: [{attrId: Zcl.getAttributeLegacy(cid, attrId).value}],
cfg: cfg.default,
}];
}
@@ -841,7 +937,7 @@ const converters = {
cid: cid,
cmd: 'read',
cmdType: 'foundation',
zclData: [{attrId: zclId.attr(cid, attrId).value}],
zclData: [{attrId: Zcl.getAttributeLegacy(cid, attrId).value}],
cfg: cfg.default,
}];
}
@@ -858,9 +954,29 @@ const converters = {
cmd: 'write',
cmdType: 'foundation',
zclData: [{
attrId: zclId.attr(cid, attrId).value,
dataType: zclId.attrType(cid, attrId).value,
attrData: value,
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,
}];
@@ -881,7 +997,7 @@ const converters = {
cid: cid,
cmd: 'write',
cmdType: 'foundation',
zclData: [{attrId: zclId.attr(cid, attrId).value, attrData: attrData, dataType: 48}],
zclData: [{attrId: Zcl.getAttributeLegacy(cid, attrId).value, attrData: attrData, dataType: 48}],
cfg: cfg.default,
newState: {fan_mode: value, fan_state: value === 'off' ? 'OFF' : 'ON'},
}];
@@ -890,7 +1006,7 @@ const converters = {
cid: cid,
cmd: 'read',
cmdType: 'foundation',
zclData: [{attrId: zclId.attr(cid, attrId).value}],
zclData: [{attrId: Zcl.getAttributeLegacy(cid, attrId).value}],
cfg: cfg.default,
}];
}
@@ -1062,7 +1178,7 @@ const converters = {
cid: cid,
cmd: 'read',
cmdType: 'foundation',
zclData: [{attrId: zclId.attr(cid, attrId).value}],
zclData: [{attrId: Zcl.getAttributeLegacy(cid, attrId).value}],
cfg: cfg.default,
}];
}
@@ -1329,7 +1445,7 @@ const converters = {
cid: cid,
cmd: 'read',
cmdType: 'foundation',
zclData: [{attrId: zclId.attr(cid, attrId).value}],
zclData: [{attrId: Zcl.getAttributeLegacy(cid, attrId).value}],
cfg: cfg.default,
}];
}
@@ -1361,7 +1477,7 @@ const converters = {
cid: cid,
cmd: 'read',
cmdType: 'foundation',
zclData: [{attrId: zclId.attr(cid, attrId).value}],
zclData: [{attrId: Zcl.getAttributeLegacy(cid, attrId).value}],
cfg: cfg.default,
}];
}
@@ -1380,7 +1496,7 @@ const converters = {
gledopto_light_color_colortemp: {
key: ['color', 'color_temp', 'color_temp_percent'],
convert: (key, value, message, type, postfix, options) => {
if (message.hasOwnProperty('transition')) {
if (message && message.hasOwnProperty('transition')) {
message.transition = message.transition * 3.3;
}
@@ -1390,7 +1506,7 @@ const converters = {
gledopto_light_colortemp: {
key: ['color_temp', 'color_temp_percent'],
convert: (key, value, message, type, postfix, options) => {
if (message.hasOwnProperty('transition')) {
if (message && message.hasOwnProperty('transition')) {
message.transition = message.transition * 3.3;
}
@@ -1532,6 +1648,195 @@ const converters = {
},
},
// 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
*/

File diff suppressed because it is too large Load Diff