mirror of
https://github.com/Koenkk/zigbee2mqtt.git
synced 2026-10-23 06:12:23 +00:00
Change device options. #3181
This commit is contained in:
1 parent
7b3c07dcfe
commit
a90ebce677
5 files changed
+97
-16
No files matched your search
+39
-11
@@ -13,11 +13,13 @@ class Bridge extends Extension {
|
||||
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),
|
||||
'device/changeoptions': this.deviceChangeOptions.bind(this),
|
||||
'group/changeoptions': this.groupChangeOptions.bind(this),
|
||||
'group/remove': this.groupRemove.bind(this),
|
||||
'group/rename': this.groupRename.bind(this),
|
||||
'permitjoin': this.permitJoin.bind(this),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -80,6 +82,14 @@ class Bridge extends Extension {
|
||||
* Requests
|
||||
*/
|
||||
|
||||
async deviceChangeOptions(message) {
|
||||
return this.changeEntityOptions('device', message);
|
||||
}
|
||||
|
||||
async groupChangeOptions(message) {
|
||||
return this.changeEntityOptions('group', message);
|
||||
}
|
||||
|
||||
async deviceRemove(message) {
|
||||
return this.removeEntity('device', message);
|
||||
}
|
||||
@@ -107,6 +117,23 @@ class Bridge extends Extension {
|
||||
* Utils
|
||||
*/
|
||||
|
||||
changeEntityOptions(entityType, message) {
|
||||
if (typeof message !== 'object' || !message.hasOwnProperty('ID') || !message.hasOwnProperty('options')) {
|
||||
throw new Error(`Invalid payload`);
|
||||
}
|
||||
|
||||
const ID = message.ID;
|
||||
const entity = this.getEntity(entityType, ID);
|
||||
settings.changeEntityOptions(ID, message.options);
|
||||
const cleanup = (o) => {
|
||||
delete o.friendlyName; delete o.friendly_name; delete o.ID; delete o.type; delete o.devices;
|
||||
return o;
|
||||
}
|
||||
const oldOptions = cleanup(entity.settings);
|
||||
const newOptions = cleanup(settings.getEntity(ID));
|
||||
return utils.getResponse(message, {from: oldOptions, to: newOptions, ID}, null);
|
||||
}
|
||||
|
||||
renameEntity(entityType, message) {
|
||||
const deviceAndHasLast = entityType === 'device' && typeof message === 'object' && message.last === true;
|
||||
if (typeof message !== 'object' || (!message.hasOwnProperty('from') && !deviceAndHasLast) || !message.hasOwnProperty('to')) {
|
||||
@@ -119,11 +146,7 @@ class Bridge extends Extension {
|
||||
|
||||
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`);
|
||||
}
|
||||
|
||||
const entity = this.getEntity(entityType, from);
|
||||
|
||||
settings.changeFriendlyName(from, to);
|
||||
|
||||
@@ -140,10 +163,7 @@ class Bridge extends Extension {
|
||||
|
||||
async removeEntity(entityType, message) {
|
||||
const ID = typeof message === 'object' ? message.ID : message.trim();
|
||||
const entity = this.zigbee.resolveEntity(ID);
|
||||
if (!entity || entity.type !== entityType) {
|
||||
throw new Error(`${utils.capitalize(entityType)} '${ID}' does not exist`);
|
||||
}
|
||||
const entity = this.getEntity(entityType, ID);
|
||||
|
||||
let ban = false;
|
||||
let force = false;
|
||||
@@ -200,6 +220,14 @@ class Bridge extends Extension {
|
||||
}
|
||||
}
|
||||
|
||||
getEntity(type, ID) {
|
||||
const entity = this.zigbee.resolveEntity(ID);
|
||||
if (!entity || entity.type !== type) {
|
||||
throw new Error(`${utils.capitalize(type)} '${ID}' does not exist`);
|
||||
}
|
||||
return entity;
|
||||
}
|
||||
|
||||
async publishInfo() {
|
||||
const payload = {
|
||||
version: this.zigbee2mqttVersion.version,
|
||||
|
||||
@@ -88,7 +88,7 @@ class BridgeLegacy extends Extension {
|
||||
|
||||
const entity = settings.getEntity(json.friendly_name);
|
||||
assert(entity, `Entity '${json.friendly_name}' does not exist`);
|
||||
settings.changeDeviceOptions(entity.ID, json.options);
|
||||
settings.changeEntityOptions(entity.ID, json.options);
|
||||
logger.info(`Changed device specific options of '${json.friendly_name}' (${JSON.stringify(json.options)})`);
|
||||
}
|
||||
|
||||
|
||||
+12
-4
@@ -638,10 +638,18 @@ function removeGroup(groupIDorName) {
|
||||
write();
|
||||
}
|
||||
|
||||
function changeDeviceOptions(IDorName, newOptions) {
|
||||
const device = getDeviceThrowIfNotExists(IDorName);
|
||||
function changeEntityOptions(IDorName, newOptions) {
|
||||
const settings = get();
|
||||
objectAssignDeep(settings.devices[device.ID], newOptions);
|
||||
delete newOptions.friendly_name;
|
||||
delete newOptions.devices;
|
||||
if (getDevice(IDorName)) {
|
||||
objectAssignDeep(settings.devices[getDevice(IDorName).ID], newOptions);
|
||||
} else if (getGroup(IDorName)) {
|
||||
objectAssignDeep(settings.groups[getGroup(IDorName).ID], newOptions);
|
||||
} else {
|
||||
throw new Error(`Device or group '${IDorName}' does not exist`);
|
||||
}
|
||||
|
||||
write();
|
||||
}
|
||||
|
||||
@@ -678,7 +686,7 @@ module.exports = {
|
||||
removeGroup,
|
||||
addDeviceToGroup,
|
||||
removeDeviceFromGroup,
|
||||
changeDeviceOptions,
|
||||
changeEntityOptions,
|
||||
changeFriendlyName,
|
||||
|
||||
// For tests only
|
||||
|
||||
@@ -391,4 +391,41 @@ describe('Bridge', () => {
|
||||
{retain: false, qos: 0}, expect.any(Function)
|
||||
);
|
||||
});
|
||||
|
||||
it('Should allow change device options', async () => {
|
||||
MQTT.publish.mockClear();
|
||||
expect(settings.getDevice('bulb')).toStrictEqual({"ID": "0x000b57fffec6a5b2", "friendlyName": "bulb", "friendly_name": "bulb", "retain": true});
|
||||
MQTT.events.message('zigbee2mqtt/bridge/request/device/changeOptions', JSON.stringify({options: {retain: false, transition: 1}, ID: 'bulb'}));
|
||||
await flushPromises();
|
||||
expect(settings.getDevice('bulb')).toStrictEqual({"ID": "0x000b57fffec6a5b2", "friendlyName": "bulb", "friendly_name": "bulb", "retain": false, "transition": 1});
|
||||
expect(MQTT.publish).toHaveBeenCalledWith(
|
||||
'zigbee2mqtt/bridge/response/device/changeOptions',
|
||||
JSON.stringify({"data":{"from":{"retain": true},"to":{"retain": false,"transition":1}, "ID":"bulb"},"status":"ok"}),
|
||||
{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});
|
||||
MQTT.events.message('zigbee2mqtt/bridge/request/group/changeOptions', JSON.stringify({options: {retain: true, transition: 1}, ID: 'group_1'}));
|
||||
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(
|
||||
'zigbee2mqtt/bridge/response/group/changeOptions',
|
||||
JSON.stringify({"data":{"from":{"optimistic": true,"retain": false},"to":{"optimistic": true,"retain": true,"transition":1}, "ID":"group_1"},"status":"ok"}),
|
||||
{retain: false, qos: 0}, expect.any(Function)
|
||||
);
|
||||
});
|
||||
|
||||
it('Should throw error on invalid device change options payload', async () => {
|
||||
MQTT.publish.mockClear();
|
||||
MQTT.events.message('zigbee2mqtt/bridge/request/device/changeOptions', JSON.stringify({options_: {retain: true, transition: 1}, ID: 'bulb'}));
|
||||
await flushPromises();
|
||||
expect(MQTT.publish).toHaveBeenCalledWith(
|
||||
'zigbee2mqtt/bridge/response/device/changeOptions',
|
||||
JSON.stringify({"data":{},"status":"error","error":"Invalid payload"}),
|
||||
{retain: false, qos: 0}, expect.any(Function)
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -359,6 +359,14 @@ describe('Settings', () => {
|
||||
expect(settings.get().groups).toStrictEqual(expected);
|
||||
});
|
||||
|
||||
it('Should throw error when changing entity options of non-existing device', () => {
|
||||
write(configurationFile, {});
|
||||
|
||||
expect(() => {
|
||||
settings.changeEntityOptions('not_existing_123', {});
|
||||
}).toThrow(new Error("Device or group 'not_existing_123' does not exist"));
|
||||
});
|
||||
|
||||
it('Should not add duplicate groups', () => {
|
||||
write(configurationFile, {});
|
||||
|
||||
|
||||
Reference in new issue
Block a user