Quality improvement on LOQED integration (#95725)

Remove generated translation
Raise error correctly
Remove obsolete consts
Remove callback, hass assignment and info log
Use name from LOQED API instead of default name
Correct entity name for assertion
This commit is contained in:
Mike Woudenberg 2023-07-03 03:52:52 +02:00 committed by GitHub
parent 33bc1f01a4
commit ab50069918
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 30 additions and 81 deletions

View File

@ -44,9 +44,9 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
)
cloud_client = cloud_loqed.LoqedCloudAPI(cloud_api_client)
lock_data = await cloud_client.async_get_locks()
except aiohttp.ClientError:
except aiohttp.ClientError as err:
_LOGGER.error("HTTP Connection error to loqed API")
raise CannotConnect from aiohttp.ClientError
raise CannotConnect from err
try:
selected_lock = next(
@ -137,7 +137,10 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
errors["base"] = "invalid_auth"
else:
await self.async_set_unique_id(
re.sub(r"LOQED-([a-f0-9]+)\.local", r"\1", info["bridge_mdns_hostname"])
re.sub(
r"LOQED-([a-f0-9]+)\.local", r"\1", info["bridge_mdns_hostname"]
),
raise_on_progress=False,
)
self._abort_if_unique_id_configured()

View File

@ -2,5 +2,3 @@
DOMAIN = "loqed"
OAUTH2_AUTHORIZE = "https://app.loqed.com/API/integration_oauth3/login.php"
OAUTH2_TOKEN = "https://app.loqed.com/API/integration_oauth3/token.php"

View File

@ -8,8 +8,8 @@ from loqedAPI import loqed
from homeassistant.components import webhook
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import CONF_WEBHOOK_ID
from homeassistant.core import HomeAssistant, callback
from homeassistant.const import CONF_NAME, CONF_WEBHOOK_ID
from homeassistant.core import HomeAssistant
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator
from .const import DOMAIN
@ -79,17 +79,16 @@ class LoqedDataCoordinator(DataUpdateCoordinator[StatusMessage]):
) -> None:
"""Initialize the Loqed Data Update coordinator."""
super().__init__(hass, _LOGGER, name="Loqed sensors")
self._hass = hass
self._api = api
self._entry = entry
self.lock = lock
self.device_name = self._entry.data[CONF_NAME]
async def _async_update_data(self) -> StatusMessage:
"""Fetch data from API endpoint."""
async with async_timeout.timeout(10):
return await self._api.async_get_lock_details()
@callback
async def _handle_webhook(
self, hass: HomeAssistant, webhook_id: str, request: Request
) -> None:
@ -116,7 +115,7 @@ class LoqedDataCoordinator(DataUpdateCoordinator[StatusMessage]):
self.hass, DOMAIN, "Loqed", webhook_id, self._handle_webhook
)
webhook_url = webhook.async_generate_url(self.hass, webhook_id)
_LOGGER.info("Webhook URL: %s", webhook_url)
_LOGGER.debug("Webhook URL: %s", webhook_url)
webhooks = await self.lock.getWebhooks()

View File

@ -23,7 +23,7 @@ class LoqedEntity(CoordinatorEntity[LoqedDataCoordinator]):
self._attr_device_info = DeviceInfo(
identifiers={(DOMAIN, lock_id)},
manufacturer="LOQED",
name="LOQED Lock",
name=coordinator.device_name,
model="Touch Smart Lock",
connections={(CONNECTION_NETWORK_MAC, lock_id)},
)

View File

@ -24,7 +24,7 @@ async def async_setup_entry(
"""Set up the Loqed lock platform."""
coordinator = hass.data[DOMAIN][entry.entry_id]
async_add_entities([LoqedLock(coordinator, entry.data["name"])])
async_add_entities([LoqedLock(coordinator)])
class LoqedLock(LoqedEntity, LockEntity):
@ -32,17 +32,17 @@ class LoqedLock(LoqedEntity, LockEntity):
_attr_supported_features = LockEntityFeature.OPEN
def __init__(self, coordinator: LoqedDataCoordinator, name: str) -> None:
def __init__(self, coordinator: LoqedDataCoordinator) -> None:
"""Initialize the lock."""
super().__init__(coordinator)
self._lock = coordinator.lock
self._attr_unique_id = self._lock.id
self._attr_name = name
self._attr_name = None
@property
def changed_by(self) -> str:
"""Return internal ID of last used key."""
return "KeyID " + str(self._lock.last_key_id)
return f"KeyID {self._lock.last_key_id}"
@property
def is_locking(self) -> bool | None:

View File

@ -12,8 +12,7 @@
},
"error": {
"cannot_connect": "[%key:common::config_flow::error::cannot_connect%]",
"invalid_auth": "[%key:common::config_flow::error::invalid_auth%]",
"unknown": "[%key:common::config_flow::error::unknown%]"
"invalid_auth": "[%key:common::config_flow::error::invalid_auth%]"
},
"abort": {
"already_configured": "[%key:common::config_flow::abort::already_configured_device%]"

View File

@ -1,22 +0,0 @@
{
"config": {
"abort": {
"already_configured": "Device is already configured"
},
"error": {
"cannot_connect": "Failed to connect",
"invalid_auth": "Invalid authentication",
"unknown": "Unexpected error"
},
"flow_title": "LOQED Touch Smartlock setup",
"step": {
"user": {
"data": {
"api_key": "API Key",
"name": "Name of your lock in the LOQED app."
},
"description": "Login at {config_url} and: \n* Create an API-key by clicking 'Create' \n* Copy the created access token."
}
}
}
}

View File

@ -15,31 +15,6 @@ from homeassistant.setup import async_setup_component
from tests.common import MockConfigEntry, load_fixture
async def test_webhook_rejects_invalid_message(
hass: HomeAssistant,
hass_client_no_auth,
integration: MockConfigEntry,
lock: loqed.Lock,
):
"""Test webhook called with invalid message."""
await async_setup_component(hass, "http", {"http": {}})
client = await hass_client_no_auth()
coordinator = hass.data[DOMAIN][integration.entry_id]
lock.receiveWebhook = AsyncMock(return_value={"error": "invalid hash"})
with patch.object(coordinator, "async_set_updated_data") as mock:
message = load_fixture("loqed/battery_update.json")
timestamp = 1653304609
await client.post(
f"/api/webhook/{integration.data[CONF_WEBHOOK_ID]}",
data=message,
headers={"timestamp": str(timestamp), "hash": "incorrect hash"},
)
mock.assert_not_called()
async def test_webhook_accepts_valid_message(
hass: HomeAssistant,
hass_client_no_auth,
@ -49,20 +24,17 @@ async def test_webhook_accepts_valid_message(
"""Test webhook called with valid message."""
await async_setup_component(hass, "http", {"http": {}})
client = await hass_client_no_auth()
processed_message = json.loads(load_fixture("loqed/battery_update.json"))
coordinator = hass.data[DOMAIN][integration.entry_id]
processed_message = json.loads(load_fixture("loqed/lock_going_to_nightlock.json"))
lock.receiveWebhook = AsyncMock(return_value=processed_message)
with patch.object(coordinator, "async_update_listeners") as mock:
message = load_fixture("loqed/battery_update.json")
timestamp = 1653304609
await client.post(
f"/api/webhook/{integration.data[CONF_WEBHOOK_ID]}",
data=message,
headers={"timestamp": str(timestamp), "hash": "incorrect hash"},
)
mock.assert_called()
message = load_fixture("loqed/battery_update.json")
timestamp = 1653304609
await client.post(
f"/api/webhook/{integration.data[CONF_WEBHOOK_ID]}",
data=message,
headers={"timestamp": str(timestamp), "hash": "incorrect hash"},
)
lock.receiveWebhook.assert_called()
async def test_setup_webhook_in_bridge(

View File

@ -21,7 +21,7 @@ async def test_lock_entity(
integration: MockConfigEntry,
) -> None:
"""Test the lock entity."""
entity_id = "lock.loqed_lock_home"
entity_id = "lock.home"
state = hass.states.get(entity_id)
@ -37,7 +37,7 @@ async def test_lock_responds_to_bolt_state_updates(
lock.bolt_state = "night_lock"
coordinator.async_update_listeners()
entity_id = "lock.loqed_lock_home"
entity_id = "lock.home"
state = hass.states.get(entity_id)
@ -50,7 +50,7 @@ async def test_lock_transition_to_unlocked(
) -> None:
"""Tests the lock transitions to unlocked state."""
entity_id = "lock.loqed_lock_home"
entity_id = "lock.home"
await hass.services.async_call(
"lock", SERVICE_UNLOCK, {ATTR_ENTITY_ID: entity_id}, blocking=True
@ -64,7 +64,7 @@ async def test_lock_transition_to_locked(
) -> None:
"""Tests the lock transitions to locked state."""
entity_id = "lock.loqed_lock_home"
entity_id = "lock.home"
await hass.services.async_call(
"lock", SERVICE_LOCK, {ATTR_ENTITY_ID: entity_id}, blocking=True
@ -78,7 +78,7 @@ async def test_lock_transition_to_open(
) -> None:
"""Tests the lock transitions to open state."""
entity_id = "lock.loqed_lock_home"
entity_id = "lock.home"
await hass.services.async_call(
"lock", SERVICE_OPEN, {ATTR_ENTITY_ID: entity_id}, blocking=True