mirror of
https://github.com/home-assistant/core.git
synced 2025-07-22 12:47:08 +00:00
Switch to UpdateCoordinator for eight sleep (#52614)
* Switch to UpdateCoordinator for eight sleep * use super call * add self as codeowner * Call API update method directly when creating coordinator * Update homeassistant/components/eight_sleep/__init__.py Co-authored-by: J. Nick Koston <nick@koston.org> * Update homeassistant/components/eight_sleep/__init__.py Co-authored-by: J. Nick Koston <nick@koston.org> Co-authored-by: J. Nick Koston <nick@koston.org>
This commit is contained in:
parent
787de8ba66
commit
6bc5961f8a
@ -138,7 +138,7 @@ homeassistant/components/ecovacs/* @OverloadUT
|
||||
homeassistant/components/edl21/* @mtdcr
|
||||
homeassistant/components/efergy/* @tkdrob
|
||||
homeassistant/components/egardia/* @jeroenterheerdt
|
||||
homeassistant/components/eight_sleep/* @mezz64
|
||||
homeassistant/components/eight_sleep/* @mezz64 @raman325
|
||||
homeassistant/components/elgato/* @frenck
|
||||
homeassistant/components/elkm1/* @gwww @bdraco
|
||||
homeassistant/components/elv/* @majuss
|
||||
|
@ -12,23 +12,22 @@ from homeassistant.const import (
|
||||
CONF_SENSORS,
|
||||
CONF_USERNAME,
|
||||
)
|
||||
from homeassistant.core import callback
|
||||
from homeassistant.helpers import discovery
|
||||
from homeassistant.helpers.aiohttp_client import async_get_clientsession
|
||||
import homeassistant.helpers.config_validation as cv
|
||||
from homeassistant.helpers.dispatcher import (
|
||||
async_dispatcher_connect,
|
||||
async_dispatcher_send,
|
||||
from homeassistant.helpers.update_coordinator import (
|
||||
CoordinatorEntity,
|
||||
DataUpdateCoordinator,
|
||||
)
|
||||
from homeassistant.helpers.entity import Entity
|
||||
from homeassistant.helpers.event import async_track_point_in_utc_time
|
||||
from homeassistant.util.dt import utcnow
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
CONF_PARTNER = "partner"
|
||||
|
||||
DATA_EIGHT = "eight_sleep"
|
||||
DATA_HEAT = "heat"
|
||||
DATA_USER = "user"
|
||||
DATA_API = "api"
|
||||
DOMAIN = "eight_sleep"
|
||||
|
||||
HEAT_ENTITY = "heat"
|
||||
@ -115,7 +114,7 @@ async def async_setup(hass, config):
|
||||
|
||||
eight = EightSleep(user, password, timezone, async_get_clientsession(hass))
|
||||
|
||||
hass.data[DATA_EIGHT] = eight
|
||||
hass.data.setdefault(DATA_EIGHT, {})[DATA_API] = eight
|
||||
|
||||
# Authenticate, build sensors
|
||||
success = await eight.start()
|
||||
@ -123,26 +122,14 @@ async def async_setup(hass, config):
|
||||
# Authentication failed, cannot continue
|
||||
return False
|
||||
|
||||
async def async_update_heat_data(now):
|
||||
"""Update heat data from eight in HEAT_SCAN_INTERVAL."""
|
||||
await eight.update_device_data()
|
||||
async_dispatcher_send(hass, SIGNAL_UPDATE_HEAT)
|
||||
|
||||
async_track_point_in_utc_time(
|
||||
hass, async_update_heat_data, utcnow() + HEAT_SCAN_INTERVAL
|
||||
)
|
||||
|
||||
async def async_update_user_data(now):
|
||||
"""Update user data from eight in USER_SCAN_INTERVAL."""
|
||||
await eight.update_user_data()
|
||||
async_dispatcher_send(hass, SIGNAL_UPDATE_USER)
|
||||
|
||||
async_track_point_in_utc_time(
|
||||
hass, async_update_user_data, utcnow() + USER_SCAN_INTERVAL
|
||||
)
|
||||
|
||||
await async_update_heat_data(None)
|
||||
await async_update_user_data(None)
|
||||
heat_coordinator = hass.data[DOMAIN][DATA_HEAT] = EightSleepHeatDataCoordinator(
|
||||
hass, eight
|
||||
)
|
||||
user_coordinator = hass.data[DOMAIN][DATA_USER] = EightSleepUserDataCoordinator(
|
||||
hass, eight
|
||||
)
|
||||
await heat_coordinator.async_config_entry_first_refresh()
|
||||
await user_coordinator.async_config_entry_first_refresh()
|
||||
|
||||
# Load sub components
|
||||
sensors = []
|
||||
@ -183,7 +170,7 @@ async def async_setup(hass, config):
|
||||
usrobj = eight.users[userid]
|
||||
await usrobj.set_heating_level(target, duration)
|
||||
|
||||
async_dispatcher_send(hass, SIGNAL_UPDATE_HEAT)
|
||||
await heat_coordinator.async_request_refresh()
|
||||
|
||||
# Register services
|
||||
hass.services.async_register(
|
||||
@ -193,55 +180,40 @@ async def async_setup(hass, config):
|
||||
return True
|
||||
|
||||
|
||||
class EightSleepUserEntity(Entity):
|
||||
"""The Eight Sleep device entity."""
|
||||
class EightSleepHeatDataCoordinator(DataUpdateCoordinator):
|
||||
"""Class to retrieve heat data from Eight Sleep."""
|
||||
|
||||
def __init__(self, eight):
|
||||
"""Initialize the data object."""
|
||||
self._eight = eight
|
||||
|
||||
async def async_added_to_hass(self):
|
||||
"""Register update dispatcher."""
|
||||
|
||||
@callback
|
||||
def async_eight_user_update():
|
||||
"""Update callback."""
|
||||
self.async_schedule_update_ha_state(True)
|
||||
|
||||
self.async_on_remove(
|
||||
async_dispatcher_connect(
|
||||
self.hass, SIGNAL_UPDATE_USER, async_eight_user_update
|
||||
)
|
||||
def __init__(self, hass, api):
|
||||
"""Initialize coordinator."""
|
||||
self.api = api
|
||||
super().__init__(
|
||||
hass,
|
||||
_LOGGER,
|
||||
name=f"{DOMAIN}_heat",
|
||||
update_interval=HEAT_SCAN_INTERVAL,
|
||||
update_method=self.api.update_device_data,
|
||||
)
|
||||
|
||||
@property
|
||||
def should_poll(self):
|
||||
"""Return True if entity has to be polled for state."""
|
||||
return False
|
||||
|
||||
class EightSleepUserDataCoordinator(DataUpdateCoordinator):
|
||||
"""Class to retrieve user data from Eight Sleep."""
|
||||
|
||||
class EightSleepHeatEntity(Entity):
|
||||
"""The Eight Sleep device entity."""
|
||||
|
||||
def __init__(self, eight):
|
||||
"""Initialize the data object."""
|
||||
self._eight = eight
|
||||
|
||||
async def async_added_to_hass(self):
|
||||
"""Register update dispatcher."""
|
||||
|
||||
@callback
|
||||
def async_eight_heat_update():
|
||||
"""Update callback."""
|
||||
self.async_schedule_update_ha_state(True)
|
||||
|
||||
self.async_on_remove(
|
||||
async_dispatcher_connect(
|
||||
self.hass, SIGNAL_UPDATE_HEAT, async_eight_heat_update
|
||||
)
|
||||
def __init__(self, hass, api):
|
||||
"""Initialize coordinator."""
|
||||
self.api = api
|
||||
super().__init__(
|
||||
hass,
|
||||
_LOGGER,
|
||||
name=f"{DOMAIN}_user",
|
||||
update_interval=USER_SCAN_INTERVAL,
|
||||
update_method=self.api.update_user_data,
|
||||
)
|
||||
|
||||
@property
|
||||
def should_poll(self):
|
||||
"""Return True if entity has to be polled for state."""
|
||||
return False
|
||||
|
||||
class EightSleepEntity(CoordinatorEntity):
|
||||
"""The Eight Sleep device entity."""
|
||||
|
||||
def __init__(self, coordinator, eight):
|
||||
"""Initialize the data object."""
|
||||
super().__init__(coordinator)
|
||||
self._eight = eight
|
||||
|
@ -5,8 +5,16 @@ from homeassistant.components.binary_sensor import (
|
||||
DEVICE_CLASS_OCCUPANCY,
|
||||
BinarySensorEntity,
|
||||
)
|
||||
from homeassistant.core import callback
|
||||
|
||||
from . import CONF_BINARY_SENSORS, DATA_EIGHT, NAME_MAP, EightSleepHeatEntity
|
||||
from . import (
|
||||
CONF_BINARY_SENSORS,
|
||||
DATA_API,
|
||||
DATA_EIGHT,
|
||||
DATA_HEAT,
|
||||
NAME_MAP,
|
||||
EightSleepEntity,
|
||||
)
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
@ -18,22 +26,23 @@ async def async_setup_platform(hass, config, async_add_entities, discovery_info=
|
||||
|
||||
name = "Eight"
|
||||
sensors = discovery_info[CONF_BINARY_SENSORS]
|
||||
eight = hass.data[DATA_EIGHT]
|
||||
eight = hass.data[DATA_EIGHT][DATA_API]
|
||||
heat_coordinator = hass.data[DATA_EIGHT][DATA_HEAT]
|
||||
|
||||
all_sensors = []
|
||||
|
||||
for sensor in sensors:
|
||||
all_sensors.append(EightHeatSensor(name, eight, sensor))
|
||||
all_sensors.append(EightHeatSensor(name, heat_coordinator, eight, sensor))
|
||||
|
||||
async_add_entities(all_sensors, True)
|
||||
|
||||
|
||||
class EightHeatSensor(EightSleepHeatEntity, BinarySensorEntity):
|
||||
class EightHeatSensor(EightSleepEntity, BinarySensorEntity):
|
||||
"""Representation of a Eight Sleep heat-based sensor."""
|
||||
|
||||
def __init__(self, name, eight, sensor):
|
||||
def __init__(self, name, coordinator, eight, sensor):
|
||||
"""Initialize the sensor."""
|
||||
super().__init__(eight)
|
||||
super().__init__(coordinator, eight)
|
||||
|
||||
self._sensor = sensor
|
||||
self._mapped_name = NAME_MAP.get(self._sensor, self._sensor)
|
||||
@ -58,6 +67,8 @@ class EightHeatSensor(EightSleepHeatEntity, BinarySensorEntity):
|
||||
"""Return true if the binary sensor is on."""
|
||||
return self._state
|
||||
|
||||
async def async_update(self):
|
||||
"""Retrieve latest state."""
|
||||
@callback
|
||||
def _handle_coordinator_update(self):
|
||||
"""Handle updated data from the coordinator."""
|
||||
self._state = self._usrobj.bed_presence
|
||||
super()._handle_coordinator_update()
|
||||
|
@ -3,6 +3,6 @@
|
||||
"name": "Eight Sleep",
|
||||
"documentation": "https://www.home-assistant.io/integrations/eight_sleep",
|
||||
"requirements": ["pyeight==0.1.9"],
|
||||
"codeowners": ["@mezz64"],
|
||||
"codeowners": ["@mezz64", "@raman325"],
|
||||
"iot_class": "cloud_polling"
|
||||
}
|
||||
|
@ -8,13 +8,16 @@ from homeassistant.const import (
|
||||
TEMP_CELSIUS,
|
||||
TEMP_FAHRENHEIT,
|
||||
)
|
||||
from homeassistant.core import callback
|
||||
|
||||
from . import (
|
||||
CONF_SENSORS,
|
||||
DATA_API,
|
||||
DATA_EIGHT,
|
||||
DATA_HEAT,
|
||||
DATA_USER,
|
||||
NAME_MAP,
|
||||
EightSleepHeatEntity,
|
||||
EightSleepUserEntity,
|
||||
EightSleepEntity,
|
||||
)
|
||||
|
||||
ATTR_ROOM_TEMP = "Room Temperature"
|
||||
@ -52,7 +55,9 @@ async def async_setup_platform(hass, config, async_add_entities, discovery_info=
|
||||
|
||||
name = "Eight"
|
||||
sensors = discovery_info[CONF_SENSORS]
|
||||
eight = hass.data[DATA_EIGHT]
|
||||
eight = hass.data[DATA_EIGHT][DATA_API]
|
||||
heat_coordinator = hass.data[DATA_EIGHT][DATA_HEAT]
|
||||
user_coordinator = hass.data[DATA_EIGHT][DATA_USER]
|
||||
|
||||
if hass.config.units.is_metric:
|
||||
units = "si"
|
||||
@ -63,21 +68,25 @@ async def async_setup_platform(hass, config, async_add_entities, discovery_info=
|
||||
|
||||
for sensor in sensors:
|
||||
if "bed_state" in sensor:
|
||||
all_sensors.append(EightHeatSensor(name, eight, sensor))
|
||||
all_sensors.append(EightHeatSensor(name, heat_coordinator, eight, sensor))
|
||||
elif "room_temp" in sensor:
|
||||
all_sensors.append(EightRoomSensor(name, eight, sensor, units))
|
||||
all_sensors.append(
|
||||
EightRoomSensor(name, user_coordinator, eight, sensor, units)
|
||||
)
|
||||
else:
|
||||
all_sensors.append(EightUserSensor(name, eight, sensor, units))
|
||||
all_sensors.append(
|
||||
EightUserSensor(name, user_coordinator, eight, sensor, units)
|
||||
)
|
||||
|
||||
async_add_entities(all_sensors, True)
|
||||
|
||||
|
||||
class EightHeatSensor(EightSleepHeatEntity, SensorEntity):
|
||||
class EightHeatSensor(EightSleepEntity, SensorEntity):
|
||||
"""Representation of an eight sleep heat-based sensor."""
|
||||
|
||||
def __init__(self, name, eight, sensor):
|
||||
def __init__(self, name, coordinator, eight, sensor):
|
||||
"""Initialize the sensor."""
|
||||
super().__init__(eight)
|
||||
super().__init__(coordinator, eight)
|
||||
|
||||
self._sensor = sensor
|
||||
self._mapped_name = NAME_MAP.get(self._sensor, self._sensor)
|
||||
@ -110,10 +119,12 @@ class EightHeatSensor(EightSleepHeatEntity, SensorEntity):
|
||||
"""Return the unit the value is expressed in."""
|
||||
return PERCENTAGE
|
||||
|
||||
async def async_update(self):
|
||||
"""Retrieve latest state."""
|
||||
@callback
|
||||
def _handle_coordinator_update(self):
|
||||
"""Handle updated data from the coordinator."""
|
||||
_LOGGER.debug("Updating Heat sensor: %s", self._sensor)
|
||||
self._state = self._usrobj.heating_level
|
||||
super()._handle_coordinator_update()
|
||||
|
||||
@property
|
||||
def extra_state_attributes(self):
|
||||
@ -125,12 +136,12 @@ class EightHeatSensor(EightSleepHeatEntity, SensorEntity):
|
||||
}
|
||||
|
||||
|
||||
class EightUserSensor(EightSleepUserEntity, SensorEntity):
|
||||
class EightUserSensor(EightSleepEntity, SensorEntity):
|
||||
"""Representation of an eight sleep user-based sensor."""
|
||||
|
||||
def __init__(self, name, eight, sensor, units):
|
||||
def __init__(self, name, coordinator, eight, sensor, units):
|
||||
"""Initialize the sensor."""
|
||||
super().__init__(eight)
|
||||
super().__init__(coordinator, eight)
|
||||
|
||||
self._sensor = sensor
|
||||
self._sensor_root = self._sensor.split("_", 1)[1]
|
||||
@ -183,8 +194,9 @@ class EightUserSensor(EightSleepUserEntity, SensorEntity):
|
||||
return DEVICE_CLASS_TEMPERATURE
|
||||
return None
|
||||
|
||||
async def async_update(self):
|
||||
"""Retrieve latest state."""
|
||||
@callback
|
||||
def _handle_coordinator_update(self):
|
||||
"""Handle updated data from the coordinator."""
|
||||
_LOGGER.debug("Updating User sensor: %s", self._sensor)
|
||||
if "current" in self._sensor:
|
||||
if "fitness" in self._sensor:
|
||||
@ -208,6 +220,8 @@ class EightUserSensor(EightSleepUserEntity, SensorEntity):
|
||||
elif "sleep_stage" in self._sensor:
|
||||
self._state = self._usrobj.current_values["stage"]
|
||||
|
||||
super()._handle_coordinator_update()
|
||||
|
||||
@property
|
||||
def extra_state_attributes(self):
|
||||
"""Return device state attributes."""
|
||||
@ -296,12 +310,12 @@ class EightUserSensor(EightSleepUserEntity, SensorEntity):
|
||||
return state_attr
|
||||
|
||||
|
||||
class EightRoomSensor(EightSleepUserEntity, SensorEntity):
|
||||
class EightRoomSensor(EightSleepEntity, SensorEntity):
|
||||
"""Representation of an eight sleep room sensor."""
|
||||
|
||||
def __init__(self, name, eight, sensor, units):
|
||||
def __init__(self, name, coordinator, eight, sensor, units):
|
||||
"""Initialize the sensor."""
|
||||
super().__init__(eight)
|
||||
super().__init__(coordinator, eight)
|
||||
|
||||
self._sensor = sensor
|
||||
self._mapped_name = NAME_MAP.get(self._sensor, self._sensor)
|
||||
@ -320,8 +334,9 @@ class EightRoomSensor(EightSleepUserEntity, SensorEntity):
|
||||
"""Return the state of the sensor."""
|
||||
return self._state
|
||||
|
||||
async def async_update(self):
|
||||
"""Retrieve latest state."""
|
||||
@callback
|
||||
def _handle_coordinator_update(self):
|
||||
"""Handle updated data from the coordinator."""
|
||||
_LOGGER.debug("Updating Room sensor: %s", self._sensor)
|
||||
temp = self._eight.room_temperature()
|
||||
try:
|
||||
@ -331,6 +346,7 @@ class EightRoomSensor(EightSleepUserEntity, SensorEntity):
|
||||
self._state = round((temp * 1.8) + 32, 2)
|
||||
except TypeError:
|
||||
self._state = None
|
||||
super()._handle_coordinator_update()
|
||||
|
||||
@property
|
||||
def native_unit_of_measurement(self):
|
||||
|
Loading…
x
Reference in New Issue
Block a user