Mill data coordinator (#53603)
Co-authored-by: Paulus Schoutsen <balloob@gmail.com>
This commit is contained in:
parent
f9fa5fa804
commit
98e8e89364
6 changed files with 126 additions and 116 deletions
|
@ -1,15 +1,43 @@
|
|||
"""The mill component."""
|
||||
from datetime import timedelta
|
||||
import logging
|
||||
|
||||
from mill import Mill
|
||||
|
||||
from homeassistant.const import CONF_PASSWORD, CONF_USERNAME
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.exceptions import ConfigEntryNotReady
|
||||
from homeassistant.helpers.aiohttp_client import async_get_clientsession
|
||||
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator
|
||||
|
||||
from .const import DOMAIN
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
PLATFORMS = ["climate", "sensor"]
|
||||
|
||||
|
||||
class MillDataUpdateCoordinator(DataUpdateCoordinator):
|
||||
"""Class to manage fetching Mill data."""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
hass: HomeAssistant,
|
||||
*,
|
||||
mill_data_connection: Mill,
|
||||
) -> None:
|
||||
"""Initialize global Mill data updater."""
|
||||
self.mill_data_connection = mill_data_connection
|
||||
|
||||
super().__init__(
|
||||
hass,
|
||||
_LOGGER,
|
||||
name=DOMAIN,
|
||||
update_method=mill_data_connection.fetch_heater_data,
|
||||
update_interval=timedelta(seconds=30),
|
||||
)
|
||||
|
||||
|
||||
async def async_setup_entry(hass, entry):
|
||||
"""Set up the Mill heater."""
|
||||
mill_data_connection = Mill(
|
||||
|
@ -20,9 +48,12 @@ async def async_setup_entry(hass, entry):
|
|||
if not await mill_data_connection.connect():
|
||||
raise ConfigEntryNotReady
|
||||
|
||||
await mill_data_connection.find_all_heaters()
|
||||
hass.data[DOMAIN] = MillDataUpdateCoordinator(
|
||||
hass,
|
||||
mill_data_connection=mill_data_connection,
|
||||
)
|
||||
|
||||
hass.data[DOMAIN] = mill_data_connection
|
||||
await hass.data[DOMAIN].async_config_entry_first_refresh()
|
||||
|
||||
hass.config_entries.async_setup_platforms(entry, PLATFORMS)
|
||||
return True
|
||||
|
|
|
@ -11,8 +11,10 @@ from homeassistant.components.climate.const import (
|
|||
SUPPORT_FAN_MODE,
|
||||
SUPPORT_TARGET_TEMPERATURE,
|
||||
)
|
||||
from homeassistant.const import ATTR_TEMPERATURE, TEMP_CELSIUS
|
||||
from homeassistant.const import ATTR_TEMPERATURE, PRECISION_WHOLE, TEMP_CELSIUS
|
||||
from homeassistant.core import callback
|
||||
from homeassistant.helpers import config_validation as cv
|
||||
from homeassistant.helpers.update_coordinator import CoordinatorEntity
|
||||
|
||||
from .const import (
|
||||
ATTR_AWAY_TEMP,
|
||||
|
@ -41,11 +43,11 @@ SET_ROOM_TEMP_SCHEMA = vol.Schema(
|
|||
async def async_setup_entry(hass, entry, async_add_entities):
|
||||
"""Set up the Mill climate."""
|
||||
|
||||
mill_data_connection = hass.data[DOMAIN]
|
||||
mill_data_coordinator = hass.data[DOMAIN]
|
||||
|
||||
dev = []
|
||||
for heater in mill_data_connection.heaters.values():
|
||||
dev.append(MillHeater(heater, mill_data_connection))
|
||||
for heater in mill_data_coordinator.data.values():
|
||||
dev.append(MillHeater(mill_data_coordinator, heater))
|
||||
async_add_entities(dev)
|
||||
|
||||
async def set_room_temp(service):
|
||||
|
@ -54,7 +56,7 @@ async def async_setup_entry(hass, entry, async_add_entities):
|
|||
sleep_temp = service.data.get(ATTR_SLEEP_TEMP)
|
||||
comfort_temp = service.data.get(ATTR_COMFORT_TEMP)
|
||||
away_temp = service.data.get(ATTR_AWAY_TEMP)
|
||||
await mill_data_connection.set_room_temperatures_by_name(
|
||||
await mill_data_coordinator.mill_data_connection.set_room_temperatures_by_name(
|
||||
room_name, sleep_temp, comfort_temp, away_temp
|
||||
)
|
||||
|
||||
|
@ -63,122 +65,97 @@ async def async_setup_entry(hass, entry, async_add_entities):
|
|||
)
|
||||
|
||||
|
||||
class MillHeater(ClimateEntity):
|
||||
class MillHeater(CoordinatorEntity, ClimateEntity):
|
||||
"""Representation of a Mill Thermostat device."""
|
||||
|
||||
_attr_fan_modes = [FAN_ON, HVAC_MODE_OFF]
|
||||
_attr_max_temp = MAX_TEMP
|
||||
_attr_min_temp = MIN_TEMP
|
||||
_attr_supported_features = SUPPORT_FLAGS
|
||||
_attr_target_temperature_step = 1
|
||||
_attr_target_temperature_step = PRECISION_WHOLE
|
||||
_attr_temperature_unit = TEMP_CELSIUS
|
||||
|
||||
def __init__(self, heater, mill_data_connection):
|
||||
def __init__(self, coordinator, heater):
|
||||
"""Initialize the thermostat."""
|
||||
self._heater = heater
|
||||
self._conn = mill_data_connection
|
||||
|
||||
super().__init__(coordinator)
|
||||
|
||||
self._id = heater.device_id
|
||||
self._attr_unique_id = heater.device_id
|
||||
self._attr_name = heater.name
|
||||
|
||||
@property
|
||||
def available(self):
|
||||
"""Return True if entity is available."""
|
||||
return self._heater.available
|
||||
|
||||
@property
|
||||
def extra_state_attributes(self):
|
||||
"""Return the state attributes."""
|
||||
res = {
|
||||
"open_window": self._heater.open_window,
|
||||
"heating": self._heater.is_heating,
|
||||
"controlled_by_tibber": self._heater.tibber_control,
|
||||
"heater_generation": 1 if self._heater.is_gen1 else 2,
|
||||
self._attr_device_info = {
|
||||
"identifiers": {(DOMAIN, heater.device_id)},
|
||||
"name": self.name,
|
||||
"manufacturer": MANUFACTURER,
|
||||
"model": f"generation {1 if heater.is_gen1 else 2}",
|
||||
}
|
||||
if self._heater.room:
|
||||
res["room"] = self._heater.room.name
|
||||
res["avg_room_temp"] = self._heater.room.avg_temp
|
||||
if heater.is_gen1:
|
||||
self._attr_hvac_modes = [HVAC_MODE_HEAT]
|
||||
else:
|
||||
res["room"] = "Independent device"
|
||||
return res
|
||||
|
||||
@property
|
||||
def target_temperature(self):
|
||||
"""Return the temperature we try to reach."""
|
||||
return self._heater.set_temp
|
||||
|
||||
@property
|
||||
def current_temperature(self):
|
||||
"""Return the current temperature."""
|
||||
return self._heater.current_temp
|
||||
|
||||
@property
|
||||
def fan_mode(self):
|
||||
"""Return the fan setting."""
|
||||
return FAN_ON if self._heater.fan_status == 1 else HVAC_MODE_OFF
|
||||
|
||||
@property
|
||||
def hvac_action(self):
|
||||
"""Return current hvac i.e. heat, cool, idle."""
|
||||
if self._heater.is_gen1 or self._heater.is_heating == 1:
|
||||
return CURRENT_HVAC_HEAT
|
||||
return CURRENT_HVAC_IDLE
|
||||
|
||||
@property
|
||||
def hvac_mode(self) -> str:
|
||||
"""Return hvac operation ie. heat, cool mode.
|
||||
|
||||
Need to be one of HVAC_MODE_*.
|
||||
"""
|
||||
if self._heater.is_gen1 or self._heater.power_status == 1:
|
||||
return HVAC_MODE_HEAT
|
||||
return HVAC_MODE_OFF
|
||||
|
||||
@property
|
||||
def hvac_modes(self):
|
||||
"""Return the list of available hvac operation modes.
|
||||
|
||||
Need to be a subset of HVAC_MODES.
|
||||
"""
|
||||
if self._heater.is_gen1:
|
||||
return [HVAC_MODE_HEAT]
|
||||
return [HVAC_MODE_HEAT, HVAC_MODE_OFF]
|
||||
self._attr_hvac_modes = [HVAC_MODE_HEAT, HVAC_MODE_OFF]
|
||||
self._update_attr(heater)
|
||||
|
||||
async def async_set_temperature(self, **kwargs):
|
||||
"""Set new target temperature."""
|
||||
temperature = kwargs.get(ATTR_TEMPERATURE)
|
||||
if temperature is None:
|
||||
return
|
||||
await self._conn.set_heater_temp(self._heater.device_id, int(temperature))
|
||||
await self.coordinator.mill_data_connection.set_heater_temp(
|
||||
self._id, int(temperature)
|
||||
)
|
||||
await self.coordinator.async_request_refresh()
|
||||
|
||||
async def async_set_fan_mode(self, fan_mode):
|
||||
"""Set new target fan mode."""
|
||||
fan_status = 1 if fan_mode == FAN_ON else 0
|
||||
await self._conn.heater_control(self._heater.device_id, fan_status=fan_status)
|
||||
await self.coordinator.mill_data_connection.heater_control(
|
||||
self._id, fan_status=fan_status
|
||||
)
|
||||
await self.coordinator.async_request_refresh()
|
||||
|
||||
async def async_set_hvac_mode(self, hvac_mode):
|
||||
"""Set new target hvac mode."""
|
||||
heater = self.coordinator.data[self._id]
|
||||
|
||||
if hvac_mode == HVAC_MODE_HEAT:
|
||||
await self._conn.heater_control(self._heater.device_id, power_status=1)
|
||||
elif hvac_mode == HVAC_MODE_OFF and not self._heater.is_gen1:
|
||||
await self._conn.heater_control(self._heater.device_id, power_status=0)
|
||||
await self.coordinator.mill_data_connection.heater_control(
|
||||
self._id, power_status=1
|
||||
)
|
||||
await self.coordinator.async_request_refresh()
|
||||
elif hvac_mode == HVAC_MODE_OFF and not heater.is_gen1:
|
||||
await self.coordinator.mill_data_connection.heater_control(
|
||||
self._id, power_status=0
|
||||
)
|
||||
await self.coordinator.async_request_refresh()
|
||||
|
||||
async def async_update(self):
|
||||
"""Retrieve latest state."""
|
||||
self._heater = await self._conn.update_device(self._heater.device_id)
|
||||
@callback
|
||||
def _handle_coordinator_update(self) -> None:
|
||||
"""Handle updated data from the coordinator."""
|
||||
self._update_attr(self.coordinator.data[self._id])
|
||||
self.async_write_ha_state()
|
||||
|
||||
@property
|
||||
def device_id(self):
|
||||
"""Return the ID of the physical device this sensor is part of."""
|
||||
return self._heater.device_id
|
||||
|
||||
@property
|
||||
def device_info(self):
|
||||
"""Return the device_info of the device."""
|
||||
device_info = {
|
||||
"identifiers": {(DOMAIN, self.device_id)},
|
||||
"name": self.name,
|
||||
"manufacturer": MANUFACTURER,
|
||||
"model": f"generation {1 if self._heater.is_gen1 else 2}",
|
||||
@callback
|
||||
def _update_attr(self, heater):
|
||||
self._attr_available = heater.available
|
||||
self._attr_extra_state_attributes = {
|
||||
"open_window": heater.open_window,
|
||||
"heating": heater.is_heating,
|
||||
"controlled_by_tibber": heater.tibber_control,
|
||||
"heater_generation": 1 if heater.is_gen1 else 2,
|
||||
}
|
||||
return device_info
|
||||
if heater.room:
|
||||
self._attr_extra_state_attributes["room"] = heater.room.name
|
||||
self._attr_extra_state_attributes["avg_room_temp"] = heater.room.avg_temp
|
||||
else:
|
||||
self._attr_extra_state_attributes["room"] = "Independent device"
|
||||
self._attr_target_temperature = heater.set_temp
|
||||
self._attr_current_temperature = heater.current_temp
|
||||
self._attr_fan_mode = FAN_ON if heater.fan_status == 1 else HVAC_MODE_OFF
|
||||
if heater.is_gen1 or heater.is_heating == 1:
|
||||
self._attr_hvac_action = CURRENT_HVAC_HEAT
|
||||
else:
|
||||
self._attr_hvac_action = CURRENT_HVAC_IDLE
|
||||
if heater.is_gen1 or heater.power_status == 1:
|
||||
self._attr_hvac_mode = HVAC_MODE_HEAT
|
||||
else:
|
||||
self._attr_hvac_mode = HVAC_MODE_OFF
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
"domain": "mill",
|
||||
"name": "Mill",
|
||||
"documentation": "https://www.home-assistant.io/integrations/mill",
|
||||
"requirements": ["millheater==0.5.0"],
|
||||
"requirements": ["millheater==0.5.2"],
|
||||
"codeowners": ["@danielhiversen"],
|
||||
"config_flow": true,
|
||||
"iot_class": "cloud_polling"
|
||||
|
|
|
@ -6,6 +6,8 @@ from homeassistant.components.sensor import (
|
|||
SensorEntity,
|
||||
)
|
||||
from homeassistant.const import ENERGY_KILO_WATT_HOUR
|
||||
from homeassistant.core import callback
|
||||
from homeassistant.helpers.update_coordinator import CoordinatorEntity
|
||||
|
||||
from .const import CONSUMPTION_TODAY, CONSUMPTION_YEAR, DOMAIN, MANUFACTURER
|
||||
|
||||
|
@ -13,27 +15,28 @@ from .const import CONSUMPTION_TODAY, CONSUMPTION_YEAR, DOMAIN, MANUFACTURER
|
|||
async def async_setup_entry(hass, entry, async_add_entities):
|
||||
"""Set up the Mill sensor."""
|
||||
|
||||
mill_data_connection = hass.data[DOMAIN]
|
||||
mill_data_coordinator = hass.data[DOMAIN]
|
||||
|
||||
entities = [
|
||||
MillHeaterEnergySensor(heater, mill_data_connection, sensor_type)
|
||||
MillHeaterEnergySensor(mill_data_coordinator, sensor_type, heater)
|
||||
for sensor_type in (CONSUMPTION_TODAY, CONSUMPTION_YEAR)
|
||||
for heater in mill_data_connection.heaters.values()
|
||||
for heater in mill_data_coordinator.data.values()
|
||||
]
|
||||
async_add_entities(entities)
|
||||
|
||||
|
||||
class MillHeaterEnergySensor(SensorEntity):
|
||||
class MillHeaterEnergySensor(CoordinatorEntity, SensorEntity):
|
||||
"""Representation of a Mill Sensor device."""
|
||||
|
||||
_attr_device_class = DEVICE_CLASS_ENERGY
|
||||
_attr_native_unit_of_measurement = ENERGY_KILO_WATT_HOUR
|
||||
_attr_state_class = STATE_CLASS_TOTAL_INCREASING
|
||||
|
||||
def __init__(self, heater, mill_data_connection, sensor_type):
|
||||
def __init__(self, coordinator, sensor_type, heater):
|
||||
"""Initialize the sensor."""
|
||||
super().__init__(coordinator)
|
||||
|
||||
self._id = heater.device_id
|
||||
self._conn = mill_data_connection
|
||||
self._sensor_type = sensor_type
|
||||
|
||||
self._attr_name = f"{heater.name} {sensor_type.replace('_', ' ')}"
|
||||
|
@ -44,20 +47,19 @@ class MillHeaterEnergySensor(SensorEntity):
|
|||
"manufacturer": MANUFACTURER,
|
||||
"model": f"generation {1 if heater.is_gen1 else 2}",
|
||||
}
|
||||
self._update_attr(heater)
|
||||
|
||||
async def async_update(self):
|
||||
"""Retrieve latest state."""
|
||||
heater = await self._conn.update_device(self._id)
|
||||
@callback
|
||||
def _handle_coordinator_update(self) -> None:
|
||||
"""Handle updated data from the coordinator."""
|
||||
self._update_attr(self.coordinator.data[self._id])
|
||||
self.async_write_ha_state()
|
||||
|
||||
@callback
|
||||
def _update_attr(self, heater):
|
||||
self._attr_available = heater.available
|
||||
|
||||
if self._sensor_type == CONSUMPTION_TODAY:
|
||||
_state = heater.day_consumption
|
||||
self._attr_native_value = heater.day_consumption
|
||||
elif self._sensor_type == CONSUMPTION_YEAR:
|
||||
_state = heater.year_consumption
|
||||
else:
|
||||
_state = None
|
||||
if _state is None:
|
||||
self._attr_native_value = _state
|
||||
return
|
||||
|
||||
self._attr_native_value = _state
|
||||
self._attr_native_value = heater.year_consumption
|
||||
|
|
|
@ -980,7 +980,7 @@ micloud==0.3
|
|||
miflora==0.7.0
|
||||
|
||||
# homeassistant.components.mill
|
||||
millheater==0.5.0
|
||||
millheater==0.5.2
|
||||
|
||||
# homeassistant.components.minio
|
||||
minio==4.0.9
|
||||
|
|
|
@ -551,7 +551,7 @@ mficlient==0.3.0
|
|||
micloud==0.3
|
||||
|
||||
# homeassistant.components.mill
|
||||
millheater==0.5.0
|
||||
millheater==0.5.2
|
||||
|
||||
# homeassistant.components.minio
|
||||
minio==4.0.9
|
||||
|
|
Loading…
Add table
Reference in a new issue