mirror of
https://github.com/Koenkk/zigbee2mqtt.git
synced 2024-11-17 10:58:31 -07:00
b28da0265e
* Include last seen in device boxes on graphviz map * Refactor * Fix test cases * Bind * Refactor last seen label construction * style fix
279 lines
14 KiB
JavaScript
279 lines
14 KiB
JavaScript
const DeviceReceive = require('../lib/extension/deviceReceive');
|
|
const settings = require('../lib/util/settings');
|
|
const devices = require('zigbee-shepherd-converters').devices;
|
|
const utils = require('./utils');
|
|
|
|
const wait = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
|
|
// Devices
|
|
const WXKG11LM = devices.find((d) => d.model === 'WXKG11LM');
|
|
const WXKG02LM = devices.find((d) => d.model === 'WXKG02LM');
|
|
const WSDCGQ11LM = devices.find((d) => d.model === 'WSDCGQ11LM');
|
|
const RTCGQ11LM = devices.find((d) => d.model === 'RTCGQ11LM');
|
|
const ZNCZ02LM = devices.find((d) => d.model === 'ZNCZ02LM');
|
|
|
|
const mqtt = {
|
|
log: () => {},
|
|
};
|
|
|
|
describe('DeviceReceive', () => {
|
|
let deviceReceive;
|
|
let publishEntityState;
|
|
|
|
beforeEach(() => {
|
|
utils.stubLogger(jest);
|
|
jest.spyOn(settings, 'addDevice').mockReturnValue(undefined);
|
|
publishEntityState = jest.fn();
|
|
deviceReceive = new DeviceReceive(null, mqtt, null, publishEntityState);
|
|
});
|
|
|
|
afterEach(() => {
|
|
jest.restoreAllMocks();
|
|
});
|
|
|
|
describe('Handling zigbee messages', () => {
|
|
it('Should handle a zigbee message', () => {
|
|
const device = {ieeeAddr: '0x12345678'};
|
|
const message = utils.zigbeeMessage(device, 'genOnOff', 'attReport', {onOff: 1}, 1);
|
|
deviceReceive.onZigbeeMessage(message, device, WXKG11LM);
|
|
expect(publishEntityState).toHaveBeenCalledTimes(1);
|
|
expect(publishEntityState.mock.calls[0][1]).toStrictEqual({click: 'single'});
|
|
});
|
|
|
|
it('Should handle a zigbee message and counter it when Home Assistant integration is enabled', () => {
|
|
jest.spyOn(settings, 'get').mockReturnValue({homeassistant: true, advanced: {last_seen: 'disable'}});
|
|
const device = {ieeeAddr: '0x12345678'};
|
|
const message = utils.zigbeeMessage(device, 'genOnOff', 'attReport', {onOff: 1}, 1);
|
|
deviceReceive.onZigbeeMessage(message, device, WXKG11LM);
|
|
expect(publishEntityState).toHaveBeenCalledTimes(2);
|
|
expect(publishEntityState.mock.calls[0][1]).toStrictEqual({click: 'single'});
|
|
expect(publishEntityState.mock.calls[1][1]).toStrictEqual({click: ''});
|
|
});
|
|
|
|
it('Should handle a zigbee message which uses ep (left)', () => {
|
|
const device = {ieeeAddr: '0x12345678'};
|
|
const message = utils.zigbeeMessage(device, 'genOnOff', 'attReport', {onOff: 1}, 1);
|
|
deviceReceive.onZigbeeMessage(message, device, WXKG02LM);
|
|
expect(publishEntityState).toHaveBeenCalledTimes(1);
|
|
expect(publishEntityState.mock.calls[0][1]).toStrictEqual({click: 'left'});
|
|
});
|
|
|
|
it('Should handle a zigbee message which uses ep (right)', () => {
|
|
const device = {ieeeAddr: '0x12345678'};
|
|
const message = utils.zigbeeMessage(device, 'genOnOff', 'attReport', {onOff: 1}, 2);
|
|
deviceReceive.onZigbeeMessage(message, device, WXKG02LM);
|
|
expect(publishEntityState).toHaveBeenCalledTimes(1);
|
|
expect(publishEntityState.mock.calls[0][1]).toStrictEqual({click: 'right'});
|
|
});
|
|
|
|
it('Should handle a zigbee message with default precision', () => {
|
|
const device = {ieeeAddr: '0x12345678'};
|
|
const message = utils.zigbeeMessage(
|
|
device, 'msTemperatureMeasurement', 'attReport', {measuredValue: -85}, 1
|
|
);
|
|
deviceReceive.onZigbeeMessage(message, device, WSDCGQ11LM);
|
|
expect(publishEntityState).toHaveBeenCalledTimes(1);
|
|
expect(publishEntityState.mock.calls[0][1]).toStrictEqual({temperature: -0.85});
|
|
});
|
|
|
|
it('Should debounce messages', async () => {
|
|
const device = {ieeeAddr: '0x12345678'};
|
|
jest.spyOn(settings, 'getDevice').mockReturnValue({debounce: 0.1});
|
|
const message1 = utils.zigbeeMessage(
|
|
device, 'msTemperatureMeasurement', 'attReport', {measuredValue: 8}, 1
|
|
);
|
|
const message2 = utils.zigbeeMessage(
|
|
device, 'msRelativeHumidity', 'attReport', {measuredValue: 1}, 1
|
|
);
|
|
const message3 = utils.zigbeeMessage(
|
|
device, 'msPressureMeasurement', 'attReport', {measuredValue: 2}, 1
|
|
);
|
|
deviceReceive.onZigbeeMessage(message1, device, WSDCGQ11LM);
|
|
deviceReceive.onZigbeeMessage(message2, device, WSDCGQ11LM);
|
|
deviceReceive.onZigbeeMessage(message3, device, WSDCGQ11LM);
|
|
await wait(200);
|
|
expect(publishEntityState).toHaveBeenCalledTimes(1);
|
|
expect(publishEntityState.mock.calls[0][1]).toStrictEqual({temperature: 0.08, humidity: 0.01, pressure: 2});
|
|
|
|
deviceReceive.onZigbeeMessage(message1, device, WSDCGQ11LM);
|
|
await wait(200);
|
|
expect(publishEntityState).toHaveBeenCalledTimes(2);
|
|
expect(publishEntityState.mock.calls[1][1]).toStrictEqual({temperature: 0.08});
|
|
});
|
|
|
|
it('Should handle a zigbee message with 1 precision', () => {
|
|
const device = {ieeeAddr: '0x12345678'};
|
|
jest.spyOn(settings, 'getDevice').mockReturnValue({temperature_precision: 1});
|
|
const message = utils.zigbeeMessage(
|
|
device, 'msTemperatureMeasurement', 'attReport', {measuredValue: -85}, 1
|
|
);
|
|
deviceReceive.onZigbeeMessage(message, device, WSDCGQ11LM);
|
|
expect(publishEntityState).toHaveBeenCalledTimes(1);
|
|
expect(publishEntityState.mock.calls[0][1]).toStrictEqual({temperature: -0.8});
|
|
});
|
|
|
|
it('Should handle a zigbee message with 0 precision', () => {
|
|
const device = {ieeeAddr: '0x12345678'};
|
|
jest.spyOn(settings, 'getDevice').mockReturnValue({temperature_precision: 0});
|
|
const message = utils.zigbeeMessage(
|
|
device, 'msTemperatureMeasurement', 'attReport', {measuredValue: -85}, 1
|
|
);
|
|
deviceReceive.onZigbeeMessage(message, device, WSDCGQ11LM);
|
|
expect(publishEntityState).toHaveBeenCalledTimes(1);
|
|
expect(publishEntityState.mock.calls[0][1]).toStrictEqual({temperature: -1});
|
|
});
|
|
|
|
it('Should handle a zigbee message with 1 precision when set via device_options', () => {
|
|
const device = {ieeeAddr: '0x12345678'};
|
|
jest.spyOn(settings, 'get').mockReturnValue({
|
|
device_options: {
|
|
temperature_precision: 1,
|
|
},
|
|
advanced: {
|
|
last_seen: 'disable',
|
|
},
|
|
});
|
|
jest.spyOn(settings, 'getDevice').mockReturnValue({});
|
|
const message = utils.zigbeeMessage(
|
|
device, 'msTemperatureMeasurement', 'attReport', {measuredValue: -85}, 1
|
|
);
|
|
deviceReceive.onZigbeeMessage(message, device, WSDCGQ11LM);
|
|
expect(publishEntityState).toHaveBeenCalledTimes(1);
|
|
expect(publishEntityState.mock.calls[0][1]).toStrictEqual({temperature: -0.8});
|
|
}
|
|
);
|
|
|
|
it('Should handle a zigbee message with 2 precision when overrides device_options', () => {
|
|
const device = {ieeeAddr: '0x12345678'};
|
|
jest.spyOn(settings, 'get').mockReturnValue({
|
|
device_options: {
|
|
temperature_precision: 1,
|
|
},
|
|
advanced: {
|
|
last_seen: 'disable',
|
|
},
|
|
});
|
|
jest.spyOn(settings, 'getDevice').mockReturnValue({
|
|
temperature_precision: 2,
|
|
});
|
|
const message = utils.zigbeeMessage(
|
|
device, 'msTemperatureMeasurement', 'attReport', {measuredValue: -85}, 1
|
|
);
|
|
deviceReceive.onZigbeeMessage(message, device, WSDCGQ11LM);
|
|
expect(publishEntityState).toHaveBeenCalledTimes(1);
|
|
expect(publishEntityState.mock.calls[0][1]).toStrictEqual({temperature: -0.85});
|
|
}
|
|
);
|
|
|
|
it('Should handle a zigbee message with voltage 3010', () => {
|
|
const device = {ieeeAddr: '0x12345678'};
|
|
const message = utils.zigbeeMessage(device, 'genBasic', 'attReport', {'65281': {'1': 3010}}, 1);
|
|
deviceReceive.onZigbeeMessage(message, device, WXKG02LM);
|
|
expect(publishEntityState).toHaveBeenCalledTimes(1);
|
|
const expected = {battery: 100, voltage: 3010};
|
|
expect(publishEntityState.mock.calls[0][1]).toStrictEqual(expected);
|
|
});
|
|
|
|
it('Should handle a zigbee message with voltage 2850', () => {
|
|
const device = {ieeeAddr: '0x12345678'};
|
|
const message = utils.zigbeeMessage(device, 'genBasic', 'attReport', {'65281': {'1': 2850}}, 1);
|
|
deviceReceive.onZigbeeMessage(message, device, WXKG02LM);
|
|
expect(publishEntityState).toHaveBeenCalledTimes(1);
|
|
const expected = {battery: 35, voltage: 2850};
|
|
expect(publishEntityState.mock.calls[0][1]).toStrictEqual(expected);
|
|
});
|
|
|
|
it('Should handle a zigbee message with voltage 2650', () => {
|
|
const device = {ieeeAddr: '0x12345678'};
|
|
const message = utils.zigbeeMessage(device, 'genBasic', 'attReport', {'65281': {'1': 2650}}, 1);
|
|
deviceReceive.onZigbeeMessage(message, device, WXKG02LM);
|
|
expect(publishEntityState).toHaveBeenCalledTimes(1);
|
|
const expected = {battery: 14, voltage: 2650};
|
|
expect(publishEntityState.mock.calls[0][1]).toStrictEqual(expected);
|
|
});
|
|
|
|
it('Should handle a zigbee message with voltage 2000', () => {
|
|
const device = {ieeeAddr: '0x12345678'};
|
|
const message = utils.zigbeeMessage(device, 'genBasic', 'attReport', {'65281': {'1': 2000}}, 1);
|
|
deviceReceive.onZigbeeMessage(message, device, WXKG02LM);
|
|
expect(publishEntityState).toHaveBeenCalledTimes(1);
|
|
const expected = {battery: 0, voltage: 2000};
|
|
expect(publishEntityState.mock.calls[0][1]).toStrictEqual(expected);
|
|
});
|
|
|
|
it('Should publish 1 message when converted twice', () => {
|
|
const device = {ieeeAddr: '0x12345678'};
|
|
const payload = {
|
|
'65281': {'1': 3045, '3': 19, '4': 17320, '5': 35, '6': [0, 3], '10': 51107, '11': 381, '100': 0},
|
|
};
|
|
const message = utils.zigbeeMessage(device, 'genBasic', 'attReport', payload, 1);
|
|
deviceReceive.onZigbeeMessage(message, device, RTCGQ11LM);
|
|
expect(publishEntityState).toHaveBeenCalledTimes(1);
|
|
const expected = {'battery': 100, 'illuminance': 381, 'voltage': 3045};
|
|
expect(publishEntityState.mock.calls[0][1]).toStrictEqual(expected);
|
|
});
|
|
|
|
it('Should publish no message when converted without result', () => {
|
|
const device = {ieeeAddr: '0x12345678'};
|
|
const payload = {'9999': {'1': 3045}};
|
|
const message = utils.zigbeeMessage(device, 'genBasic', 'attReport', payload, 1);
|
|
deviceReceive.onZigbeeMessage(message, device, RTCGQ11LM);
|
|
expect(publishEntityState).toHaveBeenCalledTimes(0);
|
|
});
|
|
|
|
it('Should publish last_seen epoch', () => {
|
|
const device = {ieeeAddr: '0x12345678'};
|
|
const message = utils.zigbeeMessage(device, 'genOnOff', 'attReport', {onOff: 1}, 1);
|
|
jest.spyOn(settings, 'get').mockReturnValue({
|
|
advanced: {
|
|
last_seen: 'epoch',
|
|
},
|
|
});
|
|
deviceReceive.onZigbeeMessage(message, device, WXKG02LM);
|
|
expect(publishEntityState).toHaveBeenCalledTimes(1);
|
|
expect(typeof publishEntityState.mock.calls[0][1].last_seen).toBe('number');
|
|
});
|
|
|
|
it('Should publish last_seen ISO_8601', () => {
|
|
const device = {ieeeAddr: '0x12345678'};
|
|
const message = utils.zigbeeMessage(device, 'genOnOff', 'attReport', {onOff: 1}, 1);
|
|
jest.spyOn(settings, 'get').mockReturnValue({
|
|
advanced: {
|
|
last_seen: 'ISO_8601',
|
|
},
|
|
});
|
|
deviceReceive.onZigbeeMessage(message, device, WXKG02LM);
|
|
expect(publishEntityState).toHaveBeenCalledTimes(1);
|
|
expect(typeof publishEntityState.mock.calls[0][1].last_seen).toBe('string');
|
|
});
|
|
|
|
it('Should publish last_seen ISO_8601_local', () => {
|
|
const device = {ieeeAddr: '0x12345678'};
|
|
const message = utils.zigbeeMessage(device, 'genOnOff', 'attReport', {onOff: 1}, 1);
|
|
jest.spyOn(settings, 'get').mockReturnValue({
|
|
advanced: {
|
|
last_seen: 'ISO_8601_local',
|
|
},
|
|
});
|
|
deviceReceive.onZigbeeMessage(message, device, WXKG02LM);
|
|
expect(publishEntityState).toHaveBeenCalledTimes(1);
|
|
expect(typeof publishEntityState.mock.calls[0][1].last_seen).toBe('string');
|
|
});
|
|
|
|
it('Should not handle messages forwarded Xiaomi messages', () => {
|
|
const device = {ieeeAddr: '0x12345678', manufId: 4151, type: 'Router'};
|
|
const message = utils.zigbeeMessage(device, 'genOnOff', 'attReport', {onOff: 1}, 1, 599);
|
|
deviceReceive.onZigbeeMessage(message, device, ZNCZ02LM);
|
|
expect(publishEntityState).toHaveBeenCalledTimes(0);
|
|
});
|
|
|
|
it('Should handle messages from Xiaomi router devices', () => {
|
|
const device = {ieeeAddr: '0x12345678', manufId: 4151, type: 'Router'};
|
|
const message = utils.zigbeeMessage(device, 'genOnOff', 'attReport', {onOff: 1});
|
|
deviceReceive.onZigbeeMessage(message, device, ZNCZ02LM);
|
|
expect(publishEntityState).toHaveBeenCalledTimes(1);
|
|
expect(publishEntityState.mock.calls[0][1]).toStrictEqual({state: 'ON'});
|
|
});
|
|
});
|
|
});
|