forked from platypush/platypush
Added support for virtual IR devices in Switchbot plugin
This commit is contained in:
parent
d844890ab2
commit
31ef9515f8
2 changed files with 404 additions and 48 deletions
|
@ -75,7 +75,21 @@ class SwitchbotPlugin(SwitchPlugin):
|
||||||
|
|
||||||
:return: .. schema:: switchbot.DeviceSchema(many=True)
|
:return: .. schema:: switchbot.DeviceSchema(many=True)
|
||||||
"""
|
"""
|
||||||
devices = DeviceSchema().dump(self._run('get', 'devices').get('deviceList', []), many=True)
|
devices = self._run('get', 'devices')
|
||||||
|
devices = [
|
||||||
|
DeviceSchema().dump({
|
||||||
|
**device,
|
||||||
|
'is_virtual': False,
|
||||||
|
})
|
||||||
|
for device in devices.get('deviceList', [])
|
||||||
|
] + [
|
||||||
|
DeviceSchema().dump({
|
||||||
|
**device,
|
||||||
|
'is_virtual': True,
|
||||||
|
})
|
||||||
|
for device in devices.get('infraredRemoteList', [])
|
||||||
|
]
|
||||||
|
|
||||||
for device in devices:
|
for device in devices:
|
||||||
self._devices_by_id[device['id']] = device
|
self._devices_by_id[device['id']] = device
|
||||||
self._devices_by_name[device['name']] = device
|
self._devices_by_name[device['name']] = device
|
||||||
|
@ -127,6 +141,7 @@ class SwitchbotPlugin(SwitchPlugin):
|
||||||
**devices_by_id.get(response.get('id'), {}),
|
**devices_by_id.get(response.get('id'), {}),
|
||||||
**response,
|
**response,
|
||||||
})
|
})
|
||||||
|
|
||||||
for worker in workers:
|
for worker in workers:
|
||||||
worker.join()
|
worker.join()
|
||||||
|
|
||||||
|
@ -209,32 +224,297 @@ class SwitchbotPlugin(SwitchPlugin):
|
||||||
})
|
})
|
||||||
|
|
||||||
@action
|
@action
|
||||||
def set_fan(self, device: str, speed: Optional[int] = None, swing_range: Optional[int] = None,
|
def set_fan_speed(self, device: str, speed: int):
|
||||||
mode: Optional[int] = None):
|
|
||||||
"""
|
"""
|
||||||
Set properties of a smart fan device.
|
Set the speed of a fan.
|
||||||
|
|
||||||
:param device: Device name or ID.
|
:param device: Device name or ID.
|
||||||
:param speed: Speed between 1 and 4.
|
: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
|
# noinspection PyUnresolvedReferences
|
||||||
status = self.status(device=device).output
|
status = self.status(device=device).output
|
||||||
|
|
||||||
if speed is None:
|
|
||||||
speed = status.get('speed')
|
|
||||||
if mode is None:
|
|
||||||
mode = status.get('mode')
|
mode = status.get('mode')
|
||||||
if swing_range is None:
|
|
||||||
swing_range = status.get('swing_range')
|
swing_range = status.get('swing_range')
|
||||||
|
|
||||||
return self._run('post', 'commands', device=device, json={
|
return self._run('post', 'commands', device=device, json={
|
||||||
'command': 'set',
|
'command': 'set',
|
||||||
'commandType': 'command',
|
'commandType': 'command',
|
||||||
'parameter': ','.join(['on', str(mode), str(speed), str(swing_range)]),
|
'parameter': ','.join(['on', str(mode), str(speed), str(swing_range)]),
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@action
|
||||||
|
def set_fan_mode(self, device: str, mode: int):
|
||||||
|
"""
|
||||||
|
Set the mode of a fan.
|
||||||
|
|
||||||
|
:param device: Device name or ID.
|
||||||
|
:param mode: Fan mode (1 or 2).
|
||||||
|
"""
|
||||||
|
# noinspection PyUnresolvedReferences
|
||||||
|
status = self.status(device=device).output
|
||||||
|
speed = status.get('speed')
|
||||||
|
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 set_swing_range(self, device: str, swing_range: int):
|
||||||
|
"""
|
||||||
|
Set the swing range of a fan.
|
||||||
|
|
||||||
|
:param device: Device name or ID.
|
||||||
|
:param swing_range: Swing range angle, between 0 and 120.
|
||||||
|
"""
|
||||||
|
# noinspection PyUnresolvedReferences
|
||||||
|
status = self.status(device=device).output
|
||||||
|
speed = status.get('speed')
|
||||||
|
mode = status.get('mode')
|
||||||
|
return self._run('post', 'commands', device=device, json={
|
||||||
|
'command': 'set',
|
||||||
|
'commandType': 'command',
|
||||||
|
'parameter': ','.join(['on', str(mode), str(speed), str(swing_range)]),
|
||||||
|
})
|
||||||
|
|
||||||
|
@action
|
||||||
|
def set_temperature(self, device: str, temperature: float):
|
||||||
|
"""
|
||||||
|
Set the temperature of an air conditioner.
|
||||||
|
|
||||||
|
:param device: Device name or ID.
|
||||||
|
:param temperature: Temperature, in Celsius.
|
||||||
|
"""
|
||||||
|
# noinspection PyUnresolvedReferences
|
||||||
|
status = self.status(device=device).output
|
||||||
|
mode = status.get('mode')
|
||||||
|
fan_speed = status.get('fan_speed')
|
||||||
|
return self._run('post', 'commands', device=device, json={
|
||||||
|
'command': 'setAll',
|
||||||
|
'commandType': 'command',
|
||||||
|
'parameter': ','.join([str(temperature), str(mode), str(fan_speed), 'on']),
|
||||||
|
})
|
||||||
|
|
||||||
|
@action
|
||||||
|
def set_ac_mode(self, device: str, mode: int):
|
||||||
|
"""
|
||||||
|
Set the mode of an air conditioner.
|
||||||
|
|
||||||
|
:param device: Device name or ID.
|
||||||
|
:param mode: Air conditioner mode. Supported values:
|
||||||
|
|
||||||
|
* 1: ``auto``
|
||||||
|
* 2: ``cool``
|
||||||
|
* 3: ``dry``
|
||||||
|
* 4: ``fan``
|
||||||
|
* 5: ``heat``
|
||||||
|
|
||||||
|
"""
|
||||||
|
# noinspection PyUnresolvedReferences
|
||||||
|
status = self.status(device=device).output
|
||||||
|
temperature = status.get('temperature')
|
||||||
|
fan_speed = status.get('fan_speed')
|
||||||
|
return self._run('post', 'commands', device=device, json={
|
||||||
|
'command': 'setAll',
|
||||||
|
'commandType': 'command',
|
||||||
|
'parameter': ','.join([str(temperature), str(mode), str(fan_speed), 'on']),
|
||||||
|
})
|
||||||
|
|
||||||
|
@action
|
||||||
|
def set_ac_fan_speed(self, device: str, fan_speed: int):
|
||||||
|
"""
|
||||||
|
Set the fan speed for an air conditioner.
|
||||||
|
|
||||||
|
:param device: Device name or ID.
|
||||||
|
:param fan_speed: Possible values:
|
||||||
|
|
||||||
|
* 1: ``auto``
|
||||||
|
* 2: ``low``
|
||||||
|
* 3: ``medium``
|
||||||
|
* 4: ``high``
|
||||||
|
|
||||||
|
"""
|
||||||
|
# noinspection PyUnresolvedReferences
|
||||||
|
status = self.status(device=device).output
|
||||||
|
temperature = status.get('temperature')
|
||||||
|
mode = status.get('mode')
|
||||||
|
return self._run('post', 'commands', device=device, json={
|
||||||
|
'command': 'setAll',
|
||||||
|
'commandType': 'command',
|
||||||
|
'parameter': ','.join([str(temperature), str(mode), str(fan_speed), 'on']),
|
||||||
|
})
|
||||||
|
|
||||||
|
@action
|
||||||
|
def set_channel(self, device: str, channel: int):
|
||||||
|
"""
|
||||||
|
Set the channel on a TV, IPTV/Streamer, Set Top Box device.
|
||||||
|
|
||||||
|
:param device: Device name or ID.
|
||||||
|
:param channel: Channel number.
|
||||||
|
"""
|
||||||
|
device = self._get_device(device)
|
||||||
|
return self._run('post', 'commands', device=device, json={
|
||||||
|
'command': 'SetChannel',
|
||||||
|
'commandType': 'command',
|
||||||
|
'parameter': [str(channel)],
|
||||||
|
})
|
||||||
|
|
||||||
|
@action
|
||||||
|
def volup(self, device: str):
|
||||||
|
"""
|
||||||
|
Send volume up IR event to a device (for TV, IPTV/Streamer, Set Top Box, DVD and Speaker).
|
||||||
|
|
||||||
|
:param device: Device name or ID.
|
||||||
|
"""
|
||||||
|
device = self._get_device(device)
|
||||||
|
return self._run('post', 'commands', device=device, json={
|
||||||
|
'command': 'volumeAdd',
|
||||||
|
'commandType': 'command',
|
||||||
|
})
|
||||||
|
|
||||||
|
@action
|
||||||
|
def voldown(self, device: str):
|
||||||
|
"""
|
||||||
|
Send volume down IR event to a device (for TV, IPTV/Streamer, Set Top Box, DVD and Speaker).
|
||||||
|
|
||||||
|
:param device: Device name or ID.
|
||||||
|
"""
|
||||||
|
device = self._get_device(device)
|
||||||
|
return self._run('post', 'commands', device=device, json={
|
||||||
|
'command': 'volumeSub',
|
||||||
|
'commandType': 'command',
|
||||||
|
})
|
||||||
|
|
||||||
|
@action
|
||||||
|
def mute(self, device: str):
|
||||||
|
"""
|
||||||
|
Send mute/unmute IR event to a device (for TV, IPTV/Streamer, Set Top Box, DVD and Speaker).
|
||||||
|
|
||||||
|
:param device: Device name or ID.
|
||||||
|
"""
|
||||||
|
device = self._get_device(device)
|
||||||
|
return self._run('post', 'commands', device=device, json={
|
||||||
|
'command': 'setMute',
|
||||||
|
'commandType': 'command',
|
||||||
|
})
|
||||||
|
|
||||||
|
@action
|
||||||
|
def channel_next(self, device: str):
|
||||||
|
"""
|
||||||
|
Send next channel IR event to a device (for TV, IPTV/Streamer, and Set Top Box).
|
||||||
|
|
||||||
|
:param device: Device name or ID.
|
||||||
|
"""
|
||||||
|
device = self._get_device(device)
|
||||||
|
return self._run('post', 'commands', device=device, json={
|
||||||
|
'command': 'channelAdd',
|
||||||
|
'commandType': 'command',
|
||||||
|
})
|
||||||
|
|
||||||
|
@action
|
||||||
|
def channel_prev(self, device: str):
|
||||||
|
"""
|
||||||
|
Send previous channel IR event to a device (for TV, IPTV/Streamer, and Set Top Box).
|
||||||
|
|
||||||
|
:param device: Device name or ID.
|
||||||
|
"""
|
||||||
|
device = self._get_device(device)
|
||||||
|
return self._run('post', 'commands', device=device, json={
|
||||||
|
'command': 'channelSub',
|
||||||
|
'commandType': 'command',
|
||||||
|
})
|
||||||
|
|
||||||
|
@action
|
||||||
|
def play(self, device: str):
|
||||||
|
"""
|
||||||
|
Send play IR event to a device (for DVD and Speaker).
|
||||||
|
|
||||||
|
:param device: Device name or ID.
|
||||||
|
"""
|
||||||
|
device = self._get_device(device)
|
||||||
|
return self._run('post', 'commands', device=device, json={
|
||||||
|
'command': 'Play',
|
||||||
|
'commandType': 'command',
|
||||||
|
})
|
||||||
|
|
||||||
|
@action
|
||||||
|
def pause(self, device: str):
|
||||||
|
"""
|
||||||
|
Send pause IR event to a device (for DVD and Speaker).
|
||||||
|
|
||||||
|
:param device: Device name or ID.
|
||||||
|
"""
|
||||||
|
device = self._get_device(device)
|
||||||
|
return self._run('post', 'commands', device=device, json={
|
||||||
|
'command': 'Pause',
|
||||||
|
'commandType': 'command',
|
||||||
|
})
|
||||||
|
|
||||||
|
@action
|
||||||
|
def stop(self, device: str):
|
||||||
|
"""
|
||||||
|
Send stop IR event to a device (for DVD and Speaker).
|
||||||
|
|
||||||
|
:param device: Device name or ID.
|
||||||
|
"""
|
||||||
|
device = self._get_device(device)
|
||||||
|
return self._run('post', 'commands', device=device, json={
|
||||||
|
'command': 'Stop',
|
||||||
|
'commandType': 'command',
|
||||||
|
})
|
||||||
|
|
||||||
|
@action
|
||||||
|
def forward(self, device: str):
|
||||||
|
"""
|
||||||
|
Send forward IR event to a device (for DVD and Speaker).
|
||||||
|
|
||||||
|
:param device: Device name or ID.
|
||||||
|
"""
|
||||||
|
device = self._get_device(device)
|
||||||
|
return self._run('post', 'commands', device=device, json={
|
||||||
|
'command': 'FastForward',
|
||||||
|
'commandType': 'command',
|
||||||
|
})
|
||||||
|
|
||||||
|
@action
|
||||||
|
def back(self, device: str):
|
||||||
|
"""
|
||||||
|
Send backward IR event to a device (for DVD and Speaker).
|
||||||
|
|
||||||
|
:param device: Device name or ID.
|
||||||
|
"""
|
||||||
|
device = self._get_device(device)
|
||||||
|
return self._run('post', 'commands', device=device, json={
|
||||||
|
'command': 'Rewind',
|
||||||
|
'commandType': 'command',
|
||||||
|
})
|
||||||
|
|
||||||
|
@action
|
||||||
|
def next(self, device: str):
|
||||||
|
"""
|
||||||
|
Send next IR event to a device (for DVD and Speaker).
|
||||||
|
|
||||||
|
:param device: Device name or ID.
|
||||||
|
"""
|
||||||
|
device = self._get_device(device)
|
||||||
|
return self._run('post', 'commands', device=device, json={
|
||||||
|
'command': 'Next',
|
||||||
|
'commandType': 'command',
|
||||||
|
})
|
||||||
|
|
||||||
|
@action
|
||||||
|
def previous(self, device: str):
|
||||||
|
"""
|
||||||
|
Send previous IR event to a device (for DVD and Speaker).
|
||||||
|
|
||||||
|
:param device: Device name or ID.
|
||||||
|
"""
|
||||||
|
device = self._get_device(device)
|
||||||
|
return self._run('post', 'commands', device=device, json={
|
||||||
|
'command': 'Previous',
|
||||||
|
'commandType': 'command',
|
||||||
|
})
|
||||||
|
|
||||||
@action
|
@action
|
||||||
def scenes(self) -> List[dict]:
|
def scenes(self) -> List[dict]:
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -29,50 +29,126 @@ device_types = [
|
||||||
'Others',
|
'Others',
|
||||||
]
|
]
|
||||||
|
|
||||||
|
remote_types = [
|
||||||
|
'Air Conditioner',
|
||||||
|
'TV',
|
||||||
|
'Light',
|
||||||
|
'IPTV / Streamer',
|
||||||
|
'Set Top Box',
|
||||||
|
'DVD',
|
||||||
|
'Fan',
|
||||||
|
'Projector',
|
||||||
|
'Camera',
|
||||||
|
'Air Purifier',
|
||||||
|
'Speaker',
|
||||||
|
'Water Heater',
|
||||||
|
'Vacuum Cleaner',
|
||||||
|
'Others',
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
class DeviceSchema(Schema):
|
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'))
|
||||||
type = fields.String(attribute='deviceType', required=True, validate=OneOf(device_types),
|
device_type = fields.String(
|
||||||
metadata=dict(description=f'Supported types: [{", ".join(device_types)}]'))
|
attribute='deviceType', validate=OneOf(device_types),
|
||||||
|
metadata=dict(description=f'Supported types: [{", ".join(device_types)}]')
|
||||||
|
)
|
||||||
|
remote_type = fields.String(
|
||||||
|
attribute='remoteType', validate=OneOf(remote_types),
|
||||||
|
metadata=dict(description=f'Supported 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(attribute='enableCloudService',
|
cloud_service_enabled = fields.Boolean(
|
||||||
metadata=dict(description='True if cloud access is enabled on this device,'
|
attribute='enableCloudService',
|
||||||
|
metadata=dict(
|
||||||
|
description='True if cloud access is enabled on this device,'
|
||||||
'False otherwise. Only cloud-enabled devices can be '
|
'False otherwise. Only cloud-enabled devices can be '
|
||||||
'controlled from the switchbot plugin.'))
|
'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'))
|
is_calibrated = fields.Boolean(
|
||||||
open_direction = fields.String(attribute='openDirection',
|
attribute='calibrate',
|
||||||
metadata=dict(description='[Curtain devices only] Direction where the curtains will '
|
metadata=dict(
|
||||||
'be opened ("left" or "right")'))
|
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")'
|
||||||
|
)
|
||||||
|
)
|
||||||
|
is_virtual = fields.Boolean(
|
||||||
|
metadata=dict(
|
||||||
|
description='True if this is a virtual device, i.e. a device with an IR remote configuration but not '
|
||||||
|
'managed directly by the Switchbot bridge'
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class DeviceStatusSchema(DeviceSchema):
|
class DeviceStatusSchema(DeviceSchema):
|
||||||
on = fields.Boolean(attribute='power', metadata=dict(description='True if the device is on, False otherwise'))
|
on = fields.Boolean(attribute='power', metadata=dict(description='True if the device is on, False otherwise'))
|
||||||
moving = fields.Boolean(metadata=dict(
|
moving = fields.Boolean(
|
||||||
description='[Curtain devices only] True if the device is moving, False otherwise'))
|
metadata=dict(
|
||||||
position = fields.Int(attribute='slidePosition', 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 '
|
description='[Curtain devices only] Position of the device on the curtain rail, between '
|
||||||
'0 (open) and 1 (closed)'))
|
'0 (open) and 1 (closed)'
|
||||||
temperature = fields.Float(metadata=dict(description='[Meter/humidifier devices only] Temperature in Celsius'))
|
)
|
||||||
|
)
|
||||||
|
temperature = fields.Float(
|
||||||
|
metadata=dict(description='[Meter/humidifier/Air conditioner devices only] Temperature in Celsius')
|
||||||
|
)
|
||||||
humidity = fields.Float(metadata=dict(description='[Meter/humidifier devices only] Humidity in %'))
|
humidity = fields.Float(metadata=dict(description='[Meter/humidifier devices only] Humidity in %'))
|
||||||
nebulization_efficiency = fields.Float(attribute='nebulizationEfficiency',
|
fan_speed = fields.Int(
|
||||||
metadata=dict(description='[Humidifier devices only] Nebulization '
|
metadata=dict(description='[Air conditioner devices only] Speed of the fan')
|
||||||
'efficiency in %'))
|
)
|
||||||
auto = fields.Boolean(metadata=dict(description='[Humidifier devices only] True if auto mode is on'))
|
nebulization_efficiency = fields.Float(
|
||||||
child_lock = fields.Boolean(attribute='childLock',
|
attribute='nebulizationEfficiency',
|
||||||
metadata=dict(description='[Humidifier devices only] True if safety lock is on'))
|
metadata=dict(
|
||||||
sound = fields.Boolean(metadata=dict(description='[Humidifier devices only] True if sound is muted'))
|
description='[Humidifier devices only] Nebulization efficiency in %'
|
||||||
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',
|
auto = fields.Boolean(
|
||||||
metadata=dict(description='[Smart fan devices only] True if the device is swinging'))
|
metadata=dict(
|
||||||
swing_direction = fields.Int(attribute='shakeCenter',
|
description='[Humidifier devices only] True if auto mode is on'
|
||||||
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, '
|
child_lock = fields.Boolean(
|
||||||
'between 0 and 120'))
|
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='[Fan/Air conditioner 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):
|
class SceneSchema(Schema):
|
||||||
|
|
Loading…
Reference in a new issue