Skip to content
This repository has been archived by the owner on May 9, 2024. It is now read-only.

Commit

Permalink
v0.6.4
Browse files Browse the repository at this point in the history
- Adds support and well-known attributes/methods for the following capabilities: `demandResponseLoadControl`, `execute`, `ocf`, and `powerConsumptionReport` found in Samsung Air Conditioners.
- Update test dependencies
  • Loading branch information
andrewsayre committed Mar 2, 2019
2 parents 330f163 + 481694d commit 1737290
Show file tree
Hide file tree
Showing 10 changed files with 553 additions and 9 deletions.
2 changes: 1 addition & 1 deletion lint.cmd
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
@echo off
pip install isort --quiet
pip install isort --quiet --upgrade
isort tests pysmartthings --recursive
pip install -r test-requirements.txt --quiet
pylint tests pysmartthings
Expand Down
28 changes: 28 additions & 0 deletions pysmartthings/capability.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
'colorControl': ['color', 'hue', 'saturation'],
'colorTemperature': ['colorTemperature'],
'contactSensor': ['contact'],
'demandResponseLoadControl': ['drlcStatus'],
'dishwasherMode': ['dishwasherMode'],
'dishwasherOperatingState': ['machineState', 'supportedMachineStates',
'dishwasherJobState', 'completionTime'],
Expand All @@ -33,6 +34,7 @@
'energyMeter': ['energy'],
'equivalentCarbonDioxideMeasurement':
['equivalentCarbonDioxideMeasurement'],
'execute': ['data'],
'fanSpeed': ['fanSpeed'],
'filterStatus': ['filterStatus'],
'formaldehydeMeasurement': ['formaldehydeLevel'],
Expand All @@ -45,12 +47,15 @@
'mediaPlaybackShuffle': ['playbackShuffle'],
'mediaPlayback': ['playbackStatus', 'supportedPlaybackCommands'],
'motionSensor': ['motion'],
'ocf': ['st', 'mnfv', 'mndt', 'mnhw', 'di', 'mnsl', 'dmv', 'n', 'vid',
'mnmo', 'mnmn', 'mnml', 'mnpv', 'mnos', 'pi', 'icv'],
'odorSensor': ['odorLevel'],
'ovenMode': ['ovenMode'],
'ovenOperatingState': ['machineState', 'supportedMachineStates',
'ovenJobState', 'completionTime', 'operationTime',
'progress'],
'ovenSetpoint': ['ovenSetpoint'],
'powerConsumptionReport': ['powerConsumption'],
'powerMeter': ['power'],
'powerSource': ['powerSource'],
'presenceSensor': ['presence'],
Expand Down Expand Up @@ -116,6 +121,7 @@ class Capability:
color_control = 'colorControl'
color_temperature = 'colorTemperature'
contact_sensor = 'contactSensor'
demand_response_load_control = 'demandResponseLoadControl'
dishwasher_mode = 'dishwasherMode'
dishwasher_operating_state = 'dishwasherOperatingState'
door_control = 'doorControl'
Expand All @@ -125,6 +131,7 @@ class Capability:
energy_meter = 'energyMeter'
equivalent_carbon_dioxide_measurement = \
'equivalentCarbonDioxideMeasurement'
execute = 'execute'
fan_speed = 'fanSpeed'
filter_status = 'filterStatus'
formaldehyde_measurement = 'formaldehydeMeasurement'
Expand All @@ -137,10 +144,12 @@ class Capability:
media_playback_repeat = 'mediaPlaybackRepeat'
media_playback_shuffle = 'mediaPlaybackShuffle'
motion_sensor = 'motionSensor'
ocf = 'ocf'
odor_sensor = 'odorSensor'
oven_mode = 'ovenMode'
oven_operating_state = 'ovenOperatingState'
oven_setpoint = 'ovenSetpoint'
power_consumption_report = 'powerConsumptionReport'
power_meter = 'powerMeter'
power_source = 'powerSource'
presence_sensor = 'presenceSensor'
Expand Down Expand Up @@ -195,9 +204,13 @@ class Attribute:
contact = 'contact'
cooling_setpoint = 'coolingSetpoint'
cooling_setpoint_range = 'coolingSetpointRange'
data = 'data'
di = 'di'
dishwasher_job_state = 'dishwasherJobState'
dishwasher_mode = 'dishwasherMode'
dmv = 'dmv'
door = 'door'
drlc_status = 'drlcStatus'
dryer_job_state = 'dryerJobState'
dryer_mode = 'dryerMode'
dust_level = 'dustLevel'
Expand All @@ -212,6 +225,7 @@ class Attribute:
heating_setpoint_range = 'heatingSetpointRange'
hue = 'hue'
humidity = 'humidity'
icv = 'icv'
illuminance = 'illuminance'
infrared_level = 'infraredLevel'
input_source = 'inputSource'
Expand All @@ -220,18 +234,30 @@ class Attribute:
lock = 'lock'
lqi = 'lqi'
machine_state = 'machineState'
mndt = 'mndt'
mnfv = 'mnfv'
mnhw = 'mnhw'
mnml = 'mnml'
mnmn = 'mnmn'
mnmo = 'mnmo'
mnos = 'mnos'
mnpv = 'mnpv'
mnsl = 'mnsl'
motion = 'motion'
mute = 'mute'
n = 'n'
number_of_buttons = 'numberOfButtons'
odor_level = 'odorLevel'
operation_time = 'operationTime'
oven_job_state = 'ovenJobState'
oven_mode = 'ovenMode'
oven_setpoint = 'ovenSetpoint'
pi = 'pi'
playback_repeat_mode = 'playbackRepeatMode'
playback_shuffle = 'playbackShuffle'
playback_status = 'playbackStatus'
power = 'power'
power_consumption = 'powerConsumption'
power_source = 'powerSource'
presence = 'presence'
progress = 'progress'
Expand All @@ -245,6 +271,7 @@ class Attribute:
schedule = 'schedule'
smoke = 'smoke'
sound = 'sound'
st = 'st'
supported_button_values = 'supportedButtonValues'
supported_input_sources = 'supportedInputSources'
supported_machine_states = 'supportedMachineStates'
Expand All @@ -263,6 +290,7 @@ class Attribute:
tvoc_level = 'tvocLevel'
ultraviolet_index = 'ultravioletIndex'
valve = 'valve'
vid = 'vid'
voltage = 'voltage'
volume = 'volume'
washer_job_state = 'washerJobState'
Expand Down
2 changes: 1 addition & 1 deletion pysmartthings/const.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
"""Define consts for the pysmartthings package."""

__title__ = "pysmartthings"
__version__ = "0.6.3"
__version__ = "0.6.4"
222 changes: 219 additions & 3 deletions pysmartthings/device.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,21 +34,24 @@ class Command:
"""Define common commands."""

close = 'close'
off = 'off'
execute = 'execute'
lock = 'lock'
off = 'off'
open = 'open'
on = 'on'
override_drlc_action = 'overrideDrlcAction'
preset_position = 'presetPosition'
request_drlc_action = 'requestDrlcAction'
set_color = 'setColor'
set_color_temperature = 'setColorTemperature'
set_fan_speed = 'setFanSpeed'
set_cooling_setpoint = 'setCoolingSetpoint'
set_fan_speed = 'setFanSpeed'
set_heating_setpoint = 'setHeatingSetpoint'
set_hue = 'setHue'
set_level = 'setLevel'
set_saturation = 'setSaturation'
set_thermostat_fan_mode = 'setThermostatFanMode'
set_thermostat_mode = 'setThermostatMode'
set_saturation = 'setSaturation'
unlock = 'unlock'


Expand Down Expand Up @@ -371,6 +374,165 @@ def window_shade(self):
"""Get the windowShade attribute."""
return self._attributes[Attribute.window_shade].value

@property
def drlc_status(self) -> Optional[dict]:
"""Get the demand response load control status."""
return self._attributes[Attribute.drlc_status].value

@property
def drlc_status_duration(self) -> Optional[int]:
"""Get the duration component of the drlc status."""
try:
return int(self.drlc_status['duration'])
except (KeyError, ValueError, TypeError):
return None

@property
def drlc_status_level(self) -> Optional[int]:
"""Get the level component of the drlc status."""
try:
return int(self.drlc_status['drlcLevel'])
except (KeyError, ValueError, TypeError):
return None

@property
def drlc_status_start(self) -> Optional[str]:
"""Get the level component of the drlc status."""
try:
return self.drlc_status['start']
except (KeyError, TypeError):
return None

@property
def drlc_status_override(self) -> Optional[bool]:
"""Get the override component of the drlc status."""
try:
return bool(self.drlc_status['override'])
except (KeyError, ValueError, TypeError):
return None

@property
def power_consumption(self) -> Optional[dict]:
"""Get the power consumption report data."""
return self._attributes[Attribute.power_consumption].value

@property
def power_consumption_start(self) -> Optional[str]:
"""Get the start component of power consumption data."""
try:
return self.power_consumption['start']
except (KeyError, TypeError):
return None

@property
def power_consumption_power(self) -> Optional[int]:
"""Get the power component of power consumption data."""
try:
return int(self.power_consumption['power'])
except (KeyError, ValueError, TypeError):
return None

@property
def power_consumption_energy(self) -> Optional[int]:
"""Get the energy component of power consumption data."""
try:
return int(self.power_consumption['energy'])
except (KeyError, ValueError, TypeError):
return None

@property
def power_consumption_end(self) -> Optional[str]:
"""Get the end component of power consumption data."""
try:
return self.power_consumption['end']
except (KeyError, TypeError):
return None

@property
def ocf_system_time(self) -> Optional[str]:
"""Get the OCF system time."""
return self._attributes[Attribute.st].value

@property
def ocf_firmware_version(self) -> Optional[str]:
"""Get the OCF firmware version."""
return self._attributes[Attribute.mnfv].value

@property
def ocf_date_of_manufacture(self) -> Optional[str]:
"""Get the OCF date of manufacture."""
return self._attributes[Attribute.mndt].value

@property
def ocf_hardware_version(self) -> Optional[str]:
"""Get the OCF hardware version."""
return self._attributes[Attribute.mnhw].value

@property
def ocf_device_id(self) -> Optional[str]:
"""Get the OCF device id."""
return self._attributes[Attribute.di].value

@property
def ocf_support_link(self) -> Optional[str]:
"""Get the OCF support link."""
return self._attributes[Attribute.mnsl].value

@property
def ocf_data_model_version(self) -> Optional[str]:
"""Get the OCF data model version."""
return self._attributes[Attribute.dmv].value

@property
def ocf_name(self) -> Optional[str]:
"""Get the OCF name."""
return self._attributes[Attribute.n].value

@property
def ocf_vendor_id(self) -> Optional[str]:
"""Get the OCF vendor id."""
return self._attributes[Attribute.vid].value

@property
def ocf_model_number(self):
"""Get the OCF model number."""
return self._attributes[Attribute.mnmo].value

@property
def ocf_manufacturer_name(self) -> Optional[str]:
"""Get the OCF manufacturer name."""
return self._attributes[Attribute.mnmn].value

@property
def ocf_manufacturer_details_link(self) -> Optional[str]:
"""Get the OCF manufacturer details link."""
return self._attributes[Attribute.mnml].value

@property
def ocf_platform_version(self) -> Optional[str]:
"""Get the OCF platform version."""
return self._attributes[Attribute.mnpv].value

@property
def ocf_os_version(self) -> Optional[str]:
"""Get the OCF OS version."""
return self._attributes[Attribute.mnos].value

@property
def ocf_platform_id(self) -> Optional[str]:
"""Get the OCF platform id."""
return self._attributes[Attribute.pi].value

@property
def ocf_spec_version(self) -> Optional[str]:
"""Get the OCF spec version."""
return self._attributes[Attribute.icv].value

@property
def data(self) -> Optional[dict]:
"""Get the data attribute."""
return self._attributes[Attribute.data].value


class DeviceStatus(DeviceStatusBase):
"""Define the device status."""
Expand Down Expand Up @@ -690,6 +852,60 @@ async def preset_position(self, *, component_id: str = 'main') -> bool:
return await self.command(
component_id, Capability.window_shade, Command.close)

async def request_drlc_action(
self, drlc_type: int, drlc_level: int, start: str, duration: int,
reporting_period: int = None, *, set_status: bool = False,
component_id: str = 'main'):
"""Call the drlc action command."""
args = [
drlc_type,
drlc_level,
start,
duration
]
if reporting_period is not None:
args.append(reporting_period)
result = await self.command(
component_id, Capability.demand_response_load_control,
Command.request_drlc_action, args)
if result and set_status:
data = {
"duration": duration,
"drlcLevel": drlc_level,
"start": start,
"override": False
}
self.status.apply_attribute_update(
component_id, Capability.demand_response_load_control,
Attribute.drlc_status, data)
return result

async def override_drlc_action(
self, value: bool, *, set_status: bool = False,
component_id: str = 'main'):
"""Call the drlc override command."""
result = await self.command(
component_id, Capability.demand_response_load_control,
Command.override_drlc_action, [value])
if result and set_status:
data = self.status.drlc_status
if not data:
data = {}
self.status.apply_attribute_update(
component_id, Capability.demand_response_load_control,
Attribute.drlc_status, data)
data['override'] = value
return result

async def execute(self, command: str, args: Dict = None, *,
component_id: str = 'main'):
"""Call the execute command."""
command_args = [command]
if args:
command_args.append(args)
return await self.command(component_id, Capability.execute,
Command.execute, command_args)

@property
def status(self):
"""Get the status entity of the device."""
Expand Down

0 comments on commit 1737290

Please sign in to comment.