mirror of
https://github.com/home-assistant/core.git
synced 2026-05-08 17:49:37 +01:00
Cache get api calls in FRITZ!Box tools (#160246)
Co-authored-by: Simone Chemelli <simone.chemelli@gmail.com>
This commit is contained in:
@@ -36,6 +36,7 @@ from homeassistant.helpers.device_registry import CONNECTION_NETWORK_MAC
|
||||
from homeassistant.helpers.dispatcher import async_dispatcher_send
|
||||
from homeassistant.helpers.typing import StateType
|
||||
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed
|
||||
from homeassistant.util import slugify
|
||||
from homeassistant.util.hass_dict import HassKey
|
||||
|
||||
from .const import (
|
||||
@@ -90,10 +91,56 @@ class UpdateCoordinatorDataType(TypedDict):
|
||||
entity_states: dict[str, StateType | bool]
|
||||
|
||||
|
||||
class FritzConnectionCached(FritzConnection): # type: ignore[misc]
|
||||
"""FritzConnection with cached call action."""
|
||||
|
||||
_call_cache: dict[str, dict[str, Any]]
|
||||
|
||||
def clear_cache(self) -> None:
|
||||
"""Clear cached calls."""
|
||||
self._call_cache = {}
|
||||
_LOGGER.debug("Cleared FritzConnection call action cache")
|
||||
|
||||
def call_action(
|
||||
self,
|
||||
service_name: str,
|
||||
action_name: str,
|
||||
*,
|
||||
arguments: dict | None = None,
|
||||
**kwargs: Any,
|
||||
) -> dict[str, Any]:
|
||||
"""Call action with cached services. Only get actions are cached."""
|
||||
if not action_name.lower().startswith("get"):
|
||||
return super().call_action( # type: ignore[no-any-return]
|
||||
service_name, action_name, arguments=arguments, **kwargs
|
||||
)
|
||||
|
||||
if not hasattr(self, "_call_cache"):
|
||||
self._call_cache = {}
|
||||
|
||||
kwargs_key = ",".join(f"{k}={v!r}" for k, v in sorted(kwargs.items()))
|
||||
|
||||
cache_key = slugify(f"{service_name}:{action_name}:{arguments}:{kwargs_key}")
|
||||
if (result := self._call_cache.get(cache_key)) is not None:
|
||||
_LOGGER.debug("Using cached result for %s %s", service_name, action_name)
|
||||
return result
|
||||
|
||||
result = super().call_action(
|
||||
service_name, action_name, arguments=arguments, **kwargs
|
||||
)
|
||||
self._call_cache[cache_key] = result
|
||||
return result # type: ignore[no-any-return]
|
||||
|
||||
|
||||
class FritzBoxTools(DataUpdateCoordinator[UpdateCoordinatorDataType]):
|
||||
"""FritzBoxTools class."""
|
||||
|
||||
config_entry: FritzConfigEntry
|
||||
connection: FritzConnectionCached
|
||||
fritz_guest_wifi: FritzGuestWLAN
|
||||
fritz_hosts: FritzHosts
|
||||
fritz_status: FritzStatus
|
||||
fritz_call: FritzCall
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
@@ -118,11 +165,6 @@ class FritzBoxTools(DataUpdateCoordinator[UpdateCoordinatorDataType]):
|
||||
self._devices: dict[str, FritzDevice] = {}
|
||||
self._options: Mapping[str, Any] | None = None
|
||||
self._unique_id: str | None = None
|
||||
self.connection: FritzConnection = None
|
||||
self.fritz_guest_wifi: FritzGuestWLAN = None
|
||||
self.fritz_hosts: FritzHosts = None
|
||||
self.fritz_status: FritzStatus = None
|
||||
self.fritz_call: FritzCall = None
|
||||
self.host = host
|
||||
self.mesh_role = MeshRoles.NONE
|
||||
self.mesh_wifi_uplink = False
|
||||
@@ -159,11 +201,12 @@ class FritzBoxTools(DataUpdateCoordinator[UpdateCoordinatorDataType]):
|
||||
name=self.config_entry.title,
|
||||
sw_version=self.current_firmware,
|
||||
)
|
||||
self.connection.clear_cache()
|
||||
|
||||
def setup(self) -> None:
|
||||
"""Set up FritzboxTools class."""
|
||||
|
||||
self.connection = FritzConnection(
|
||||
self.connection = FritzConnectionCached(
|
||||
address=self.host,
|
||||
port=self.port,
|
||||
user=self.username,
|
||||
@@ -263,6 +306,7 @@ class FritzBoxTools(DataUpdateCoordinator[UpdateCoordinatorDataType]):
|
||||
"call_deflections": {},
|
||||
"entity_states": {},
|
||||
}
|
||||
self.connection.clear_cache()
|
||||
try:
|
||||
await self.async_update_device_info()
|
||||
|
||||
|
||||
@@ -58,6 +58,10 @@ class FritzConnectionMock:
|
||||
"""Overrire services data."""
|
||||
self._services = services
|
||||
|
||||
def clear_cache(self) -> None:
|
||||
"""Mock clear_cache method."""
|
||||
return
|
||||
|
||||
def _call_action(self, service: str, action: str, **kwargs):
|
||||
LOGGER.debug(
|
||||
"_call_action service: %s, action: %s, **kwargs: %s",
|
||||
@@ -89,7 +93,8 @@ def fc_data_mock():
|
||||
def fc_class_mock(fc_data):
|
||||
"""Fixture that sets up a mocked FritzConnection class."""
|
||||
with patch(
|
||||
"homeassistant.components.fritz.coordinator.FritzConnection", autospec=True
|
||||
"homeassistant.components.fritz.coordinator.FritzConnectionCached",
|
||||
autospec=True,
|
||||
) as result:
|
||||
result.return_value = FritzConnectionMock(fc_data)
|
||||
yield result
|
||||
|
||||
@@ -75,7 +75,7 @@ async def test_setup_auth_fail(hass: HomeAssistant, error) -> None:
|
||||
entry.add_to_hass(hass)
|
||||
|
||||
with patch(
|
||||
"homeassistant.components.fritz.coordinator.FritzConnection",
|
||||
"homeassistant.components.fritz.coordinator.FritzConnectionCached",
|
||||
side_effect=error,
|
||||
):
|
||||
await hass.config_entries.async_setup(entry.entry_id)
|
||||
@@ -95,7 +95,7 @@ async def test_setup_fail(hass: HomeAssistant, error) -> None:
|
||||
entry.add_to_hass(hass)
|
||||
|
||||
with patch(
|
||||
"homeassistant.components.fritz.coordinator.FritzConnection",
|
||||
"homeassistant.components.fritz.coordinator.FritzConnectionCached",
|
||||
side_effect=error,
|
||||
):
|
||||
await hass.config_entries.async_setup(entry.entry_id)
|
||||
|
||||
Reference in New Issue
Block a user