2020-05-24 09:16:39 -07:00
|
|
|
const data = require('./stub/data');
|
|
|
|
const logger = require('./stub/logger');
|
|
|
|
const zigbeeHerdsman = require('./stub/zigbeeHerdsman');
|
|
|
|
const MQTT = require('./stub/mqtt');
|
|
|
|
const settings = require('../lib/util/settings');
|
|
|
|
const Controller = require('../lib/controller');
|
|
|
|
const flushPromises = () => new Promise(setImmediate);
|
2020-09-24 09:06:43 -07:00
|
|
|
const stringify = require('json-stable-stringify-without-jsonify');
|
2020-05-24 09:16:39 -07:00
|
|
|
|
2020-08-31 09:48:04 -07:00
|
|
|
const {coordinator, bulb, unsupported, WXKG11LM, remote} = zigbeeHerdsman.devices;
|
2020-05-24 09:16:39 -07:00
|
|
|
zigbeeHerdsman.returnDevices.push(coordinator.ieeeAddr);
|
|
|
|
zigbeeHerdsman.returnDevices.push(bulb.ieeeAddr);
|
|
|
|
zigbeeHerdsman.returnDevices.push(unsupported.ieeeAddr);
|
2020-07-29 14:10:03 -07:00
|
|
|
zigbeeHerdsman.returnDevices.push(WXKG11LM.ieeeAddr);
|
2020-08-31 09:48:04 -07:00
|
|
|
zigbeeHerdsman.returnDevices.push(remote.ieeeAddr);
|
2020-05-24 09:16:39 -07:00
|
|
|
|
|
|
|
describe('Bridge', () => {
|
|
|
|
let controller;
|
|
|
|
|
|
|
|
beforeEach(async () => {
|
2020-07-28 13:12:22 -07:00
|
|
|
MQTT.mock.reconnecting = false;
|
2020-05-24 09:16:39 -07:00
|
|
|
data.writeDefaultConfiguration();
|
|
|
|
settings._reRead();
|
|
|
|
settings.set(['advanced', 'legacy_api'], false);
|
|
|
|
settings.set(['experimental', 'new_api'], true);
|
|
|
|
data.writeDefaultState();
|
|
|
|
logger.info.mockClear();
|
|
|
|
logger.warn.mockClear();
|
2020-07-28 13:12:22 -07:00
|
|
|
logger.setTransportsEnabled(false);
|
2020-05-24 09:16:39 -07:00
|
|
|
MQTT.publish.mockClear();
|
2020-06-13 08:22:00 -07:00
|
|
|
const device = zigbeeHerdsman.devices.bulb;
|
|
|
|
device.removeFromDatabase.mockClear();
|
|
|
|
device.removeFromNetwork.mockClear();
|
2020-05-24 09:16:39 -07:00
|
|
|
controller = new Controller();
|
|
|
|
await controller.start();
|
|
|
|
await flushPromises();
|
|
|
|
});
|
|
|
|
|
|
|
|
it('Should publish bridge info on startup', async () => {
|
|
|
|
const version = await require('../lib/util/utils').getZigbee2mqttVersion();
|
2020-09-04 09:42:24 -07:00
|
|
|
const directory = settings.get().advanced.log_directory;
|
2020-05-24 09:16:39 -07:00
|
|
|
expect(MQTT.publish).toHaveBeenCalledWith(
|
|
|
|
'zigbee2mqtt/bridge/info',
|
2020-09-04 09:42:24 -07:00
|
|
|
stringify({"commit":version.commitHash,"config":{"advanced":{"adapter_concurrent":null,"availability_blacklist":[],"availability_blocklist":[],"availability_passlist":[],"availability_timeout":0,"availability_whitelist":[],"cache_state":true,"cache_state_persistent":true,"cache_state_send_on_startup":true,"channel":11,"elapsed":false,"ext_pan_id":[221,221,221,221,221,221,221,221],"homeassistant_discovery_topic":"homeassistant","homeassistant_legacy_triggers":true,"homeassistant_status_topic":"hass/status","last_seen":"disable","legacy_api":false,"log_directory":directory,"log_file":"log.txt","log_level":"info","log_output":["console","file"],"log_rotation":true,"log_syslog":{},"pan_id":6754,"report":false,"soft_reset_timeout":0,"timestamp_format":"YYYY-MM-DD HH:mm:ss"},"ban":[],"blocklist":[],"device_options":{},"devices":{"0x000b57fffec6a5b2":{"friendly_name":"bulb","retain":true},"0x000b57fffec6a5b3":{"friendly_name":"bulb_color","retain":false},"0x000b57fffec6a5b4":{"friendly_name":"bulb_color_2","retain":false},"0x000b57fffec6a5b7":{"friendly_name":"bulb_2","retain":false},"0x0017880104a44559":{"friendly_name":"J1_cover"},"0x0017880104e43559":{"friendly_name":"U202DST600ZB"},"0x0017880104e44559":{"friendly_name":"3157100_thermostat"},"0x0017880104e45517":{"friendly_name":"remote","retain":true},"0x0017880104e45518":{"friendly_name":"0x0017880104e45518"},"0x0017880104e45520":{"friendly_name":"button","retain":false},"0x0017880104e45521":{"friendly_name":"button_double_key","retain":false},"0x0017880104e45522":{"friendly_name":"weather_sensor","qos":1,"retain":false},"0x0017880104e45523":{"friendly_name":"occupancy_sensor","retain":false},"0x0017880104e45524":{"friendly_name":"power_plug","retain":false},"0x0017880104e45526":{"friendly_name":"GL-S-007ZS"},"0x0017880104e45529":{"friendly_name":"unsupported2","retain":false},"0x0017880104e45530":{"friendly_name":"button_double_key_interviewing","retain":false},"0x0017880104e45540":{"friendly_name":"ikea_onoff"},"0x0017880104e45541":{"friendly_name":"wall_switch","retain":false},"0x0017880104e45542":{"friendly_name":"wall_switch_double","retain":false},"0x0017880104e45543":{"friendly_name":"led_controller_1","retain":false},"0x0017880104e45544":{"friendly_name":"led_controller_2","retain":false},"0x0017880104e45545":{"friendly_name":"dimmer_wall_switch","retain":false},"0x0017880104e45547":{"friendly_name":"curtain","retain":false},"0x0017880104e45548":{"friendly_name":"fan","retain":false},"0x0017880104e45549":{"friendly_name":"siren","retain":false},"0x0017880104e45550":{"friendly_name":"thermostat","retain":false},"0x0017880104e45551":{"friendly_name":"smart vent","retain":false},"0x0017880104e45552":{"friendly_name":"j1","retain":false},"0x0017880104e45553":{"friendly_name":"bulb_enddevice","retain":false},"0x0017880104e45559":{"friendly_name":"cc2530_router","retain":false},"0x0017880104e45560":{"friendly_name":"livolo","retain":false},"0x0017882104a44559":{"friendly_name":"TS0601_thermostat"},"0x90fd9ffffe4b64aa":{"friendly_name":"SP600_OLD"},"0x90fd9ffffe4b64ab":{"friendly_name":"SP600_NEW"},"0x90fd9ffffe4b64ac":{"friendly_name":"MKS-CM-W5"},"0x90fd9ffffe4b64ae":{"friendly_name":"tradfri_remote","retain":false},"0x90fd9ffffe4b64af":{"friendly_name":"roller_shutter"},"0x90fd9ffffe4b64ax":{"friendly_name":"ZNLDP12LM"}},"experimental":{"new_api":true,"output":"json"},"external_converters":[],"groups":{"1":{"friendly_name":"group_1","retain":false},"11":{"devices":["bulb_2"],"friendly_name":"group_with_tradfri","retain":false},"12":{"devices":["TS0601_thermostat"],"friendly_name":"thermostat_group","retain":false},"15071":{"devices":["bulb_color_2","bulb_2"],"friendly_name":"group_tradfri_remote","retain":false},"2":{"friendly_name":"group_2","retain":false}},"homeassistant":false,"map_options":{"graphviz":{"colors":{"fill":{"coordinator":"#e04e5d","enddevice":"#fff8ce","router":"#4ea3e0"},"font":{"coordinator":"#ffffff","enddevice":"#000000","router":"#ffffff"},"line":{"active":"#009900","inactive":"#994444"}}}},"mqtt":{"base_topic":"zigbee2mqtt","include_d
|
2020-05-24 09:16:39 -07:00
|
|
|
{ retain: true, qos: 0 },
|
|
|
|
expect.any(Function)
|
|
|
|
);
|
|
|
|
});
|
|
|
|
|
|
|
|
it('Should publish devices on startup', async () => {
|
|
|
|
expect(MQTT.publish).toHaveBeenCalledWith(
|
|
|
|
'zigbee2mqtt/bridge/devices',
|
2020-10-01 09:33:59 -07:00
|
|
|
stringify([{"date_code":null,"definition":null,"endpoints":{"1":{"bindings":[],"clusters":{"input":[],"output":[]}}},"friendly_name":"Coordinator","ieee_address":"0x00124b00120144ae","interview_completed":false,"interviewing":false,"network_address":0,"power_source":null,"software_build_id":null,"supported":false,"type":"Coordinator"},{"date_code":null,"definition":{"description":"TRADFRI LED bulb E26/E27 980 lumen, dimmable, white spectrum, opal white","exposes":[{"features":["state","brightness","color_temp"],"type":"light"}],"model":"LED1545G12","vendor":"IKEA"},"endpoints":{"1":{"bindings":[],"clusters":{"input":["genBasic","genScenes","genOnOff","genLevelCtrl","lightingColorCtrl"],"output":["genScenes","genOta"]}}},"friendly_name":"bulb","ieee_address":"0x000b57fffec6a5b2","interview_completed":true,"interviewing":false,"network_address":40369,"power_source":"Mains (single phase)","software_build_id":null,"supported":true,"type":"Router"},{"date_code":null,"definition":{"description":"Hue dimmer switch","model":"324131092621","supports":"on/off, brightness, up/down/hold/release, click count","vendor":"Philips"},"endpoints":{"1":{"bindings":[{"cluster":"genLevelCtrl","target":{"endpoint":1,"ieee_address":"0x000b57fffec6a5b3","type":"endpoint"}},{"cluster":"genOnOff","target":{"id":1,"type":"group"}}],"clusters":{"input":["genBasic"],"output":["genBasic","genOnOff","genLevelCtrl","genScenes"]}},"2":{"bindings":[],"clusters":{"input":["genBasic"],"output":["genOta","genOnOff"]}}},"friendly_name":"remote","ieee_address":"0x0017880104e45517","interview_completed":true,"interviewing":false,"network_address":6535,"power_source":"Battery","software_build_id":null,"supported":true,"type":"EndDevice"},{"date_code":null,"definition":null,"endpoints":{"1":{"bindings":[],"clusters":{"input":["genBasic"],"output":["genBasic","genOnOff","genLevelCtrl","genScenes"]}}},"friendly_name":"0x0017880104e45518","ieee_address":"0x0017880104e45518","interview_completed":true,"interviewing":false,"network_address":6536,"power_source":"Battery","software_build_id":null,"supported":false,"type":"EndDevice"},{"date_code":null,"definition":{"description":"Aqara wireless switch","model":"WXKG11LM","supports":"single, double click (and triple, quadruple, hold, release depending on model)","vendor":"Xiaomi"},"endpoints":{"1":{"bindings":[],"clusters":{"input":["genBasic"],"output":["genBasic","genOnOff","genLevelCtrl","genScenes"]}}},"friendly_name":"button","ieee_address":"0x0017880104e45520","interview_completed":true,"interviewing":false,"network_address":6537,"power_source":"Battery","software_build_id":null,"supported":true,"type":"EndDevice"}]),
|
2020-05-24 09:16:39 -07:00
|
|
|
{ retain: true, qos: 0 },
|
|
|
|
expect.any(Function)
|
|
|
|
);
|
|
|
|
});
|
|
|
|
|
2020-07-28 13:12:22 -07:00
|
|
|
it('Should log to MQTT', async () => {
|
|
|
|
logger.setTransportsEnabled(true);
|
|
|
|
MQTT.publish.mockClear();
|
|
|
|
logger.info.mockClear();
|
|
|
|
logger.info("this is a test");
|
|
|
|
expect(MQTT.publish).toHaveBeenCalledWith(
|
|
|
|
'zigbee2mqtt/bridge/logging',
|
2020-08-13 11:00:35 -07:00
|
|
|
stringify({message: 'this is a test', level: 'info'}),
|
2020-07-28 13:12:22 -07:00
|
|
|
{ retain: false, qos: 0 },
|
|
|
|
expect.any(Function)
|
|
|
|
);
|
|
|
|
expect(logger.info).toHaveBeenCalledTimes(1);
|
|
|
|
});
|
|
|
|
|
|
|
|
it('Shouldnt log to MQTT when not connected', async () => {
|
|
|
|
logger.setTransportsEnabled(true);
|
|
|
|
MQTT.mock.reconnecting = true;
|
|
|
|
MQTT.publish.mockClear();
|
|
|
|
logger.info.mockClear();
|
|
|
|
logger.error.mockClear();
|
|
|
|
logger.info("this is a test");
|
|
|
|
expect(MQTT.publish).toHaveBeenCalledTimes(0);
|
|
|
|
expect(logger.info).toHaveBeenCalledTimes(1);
|
|
|
|
expect(logger.error).toHaveBeenCalledTimes(0);
|
|
|
|
});
|
|
|
|
|
2020-08-30 02:30:38 -07:00
|
|
|
it('Should publish groups on startup', async () => {
|
2020-07-28 13:12:22 -07:00
|
|
|
logger.setTransportsEnabled(true);
|
2020-05-24 09:16:39 -07:00
|
|
|
expect(MQTT.publish).toHaveBeenCalledWith(
|
|
|
|
'zigbee2mqtt/bridge/groups',
|
2020-08-30 02:30:38 -07:00
|
|
|
stringify([{"friendly_name":"group_1","id":1,"members":[]},{"friendly_name":"group_tradfri_remote","id":15071,"members":[]},{"friendly_name":99,"id":99,"members":[]},{"friendly_name":"group_with_tradfri","id":11,"members":[]},{"friendly_name":"thermostat_group","id":12,"members":[]},{"friendly_name":"group_2","id":2,"members":[]}]),
|
2020-05-24 09:16:39 -07:00
|
|
|
{ retain: true, qos: 0 },
|
|
|
|
expect.any(Function)
|
|
|
|
);
|
|
|
|
});
|
|
|
|
|
|
|
|
it('Should publish event when device joined', async () => {
|
|
|
|
MQTT.publish.mockClear();
|
|
|
|
await zigbeeHerdsman.events.deviceJoined({device: zigbeeHerdsman.devices.bulb});
|
|
|
|
await flushPromises();
|
|
|
|
expect(MQTT.publish).toHaveBeenCalledTimes(1);
|
|
|
|
expect(MQTT.publish).toHaveBeenCalledWith(
|
|
|
|
'zigbee2mqtt/bridge/event',
|
2020-08-13 11:00:35 -07:00
|
|
|
stringify({"type":"device_joined","data":{"friendly_name":"bulb","ieee_address":"0x000b57fffec6a5b2"}}),
|
2020-08-20 11:55:40 -07:00
|
|
|
{ retain: false, qos: 0 },
|
|
|
|
expect.any(Function)
|
|
|
|
);
|
|
|
|
});
|
|
|
|
|
|
|
|
it('Should publish event when device announces', async () => {
|
|
|
|
MQTT.publish.mockClear();
|
|
|
|
await zigbeeHerdsman.events.deviceAnnounce({device: zigbeeHerdsman.devices.bulb});
|
|
|
|
await flushPromises();
|
|
|
|
expect(MQTT.publish).toHaveBeenCalledTimes(1);
|
|
|
|
expect(MQTT.publish).toHaveBeenCalledWith(
|
|
|
|
'zigbee2mqtt/bridge/event',
|
|
|
|
stringify({"type":"device_announce","data":{"friendly_name":"bulb","ieee_address":"0x000b57fffec6a5b2"}}),
|
2020-05-24 09:16:39 -07:00
|
|
|
{ retain: false, qos: 0 },
|
|
|
|
expect.any(Function)
|
|
|
|
);
|
|
|
|
});
|
|
|
|
|
|
|
|
it('Should publish event when device interview started', async () => {
|
|
|
|
MQTT.publish.mockClear();
|
|
|
|
await zigbeeHerdsman.events.deviceInterview({device: zigbeeHerdsman.devices.bulb, status: 'started'});
|
|
|
|
await flushPromises();
|
|
|
|
expect(MQTT.publish).toHaveBeenCalledTimes(1);
|
|
|
|
expect(MQTT.publish).toHaveBeenCalledWith(
|
|
|
|
'zigbee2mqtt/bridge/event',
|
2020-08-13 11:00:35 -07:00
|
|
|
stringify({"type":"device_interview","data":{"friendly_name":"bulb","status":"started","ieee_address":"0x000b57fffec6a5b2"}}),
|
2020-05-24 09:16:39 -07:00
|
|
|
{ retain: false, qos: 0 },
|
|
|
|
expect.any(Function)
|
|
|
|
);
|
|
|
|
});
|
|
|
|
|
|
|
|
it('Should publish event and devices when device interview failed', async () => {
|
|
|
|
MQTT.publish.mockClear();
|
|
|
|
await zigbeeHerdsman.events.deviceInterview({device: zigbeeHerdsman.devices.bulb, status: 'failed'});
|
|
|
|
await flushPromises();
|
|
|
|
expect(MQTT.publish).toHaveBeenCalledTimes(2);
|
|
|
|
expect(MQTT.publish).toHaveBeenCalledWith(
|
|
|
|
'zigbee2mqtt/bridge/event',
|
2020-08-13 11:00:35 -07:00
|
|
|
stringify({"type":"device_interview","data":{"friendly_name":"bulb","status":"failed","ieee_address":"0x000b57fffec6a5b2"}}),
|
2020-05-24 09:16:39 -07:00
|
|
|
{ retain: false, qos: 0 },
|
|
|
|
expect.any(Function)
|
|
|
|
);
|
|
|
|
expect(MQTT.publish).toHaveBeenCalledWith(
|
|
|
|
'zigbee2mqtt/bridge/devices',
|
|
|
|
expect.any(String),
|
|
|
|
{ retain: true, qos: 0 },
|
|
|
|
expect.any(Function)
|
|
|
|
);
|
|
|
|
});
|
|
|
|
|
|
|
|
it('Should publish event and devices when device interview successful', async () => {
|
|
|
|
MQTT.publish.mockClear();
|
|
|
|
await zigbeeHerdsman.events.deviceInterview({device: zigbeeHerdsman.devices.bulb, status: 'successful'});
|
|
|
|
await zigbeeHerdsman.events.deviceInterview({device: zigbeeHerdsman.devices.unsupported, status: 'successful'});
|
|
|
|
await flushPromises();
|
|
|
|
expect(MQTT.publish).toHaveBeenCalledTimes(4);
|
|
|
|
expect(MQTT.publish).toHaveBeenCalledWith(
|
|
|
|
'zigbee2mqtt/bridge/event',
|
2020-10-01 09:33:59 -07:00
|
|
|
stringify({"type":"device_interview","data":{"friendly_name":"bulb","status":"successful","ieee_address":"0x000b57fffec6a5b2","supported":true,"definition":{"model":"LED1545G12","vendor":"IKEA","description":"TRADFRI LED bulb E26/E27 980 lumen, dimmable, white spectrum, opal white","exposes":[{"features":["state","brightness","color_temp"],"type":"light"}]}}}),
|
2020-05-24 09:16:39 -07:00
|
|
|
{ retain: false, qos: 0 },
|
|
|
|
expect.any(Function)
|
|
|
|
);
|
|
|
|
expect(MQTT.publish).toHaveBeenCalledWith(
|
|
|
|
'zigbee2mqtt/bridge/event',
|
2020-08-13 11:00:35 -07:00
|
|
|
stringify({"type":"device_interview","data":{"friendly_name":"0x0017880104e45518","status":"successful","ieee_address":"0x0017880104e45518","supported":false,"definition":null}}),
|
2020-05-24 09:16:39 -07:00
|
|
|
{ retain: false, qos: 0 },
|
|
|
|
expect.any(Function)
|
|
|
|
);
|
|
|
|
expect(MQTT.publish).toHaveBeenCalledWith(
|
|
|
|
'zigbee2mqtt/bridge/devices',
|
|
|
|
expect.any(String),
|
|
|
|
{ retain: true, qos: 0 },
|
|
|
|
expect.any(Function)
|
|
|
|
);
|
|
|
|
});
|
|
|
|
|
|
|
|
it('Should publish event and devices when device leaves', async () => {
|
|
|
|
MQTT.publish.mockClear();
|
|
|
|
await zigbeeHerdsman.events.deviceLeave({ieeeAddr: zigbeeHerdsman.devices.bulb.ieeeAddr});
|
|
|
|
await flushPromises();
|
|
|
|
expect(MQTT.publish).toHaveBeenCalledTimes(2);
|
|
|
|
expect(MQTT.publish).toHaveBeenCalledWith(
|
|
|
|
'zigbee2mqtt/bridge/event',
|
2020-08-13 11:00:35 -07:00
|
|
|
stringify({"type":"device_leave","data":{"ieee_address":"0x000b57fffec6a5b2"}}),
|
2020-05-24 09:16:39 -07:00
|
|
|
{ retain: false, qos: 0 },
|
|
|
|
expect.any(Function)
|
|
|
|
);
|
|
|
|
expect(MQTT.publish).toHaveBeenCalledWith(
|
|
|
|
'zigbee2mqtt/bridge/devices',
|
|
|
|
expect.any(String),
|
|
|
|
{ retain: true, qos: 0 },
|
|
|
|
expect.any(Function)
|
|
|
|
);
|
|
|
|
});
|
|
|
|
|
|
|
|
it('Should allow permit join', async () => {
|
|
|
|
zigbeeHerdsman.permitJoin.mockClear();
|
|
|
|
MQTT.publish.mockClear();
|
2020-07-13 14:00:33 -07:00
|
|
|
MQTT.events.message('zigbee2mqtt/bridge/request/permit_join', 'true');
|
2020-05-24 09:16:39 -07:00
|
|
|
await flushPromises();
|
|
|
|
expect(zigbeeHerdsman.permitJoin).toHaveBeenCalledTimes(1);
|
|
|
|
expect(zigbeeHerdsman.permitJoin).toHaveBeenCalledWith(true);
|
|
|
|
expect(MQTT.publish).toHaveBeenCalledWith('zigbee2mqtt/bridge/info', expect.any(String), { retain: true, qos: 0 }, expect.any(Function));
|
|
|
|
expect(MQTT.publish).toHaveBeenCalledWith(
|
2020-07-13 14:00:33 -07:00
|
|
|
'zigbee2mqtt/bridge/response/permit_join',
|
2020-08-13 11:00:35 -07:00
|
|
|
stringify({"data":{"value":true},"status":"ok"}),
|
2020-05-24 09:16:39 -07:00
|
|
|
{retain: false, qos: 0}, expect.any(Function)
|
|
|
|
);
|
|
|
|
|
|
|
|
zigbeeHerdsman.permitJoin.mockClear();
|
|
|
|
MQTT.publish.mockClear();
|
2020-08-13 11:00:35 -07:00
|
|
|
MQTT.events.message('zigbee2mqtt/bridge/request/permit_join', stringify({"value": false}));
|
2020-05-24 09:16:39 -07:00
|
|
|
await flushPromises();
|
|
|
|
expect(zigbeeHerdsman.permitJoin).toHaveBeenCalledTimes(1);
|
|
|
|
expect(zigbeeHerdsman.permitJoin).toHaveBeenCalledWith(false);
|
|
|
|
expect(MQTT.publish).toHaveBeenCalledWith('zigbee2mqtt/bridge/info', expect.any(String), { retain: true, qos: 0 }, expect.any(Function));
|
|
|
|
expect(MQTT.publish).toHaveBeenCalledWith(
|
2020-07-13 14:00:33 -07:00
|
|
|
'zigbee2mqtt/bridge/response/permit_join',
|
2020-08-13 11:00:35 -07:00
|
|
|
stringify({"data":{"value":false},"status":"ok"}),
|
2020-05-24 09:16:39 -07:00
|
|
|
{retain: false, qos: 0}, expect.any(Function)
|
|
|
|
);
|
|
|
|
});
|
|
|
|
|
|
|
|
it('Should put transaction in response when request is done with transaction', async () => {
|
|
|
|
MQTT.publish.mockClear();
|
2020-08-13 11:00:35 -07:00
|
|
|
MQTT.events.message('zigbee2mqtt/bridge/request/permit_join', stringify({"value": false, "transaction": 22}));
|
2020-05-24 09:16:39 -07:00
|
|
|
await flushPromises();
|
|
|
|
expect(MQTT.publish).toHaveBeenCalledWith(
|
2020-07-13 14:00:33 -07:00
|
|
|
'zigbee2mqtt/bridge/response/permit_join',
|
2020-08-13 11:00:35 -07:00
|
|
|
stringify({"data":{"value":false},"status":"ok", "transaction": 22}),
|
2020-05-24 09:16:39 -07:00
|
|
|
{retain: false, qos: 0}, expect.any(Function)
|
|
|
|
);
|
|
|
|
});
|
|
|
|
|
2020-05-26 08:36:04 -07:00
|
|
|
it('Should put error in response when request fails', async () => {
|
2020-05-24 09:16:39 -07:00
|
|
|
zigbeeHerdsman.permitJoin.mockImplementationOnce(() => {throw new Error('Failed to connect to adapter')});
|
|
|
|
MQTT.publish.mockClear();
|
2020-08-13 11:00:35 -07:00
|
|
|
MQTT.events.message('zigbee2mqtt/bridge/request/permit_join', stringify({"value": false}));
|
2020-05-24 09:16:39 -07:00
|
|
|
await flushPromises();
|
|
|
|
expect(MQTT.publish).toHaveBeenCalledWith(
|
2020-07-13 14:00:33 -07:00
|
|
|
'zigbee2mqtt/bridge/response/permit_join',
|
2020-08-13 11:00:35 -07:00
|
|
|
stringify({"data":{},"status":"error","error": "Failed to connect to adapter"}),
|
2020-05-24 09:16:39 -07:00
|
|
|
{retain: false, qos: 0}, expect.any(Function)
|
|
|
|
);
|
|
|
|
});
|
|
|
|
|
2020-06-15 09:58:32 -07:00
|
|
|
it('Should put error in response when format is incorrect', async () => {
|
|
|
|
MQTT.publish.mockClear();
|
2020-08-13 11:00:35 -07:00
|
|
|
MQTT.events.message('zigbee2mqtt/bridge/request/permit_join', stringify({"value_not_good": false}));
|
2020-06-15 09:58:32 -07:00
|
|
|
await flushPromises();
|
|
|
|
expect(MQTT.publish).toHaveBeenCalledWith(
|
2020-07-13 14:00:33 -07:00
|
|
|
'zigbee2mqtt/bridge/response/permit_join',
|
2020-08-13 11:00:35 -07:00
|
|
|
stringify({"data":{},"status":"error","error": "No value given"}),
|
2020-06-15 09:58:32 -07:00
|
|
|
{retain: false, qos: 0}, expect.any(Function)
|
|
|
|
);
|
|
|
|
});
|
|
|
|
|
2020-05-24 09:16:39 -07:00
|
|
|
it('Coverage satisfaction', async () => {
|
|
|
|
MQTT.publish.mockClear();
|
2020-08-13 11:00:35 -07:00
|
|
|
MQTT.events.message('zigbee2mqtt/bridge/request/random', stringify({"value": false}));
|
2020-05-24 09:16:39 -07:00
|
|
|
const device = zigbeeHerdsman.devices.bulb;
|
|
|
|
await zigbeeHerdsman.events.message({data: {onOff: 1}, cluster: 'genOnOff', device, endpoint: device.getEndpoint(1), type: 'attributeReport', linkquality: 10});
|
|
|
|
await flushPromises();
|
|
|
|
});
|
2020-06-13 08:22:00 -07:00
|
|
|
|
2020-07-21 12:14:39 -07:00
|
|
|
it('Should allow a healthcheck', async () => {
|
|
|
|
MQTT.publish.mockClear();
|
|
|
|
MQTT.events.message('zigbee2mqtt/bridge/request/health_check', '');
|
|
|
|
await flushPromises();
|
|
|
|
expect(MQTT.publish).toHaveBeenCalledWith(
|
|
|
|
'zigbee2mqtt/bridge/response/health_check',
|
2020-08-13 11:00:35 -07:00
|
|
|
stringify({"data":{"healthy": true},"status":"ok"}),
|
2020-07-21 12:14:39 -07:00
|
|
|
{retain: false, qos: 0}, expect.any(Function)
|
|
|
|
);
|
|
|
|
});
|
|
|
|
|
2020-06-13 08:22:00 -07:00
|
|
|
it('Should allow to remove device by string', async () => {
|
|
|
|
const device = zigbeeHerdsman.devices.bulb;
|
|
|
|
controller.state.state = {'0x000b57fffec6a5b3': {brightness: 100}};
|
|
|
|
MQTT.publish.mockClear();
|
|
|
|
MQTT.events.message('zigbee2mqtt/bridge/request/device/remove', 'bulb');
|
|
|
|
await flushPromises();
|
|
|
|
expect(controller.state[device.ieeeAddr]).toBeUndefined();
|
|
|
|
expect(device.removeFromNetwork).toHaveBeenCalledTimes(1);
|
|
|
|
expect(device.removeFromDatabase).not.toHaveBeenCalled();
|
|
|
|
expect(settings.getDevice('bulb')).toBeNull();
|
2020-09-07 08:29:53 -07:00
|
|
|
expect(MQTT.publish).toHaveBeenCalledWith('zigbee2mqtt/bulb', '', {retain: true, qos: 0}, expect.any(Function));
|
2020-06-13 08:22:00 -07:00
|
|
|
expect(MQTT.publish).toHaveBeenCalledWith('zigbee2mqtt/bridge/devices', expect.any(String), expect.any(Object), expect.any(Function));
|
|
|
|
expect(MQTT.publish).toHaveBeenCalledWith(
|
|
|
|
'zigbee2mqtt/bridge/response/device/remove',
|
2020-08-13 11:00:35 -07:00
|
|
|
stringify({"data":{"id": "bulb","block":false,"force":false},"status":"ok"}),
|
2020-06-13 08:22:00 -07:00
|
|
|
{retain: false, qos: 0}, expect.any(Function)
|
|
|
|
);
|
2020-07-15 14:22:32 -07:00
|
|
|
expect(settings.get().blocklist).toStrictEqual([]);
|
2020-10-06 10:28:32 -07:00
|
|
|
expect(MQTT.publish).toHaveBeenCalledWith('zigbee2mqtt/bridge/devices', expect.any(String), expect.any(Object), expect.any(Function));
|
|
|
|
expect(MQTT.publish).toHaveBeenCalledWith('zigbee2mqtt/bridge/groups', expect.any(String), expect.any(Object), expect.any(Function));
|
2020-06-13 08:22:00 -07:00
|
|
|
});
|
|
|
|
|
|
|
|
it('Should allow to remove device by object ID', async () => {
|
|
|
|
const device = zigbeeHerdsman.devices.bulb;
|
|
|
|
MQTT.publish.mockClear();
|
2020-08-13 11:00:35 -07:00
|
|
|
MQTT.events.message('zigbee2mqtt/bridge/request/device/remove', stringify({id: "bulb"}));
|
2020-06-13 08:22:00 -07:00
|
|
|
await flushPromises();
|
|
|
|
expect(device.removeFromNetwork).toHaveBeenCalledTimes(1);
|
|
|
|
expect(device.removeFromDatabase).not.toHaveBeenCalled();
|
|
|
|
expect(settings.getDevice('bulb')).toBeNull();
|
|
|
|
expect(MQTT.publish).toHaveBeenCalledWith('zigbee2mqtt/bridge/devices', expect.any(String), expect.any(Object), expect.any(Function));
|
|
|
|
expect(MQTT.publish).toHaveBeenCalledWith(
|
|
|
|
'zigbee2mqtt/bridge/response/device/remove',
|
2020-08-13 11:00:35 -07:00
|
|
|
stringify({"data":{"id": "bulb","block":false,"force":false},"status":"ok"}),
|
2020-06-13 08:22:00 -07:00
|
|
|
{retain: false, qos: 0}, expect.any(Function)
|
|
|
|
);
|
|
|
|
});
|
|
|
|
|
|
|
|
it('Should allow to force remove device', async () => {
|
|
|
|
const device = zigbeeHerdsman.devices.bulb;
|
|
|
|
MQTT.publish.mockClear();
|
2020-08-13 11:00:35 -07:00
|
|
|
MQTT.events.message('zigbee2mqtt/bridge/request/device/remove', stringify({id: "bulb", force: true}));
|
2020-06-13 08:22:00 -07:00
|
|
|
await flushPromises();
|
|
|
|
expect(device.removeFromDatabase).toHaveBeenCalledTimes(1);
|
|
|
|
expect(device.removeFromNetwork).not.toHaveBeenCalled();
|
|
|
|
expect(settings.getDevice('bulb')).toBeNull();
|
|
|
|
expect(MQTT.publish).toHaveBeenCalledWith('zigbee2mqtt/bridge/devices', expect.any(String), expect.any(Object), expect.any(Function));
|
|
|
|
expect(MQTT.publish).toHaveBeenCalledWith(
|
|
|
|
'zigbee2mqtt/bridge/response/device/remove',
|
2020-08-13 11:00:35 -07:00
|
|
|
stringify({"data":{"id": "bulb","block":false,"force":true},"status":"ok"}),
|
2020-06-13 08:22:00 -07:00
|
|
|
{retain: false, qos: 0}, expect.any(Function)
|
|
|
|
);
|
|
|
|
});
|
|
|
|
|
2020-07-15 14:22:32 -07:00
|
|
|
it('Should allow to block device', async () => {
|
2020-06-13 08:22:00 -07:00
|
|
|
const device = zigbeeHerdsman.devices.bulb;
|
|
|
|
MQTT.publish.mockClear();
|
2020-08-13 11:00:35 -07:00
|
|
|
MQTT.events.message('zigbee2mqtt/bridge/request/device/remove', stringify({id: "bulb", block: true, force: true}));
|
2020-06-13 08:22:00 -07:00
|
|
|
await flushPromises();
|
|
|
|
expect(device.removeFromDatabase).toHaveBeenCalledTimes(1);
|
|
|
|
expect(settings.getDevice('bulb')).toBeNull();
|
|
|
|
expect(MQTT.publish).toHaveBeenCalledWith('zigbee2mqtt/bridge/devices', expect.any(String), expect.any(Object), expect.any(Function));
|
|
|
|
expect(MQTT.publish).toHaveBeenCalledWith(
|
|
|
|
'zigbee2mqtt/bridge/response/device/remove',
|
2020-08-13 11:00:35 -07:00
|
|
|
stringify({"data":{"id": "bulb","block":true,"force":true},"status":"ok"}),
|
2020-06-13 08:22:00 -07:00
|
|
|
{retain: false, qos: 0}, expect.any(Function)
|
|
|
|
);
|
2020-07-15 14:22:32 -07:00
|
|
|
expect(settings.get().blocklist).toStrictEqual(["0x000b57fffec6a5b2"]);
|
2020-06-13 08:22:00 -07:00
|
|
|
});
|
|
|
|
|
|
|
|
it('Should allow to remove group', async () => {
|
|
|
|
const group = zigbeeHerdsman.groups.group_1;
|
|
|
|
MQTT.publish.mockClear();
|
|
|
|
MQTT.events.message('zigbee2mqtt/bridge/request/group/remove', 'group_1');
|
|
|
|
await flushPromises();
|
2020-07-07 12:16:35 -07:00
|
|
|
expect(group.removeFromNetwork).toHaveBeenCalledTimes(1);
|
|
|
|
expect(settings.getGroup('group_1')).toBeNull();
|
|
|
|
expect(MQTT.publish).toHaveBeenCalledWith('zigbee2mqtt/bridge/groups', expect.any(String), expect.any(Object), expect.any(Function));
|
|
|
|
expect(MQTT.publish).toHaveBeenCalledWith(
|
|
|
|
'zigbee2mqtt/bridge/response/group/remove',
|
2020-08-13 11:00:35 -07:00
|
|
|
stringify({"data":{"id": "group_1", "force": false},"status":"ok"}),
|
2020-07-07 12:16:35 -07:00
|
|
|
{retain: false, qos: 0}, expect.any(Function)
|
|
|
|
);
|
|
|
|
});
|
|
|
|
|
|
|
|
it('Should allow to force remove group', async () => {
|
|
|
|
const group = zigbeeHerdsman.groups.group_1;
|
|
|
|
MQTT.publish.mockClear();
|
2020-08-13 11:00:35 -07:00
|
|
|
MQTT.events.message('zigbee2mqtt/bridge/request/group/remove', stringify({id: "group_1", force: true}));
|
2020-07-07 12:16:35 -07:00
|
|
|
await flushPromises();
|
2020-06-13 08:22:00 -07:00
|
|
|
expect(group.removeFromDatabase).toHaveBeenCalledTimes(1);
|
|
|
|
expect(settings.getGroup('group_1')).toBeNull();
|
|
|
|
expect(MQTT.publish).toHaveBeenCalledWith('zigbee2mqtt/bridge/groups', expect.any(String), expect.any(Object), expect.any(Function));
|
|
|
|
expect(MQTT.publish).toHaveBeenCalledWith(
|
|
|
|
'zigbee2mqtt/bridge/response/group/remove',
|
2020-08-13 11:00:35 -07:00
|
|
|
stringify({"data":{"id": "group_1", "force": true},"status":"ok"}),
|
2020-06-13 08:22:00 -07:00
|
|
|
{retain: false, qos: 0}, expect.any(Function)
|
|
|
|
);
|
|
|
|
});
|
|
|
|
|
|
|
|
it('Should throw error on removing non-existing device', async () => {
|
|
|
|
const device = zigbeeHerdsman.devices.bulb;
|
|
|
|
MQTT.publish.mockClear();
|
2020-08-13 11:00:35 -07:00
|
|
|
MQTT.events.message('zigbee2mqtt/bridge/request/device/remove', stringify({id: "non-existing-device"}));
|
2020-06-13 08:22:00 -07:00
|
|
|
await flushPromises();
|
|
|
|
expect(MQTT.publish).toHaveBeenCalledWith(
|
|
|
|
'zigbee2mqtt/bridge/response/device/remove',
|
2020-08-13 11:00:35 -07:00
|
|
|
stringify({"data":{},"status":"error","error":"Device 'non-existing-device' does not exist"}),
|
2020-06-13 08:22:00 -07:00
|
|
|
{retain: false, qos: 0}, expect.any(Function)
|
|
|
|
);
|
|
|
|
});
|
|
|
|
|
|
|
|
it('Should throw error when remove device fails', async () => {
|
|
|
|
const device = zigbeeHerdsman.devices.bulb;
|
|
|
|
MQTT.publish.mockClear();
|
|
|
|
device.removeFromNetwork.mockImplementationOnce(() => {throw new Error('device timeout')})
|
2020-08-13 11:00:35 -07:00
|
|
|
MQTT.events.message('zigbee2mqtt/bridge/request/device/remove', stringify({id: "bulb"}));
|
2020-06-13 08:22:00 -07:00
|
|
|
await flushPromises();
|
|
|
|
expect(MQTT.publish).toHaveBeenCalledWith(
|
|
|
|
'zigbee2mqtt/bridge/response/device/remove',
|
2020-08-13 11:00:35 -07:00
|
|
|
stringify({"data":{},"status":"error","error":"Failed to remove device 'bulb' (block: false, force: false) (Error: device timeout)"}),
|
2020-06-13 08:22:00 -07:00
|
|
|
{retain: false, qos: 0}, expect.any(Function)
|
|
|
|
);
|
|
|
|
});
|
2020-06-13 10:35:09 -07:00
|
|
|
|
|
|
|
it('Should allow rename device', async () => {
|
|
|
|
MQTT.publish.mockClear();
|
2020-08-13 11:00:35 -07:00
|
|
|
MQTT.events.message('zigbee2mqtt/bridge/request/device/rename', stringify({from: 'bulb', to: 'bulb_new_name'}));
|
2020-06-13 10:35:09 -07:00
|
|
|
await flushPromises();
|
|
|
|
expect(settings.getDevice('bulb')).toBeNull();
|
2020-07-13 14:00:33 -07:00
|
|
|
expect(settings.getDevice('bulb_new_name')).toStrictEqual({"ID": "0x000b57fffec6a5b2", "friendly_name": "bulb_new_name", "friendlyName": "bulb_new_name", "retain": true});
|
2020-09-07 08:29:53 -07:00
|
|
|
expect(MQTT.publish).toHaveBeenCalledWith('zigbee2mqtt/bulb', '', {retain: true, qos: 0}, expect.any(Function));
|
2020-06-13 10:35:09 -07:00
|
|
|
expect(MQTT.publish).toHaveBeenCalledWith('zigbee2mqtt/bridge/devices', expect.any(String), expect.any(Object), expect.any(Function));
|
|
|
|
expect(MQTT.publish).toHaveBeenCalledWith(
|
|
|
|
'zigbee2mqtt/bridge/response/device/rename',
|
2020-09-04 12:08:20 -07:00
|
|
|
stringify({"data":{"from":"bulb","to":"bulb_new_name","homeassistant_rename":false},"status":"ok"}),
|
2020-06-13 10:35:09 -07:00
|
|
|
{retain: false, qos: 0}, expect.any(Function)
|
|
|
|
);
|
|
|
|
});
|
|
|
|
|
|
|
|
it('Should allow rename group', async () => {
|
|
|
|
MQTT.publish.mockClear();
|
2020-08-13 11:00:35 -07:00
|
|
|
MQTT.events.message('zigbee2mqtt/bridge/request/group/rename', stringify({from: 'group_1', to: 'group_new_name'}));
|
2020-06-13 10:35:09 -07:00
|
|
|
await flushPromises();
|
|
|
|
expect(settings.getGroup('group_1')).toBeNull();
|
2020-07-13 14:00:33 -07:00
|
|
|
expect(settings.getGroup('group_new_name')).toStrictEqual({"ID": 1, "devices": [], "friendly_name": "group_new_name", "friendlyName": "group_new_name", "optimistic": true, "retain": false});
|
2020-06-13 10:35:09 -07:00
|
|
|
expect(MQTT.publish).toHaveBeenCalledWith('zigbee2mqtt/bridge/groups', expect.any(String), expect.any(Object), expect.any(Function));
|
|
|
|
expect(MQTT.publish).toHaveBeenCalledWith(
|
|
|
|
'zigbee2mqtt/bridge/response/group/rename',
|
2020-09-04 12:08:20 -07:00
|
|
|
stringify({"data":{"from":"group_1","to":"group_new_name","homeassistant_rename":false},"status":"ok"}),
|
2020-06-13 10:35:09 -07:00
|
|
|
{retain: false, qos: 0}, expect.any(Function)
|
|
|
|
);
|
|
|
|
});
|
|
|
|
|
|
|
|
it('Should throw error on invalid device rename payload', async () => {
|
|
|
|
MQTT.publish.mockClear();
|
2020-08-13 11:00:35 -07:00
|
|
|
MQTT.events.message('zigbee2mqtt/bridge/request/device/rename', stringify({from_bla: 'bulb', to: 'bulb_new_name'}));
|
2020-06-13 10:35:09 -07:00
|
|
|
await flushPromises();
|
|
|
|
expect(MQTT.publish).toHaveBeenCalledWith(
|
|
|
|
'zigbee2mqtt/bridge/response/device/rename',
|
2020-08-13 11:00:35 -07:00
|
|
|
stringify({"data":{},"status":"error","error":"Invalid payload"}),
|
2020-06-13 10:35:09 -07:00
|
|
|
{retain: false, qos: 0}, expect.any(Function)
|
|
|
|
);
|
|
|
|
});
|
|
|
|
|
|
|
|
it('Should throw error on non-existing device rename', async () => {
|
|
|
|
MQTT.publish.mockClear();
|
2020-08-13 11:00:35 -07:00
|
|
|
MQTT.events.message('zigbee2mqtt/bridge/request/device/rename', stringify({from: 'bulb_not_existing', to: 'bulb_new_name'}));
|
2020-06-13 10:35:09 -07:00
|
|
|
await flushPromises();
|
|
|
|
expect(MQTT.publish).toHaveBeenCalledWith(
|
|
|
|
'zigbee2mqtt/bridge/response/device/rename',
|
2020-08-13 11:00:35 -07:00
|
|
|
stringify({"data":{},"status":"error","error":"Device 'bulb_not_existing' does not exist"}),
|
2020-06-13 10:35:09 -07:00
|
|
|
{retain: false, qos: 0}, expect.any(Function)
|
|
|
|
);
|
|
|
|
});
|
|
|
|
|
|
|
|
it('Should allow to rename last joined device', async () => {
|
|
|
|
MQTT.publish.mockClear();
|
|
|
|
await zigbeeHerdsman.events.deviceJoined({device: zigbeeHerdsman.devices.bulb});
|
2020-08-13 11:00:35 -07:00
|
|
|
MQTT.events.message('zigbee2mqtt/bridge/request/device/rename', stringify({last: true, to: 'bulb_new_name'}));
|
2020-06-13 10:35:09 -07:00
|
|
|
await flushPromises();
|
|
|
|
expect(settings.getDevice('bulb')).toBeNull();
|
2020-07-13 14:00:33 -07:00
|
|
|
expect(settings.getDevice('bulb_new_name')).toStrictEqual({"ID": "0x000b57fffec6a5b2", "friendly_name": "bulb_new_name", "friendlyName": "bulb_new_name", "retain": true});
|
2020-06-13 10:35:09 -07:00
|
|
|
expect(MQTT.publish).toHaveBeenCalledWith('zigbee2mqtt/bridge/devices', expect.any(String), expect.any(Object), expect.any(Function));
|
|
|
|
expect(MQTT.publish).toHaveBeenCalledWith(
|
|
|
|
'zigbee2mqtt/bridge/response/device/rename',
|
2020-09-04 12:08:20 -07:00
|
|
|
stringify({"data":{"from":"bulb","to":"bulb_new_name","homeassistant_rename":false},"status":"ok"}),
|
2020-06-13 10:35:09 -07:00
|
|
|
{retain: false, qos: 0}, expect.any(Function)
|
|
|
|
);
|
|
|
|
});
|
|
|
|
|
2020-09-19 01:57:39 -07:00
|
|
|
it('Should throw error when renaming device through not allowed friendlyName', async () => {
|
|
|
|
MQTT.publish.mockClear();
|
|
|
|
MQTT.events.message('zigbee2mqtt/bridge/request/device/rename', stringify({from: 'bulb', to: 'bulb_new_name/1'}));
|
|
|
|
await flushPromises();
|
|
|
|
expect(MQTT.publish).toHaveBeenCalledWith(
|
|
|
|
'zigbee2mqtt/bridge/response/device/rename',
|
|
|
|
stringify({"data":{},"status":"error","error":`Friendly name cannot end with a "/DIGIT" ('bulb_new_name/1')`}),
|
|
|
|
{retain: false, qos: 0}, expect.any(Function)
|
|
|
|
);
|
|
|
|
});
|
|
|
|
|
2020-06-13 10:35:09 -07:00
|
|
|
it('Should throw error when renaming last joined device but none has joined', async () => {
|
|
|
|
MQTT.publish.mockClear();
|
2020-08-13 11:00:35 -07:00
|
|
|
MQTT.events.message('zigbee2mqtt/bridge/request/device/rename', stringify({last: true, to: 'bulb_new_name'}));
|
2020-06-13 10:35:09 -07:00
|
|
|
await flushPromises();
|
|
|
|
expect(MQTT.publish).toHaveBeenCalledWith(
|
|
|
|
'zigbee2mqtt/bridge/response/device/rename',
|
2020-08-13 11:00:35 -07:00
|
|
|
stringify({"data":{},"status":"error","error":"No device has joined since start"}),
|
2020-06-13 10:35:09 -07:00
|
|
|
{retain: false, qos: 0}, expect.any(Function)
|
|
|
|
);
|
|
|
|
});
|
2020-06-13 14:28:06 -07:00
|
|
|
|
|
|
|
it('Should allow change device options', async () => {
|
|
|
|
MQTT.publish.mockClear();
|
2020-07-13 14:00:33 -07:00
|
|
|
expect(settings.getDevice('bulb')).toStrictEqual({"ID": "0x000b57fffec6a5b2", "friendly_name": "bulb", "friendlyName": "bulb", "retain": true});
|
2020-08-13 11:00:35 -07:00
|
|
|
MQTT.events.message('zigbee2mqtt/bridge/request/device/options', stringify({options: {retain: false, transition: 1}, id: 'bulb'}));
|
2020-06-13 14:28:06 -07:00
|
|
|
await flushPromises();
|
2020-07-13 14:00:33 -07:00
|
|
|
expect(settings.getDevice('bulb')).toStrictEqual({"ID": "0x000b57fffec6a5b2", "friendly_name": "bulb", "friendlyName": "bulb", "retain": false, "transition": 1});
|
2020-06-13 14:28:06 -07:00
|
|
|
expect(MQTT.publish).toHaveBeenCalledWith(
|
2020-06-13 15:28:24 -07:00
|
|
|
'zigbee2mqtt/bridge/response/device/options',
|
2020-08-13 11:00:35 -07:00
|
|
|
stringify({"data":{"from":{"retain": true},"to":{"retain": false,"transition":1}, "id":"bulb"},"status":"ok"}),
|
2020-06-13 14:28:06 -07:00
|
|
|
{retain: false, qos: 0}, expect.any(Function)
|
|
|
|
);
|
|
|
|
});
|
|
|
|
|
|
|
|
it('Should allow change group options', async () => {
|
|
|
|
MQTT.publish.mockClear();
|
|
|
|
expect(settings.getGroup('group_1')).toStrictEqual({"ID": 1, "devices": [], "friendly_name": "group_1", "retain": false, "friendlyName": "group_1", "optimistic": true});
|
2020-08-13 11:00:35 -07:00
|
|
|
MQTT.events.message('zigbee2mqtt/bridge/request/group/options', stringify({options: {retain: true, transition: 1}, id: 'group_1'}));
|
2020-06-13 14:28:06 -07:00
|
|
|
await flushPromises();
|
|
|
|
expect(settings.getGroup('group_1')).toStrictEqual({"ID": 1, "devices": [], "friendly_name": "group_1", "retain": true, "friendlyName": "group_1", "optimistic": true, "transition": 1});
|
|
|
|
expect(MQTT.publish).toHaveBeenCalledWith(
|
2020-06-13 15:28:24 -07:00
|
|
|
'zigbee2mqtt/bridge/response/group/options',
|
2020-08-13 11:00:35 -07:00
|
|
|
stringify({"data":{"from":{"optimistic": true,"retain": false},"to":{"optimistic": true,"retain": true,"transition":1}, "id":"group_1"},"status":"ok"}),
|
2020-06-13 14:28:06 -07:00
|
|
|
{retain: false, qos: 0}, expect.any(Function)
|
|
|
|
);
|
|
|
|
});
|
|
|
|
|
|
|
|
it('Should throw error on invalid device change options payload', async () => {
|
|
|
|
MQTT.publish.mockClear();
|
2020-08-13 11:00:35 -07:00
|
|
|
MQTT.events.message('zigbee2mqtt/bridge/request/device/options', stringify({options_: {retain: true, transition: 1}, id: 'bulb'}));
|
2020-06-13 14:28:06 -07:00
|
|
|
await flushPromises();
|
|
|
|
expect(MQTT.publish).toHaveBeenCalledWith(
|
2020-06-13 15:28:24 -07:00
|
|
|
'zigbee2mqtt/bridge/response/device/options',
|
2020-08-13 11:00:35 -07:00
|
|
|
stringify({"data":{},"status":"error","error":"Invalid payload"}),
|
2020-06-13 14:28:06 -07:00
|
|
|
{retain: false, qos: 0}, expect.any(Function)
|
|
|
|
);
|
|
|
|
});
|
2020-06-13 14:42:58 -07:00
|
|
|
|
|
|
|
it('Should allow to add group by string', async () => {
|
|
|
|
MQTT.publish.mockClear();
|
|
|
|
MQTT.events.message('zigbee2mqtt/bridge/request/group/add', 'group_193');
|
|
|
|
await flushPromises();
|
2020-07-13 14:00:33 -07:00
|
|
|
expect(settings.getGroup('group_193')).toStrictEqual({"ID": 3, "devices": [], "friendly_name": "group_193", "friendlyName": "group_193", "optimistic": true});
|
2020-06-13 14:42:58 -07:00
|
|
|
expect(MQTT.publish).toHaveBeenCalledWith('zigbee2mqtt/bridge/groups', expect.any(String), expect.any(Object), expect.any(Function));
|
|
|
|
expect(MQTT.publish).toHaveBeenCalledWith(
|
|
|
|
'zigbee2mqtt/bridge/response/group/add',
|
2020-08-13 11:00:35 -07:00
|
|
|
stringify({"data":{"friendly_name":"group_193","id": 3},"status":"ok"}),
|
2020-06-13 14:42:58 -07:00
|
|
|
{retain: false, qos: 0}, expect.any(Function)
|
|
|
|
);
|
|
|
|
});
|
|
|
|
|
|
|
|
it('Should allow to add group with ID', async () => {
|
|
|
|
MQTT.publish.mockClear();
|
2020-08-13 11:00:35 -07:00
|
|
|
MQTT.events.message('zigbee2mqtt/bridge/request/group/add', stringify({friendly_name: "group_193", id: 9}));
|
2020-06-13 14:42:58 -07:00
|
|
|
await flushPromises();
|
2020-07-13 14:00:33 -07:00
|
|
|
expect(settings.getGroup('group_193')).toStrictEqual({"ID": 9, "devices": [], "friendly_name": "group_193", "friendlyName": "group_193", "optimistic": true});
|
2020-06-13 14:42:58 -07:00
|
|
|
expect(MQTT.publish).toHaveBeenCalledWith('zigbee2mqtt/bridge/groups', expect.any(String), expect.any(Object), expect.any(Function));
|
|
|
|
expect(MQTT.publish).toHaveBeenCalledWith(
|
|
|
|
'zigbee2mqtt/bridge/response/group/add',
|
2020-08-13 11:00:35 -07:00
|
|
|
stringify({"data":{"friendly_name":"group_193","id": 9},"status":"ok"}),
|
2020-06-13 14:42:58 -07:00
|
|
|
{retain: false, qos: 0}, expect.any(Function)
|
|
|
|
);
|
|
|
|
});
|
|
|
|
|
|
|
|
it('Should throw error when add with invalid payload', async () => {
|
|
|
|
MQTT.publish.mockClear();
|
2020-08-13 11:00:35 -07:00
|
|
|
MQTT.events.message('zigbee2mqtt/bridge/request/group/add', stringify({friendly_name9: "group_193"}));
|
2020-06-13 14:42:58 -07:00
|
|
|
await flushPromises();
|
|
|
|
expect(MQTT.publish).toHaveBeenCalledWith(
|
|
|
|
'zigbee2mqtt/bridge/response/group/add',
|
2020-08-13 11:00:35 -07:00
|
|
|
stringify({"data":{},"status":"error","error":"Invalid payload"}),
|
2020-06-13 14:42:58 -07:00
|
|
|
{retain: false, qos: 0}, expect.any(Function)
|
|
|
|
);
|
|
|
|
});
|
2020-06-15 09:58:32 -07:00
|
|
|
|
2020-07-29 14:10:03 -07:00
|
|
|
it('Should allow to enable/disable Home Assistant extension', async () => {
|
|
|
|
// Test if disabled intially
|
|
|
|
const device = zigbeeHerdsman.devices.WXKG11LM;
|
|
|
|
settings.set(['devices', device.ieeeAddr, 'legacy'], false);
|
|
|
|
const payload = {data: {onOff: 1}, cluster: 'genOnOff', device, endpoint: device.getEndpoint(1), type: 'attributeReport', linkquality: 10};
|
|
|
|
await zigbeeHerdsman.events.message(payload);
|
|
|
|
expect(settings.get().homeassistant).toBeFalsy();
|
|
|
|
expect(MQTT.publish).not.toHaveBeenCalledWith('zigbee2mqtt/button/action', 'single', {retain: false, qos: 0}, expect.any(Function));
|
|
|
|
|
|
|
|
// Disable when already disabled should go OK
|
2020-08-13 11:00:35 -07:00
|
|
|
MQTT.events.message('zigbee2mqtt/bridge/request/config/homeassistant', stringify({value: false}));
|
2020-07-29 14:10:03 -07:00
|
|
|
await flushPromises();
|
|
|
|
expect(MQTT.publish).toHaveBeenCalledWith(
|
|
|
|
'zigbee2mqtt/bridge/response/config/homeassistant',
|
2020-08-13 11:00:35 -07:00
|
|
|
stringify({"data":{"value":false},"status":"ok"}),
|
2020-07-29 14:10:03 -07:00
|
|
|
{retain: false, qos: 0}, expect.any(Function)
|
|
|
|
);
|
|
|
|
expect(settings.get().homeassistant).toBeFalsy();
|
|
|
|
|
|
|
|
// Enable
|
2020-08-13 11:00:35 -07:00
|
|
|
MQTT.events.message('zigbee2mqtt/bridge/request/config/homeassistant', stringify({value: true}));
|
2020-07-29 14:10:03 -07:00
|
|
|
await flushPromises();
|
|
|
|
expect(MQTT.publish).toHaveBeenCalledWith(
|
|
|
|
'zigbee2mqtt/bridge/response/config/homeassistant',
|
2020-08-13 11:00:35 -07:00
|
|
|
stringify({"data":{"value":true},"status":"ok"}),
|
2020-07-29 14:10:03 -07:00
|
|
|
{retain: false, qos: 0}, expect.any(Function)
|
|
|
|
);
|
|
|
|
expect(settings.get().homeassistant).toBeTruthy();
|
|
|
|
MQTT.publish.mockClear();
|
|
|
|
await zigbeeHerdsman.events.message(payload);
|
|
|
|
await flushPromises();
|
|
|
|
expect(MQTT.publish).toHaveBeenCalledWith('zigbee2mqtt/button/action', 'single', {retain: false, qos: 0}, expect.any(Function));
|
|
|
|
|
|
|
|
// Disable
|
2020-08-13 11:00:35 -07:00
|
|
|
MQTT.events.message('zigbee2mqtt/bridge/request/config/homeassistant', stringify({value: false}));
|
2020-07-29 14:10:03 -07:00
|
|
|
await flushPromises();
|
|
|
|
expect(MQTT.publish).toHaveBeenCalledWith(
|
|
|
|
'zigbee2mqtt/bridge/response/config/homeassistant',
|
2020-08-13 11:00:35 -07:00
|
|
|
stringify({"data":{"value":false},"status":"ok"}),
|
2020-07-29 14:10:03 -07:00
|
|
|
{retain: false, qos: 0}, expect.any(Function)
|
|
|
|
);
|
|
|
|
expect(settings.get().homeassistant).toBeFalsy();
|
|
|
|
MQTT.publish.mockClear();
|
|
|
|
await zigbeeHerdsman.events.message(payload);
|
|
|
|
await flushPromises();
|
|
|
|
expect(MQTT.publish).not.toHaveBeenCalledWith('zigbee2mqtt/button/action', 'single', {retain: false, qos: 0}, expect.any(Function));
|
|
|
|
});
|
|
|
|
|
|
|
|
it('Should fail to set Home Assistant when invalid type', async () => {
|
|
|
|
MQTT.publish.mockClear();
|
|
|
|
MQTT.events.message('zigbee2mqtt/bridge/request/config/homeassistant', 'invalid_one');
|
|
|
|
await flushPromises();
|
|
|
|
expect(settings.get().homeassistant).toBeFalsy();
|
|
|
|
expect(MQTT.publish).toHaveBeenCalledWith(
|
|
|
|
'zigbee2mqtt/bridge/response/config/homeassistant',
|
2020-08-13 11:00:35 -07:00
|
|
|
stringify({"data":{},"status":"error","error":"'invalid_one' is not an allowed value, allowed: true,false"}),
|
2020-07-29 14:10:03 -07:00
|
|
|
{retain: false, qos: 0}, expect.any(Function)
|
|
|
|
);
|
|
|
|
});
|
|
|
|
|
2020-06-15 09:58:32 -07:00
|
|
|
it('Should allow to set last_seen', async () => {
|
|
|
|
MQTT.publish.mockClear();
|
2020-07-13 14:00:33 -07:00
|
|
|
MQTT.events.message('zigbee2mqtt/bridge/request/config/last_seen', 'ISO_8601');
|
2020-06-15 09:58:32 -07:00
|
|
|
await flushPromises();
|
|
|
|
expect(settings.get().advanced.last_seen).toBe('ISO_8601');
|
|
|
|
expect(MQTT.publish).toHaveBeenCalledWith(
|
2020-07-13 14:00:33 -07:00
|
|
|
'zigbee2mqtt/bridge/response/config/last_seen',
|
2020-08-13 11:00:35 -07:00
|
|
|
stringify({"data":{"value":"ISO_8601"},"status":"ok"}),
|
2020-06-15 09:58:32 -07:00
|
|
|
{retain: false, qos: 0}, expect.any(Function)
|
|
|
|
);
|
|
|
|
});
|
|
|
|
|
|
|
|
it('Should fail to set last_seen when invalid type', async () => {
|
|
|
|
MQTT.publish.mockClear();
|
2020-07-13 14:00:33 -07:00
|
|
|
MQTT.events.message('zigbee2mqtt/bridge/request/config/last_seen', 'invalid_one');
|
2020-06-15 09:58:32 -07:00
|
|
|
await flushPromises();
|
|
|
|
expect(settings.get().advanced.last_seen).toBe('disable');
|
|
|
|
expect(MQTT.publish).toHaveBeenCalledWith(
|
2020-07-13 14:00:33 -07:00
|
|
|
'zigbee2mqtt/bridge/response/config/last_seen',
|
2020-08-13 11:00:35 -07:00
|
|
|
stringify({"data":{},"status":"error","error":"'invalid_one' is not an allowed value, allowed: disable,ISO_8601,epoch,ISO_8601_local"}),
|
2020-06-15 09:58:32 -07:00
|
|
|
{retain: false, qos: 0}, expect.any(Function)
|
|
|
|
);
|
|
|
|
});
|
|
|
|
|
|
|
|
it('Should allow to set elapsed', async () => {
|
|
|
|
MQTT.publish.mockClear();
|
|
|
|
MQTT.events.message('zigbee2mqtt/bridge/request/config/elapsed', 'true');
|
|
|
|
await flushPromises();
|
|
|
|
expect(settings.get().advanced.elapsed).toBe(true);
|
|
|
|
expect(MQTT.publish).toHaveBeenCalledWith(
|
|
|
|
'zigbee2mqtt/bridge/response/config/elapsed',
|
2020-08-13 11:00:35 -07:00
|
|
|
stringify({"data":{"value":true},"status":"ok"}),
|
2020-06-15 09:58:32 -07:00
|
|
|
{retain: false, qos: 0}, expect.any(Function)
|
|
|
|
);
|
|
|
|
});
|
|
|
|
|
|
|
|
it('Should fail to set last_seen when invalid type', async () => {
|
|
|
|
MQTT.publish.mockClear();
|
|
|
|
MQTT.events.message('zigbee2mqtt/bridge/request/config/elapsed', 'not_valid');
|
|
|
|
await flushPromises();
|
|
|
|
expect(settings.get().advanced.elapsed).toBe(false);
|
|
|
|
expect(MQTT.publish).toHaveBeenCalledWith(
|
|
|
|
'zigbee2mqtt/bridge/response/config/elapsed',
|
2020-08-13 11:00:35 -07:00
|
|
|
stringify({"data":{},"status":"error","error":"'not_valid' is not an allowed value, allowed: true,false"}),
|
2020-06-15 09:58:32 -07:00
|
|
|
{retain: false, qos: 0}, expect.any(Function)
|
|
|
|
);
|
|
|
|
});
|
|
|
|
|
|
|
|
it('Should allow to set log level', async () => {
|
|
|
|
MQTT.publish.mockClear();
|
2020-07-13 14:00:33 -07:00
|
|
|
MQTT.events.message('zigbee2mqtt/bridge/request/config/log_level', 'debug');
|
2020-06-15 09:58:32 -07:00
|
|
|
await flushPromises();
|
|
|
|
expect(logger.getLevel()).toBe('debug');
|
2020-06-15 10:06:08 -07:00
|
|
|
expect(MQTT.publish).toHaveBeenCalledWith('zigbee2mqtt/bridge/info', expect.any(String), expect.any(Object), expect.any(Function));
|
2020-06-15 09:58:32 -07:00
|
|
|
expect(MQTT.publish).toHaveBeenCalledWith(
|
2020-07-13 14:00:33 -07:00
|
|
|
'zigbee2mqtt/bridge/response/config/log_level',
|
2020-08-13 11:00:35 -07:00
|
|
|
stringify({"data":{"value":'debug'},"status":"ok"}),
|
2020-06-15 09:58:32 -07:00
|
|
|
{retain: false, qos: 0}, expect.any(Function)
|
|
|
|
);
|
|
|
|
});
|
|
|
|
|
|
|
|
it('Should fail to set log level when invalid type', async () => {
|
|
|
|
MQTT.publish.mockClear();
|
2020-07-13 14:00:33 -07:00
|
|
|
MQTT.events.message('zigbee2mqtt/bridge/request/config/log_level', 'not_valid');
|
2020-06-15 09:58:32 -07:00
|
|
|
await flushPromises();
|
|
|
|
expect(MQTT.publish).toHaveBeenCalledWith(
|
2020-07-13 14:00:33 -07:00
|
|
|
'zigbee2mqtt/bridge/response/config/log_level',
|
2020-08-13 11:00:35 -07:00
|
|
|
stringify({"data":{},"status":"error","error":"'not_valid' is not an allowed value, allowed: error,warn,info,debug"}),
|
2020-06-15 09:58:32 -07:00
|
|
|
{retain: false, qos: 0}, expect.any(Function)
|
|
|
|
);
|
|
|
|
});
|
2020-06-15 10:19:57 -07:00
|
|
|
|
|
|
|
it('Should allow to touchlink factory reset (succeeds)', async () => {
|
2020-09-08 11:24:49 -07:00
|
|
|
MQTT.publish.mockClear();
|
|
|
|
zigbeeHerdsman.touchlinkFactoryResetFirst.mockClear();
|
|
|
|
zigbeeHerdsman.touchlinkFactoryResetFirst.mockReturnValueOnce(true);
|
|
|
|
MQTT.events.message('zigbee2mqtt/bridge/request/touchlink/factory_reset', '');
|
|
|
|
await flushPromises();
|
|
|
|
expect(zigbeeHerdsman.touchlinkFactoryResetFirst).toHaveBeenCalledTimes(1);
|
|
|
|
expect(MQTT.publish).toHaveBeenCalledWith(
|
|
|
|
'zigbee2mqtt/bridge/response/touchlink/factory_reset',
|
|
|
|
stringify({"data":{},"status":"ok"}),
|
|
|
|
{retain: false, qos: 0}, expect.any(Function)
|
|
|
|
);
|
|
|
|
});
|
|
|
|
|
|
|
|
it('Should allow to touchlink factory reset specific device', async () => {
|
2020-06-15 10:19:57 -07:00
|
|
|
MQTT.publish.mockClear();
|
|
|
|
zigbeeHerdsman.touchlinkFactoryReset.mockClear();
|
|
|
|
zigbeeHerdsman.touchlinkFactoryReset.mockReturnValueOnce(true);
|
2020-09-08 11:24:49 -07:00
|
|
|
MQTT.events.message('zigbee2mqtt/bridge/request/touchlink/factory_reset', stringify({ieee_address: '0x1239', channel: 12}));
|
2020-06-15 10:19:57 -07:00
|
|
|
await flushPromises();
|
|
|
|
expect(zigbeeHerdsman.touchlinkFactoryReset).toHaveBeenCalledTimes(1);
|
2020-09-08 11:24:49 -07:00
|
|
|
expect(zigbeeHerdsman.touchlinkFactoryReset).toHaveBeenCalledWith('0x1239', 12);
|
2020-06-15 10:19:57 -07:00
|
|
|
expect(MQTT.publish).toHaveBeenCalledWith(
|
2020-07-13 14:00:33 -07:00
|
|
|
'zigbee2mqtt/bridge/response/touchlink/factory_reset',
|
2020-09-08 11:24:49 -07:00
|
|
|
stringify({"data":{"ieee_address":'0x1239',"channel":12},"status":"ok"}),
|
2020-06-15 10:19:57 -07:00
|
|
|
{retain: false, qos: 0}, expect.any(Function)
|
|
|
|
);
|
|
|
|
});
|
|
|
|
|
2020-09-09 09:24:32 -07:00
|
|
|
it('Should allow to touchlink identify specific device', async () => {
|
|
|
|
MQTT.publish.mockClear();
|
|
|
|
zigbeeHerdsman.touchlinkIdentify.mockClear();
|
|
|
|
MQTT.events.message('zigbee2mqtt/bridge/request/touchlink/identify', stringify({ieee_address: '0x1239', channel: 12}));
|
|
|
|
await flushPromises();
|
|
|
|
expect(zigbeeHerdsman.touchlinkIdentify).toHaveBeenCalledTimes(1);
|
|
|
|
expect(zigbeeHerdsman.touchlinkIdentify).toHaveBeenCalledWith('0x1239', 12);
|
|
|
|
expect(MQTT.publish).toHaveBeenCalledWith(
|
|
|
|
'zigbee2mqtt/bridge/response/touchlink/identify',
|
|
|
|
stringify({"data":{"ieee_address":'0x1239',"channel":12},"status":"ok"}),
|
|
|
|
{retain: false, qos: 0}, expect.any(Function)
|
|
|
|
);
|
|
|
|
});
|
|
|
|
|
|
|
|
it('Touchlink identify fails when payload is invalid', async () => {
|
|
|
|
MQTT.publish.mockClear();
|
|
|
|
zigbeeHerdsman.touchlinkIdentify.mockClear();
|
|
|
|
MQTT.events.message('zigbee2mqtt/bridge/request/touchlink/identify', stringify({ieee_address: '0x1239'}));
|
|
|
|
await flushPromises();
|
|
|
|
expect(zigbeeHerdsman.touchlinkIdentify).toHaveBeenCalledTimes(0);
|
|
|
|
expect(MQTT.publish).toHaveBeenCalledWith(
|
|
|
|
'zigbee2mqtt/bridge/response/touchlink/identify',
|
|
|
|
stringify({"data":{},"status":"error","error":"Invalid payload"}),
|
|
|
|
{retain: false, qos: 0}, expect.any(Function)
|
|
|
|
);
|
|
|
|
});
|
|
|
|
|
2020-06-15 10:19:57 -07:00
|
|
|
it('Should allow to touchlink factory reset (fails)', async () => {
|
|
|
|
MQTT.publish.mockClear();
|
2020-09-08 11:24:49 -07:00
|
|
|
zigbeeHerdsman.touchlinkFactoryResetFirst.mockClear();
|
|
|
|
zigbeeHerdsman.touchlinkFactoryResetFirst.mockReturnValueOnce(false);
|
2020-07-13 14:00:33 -07:00
|
|
|
MQTT.events.message('zigbee2mqtt/bridge/request/touchlink/factory_reset', '');
|
2020-06-15 10:19:57 -07:00
|
|
|
await flushPromises();
|
2020-09-08 11:24:49 -07:00
|
|
|
expect(zigbeeHerdsman.touchlinkFactoryResetFirst).toHaveBeenCalledTimes(1);
|
2020-06-15 10:19:57 -07:00
|
|
|
expect(MQTT.publish).toHaveBeenCalledWith(
|
2020-07-13 14:00:33 -07:00
|
|
|
'zigbee2mqtt/bridge/response/touchlink/factory_reset',
|
2020-08-13 11:00:35 -07:00
|
|
|
stringify({"data":{},"status":"error","error":"Failed to factory reset device through Touchlink"}),
|
2020-06-15 10:19:57 -07:00
|
|
|
{retain: false, qos: 0}, expect.any(Function)
|
|
|
|
);
|
|
|
|
});
|
2020-09-08 11:24:49 -07:00
|
|
|
|
|
|
|
it('Should allow to touchlink scan', async () => {
|
|
|
|
MQTT.publish.mockClear();
|
|
|
|
zigbeeHerdsman.touchlinkScan.mockClear();
|
|
|
|
zigbeeHerdsman.touchlinkScan.mockReturnValueOnce([{ieeeAddr: '0x123', channel: 12}, {ieeeAddr: '0x124', channel: 24}]);
|
|
|
|
MQTT.events.message('zigbee2mqtt/bridge/request/touchlink/scan', '');
|
|
|
|
await flushPromises();
|
|
|
|
expect(zigbeeHerdsman.touchlinkScan).toHaveBeenCalledTimes(1);
|
|
|
|
expect(MQTT.publish).toHaveBeenCalledWith(
|
|
|
|
'zigbee2mqtt/bridge/response/touchlink/scan',
|
|
|
|
stringify({"data":{"found":[{ieee_address: '0x123', channel: 12}, {ieee_address: '0x124', channel: 24}]},"status":"ok"}),
|
|
|
|
{retain: false, qos: 0}, expect.any(Function)
|
|
|
|
);
|
|
|
|
});
|
2020-05-24 09:16:39 -07:00
|
|
|
});
|