Skip to content

Commit

Permalink
fix: correctly parse frequency dates from device (#44)
Browse files Browse the repository at this point in the history
  • Loading branch information
vanstinator committed May 22, 2023
1 parent 3865223 commit 74241cd
Show file tree
Hide file tree
Showing 3 changed files with 36 additions and 41 deletions.
2 changes: 1 addition & 1 deletion melnor_bluetooth/device.py
Original file line number Diff line number Diff line change
Expand Up @@ -419,7 +419,7 @@ async def _unsafe_push_state(self) -> None:
if updated_at is not None:
await self._connection.write_gatt_char(
updated_at.handle,
date.get_current_time_bytes(),
struct.pack(">I", date.get_timestamp()),
True,
)

Expand Down
29 changes: 7 additions & 22 deletions melnor_bluetooth/models/frequency.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,7 @@ def __init__(self) -> None:
self._attr_duration_minutes = 10
self._attr_interval_hours = 24
self._attr_raw_start_time = int(
datetime.now().replace(tzinfo=get_localzone()).timestamp()
+ date.time_shift()
datetime.now(tz=get_localzone()).timestamp() + date.time_shift()
)
self._attr_next_run_time = None

Expand All @@ -51,13 +50,7 @@ def to_bytes(self) -> bytes:
def _compute_dates(self):
# Set the start time to the start time yesterday
# this makes computing the next_run_time easier
start_time = datetime.today().replace(
hour=self.start_time.hour,
minute=self.start_time.minute,
second=0,
microsecond=0,
tzinfo=get_localzone(),
)
start_time = date.to_start_time(self._attr_raw_start_time)

# Find the next run time by adding the frequency to the start time until it is
# in the future. If during the loop the current date is between a candidate
Expand Down Expand Up @@ -121,27 +114,19 @@ def start_time(self) -> time:
"""The start time of the watering"""

if self._attr_raw_start_time == 0:
return datetime.fromtimestamp(
datetime.now().replace(tzinfo=get_localzone()).timestamp()
+ float(date.time_shift())
return date.to_start_time(
int(datetime.now(tz=get_localzone()).timestamp())
).time()

else:
# This date we get from the device, even with the time_shift, is always
# wrong but the time should be correcct
return datetime.fromtimestamp(
self._attr_raw_start_time - date.time_shift(), tz=get_localzone()
).time()
return date.to_start_time(self._attr_raw_start_time).time()

@start_time.setter
def start_time(self, value: time) -> None:
device_date = datetime.fromtimestamp(
self._attr_raw_start_time - date.time_shift()
)

self._attr_raw_start_time = int(
device_date.replace(hour=value.hour, minute=value.minute).timestamp()
+ date.time_shift()
self._attr_raw_start_time = date.from_start_time(
datetime.now().replace(hour=value.hour, minute=value.minute)
)

@property
Expand Down
46 changes: 28 additions & 18 deletions melnor_bluetooth/utils/date.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
import datetime
import logging
import struct
import time
import zoneinfo
from datetime import datetime, timedelta, tzinfo

from tzlocal import get_localzone

_LOGGER = logging.getLogger(__name__)


def _time_offset(tz: datetime.tzinfo = get_localzone()):
def _time_offset(tz: tzinfo = get_localzone()):
"""
Returns the archaic timezone offset in seconds.
Expand All @@ -20,8 +20,8 @@ def _time_offset(tz: datetime.tzinfo = get_localzone()):
will show bad info we don't replicate the algorithm
"""

base_time = datetime.datetime.now(tz=zoneinfo.ZoneInfo("Asia/Shanghai"))
local_time = datetime.datetime.now(tz=tz)
base_time = datetime.now(tz=zoneinfo.ZoneInfo("Asia/Shanghai"))
local_time = datetime.now(tz=tz)

base_offset = base_time.utcoffset()
local_offset = local_time.utcoffset()
Expand All @@ -39,36 +39,46 @@ def _time_offset(tz: datetime.tzinfo = get_localzone()):
# TODO log or throw an exception here. caller should handle this
return 0

return int(base_offset.total_seconds() - local_offset.total_seconds())
is_dst = time.daylight and time.localtime().tm_isdst > 0
dst_offset = timedelta(hours=1) if is_dst else timedelta(hours=0)

return (
int(base_offset.total_seconds() - local_offset.total_seconds())
+ dst_offset.total_seconds()
)

def time_shift(tz: datetime.tzinfo = get_localzone()) -> int:
date = datetime.datetime(1970, 1, 1, tzinfo=tz).replace(fold=1)

return int(
(date + datetime.timedelta(seconds=-_time_offset(tz) - 946656000)).timestamp()
)
def time_shift(tz: tzinfo = get_localzone()) -> int:
date = datetime(1970, 1, 1, tzinfo=tz).replace(fold=1)

return int((date + timedelta(seconds=-_time_offset(tz) - 946656000)).timestamp())


def get_timestamp(tz: datetime.tzinfo = get_localzone()) -> int:
def get_timestamp(tz: tzinfo = get_localzone()) -> int:
"""
Returns the current timestamp as a byte array.
"""

return int(datetime.datetime.now(tz).timestamp() + time_shift(tz))
return int(datetime.now(tz).timestamp() + time_shift(tz))


def get_current_time(tz: datetime.tzinfo = get_localzone()) -> datetime.datetime:
def to_start_time(timestamp: int, tz: tzinfo = get_localzone()) -> datetime:
"""
Returns the current date.
Returns the current timestamp as a byte array.
"""

return datetime.datetime.fromtimestamp(get_timestamp(tz))
return datetime.utcfromtimestamp(
timestamp,
).replace(
tzinfo=get_localzone()
) - timedelta(seconds=time_shift())


def get_current_time_bytes(tz: datetime.tzinfo = get_localzone()) -> bytes:
def from_start_time(timestamp: datetime) -> int:
"""
Returns the current timestamp as a byte array.
"""

return struct.pack(">I", get_timestamp(tz))
return int(
timestamp.replace(tzinfo=zoneinfo.ZoneInfo("UTC")).timestamp() + time_shift()
)

0 comments on commit 74241cd

Please sign in to comment.