forked from platypush/platypush
Proper support for native entities in zigbee.mqtt integration
This commit is contained in:
parent
db4ad5825e
commit
be4d1e8e01
2 changed files with 168 additions and 61 deletions
|
@ -1,21 +1,38 @@
|
||||||
|
import contextlib
|
||||||
import json
|
import json
|
||||||
from typing import Optional
|
from typing import Optional, Mapping
|
||||||
|
|
||||||
from platypush.backend.mqtt import MqttBackend
|
from platypush.backend.mqtt import MqttBackend
|
||||||
from platypush.context import get_plugin
|
from platypush.context import get_plugin
|
||||||
from platypush.message.event.zigbee.mqtt import ZigbeeMqttOnlineEvent, ZigbeeMqttOfflineEvent, \
|
from platypush.message.event.zigbee.mqtt import (
|
||||||
ZigbeeMqttDevicePropertySetEvent, ZigbeeMqttDevicePairingEvent, ZigbeeMqttDeviceConnectedEvent, \
|
ZigbeeMqttOnlineEvent,
|
||||||
ZigbeeMqttDeviceBannedEvent, ZigbeeMqttDeviceRemovedEvent, ZigbeeMqttDeviceRemovedFailedEvent, \
|
ZigbeeMqttOfflineEvent,
|
||||||
ZigbeeMqttDeviceWhitelistedEvent, ZigbeeMqttDeviceRenamedEvent, ZigbeeMqttDeviceBindEvent, \
|
ZigbeeMqttDevicePropertySetEvent,
|
||||||
ZigbeeMqttDeviceUnbindEvent, ZigbeeMqttGroupAddedEvent, ZigbeeMqttGroupAddedFailedEvent, \
|
ZigbeeMqttDevicePairingEvent,
|
||||||
ZigbeeMqttGroupRemovedEvent, ZigbeeMqttGroupRemovedFailedEvent, ZigbeeMqttGroupRemoveAllEvent, \
|
ZigbeeMqttDeviceConnectedEvent,
|
||||||
ZigbeeMqttGroupRemoveAllFailedEvent, ZigbeeMqttErrorEvent
|
ZigbeeMqttDeviceBannedEvent,
|
||||||
|
ZigbeeMqttDeviceRemovedEvent,
|
||||||
|
ZigbeeMqttDeviceRemovedFailedEvent,
|
||||||
|
ZigbeeMqttDeviceWhitelistedEvent,
|
||||||
|
ZigbeeMqttDeviceRenamedEvent,
|
||||||
|
ZigbeeMqttDeviceBindEvent,
|
||||||
|
ZigbeeMqttDeviceUnbindEvent,
|
||||||
|
ZigbeeMqttGroupAddedEvent,
|
||||||
|
ZigbeeMqttGroupAddedFailedEvent,
|
||||||
|
ZigbeeMqttGroupRemovedEvent,
|
||||||
|
ZigbeeMqttGroupRemovedFailedEvent,
|
||||||
|
ZigbeeMqttGroupRemoveAllEvent,
|
||||||
|
ZigbeeMqttGroupRemoveAllFailedEvent,
|
||||||
|
ZigbeeMqttErrorEvent,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class ZigbeeMqttBackend(MqttBackend):
|
class ZigbeeMqttBackend(MqttBackend):
|
||||||
"""
|
"""
|
||||||
Listen for events on a zigbee2mqtt service.
|
Listen for events on a zigbee2mqtt service.
|
||||||
|
|
||||||
|
For historical reasons, this backend should be enabled together with the `zigbee.mqtt` plugin.
|
||||||
|
|
||||||
Triggers:
|
Triggers:
|
||||||
|
|
||||||
* :class:`platypush.message.event.zigbee.mqtt.ZigbeeMqttOnlineEvent` when the service comes online.
|
* :class:`platypush.message.event.zigbee.mqtt.ZigbeeMqttOnlineEvent` when the service comes online.
|
||||||
|
@ -59,11 +76,22 @@ class ZigbeeMqttBackend(MqttBackend):
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, host: Optional[str] = None, port: Optional[int] = None, base_topic='zigbee2mqtt',
|
def __init__(
|
||||||
tls_cafile: Optional[str] = None, tls_certfile: Optional[str] = None,
|
self,
|
||||||
tls_keyfile: Optional[str] = None, tls_version: Optional[str] = None,
|
host: Optional[str] = None,
|
||||||
tls_ciphers: Optional[str] = None, username: Optional[str] = None,
|
port: Optional[int] = None,
|
||||||
password: Optional[str] = None, client_id: Optional[str] = None, *args, **kwargs):
|
base_topic='zigbee2mqtt',
|
||||||
|
tls_cafile: Optional[str] = None,
|
||||||
|
tls_certfile: Optional[str] = None,
|
||||||
|
tls_keyfile: Optional[str] = None,
|
||||||
|
tls_version: Optional[str] = None,
|
||||||
|
tls_ciphers: Optional[str] = None,
|
||||||
|
username: Optional[str] = None,
|
||||||
|
password: Optional[str] = None,
|
||||||
|
client_id: Optional[str] = None,
|
||||||
|
*args,
|
||||||
|
**kwargs
|
||||||
|
):
|
||||||
"""
|
"""
|
||||||
:param host: MQTT broker host (default: host configured on the ``zigbee.mqtt`` plugin).
|
:param host: MQTT broker host (default: host configured on the ``zigbee.mqtt`` plugin).
|
||||||
:param port: MQTT broker port (default: 1883).
|
:param port: MQTT broker port (default: 1883).
|
||||||
|
@ -87,6 +115,7 @@ class ZigbeeMqttBackend(MqttBackend):
|
||||||
plugin = get_plugin('zigbee.mqtt')
|
plugin = get_plugin('zigbee.mqtt')
|
||||||
self.base_topic = base_topic or plugin.base_topic
|
self.base_topic = base_topic or plugin.base_topic
|
||||||
self._devices = {}
|
self._devices = {}
|
||||||
|
self._devices_info = {}
|
||||||
self._groups = {}
|
self._groups = {}
|
||||||
self._last_state = None
|
self._last_state = None
|
||||||
self.server_info = {
|
self.server_info = {
|
||||||
|
@ -106,17 +135,28 @@ class ZigbeeMqttBackend(MqttBackend):
|
||||||
**self.server_info,
|
**self.server_info,
|
||||||
}
|
}
|
||||||
|
|
||||||
listeners = [{
|
listeners = [
|
||||||
|
{
|
||||||
**self.server_info,
|
**self.server_info,
|
||||||
'topics': [
|
'topics': [
|
||||||
self.base_topic + '/' + topic
|
self.base_topic + '/' + topic
|
||||||
for topic in ['bridge/state', 'bridge/log', 'bridge/logging', 'bridge/devices', 'bridge/groups']
|
for topic in [
|
||||||
|
'bridge/state',
|
||||||
|
'bridge/log',
|
||||||
|
'bridge/logging',
|
||||||
|
'bridge/devices',
|
||||||
|
'bridge/groups',
|
||||||
|
]
|
||||||
],
|
],
|
||||||
}]
|
}
|
||||||
|
]
|
||||||
|
|
||||||
super().__init__(
|
super().__init__(
|
||||||
*args, subscribe_default_topic=False,
|
*args,
|
||||||
listeners=listeners, client_id=client_id, **kwargs
|
subscribe_default_topic=False,
|
||||||
|
listeners=listeners,
|
||||||
|
client_id=client_id,
|
||||||
|
**kwargs
|
||||||
)
|
)
|
||||||
|
|
||||||
if not client_id:
|
if not client_id:
|
||||||
|
@ -146,7 +186,7 @@ class ZigbeeMqttBackend(MqttBackend):
|
||||||
|
|
||||||
if msg_type == 'devices':
|
if msg_type == 'devices':
|
||||||
devices = {}
|
devices = {}
|
||||||
for dev in (text or []):
|
for dev in text or []:
|
||||||
devices[dev['friendly_name']] = dev
|
devices[dev['friendly_name']] = dev
|
||||||
client.subscribe(self.base_topic + '/' + dev['friendly_name'])
|
client.subscribe(self.base_topic + '/' + dev['friendly_name'])
|
||||||
elif msg_type == 'pairing':
|
elif msg_type == 'pairing':
|
||||||
|
@ -155,7 +195,9 @@ class ZigbeeMqttBackend(MqttBackend):
|
||||||
self.bus.post(ZigbeeMqttDeviceBannedEvent(device=text, **args))
|
self.bus.post(ZigbeeMqttDeviceBannedEvent(device=text, **args))
|
||||||
elif msg_type in ['device_removed_failed', 'device_force_removed_failed']:
|
elif msg_type in ['device_removed_failed', 'device_force_removed_failed']:
|
||||||
force = msg_type == 'device_force_removed_failed'
|
force = msg_type == 'device_force_removed_failed'
|
||||||
self.bus.post(ZigbeeMqttDeviceRemovedFailedEvent(device=text, force=force, **args))
|
self.bus.post(
|
||||||
|
ZigbeeMqttDeviceRemovedFailedEvent(device=text, force=force, **args)
|
||||||
|
)
|
||||||
elif msg_type == 'device_whitelisted':
|
elif msg_type == 'device_whitelisted':
|
||||||
self.bus.post(ZigbeeMqttDeviceWhitelistedEvent(device=text, **args))
|
self.bus.post(ZigbeeMqttDeviceWhitelistedEvent(device=text, **args))
|
||||||
elif msg_type == 'device_renamed':
|
elif msg_type == 'device_renamed':
|
||||||
|
@ -181,7 +223,11 @@ class ZigbeeMqttBackend(MqttBackend):
|
||||||
self.bus.post(ZigbeeMqttErrorEvent(error=text, **args))
|
self.bus.post(ZigbeeMqttErrorEvent(error=text, **args))
|
||||||
elif msg.get('level') in ['warning', 'error']:
|
elif msg.get('level') in ['warning', 'error']:
|
||||||
log = getattr(self.logger, msg['level'])
|
log = getattr(self.logger, msg['level'])
|
||||||
log('zigbee2mqtt {}: {}'.format(msg['level'], text or msg.get('error', msg.get('warning'))))
|
log(
|
||||||
|
'zigbee2mqtt {}: {}'.format(
|
||||||
|
msg['level'], text or msg.get('error', msg.get('warning'))
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
def _process_devices(self, client, msg):
|
def _process_devices(self, client, msg):
|
||||||
devices_info = {
|
devices_info = {
|
||||||
|
@ -191,10 +237,9 @@ class ZigbeeMqttBackend(MqttBackend):
|
||||||
|
|
||||||
# noinspection PyProtectedMember
|
# noinspection PyProtectedMember
|
||||||
event_args = {'host': client._host, 'port': client._port}
|
event_args = {'host': client._host, 'port': client._port}
|
||||||
client.subscribe(*[
|
client.subscribe(
|
||||||
self.base_topic + '/' + device
|
*[self.base_topic + '/' + device for device in devices_info.keys()]
|
||||||
for device in devices_info.keys()
|
)
|
||||||
])
|
|
||||||
|
|
||||||
for name, device in devices_info.items():
|
for name, device in devices_info.items():
|
||||||
if name not in self._devices:
|
if name not in self._devices:
|
||||||
|
@ -203,7 +248,7 @@ class ZigbeeMqttBackend(MqttBackend):
|
||||||
exposes = (device.get('definition', {}) or {}).get('exposes', [])
|
exposes = (device.get('definition', {}) or {}).get('exposes', [])
|
||||||
client.publish(
|
client.publish(
|
||||||
self.base_topic + '/' + name + '/get',
|
self.base_topic + '/' + name + '/get',
|
||||||
json.dumps(get_plugin('zigbee.mqtt').build_device_get_request(exposes))
|
json.dumps(self._plugin.build_device_get_request(exposes)),
|
||||||
)
|
)
|
||||||
|
|
||||||
devices_copy = [*self._devices.keys()]
|
devices_copy = [*self._devices.keys()]
|
||||||
|
@ -213,13 +258,13 @@ class ZigbeeMqttBackend(MqttBackend):
|
||||||
del self._devices[name]
|
del self._devices[name]
|
||||||
|
|
||||||
self._devices = {device: {} for device in devices_info.keys()}
|
self._devices = {device: {} for device in devices_info.keys()}
|
||||||
|
self._devices_info = devices_info
|
||||||
|
|
||||||
def _process_groups(self, client, msg):
|
def _process_groups(self, client, msg):
|
||||||
# noinspection PyProtectedMember
|
# noinspection PyProtectedMember
|
||||||
event_args = {'host': client._host, 'port': client._port}
|
event_args = {'host': client._host, 'port': client._port}
|
||||||
groups_info = {
|
groups_info = {
|
||||||
group.get('friendly_name', group.get('id')): group
|
group.get('friendly_name', group.get('id')): group for group in msg
|
||||||
for group in msg
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for name in groups_info.keys():
|
for name in groups_info.keys():
|
||||||
|
@ -236,15 +281,13 @@ class ZigbeeMqttBackend(MqttBackend):
|
||||||
|
|
||||||
def on_mqtt_message(self):
|
def on_mqtt_message(self):
|
||||||
def handler(client, _, msg):
|
def handler(client, _, msg):
|
||||||
topic = msg.topic[len(self.base_topic)+1:]
|
topic = msg.topic[len(self.base_topic) + 1 :]
|
||||||
data = msg.payload.decode()
|
data = msg.payload.decode()
|
||||||
if not data:
|
if not data:
|
||||||
return
|
return
|
||||||
|
|
||||||
try:
|
with contextlib.suppress(ValueError, TypeError):
|
||||||
data = json.loads(data)
|
data = json.loads(data)
|
||||||
except (ValueError, TypeError):
|
|
||||||
pass
|
|
||||||
|
|
||||||
if topic == 'bridge/state':
|
if topic == 'bridge/state':
|
||||||
self._process_state_message(client, data)
|
self._process_state_message(client, data)
|
||||||
|
@ -260,17 +303,45 @@ class ZigbeeMqttBackend(MqttBackend):
|
||||||
return
|
return
|
||||||
|
|
||||||
name = suffix
|
name = suffix
|
||||||
changed_props = {k: v for k, v in data.items() if v != self._devices[name].get(k)}
|
changed_props = {
|
||||||
|
k: v for k, v in data.items() if v != self._devices[name].get(k)
|
||||||
|
}
|
||||||
|
|
||||||
if changed_props:
|
if changed_props:
|
||||||
# noinspection PyProtectedMember
|
self._process_property_update(name, changed_props)
|
||||||
self.bus.post(ZigbeeMqttDevicePropertySetEvent(host=client._host, port=client._port,
|
self.bus.post(
|
||||||
device=name, properties=changed_props))
|
ZigbeeMqttDevicePropertySetEvent(
|
||||||
|
host=client._host,
|
||||||
|
port=client._port,
|
||||||
|
device=name,
|
||||||
|
properties=changed_props,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
self._devices[name].update(data)
|
self._devices[name].update(data)
|
||||||
|
|
||||||
return handler
|
return handler
|
||||||
|
|
||||||
|
@property
|
||||||
|
def _plugin(self):
|
||||||
|
plugin = get_plugin('zigbee.mqtt')
|
||||||
|
assert plugin, 'The zigbee.mqtt plugin is not configured'
|
||||||
|
return plugin
|
||||||
|
|
||||||
|
def _process_property_update(self, device_name: str, properties: Mapping):
|
||||||
|
device_info = self._devices_info.get(device_name)
|
||||||
|
if not (device_info and properties):
|
||||||
|
return
|
||||||
|
|
||||||
|
self._plugin.publish_entities(
|
||||||
|
[
|
||||||
|
{
|
||||||
|
**device_info,
|
||||||
|
'state': properties,
|
||||||
|
}
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
super().run()
|
super().run()
|
||||||
|
|
||||||
|
|
|
@ -3,6 +3,7 @@ import threading
|
||||||
|
|
||||||
from queue import Queue
|
from queue import Queue
|
||||||
from typing import Optional, List, Any, Dict, Union
|
from typing import Optional, List, Any, Dict, Union
|
||||||
|
from platypush.message import Mapping
|
||||||
|
|
||||||
from platypush.message.response import Response
|
from platypush.message.response import Response
|
||||||
from platypush.plugins.mqtt import MqttPlugin, action
|
from platypush.plugins.mqtt import MqttPlugin, action
|
||||||
|
@ -153,6 +154,7 @@ class ZigbeeMqttPlugin(MqttPlugin, SwitchPlugin): # lgtm [py/missing-call-to-in
|
||||||
self._info = {
|
self._info = {
|
||||||
'devices': {},
|
'devices': {},
|
||||||
'groups': {},
|
'groups': {},
|
||||||
|
'devices_by_addr': {},
|
||||||
}
|
}
|
||||||
|
|
||||||
def transform_entities(self, devices):
|
def transform_entities(self, devices):
|
||||||
|
@ -163,6 +165,7 @@ class ZigbeeMqttPlugin(MqttPlugin, SwitchPlugin): # lgtm [py/missing-call-to-in
|
||||||
if not dev:
|
if not dev:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
converted_entity = None
|
||||||
dev_def = dev.get("definition") or {}
|
dev_def = dev.get("definition") or {}
|
||||||
dev_info = {
|
dev_info = {
|
||||||
"type": dev.get("type"),
|
"type": dev.get("type"),
|
||||||
|
@ -178,16 +181,17 @@ class ZigbeeMqttPlugin(MqttPlugin, SwitchPlugin): # lgtm [py/missing-call-to-in
|
||||||
"description": dev_def.get("description"),
|
"description": dev_def.get("description"),
|
||||||
}
|
}
|
||||||
|
|
||||||
switch_info = self._get_switch_info(dev)
|
switch_info = self._get_switch_meta(dev)
|
||||||
if switch_info:
|
if switch_info:
|
||||||
compatible_entities.append(
|
converted_entity = Switch(
|
||||||
Switch(
|
|
||||||
id=dev['ieee_address'],
|
id=dev['ieee_address'],
|
||||||
name=dev.get('friendly_name'),
|
name=dev.get('friendly_name'),
|
||||||
state=switch_info['property'] == switch_info['value_on'],
|
state=dev.get('state', {}).get('state') == 'ON',
|
||||||
data=dev_info,
|
data=dev_info,
|
||||||
)
|
)
|
||||||
)
|
|
||||||
|
if converted_entity:
|
||||||
|
compatible_entities.append(converted_entity)
|
||||||
|
|
||||||
return super().transform_entities(compatible_entities) # type: ignore
|
return super().transform_entities(compatible_entities) # type: ignore
|
||||||
|
|
||||||
|
@ -244,11 +248,14 @@ class ZigbeeMqttPlugin(MqttPlugin, SwitchPlugin): # lgtm [py/missing-call-to-in
|
||||||
for device in info.get('devices', [])
|
for device in info.get('devices', [])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self._info['devices_by_addr'] = {
|
||||||
|
device['ieee_address']: device for device in info.get('devices', [])
|
||||||
|
}
|
||||||
|
|
||||||
self._info['groups'] = {
|
self._info['groups'] = {
|
||||||
group.get('name'): group for group in info.get('groups', [])
|
group.get('name'): group for group in info.get('groups', [])
|
||||||
}
|
}
|
||||||
|
|
||||||
self.publish_entities(self._info['devices'].values()) # type: ignore
|
|
||||||
self.logger.info('Zigbee network configuration updated')
|
self.logger.info('Zigbee network configuration updated')
|
||||||
return info
|
return info
|
||||||
finally:
|
finally:
|
||||||
|
@ -659,6 +666,11 @@ class ZigbeeMqttPlugin(MqttPlugin, SwitchPlugin): # lgtm [py/missing-call-to-in
|
||||||
|
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
|
def _get_device_info(self, device: str) -> Mapping:
|
||||||
|
return self._info['devices'].get(
|
||||||
|
device, self._info['devices_by_addr'].get(device, {})
|
||||||
|
)
|
||||||
|
|
||||||
# noinspection PyShadowingBuiltins
|
# noinspection PyShadowingBuiltins
|
||||||
@action
|
@action
|
||||||
def device_get(
|
def device_get(
|
||||||
|
@ -676,6 +688,9 @@ class ZigbeeMqttPlugin(MqttPlugin, SwitchPlugin): # lgtm [py/missing-call-to-in
|
||||||
:return: Key->value map of the device properties.
|
:return: Key->value map of the device properties.
|
||||||
"""
|
"""
|
||||||
kwargs = self._mqtt_args(**kwargs)
|
kwargs = self._mqtt_args(**kwargs)
|
||||||
|
device_info = self._get_device_info(device)
|
||||||
|
if device_info:
|
||||||
|
device = device_info.get('friendly_name') or device_info['ieee_address']
|
||||||
|
|
||||||
if property:
|
if property:
|
||||||
properties = self.publish(
|
properties = self.publish(
|
||||||
|
@ -688,11 +703,9 @@ class ZigbeeMqttPlugin(MqttPlugin, SwitchPlugin): # lgtm [py/missing-call-to-in
|
||||||
assert property in properties, f'No such property: {property}'
|
assert property in properties, f'No such property: {property}'
|
||||||
return {property: properties[property]}
|
return {property: properties[property]}
|
||||||
|
|
||||||
refreshed = False
|
|
||||||
if device not in self._info.get('devices', {}):
|
if device not in self._info.get('devices', {}):
|
||||||
# Refresh devices info
|
# Refresh devices info
|
||||||
self._get_network_info(**kwargs)
|
self._get_network_info(**kwargs)
|
||||||
refreshed = True
|
|
||||||
|
|
||||||
assert self._info.get('devices', {}).get(device), f'No such device: {device}'
|
assert self._info.get('devices', {}).get(device), f'No such device: {device}'
|
||||||
exposes = (
|
exposes = (
|
||||||
|
@ -701,17 +714,24 @@ class ZigbeeMqttPlugin(MqttPlugin, SwitchPlugin): # lgtm [py/missing-call-to-in
|
||||||
if not exposes:
|
if not exposes:
|
||||||
return {}
|
return {}
|
||||||
|
|
||||||
device_info = self.publish(
|
device_state = self.publish(
|
||||||
topic=self._topic(device) + '/get',
|
topic=self._topic(device) + '/get',
|
||||||
reply_topic=self._topic(device),
|
reply_topic=self._topic(device),
|
||||||
msg=self.build_device_get_request(exposes),
|
msg=self.build_device_get_request(exposes),
|
||||||
**kwargs,
|
**kwargs,
|
||||||
|
).output
|
||||||
|
|
||||||
|
if device_info:
|
||||||
|
self.publish_entities(
|
||||||
|
[
|
||||||
|
{ # type: ignore
|
||||||
|
**device_info,
|
||||||
|
'state': device_state,
|
||||||
|
}
|
||||||
|
]
|
||||||
)
|
)
|
||||||
|
|
||||||
if not refreshed:
|
return device_state
|
||||||
self.publish_entities([device_info]) # type: ignore
|
|
||||||
|
|
||||||
return device_info
|
|
||||||
|
|
||||||
@action
|
@action
|
||||||
def devices_get(
|
def devices_get(
|
||||||
|
@ -1242,8 +1262,9 @@ class ZigbeeMqttPlugin(MqttPlugin, SwitchPlugin): # lgtm [py/missing-call-to-in
|
||||||
Implements :meth:`platypush.plugins.switch.plugin.SwitchPlugin.on` and turns on a Zigbee device with a writable
|
Implements :meth:`platypush.plugins.switch.plugin.SwitchPlugin.on` and turns on a Zigbee device with a writable
|
||||||
binary property.
|
binary property.
|
||||||
"""
|
"""
|
||||||
switch_info = self._get_switches_info().get(device)
|
switch_info = self._get_switch_info(device)
|
||||||
assert switch_info, '{} is not a valid switch'.format(device)
|
assert switch_info, '{} is not a valid switch'.format(device)
|
||||||
|
device = switch_info.get('friendly_name') or switch_info['ieee_address']
|
||||||
props = self.device_set(
|
props = self.device_set(
|
||||||
device, switch_info['property'], switch_info['value_on']
|
device, switch_info['property'], switch_info['value_on']
|
||||||
).output
|
).output
|
||||||
|
@ -1257,8 +1278,9 @@ class ZigbeeMqttPlugin(MqttPlugin, SwitchPlugin): # lgtm [py/missing-call-to-in
|
||||||
Implements :meth:`platypush.plugins.switch.plugin.SwitchPlugin.off` and turns off a Zigbee device with a
|
Implements :meth:`platypush.plugins.switch.plugin.SwitchPlugin.off` and turns off a Zigbee device with a
|
||||||
writable binary property.
|
writable binary property.
|
||||||
"""
|
"""
|
||||||
switch_info = self._get_switches_info().get(device)
|
switch_info = self._get_switch_info(device)
|
||||||
assert switch_info, '{} is not a valid switch'.format(device)
|
assert switch_info, '{} is not a valid switch'.format(device)
|
||||||
|
device = switch_info.get('friendly_name') or switch_info['ieee_address']
|
||||||
props = self.device_set(
|
props = self.device_set(
|
||||||
device, switch_info['property'], switch_info['value_off']
|
device, switch_info['property'], switch_info['value_off']
|
||||||
).output
|
).output
|
||||||
|
@ -1272,8 +1294,9 @@ class ZigbeeMqttPlugin(MqttPlugin, SwitchPlugin): # lgtm [py/missing-call-to-in
|
||||||
Implements :meth:`platypush.plugins.switch.plugin.SwitchPlugin.toggle` and toggles a Zigbee device with a
|
Implements :meth:`platypush.plugins.switch.plugin.SwitchPlugin.toggle` and toggles a Zigbee device with a
|
||||||
writable binary property.
|
writable binary property.
|
||||||
"""
|
"""
|
||||||
switch_info = self._get_switches_info().get(device)
|
switch_info = self._get_switch_info(device)
|
||||||
assert switch_info, '{} is not a valid switch'.format(device)
|
assert switch_info, '{} is not a valid switch'.format(device)
|
||||||
|
device = switch_info.get('friendly_name') or switch_info['ieee_address']
|
||||||
props = self.device_set(
|
props = self.device_set(
|
||||||
device, switch_info['property'], switch_info['value_toggle']
|
device, switch_info['property'], switch_info['value_toggle']
|
||||||
).output
|
).output
|
||||||
|
@ -1281,6 +1304,17 @@ class ZigbeeMqttPlugin(MqttPlugin, SwitchPlugin): # lgtm [py/missing-call-to-in
|
||||||
device=device, props=props, switch_info=switch_info
|
device=device, props=props, switch_info=switch_info
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def _get_switch_info(self, device: str):
|
||||||
|
switches_info = self._get_switches_info()
|
||||||
|
info = switches_info.get(device)
|
||||||
|
if info:
|
||||||
|
return info
|
||||||
|
|
||||||
|
device_info = self._get_device_info(device)
|
||||||
|
if device_info:
|
||||||
|
device = device_info.get('friendly_name') or device_info['ieee_address']
|
||||||
|
return switches_info.get(device)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _properties_to_switch(device: str, props: dict, switch_info: dict) -> dict:
|
def _properties_to_switch(device: str, props: dict, switch_info: dict) -> dict:
|
||||||
return {
|
return {
|
||||||
|
@ -1291,7 +1325,7 @@ class ZigbeeMqttPlugin(MqttPlugin, SwitchPlugin): # lgtm [py/missing-call-to-in
|
||||||
}
|
}
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _get_switch_info(device_info: dict) -> dict:
|
def _get_switch_meta(device_info: dict) -> dict:
|
||||||
exposes = (device_info.get('definition', {}) or {}).get('exposes', [])
|
exposes = (device_info.get('definition', {}) or {}).get('exposes', [])
|
||||||
for exposed in exposes:
|
for exposed in exposes:
|
||||||
for feature in exposed.get('features', []):
|
for feature in exposed.get('features', []):
|
||||||
|
@ -1302,6 +1336,8 @@ class ZigbeeMqttPlugin(MqttPlugin, SwitchPlugin): # lgtm [py/missing-call-to-in
|
||||||
and feature.get('access', 0) & 2
|
and feature.get('access', 0) & 2
|
||||||
):
|
):
|
||||||
return {
|
return {
|
||||||
|
'friendly_name': device_info.get('friendly_name'),
|
||||||
|
'ieee_address': device_info.get('friendly_name'),
|
||||||
'property': feature['property'],
|
'property': feature['property'],
|
||||||
'value_on': feature['value_on'],
|
'value_on': feature['value_on'],
|
||||||
'value_off': feature['value_off'],
|
'value_off': feature['value_off'],
|
||||||
|
@ -1316,7 +1352,7 @@ class ZigbeeMqttPlugin(MqttPlugin, SwitchPlugin): # lgtm [py/missing-call-to-in
|
||||||
switches_info = {}
|
switches_info = {}
|
||||||
|
|
||||||
for device in devices:
|
for device in devices:
|
||||||
info = self._get_switch_info(device)
|
info = self._get_switch_meta(device)
|
||||||
if not info:
|
if not info:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue