Bump yalesmartalarmclient to 0.4.0 (#124165)

* Bump yalesmartalarmclient to 0.4.0

* Fix various
This commit is contained in:
G Johansson 2024-08-18 22:41:45 +02:00 committed by GitHub
parent 50f9c1e5a4
commit 05aeb3fbd1
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
12 changed files with 121 additions and 62 deletions

View File

@ -146,12 +146,7 @@ class YaleDataUpdateCoordinator(DataUpdateCoordinator[dict[str, Any]]):
"""Fetch data from Yale."""
try:
arm_status = self.yale.get_armed_status()
data = self.yale.get_all()
cycle = data["CYCLE"]
status = data["STATUS"]
online = data["ONLINE"]
panel_info = data["PANEL INFO"]
data = self.yale.get_information()
except AuthenticationError as error:
raise ConfigEntryAuthFailed from error
except YALE_BASE_ERRORS as error:
@ -159,8 +154,8 @@ class YaleDataUpdateCoordinator(DataUpdateCoordinator[dict[str, Any]]):
return {
"arm_status": arm_status,
"cycle": cycle,
"status": status,
"online": online,
"panel_info": panel_info,
"cycle": data.cycle,
"status": data.status,
"online": data.online,
"panel_info": data.panel_info,
}

View File

@ -2,6 +2,7 @@
from __future__ import annotations
from dataclasses import asdict
from typing import Any
from homeassistant.components.diagnostics import async_redact_data
@ -29,4 +30,4 @@ async def async_get_config_entry_diagnostics(
assert coordinator.yale
get_all_data = await hass.async_add_executor_job(coordinator.yale.get_all)
return async_redact_data(get_all_data, TO_REDACT)
return async_redact_data(asdict(get_all_data), TO_REDACT)

View File

@ -7,7 +7,7 @@ from typing import TYPE_CHECKING, Any
from homeassistant.components.lock import LockEntity
from homeassistant.const import ATTR_CODE
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import HomeAssistantError
from homeassistant.exceptions import HomeAssistantError, ServiceValidationError
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from . import YaleConfigEntry
@ -61,17 +61,22 @@ class YaleDoorlock(YaleEntity, LockEntity):
"""Set lock."""
if TYPE_CHECKING:
assert self.coordinator.yale, "Connection to API is missing"
if command == "unlocked" and not code:
raise ServiceValidationError(
translation_domain=DOMAIN,
translation_key="no_code",
)
try:
get_lock = await self.hass.async_add_executor_job(
self.coordinator.yale.lock_api.get, self.lock_name
)
if command == "locked":
if get_lock and command == "locked":
lock_state = await self.hass.async_add_executor_job(
self.coordinator.yale.lock_api.close_lock,
get_lock,
)
if command == "unlocked":
if code and get_lock and command == "unlocked":
lock_state = await self.hass.async_add_executor_job(
self.coordinator.yale.lock_api.open_lock, get_lock, code
)

View File

@ -6,5 +6,5 @@
"documentation": "https://www.home-assistant.io/integrations/yale_smart_alarm",
"iot_class": "cloud_polling",
"loggers": ["yalesmartalarmclient"],
"requirements": ["yalesmartalarmclient==0.3.9"]
"requirements": ["yalesmartalarmclient==0.4.0"]
}

View File

@ -67,6 +67,9 @@
"set_lock": {
"message": "Could not set lock for {name}: {error}"
},
"no_code": {
"message": "Can not unlock without code"
},
"could_not_change_lock": {
"message": "Could not set lock, check system ready for lock"
},

View File

@ -2955,7 +2955,7 @@ xmltodict==0.13.0
xs1-api-client==3.0.0
# homeassistant.components.yale_smart_alarm
yalesmartalarmclient==0.3.9
yalesmartalarmclient==0.4.0
# homeassistant.components.august
# homeassistant.components.yalexs_ble

View File

@ -2335,7 +2335,7 @@ xknxproject==3.7.1
xmltodict==0.13.0
# homeassistant.components.yale_smart_alarm
yalesmartalarmclient==0.3.9
yalesmartalarmclient==0.4.0
# homeassistant.components.august
# homeassistant.components.yalexs_ble

View File

@ -7,6 +7,7 @@ from typing import Any
from unittest.mock import Mock, patch
import pytest
from yalesmartalarmclient import YaleSmartAlarmData
from yalesmartalarmclient.const import YALE_STATE_ARM_FULL
from homeassistant.components.yale_smart_alarm.const import DOMAIN, PLATFORMS
@ -33,7 +34,10 @@ async def patch_platform_constant() -> list[Platform]:
@pytest.fixture
async def load_config_entry(
hass: HomeAssistant, load_json: dict[str, Any], load_platforms: list[Platform]
hass: HomeAssistant,
get_data: YaleSmartAlarmData,
get_all_data: YaleSmartAlarmData,
load_platforms: list[Platform],
) -> tuple[MockConfigEntry, Mock]:
"""Set up the Yale Smart Living integration in Home Assistant."""
with patch("homeassistant.components.yale_smart_alarm.PLATFORMS", load_platforms):
@ -56,7 +60,8 @@ async def load_config_entry(
client = mock_client_class.return_value
client.auth = Mock()
client.lock_api = Mock()
client.get_all.return_value = load_json
client.get_all.return_value = get_all_data
client.get_information.return_value = get_data
client.get_armed_status.return_value = YALE_STATE_ARM_FULL
await hass.config_entries.async_setup(config_entry.entry_id)
await hass.async_block_till_done()
@ -64,10 +69,50 @@ async def load_config_entry(
return (config_entry, client)
@pytest.fixture(name="load_json", scope="package")
def load_json_from_fixture() -> dict[str, Any]:
@pytest.fixture(name="loaded_fixture", scope="package")
def get_fixture_data() -> dict[str, Any]:
"""Load fixture with json data and return."""
data_fixture = load_fixture("get_all.json", "yale_smart_alarm")
json_data: dict[str, Any] = json.loads(data_fixture)
return json_data
@pytest.fixture(name="get_data", scope="package")
def get_update_data(loaded_fixture: dict[str, Any]) -> YaleSmartAlarmData:
"""Load update data and return."""
status = loaded_fixture["STATUS"]
cycle = loaded_fixture["CYCLE"]
online = loaded_fixture["ONLINE"]
panel_info = loaded_fixture["PANEL INFO"]
return YaleSmartAlarmData(
status=status,
cycle=cycle,
online=online,
panel_info=panel_info,
)
@pytest.fixture(name="get_all_data", scope="package")
def get_diag_data(loaded_fixture: dict[str, Any]) -> YaleSmartAlarmData:
"""Load all data and return."""
devices = loaded_fixture["DEVICES"]
mode = loaded_fixture["MODE"]
status = loaded_fixture["STATUS"]
cycle = loaded_fixture["CYCLE"]
online = loaded_fixture["ONLINE"]
history = loaded_fixture["HISTORY"]
panel_info = loaded_fixture["PANEL INFO"]
auth_check = loaded_fixture["AUTH CHECK"]
return YaleSmartAlarmData(
devices=devices,
mode=mode,
status=status,
cycle=cycle,
online=online,
history=history,
panel_info=panel_info,
auth_check=auth_check,
)

View File

@ -1,7 +1,7 @@
# serializer version: 1
# name: test_diagnostics
dict({
'AUTH CHECK': dict({
'auth_check': dict({
'agent': False,
'dealer_group': 'yale',
'dealer_id': '605',
@ -16,7 +16,7 @@
'user_id': '**REDACTED**',
'xml_version': '2',
}),
'CYCLE': dict({
'cycle': dict({
'alarm_event_latest': None,
'capture_latest': None,
'device_status': list([
@ -650,7 +650,7 @@
'utc_event_time': None,
}),
}),
'DEVICES': list([
'devices': list([
dict({
'address': '**REDACTED**',
'area': '1',
@ -1249,7 +1249,7 @@
'type_no': '40',
}),
]),
'HISTORY': list([
'history': list([
dict({
'area': 1,
'cid': '18180701000',
@ -1391,14 +1391,14 @@
'zone': 1,
}),
]),
'MODE': list([
'mode': list([
dict({
'area': '1',
'mode': 'disarm',
}),
]),
'ONLINE': 'online',
'PANEL INFO': dict({
'online': 'online',
'panel_info': dict({
'SMS_Balance': '50',
'contact': '',
'dealer_name': 'Poland',
@ -1416,7 +1416,7 @@
'zb_version': '4.1.2.6.2',
'zw_version': '',
}),
'STATUS': dict({
'status': dict({
'acfail': 'main.normal',
'battery': 'main.normal',
'gsm_rssi': '0',

View File

@ -3,12 +3,15 @@
from __future__ import annotations
from datetime import timedelta
from typing import Any
from unittest.mock import Mock, patch
import pytest
from yalesmartalarmclient.const import YALE_STATE_ARM_FULL
from yalesmartalarmclient.exceptions import AuthenticationError, UnknownError
from yalesmartalarmclient import (
YALE_STATE_ARM_FULL,
AuthenticationError,
UnknownError,
YaleSmartAlarmData,
)
from homeassistant.components.yale_smart_alarm.const import DOMAIN
from homeassistant.config_entries import SOURCE_USER
@ -32,7 +35,7 @@ from tests.common import MockConfigEntry, async_fire_time_changed
)
async def test_coordinator_setup_errors(
hass: HomeAssistant,
load_json: dict[str, Any],
get_data: YaleSmartAlarmData,
p_error: Exception,
) -> None:
"""Test the Yale Smart Living coordinator with errors."""
@ -64,7 +67,7 @@ async def test_coordinator_setup_errors(
async def test_coordinator_setup_and_update_errors(
hass: HomeAssistant,
load_config_entry: tuple[MockConfigEntry, Mock],
load_json: dict[str, Any],
get_data: YaleSmartAlarmData,
) -> None:
"""Test the Yale Smart Living coordinator with errors."""
@ -74,51 +77,51 @@ async def test_coordinator_setup_and_update_errors(
assert state.state == STATE_ALARM_ARMED_AWAY
client.reset_mock()
client.get_all.side_effect = ConnectionError("Could not connect")
client.get_information.side_effect = ConnectionError("Could not connect")
async_fire_time_changed(hass, dt_util.utcnow() + timedelta(minutes=1))
await hass.async_block_till_done(wait_background_tasks=True)
client.get_all.assert_called_once()
client.get_information.assert_called_once()
state = hass.states.get("alarm_control_panel.yale_smart_alarm")
assert state.state == STATE_UNAVAILABLE
client.reset_mock()
client.get_all.side_effect = ConnectionError("Could not connect")
client.get_information.side_effect = ConnectionError("Could not connect")
async_fire_time_changed(hass, dt_util.utcnow() + timedelta(minutes=2))
await hass.async_block_till_done(wait_background_tasks=True)
client.get_all.assert_called_once()
client.get_information.assert_called_once()
state = hass.states.get("alarm_control_panel.yale_smart_alarm")
assert state.state == STATE_UNAVAILABLE
client.reset_mock()
client.get_all.side_effect = TimeoutError("Could not connect")
client.get_information.side_effect = TimeoutError("Could not connect")
async_fire_time_changed(hass, dt_util.utcnow() + timedelta(minutes=3))
await hass.async_block_till_done(wait_background_tasks=True)
client.get_all.assert_called_once()
client.get_information.assert_called_once()
state = hass.states.get("alarm_control_panel.yale_smart_alarm")
assert state.state == STATE_UNAVAILABLE
client.reset_mock()
client.get_all.side_effect = UnknownError("info")
client.get_information.side_effect = UnknownError("info")
async_fire_time_changed(hass, dt_util.utcnow() + timedelta(minutes=4))
await hass.async_block_till_done(wait_background_tasks=True)
client.get_all.assert_called_once()
client.get_information.assert_called_once()
state = hass.states.get("alarm_control_panel.yale_smart_alarm")
assert state.state == STATE_UNAVAILABLE
client.reset_mock()
client.get_all.side_effect = None
client.get_all.return_value = load_json
client.get_information.side_effect = None
client.get_information.return_value = get_data
client.get_armed_status.return_value = YALE_STATE_ARM_FULL
async_fire_time_changed(hass, dt_util.utcnow() + timedelta(minutes=5))
await hass.async_block_till_done(wait_background_tasks=True)
client.get_all.assert_called_once()
client.get_information.assert_called_once()
state = hass.states.get("alarm_control_panel.yale_smart_alarm")
assert state.state == STATE_ALARM_ARMED_AWAY
client.reset_mock()
client.get_all.side_effect = AuthenticationError("Can not authenticate")
client.get_information.side_effect = AuthenticationError("Can not authenticate")
async_fire_time_changed(hass, dt_util.utcnow() + timedelta(minutes=6))
await hass.async_block_till_done(wait_background_tasks=True)
client.get_all.assert_called_once()
client.get_information.assert_called_once()
state = hass.states.get("alarm_control_panel.yale_smart_alarm")
assert state.state == STATE_UNAVAILABLE

View File

@ -3,13 +3,11 @@
from __future__ import annotations
from copy import deepcopy
from typing import Any
from unittest.mock import Mock
import pytest
from syrupy.assertion import SnapshotAssertion
from yalesmartalarmclient.exceptions import UnknownError
from yalesmartalarmclient.lock import YaleDoorManAPI
from yalesmartalarmclient import UnknownError, YaleDoorManAPI, YaleSmartAlarmData
from homeassistant.components.lock import DOMAIN as LOCK_DOMAIN
from homeassistant.const import (
@ -20,7 +18,7 @@ from homeassistant.const import (
Platform,
)
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import HomeAssistantError
from homeassistant.exceptions import HomeAssistantError, ServiceValidationError
from homeassistant.helpers import entity_registry as er
from tests.common import MockConfigEntry, snapshot_platform
@ -47,7 +45,7 @@ async def test_lock(
)
async def test_lock_service_calls(
hass: HomeAssistant,
load_json: dict[str, Any],
get_data: YaleSmartAlarmData,
load_config_entry: tuple[MockConfigEntry, Mock],
entity_registry: er.EntityRegistry,
snapshot: SnapshotAssertion,
@ -56,8 +54,8 @@ async def test_lock_service_calls(
client = load_config_entry[1]
data = deepcopy(load_json)
data["data"] = data.pop("DEVICES")
data = deepcopy(get_data.cycle)
data["data"] = data.pop("device_status")
client.auth.get_authenticated = Mock(return_value=data)
client.auth.post_authenticated = Mock(return_value={"code": "000"})
@ -66,6 +64,14 @@ async def test_lock_service_calls(
state = hass.states.get("lock.device1")
assert state.state == "locked"
with pytest.raises(ServiceValidationError):
await hass.services.async_call(
LOCK_DOMAIN,
SERVICE_UNLOCK,
{ATTR_ENTITY_ID: "lock.device1"},
blocking=True,
)
await hass.services.async_call(
LOCK_DOMAIN,
SERVICE_UNLOCK,
@ -93,7 +99,7 @@ async def test_lock_service_calls(
)
async def test_lock_service_call_fails(
hass: HomeAssistant,
load_json: dict[str, Any],
get_data: YaleSmartAlarmData,
load_config_entry: tuple[MockConfigEntry, Mock],
entity_registry: er.EntityRegistry,
snapshot: SnapshotAssertion,
@ -102,8 +108,8 @@ async def test_lock_service_call_fails(
client = load_config_entry[1]
data = deepcopy(load_json)
data["data"] = data.pop("DEVICES")
data = deepcopy(get_data.cycle)
data["data"] = data.pop("device_status")
client.auth.get_authenticated = Mock(return_value=data)
client.auth.post_authenticated = Mock(side_effect=UnknownError("test_side_effect"))
@ -145,7 +151,7 @@ async def test_lock_service_call_fails(
)
async def test_lock_service_call_fails_with_incorrect_status(
hass: HomeAssistant,
load_json: dict[str, Any],
get_data: YaleSmartAlarmData,
load_config_entry: tuple[MockConfigEntry, Mock],
entity_registry: er.EntityRegistry,
snapshot: SnapshotAssertion,
@ -154,8 +160,8 @@ async def test_lock_service_call_fails_with_incorrect_status(
client = load_config_entry[1]
data = deepcopy(load_json)
data["data"] = data.pop("DEVICES")
data = deepcopy(get_data.cycle)
data["data"] = data.pop("device_status")
client.auth.get_authenticated = Mock(return_value=data)
client.auth.post_authenticated = Mock(return_value={"code": "FFF"})

View File

@ -2,9 +2,10 @@
from __future__ import annotations
from typing import Any
from unittest.mock import Mock
from yalesmartalarmclient import YaleSmartAlarmData
from homeassistant.core import HomeAssistant
from tests.common import MockConfigEntry
@ -13,7 +14,7 @@ from tests.common import MockConfigEntry
async def test_coordinator_setup_and_update_errors(
hass: HomeAssistant,
load_config_entry: tuple[MockConfigEntry, Mock],
load_json: dict[str, Any],
get_data: YaleSmartAlarmData,
) -> None:
"""Test the Yale Smart Living coordinator with errors."""