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