Actually, plugins and script can delete any device (or variable), not just ones they created or define. As to if this should be the case is probably something we need to evaluate some more. But currently both plugins and scripts have the ability to add and delete devices (and variables).
As Jay mentioned, device deletion can be useful for factory created devices, where multiple devices are created as part of a discovery/sync process. For example, if you are syncing with a Z-Wave node to create a new device in Indigo and it is a multi-sensor then multiple devices are created (Motion, Temperature, Humidity and Lux ). If that device then needs to be replaced in the future but it gets replaced with a different type of multi-sensor (or maybe just a sensor) then when the user re-performs the sync they may end up with more or fewer devices in that group. So for example, the new device might just have Motion and Temperature, so the Humidity and Lux devices now need to be removed from the group (and from Indigo's database).
One issue with device deletion is that if the device is used as the primary component of a Trigger then upon deletion that Trigger will also be deleted. So there can be a cascading effect of objects that end up getting removed. Our internal Z-Wave plugin handles this by first pre-flighting the delete with a indigo.device.getDependencies() call. So in the example, above where the user is re-syncing a module and it now has fewer devices needed (because it is a different model), Indigo first checks to see if it can cleanly delete the devices without it impacting other database elements (Triggers, Control Pages, Conditionals). If it can then it deletes those devices. If it cannot then it logs an error to the Event Log and tells the user to manually edit/remove the dependent objects first. Here is some example code snippets of what it does:
- Code: Select all
dev_id_list = [] # this contains a list of the device IDs for a device group
if old_num_devs_in_group > new_num_devs_in_group:
# The current number of devices assigned to this node is greater than
# what it will be after the sync. The user must have swapped modules
# with a different module type. We can only allow this if the extra
# devices that will be deleted are not currently being used in any
# actions, triggers, etc. Here we make sure that is the case, and abort
# if it isn't in which case the user will have to manually fix/remove
# the device dependencies first (ideally we would offer to manually
# fix those dependencies by having them point to a placeholder/nil
# device). We don't want to delete the devices automatically in this
# case since it will potentially alter logic.
dependent_dev_names = []
for dev_id in dev_id_list[(new_num_devs_in_group - old_num_devs_in_group):]:
dependencies = indigo.device.getDependencies(dev_id)
if len(dependencies['actionGroups']) > 0 or len(dependencies['controlPages']) > 0 \
or len(dependencies['schedules']) > 0 or len(dependencies['triggers']) > 0 \
or len(dependencies['variables']) > 0:
dependent_dev_names.append(indigo.devices.getName(dev_id))
if len(dependent_dev_names) > 0:
# User is going to have to remove dependencies before we can proceed.
dev_name = ', '.join(["%s" % name for name in dependent_dev_names])
errdesc = u"Devices \"%s\" are currently referenced by other objects, so the sync cannot be performed. Right-click on the devices in the main window and choose the Show Dependencies menu to find the references, then modify those objects to not reference the device and re-sync this device again." % (dev_name)
self.logError(errdesc)
return (False, errdesc)
for dev_id in dev_id_list[(new_num_devs_in_group - old_num_devs_in_group):]:
del_dev_name = indigo.devices.getName(dev_id)
indigo.device.delete(dev_id)
self.logInfo(u"deleted unused device \"%s\" " % (del_dev_name), forceSyncPrefix=True)
dev_id_list = dev_id_list[:new_num_devs_in_group]
old_num_devs_in_group = new_num_devs_in_group # dev_id_list size now matches node's sub model list size.
So in summary, if a plugin is going to delete a device it should definitely use the indigo.device.getDependencies() call first to make sure the result will only be that device being deleted. We should have made the delete call default to throwing an exception if there are dependencies, unless an override parameter is set. We might add that in the future, but it gets complicated since we don't want to break any legacy logic/plugins. Although it should be pretty rare that a plugin really will want both the device and its dependencies removed.