Migrate elkm1 to use a dataclass for integration data (#99830)

* Migrate elkm1 to use a dataclass for integration data

* fix unsaved

* slotted

* missing coveragerc

* Revert "missing coveragerc"

This reverts commit 3397b40309033276d20fef59098b0a1b5b681a30.
This commit is contained in:
J. Nick Koston 2023-09-08 12:07:09 -05:00 committed by GitHub
parent 3d403c9b60
commit d624bbbc0c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 77 additions and 47 deletions

View File

@ -2,11 +2,12 @@
from __future__ import annotations
import asyncio
from collections.abc import Iterable
from enum import Enum
import logging
import re
from types import MappingProxyType
from typing import Any, cast
from typing import Any
from elkm1_lib.elements import Element
from elkm1_lib.elk import Elk
@ -65,6 +66,7 @@ from .discovery import (
async_trigger_discovery,
async_update_entry_from_discovery,
)
from .models import ELKM1Data
SYNC_TIMEOUT = 120
@ -303,14 +305,16 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
else:
temperature_unit = UnitOfTemperature.FAHRENHEIT
config["temperature_unit"] = temperature_unit
hass.data[DOMAIN][entry.entry_id] = {
"elk": elk,
"prefix": conf[CONF_PREFIX],
"mac": entry.unique_id,
"auto_configure": conf[CONF_AUTO_CONFIGURE],
"config": config,
"keypads": {},
}
prefix: str = conf[CONF_PREFIX]
auto_configure: bool = conf[CONF_AUTO_CONFIGURE]
hass.data[DOMAIN][entry.entry_id] = ELKM1Data(
elk=elk,
prefix=prefix,
mac=entry.unique_id,
auto_configure=auto_configure,
config=config,
keypads={},
)
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
@ -326,21 +330,23 @@ def _included(ranges: list[tuple[int, int]], set_to: bool, values: list[bool]) -
def _find_elk_by_prefix(hass: HomeAssistant, prefix: str) -> Elk | None:
"""Search all config entries for a given prefix."""
for entry_id in hass.data[DOMAIN]:
if hass.data[DOMAIN][entry_id]["prefix"] == prefix:
return cast(Elk, hass.data[DOMAIN][entry_id]["elk"])
all_elk: dict[str, ELKM1Data] = hass.data[DOMAIN]
for elk_data in all_elk.values():
if elk_data.prefix == prefix:
return elk_data.elk
return None
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
"""Unload a config entry."""
unload_ok = await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
all_elk: dict[str, ELKM1Data] = hass.data[DOMAIN]
# disconnect cleanly
hass.data[DOMAIN][entry.entry_id]["elk"].disconnect()
all_elk[entry.entry_id].elk.disconnect()
if unload_ok:
hass.data[DOMAIN].pop(entry.entry_id)
all_elk.pop(entry.entry_id)
return unload_ok
@ -421,19 +427,19 @@ def _create_elk_services(hass: HomeAssistant) -> None:
def create_elk_entities(
elk_data: dict[str, Any],
elk_elements: list[Element],
elk_data: ELKM1Data,
elk_elements: Iterable[Element],
element_type: str,
class_: Any,
entities: list[ElkEntity],
) -> list[ElkEntity] | None:
"""Create the ElkM1 devices of a particular class."""
auto_configure = elk_data["auto_configure"]
auto_configure = elk_data.auto_configure
if not auto_configure and not elk_data["config"][element_type]["enabled"]:
if not auto_configure and not elk_data.config[element_type]["enabled"]:
return None
elk = elk_data["elk"]
elk = elk_data.elk
_LOGGER.debug("Creating elk entities for %s", elk)
for element in elk_elements:
@ -441,7 +447,7 @@ def create_elk_entities(
if not element.configured:
continue
# Only check the included list if auto configure is not
elif not elk_data["config"][element_type]["included"][element.index]:
elif not elk_data.config[element_type]["included"][element.index]:
continue
entities.append(class_(element, elk, elk_data))
@ -454,13 +460,13 @@ class ElkEntity(Entity):
_attr_has_entity_name = True
_attr_should_poll = False
def __init__(self, element: Element, elk: Elk, elk_data: dict[str, Any]) -> None:
def __init__(self, element: Element, elk: Elk, elk_data: ELKM1Data) -> None:
"""Initialize the base of all Elk devices."""
self._elk = elk
self._element = element
self._mac = elk_data["mac"]
self._prefix = elk_data["prefix"]
self._temperature_unit: str = elk_data["config"]["temperature_unit"]
self._mac = elk_data.mac
self._prefix = elk_data.prefix
self._temperature_unit: str = elk_data.config["temperature_unit"]
# unique_id starts with elkm1_ iff there is no prefix
# it starts with elkm1m_{prefix} iff there is a prefix
# this is to avoid a conflict between
@ -496,9 +502,7 @@ class ElkEntity(Entity):
def initial_attrs(self) -> dict[str, Any]:
"""Return the underlying element's attributes as a dict."""
attrs = {}
attrs["index"] = self._element.index + 1
return attrs
return {"index": self._element.index + 1}
def _element_changed(self, element: Element, changeset: dict[str, Any]) -> None:
pass

View File

@ -40,6 +40,7 @@ from .const import (
DOMAIN,
ELK_USER_CODE_SERVICE_SCHEMA,
)
from .models import ELKM1Data
DISPLAY_MESSAGE_SERVICE_SCHEMA = {
vol.Optional("clear", default=2): vol.All(vol.Coerce(int), vol.In([0, 1, 2])),
@ -65,8 +66,9 @@ async def async_setup_entry(
async_add_entities: AddEntitiesCallback,
) -> None:
"""Set up the ElkM1 alarm platform."""
elk_data = hass.data[DOMAIN][config_entry.entry_id]
elk = elk_data["elk"]
elk_data: ELKM1Data = hass.data[DOMAIN][config_entry.entry_id]
elk = elk_data.elk
entities: list[ElkEntity] = []
create_elk_entities(elk_data, elk.areas, "area", ElkArea, entities)
async_add_entities(entities)
@ -115,7 +117,7 @@ class ElkArea(ElkAttachedEntity, AlarmControlPanelEntity, RestoreEntity):
)
_element: Area
def __init__(self, element: Element, elk: Elk, elk_data: dict[str, Any]) -> None:
def __init__(self, element: Element, elk: Elk, elk_data: ELKM1Data) -> None:
"""Initialize Area as Alarm Control Panel."""
super().__init__(element, elk, elk_data)
self._elk = elk

View File

@ -14,6 +14,7 @@ from homeassistant.helpers.entity_platform import AddEntitiesCallback
from . import ElkAttachedEntity, ElkEntity
from .const import DOMAIN
from .models import ELKM1Data
async def async_setup_entry(
@ -22,21 +23,20 @@ async def async_setup_entry(
async_add_entities: AddEntitiesCallback,
) -> None:
"""Create the Elk-M1 sensor platform."""
elk_data = hass.data[DOMAIN][config_entry.entry_id]
auto_configure = elk_data["auto_configure"]
elk = elk_data["elk"]
elk_data: ELKM1Data = hass.data[DOMAIN][config_entry.entry_id]
elk = elk_data.elk
auto_configure = elk_data.auto_configure
entities: list[ElkEntity] = []
for element in elk.zones:
# Don't create binary sensors for zones that are analog
if element.definition in {ZoneType.TEMPERATURE, ZoneType.ANALOG_ZONE}:
if element.definition in {ZoneType.TEMPERATURE, ZoneType.ANALOG_ZONE}: # type: ignore[attr-defined]
continue
if auto_configure:
if not element.configured:
continue
elif not elk_data["config"]["zone"]["included"][element.index]:
elif not elk_data.config["zone"]["included"][element.index]:
continue
entities.append(ElkBinarySensor(element, elk, elk_data))

View File

@ -23,6 +23,7 @@ from homeassistant.helpers.entity_platform import AddEntitiesCallback
from . import ElkEntity, create_elk_entities
from .const import DOMAIN
from .models import ELKM1Data
SUPPORT_HVAC = [
HVACMode.OFF,
@ -61,9 +62,9 @@ async def async_setup_entry(
async_add_entities: AddEntitiesCallback,
) -> None:
"""Create the Elk-M1 thermostat platform."""
elk_data = hass.data[DOMAIN][config_entry.entry_id]
elk_data: ELKM1Data = hass.data[DOMAIN][config_entry.entry_id]
elk = elk_data.elk
entities: list[ElkEntity] = []
elk = elk_data["elk"]
create_elk_entities(
elk_data, elk.thermostats, "thermostat", ElkThermostat, entities
)

View File

@ -14,6 +14,7 @@ from homeassistant.helpers.entity_platform import AddEntitiesCallback
from . import ElkEntity, create_elk_entities
from .const import DOMAIN
from .models import ELKM1Data
async def async_setup_entry(
@ -22,9 +23,9 @@ async def async_setup_entry(
async_add_entities: AddEntitiesCallback,
) -> None:
"""Set up the Elk light platform."""
elk_data = hass.data[DOMAIN][config_entry.entry_id]
elk_data: ELKM1Data = hass.data[DOMAIN][config_entry.entry_id]
elk = elk_data.elk
entities: list[ElkEntity] = []
elk = elk_data["elk"]
create_elk_entities(elk_data, elk.lights, "plc", ElkLight, entities)
async_add_entities(entities)
@ -36,7 +37,7 @@ class ElkLight(ElkEntity, LightEntity):
_attr_supported_color_modes = {ColorMode.BRIGHTNESS}
_element: Light
def __init__(self, element: Element, elk: Elk, elk_data: dict[str, Any]) -> None:
def __init__(self, element: Element, elk: Elk, elk_data: ELKM1Data) -> None:
"""Initialize the Elk light."""
super().__init__(element, elk, elk_data)
self._brightness = self._element.status

View File

@ -0,0 +1,19 @@
"""The elkm1 integration models."""
from __future__ import annotations
from dataclasses import dataclass
from typing import Any
from elkm1_lib import Elk
@dataclass(slots=True)
class ELKM1Data:
"""Data for the elkm1 integration."""
elk: Elk
prefix: str
mac: str | None
auto_configure: bool
config: dict[str, Any]
keypads: dict[str, Any]

View File

@ -12,6 +12,7 @@ from homeassistant.helpers.entity_platform import AddEntitiesCallback
from . import ElkAttachedEntity, ElkEntity, create_elk_entities
from .const import DOMAIN
from .models import ELKM1Data
async def async_setup_entry(
@ -20,9 +21,9 @@ async def async_setup_entry(
async_add_entities: AddEntitiesCallback,
) -> None:
"""Create the Elk-M1 scene platform."""
elk_data = hass.data[DOMAIN][config_entry.entry_id]
elk_data: ELKM1Data = hass.data[DOMAIN][config_entry.entry_id]
elk = elk_data.elk
entities: list[ElkEntity] = []
elk = elk_data["elk"]
create_elk_entities(elk_data, elk.tasks, "task", ElkTask, entities)
async_add_entities(entities)

View File

@ -23,6 +23,7 @@ from homeassistant.helpers.entity_platform import AddEntitiesCallback
from . import ElkAttachedEntity, ElkEntity, create_elk_entities
from .const import ATTR_VALUE, DOMAIN, ELK_USER_CODE_SERVICE_SCHEMA
from .models import ELKM1Data
SERVICE_SENSOR_COUNTER_REFRESH = "sensor_counter_refresh"
SERVICE_SENSOR_COUNTER_SET = "sensor_counter_set"
@ -41,9 +42,9 @@ async def async_setup_entry(
async_add_entities: AddEntitiesCallback,
) -> None:
"""Create the Elk-M1 sensor platform."""
elk_data = hass.data[DOMAIN][config_entry.entry_id]
elk_data: ELKM1Data = hass.data[DOMAIN][config_entry.entry_id]
elk = elk_data.elk
entities: list[ElkEntity] = []
elk = elk_data["elk"]
create_elk_entities(elk_data, elk.counters, "counter", ElkCounter, entities)
create_elk_entities(elk_data, elk.keypads, "keypad", ElkKeypad, entities)
create_elk_entities(elk_data, [elk.panel], "panel", ElkPanel, entities)

View File

@ -12,6 +12,7 @@ from homeassistant.helpers.entity_platform import AddEntitiesCallback
from . import ElkAttachedEntity, ElkEntity, create_elk_entities
from .const import DOMAIN
from .models import ELKM1Data
async def async_setup_entry(
@ -20,9 +21,9 @@ async def async_setup_entry(
async_add_entities: AddEntitiesCallback,
) -> None:
"""Create the Elk-M1 switch platform."""
elk_data = hass.data[DOMAIN][config_entry.entry_id]
elk_data: ELKM1Data = hass.data[DOMAIN][config_entry.entry_id]
elk = elk_data.elk
entities: list[ElkEntity] = []
elk = elk_data["elk"]
create_elk_entities(elk_data, elk.outputs, "output", ElkOutput, entities)
async_add_entities(entities)