Skip to content

Commit

Permalink
Migrate AirConditioning and Status to pydantic
Browse files Browse the repository at this point in the history
  • Loading branch information
Prior99 committed Sep 13, 2024
1 parent 8505d2f commit 51d5842
Show file tree
Hide file tree
Showing 5 changed files with 197 additions and 91 deletions.
95 changes: 71 additions & 24 deletions myskoda/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,14 @@
import asyncclick as click

from myskoda.models.charging import MaxChargeCurrent
from myskoda.models.common import OnOffState
from myskoda.models.common import (
ActiveState,
ChargerLockedState,
ConnectionState,
DoorLockedState,
OnOffState,
OpenState,
)
from myskoda.myskoda import MySkodaHub
from . import idk_authorize

Expand Down Expand Up @@ -72,13 +79,13 @@ async def status(vin):
status = await hub.get_status(vin)

print(
f"{colored("doors:", "blue")} {open(status.doors_open)}, {locked(status.doors_locked)}"
f"{colored("doors:", "blue")} {open(status.overall.doors)}, {locked(status.overall.doors_locked)}"
)
print(f"{colored("bonnet:", "blue")} {open(status.bonnet_open)}")
print(f"{colored("trunk:", "blue")} {open(status.trunk_open)}")
print(f"{colored("windows:", "blue")} {open(status.windows_open)}")
print(f"{colored("lights:", "blue")} {on(status.lights_on)}")
print(f"{colored("last update:", "blue")} {status.car_captured}")
print(f"{colored("bonnet:", "blue")} {open(status.detail.bonnet)}")
print(f"{colored("trunk:", "blue")} {open(status.detail.trunk)}")
print(f"{colored("windows:", "blue")} {open(status.overall.windows)}")
print(f"{colored("lights:", "blue")} {on(status.overall.lights)}")
print(f"{colored("last update:", "blue")} {status.car_captured_timestamp}")


@cli.command()
Expand All @@ -90,26 +97,28 @@ async def air_conditioning(vin):
await hub.authenticate(username, password)
ac = await hub.get_air_conditioning(vin)

print(f"{colored("window heating:", "blue")} {on(ac.window_heating_enabled)}")
print(
f"{colored("window heating (front):", "blue")} {on(ac.window_heating_front_on)}"
f"{colored("window heating:", "blue")} {bool_state(ac.window_heating_enabled)}"
)
print(
f"{colored("window heating (back):", "blue")} {on(ac.window_heating_rear_on)}"
f"{colored("window heating (front):", "blue")} {on(ac.window_heating_state.front)}"
)
print(
f"{colored("target temperature:", "blue")} {ac.target_temperature_celsius}°C"
f"{colored("window heating (back):", "blue")} {on(ac.window_heating_state.rear)}"
)
print(
f"{colored("target temperature:", "blue")} {ac.target_temperature.temperature_value}°C"
)
print(
f"{colored("steering wheel position:", "blue")} {ac.steering_wheel_position}"
)
print(f"{colored("air conditioning:", "blue")} {on(ac.air_conditioning_on)}")
print(f"{colored("air conditioning:", "blue")} {on(ac.state)}")
print(f"{colored("state:", "blue")} {ac.state}")
print(
f"{colored("charger:", "blue")} {connected(ac.charger_connected)}, {locked(ac.charger_locked)}"
f"{colored("charger:", "blue")} {connected(ac.charger_connection_state)}, {charger_locked(ac.charger_lock_state)}"
)
print(
f"{colored("temperature reached:", "blue")} {ac.time_to_reach_target_temperature}"
f"{colored("temperature reached:", "blue")} {ac.estimated_date_time_to_reach_target_temperature}"
)


Expand Down Expand Up @@ -171,24 +180,62 @@ async def charging(vin):
)
print(f"{colored("state:", "blue")} {charging.status.state}")
print(
f"{colored("battery care mode:", "blue")} {on(charging.settings.charging_care_mode is OnOffState.ON)}"
f"{colored("battery care mode:", "blue")} {active(charging.settings.charging_care_mode)}"
)
print(
f"{colored("reduced current:", "blue")} {on(charging.settings.max_charge_current_ac is MaxChargeCurrent.REDUCED)}"
f"{colored("current:", "blue")} {charge_current(charging.settings.max_charge_current_ac)}"
)


def open(cond: bool) -> str:
return colored("open", "red") if cond else colored("closed", "green")
def open(cond: OpenState) -> str:
return (
colored("open", "red") if cond == OpenState.OPEN else colored("closed", "green")
)


def locked(cond: DoorLockedState) -> str:
return (
colored("locked", "green")
if cond == DoorLockedState.LOCKED
else colored("unlocked", "red")
)


def on(cond: OnOffState) -> str:
return colored("on", "green") if cond == OnOffState.ON else colored("off", "red")


def connected(cond: ConnectionState) -> str:
return (
colored("connected", "green")
if cond == ConnectionState.CONNECTED
else colored("disconnected", "red")
)


def active(cond: ActiveState) -> str:
return (
colored("active", "green")
if cond == ActiveState.ACTIVATED
else colored("inactive", "red")
)


def locked(cond: bool) -> str:
return colored("locked", "green") if cond else colored("unlocked", "red")
def charge_current(cond: MaxChargeCurrent) -> str:
return (
colored("maximum", "green")
if cond == MaxChargeCurrent.MAXIMUM
else colored("reduced", "red")
)


def on(cond: bool) -> str:
return colored("on", "green") if cond else colored("off", "red")
def bool_state(cond: bool) -> str:
return colored("true", "green") if cond else colored("false", "red")


def connected(cond: bool) -> str:
return colored("connected", "green") if cond else colored("disconnected", "red")
def charger_locked(cond: ChargerLockedState) -> str:
return (
colored("locked", "green")
if cond == ChargerLockedState.LOCKED
else colored("unlocked", "red")
)
70 changes: 70 additions & 0 deletions myskoda/models/air_conditioning.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
from datetime import datetime, time
from enum import Enum
from pydantic import BaseModel, Field
from typing import Any

from myskoda.models.common import ChargerLockedState, ConnectionState, OnOffState, Side


class Weekday(str, Enum):
MONDAY = "MONDAY"
TUESDAY = "TUESDAY"
WEDNESDAY = "WEDNESDAY"
THURSDAY = "THURSDAY"
FRIDAY = "FRIDAY"
SATURDAY = "SATURDAY"
SUNDAY = "SUNDAY"


class TemperatureUnit(str, Enum):
CELSIUS = "CELSIUS"


class TimerMode(str, Enum):
ONE_OFF = "ONE_OFF"


class Timer(BaseModel):
enabled: bool
id: int
selected_days: list[Weekday] = Field(None, alias="selectedDays")
time: time
type: TimerMode


class SeatHeating(BaseModel):
front_left: bool = Field(None, alias="frontLeft")
front_right: bool = Field(None, alias="frontRight")


class TargetTemperature(BaseModel):
temperature_value: float = Field(None, alias="temperatureValue")
unit_in_car: TemperatureUnit = Field(None, alias="unitInCar")


class WindowHeatingState(BaseModel):
front: OnOffState
rear: OnOffState
unspecified: Any


class AirConditioning(BaseModel):
"""Information related to air conditioning."""

air_conditioning_at_unlock: bool = Field(None, alias="airConditioningAtUnlock")
car_captured_timestamp: datetime = Field(None, alias="carCapturedTimestamp")
charger_connection_state: ConnectionState = Field(
None, alias="chargerConnectionState"
)
charger_lock_state: ChargerLockedState = Field(None, alias="chargerLockState")
errors: list[Any]
estimated_date_time_to_reach_target_temperature: datetime = Field(
None, alias="estimatedDateTimeToReachTargetTemperature"
)
seat_heating_activated: SeatHeating = Field(None, alias="seatHeatingActivated")
state: OnOffState
steering_wheel_position: Side = Field(None, alias="steeringWheelPosition")
target_temperature: TargetTemperature = Field(None, alias="targetTemperature")
timers: list[Timer]
window_heating_enabled: bool = Field(None, alias="windowHeatingEnabled")
window_heating_state: WindowHeatingState = Field(None, alias="windowHeatingState")
26 changes: 26 additions & 0 deletions myskoda/models/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,29 @@ class EnabledState(str, Enum):
class ActiveState(str, Enum):
ACTIVATED = "ACTIVATED"
DEACTIVATED = "DEACTIVATED"


class OpenState(str, Enum):
OPEN = "OPEN"
CLOSED = "CLOSED"
UNSUPPORTED = "UNSUPPORTED"


class DoorLockedState(str, Enum):
LOCKED = "YES"
UNLOCKED = "NO"


class ChargerLockedState(str, Enum):
LOCKED = "LOCKED"
UNLOCKED = "UNLOCKED"


class ConnectionState(str, Enum):
CONNECTED = "CONNECTED"
DISCONNECTED = "DISCONNECTED"


class Side(str, Enum):
LEFT = "LEFT"
RIGHT = "RIGHT"
26 changes: 26 additions & 0 deletions myskoda/models/status.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
from datetime import datetime
from pydantic import BaseModel, Field

from myskoda.models.common import DoorLockedState, OnOffState, OpenState


class Detail(BaseModel):
bonnet: OpenState
sunroof: OpenState
trunk: OpenState


class Overall(BaseModel):
doors: OpenState
doors_locked: DoorLockedState = Field(None, alias="doorsLocked")
lights: OnOffState
locked: DoorLockedState
windows: OpenState


class Status(BaseModel):
"""Current status information for a vehicle."""

car_captured_timestamp: datetime = Field(None, alias="carCapturedTimestamp")
detail: Detail
overall: Overall
71 changes: 4 additions & 67 deletions myskoda/myskoda.py
Original file line number Diff line number Diff line change
@@ -1,84 +1,21 @@
"""Contains API representation for the MySkoda REST API."""

from asyncio import gather
from datetime import datetime
import logging

from aiohttp import ClientSession

from .authorization import IDKSession, idk_authorize
from .const import BASE_URL_SKODA
from .models.air_conditioning import AirConditioning
from .models.charging import Charging
from .models.health import Health
from .models.info import Info
from .models.status import Status

_LOGGER = logging.getLogger(__name__)


class Status:
"""Current status information for a vehicle."""

doors_open: bool
bonnet_open: bool
trunk_open: bool
doors_locked: bool
lights_on: bool
locked: bool
windows_open: bool
car_captured: datetime

def __init__(self, dict): # noqa: D107
self.bonnet_open = dict.get("detail", {}).get("bonnet") == "OPEN"
self.doors_open = dict.get("overall", {}).get("doors") == "OPEN"
self.trunk_open = dict.get("detail", {}).get("trunk") == "OPEN"
self.doors_locked = dict.get("overall", {}).get("doorsLocked") == "YES"
self.lights_on = dict.get("overall", {}).get("lights") == "ON"
self.locked = dict.get("overall", {}).get("locked") == "YES"
self.windows_open = dict.get("overall", {}).get("windows") == "OPEN"
self.car_captured = datetime.fromisoformat(dict.get("carCapturedTimestamp"))


class AirConditioning:
"""Information related to air conditioning."""

window_heating_enabled: bool
window_heating_front_on: bool
window_heating_rear_on: bool
target_temperature_celsius: float
steering_wheel_position: str
air_conditioning_on: bool
state: str
charger_connected: bool
charger_locked: bool
time_to_reach_target_temperature: str

def __init__(self, dict): # noqa: D107
self.window_heating_enabled = dict.get("windowHeatingEnabled")
self.window_heating_front_on = (
dict.get("windowHeatingState", {}).get("front") == "ON"
)
self.window_heating_rear_on = (
dict.get("windowHeatingState", {}).get("rear") == "ON"
)
self.target_temperature_celsius = dict.get("targetTemperature", {}).get(
"temperatureValue"
)
self.steering_wheel_position = dict.get("steeringWheelPosition")
self.air_conditioning_on = (
dict.get("state") == "ON"
or dict.get("state") == "COOLING"
or dict.get("state") == "HEATING"
)
# COOLING, HEATING, OFF, ON
self.state = dict.get("state")

self.charger_connected = dict.get("chargerConnectionState") == "CONNECTED"
self.charger_locked = dict.get("chargerLockState") == "LOCKED"
self.time_to_reach_target_temperature = dict.get(
"estimatedDateTimeToReachTargetTemperature"
)


class Position:
"""Positional information (GPS) for the vehicle."""

Expand Down Expand Up @@ -175,7 +112,7 @@ async def get_status(self, vin):
headers=await self._headers(),
) as response:
_LOGGER.debug("vin %s: Received status")
return Status(await response.json())
return Status(**await response.json())

async def get_air_conditioning(self, vin):
"""Retrieve the current air conditioning status for the specified vehicle."""
Expand All @@ -184,7 +121,7 @@ async def get_air_conditioning(self, vin):
headers=await self._headers(),
) as response:
_LOGGER.debug("vin %s: Received air conditioning")
return AirConditioning(await response.json())
return AirConditioning(**await response.json())

async def get_position(self, vin):
"""Retrieve the current position for the specified vehicle."""
Expand Down

0 comments on commit 51d5842

Please sign in to comment.