Address yale review comments (#124810)
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
This commit is contained in:
parent
2900fa733d
commit
70488ffd15
20 changed files with 267 additions and 283 deletions
|
@ -26,7 +26,7 @@ from .util import async_create_yale_clientsession
|
||||||
type YaleConfigEntry = ConfigEntry[YaleData]
|
type YaleConfigEntry = ConfigEntry[YaleData]
|
||||||
|
|
||||||
|
|
||||||
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
async def async_setup_entry(hass: HomeAssistant, entry: YaleConfigEntry) -> bool:
|
||||||
"""Set up yale from a config entry."""
|
"""Set up yale from a config entry."""
|
||||||
session = async_create_yale_clientsession(hass)
|
session = async_create_yale_clientsession(hass)
|
||||||
implementation = (
|
implementation = (
|
||||||
|
|
|
@ -109,12 +109,11 @@ async def async_setup_entry(
|
||||||
for description in SENSOR_TYPES_DOORBELL
|
for description in SENSOR_TYPES_DOORBELL
|
||||||
)
|
)
|
||||||
|
|
||||||
for doorbell in data.doorbells:
|
entities.extend(
|
||||||
entities.extend(
|
YaleDoorbellBinarySensor(data, doorbell, description)
|
||||||
YaleDoorbellBinarySensor(data, doorbell, description)
|
for description in SENSOR_TYPES_DOORBELL + SENSOR_TYPES_VIDEO_DOORBELL
|
||||||
for description in SENSOR_TYPES_DOORBELL + SENSOR_TYPES_VIDEO_DOORBELL
|
for doorbell in data.doorbells
|
||||||
)
|
)
|
||||||
|
|
||||||
async_add_entities(entities)
|
async_add_entities(entities)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,7 @@ from homeassistant.core import HomeAssistant, callback
|
||||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||||
|
|
||||||
from . import YaleConfigEntry
|
from . import YaleConfigEntry
|
||||||
from .entity import YaleEntityMixin
|
from .entity import YaleEntity
|
||||||
|
|
||||||
|
|
||||||
async def async_setup_entry(
|
async def async_setup_entry(
|
||||||
|
@ -18,7 +18,7 @@ async def async_setup_entry(
|
||||||
async_add_entities(YaleWakeLockButton(data, lock, "wake") for lock in data.locks)
|
async_add_entities(YaleWakeLockButton(data, lock, "wake") for lock in data.locks)
|
||||||
|
|
||||||
|
|
||||||
class YaleWakeLockButton(YaleEntityMixin, ButtonEntity):
|
class YaleWakeLockButton(YaleEntity, ButtonEntity):
|
||||||
"""Representation of an Yale lock wake button."""
|
"""Representation of an Yale lock wake button."""
|
||||||
|
|
||||||
_attr_translation_key = "wake"
|
_attr_translation_key = "wake"
|
||||||
|
|
|
@ -16,7 +16,7 @@ from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||||
|
|
||||||
from . import YaleConfigEntry, YaleData
|
from . import YaleConfigEntry, YaleData
|
||||||
from .const import DEFAULT_NAME, DEFAULT_TIMEOUT
|
from .const import DEFAULT_NAME, DEFAULT_TIMEOUT
|
||||||
from .entity import YaleEntityMixin
|
from .entity import YaleEntity
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
@ -38,7 +38,7 @@ async def async_setup_entry(
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class YaleCamera(YaleEntityMixin, Camera):
|
class YaleCamera(YaleEntity, Camera):
|
||||||
"""An implementation of an Yale security camera."""
|
"""An implementation of an Yale security camera."""
|
||||||
|
|
||||||
_attr_translation_key = "camera"
|
_attr_translation_key = "camera"
|
||||||
|
|
|
@ -26,7 +26,9 @@ class YaleConfigFlow(config_entry_oauth2_flow.AbstractOAuth2FlowHandler, domain=
|
||||||
"""Return logger."""
|
"""Return logger."""
|
||||||
return _LOGGER
|
return _LOGGER
|
||||||
|
|
||||||
async def async_step_reauth(self, data: Mapping[str, Any]) -> ConfigFlowResult:
|
async def async_step_reauth(
|
||||||
|
self, entry_data: Mapping[str, Any]
|
||||||
|
) -> ConfigFlowResult:
|
||||||
"""Handle configuration by re-auth."""
|
"""Handle configuration by re-auth."""
|
||||||
self.reauth_entry = self.hass.config_entries.async_get_entry(
|
self.reauth_entry = self.hass.config_entries.async_get_entry(
|
||||||
self.context["entry_id"]
|
self.context["entry_id"]
|
||||||
|
@ -54,4 +56,5 @@ class YaleConfigFlow(config_entry_oauth2_flow.AbstractOAuth2FlowHandler, domain=
|
||||||
return self.async_abort(reason="reauth_invalid_user")
|
return self.async_abort(reason="reauth_invalid_user")
|
||||||
return self.async_update_reload_and_abort(entry, data=data)
|
return self.async_update_reload_and_abort(entry, data=data)
|
||||||
await self.async_set_unique_id(user_id)
|
await self.async_set_unique_id(user_id)
|
||||||
|
self._abort_if_unique_id_configured()
|
||||||
return await super().async_oauth_create_entry(data)
|
return await super().async_oauth_create_entry(data)
|
||||||
|
|
|
@ -1,7 +1,5 @@
|
||||||
"""Constants for Yale devices."""
|
"""Constants for Yale devices."""
|
||||||
|
|
||||||
from yalexs.const import Brand
|
|
||||||
|
|
||||||
from homeassistant.const import Platform
|
from homeassistant.const import Platform
|
||||||
|
|
||||||
DEFAULT_TIMEOUT = 25
|
DEFAULT_TIMEOUT = 25
|
||||||
|
@ -13,8 +11,6 @@ CONF_INSTALL_ID = "install_id"
|
||||||
|
|
||||||
VERIFICATION_CODE_KEY = "verification_code"
|
VERIFICATION_CODE_KEY = "verification_code"
|
||||||
|
|
||||||
DEFAULT_BRAND = Brand.YALE_HOME
|
|
||||||
|
|
||||||
MANUFACTURER = "Yale Home Inc."
|
MANUFACTURER = "Yale Home Inc."
|
||||||
|
|
||||||
DEFAULT_NAME = "Yale"
|
DEFAULT_NAME = "Yale"
|
||||||
|
|
|
@ -4,11 +4,12 @@ from __future__ import annotations
|
||||||
|
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
|
from yalexs.const import Brand
|
||||||
|
|
||||||
from homeassistant.components.diagnostics import async_redact_data
|
from homeassistant.components.diagnostics import async_redact_data
|
||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant
|
||||||
|
|
||||||
from . import YaleConfigEntry
|
from . import YaleConfigEntry
|
||||||
from .const import CONF_BRAND, DEFAULT_BRAND
|
|
||||||
|
|
||||||
TO_REDACT = {
|
TO_REDACT = {
|
||||||
"HouseID",
|
"HouseID",
|
||||||
|
@ -45,5 +46,5 @@ async def async_get_config_entry_diagnostics(
|
||||||
)
|
)
|
||||||
for doorbell in data.doorbells
|
for doorbell in data.doorbells
|
||||||
},
|
},
|
||||||
"brand": entry.data.get(CONF_BRAND, DEFAULT_BRAND),
|
"brand": Brand.YALE_GLOBAL.value,
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,7 +20,7 @@ from .const import MANUFACTURER
|
||||||
DEVICE_TYPES = ["keypad", "lock", "camera", "doorbell", "door", "bell"]
|
DEVICE_TYPES = ["keypad", "lock", "camera", "doorbell", "door", "bell"]
|
||||||
|
|
||||||
|
|
||||||
class YaleEntityMixin(Entity):
|
class YaleEntity(Entity):
|
||||||
"""Base implementation for Yale device."""
|
"""Base implementation for Yale device."""
|
||||||
|
|
||||||
_attr_should_poll = False
|
_attr_should_poll = False
|
||||||
|
@ -87,7 +87,7 @@ class YaleEntityMixin(Entity):
|
||||||
self._update_from_data()
|
self._update_from_data()
|
||||||
|
|
||||||
|
|
||||||
class YaleDescriptionEntity(YaleEntityMixin):
|
class YaleDescriptionEntity(YaleEntity):
|
||||||
"""An Yale entity with a description."""
|
"""An Yale entity with a description."""
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
|
|
|
@ -63,22 +63,17 @@ async def async_setup_entry(
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Set up the yale event platform."""
|
"""Set up the yale event platform."""
|
||||||
data = config_entry.runtime_data
|
data = config_entry.runtime_data
|
||||||
entities: list[YaleEventEntity] = []
|
entities: list[YaleEventEntity] = [
|
||||||
|
YaleEventEntity(data, lock, description)
|
||||||
for lock in data.locks:
|
for description in TYPES_DOORBELL
|
||||||
detail = data.get_device_detail(lock.device_id)
|
for lock in data.locks
|
||||||
if detail.doorbell:
|
if (detail := data.get_device_detail(lock.device_id)) and detail.doorbell
|
||||||
entities.extend(
|
]
|
||||||
YaleEventEntity(data, lock, description)
|
entities.extend(
|
||||||
for description in TYPES_DOORBELL
|
YaleEventEntity(data, doorbell, description)
|
||||||
)
|
for description in TYPES_DOORBELL + TYPES_VIDEO_DOORBELL
|
||||||
|
for doorbell in data.doorbells
|
||||||
for doorbell in data.doorbells:
|
)
|
||||||
entities.extend(
|
|
||||||
YaleEventEntity(data, doorbell, description)
|
|
||||||
for description in TYPES_DOORBELL + TYPES_VIDEO_DOORBELL
|
|
||||||
)
|
|
||||||
|
|
||||||
async_add_entities(entities)
|
async_add_entities(entities)
|
||||||
|
|
||||||
|
|
||||||
|
@ -86,7 +81,6 @@ class YaleEventEntity(YaleDescriptionEntity, EventEntity):
|
||||||
"""An yale event entity."""
|
"""An yale event entity."""
|
||||||
|
|
||||||
entity_description: YaleEventEntityDescription
|
entity_description: YaleEventEntityDescription
|
||||||
_attr_has_entity_name = True
|
|
||||||
_last_activity: Activity | None = None
|
_last_activity: Activity | None = None
|
||||||
|
|
||||||
@callback
|
@callback
|
||||||
|
|
|
@ -19,7 +19,7 @@ from homeassistant.helpers.restore_state import RestoreEntity
|
||||||
import homeassistant.util.dt as dt_util
|
import homeassistant.util.dt as dt_util
|
||||||
|
|
||||||
from . import YaleConfigEntry, YaleData
|
from . import YaleConfigEntry, YaleData
|
||||||
from .entity import YaleEntityMixin
|
from .entity import YaleEntity
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
@ -36,7 +36,7 @@ async def async_setup_entry(
|
||||||
async_add_entities(YaleLock(data, lock) for lock in data.locks)
|
async_add_entities(YaleLock(data, lock) for lock in data.locks)
|
||||||
|
|
||||||
|
|
||||||
class YaleLock(YaleEntityMixin, RestoreEntity, LockEntity):
|
class YaleLock(YaleEntity, RestoreEntity, LockEntity):
|
||||||
"""Representation of an Yale lock."""
|
"""Representation of an Yale lock."""
|
||||||
|
|
||||||
_attr_name = None
|
_attr_name = None
|
||||||
|
|
|
@ -11,6 +11,6 @@
|
||||||
],
|
],
|
||||||
"documentation": "https://www.home-assistant.io/integrations/yale",
|
"documentation": "https://www.home-assistant.io/integrations/yale",
|
||||||
"iot_class": "cloud_push",
|
"iot_class": "cloud_push",
|
||||||
"loggers": ["pubnub", "yalexs"],
|
"loggers": ["socketio", "engineio", "yalexs"],
|
||||||
"requirements": ["yalexs==8.5.4", "yalexs-ble==2.4.3"]
|
"requirements": ["yalexs==8.5.4", "yalexs-ble==2.4.3"]
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,7 +4,7 @@ from __future__ import annotations
|
||||||
|
|
||||||
from collections.abc import Callable
|
from collections.abc import Callable
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
from typing import Any, Generic, TypeVar, cast
|
from typing import Any, cast
|
||||||
|
|
||||||
from yalexs.activity import ActivityType, LockOperationActivity
|
from yalexs.activity import ActivityType, LockOperationActivity
|
||||||
from yalexs.doorbell import Doorbell
|
from yalexs.doorbell import Doorbell
|
||||||
|
@ -42,7 +42,7 @@ from .const import (
|
||||||
OPERATION_METHOD_REMOTE,
|
OPERATION_METHOD_REMOTE,
|
||||||
OPERATION_METHOD_TAG,
|
OPERATION_METHOD_TAG,
|
||||||
)
|
)
|
||||||
from .entity import YaleDescriptionEntity, YaleEntityMixin
|
from .entity import YaleDescriptionEntity, YaleEntity
|
||||||
|
|
||||||
|
|
||||||
def _retrieve_device_battery_state(detail: LockDetail) -> int:
|
def _retrieve_device_battery_state(detail: LockDetail) -> int:
|
||||||
|
@ -55,14 +55,13 @@ def _retrieve_linked_keypad_battery_state(detail: KeypadDetail) -> int | None:
|
||||||
return detail.battery_percentage
|
return detail.battery_percentage
|
||||||
|
|
||||||
|
|
||||||
_T = TypeVar("_T", LockDetail, KeypadDetail)
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass(frozen=True, kw_only=True)
|
@dataclass(frozen=True, kw_only=True)
|
||||||
class YaleSensorEntityDescription(SensorEntityDescription, Generic[_T]):
|
class YaleSensorEntityDescription[T: LockDetail | KeypadDetail](
|
||||||
|
SensorEntityDescription
|
||||||
|
):
|
||||||
"""Mixin for required keys."""
|
"""Mixin for required keys."""
|
||||||
|
|
||||||
value_fn: Callable[[_T], int | None]
|
value_fn: Callable[[T], int | None]
|
||||||
|
|
||||||
|
|
||||||
SENSOR_TYPE_DEVICE_BATTERY = YaleSensorEntityDescription[LockDetail](
|
SENSOR_TYPE_DEVICE_BATTERY = YaleSensorEntityDescription[LockDetail](
|
||||||
|
@ -112,7 +111,7 @@ async def async_setup_entry(
|
||||||
async_add_entities(entities)
|
async_add_entities(entities)
|
||||||
|
|
||||||
|
|
||||||
class YaleOperatorSensor(YaleEntityMixin, RestoreSensor):
|
class YaleOperatorSensor(YaleEntity, RestoreSensor):
|
||||||
"""Representation of an Yale lock operation sensor."""
|
"""Representation of an Yale lock operation sensor."""
|
||||||
|
|
||||||
_attr_translation_key = "operator"
|
_attr_translation_key = "operator"
|
||||||
|
@ -196,10 +195,12 @@ class YaleOperatorSensor(YaleEntityMixin, RestoreSensor):
|
||||||
self._operated_autorelock = last_attrs[ATTR_OPERATION_AUTORELOCK]
|
self._operated_autorelock = last_attrs[ATTR_OPERATION_AUTORELOCK]
|
||||||
|
|
||||||
|
|
||||||
class YaleBatterySensor(YaleDescriptionEntity, SensorEntity, Generic[_T]):
|
class YaleBatterySensor[T: LockDetail | KeypadDetail](
|
||||||
|
YaleDescriptionEntity, SensorEntity
|
||||||
|
):
|
||||||
"""Representation of an Yale sensor."""
|
"""Representation of an Yale sensor."""
|
||||||
|
|
||||||
entity_description: YaleSensorEntityDescription[_T]
|
entity_description: YaleSensorEntityDescription[T]
|
||||||
_attr_device_class = SensorDeviceClass.BATTERY
|
_attr_device_class = SensorDeviceClass.BATTERY
|
||||||
_attr_native_unit_of_measurement = PERCENTAGE
|
_attr_native_unit_of_measurement = PERCENTAGE
|
||||||
|
|
||||||
|
|
|
@ -63,16 +63,11 @@ def _activity_time_based(latest: Activity) -> Activity | None:
|
||||||
"""Get the latest state of the sensor."""
|
"""Get the latest state of the sensor."""
|
||||||
start = latest.activity_start_time
|
start = latest.activity_start_time
|
||||||
end = latest.activity_end_time + TIME_TO_DECLARE_DETECTION
|
end = latest.activity_end_time + TIME_TO_DECLARE_DETECTION
|
||||||
if start <= _native_datetime() <= end:
|
if start <= datetime.now() <= end:
|
||||||
return latest
|
return latest
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
def _native_datetime() -> datetime:
|
|
||||||
"""Return time in the format yale uses without timezone."""
|
|
||||||
return datetime.now()
|
|
||||||
|
|
||||||
|
|
||||||
def retrieve_online_state(data: YaleData, detail: DoorbellDetail | LockDetail) -> bool:
|
def retrieve_online_state(data: YaleData, detail: DoorbellDetail | LockDetail) -> bool:
|
||||||
"""Get the latest state of the sensor."""
|
"""Get the latest state of the sensor."""
|
||||||
# The doorbell will go into standby mode when there is no motion
|
# The doorbell will go into standby mode when there is no motion
|
||||||
|
|
|
@ -1,12 +1 @@
|
||||||
"""Tests for the yale component."""
|
"""Tests for the yale component."""
|
||||||
|
|
||||||
MOCK_CONFIG_ENTRY_DATA = {
|
|
||||||
"auth_implementation": "cloud",
|
|
||||||
"token": {
|
|
||||||
"access_token": "access_token",
|
|
||||||
"expires_in": 1,
|
|
||||||
"refresh_token": "refresh_token",
|
|
||||||
"expires_at": 2,
|
|
||||||
"service": "yale",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
33
tests/components/yale/snapshots/test_binary_sensor.ambr
Normal file
33
tests/components/yale/snapshots/test_binary_sensor.ambr
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
# serializer version: 1
|
||||||
|
# name: test_doorbell_device_registry
|
||||||
|
DeviceRegistryEntrySnapshot({
|
||||||
|
'area_id': 'tmt100_name',
|
||||||
|
'config_entries': <ANY>,
|
||||||
|
'configuration_url': 'https://account.aaecosystem.com',
|
||||||
|
'connections': set({
|
||||||
|
}),
|
||||||
|
'disabled_by': None,
|
||||||
|
'entry_type': None,
|
||||||
|
'hw_version': None,
|
||||||
|
'id': <ANY>,
|
||||||
|
'identifiers': set({
|
||||||
|
tuple(
|
||||||
|
'yale',
|
||||||
|
'tmt100',
|
||||||
|
),
|
||||||
|
}),
|
||||||
|
'is_new': False,
|
||||||
|
'labels': set({
|
||||||
|
}),
|
||||||
|
'manufacturer': 'Yale Home Inc.',
|
||||||
|
'model': 'hydra1',
|
||||||
|
'model_id': None,
|
||||||
|
'name': 'tmt100 Name',
|
||||||
|
'name_by_user': None,
|
||||||
|
'primary_config_entry': <ANY>,
|
||||||
|
'serial_number': None,
|
||||||
|
'suggested_area': 'tmt100 Name',
|
||||||
|
'sw_version': '3.1.0-HYDRC75+201909251139',
|
||||||
|
'via_device_id': None,
|
||||||
|
})
|
||||||
|
# ---
|
|
@ -1,7 +1,7 @@
|
||||||
# serializer version: 1
|
# serializer version: 1
|
||||||
# name: test_diagnostics
|
# name: test_diagnostics
|
||||||
dict({
|
dict({
|
||||||
'brand': 'yale_home',
|
'brand': 'yale_global',
|
||||||
'doorbells': dict({
|
'doorbells': dict({
|
||||||
'K98GiDT45GUL': dict({
|
'K98GiDT45GUL': dict({
|
||||||
'HouseID': '**REDACTED**',
|
'HouseID': '**REDACTED**',
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
"""The binary_sensor tests for the yale platform."""
|
"""The binary_sensor tests for the yale platform."""
|
||||||
|
|
||||||
import datetime
|
import datetime
|
||||||
from unittest.mock import patch
|
|
||||||
|
from freezegun.api import FrozenDateTimeFactory
|
||||||
|
from syrupy import SnapshotAssertion
|
||||||
|
|
||||||
from homeassistant.components.lock import DOMAIN as LOCK_DOMAIN
|
from homeassistant.components.lock import DOMAIN as LOCK_DOMAIN
|
||||||
from homeassistant.const import (
|
from homeassistant.const import (
|
||||||
|
@ -33,28 +35,19 @@ async def test_doorsense(hass: HomeAssistant) -> None:
|
||||||
hass, "get_lock.online_with_doorsense.json"
|
hass, "get_lock.online_with_doorsense.json"
|
||||||
)
|
)
|
||||||
await _create_yale_with_devices(hass, [lock_one])
|
await _create_yale_with_devices(hass, [lock_one])
|
||||||
|
states = hass.states
|
||||||
binary_sensor_online_with_doorsense_name = hass.states.get(
|
assert states.get("binary_sensor.online_with_doorsense_name_door").state == STATE_ON
|
||||||
"binary_sensor.online_with_doorsense_name_door"
|
|
||||||
)
|
|
||||||
assert binary_sensor_online_with_doorsense_name.state == STATE_ON
|
|
||||||
|
|
||||||
data = {ATTR_ENTITY_ID: "lock.online_with_doorsense_name"}
|
data = {ATTR_ENTITY_ID: "lock.online_with_doorsense_name"}
|
||||||
await hass.services.async_call(LOCK_DOMAIN, SERVICE_UNLOCK, data, blocking=True)
|
await hass.services.async_call(LOCK_DOMAIN, SERVICE_UNLOCK, data, blocking=True)
|
||||||
await hass.async_block_till_done()
|
|
||||||
|
|
||||||
binary_sensor_online_with_doorsense_name = hass.states.get(
|
assert states.get("binary_sensor.online_with_doorsense_name_door").state == STATE_ON
|
||||||
"binary_sensor.online_with_doorsense_name_door"
|
|
||||||
)
|
|
||||||
assert binary_sensor_online_with_doorsense_name.state == STATE_ON
|
|
||||||
|
|
||||||
await hass.services.async_call(LOCK_DOMAIN, SERVICE_LOCK, data, blocking=True)
|
await hass.services.async_call(LOCK_DOMAIN, SERVICE_LOCK, data, blocking=True)
|
||||||
await hass.async_block_till_done()
|
|
||||||
|
|
||||||
binary_sensor_online_with_doorsense_name = hass.states.get(
|
assert (
|
||||||
"binary_sensor.online_with_doorsense_name_door"
|
states.get("binary_sensor.online_with_doorsense_name_door").state == STATE_OFF
|
||||||
)
|
)
|
||||||
assert binary_sensor_online_with_doorsense_name.state == STATE_OFF
|
|
||||||
|
|
||||||
|
|
||||||
async def test_lock_bridge_offline(hass: HomeAssistant) -> None:
|
async def test_lock_bridge_offline(hass: HomeAssistant) -> None:
|
||||||
|
@ -66,112 +59,78 @@ async def test_lock_bridge_offline(hass: HomeAssistant) -> None:
|
||||||
hass, "get_activity.bridge_offline.json"
|
hass, "get_activity.bridge_offline.json"
|
||||||
)
|
)
|
||||||
await _create_yale_with_devices(hass, [lock_one], activities=activities)
|
await _create_yale_with_devices(hass, [lock_one], activities=activities)
|
||||||
|
states = hass.states
|
||||||
binary_sensor_online_with_doorsense_name = hass.states.get(
|
assert (
|
||||||
"binary_sensor.online_with_doorsense_name_door"
|
states.get("binary_sensor.online_with_doorsense_name_door").state
|
||||||
|
== STATE_UNAVAILABLE
|
||||||
)
|
)
|
||||||
assert binary_sensor_online_with_doorsense_name.state == STATE_UNAVAILABLE
|
|
||||||
|
|
||||||
|
|
||||||
async def test_create_doorbell(hass: HomeAssistant) -> None:
|
async def test_create_doorbell(hass: HomeAssistant) -> None:
|
||||||
"""Test creation of a doorbell."""
|
"""Test creation of a doorbell."""
|
||||||
doorbell_one = await _mock_doorbell_from_fixture(hass, "get_doorbell.json")
|
doorbell_one = await _mock_doorbell_from_fixture(hass, "get_doorbell.json")
|
||||||
await _create_yale_with_devices(hass, [doorbell_one])
|
await _create_yale_with_devices(hass, [doorbell_one])
|
||||||
|
states = hass.states
|
||||||
binary_sensor_k98gidt45gul_name_motion = hass.states.get(
|
assert states.get("binary_sensor.k98gidt45gul_name_motion").state == STATE_OFF
|
||||||
"binary_sensor.k98gidt45gul_name_motion"
|
assert (
|
||||||
|
states.get("binary_sensor.k98gidt45gul_name_image_capture").state == STATE_OFF
|
||||||
)
|
)
|
||||||
assert binary_sensor_k98gidt45gul_name_motion.state == STATE_OFF
|
assert states.get("binary_sensor.k98gidt45gul_name_connectivity").state == STATE_ON
|
||||||
binary_sensor_k98gidt45gul_name_image_capture = hass.states.get(
|
assert (
|
||||||
"binary_sensor.k98gidt45gul_name_image_capture"
|
states.get("binary_sensor.k98gidt45gul_name_doorbell_ding").state == STATE_OFF
|
||||||
)
|
)
|
||||||
assert binary_sensor_k98gidt45gul_name_image_capture.state == STATE_OFF
|
assert states.get("binary_sensor.k98gidt45gul_name_motion").state == STATE_OFF
|
||||||
binary_sensor_k98gidt45gul_name_online = hass.states.get(
|
assert (
|
||||||
"binary_sensor.k98gidt45gul_name_connectivity"
|
states.get("binary_sensor.k98gidt45gul_name_image_capture").state == STATE_OFF
|
||||||
)
|
)
|
||||||
assert binary_sensor_k98gidt45gul_name_online.state == STATE_ON
|
|
||||||
binary_sensor_k98gidt45gul_name_ding = hass.states.get(
|
|
||||||
"binary_sensor.k98gidt45gul_name_doorbell_ding"
|
|
||||||
)
|
|
||||||
assert binary_sensor_k98gidt45gul_name_ding.state == STATE_OFF
|
|
||||||
binary_sensor_k98gidt45gul_name_motion = hass.states.get(
|
|
||||||
"binary_sensor.k98gidt45gul_name_motion"
|
|
||||||
)
|
|
||||||
assert binary_sensor_k98gidt45gul_name_motion.state == STATE_OFF
|
|
||||||
binary_sensor_k98gidt45gul_name_image_capture = hass.states.get(
|
|
||||||
"binary_sensor.k98gidt45gul_name_image_capture"
|
|
||||||
)
|
|
||||||
assert binary_sensor_k98gidt45gul_name_image_capture.state == STATE_OFF
|
|
||||||
|
|
||||||
|
|
||||||
async def test_create_doorbell_offline(hass: HomeAssistant) -> None:
|
async def test_create_doorbell_offline(hass: HomeAssistant) -> None:
|
||||||
"""Test creation of a doorbell that is offline."""
|
"""Test creation of a doorbell that is offline."""
|
||||||
doorbell_one = await _mock_doorbell_from_fixture(hass, "get_doorbell.offline.json")
|
doorbell_one = await _mock_doorbell_from_fixture(hass, "get_doorbell.offline.json")
|
||||||
await _create_yale_with_devices(hass, [doorbell_one])
|
await _create_yale_with_devices(hass, [doorbell_one])
|
||||||
|
states = hass.states
|
||||||
binary_sensor_tmt100_name_motion = hass.states.get(
|
assert states.get("binary_sensor.tmt100_name_motion").state == STATE_UNAVAILABLE
|
||||||
"binary_sensor.tmt100_name_motion"
|
assert states.get("binary_sensor.tmt100_name_connectivity").state == STATE_OFF
|
||||||
|
assert (
|
||||||
|
states.get("binary_sensor.tmt100_name_doorbell_ding").state == STATE_UNAVAILABLE
|
||||||
)
|
)
|
||||||
assert binary_sensor_tmt100_name_motion.state == STATE_UNAVAILABLE
|
|
||||||
binary_sensor_tmt100_name_online = hass.states.get(
|
|
||||||
"binary_sensor.tmt100_name_connectivity"
|
|
||||||
)
|
|
||||||
assert binary_sensor_tmt100_name_online.state == STATE_OFF
|
|
||||||
binary_sensor_tmt100_name_ding = hass.states.get(
|
|
||||||
"binary_sensor.tmt100_name_doorbell_ding"
|
|
||||||
)
|
|
||||||
assert binary_sensor_tmt100_name_ding.state == STATE_UNAVAILABLE
|
|
||||||
|
|
||||||
|
|
||||||
async def test_create_doorbell_with_motion(hass: HomeAssistant) -> None:
|
async def test_create_doorbell_with_motion(
|
||||||
|
hass: HomeAssistant, freezer: FrozenDateTimeFactory
|
||||||
|
) -> None:
|
||||||
"""Test creation of a doorbell."""
|
"""Test creation of a doorbell."""
|
||||||
doorbell_one = await _mock_doorbell_from_fixture(hass, "get_doorbell.json")
|
doorbell_one = await _mock_doorbell_from_fixture(hass, "get_doorbell.json")
|
||||||
activities = await _mock_activities_from_fixture(
|
activities = await _mock_activities_from_fixture(
|
||||||
hass, "get_activity.doorbell_motion.json"
|
hass, "get_activity.doorbell_motion.json"
|
||||||
)
|
)
|
||||||
await _create_yale_with_devices(hass, [doorbell_one], activities=activities)
|
await _create_yale_with_devices(hass, [doorbell_one], activities=activities)
|
||||||
|
states = hass.states
|
||||||
binary_sensor_k98gidt45gul_name_motion = hass.states.get(
|
assert states.get("binary_sensor.k98gidt45gul_name_motion").state == STATE_ON
|
||||||
"binary_sensor.k98gidt45gul_name_motion"
|
assert states.get("binary_sensor.k98gidt45gul_name_connectivity").state == STATE_ON
|
||||||
|
assert (
|
||||||
|
states.get("binary_sensor.k98gidt45gul_name_doorbell_ding").state == STATE_OFF
|
||||||
)
|
)
|
||||||
assert binary_sensor_k98gidt45gul_name_motion.state == STATE_ON
|
freezer.tick(40)
|
||||||
binary_sensor_k98gidt45gul_name_online = hass.states.get(
|
async_fire_time_changed(hass)
|
||||||
"binary_sensor.k98gidt45gul_name_connectivity"
|
await hass.async_block_till_done()
|
||||||
)
|
assert states.get("binary_sensor.k98gidt45gul_name_motion").state == STATE_OFF
|
||||||
assert binary_sensor_k98gidt45gul_name_online.state == STATE_ON
|
|
||||||
binary_sensor_k98gidt45gul_name_ding = hass.states.get(
|
|
||||||
"binary_sensor.k98gidt45gul_name_doorbell_ding"
|
|
||||||
)
|
|
||||||
assert binary_sensor_k98gidt45gul_name_ding.state == STATE_OFF
|
|
||||||
new_time = dt_util.utcnow() + datetime.timedelta(seconds=40)
|
|
||||||
native_time = datetime.datetime.now() + datetime.timedelta(seconds=40)
|
|
||||||
with patch(
|
|
||||||
"homeassistant.components.yale.util._native_datetime",
|
|
||||||
return_value=native_time,
|
|
||||||
):
|
|
||||||
async_fire_time_changed(hass, new_time)
|
|
||||||
await hass.async_block_till_done()
|
|
||||||
binary_sensor_k98gidt45gul_name_motion = hass.states.get(
|
|
||||||
"binary_sensor.k98gidt45gul_name_motion"
|
|
||||||
)
|
|
||||||
assert binary_sensor_k98gidt45gul_name_motion.state == STATE_OFF
|
|
||||||
|
|
||||||
|
|
||||||
async def test_doorbell_update_via_socketio(hass: HomeAssistant) -> None:
|
async def test_doorbell_update_via_socketio(
|
||||||
|
hass: HomeAssistant, freezer: FrozenDateTimeFactory
|
||||||
|
) -> None:
|
||||||
"""Test creation of a doorbell that can be updated via socketio."""
|
"""Test creation of a doorbell that can be updated via socketio."""
|
||||||
doorbell_one = await _mock_doorbell_from_fixture(hass, "get_doorbell.json")
|
doorbell_one = await _mock_doorbell_from_fixture(hass, "get_doorbell.json")
|
||||||
|
|
||||||
_, socketio = await _create_yale_with_devices(hass, [doorbell_one])
|
_, socketio = await _create_yale_with_devices(hass, [doorbell_one])
|
||||||
assert doorbell_one.pubsub_channel == "7c7a6672-59c8-3333-ffff-dcd98705cccc"
|
assert doorbell_one.pubsub_channel == "7c7a6672-59c8-3333-ffff-dcd98705cccc"
|
||||||
|
states = hass.states
|
||||||
binary_sensor_k98gidt45gul_name_motion = hass.states.get(
|
assert states.get("binary_sensor.k98gidt45gul_name_motion").state == STATE_OFF
|
||||||
"binary_sensor.k98gidt45gul_name_motion"
|
assert (
|
||||||
|
states.get("binary_sensor.k98gidt45gul_name_doorbell_ding").state == STATE_OFF
|
||||||
)
|
)
|
||||||
assert binary_sensor_k98gidt45gul_name_motion.state == STATE_OFF
|
|
||||||
binary_sensor_k98gidt45gul_name_ding = hass.states.get(
|
|
||||||
"binary_sensor.k98gidt45gul_name_doorbell_ding"
|
|
||||||
)
|
|
||||||
assert binary_sensor_k98gidt45gul_name_ding.state == STATE_OFF
|
|
||||||
|
|
||||||
listener = list(socketio._listeners)[0]
|
listener = list(socketio._listeners)[0]
|
||||||
listener(
|
listener(
|
||||||
|
@ -192,10 +151,7 @@ async def test_doorbell_update_via_socketio(hass: HomeAssistant) -> None:
|
||||||
|
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
binary_sensor_k98gidt45gul_name_image_capture = hass.states.get(
|
assert states.get("binary_sensor.k98gidt45gul_name_image_capture").state == STATE_ON
|
||||||
"binary_sensor.k98gidt45gul_name_image_capture"
|
|
||||||
)
|
|
||||||
assert binary_sensor_k98gidt45gul_name_image_capture.state == STATE_ON
|
|
||||||
|
|
||||||
listener(
|
listener(
|
||||||
doorbell_one.device_id,
|
doorbell_one.device_id,
|
||||||
|
@ -226,29 +182,18 @@ async def test_doorbell_update_via_socketio(hass: HomeAssistant) -> None:
|
||||||
|
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
binary_sensor_k98gidt45gul_name_motion = hass.states.get(
|
assert states.get("binary_sensor.k98gidt45gul_name_motion").state == STATE_ON
|
||||||
"binary_sensor.k98gidt45gul_name_motion"
|
assert (
|
||||||
|
states.get("binary_sensor.k98gidt45gul_name_doorbell_ding").state == STATE_OFF
|
||||||
)
|
)
|
||||||
assert binary_sensor_k98gidt45gul_name_motion.state == STATE_ON
|
|
||||||
|
|
||||||
binary_sensor_k98gidt45gul_name_ding = hass.states.get(
|
freezer.tick(40)
|
||||||
"binary_sensor.k98gidt45gul_name_doorbell_ding"
|
async_fire_time_changed(hass)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
assert (
|
||||||
|
states.get("binary_sensor.k98gidt45gul_name_image_capture").state == STATE_OFF
|
||||||
)
|
)
|
||||||
assert binary_sensor_k98gidt45gul_name_ding.state == STATE_OFF
|
|
||||||
|
|
||||||
new_time = dt_util.utcnow() + datetime.timedelta(seconds=40)
|
|
||||||
native_time = datetime.datetime.now() + datetime.timedelta(seconds=40)
|
|
||||||
with patch(
|
|
||||||
"homeassistant.components.yale.util._native_datetime",
|
|
||||||
return_value=native_time,
|
|
||||||
):
|
|
||||||
async_fire_time_changed(hass, new_time)
|
|
||||||
await hass.async_block_till_done()
|
|
||||||
|
|
||||||
binary_sensor_k98gidt45gul_name_image_capture = hass.states.get(
|
|
||||||
"binary_sensor.k98gidt45gul_name_image_capture"
|
|
||||||
)
|
|
||||||
assert binary_sensor_k98gidt45gul_name_image_capture.state == STATE_OFF
|
|
||||||
|
|
||||||
listener(
|
listener(
|
||||||
doorbell_one.device_id,
|
doorbell_one.device_id,
|
||||||
|
@ -260,37 +205,28 @@ async def test_doorbell_update_via_socketio(hass: HomeAssistant) -> None:
|
||||||
|
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
binary_sensor_k98gidt45gul_name_ding = hass.states.get(
|
assert states.get("binary_sensor.k98gidt45gul_name_doorbell_ding").state == STATE_ON
|
||||||
"binary_sensor.k98gidt45gul_name_doorbell_ding"
|
|
||||||
)
|
|
||||||
assert binary_sensor_k98gidt45gul_name_ding.state == STATE_ON
|
|
||||||
new_time = dt_util.utcnow() + datetime.timedelta(seconds=40)
|
|
||||||
native_time = datetime.datetime.now() + datetime.timedelta(seconds=40)
|
|
||||||
with patch(
|
|
||||||
"homeassistant.components.yale.util._native_datetime",
|
|
||||||
return_value=native_time,
|
|
||||||
):
|
|
||||||
async_fire_time_changed(hass, new_time)
|
|
||||||
await hass.async_block_till_done()
|
|
||||||
|
|
||||||
binary_sensor_k98gidt45gul_name_ding = hass.states.get(
|
freezer.tick(40)
|
||||||
"binary_sensor.k98gidt45gul_name_doorbell_ding"
|
async_fire_time_changed(hass)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
assert (
|
||||||
|
states.get("binary_sensor.k98gidt45gul_name_doorbell_ding").state == STATE_OFF
|
||||||
)
|
)
|
||||||
assert binary_sensor_k98gidt45gul_name_ding.state == STATE_OFF
|
|
||||||
|
|
||||||
|
|
||||||
async def test_doorbell_device_registry(
|
async def test_doorbell_device_registry(
|
||||||
hass: HomeAssistant, device_registry: dr.DeviceRegistry
|
hass: HomeAssistant,
|
||||||
|
device_registry: dr.DeviceRegistry,
|
||||||
|
snapshot: SnapshotAssertion,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Test creation of a lock with doorsense and bridge ands up in the registry."""
|
"""Test creation of a lock with doorsense and bridge ands up in the registry."""
|
||||||
doorbell_one = await _mock_doorbell_from_fixture(hass, "get_doorbell.offline.json")
|
doorbell_one = await _mock_doorbell_from_fixture(hass, "get_doorbell.offline.json")
|
||||||
await _create_yale_with_devices(hass, [doorbell_one])
|
await _create_yale_with_devices(hass, [doorbell_one])
|
||||||
|
|
||||||
reg_device = device_registry.async_get_device(identifiers={("yale", "tmt100")})
|
reg_device = device_registry.async_get_device(identifiers={("yale", "tmt100")})
|
||||||
assert reg_device.model == "hydra1"
|
assert reg_device == snapshot
|
||||||
assert reg_device.name == "tmt100 Name"
|
|
||||||
assert reg_device.manufacturer == "Yale Home Inc."
|
|
||||||
assert reg_device.sw_version == "3.1.0-HYDRC75+201909251139"
|
|
||||||
|
|
||||||
|
|
||||||
async def test_door_sense_update_via_socketio(hass: HomeAssistant) -> None:
|
async def test_door_sense_update_via_socketio(hass: HomeAssistant) -> None:
|
||||||
|
@ -302,11 +238,8 @@ async def test_door_sense_update_via_socketio(hass: HomeAssistant) -> None:
|
||||||
config_entry, socketio = await _create_yale_with_devices(
|
config_entry, socketio = await _create_yale_with_devices(
|
||||||
hass, [lock_one], activities=activities
|
hass, [lock_one], activities=activities
|
||||||
)
|
)
|
||||||
|
states = hass.states
|
||||||
binary_sensor_online_with_doorsense_name = hass.states.get(
|
assert states.get("binary_sensor.online_with_doorsense_name_door").state == STATE_ON
|
||||||
"binary_sensor.online_with_doorsense_name_door"
|
|
||||||
)
|
|
||||||
assert binary_sensor_online_with_doorsense_name.state == STATE_ON
|
|
||||||
|
|
||||||
listener = list(socketio._listeners)[0]
|
listener = list(socketio._listeners)[0]
|
||||||
listener(
|
listener(
|
||||||
|
@ -316,10 +249,10 @@ async def test_door_sense_update_via_socketio(hass: HomeAssistant) -> None:
|
||||||
)
|
)
|
||||||
|
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
binary_sensor_online_with_doorsense_name = hass.states.get(
|
|
||||||
"binary_sensor.online_with_doorsense_name_door"
|
assert (
|
||||||
|
states.get("binary_sensor.online_with_doorsense_name_door").state == STATE_OFF
|
||||||
)
|
)
|
||||||
assert binary_sensor_online_with_doorsense_name.state == STATE_OFF
|
|
||||||
|
|
||||||
listener(
|
listener(
|
||||||
lock_one.device_id,
|
lock_one.device_id,
|
||||||
|
@ -328,33 +261,22 @@ async def test_door_sense_update_via_socketio(hass: HomeAssistant) -> None:
|
||||||
)
|
)
|
||||||
|
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
binary_sensor_online_with_doorsense_name = hass.states.get(
|
|
||||||
"binary_sensor.online_with_doorsense_name_door"
|
assert states.get("binary_sensor.online_with_doorsense_name_door").state == STATE_ON
|
||||||
)
|
|
||||||
assert binary_sensor_online_with_doorsense_name.state == STATE_ON
|
|
||||||
|
|
||||||
async_fire_time_changed(hass, dt_util.utcnow() + datetime.timedelta(seconds=30))
|
async_fire_time_changed(hass, dt_util.utcnow() + datetime.timedelta(seconds=30))
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
binary_sensor_online_with_doorsense_name = hass.states.get(
|
assert states.get("binary_sensor.online_with_doorsense_name_door").state == STATE_ON
|
||||||
"binary_sensor.online_with_doorsense_name_door"
|
|
||||||
)
|
|
||||||
assert binary_sensor_online_with_doorsense_name.state == STATE_ON
|
|
||||||
|
|
||||||
socketio.connected = True
|
socketio.connected = True
|
||||||
async_fire_time_changed(hass, dt_util.utcnow() + datetime.timedelta(seconds=30))
|
async_fire_time_changed(hass, dt_util.utcnow() + datetime.timedelta(seconds=30))
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
binary_sensor_online_with_doorsense_name = hass.states.get(
|
assert states.get("binary_sensor.online_with_doorsense_name_door").state == STATE_ON
|
||||||
"binary_sensor.online_with_doorsense_name_door"
|
|
||||||
)
|
|
||||||
assert binary_sensor_online_with_doorsense_name.state == STATE_ON
|
|
||||||
|
|
||||||
# Ensure socketio status is always preserved
|
# Ensure socketio status is always preserved
|
||||||
async_fire_time_changed(hass, dt_util.utcnow() + datetime.timedelta(hours=2))
|
async_fire_time_changed(hass, dt_util.utcnow() + datetime.timedelta(hours=2))
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
binary_sensor_online_with_doorsense_name = hass.states.get(
|
assert states.get("binary_sensor.online_with_doorsense_name_door").state == STATE_ON
|
||||||
"binary_sensor.online_with_doorsense_name_door"
|
|
||||||
)
|
|
||||||
assert binary_sensor_online_with_doorsense_name.state == STATE_ON
|
|
||||||
|
|
||||||
listener(
|
listener(
|
||||||
lock_one.device_id,
|
lock_one.device_id,
|
||||||
|
@ -363,17 +285,11 @@ async def test_door_sense_update_via_socketio(hass: HomeAssistant) -> None:
|
||||||
)
|
)
|
||||||
|
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
binary_sensor_online_with_doorsense_name = hass.states.get(
|
assert states.get("binary_sensor.online_with_doorsense_name_door").state == STATE_ON
|
||||||
"binary_sensor.online_with_doorsense_name_door"
|
|
||||||
)
|
|
||||||
assert binary_sensor_online_with_doorsense_name.state == STATE_ON
|
|
||||||
|
|
||||||
async_fire_time_changed(hass, dt_util.utcnow() + datetime.timedelta(hours=4))
|
async_fire_time_changed(hass, dt_util.utcnow() + datetime.timedelta(hours=4))
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
binary_sensor_online_with_doorsense_name = hass.states.get(
|
assert states.get("binary_sensor.online_with_doorsense_name_door").state == STATE_ON
|
||||||
"binary_sensor.online_with_doorsense_name_door"
|
|
||||||
)
|
|
||||||
assert binary_sensor_online_with_doorsense_name.state == STATE_ON
|
|
||||||
|
|
||||||
await hass.config_entries.async_unload(config_entry.entry_id)
|
await hass.config_entries.async_unload(config_entry.entry_id)
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
|
@ -383,8 +299,10 @@ async def test_create_lock_with_doorbell(hass: HomeAssistant) -> None:
|
||||||
"""Test creation of a lock with a doorbell."""
|
"""Test creation of a lock with a doorbell."""
|
||||||
lock_one = await _mock_lock_from_fixture(hass, "lock_with_doorbell.online.json")
|
lock_one = await _mock_lock_from_fixture(hass, "lock_with_doorbell.online.json")
|
||||||
await _create_yale_with_devices(hass, [lock_one])
|
await _create_yale_with_devices(hass, [lock_one])
|
||||||
|
states = hass.states
|
||||||
ding_sensor = hass.states.get(
|
assert (
|
||||||
"binary_sensor.a6697750d607098bae8d6baa11ef8063_name_doorbell_ding"
|
states.get(
|
||||||
|
"binary_sensor.a6697750d607098bae8d6baa11ef8063_name_doorbell_ding"
|
||||||
|
).state
|
||||||
|
== STATE_OFF
|
||||||
)
|
)
|
||||||
assert ding_sensor.state == STATE_OFF
|
|
||||||
|
|
|
@ -1,16 +1,16 @@
|
||||||
"""Test the yale config flow."""
|
"""Test the yale config flow."""
|
||||||
|
|
||||||
from collections.abc import Generator
|
from collections.abc import Generator
|
||||||
from unittest.mock import Mock, patch
|
from unittest.mock import ANY, Mock, patch
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from homeassistant import config_entries
|
|
||||||
from homeassistant.components.yale.application_credentials import (
|
from homeassistant.components.yale.application_credentials import (
|
||||||
OAUTH2_AUTHORIZE,
|
OAUTH2_AUTHORIZE,
|
||||||
OAUTH2_TOKEN,
|
OAUTH2_TOKEN,
|
||||||
)
|
)
|
||||||
from homeassistant.components.yale.const import DOMAIN
|
from homeassistant.components.yale.const import DOMAIN
|
||||||
|
from homeassistant.config_entries import SOURCE_USER
|
||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant
|
||||||
from homeassistant.data_entry_flow import FlowResultType
|
from homeassistant.data_entry_flow import FlowResultType
|
||||||
from homeassistant.helpers import config_entry_oauth2_flow
|
from homeassistant.helpers import config_entry_oauth2_flow
|
||||||
|
@ -44,7 +44,7 @@ async def test_full_flow(
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Check full flow."""
|
"""Check full flow."""
|
||||||
result = await hass.config_entries.flow.async_init(
|
result = await hass.config_entries.flow.async_init(
|
||||||
DOMAIN, context={"source": config_entries.SOURCE_USER}
|
DOMAIN, context={"source": SOURCE_USER}
|
||||||
)
|
)
|
||||||
state = config_entry_oauth2_flow._encode_jwt(
|
state = config_entry_oauth2_flow._encode_jwt(
|
||||||
hass,
|
hass,
|
||||||
|
@ -78,13 +78,81 @@ async def test_full_flow(
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
await hass.config_entries.flow.async_configure(result["flow_id"])
|
result2 = await hass.config_entries.flow.async_configure(result["flow_id"])
|
||||||
|
|
||||||
assert len(hass.config_entries.async_entries(DOMAIN)) == 1
|
assert len(hass.config_entries.async_entries(DOMAIN)) == 1
|
||||||
assert len(mock_setup_entry.mock_calls) == 1
|
assert len(mock_setup_entry.mock_calls) == 1
|
||||||
entry = hass.config_entries.async_entries(DOMAIN)[0]
|
entry = hass.config_entries.async_entries(DOMAIN)[0]
|
||||||
assert entry.unique_id == USER_ID
|
assert entry.unique_id == USER_ID
|
||||||
|
|
||||||
|
assert result2["type"] is FlowResultType.CREATE_ENTRY
|
||||||
|
assert result2["result"].unique_id == USER_ID
|
||||||
|
assert entry.data == {
|
||||||
|
"auth_implementation": "yale",
|
||||||
|
"token": {
|
||||||
|
"access_token": jwt,
|
||||||
|
"expires_at": ANY,
|
||||||
|
"expires_in": ANY,
|
||||||
|
"refresh_token": "mock-refresh-token",
|
||||||
|
"scope": "any",
|
||||||
|
"user_id": "mock-user-id",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.usefixtures("client_credentials")
|
||||||
|
@pytest.mark.usefixtures("current_request_with_host")
|
||||||
|
async def test_full_flow_already_exists(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
hass_client_no_auth: ClientSessionGenerator,
|
||||||
|
aioclient_mock: AiohttpClientMocker,
|
||||||
|
jwt: str,
|
||||||
|
mock_setup_entry: Mock,
|
||||||
|
mock_config_entry: MockConfigEntry,
|
||||||
|
) -> None:
|
||||||
|
"""Check full flow for a user that already exists."""
|
||||||
|
|
||||||
|
mock_config_entry.add_to_hass(hass)
|
||||||
|
|
||||||
|
result = await hass.config_entries.flow.async_init(
|
||||||
|
DOMAIN, context={"source": SOURCE_USER}
|
||||||
|
)
|
||||||
|
state = config_entry_oauth2_flow._encode_jwt(
|
||||||
|
hass,
|
||||||
|
{
|
||||||
|
"flow_id": result["flow_id"],
|
||||||
|
"redirect_uri": "https://example.com/auth/external/callback",
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
assert result["url"] == (
|
||||||
|
f"{OAUTH2_AUTHORIZE}?response_type=code&client_id={CLIENT_ID}"
|
||||||
|
"&redirect_uri=https://example.com/auth/external/callback"
|
||||||
|
f"&state={state}"
|
||||||
|
)
|
||||||
|
|
||||||
|
client = await hass_client_no_auth()
|
||||||
|
resp = await client.get(f"/auth/external/callback?code=abcd&state={state}")
|
||||||
|
assert resp.status == 200
|
||||||
|
assert resp.headers["content-type"] == "text/html; charset=utf-8"
|
||||||
|
|
||||||
|
aioclient_mock.clear_requests()
|
||||||
|
aioclient_mock.post(
|
||||||
|
OAUTH2_TOKEN,
|
||||||
|
json={
|
||||||
|
"access_token": jwt,
|
||||||
|
"scope": "any",
|
||||||
|
"expires_in": 86399,
|
||||||
|
"refresh_token": "mock-refresh-token",
|
||||||
|
"user_id": "mock-user-id",
|
||||||
|
"expires_at": 1697753347,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
result2 = await hass.config_entries.flow.async_configure(result["flow_id"])
|
||||||
|
assert result2["type"] is FlowResultType.ABORT
|
||||||
|
assert result2["reason"] == "already_configured"
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.usefixtures("client_credentials")
|
@pytest.mark.usefixtures("client_credentials")
|
||||||
@pytest.mark.usefixtures("current_request_with_host")
|
@pytest.mark.usefixtures("current_request_with_host")
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
"""The event tests for the yale."""
|
"""The event tests for the yale."""
|
||||||
|
|
||||||
import datetime
|
from freezegun.api import FrozenDateTimeFactory
|
||||||
from unittest.mock import patch
|
|
||||||
|
|
||||||
from homeassistant.const import STATE_UNAVAILABLE, STATE_UNKNOWN
|
from homeassistant.const import STATE_UNAVAILABLE, STATE_UNKNOWN
|
||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant
|
||||||
|
@ -42,7 +41,9 @@ async def test_create_doorbell_offline(hass: HomeAssistant) -> None:
|
||||||
assert doorbell_state.state == STATE_UNAVAILABLE
|
assert doorbell_state.state == STATE_UNAVAILABLE
|
||||||
|
|
||||||
|
|
||||||
async def test_create_doorbell_with_motion(hass: HomeAssistant) -> None:
|
async def test_create_doorbell_with_motion(
|
||||||
|
hass: HomeAssistant, freezer: FrozenDateTimeFactory
|
||||||
|
) -> None:
|
||||||
"""Test creation of a doorbell."""
|
"""Test creation of a doorbell."""
|
||||||
doorbell_one = await _mock_doorbell_from_fixture(hass, "get_doorbell.json")
|
doorbell_one = await _mock_doorbell_from_fixture(hass, "get_doorbell.json")
|
||||||
activities = await _mock_activities_from_fixture(
|
activities = await _mock_activities_from_fixture(
|
||||||
|
@ -58,19 +59,16 @@ async def test_create_doorbell_with_motion(hass: HomeAssistant) -> None:
|
||||||
assert doorbell_state is not None
|
assert doorbell_state is not None
|
||||||
assert doorbell_state.state == STATE_UNKNOWN
|
assert doorbell_state.state == STATE_UNKNOWN
|
||||||
|
|
||||||
new_time = dt_util.utcnow() + datetime.timedelta(seconds=40)
|
freezer.tick(40)
|
||||||
native_time = datetime.datetime.now() + datetime.timedelta(seconds=40)
|
async_fire_time_changed(hass)
|
||||||
with patch(
|
await hass.async_block_till_done()
|
||||||
"homeassistant.components.yale.util._native_datetime",
|
|
||||||
return_value=native_time,
|
|
||||||
):
|
|
||||||
async_fire_time_changed(hass, new_time)
|
|
||||||
await hass.async_block_till_done()
|
|
||||||
motion_state = hass.states.get("event.k98gidt45gul_name_motion")
|
motion_state = hass.states.get("event.k98gidt45gul_name_motion")
|
||||||
assert motion_state.state == isotime
|
assert motion_state.state == isotime
|
||||||
|
|
||||||
|
|
||||||
async def test_doorbell_update_via_socketio(hass: HomeAssistant) -> None:
|
async def test_doorbell_update_via_socketio(
|
||||||
|
hass: HomeAssistant, freezer: FrozenDateTimeFactory
|
||||||
|
) -> None:
|
||||||
"""Test creation of a doorbell that can be updated via socketio."""
|
"""Test creation of a doorbell that can be updated via socketio."""
|
||||||
doorbell_one = await _mock_doorbell_from_fixture(hass, "get_doorbell.json")
|
doorbell_one = await _mock_doorbell_from_fixture(hass, "get_doorbell.json")
|
||||||
|
|
||||||
|
@ -119,14 +117,9 @@ async def test_doorbell_update_via_socketio(hass: HomeAssistant) -> None:
|
||||||
assert motion_state.state != STATE_UNKNOWN
|
assert motion_state.state != STATE_UNKNOWN
|
||||||
isotime = motion_state.state
|
isotime = motion_state.state
|
||||||
|
|
||||||
new_time = dt_util.utcnow() + datetime.timedelta(seconds=40)
|
freezer.tick(40)
|
||||||
native_time = datetime.datetime.now() + datetime.timedelta(seconds=40)
|
async_fire_time_changed(hass)
|
||||||
with patch(
|
await hass.async_block_till_done()
|
||||||
"homeassistant.components.yale.util._native_datetime",
|
|
||||||
return_value=native_time,
|
|
||||||
):
|
|
||||||
async_fire_time_changed(hass, new_time)
|
|
||||||
await hass.async_block_till_done()
|
|
||||||
|
|
||||||
motion_state = hass.states.get("event.k98gidt45gul_name_motion")
|
motion_state = hass.states.get("event.k98gidt45gul_name_motion")
|
||||||
assert motion_state is not None
|
assert motion_state is not None
|
||||||
|
@ -147,14 +140,9 @@ async def test_doorbell_update_via_socketio(hass: HomeAssistant) -> None:
|
||||||
assert doorbell_state.state != STATE_UNKNOWN
|
assert doorbell_state.state != STATE_UNKNOWN
|
||||||
isotime = motion_state.state
|
isotime = motion_state.state
|
||||||
|
|
||||||
new_time = dt_util.utcnow() + datetime.timedelta(seconds=40)
|
freezer.tick(40)
|
||||||
native_time = datetime.datetime.now() + datetime.timedelta(seconds=40)
|
async_fire_time_changed(hass)
|
||||||
with patch(
|
await hass.async_block_till_done()
|
||||||
"homeassistant.components.yale.util._native_datetime",
|
|
||||||
return_value=native_time,
|
|
||||||
):
|
|
||||||
async_fire_time_changed(hass, new_time)
|
|
||||||
await hass.async_block_till_done()
|
|
||||||
|
|
||||||
doorbell_state = hass.states.get("event.k98gidt45gul_name_doorbell")
|
doorbell_state = hass.states.get("event.k98gidt45gul_name_doorbell")
|
||||||
assert doorbell_state is not None
|
assert doorbell_state is not None
|
||||||
|
|
|
@ -89,16 +89,15 @@ async def test_unlock_throws_yale_api_http_error(hass: HomeAssistant) -> None:
|
||||||
"unlock_return_activities": _unlock_return_activities_side_effect
|
"unlock_return_activities": _unlock_return_activities_side_effect
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
last_err = None
|
|
||||||
data = {ATTR_ENTITY_ID: "lock.a6697750d607098bae8d6baa11ef8063_name"}
|
data = {ATTR_ENTITY_ID: "lock.a6697750d607098bae8d6baa11ef8063_name"}
|
||||||
try:
|
with pytest.raises(
|
||||||
|
HomeAssistantError,
|
||||||
|
match=(
|
||||||
|
"A6697750D607098BAE8D6BAA11EF8063 Name: This should bubble up as its user"
|
||||||
|
" consumable"
|
||||||
|
),
|
||||||
|
):
|
||||||
await hass.services.async_call(LOCK_DOMAIN, SERVICE_UNLOCK, data, blocking=True)
|
await hass.services.async_call(LOCK_DOMAIN, SERVICE_UNLOCK, data, blocking=True)
|
||||||
except HomeAssistantError as err:
|
|
||||||
last_err = err
|
|
||||||
assert str(last_err) == (
|
|
||||||
"A6697750D607098BAE8D6BAA11EF8063 Name: This should bubble up as its user"
|
|
||||||
" consumable"
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
async def test_lock_throws_yale_api_http_error(hass: HomeAssistant) -> None:
|
async def test_lock_throws_yale_api_http_error(hass: HomeAssistant) -> None:
|
||||||
|
@ -119,16 +118,15 @@ async def test_lock_throws_yale_api_http_error(hass: HomeAssistant) -> None:
|
||||||
"lock_return_activities": _lock_return_activities_side_effect
|
"lock_return_activities": _lock_return_activities_side_effect
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
last_err = None
|
|
||||||
data = {ATTR_ENTITY_ID: "lock.a6697750d607098bae8d6baa11ef8063_name"}
|
data = {ATTR_ENTITY_ID: "lock.a6697750d607098bae8d6baa11ef8063_name"}
|
||||||
try:
|
with pytest.raises(
|
||||||
|
HomeAssistantError,
|
||||||
|
match=(
|
||||||
|
"A6697750D607098BAE8D6BAA11EF8063 Name: This should bubble up as its user"
|
||||||
|
" consumable"
|
||||||
|
),
|
||||||
|
):
|
||||||
await hass.services.async_call(LOCK_DOMAIN, SERVICE_LOCK, data, blocking=True)
|
await hass.services.async_call(LOCK_DOMAIN, SERVICE_LOCK, data, blocking=True)
|
||||||
except HomeAssistantError as err:
|
|
||||||
last_err = err
|
|
||||||
assert str(last_err) == (
|
|
||||||
"A6697750D607098BAE8D6BAA11EF8063 Name: This should bubble up as its user"
|
|
||||||
" consumable"
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
async def test_open_throws_hass_service_not_supported_error(
|
async def test_open_throws_hass_service_not_supported_error(
|
||||||
|
@ -185,6 +183,7 @@ async def test_load_unload(hass: HomeAssistant) -> None:
|
||||||
|
|
||||||
await hass.config_entries.async_unload(config_entry.entry_id)
|
await hass.config_entries.async_unload(config_entry.entry_id)
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
|
assert config_entry.state is ConfigEntryState.NOT_LOADED
|
||||||
|
|
||||||
|
|
||||||
async def test_load_triggers_ble_discovery(
|
async def test_load_triggers_ble_discovery(
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue