forked from platypush/platypush
Added Switchbot plugin
This commit is contained in:
parent
69583d2e15
commit
664ce4050d
14 changed files with 407 additions and 37 deletions
16
CHANGELOG.md
16
CHANGELOG.md
|
@ -3,6 +3,22 @@
|
||||||
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]
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- Added `switchbot` plugin to interact with Switchbot devices over the cloud API instead of
|
||||||
|
directly accessing the device's Bluetooth interface.
|
||||||
|
|
||||||
|
- Added `marshmallow` dependency - it will be used from now own to dump and document schemas
|
||||||
|
and responses instead of the currently mixed approach with `Response` objects and plain
|
||||||
|
dictionaries and lists.
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- `switch.switchbot` plugin renamed to `switchbot.bluetooth` plugin, while the new plugin
|
||||||
|
that uses the Switchbot API is simply named `switchbot`.
|
||||||
|
|
||||||
## [0.21.0] - 2021-05-06
|
## [0.21.0] - 2021-05-06
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
|
@ -1,6 +0,0 @@
|
||||||
``platypush.plugins.switch.switchbot``
|
|
||||||
======================================
|
|
||||||
|
|
||||||
.. automodule:: platypush.plugins.switch.switchbot
|
|
||||||
:members:
|
|
||||||
|
|
5
docs/source/platypush/plugins/switchbot.bluetooth.rst
Normal file
5
docs/source/platypush/plugins/switchbot.bluetooth.rst
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
``platypush.plugins.switchbot.bluetooth``
|
||||||
|
=========================================
|
||||||
|
|
||||||
|
.. automodule:: platypush.plugins.switchbot.bluetooth
|
||||||
|
:members:
|
5
docs/source/platypush/plugins/switchbot.rst
Normal file
5
docs/source/platypush/plugins/switchbot.rst
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
``platypush.plugins.switchbot``
|
||||||
|
===============================
|
||||||
|
|
||||||
|
.. automodule:: platypush.plugins.switchbot
|
||||||
|
:members:
|
5
docs/source/platypush/plugins/zwave._base.rst
Normal file
5
docs/source/platypush/plugins/zwave._base.rst
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
``platypush.plugins.zwave._base``
|
||||||
|
=================================
|
||||||
|
|
||||||
|
.. automodule:: platypush.plugins.zwave._base
|
||||||
|
:members:
|
|
@ -118,9 +118,10 @@ Plugins
|
||||||
platypush/plugins/stt.picovoice.hotword.rst
|
platypush/plugins/stt.picovoice.hotword.rst
|
||||||
platypush/plugins/stt.picovoice.speech.rst
|
platypush/plugins/stt.picovoice.speech.rst
|
||||||
platypush/plugins/switch.rst
|
platypush/plugins/switch.rst
|
||||||
platypush/plugins/switch.switchbot.rst
|
|
||||||
platypush/plugins/switch.tplink.rst
|
platypush/plugins/switch.tplink.rst
|
||||||
platypush/plugins/switch.wemo.rst
|
platypush/plugins/switch.wemo.rst
|
||||||
|
platypush/plugins/switchbot.rst
|
||||||
|
platypush/plugins/switchbot.bluetooth.rst
|
||||||
platypush/plugins/system.rst
|
platypush/plugins/system.rst
|
||||||
platypush/plugins/tcp.rst
|
platypush/plugins/tcp.rst
|
||||||
platypush/plugins/tensorflow.rst
|
platypush/plugins/tensorflow.rst
|
||||||
|
@ -145,4 +146,5 @@ Plugins
|
||||||
platypush/plugins/zeroconf.rst
|
platypush/plugins/zeroconf.rst
|
||||||
platypush/plugins/zigbee.mqtt.rst
|
platypush/plugins/zigbee.mqtt.rst
|
||||||
platypush/plugins/zwave.rst
|
platypush/plugins/zwave.rst
|
||||||
|
platypush/plugins/zwave._base.rst
|
||||||
platypush/plugins/zwave.mqtt.rst
|
platypush/plugins/zwave.mqtt.rst
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
SwitchbotBluetooth
|
|
@ -35,7 +35,7 @@ import Switch from "@/components/panels/Switches/Switch";
|
||||||
import Modal from "@/components/Modal";
|
import Modal from "@/components/Modal";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "SwitchSwitchbot",
|
name: "SwitchbotBluetooth",
|
||||||
components: {Modal, Switch, Loading},
|
components: {Modal, Switch, Loading},
|
||||||
mixins: [SwitchMixin],
|
mixins: [SwitchMixin],
|
||||||
}
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
from typing import List
|
from typing import List, Union
|
||||||
|
|
||||||
from platypush.plugins import Plugin, action
|
from platypush.plugins import Plugin, action
|
||||||
|
|
||||||
|
@ -27,46 +27,34 @@ class SwitchPlugin(Plugin):
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|
||||||
@action
|
@action
|
||||||
def switch_status(self, device=None):
|
def switch_status(self, device=None) -> Union[dict, List[dict]]:
|
||||||
""" Get the status of a specified device or of all the configured devices (default)"""
|
"""
|
||||||
|
Get the status of a specified device or of all the configured devices (default).
|
||||||
|
|
||||||
|
:param device: Filter by device name or ID.
|
||||||
|
:return: .. schema:: switch.SwitchStatusSchema(many=True)
|
||||||
|
"""
|
||||||
devices = self.switches
|
devices = self.switches
|
||||||
if device:
|
if device:
|
||||||
devices = [d for d in self.switches if d.get('id') == device or d.get('name') == device]
|
devices = [d for d in self.switches if d.get('id') == device or d.get('name') == device]
|
||||||
if devices:
|
return devices[0] if devices else []
|
||||||
return devices[0]
|
|
||||||
else:
|
|
||||||
return None
|
|
||||||
|
|
||||||
return devices
|
return devices
|
||||||
|
|
||||||
@action
|
@action
|
||||||
def status(self, device=None, *args, **kwargs):
|
def status(self, device=None, *args, **kwargs) -> Union[dict, List[dict]]:
|
||||||
"""
|
"""
|
||||||
Status function - if not overridden it calls :meth:`.switch_status`. You may want to override it if your plugin
|
Get the status of all the devices, or filter by device name or ID (alias for :meth:`.switch_status`).
|
||||||
does not handle only switches.
|
|
||||||
|
:param device: Filter by device name or ID.
|
||||||
|
:return: .. schema:: switch.SwitchStatusSchema(many=True)
|
||||||
"""
|
"""
|
||||||
return self.switch_status(device)
|
return self.switch_status(device)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def switches(self) -> List[dict]:
|
def switches(self) -> List[dict]:
|
||||||
"""
|
"""
|
||||||
This property must be implemented by the derived classes and must return a dictionary in the following format:
|
:return: .. schema:: switch.SwitchStatusSchema(many=True)
|
||||||
|
|
||||||
.. code-block:: json
|
|
||||||
|
|
||||||
[
|
|
||||||
{
|
|
||||||
"name": "switch_1",
|
|
||||||
"on": true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "switch_2",
|
|
||||||
"on": false
|
|
||||||
},
|
|
||||||
]
|
|
||||||
|
|
||||||
``name`` and ``on`` are the minimum set of attributes that should be returned for a switch, but more attributes
|
|
||||||
can also be added.
|
|
||||||
"""
|
"""
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
|
264
platypush/plugins/switchbot/__init__.py
Normal file
264
platypush/plugins/switchbot/__init__.py
Normal file
|
@ -0,0 +1,264 @@
|
||||||
|
import queue
|
||||||
|
import requests
|
||||||
|
import threading
|
||||||
|
from typing import List, Optional, Union
|
||||||
|
|
||||||
|
from platypush.plugins import action
|
||||||
|
from platypush.plugins.switch import SwitchPlugin
|
||||||
|
from platypush.schemas.switchbot import DeviceSchema, DeviceStatusSchema, SceneSchema
|
||||||
|
|
||||||
|
|
||||||
|
class SwitchbotPlugin(SwitchPlugin):
|
||||||
|
"""
|
||||||
|
Plugin to interact with the devices registered to a Switchbot (https://www.switch-bot.com/) account/hub.
|
||||||
|
|
||||||
|
The difference between this plugin and :class:`platypush.plugins.switchbot.bluetooth.SwitchbotBluetoothPlugin` is
|
||||||
|
that the latter acts like a Bluetooth hub/bridge that interacts directly with your Switchbot devices, while this
|
||||||
|
plugin requires the devices to be connected to a Switchbot Hub and it controls them through your cloud account.
|
||||||
|
|
||||||
|
In order to use this plugin:
|
||||||
|
|
||||||
|
- Set up a Switchbot Hub and configure your devices through the Switchbot app.
|
||||||
|
- Follow the steps on the `Switchbot API repo <https://github.com/OpenWonderLabs/SwitchBotAPI#getting-started>`_
|
||||||
|
to get an API token from the app.
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, api_token: str, **kwargs):
|
||||||
|
"""
|
||||||
|
:param api_token: API token (see
|
||||||
|
`Getting started with the Switchbot API <https://github.com/OpenWonderLabs/SwitchBotAPI#getting-started>`_).
|
||||||
|
"""
|
||||||
|
super().__init__(**kwargs)
|
||||||
|
self._api_token = api_token
|
||||||
|
self._devices_by_id = {}
|
||||||
|
self._devices_by_name = {}
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _url_for(*args, device=None):
|
||||||
|
url = 'https://api.switch-bot.com/v1.0/'
|
||||||
|
if device:
|
||||||
|
url += f'devices/{device["id"]}/'
|
||||||
|
url += '/'.join(args)
|
||||||
|
return url
|
||||||
|
|
||||||
|
def _run(self, method: str = 'get', *args, device=None, **kwargs):
|
||||||
|
response = getattr(requests, method)(self._url_for(*args, device=device), headers={
|
||||||
|
'Authorization': self._api_token,
|
||||||
|
'Accept': 'application/json',
|
||||||
|
'Content-Type': 'application/json; charset=utf-8',
|
||||||
|
}, **kwargs)
|
||||||
|
|
||||||
|
response.raise_for_status()
|
||||||
|
response = response.json()
|
||||||
|
assert response.get('statusCode') == 100, \
|
||||||
|
f'Switchbot API request failed: {response.get("statusCode")}: {response.get("message")}'
|
||||||
|
|
||||||
|
return response.get('body')
|
||||||
|
|
||||||
|
def _get_device(self, device: str, use_cache=True):
|
||||||
|
if not use_cache:
|
||||||
|
self.devices()
|
||||||
|
|
||||||
|
if device in self._devices_by_id:
|
||||||
|
return self._devices_by_id[device]
|
||||||
|
if device in self._devices_by_name:
|
||||||
|
return self._devices_by_name[device]
|
||||||
|
|
||||||
|
assert use_cache, f'Device not found: {device}'
|
||||||
|
return self._get_device(device, use_cache=False)
|
||||||
|
|
||||||
|
@action
|
||||||
|
def devices(self) -> List[dict]:
|
||||||
|
"""
|
||||||
|
Get the list of devices associated to the specified Switchbot API account.
|
||||||
|
|
||||||
|
:return: .. schema:: switchbot.DeviceSchema(many=True)
|
||||||
|
"""
|
||||||
|
devices = DeviceSchema().dump(self._run('get', 'devices').get('deviceList', []), many=True)
|
||||||
|
for device in devices:
|
||||||
|
self._devices_by_id[device['id']] = device
|
||||||
|
self._devices_by_name[device['name']] = device
|
||||||
|
|
||||||
|
return devices
|
||||||
|
|
||||||
|
def _worker(self, q: queue.Queue, method: str = 'get', *args, device=None, **kwargs):
|
||||||
|
try:
|
||||||
|
res = self._run(method, *args, device=device, **kwargs)
|
||||||
|
q.put(DeviceStatusSchema().dump(res))
|
||||||
|
except Exception as e:
|
||||||
|
self.logger.exception(e)
|
||||||
|
q.put(e)
|
||||||
|
|
||||||
|
@action
|
||||||
|
def status(self, device: Optional[str] = None) -> Union[dict, List[dict]]:
|
||||||
|
"""
|
||||||
|
Get the status of all the registered devices or of a specific device.
|
||||||
|
|
||||||
|
:param device: Filter by device ID or name.
|
||||||
|
:return: .. schema:: switchbot.DeviceStatusSchema(many=True)
|
||||||
|
"""
|
||||||
|
# noinspection PyUnresolvedReferences
|
||||||
|
devices = self.devices().output
|
||||||
|
if device:
|
||||||
|
return {
|
||||||
|
**device,
|
||||||
|
**self._run('get', 'status', device=self._get_device(device)),
|
||||||
|
}
|
||||||
|
|
||||||
|
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})
|
||||||
|
for i, dev in enumerate(devices)
|
||||||
|
]
|
||||||
|
|
||||||
|
results = []
|
||||||
|
for worker in workers:
|
||||||
|
worker.start()
|
||||||
|
|
||||||
|
for q in queues:
|
||||||
|
response = q.get()
|
||||||
|
if not response:
|
||||||
|
continue
|
||||||
|
|
||||||
|
assert not isinstance(response, Exception), str(response)
|
||||||
|
results.append({
|
||||||
|
**devices_by_id.get(response.get('id'), {}),
|
||||||
|
**response,
|
||||||
|
})
|
||||||
|
for worker in workers:
|
||||||
|
worker.join()
|
||||||
|
|
||||||
|
return results
|
||||||
|
|
||||||
|
@action
|
||||||
|
def press(self, device: str):
|
||||||
|
"""
|
||||||
|
Send a press-button command to a device.
|
||||||
|
|
||||||
|
:param device: Device name or ID.
|
||||||
|
"""
|
||||||
|
device = self._get_device(device)
|
||||||
|
return self._run('post', 'commands', device=device, json={'command': 'press'})
|
||||||
|
|
||||||
|
@action
|
||||||
|
def toggle(self, device: str, **kwargs):
|
||||||
|
"""
|
||||||
|
Shortcut for :meth:`.press`.
|
||||||
|
|
||||||
|
:param device: Device name or ID.
|
||||||
|
"""
|
||||||
|
return self.press(device)
|
||||||
|
|
||||||
|
@action
|
||||||
|
def on(self, device: str, **kwargs):
|
||||||
|
"""
|
||||||
|
Send a turn-on command to a device
|
||||||
|
|
||||||
|
:param device: Device name or ID.
|
||||||
|
"""
|
||||||
|
device = self._get_device(device)
|
||||||
|
return self._run('post', 'commands', device=device, json={'command': 'turnOn'})
|
||||||
|
|
||||||
|
@action
|
||||||
|
def off(self, device: str, **kwargs):
|
||||||
|
"""
|
||||||
|
Send a turn-off command to a device
|
||||||
|
|
||||||
|
:param device: Device name or ID.
|
||||||
|
"""
|
||||||
|
device = self._get_device(device)
|
||||||
|
return self._run('post', 'commands', device=device, json={'command': 'turnOff'})
|
||||||
|
|
||||||
|
@property
|
||||||
|
def switches(self) -> List[dict]:
|
||||||
|
# noinspection PyUnresolvedReferences
|
||||||
|
return [
|
||||||
|
dev for dev in self.status().output if 'on' in dev
|
||||||
|
]
|
||||||
|
|
||||||
|
@action
|
||||||
|
def set_curtain_position(self, device: str, position: int):
|
||||||
|
"""
|
||||||
|
Set the position of a curtain device.
|
||||||
|
|
||||||
|
:param device: Device name or ID.
|
||||||
|
:param position: An integer between 0 (open) and 100 (closed).
|
||||||
|
"""
|
||||||
|
device = self._get_device(device)
|
||||||
|
return self._run('post', 'commands', device=device, json={
|
||||||
|
'command': 'setPosition',
|
||||||
|
'commandType': 'command',
|
||||||
|
'parameter': f'0,ff,{position}',
|
||||||
|
})
|
||||||
|
|
||||||
|
@action
|
||||||
|
def set_humidifier_efficiency(self, device: str, efficiency: Union[int, str]):
|
||||||
|
"""
|
||||||
|
Set the nebulization efficiency of a humidifier device.
|
||||||
|
|
||||||
|
:param device: Device name or ID.
|
||||||
|
:param efficiency: An integer between 0 (open) and 100 (closed) or `auto`.
|
||||||
|
"""
|
||||||
|
device = self._get_device(device)
|
||||||
|
return self._run('post', 'commands', device=device, json={
|
||||||
|
'command': 'setMode',
|
||||||
|
'commandType': 'command',
|
||||||
|
'parameter': efficiency,
|
||||||
|
})
|
||||||
|
|
||||||
|
@action
|
||||||
|
def set_fan(self, device: str, speed: Optional[int] = None, swing_range: Optional[int] = None,
|
||||||
|
mode: Optional[int] = None):
|
||||||
|
"""
|
||||||
|
Set properties of a smart fan device.
|
||||||
|
|
||||||
|
:param device: Device name or ID.
|
||||||
|
:param speed: Speed between 1 and 4.
|
||||||
|
:param swing_range: Swing range angle, between 0 and 120.
|
||||||
|
:param mode: Fan mode (1 or 2).
|
||||||
|
"""
|
||||||
|
# noinspection PyUnresolvedReferences
|
||||||
|
status = self.status(device=device).output
|
||||||
|
|
||||||
|
if speed is None:
|
||||||
|
speed = status.get('speed')
|
||||||
|
if mode is None:
|
||||||
|
mode = status.get('mode')
|
||||||
|
if swing_range is None:
|
||||||
|
swing_range = status.get('swing_range')
|
||||||
|
|
||||||
|
return self._run('post', 'commands', device=device, json={
|
||||||
|
'command': 'set',
|
||||||
|
'commandType': 'command',
|
||||||
|
'parameter': ','.join(['on', str(mode), str(speed), str(swing_range)]),
|
||||||
|
})
|
||||||
|
|
||||||
|
@action
|
||||||
|
def scenes(self) -> List[dict]:
|
||||||
|
"""
|
||||||
|
Get the list of registered scenes.
|
||||||
|
|
||||||
|
:return: .. schema:: switchbot.SceneSchema(many=True)
|
||||||
|
"""
|
||||||
|
return SceneSchema().dump(self._run('get', 'scenes'), many=True)
|
||||||
|
|
||||||
|
@action
|
||||||
|
def run_scene(self, scene: str):
|
||||||
|
"""
|
||||||
|
Execute a scene.
|
||||||
|
|
||||||
|
:param scene: Scene ID or name.
|
||||||
|
"""
|
||||||
|
# noinspection PyUnresolvedReferences
|
||||||
|
scenes = [
|
||||||
|
s for s in self.scenes().output
|
||||||
|
if s.get('id') == scene or s.get('name') == scene
|
||||||
|
]
|
||||||
|
|
||||||
|
assert scenes, f'No such scene: {scene}'
|
||||||
|
return self._run('post', 'scenes', scenes[0]['id'], 'execute')
|
||||||
|
|
||||||
|
|
||||||
|
# vim:sw=4:ts=4:et:
|
|
@ -8,10 +8,10 @@ from platypush.plugins.bluetooth.ble import BluetoothBlePlugin
|
||||||
from platypush.plugins.switch import SwitchPlugin
|
from platypush.plugins.switch import SwitchPlugin
|
||||||
|
|
||||||
|
|
||||||
class SwitchSwitchbotPlugin(SwitchPlugin, BluetoothBlePlugin):
|
class SwitchbotBluetoothPlugin(SwitchPlugin, BluetoothBlePlugin):
|
||||||
"""
|
"""
|
||||||
Plugin to interact with a Switchbot (https://www.switch-bot.com/) device and
|
Plugin to interact with a Switchbot (https://www.switch-bot.com/) device and
|
||||||
programmatically control buttons.
|
programmatically control switches over a Bluetooth interface.
|
||||||
|
|
||||||
See :class:`platypush.plugins.bluetooth.ble.BluetoothBlePlugin` for how to enable BLE permissions for
|
See :class:`platypush.plugins.bluetooth.ble.BluetoothBlePlugin` for how to enable BLE permissions for
|
||||||
the platypush user (a simple solution may be to run it as root, but that's usually NOT a good idea).
|
the platypush user (a simple solution may be to run it as root, but that's usually NOT a good idea).
|
||||||
|
@ -21,6 +21,7 @@ class SwitchSwitchbotPlugin(SwitchPlugin, BluetoothBlePlugin):
|
||||||
* **pybluez** (``pip install pybluez``)
|
* **pybluez** (``pip install pybluez``)
|
||||||
* **gattlib** (``pip install gattlib``)
|
* **gattlib** (``pip install gattlib``)
|
||||||
* **libboost** (on Debian ```apt-get install libboost-python-dev libboost-thread-dev``)
|
* **libboost** (on Debian ```apt-get install libboost-python-dev libboost-thread-dev``)
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
uuid = 'cba20002-224d-11e6-9fb8-0002a5d5c51b'
|
uuid = 'cba20002-224d-11e6-9fb8-0002a5d5c51b'
|
|
@ -53,6 +53,7 @@ class ZwavePlugin(ZwaveBasePlugin, SwitchPlugin):
|
||||||
def status(self) -> Dict[str, Any]:
|
def status(self) -> Dict[str, Any]:
|
||||||
"""
|
"""
|
||||||
Get the status of the controller.
|
Get the status of the controller.
|
||||||
|
|
||||||
:return: dict
|
:return: dict
|
||||||
"""
|
"""
|
||||||
backend = self._get_backend()
|
backend = self._get_backend()
|
||||||
|
|
8
platypush/schemas/switch.py
Normal file
8
platypush/schemas/switch.py
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
from marshmallow import fields
|
||||||
|
from marshmallow.schema import Schema
|
||||||
|
|
||||||
|
|
||||||
|
class SwitchStatusSchema(Schema):
|
||||||
|
id = fields.Raw(metadata=dict(description='Device unique ID'))
|
||||||
|
name = fields.String(required=True, metadata=dict(description='Device name'))
|
||||||
|
on = fields.Boolean(required=True, metadata=dict(description='True if the device is on, False otherwise'))
|
80
platypush/schemas/switchbot.py
Normal file
80
platypush/schemas/switchbot.py
Normal file
|
@ -0,0 +1,80 @@
|
||||||
|
from marshmallow import fields
|
||||||
|
from marshmallow.schema import Schema
|
||||||
|
from marshmallow.validate import OneOf
|
||||||
|
|
||||||
|
|
||||||
|
device_types = [
|
||||||
|
'Hub',
|
||||||
|
'Hub Plus',
|
||||||
|
'Hub Mini',
|
||||||
|
'Bot',
|
||||||
|
'Curtain',
|
||||||
|
'Plug',
|
||||||
|
'Meter',
|
||||||
|
'Humidifier',
|
||||||
|
'Smart Fan',
|
||||||
|
'Air Conditioner',
|
||||||
|
'TV',
|
||||||
|
'Light',
|
||||||
|
'IPTV / Streamer',
|
||||||
|
'Set Top Box',
|
||||||
|
'DVD',
|
||||||
|
'Fan',
|
||||||
|
'Projector',
|
||||||
|
'Camera',
|
||||||
|
'Air Purifier',
|
||||||
|
'Speaker',
|
||||||
|
'Water Heater',
|
||||||
|
'Vacuum Cleaner',
|
||||||
|
'Others',
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
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'))
|
||||||
|
type = fields.String(attribute='deviceType', required=True, validate=OneOf(device_types),
|
||||||
|
metadata=dict(description=f'Supported types: [{", ".join(device_types)}]'))
|
||||||
|
hub_id = fields.String(attribute='hubDeviceId', metadata=dict(description='Parent hub device unique ID'))
|
||||||
|
cloud_service_enabled = fields.Boolean(attribute='enableCloudService',
|
||||||
|
metadata=dict(description='True if cloud access is enabled on this device,'
|
||||||
|
'False otherwise. Only cloud-enabled devices can be '
|
||||||
|
'controlled from the switchbot plugin.'))
|
||||||
|
calibrated = fields.Boolean(attribute='calibrate',
|
||||||
|
metadata=dict(description='[Curtain devices only] Set to True if the device has '
|
||||||
|
'been calibrated, False otherwise'))
|
||||||
|
open_direction = fields.String(attribute='openDirection',
|
||||||
|
metadata=dict(description='[Curtain devices only] Direction where the curtains will '
|
||||||
|
'be opened ("left" or "right")'))
|
||||||
|
|
||||||
|
|
||||||
|
class DeviceStatusSchema(DeviceSchema):
|
||||||
|
on = fields.Boolean(attribute='power', metadata=dict(description='True if the device is on, False otherwise'))
|
||||||
|
moving = fields.Boolean(metadata=dict(
|
||||||
|
description='[Curtain devices only] True if the device is moving, False otherwise'))
|
||||||
|
position = fields.Int(attribute='slidePosition', metadata=dict(
|
||||||
|
description='[Curtain devices only] Position of the device on the curtain rail, between '
|
||||||
|
'0 (open) and 1 (closed)'))
|
||||||
|
temperature = fields.Float(metadata=dict(description='[Meter/humidifier devices only] Temperature in Celsius'))
|
||||||
|
humidity = fields.Float(metadata=dict(description='[Meter/humidifier devices only] Humidity in %'))
|
||||||
|
nebulization_efficiency = fields.Float(attribute='nebulizationEfficiency',
|
||||||
|
metadata=dict(description='[Humidifier devices only] Nebulization '
|
||||||
|
'efficiency in %'))
|
||||||
|
auto = fields.Boolean(metadata=dict(description='[Humidifier devices only] True if auto mode is on'))
|
||||||
|
child_lock = fields.Boolean(attribute='childLock',
|
||||||
|
metadata=dict(description='[Humidifier devices only] True if safety lock is on'))
|
||||||
|
sound = fields.Boolean(metadata=dict(description='[Humidifier devices only] True if sound is muted'))
|
||||||
|
mode = fields.Int(metadata=dict(description='[Smart fan devices only] Fan mode'))
|
||||||
|
speed = fields.Float(metadata=dict(description='[Smart fan devices only] Fan speed, between 1 and 4'))
|
||||||
|
swinging = fields.Boolean(attribute='shaking',
|
||||||
|
metadata=dict(description='[Smart fan devices only] True if the device is swinging'))
|
||||||
|
swing_direction = fields.Int(attribute='shakeCenter',
|
||||||
|
metadata=dict(description='[Smart fan devices only] Swing direction'))
|
||||||
|
swing_range = fields.Float(attribute='shakeRange',
|
||||||
|
metadata=dict(description='[Smart fan devices only] Swing range angle, '
|
||||||
|
'between 0 and 120'))
|
||||||
|
|
||||||
|
|
||||||
|
class SceneSchema(Schema):
|
||||||
|
id = fields.String(attribute='sceneId', required=True, metadata=dict(description='Scene ID'))
|
||||||
|
name = fields.String(attribute='sceneName', metadata=dict(description='Scene name'))
|
Loading…
Reference in a new issue