commit
0727c7b9e8
29 changed files with 715 additions and 335 deletions
|
@ -298,8 +298,6 @@ class BayesianBinarySensor(BinarySensorDevice):
|
|||
@property
|
||||
def device_state_attributes(self):
|
||||
"""Return the state attributes of the sensor."""
|
||||
print(self.current_observations)
|
||||
print(self.observations_by_entity)
|
||||
return {
|
||||
ATTR_OBSERVATIONS: list(self.current_observations.values()),
|
||||
ATTR_OCCURRED_OBSERVATION_ENTITIES: list(
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
"domain": "frontend",
|
||||
"name": "Home Assistant Frontend",
|
||||
"documentation": "https://www.home-assistant.io/integrations/frontend",
|
||||
"requirements": ["home-assistant-frontend==20200407.1"],
|
||||
"requirements": ["home-assistant-frontend==20200407.2"],
|
||||
"dependencies": [
|
||||
"api",
|
||||
"auth",
|
||||
|
|
|
@ -116,7 +116,12 @@ class IPPMarkerSensor(IPPSensor):
|
|||
@property
|
||||
def state(self) -> Union[None, str, int, float]:
|
||||
"""Return the state of the sensor."""
|
||||
return self.coordinator.data.markers[self.marker_index].level
|
||||
level = self.coordinator.data.markers[self.marker_index].level
|
||||
|
||||
if level >= 0:
|
||||
return level
|
||||
|
||||
return None
|
||||
|
||||
|
||||
class IPPPrinterSensor(IPPSensor):
|
||||
|
|
|
@ -283,11 +283,6 @@ class KonnectedFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
|
|||
# build config info and wait for user confirmation
|
||||
self.data[CONF_HOST] = user_input[CONF_HOST]
|
||||
self.data[CONF_PORT] = user_input[CONF_PORT]
|
||||
self.data[CONF_ACCESS_TOKEN] = self.hass.data.get(DOMAIN, {}).get(
|
||||
CONF_ACCESS_TOKEN
|
||||
) or "".join(
|
||||
random.choices(f"{string.ascii_uppercase}{string.digits}", k=20)
|
||||
)
|
||||
|
||||
# brief delay to allow processing of recent status req
|
||||
await asyncio.sleep(0.1)
|
||||
|
@ -343,8 +338,12 @@ class KonnectedFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
|
|||
},
|
||||
)
|
||||
|
||||
# Attach default options and create entry
|
||||
# Create access token, attach default options and create entry
|
||||
self.data[CONF_DEFAULT_OPTIONS] = self.options
|
||||
self.data[CONF_ACCESS_TOKEN] = self.hass.data.get(DOMAIN, {}).get(
|
||||
CONF_ACCESS_TOKEN
|
||||
) or "".join(random.choices(f"{string.ascii_uppercase}{string.digits}", k=20))
|
||||
|
||||
return self.async_create_entry(
|
||||
title=KONN_PANEL_MODEL_NAMES[self.data[CONF_MODEL]], data=self.data,
|
||||
)
|
||||
|
|
|
@ -21,7 +21,6 @@ from homeassistant.const import (
|
|||
CONF_PORT,
|
||||
CONF_TIMEOUT,
|
||||
CONF_TYPE,
|
||||
EVENT_HOMEASSISTANT_START,
|
||||
EVENT_HOMEASSISTANT_STOP,
|
||||
)
|
||||
import homeassistant.helpers.config_validation as cv
|
||||
|
@ -36,7 +35,7 @@ from .const import (
|
|||
CONF_PARITY,
|
||||
CONF_STOPBITS,
|
||||
DEFAULT_HUB,
|
||||
MODBUS_DOMAIN,
|
||||
MODBUS_DOMAIN as DOMAIN,
|
||||
SERVICE_WRITE_COIL,
|
||||
SERVICE_WRITE_REGISTER,
|
||||
)
|
||||
|
@ -69,7 +68,7 @@ ETHERNET_SCHEMA = BASE_SCHEMA.extend(
|
|||
)
|
||||
|
||||
CONFIG_SCHEMA = vol.Schema(
|
||||
{MODBUS_DOMAIN: vol.All(cv.ensure_list, [vol.Any(SERIAL_SCHEMA, ETHERNET_SCHEMA)])},
|
||||
{DOMAIN: vol.All(cv.ensure_list, [vol.Any(SERIAL_SCHEMA, ETHERNET_SCHEMA)])},
|
||||
extra=vol.ALLOW_EXTRA,
|
||||
)
|
||||
|
||||
|
@ -96,10 +95,9 @@ SERVICE_WRITE_COIL_SCHEMA = vol.Schema(
|
|||
|
||||
async def async_setup(hass, config):
|
||||
"""Set up Modbus component."""
|
||||
hass.data[MODBUS_DOMAIN] = hub_collect = {}
|
||||
hass.data[DOMAIN] = hub_collect = {}
|
||||
|
||||
_LOGGER.debug("registering hubs")
|
||||
for client_config in config[MODBUS_DOMAIN]:
|
||||
for client_config in config[DOMAIN]:
|
||||
hub_collect[client_config[CONF_NAME]] = ModbusHub(client_config, hass.loop)
|
||||
|
||||
def stop_modbus(event):
|
||||
|
@ -107,28 +105,13 @@ async def async_setup(hass, config):
|
|||
for client in hub_collect.values():
|
||||
del client
|
||||
|
||||
def start_modbus(event):
|
||||
def start_modbus():
|
||||
"""Start Modbus service."""
|
||||
for client in hub_collect.values():
|
||||
_LOGGER.debug("setup hub %s", client.name)
|
||||
client.setup()
|
||||
|
||||
hass.bus.listen_once(EVENT_HOMEASSISTANT_STOP, stop_modbus)
|
||||
|
||||
# Register services for modbus
|
||||
hass.services.async_register(
|
||||
MODBUS_DOMAIN,
|
||||
SERVICE_WRITE_REGISTER,
|
||||
write_register,
|
||||
schema=SERVICE_WRITE_REGISTER_SCHEMA,
|
||||
)
|
||||
hass.services.async_register(
|
||||
MODBUS_DOMAIN,
|
||||
SERVICE_WRITE_COIL,
|
||||
write_coil,
|
||||
schema=SERVICE_WRITE_COIL_SCHEMA,
|
||||
)
|
||||
|
||||
async def write_register(service):
|
||||
"""Write Modbus registers."""
|
||||
unit = int(float(service.data[ATTR_UNIT]))
|
||||
|
@ -152,8 +135,19 @@ async def async_setup(hass, config):
|
|||
client_name = service.data[ATTR_HUB]
|
||||
await hub_collect[client_name].write_coil(unit, address, state)
|
||||
|
||||
hass.bus.async_listen_once(EVENT_HOMEASSISTANT_START, start_modbus)
|
||||
# do not wait for EVENT_HOMEASSISTANT_START, activate pymodbus now
|
||||
await hass.async_add_executor_job(start_modbus)
|
||||
|
||||
# Register services for modbus
|
||||
hass.services.async_register(
|
||||
DOMAIN,
|
||||
SERVICE_WRITE_REGISTER,
|
||||
write_register,
|
||||
schema=SERVICE_WRITE_REGISTER_SCHEMA,
|
||||
)
|
||||
hass.services.async_register(
|
||||
DOMAIN, SERVICE_WRITE_COIL, write_coil, schema=SERVICE_WRITE_COIL_SCHEMA,
|
||||
)
|
||||
return True
|
||||
|
||||
|
||||
|
@ -162,7 +156,6 @@ class ModbusHub:
|
|||
|
||||
def __init__(self, client_config, main_loop):
|
||||
"""Initialize the Modbus hub."""
|
||||
_LOGGER.debug("Preparing setup: %s", client_config)
|
||||
|
||||
# generic configuration
|
||||
self._loop = main_loop
|
||||
|
@ -172,7 +165,7 @@ class ModbusHub:
|
|||
self._config_type = client_config[CONF_TYPE]
|
||||
self._config_port = client_config[CONF_PORT]
|
||||
self._config_timeout = client_config[CONF_TIMEOUT]
|
||||
self._config_delay = client_config[CONF_DELAY]
|
||||
self._config_delay = 0
|
||||
|
||||
if self._config_type == "serial":
|
||||
# serial configuration
|
||||
|
@ -184,6 +177,7 @@ class ModbusHub:
|
|||
else:
|
||||
# network configuration
|
||||
self._config_host = client_config[CONF_HOST]
|
||||
self._config_delay = client_config[CONF_DELAY]
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
|
@ -201,7 +195,6 @@ class ModbusHub:
|
|||
# Client* do deliver loop, client as result but
|
||||
# pylint does not accept that fact
|
||||
|
||||
_LOGGER.debug("doing setup")
|
||||
if self._config_type == "serial":
|
||||
_, self._client = ClientSerial(
|
||||
schedulers.ASYNC_IO,
|
||||
|
@ -211,7 +204,6 @@ class ModbusHub:
|
|||
stopbits=self._config_stopbits,
|
||||
bytesize=self._config_bytesize,
|
||||
parity=self._config_parity,
|
||||
timeout=self._config_timeout,
|
||||
loop=self._loop,
|
||||
)
|
||||
elif self._config_type == "rtuovertcp":
|
||||
|
|
|
@ -54,7 +54,7 @@ PLATFORM_SCHEMA = vol.All(
|
|||
)
|
||||
|
||||
|
||||
async def async_setup_platform(hass, config, add_entities, discovery_info=None):
|
||||
async def async_setup_platform(hass, config, async_add_entities, discovery_info=None):
|
||||
"""Set up the Modbus binary sensors."""
|
||||
sensors = []
|
||||
for entry in config[CONF_INPUTS]:
|
||||
|
@ -70,7 +70,7 @@ async def async_setup_platform(hass, config, add_entities, discovery_info=None):
|
|||
)
|
||||
)
|
||||
|
||||
add_entities(sensors)
|
||||
async_add_entities(sensors)
|
||||
|
||||
|
||||
class ModbusBinarySensor(BinarySensorDevice):
|
||||
|
|
|
@ -72,7 +72,7 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(
|
|||
)
|
||||
|
||||
|
||||
async def async_setup_platform(hass, config, add_entities, discovery_info=None):
|
||||
async def async_setup_platform(hass, config, async_add_entities, discovery_info=None):
|
||||
"""Set up the Modbus Thermostat Platform."""
|
||||
name = config[CONF_NAME]
|
||||
modbus_slave = config[CONF_SLAVE]
|
||||
|
@ -91,7 +91,7 @@ async def async_setup_platform(hass, config, add_entities, discovery_info=None):
|
|||
hub_name = config[CONF_HUB]
|
||||
hub = hass.data[MODBUS_DOMAIN][hub_name]
|
||||
|
||||
add_entities(
|
||||
async_add_entities(
|
||||
[
|
||||
ModbusThermostat(
|
||||
hub,
|
||||
|
|
|
@ -89,7 +89,7 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(
|
|||
)
|
||||
|
||||
|
||||
async def async_setup_platform(hass, config, add_entities, discovery_info=None):
|
||||
async def async_setup_platform(hass, config, async_add_entities, discovery_info=None):
|
||||
"""Set up the Modbus sensors."""
|
||||
sensors = []
|
||||
data_types = {DATA_TYPE_INT: {1: "h", 2: "i", 4: "q"}}
|
||||
|
@ -148,7 +148,7 @@ async def async_setup_platform(hass, config, add_entities, discovery_info=None):
|
|||
|
||||
if not sensors:
|
||||
return False
|
||||
add_entities(sensors)
|
||||
async_add_entities(sensors)
|
||||
|
||||
|
||||
class ModbusRegisterSensor(RestoreEntity):
|
||||
|
|
|
@ -76,7 +76,7 @@ PLATFORM_SCHEMA = vol.All(
|
|||
)
|
||||
|
||||
|
||||
async def async_setup_platform(hass, config, add_entities, discovery_info=None):
|
||||
async def async_setup_platform(hass, config, async_add_entities, discovery_info=None):
|
||||
"""Read configuration and create Modbus devices."""
|
||||
switches = []
|
||||
if CONF_COILS in config:
|
||||
|
@ -109,7 +109,7 @@ async def async_setup_platform(hass, config, add_entities, discovery_info=None):
|
|||
)
|
||||
)
|
||||
|
||||
add_entities(switches)
|
||||
async_add_entities(switches)
|
||||
|
||||
|
||||
class ModbusCoilSwitch(ToggleEntity, RestoreEntity):
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
"""Support for interfacing with Monoprice 6 zone home audio controller."""
|
||||
import logging
|
||||
|
||||
from serial import SerialException
|
||||
|
||||
from homeassistant import core
|
||||
from homeassistant.components.media_player import MediaPlayerDevice
|
||||
from homeassistant.components.media_player.const import (
|
||||
|
@ -18,6 +20,8 @@ from .const import CONF_SOURCES, DOMAIN, SERVICE_RESTORE, SERVICE_SNAPSHOT
|
|||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
PARALLEL_UPDATES = 1
|
||||
|
||||
SUPPORT_MONOPRICE = (
|
||||
SUPPORT_VOLUME_MUTE
|
||||
| SUPPORT_VOLUME_SET
|
||||
|
@ -127,9 +131,15 @@ class MonopriceZone(MediaPlayerDevice):
|
|||
|
||||
def update(self):
|
||||
"""Retrieve latest state."""
|
||||
state = self._monoprice.zone_status(self._zone_id)
|
||||
try:
|
||||
state = self._monoprice.zone_status(self._zone_id)
|
||||
except SerialException:
|
||||
_LOGGER.warning("Could not update zone %d", self._zone_id)
|
||||
return
|
||||
|
||||
if not state:
|
||||
return False
|
||||
return
|
||||
|
||||
self._state = STATE_ON if state.power else STATE_OFF
|
||||
self._volume = state.volume
|
||||
self._mute = state.mute
|
||||
|
@ -138,7 +148,6 @@ class MonopriceZone(MediaPlayerDevice):
|
|||
self._source = self._source_id_name[idx]
|
||||
else:
|
||||
self._source = None
|
||||
return True
|
||||
|
||||
@property
|
||||
def entity_registry_enabled_default(self):
|
||||
|
|
|
@ -63,7 +63,7 @@ SENSORS = (
|
|||
"nextcloud_storage_num_files",
|
||||
"nextcloud_storage_num_storages",
|
||||
"nextcloud_storage_num_storages_local",
|
||||
"nextcloud_storage_num_storage_home",
|
||||
"nextcloud_storage_num_storages_home",
|
||||
"nextcloud_storage_num_storages_other",
|
||||
"nextcloud_shares_num_shares",
|
||||
"nextcloud_shares_num_shares_user",
|
||||
|
@ -83,9 +83,9 @@ SENSORS = (
|
|||
"nextcloud_database_type",
|
||||
"nextcloud_database_version",
|
||||
"nextcloud_database_version",
|
||||
"nextcloud_activeusers_last5minutes",
|
||||
"nextcloud_activeusers_last1hour",
|
||||
"nextcloud_activeusers_last24hours",
|
||||
"nextcloud_activeUsers_last5minutes",
|
||||
"nextcloud_activeUsers_last1hour",
|
||||
"nextcloud_activeUsers_last24hours",
|
||||
)
|
||||
|
||||
|
||||
|
|
|
@ -516,7 +516,8 @@ class ONVIFHassCamera(Camera):
|
|||
"""Read image from a URL."""
|
||||
try:
|
||||
response = requests.get(self._snapshot, timeout=5, auth=auth)
|
||||
return response.content
|
||||
if response.status_code < 300:
|
||||
return response.content
|
||||
except requests.exceptions.RequestException as error:
|
||||
_LOGGER.error(
|
||||
"Fetch snapshot image failed from %s, falling back to FFmpeg; %s",
|
||||
|
@ -524,6 +525,8 @@ class ONVIFHassCamera(Camera):
|
|||
error,
|
||||
)
|
||||
|
||||
return None
|
||||
|
||||
image = await self.hass.async_add_job(fetch)
|
||||
|
||||
if image is None:
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
"""Shared class to maintain Plex server instances."""
|
||||
from functools import partial, wraps
|
||||
import logging
|
||||
import ssl
|
||||
from urllib.parse import urlparse
|
||||
|
@ -13,8 +12,8 @@ import requests.exceptions
|
|||
from homeassistant.components.media_player import DOMAIN as MP_DOMAIN
|
||||
from homeassistant.const import CONF_TOKEN, CONF_URL, CONF_VERIFY_SSL
|
||||
from homeassistant.core import callback
|
||||
from homeassistant.helpers.debounce import Debouncer
|
||||
from homeassistant.helpers.dispatcher import async_dispatcher_send
|
||||
from homeassistant.helpers.event import async_call_later
|
||||
|
||||
from .const import (
|
||||
CONF_CLIENT_IDENTIFIER,
|
||||
|
@ -43,31 +42,6 @@ plexapi.X_PLEX_PRODUCT = X_PLEX_PRODUCT
|
|||
plexapi.X_PLEX_VERSION = X_PLEX_VERSION
|
||||
|
||||
|
||||
def debounce(func):
|
||||
"""Decorate function to debounce callbacks from Plex websocket."""
|
||||
|
||||
unsub = None
|
||||
|
||||
async def call_later_listener(self, _):
|
||||
"""Handle call_later callback."""
|
||||
nonlocal unsub
|
||||
unsub = None
|
||||
await func(self)
|
||||
|
||||
@wraps(func)
|
||||
async def wrapper(self):
|
||||
"""Schedule async callback."""
|
||||
nonlocal unsub
|
||||
if unsub:
|
||||
_LOGGER.debug("Throttling update of %s", self.friendly_name)
|
||||
unsub() # pylint: disable=not-callable
|
||||
unsub = async_call_later(
|
||||
self.hass, DEBOUNCE_TIMEOUT, partial(call_later_listener, self),
|
||||
)
|
||||
|
||||
return wrapper
|
||||
|
||||
|
||||
class PlexServer:
|
||||
"""Manages a single Plex server connection."""
|
||||
|
||||
|
@ -87,6 +61,13 @@ class PlexServer:
|
|||
self._accounts = []
|
||||
self._owner_username = None
|
||||
self._version = None
|
||||
self.async_update_platforms = Debouncer(
|
||||
hass,
|
||||
_LOGGER,
|
||||
cooldown=DEBOUNCE_TIMEOUT,
|
||||
immediate=True,
|
||||
function=self._async_update_platforms,
|
||||
).async_call
|
||||
|
||||
# Header conditionally added as it is not available in config entry v1
|
||||
if CONF_CLIENT_IDENTIFIER in server_config:
|
||||
|
@ -192,8 +173,7 @@ class PlexServer:
|
|||
"""Fetch all data from the Plex server in a single method."""
|
||||
return (self._plex_server.clients(), self._plex_server.sessions())
|
||||
|
||||
@debounce
|
||||
async def async_update_platforms(self):
|
||||
async def _async_update_platforms(self):
|
||||
"""Update the platform entities."""
|
||||
_LOGGER.debug("Updating devices")
|
||||
|
||||
|
|
|
@ -3,6 +3,5 @@
|
|||
"name": "Switcher",
|
||||
"documentation": "https://www.home-assistant.io/integrations/switcher_kis/",
|
||||
"codeowners": ["@tomerfi"],
|
||||
"requirements": ["aioswitcher==2019.4.26"],
|
||||
"dependencies": []
|
||||
"requirements": ["aioswitcher==1.1.0"]
|
||||
}
|
||||
|
|
|
@ -40,6 +40,7 @@ ATTR_MONTHLY_ENERGY_KWH = "monthly_energy_kwh"
|
|||
|
||||
LIGHT_STATE_DFT_ON = "dft_on_state"
|
||||
LIGHT_STATE_ON_OFF = "on_off"
|
||||
LIGHT_STATE_RELAY_STATE = "relay_state"
|
||||
LIGHT_STATE_BRIGHTNESS = "brightness"
|
||||
LIGHT_STATE_COLOR_TEMP = "color_temp"
|
||||
LIGHT_STATE_HUE = "hue"
|
||||
|
@ -128,6 +129,7 @@ class LightFeatures(NamedTuple):
|
|||
supported_features: int
|
||||
min_mireds: float
|
||||
max_mireds: float
|
||||
has_emeter: bool
|
||||
|
||||
|
||||
class TPLinkSmartBulb(Light):
|
||||
|
@ -285,8 +287,9 @@ class TPLinkSmartBulb(Light):
|
|||
model = sysinfo[LIGHT_SYSINFO_MODEL]
|
||||
min_mireds = None
|
||||
max_mireds = None
|
||||
has_emeter = self.smartbulb.has_emeter
|
||||
|
||||
if sysinfo.get(LIGHT_SYSINFO_IS_DIMMABLE):
|
||||
if sysinfo.get(LIGHT_SYSINFO_IS_DIMMABLE) or LIGHT_STATE_BRIGHTNESS in sysinfo:
|
||||
supported_features += SUPPORT_BRIGHTNESS
|
||||
if sysinfo.get(LIGHT_SYSINFO_IS_VARIABLE_COLOR_TEMP):
|
||||
supported_features += SUPPORT_COLOR_TEMP
|
||||
|
@ -306,6 +309,7 @@ class TPLinkSmartBulb(Light):
|
|||
supported_features=supported_features,
|
||||
min_mireds=min_mireds,
|
||||
max_mireds=max_mireds,
|
||||
has_emeter=has_emeter,
|
||||
)
|
||||
|
||||
def _get_light_state_retry(self) -> LightState:
|
||||
|
@ -357,10 +361,10 @@ class TPLinkSmartBulb(Light):
|
|||
def _get_light_state(self) -> LightState:
|
||||
"""Get the light state."""
|
||||
self._update_emeter()
|
||||
return self._light_state_from_params(self.smartbulb.get_light_state())
|
||||
return self._light_state_from_params(self._get_device_state())
|
||||
|
||||
def _update_emeter(self):
|
||||
if not self.smartbulb.has_emeter:
|
||||
if not self._light_features.has_emeter:
|
||||
return
|
||||
|
||||
now = dt_util.utcnow()
|
||||
|
@ -439,7 +443,44 @@ class TPLinkSmartBulb(Light):
|
|||
if not diff:
|
||||
return
|
||||
|
||||
return self.smartbulb.set_light_state(diff)
|
||||
return self._set_device_state(diff)
|
||||
|
||||
def _get_device_state(self):
|
||||
"""State of the bulb or smart dimmer switch."""
|
||||
if isinstance(self.smartbulb, SmartBulb):
|
||||
return self.smartbulb.get_light_state()
|
||||
|
||||
sysinfo = self.smartbulb.sys_info
|
||||
# Its not really a bulb, its a dimmable SmartPlug (aka Wall Switch)
|
||||
return {
|
||||
LIGHT_STATE_ON_OFF: sysinfo[LIGHT_STATE_RELAY_STATE],
|
||||
LIGHT_STATE_BRIGHTNESS: sysinfo.get(LIGHT_STATE_BRIGHTNESS, 0),
|
||||
LIGHT_STATE_COLOR_TEMP: 0,
|
||||
LIGHT_STATE_HUE: 0,
|
||||
LIGHT_STATE_SATURATION: 0,
|
||||
}
|
||||
|
||||
def _set_device_state(self, state):
|
||||
"""Set state of the bulb or smart dimmer switch."""
|
||||
if isinstance(self.smartbulb, SmartBulb):
|
||||
return self.smartbulb.set_light_state(state)
|
||||
|
||||
# Its not really a bulb, its a dimmable SmartPlug (aka Wall Switch)
|
||||
if LIGHT_STATE_BRIGHTNESS in state:
|
||||
# Brightness of 0 is accepted by the
|
||||
# device but the underlying library rejects it
|
||||
# so we turn off instead.
|
||||
if state[LIGHT_STATE_BRIGHTNESS]:
|
||||
self.smartbulb.brightness = state[LIGHT_STATE_BRIGHTNESS]
|
||||
else:
|
||||
self.smartbulb.state = self.smartbulb.SWITCH_STATE_OFF
|
||||
elif LIGHT_STATE_ON_OFF in state:
|
||||
if state[LIGHT_STATE_ON_OFF]:
|
||||
self.smartbulb.state = self.smartbulb.SWITCH_STATE_ON
|
||||
else:
|
||||
self.smartbulb.state = self.smartbulb.SWITCH_STATE_OFF
|
||||
|
||||
return self._get_device_state()
|
||||
|
||||
|
||||
def _light_state_diff(old_light_state: LightState, new_light_state: LightState):
|
||||
|
|
|
@ -2,8 +2,7 @@
|
|||
"domain": "vizio",
|
||||
"name": "VIZIO SmartCast",
|
||||
"documentation": "https://www.home-assistant.io/integrations/vizio",
|
||||
"requirements": ["pyvizio==0.1.44"],
|
||||
"dependencies": [],
|
||||
"requirements": ["pyvizio==0.1.46"],
|
||||
"codeowners": ["@raman325"],
|
||||
"config_flow": true,
|
||||
"zeroconf": ["_viziocast._tcp.local."],
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
"""Constants used by Home Assistant components."""
|
||||
MAJOR_VERSION = 0
|
||||
MINOR_VERSION = 108
|
||||
PATCH_VERSION = "1"
|
||||
PATCH_VERSION = "2"
|
||||
__short_version__ = f"{MAJOR_VERSION}.{MINOR_VERSION}"
|
||||
__version__ = f"{__short_version__}.{PATCH_VERSION}"
|
||||
REQUIRED_PYTHON_VER = (3, 7, 0)
|
||||
|
|
|
@ -12,7 +12,7 @@ cryptography==2.8
|
|||
defusedxml==0.6.0
|
||||
distro==1.4.0
|
||||
hass-nabucasa==0.32.2
|
||||
home-assistant-frontend==20200407.1
|
||||
home-assistant-frontend==20200407.2
|
||||
importlib-metadata==1.5.0
|
||||
jinja2>=2.11.1
|
||||
netdisco==2.6.0
|
||||
|
|
|
@ -208,7 +208,7 @@ aiopvpc==1.0.2
|
|||
aiopylgtv==0.3.3
|
||||
|
||||
# homeassistant.components.switcher_kis
|
||||
aioswitcher==2019.4.26
|
||||
aioswitcher==1.1.0
|
||||
|
||||
# homeassistant.components.unifi
|
||||
aiounifi==15
|
||||
|
@ -704,7 +704,7 @@ hole==0.5.1
|
|||
holidays==0.10.1
|
||||
|
||||
# homeassistant.components.frontend
|
||||
home-assistant-frontend==20200407.1
|
||||
home-assistant-frontend==20200407.2
|
||||
|
||||
# homeassistant.components.zwave
|
||||
homeassistant-pyozw==0.1.10
|
||||
|
@ -1738,7 +1738,7 @@ pyversasense==0.0.6
|
|||
pyvesync==1.1.0
|
||||
|
||||
# homeassistant.components.vizio
|
||||
pyvizio==0.1.44
|
||||
pyvizio==0.1.46
|
||||
|
||||
# homeassistant.components.velux
|
||||
pyvlx==0.2.12
|
||||
|
|
|
@ -91,7 +91,7 @@ aiopvpc==1.0.2
|
|||
aiopylgtv==0.3.3
|
||||
|
||||
# homeassistant.components.switcher_kis
|
||||
aioswitcher==2019.4.26
|
||||
aioswitcher==1.1.0
|
||||
|
||||
# homeassistant.components.unifi
|
||||
aiounifi==15
|
||||
|
@ -282,7 +282,7 @@ hole==0.5.1
|
|||
holidays==0.10.1
|
||||
|
||||
# homeassistant.components.frontend
|
||||
home-assistant-frontend==20200407.1
|
||||
home-assistant-frontend==20200407.2
|
||||
|
||||
# homeassistant.components.zwave
|
||||
homeassistant-pyozw==0.1.10
|
||||
|
@ -647,7 +647,7 @@ pyvera==0.3.7
|
|||
pyvesync==1.1.0
|
||||
|
||||
# homeassistant.components.vizio
|
||||
pyvizio==0.1.44
|
||||
pyvizio==0.1.46
|
||||
|
||||
# homeassistant.components.html5
|
||||
pywebpush==1.9.2
|
||||
|
|
|
@ -362,10 +362,11 @@ async def test_ssdp_host_update(hass, mock_panel):
|
|||
)
|
||||
assert result["type"] == "abort"
|
||||
|
||||
# confirm the host value was updated
|
||||
# confirm the host value was updated, access_token was not
|
||||
entry = hass.config_entries.async_entries(config_flow.DOMAIN)[0]
|
||||
assert entry.data["host"] == "1.1.1.1"
|
||||
assert entry.data["port"] == 1234
|
||||
assert entry.data["access_token"] == "11223344556677889900"
|
||||
|
||||
|
||||
async def test_import_existing_config(hass, mock_panel):
|
||||
|
@ -494,6 +495,7 @@ async def test_import_existing_config_entry(hass, mock_panel):
|
|||
data={
|
||||
"host": "0.0.0.0",
|
||||
"port": 1111,
|
||||
"access_token": "ORIGINALTOKEN",
|
||||
"id": "112233445566",
|
||||
"extra": "something",
|
||||
},
|
||||
|
@ -546,14 +548,14 @@ async def test_import_existing_config_entry(hass, mock_panel):
|
|||
|
||||
assert result["type"] == "abort"
|
||||
|
||||
# We should have updated the entry
|
||||
# We should have updated the host info but not the access token
|
||||
assert len(hass.config_entries.async_entries("konnected")) == 1
|
||||
assert hass.config_entries.async_entries("konnected")[0].data == {
|
||||
"host": "1.2.3.4",
|
||||
"port": 1234,
|
||||
"access_token": "ORIGINALTOKEN",
|
||||
"id": "112233445566",
|
||||
"model": "Konnected Pro",
|
||||
"access_token": "SUPERSECRETTOKEN",
|
||||
"extra": "something",
|
||||
}
|
||||
|
||||
|
|
90
tests/components/modbus/conftest.py
Normal file
90
tests/components/modbus/conftest.py
Normal file
|
@ -0,0 +1,90 @@
|
|||
"""The tests for the Modbus sensor component."""
|
||||
from datetime import timedelta
|
||||
import logging
|
||||
from unittest import mock
|
||||
|
||||
import pytest
|
||||
|
||||
from homeassistant.components.modbus.const import (
|
||||
CALL_TYPE_REGISTER_INPUT,
|
||||
CONF_REGISTER,
|
||||
CONF_REGISTER_TYPE,
|
||||
CONF_REGISTERS,
|
||||
DEFAULT_HUB,
|
||||
MODBUS_DOMAIN as DOMAIN,
|
||||
)
|
||||
from homeassistant.const import CONF_NAME, CONF_PLATFORM, CONF_SCAN_INTERVAL
|
||||
from homeassistant.setup import async_setup_component
|
||||
import homeassistant.util.dt as dt_util
|
||||
|
||||
from tests.common import MockModule, async_fire_time_changed, mock_integration
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@pytest.fixture()
|
||||
def mock_hub(hass):
|
||||
"""Mock hub."""
|
||||
mock_integration(hass, MockModule(DOMAIN))
|
||||
hub = mock.MagicMock()
|
||||
hub.name = "hub"
|
||||
hass.data[DOMAIN] = {DEFAULT_HUB: hub}
|
||||
return hub
|
||||
|
||||
|
||||
class ReadResult:
|
||||
"""Storage class for register read results."""
|
||||
|
||||
def __init__(self, register_words):
|
||||
"""Init."""
|
||||
self.registers = register_words
|
||||
|
||||
|
||||
read_result = None
|
||||
|
||||
|
||||
async def run_test(
|
||||
hass, use_mock_hub, register_config, entity_domain, register_words, expected
|
||||
):
|
||||
"""Run test for given config and check that sensor outputs expected result."""
|
||||
|
||||
async def simulate_read_registers(unit, address, count):
|
||||
"""Simulate modbus register read."""
|
||||
del unit, address, count # not used in simulation, but in real connection
|
||||
return read_result
|
||||
|
||||
# Full sensor configuration
|
||||
sensor_name = "modbus_test_sensor"
|
||||
scan_interval = 5
|
||||
config = {
|
||||
entity_domain: {
|
||||
CONF_PLATFORM: "modbus",
|
||||
CONF_SCAN_INTERVAL: scan_interval,
|
||||
CONF_REGISTERS: [
|
||||
dict(**{CONF_NAME: sensor_name, CONF_REGISTER: 1234}, **register_config)
|
||||
],
|
||||
}
|
||||
}
|
||||
|
||||
# Setup inputs for the sensor
|
||||
read_result = ReadResult(register_words)
|
||||
if register_config.get(CONF_REGISTER_TYPE) == CALL_TYPE_REGISTER_INPUT:
|
||||
use_mock_hub.read_input_registers = simulate_read_registers
|
||||
else:
|
||||
use_mock_hub.read_holding_registers = simulate_read_registers
|
||||
|
||||
# Initialize sensor
|
||||
now = dt_util.utcnow()
|
||||
with mock.patch("homeassistant.helpers.event.dt_util.utcnow", return_value=now):
|
||||
assert await async_setup_component(hass, entity_domain, config)
|
||||
|
||||
# Trigger update call with time_changed event
|
||||
now += timedelta(seconds=scan_interval + 1)
|
||||
with mock.patch("homeassistant.helpers.event.dt_util.utcnow", return_value=now):
|
||||
async_fire_time_changed(hass, now)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
# Check state
|
||||
entity_id = f"{entity_domain}.{sensor_name}"
|
||||
state = hass.states.get(entity_id).state
|
||||
assert state == expected
|
|
@ -1,8 +1,5 @@
|
|||
"""The tests for the Modbus sensor component."""
|
||||
from datetime import timedelta
|
||||
from unittest import mock
|
||||
|
||||
import pytest
|
||||
import logging
|
||||
|
||||
from homeassistant.components.modbus.const import (
|
||||
CALL_TYPE_REGISTER_HOLDING,
|
||||
|
@ -11,78 +8,18 @@ from homeassistant.components.modbus.const import (
|
|||
CONF_DATA_TYPE,
|
||||
CONF_OFFSET,
|
||||
CONF_PRECISION,
|
||||
CONF_REGISTER,
|
||||
CONF_REGISTER_TYPE,
|
||||
CONF_REGISTERS,
|
||||
CONF_REVERSE_ORDER,
|
||||
CONF_SCALE,
|
||||
DATA_TYPE_FLOAT,
|
||||
DATA_TYPE_INT,
|
||||
DATA_TYPE_UINT,
|
||||
DEFAULT_HUB,
|
||||
MODBUS_DOMAIN,
|
||||
)
|
||||
from homeassistant.const import CONF_NAME, CONF_PLATFORM, CONF_SCAN_INTERVAL
|
||||
from homeassistant.setup import async_setup_component
|
||||
import homeassistant.util.dt as dt_util
|
||||
from homeassistant.components.sensor import DOMAIN as SENSOR_DOMAIN
|
||||
|
||||
from tests.common import MockModule, async_fire_time_changed, mock_integration
|
||||
from .conftest import run_test
|
||||
|
||||
|
||||
@pytest.fixture()
|
||||
def mock_hub(hass):
|
||||
"""Mock hub."""
|
||||
mock_integration(hass, MockModule(MODBUS_DOMAIN))
|
||||
hub = mock.MagicMock()
|
||||
hub.name = "hub"
|
||||
hass.data[MODBUS_DOMAIN] = {DEFAULT_HUB: hub}
|
||||
return hub
|
||||
|
||||
|
||||
common_register_config = {CONF_NAME: "test-config", CONF_REGISTER: 1234}
|
||||
|
||||
|
||||
class ReadResult:
|
||||
"""Storage class for register read results."""
|
||||
|
||||
def __init__(self, register_words):
|
||||
"""Init."""
|
||||
self.registers = register_words
|
||||
|
||||
|
||||
async def run_test(hass, mock_hub, register_config, register_words, expected):
|
||||
"""Run test for given config and check that sensor outputs expected result."""
|
||||
|
||||
# Full sensor configuration
|
||||
sensor_name = "modbus_test_sensor"
|
||||
scan_interval = 5
|
||||
config = {
|
||||
MODBUS_DOMAIN: {
|
||||
CONF_PLATFORM: "modbus",
|
||||
CONF_SCAN_INTERVAL: scan_interval,
|
||||
CONF_REGISTERS: [
|
||||
dict(**{CONF_NAME: sensor_name, CONF_REGISTER: 1234}, **register_config)
|
||||
],
|
||||
}
|
||||
}
|
||||
|
||||
# Setup inputs for the sensor
|
||||
read_result = ReadResult(register_words)
|
||||
if register_config.get(CONF_REGISTER_TYPE) == CALL_TYPE_REGISTER_INPUT:
|
||||
mock_hub.read_input_registers.return_value = read_result
|
||||
else:
|
||||
mock_hub.read_holding_registers.return_value = read_result
|
||||
|
||||
# Initialize sensor
|
||||
now = dt_util.utcnow()
|
||||
with mock.patch("homeassistant.helpers.event.dt_util.utcnow", return_value=now):
|
||||
assert await async_setup_component(hass, MODBUS_DOMAIN, config)
|
||||
|
||||
# Trigger update call with time_changed event
|
||||
now += timedelta(seconds=scan_interval + 1)
|
||||
with mock.patch("homeassistant.helpers.event.dt_util.utcnow", return_value=now):
|
||||
async_fire_time_changed(hass, now)
|
||||
await hass.async_block_till_done()
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
||||
async def test_simple_word_register(hass, mock_hub):
|
||||
|
@ -94,14 +31,26 @@ async def test_simple_word_register(hass, mock_hub):
|
|||
CONF_OFFSET: 0,
|
||||
CONF_PRECISION: 0,
|
||||
}
|
||||
await run_test(hass, mock_hub, register_config, register_words=[0], expected="0")
|
||||
await run_test(
|
||||
hass,
|
||||
mock_hub,
|
||||
register_config,
|
||||
SENSOR_DOMAIN,
|
||||
register_words=[0],
|
||||
expected="0",
|
||||
)
|
||||
|
||||
|
||||
async def test_optional_conf_keys(hass, mock_hub):
|
||||
"""Test handling of optional configuration keys."""
|
||||
register_config = {}
|
||||
await run_test(
|
||||
hass, mock_hub, register_config, register_words=[0x8000], expected="-32768"
|
||||
hass,
|
||||
mock_hub,
|
||||
register_config,
|
||||
SENSOR_DOMAIN,
|
||||
register_words=[0x8000],
|
||||
expected="-32768",
|
||||
)
|
||||
|
||||
|
||||
|
@ -114,7 +63,14 @@ async def test_offset(hass, mock_hub):
|
|||
CONF_OFFSET: 13,
|
||||
CONF_PRECISION: 0,
|
||||
}
|
||||
await run_test(hass, mock_hub, register_config, register_words=[7], expected="20")
|
||||
await run_test(
|
||||
hass,
|
||||
mock_hub,
|
||||
register_config,
|
||||
SENSOR_DOMAIN,
|
||||
register_words=[7],
|
||||
expected="20",
|
||||
)
|
||||
|
||||
|
||||
async def test_scale_and_offset(hass, mock_hub):
|
||||
|
@ -126,7 +82,14 @@ async def test_scale_and_offset(hass, mock_hub):
|
|||
CONF_OFFSET: 13,
|
||||
CONF_PRECISION: 0,
|
||||
}
|
||||
await run_test(hass, mock_hub, register_config, register_words=[7], expected="34")
|
||||
await run_test(
|
||||
hass,
|
||||
mock_hub,
|
||||
register_config,
|
||||
SENSOR_DOMAIN,
|
||||
register_words=[7],
|
||||
expected="34",
|
||||
)
|
||||
|
||||
|
||||
async def test_ints_can_have_precision(hass, mock_hub):
|
||||
|
@ -139,7 +102,12 @@ async def test_ints_can_have_precision(hass, mock_hub):
|
|||
CONF_PRECISION: 4,
|
||||
}
|
||||
await run_test(
|
||||
hass, mock_hub, register_config, register_words=[7], expected="34.0000"
|
||||
hass,
|
||||
mock_hub,
|
||||
register_config,
|
||||
SENSOR_DOMAIN,
|
||||
register_words=[7],
|
||||
expected="34.0000",
|
||||
)
|
||||
|
||||
|
||||
|
@ -152,7 +120,14 @@ async def test_floats_get_rounded_correctly(hass, mock_hub):
|
|||
CONF_OFFSET: 0,
|
||||
CONF_PRECISION: 0,
|
||||
}
|
||||
await run_test(hass, mock_hub, register_config, register_words=[1], expected="2")
|
||||
await run_test(
|
||||
hass,
|
||||
mock_hub,
|
||||
register_config,
|
||||
SENSOR_DOMAIN,
|
||||
register_words=[1],
|
||||
expected="2",
|
||||
)
|
||||
|
||||
|
||||
async def test_parameters_as_strings(hass, mock_hub):
|
||||
|
@ -164,7 +139,14 @@ async def test_parameters_as_strings(hass, mock_hub):
|
|||
CONF_OFFSET: "5",
|
||||
CONF_PRECISION: "1",
|
||||
}
|
||||
await run_test(hass, mock_hub, register_config, register_words=[9], expected="18.5")
|
||||
await run_test(
|
||||
hass,
|
||||
mock_hub,
|
||||
register_config,
|
||||
SENSOR_DOMAIN,
|
||||
register_words=[9],
|
||||
expected="18.5",
|
||||
)
|
||||
|
||||
|
||||
async def test_floating_point_scale(hass, mock_hub):
|
||||
|
@ -176,7 +158,14 @@ async def test_floating_point_scale(hass, mock_hub):
|
|||
CONF_OFFSET: 0,
|
||||
CONF_PRECISION: 2,
|
||||
}
|
||||
await run_test(hass, mock_hub, register_config, register_words=[1], expected="2.40")
|
||||
await run_test(
|
||||
hass,
|
||||
mock_hub,
|
||||
register_config,
|
||||
SENSOR_DOMAIN,
|
||||
register_words=[1],
|
||||
expected="2.40",
|
||||
)
|
||||
|
||||
|
||||
async def test_floating_point_offset(hass, mock_hub):
|
||||
|
@ -188,7 +177,14 @@ async def test_floating_point_offset(hass, mock_hub):
|
|||
CONF_OFFSET: -10.3,
|
||||
CONF_PRECISION: 1,
|
||||
}
|
||||
await run_test(hass, mock_hub, register_config, register_words=[2], expected="-8.3")
|
||||
await run_test(
|
||||
hass,
|
||||
mock_hub,
|
||||
register_config,
|
||||
SENSOR_DOMAIN,
|
||||
register_words=[2],
|
||||
expected="-8.3",
|
||||
)
|
||||
|
||||
|
||||
async def test_signed_two_word_register(hass, mock_hub):
|
||||
|
@ -204,6 +200,7 @@ async def test_signed_two_word_register(hass, mock_hub):
|
|||
hass,
|
||||
mock_hub,
|
||||
register_config,
|
||||
SENSOR_DOMAIN,
|
||||
register_words=[0x89AB, 0xCDEF],
|
||||
expected="-1985229329",
|
||||
)
|
||||
|
@ -222,6 +219,7 @@ async def test_unsigned_two_word_register(hass, mock_hub):
|
|||
hass,
|
||||
mock_hub,
|
||||
register_config,
|
||||
SENSOR_DOMAIN,
|
||||
register_words=[0x89AB, 0xCDEF],
|
||||
expected=str(0x89ABCDEF),
|
||||
)
|
||||
|
@ -238,6 +236,7 @@ async def test_reversed(hass, mock_hub):
|
|||
hass,
|
||||
mock_hub,
|
||||
register_config,
|
||||
SENSOR_DOMAIN,
|
||||
register_words=[0x89AB, 0xCDEF],
|
||||
expected=str(0xCDEF89AB),
|
||||
)
|
||||
|
@ -256,6 +255,7 @@ async def test_four_word_register(hass, mock_hub):
|
|||
hass,
|
||||
mock_hub,
|
||||
register_config,
|
||||
SENSOR_DOMAIN,
|
||||
register_words=[0x89AB, 0xCDEF, 0x0123, 0x4567],
|
||||
expected="9920249030613615975",
|
||||
)
|
||||
|
@ -274,6 +274,7 @@ async def test_four_word_register_precision_is_intact_with_int_params(hass, mock
|
|||
hass,
|
||||
mock_hub,
|
||||
register_config,
|
||||
SENSOR_DOMAIN,
|
||||
register_words=[0x0123, 0x4567, 0x89AB, 0xCDEF],
|
||||
expected="163971058432973793",
|
||||
)
|
||||
|
@ -292,6 +293,7 @@ async def test_four_word_register_precision_is_lost_with_float_params(hass, mock
|
|||
hass,
|
||||
mock_hub,
|
||||
register_config,
|
||||
SENSOR_DOMAIN,
|
||||
register_words=[0x0123, 0x4567, 0x89AB, 0xCDEF],
|
||||
expected="163971058432973792",
|
||||
)
|
||||
|
@ -311,6 +313,7 @@ async def test_two_word_input_register(hass, mock_hub):
|
|||
hass,
|
||||
mock_hub,
|
||||
register_config,
|
||||
SENSOR_DOMAIN,
|
||||
register_words=[0x89AB, 0xCDEF],
|
||||
expected=str(0x89ABCDEF),
|
||||
)
|
||||
|
@ -330,6 +333,7 @@ async def test_two_word_holding_register(hass, mock_hub):
|
|||
hass,
|
||||
mock_hub,
|
||||
register_config,
|
||||
SENSOR_DOMAIN,
|
||||
register_words=[0x89AB, 0xCDEF],
|
||||
expected=str(0x89ABCDEF),
|
||||
)
|
||||
|
@ -349,6 +353,7 @@ async def test_float_data_type(hass, mock_hub):
|
|||
hass,
|
||||
mock_hub,
|
||||
register_config,
|
||||
SENSOR_DOMAIN,
|
||||
register_words=[16286, 1617],
|
||||
expected="1.23457",
|
||||
)
|
||||
|
|
|
@ -294,6 +294,58 @@ async def test_update(hass):
|
|||
assert "three" == state.attributes[ATTR_INPUT_SOURCE]
|
||||
|
||||
|
||||
async def test_failed_update(hass):
|
||||
"""Test updating failure from monoprice."""
|
||||
monoprice = MockMonoprice()
|
||||
await _setup_monoprice(hass, monoprice)
|
||||
|
||||
# Changing media player to new state
|
||||
await _call_media_player_service(
|
||||
hass, SERVICE_VOLUME_SET, {"entity_id": ZONE_1_ID, "volume_level": 0.0}
|
||||
)
|
||||
await _call_media_player_service(
|
||||
hass, SERVICE_SELECT_SOURCE, {"entity_id": ZONE_1_ID, "source": "one"}
|
||||
)
|
||||
|
||||
monoprice.set_source(11, 3)
|
||||
monoprice.set_volume(11, 38)
|
||||
|
||||
with patch.object(MockMonoprice, "zone_status", side_effect=SerialException):
|
||||
await async_update_entity(hass, ZONE_1_ID)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
state = hass.states.get(ZONE_1_ID)
|
||||
|
||||
assert state.attributes[ATTR_MEDIA_VOLUME_LEVEL] == 0.0
|
||||
assert state.attributes[ATTR_INPUT_SOURCE] == "one"
|
||||
|
||||
|
||||
async def test_empty_update(hass):
|
||||
"""Test updating with no state from monoprice."""
|
||||
monoprice = MockMonoprice()
|
||||
await _setup_monoprice(hass, monoprice)
|
||||
|
||||
# Changing media player to new state
|
||||
await _call_media_player_service(
|
||||
hass, SERVICE_VOLUME_SET, {"entity_id": ZONE_1_ID, "volume_level": 0.0}
|
||||
)
|
||||
await _call_media_player_service(
|
||||
hass, SERVICE_SELECT_SOURCE, {"entity_id": ZONE_1_ID, "source": "one"}
|
||||
)
|
||||
|
||||
monoprice.set_source(11, 3)
|
||||
monoprice.set_volume(11, 38)
|
||||
|
||||
with patch.object(MockMonoprice, "zone_status", return_value=None):
|
||||
await async_update_entity(hass, ZONE_1_ID)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
state = hass.states.get(ZONE_1_ID)
|
||||
|
||||
assert state.attributes[ATTR_MEDIA_VOLUME_LEVEL] == 0.0
|
||||
assert state.attributes[ATTR_INPUT_SOURCE] == "one"
|
||||
|
||||
|
||||
async def test_supported_features(hass):
|
||||
"""Test supported features property."""
|
||||
await _setup_monoprice(hass, MockMonoprice())
|
||||
|
|
|
@ -1,20 +0,0 @@
|
|||
"""Common fixtures and functions for Plex tests."""
|
||||
from datetime import timedelta
|
||||
|
||||
from homeassistant.components.plex.const import (
|
||||
DEBOUNCE_TIMEOUT,
|
||||
PLEX_UPDATE_PLATFORMS_SIGNAL,
|
||||
)
|
||||
from homeassistant.helpers.dispatcher import async_dispatcher_send
|
||||
import homeassistant.util.dt as dt_util
|
||||
|
||||
from tests.common import async_fire_time_changed
|
||||
|
||||
|
||||
async def trigger_plex_update(hass, server_id):
|
||||
"""Update Plex by sending signal and jumping ahead by debounce timeout."""
|
||||
async_dispatcher_send(hass, PLEX_UPDATE_PLATFORMS_SIGNAL.format(server_id))
|
||||
await hass.async_block_till_done()
|
||||
next_update = dt_util.utcnow() + timedelta(seconds=DEBOUNCE_TIMEOUT)
|
||||
async_fire_time_changed(hass, next_update)
|
||||
await hass.async_block_till_done()
|
|
@ -15,13 +15,14 @@ from homeassistant.components.plex.const import (
|
|||
CONF_USE_EPISODE_ART,
|
||||
DOMAIN,
|
||||
PLEX_SERVER_CONFIG,
|
||||
PLEX_UPDATE_PLATFORMS_SIGNAL,
|
||||
SERVERS,
|
||||
)
|
||||
from homeassistant.config_entries import ENTRY_STATE_LOADED
|
||||
from homeassistant.const import CONF_HOST, CONF_PORT, CONF_TOKEN, CONF_URL
|
||||
from homeassistant.helpers.dispatcher import async_dispatcher_send
|
||||
from homeassistant.setup import async_setup_component
|
||||
|
||||
from .common import trigger_plex_update
|
||||
from .const import DEFAULT_DATA, DEFAULT_OPTIONS, MOCK_SERVERS, MOCK_TOKEN
|
||||
from .mock_classes import MockPlexAccount, MockPlexServer
|
||||
|
||||
|
@ -415,7 +416,8 @@ async def test_option_flow_new_users_available(hass, caplog):
|
|||
|
||||
server_id = mock_plex_server.machineIdentifier
|
||||
|
||||
await trigger_plex_update(hass, server_id)
|
||||
async_dispatcher_send(hass, PLEX_UPDATE_PLATFORMS_SIGNAL.format(server_id))
|
||||
await hass.async_block_till_done()
|
||||
|
||||
monitored_users = hass.data[DOMAIN][SERVERS][server_id].option_monitored_users
|
||||
|
||||
|
|
|
@ -3,8 +3,9 @@ import copy
|
|||
from datetime import timedelta
|
||||
import ssl
|
||||
|
||||
from asynctest import patch
|
||||
from asynctest import ClockedTestCase, patch
|
||||
import plexapi
|
||||
import pytest
|
||||
import requests
|
||||
|
||||
from homeassistant.components.media_player import DOMAIN as MP_DOMAIN
|
||||
|
@ -23,14 +24,19 @@ from homeassistant.const import (
|
|||
CONF_URL,
|
||||
CONF_VERIFY_SSL,
|
||||
)
|
||||
from homeassistant.helpers.dispatcher import async_dispatcher_send
|
||||
from homeassistant.setup import async_setup_component
|
||||
import homeassistant.util.dt as dt_util
|
||||
|
||||
from .common import trigger_plex_update
|
||||
from .const import DEFAULT_DATA, DEFAULT_OPTIONS, MOCK_SERVERS, MOCK_TOKEN
|
||||
from .mock_classes import MockPlexAccount, MockPlexServer
|
||||
|
||||
from tests.common import MockConfigEntry, async_fire_time_changed
|
||||
from tests.common import (
|
||||
MockConfigEntry,
|
||||
async_fire_time_changed,
|
||||
async_test_home_assistant,
|
||||
mock_storage,
|
||||
)
|
||||
|
||||
|
||||
async def test_setup_with_config(hass):
|
||||
|
@ -67,70 +73,90 @@ async def test_setup_with_config(hass):
|
|||
|
||||
assert loaded_server.plex_server == mock_plex_server
|
||||
|
||||
assert server_id in hass.data[const.DOMAIN][const.DISPATCHERS]
|
||||
assert server_id in hass.data[const.DOMAIN][const.WEBSOCKETS]
|
||||
assert (
|
||||
hass.data[const.DOMAIN][const.PLATFORMS_COMPLETED][server_id] == const.PLATFORMS
|
||||
)
|
||||
|
||||
class TestClockedPlex(ClockedTestCase):
|
||||
"""Create clock-controlled asynctest class."""
|
||||
|
||||
async def test_setup_with_config_entry(hass, caplog):
|
||||
"""Test setup component with config."""
|
||||
@pytest.fixture(autouse=True)
|
||||
def inject_fixture(self, caplog):
|
||||
"""Inject pytest fixtures as instance attributes."""
|
||||
self.caplog = caplog
|
||||
|
||||
mock_plex_server = MockPlexServer()
|
||||
async def setUp(self):
|
||||
"""Initialize this test class."""
|
||||
self.hass = await async_test_home_assistant(self.loop)
|
||||
self.mock_storage = mock_storage()
|
||||
self.mock_storage.__enter__()
|
||||
|
||||
entry = MockConfigEntry(
|
||||
domain=const.DOMAIN,
|
||||
data=DEFAULT_DATA,
|
||||
options=DEFAULT_OPTIONS,
|
||||
unique_id=DEFAULT_DATA["server_id"],
|
||||
)
|
||||
async def tearDown(self):
|
||||
"""Clean up the HomeAssistant instance."""
|
||||
await self.hass.async_stop()
|
||||
self.mock_storage.__exit__(None, None, None)
|
||||
|
||||
with patch("plexapi.server.PlexServer", return_value=mock_plex_server), patch(
|
||||
"homeassistant.components.plex.PlexWebsocket.listen"
|
||||
) as mock_listen:
|
||||
entry.add_to_hass(hass)
|
||||
assert await hass.config_entries.async_setup(entry.entry_id)
|
||||
async def test_setup_with_config_entry(self):
|
||||
"""Test setup component with config."""
|
||||
hass = self.hass
|
||||
|
||||
mock_plex_server = MockPlexServer()
|
||||
|
||||
entry = MockConfigEntry(
|
||||
domain=const.DOMAIN,
|
||||
data=DEFAULT_DATA,
|
||||
options=DEFAULT_OPTIONS,
|
||||
unique_id=DEFAULT_DATA["server_id"],
|
||||
)
|
||||
|
||||
with patch("plexapi.server.PlexServer", return_value=mock_plex_server), patch(
|
||||
"homeassistant.components.plex.PlexWebsocket.listen"
|
||||
) as mock_listen:
|
||||
entry.add_to_hass(hass)
|
||||
assert await hass.config_entries.async_setup(entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert mock_listen.called
|
||||
|
||||
assert len(hass.config_entries.async_entries(const.DOMAIN)) == 1
|
||||
assert entry.state == ENTRY_STATE_LOADED
|
||||
|
||||
server_id = mock_plex_server.machineIdentifier
|
||||
loaded_server = hass.data[const.DOMAIN][const.SERVERS][server_id]
|
||||
|
||||
assert loaded_server.plex_server == mock_plex_server
|
||||
|
||||
async_dispatcher_send(
|
||||
hass, const.PLEX_UPDATE_PLATFORMS_SIGNAL.format(server_id)
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert mock_listen.called
|
||||
sensor = hass.states.get("sensor.plex_plex_server_1")
|
||||
assert sensor.state == str(len(mock_plex_server.accounts))
|
||||
|
||||
assert len(hass.config_entries.async_entries(const.DOMAIN)) == 1
|
||||
assert entry.state == ENTRY_STATE_LOADED
|
||||
|
||||
server_id = mock_plex_server.machineIdentifier
|
||||
loaded_server = hass.data[const.DOMAIN][const.SERVERS][server_id]
|
||||
|
||||
assert loaded_server.plex_server == mock_plex_server
|
||||
|
||||
assert server_id in hass.data[const.DOMAIN][const.DISPATCHERS]
|
||||
assert server_id in hass.data[const.DOMAIN][const.WEBSOCKETS]
|
||||
assert (
|
||||
hass.data[const.DOMAIN][const.PLATFORMS_COMPLETED][server_id] == const.PLATFORMS
|
||||
)
|
||||
|
||||
await trigger_plex_update(hass, server_id)
|
||||
|
||||
sensor = hass.states.get("sensor.plex_plex_server_1")
|
||||
assert sensor.state == str(len(mock_plex_server.accounts))
|
||||
|
||||
await trigger_plex_update(hass, server_id)
|
||||
|
||||
for test_exception in (
|
||||
plexapi.exceptions.BadRequest,
|
||||
requests.exceptions.RequestException,
|
||||
):
|
||||
with patch.object(
|
||||
mock_plex_server, "clients", side_effect=test_exception
|
||||
) as patched_clients_bad_request:
|
||||
await trigger_plex_update(hass, server_id)
|
||||
|
||||
assert patched_clients_bad_request.called
|
||||
assert (
|
||||
f"Could not connect to Plex server: {mock_plex_server.friendlyName}"
|
||||
in caplog.text
|
||||
# Ensure existing entities refresh
|
||||
await self.advance(const.DEBOUNCE_TIMEOUT)
|
||||
async_dispatcher_send(
|
||||
hass, const.PLEX_UPDATE_PLATFORMS_SIGNAL.format(server_id)
|
||||
)
|
||||
caplog.clear()
|
||||
await hass.async_block_till_done()
|
||||
|
||||
for test_exception in (
|
||||
plexapi.exceptions.BadRequest,
|
||||
requests.exceptions.RequestException,
|
||||
):
|
||||
with patch.object(
|
||||
mock_plex_server, "clients", side_effect=test_exception
|
||||
) as patched_clients_bad_request:
|
||||
await self.advance(const.DEBOUNCE_TIMEOUT)
|
||||
async_dispatcher_send(
|
||||
hass, const.PLEX_UPDATE_PLATFORMS_SIGNAL.format(server_id)
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert patched_clients_bad_request.called
|
||||
assert (
|
||||
f"Could not connect to Plex server: {mock_plex_server.friendlyName}"
|
||||
in self.caplog.text
|
||||
)
|
||||
self.caplog.clear()
|
||||
|
||||
|
||||
async def test_set_config_entry_unique_id(hass):
|
||||
|
@ -251,22 +277,12 @@ async def test_unload_config_entry(hass):
|
|||
|
||||
assert loaded_server.plex_server == mock_plex_server
|
||||
|
||||
assert server_id in hass.data[const.DOMAIN][const.DISPATCHERS]
|
||||
assert server_id in hass.data[const.DOMAIN][const.WEBSOCKETS]
|
||||
assert (
|
||||
hass.data[const.DOMAIN][const.PLATFORMS_COMPLETED][server_id] == const.PLATFORMS
|
||||
)
|
||||
|
||||
with patch("homeassistant.components.plex.PlexWebsocket.close") as mock_close:
|
||||
await hass.config_entries.async_unload(entry.entry_id)
|
||||
assert mock_close.called
|
||||
|
||||
assert entry.state == ENTRY_STATE_NOT_LOADED
|
||||
|
||||
assert server_id not in hass.data[const.DOMAIN][const.SERVERS]
|
||||
assert server_id not in hass.data[const.DOMAIN][const.DISPATCHERS]
|
||||
assert server_id not in hass.data[const.DOMAIN][const.WEBSOCKETS]
|
||||
|
||||
|
||||
async def test_setup_with_photo_session(hass):
|
||||
"""Test setup component with config."""
|
||||
|
@ -292,7 +308,8 @@ async def test_setup_with_photo_session(hass):
|
|||
|
||||
server_id = mock_plex_server.machineIdentifier
|
||||
|
||||
await trigger_plex_update(hass, server_id)
|
||||
async_dispatcher_send(hass, const.PLEX_UPDATE_PLATFORMS_SIGNAL.format(server_id))
|
||||
await hass.async_block_till_done()
|
||||
|
||||
media_player = hass.states.get("media_player.plex_product_title")
|
||||
assert media_player.state == "idle"
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
"""Tests for Plex server."""
|
||||
import copy
|
||||
from datetime import timedelta
|
||||
|
||||
from asynctest import patch
|
||||
from asynctest import ClockedTestCase, patch
|
||||
|
||||
from homeassistant.components.media_player import DOMAIN as MP_DOMAIN
|
||||
from homeassistant.components.plex.const import (
|
||||
|
@ -14,13 +13,11 @@ from homeassistant.components.plex.const import (
|
|||
SERVERS,
|
||||
)
|
||||
from homeassistant.helpers.dispatcher import async_dispatcher_send
|
||||
import homeassistant.util.dt as dt_util
|
||||
|
||||
from .common import trigger_plex_update
|
||||
from .const import DEFAULT_DATA, DEFAULT_OPTIONS
|
||||
from .mock_classes import MockPlexServer
|
||||
|
||||
from tests.common import MockConfigEntry, async_fire_time_changed
|
||||
from tests.common import MockConfigEntry, async_test_home_assistant, mock_storage
|
||||
|
||||
|
||||
async def test_new_users_available(hass):
|
||||
|
@ -48,7 +45,8 @@ async def test_new_users_available(hass):
|
|||
|
||||
server_id = mock_plex_server.machineIdentifier
|
||||
|
||||
await trigger_plex_update(hass, server_id)
|
||||
async_dispatcher_send(hass, PLEX_UPDATE_PLATFORMS_SIGNAL.format(server_id))
|
||||
await hass.async_block_till_done()
|
||||
|
||||
monitored_users = hass.data[DOMAIN][SERVERS][server_id].option_monitored_users
|
||||
|
||||
|
@ -86,7 +84,8 @@ async def test_new_ignored_users_available(hass, caplog):
|
|||
|
||||
server_id = mock_plex_server.machineIdentifier
|
||||
|
||||
await trigger_plex_update(hass, server_id)
|
||||
async_dispatcher_send(hass, PLEX_UPDATE_PLATFORMS_SIGNAL.format(server_id))
|
||||
await hass.async_block_till_done()
|
||||
|
||||
monitored_users = hass.data[DOMAIN][SERVERS][server_id].option_monitored_users
|
||||
|
||||
|
@ -100,72 +99,109 @@ async def test_new_ignored_users_available(hass, caplog):
|
|||
assert sensor.state == str(len(mock_plex_server.accounts))
|
||||
|
||||
|
||||
async def test_mark_sessions_idle(hass):
|
||||
"""Test marking media_players as idle when sessions end."""
|
||||
entry = MockConfigEntry(
|
||||
domain=DOMAIN,
|
||||
data=DEFAULT_DATA,
|
||||
options=DEFAULT_OPTIONS,
|
||||
unique_id=DEFAULT_DATA["server_id"],
|
||||
)
|
||||
class TestClockedPlex(ClockedTestCase):
|
||||
"""Create clock-controlled asynctest class."""
|
||||
|
||||
mock_plex_server = MockPlexServer(config_entry=entry)
|
||||
async def setUp(self):
|
||||
"""Initialize this test class."""
|
||||
self.hass = await async_test_home_assistant(self.loop)
|
||||
self.mock_storage = mock_storage()
|
||||
self.mock_storage.__enter__()
|
||||
|
||||
with patch("plexapi.server.PlexServer", return_value=mock_plex_server), patch(
|
||||
"homeassistant.components.plex.PlexWebsocket.listen"
|
||||
):
|
||||
entry.add_to_hass(hass)
|
||||
assert await hass.config_entries.async_setup(entry.entry_id)
|
||||
async def tearDown(self):
|
||||
"""Clean up the HomeAssistant instance."""
|
||||
await self.hass.async_stop()
|
||||
self.mock_storage.__exit__(None, None, None)
|
||||
|
||||
async def test_mark_sessions_idle(self):
|
||||
"""Test marking media_players as idle when sessions end."""
|
||||
hass = self.hass
|
||||
|
||||
entry = MockConfigEntry(
|
||||
domain=DOMAIN,
|
||||
data=DEFAULT_DATA,
|
||||
options=DEFAULT_OPTIONS,
|
||||
unique_id=DEFAULT_DATA["server_id"],
|
||||
)
|
||||
|
||||
mock_plex_server = MockPlexServer(config_entry=entry)
|
||||
|
||||
with patch("plexapi.server.PlexServer", return_value=mock_plex_server), patch(
|
||||
"homeassistant.components.plex.PlexWebsocket.listen"
|
||||
):
|
||||
entry.add_to_hass(hass)
|
||||
assert await hass.config_entries.async_setup(entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
server_id = mock_plex_server.machineIdentifier
|
||||
|
||||
async_dispatcher_send(hass, PLEX_UPDATE_PLATFORMS_SIGNAL.format(server_id))
|
||||
await hass.async_block_till_done()
|
||||
|
||||
server_id = mock_plex_server.machineIdentifier
|
||||
sensor = hass.states.get("sensor.plex_plex_server_1")
|
||||
assert sensor.state == str(len(mock_plex_server.accounts))
|
||||
|
||||
await trigger_plex_update(hass, server_id)
|
||||
mock_plex_server.clear_clients()
|
||||
mock_plex_server.clear_sessions()
|
||||
|
||||
sensor = hass.states.get("sensor.plex_plex_server_1")
|
||||
assert sensor.state == str(len(mock_plex_server.accounts))
|
||||
|
||||
mock_plex_server.clear_clients()
|
||||
mock_plex_server.clear_sessions()
|
||||
|
||||
await trigger_plex_update(hass, server_id)
|
||||
|
||||
sensor = hass.states.get("sensor.plex_plex_server_1")
|
||||
assert sensor.state == "0"
|
||||
|
||||
|
||||
async def test_debouncer(hass, caplog):
|
||||
"""Test debouncer decorator logic."""
|
||||
entry = MockConfigEntry(
|
||||
domain=DOMAIN,
|
||||
data=DEFAULT_DATA,
|
||||
options=DEFAULT_OPTIONS,
|
||||
unique_id=DEFAULT_DATA["server_id"],
|
||||
)
|
||||
|
||||
mock_plex_server = MockPlexServer(config_entry=entry)
|
||||
|
||||
with patch("plexapi.server.PlexServer", return_value=mock_plex_server), patch(
|
||||
"homeassistant.components.plex.PlexWebsocket.listen"
|
||||
):
|
||||
entry.add_to_hass(hass)
|
||||
assert await hass.config_entries.async_setup(entry.entry_id)
|
||||
await self.advance(DEBOUNCE_TIMEOUT)
|
||||
async_dispatcher_send(hass, PLEX_UPDATE_PLATFORMS_SIGNAL.format(server_id))
|
||||
await hass.async_block_till_done()
|
||||
|
||||
server_id = mock_plex_server.machineIdentifier
|
||||
sensor = hass.states.get("sensor.plex_plex_server_1")
|
||||
assert sensor.state == "0"
|
||||
|
||||
# First two updates are skipped
|
||||
async_dispatcher_send(hass, PLEX_UPDATE_PLATFORMS_SIGNAL.format(server_id))
|
||||
await hass.async_block_till_done()
|
||||
async_dispatcher_send(hass, PLEX_UPDATE_PLATFORMS_SIGNAL.format(server_id))
|
||||
await hass.async_block_till_done()
|
||||
async_dispatcher_send(hass, PLEX_UPDATE_PLATFORMS_SIGNAL.format(server_id))
|
||||
await hass.async_block_till_done()
|
||||
async def test_debouncer(self):
|
||||
"""Test debouncer behavior."""
|
||||
hass = self.hass
|
||||
|
||||
next_update = dt_util.utcnow() + timedelta(seconds=DEBOUNCE_TIMEOUT)
|
||||
async_fire_time_changed(hass, next_update)
|
||||
await hass.async_block_till_done()
|
||||
entry = MockConfigEntry(
|
||||
domain=DOMAIN,
|
||||
data=DEFAULT_DATA,
|
||||
options=DEFAULT_OPTIONS,
|
||||
unique_id=DEFAULT_DATA["server_id"],
|
||||
)
|
||||
|
||||
assert (
|
||||
caplog.text.count(f"Throttling update of {mock_plex_server.friendlyName}") == 2
|
||||
)
|
||||
mock_plex_server = MockPlexServer(config_entry=entry)
|
||||
|
||||
with patch("plexapi.server.PlexServer", return_value=mock_plex_server), patch(
|
||||
"homeassistant.components.plex.PlexWebsocket.listen"
|
||||
):
|
||||
entry.add_to_hass(hass)
|
||||
assert await hass.config_entries.async_setup(entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
server_id = mock_plex_server.machineIdentifier
|
||||
|
||||
with patch.object(mock_plex_server, "clients", return_value=[]), patch.object(
|
||||
mock_plex_server, "sessions", return_value=[]
|
||||
) as mock_update:
|
||||
# Called immediately
|
||||
async_dispatcher_send(hass, PLEX_UPDATE_PLATFORMS_SIGNAL.format(server_id))
|
||||
await hass.async_block_till_done()
|
||||
assert mock_update.call_count == 1
|
||||
|
||||
# Throttled
|
||||
async_dispatcher_send(hass, PLEX_UPDATE_PLATFORMS_SIGNAL.format(server_id))
|
||||
await hass.async_block_till_done()
|
||||
assert mock_update.call_count == 1
|
||||
|
||||
# Throttled
|
||||
async_dispatcher_send(hass, PLEX_UPDATE_PLATFORMS_SIGNAL.format(server_id))
|
||||
await hass.async_block_till_done()
|
||||
assert mock_update.call_count == 1
|
||||
|
||||
# Called from scheduler
|
||||
await self.advance(DEBOUNCE_TIMEOUT)
|
||||
await hass.async_block_till_done()
|
||||
assert mock_update.call_count == 2
|
||||
|
||||
# Throttled
|
||||
async_dispatcher_send(hass, PLEX_UPDATE_PLATFORMS_SIGNAL.format(server_id))
|
||||
await hass.async_block_till_done()
|
||||
assert mock_update.call_count == 2
|
||||
|
||||
# Called from scheduler
|
||||
await self.advance(DEBOUNCE_TIMEOUT)
|
||||
await hass.async_block_till_done()
|
||||
assert mock_update.call_count == 3
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
"""Tests for light platform."""
|
||||
from typing import Callable, NamedTuple
|
||||
from unittest.mock import Mock, patch
|
||||
from unittest.mock import Mock, PropertyMock, patch
|
||||
|
||||
from pyHS100 import SmartDeviceException
|
||||
import pytest
|
||||
|
@ -16,7 +16,11 @@ from homeassistant.components.light import (
|
|||
ATTR_HS_COLOR,
|
||||
DOMAIN as LIGHT_DOMAIN,
|
||||
)
|
||||
from homeassistant.components.tplink.common import CONF_DISCOVERY, CONF_LIGHT
|
||||
from homeassistant.components.tplink.common import (
|
||||
CONF_DIMMER,
|
||||
CONF_DISCOVERY,
|
||||
CONF_LIGHT,
|
||||
)
|
||||
from homeassistant.const import (
|
||||
ATTR_ENTITY_ID,
|
||||
CONF_HOST,
|
||||
|
@ -41,6 +45,15 @@ class LightMockData(NamedTuple):
|
|||
get_emeter_monthly_mock: Mock
|
||||
|
||||
|
||||
class SmartSwitchMockData(NamedTuple):
|
||||
"""Mock smart switch data."""
|
||||
|
||||
sys_info: dict
|
||||
state_mock: Mock
|
||||
brightness_mock: Mock
|
||||
get_sysinfo_mock: Mock
|
||||
|
||||
|
||||
@pytest.fixture(name="light_mock_data")
|
||||
def light_mock_data_fixture() -> None:
|
||||
"""Create light mock data."""
|
||||
|
@ -152,6 +165,74 @@ def light_mock_data_fixture() -> None:
|
|||
)
|
||||
|
||||
|
||||
@pytest.fixture(name="dimmer_switch_mock_data")
|
||||
def dimmer_switch_mock_data_fixture() -> None:
|
||||
"""Create dimmer switch mock data."""
|
||||
sys_info = {
|
||||
"sw_ver": "1.2.3",
|
||||
"hw_ver": "2.3.4",
|
||||
"mac": "aa:bb:cc:dd:ee:ff",
|
||||
"mic_mac": "00:11:22:33:44",
|
||||
"type": "switch",
|
||||
"hwId": "1234",
|
||||
"fwId": "4567",
|
||||
"oemId": "891011",
|
||||
"dev_name": "dimmer1",
|
||||
"rssi": 11,
|
||||
"latitude": "0",
|
||||
"longitude": "0",
|
||||
"is_color": False,
|
||||
"is_dimmable": True,
|
||||
"is_variable_color_temp": False,
|
||||
"model": "HS220",
|
||||
"alias": "dimmer1",
|
||||
"feature": ":",
|
||||
"relay_state": 1,
|
||||
"brightness": 13,
|
||||
}
|
||||
|
||||
def state(*args, **kwargs):
|
||||
nonlocal sys_info
|
||||
if len(args) == 0:
|
||||
return sys_info["relay_state"]
|
||||
if args[0] == "ON":
|
||||
sys_info["relay_state"] = 1
|
||||
else:
|
||||
sys_info["relay_state"] = 0
|
||||
|
||||
def brightness(*args, **kwargs):
|
||||
nonlocal sys_info
|
||||
if len(args) == 0:
|
||||
return sys_info["brightness"]
|
||||
if sys_info["brightness"] == 0:
|
||||
sys_info["relay_state"] = 0
|
||||
else:
|
||||
sys_info["relay_state"] = 1
|
||||
sys_info["brightness"] = args[0]
|
||||
|
||||
get_sysinfo_patch = patch(
|
||||
"homeassistant.components.tplink.common.SmartDevice.get_sysinfo",
|
||||
return_value=sys_info,
|
||||
)
|
||||
state_patch = patch(
|
||||
"homeassistant.components.tplink.common.SmartPlug.state",
|
||||
new_callable=PropertyMock,
|
||||
side_effect=state,
|
||||
)
|
||||
brightness_patch = patch(
|
||||
"homeassistant.components.tplink.common.SmartPlug.brightness",
|
||||
new_callable=PropertyMock,
|
||||
side_effect=brightness,
|
||||
)
|
||||
with brightness_patch as brightness_mock, state_patch as state_mock, get_sysinfo_patch as get_sysinfo_mock:
|
||||
yield SmartSwitchMockData(
|
||||
sys_info=sys_info,
|
||||
brightness_mock=brightness_mock,
|
||||
state_mock=state_mock,
|
||||
get_sysinfo_mock=get_sysinfo_mock,
|
||||
)
|
||||
|
||||
|
||||
async def update_entity(hass: HomeAssistant, entity_id: str) -> None:
|
||||
"""Run an update action for an entity."""
|
||||
await hass.services.async_call(
|
||||
|
@ -160,6 +241,96 @@ async def update_entity(hass: HomeAssistant, entity_id: str) -> None:
|
|||
await hass.async_block_till_done()
|
||||
|
||||
|
||||
async def test_smartswitch(
|
||||
hass: HomeAssistant, dimmer_switch_mock_data: SmartSwitchMockData
|
||||
) -> None:
|
||||
"""Test function."""
|
||||
sys_info = dimmer_switch_mock_data.sys_info
|
||||
|
||||
await async_setup_component(hass, HA_DOMAIN, {})
|
||||
await hass.async_block_till_done()
|
||||
|
||||
await async_setup_component(
|
||||
hass,
|
||||
tplink.DOMAIN,
|
||||
{
|
||||
tplink.DOMAIN: {
|
||||
CONF_DISCOVERY: False,
|
||||
CONF_DIMMER: [{CONF_HOST: "123.123.123.123"}],
|
||||
}
|
||||
},
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert hass.states.get("light.dimmer1")
|
||||
|
||||
await hass.services.async_call(
|
||||
LIGHT_DOMAIN,
|
||||
SERVICE_TURN_OFF,
|
||||
{ATTR_ENTITY_ID: "light.dimmer1"},
|
||||
blocking=True,
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
await update_entity(hass, "light.dimmer1")
|
||||
|
||||
assert hass.states.get("light.dimmer1").state == "off"
|
||||
assert sys_info["relay_state"] == 0
|
||||
|
||||
await hass.services.async_call(
|
||||
LIGHT_DOMAIN,
|
||||
SERVICE_TURN_ON,
|
||||
{ATTR_ENTITY_ID: "light.dimmer1", ATTR_BRIGHTNESS: 50},
|
||||
blocking=True,
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
await update_entity(hass, "light.dimmer1")
|
||||
|
||||
state = hass.states.get("light.dimmer1")
|
||||
assert state.state == "on"
|
||||
assert state.attributes["brightness"] == 48.45
|
||||
assert sys_info["relay_state"] == 1
|
||||
|
||||
await hass.services.async_call(
|
||||
LIGHT_DOMAIN,
|
||||
SERVICE_TURN_ON,
|
||||
{ATTR_ENTITY_ID: "light.dimmer1", ATTR_BRIGHTNESS: 55},
|
||||
blocking=True,
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
await update_entity(hass, "light.dimmer1")
|
||||
|
||||
state = hass.states.get("light.dimmer1")
|
||||
assert state.state == "on"
|
||||
assert state.attributes["brightness"] == 53.55
|
||||
assert sys_info["brightness"] == 21
|
||||
|
||||
sys_info["relay_state"] = 0
|
||||
sys_info["brightness"] = 66
|
||||
|
||||
await hass.services.async_call(
|
||||
LIGHT_DOMAIN,
|
||||
SERVICE_TURN_OFF,
|
||||
{ATTR_ENTITY_ID: "light.dimmer1"},
|
||||
blocking=True,
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
await update_entity(hass, "light.dimmer1")
|
||||
|
||||
state = hass.states.get("light.dimmer1")
|
||||
assert state.state == "off"
|
||||
|
||||
await hass.services.async_call(
|
||||
LIGHT_DOMAIN, SERVICE_TURN_ON, {ATTR_ENTITY_ID: "light.dimmer1"}, blocking=True,
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
await update_entity(hass, "light.dimmer1")
|
||||
|
||||
state = hass.states.get("light.dimmer1")
|
||||
assert state.state == "on"
|
||||
assert state.attributes["brightness"] == 168.3
|
||||
assert sys_info["brightness"] == 66
|
||||
|
||||
|
||||
async def test_light(hass: HomeAssistant, light_mock_data: LightMockData) -> None:
|
||||
"""Test function."""
|
||||
light_state = light_mock_data.light_state
|
||||
|
|
Loading…
Add table
Reference in a new issue