diff --git a/lib/controller.js b/lib/controller.js index 50648916e..f9cb586ce 100644 --- a/lib/controller.js +++ b/lib/controller.js @@ -328,6 +328,40 @@ class Controller { }); this.mqtt.log('devices', devices); + } else if (option === 'remove') { + message = message.toString(); + const IDByFriendlyName = settings.getIDByFriendlyName(message); + const deviceID = IDByFriendlyName ? IDByFriendlyName : message; + const device = this.zigbee.getDevice(deviceID); + + const cleanup = () => { + // Clear Home Assistant MQTT discovery message + if (settings.get().homeassistant && device) { + const mappedModel = zigbeeShepherdConverters.findByZigbeeModel(device.modelId); + if (mappedModel) { + homeassistant.clear(deviceID, mappedModel.model, this.mqtt); + } + } + + // Remove from configuration.yaml + settings.removeDevice(deviceID); + + logger.info(`Successfully removed ${deviceID}`); + this.mqtt.log('device_removed', message); + }; + + // Remove from zigbee network. + if (device) { + this.zigbee.removeDevice(deviceID, (error) => { + if (!error) { + cleanup(); + } else { + logger.error(`Failed to remove ${deviceID}`); + } + }); + } else { + cleanup(); + } } else { logger.warn(`Cannot handle MQTT config option '${option}' with message '${message}'`); } @@ -338,9 +372,7 @@ class Controller { const topicPrefix = withPrefix ? topic.split('/')[2] : ''; // Map friendlyName to deviceID. - const deviceID = Object.keys(settings.get().devices).find((id) => - settings.getDevice(id).friendly_name === friendlyName - ); + const deviceID = settings.getIDByFriendlyName(friendlyName); if (!deviceID) { logger.error(`Cannot handle '${topic}' because deviceID of '${friendlyName}' cannot be found`); diff --git a/lib/homeassistant.js b/lib/homeassistant.js index 47553e60d..9c9916559 100644 --- a/lib/homeassistant.js +++ b/lib/homeassistant.js @@ -296,7 +296,23 @@ function discover(deviceID, model, mqtt) { discovered[deviceID] = true; } +function clear(deviceID, model, mqtt) { + // Check if there are configs. + if (!mapping[model]) { + return; + } + + mapping[model].forEach((config) => { + const topic = `${config.type}/${deviceID}/${config.object_id}/config`; + const payload = ''; + mqtt.publish(topic, payload, {retain: true, qos: 0}, null, 'homeassistant'); + }); + + discovered[deviceID] = false; +} + module.exports = { mapping: mapping, discover: (deviceID, model, mqtt) => discover(deviceID, model, mqtt), + clear: (deviceID, model, mqtt) => clear(deviceID, model, mqtt), }; diff --git a/lib/util/settings.js b/lib/util/settings.js index d642381a5..c29e34dc4 100644 --- a/lib/util/settings.js +++ b/lib/util/settings.js @@ -27,9 +27,28 @@ function addDevice(id) { writeRead(); } +function removeDevice(id) { + if (settings.devices && settings.devices[id]) { + delete settings.devices[id]; + writeRead(); + } +} + +function getIDByFriendlyName(friendlyName) { + if (!settings.devices) { + return null; + } + + return Object.keys(settings.devices).find((id) => + settings.devices[id].friendly_name === friendlyName + ); +} + module.exports = { get: () => settings, write: () => write(), - getDevice: (id) => settings.devices ? settings.devices[id] : false, + getDevice: (id) => settings.devices ? settings.devices[id] : null, addDevice: (id) => addDevice(id), + removeDevice: (id) => removeDevice(id), + getIDByFriendlyName: (friendlyName) => getIDByFriendlyName(friendlyName), }; diff --git a/lib/zigbee.js b/lib/zigbee.js index d71213298..49cda420a 100644 --- a/lib/zigbee.js +++ b/lib/zigbee.js @@ -109,6 +109,28 @@ class Zigbee { return this.shepherd.list().filter((device) => device.type !== 'Coordinator'); } + removeDevice(deviceID, callback) { + this.shepherd.remove(deviceID, (error) => { + if (error) { + logger.warn(`Failed to remove '${deviceID}', trying force remove...`); + this._forceRemove(deviceID, callback); + } else { + callback(null); + } + }); + } + + _forceRemove(deviceID, callback) { + const device = this.shepherd._findDevByAddr(deviceID); + + if (device) { + return this.shepherd._unregisterDev(device, (error) => callback(error)); + } else { + logger.warn(`Could not find ${deviceID} for force removal`); + callback(true); + } + } + ping(deviceID) { const device = this.shepherd._findDevByAddr(deviceID);