mirror of
https://github.com/home-assistant/core.git
synced 2025-04-24 01:08:12 +00:00
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:
parent
33bc1f01a4
commit
ab50069918
@ -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()
|
||||
|
||||
|
@ -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"
|
||||
|
@ -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()
|
||||
|
||||
|
@ -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)},
|
||||
)
|
||||
|
@ -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:
|
||||
|
@ -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%]"
|
||||
|
@ -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."
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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(
|
||||
|
@ -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
|
||||
|
Loading…
x
Reference in New Issue
Block a user