Use runtime_data in gree (#144880)

This commit is contained in:
epenet 2025-05-14 14:06:40 +02:00 committed by GitHub
parent 67b9904740
commit 710e18f399
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 40 additions and 58 deletions

View File

@ -1,33 +1,29 @@
"""The Gree Climate integration.""" """The Gree Climate integration."""
from __future__ import annotations
from datetime import timedelta from datetime import timedelta
import logging import logging
from homeassistant.components.network import async_get_ipv4_broadcast_addresses from homeassistant.components.network import async_get_ipv4_broadcast_addresses
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import Platform from homeassistant.const import Platform
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.helpers.event import async_track_time_interval from homeassistant.helpers.event import async_track_time_interval
from .const import ( from .const import DISCOVERY_SCAN_INTERVAL
COORDINATORS, from .coordinator import DiscoveryService, GreeConfigEntry, GreeRuntimeData
DATA_DISCOVERY_SERVICE,
DISCOVERY_SCAN_INTERVAL,
DISPATCHERS,
DOMAIN,
)
from .coordinator import DiscoveryService
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
PLATFORMS = [Platform.CLIMATE, Platform.SWITCH] PLATFORMS = [Platform.CLIMATE, Platform.SWITCH]
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: async def async_setup_entry(hass: HomeAssistant, entry: GreeConfigEntry) -> bool:
"""Set up Gree Climate from a config entry.""" """Set up Gree Climate from a config entry."""
hass.data.setdefault(DOMAIN, {})
gree_discovery = DiscoveryService(hass, entry) gree_discovery = DiscoveryService(hass, entry)
hass.data[DATA_DISCOVERY_SERVICE] = gree_discovery entry.runtime_data = GreeRuntimeData(
discovery_service=gree_discovery, coordinators=[]
)
async def _async_scan_update(_=None): async def _async_scan_update(_=None):
bcast_addr = list(await async_get_ipv4_broadcast_addresses(hass)) bcast_addr = list(await async_get_ipv4_broadcast_addresses(hass))
@ -47,15 +43,6 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
return True return True
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: async def async_unload_entry(hass: HomeAssistant, entry: GreeConfigEntry) -> bool:
"""Unload a config entry.""" """Unload a config entry."""
if hass.data.get(DATA_DISCOVERY_SERVICE) is not None: return await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
hass.data.pop(DATA_DISCOVERY_SERVICE)
unload_ok = await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
if unload_ok:
hass.data[DOMAIN].pop(COORDINATORS, None)
hass.data[DOMAIN].pop(DISPATCHERS, None)
return unload_ok

View File

@ -36,21 +36,18 @@ from homeassistant.components.climate import (
ClimateEntityFeature, ClimateEntityFeature,
HVACMode, HVACMode,
) )
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import ATTR_TEMPERATURE, PRECISION_WHOLE, UnitOfTemperature from homeassistant.const import ATTR_TEMPERATURE, PRECISION_WHOLE, UnitOfTemperature
from homeassistant.core import HomeAssistant, callback from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers.dispatcher import async_dispatcher_connect from homeassistant.helpers.dispatcher import async_dispatcher_connect
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
from .const import ( from .const import (
COORDINATORS,
DISPATCH_DEVICE_DISCOVERED, DISPATCH_DEVICE_DISCOVERED,
DOMAIN,
FAN_MEDIUM_HIGH, FAN_MEDIUM_HIGH,
FAN_MEDIUM_LOW, FAN_MEDIUM_LOW,
TARGET_TEMPERATURE_STEP, TARGET_TEMPERATURE_STEP,
) )
from .coordinator import DeviceDataUpdateCoordinator from .coordinator import DeviceDataUpdateCoordinator, GreeConfigEntry
from .entity import GreeEntity from .entity import GreeEntity
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
@ -87,17 +84,17 @@ SWING_MODES = [SWING_OFF, SWING_VERTICAL, SWING_HORIZONTAL, SWING_BOTH]
async def async_setup_entry( async def async_setup_entry(
hass: HomeAssistant, hass: HomeAssistant,
entry: ConfigEntry, entry: GreeConfigEntry,
async_add_entities: AddConfigEntryEntitiesCallback, async_add_entities: AddConfigEntryEntitiesCallback,
) -> None: ) -> None:
"""Set up the Gree HVAC device from a config entry.""" """Set up the Gree HVAC device from a config entry."""
@callback @callback
def init_device(coordinator): def init_device(coordinator: DeviceDataUpdateCoordinator) -> None:
"""Register the device.""" """Register the device."""
async_add_entities([GreeClimateEntity(coordinator)]) async_add_entities([GreeClimateEntity(coordinator)])
for coordinator in hass.data[DOMAIN][COORDINATORS]: for coordinator in entry.runtime_data.coordinators:
init_device(coordinator) init_device(coordinator)
entry.async_on_unload( entry.async_on_unload(

View File

@ -1,16 +1,10 @@
"""Constants for the Gree Climate integration.""" """Constants for the Gree Climate integration."""
COORDINATORS = "coordinators"
DATA_DISCOVERY_SERVICE = "gree_discovery"
DISCOVERY_SCAN_INTERVAL = 300 DISCOVERY_SCAN_INTERVAL = 300
DISCOVERY_TIMEOUT = 8 DISCOVERY_TIMEOUT = 8
DISPATCH_DEVICE_DISCOVERED = "gree_device_discovered" DISPATCH_DEVICE_DISCOVERED = "gree_device_discovered"
DISPATCHERS = "dispatchers"
DOMAIN = "gree" DOMAIN = "gree"
COORDINATOR = "coordinator"
FAN_MEDIUM_LOW = "medium low" FAN_MEDIUM_LOW = "medium low"
FAN_MEDIUM_HIGH = "medium high" FAN_MEDIUM_HIGH = "medium high"

View File

@ -3,6 +3,7 @@
from __future__ import annotations from __future__ import annotations
import copy import copy
from dataclasses import dataclass
from datetime import datetime, timedelta from datetime import datetime, timedelta
import logging import logging
from typing import Any from typing import Any
@ -20,7 +21,6 @@ from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, Upda
from homeassistant.util.dt import utcnow from homeassistant.util.dt import utcnow
from .const import ( from .const import (
COORDINATORS,
DISCOVERY_TIMEOUT, DISCOVERY_TIMEOUT,
DISPATCH_DEVICE_DISCOVERED, DISPATCH_DEVICE_DISCOVERED,
DOMAIN, DOMAIN,
@ -31,14 +31,24 @@ from .const import (
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
type GreeConfigEntry = ConfigEntry[GreeRuntimeData]
@dataclass
class GreeRuntimeData:
"""RUntime data for Gree Climate integration."""
discovery_service: DiscoveryService
coordinators: list[DeviceDataUpdateCoordinator]
class DeviceDataUpdateCoordinator(DataUpdateCoordinator[dict[str, Any]]): class DeviceDataUpdateCoordinator(DataUpdateCoordinator[dict[str, Any]]):
"""Manages polling for state changes from the device.""" """Manages polling for state changes from the device."""
config_entry: ConfigEntry config_entry: GreeConfigEntry
def __init__( def __init__(
self, hass: HomeAssistant, config_entry: ConfigEntry, device: Device self, hass: HomeAssistant, config_entry: GreeConfigEntry, device: Device
) -> None: ) -> None:
"""Initialize the data update coordinator.""" """Initialize the data update coordinator."""
super().__init__( super().__init__(
@ -128,7 +138,7 @@ class DeviceDataUpdateCoordinator(DataUpdateCoordinator[dict[str, Any]]):
class DiscoveryService(Listener): class DiscoveryService(Listener):
"""Discovery event handler for gree devices.""" """Discovery event handler for gree devices."""
def __init__(self, hass: HomeAssistant, entry: ConfigEntry) -> None: def __init__(self, hass: HomeAssistant, entry: GreeConfigEntry) -> None:
"""Initialize discovery service.""" """Initialize discovery service."""
super().__init__() super().__init__()
self.hass = hass self.hass = hass
@ -137,8 +147,6 @@ class DiscoveryService(Listener):
self.discovery = Discovery(DISCOVERY_TIMEOUT) self.discovery = Discovery(DISCOVERY_TIMEOUT)
self.discovery.add_listener(self) self.discovery.add_listener(self)
hass.data[DOMAIN].setdefault(COORDINATORS, [])
async def device_found(self, device_info: DeviceInfo) -> None: async def device_found(self, device_info: DeviceInfo) -> None:
"""Handle new device found on the network.""" """Handle new device found on the network."""
@ -157,14 +165,14 @@ class DiscoveryService(Listener):
device.device_info.port, device.device_info.port,
) )
coordo = DeviceDataUpdateCoordinator(self.hass, self.entry, device) coordo = DeviceDataUpdateCoordinator(self.hass, self.entry, device)
self.hass.data[DOMAIN][COORDINATORS].append(coordo) self.entry.runtime_data.coordinators.append(coordo)
await coordo.async_refresh() await coordo.async_refresh()
async_dispatcher_send(self.hass, DISPATCH_DEVICE_DISCOVERED, coordo) async_dispatcher_send(self.hass, DISPATCH_DEVICE_DISCOVERED, coordo)
async def device_update(self, device_info: DeviceInfo) -> None: async def device_update(self, device_info: DeviceInfo) -> None:
"""Handle updates in device information, update if ip has changed.""" """Handle updates in device information, update if ip has changed."""
for coordinator in self.hass.data[DOMAIN][COORDINATORS]: for coordinator in self.entry.runtime_data.coordinators:
if coordinator.device.device_info.mac == device_info.mac: if coordinator.device.device_info.mac == device_info.mac:
coordinator.device.device_info.ip = device_info.ip coordinator.device.device_info.ip = device_info.ip
await coordinator.async_refresh() await coordinator.async_refresh()

View File

@ -13,13 +13,13 @@ from homeassistant.components.switch import (
SwitchEntity, SwitchEntity,
SwitchEntityDescription, SwitchEntityDescription,
) )
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant, callback from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers.dispatcher import async_dispatcher_connect from homeassistant.helpers.dispatcher import async_dispatcher_connect
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
from .const import COORDINATORS, DISPATCH_DEVICE_DISCOVERED, DOMAIN from .const import DISPATCH_DEVICE_DISCOVERED
from .entity import GreeEntity from .coordinator import GreeConfigEntry
from .entity import DeviceDataUpdateCoordinator, GreeEntity
@dataclass(kw_only=True, frozen=True) @dataclass(kw_only=True, frozen=True)
@ -92,13 +92,13 @@ GREE_SWITCHES: tuple[GreeSwitchEntityDescription, ...] = (
async def async_setup_entry( async def async_setup_entry(
hass: HomeAssistant, hass: HomeAssistant,
entry: ConfigEntry, entry: GreeConfigEntry,
async_add_entities: AddConfigEntryEntitiesCallback, async_add_entities: AddConfigEntryEntitiesCallback,
) -> None: ) -> None:
"""Set up the Gree HVAC device from a config entry.""" """Set up the Gree HVAC device from a config entry."""
@callback @callback
def init_device(coordinator): def init_device(coordinator: DeviceDataUpdateCoordinator) -> None:
"""Register the device.""" """Register the device."""
async_add_entities( async_add_entities(
@ -106,7 +106,7 @@ async def async_setup_entry(
for description in GREE_SWITCHES for description in GREE_SWITCHES
) )
for coordinator in hass.data[DOMAIN][COORDINATORS]: for coordinator in entry.runtime_data.coordinators:
init_device(coordinator) init_device(coordinator)
entry.async_on_unload( entry.async_on_unload(

View File

@ -6,11 +6,7 @@ from freezegun.api import FrozenDateTimeFactory
import pytest import pytest
from homeassistant.components.climate import DOMAIN as CLIMATE_DOMAIN, HVACMode from homeassistant.components.climate import DOMAIN as CLIMATE_DOMAIN, HVACMode
from homeassistant.components.gree.const import ( from homeassistant.components.gree.const import UPDATE_INTERVAL
COORDINATORS,
DOMAIN as GREE,
UPDATE_INTERVAL,
)
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.util import dt as dt_util from homeassistant.util import dt as dt_util
@ -42,13 +38,13 @@ async def test_discovery_after_setup(
discovery.return_value.mock_devices = [mock_device_1, mock_device_2] discovery.return_value.mock_devices = [mock_device_1, mock_device_2]
device.side_effect = [mock_device_1, mock_device_2] device.side_effect = [mock_device_1, mock_device_2]
await async_setup_gree(hass) entry = await async_setup_gree(hass)
await hass.async_block_till_done() await hass.async_block_till_done()
assert discovery.return_value.scan_count == 1 assert discovery.return_value.scan_count == 1
assert len(hass.states.async_all(CLIMATE_DOMAIN)) == 2 assert len(hass.states.async_all(CLIMATE_DOMAIN)) == 2
device_infos = [x.device.device_info for x in hass.data[GREE][COORDINATORS]] device_infos = [x.device.device_info for x in entry.runtime_data.coordinators]
assert device_infos[0].ip == "1.1.1.1" assert device_infos[0].ip == "1.1.1.1"
assert device_infos[1].ip == "2.2.2.2" assert device_infos[1].ip == "2.2.2.2"
@ -70,7 +66,7 @@ async def test_discovery_after_setup(
assert discovery.return_value.scan_count == 2 assert discovery.return_value.scan_count == 2
assert len(hass.states.async_all(CLIMATE_DOMAIN)) == 2 assert len(hass.states.async_all(CLIMATE_DOMAIN)) == 2
device_infos = [x.device.device_info for x in hass.data[GREE][COORDINATORS]] device_infos = [x.device.device_info for x in entry.runtime_data.coordinators]
assert device_infos[0].ip == "1.1.1.2" assert device_infos[0].ip == "1.1.1.2"
assert device_infos[1].ip == "2.2.2.1" assert device_infos[1].ip == "2.2.2.1"