Skip to content

Commit

Permalink
Replace local timezone with UTC (#606)
Browse files Browse the repository at this point in the history
* Replace local timezone with UTC

* Fixes

* Linting

* Fix pipeline

---------

Co-authored-by: Richard <rikroe@users.noreply.github.com>
  • Loading branch information
rikroe and rikroe authored Apr 30, 2024
1 parent e9d9f37 commit d9fbf14
Show file tree
Hide file tree
Showing 8 changed files with 19 additions and 65 deletions.
10 changes: 0 additions & 10 deletions bimmer_connected/account.py
Original file line number Diff line number Diff line change
Expand Up @@ -163,16 +163,6 @@ def get_stored_responses() -> List[AnonymizedResponse]:
RESPONSE_STORE.clear()
return responses

@property
def timezone(self):
"""Returns the current tzinfo."""
return datetime.datetime.now().astimezone().tzinfo

@property
def utcdiff(self):
"""Returns the difference to UTC in minutes."""
return round(self.timezone.utcoffset(datetime.datetime.now()).seconds / 60, 0)

@property
def refresh_token(self) -> Optional[str]:
"""Returns the current refresh_token."""
Expand Down
12 changes: 0 additions & 12 deletions bimmer_connected/tests/test_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@

import pytest
import respx
import time_machine

from bimmer_connected.api.utils import get_capture_position
from bimmer_connected.models import ChargingSettings, ValueWithUnit
Expand Down Expand Up @@ -72,17 +71,6 @@ def test_parse_datetime(caplog):
assert len(errors) == 1


@time_machine.travel(
datetime.datetime(2011, 11, 28, tzinfo=zoneinfo.ZoneInfo("America/Los_Angeles")),
tick=False,
)
@pytest.mark.asyncio
async def test_account_timezone(bmw_fixture: respx.Router):
"""Test the timezone in MyBMWAccount."""
account = await prepare_account_with_vehicles()
assert account.utcdiff == 960


def test_json_encoder():
"""Test the MyBMWJSONEncoder."""
encoded = json.dumps(
Expand Down
15 changes: 4 additions & 11 deletions bimmer_connected/tests/test_vehicle_status.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ async def test_generic_error_handling(caplog, bmw_fixture: respx.Router):
vehicle = account.get_vehicle(VIN_I20)
state_wo_climate_activity = vehicle.data["state"].copy()
state_wo_climate_activity["climateControlState"].pop("activity", None)
vehicle.climate = Climate(account_timezone=account.timezone)
vehicle.climate = Climate()

assert vehicle.climate.activity == ClimateActivityState.UNKNOWN
vehicle.update_state(vehicle.data, state_wo_climate_activity)
Expand Down Expand Up @@ -113,7 +113,6 @@ async def test_range_combustion(caplog, bmw_fixture: respx.Router):
assert (629, "km") == status.remaining_range_total

status_from_vehicle_data = FuelAndBattery.from_vehicle_data(vehicle.data)
status_from_vehicle_data.account_timezone = status.account_timezone
assert status_from_vehicle_data == status
assert FuelAndBattery.from_vehicle_data({}) is None

Expand Down Expand Up @@ -182,9 +181,7 @@ async def test_charging_end_time(caplog, bmw_fixture: respx.Router):
account = await prepare_account_with_vehicles()
vehicle = account.get_vehicle(VIN_I01_NOREX)

assert vehicle.fuel_and_battery.charging_end_time.astimezone(UTC) == datetime.datetime(
2021, 11, 28, 23, 27, 59, tzinfo=UTC
)
assert vehicle.fuel_and_battery.charging_end_time == datetime.datetime(2021, 11, 28, 23, 27, 59, tzinfo=UTC)
assert vehicle.fuel_and_battery.charging_status == ChargingState.CHARGING
assert vehicle.fuel_and_battery.is_charger_connected is True
assert vehicle.fuel_and_battery.charging_start_time is None
Expand All @@ -202,9 +199,7 @@ async def test_plugged_in_waiting_for_charge_window(caplog, bmw_fixture: respx.R
assert vehicle.fuel_and_battery.charging_end_time is None
assert vehicle.fuel_and_battery.charging_status == ChargingState.WAITING_FOR_CHARGING
assert vehicle.fuel_and_battery.is_charger_connected is True
assert vehicle.fuel_and_battery.charging_start_time.astimezone(UTC) == datetime.datetime(
2021, 11, 28, 18, 1, tzinfo=account.timezone
)
assert vehicle.fuel_and_battery.charging_start_time == datetime.datetime(2021, 11, 28, 18, 1, tzinfo=UTC)
assert vehicle.fuel_and_battery.charging_target == 100

assert len(get_deprecation_warning_count(caplog)) == 0
Expand Down Expand Up @@ -493,7 +488,5 @@ async def test_climate(bmw_fixture: respx.Router):
# Running climatization
climate = account.get_vehicle(VIN_G26).climate
assert climate.activity == ClimateActivityState.HEATING
assert climate.activity_end_time.astimezone(datetime.timezone.utc) == datetime.datetime(
2021, 11, 28, 21, 58, 49, tzinfo=UTC
)
assert climate.activity_end_time == datetime.datetime(2021, 11, 28, 21, 58, 49, tzinfo=UTC)
assert climate.is_climate_on is True
17 changes: 4 additions & 13 deletions bimmer_connected/vehicle/climate.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,23 +25,14 @@ class Climate(VehicleDataBase):
activity: ClimateActivityState = ClimateActivityState.UNKNOWN
"""Current climate activity state."""

activity_end_time_no_tz: Optional[datetime.datetime] = None
"""Climatization end time w/o timezone."""

account_timezone: datetime.timezone = datetime.timezone.utc
activity_end_time: Optional[datetime.datetime] = None
"""Climatization end time in UTC."""

@property
def is_climate_on(self) -> bool:
"""Return True if climatization is active."""
return self.activity in [ClimateActivityState.COOLING, ClimateActivityState.HEATING]

@property
def activity_end_time(self) -> Optional[datetime.datetime]:
"""Climatization end time."""
if self.activity_end_time_no_tz:
return self.activity_end_time_no_tz.astimezone(self.account_timezone)
return None

@classmethod
def _parse_vehicle_data(cls, vehicle_data: Dict) -> Dict:
"""Parse tire status."""
Expand All @@ -50,9 +41,9 @@ def _parse_vehicle_data(cls, vehicle_data: Dict) -> Dict:
if ATTR_STATE in vehicle_data:
if "climateControlState" in vehicle_data[ATTR_STATE]:
retval["activity"] = ClimateActivityState(vehicle_data[ATTR_STATE]["climateControlState"]["activity"])
retval["activity_end_time_no_tz"] = (
retval["activity_end_time"] = (
(
datetime.datetime.now()
datetime.datetime.now(datetime.timezone.utc)
+ datetime.timedelta(
seconds=int(vehicle_data[ATTR_STATE]["climateControlState"]["remainingSeconds"])
)
Expand Down
18 changes: 5 additions & 13 deletions bimmer_connected/vehicle/fuel_and_battery.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,8 +55,8 @@ class FuelAndBattery(VehicleDataBase):
charging_status: Optional[ChargingState] = None
"""Charging state of the vehicle."""

charging_start_time_no_tz: Optional[datetime.datetime] = None
"""The planned time the vehicle will start charging without time zone information."""
charging_start_time: Optional[datetime.datetime] = None
"""The planned time the vehicle will start charging in UTC."""

charging_end_time: Optional[datetime.datetime] = None
"""The estimated time the vehicle will have finished charging."""
Expand All @@ -67,15 +67,6 @@ class FuelAndBattery(VehicleDataBase):
charging_target: Optional[int] = None
"""State of charging target in percent."""

account_timezone: datetime.timezone = datetime.timezone.utc

@property
def charging_start_time(self) -> Optional[datetime.datetime]:
"""The planned time the vehicle will start charging."""
if self.charging_start_time_no_tz:
return self.charging_start_time_no_tz.astimezone(self.account_timezone)
return None

@classmethod
def from_vehicle_data(cls, vehicle_data: Dict):
"""Create the class based on vehicle data from API."""
Expand Down Expand Up @@ -175,9 +166,10 @@ def _parse_electric_data(
retval["charging_target"] = int(electric_data["chargingTarget"])

if retval["charging_status"] == ChargingState.WAITING_FOR_CHARGING and isinstance(charging_window, Dict):
retval["charging_start_time_no_tz"] = datetime.datetime.combine(
datetime.datetime.now().date(),
retval["charging_start_time"] = datetime.datetime.combine(
datetime.datetime.now(datetime.timezone.utc).date(),
datetime.time(int(charging_window["start"]["hour"]), int(charging_window["start"]["minute"])),
tzinfo=datetime.timezone.utc,
)

return retval
4 changes: 2 additions & 2 deletions bimmer_connected/vehicle/remote_services.py
Original file line number Diff line number Diff line change
Expand Up @@ -151,8 +151,8 @@ async def _block_until_done(self, client: MyBMWClient, event_id: str) -> RemoteS
:raises TimeoutError: if there is no final answer before _POLLING_TIMEOUT
"""

fail_after = datetime.datetime.now() + datetime.timedelta(seconds=_POLLING_TIMEOUT)
while datetime.datetime.now() < fail_after:
fail_after = datetime.datetime.now(datetime.timezone.utc) + datetime.timedelta(seconds=_POLLING_TIMEOUT)
while datetime.datetime.now(datetime.timezone.utc) < fail_after:
await asyncio.sleep(_POLLING_CYCLE)
status = await self._get_remote_service_status(client, event_id)
_LOGGER.debug("current state of '%s' is: %s", event_id, status.state.value)
Expand Down
6 changes: 3 additions & 3 deletions bimmer_connected/vehicle/vehicle.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,13 +81,13 @@ def __init__(
self.account = account
self.data = {}
self.remote_services = RemoteServices(self)
self.fuel_and_battery: FuelAndBattery = FuelAndBattery(account_timezone=account.timezone)
self.fuel_and_battery: FuelAndBattery = FuelAndBattery()
self.vehicle_location: VehicleLocation = VehicleLocation(account_region=account.region)
self.doors_and_windows: DoorsAndWindows = DoorsAndWindows()
self.condition_based_services: ConditionBasedServiceReport = ConditionBasedServiceReport()
self.headunit: Headunit = Headunit()
self.check_control_messages: CheckControlMessageReport = CheckControlMessageReport()
self.climate: Climate = Climate(account_timezone=account.timezone)
self.climate: Climate = Climate()
self.charging_profile: Optional[ChargingProfile] = None
self.tires: Optional[Tires] = None

Expand All @@ -104,7 +104,7 @@ async def get_vehicle_state(self) -> None:
state_response = await client.get(
VEHICLE_STATE_URL,
params={
"apptimezone": self.account.utcdiff,
"apptimezone": 0,
"appDateTime": int(fetched_at.timestamp() * 1000),
},
headers={
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ exclude = [
"bimmer_connected/coord_convert.py",
]

[tool.ruff.per-file-ignores]
[tool.ruff.lint.per-file-ignores]
"docs/source/conf.py" = ["D100"]
"bimmer_connected/api/authentication.py" = ["D102", "D107"]

Expand Down

0 comments on commit d9fbf14

Please sign in to comment.