mirror of
https://github.com/formtapez/ZigUP.git
synced 2025-02-23 09:34:50 +01:00
fetched version from zigbee2mqtt-pullrequest
This commit is contained in:
@@ -30,6 +30,17 @@ const precisionRound = (number, precision) => {
|
|||||||
return Math.round(number * factor) / factor;
|
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) => {
|
const toPercentage = (value, min, max) => {
|
||||||
if (value > max) {
|
if (value > max) {
|
||||||
value = max;
|
value = max;
|
||||||
@@ -447,7 +458,8 @@ const converters = {
|
|||||||
type: ['attReport', 'readRsp'],
|
type: ['attReport', 'readRsp'],
|
||||||
convert: (model, msg, publish, options) => {
|
convert: (model, msg, publish, options) => {
|
||||||
const temperature = parseFloat(msg.data.data['measuredValue']) / 100.0;
|
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: {
|
generic_temperature_change: {
|
||||||
@@ -455,7 +467,8 @@ const converters = {
|
|||||||
type: 'devChange',
|
type: 'devChange',
|
||||||
convert: (model, msg, publish, options) => {
|
convert: (model, msg, publish, options) => {
|
||||||
const temperature = parseFloat(msg.data.data['measuredValue']) / 100.0;
|
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: {
|
xiaomi_temperature: {
|
||||||
@@ -558,7 +571,7 @@ const converters = {
|
|||||||
return lookup[value] ? lookup[value] : null;
|
return lookup[value] ? lookup[value] : null;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
xiaomi_humidity: {
|
generic_humidity: {
|
||||||
cid: 'msRelativeHumidity',
|
cid: 'msRelativeHumidity',
|
||||||
type: ['attReport', 'readRsp'],
|
type: ['attReport', 'readRsp'],
|
||||||
convert: (model, msg, publish, options) => {
|
convert: (model, msg, publish, options) => {
|
||||||
@@ -671,7 +684,7 @@ const converters = {
|
|||||||
cid: 'genBasic',
|
cid: 'genBasic',
|
||||||
type: ['attReport', 'readRsp'],
|
type: ['attReport', 'readRsp'],
|
||||||
convert: (model, msg, publish, options) => {
|
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};
|
return {contact: msg.data.data['65281']['100'] === 0};
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -708,11 +721,11 @@ const converters = {
|
|||||||
result.color = {};
|
result.color = {};
|
||||||
|
|
||||||
if (msg.data.data['currentX']) {
|
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']) {
|
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']) {
|
if (msg.data.data['currentSaturation']) {
|
||||||
@@ -750,11 +763,11 @@ const converters = {
|
|||||||
result.color = {};
|
result.color = {};
|
||||||
|
|
||||||
if (msg.data.data['currentX']) {
|
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']) {
|
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']) {
|
if (msg.data.data['currentSaturation']) {
|
||||||
@@ -791,7 +804,10 @@ const converters = {
|
|||||||
cid: 'msIlluminanceMeasurement',
|
cid: 'msIlluminanceMeasurement',
|
||||||
type: ['attReport', 'readRsp'],
|
type: ['attReport', 'readRsp'],
|
||||||
convert: (model, msg, publish, options) => {
|
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: {
|
generic_pressure: {
|
||||||
@@ -799,7 +815,8 @@ const converters = {
|
|||||||
type: ['attReport', 'readRsp'],
|
type: ['attReport', 'readRsp'],
|
||||||
convert: (model, msg, publish, options) => {
|
convert: (model, msg, publish, options) => {
|
||||||
const pressure = parseFloat(msg.data.data['measuredValue']);
|
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: {
|
WXKG02LM_click: {
|
||||||
@@ -867,6 +884,15 @@ const converters = {
|
|||||||
return {water_leak: msg.data.zoneStatus === 1};
|
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: {
|
state: {
|
||||||
cid: 'genOnOff',
|
cid: 'genOnOff',
|
||||||
type: ['attReport', 'readRsp'],
|
type: ['attReport', 'readRsp'],
|
||||||
@@ -1190,6 +1216,7 @@ const converters = {
|
|||||||
convert: (model, msg, publish, options) => {
|
convert: (model, msg, publish, options) => {
|
||||||
const batt = msg.data.data.batteryPercentageRemaining;
|
const batt = msg.data.data.batteryPercentageRemaining;
|
||||||
const battLow = msg.data.data.batteryAlarmState;
|
const battLow = msg.data.data.batteryAlarmState;
|
||||||
|
const voltage = msg.data.data.batteryVoltage;
|
||||||
const results = {};
|
const results = {};
|
||||||
if (batt != null) {
|
if (batt != null) {
|
||||||
const value = Math.round(batt/200.0*10000)/100; // Out of 200
|
const value = Math.round(batt/200.0*10000)/100; // Out of 200
|
||||||
@@ -1202,6 +1229,9 @@ const converters = {
|
|||||||
results['battery_low'] = false;
|
results['battery_low'] = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (voltage != null) {
|
||||||
|
results['voltage'] = voltage * 100;
|
||||||
|
}
|
||||||
return results;
|
return results;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -1449,12 +1479,12 @@ const converters = {
|
|||||||
'2': 'dig-in',
|
'2': 'dig-in',
|
||||||
};
|
};
|
||||||
|
|
||||||
var ds18b20_id = null;
|
let ds18b20Id = null;
|
||||||
var ds18b20_value = null;
|
let ds18b20Value = null;
|
||||||
if (msg.data.data['41368']) {
|
if (msg.data.data['41368']) {
|
||||||
ds18b20_id = msg.data.data['41368'].split(':')[0];
|
ds18b20Id = msg.data.data['41368'].split(':')[0];
|
||||||
ds18b20_value = precisionRound(msg.data.data['41368'].split(':')[1], 2);
|
ds18b20Value = precisionRound(msg.data.data['41368'].split(':')[1], 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
state: msg.data.data['onOff'] === 1 ? 'ON' : 'OFF',
|
state: msg.data.data['onOff'] === 1 ? 'ON' : 'OFF',
|
||||||
@@ -1465,7 +1495,7 @@ const converters = {
|
|||||||
adc_volt: precisionRound(msg.data.data['41365'], 3),
|
adc_volt: precisionRound(msg.data.data['41365'], 3),
|
||||||
dig_input: msg.data.data['41366'],
|
dig_input: msg.data.data['41366'],
|
||||||
reason: lookup[msg.data.data['41367']],
|
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: {
|
generic_ias_zone_motion_dev_change: {
|
||||||
cid: 'ssIasZone',
|
cid: 'ssIasZone',
|
||||||
type: 'devChange',
|
type: 'devChange',
|
||||||
@@ -2076,6 +2134,10 @@ const converters = {
|
|||||||
result.unoccupied_heating_setpoint =
|
result.unoccupied_heating_setpoint =
|
||||||
precisionRound(msg.data.data['unoccupiedHeatingSetpoint'], 2) / 100;
|
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') {
|
if (typeof msg.data.data['weeklySchedule'] == 'number') {
|
||||||
result.weekly_schedule = msg.data.data['weeklySchedule'];
|
result.weekly_schedule = msg.data.data['weeklySchedule'];
|
||||||
}
|
}
|
||||||
@@ -2136,6 +2198,10 @@ const converters = {
|
|||||||
result.unoccupied_heating_setpoint =
|
result.unoccupied_heating_setpoint =
|
||||||
precisionRound(msg.data.data['unoccupiedHeatingSetpoint'], 2) / 100;
|
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') {
|
if (typeof msg.data.data['weeklySchedule'] == 'number') {
|
||||||
result.weekly_schedule = msg.data.data['weeklySchedule'];
|
result.weekly_schedule = msg.data.data['weeklySchedule'];
|
||||||
}
|
}
|
||||||
@@ -2606,6 +2672,25 @@ const converters = {
|
|||||||
return {position: msg.data.data.currentPositionLiftPercentage};
|
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: {
|
generic_fan_mode: {
|
||||||
cid: 'hvacFanCtrl',
|
cid: 'hvacFanCtrl',
|
||||||
type: 'attReport',
|
type: 'attReport',
|
||||||
@@ -2799,7 +2884,7 @@ const converters = {
|
|||||||
return result;
|
return result;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
ZNMS12LM_closuresDoorLock_report: {
|
ZNMS12LM_ZNMS13LM_closuresDoorLock_report: {
|
||||||
cid: 'closuresDoorLock',
|
cid: 'closuresDoorLock',
|
||||||
type: 'attReport',
|
type: 'attReport',
|
||||||
convert: (model, msg, publish, options) => {
|
convert: (model, msg, publish, options) => {
|
||||||
@@ -2821,6 +2906,7 @@ const converters = {
|
|||||||
14: 'change_language_to',
|
14: 'change_language_to',
|
||||||
15: 'finger_open',
|
15: 'finger_open',
|
||||||
16: 'password_open',
|
16: 'password_open',
|
||||||
|
17: 'door_closed',
|
||||||
};
|
};
|
||||||
result.user = null;
|
result.user = null;
|
||||||
result.repeat = null;
|
result.repeat = null;
|
||||||
@@ -2828,34 +2914,68 @@ const converters = {
|
|||||||
// Convert data back to hex to decode
|
// Convert data back to hex to decode
|
||||||
const data = Buffer.from(msg.data.data['65526'], 'ascii').toString('hex');
|
const data = Buffer.from(msg.data.data['65526'], 'ascii').toString('hex');
|
||||||
const command = data.substr(6, 4);
|
const command = data.substr(6, 4);
|
||||||
if (command === '0301') {
|
if (
|
||||||
|
command === '0301' // ZNMS12LM
|
||||||
|
|| command === '0341' // ZNMS13LM
|
||||||
|
) {
|
||||||
result.action = lockStatusLookup[4];
|
result.action = lockStatusLookup[4];
|
||||||
result.state = 'UNLOCK';
|
result.state = 'UNLOCK';
|
||||||
result.reverse = 'UNLOCK';
|
result.reverse = 'UNLOCK';
|
||||||
} else if (command === '0311') {
|
} else if (
|
||||||
|
command === '0311' // ZNMS12LM
|
||||||
|
|| command === '0351' // ZNMS13LM
|
||||||
|
) {
|
||||||
result.action = lockStatusLookup[4];
|
result.action = lockStatusLookup[4];
|
||||||
result.state = 'LOCK';
|
result.state = 'LOCK';
|
||||||
result.reverse = 'UNLOCK';
|
result.reverse = 'UNLOCK';
|
||||||
} else if (command === '0205') {
|
} else if (
|
||||||
|
command === '0205' // ZNMS12LM
|
||||||
|
|| command === '0245' // ZNMS13LM
|
||||||
|
) {
|
||||||
result.action = lockStatusLookup[3];
|
result.action = lockStatusLookup[3];
|
||||||
result.state = 'UNLOCK';
|
result.state = 'UNLOCK';
|
||||||
result.reverse = 'LOCK';
|
result.reverse = 'LOCK';
|
||||||
} else if (command === '0215') {
|
} else if (
|
||||||
|
command === '0215' // ZNMS12LM
|
||||||
|
|| command === '0255' // ZNMS13LM
|
||||||
|
|| command === '1355' // ZNMS13LM
|
||||||
|
) {
|
||||||
result.action = lockStatusLookup[3];
|
result.action = lockStatusLookup[3];
|
||||||
result.state = 'LOCK';
|
result.state = 'LOCK';
|
||||||
result.reverse = '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.action = lockStatusLookup[5];
|
||||||
result.state = 'LOCK';
|
result.state = 'LOCK';
|
||||||
result.reverse = 'UNLOCK';
|
result.reverse = 'UNLOCK';
|
||||||
} else if (command === '0b00') {
|
} else if (
|
||||||
|
command === '0b00' // ZNMS12LM
|
||||||
|
|| command === '0640' // ZNMS13LM
|
||||||
|
||command === '0600' // ZNMS13LM
|
||||||
|
|
||||||
|
) {
|
||||||
result.action = lockStatusLookup[12];
|
result.action = lockStatusLookup[12];
|
||||||
result.state = 'UNLOCK';
|
result.state = 'UNLOCK';
|
||||||
result.reverse = '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.action = lockStatusLookup[11];
|
||||||
result.state = 'UNLOCK';
|
result.state = 'UNLOCK';
|
||||||
result.reverse = '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
|
} else if (msg.data.data['65296']) { // finger/password success
|
||||||
const data = Buffer.from(msg.data.data['65296'], 'ascii').toString('hex');
|
const data = Buffer.from(msg.data.data['65296'], 'ascii').toString('hex');
|
||||||
@@ -2896,6 +3016,126 @@ const converters = {
|
|||||||
return result;
|
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 converters (these message dont need parsing).
|
||||||
ignore_fan_change: {
|
ignore_fan_change: {
|
||||||
@@ -3015,7 +3255,7 @@ const converters = {
|
|||||||
},
|
},
|
||||||
ignore_metering_change: {
|
ignore_metering_change: {
|
||||||
cid: 'seMetering',
|
cid: 'seMetering',
|
||||||
type: 'devChange',
|
type: ['devChange', 'attReport'],
|
||||||
convert: (model, msg, publish, options) => null,
|
convert: (model, msg, publish, options) => null,
|
||||||
},
|
},
|
||||||
ignore_electrical_change: {
|
ignore_electrical_change: {
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
const utils = require('./utils');
|
const utils = require('./utils');
|
||||||
const common = require('./common');
|
const common = require('./common');
|
||||||
const zclId = require('zigbee-herdsman/dist/zcl-id');
|
const Zcl = require('zigbee-herdsman/dist/zcl');
|
||||||
|
|
||||||
const cfg = {
|
const cfg = {
|
||||||
default: {
|
default: {
|
||||||
@@ -31,8 +31,22 @@ const cfg = {
|
|||||||
manufSpec: 1,
|
manufSpec: 1,
|
||||||
manufCode: 0x110c,
|
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 = {
|
const converters = {
|
||||||
/**
|
/**
|
||||||
* Generic
|
* Generic
|
||||||
@@ -75,7 +89,7 @@ const converters = {
|
|||||||
cid: cid,
|
cid: cid,
|
||||||
cmd: 'read',
|
cmd: 'read',
|
||||||
cmdType: 'foundation',
|
cmdType: 'foundation',
|
||||||
zclData: [{attrId: zclId.attr(cid, attrId).value}],
|
zclData: [{attrId: Zcl.getAttributeLegacy(cid, attrId).value}],
|
||||||
cfg: cfg.default,
|
cfg: cfg.default,
|
||||||
}];
|
}];
|
||||||
}
|
}
|
||||||
@@ -124,7 +138,7 @@ const converters = {
|
|||||||
cid: cid,
|
cid: cid,
|
||||||
cmd: 'read',
|
cmd: 'read',
|
||||||
cmdType: 'foundation',
|
cmdType: 'foundation',
|
||||||
zclData: [{attrId: zclId.attr(cid, attrId).value}],
|
zclData: [{attrId: Zcl.getAttributeLegacy(cid, attrId).value}],
|
||||||
cfg: cfg.default,
|
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: {
|
occupancy_timeout: {
|
||||||
// set delay after motion detector changes from occupied to unoccupied
|
// set delay after motion detector changes from occupied to unoccupied
|
||||||
key: ['occupancy_timeout'],
|
key: ['occupancy_timeout'],
|
||||||
convert: (key, value, message, type, postfix, options) => {
|
convert: (key, value, message, type, postfix, options) => {
|
||||||
const cid = 'msOccupancySensing'; // 1030
|
const cid = 'msOccupancySensing'; // 1030
|
||||||
const attrId = zclId.attr(cid, 'pirOToUDelay').value; // = 16
|
const attrId = Zcl.getAttributeLegacy(cid, 'pirOToUDelay').value; // = 16
|
||||||
|
|
||||||
if (type === 'set') {
|
if (type === 'set') {
|
||||||
return [{
|
return [{
|
||||||
@@ -228,7 +294,7 @@ const converters = {
|
|||||||
cmdType: 'functional',
|
cmdType: 'functional',
|
||||||
zclData: {
|
zclData: {
|
||||||
level: Number(value),
|
level: Number(value),
|
||||||
transtime: message.hasOwnProperty('transition') ? message.transition * 10 : 0,
|
transtime: getTransition(message, options) * 10,
|
||||||
},
|
},
|
||||||
cfg: cfg.default,
|
cfg: cfg.default,
|
||||||
newState: {brightness: Number(value)},
|
newState: {brightness: Number(value)},
|
||||||
@@ -240,7 +306,7 @@ const converters = {
|
|||||||
cid: cid,
|
cid: cid,
|
||||||
cmd: 'read',
|
cmd: 'read',
|
||||||
cmdType: 'foundation',
|
cmdType: 'foundation',
|
||||||
zclData: [{attrId: zclId.attr(cid, attrId).value}],
|
zclData: [{attrId: Zcl.getAttributeLegacy(cid, attrId).value}],
|
||||||
cfg: cfg.default,
|
cfg: cfg.default,
|
||||||
}];
|
}];
|
||||||
}
|
}
|
||||||
@@ -255,7 +321,7 @@ const converters = {
|
|||||||
const brightnessValue = message.hasOwnProperty('brightness') ?
|
const brightnessValue = message.hasOwnProperty('brightness') ?
|
||||||
message.brightness : message.brightness_percent;
|
message.brightness : message.brightness_percent;
|
||||||
const hasState = message.hasOwnProperty('state');
|
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;
|
const state = hasState ? message.state.toLowerCase() : null;
|
||||||
|
|
||||||
if (hasState && (state === 'off' || !hasBrightness) && !hasTrasition) {
|
if (hasState && (state === 'off' || !hasBrightness) && !hasTrasition) {
|
||||||
@@ -279,17 +345,19 @@ const converters = {
|
|||||||
brightness = Math.round(Number(message.brightness_percent) * 2.55).toString();
|
brightness = Math.round(Number(message.brightness_percent) * 2.55).toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const transition = getTransition(message, options);
|
||||||
|
|
||||||
return [{
|
return [{
|
||||||
cid: 'genLevelCtrl',
|
cid: 'genLevelCtrl',
|
||||||
cmd: 'moveToLevelWithOnOff',
|
cmd: 'moveToLevelWithOnOff',
|
||||||
cmdType: 'functional',
|
cmdType: 'functional',
|
||||||
zclData: {
|
zclData: {
|
||||||
level: Number(brightness),
|
level: Number(brightness),
|
||||||
transtime: message.hasOwnProperty('transition') ? message.transition * 10 : 0,
|
transtime: transition * 10,
|
||||||
},
|
},
|
||||||
cfg: options.disFeedbackRsp ? cfg.defaultdisFeedbackRsp : cfg.default,
|
cfg: options.disFeedbackRsp ? cfg.defaultdisFeedbackRsp : cfg.default,
|
||||||
newState: {state: brightness === 0 ? 'OFF' : 'ON', brightness: Number(brightness)},
|
newState: {state: brightness === 0 ? 'OFF' : 'ON', brightness: Number(brightness)},
|
||||||
readAfterWriteTime: message.hasOwnProperty('transition') ? message.transition * 1000 : 0,
|
readAfterWriteTime: transition * 1000,
|
||||||
}];
|
}];
|
||||||
}
|
}
|
||||||
} else if (type === 'get') {
|
} else if (type === 'get') {
|
||||||
@@ -298,14 +366,14 @@ const converters = {
|
|||||||
cid: 'genOnOff',
|
cid: 'genOnOff',
|
||||||
cmd: 'read',
|
cmd: 'read',
|
||||||
cmdType: 'foundation',
|
cmdType: 'foundation',
|
||||||
zclData: [{attrId: zclId.attr('genOnOff', 'onOff').value}],
|
zclData: [{attrId: Zcl.getAttributeLegacy('genOnOff', 'onOff').value}],
|
||||||
cfg: cfg.default,
|
cfg: cfg.default,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
cid: 'genLevelCtrl',
|
cid: 'genLevelCtrl',
|
||||||
cmd: 'read',
|
cmd: 'read',
|
||||||
cmdType: 'foundation',
|
cmdType: 'foundation',
|
||||||
zclData: [{attrId: zclId.attr('genLevelCtrl', 'currentLevel').value}],
|
zclData: [{attrId: Zcl.getAttributeLegacy('genLevelCtrl', 'currentLevel').value}],
|
||||||
cfg: cfg.default,
|
cfg: cfg.default,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
@@ -332,7 +400,7 @@ const converters = {
|
|||||||
cmdType: 'functional',
|
cmdType: 'functional',
|
||||||
zclData: {
|
zclData: {
|
||||||
colortemp: value,
|
colortemp: value,
|
||||||
transtime: message.hasOwnProperty('transition') ? message.transition * 10 : 0,
|
transtime: getTransition(message, options) * 10,
|
||||||
},
|
},
|
||||||
cfg: cfg.default,
|
cfg: cfg.default,
|
||||||
newState: {color_temp: value},
|
newState: {color_temp: value},
|
||||||
@@ -343,7 +411,7 @@ const converters = {
|
|||||||
cid: cid,
|
cid: cid,
|
||||||
cmd: 'read',
|
cmd: 'read',
|
||||||
cmdType: 'foundation',
|
cmdType: 'foundation',
|
||||||
zclData: [{attrId: zclId.attr(cid, attrId).value}],
|
zclData: [{attrId: Zcl.getAttributeLegacy(cid, attrId).value}],
|
||||||
cfg: cfg.default,
|
cfg: cfg.default,
|
||||||
}];
|
}];
|
||||||
}
|
}
|
||||||
@@ -383,7 +451,7 @@ const converters = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const zclData = {
|
const zclData = {
|
||||||
transtime: message.hasOwnProperty('transition') ? message.transition * 10 : 0,
|
transtime: getTransition(message, options) * 10,
|
||||||
};
|
};
|
||||||
|
|
||||||
let newState = null;
|
let newState = null;
|
||||||
@@ -424,8 +492,8 @@ const converters = {
|
|||||||
cmd: 'read',
|
cmd: 'read',
|
||||||
cmdType: 'foundation',
|
cmdType: 'foundation',
|
||||||
zclData: [
|
zclData: [
|
||||||
{attrId: zclId.attr(cid, 'currentX').value},
|
{attrId: Zcl.getAttributeLegacy(cid, 'currentX').value},
|
||||||
{attrId: zclId.attr(cid, 'currentY').value},
|
{attrId: Zcl.getAttributeLegacy(cid, 'currentY').value},
|
||||||
],
|
],
|
||||||
cfg: cfg.default,
|
cfg: cfg.default,
|
||||||
}];
|
}];
|
||||||
@@ -460,9 +528,9 @@ const converters = {
|
|||||||
cmd: 'read',
|
cmd: 'read',
|
||||||
cmdType: 'foundation',
|
cmdType: 'foundation',
|
||||||
zclData: [
|
zclData: [
|
||||||
{attrId: zclId.attr(cid, 'currentX').value},
|
{attrId: Zcl.getAttributeLegacy(cid, 'currentX').value},
|
||||||
{attrId: zclId.attr(cid, 'currentY').value},
|
{attrId: Zcl.getAttributeLegacy(cid, 'currentY').value},
|
||||||
{attrId: zclId.attr(cid, 'colorTemperature').value},
|
{attrId: Zcl.getAttributeLegacy(cid, 'colorTemperature').value},
|
||||||
],
|
],
|
||||||
cfg: cfg.default,
|
cfg: cfg.default,
|
||||||
}];
|
}];
|
||||||
@@ -509,7 +577,7 @@ const converters = {
|
|||||||
cid: cid,
|
cid: cid,
|
||||||
cmd: 'read',
|
cmd: 'read',
|
||||||
cmdType: 'foundation',
|
cmdType: 'foundation',
|
||||||
zclData: [{attrId: zclId.attr(cid, attrId).value}],
|
zclData: [{attrId: Zcl.getAttributeLegacy(cid, attrId).value}],
|
||||||
cfg: cfg.default,
|
cfg: cfg.default,
|
||||||
}];
|
}];
|
||||||
}
|
}
|
||||||
@@ -526,8 +594,8 @@ const converters = {
|
|||||||
cmd: 'write',
|
cmd: 'write',
|
||||||
cmdType: 'foundation',
|
cmdType: 'foundation',
|
||||||
zclData: [{
|
zclData: [{
|
||||||
attrId: zclId.attr(cid, attrId).value,
|
attrId: Zcl.getAttributeLegacy(cid, attrId).value,
|
||||||
dataType: zclId.attrType(cid, attrId).value,
|
dataType: Zcl.getAttributeTypeLegacy(cid, attrId).value,
|
||||||
attrData: Math.round(value * 10),
|
attrData: Math.round(value * 10),
|
||||||
}],
|
}],
|
||||||
cfg: cfg.default,
|
cfg: cfg.default,
|
||||||
@@ -537,7 +605,7 @@ const converters = {
|
|||||||
cid: cid,
|
cid: cid,
|
||||||
cmd: 'read',
|
cmd: 'read',
|
||||||
cmdType: 'foundation',
|
cmdType: 'foundation',
|
||||||
zclData: [{attrId: zclId.attr(cid, attrId).value}],
|
zclData: [{attrId: Zcl.getAttributeLegacy(cid, attrId).value}],
|
||||||
cfg: cfg.default,
|
cfg: cfg.default,
|
||||||
}];
|
}];
|
||||||
}
|
}
|
||||||
@@ -553,7 +621,7 @@ const converters = {
|
|||||||
cid: cid,
|
cid: cid,
|
||||||
cmd: 'read',
|
cmd: 'read',
|
||||||
cmdType: 'foundation',
|
cmdType: 'foundation',
|
||||||
zclData: [{attrId: zclId.attr(cid, attrId).value}],
|
zclData: [{attrId: Zcl.getAttributeLegacy(cid, attrId).value}],
|
||||||
cfg: cfg.default,
|
cfg: cfg.default,
|
||||||
}];
|
}];
|
||||||
}
|
}
|
||||||
@@ -570,8 +638,8 @@ const converters = {
|
|||||||
cmd: 'write',
|
cmd: 'write',
|
||||||
cmdType: 'foundation',
|
cmdType: 'foundation',
|
||||||
zclData: [{
|
zclData: [{
|
||||||
attrId: zclId.attr(cid, attrId).value,
|
attrId: Zcl.getAttributeLegacy(cid, attrId).value,
|
||||||
dataType: zclId.attrType(cid, attrId).value,
|
dataType: Zcl.getAttributeTypeLegacy(cid, attrId).value,
|
||||||
attrData: (Math.round((value * 2).toFixed(1))/2).toFixed(1) * 100,
|
attrData: (Math.round((value * 2).toFixed(1))/2).toFixed(1) * 100,
|
||||||
}],
|
}],
|
||||||
cfg: cfg.default,
|
cfg: cfg.default,
|
||||||
@@ -581,7 +649,7 @@ const converters = {
|
|||||||
cid: cid,
|
cid: cid,
|
||||||
cmd: 'read',
|
cmd: 'read',
|
||||||
cmdType: 'foundation',
|
cmdType: 'foundation',
|
||||||
zclData: [{attrId: zclId.attr(cid, attrId).value}],
|
zclData: [{attrId: Zcl.getAttributeLegacy(cid, attrId).value}],
|
||||||
cfg: cfg.default,
|
cfg: cfg.default,
|
||||||
}];
|
}];
|
||||||
}
|
}
|
||||||
@@ -598,8 +666,8 @@ const converters = {
|
|||||||
cmd: 'write',
|
cmd: 'write',
|
||||||
cmdType: 'foundation',
|
cmdType: 'foundation',
|
||||||
zclData: [{
|
zclData: [{
|
||||||
attrId: zclId.attr(cid, attrId).value,
|
attrId: Zcl.getAttributeLegacy(cid, attrId).value,
|
||||||
dataType: zclId.attrType(cid, attrId).value,
|
dataType: Zcl.getAttributeTypeLegacy(cid, attrId).value,
|
||||||
attrData: (Math.round((value * 2).toFixed(1))/2).toFixed(1) * 100,
|
attrData: (Math.round((value * 2).toFixed(1))/2).toFixed(1) * 100,
|
||||||
}],
|
}],
|
||||||
cfg: cfg.default,
|
cfg: cfg.default,
|
||||||
@@ -609,7 +677,35 @@ const converters = {
|
|||||||
cid: cid,
|
cid: cid,
|
||||||
cmd: 'read',
|
cmd: 'read',
|
||||||
cmdType: 'foundation',
|
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,
|
cfg: cfg.default,
|
||||||
}];
|
}];
|
||||||
}
|
}
|
||||||
@@ -632,8 +728,8 @@ const converters = {
|
|||||||
// Bit 1 = 1 – outdoor temperature sensed remotely
|
// Bit 1 = 1 – outdoor temperature sensed remotely
|
||||||
// Bit 2 = 0 – occupancy sensed internally
|
// Bit 2 = 0 – occupancy sensed internally
|
||||||
// Bit 2 = 1 – occupancy sensed remotely
|
// Bit 2 = 1 – occupancy sensed remotely
|
||||||
attrId: zclId.attr(cid, attrId).value,
|
attrId: Zcl.getAttributeLegacy(cid, attrId).value,
|
||||||
dataType: zclId.attrType(cid, attrId).value,
|
dataType: Zcl.getAttributeTypeLegacy(cid, attrId).value,
|
||||||
attrData: value, // TODO: Lookup in Zigbee documentation
|
attrData: value, // TODO: Lookup in Zigbee documentation
|
||||||
}],
|
}],
|
||||||
cfg: cfg.default,
|
cfg: cfg.default,
|
||||||
@@ -643,7 +739,7 @@ const converters = {
|
|||||||
cid: cid,
|
cid: cid,
|
||||||
cmd: 'read',
|
cmd: 'read',
|
||||||
cmdType: 'foundation',
|
cmdType: 'foundation',
|
||||||
zclData: [{attrId: zclId.attr(cid, attrId).value}],
|
zclData: [{attrId: Zcl.getAttributeLegacy(cid, attrId).value}],
|
||||||
cfg: cfg.default,
|
cfg: cfg.default,
|
||||||
}];
|
}];
|
||||||
}
|
}
|
||||||
@@ -660,8 +756,8 @@ const converters = {
|
|||||||
cmd: 'write',
|
cmd: 'write',
|
||||||
cmdType: 'foundation',
|
cmdType: 'foundation',
|
||||||
zclData: [{
|
zclData: [{
|
||||||
attrId: zclId.attr(cid, attrId).value,
|
attrId: Zcl.getAttributeLegacy(cid, attrId).value,
|
||||||
dataType: zclId.attrType(cid, attrId).value,
|
dataType: Zcl.getAttributeTypeLegacy(cid, attrId).value,
|
||||||
attrData: utils.getKeyByValue(common.thermostatControlSequenceOfOperations, value, value),
|
attrData: utils.getKeyByValue(common.thermostatControlSequenceOfOperations, value, value),
|
||||||
}],
|
}],
|
||||||
cfg: cfg.default,
|
cfg: cfg.default,
|
||||||
@@ -671,7 +767,7 @@ const converters = {
|
|||||||
cid: cid,
|
cid: cid,
|
||||||
cmd: 'read',
|
cmd: 'read',
|
||||||
cmdType: 'foundation',
|
cmdType: 'foundation',
|
||||||
zclData: [{attrId: zclId.attr(cid, attrId).value}],
|
zclData: [{attrId: Zcl.getAttributeLegacy(cid, attrId).value}],
|
||||||
cfg: cfg.default,
|
cfg: cfg.default,
|
||||||
}];
|
}];
|
||||||
}
|
}
|
||||||
@@ -688,8 +784,8 @@ const converters = {
|
|||||||
cmd: 'write',
|
cmd: 'write',
|
||||||
cmdType: 'foundation',
|
cmdType: 'foundation',
|
||||||
zclData: [{
|
zclData: [{
|
||||||
attrId: zclId.attr(cid, attrId).value,
|
attrId: Zcl.getAttributeLegacy(cid, attrId).value,
|
||||||
dataType: zclId.attrType(cid, attrId).value,
|
dataType: Zcl.getAttributeTypeLegacy(cid, attrId).value,
|
||||||
attrData: utils.getKeyByValue(common.thermostatSystemModes, value, value),
|
attrData: utils.getKeyByValue(common.thermostatSystemModes, value, value),
|
||||||
}],
|
}],
|
||||||
cfg: cfg.default,
|
cfg: cfg.default,
|
||||||
@@ -700,7 +796,7 @@ const converters = {
|
|||||||
cid: cid,
|
cid: cid,
|
||||||
cmd: 'read',
|
cmd: 'read',
|
||||||
cmdType: 'foundation',
|
cmdType: 'foundation',
|
||||||
zclData: [{attrId: zclId.attr(cid, attrId).value}],
|
zclData: [{attrId: Zcl.getAttributeLegacy(cid, attrId).value}],
|
||||||
cfg: cfg.default,
|
cfg: cfg.default,
|
||||||
}];
|
}];
|
||||||
}
|
}
|
||||||
@@ -735,7 +831,7 @@ const converters = {
|
|||||||
cmd: 'setWeeklySchedule',
|
cmd: 'setWeeklySchedule',
|
||||||
cmdType: 'functional',
|
cmdType: 'functional',
|
||||||
zclData: {
|
zclData: {
|
||||||
dataType: zclId.attrType(cid, attrId).value,
|
dataType: Zcl.getAttributeTypeLegacy(cid, attrId).value,
|
||||||
attrData: value, // TODO: Combine attributes in attrData?
|
attrData: value, // TODO: Combine attributes in attrData?
|
||||||
temperature_setpoint_hold: value.temperature_setpoint_hold,
|
temperature_setpoint_hold: value.temperature_setpoint_hold,
|
||||||
temperature_setpoint_hold_duration: value.temperature_setpoint_hold_duration,
|
temperature_setpoint_hold_duration: value.temperature_setpoint_hold_duration,
|
||||||
@@ -825,7 +921,7 @@ const converters = {
|
|||||||
cid: cid,
|
cid: cid,
|
||||||
cmd: 'read',
|
cmd: 'read',
|
||||||
cmdType: 'foundation',
|
cmdType: 'foundation',
|
||||||
zclData: [{attrId: zclId.attr(cid, attrId).value}],
|
zclData: [{attrId: Zcl.getAttributeLegacy(cid, attrId).value}],
|
||||||
cfg: cfg.default,
|
cfg: cfg.default,
|
||||||
}];
|
}];
|
||||||
}
|
}
|
||||||
@@ -841,7 +937,7 @@ const converters = {
|
|||||||
cid: cid,
|
cid: cid,
|
||||||
cmd: 'read',
|
cmd: 'read',
|
||||||
cmdType: 'foundation',
|
cmdType: 'foundation',
|
||||||
zclData: [{attrId: zclId.attr(cid, attrId).value}],
|
zclData: [{attrId: Zcl.getAttributeLegacy(cid, attrId).value}],
|
||||||
cfg: cfg.default,
|
cfg: cfg.default,
|
||||||
}];
|
}];
|
||||||
}
|
}
|
||||||
@@ -858,9 +954,29 @@ const converters = {
|
|||||||
cmd: 'write',
|
cmd: 'write',
|
||||||
cmdType: 'foundation',
|
cmdType: 'foundation',
|
||||||
zclData: [{
|
zclData: [{
|
||||||
attrId: zclId.attr(cid, attrId).value,
|
attrId: Zcl.getAttributeLegacy(cid, attrId).value,
|
||||||
dataType: zclId.attrType(cid, attrId).value,
|
dataType: Zcl.getAttributeTypeLegacy(cid, attrId).value,
|
||||||
attrData: 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,
|
cfg: cfg.default,
|
||||||
}];
|
}];
|
||||||
@@ -881,7 +997,7 @@ const converters = {
|
|||||||
cid: cid,
|
cid: cid,
|
||||||
cmd: 'write',
|
cmd: 'write',
|
||||||
cmdType: 'foundation',
|
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,
|
cfg: cfg.default,
|
||||||
newState: {fan_mode: value, fan_state: value === 'off' ? 'OFF' : 'ON'},
|
newState: {fan_mode: value, fan_state: value === 'off' ? 'OFF' : 'ON'},
|
||||||
}];
|
}];
|
||||||
@@ -890,7 +1006,7 @@ const converters = {
|
|||||||
cid: cid,
|
cid: cid,
|
||||||
cmd: 'read',
|
cmd: 'read',
|
||||||
cmdType: 'foundation',
|
cmdType: 'foundation',
|
||||||
zclData: [{attrId: zclId.attr(cid, attrId).value}],
|
zclData: [{attrId: Zcl.getAttributeLegacy(cid, attrId).value}],
|
||||||
cfg: cfg.default,
|
cfg: cfg.default,
|
||||||
}];
|
}];
|
||||||
}
|
}
|
||||||
@@ -1062,7 +1178,7 @@ const converters = {
|
|||||||
cid: cid,
|
cid: cid,
|
||||||
cmd: 'read',
|
cmd: 'read',
|
||||||
cmdType: 'foundation',
|
cmdType: 'foundation',
|
||||||
zclData: [{attrId: zclId.attr(cid, attrId).value}],
|
zclData: [{attrId: Zcl.getAttributeLegacy(cid, attrId).value}],
|
||||||
cfg: cfg.default,
|
cfg: cfg.default,
|
||||||
}];
|
}];
|
||||||
}
|
}
|
||||||
@@ -1329,7 +1445,7 @@ const converters = {
|
|||||||
cid: cid,
|
cid: cid,
|
||||||
cmd: 'read',
|
cmd: 'read',
|
||||||
cmdType: 'foundation',
|
cmdType: 'foundation',
|
||||||
zclData: [{attrId: zclId.attr(cid, attrId).value}],
|
zclData: [{attrId: Zcl.getAttributeLegacy(cid, attrId).value}],
|
||||||
cfg: cfg.default,
|
cfg: cfg.default,
|
||||||
}];
|
}];
|
||||||
}
|
}
|
||||||
@@ -1361,7 +1477,7 @@ const converters = {
|
|||||||
cid: cid,
|
cid: cid,
|
||||||
cmd: 'read',
|
cmd: 'read',
|
||||||
cmdType: 'foundation',
|
cmdType: 'foundation',
|
||||||
zclData: [{attrId: zclId.attr(cid, attrId).value}],
|
zclData: [{attrId: Zcl.getAttributeLegacy(cid, attrId).value}],
|
||||||
cfg: cfg.default,
|
cfg: cfg.default,
|
||||||
}];
|
}];
|
||||||
}
|
}
|
||||||
@@ -1380,7 +1496,7 @@ const converters = {
|
|||||||
gledopto_light_color_colortemp: {
|
gledopto_light_color_colortemp: {
|
||||||
key: ['color', 'color_temp', 'color_temp_percent'],
|
key: ['color', 'color_temp', 'color_temp_percent'],
|
||||||
convert: (key, value, message, type, postfix, options) => {
|
convert: (key, value, message, type, postfix, options) => {
|
||||||
if (message.hasOwnProperty('transition')) {
|
if (message && message.hasOwnProperty('transition')) {
|
||||||
message.transition = message.transition * 3.3;
|
message.transition = message.transition * 3.3;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1390,7 +1506,7 @@ const converters = {
|
|||||||
gledopto_light_colortemp: {
|
gledopto_light_colortemp: {
|
||||||
key: ['color_temp', 'color_temp_percent'],
|
key: ['color_temp', 'color_temp_percent'],
|
||||||
convert: (key, value, message, type, postfix, options) => {
|
convert: (key, value, message, type, postfix, options) => {
|
||||||
if (message.hasOwnProperty('transition')) {
|
if (message && message.hasOwnProperty('transition')) {
|
||||||
message.transition = message.transition * 3.3;
|
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
|
* Ignore converters
|
||||||
*/
|
*/
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user