Merge pull request #53305 from home-assistant/rc
This commit is contained in:
commit
ccfe32257e
28 changed files with 176 additions and 161 deletions
|
@ -4,7 +4,7 @@
|
||||||
"config_flow": true,
|
"config_flow": true,
|
||||||
"documentation": "https://www.home-assistant.io/integrations/deconz",
|
"documentation": "https://www.home-assistant.io/integrations/deconz",
|
||||||
"requirements": [
|
"requirements": [
|
||||||
"pydeconz==81"
|
"pydeconz==82"
|
||||||
],
|
],
|
||||||
"ssdp": [
|
"ssdp": [
|
||||||
{
|
{
|
||||||
|
|
|
@ -11,7 +11,6 @@ from homeassistant.const import (
|
||||||
CONF_ENTITY_ID,
|
CONF_ENTITY_ID,
|
||||||
CONF_TYPE,
|
CONF_TYPE,
|
||||||
STATE_HOME,
|
STATE_HOME,
|
||||||
STATE_NOT_HOME,
|
|
||||||
)
|
)
|
||||||
from homeassistant.core import HomeAssistant, callback
|
from homeassistant.core import HomeAssistant, callback
|
||||||
from homeassistant.helpers import condition, config_validation as cv, entity_registry
|
from homeassistant.helpers import condition, config_validation as cv, entity_registry
|
||||||
|
@ -62,14 +61,15 @@ def async_condition_from_config(
|
||||||
"""Create a function to test a device condition."""
|
"""Create a function to test a device condition."""
|
||||||
if config_validation:
|
if config_validation:
|
||||||
config = CONDITION_SCHEMA(config)
|
config = CONDITION_SCHEMA(config)
|
||||||
if config[CONF_TYPE] == "is_home":
|
|
||||||
state = STATE_HOME
|
reverse = config[CONF_TYPE] == "is_not_home"
|
||||||
else:
|
|
||||||
state = STATE_NOT_HOME
|
|
||||||
|
|
||||||
@callback
|
@callback
|
||||||
def test_is_state(hass: HomeAssistant, variables: TemplateVarsType) -> bool:
|
def test_is_state(hass: HomeAssistant, variables: TemplateVarsType) -> bool:
|
||||||
"""Test if an entity is a certain state."""
|
"""Test if an entity is a certain state."""
|
||||||
return condition.state(hass, config[ATTR_ENTITY_ID], state)
|
result = condition.state(hass, config[ATTR_ENTITY_ID], STATE_HOME)
|
||||||
|
if reverse:
|
||||||
|
result = not result
|
||||||
|
return result
|
||||||
|
|
||||||
return test_is_state
|
return test_is_state
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
"domain": "dlna_dmr",
|
"domain": "dlna_dmr",
|
||||||
"name": "DLNA Digital Media Renderer",
|
"name": "DLNA Digital Media Renderer",
|
||||||
"documentation": "https://www.home-assistant.io/integrations/dlna_dmr",
|
"documentation": "https://www.home-assistant.io/integrations/dlna_dmr",
|
||||||
"requirements": ["async-upnp-client==0.19.0"],
|
"requirements": ["async-upnp-client==0.19.1"],
|
||||||
"codeowners": [],
|
"codeowners": [],
|
||||||
"iot_class": "local_push"
|
"iot_class": "local_push"
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,6 +7,7 @@ import voluptuous as vol
|
||||||
|
|
||||||
from homeassistant import config_entries
|
from homeassistant import config_entries
|
||||||
from homeassistant.components.camera import DOMAIN as CAMERA_DOMAIN
|
from homeassistant.components.camera import DOMAIN as CAMERA_DOMAIN
|
||||||
|
from homeassistant.components.lock import DOMAIN as LOCK_DOMAIN
|
||||||
from homeassistant.components.media_player import DOMAIN as MEDIA_PLAYER_DOMAIN
|
from homeassistant.components.media_player import DOMAIN as MEDIA_PLAYER_DOMAIN
|
||||||
from homeassistant.components.remote import DOMAIN as REMOTE_DOMAIN
|
from homeassistant.components.remote import DOMAIN as REMOTE_DOMAIN
|
||||||
from homeassistant.config_entries import SOURCE_IMPORT
|
from homeassistant.config_entries import SOURCE_IMPORT
|
||||||
|
@ -54,7 +55,12 @@ MODE_EXCLUDE = "exclude"
|
||||||
|
|
||||||
INCLUDE_EXCLUDE_MODES = [MODE_EXCLUDE, MODE_INCLUDE]
|
INCLUDE_EXCLUDE_MODES = [MODE_EXCLUDE, MODE_INCLUDE]
|
||||||
|
|
||||||
DOMAINS_NEED_ACCESSORY_MODE = [CAMERA_DOMAIN, MEDIA_PLAYER_DOMAIN, REMOTE_DOMAIN]
|
DOMAINS_NEED_ACCESSORY_MODE = [
|
||||||
|
CAMERA_DOMAIN,
|
||||||
|
LOCK_DOMAIN,
|
||||||
|
MEDIA_PLAYER_DOMAIN,
|
||||||
|
REMOTE_DOMAIN,
|
||||||
|
]
|
||||||
NEVER_BRIDGED_DOMAINS = [CAMERA_DOMAIN]
|
NEVER_BRIDGED_DOMAINS = [CAMERA_DOMAIN]
|
||||||
|
|
||||||
CAMERA_ENTITY_PREFIX = f"{CAMERA_DOMAIN}."
|
CAMERA_ENTITY_PREFIX = f"{CAMERA_DOMAIN}."
|
||||||
|
|
|
@ -43,7 +43,7 @@
|
||||||
"data": {
|
"data": {
|
||||||
"include_domains": "Domains to include"
|
"include_domains": "Domains to include"
|
||||||
},
|
},
|
||||||
"description": "Choose the domains to be included. All supported entities in the domain will be included. A separate HomeKit instance in accessory mode will be created for each tv media player and camera.",
|
"description": "Choose the domains to be included. All supported entities in the domain will be included. A separate HomeKit instance in accessory mode will be created for each tv media player, activity based remote, lock, and camera.",
|
||||||
"title": "Select domains to be included"
|
"title": "Select domains to be included"
|
||||||
},
|
},
|
||||||
"pairing": {
|
"pairing": {
|
||||||
|
|
|
@ -12,7 +12,7 @@
|
||||||
"data": {
|
"data": {
|
||||||
"include_domains": "Domains to include"
|
"include_domains": "Domains to include"
|
||||||
},
|
},
|
||||||
"description": "Choose the domains to be included. All supported entities in the domain will be included. A separate HomeKit instance in accessory mode will be created for each tv media player and camera.",
|
"description": "Choose the domains to be included. All supported entities in the domain will be included. A separate HomeKit instance in accessory mode will be created for each tv media player, activity based remote, lock, and camera.",
|
||||||
"title": "Select domains to be included"
|
"title": "Select domains to be included"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -499,12 +499,11 @@ def accessory_friendly_name(hass_name, accessory):
|
||||||
|
|
||||||
def state_needs_accessory_mode(state):
|
def state_needs_accessory_mode(state):
|
||||||
"""Return if the entity represented by the state must be paired in accessory mode."""
|
"""Return if the entity represented by the state must be paired in accessory mode."""
|
||||||
if state.domain == CAMERA_DOMAIN:
|
if state.domain in (CAMERA_DOMAIN, LOCK_DOMAIN):
|
||||||
return True
|
return True
|
||||||
|
|
||||||
return (
|
return (
|
||||||
state.domain == LOCK_DOMAIN
|
state.domain == MEDIA_PLAYER_DOMAIN
|
||||||
or state.domain == MEDIA_PLAYER_DOMAIN
|
|
||||||
and state.attributes.get(ATTR_DEVICE_CLASS) == DEVICE_CLASS_TV
|
and state.attributes.get(ATTR_DEVICE_CLASS) == DEVICE_CLASS_TV
|
||||||
or state.domain == REMOTE_DOMAIN
|
or state.domain == REMOTE_DOMAIN
|
||||||
and state.attributes.get(ATTR_SUPPORTED_FEATURES, 0) & SUPPORT_ACTIVITY
|
and state.attributes.get(ATTR_SUPPORTED_FEATURES, 0) & SUPPORT_ACTIVITY
|
||||||
|
|
|
@ -249,17 +249,22 @@ class ModbusHub:
|
||||||
for entry in self._pb_call.values():
|
for entry in self._pb_call.values():
|
||||||
entry[ENTRY_FUNC] = getattr(self._client, entry[ENTRY_NAME])
|
entry[ENTRY_FUNC] = getattr(self._client, entry[ENTRY_NAME])
|
||||||
|
|
||||||
|
await self.async_connect_task()
|
||||||
|
return True
|
||||||
|
|
||||||
|
async def async_connect_task(self):
|
||||||
|
"""Try to connect, and retry if needed."""
|
||||||
async with self._lock:
|
async with self._lock:
|
||||||
if not await self.hass.async_add_executor_job(self._pymodbus_connect):
|
if not await self.hass.async_add_executor_job(self._pymodbus_connect):
|
||||||
self._log_error("initial connect failed, no retry", error_state=False)
|
err = f"{self._config_name} connect failed, retry in pymodbus"
|
||||||
return False
|
self._log_error(err, error_state=False)
|
||||||
|
return
|
||||||
|
|
||||||
# Start counting down to allow modbus requests.
|
# Start counting down to allow modbus requests.
|
||||||
if self._config_delay:
|
if self._config_delay:
|
||||||
self._async_cancel_listener = async_call_later(
|
self._async_cancel_listener = async_call_later(
|
||||||
self.hass, self._config_delay, self.async_end_delay
|
self.hass, self._config_delay, self.async_end_delay
|
||||||
)
|
)
|
||||||
return True
|
|
||||||
|
|
||||||
@callback
|
@callback
|
||||||
def async_end_delay(self, args):
|
def async_end_delay(self, args):
|
||||||
|
@ -313,8 +318,6 @@ class ModbusHub:
|
||||||
return None
|
return None
|
||||||
if not self._client:
|
if not self._client:
|
||||||
return None
|
return None
|
||||||
if not self._client.is_socket_open():
|
|
||||||
return None
|
|
||||||
async with self._lock:
|
async with self._lock:
|
||||||
result = await self.hass.async_add_executor_job(
|
result = await self.hass.async_add_executor_job(
|
||||||
self._pymodbus_call, unit, address, value, use_call
|
self._pymodbus_call, unit, address, value, use_call
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"domain": "nexia",
|
"domain": "nexia",
|
||||||
"name": "Nexia/American Standard/Trane",
|
"name": "Nexia/American Standard/Trane",
|
||||||
"requirements": ["nexia==0.9.9"],
|
"requirements": ["nexia==0.9.10"],
|
||||||
"codeowners": ["@bdraco"],
|
"codeowners": ["@bdraco"],
|
||||||
"documentation": "https://www.home-assistant.io/integrations/nexia",
|
"documentation": "https://www.home-assistant.io/integrations/nexia",
|
||||||
"config_flow": true,
|
"config_flow": true,
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
"""Support for controlling GPIO pins of a Raspberry Pi."""
|
"""Support for controlling GPIO pins of a Raspberry Pi."""
|
||||||
from gpiozero import LED, Button
|
from gpiozero import LED, DigitalInputDevice
|
||||||
from gpiozero.pins.pigpio import PiGPIOFactory
|
from gpiozero.pins.pigpio import PiGPIOFactory
|
||||||
|
|
||||||
CONF_BOUNCETIME = "bouncetime"
|
CONF_BOUNCETIME = "bouncetime"
|
||||||
|
@ -38,7 +38,7 @@ def setup_input(address, port, pull_mode, bouncetime):
|
||||||
pull_gpio_up = False
|
pull_gpio_up = False
|
||||||
|
|
||||||
try:
|
try:
|
||||||
return Button(
|
return DigitalInputDevice(
|
||||||
port,
|
port,
|
||||||
pull_up=pull_gpio_up,
|
pull_up=pull_gpio_up,
|
||||||
bounce_time=bouncetime,
|
bounce_time=bouncetime,
|
||||||
|
@ -56,6 +56,6 @@ def write_output(switch, value):
|
||||||
switch.off()
|
switch.off()
|
||||||
|
|
||||||
|
|
||||||
def read_input(button):
|
def read_input(sensor):
|
||||||
"""Read a value from a GPIO."""
|
"""Read a value from a GPIO."""
|
||||||
return button.is_pressed
|
return sensor.value
|
||||||
|
|
|
@ -42,12 +42,12 @@ def setup_platform(hass, config, add_entities, discovery_info=None):
|
||||||
devices = []
|
devices = []
|
||||||
for port_num, port_name in ports.items():
|
for port_num, port_name in ports.items():
|
||||||
try:
|
try:
|
||||||
button = remote_rpi_gpio.setup_input(
|
remote_sensor = remote_rpi_gpio.setup_input(
|
||||||
address, port_num, pull_mode, bouncetime
|
address, port_num, pull_mode, bouncetime
|
||||||
)
|
)
|
||||||
except (ValueError, IndexError, KeyError, OSError):
|
except (ValueError, IndexError, KeyError, OSError):
|
||||||
return
|
return
|
||||||
new_sensor = RemoteRPiGPIOBinarySensor(port_name, button, invert_logic)
|
new_sensor = RemoteRPiGPIOBinarySensor(port_name, remote_sensor, invert_logic)
|
||||||
devices.append(new_sensor)
|
devices.append(new_sensor)
|
||||||
|
|
||||||
add_entities(devices, True)
|
add_entities(devices, True)
|
||||||
|
@ -56,23 +56,23 @@ def setup_platform(hass, config, add_entities, discovery_info=None):
|
||||||
class RemoteRPiGPIOBinarySensor(BinarySensorEntity):
|
class RemoteRPiGPIOBinarySensor(BinarySensorEntity):
|
||||||
"""Represent a binary sensor that uses a Remote Raspberry Pi GPIO."""
|
"""Represent a binary sensor that uses a Remote Raspberry Pi GPIO."""
|
||||||
|
|
||||||
def __init__(self, name, button, invert_logic):
|
def __init__(self, name, sensor, invert_logic):
|
||||||
"""Initialize the RPi binary sensor."""
|
"""Initialize the RPi binary sensor."""
|
||||||
self._name = name
|
self._name = name
|
||||||
self._invert_logic = invert_logic
|
self._invert_logic = invert_logic
|
||||||
self._state = False
|
self._state = False
|
||||||
self._button = button
|
self._sensor = sensor
|
||||||
|
|
||||||
async def async_added_to_hass(self):
|
async def async_added_to_hass(self):
|
||||||
"""Run when entity about to be added to hass."""
|
"""Run when entity about to be added to hass."""
|
||||||
|
|
||||||
def read_gpio():
|
def read_gpio():
|
||||||
"""Read state from GPIO."""
|
"""Read state from GPIO."""
|
||||||
self._state = remote_rpi_gpio.read_input(self._button)
|
self._state = remote_rpi_gpio.read_input(self._sensor)
|
||||||
self.schedule_update_ha_state()
|
self.schedule_update_ha_state()
|
||||||
|
|
||||||
self._button.when_released = read_gpio
|
self._sensor.when_deactivated = read_gpio
|
||||||
self._button.when_pressed = read_gpio
|
self._sensor.when_activated = read_gpio
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def should_poll(self):
|
def should_poll(self):
|
||||||
|
@ -97,6 +97,6 @@ class RemoteRPiGPIOBinarySensor(BinarySensorEntity):
|
||||||
def update(self):
|
def update(self):
|
||||||
"""Update the GPIO state."""
|
"""Update the GPIO state."""
|
||||||
try:
|
try:
|
||||||
self._state = remote_rpi_gpio.read_input(self._button)
|
self._state = remote_rpi_gpio.read_input(self._sensor)
|
||||||
except requests.exceptions.ConnectionError:
|
except requests.exceptions.ConnectionError:
|
||||||
return
|
return
|
||||||
|
|
|
@ -3,7 +3,11 @@ import asyncio
|
||||||
from uuid import UUID
|
from uuid import UUID
|
||||||
|
|
||||||
from simplipy import get_api
|
from simplipy import get_api
|
||||||
from simplipy.errors import EndpointUnavailable, InvalidCredentialsError, SimplipyError
|
from simplipy.errors import (
|
||||||
|
EndpointUnavailableError,
|
||||||
|
InvalidCredentialsError,
|
||||||
|
SimplipyError,
|
||||||
|
)
|
||||||
import voluptuous as vol
|
import voluptuous as vol
|
||||||
|
|
||||||
from homeassistant.const import ATTR_CODE, CONF_CODE, CONF_PASSWORD, CONF_USERNAME
|
from homeassistant.const import ATTR_CODE, CONF_CODE, CONF_PASSWORD, CONF_USERNAME
|
||||||
|
@ -379,7 +383,7 @@ class SimpliSafe:
|
||||||
if isinstance(result, InvalidCredentialsError):
|
if isinstance(result, InvalidCredentialsError):
|
||||||
raise ConfigEntryAuthFailed("Invalid credentials") from result
|
raise ConfigEntryAuthFailed("Invalid credentials") from result
|
||||||
|
|
||||||
if isinstance(result, EndpointUnavailable):
|
if isinstance(result, EndpointUnavailableError):
|
||||||
# In case the user attempts an action not allowed in their current plan,
|
# In case the user attempts an action not allowed in their current plan,
|
||||||
# we merely log that message at INFO level (so the user is aware,
|
# we merely log that message at INFO level (so the user is aware,
|
||||||
# but not spammed with ERROR messages that they cannot change):
|
# but not spammed with ERROR messages that they cannot change):
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
"name": "SimpliSafe",
|
"name": "SimpliSafe",
|
||||||
"config_flow": true,
|
"config_flow": true,
|
||||||
"documentation": "https://www.home-assistant.io/integrations/simplisafe",
|
"documentation": "https://www.home-assistant.io/integrations/simplisafe",
|
||||||
"requirements": ["simplisafe-python==11.0.1"],
|
"requirements": ["simplisafe-python==11.0.2"],
|
||||||
"codeowners": ["@bachya"],
|
"codeowners": ["@bachya"],
|
||||||
"iot_class": "cloud_polling"
|
"iot_class": "cloud_polling"
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
"name": "Sonos",
|
"name": "Sonos",
|
||||||
"config_flow": true,
|
"config_flow": true,
|
||||||
"documentation": "https://www.home-assistant.io/integrations/sonos",
|
"documentation": "https://www.home-assistant.io/integrations/sonos",
|
||||||
"requirements": ["pysonos==0.0.52"],
|
"requirements": ["pysonos==0.0.53"],
|
||||||
"dependencies": ["ssdp"],
|
"dependencies": ["ssdp"],
|
||||||
"after_dependencies": ["plex", "zeroconf"],
|
"after_dependencies": ["plex", "zeroconf"],
|
||||||
"zeroconf": ["_sonos._tcp.local."],
|
"zeroconf": ["_sonos._tcp.local."],
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
"documentation": "https://www.home-assistant.io/integrations/ssdp",
|
"documentation": "https://www.home-assistant.io/integrations/ssdp",
|
||||||
"requirements": [
|
"requirements": [
|
||||||
"defusedxml==0.7.1",
|
"defusedxml==0.7.1",
|
||||||
"async-upnp-client==0.19.0"
|
"async-upnp-client==0.19.1"
|
||||||
],
|
],
|
||||||
"dependencies": ["network"],
|
"dependencies": ["network"],
|
||||||
"after_dependencies": ["zeroconf"],
|
"after_dependencies": ["zeroconf"],
|
||||||
|
|
|
@ -3,6 +3,6 @@
|
||||||
"name": "Sure Petcare",
|
"name": "Sure Petcare",
|
||||||
"documentation": "https://www.home-assistant.io/integrations/surepetcare",
|
"documentation": "https://www.home-assistant.io/integrations/surepetcare",
|
||||||
"codeowners": ["@benleb", "@danielhiversen"],
|
"codeowners": ["@benleb", "@danielhiversen"],
|
||||||
"requirements": ["surepy==0.6.0"],
|
"requirements": ["surepy==0.7.0"],
|
||||||
"iot_class": "cloud_polling"
|
"iot_class": "cloud_polling"
|
||||||
}
|
}
|
||||||
|
|
|
@ -133,6 +133,34 @@ class UniFiUpTimeSensor(UniFiClient, SensorEntity):
|
||||||
|
|
||||||
_attr_device_class = DEVICE_CLASS_TIMESTAMP
|
_attr_device_class = DEVICE_CLASS_TIMESTAMP
|
||||||
|
|
||||||
|
def __init__(self, client, controller):
|
||||||
|
"""Set up tracked client."""
|
||||||
|
super().__init__(client, controller)
|
||||||
|
|
||||||
|
self.last_updated_time = self.client.uptime
|
||||||
|
|
||||||
|
@callback
|
||||||
|
def async_update_callback(self) -> None:
|
||||||
|
"""Update sensor when time has changed significantly.
|
||||||
|
|
||||||
|
This will help avoid unnecessary updates to the state machine.
|
||||||
|
"""
|
||||||
|
update_state = True
|
||||||
|
|
||||||
|
if self.client.uptime < 1000000000:
|
||||||
|
if self.client.uptime > self.last_updated_time:
|
||||||
|
update_state = False
|
||||||
|
else:
|
||||||
|
if self.client.uptime <= self.last_updated_time:
|
||||||
|
update_state = False
|
||||||
|
|
||||||
|
self.last_updated_time = self.client.uptime
|
||||||
|
|
||||||
|
if not update_state:
|
||||||
|
return None
|
||||||
|
|
||||||
|
super().async_update_callback()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def name(self) -> str:
|
def name(self) -> str:
|
||||||
"""Return the name of the client."""
|
"""Return the name of the client."""
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
"name": "UPnP/IGD",
|
"name": "UPnP/IGD",
|
||||||
"config_flow": true,
|
"config_flow": true,
|
||||||
"documentation": "https://www.home-assistant.io/integrations/upnp",
|
"documentation": "https://www.home-assistant.io/integrations/upnp",
|
||||||
"requirements": ["async-upnp-client==0.19.0"],
|
"requirements": ["async-upnp-client==0.19.1"],
|
||||||
"dependencies": ["ssdp"],
|
"dependencies": ["ssdp"],
|
||||||
"codeowners": ["@StevenLooman"],
|
"codeowners": ["@StevenLooman"],
|
||||||
"ssdp": [
|
"ssdp": [
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
"""Home Assistant wrapper for a pyWeMo device."""
|
"""Home Assistant wrapper for a pyWeMo device."""
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from pywemo import PyWeMoException, WeMoDevice
|
from pywemo import WeMoDevice
|
||||||
from pywemo.subscribe import EVENT_TYPE_LONG_PRESS
|
from pywemo.subscribe import EVENT_TYPE_LONG_PRESS
|
||||||
|
|
||||||
from homeassistant.config_entries import ConfigEntry
|
from homeassistant.config_entries import ConfigEntry
|
||||||
|
@ -81,8 +81,10 @@ async def async_register_device(
|
||||||
if device.supports_long_press:
|
if device.supports_long_press:
|
||||||
try:
|
try:
|
||||||
await hass.async_add_executor_job(wemo.ensure_long_press_virtual_device)
|
await hass.async_add_executor_job(wemo.ensure_long_press_virtual_device)
|
||||||
except PyWeMoException:
|
# Temporarily handling all exceptions for #52996 & pywemo/pywemo/issues/276
|
||||||
_LOGGER.warning(
|
# Replace this with `except: PyWeMoException` after upstream has been fixed.
|
||||||
|
except Exception: # pylint: disable=broad-except
|
||||||
|
_LOGGER.exception(
|
||||||
"Failed to enable long press support for device: %s", wemo.name
|
"Failed to enable long press support for device: %s", wemo.name
|
||||||
)
|
)
|
||||||
device.supports_long_press = False
|
device.supports_long_press = False
|
||||||
|
|
|
@ -5,7 +5,7 @@ from typing import Final
|
||||||
|
|
||||||
MAJOR_VERSION: Final = 2021
|
MAJOR_VERSION: Final = 2021
|
||||||
MINOR_VERSION: Final = 7
|
MINOR_VERSION: Final = 7
|
||||||
PATCH_VERSION: Final = "3"
|
PATCH_VERSION: Final = "4"
|
||||||
__short_version__: Final = f"{MAJOR_VERSION}.{MINOR_VERSION}"
|
__short_version__: Final = f"{MAJOR_VERSION}.{MINOR_VERSION}"
|
||||||
__version__: Final = f"{__short_version__}.{PATCH_VERSION}"
|
__version__: Final = f"{__short_version__}.{PATCH_VERSION}"
|
||||||
REQUIRED_PYTHON_VER: Final[tuple[int, int, int]] = (3, 8, 0)
|
REQUIRED_PYTHON_VER: Final[tuple[int, int, int]] = (3, 8, 0)
|
||||||
|
|
|
@ -4,7 +4,7 @@ aiodiscover==1.4.2
|
||||||
aiohttp==3.7.4.post0
|
aiohttp==3.7.4.post0
|
||||||
aiohttp_cors==0.7.0
|
aiohttp_cors==0.7.0
|
||||||
astral==2.2
|
astral==2.2
|
||||||
async-upnp-client==0.19.0
|
async-upnp-client==0.19.1
|
||||||
async_timeout==3.0.1
|
async_timeout==3.0.1
|
||||||
attrs==21.2.0
|
attrs==21.2.0
|
||||||
awesomeversion==21.4.0
|
awesomeversion==21.4.0
|
||||||
|
|
|
@ -304,7 +304,7 @@ asterisk_mbox==0.5.0
|
||||||
# homeassistant.components.dlna_dmr
|
# homeassistant.components.dlna_dmr
|
||||||
# homeassistant.components.ssdp
|
# homeassistant.components.ssdp
|
||||||
# homeassistant.components.upnp
|
# homeassistant.components.upnp
|
||||||
async-upnp-client==0.19.0
|
async-upnp-client==0.19.1
|
||||||
|
|
||||||
# homeassistant.components.supla
|
# homeassistant.components.supla
|
||||||
asyncpysupla==0.0.5
|
asyncpysupla==0.0.5
|
||||||
|
@ -1020,7 +1020,7 @@ nettigo-air-monitor==1.0.0
|
||||||
neurio==0.3.1
|
neurio==0.3.1
|
||||||
|
|
||||||
# homeassistant.components.nexia
|
# homeassistant.components.nexia
|
||||||
nexia==0.9.9
|
nexia==0.9.10
|
||||||
|
|
||||||
# homeassistant.components.nextcloud
|
# homeassistant.components.nextcloud
|
||||||
nextcloudmonitor==1.1.0
|
nextcloudmonitor==1.1.0
|
||||||
|
@ -1375,7 +1375,7 @@ pydaikin==2.4.4
|
||||||
pydanfossair==0.1.0
|
pydanfossair==0.1.0
|
||||||
|
|
||||||
# homeassistant.components.deconz
|
# homeassistant.components.deconz
|
||||||
pydeconz==81
|
pydeconz==82
|
||||||
|
|
||||||
# homeassistant.components.delijn
|
# homeassistant.components.delijn
|
||||||
pydelijn==0.6.1
|
pydelijn==0.6.1
|
||||||
|
@ -1773,7 +1773,7 @@ pysnmp==4.4.12
|
||||||
pysoma==0.0.10
|
pysoma==0.0.10
|
||||||
|
|
||||||
# homeassistant.components.sonos
|
# homeassistant.components.sonos
|
||||||
pysonos==0.0.52
|
pysonos==0.0.53
|
||||||
|
|
||||||
# homeassistant.components.spc
|
# homeassistant.components.spc
|
||||||
pyspcwebgw==0.4.0
|
pyspcwebgw==0.4.0
|
||||||
|
@ -2102,7 +2102,7 @@ simplehound==0.3
|
||||||
simplepush==1.1.4
|
simplepush==1.1.4
|
||||||
|
|
||||||
# homeassistant.components.simplisafe
|
# homeassistant.components.simplisafe
|
||||||
simplisafe-python==11.0.1
|
simplisafe-python==11.0.2
|
||||||
|
|
||||||
# homeassistant.components.sisyphus
|
# homeassistant.components.sisyphus
|
||||||
sisyphus-control==3.0
|
sisyphus-control==3.0
|
||||||
|
@ -2210,7 +2210,7 @@ sucks==0.9.4
|
||||||
sunwatcher==0.2.1
|
sunwatcher==0.2.1
|
||||||
|
|
||||||
# homeassistant.components.surepetcare
|
# homeassistant.components.surepetcare
|
||||||
surepy==0.6.0
|
surepy==0.7.0
|
||||||
|
|
||||||
# homeassistant.components.swiss_hydrological_data
|
# homeassistant.components.swiss_hydrological_data
|
||||||
swisshydrodata==0.1.0
|
swisshydrodata==0.1.0
|
||||||
|
|
|
@ -196,7 +196,7 @@ arcam-fmj==0.7.0
|
||||||
# homeassistant.components.dlna_dmr
|
# homeassistant.components.dlna_dmr
|
||||||
# homeassistant.components.ssdp
|
# homeassistant.components.ssdp
|
||||||
# homeassistant.components.upnp
|
# homeassistant.components.upnp
|
||||||
async-upnp-client==0.19.0
|
async-upnp-client==0.19.1
|
||||||
|
|
||||||
# homeassistant.components.aurora
|
# homeassistant.components.aurora
|
||||||
auroranoaa==0.0.2
|
auroranoaa==0.0.2
|
||||||
|
@ -574,7 +574,7 @@ netdisco==2.9.0
|
||||||
nettigo-air-monitor==1.0.0
|
nettigo-air-monitor==1.0.0
|
||||||
|
|
||||||
# homeassistant.components.nexia
|
# homeassistant.components.nexia
|
||||||
nexia==0.9.9
|
nexia==0.9.10
|
||||||
|
|
||||||
# homeassistant.components.notify_events
|
# homeassistant.components.notify_events
|
||||||
notify-events==1.0.4
|
notify-events==1.0.4
|
||||||
|
@ -770,7 +770,7 @@ pycoolmasternet-async==0.1.2
|
||||||
pydaikin==2.4.4
|
pydaikin==2.4.4
|
||||||
|
|
||||||
# homeassistant.components.deconz
|
# homeassistant.components.deconz
|
||||||
pydeconz==81
|
pydeconz==82
|
||||||
|
|
||||||
# homeassistant.components.dexcom
|
# homeassistant.components.dexcom
|
||||||
pydexcom==0.2.0
|
pydexcom==0.2.0
|
||||||
|
@ -1006,7 +1006,7 @@ pysmartthings==0.7.6
|
||||||
pysoma==0.0.10
|
pysoma==0.0.10
|
||||||
|
|
||||||
# homeassistant.components.sonos
|
# homeassistant.components.sonos
|
||||||
pysonos==0.0.52
|
pysonos==0.0.53
|
||||||
|
|
||||||
# homeassistant.components.spc
|
# homeassistant.components.spc
|
||||||
pyspcwebgw==0.4.0
|
pyspcwebgw==0.4.0
|
||||||
|
@ -1148,7 +1148,7 @@ sharkiqpy==0.1.8
|
||||||
simplehound==0.3
|
simplehound==0.3
|
||||||
|
|
||||||
# homeassistant.components.simplisafe
|
# homeassistant.components.simplisafe
|
||||||
simplisafe-python==11.0.1
|
simplisafe-python==11.0.2
|
||||||
|
|
||||||
# homeassistant.components.slack
|
# homeassistant.components.slack
|
||||||
slackclient==2.5.0
|
slackclient==2.5.0
|
||||||
|
@ -1215,7 +1215,7 @@ subarulink==0.3.12
|
||||||
sunwatcher==0.2.1
|
sunwatcher==0.2.1
|
||||||
|
|
||||||
# homeassistant.components.surepetcare
|
# homeassistant.components.surepetcare
|
||||||
surepy==0.6.0
|
surepy==0.7.0
|
||||||
|
|
||||||
# homeassistant.components.synology_dsm
|
# homeassistant.components.synology_dsm
|
||||||
synologydsm-api==1.0.2
|
synologydsm-api==1.0.2
|
||||||
|
|
|
@ -742,7 +742,18 @@ async def test_groups(hass, aioclient_mock, input, expected):
|
||||||
"name": "Group",
|
"name": "Group",
|
||||||
"type": "LightGroup",
|
"type": "LightGroup",
|
||||||
"state": {"all_on": False, "any_on": True},
|
"state": {"all_on": False, "any_on": True},
|
||||||
"action": {},
|
"action": {
|
||||||
|
"alert": "none",
|
||||||
|
"bri": 127,
|
||||||
|
"colormode": "hs",
|
||||||
|
"ct": 0,
|
||||||
|
"effect": "none",
|
||||||
|
"hue": 0,
|
||||||
|
"on": True,
|
||||||
|
"sat": 127,
|
||||||
|
"scene": None,
|
||||||
|
"xy": [0, 0],
|
||||||
|
},
|
||||||
"scenes": [],
|
"scenes": [],
|
||||||
"lights": input["lights"],
|
"lights": input["lights"],
|
||||||
},
|
},
|
||||||
|
|
|
@ -3,7 +3,7 @@ import pytest
|
||||||
|
|
||||||
import homeassistant.components.automation as automation
|
import homeassistant.components.automation as automation
|
||||||
from homeassistant.components.device_tracker import DOMAIN
|
from homeassistant.components.device_tracker import DOMAIN
|
||||||
from homeassistant.const import STATE_HOME, STATE_NOT_HOME
|
from homeassistant.const import STATE_HOME
|
||||||
from homeassistant.helpers import device_registry
|
from homeassistant.helpers import device_registry
|
||||||
from homeassistant.setup import async_setup_component
|
from homeassistant.setup import async_setup_component
|
||||||
|
|
||||||
|
@ -119,7 +119,7 @@ async def test_if_state(hass, calls):
|
||||||
assert len(calls) == 1
|
assert len(calls) == 1
|
||||||
assert calls[0].data["some"] == "is_home - event - test_event1"
|
assert calls[0].data["some"] == "is_home - event - test_event1"
|
||||||
|
|
||||||
hass.states.async_set("device_tracker.entity", STATE_NOT_HOME)
|
hass.states.async_set("device_tracker.entity", "school")
|
||||||
hass.bus.async_fire("test_event1")
|
hass.bus.async_fire("test_event1")
|
||||||
hass.bus.async_fire("test_event2")
|
hass.bus.async_fire("test_event2")
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
|
|
|
@ -144,6 +144,7 @@ async def test_setup_creates_entries_for_accessory_mode_devices(hass):
|
||||||
"""Test we can setup a new instance and we create entries for accessory mode devices."""
|
"""Test we can setup a new instance and we create entries for accessory mode devices."""
|
||||||
hass.states.async_set("camera.one", "on")
|
hass.states.async_set("camera.one", "on")
|
||||||
hass.states.async_set("camera.existing", "on")
|
hass.states.async_set("camera.existing", "on")
|
||||||
|
hass.states.async_set("lock.new", "on")
|
||||||
hass.states.async_set("media_player.two", "on", {"device_class": "tv"})
|
hass.states.async_set("media_player.two", "on", {"device_class": "tv"})
|
||||||
hass.states.async_set("remote.standard", "on")
|
hass.states.async_set("remote.standard", "on")
|
||||||
hass.states.async_set("remote.activity", "on", {"supported_features": 4})
|
hass.states.async_set("remote.activity", "on", {"supported_features": 4})
|
||||||
|
@ -180,7 +181,7 @@ async def test_setup_creates_entries_for_accessory_mode_devices(hass):
|
||||||
|
|
||||||
result2 = await hass.config_entries.flow.async_configure(
|
result2 = await hass.config_entries.flow.async_configure(
|
||||||
result["flow_id"],
|
result["flow_id"],
|
||||||
{"include_domains": ["camera", "media_player", "light", "remote"]},
|
{"include_domains": ["camera", "media_player", "light", "lock", "remote"]},
|
||||||
)
|
)
|
||||||
assert result2["type"] == data_entry_flow.RESULT_TYPE_FORM
|
assert result2["type"] == data_entry_flow.RESULT_TYPE_FORM
|
||||||
assert result2["step_id"] == "pairing"
|
assert result2["step_id"] == "pairing"
|
||||||
|
@ -207,7 +208,7 @@ async def test_setup_creates_entries_for_accessory_mode_devices(hass):
|
||||||
"filter": {
|
"filter": {
|
||||||
"exclude_domains": [],
|
"exclude_domains": [],
|
||||||
"exclude_entities": [],
|
"exclude_entities": [],
|
||||||
"include_domains": ["media_player", "light", "remote"],
|
"include_domains": ["media_player", "light", "lock", "remote"],
|
||||||
"include_entities": [],
|
"include_entities": [],
|
||||||
},
|
},
|
||||||
"exclude_accessory_mode": True,
|
"exclude_accessory_mode": True,
|
||||||
|
@ -225,7 +226,8 @@ async def test_setup_creates_entries_for_accessory_mode_devices(hass):
|
||||||
# 4 - camera.one in accessory mode
|
# 4 - camera.one in accessory mode
|
||||||
# 5 - media_player.two in accessory mode
|
# 5 - media_player.two in accessory mode
|
||||||
# 6 - remote.activity in accessory mode
|
# 6 - remote.activity in accessory mode
|
||||||
assert len(mock_setup_entry.mock_calls) == 6
|
# 7 - lock.new in accessory mode
|
||||||
|
assert len(mock_setup_entry.mock_calls) == 7
|
||||||
|
|
||||||
|
|
||||||
async def test_import(hass):
|
async def test_import(hass):
|
||||||
|
|
|
@ -514,35 +514,6 @@ async def test_pymodbus_constructor_fail(hass, caplog):
|
||||||
assert mock_pb.called
|
assert mock_pb.called
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize(
|
|
||||||
"do_connect,do_exception,do_text",
|
|
||||||
[
|
|
||||||
[False, None, "initial connect failed, no retry"],
|
|
||||||
[True, ModbusException("no connect"), "Modbus Error: no connect"],
|
|
||||||
],
|
|
||||||
)
|
|
||||||
async def test_pymodbus_connect_fail(
|
|
||||||
hass, do_connect, do_exception, do_text, caplog, mock_pymodbus
|
|
||||||
):
|
|
||||||
"""Run test for failing pymodbus connect."""
|
|
||||||
config = {
|
|
||||||
DOMAIN: [
|
|
||||||
{
|
|
||||||
CONF_TYPE: "tcp",
|
|
||||||
CONF_HOST: TEST_HOST,
|
|
||||||
CONF_PORT: 5501,
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
caplog.set_level(logging.ERROR)
|
|
||||||
mock_pymodbus.connect.return_value = do_connect
|
|
||||||
mock_pymodbus.connect.side_effect = do_exception
|
|
||||||
assert await async_setup_component(hass, DOMAIN, config) is False
|
|
||||||
await hass.async_block_till_done()
|
|
||||||
assert caplog.messages[0].startswith(f"Pymodbus: {do_text}")
|
|
||||||
assert caplog.records[0].levelname == "ERROR"
|
|
||||||
|
|
||||||
|
|
||||||
async def test_pymodbus_close_fail(hass, caplog, mock_pymodbus):
|
async def test_pymodbus_close_fail(hass, caplog, mock_pymodbus):
|
||||||
"""Run test for failing pymodbus close."""
|
"""Run test for failing pymodbus close."""
|
||||||
config = {
|
config = {
|
||||||
|
@ -562,44 +533,6 @@ async def test_pymodbus_close_fail(hass, caplog, mock_pymodbus):
|
||||||
# Close() is called as part of teardown
|
# Close() is called as part of teardown
|
||||||
|
|
||||||
|
|
||||||
async def test_disconnect(hass, mock_pymodbus):
|
|
||||||
"""Run test for startup delay."""
|
|
||||||
|
|
||||||
# the purpose of this test is to test a device disconnect
|
|
||||||
# We "hijiack" a binary_sensor to make a proper blackbox test.
|
|
||||||
entity_id = f"{BINARY_SENSOR_DOMAIN}.{TEST_SENSOR_NAME}"
|
|
||||||
config = {
|
|
||||||
DOMAIN: [
|
|
||||||
{
|
|
||||||
CONF_TYPE: "tcp",
|
|
||||||
CONF_HOST: TEST_HOST,
|
|
||||||
CONF_PORT: 5501,
|
|
||||||
CONF_NAME: TEST_MODBUS_NAME,
|
|
||||||
CONF_BINARY_SENSORS: [
|
|
||||||
{
|
|
||||||
CONF_INPUT_TYPE: CALL_TYPE_COIL,
|
|
||||||
CONF_NAME: f"{TEST_SENSOR_NAME}",
|
|
||||||
CONF_ADDRESS: 52,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
mock_pymodbus.read_coils.return_value = ReadResult([0x01])
|
|
||||||
mock_pymodbus.is_socket_open.return_value = False
|
|
||||||
now = dt_util.utcnow()
|
|
||||||
with mock.patch("homeassistant.helpers.event.dt_util.utcnow", return_value=now):
|
|
||||||
assert await async_setup_component(hass, DOMAIN, config) is True
|
|
||||||
await hass.async_block_till_done()
|
|
||||||
|
|
||||||
# pass first scan_interval
|
|
||||||
now = now + timedelta(seconds=20)
|
|
||||||
with mock.patch("homeassistant.helpers.event.dt_util.utcnow", return_value=now):
|
|
||||||
async_fire_time_changed(hass, now)
|
|
||||||
await hass.async_block_till_done()
|
|
||||||
assert hass.states.get(entity_id).state == STATE_UNAVAILABLE
|
|
||||||
|
|
||||||
|
|
||||||
async def test_delay(hass, mock_pymodbus):
|
async def test_delay(hass, mock_pymodbus):
|
||||||
"""Run test for startup delay."""
|
"""Run test for startup delay."""
|
||||||
|
|
||||||
|
|
|
@ -4,6 +4,7 @@ from datetime import datetime
|
||||||
from unittest.mock import patch
|
from unittest.mock import patch
|
||||||
|
|
||||||
from aiounifi.controller import MESSAGE_CLIENT, MESSAGE_CLIENT_REMOVED
|
from aiounifi.controller import MESSAGE_CLIENT, MESSAGE_CLIENT_REMOVED
|
||||||
|
import pytest
|
||||||
|
|
||||||
from homeassistant.components.device_tracker import DOMAIN as TRACKER_DOMAIN
|
from homeassistant.components.device_tracker import DOMAIN as TRACKER_DOMAIN
|
||||||
from homeassistant.components.sensor import DOMAIN as SENSOR_DOMAIN
|
from homeassistant.components.sensor import DOMAIN as SENSOR_DOMAIN
|
||||||
|
@ -134,19 +135,29 @@ async def test_bandwidth_sensors(hass, aioclient_mock, mock_unifi_websocket):
|
||||||
assert len(hass.states.async_entity_ids(SENSOR_DOMAIN)) == 4
|
assert len(hass.states.async_entity_ids(SENSOR_DOMAIN)) == 4
|
||||||
|
|
||||||
|
|
||||||
async def test_uptime_sensors(hass, aioclient_mock, mock_unifi_websocket):
|
@pytest.mark.parametrize(
|
||||||
|
"initial_uptime,event_uptime,new_uptime",
|
||||||
|
[
|
||||||
|
# Uptime listed in epoch time should never change
|
||||||
|
(1609462800, 1609462800, 1612141200),
|
||||||
|
# Uptime counted in seconds increases with every event
|
||||||
|
(60, 64, 60),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
async def test_uptime_sensors(
|
||||||
|
hass,
|
||||||
|
aioclient_mock,
|
||||||
|
mock_unifi_websocket,
|
||||||
|
initial_uptime,
|
||||||
|
event_uptime,
|
||||||
|
new_uptime,
|
||||||
|
):
|
||||||
"""Verify that uptime sensors are working as expected."""
|
"""Verify that uptime sensors are working as expected."""
|
||||||
client1 = {
|
uptime_client = {
|
||||||
"mac": "00:00:00:00:00:01",
|
"mac": "00:00:00:00:00:01",
|
||||||
"name": "client1",
|
"name": "client1",
|
||||||
"oui": "Producer",
|
"oui": "Producer",
|
||||||
"uptime": 1609506061,
|
"uptime": initial_uptime,
|
||||||
}
|
|
||||||
client2 = {
|
|
||||||
"hostname": "Client2",
|
|
||||||
"mac": "00:00:00:00:00:02",
|
|
||||||
"oui": "Producer",
|
|
||||||
"uptime": 60,
|
|
||||||
}
|
}
|
||||||
options = {
|
options = {
|
||||||
CONF_ALLOW_BANDWIDTH_SENSORS: False,
|
CONF_ALLOW_BANDWIDTH_SENSORS: False,
|
||||||
|
@ -155,32 +166,50 @@ async def test_uptime_sensors(hass, aioclient_mock, mock_unifi_websocket):
|
||||||
CONF_TRACK_DEVICES: False,
|
CONF_TRACK_DEVICES: False,
|
||||||
}
|
}
|
||||||
|
|
||||||
now = datetime(2021, 1, 1, 1, tzinfo=dt_util.UTC)
|
now = datetime(2021, 1, 1, 1, 1, 0, tzinfo=dt_util.UTC)
|
||||||
with patch("homeassistant.util.dt.now", return_value=now):
|
with patch("homeassistant.util.dt.now", return_value=now):
|
||||||
config_entry = await setup_unifi_integration(
|
config_entry = await setup_unifi_integration(
|
||||||
hass,
|
hass,
|
||||||
aioclient_mock,
|
aioclient_mock,
|
||||||
options=options,
|
options=options,
|
||||||
clients_response=[client1, client2],
|
clients_response=[uptime_client],
|
||||||
)
|
)
|
||||||
|
|
||||||
assert len(hass.states.async_all()) == 3
|
assert len(hass.states.async_all()) == 2
|
||||||
assert len(hass.states.async_entity_ids(SENSOR_DOMAIN)) == 2
|
assert len(hass.states.async_entity_ids(SENSOR_DOMAIN)) == 1
|
||||||
assert hass.states.get("sensor.client1_uptime").state == "2021-01-01T13:01:01+00:00"
|
assert hass.states.get("sensor.client1_uptime").state == "2021-01-01T01:00:00+00:00"
|
||||||
assert hass.states.get("sensor.client2_uptime").state == "2021-01-01T00:59:00+00:00"
|
|
||||||
|
|
||||||
# Verify state update
|
# Verify normal new event doesn't change uptime
|
||||||
|
# 4 seconds has passed
|
||||||
|
|
||||||
client1["uptime"] = 1609506062
|
uptime_client["uptime"] = event_uptime
|
||||||
mock_unifi_websocket(
|
now = datetime(2021, 1, 1, 1, 1, 4, tzinfo=dt_util.UTC)
|
||||||
data={
|
with patch("homeassistant.util.dt.now", return_value=now):
|
||||||
"meta": {"message": MESSAGE_CLIENT},
|
mock_unifi_websocket(
|
||||||
"data": [client1],
|
data={
|
||||||
}
|
"meta": {"message": MESSAGE_CLIENT},
|
||||||
)
|
"data": [uptime_client],
|
||||||
await hass.async_block_till_done()
|
}
|
||||||
|
)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
assert hass.states.get("sensor.client1_uptime").state == "2021-01-01T13:01:02+00:00"
|
assert hass.states.get("sensor.client1_uptime").state == "2021-01-01T01:00:00+00:00"
|
||||||
|
|
||||||
|
# Verify new event change uptime
|
||||||
|
# 1 month has passed
|
||||||
|
|
||||||
|
uptime_client["uptime"] = new_uptime
|
||||||
|
now = datetime(2021, 2, 1, 1, 1, 0, tzinfo=dt_util.UTC)
|
||||||
|
with patch("homeassistant.util.dt.now", return_value=now):
|
||||||
|
mock_unifi_websocket(
|
||||||
|
data={
|
||||||
|
"meta": {"message": MESSAGE_CLIENT},
|
||||||
|
"data": [uptime_client],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
assert hass.states.get("sensor.client1_uptime").state == "2021-02-01T01:00:00+00:00"
|
||||||
|
|
||||||
# Disable option
|
# Disable option
|
||||||
|
|
||||||
|
@ -191,7 +220,6 @@ async def test_uptime_sensors(hass, aioclient_mock, mock_unifi_websocket):
|
||||||
assert len(hass.states.async_all()) == 1
|
assert len(hass.states.async_all()) == 1
|
||||||
assert len(hass.states.async_entity_ids(SENSOR_DOMAIN)) == 0
|
assert len(hass.states.async_entity_ids(SENSOR_DOMAIN)) == 0
|
||||||
assert hass.states.get("sensor.client1_uptime") is None
|
assert hass.states.get("sensor.client1_uptime") is None
|
||||||
assert hass.states.get("sensor.client2_uptime") is None
|
|
||||||
|
|
||||||
# Enable option
|
# Enable option
|
||||||
|
|
||||||
|
@ -200,14 +228,13 @@ async def test_uptime_sensors(hass, aioclient_mock, mock_unifi_websocket):
|
||||||
hass.config_entries.async_update_entry(config_entry, options=options.copy())
|
hass.config_entries.async_update_entry(config_entry, options=options.copy())
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
assert len(hass.states.async_all()) == 3
|
assert len(hass.states.async_all()) == 2
|
||||||
assert len(hass.states.async_entity_ids(SENSOR_DOMAIN)) == 2
|
assert len(hass.states.async_entity_ids(SENSOR_DOMAIN)) == 1
|
||||||
assert hass.states.get("sensor.client1_uptime")
|
assert hass.states.get("sensor.client1_uptime")
|
||||||
assert hass.states.get("sensor.client2_uptime")
|
|
||||||
|
|
||||||
# Try to add the sensors again, using a signal
|
# Try to add the sensors again, using a signal
|
||||||
|
|
||||||
clients_connected = {client1["mac"], client2["mac"]}
|
clients_connected = {uptime_client["mac"]}
|
||||||
devices_connected = set()
|
devices_connected = set()
|
||||||
|
|
||||||
controller = hass.data[UNIFI_DOMAIN][config_entry.entry_id]
|
controller = hass.data[UNIFI_DOMAIN][config_entry.entry_id]
|
||||||
|
@ -220,8 +247,8 @@ async def test_uptime_sensors(hass, aioclient_mock, mock_unifi_websocket):
|
||||||
)
|
)
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
assert len(hass.states.async_all()) == 3
|
assert len(hass.states.async_all()) == 2
|
||||||
assert len(hass.states.async_entity_ids(SENSOR_DOMAIN)) == 2
|
assert len(hass.states.async_entity_ids(SENSOR_DOMAIN)) == 1
|
||||||
|
|
||||||
|
|
||||||
async def test_remove_sensors(hass, aioclient_mock, mock_unifi_websocket):
|
async def test_remove_sensors(hass, aioclient_mock, mock_unifi_websocket):
|
||||||
|
|
Loading…
Add table
Reference in a new issue