forked from platypush/platypush
- Simplified prototype for EntityManager.set
- Added small documentation/annotations notes to the `Plugin` module. - Small LINT fixes
This commit is contained in:
parent
575635fd6b
commit
1d0be5c929
7 changed files with 35 additions and 16 deletions
|
@ -1,5 +1,5 @@
|
||||||
from abc import ABC, abstractmethod
|
from abc import ABC, abstractmethod
|
||||||
from typing import Any, Optional
|
from typing import Any
|
||||||
from typing_extensions import override
|
from typing_extensions import override
|
||||||
|
|
||||||
from . import EntityManager
|
from . import EntityManager
|
||||||
|
@ -11,15 +11,13 @@ class WriteableEntityManager(EntityManager, ABC):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
def set(self, entity: str, value: Any, attribute: Optional[str] = None, **kwargs):
|
def set(self, entity: str, value: Any, **kwargs):
|
||||||
"""
|
"""
|
||||||
Set the value of an entity.
|
Set the value of an entity.
|
||||||
|
|
||||||
:param entity: The entity to set the value for. It's usually the ID of
|
:param entity: The entity to set the value for. It's usually the ID of
|
||||||
the entity provided by the plugin.
|
the entity provided by the plugin.
|
||||||
:param value: The value to set the entity to.
|
:param value: The value to set the entity to.
|
||||||
:param attribute: The name of the attribute to set for the entity, if
|
|
||||||
required by the integration.
|
|
||||||
"""
|
"""
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
@ -45,7 +43,7 @@ class SwitchEntityManager(WriteableEntityManager, ABC):
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|
||||||
@override
|
@override
|
||||||
def set(self, entity: str, value: Any, attribute: Optional[str] = None, **kwargs):
|
def set(self, entity: str, value: Any, **kwargs):
|
||||||
method = self.on if value else self.off
|
method = self.on if value else self.off
|
||||||
return method(entity, **kwargs)
|
return method(entity, **kwargs)
|
||||||
|
|
||||||
|
|
|
@ -5,6 +5,7 @@ import threading
|
||||||
from abc import ABC, abstractmethod
|
from abc import ABC, abstractmethod
|
||||||
from functools import wraps
|
from functools import wraps
|
||||||
from typing import Any, Callable, Optional
|
from typing import Any, Callable, Optional
|
||||||
|
from typing_extensions import override
|
||||||
|
|
||||||
from platypush.bus import Bus
|
from platypush.bus import Bus
|
||||||
from platypush.common import ExtensionWithManifest
|
from platypush.common import ExtensionWithManifest
|
||||||
|
@ -97,21 +98,33 @@ class RunnablePlugin(Plugin):
|
||||||
self._thread: Optional[threading.Thread] = None
|
self._thread: Optional[threading.Thread] = None
|
||||||
|
|
||||||
def main(self):
|
def main(self):
|
||||||
|
"""
|
||||||
|
Implementation of the main loop of the plugin.
|
||||||
|
"""
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|
||||||
def should_stop(self):
|
def should_stop(self) -> bool:
|
||||||
return self._should_stop.is_set()
|
return self._should_stop.is_set()
|
||||||
|
|
||||||
def wait_stop(self, timeout=None):
|
def wait_stop(self, timeout=None):
|
||||||
|
"""
|
||||||
|
Wait until a stop event is received.
|
||||||
|
"""
|
||||||
return self._should_stop.wait(timeout=timeout)
|
return self._should_stop.wait(timeout=timeout)
|
||||||
|
|
||||||
def start(self):
|
def start(self):
|
||||||
|
"""
|
||||||
|
Start the plugin.
|
||||||
|
"""
|
||||||
self._thread = threading.Thread(
|
self._thread = threading.Thread(
|
||||||
target=self._runner, name=self.__class__.__name__
|
target=self._runner, name=self.__class__.__name__
|
||||||
)
|
)
|
||||||
self._thread.start()
|
self._thread.start()
|
||||||
|
|
||||||
def stop(self):
|
def stop(self):
|
||||||
|
"""
|
||||||
|
Stop the plugin.
|
||||||
|
"""
|
||||||
self._should_stop.set()
|
self._should_stop.set()
|
||||||
if self._thread and self._thread.is_alive():
|
if self._thread and self._thread.is_alive():
|
||||||
self.logger.info('Waiting for the plugin to stop')
|
self.logger.info('Waiting for the plugin to stop')
|
||||||
|
@ -129,6 +142,9 @@ class RunnablePlugin(Plugin):
|
||||||
self.logger.info('%s stopped', self.__class__.__name__)
|
self.logger.info('%s stopped', self.__class__.__name__)
|
||||||
|
|
||||||
def _runner(self):
|
def _runner(self):
|
||||||
|
"""
|
||||||
|
Implementation of the runner thread.
|
||||||
|
"""
|
||||||
self.logger.info('Starting %s', self.__class__.__name__)
|
self.logger.info('Starting %s', self.__class__.__name__)
|
||||||
|
|
||||||
while not self.should_stop():
|
while not self.should_stop():
|
||||||
|
@ -185,6 +201,9 @@ class AsyncRunnablePlugin(RunnablePlugin, ABC):
|
||||||
raise e
|
raise e
|
||||||
|
|
||||||
def _run_listener(self):
|
def _run_listener(self):
|
||||||
|
"""
|
||||||
|
Initialize an event loop and run the listener as a task.
|
||||||
|
"""
|
||||||
self._loop = asyncio.new_event_loop()
|
self._loop = asyncio.new_event_loop()
|
||||||
asyncio.set_event_loop(self._loop)
|
asyncio.set_event_loop(self._loop)
|
||||||
|
|
||||||
|
@ -198,6 +217,7 @@ class AsyncRunnablePlugin(RunnablePlugin, ABC):
|
||||||
|
|
||||||
self._task.cancel()
|
self._task.cancel()
|
||||||
|
|
||||||
|
@override
|
||||||
def main(self):
|
def main(self):
|
||||||
if self.should_stop():
|
if self.should_stop():
|
||||||
self.logger.info('The plugin is already scheduled to stop')
|
self.logger.info('The plugin is already scheduled to stop')
|
||||||
|
@ -214,6 +234,7 @@ class AsyncRunnablePlugin(RunnablePlugin, ABC):
|
||||||
else:
|
else:
|
||||||
self.wait_stop()
|
self.wait_stop()
|
||||||
|
|
||||||
|
@override
|
||||||
def stop(self):
|
def stop(self):
|
||||||
if self._loop and self._loop.is_running():
|
if self._loop and self._loop.is_running():
|
||||||
self._loop.call_soon_threadsafe(self._loop.stop)
|
self._loop.call_soon_threadsafe(self._loop.stop)
|
||||||
|
|
|
@ -126,7 +126,7 @@ class MqttPlugin(Plugin):
|
||||||
if version == 'tlsv1.2':
|
if version == 'tlsv1.2':
|
||||||
return ssl.PROTOCOL_TLSv1_2
|
return ssl.PROTOCOL_TLSv1_2
|
||||||
|
|
||||||
assert 'Unrecognized TLS version: {}'.format(version)
|
assert f'Unrecognized TLS version: {version}'
|
||||||
|
|
||||||
def _mqtt_args(self, **kwargs):
|
def _mqtt_args(self, **kwargs):
|
||||||
return {
|
return {
|
||||||
|
|
|
@ -356,7 +356,7 @@ class SmartthingsPlugin(
|
||||||
}
|
}
|
||||||
|
|
||||||
missing_devs = {dev for dev in devices if dev not in found_devs}
|
missing_devs = {dev for dev in devices if dev not in found_devs}
|
||||||
return list(found_devs.values()), list(missing_devs) # type: ignore
|
return list(found_devs.values()), list(missing_devs)
|
||||||
|
|
||||||
def _get_devices(self, *devices: str) -> List[DeviceEntity]:
|
def _get_devices(self, *devices: str) -> List[DeviceEntity]:
|
||||||
devs, missing_devs = self._get_existing_and_missing_devices(*devices)
|
devs, missing_devs = self._get_existing_and_missing_devices(*devices)
|
||||||
|
@ -633,7 +633,7 @@ class SmartthingsPlugin(
|
||||||
|
|
||||||
self._entities_by_id.update({e.id: e for e in compatible_entities})
|
self._entities_by_id.update({e.id: e for e in compatible_entities})
|
||||||
|
|
||||||
return super().transform_entities(compatible_entities) # type: ignore
|
return super().transform_entities(compatible_entities)
|
||||||
|
|
||||||
async def _get_device_status(
|
async def _get_device_status(
|
||||||
self, api, device_id: str, publish_entities: bool
|
self, api, device_id: str, publish_entities: bool
|
||||||
|
@ -642,7 +642,7 @@ class SmartthingsPlugin(
|
||||||
assert device, f'No such device: {device_id}'
|
assert device, f'No such device: {device_id}'
|
||||||
await device.status.refresh()
|
await device.status.refresh()
|
||||||
if publish_entities:
|
if publish_entities:
|
||||||
self.publish_entities([device]) # type: ignore
|
self.publish_entities([device])
|
||||||
|
|
||||||
self._devices_by_id[device_id] = device
|
self._devices_by_id[device_id] = device
|
||||||
self._devices_by_name[device.label] = device
|
self._devices_by_name[device.label] = device
|
||||||
|
@ -863,7 +863,6 @@ class SmartthingsPlugin(
|
||||||
|
|
||||||
@action
|
@action
|
||||||
def set(self, entity: str, value: Any, attribute: Optional[str] = None, **kwargs):
|
def set(self, entity: str, value: Any, attribute: Optional[str] = None, **kwargs):
|
||||||
super().set(entity, value, attribute, **kwargs)
|
|
||||||
return self.set_value(entity, property=attribute, value=value, **kwargs)
|
return self.set_value(entity, property=attribute, value=value, **kwargs)
|
||||||
|
|
||||||
@action
|
@action
|
||||||
|
@ -994,6 +993,7 @@ class SmartthingsPlugin(
|
||||||
self.logger.exception(e)
|
self.logger.exception(e)
|
||||||
self.logger.error('Could not refresh the status: %s', e)
|
self.logger.error('Could not refresh the status: %s', e)
|
||||||
self.wait_stop(3 * (self.poll_interval or 5))
|
self.wait_stop(3 * (self.poll_interval or 5))
|
||||||
|
return None
|
||||||
|
|
||||||
while not self.should_stop():
|
while not self.should_stop():
|
||||||
updated_devices = {}
|
updated_devices = {}
|
||||||
|
@ -1010,7 +1010,7 @@ class SmartthingsPlugin(
|
||||||
if self._has_status_changed(devices.get(device_id, {}), new_status)
|
if self._has_status_changed(devices.get(device_id, {}), new_status)
|
||||||
}
|
}
|
||||||
|
|
||||||
self.publish_entities(updated_devices.values()) # type: ignore
|
self.publish_entities(updated_devices.values())
|
||||||
devices.update(new_devices)
|
devices.update(new_devices)
|
||||||
self.wait_stop(self.poll_interval)
|
self.wait_stop(self.poll_interval)
|
||||||
refresh_status_safe()
|
refresh_status_safe()
|
||||||
|
|
|
@ -34,7 +34,7 @@ class DeviceMapper:
|
||||||
entity_type: Type[Entity],
|
entity_type: Type[Entity],
|
||||||
capability: str,
|
capability: str,
|
||||||
attribute: str,
|
attribute: str,
|
||||||
value_type: Union[Type, str],
|
value_type: Union[Type, Enum, str],
|
||||||
set_command: Optional[Union[str, Callable[[Any], str]]] = None,
|
set_command: Optional[Union[str, Callable[[Any], str]]] = None,
|
||||||
get_value: Optional[Callable[[DeviceEntity], Any]] = None,
|
get_value: Optional[Callable[[DeviceEntity], Any]] = None,
|
||||||
set_value_args: Optional[Callable[..., Any]] = None,
|
set_value_args: Optional[Callable[..., Any]] = None,
|
||||||
|
@ -46,7 +46,7 @@ class DeviceMapper:
|
||||||
self.attribute = attribute
|
self.attribute = attribute
|
||||||
self.value_type = value_type
|
self.value_type = value_type
|
||||||
self.get_value = get_value if get_value else self._default_get_value
|
self.get_value = get_value if get_value else self._default_get_value
|
||||||
self.values = []
|
self.values: List[str] = []
|
||||||
self.entity_args = kwargs
|
self.entity_args = kwargs
|
||||||
|
|
||||||
if isinstance(value_type, Enum):
|
if isinstance(value_type, Enum):
|
||||||
|
|
|
@ -122,7 +122,7 @@ class SwitchbotBluetoothPlugin(BluetoothBlePlugin, EnumSwitchEntityManager):
|
||||||
self.logger.warning('Unknown command for SwitchBot "%s": "%s"', device, value)
|
self.logger.warning('Unknown command for SwitchBot "%s": "%s"', device, value)
|
||||||
|
|
||||||
@override
|
@override
|
||||||
def set(self, entity: str, value: Any, attribute: Optional[str] = None, **kwargs):
|
def set(self, entity: str, value: Any, **kwargs):
|
||||||
return self.set_value(entity, value, **kwargs)
|
return self.set_value(entity, value, **kwargs)
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
|
|
@ -348,7 +348,7 @@ class ZwaveBasePlugin(
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
@action
|
@action
|
||||||
def set(self, entity: str, value: Any, attribute: Optional[str] = None, **kwargs):
|
def set(self, entity: str, value: Any, **kwargs):
|
||||||
return self.set_value(
|
return self.set_value(
|
||||||
value_id=entity, id_on_network=entity, data=value, **kwargs
|
value_id=entity, id_on_network=entity, data=value, **kwargs
|
||||||
)
|
)
|
||||||
|
|
Loading…
Reference in a new issue