Skip to content

Commit

Permalink
Merge pull request #135 from YorkshireIoT/127-unknown-sleep-stage-type
Browse files Browse the repository at this point in the history
Handle case when sleep segment type is 0
  • Loading branch information
YorkshireIoT authored Nov 18, 2023
2 parents 4bebd03 + 2a7f5b7 commit ff1d173
Show file tree
Hide file tree
Showing 3 changed files with 31 additions and 13 deletions.
32 changes: 22 additions & 10 deletions custom_components/google_fit/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
LastPointSensorDescription,
SumSessionSensorDescription,
)
from .const import SLEEP_STAGE, LOGGER
from .const import SLEEP_STAGE, LOGGER, NANOSECONDS_SECONDS_CONVERSION


class AsyncConfigEntryAuth(OAuthClientAuthHandler):
Expand Down Expand Up @@ -92,6 +92,7 @@ class GoogleFitParse:
"""Parse raw data received from the Google Fit API."""

data: FitnessData
unknown_sleep_warn: bool

def __init__(self):
"""Initialise the data to base value and add a timestamp."""
Expand Down Expand Up @@ -120,6 +121,7 @@ def __init__(self):
hydration=None,
oxygenSaturation=None,
)
self.unknown_sleep_warn = False

def _sum_points_int(self, response: FitnessObject) -> int | None:
counter = 0
Expand Down Expand Up @@ -197,30 +199,40 @@ def _parse_sleep(self, response: FitnessObject) -> None:
for point in data_points:
found_point = True
sleep_type = point.get("value")[0].get("intVal")
start_time = point.get("startTimeNanos")
end_time = point.get("endTimeNanos")
start_time_ns = point.get("startTimeNanos")
end_time_ns = point.get("endTimeNanos")
if (
sleep_type is not None
and start_time is not None
and end_time is not None
and start_time_ns is not None
and end_time_ns is not None
):
sleep_stage = SLEEP_STAGE.get(sleep_type)
start_time = int(start_time_ns) / NANOSECONDS_SECONDS_CONVERSION
start_time_str = datetime.fromtimestamp(start_time).strftime('%Y-%m-%d %H:%M:%S')
end_time = int(end_time_ns) / NANOSECONDS_SECONDS_CONVERSION
end_time_str = datetime.fromtimestamp(end_time).strftime('%Y-%m-%d %H:%M:%S')

if sleep_stage == "Out-of-bed":
LOGGER.debug("Out of bed sleep sensor not supported. Ignoring.")
elif sleep_stage == "unspecified":
LOGGER.warning(
"Google Fit reported an unspecified or unknown value "
"for sleep stage between %s and %s. Please report this as a bug to the "
"original data provider. This will not be reported in "
"Home Assistant.", start_time_str, end_time_str
)
elif sleep_stage is not None:
# If field is still at None, initialise it to zero
if self.data[sleep_stage] is None:
self.data[sleep_stage] = 0

if end_time >= start_time:
self.data[sleep_stage] += (
int(end_time) - int(start_time)
) / 1000000000
self.data[sleep_stage] += end_time - start_time
else:
raise UpdateFailed(
"Invalid data from Google. End time "
f"({end_time}) is less than the start time "
f"({start_time})."
f"({end_time_str}) is less than the start time "
f"({start_time_str})."
)
else:
raise UpdateFailed(
Expand Down
4 changes: 4 additions & 0 deletions custom_components/google_fit/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,9 @@
DEFAULT_SCAN_INTERVAL = 5
DEFAULT_NO_DATA_USE_ZERO = True

# Useful constants
NANOSECONDS_SECONDS_CONVERSION: Final = 1000000000

# Required Scopes
DEFAULT_ACCESS = [
"https://www.googleapis.com/auth/userinfo.email",
Expand All @@ -54,6 +57,7 @@
# Sleep Data Enum. Taken from:
# https://developers.google.com/fit/scenarios/read-sleep-data
SLEEP_STAGE: Final = {
0: "unspecified",
1: "awakeSeconds",
2: "sleepSeconds",
3: "Out-of-bed", # Not supported
Expand Down
8 changes: 5 additions & 3 deletions custom_components/google_fit/coordinator.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
DEFAULT_SCAN_INTERVAL,
DEFAULT_NO_DATA_USE_ZERO,
CONF_NO_DATA_USE_ZERO,
NANOSECONDS_SECONDS_CONVERSION,
)


Expand Down Expand Up @@ -100,11 +101,12 @@ def _get_interval(self, interval_period: int = 0) -> str:
datetime.today().date(), datetime.min.time()
).timestamp()
)
* 1000000000
* NANOSECONDS_SECONDS_CONVERSION
)
else:
start = (int(datetime.today().timestamp()) - interval_period) * 1000000000
now = int(datetime.today().timestamp() * 1000000000)
start = (int(datetime.today().timestamp()) - interval_period)
start = start * NANOSECONDS_SECONDS_CONVERSION
now = int(datetime.today().timestamp() * NANOSECONDS_SECONDS_CONVERSION)
return f"{start}-{now}"

async def _async_update_data(self) -> FitService | None:
Expand Down

0 comments on commit ff1d173

Please sign in to comment.