Add filtered_cache option (#12988)

* want a way to prevent attribute from being cached

* Update filtered_cache description to be more clear

* Fix emitStateChange was returning wrong payload in to parameter

* Directly get filtered_cache from entity and forgo parameter to State.set()

* Add tests to cover filtered_cache

* Update state.ts

* Update controller.test.js

Co-authored-by: Koen Kanters <koenkanters94@gmail.com>
This commit is contained in:
Jorge Schrauwen
2022-07-02 20:33:04 +02:00
committed by GitHub
co-authored by Koen Kanters
parent 5662e6c29e
commit dedbf336af
5 changed files with 70 additions and 18 deletions
+1 -1
View File
@@ -265,7 +265,7 @@ class Controller {
extension.adjustMessageBeforePublish?.(entity, message);
}
// filter mqtt message attributes
// Filter mqtt message attributes
if (entity.options.filtered_attributes) {
entity.options.filtered_attributes.forEach((a) => delete message[a]);
}
+7 -6
View File
@@ -74,17 +74,18 @@ class State {
set(entity: Group | Device, update: KeyValue, reason: string=null): KeyValue {
const fromState = this.state[entity.ID] || {};
const toState = objectAssignDeep({}, fromState, update);
const result = {...toState};
const newCache = {...toState};
const entityDontCacheProperties = entity.options.filtered_cache || [];
for (const property of Object.keys(toState)) {
if (dontCacheProperties.find((p) => property.match(p))) {
delete toState[property];
for (const property of Object.keys(newCache)) {
if (dontCacheProperties.concat(entityDontCacheProperties).find((p) => property.match(p))) {
delete newCache[property];
}
}
this.state[entity.ID] = toState;
this.state[entity.ID] = newCache;
this.eventBus.emitStateChange({entity, from: fromState, to: toState, reason, update});
return result;
return toState;
}
remove(ID: string | number): void {
+4 -2
View File
@@ -280,11 +280,12 @@ declare global {
retrieve_state?: boolean,
debounce?: number,
debounce_ignore?: string[],
filtered_attributes?: string[],
filtered_cache?: string[],
filtered_optimistic?: string[],
icon?: string,
homeassistant?: KeyValue,
legacy?: boolean,
filtered_attributes?: string[],
friendly_name: string,
description?: string,
qos?: 0 | 1 | 2,
@@ -295,10 +296,11 @@ declare global {
ID?: number,
optimistic?: boolean,
off_state?: 'all_members_off' | 'last_member_state'
filtered_attributes?: string[],
filtered_cache?: string[],
filtered_optimistic?: string[],
retrieve_state?: boolean,
homeassistant?: KeyValue,
filtered_attributes?: string[],
friendly_name: string,
description?: string,
qos?: 0 | 1 | 2,
+18 -9
View File
@@ -812,15 +812,6 @@
"description": "Publish optimistic state after set",
"default": true
},
"filtered_optimistic": {
"type": "array",
"items": {
"type": "string"
},
"examples": ["color_mode", "color_temp", "color"],
"title": "Filtered optimistic attributes",
"description": "Filter attributes from optimistic publish payload when calling /set. (This has no effect if optimistic is set to false)."
},
"filtered_attributes": {
"type": "array",
"items": {
@@ -830,6 +821,24 @@
"title": "Filtered publish attributes",
"description": "Filter attributes from publish payload."
},
"filtered_cache": {
"type": "array",
"items": {
"type": "string"
},
"examples": ["action", "input_actions"],
"title": "Filtered attributes from cache",
"description": "Filter attributes from being added to the cache, this prevents the attribute from being in the published payload when the value didn't change."
},
"filtered_optimistic": {
"type": "array",
"items": {
"type": "string"
},
"examples": ["color_mode", "color_temp", "color"],
"title": "Filtered optimistic attributes",
"description": "Filter attributes from optimistic publish payload when calling /set. (This has no effect if optimistic is set to false)."
},
"icon": {
"type": "string",
"title": "Icon",
+40
View File
@@ -490,6 +490,46 @@ describe('Controller', () => {
expect(MQTT.publish).toHaveBeenCalledWith("zigbee2mqtt/bulb", stringify({"state":"ON","brightness":200}), {"qos": 0, "retain": true}, expect.any(Function));
});
it('Publish entity state attribute_json output filtered cache', async () => {
await controller.start();
settings.set(['advanced', 'output'], 'attribute_and_json');
settings.set(['devices', zigbeeHerdsman.devices.bulb.ieeeAddr, 'filtered_cache'], ['linkquality']);
MQTT.publish.mockClear();
const device = controller.zigbee.resolveEntity('bulb');
expect(controller.state.state[device.ieeeAddr]).toStrictEqual({"brightness":50,"color_temp":370,"linkquality":99,"state":"ON"});
await controller.publishEntityState(device, {state: 'ON', brightness: 200, color_temp: 370, linkquality: 87});
await flushPromises();
expect(controller.state.state[device.ieeeAddr]).toStrictEqual({"brightness":200,"color_temp":370,"state":"ON"});
expect(MQTT.publish).toHaveBeenCalledTimes(5);
expect(MQTT.publish).toHaveBeenCalledWith("zigbee2mqtt/bulb/state", "ON", {"qos": 0, "retain": true}, expect.any(Function));
expect(MQTT.publish).toHaveBeenCalledWith("zigbee2mqtt/bulb/brightness", "200", {"qos": 0, "retain": true}, expect.any(Function));
expect(MQTT.publish).toHaveBeenCalledWith("zigbee2mqtt/bulb/linkquality", "87", {"qos": 0, "retain": true}, expect.any(Function));
expect(MQTT.publish).toHaveBeenCalledWith("zigbee2mqtt/bulb", stringify({"state":"ON","brightness":200,"color_temp":370,"linkquality":87}), {"qos": 0, "retain": true}, expect.any(Function));
});
it('Publish entity state attribute_json output filtered cache (device_options)', async () => {
await controller.start();
settings.set(['advanced', 'output'], 'attribute_and_json');
settings.set(['device_options', 'filtered_cache'], ['linkquality']);
MQTT.publish.mockClear();
const device = controller.zigbee.resolveEntity('bulb');
expect(controller.state.state[device.ieeeAddr]).toStrictEqual({"brightness":50,"color_temp":370,"linkquality":99,"state":"ON"});
await controller.publishEntityState(device, {state: 'ON', brightness: 200, color_temp: 370, linkquality: 87});
await flushPromises();
expect(controller.state.state[device.ieeeAddr]).toStrictEqual({"brightness":200,"color_temp":370,"state":"ON"});
expect(MQTT.publish).toHaveBeenCalledTimes(5);
expect(MQTT.publish).toHaveBeenCalledWith("zigbee2mqtt/bulb/state", "ON", {"qos": 0, "retain": true}, expect.any(Function));
expect(MQTT.publish).toHaveBeenCalledWith("zigbee2mqtt/bulb/brightness", "200", {"qos": 0, "retain": true}, expect.any(Function));
expect(MQTT.publish).toHaveBeenCalledWith("zigbee2mqtt/bulb/linkquality", "87", {"qos": 0, "retain": true}, expect.any(Function));
expect(MQTT.publish).toHaveBeenCalledWith("zigbee2mqtt/bulb", stringify({"state":"ON","brightness":200,"color_temp":370,"linkquality":87}), {"qos": 0, "retain": true}, expect.any(Function));
});
it('Publish entity state with device information', async () => {
await controller.start();
settings.set(['mqtt', 'include_device_information'], true);