1 """Withings coordinator."""
3 from __future__
import annotations
5 from abc
import abstractmethod
6 from datetime
import date, datetime, timedelta
7 from typing
import TYPE_CHECKING
9 from aiowithings
import (
17 SleepSummaryDataFields,
18 WithingsAuthenticationFailedError,
20 WithingsUnauthorizedError,
22 aggregate_measurements,
30 from .const
import LOGGER
33 from .
import WithingsConfigEntry
39 """Base coordinator."""
41 config_entry: WithingsConfigEntry
42 _default_update_interval: timedelta |
None = UPDATE_INTERVAL
43 _last_valid_update: datetime |
None =
None
44 webhooks_connected: bool =
False
45 coordinator_name: str =
""
47 def __init__(self, hass: HomeAssistant, client: WithingsClient) ->
None:
48 """Initialize the Withings data coordinator."""
53 update_interval=self._default_update_interval,
55 self.
namenamename = f
"Withings {self.config_entry.unique_id} {self.coordinator_name}"
57 self.notification_categories: set[NotificationCategory] = set()
60 """Call when webhook status changed."""
68 self, notification_category: NotificationCategory
70 """Update data when webhook is called."""
72 "Withings webhook triggered for category %s for user %s",
73 notification_category,
81 except (WithingsUnauthorizedError, WithingsAuthenticationFailedError)
as exc:
82 raise ConfigEntryAuthFailed
from exc
86 """Update coordinator data."""
89 class WithingsMeasurementDataUpdateCoordinator(
90 WithingsDataUpdateCoordinator[
91 dict[tuple[MeasurementType, MeasurementPosition |
None], float]
94 """Withings measurement coordinator."""
96 coordinator_name: str =
"measurements"
98 def __init__(self, hass: HomeAssistant, client: WithingsClient) ->
None:
99 """Initialize the Withings data coordinator."""
102 NotificationCategory.WEIGHT,
103 NotificationCategory.PRESSURE,
105 self._previous_data: dict[
106 tuple[MeasurementType, MeasurementPosition |
None], float
111 ) -> dict[tuple[MeasurementType, MeasurementPosition | None], float]:
112 """Retrieve measurement data."""
114 now = dt_util.utcnow()
116 measurements = await self.
_client_client.get_measurement_in_period(startdate, now)
118 measurements = await self.
_client_client.get_measurement_since(
124 aggregated_measurements = aggregate_measurements(measurements)
125 self._previous_data.
update(aggregated_measurements)
126 return self._previous_data
130 WithingsDataUpdateCoordinator[SleepSummary |
None]
132 """Withings sleep coordinator."""
134 coordinator_name: str =
"sleep"
136 def __init__(self, hass: HomeAssistant, client: WithingsClient) ->
None:
137 """Initialize the Withings data coordinator."""
140 NotificationCategory.SLEEP,
144 """Retrieve sleep data."""
147 yesterday_noon = dt_util.start_of_local_day(yesterday) +
timedelta(hours=12)
148 yesterday_noon_utc = dt_util.as_utc(yesterday_noon)
150 response = await self.
_client_client.get_sleep_summary_since(
151 sleep_summary_since=yesterday_noon_utc,
152 sleep_summary_data_fields=[
153 SleepSummaryDataFields.BREATHING_DISTURBANCES_INTENSITY,
154 SleepSummaryDataFields.DEEP_SLEEP_DURATION,
155 SleepSummaryDataFields.SLEEP_LATENCY,
156 SleepSummaryDataFields.WAKE_UP_LATENCY,
157 SleepSummaryDataFields.AVERAGE_HEART_RATE,
158 SleepSummaryDataFields.MIN_HEART_RATE,
159 SleepSummaryDataFields.MAX_HEART_RATE,
160 SleepSummaryDataFields.LIGHT_SLEEP_DURATION,
161 SleepSummaryDataFields.REM_SLEEP_DURATION,
162 SleepSummaryDataFields.AVERAGE_RESPIRATION_RATE,
163 SleepSummaryDataFields.MIN_RESPIRATION_RATE,
164 SleepSummaryDataFields.MAX_RESPIRATION_RATE,
165 SleepSummaryDataFields.SLEEP_SCORE,
166 SleepSummaryDataFields.SNORING,
167 SleepSummaryDataFields.SNORING_COUNT,
168 SleepSummaryDataFields.WAKE_UP_COUNT,
169 SleepSummaryDataFields.TOTAL_TIME_AWAKE,
176 response, key=
lambda sleep_summary: sleep_summary.end_date, reverse=
True
181 """Withings bed presence coordinator."""
183 coordinator_name: str =
"bed presence"
184 in_bed: bool |
None =
None
185 _default_update_interval =
None
187 def __init__(self, hass: HomeAssistant, client: WithingsClient) ->
None:
188 """Initialize the Withings data coordinator."""
191 NotificationCategory.IN_BED,
192 NotificationCategory.OUT_BED,
196 self, notification_category: NotificationCategory
198 """Only set new in bed value instead of refresh."""
199 self.
in_bedin_bed = notification_category == NotificationCategory.IN_BED
203 """Update coordinator data."""
206 class WithingsGoalsDataUpdateCoordinator(WithingsDataUpdateCoordinator[Goals]):
207 """Withings goals coordinator."""
209 coordinator_name: str =
"goals"
213 """Call when webhook status changed."""
217 """Retrieve goals data."""
218 return await self.
_client_client.get_goals()
222 WithingsDataUpdateCoordinator[Activity |
None]
224 """Withings activity coordinator."""
226 coordinator_name: str =
"activity"
227 _previous_data: Activity |
None =
None
229 def __init__(self, hass: HomeAssistant, client: WithingsClient) ->
None:
230 """Initialize the Withings data coordinator."""
233 NotificationCategory.ACTIVITY,
237 """Retrieve latest activity."""
239 now = dt_util.utcnow()
241 activities = await self.
_client_client.get_activities_in_period(
242 startdate.date(), now.date()
245 activities = await self.
_client_client.get_activities_since(
250 for activity
in activities:
251 if activity.date == today:
261 WithingsDataUpdateCoordinator[Workout |
None]
263 """Withings workout coordinator."""
265 coordinator_name: str =
"workout"
266 _previous_data: Workout |
None =
None
268 def __init__(self, hass: HomeAssistant, client: WithingsClient) ->
None:
269 """Initialize the Withings data coordinator."""
272 NotificationCategory.ACTIVITY,
276 """Retrieve latest workout."""
278 now = dt_util.utcnow()
280 workouts = await self.
_client_client.get_workouts_in_period(
281 startdate.date(), now.date()
287 latest_workout =
max(workouts, key=
lambda workout: workout.end_date)
290 or self.
_previous_data_previous_data.end_date >= latest_workout.end_date
298 WithingsDataUpdateCoordinator[dict[str, Device]]
300 """Withings device coordinator."""
302 coordinator_name: str =
"device"
306 """Update coordinator data."""
307 devices = await self.
_client_client.get_devices()
308 return {device.device_id: device
for device
in devices}
None __init__(self, HomeAssistant hass, WithingsClient client)
Activity|None _internal_update_data(self)
None __init__(self, HomeAssistant hass, WithingsClient client)
None _internal_update_data(self)
None async_webhook_data_updated(self, NotificationCategory notification_category)
_DataT _async_update_data(self)
None webhook_subscription_listener(self, bool connected)
_DataT _internal_update_data(self)
None async_webhook_data_updated(self, NotificationCategory notification_category)
None __init__(self, HomeAssistant hass, WithingsClient client)
dict[str, Device] _internal_update_data(self)
None webhook_subscription_listener(self, bool connected)
Goals _internal_update_data(self)
dict[tuple[MeasurementType, MeasurementPosition|None], float] _internal_update_data(self)
None __init__(self, HomeAssistant hass, WithingsClient client)
None __init__(self, HomeAssistant hass, WithingsClient client)
SleepSummary|None _internal_update_data(self)
None __init__(self, HomeAssistant hass, WithingsClient client)
Workout|None _internal_update_data(self)
None update_interval(self, timedelta|None value)
timedelta|None update_interval(self)
None async_update_listeners(self)
None async_request_refresh(self)
IssData update(pyiss.ISS iss)