mirror of
https://github.com/Koenkk/zigbee2mqtt.git
synced 2026-08-26 20:50:07 +00:00
Implement renaming functionallity. https://github.com/Koenkk/zigbee2mqtt/issues/3281
This commit is contained in:
@@ -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);
|
||||
|
||||
@@ -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)
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user