Skip to content

Commit

Permalink
Store Switcher runtime data in config entry (#118054)
Browse files Browse the repository at this point in the history
  • Loading branch information
thecode authored May 25, 2024
1 parent 3f76b86 commit e8226a8
Show file tree
Hide file tree
Showing 9 changed files with 42 additions and 73 deletions.
42 changes: 19 additions & 23 deletions homeassistant/components/switcher_kis/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,14 @@

import logging

from aioswitcher.bridge import SwitcherBridge
from aioswitcher.device import SwitcherBase

from homeassistant.config_entries import ConfigEntry
from homeassistant.const import EVENT_HOMEASSISTANT_STOP, Platform
from homeassistant.core import Event, HomeAssistant, callback

from .const import DATA_DEVICE, DOMAIN
from .coordinator import SwitcherDataUpdateCoordinator
from .utils import async_start_bridge, async_stop_bridge

PLATFORMS = [
Platform.BUTTON,
Expand All @@ -25,20 +24,20 @@
_LOGGER = logging.getLogger(__name__)


async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
type SwitcherConfigEntry = ConfigEntry[dict[str, SwitcherDataUpdateCoordinator]]


async def async_setup_entry(hass: HomeAssistant, entry: SwitcherConfigEntry) -> bool:
"""Set up Switcher from a config entry."""
hass.data.setdefault(DOMAIN, {})
hass.data[DOMAIN][DATA_DEVICE] = {}

@callback
def on_device_data_callback(device: SwitcherBase) -> None:
"""Use as a callback for device data."""

coordinators = entry.runtime_data

# Existing device update device data
if device.device_id in hass.data[DOMAIN][DATA_DEVICE]:
coordinator: SwitcherDataUpdateCoordinator = hass.data[DOMAIN][DATA_DEVICE][
device.device_id
]
if coordinator := coordinators.get(device.device_id):
coordinator.async_set_updated_data(device)
return

Expand All @@ -52,18 +51,21 @@ def on_device_data_callback(device: SwitcherBase) -> None:
device.device_type.hex_rep,
)

coordinator = hass.data[DOMAIN][DATA_DEVICE][device.device_id] = (
SwitcherDataUpdateCoordinator(hass, entry, device)
)
coordinator = SwitcherDataUpdateCoordinator(hass, entry, device)
coordinator.async_setup()
coordinators[device.device_id] = coordinator

# Must be ready before dispatcher is called
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)

await async_start_bridge(hass, on_device_data_callback)
entry.runtime_data = {}
bridge = SwitcherBridge(on_device_data_callback)
await bridge.start()

async def stop_bridge(event: Event) -> None:
await async_stop_bridge(hass)
async def stop_bridge(event: Event | None = None) -> None:
await bridge.stop()

entry.async_on_unload(stop_bridge)

entry.async_on_unload(
hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, stop_bridge)
Expand All @@ -72,12 +74,6 @@ async def stop_bridge(event: Event) -> None:
return True


async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
async def async_unload_entry(hass: HomeAssistant, entry: SwitcherConfigEntry) -> bool:
"""Unload a config entry."""
await async_stop_bridge(hass)

unload_ok = await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
if unload_ok:
hass.data[DOMAIN].pop(DATA_DEVICE)

return unload_ok
return await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
4 changes: 2 additions & 2 deletions homeassistant/components/switcher_kis/button.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@
from aioswitcher.device import DeviceCategory

from homeassistant.components.button import ButtonEntity, ButtonEntityDescription
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import EntityCategory
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import HomeAssistantError
Expand All @@ -25,6 +24,7 @@
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.update_coordinator import CoordinatorEntity

from . import SwitcherConfigEntry
from .const import SIGNAL_DEVICE_ADD
from .coordinator import SwitcherDataUpdateCoordinator
from .utils import get_breeze_remote_manager
Expand Down Expand Up @@ -78,7 +78,7 @@ class SwitcherThermostatButtonEntityDescription(ButtonEntityDescription):

async def async_setup_entry(
hass: HomeAssistant,
config_entry: ConfigEntry,
config_entry: SwitcherConfigEntry,
async_add_entities: AddEntitiesCallback,
) -> None:
"""Set up Switcher button from config entry."""
Expand Down
4 changes: 2 additions & 2 deletions homeassistant/components/switcher_kis/climate.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@
ClimateEntityFeature,
HVACMode,
)
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import ATTR_TEMPERATURE, UnitOfTemperature
from homeassistant.core import HomeAssistant, callback
from homeassistant.exceptions import HomeAssistantError
Expand All @@ -35,6 +34,7 @@
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.update_coordinator import CoordinatorEntity

from . import SwitcherConfigEntry
from .const import SIGNAL_DEVICE_ADD
from .coordinator import SwitcherDataUpdateCoordinator
from .utils import get_breeze_remote_manager
Expand All @@ -61,7 +61,7 @@

async def async_setup_entry(
hass: HomeAssistant,
config_entry: ConfigEntry,
config_entry: SwitcherConfigEntry,
async_add_entities: AddEntitiesCallback,
) -> None:
"""Set up Switcher climate from config entry."""
Expand Down
3 changes: 0 additions & 3 deletions homeassistant/components/switcher_kis/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,6 @@

DOMAIN = "switcher_kis"

DATA_BRIDGE = "bridge"
DATA_DEVICE = "device"

DISCOVERY_TIME_SEC = 12

SIGNAL_DEVICE_ADD = "switcher_device_add"
Expand Down
9 changes: 4 additions & 5 deletions homeassistant/components/switcher_kis/diagnostics.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,24 +6,23 @@
from typing import Any

from homeassistant.components.diagnostics import async_redact_data
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant

from .const import DATA_DEVICE, DOMAIN
from . import SwitcherConfigEntry

TO_REDACT = {"device_id", "device_key", "ip_address", "mac_address"}


async def async_get_config_entry_diagnostics(
hass: HomeAssistant, entry: ConfigEntry
hass: HomeAssistant, entry: SwitcherConfigEntry
) -> dict[str, Any]:
"""Return diagnostics for a config entry."""
devices = hass.data[DOMAIN][DATA_DEVICE]
coordinators = entry.runtime_data

return async_redact_data(
{
"entry": entry.as_dict(),
"devices": [asdict(devices[d].data) for d in devices],
"devices": [asdict(coordinators[d].data) for d in coordinators],
},
TO_REDACT,
)
22 changes: 1 addition & 21 deletions homeassistant/components/switcher_kis/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,39 +3,19 @@
from __future__ import annotations

import asyncio
from collections.abc import Callable
import logging
from typing import Any

from aioswitcher.api.remotes import SwitcherBreezeRemoteManager
from aioswitcher.bridge import SwitcherBase, SwitcherBridge

from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers import singleton

from .const import DATA_BRIDGE, DISCOVERY_TIME_SEC, DOMAIN
from .const import DISCOVERY_TIME_SEC

_LOGGER = logging.getLogger(__name__)


async def async_start_bridge(
hass: HomeAssistant, on_device_callback: Callable[[SwitcherBase], Any]
) -> None:
"""Start switcher UDP bridge."""
bridge = hass.data[DOMAIN][DATA_BRIDGE] = SwitcherBridge(on_device_callback)
_LOGGER.debug("Starting Switcher bridge")
await bridge.start()


async def async_stop_bridge(hass: HomeAssistant) -> None:
"""Stop switcher UDP bridge."""
bridge: SwitcherBridge = hass.data[DOMAIN].get(DATA_BRIDGE)
if bridge is not None:
_LOGGER.debug("Stopping Switcher bridge")
await bridge.stop()
hass.data[DOMAIN].pop(DATA_BRIDGE)


async def async_has_devices(hass: HomeAssistant) -> bool:
"""Discover Switcher devices."""
_LOGGER.debug("Starting discovery")
Expand Down
12 changes: 9 additions & 3 deletions tests/components/switcher_kis/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,15 @@ def mock_setup_entry() -> Generator[AsyncMock, None, None]:
@pytest.fixture
def mock_bridge(request):
"""Return a mocked SwitcherBridge."""
with patch(
"homeassistant.components.switcher_kis.utils.SwitcherBridge", autospec=True
) as bridge_mock:
with (
patch(
"homeassistant.components.switcher_kis.SwitcherBridge", autospec=True
) as bridge_mock,
patch(
"homeassistant.components.switcher_kis.utils.SwitcherBridge",
new=bridge_mock,
),
):
bridge = bridge_mock.return_value

bridge.devices = []
Expand Down
13 changes: 3 additions & 10 deletions tests/components/switcher_kis/test_init.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,7 @@

import pytest

from homeassistant.components.switcher_kis.const import (
DATA_DEVICE,
DOMAIN,
MAX_UPDATE_INTERVAL_SEC,
)
from homeassistant.components.switcher_kis.const import MAX_UPDATE_INTERVAL_SEC
from homeassistant.config_entries import ConfigEntryState
from homeassistant.const import STATE_UNAVAILABLE
from homeassistant.core import HomeAssistant
Expand All @@ -24,15 +20,14 @@ async def test_update_fail(
hass: HomeAssistant, mock_bridge, caplog: pytest.LogCaptureFixture
) -> None:
"""Test entities state unavailable when updates fail.."""
await init_integration(hass)
entry = await init_integration(hass)
assert mock_bridge

mock_bridge.mock_callbacks(DUMMY_SWITCHER_DEVICES)
await hass.async_block_till_done()

assert mock_bridge.is_running is True
assert len(hass.data[DOMAIN]) == 2
assert len(hass.data[DOMAIN][DATA_DEVICE]) == 2
assert len(entry.runtime_data) == 2

async_fire_time_changed(
hass, dt_util.utcnow() + timedelta(seconds=MAX_UPDATE_INTERVAL_SEC + 1)
Expand Down Expand Up @@ -77,11 +72,9 @@ async def test_entry_unload(hass: HomeAssistant, mock_bridge) -> None:

assert entry.state is ConfigEntryState.LOADED
assert mock_bridge.is_running is True
assert len(hass.data[DOMAIN]) == 2

await hass.config_entries.async_unload(entry.entry_id)
await hass.async_block_till_done()

assert entry.state is ConfigEntryState.NOT_LOADED
assert mock_bridge.is_running is False
assert len(hass.data[DOMAIN]) == 0
6 changes: 2 additions & 4 deletions tests/components/switcher_kis/test_sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

import pytest

from homeassistant.components.switcher_kis.const import DATA_DEVICE, DOMAIN
from homeassistant.core import HomeAssistant
from homeassistant.helpers import entity_registry as er
from homeassistant.util import slugify
Expand Down Expand Up @@ -32,12 +31,11 @@
@pytest.mark.parametrize("mock_bridge", [DUMMY_SWITCHER_DEVICES], indirect=True)
async def test_sensor_platform(hass: HomeAssistant, mock_bridge) -> None:
"""Test sensor platform."""
await init_integration(hass)
entry = await init_integration(hass)
assert mock_bridge

assert mock_bridge.is_running is True
assert len(hass.data[DOMAIN]) == 2
assert len(hass.data[DOMAIN][DATA_DEVICE]) == 2
assert len(entry.runtime_data) == 2

for device, sensors in DEVICE_SENSORS_TUPLE:
for sensor, field in sensors:
Expand Down

0 comments on commit e8226a8

Please sign in to comment.