DaveL17 wrote:A possibility --> if you're relying on external XML (or JSON), then the plugin could be trying to create the conflicting state dynamically. Look at your source payload to see if there's a node called 'brightnessLevel' (or whatever's conflicting) and trap for that. All you'd need to do is replace 'brightnessLevel' with 'brightnessLevelX' or something similar.
Really like your plugin, it's saved me a bunch of time. Assuming I get over this error, will be in touch about a pull request.
I did think about that, and I've confirmed there's no conflicting state keys. My source JSON is below.
But, this does make me wonder, your plugin overrides the base instance of getDeviceStateList. that method appends additional states to that list. For a Custom device type does this behave differently since there are no native states?
- Code: Select all
def getDeviceStateList(self, dev):
"""
Assign data keys to device state names (Indigo)
The getDeviceStateList() method pulls out all the keys in self.finalDict and
assigns them to device states. It returns the modified stateList which is then
written back to the device in the main thread. This method is automatically
called by
stateListOrDisplayStateIdChanged()
and by Indigo when Triggers and Control Pages are built. Note that it's not
possible to override Indigo's sorting of devices states which will present them
as A, B, a, b.
-----
:param dev:
:return state_list:
"""
# This statement goes out and gets the existing state list for dev from Devices.xml.
# It seems like it's calling itself, but the structure was recommended by Matt:
# https://forums.indigodomo.com/viewtopic.php?f=108&t=12898#p87456
state_list = indigo.PluginBase.getDeviceStateList(self, dev)
# ========================= Custom States as Strings ==========================
if dev.deviceTypeId == 'GhostXMLdevice':
# If there are no managed devices, return the existing states.
if dev.id not in self.managedDevices.keys():
for key in dev.states:
dynamic_state = self.getDeviceStateDictForStringType(unicode(key), unicode(key), unicode(key))
state_list.append(dynamic_state)
# If there are managed devices, return the keys that are in finalDict.
else:
for key in sorted(self.managedDevices[dev.id].finalDict.keys()):
dynamic_state = self.getDeviceStateDictForStringType(unicode(key), unicode(key), unicode(key))
state_list.append(dynamic_state)
# ======================== Custom States as True Type =========================
#
# 2019-12-18 DaveL17 -- Reconfigured to allow for the establishment of other device state types (int, float, bool, etc.)
#
if dev.deviceTypeId == 'GhostXMLdeviceTrue' or dev.deviceTypeId == 'GhostXML_RGBWdevice':
# If there are no managed devices, return the existing states.
if dev.id not in self.managedDevices.keys():
for key in dev.states:
dynamic_state = self.getDeviceStateDictForStringType(unicode(key), unicode(key), unicode(key))
state_list.append(dynamic_state)
# If there are managed devices, return the keys that are in finalDict.
else:
for key in sorted(self.managedDevices[dev.id].finalDict.keys()):
value = self.managedDevices[dev.id].finalDict[key]
try:
# Integers
_ = int(value)
state_list.append(self.getDeviceStateDictForNumberType(unicode(key), unicode(key), unicode(key)))
except (TypeError, ValueError):
try:
# Floats
_ = float(value)
state_list.append(self.getDeviceStateDictForNumberType(unicode(key), unicode(key), unicode(key)))
except (TypeError, ValueError):
try:
# Bools - we create a state for the original data (in string form) and for the boolean representation.
if value.lower() in ('on', 'off', 'open', 'locked', 'up', 'armed', 'closed', 'unlocked', 'down', 'disarmed'):
state_list.append(self.getDeviceStateDictForBoolOnOffType(unicode(key), unicode(key), unicode(key)))
state_list.append(self.getDeviceStateDictForBoolOnOffType(unicode(u"{0}_bool".format(key)), unicode(u"{0}_bool".format(key)), unicode(u"{0}_bool".format(key))))
elif value.lower() in ('yes', 'no'):
state_list.append(self.getDeviceStateDictForBoolYesNoType(unicode(key), unicode(key), unicode(key)))
state_list.append(self.getDeviceStateDictForBoolYesNoType(unicode(u"{0}_bool".format(key)), unicode(u"{0}_bool".format(key)), unicode(u"{0}_bool".format(key))))
elif value.lower() in ('true', 'false'):
state_list.append(self.getDeviceStateDictForBoolTrueFalseType(unicode(key), unicode(key), unicode(key)))
state_list.append(self.getDeviceStateDictForBoolTrueFalseType(unicode(u"{0}_bool".format(key)), unicode(u"{0}_bool".format(key)), unicode(u"{0}_bool".format(key))))
else:
state_list.append(self.getDeviceStateDictForStringType(unicode(key), unicode(key), unicode(key)))
except (AttributeError, TypeError, ValueError):
state_list.append(self.getDeviceStateDictForStringType(unicode(key), unicode(key), unicode(key)))
return state_list
When I restore your plugin's original device types to the Devices.XML, and add a realXML device using the same source URL, I do not get the illegal state key errors. The only static wites to onOffState and brightnessLevel in the plugin.py source is using the updateStateOnServer method. I added these to support the RGB device type.
The source JSON for my device is:
- Code: Select all
{
"active_mode": "none",
"alias": "Server Rack Light",
"ctrl_protocols": {
"name": "Linkie",
"version": "1.0"
},
"description": "Kasa Smart Light Strip, Multicolor",
"dev_state": "normal",
"deviceId": "8012350FA58E795379DA8F6E5BFFACC01D1009B7",
"disco_ver": "1.0",
"err_code": 0,
"hwId": "375D4CCE7C909516CFD57BA93A304404",
"hw_ver": "1.0",
"is_color": 1,
"is_dimmable": 1,
"is_factory": false,
"is_variable_color_temp": 1,
"latitude_i": xxxx,
"length": 16,
"light_state": {
"dft_on_state": {
"brightness": 100,
"color_temp": 0,
"hue": 37,
"mode": "normal",
"saturation": 29
},
"on_off": 0
},
"lighting_effect_state": {
"brightness": 100,
"custom": 0,
"enable": 0,
"id": "bwTatyinOUajKrDwzMmqxxJdnInQUgvM",
"name": "Christmas"
},
"longitude_i": -xxx,
"mic_mac": "xxxx",
"mic_type": "IOT.SMARTBULB",
"model": "KL430(US)",
"oemId": "xxxx",
"preferred_state": [],
"rssi": -51,
"status": "new",
"sw_ver": "1.0.10 Build 200522 Rel.104340"
}