This commit is contained in:
Koen Kanters
2020-06-13 19:35:09 +02:00
parent 16f5812f6c
commit 7b3c07dcfe
2 changed files with 122 additions and 0 deletions
+46
View File
@@ -10,11 +10,14 @@ class Bridge extends Extension {
constructor(zigbee, mqtt, state, publishEntityState, eventBus) {
super(zigbee, mqtt, state, publishEntityState, eventBus);
this.lastJoinedDeviceIeeeAddr = null;
this.requestLookup = {
'permitjoin': this.permitJoin.bind(this),
'device/remove': this.deviceRemove.bind(this),
'group/remove': this.groupRemove.bind(this),
'device/rename': this.deviceRename.bind(this),
'group/rename': this.groupRename.bind(this),
};
}
@@ -44,6 +47,10 @@ class Bridge extends Extension {
}
async onZigbeeEvent(type, data, resolvedEntity) {
if (type === 'deviceJoined' && resolvedEntity) {
this.lastJoinedDeviceIeeeAddr = resolvedEntity.device.ieeeAddr;
}
if (['deviceJoined', 'deviceLeave', 'deviceInterview'].includes(type)) {
let payload;
const ieeeAddress = data.device ? data.device.ieeeAddr : data.ieeeAddr;
@@ -81,6 +88,14 @@ class Bridge extends Extension {
return this.removeEntity('group', message);
}
async deviceRename(message) {
return this.renameEntity('device', message);
}
async groupRename(message) {
return this.renameEntity('group', message);
}
async permitJoin(message) {
const value = typeof message === 'object' ? message.value : message;
await this.zigbee.permitJoin(value);
@@ -92,6 +107,37 @@ class Bridge extends Extension {
* Utils
*/
renameEntity(entityType, message) {
const deviceAndHasLast = entityType === 'device' && typeof message === 'object' && message.last === true;
if (typeof message !== 'object' || (!message.hasOwnProperty('from') && !deviceAndHasLast) || !message.hasOwnProperty('to')) {
throw new Error(`Invalid payload`);
}
if (deviceAndHasLast && !this.lastJoinedDeviceIeeeAddr) {
throw new Error('No device has joined since start')
}
const from = deviceAndHasLast ? this.lastJoinedDeviceIeeeAddr : message.from;
const to = message.to;
const entity = this.zigbee.resolveEntity(from);
if (!entity || entity.type !== entityType) {
throw new Error(`${utils.capitalize(entityType)} '${from}' does not exist`);
}
settings.changeFriendlyName(from, to);
if (entity.type === 'device') {
this.publishDevices();
this.eventBus.emit(`deviceRenamed`, {device: entity.device});
} else {
this.publishGroups();
this.eventBus.emit(`groupRenamed`, {group: entity.group});
}
return utils.getResponse(message, {from: entity.settings.friendlyName, to}, null);
}
async removeEntity(entityType, message) {
const ID = typeof message === 'object' ? message.ID : message.trim();
const entity = this.zigbee.resolveEntity(ID);
+76
View File
@@ -315,4 +315,80 @@ describe('Bridge', () => {
{retain: false, qos: 0}, expect.any(Function)
);
});
it('Should allow rename device', async () => {
MQTT.publish.mockClear();
MQTT.events.message('zigbee2mqtt/bridge/request/device/rename', JSON.stringify({from: 'bulb', to: 'bulb_new_name'}));
await flushPromises();
expect(settings.getDevice('bulb')).toBeNull();
expect(settings.getDevice('bulb_new_name')).toStrictEqual({"ID": "0x000b57fffec6a5b2", "friendlyName": "bulb_new_name", "friendly_name": "bulb_new_name", "retain": true});
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',
JSON.stringify({"data":{"from":"bulb","to":"bulb_new_name"},"status":"ok"}),
{retain: false, qos: 0}, expect.any(Function)
);
});
it('Should allow rename group', async () => {
MQTT.publish.mockClear();
MQTT.events.message('zigbee2mqtt/bridge/request/group/rename', JSON.stringify({from: 'group_1', to: 'group_new_name'}));
await flushPromises();
expect(settings.getGroup('group_1')).toBeNull();
expect(settings.getGroup('group_new_name')).toStrictEqual({"ID": 1, "devices": [], "friendlyName": "group_new_name", "friendly_name": "group_new_name", "optimistic": true, "retain": false});
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',
JSON.stringify({"data":{"from":"group_1","to":"group_new_name"},"status":"ok"}),
{retain: false, qos: 0}, expect.any(Function)
);
});
it('Should throw error on invalid device rename payload', async () => {
MQTT.publish.mockClear();
MQTT.events.message('zigbee2mqtt/bridge/request/device/rename', JSON.stringify({from_bla: 'bulb', to: 'bulb_new_name'}));
await flushPromises();
expect(MQTT.publish).toHaveBeenCalledWith(
'zigbee2mqtt/bridge/response/device/rename',
JSON.stringify({"data":{},"status":"error","error":"Invalid payload"}),
{retain: false, qos: 0}, expect.any(Function)
);
});
it('Should throw error on non-existing device rename', async () => {
MQTT.publish.mockClear();
MQTT.events.message('zigbee2mqtt/bridge/request/device/rename', JSON.stringify({from: 'bulb_not_existing', to: 'bulb_new_name'}));
await flushPromises();
expect(MQTT.publish).toHaveBeenCalledWith(
'zigbee2mqtt/bridge/response/device/rename',
JSON.stringify({"data":{},"status":"error","error":"Device 'bulb_not_existing' does not exist"}),
{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});
MQTT.events.message('zigbee2mqtt/bridge/request/device/rename', JSON.stringify({last: true, to: 'bulb_new_name'}));
await flushPromises();
expect(settings.getDevice('bulb')).toBeNull();
expect(settings.getDevice('bulb_new_name')).toStrictEqual({"ID": "0x000b57fffec6a5b2", "friendlyName": "bulb_new_name", "friendly_name": "bulb_new_name", "retain": true});
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',
JSON.stringify({"data":{"from":"bulb","to":"bulb_new_name"},"status":"ok"}),
{retain: false, qos: 0}, expect.any(Function)
);
});
it('Should throw error when renaming last joined device but none has joined', async () => {
MQTT.publish.mockClear();
MQTT.events.message('zigbee2mqtt/bridge/request/device/rename', JSON.stringify({last: true, to: 'bulb_new_name'}));
await flushPromises();
expect(MQTT.publish).toHaveBeenCalledWith(
'zigbee2mqtt/bridge/response/device/rename',
JSON.stringify({"data":{},"status":"error","error":"No device has joined since start"}),
{retain: false, qos: 0}, expect.any(Function)
);
});
});