Clear outdated availability topics. #5414

This commit is contained in:
Koen Kanters 2020-12-28 20:19:00 +01:00
parent 2385cf62e5
commit 9b7ecff5d0
4 changed files with 50 additions and 4 deletions

View File

@ -3,6 +3,7 @@ const settings = require('../util/settings');
const utils = require('../util/utils');
const zigbeeHerdsmanConverters = require('zigbee-herdsman-converters');
const Extension = require('./extension');
const topicRegex = new RegExp(`^${settings.get().mqtt.base_topic}/(.*)/availability`);
// Pingable end devices, some end devices should be pinged
// e.g. E11-G13 https://github.com/Koenkk/zigbee2mqtt/issues/775#issuecomment-453683846
@ -25,6 +26,7 @@ class Availability extends Extension {
this.state = {};
this.eventBus.on('deviceRemoved', (data) => this.onDeviceRemoved(data.resolvedEntity), this.constructor.name);
this.eventBus.on('deviceRenamed', (data) => this.onDeviceRenamed(data), this.constructor.name);
this.blocklist = settings.get().advanced.availability_blocklist
.concat(settings.get().advanced.availability_blacklist)
@ -35,6 +37,10 @@ class Availability extends Extension {
.map((e) => settings.getEntity(e).ID);
}
onDeviceRenamed(data) {
this.mqtt.publish(`${data.from}/availability`, null, {retain: true, qos: 0});
}
onDeviceRemoved(resolvedEntity) {
this.mqtt.publish(`${resolvedEntity.name}/availability`, null, {retain: true, qos: 0});
delete this.state[resolvedEntity.device.ieeeAddr];
@ -86,6 +92,14 @@ class Availability extends Extension {
}
}
async onMQTTMessage(topic, message) {
// Clear topics for non-existing devices
const match = topic.match(topicRegex);
if (match && (!this.zigbee.resolveEntity(match[1]) || this.zigbee.resolveEntity(match[1]).name !== match[1])) {
this.mqtt.publish(`${match[1]}/availability`, null, {retain: true, qos: 0});
}
}
async handleIntervalPingable(device) {
// When a device is already unavailable, log the ping failed on 'debug' instead of 'error'.
const resolvedEntity = this.zigbee.resolveEntity(device.ieeeAddr);

View File

@ -337,12 +337,13 @@ class Bridge extends Extension {
// Clear retained messages
this.mqtt.publish(entity.name, '', {retain: true});
const oldFriendlyName = entity.settings.friendlyName;
if (entity.type === 'device') {
this.publishDevices();
this.eventBus.emit(`deviceRenamed`, {device: entity.device, homeAssisantRename});
this.eventBus.emit(`deviceRenamed`, {device: entity.device, homeAssisantRename, from: oldFriendlyName, to});
} else {
this.publishGroups();
this.eventBus.emit(`groupRenamed`, {group: entity.group, homeAssisantRename});
this.eventBus.emit(`groupRenamed`, {group: entity.group, homeAssisantRename, from: oldFriendlyName, to});
}
// Repulish entity state
@ -350,7 +351,7 @@ class Bridge extends Extension {
return utils.getResponse(
message,
{from: entity.settings.friendlyName, to, homeassistant_rename: homeAssisantRename},
{from: oldFriendlyName, to, homeassistant_rename: homeAssisantRename},
null,
);
}

View File

@ -231,7 +231,7 @@ class BridgeLegacy extends Extension {
settings.changeFriendlyName(from, to);
logger.info(`Successfully renamed - ${from} to ${to} `);
const entity = this.zigbee.resolveEntity(to);
const eventData = isGroup ? {group: entity.group} : {device: entity.device};
const eventData = isGroup ? {group: entity.group} : {device: entity.device, from, to};
eventData.homeAssisantRename = false;
this.eventBus.emit(`${isGroup ? 'group' : 'device'}Renamed`, eventData);

View File

@ -1,5 +1,6 @@
const data = require('./stub/data');
const logger = require('./stub/logger');
const stringify = require('json-stable-stringify-without-jsonify');
const zigbeeHerdsman = require('./stub/zigbeeHerdsman');
zigbeeHerdsman.returnDevices.push('0x000b57fffec6a5b3');
zigbeeHerdsman.returnDevices.push('0x00124b00120144ae');
@ -408,4 +409,34 @@ describe('Availability', () => {
{retain: true, qos: 0}, expect.any(Function)
);
});
it('Should clear retained availability topic when device is renamed', async () => {
MQTT.publish.mockClear();
MQTT.events.message('zigbee2mqtt/bridge/request/device/rename', stringify({"from": "bulb_color", "to": "bulb_color_new_name"}));
await flushPromises();
expect(MQTT.publish).toHaveBeenCalledWith(
'zigbee2mqtt/bulb_color/availability',
null,
{retain: true, qos: 0}, expect.any(Function)
);
});
it('Should clear retained availability topic when device does not exist', async () => {
MQTT.publish.mockClear();
MQTT.events.message('zigbee2mqtt/not_existing_hahaha/availability', 'offline');
await flushPromises();
expect(MQTT.publish).toHaveBeenCalledTimes(1);
expect(MQTT.publish).toHaveBeenCalledWith('zigbee2mqtt/not_existing_hahaha/availability', null, {retain: true, qos: 0}, expect.any(Function));
MQTT.publish.mockClear();
MQTT.events.message('zigbee2mqtt/0x000b57fffec6a5b3/availability', 'offline');
await flushPromises();
expect(MQTT.publish).toHaveBeenCalledTimes(1);
expect(MQTT.publish).toHaveBeenCalledWith('zigbee2mqtt/0x000b57fffec6a5b3/availability', null, {retain: true, qos: 0}, expect.any(Function));
MQTT.publish.mockClear();
MQTT.events.message('zigbee2mqtt/bulb_color/availability', 'offline');
await flushPromises();
expect(MQTT.publish).toHaveBeenCalledTimes(0);
});
});