- Fixed switchbot.status to handle virtual devices

- Fixed StrippedString schema field serialize handler

- Fixed rendering of lists in documentation schemas
This commit is contained in:
Fabio Manganiello 2021-10-26 00:48:05 +02:00
parent 952a2a9379
commit 6db070db1c
Signed by: blacklight
GPG key ID: D90FBA7F76362774
5 changed files with 66 additions and 15 deletions

View file

@ -3,6 +3,12 @@
All notable changes to this project will be documented in this file.
Given the high speed of development in the first phase, changes are being reported only starting from v0.20.2.
## [Unreleased]
### Fixed
- Fixed `switchbot.status` method in case of virtual devices.
## [0.22.4] - 2021-10-19
### Added

View file

@ -38,6 +38,13 @@ class SchemaDirective(Directive):
return bool(randint(0, 1))
if isinstance(field, fields.URL):
return 'https://example.org'
if isinstance(field, fields.List):
return [cls._get_field_value(field.inner)]
if isinstance(field, fields.Dict):
return {
cls._get_field_value(field.key_field) if field.key_field else 'key':
cls._get_field_value(field.value_field) if field.value_field else 'value'
}
if isinstance(field, fields.Nested):
ret = {
name: cls._get_field_value(f)

View file

@ -96,10 +96,15 @@ class SwitchbotPlugin(SwitchPlugin):
return devices
def _worker(self, q: queue.Queue, method: str = 'get', *args, device=None, **kwargs):
def _worker(self, q: queue.Queue, method: str = 'get', *args, device: Optional[dict] = None, **kwargs):
schema = DeviceStatusSchema()
try:
if method == 'get' and args and args[0] == 'status' and device and device.get('is_virtual'):
res = schema.load(device)
else:
res = self._run(method, *args, device=device, **kwargs)
q.put(DeviceStatusSchema().dump(res))
q.put(schema.dump(res))
except Exception as e:
self.logger.exception(e)
q.put(e)
@ -115,15 +120,21 @@ class SwitchbotPlugin(SwitchPlugin):
# noinspection PyUnresolvedReferences
devices = self.devices().output
if device:
device_info = self._get_device(device)
status = {} if device_info['is_virtual'] else self._run('get', 'status', device=device_info)
return {
**device,
**self._run('get', 'status', device=self._get_device(device)),
**device_info,
**status,
}
devices_by_id = {dev['id']: dev for dev in devices}
queues = [queue.Queue()] * len(devices)
workers = [
threading.Thread(target=self._worker, args=(queues[i], 'get', 'status'), kwargs={'device': dev})
threading.Thread(
target=self._worker,
args=(queues[i], 'get', 'status'),
kwargs={'device': dev}
)
for i, dev in enumerate(devices)
]

View file

@ -1,4 +1,4 @@
from datetime import datetime
from datetime import datetime, date
from typing import Optional, Union
from dateutil.parser import isoparse
@ -12,6 +12,10 @@ class StrippedString(fields.Function): # lgtm [py/missing-call-to-init]
kwargs['deserialize'] = self._strip
super().__init__(*args, **kwargs)
def _serialize(self, value, attr, obj, **kwargs) -> Optional[str]:
if obj.get(attr) is not None:
return self._strip(obj.get(attr))
@staticmethod
def _strip(value: str):
return value.strip()
@ -34,9 +38,32 @@ class DateTime(fields.Function):
return normalize_datetime(value)
def normalize_datetime(dt: Union[str, datetime]) -> Optional[datetime]:
class Date(fields.Function):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.metadata = {
'example': date.today().isoformat(),
**(self.metadata or {}),
}
def _serialize(self, value, attr, obj, **kwargs) -> Optional[str]:
value = normalize_datetime(obj.get(attr))
if value:
return date(value.year, value.month, value.day).isoformat()
def _deserialize(self, value, attr, data, **kwargs) -> Optional[date]:
dt = normalize_datetime(value)
return date.fromtimestamp(dt.timestamp())
def normalize_datetime(dt: Union[str, date, datetime]) -> Optional[Union[date, datetime]]:
if not dt:
return
if isinstance(dt, datetime):
if isinstance(dt, datetime) or isinstance(dt, date):
return dt
try:
dt = float(dt)
return datetime.fromtimestamp(dt)
except (TypeError, ValueError):
return isoparse(dt)

View file

@ -1,6 +1,5 @@
from marshmallow import fields
from marshmallow.schema import Schema
from marshmallow.validate import OneOf
device_types = [
@ -26,6 +25,7 @@ device_types = [
'Speaker',
'Water Heater',
'Vacuum Cleaner',
'Remote',
'Others',
]
@ -51,12 +51,12 @@ class DeviceSchema(Schema):
id = fields.String(attribute='deviceId', required=True, metadata=dict(description='Device unique ID'))
name = fields.String(attribute='deviceName', metadata=dict(description='Device name'))
device_type = fields.String(
attribute='deviceType', validate=OneOf(device_types),
metadata=dict(description=f'Supported types: [{", ".join(device_types)}]')
attribute='deviceType',
metadata=dict(description=f'Default types: [{", ".join(device_types)}]')
)
remote_type = fields.String(
attribute='remoteType', validate=OneOf(remote_types),
metadata=dict(description=f'Supported types: [{", ".join(remote_types)}]')
attribute='remoteType',
metadata=dict(description=f'Default types: [{", ".join(remote_types)}]')
)
hub_id = fields.String(attribute='hubDeviceId', metadata=dict(description='Parent hub device unique ID'))
cloud_service_enabled = fields.Boolean(