Move SmartThings imports to top (#24878)
* Move imports to top * use lib constants * Add missing three_axis mapping
This commit is contained in:
parent
7d651e2b7a
commit
7db4eeaf7f
12 changed files with 176 additions and 191 deletions
|
@ -6,6 +6,8 @@ from typing import Iterable
|
||||||
|
|
||||||
from aiohttp.client_exceptions import (
|
from aiohttp.client_exceptions import (
|
||||||
ClientConnectionError, ClientResponseError)
|
ClientConnectionError, ClientResponseError)
|
||||||
|
from pysmartapp.event import EVENT_TYPE_DEVICE
|
||||||
|
from pysmartthings import Attribute, Capability, SmartThings
|
||||||
|
|
||||||
from homeassistant.config_entries import ConfigEntry
|
from homeassistant.config_entries import ConfigEntry
|
||||||
from homeassistant.const import CONF_ACCESS_TOKEN
|
from homeassistant.const import CONF_ACCESS_TOKEN
|
||||||
|
@ -60,8 +62,6 @@ async def async_migrate_entry(hass: HomeAssistantType, entry: ConfigEntry):
|
||||||
|
|
||||||
async def async_setup_entry(hass: HomeAssistantType, entry: ConfigEntry):
|
async def async_setup_entry(hass: HomeAssistantType, entry: ConfigEntry):
|
||||||
"""Initialize config entry which represents an installed SmartApp."""
|
"""Initialize config entry which represents an installed SmartApp."""
|
||||||
from pysmartthings import SmartThings
|
|
||||||
|
|
||||||
if not validate_webhook_requirements(hass):
|
if not validate_webhook_requirements(hass):
|
||||||
_LOGGER.warning("The 'base_url' of the 'http' component must be "
|
_LOGGER.warning("The 'base_url' of the 'http' component must be "
|
||||||
"configured and start with 'https://'")
|
"configured and start with 'https://'")
|
||||||
|
@ -179,8 +179,6 @@ async def async_unload_entry(hass: HomeAssistantType, entry: ConfigEntry):
|
||||||
async def async_remove_entry(
|
async def async_remove_entry(
|
||||||
hass: HomeAssistantType, entry: ConfigEntry) -> None:
|
hass: HomeAssistantType, entry: ConfigEntry) -> None:
|
||||||
"""Perform clean-up when entry is being removed."""
|
"""Perform clean-up when entry is being removed."""
|
||||||
from pysmartthings import SmartThings
|
|
||||||
|
|
||||||
api = SmartThings(async_get_clientsession(hass),
|
api = SmartThings(async_get_clientsession(hass),
|
||||||
entry.data[CONF_ACCESS_TOKEN])
|
entry.data[CONF_ACCESS_TOKEN])
|
||||||
|
|
||||||
|
@ -301,9 +299,6 @@ class DeviceBroker:
|
||||||
|
|
||||||
async def _event_handler(self, req, resp, app):
|
async def _event_handler(self, req, resp, app):
|
||||||
"""Broker for incoming events."""
|
"""Broker for incoming events."""
|
||||||
from pysmartapp.event import EVENT_TYPE_DEVICE
|
|
||||||
from pysmartthings import Capability, Attribute
|
|
||||||
|
|
||||||
# Do not process events received from a different installed app
|
# Do not process events received from a different installed app
|
||||||
# under the same parent SmartApp (valid use-scenario)
|
# under the same parent SmartApp (valid use-scenario)
|
||||||
if req.installed_app_id != self._installed_app_id:
|
if req.installed_app_id != self._installed_app_id:
|
||||||
|
|
|
@ -1,32 +1,34 @@
|
||||||
"""Support for binary sensors through the SmartThings cloud API."""
|
"""Support for binary sensors through the SmartThings cloud API."""
|
||||||
from typing import Optional, Sequence
|
from typing import Optional, Sequence
|
||||||
|
|
||||||
|
from pysmartthings import Attribute, Capability
|
||||||
|
|
||||||
from homeassistant.components.binary_sensor import BinarySensorDevice
|
from homeassistant.components.binary_sensor import BinarySensorDevice
|
||||||
|
|
||||||
from . import SmartThingsEntity
|
from . import SmartThingsEntity
|
||||||
from .const import DATA_BROKERS, DOMAIN
|
from .const import DATA_BROKERS, DOMAIN
|
||||||
|
|
||||||
CAPABILITY_TO_ATTRIB = {
|
CAPABILITY_TO_ATTRIB = {
|
||||||
'accelerationSensor': 'acceleration',
|
Capability.acceleration_sensor: Attribute.acceleration,
|
||||||
'contactSensor': 'contact',
|
Capability.contact_sensor: Attribute.contact,
|
||||||
'filterStatus': 'filterStatus',
|
Capability.filter_status: Attribute.filter_status,
|
||||||
'motionSensor': 'motion',
|
Capability.motion_sensor: Attribute.motion,
|
||||||
'presenceSensor': 'presence',
|
Capability.presence_sensor: Attribute.presence,
|
||||||
'soundSensor': 'sound',
|
Capability.sound_sensor: Attribute.sound,
|
||||||
'tamperAlert': 'tamper',
|
Capability.tamper_alert: Attribute.tamper,
|
||||||
'valve': 'valve',
|
Capability.valve: Attribute.valve,
|
||||||
'waterSensor': 'water',
|
Capability.water_sensor: Attribute.water,
|
||||||
}
|
}
|
||||||
ATTRIB_TO_CLASS = {
|
ATTRIB_TO_CLASS = {
|
||||||
'acceleration': 'moving',
|
Attribute.acceleration: 'moving',
|
||||||
'contact': 'opening',
|
Attribute.contact: 'opening',
|
||||||
'filterStatus': 'problem',
|
Attribute.filter_status: 'problem',
|
||||||
'motion': 'motion',
|
Attribute.motion: 'motion',
|
||||||
'presence': 'presence',
|
Attribute.presence: 'presence',
|
||||||
'sound': 'sound',
|
Attribute.sound: 'sound',
|
||||||
'tamper': 'problem',
|
Attribute.tamper: 'problem',
|
||||||
'valve': 'opening',
|
Attribute.valve: 'opening',
|
||||||
'water': 'moisture',
|
Attribute.water: 'moisture',
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -3,6 +3,8 @@ import asyncio
|
||||||
import logging
|
import logging
|
||||||
from typing import Iterable, Optional, Sequence
|
from typing import Iterable, Optional, Sequence
|
||||||
|
|
||||||
|
from pysmartthings import Attribute, Capability
|
||||||
|
|
||||||
from homeassistant.components.climate import (
|
from homeassistant.components.climate import (
|
||||||
DOMAIN as CLIMATE_DOMAIN, ClimateDevice)
|
DOMAIN as CLIMATE_DOMAIN, ClimateDevice)
|
||||||
from homeassistant.components.climate.const import (
|
from homeassistant.components.climate.const import (
|
||||||
|
@ -69,8 +71,6 @@ async def async_setup_platform(
|
||||||
|
|
||||||
async def async_setup_entry(hass, config_entry, async_add_entities):
|
async def async_setup_entry(hass, config_entry, async_add_entities):
|
||||||
"""Add climate entities for a config entry."""
|
"""Add climate entities for a config entry."""
|
||||||
from pysmartthings import Capability
|
|
||||||
|
|
||||||
ac_capabilities = [
|
ac_capabilities = [
|
||||||
Capability.air_conditioner_mode,
|
Capability.air_conditioner_mode,
|
||||||
Capability.air_conditioner_fan_mode,
|
Capability.air_conditioner_fan_mode,
|
||||||
|
@ -93,8 +93,6 @@ async def async_setup_entry(hass, config_entry, async_add_entities):
|
||||||
|
|
||||||
def get_capabilities(capabilities: Sequence[str]) -> Optional[Sequence[str]]:
|
def get_capabilities(capabilities: Sequence[str]) -> Optional[Sequence[str]]:
|
||||||
"""Return all capabilities supported if minimum required are present."""
|
"""Return all capabilities supported if minimum required are present."""
|
||||||
from pysmartthings import Capability
|
|
||||||
|
|
||||||
supported = [
|
supported = [
|
||||||
Capability.air_conditioner_mode,
|
Capability.air_conditioner_mode,
|
||||||
Capability.demand_response_load_control,
|
Capability.demand_response_load_control,
|
||||||
|
@ -145,8 +143,6 @@ class SmartThingsThermostat(SmartThingsEntity, ClimateDevice):
|
||||||
self._operations = None
|
self._operations = None
|
||||||
|
|
||||||
def _determine_features(self):
|
def _determine_features(self):
|
||||||
from pysmartthings import Capability
|
|
||||||
|
|
||||||
flags = SUPPORT_OPERATION_MODE \
|
flags = SUPPORT_OPERATION_MODE \
|
||||||
| SUPPORT_TARGET_TEMPERATURE \
|
| SUPPORT_TARGET_TEMPERATURE \
|
||||||
| SUPPORT_TARGET_TEMPERATURE_LOW \
|
| SUPPORT_TARGET_TEMPERATURE_LOW \
|
||||||
|
@ -301,7 +297,6 @@ class SmartThingsThermostat(SmartThingsEntity, ClimateDevice):
|
||||||
@property
|
@property
|
||||||
def temperature_unit(self):
|
def temperature_unit(self):
|
||||||
"""Return the unit of measurement."""
|
"""Return the unit of measurement."""
|
||||||
from pysmartthings import Attribute
|
|
||||||
return UNIT_MAP.get(
|
return UNIT_MAP.get(
|
||||||
self._device.status.attributes[Attribute.temperature].unit)
|
self._device.status.attributes[Attribute.temperature].unit)
|
||||||
|
|
||||||
|
@ -440,6 +435,5 @@ class SmartThingsAirConditioner(SmartThingsEntity, ClimateDevice):
|
||||||
@property
|
@property
|
||||||
def temperature_unit(self):
|
def temperature_unit(self):
|
||||||
"""Return the unit of measurement."""
|
"""Return the unit of measurement."""
|
||||||
from pysmartthings import Attribute
|
|
||||||
return UNIT_MAP.get(
|
return UNIT_MAP.get(
|
||||||
self._device.status.attributes[Attribute.temperature].unit)
|
self._device.status.attributes[Attribute.temperature].unit)
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from aiohttp import ClientResponseError
|
from aiohttp import ClientResponseError
|
||||||
|
from pysmartthings import APIResponseError, AppOAuth, SmartThings
|
||||||
import voluptuous as vol
|
import voluptuous as vol
|
||||||
|
|
||||||
from homeassistant import config_entries
|
from homeassistant import config_entries
|
||||||
|
@ -54,8 +55,6 @@ class SmartThingsFlowHandler(config_entries.ConfigFlow):
|
||||||
|
|
||||||
async def async_step_user(self, user_input=None):
|
async def async_step_user(self, user_input=None):
|
||||||
"""Get access token and validate it."""
|
"""Get access token and validate it."""
|
||||||
from pysmartthings import APIResponseError, AppOAuth, SmartThings
|
|
||||||
|
|
||||||
errors = {}
|
errors = {}
|
||||||
if user_input is None or CONF_ACCESS_TOKEN not in user_input:
|
if user_input is None or CONF_ACCESS_TOKEN not in user_input:
|
||||||
return self._show_step_user(errors)
|
return self._show_step_user(errors)
|
||||||
|
@ -182,8 +181,6 @@ class SmartThingsFlowHandler(config_entries.ConfigFlow):
|
||||||
Launched when the user completes the flow or when the SmartApp
|
Launched when the user completes the flow or when the SmartApp
|
||||||
is installed into an additional location.
|
is installed into an additional location.
|
||||||
"""
|
"""
|
||||||
from pysmartthings import SmartThings
|
|
||||||
|
|
||||||
if not self.api:
|
if not self.api:
|
||||||
# Launched from the SmartApp install event handler
|
# Launched from the SmartApp install event handler
|
||||||
self.api = SmartThings(
|
self.api = SmartThings(
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
"""Support for covers through the SmartThings cloud API."""
|
"""Support for covers through the SmartThings cloud API."""
|
||||||
from typing import Optional, Sequence
|
from typing import Optional, Sequence
|
||||||
|
|
||||||
|
from pysmartthings import Attribute, Capability
|
||||||
|
|
||||||
from homeassistant.components.cover import (
|
from homeassistant.components.cover import (
|
||||||
ATTR_POSITION, DEVICE_CLASS_DOOR, DEVICE_CLASS_GARAGE, DEVICE_CLASS_SHADE,
|
ATTR_POSITION, DEVICE_CLASS_DOOR, DEVICE_CLASS_GARAGE, DEVICE_CLASS_SHADE,
|
||||||
DOMAIN as COVER_DOMAIN, STATE_CLOSED, STATE_CLOSING, STATE_OPEN,
|
DOMAIN as COVER_DOMAIN, STATE_CLOSED, STATE_CLOSING, STATE_OPEN,
|
||||||
|
@ -37,8 +39,6 @@ async def async_setup_entry(hass, config_entry, async_add_entities):
|
||||||
|
|
||||||
def get_capabilities(capabilities: Sequence[str]) -> Optional[Sequence[str]]:
|
def get_capabilities(capabilities: Sequence[str]) -> Optional[Sequence[str]]:
|
||||||
"""Return all capabilities supported if minimum required are present."""
|
"""Return all capabilities supported if minimum required are present."""
|
||||||
from pysmartthings import Capability
|
|
||||||
|
|
||||||
min_required = [
|
min_required = [
|
||||||
Capability.door_control,
|
Capability.door_control,
|
||||||
Capability.garage_door_control,
|
Capability.garage_door_control,
|
||||||
|
@ -58,8 +58,6 @@ class SmartThingsCover(SmartThingsEntity, CoverDevice):
|
||||||
|
|
||||||
def __init__(self, device):
|
def __init__(self, device):
|
||||||
"""Initialize the cover class."""
|
"""Initialize the cover class."""
|
||||||
from pysmartthings import Capability
|
|
||||||
|
|
||||||
super().__init__(device)
|
super().__init__(device)
|
||||||
self._device_class = None
|
self._device_class = None
|
||||||
self._state = None
|
self._state = None
|
||||||
|
@ -93,8 +91,6 @@ class SmartThingsCover(SmartThingsEntity, CoverDevice):
|
||||||
|
|
||||||
async def async_update(self):
|
async def async_update(self):
|
||||||
"""Update the attrs of the cover."""
|
"""Update the attrs of the cover."""
|
||||||
from pysmartthings import Attribute, Capability
|
|
||||||
|
|
||||||
value = None
|
value = None
|
||||||
if Capability.door_control in self._device.capabilities:
|
if Capability.door_control in self._device.capabilities:
|
||||||
self._device_class = DEVICE_CLASS_DOOR
|
self._device_class = DEVICE_CLASS_DOOR
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
"""Support for fans through the SmartThings cloud API."""
|
"""Support for fans through the SmartThings cloud API."""
|
||||||
from typing import Optional, Sequence
|
from typing import Optional, Sequence
|
||||||
|
|
||||||
|
from pysmartthings import Capability
|
||||||
|
|
||||||
from homeassistant.components.fan import (
|
from homeassistant.components.fan import (
|
||||||
SPEED_HIGH, SPEED_LOW, SPEED_MEDIUM, SPEED_OFF, SUPPORT_SET_SPEED,
|
SPEED_HIGH, SPEED_LOW, SPEED_MEDIUM, SPEED_OFF, SUPPORT_SET_SPEED,
|
||||||
FanEntity)
|
FanEntity)
|
||||||
|
@ -34,8 +36,6 @@ async def async_setup_entry(hass, config_entry, async_add_entities):
|
||||||
|
|
||||||
def get_capabilities(capabilities: Sequence[str]) -> Optional[Sequence[str]]:
|
def get_capabilities(capabilities: Sequence[str]) -> Optional[Sequence[str]]:
|
||||||
"""Return all capabilities supported if minimum required are present."""
|
"""Return all capabilities supported if minimum required are present."""
|
||||||
from pysmartthings import Capability
|
|
||||||
|
|
||||||
supported = [Capability.switch, Capability.fan_speed]
|
supported = [Capability.switch, Capability.fan_speed]
|
||||||
# Must have switch and fan_speed
|
# Must have switch and fan_speed
|
||||||
if all(capability in capabilities for capability in supported):
|
if all(capability in capabilities for capability in supported):
|
||||||
|
|
|
@ -2,6 +2,8 @@
|
||||||
import asyncio
|
import asyncio
|
||||||
from typing import Optional, Sequence
|
from typing import Optional, Sequence
|
||||||
|
|
||||||
|
from pysmartthings import Capability
|
||||||
|
|
||||||
from homeassistant.components.light import (
|
from homeassistant.components.light import (
|
||||||
ATTR_BRIGHTNESS, ATTR_COLOR_TEMP, ATTR_HS_COLOR, ATTR_TRANSITION,
|
ATTR_BRIGHTNESS, ATTR_COLOR_TEMP, ATTR_HS_COLOR, ATTR_TRANSITION,
|
||||||
SUPPORT_BRIGHTNESS, SUPPORT_COLOR, SUPPORT_COLOR_TEMP, SUPPORT_TRANSITION,
|
SUPPORT_BRIGHTNESS, SUPPORT_COLOR, SUPPORT_COLOR_TEMP, SUPPORT_TRANSITION,
|
||||||
|
@ -28,8 +30,6 @@ async def async_setup_entry(hass, config_entry, async_add_entities):
|
||||||
|
|
||||||
def get_capabilities(capabilities: Sequence[str]) -> Optional[Sequence[str]]:
|
def get_capabilities(capabilities: Sequence[str]) -> Optional[Sequence[str]]:
|
||||||
"""Return all capabilities supported if minimum required are present."""
|
"""Return all capabilities supported if minimum required are present."""
|
||||||
from pysmartthings import Capability
|
|
||||||
|
|
||||||
supported = [
|
supported = [
|
||||||
Capability.switch,
|
Capability.switch,
|
||||||
Capability.switch_level,
|
Capability.switch_level,
|
||||||
|
@ -69,8 +69,6 @@ class SmartThingsLight(SmartThingsEntity, Light):
|
||||||
|
|
||||||
def _determine_features(self):
|
def _determine_features(self):
|
||||||
"""Get features supported by the device."""
|
"""Get features supported by the device."""
|
||||||
from pysmartthings.device import Capability
|
|
||||||
|
|
||||||
features = 0
|
features = 0
|
||||||
# Brightness and transition
|
# Brightness and transition
|
||||||
if Capability.switch_level in self._device.capabilities:
|
if Capability.switch_level in self._device.capabilities:
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
"""Support for locks through the SmartThings cloud API."""
|
"""Support for locks through the SmartThings cloud API."""
|
||||||
from typing import Optional, Sequence
|
from typing import Optional, Sequence
|
||||||
|
|
||||||
|
from pysmartthings import Attribute, Capability
|
||||||
|
|
||||||
from homeassistant.components.lock import LockDevice
|
from homeassistant.components.lock import LockDevice
|
||||||
|
|
||||||
from . import SmartThingsEntity
|
from . import SmartThingsEntity
|
||||||
|
@ -33,8 +35,6 @@ async def async_setup_entry(hass, config_entry, async_add_entities):
|
||||||
|
|
||||||
def get_capabilities(capabilities: Sequence[str]) -> Optional[Sequence[str]]:
|
def get_capabilities(capabilities: Sequence[str]) -> Optional[Sequence[str]]:
|
||||||
"""Return all capabilities supported if minimum required are present."""
|
"""Return all capabilities supported if minimum required are present."""
|
||||||
from pysmartthings import Capability
|
|
||||||
|
|
||||||
if Capability.lock in capabilities:
|
if Capability.lock in capabilities:
|
||||||
return [Capability.lock]
|
return [Capability.lock]
|
||||||
return None
|
return None
|
||||||
|
@ -61,7 +61,6 @@ class SmartThingsLock(SmartThingsEntity, LockDevice):
|
||||||
@property
|
@property
|
||||||
def device_state_attributes(self):
|
def device_state_attributes(self):
|
||||||
"""Return device specific state attributes."""
|
"""Return device specific state attributes."""
|
||||||
from pysmartthings import Attribute
|
|
||||||
state_attrs = {}
|
state_attrs = {}
|
||||||
status = self._device.status.attributes[Attribute.lock]
|
status = self._device.status.attributes[Attribute.lock]
|
||||||
if status.value:
|
if status.value:
|
||||||
|
|
|
@ -2,10 +2,12 @@
|
||||||
from collections import namedtuple
|
from collections import namedtuple
|
||||||
from typing import Optional, Sequence
|
from typing import Optional, Sequence
|
||||||
|
|
||||||
|
from pysmartthings import Attribute, Capability
|
||||||
|
|
||||||
from homeassistant.const import (
|
from homeassistant.const import (
|
||||||
DEVICE_CLASS_BATTERY, DEVICE_CLASS_HUMIDITY, DEVICE_CLASS_ILLUMINANCE,
|
DEVICE_CLASS_BATTERY, DEVICE_CLASS_HUMIDITY, DEVICE_CLASS_ILLUMINANCE,
|
||||||
DEVICE_CLASS_TEMPERATURE, DEVICE_CLASS_TIMESTAMP, MASS_KILOGRAMS,
|
DEVICE_CLASS_TEMPERATURE, DEVICE_CLASS_TIMESTAMP, ENERGY_KILO_WATT_HOUR,
|
||||||
ENERGY_KILO_WATT_HOUR, POWER_WATT, TEMP_CELSIUS, TEMP_FAHRENHEIT)
|
MASS_KILOGRAMS, POWER_WATT, TEMP_CELSIUS, TEMP_FAHRENHEIT)
|
||||||
|
|
||||||
from . import SmartThingsEntity
|
from . import SmartThingsEntity
|
||||||
from .const import DATA_BROKERS, DOMAIN
|
from .const import DATA_BROKERS, DOMAIN
|
||||||
|
@ -13,132 +15,139 @@ from .const import DATA_BROKERS, DOMAIN
|
||||||
Map = namedtuple("map", "attribute name default_unit device_class")
|
Map = namedtuple("map", "attribute name default_unit device_class")
|
||||||
|
|
||||||
CAPABILITY_TO_SENSORS = {
|
CAPABILITY_TO_SENSORS = {
|
||||||
'activityLightingMode': [
|
Capability.activity_lighting_mode: [
|
||||||
Map('lightingMode', "Activity Lighting Mode", None, None)],
|
Map(Attribute.lighting_mode, "Activity Lighting Mode", None, None)],
|
||||||
'airConditionerMode': [
|
Capability.air_conditioner_mode: [
|
||||||
Map('airConditionerMode', "Air Conditioner Mode", None, None)],
|
Map(Attribute.air_conditioner_mode, "Air Conditioner Mode", None,
|
||||||
'airQualitySensor': [
|
|
||||||
Map('airQuality', "Air Quality", 'CAQI', None)],
|
|
||||||
'alarm': [
|
|
||||||
Map('alarm', "Alarm", None, None)],
|
|
||||||
'audioVolume': [
|
|
||||||
Map('volume', "Volume", "%", None)],
|
|
||||||
'battery': [
|
|
||||||
Map('battery', "Battery", "%", DEVICE_CLASS_BATTERY)],
|
|
||||||
'bodyMassIndexMeasurement': [
|
|
||||||
Map('bmiMeasurement', "Body Mass Index", "kg/m^2", None)],
|
|
||||||
'bodyWeightMeasurement': [
|
|
||||||
Map('bodyWeightMeasurement', "Body Weight", MASS_KILOGRAMS, None)],
|
|
||||||
'carbonDioxideMeasurement': [
|
|
||||||
Map('carbonDioxide', "Carbon Dioxide Measurement", "ppm", None)],
|
|
||||||
'carbonMonoxideDetector': [
|
|
||||||
Map('carbonMonoxide', "Carbon Monoxide Detector", None, None)],
|
|
||||||
'carbonMonoxideMeasurement': [
|
|
||||||
Map('carbonMonoxideLevel', "Carbon Monoxide Measurement", "ppm",
|
|
||||||
None)],
|
None)],
|
||||||
'dishwasherOperatingState': [
|
Capability.air_quality_sensor: [
|
||||||
Map('machineState', "Dishwasher Machine State", None, None),
|
Map(Attribute.air_quality, "Air Quality", 'CAQI', None)],
|
||||||
Map('dishwasherJobState', "Dishwasher Job State", None, None),
|
Capability.alarm: [
|
||||||
Map('completionTime', "Dishwasher Completion Time", None,
|
Map(Attribute.alarm, "Alarm", None, None)],
|
||||||
|
Capability.audio_volume: [
|
||||||
|
Map(Attribute.volume, "Volume", "%", None)],
|
||||||
|
Capability.battery: [
|
||||||
|
Map(Attribute.battery, "Battery", "%", DEVICE_CLASS_BATTERY)],
|
||||||
|
Capability.body_mass_index_measurement: [
|
||||||
|
Map(Attribute.bmi_measurement, "Body Mass Index", "kg/m^2", None)],
|
||||||
|
Capability.body_weight_measurement: [
|
||||||
|
Map(Attribute.body_weight_measurement, "Body Weight", MASS_KILOGRAMS,
|
||||||
|
None)],
|
||||||
|
Capability.carbon_dioxide_measurement: [
|
||||||
|
Map(Attribute.carbon_dioxide, "Carbon Dioxide Measurement", "ppm",
|
||||||
|
None)],
|
||||||
|
Capability.carbon_monoxide_detector: [
|
||||||
|
Map(Attribute.carbon_monoxide, "Carbon Monoxide Detector", None,
|
||||||
|
None)],
|
||||||
|
Capability.carbon_monoxide_measurement: [
|
||||||
|
Map(Attribute.carbon_monoxide_level, "Carbon Monoxide Measurement",
|
||||||
|
"ppm", None)],
|
||||||
|
Capability.dishwasher_operating_state: [
|
||||||
|
Map(Attribute.machine_state, "Dishwasher Machine State", None, None),
|
||||||
|
Map(Attribute.dishwasher_job_state, "Dishwasher Job State", None,
|
||||||
|
None),
|
||||||
|
Map(Attribute.completion_time, "Dishwasher Completion Time", None,
|
||||||
DEVICE_CLASS_TIMESTAMP)],
|
DEVICE_CLASS_TIMESTAMP)],
|
||||||
'dryerMode': [
|
Capability.dryer_mode: [
|
||||||
Map('dryerMode', "Dryer Mode", None, None)],
|
Map(Attribute.dryer_mode, "Dryer Mode", None, None)],
|
||||||
'dryerOperatingState': [
|
Capability.dryer_operating_state: [
|
||||||
Map('machineState', "Dryer Machine State", None, None),
|
Map(Attribute.machine_state, "Dryer Machine State", None, None),
|
||||||
Map('dryerJobState', "Dryer Job State", None, None),
|
Map(Attribute.dryer_job_state, "Dryer Job State", None, None),
|
||||||
Map('completionTime', "Dryer Completion Time", None,
|
Map(Attribute.completion_time, "Dryer Completion Time", None,
|
||||||
DEVICE_CLASS_TIMESTAMP)],
|
DEVICE_CLASS_TIMESTAMP)],
|
||||||
'dustSensor': [
|
Capability.dust_sensor: [
|
||||||
Map('fineDustLevel', "Fine Dust Level", None, None),
|
Map(Attribute.fine_dust_level, "Fine Dust Level", None, None),
|
||||||
Map('dustLevel', "Dust Level", None, None)],
|
Map(Attribute.dust_level, "Dust Level", None, None)],
|
||||||
'energyMeter': [
|
Capability.energy_meter: [
|
||||||
Map('energy', "Energy Meter", ENERGY_KILO_WATT_HOUR, None)],
|
Map(Attribute.energy, "Energy Meter", ENERGY_KILO_WATT_HOUR, None)],
|
||||||
'equivalentCarbonDioxideMeasurement': [
|
Capability.equivalent_carbon_dioxide_measurement: [
|
||||||
Map('equivalentCarbonDioxideMeasurement',
|
Map(Attribute.equivalent_carbon_dioxide_measurement,
|
||||||
'Equivalent Carbon Dioxide Measurement', 'ppm', None)],
|
'Equivalent Carbon Dioxide Measurement', 'ppm', None)],
|
||||||
'formaldehydeMeasurement': [
|
Capability.formaldehyde_measurement: [
|
||||||
Map('formaldehydeLevel', 'Formaldehyde Measurement', 'ppm', None)],
|
Map(Attribute.formaldehyde_level, 'Formaldehyde Measurement', 'ppm',
|
||||||
'illuminanceMeasurement': [
|
None)],
|
||||||
Map('illuminance', "Illuminance", 'lux', DEVICE_CLASS_ILLUMINANCE)],
|
Capability.illuminance_measurement: [
|
||||||
'infraredLevel': [
|
Map(Attribute.illuminance, "Illuminance", 'lux',
|
||||||
Map('infraredLevel', "Infrared Level", '%', None)],
|
DEVICE_CLASS_ILLUMINANCE)],
|
||||||
'lock': [
|
Capability.infrared_level: [
|
||||||
Map('lock', "Lock", None, None)],
|
Map(Attribute.infrared_level, "Infrared Level", '%', None)],
|
||||||
'mediaInputSource': [
|
Capability.media_input_source: [
|
||||||
Map('inputSource', "Media Input Source", None, None)],
|
Map(Attribute.input_source, "Media Input Source", None, None)],
|
||||||
'mediaPlaybackRepeat': [
|
Capability.media_playback_repeat: [
|
||||||
Map('playbackRepeatMode', "Media Playback Repeat", None, None)],
|
Map(Attribute.playback_repeat_mode, "Media Playback Repeat", None,
|
||||||
'mediaPlaybackShuffle': [
|
None)],
|
||||||
Map('playbackShuffle', "Media Playback Shuffle", None, None)],
|
Capability.media_playback_shuffle: [
|
||||||
'mediaPlayback': [
|
Map(Attribute.playback_shuffle, "Media Playback Shuffle", None, None)],
|
||||||
Map('playbackStatus', "Media Playback Status", None, None)],
|
Capability.media_playback: [
|
||||||
'odorSensor': [
|
Map(Attribute.playback_status, "Media Playback Status", None, None)],
|
||||||
Map('odorLevel', "Odor Sensor", None, None)],
|
Capability.odor_sensor: [
|
||||||
'ovenMode': [
|
Map(Attribute.odor_level, "Odor Sensor", None, None)],
|
||||||
Map('ovenMode', "Oven Mode", None, None)],
|
Capability.oven_mode: [
|
||||||
'ovenOperatingState': [
|
Map(Attribute.oven_mode, "Oven Mode", None, None)],
|
||||||
Map('machineState', "Oven Machine State", None, None),
|
Capability.oven_operating_state: [
|
||||||
Map('ovenJobState', "Oven Job State", None, None),
|
Map(Attribute.machine_state, "Oven Machine State", None, None),
|
||||||
Map('completionTime', "Oven Completion Time", None, None)],
|
Map(Attribute.oven_job_state, "Oven Job State", None, None),
|
||||||
'ovenSetpoint': [
|
Map(Attribute.completion_time, "Oven Completion Time", None, None)],
|
||||||
Map('ovenSetpoint', "Oven Set Point", None, None)],
|
Capability.oven_setpoint: [
|
||||||
'powerMeter': [
|
Map(Attribute.oven_setpoint, "Oven Set Point", None, None)],
|
||||||
Map('power', "Power Meter", POWER_WATT, None)],
|
Capability.power_meter: [
|
||||||
'powerSource': [
|
Map(Attribute.power, "Power Meter", POWER_WATT, None)],
|
||||||
Map('powerSource', "Power Source", None, None)],
|
Capability.power_source: [
|
||||||
'refrigerationSetpoint': [
|
Map(Attribute.power_source, "Power Source", None, None)],
|
||||||
Map('refrigerationSetpoint', "Refrigeration Setpoint", None,
|
Capability.refrigeration_setpoint: [
|
||||||
|
Map(Attribute.refrigeration_setpoint, "Refrigeration Setpoint", None,
|
||||||
DEVICE_CLASS_TEMPERATURE)],
|
DEVICE_CLASS_TEMPERATURE)],
|
||||||
'relativeHumidityMeasurement': [
|
Capability.relative_humidity_measurement: [
|
||||||
Map('humidity', "Relative Humidity Measurement", '%',
|
Map(Attribute.humidity, "Relative Humidity Measurement", '%',
|
||||||
DEVICE_CLASS_HUMIDITY)],
|
DEVICE_CLASS_HUMIDITY)],
|
||||||
'robotCleanerCleaningMode': [
|
Capability.robot_cleaner_cleaning_mode: [
|
||||||
Map('robotCleanerCleaningMode', "Robot Cleaner Cleaning Mode",
|
Map(Attribute.robot_cleaner_cleaning_mode,
|
||||||
|
"Robot Cleaner Cleaning Mode", None, None)],
|
||||||
|
Capability.robot_cleaner_movement: [
|
||||||
|
Map(Attribute.robot_cleaner_movement, "Robot Cleaner Movement", None,
|
||||||
|
None)],
|
||||||
|
Capability.robot_cleaner_turbo_mode: [
|
||||||
|
Map(Attribute.robot_cleaner_turbo_mode, "Robot Cleaner Turbo Mode",
|
||||||
None, None)],
|
None, None)],
|
||||||
'robotCleanerMovement': [
|
Capability.signal_strength: [
|
||||||
Map('robotCleanerMovement', "Robot Cleaner Movement", None, None)],
|
Map(Attribute.lqi, "LQI Signal Strength", None, None),
|
||||||
'robotCleanerTurboMode': [
|
Map(Attribute.rssi, "RSSI Signal Strength", None, None)],
|
||||||
Map('robotCleanerTurboMode', "Robot Cleaner Turbo Mode", None, None)],
|
Capability.smoke_detector: [
|
||||||
'signalStrength': [
|
Map(Attribute.smoke, "Smoke Detector", None, None)],
|
||||||
Map('lqi', "LQI Signal Strength", None, None),
|
Capability.temperature_measurement: [
|
||||||
Map('rssi', "RSSI Signal Strength", None, None)],
|
Map(Attribute.temperature, "Temperature Measurement", None,
|
||||||
'smokeDetector': [
|
|
||||||
Map('smoke', "Smoke Detector", None, None)],
|
|
||||||
'temperatureMeasurement': [
|
|
||||||
Map('temperature', "Temperature Measurement", None,
|
|
||||||
DEVICE_CLASS_TEMPERATURE)],
|
DEVICE_CLASS_TEMPERATURE)],
|
||||||
'thermostatCoolingSetpoint': [
|
Capability.thermostat_cooling_setpoint: [
|
||||||
Map('coolingSetpoint', "Thermostat Cooling Setpoint", None,
|
Map(Attribute.cooling_setpoint, "Thermostat Cooling Setpoint", None,
|
||||||
DEVICE_CLASS_TEMPERATURE)],
|
DEVICE_CLASS_TEMPERATURE)],
|
||||||
'thermostatFanMode': [
|
Capability.thermostat_fan_mode: [
|
||||||
Map('thermostatFanMode', "Thermostat Fan Mode", None, None)],
|
Map(Attribute.thermostat_fan_mode, "Thermostat Fan Mode", None, None)],
|
||||||
'thermostatHeatingSetpoint': [
|
Capability.thermostat_heating_setpoint: [
|
||||||
Map('heatingSetpoint', "Thermostat Heating Setpoint", None,
|
Map(Attribute.heating_setpoint, "Thermostat Heating Setpoint", None,
|
||||||
DEVICE_CLASS_TEMPERATURE)],
|
DEVICE_CLASS_TEMPERATURE)],
|
||||||
'thermostatMode': [
|
Capability.thermostat_mode: [
|
||||||
Map('thermostatMode', "Thermostat Mode", None, None)],
|
Map(Attribute.thermostat_mode, "Thermostat Mode", None, None)],
|
||||||
'thermostatOperatingState': [
|
Capability.thermostat_operating_state: [
|
||||||
Map('thermostatOperatingState', "Thermostat Operating State",
|
Map(Attribute.thermostat_operating_state, "Thermostat Operating State",
|
||||||
None, None)],
|
None, None)],
|
||||||
'thermostatSetpoint': [
|
Capability.thermostat_setpoint: [
|
||||||
Map('thermostatSetpoint', "Thermostat Setpoint", None,
|
Map(Attribute.thermostat_setpoint, "Thermostat Setpoint", None,
|
||||||
DEVICE_CLASS_TEMPERATURE)],
|
DEVICE_CLASS_TEMPERATURE)],
|
||||||
'threeAxis': [
|
Capability.three_axis: [],
|
||||||
Map('threeAxis', "Three Axis", None, None)],
|
Capability.tv_channel: [
|
||||||
'tvChannel': [
|
Map(Attribute.tv_channel, "Tv Channel", None, None)],
|
||||||
Map('tvChannel', "Tv Channel", None, None)],
|
Capability.tvoc_measurement: [
|
||||||
'tvocMeasurement': [
|
Map(Attribute.tvoc_level, "Tvoc Measurement", 'ppm', None)],
|
||||||
Map('tvocLevel', "Tvoc Measurement", 'ppm', None)],
|
Capability.ultraviolet_index: [
|
||||||
'ultravioletIndex': [
|
Map(Attribute.ultraviolet_index, "Ultraviolet Index", None, None)],
|
||||||
Map('ultravioletIndex', "Ultraviolet Index", None, None)],
|
Capability.voltage_measurement: [
|
||||||
'voltageMeasurement': [
|
Map(Attribute.voltage, "Voltage Measurement", 'V', None)],
|
||||||
Map('voltage', "Voltage Measurement", 'V', None)],
|
Capability.washer_mode: [
|
||||||
'washerMode': [
|
Map(Attribute.washer_mode, "Washer Mode", None, None)],
|
||||||
Map('washerMode', "Washer Mode", None, None)],
|
Capability.washer_operating_state: [
|
||||||
'washerOperatingState': [
|
Map(Attribute.machine_state, "Washer Machine State", None, None),
|
||||||
Map('machineState', "Washer Machine State", None, None),
|
Map(Attribute.washer_job_state, "Washer Job State", None, None),
|
||||||
Map('washerJobState', "Washer Job State", None, None),
|
Map(Attribute.completion_time, "Washer Completion Time", None,
|
||||||
Map('completionTime', "Washer Completion Time", None,
|
|
||||||
DEVICE_CLASS_TIMESTAMP)]
|
DEVICE_CLASS_TIMESTAMP)]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -158,7 +167,6 @@ async def async_setup_platform(
|
||||||
|
|
||||||
async def async_setup_entry(hass, config_entry, async_add_entities):
|
async def async_setup_entry(hass, config_entry, async_add_entities):
|
||||||
"""Add binary sensors for a config entry."""
|
"""Add binary sensors for a config entry."""
|
||||||
from pysmartthings import Capability
|
|
||||||
broker = hass.data[DOMAIN][DATA_BROKERS][config_entry.entry_id]
|
broker = hass.data[DOMAIN][DATA_BROKERS][config_entry.entry_id]
|
||||||
sensors = []
|
sensors = []
|
||||||
for device in broker.devices.values():
|
for device in broker.devices.values():
|
||||||
|
@ -245,7 +253,6 @@ class SmartThingsThreeAxisSensor(SmartThingsEntity):
|
||||||
@property
|
@property
|
||||||
def state(self):
|
def state(self):
|
||||||
"""Return the state of the sensor."""
|
"""Return the state of the sensor."""
|
||||||
from pysmartthings import Attribute
|
|
||||||
three_axis = self._device.status.attributes[Attribute.three_axis].value
|
three_axis = self._device.status.attributes[Attribute.three_axis].value
|
||||||
try:
|
try:
|
||||||
return three_axis[self._index]
|
return three_axis[self._index]
|
||||||
|
|
|
@ -6,6 +6,12 @@ from urllib.parse import urlparse
|
||||||
from uuid import uuid4
|
from uuid import uuid4
|
||||||
|
|
||||||
from aiohttp import web
|
from aiohttp import web
|
||||||
|
from pysmartapp import Dispatcher, SmartAppManager
|
||||||
|
from pysmartapp.const import SETTINGS_APP_ID
|
||||||
|
from pysmartthings import (
|
||||||
|
APP_TYPE_WEBHOOK, CAPABILITIES, CLASSIFICATION_AUTOMATION, App, AppOAuth,
|
||||||
|
AppSettings, InstalledAppStatus, SmartThings, SourceType, Subscription,
|
||||||
|
SubscriptionEntity)
|
||||||
|
|
||||||
from homeassistant.components import cloud, webhook
|
from homeassistant.components import cloud, webhook
|
||||||
from homeassistant.const import CONF_WEBHOOK_ID
|
from homeassistant.const import CONF_WEBHOOK_ID
|
||||||
|
@ -43,8 +49,6 @@ async def validate_installed_app(api, installed_app_id: str):
|
||||||
Query the API for the installed SmartApp and validate that it is tied to
|
Query the API for the installed SmartApp and validate that it is tied to
|
||||||
the specified app_id and is in an authorized state.
|
the specified app_id and is in an authorized state.
|
||||||
"""
|
"""
|
||||||
from pysmartthings import InstalledAppStatus
|
|
||||||
|
|
||||||
installed_app = await api.installed_app(installed_app_id)
|
installed_app = await api.installed_app(installed_app_id)
|
||||||
if installed_app.installed_app_status != InstalledAppStatus.AUTHORIZED:
|
if installed_app.installed_app_status != InstalledAppStatus.AUTHORIZED:
|
||||||
raise RuntimeWarning("Installed SmartApp instance '{}' ({}) is not "
|
raise RuntimeWarning("Installed SmartApp instance '{}' ({}) is not "
|
||||||
|
@ -77,8 +81,6 @@ def get_webhook_url(hass: HomeAssistantType) -> str:
|
||||||
|
|
||||||
|
|
||||||
def _get_app_template(hass: HomeAssistantType):
|
def _get_app_template(hass: HomeAssistantType):
|
||||||
from pysmartthings import APP_TYPE_WEBHOOK, CLASSIFICATION_AUTOMATION
|
|
||||||
|
|
||||||
endpoint = "at " + hass.config.api.base_url
|
endpoint = "at " + hass.config.api.base_url
|
||||||
cloudhook_url = hass.data[DOMAIN][CONF_CLOUDHOOK_URL]
|
cloudhook_url = hass.data[DOMAIN][CONF_CLOUDHOOK_URL]
|
||||||
if cloudhook_url is not None:
|
if cloudhook_url is not None:
|
||||||
|
@ -98,9 +100,6 @@ def _get_app_template(hass: HomeAssistantType):
|
||||||
|
|
||||||
async def create_app(hass: HomeAssistantType, api):
|
async def create_app(hass: HomeAssistantType, api):
|
||||||
"""Create a SmartApp for this instance of hass."""
|
"""Create a SmartApp for this instance of hass."""
|
||||||
from pysmartthings import App, AppOAuth, AppSettings
|
|
||||||
from pysmartapp.const import SETTINGS_APP_ID
|
|
||||||
|
|
||||||
# Create app from template attributes
|
# Create app from template attributes
|
||||||
template = _get_app_template(hass)
|
template = _get_app_template(hass)
|
||||||
app = App()
|
app = App()
|
||||||
|
@ -170,8 +169,6 @@ async def setup_smartapp_endpoint(hass: HomeAssistantType):
|
||||||
SmartApps are an extension point within the SmartThings ecosystem and
|
SmartApps are an extension point within the SmartThings ecosystem and
|
||||||
is used to receive push updates (i.e. device updates) from the cloud.
|
is used to receive push updates (i.e. device updates) from the cloud.
|
||||||
"""
|
"""
|
||||||
from pysmartapp import Dispatcher, SmartAppManager
|
|
||||||
|
|
||||||
data = hass.data.get(DOMAIN)
|
data = hass.data.get(DOMAIN)
|
||||||
if data:
|
if data:
|
||||||
# already setup
|
# already setup
|
||||||
|
@ -264,11 +261,6 @@ async def smartapp_sync_subscriptions(
|
||||||
hass: HomeAssistantType, auth_token: str, location_id: str,
|
hass: HomeAssistantType, auth_token: str, location_id: str,
|
||||||
installed_app_id: str, devices):
|
installed_app_id: str, devices):
|
||||||
"""Synchronize subscriptions of an installed up."""
|
"""Synchronize subscriptions of an installed up."""
|
||||||
from pysmartthings import (
|
|
||||||
CAPABILITIES, SmartThings, SourceType, Subscription,
|
|
||||||
SubscriptionEntity
|
|
||||||
)
|
|
||||||
|
|
||||||
api = SmartThings(async_get_clientsession(hass), auth_token)
|
api = SmartThings(async_get_clientsession(hass), auth_token)
|
||||||
tasks = []
|
tasks = []
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
"""Support for switches through the SmartThings cloud API."""
|
"""Support for switches through the SmartThings cloud API."""
|
||||||
from typing import Optional, Sequence
|
from typing import Optional, Sequence
|
||||||
|
|
||||||
|
from pysmartthings import Attribute, Capability
|
||||||
|
|
||||||
from homeassistant.components.switch import SwitchDevice
|
from homeassistant.components.switch import SwitchDevice
|
||||||
|
|
||||||
from . import SmartThingsEntity
|
from . import SmartThingsEntity
|
||||||
|
@ -23,8 +25,6 @@ async def async_setup_entry(hass, config_entry, async_add_entities):
|
||||||
|
|
||||||
def get_capabilities(capabilities: Sequence[str]) -> Optional[Sequence[str]]:
|
def get_capabilities(capabilities: Sequence[str]) -> Optional[Sequence[str]]:
|
||||||
"""Return all capabilities supported if minimum required are present."""
|
"""Return all capabilities supported if minimum required are present."""
|
||||||
from pysmartthings import Capability
|
|
||||||
|
|
||||||
# Must be able to be turned on/off.
|
# Must be able to be turned on/off.
|
||||||
if Capability.switch in capabilities:
|
if Capability.switch in capabilities:
|
||||||
return [Capability.switch,
|
return [Capability.switch,
|
||||||
|
@ -53,13 +53,11 @@ class SmartThingsSwitch(SmartThingsEntity, SwitchDevice):
|
||||||
@property
|
@property
|
||||||
def current_power_w(self):
|
def current_power_w(self):
|
||||||
"""Return the current power usage in W."""
|
"""Return the current power usage in W."""
|
||||||
from pysmartthings import Attribute
|
|
||||||
return self._device.status.attributes[Attribute.power].value
|
return self._device.status.attributes[Attribute.power].value
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def today_energy_kwh(self):
|
def today_energy_kwh(self):
|
||||||
"""Return the today total energy usage in kWh."""
|
"""Return the today total energy usage in kWh."""
|
||||||
from pysmartthings import Attribute
|
|
||||||
return self._device.status.attributes[Attribute.energy].value
|
return self._device.status.attributes[Attribute.energy].value
|
||||||
|
|
||||||
@property
|
@property
|
||||||
|
|
|
@ -5,7 +5,8 @@ from uuid import uuid4
|
||||||
|
|
||||||
from pysmartthings import (
|
from pysmartthings import (
|
||||||
CLASSIFICATION_AUTOMATION, AppEntity, AppOAuthClient, AppSettings,
|
CLASSIFICATION_AUTOMATION, AppEntity, AppOAuthClient, AppSettings,
|
||||||
DeviceEntity, InstalledApp, Location, SceneEntity, Subscription)
|
DeviceEntity, InstalledApp, Location, SceneEntity, SmartThings,
|
||||||
|
Subscription)
|
||||||
from pysmartthings.api import Api
|
from pysmartthings.api import Api
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
|
@ -23,6 +24,8 @@ from homeassistant.setup import async_setup_component
|
||||||
|
|
||||||
from tests.common import mock_coro
|
from tests.common import mock_coro
|
||||||
|
|
||||||
|
COMPONENT_PREFIX = "homeassistant.components.smartthings."
|
||||||
|
|
||||||
|
|
||||||
async def setup_platform(hass, platform: str, *,
|
async def setup_platform(hass, platform: str, *,
|
||||||
devices=None, scenes=None):
|
devices=None, scenes=None):
|
||||||
|
@ -163,8 +166,12 @@ def smartthings_mock_fixture(locations):
|
||||||
return_value=next(location for location in locations
|
return_value=next(location for location in locations
|
||||||
if location.location_id == location_id))
|
if location.location_id == location_id))
|
||||||
|
|
||||||
with patch("pysmartthings.SmartThings", autospec=True) as mock:
|
smartthings_mock = Mock(SmartThings)
|
||||||
mock.return_value.location.side_effect = _location
|
smartthings_mock.location.side_effect = _location
|
||||||
|
mock = Mock(return_value=smartthings_mock)
|
||||||
|
with patch(COMPONENT_PREFIX + "SmartThings", new=mock), \
|
||||||
|
patch(COMPONENT_PREFIX + "config_flow.SmartThings", new=mock), \
|
||||||
|
patch(COMPONENT_PREFIX + "smartapp.SmartThings", new=mock):
|
||||||
yield mock
|
yield mock
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue