forked from platypush/platypush
567 lines
17 KiB
Python
567 lines
17 KiB
Python
from enum import Enum
|
|
from typing import Any, Callable, List, Optional, Type, Union
|
|
|
|
from pysmartthings import Attribute, Capability, Command, DeviceEntity
|
|
|
|
from platypush.entities import Entity
|
|
|
|
from platypush.entities.audio import Muted, Volume
|
|
from platypush.entities.batteries import Battery
|
|
from platypush.entities.dimmers import Dimmer
|
|
from platypush.entities.electricity import EnergySensor, PowerSensor, VoltageSensor
|
|
from platypush.entities.humidity import HumiditySensor
|
|
from platypush.entities.illuminance import IlluminanceSensor
|
|
from platypush.entities.linkquality import LinkQuality
|
|
from platypush.entities.motion import MotionSensor
|
|
from platypush.entities.sensors import (
|
|
BinarySensor,
|
|
EnumSensor,
|
|
CompositeSensor,
|
|
NumericSensor,
|
|
)
|
|
from platypush.entities.switches import EnumSwitch, Switch
|
|
from platypush.entities.temperature import TemperatureSensor
|
|
|
|
|
|
class DeviceMapper:
|
|
"""
|
|
The purpose of these objects is to map the capabilities of SmartThings
|
|
devices to native Platypush entities.
|
|
"""
|
|
|
|
def __init__(
|
|
self,
|
|
entity_type: Type[Entity],
|
|
capability: str,
|
|
attribute: str,
|
|
value_type: Union[Type, Enum, str],
|
|
set_command: Optional[Union[str, Callable[[Any], str]]] = None,
|
|
get_value: Optional[Callable[[DeviceEntity], Any]] = None,
|
|
set_value_args: Optional[Callable[..., Any]] = None,
|
|
**kwargs
|
|
):
|
|
self.entity_type = entity_type
|
|
self.capability = capability
|
|
self.set_command = set_command
|
|
self.attribute = attribute
|
|
self.value_type = value_type
|
|
self.get_value = get_value if get_value else self._default_get_value
|
|
self.values: List[str] = []
|
|
self.entity_args = kwargs
|
|
|
|
if isinstance(value_type, Enum):
|
|
self.values = [v.name for v in entity_type] # type: ignore
|
|
|
|
self.set_value_args = (
|
|
set_value_args if set_value_args else self._default_set_value_args
|
|
)
|
|
|
|
def _cast_value(self, value: Any) -> Any:
|
|
if not isinstance(self.value_type, (str, Enum)):
|
|
try:
|
|
value = self.value_type(value)
|
|
except Exception:
|
|
# Set the value to None in case of cast errors
|
|
value = None
|
|
|
|
return value
|
|
|
|
def _default_get_value(self, device: DeviceEntity) -> Any:
|
|
if hasattr(device.status, self.attribute):
|
|
value = getattr(device.status, self.attribute)
|
|
else:
|
|
value = device.status.attributes[self.attribute].value
|
|
|
|
return self._cast_value(value)
|
|
|
|
def _default_set_value_args(self, *values: Any) -> List[Any]:
|
|
return [self._cast_value(v) for v in values]
|
|
|
|
|
|
device_mappers: List[DeviceMapper] = [
|
|
# acceleration_sensor
|
|
DeviceMapper(
|
|
entity_type=BinarySensor,
|
|
capability=Capability.acceleration_sensor,
|
|
attribute=Attribute.acceleration,
|
|
value_type=bool,
|
|
),
|
|
# air_conditioner_fan_mode
|
|
DeviceMapper(
|
|
entity_type=EnumSwitch,
|
|
capability=Capability.air_conditioner_fan_mode,
|
|
attribute=Attribute.fan_mode,
|
|
value_type=Attribute.supported_ac_fan_modes,
|
|
set_command=Command.set_fan_mode,
|
|
),
|
|
# air_conditioner_mode
|
|
DeviceMapper(
|
|
entity_type=EnumSwitch,
|
|
capability=Capability.air_conditioner_mode,
|
|
attribute=Attribute.air_conditioner_mode,
|
|
value_type=Attribute.supported_ac_modes,
|
|
set_command=Command.set_air_conditioner_mode,
|
|
),
|
|
# air_quality_sensor
|
|
DeviceMapper(
|
|
entity_type=NumericSensor,
|
|
capability=Capability.air_quality_sensor,
|
|
attribute=Attribute.air_quality,
|
|
value_type=int,
|
|
),
|
|
# alarm
|
|
DeviceMapper(
|
|
entity_type=EnumSwitch,
|
|
capability=Capability.alarm,
|
|
attribute=Attribute.alarm,
|
|
value_type=Enum('AlarmValues', ['both', 'off', 'siren', 'strobe']),
|
|
set_command=lambda value: value,
|
|
set_value_args=lambda *_: [],
|
|
),
|
|
# audio_mute
|
|
DeviceMapper(
|
|
entity_type=Muted,
|
|
capability=Capability.audio_mute,
|
|
attribute=Attribute.mute,
|
|
value_type=bool,
|
|
set_command=lambda value: Command.mute if value else Command.unmute,
|
|
set_value_args=lambda *_: [],
|
|
),
|
|
# audio_volume
|
|
DeviceMapper(
|
|
entity_type=Volume,
|
|
capability=Capability.audio_volume,
|
|
attribute=Attribute.volume,
|
|
value_type=int,
|
|
set_command=Command.set_volume,
|
|
),
|
|
# battery
|
|
DeviceMapper(
|
|
entity_type=Battery,
|
|
capability=Capability.battery,
|
|
attribute=Attribute.battery,
|
|
value_type=int,
|
|
),
|
|
# body_mass_index_measurement
|
|
DeviceMapper(
|
|
entity_type=NumericSensor,
|
|
capability=Capability.body_mass_index_measurement,
|
|
attribute=Attribute.body_weight_measurement,
|
|
value_type=float,
|
|
),
|
|
# body_weight_measurement
|
|
DeviceMapper(
|
|
entity_type=NumericSensor,
|
|
capability=Capability.body_weight_measurement,
|
|
attribute=Attribute.body_weight_measurement,
|
|
value_type=float,
|
|
),
|
|
# button
|
|
DeviceMapper(
|
|
entity_type=EnumSensor,
|
|
capability=Capability.button,
|
|
attribute=Attribute.button,
|
|
value_type=Enum(
|
|
'ButtonValues',
|
|
[
|
|
'pushed',
|
|
'held',
|
|
'double',
|
|
'pushed_2x',
|
|
'pushed_3x',
|
|
'pushed_4x',
|
|
'pushed_5x',
|
|
'pushed_6x',
|
|
'down',
|
|
'down_2x',
|
|
'down_3x',
|
|
'down_4x',
|
|
'down_5x',
|
|
'down_6x',
|
|
'down_hold',
|
|
'up',
|
|
'up_2x',
|
|
'up_3x',
|
|
'up_4x',
|
|
'up_5x',
|
|
'up_6x',
|
|
'up_hold',
|
|
'swipe_up',
|
|
'swipe_down',
|
|
'swipe_left',
|
|
'swipe_right',
|
|
'unknown',
|
|
],
|
|
),
|
|
),
|
|
# carbon_dioxide_measurement
|
|
DeviceMapper(
|
|
entity_type=NumericSensor,
|
|
capability=Capability.carbon_dioxide_measurement,
|
|
attribute=Attribute.carbon_dioxide,
|
|
value_type=float,
|
|
),
|
|
# carbon_monoxide_detector
|
|
DeviceMapper(
|
|
entity_type=EnumSensor,
|
|
capability=Capability.carbon_monoxide_detector,
|
|
attribute=Attribute.carbon_monoxide,
|
|
value_type=Enum(
|
|
'CarbonMonoxideValues', ['clear', 'detected', 'tested', 'unknown']
|
|
),
|
|
),
|
|
# carbon_monoxide_measurement
|
|
DeviceMapper(
|
|
entity_type=NumericSensor,
|
|
capability=Capability.carbon_monoxide_measurement,
|
|
attribute=Attribute.carbon_monoxide_level,
|
|
value_type=float,
|
|
),
|
|
# contact_sensor
|
|
DeviceMapper(
|
|
entity_type=BinarySensor,
|
|
capability=Capability.contact_sensor,
|
|
attribute=Attribute.contact,
|
|
value_type=bool,
|
|
),
|
|
# dishwasher_operating_state
|
|
DeviceMapper(
|
|
entity_type=EnumSensor,
|
|
capability=Capability.dishwasher_operating_state,
|
|
attribute=Attribute.machine_state,
|
|
value_type=Attribute.supported_machine_states,
|
|
),
|
|
# door_control
|
|
DeviceMapper(
|
|
entity_type=Switch,
|
|
capability=Capability.door_control,
|
|
attribute=Attribute.door,
|
|
value_type=bool,
|
|
get_value=lambda device: device.status.door in {'open', 'opening'},
|
|
set_command=lambda value: Command.open if value else Command.close,
|
|
),
|
|
# dryer_operating_state
|
|
DeviceMapper(
|
|
entity_type=EnumSensor,
|
|
capability=Capability.dryer_operating_state,
|
|
attribute=Attribute.machine_state,
|
|
value_type=Attribute.supported_machine_states,
|
|
),
|
|
# dust_sensor
|
|
DeviceMapper(
|
|
entity_type=NumericSensor,
|
|
capability=Capability.dust_sensor,
|
|
attribute=Attribute.dust_level,
|
|
value_type=float,
|
|
),
|
|
# energy_meter
|
|
DeviceMapper(
|
|
entity_type=EnergySensor,
|
|
capability=Capability.energy_meter,
|
|
attribute=Attribute.energy,
|
|
value_type=float,
|
|
),
|
|
# equivalent_carbon_dioxide_measurement
|
|
DeviceMapper(
|
|
entity_type=NumericSensor,
|
|
capability=Capability.equivalent_carbon_dioxide_measurement,
|
|
attribute=Attribute.equivalent_carbon_dioxide_measurement,
|
|
value_type=float,
|
|
),
|
|
# fan_speed
|
|
DeviceMapper(
|
|
entity_type=Dimmer,
|
|
capability=Capability.fan_speed,
|
|
attribute=Attribute.fan_speed,
|
|
value_type=int,
|
|
set_command=Command.set_fan_speed,
|
|
),
|
|
# formaldehyde_measurement
|
|
DeviceMapper(
|
|
entity_type=NumericSensor,
|
|
capability=Capability.formaldehyde_measurement,
|
|
attribute=Attribute.formaldehyde_level,
|
|
value_type=float,
|
|
),
|
|
# gas_meter
|
|
DeviceMapper(
|
|
entity_type=NumericSensor,
|
|
capability=Capability.gas_meter,
|
|
attribute=Attribute.gas_meter,
|
|
value_type=float,
|
|
),
|
|
# illuminance_measurement
|
|
DeviceMapper(
|
|
entity_type=IlluminanceSensor,
|
|
capability=Capability.illuminance_measurement,
|
|
attribute=Attribute.illuminance,
|
|
value_type=float,
|
|
),
|
|
# infrared_level
|
|
DeviceMapper(
|
|
entity_type=NumericSensor,
|
|
capability=Capability.infrared_level,
|
|
attribute=Attribute.infrared_level,
|
|
value_type=int,
|
|
min=0,
|
|
max=100,
|
|
),
|
|
# lock
|
|
DeviceMapper(
|
|
entity_type=Switch,
|
|
capability=Capability.lock,
|
|
attribute=Attribute.lock,
|
|
value_type=bool,
|
|
get_value=lambda device: device.status.lock in {True, 'locked'},
|
|
set_command=lambda value: Command.lock if value else Command.unlock,
|
|
set_value_args=lambda *_: [],
|
|
),
|
|
# media_input_source
|
|
DeviceMapper(
|
|
entity_type=EnumSwitch,
|
|
capability=Capability.media_input_source,
|
|
attribute=Attribute.input_source,
|
|
value_type=Attribute.supported_input_sources,
|
|
set_command=Command.set_input_source,
|
|
),
|
|
# motion_sensor
|
|
DeviceMapper(
|
|
entity_type=MotionSensor,
|
|
capability=Capability.motion_sensor,
|
|
attribute=Attribute.motion,
|
|
value_type=bool,
|
|
),
|
|
# odor_sensor
|
|
DeviceMapper(
|
|
entity_type=NumericSensor,
|
|
capability=Capability.odor_sensor,
|
|
attribute=Attribute.odor_level,
|
|
value_type=int,
|
|
),
|
|
# oven_operating_state
|
|
DeviceMapper(
|
|
entity_type=EnumSensor,
|
|
capability=Capability.oven_operating_state,
|
|
attribute=Attribute.machine_state,
|
|
value_type=Attribute.supported_machine_states,
|
|
),
|
|
# power_consumption_report
|
|
DeviceMapper(
|
|
entity_type=PowerSensor,
|
|
capability=Capability.power_consumption_report,
|
|
attribute=Attribute.power_consumption,
|
|
value_type=float,
|
|
),
|
|
# power_meter
|
|
DeviceMapper(
|
|
entity_type=PowerSensor,
|
|
capability=Capability.power_meter,
|
|
attribute=Attribute.power,
|
|
value_type=float,
|
|
),
|
|
# power_source
|
|
DeviceMapper(
|
|
entity_type=EnumSensor,
|
|
capability=Capability.power_source,
|
|
attribute=Attribute.power_source,
|
|
value_type=Enum('PowerSourceValues', ['battery', 'dc', 'mains', 'unknown']),
|
|
),
|
|
# presence_sensor
|
|
DeviceMapper(
|
|
entity_type=BinarySensor,
|
|
capability=Capability.presence_sensor,
|
|
attribute=Attribute.presence,
|
|
value_type=bool,
|
|
),
|
|
# relative_humidity_measurement
|
|
DeviceMapper(
|
|
entity_type=HumiditySensor,
|
|
capability=Capability.relative_humidity_measurement,
|
|
attribute=Attribute.humidity,
|
|
value_type=int,
|
|
min=0,
|
|
max=100,
|
|
),
|
|
# signal_strength
|
|
DeviceMapper(
|
|
entity_type=LinkQuality,
|
|
capability=Capability.signal_strength,
|
|
attribute=Attribute.lqi,
|
|
value_type=int,
|
|
min=0,
|
|
max=255,
|
|
),
|
|
# smoke_detector
|
|
DeviceMapper(
|
|
entity_type=EnumSensor,
|
|
capability=Capability.smoke_detector,
|
|
attribute=Attribute.smoke,
|
|
value_type=Enum('SmokeDetectorValues', ['clear', 'detected', 'tested']),
|
|
),
|
|
# sound_sensor
|
|
DeviceMapper(
|
|
entity_type=BinarySensor,
|
|
capability=Capability.sound_sensor,
|
|
attribute=Attribute.sound,
|
|
value_type=bool,
|
|
),
|
|
# switch
|
|
DeviceMapper(
|
|
entity_type=Switch,
|
|
capability=Capability.switch,
|
|
attribute=Attribute.switch,
|
|
value_type=bool,
|
|
set_command=lambda value: Command.on if value else Command.off,
|
|
set_value_args=lambda *_: [],
|
|
),
|
|
# switch_level
|
|
DeviceMapper(
|
|
entity_type=Dimmer,
|
|
capability=Capability.switch_level,
|
|
attribute=Attribute.level,
|
|
value_type=int,
|
|
set_command=Command.set_level,
|
|
),
|
|
# tamper_alert
|
|
DeviceMapper(
|
|
entity_type=BinarySensor,
|
|
capability=Capability.tamper_alert,
|
|
attribute=Attribute.tamper,
|
|
value_type=bool,
|
|
),
|
|
# temperature_measurement
|
|
DeviceMapper(
|
|
entity_type=TemperatureSensor,
|
|
capability=Capability.temperature_measurement,
|
|
attribute=Attribute.temperature,
|
|
value_type=float,
|
|
),
|
|
# thermostat_cooling_setpoint
|
|
DeviceMapper(
|
|
entity_type=Dimmer,
|
|
capability=Capability.thermostat_cooling_setpoint,
|
|
attribute=Attribute.cooling_setpoint,
|
|
value_type=float,
|
|
min=-460,
|
|
max=10000,
|
|
set_command=Command.set_cooling_setpoint,
|
|
),
|
|
# thermostat_fan_mode
|
|
DeviceMapper(
|
|
entity_type=EnumSwitch,
|
|
capability=Capability.thermostat_fan_mode,
|
|
attribute=Attribute.thermostat_fan_mode,
|
|
value_type=Attribute.supported_thermostat_fan_modes,
|
|
set_command=Command.set_thermostat_fan_mode,
|
|
),
|
|
# thermostat_heating_setpoint
|
|
DeviceMapper(
|
|
entity_type=Dimmer,
|
|
capability=Capability.thermostat_heating_setpoint,
|
|
attribute=Attribute.heating_setpoint,
|
|
value_type=float,
|
|
min=-460,
|
|
max=10000,
|
|
set_command=Command.set_heating_setpoint,
|
|
),
|
|
# thermostat_mode
|
|
DeviceMapper(
|
|
entity_type=EnumSwitch,
|
|
capability=Capability.thermostat_mode,
|
|
attribute=Attribute.thermostat_mode,
|
|
value_type=Attribute.supported_thermostat_modes,
|
|
set_command=Command.set_thermostat_mode,
|
|
),
|
|
# thermostat_operating_state
|
|
DeviceMapper(
|
|
entity_type=EnumSensor,
|
|
capability=Capability.thermostat_operating_state,
|
|
attribute=Attribute.thermostat_operating_state,
|
|
value_type=Enum(
|
|
'ThermostatOperatingState',
|
|
[
|
|
'cooling',
|
|
'fan only',
|
|
'heating',
|
|
'idle',
|
|
'pending cool',
|
|
'pending heat',
|
|
'vent economizer',
|
|
],
|
|
),
|
|
),
|
|
# three_axis
|
|
DeviceMapper(
|
|
entity_type=CompositeSensor,
|
|
capability=Capability.three_axis,
|
|
attribute=Attribute.three_axis,
|
|
value_type=list,
|
|
),
|
|
# tv_channel
|
|
DeviceMapper(
|
|
entity_type=Dimmer,
|
|
capability=Capability.tv_channel,
|
|
attribute=Attribute.tv_channel,
|
|
value_type=int,
|
|
set_command=Command.set_tv_channel,
|
|
min=1,
|
|
max=1000,
|
|
),
|
|
# tvoc_measurement
|
|
DeviceMapper(
|
|
entity_type=NumericSensor,
|
|
capability=Capability.tvoc_measurement,
|
|
attribute=Attribute.tvoc_level,
|
|
value_type=float,
|
|
),
|
|
# ultraviolet_index
|
|
DeviceMapper(
|
|
entity_type=NumericSensor,
|
|
capability=Capability.ultraviolet_index,
|
|
attribute=Attribute.ultraviolet_index,
|
|
value_type=float,
|
|
),
|
|
# valve
|
|
DeviceMapper(
|
|
entity_type=Switch,
|
|
capability=Capability.valve,
|
|
attribute=Attribute.valve,
|
|
value_type=bool,
|
|
set_command=lambda value: Command.open if value else Command.close,
|
|
set_value_args=lambda *_: [],
|
|
),
|
|
# voltage_measurement
|
|
DeviceMapper(
|
|
entity_type=VoltageSensor,
|
|
capability=Capability.voltage_measurement,
|
|
attribute=Attribute.voltage,
|
|
value_type=float,
|
|
),
|
|
# washer_operating_state
|
|
DeviceMapper(
|
|
entity_type=EnumSensor,
|
|
capability=Capability.washer_operating_state,
|
|
attribute=Attribute.machine_state,
|
|
value_type=Attribute.supported_machine_states,
|
|
),
|
|
# water_sensor
|
|
DeviceMapper(
|
|
entity_type=BinarySensor,
|
|
capability=Capability.water_sensor,
|
|
attribute=Attribute.water,
|
|
value_type=bool,
|
|
),
|
|
# window_shade
|
|
DeviceMapper(
|
|
entity_type=Dimmer,
|
|
capability=Capability.window_shade,
|
|
attribute=Attribute.window_shade,
|
|
value_type=int,
|
|
set_command='setWindowShade',
|
|
min=0,
|
|
max=100,
|
|
),
|
|
]
|
|
|
|
|
|
# vim:sw=4:ts=4:et:
|