forked from platypush/platypush
- 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:
parent
952a2a9379
commit
6db070db1c
5 changed files with 66 additions and 15 deletions
|
@ -3,6 +3,12 @@
|
||||||
All notable changes to this project will be documented in this file.
|
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.
|
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
|
## [0.22.4] - 2021-10-19
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
|
@ -38,6 +38,13 @@ class SchemaDirective(Directive):
|
||||||
return bool(randint(0, 1))
|
return bool(randint(0, 1))
|
||||||
if isinstance(field, fields.URL):
|
if isinstance(field, fields.URL):
|
||||||
return 'https://example.org'
|
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):
|
if isinstance(field, fields.Nested):
|
||||||
ret = {
|
ret = {
|
||||||
name: cls._get_field_value(f)
|
name: cls._get_field_value(f)
|
||||||
|
|
|
@ -96,10 +96,15 @@ class SwitchbotPlugin(SwitchPlugin):
|
||||||
|
|
||||||
return devices
|
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:
|
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)
|
res = self._run(method, *args, device=device, **kwargs)
|
||||||
q.put(DeviceStatusSchema().dump(res))
|
|
||||||
|
q.put(schema.dump(res))
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.logger.exception(e)
|
self.logger.exception(e)
|
||||||
q.put(e)
|
q.put(e)
|
||||||
|
@ -115,15 +120,21 @@ class SwitchbotPlugin(SwitchPlugin):
|
||||||
# noinspection PyUnresolvedReferences
|
# noinspection PyUnresolvedReferences
|
||||||
devices = self.devices().output
|
devices = self.devices().output
|
||||||
if device:
|
if device:
|
||||||
|
device_info = self._get_device(device)
|
||||||
|
status = {} if device_info['is_virtual'] else self._run('get', 'status', device=device_info)
|
||||||
return {
|
return {
|
||||||
**device,
|
**device_info,
|
||||||
**self._run('get', 'status', device=self._get_device(device)),
|
**status,
|
||||||
}
|
}
|
||||||
|
|
||||||
devices_by_id = {dev['id']: dev for dev in devices}
|
devices_by_id = {dev['id']: dev for dev in devices}
|
||||||
queues = [queue.Queue()] * len(devices)
|
queues = [queue.Queue()] * len(devices)
|
||||||
workers = [
|
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)
|
for i, dev in enumerate(devices)
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
from datetime import datetime
|
from datetime import datetime, date
|
||||||
from typing import Optional, Union
|
from typing import Optional, Union
|
||||||
|
|
||||||
from dateutil.parser import isoparse
|
from dateutil.parser import isoparse
|
||||||
|
@ -12,6 +12,10 @@ class StrippedString(fields.Function): # lgtm [py/missing-call-to-init]
|
||||||
kwargs['deserialize'] = self._strip
|
kwargs['deserialize'] = self._strip
|
||||||
super().__init__(*args, **kwargs)
|
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
|
@staticmethod
|
||||||
def _strip(value: str):
|
def _strip(value: str):
|
||||||
return value.strip()
|
return value.strip()
|
||||||
|
@ -34,9 +38,32 @@ class DateTime(fields.Function):
|
||||||
return normalize_datetime(value)
|
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:
|
if not dt:
|
||||||
return
|
return
|
||||||
if isinstance(dt, datetime):
|
if isinstance(dt, datetime) or isinstance(dt, date):
|
||||||
return dt
|
return dt
|
||||||
|
|
||||||
|
try:
|
||||||
|
dt = float(dt)
|
||||||
|
return datetime.fromtimestamp(dt)
|
||||||
|
except (TypeError, ValueError):
|
||||||
return isoparse(dt)
|
return isoparse(dt)
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
from marshmallow import fields
|
from marshmallow import fields
|
||||||
from marshmallow.schema import Schema
|
from marshmallow.schema import Schema
|
||||||
from marshmallow.validate import OneOf
|
|
||||||
|
|
||||||
|
|
||||||
device_types = [
|
device_types = [
|
||||||
|
@ -26,6 +25,7 @@ device_types = [
|
||||||
'Speaker',
|
'Speaker',
|
||||||
'Water Heater',
|
'Water Heater',
|
||||||
'Vacuum Cleaner',
|
'Vacuum Cleaner',
|
||||||
|
'Remote',
|
||||||
'Others',
|
'Others',
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -51,12 +51,12 @@ class DeviceSchema(Schema):
|
||||||
id = fields.String(attribute='deviceId', required=True, metadata=dict(description='Device unique ID'))
|
id = fields.String(attribute='deviceId', required=True, metadata=dict(description='Device unique ID'))
|
||||||
name = fields.String(attribute='deviceName', metadata=dict(description='Device name'))
|
name = fields.String(attribute='deviceName', metadata=dict(description='Device name'))
|
||||||
device_type = fields.String(
|
device_type = fields.String(
|
||||||
attribute='deviceType', validate=OneOf(device_types),
|
attribute='deviceType',
|
||||||
metadata=dict(description=f'Supported types: [{", ".join(device_types)}]')
|
metadata=dict(description=f'Default types: [{", ".join(device_types)}]')
|
||||||
)
|
)
|
||||||
remote_type = fields.String(
|
remote_type = fields.String(
|
||||||
attribute='remoteType', validate=OneOf(remote_types),
|
attribute='remoteType',
|
||||||
metadata=dict(description=f'Supported types: [{", ".join(remote_types)}]')
|
metadata=dict(description=f'Default types: [{", ".join(remote_types)}]')
|
||||||
)
|
)
|
||||||
hub_id = fields.String(attribute='hubDeviceId', metadata=dict(description='Parent hub device unique ID'))
|
hub_id = fields.String(attribute='hubDeviceId', metadata=dict(description='Parent hub device unique ID'))
|
||||||
cloud_service_enabled = fields.Boolean(
|
cloud_service_enabled = fields.Boolean(
|
||||||
|
|
Loading…
Reference in a new issue