Add coordinator to Flexit bacnet (#108295)
* Adds coordinator and base entity class * Patch the coordinator * Adds device property to base class And refactors accordingly * Use const instead of string * Moves _attr_has_entity_name to base entity * Argument as positional * Use device_id from init
This commit is contained in:
parent
c4f033e61c
commit
bfe21b33f0
5 changed files with 119 additions and 51 deletions
|
@ -1,17 +1,12 @@
|
|||
"""The Flexit Nordic (BACnet) integration."""
|
||||
from __future__ import annotations
|
||||
|
||||
import asyncio.exceptions
|
||||
|
||||
from flexit_bacnet import FlexitBACnet
|
||||
from flexit_bacnet.bacnet import DecodingError
|
||||
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import CONF_DEVICE_ID, CONF_IP_ADDRESS, Platform
|
||||
from homeassistant.const import CONF_DEVICE_ID, Platform
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.exceptions import ConfigEntryNotReady
|
||||
|
||||
from .const import DOMAIN
|
||||
from .coordinator import FlexitCoordinator
|
||||
|
||||
PLATFORMS: list[Platform] = [Platform.CLIMATE]
|
||||
|
||||
|
@ -19,24 +14,19 @@ PLATFORMS: list[Platform] = [Platform.CLIMATE]
|
|||
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
"""Set up Flexit Nordic (BACnet) from a config entry."""
|
||||
|
||||
device = FlexitBACnet(entry.data[CONF_IP_ADDRESS], entry.data[CONF_DEVICE_ID])
|
||||
device_id = entry.data[CONF_DEVICE_ID]
|
||||
|
||||
try:
|
||||
await device.update()
|
||||
except (asyncio.exceptions.TimeoutError, ConnectionError, DecodingError) as exc:
|
||||
raise ConfigEntryNotReady(
|
||||
f"Timeout while connecting to {entry.data[CONF_IP_ADDRESS]}"
|
||||
) from exc
|
||||
|
||||
hass.data.setdefault(DOMAIN, {})[entry.entry_id] = device
|
||||
coordinator = FlexitCoordinator(hass, device_id)
|
||||
await coordinator.async_config_entry_first_refresh()
|
||||
|
||||
hass.data.setdefault(DOMAIN, {})[entry.entry_id] = coordinator
|
||||
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
|
||||
|
||||
return True
|
||||
|
||||
|
||||
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
"""Unload a config entry."""
|
||||
"""Unload the Flexit Nordic (BACnet) config entry."""
|
||||
if unload_ok := await hass.config_entries.async_unload_platforms(entry, PLATFORMS):
|
||||
hass.data[DOMAIN].pop(entry.entry_id)
|
||||
|
||||
|
|
|
@ -6,7 +6,6 @@ from flexit_bacnet import (
|
|||
VENTILATION_MODE_AWAY,
|
||||
VENTILATION_MODE_HOME,
|
||||
VENTILATION_MODE_STOP,
|
||||
FlexitBACnet,
|
||||
)
|
||||
from flexit_bacnet.bacnet import DecodingError
|
||||
|
||||
|
@ -22,7 +21,6 @@ from homeassistant.config_entries import ConfigEntry
|
|||
from homeassistant.const import ATTR_TEMPERATURE, PRECISION_HALVES, UnitOfTemperature
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.exceptions import HomeAssistantError
|
||||
from homeassistant.helpers.device_registry import DeviceInfo
|
||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||
|
||||
from .const import (
|
||||
|
@ -32,6 +30,8 @@ from .const import (
|
|||
PRESET_TO_VENTILATION_MODE_MAP,
|
||||
VENTILATION_TO_PRESET_MODE_MAP,
|
||||
)
|
||||
from .coordinator import FlexitCoordinator
|
||||
from .entity import FlexitEntity
|
||||
|
||||
|
||||
async def async_setup_entry(
|
||||
|
@ -40,18 +40,16 @@ async def async_setup_entry(
|
|||
async_add_devices: AddEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up the Flexit Nordic unit."""
|
||||
device = hass.data[DOMAIN][config_entry.entry_id]
|
||||
coordinator: FlexitCoordinator = hass.data[DOMAIN][config_entry.entry_id]
|
||||
|
||||
async_add_devices([FlexitClimateEntity(device)])
|
||||
async_add_devices([FlexitClimateEntity(coordinator)])
|
||||
|
||||
|
||||
class FlexitClimateEntity(ClimateEntity):
|
||||
class FlexitClimateEntity(FlexitEntity, ClimateEntity):
|
||||
"""Flexit air handling unit."""
|
||||
|
||||
_attr_name = None
|
||||
|
||||
_attr_has_entity_name = True
|
||||
|
||||
_attr_hvac_modes = [
|
||||
HVACMode.OFF,
|
||||
HVACMode.FAN_ONLY,
|
||||
|
@ -72,36 +70,27 @@ class FlexitClimateEntity(ClimateEntity):
|
|||
_attr_max_temp = MAX_TEMP
|
||||
_attr_min_temp = MIN_TEMP
|
||||
|
||||
def __init__(self, device: FlexitBACnet) -> None:
|
||||
"""Initialize the unit."""
|
||||
self._device = device
|
||||
self._attr_unique_id = device.serial_number
|
||||
self._attr_device_info = DeviceInfo(
|
||||
identifiers={
|
||||
(DOMAIN, device.serial_number),
|
||||
},
|
||||
name=device.device_name,
|
||||
manufacturer="Flexit",
|
||||
model="Nordic",
|
||||
serial_number=device.serial_number,
|
||||
)
|
||||
def __init__(self, coordinator: FlexitCoordinator) -> None:
|
||||
"""Initialize the Flexit unit."""
|
||||
super().__init__(coordinator)
|
||||
self._attr_unique_id = coordinator.device.serial_number
|
||||
|
||||
async def async_update(self) -> None:
|
||||
"""Refresh unit state."""
|
||||
await self._device.update()
|
||||
await self.device.update()
|
||||
|
||||
@property
|
||||
def current_temperature(self) -> float:
|
||||
"""Return the current temperature."""
|
||||
return self._device.room_temperature
|
||||
return self.device.room_temperature
|
||||
|
||||
@property
|
||||
def target_temperature(self) -> float:
|
||||
"""Return the temperature we try to reach."""
|
||||
if self._device.ventilation_mode == VENTILATION_MODE_AWAY:
|
||||
return self._device.air_temp_setpoint_away
|
||||
if self.device.ventilation_mode == VENTILATION_MODE_AWAY:
|
||||
return self.device.air_temp_setpoint_away
|
||||
|
||||
return self._device.air_temp_setpoint_home
|
||||
return self.device.air_temp_setpoint_home
|
||||
|
||||
async def async_set_temperature(self, **kwargs: Any) -> None:
|
||||
"""Set new target temperature."""
|
||||
|
@ -109,12 +98,14 @@ class FlexitClimateEntity(ClimateEntity):
|
|||
return
|
||||
|
||||
try:
|
||||
if self._device.ventilation_mode == VENTILATION_MODE_AWAY:
|
||||
await self._device.set_air_temp_setpoint_away(temperature)
|
||||
if self.device.ventilation_mode == VENTILATION_MODE_AWAY:
|
||||
await self.device.set_air_temp_setpoint_away(temperature)
|
||||
else:
|
||||
await self._device.set_air_temp_setpoint_home(temperature)
|
||||
await self.device.set_air_temp_setpoint_home(temperature)
|
||||
except (asyncio.exceptions.TimeoutError, ConnectionError, DecodingError) as exc:
|
||||
raise HomeAssistantError from exc
|
||||
finally:
|
||||
await self.coordinator.async_refresh()
|
||||
|
||||
@property
|
||||
def preset_mode(self) -> str:
|
||||
|
@ -122,21 +113,23 @@ class FlexitClimateEntity(ClimateEntity):
|
|||
|
||||
Requires ClimateEntityFeature.PRESET_MODE.
|
||||
"""
|
||||
return VENTILATION_TO_PRESET_MODE_MAP[self._device.ventilation_mode]
|
||||
return VENTILATION_TO_PRESET_MODE_MAP[self.device.ventilation_mode]
|
||||
|
||||
async def async_set_preset_mode(self, preset_mode: str) -> None:
|
||||
"""Set new preset mode."""
|
||||
ventilation_mode = PRESET_TO_VENTILATION_MODE_MAP[preset_mode]
|
||||
|
||||
try:
|
||||
await self._device.set_ventilation_mode(ventilation_mode)
|
||||
await self.device.set_ventilation_mode(ventilation_mode)
|
||||
except (asyncio.exceptions.TimeoutError, ConnectionError, DecodingError) as exc:
|
||||
raise HomeAssistantError from exc
|
||||
finally:
|
||||
await self.coordinator.async_refresh()
|
||||
|
||||
@property
|
||||
def hvac_mode(self) -> HVACMode:
|
||||
"""Return hvac operation ie. heat, cool mode."""
|
||||
if self._device.ventilation_mode == VENTILATION_MODE_STOP:
|
||||
if self.device.ventilation_mode == VENTILATION_MODE_STOP:
|
||||
return HVACMode.OFF
|
||||
|
||||
return HVACMode.FAN_ONLY
|
||||
|
@ -145,8 +138,10 @@ class FlexitClimateEntity(ClimateEntity):
|
|||
"""Set new target hvac mode."""
|
||||
try:
|
||||
if hvac_mode == HVACMode.OFF:
|
||||
await self._device.set_ventilation_mode(VENTILATION_MODE_STOP)
|
||||
await self.device.set_ventilation_mode(VENTILATION_MODE_STOP)
|
||||
else:
|
||||
await self._device.set_ventilation_mode(VENTILATION_MODE_HOME)
|
||||
await self.device.set_ventilation_mode(VENTILATION_MODE_HOME)
|
||||
except (asyncio.exceptions.TimeoutError, ConnectionError, DecodingError) as exc:
|
||||
raise HomeAssistantError from exc
|
||||
finally:
|
||||
await self.coordinator.async_refresh()
|
||||
|
|
49
homeassistant/components/flexit_bacnet/coordinator.py
Normal file
49
homeassistant/components/flexit_bacnet/coordinator.py
Normal file
|
@ -0,0 +1,49 @@
|
|||
"""DataUpdateCoordinator for Flexit Nordic (BACnet) integration.."""
|
||||
import asyncio.exceptions
|
||||
from datetime import timedelta
|
||||
import logging
|
||||
|
||||
from flexit_bacnet import FlexitBACnet
|
||||
from flexit_bacnet.bacnet import DecodingError
|
||||
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import CONF_DEVICE_ID, CONF_IP_ADDRESS
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.exceptions import ConfigEntryNotReady
|
||||
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator
|
||||
|
||||
from .const import DOMAIN
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class FlexitCoordinator(DataUpdateCoordinator[FlexitBACnet]):
|
||||
"""Class to manage fetching data from a Flexit Nordic (BACnet) device."""
|
||||
|
||||
config_entry: ConfigEntry
|
||||
|
||||
def __init__(self, hass: HomeAssistant, device_id: str) -> None:
|
||||
"""Initialize my coordinator."""
|
||||
super().__init__(
|
||||
hass,
|
||||
_LOGGER,
|
||||
name=f"{DOMAIN}_{device_id}",
|
||||
update_interval=timedelta(seconds=60),
|
||||
)
|
||||
|
||||
self.device = FlexitBACnet(
|
||||
self.config_entry.data[CONF_IP_ADDRESS],
|
||||
self.config_entry.data[CONF_DEVICE_ID],
|
||||
)
|
||||
|
||||
async def _async_update_data(self) -> FlexitBACnet:
|
||||
"""Fetch data from the device."""
|
||||
|
||||
try:
|
||||
await self.device.update()
|
||||
except (asyncio.exceptions.TimeoutError, ConnectionError, DecodingError) as exc:
|
||||
raise ConfigEntryNotReady(
|
||||
f"Timeout while connecting to {self.config_entry.data[CONF_IP_ADDRESS]}"
|
||||
) from exc
|
||||
|
||||
return self.device
|
34
homeassistant/components/flexit_bacnet/entity.py
Normal file
34
homeassistant/components/flexit_bacnet/entity.py
Normal file
|
@ -0,0 +1,34 @@
|
|||
"""Base entity for the Flexit Nordic (BACnet) integration."""
|
||||
from __future__ import annotations
|
||||
|
||||
from flexit_bacnet import FlexitBACnet
|
||||
|
||||
from homeassistant.helpers.device_registry import DeviceInfo
|
||||
from homeassistant.helpers.update_coordinator import CoordinatorEntity
|
||||
|
||||
from .const import DOMAIN
|
||||
from .coordinator import FlexitCoordinator
|
||||
|
||||
|
||||
class FlexitEntity(CoordinatorEntity[FlexitCoordinator]):
|
||||
"""Defines a Flexit entity."""
|
||||
|
||||
_attr_has_entity_name = True
|
||||
|
||||
def __init__(self, coordinator: FlexitCoordinator) -> None:
|
||||
"""Initialize a Flexit Nordic (BACnet) entity."""
|
||||
super().__init__(coordinator)
|
||||
self._attr_device_info = DeviceInfo(
|
||||
identifiers={
|
||||
(DOMAIN, coordinator.device.serial_number),
|
||||
},
|
||||
name=coordinator.device.device_name,
|
||||
manufacturer="Flexit",
|
||||
model="Nordic",
|
||||
serial_number=coordinator.device.serial_number,
|
||||
)
|
||||
|
||||
@property
|
||||
def device(self) -> FlexitBACnet:
|
||||
"""Return the device."""
|
||||
return self.coordinator.data
|
|
@ -35,7 +35,7 @@ def mock_flexit_bacnet() -> Generator[AsyncMock, None, None]:
|
|||
"homeassistant.components.flexit_bacnet.config_flow.FlexitBACnet",
|
||||
return_value=flexit_bacnet,
|
||||
), patch(
|
||||
"homeassistant.components.flexit_bacnet.FlexitBACnet",
|
||||
"homeassistant.components.flexit_bacnet.coordinator.FlexitBACnet",
|
||||
return_value=flexit_bacnet,
|
||||
):
|
||||
flexit_bacnet.serial_number = "0000-0001"
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue