Virtual IR devices are not handled
markszabo opened this issue · comments
SwitchBot Hubs can send IR commands to home appliances, and one can add these home appliances as virtual devices in the app (ref). The API response is significantly different for these devices (sometimes called remotes
in the API), e.g. they don't have MAC addresses, which currently breaks this library, e.g.:
>>> switchbot.devices()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/usr/local/lib/python3.9/site-packages/switchbot/__init__.py", line 521, in devices
devices.append(self.device(id))
File "/usr/local/lib/python3.9/site-packages/switchbot/__init__.py", line 513, in device
return Device.factory(self, id)
File "/usr/local/lib/python3.9/site-packages/switchbot/__init__.py", line 85, in factory
device, _ = Device._query_device(switchbot, sanitize_id(id))
File "/usr/local/lib/python3.9/site-packages/switchbot/__init__.py", line 80, in _query_device
raise KeyError
KeyError
The reason is that the switchbot._api("get", "get_devices")
call in https://github.com/jonghwanhyeon/python-switchbot/blob/main/switchbot/__init__.py#L74 returns something similar (I have onne SwitchBot bot, one Hub Mini and one AC added:
{ 'deviceList': [ { 'ble_version': 17,
'cloudServiceAble': False,
'device_detail': { 'device_type': 'WoLinkMini',
'ip': '192.168.2.1',
'isEncrypted': False,
'model': '0',
'parent_device': '00:00:00:00:00:00',
'pubtopic': 'switchlink/123456789ABCDEF0/app_to_link',
'remote': 'on',
'subtopic': 'switchlink/123456789ABCDEF0/link_to_app',
'support_cmd': [],
'update_time': '1610279471',
'version': '1.0.0',
'wifi_mac': '123456789ABC'},
'device_mac': '123456789ABC',
'device_name': 'Switch Bot Hub',
'hardware_version': 4,
'isLinkage': True,
'json_version': 1,
'netConfigAble': False,
'platforms': [],
'userID': '12345678-1234-1234-1234-123456789ABC',
'user_name': 'user@example.com',
'wifi_version': 19},
{ 'ble_version': 0,
'cloudServiceAble': True,
'device_detail': { 'device_type': 'WoHand',
'ip': '192.168.2.1',
'isEncrypted': False,
'model': '0',
'parent_device': '12:34:56:78:9A:BC',
'pubtopic': 'switchlink/123456789ABCDEF0/app_to_link',
'remote': 'on',
'subtopic': 'switchlink/123456789ABCDEF0/link_to_app',
'support_cmd': [ { 'key': 'press',
'value': 'ACTH '
'SY '
'123456789ABCDEF0'}],
'update_time': '1610280920',
'version': '1.0.0',
'wifi_mac': 'FFFFFFFFFFFF'},
'device_mac': '123456789ABC',
'device_name': 'My Switch',
'hardware_version': 0,
'isLinkage': True,
'json_version': 1,
'netConfigAble': False,
'platforms': ['IFTTT', 'GoogleHome', 'Alexa'],
'userID': '12345678-1234-1234-1234-123456789ABC',
'user_name': 'user@example.com',
'wifi_version': 0}],
'remoteList': [ { 'acType': '1',
'brandEn': 'Toshiba AC',
'code': 'H4sIAAAAAAAAALVdO5IcxxGFs32DvQcMnY<...>IWna6IIfCFvz5Clb+DsHfWj9rUfwCsxqSGj3MAAA==',
'codeType': '3',
'createTime': '202101102102',
'dbVersion': 20,
'hxdIndex': '160672467',
'keyDetail': [],
'keyMap': 'off:0:134;16,cool,autowin,on:134:192;1<...>,1,on:29009:191;-100,wind,2,on:29200:192;-100,wind,3,on:29392:191',
'parentHubMac': '123456789ABC',
'priority': '1161600',
'remoteID': '01-202101102100-12345678',
'remoteName': 'Air Conditioner',
'serial': 12345,
'sn': '123456789ABCDEF0',
'type': 1,
'userID': '12345678-1234-1234-1234-123456789ABC',
'user_name': 'user@example.com',
'zipCodeVersion': 12}],
'sceneList': [],
'userInfo': { 'openApiToken': { 'exp': 0,
'iat': 0,
'token': '',
'version': 1},
'sortInfo': { 'array': [ '12:34:56:78:9A:BC',
'01-202101102100-12345678',
'123456789ABC'],
'timeStamp': 1610280440600},
'targetVersion': { 'WoLinkMiniBleVersion': 17,
'WoLinkMiniWifiVersion': 19},
'temperatureUnitC': True,
'userName': 'user@email.com'}}
and when _query_device(cls, switchbot, id)
is called with the id 01-202101102100-12345678
it is not found (as it's not in deviceList
but in remoteList
and also not device_mac
rather remoteID
). So extending _query_device(cls, switchbot, id)
to something like this would solve this:
@classmethod
def _query_device(cls, switchbot, id):
data, updated = switchbot._api("get", "get_devices")
for device in data["deviceList"]:
if device["device_mac"] == id:
return device, updated
for remote in data["remoteList"]:
if remote["remoteID"] == id:
return remote, updated
raise KeyError
However the problem is that the properties are so different for a remote than an actual device, that this just makes the code break in https://github.com/jonghwanhyeon/python-switchbot/blob/main/switchbot/__init__.py#L87 I think the solution would be to create a new type for remote devices, but the official doc (see #7) has a different API response so I think this should only happen after the move to the other API.
This is something I might end up implementing, but wanted to get your opinion on it first.
@markszabo I also needed this for myself, so I created a PR. I'm not sure what the issue you had back then, but it works for me to turn on/off fans and my "Other" remotes.
Close this issue thanks to PR #12 by @ekawatani