Skip to content

Commit

Permalink
Implement waiting for operations
Browse files Browse the repository at this point in the history
  • Loading branch information
Prior99 committed Sep 17, 2024
1 parent b831f67 commit b792d88
Show file tree
Hide file tree
Showing 5 changed files with 274 additions and 244 deletions.
99 changes: 50 additions & 49 deletions myskoda/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
from myskoda.models.mqtt import OperationStatus

from . import idk_authorize
from .event import Event, EventName
from .event import Event, EventType, ServiceEventTopic
from .models.charging import MaxChargeCurrent
from .models.common import (
ActiveState,
Expand Down Expand Up @@ -314,8 +314,28 @@ async def trip_statistics(ctx: Context, vin: str) -> None:


@cli.command()
@click.argument("trace_id")
@click.pass_context
async def mqtt(ctx: Context) -> None:
async def wait_for_operation(ctx: Context, trace_id: str) -> None:
"""Wait for the operation with the specified trace id to complete."""
async with ClientSession() as session:
hub = RestApi(session)
await hub.authenticate(ctx.obj["username"], ctx.obj["password"])

mqtt = MQTT(hub)
await mqtt.connect()

mqtt.loop_start()

await mqtt.wait_for_operation(trace_id)
print("Completed.")

mqtt.loop_stop()


@cli.command()
@click.pass_context
async def subscribe(ctx: Context) -> None:
"""Connect to the MQTT broker and listen for messages."""
async with ClientSession() as session:
hub = RestApi(session)
Expand All @@ -325,54 +345,35 @@ async def mqtt(ctx: Context) -> None:
await mqtt.connect()

def on_event(event: Event) -> None:
print(f"{colored("- name:", "blue")} {event.name}")
print(f"{colored("- type:", "blue")} {event.type}")
print(f"{colored(" vin:", "blue")} {event.vin}")
print(f"{colored(" payload:", "blue")}")
match event.name:
case (
EventName.UPDATE_BATTERY_SUPPORT
| EventName.LOCK_VEHICLE
| EventName.WAKEUP
| EventName.SET_TARGET_TEMPERATURE
| EventName.START_STOP_AIR_CONDITIONING
| EventName.START_STOP_WINDOW_HEATING
| EventName.START_STOP_CHARGING
| EventName.HONK_AND_FLASH
| EventName.APPLY_BACKUP
):
payload = event.payload
print(f" {colored("version:", "blue")} {payload.version}")
print(f" {colored("trace id:", "blue")} {payload.trace_id}")
print(f" {colored("request id:", "blue")} {payload.request_id}")
print(f" {colored("operation:", "blue")} {payload.operation}")
print(f" {colored("status:", "blue")} {payload.status}")
if status == OperationStatus.ERROR:
print(f" {colored("error code:", "blue")} {payload.error_code}")
case (
EventName.AIR_CONDITIONING
| EventName.ACCESS
| EventName.LIGHTS
| EventName.CHARGING
):
payload = event.payload
data = payload.data
print(f" {colored("version:", "blue")} {payload.version}")
print(f" {colored("trace id:", "blue")} {payload.trace_id}")
print(f" {colored("timestamp:", "blue")} {payload.timestamp}")
print(f" {colored("producer:", "blue")} {payload.producer}")
print(f" {colored("name:", "blue")} {payload.name}")
print(f" {colored("vin:", "blue")} {data.vin}")
print(f" {colored("user id:", "blue")} {data.user_id}")

if event.name == EventName.CHARGING:
data = event.payload.data
print(f" {colored("mode:", "blue")} {data.mode}")
print(f" {colored("state:", "blue")} {data.state}")
print(f" {colored("soc:", "blue")} {data.soc}%")
print(f" {colored("charged range:", "blue")} {data.charged_range}km")
print(
f" {colored("time to finish:", "blue")} {data.time_to_finish}min"
)
if event.type == EventType.OPERATION:
operation = event.operation
print(f" {colored("version:", "blue")} {operation.version}")
print(f" {colored("trace id:", "blue")} {operation.trace_id}")
print(f" {colored("request id:", "blue")} {operation.request_id}")
print(f" {colored("operation:", "blue")} {operation.operation}")
print(f" {colored("status:", "blue")} {operation.status}")
if status == OperationStatus.ERROR:
print(f" {colored("error code:", "blue")} {operation.error_code}")
elif event.type == EventType.SERVICE_EVENT:
data = event.event.data
print(f" {colored("version:", "blue")} {event.event.version}")
print(f" {colored("trace id:", "blue")} {event.event.trace_id}")
print(f" {colored("timestamp:", "blue")} {event.event.timestamp}")
print(f" {colored("producer:", "blue")} {event.event.producer}")
print(f" {colored("name:", "blue")} {event.event.name}")
print(f" {colored("vin:", "blue")} {data.vin}")
print(f" {colored("user id:", "blue")} {data.user_id}")
if event.topic == ServiceEventTopic.CHARGING:
data = event.payload.data
print(f" {colored("mode:", "blue")} {data.mode}")
print(f" {colored("state:", "blue")} {data.state}")
print(f" {colored("soc:", "blue")} {data.soc}%")
print(f" {colored("charged range:", "blue")} {data.charged_range}km")
print(
f" {colored("time to finish:", "blue")} {data.time_to_finish}min"
)

mqtt.subscribe(on_event)
print(f"{colored("Listening for MQTT events:", "blue")}")
Expand Down
36 changes: 36 additions & 0 deletions myskoda/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,39 @@

MQTT_BROKER_HOST = "mqtt.messagehub.de"
MQTT_BROKER_PORT = 8883


MQTT_OPERATION_TOPICS = [
"air-conditioning/set-air-conditioning-at-unlock",
"air-conditioning/set-air-conditioning-seats-heating",
"air-conditioning/set-air-conditioning-timers",
"air-conditioning/set-air-conditioning-without-external-power",
"air-conditioning/set-target-temperature",
"air-conditioning/start-stop-air-conditioning",
"air-conditioning/start-stop-auxiliary-heating",
"air-conditioning/start-stop-window-heating",
"air-conditioning/windows-heating",
"charging/start-stop-charging",
"charging/update-battery-support",
"charging/update-auto-unlock-plug",
"charging/update-care-mode",
"charging/update-charge-limit",
"charging/update-charge-mode",
"charging/update-charging-profiles",
"charging/update-charging-current",
"vehicle-access/honk-and-flash",
"vehicle-access/lock-vehicle",
"vehicle-services-backup/apply-backup",
"vehicle-wakeup/wakeup",
]

MQTT_SERVICE_EVENT_TOPICS = [
"air-conditioning",
"charging",
"vehicle-status/access",
"vehicle-status/lights",
]

MQTT_ACCOUNT_EVENT_TOPICS = [
"account-event/privacy",
]
156 changes: 33 additions & 123 deletions myskoda/event.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,21 +7,21 @@
from .models.mqtt import OperationRequest, ServiceEvent, ServiceEventCharging


class EventName(StrEnum):
class ServiceEventTopic(StrEnum):
ACCESS = "ACCESS"
ACCOUNT_PRIVACY = "ACCOUNT_PRIVACY"
AIR_CONDITIONING = "AIR_CONDITIONING"
APPLY_BACKUP = "APPLY_BACKUP"
CHARGING = "CHARGING"
HONK_AND_FLASH = "HONK_AND_FLASH"
LIGHTS = "LIGHTS"
LOCK_VEHICLE = "LOCK_VEHICLE"
SET_TARGET_TEMPERATURE = "SET_TARGET_TEMPERATURE"
START_STOP_AIR_CONDITIONING = "START_STOP_AIR_CONDITIONING"
START_STOP_CHARGING = "START_STOP_CHARGING"
START_STOP_WINDOW_HEATING = "START_STOP_WINDOW_HEATING"
UPDATE_BATTERY_SUPPORT = "UPDATE_BATTERY_SUPPORT"
WAKEUP = "WAKEUP"


class AccountEventTopic(StrEnum):
ACCOUNT_PRIVACY = "ACCOUNT_PRIVACY"


class EventType(StrEnum):
ACCOUNT_EVENT = "account-event"
OPERATION = "operation-request"
SERVICE_EVENT = "service-event"


class BaseEvent:
Expand All @@ -35,158 +35,68 @@ def __init__(self, vin: str, user_id: str) -> None: # noqa: D107
self.timestamp = datetime.now(tz=UTC)


class EventUpdateBatterySupport(BaseEvent):
name: Literal[EventName.UPDATE_BATTERY_SUPPORT]
payload: OperationRequest

def __init__(self, vin: str, user_id: str, payload: dict) -> None: # noqa: D107
super().__init__(vin, user_id)
self.name = EventName.UPDATE_BATTERY_SUPPORT
self.payload = OperationRequest(**payload)


class EventLockVehicle(BaseEvent):
name: Literal[EventName.LOCK_VEHICLE]
payload: OperationRequest

def __init__(self, vin: str, user_id: str, payload: dict) -> None: # noqa: D107
super().__init__(vin, user_id)
self.name = EventName.LOCK_VEHICLE
self.payload = OperationRequest(**payload)


class EventWakeup(BaseEvent):
name: Literal[EventName.WAKEUP]
payload: OperationRequest

def __init__(self, vin: str, user_id: str, payload: dict) -> None: # noqa: D107
super().__init__(vin, user_id)
self.name = EventName.WAKEUP
self.payload = OperationRequest(**payload)


class EventSetTargetTemperature(BaseEvent):
name: Literal[EventName.SET_TARGET_TEMPERATURE]
payload: OperationRequest

def __init__(self, vin: str, user_id: str, payload: dict) -> None: # noqa: D107
super().__init__(vin, user_id)
self.name = EventName.SET_TARGET_TEMPERATURE
self.payload = OperationRequest(**payload)
class EventOperation(BaseEvent):
type: Literal[EventType.OPERATION] = EventType.OPERATION
operation: OperationRequest


class EventStartStopAirConditioning(BaseEvent):
name: Literal[EventName.START_STOP_AIR_CONDITIONING]
payload: OperationRequest

def __init__(self, vin: str, user_id: str, payload: dict) -> None: # noqa: D107
super().__init__(vin, user_id)
self.name = EventName.START_STOP_AIR_CONDITIONING
self.payload = OperationRequest(**payload)


class EventStartStopWindowHeating(BaseEvent):
name: Literal[EventName.START_STOP_WINDOW_HEATING]
payload: OperationRequest

def __init__(self, vin: str, user_id: str, payload: dict) -> None: # noqa: D107
super().__init__(vin, user_id)
self.name = EventName.START_STOP_WINDOW_HEATING
self.payload = OperationRequest(**payload)


class EventStartStopCharging(BaseEvent):
name: Literal[EventName.START_STOP_CHARGING]
payload: OperationRequest

def __init__(self, vin: str, user_id: str, payload: dict) -> None: # noqa: D107
super().__init__(vin, user_id)
self.name = EventName.START_STOP_CHARGING
self.payload = OperationRequest(**payload)


class EventHonkAndFlash(BaseEvent):
name: Literal[EventName.HONK_AND_FLASH]
payload: OperationRequest

def __init__(self, vin: str, user_id: str, payload: dict) -> None: # noqa: D107
super().__init__(vin, user_id)
self.name = EventName.HONK_AND_FLASH
self.payload = OperationRequest(**payload)


class EventApplyBackup(BaseEvent):
name: Literal[EventName.APPLY_BACKUP]
payload: OperationRequest

def __init__(self, vin: str, user_id: str, payload: dict) -> None: # noqa: D107
def __init__(self, vin: str, user_id: str, operation: dict) -> None: # noqa: D107
super().__init__(vin, user_id)
self.name = EventName.APPLY_BACKUP
self.payload = OperationRequest(**payload)
self.operation = OperationRequest(**operation)


class EventAirConditioning(BaseEvent):
name: Literal[EventName.AIR_CONDITIONING]
payload: ServiceEvent
type: Literal[EventType.SERVICE_EVENT] = EventType.SERVICE_EVENT
topic: Literal[ServiceEventTopic.AIR_CONDITIONING] = ServiceEventTopic.AIR_CONDITIONING
event: ServiceEvent

def __init__(self, vin: str, user_id: str, payload: dict) -> None: # noqa: D107
super().__init__(vin, user_id)
self.name = EventName.AIR_CONDITIONING
self.payload = ServiceEvent(**payload)


class EventCharging(BaseEvent):
name: Literal[EventName.CHARGING]
payload: ServiceEventCharging
type: Literal[EventType.SERVICE_EVENT] = EventType.SERVICE_EVENT
topic: Literal[ServiceEventTopic.CHARGING] = ServiceEventTopic.CHARGING
event: ServiceEventCharging

def __init__(self, vin: str, user_id: str, payload: dict) -> None: # noqa: D107
super().__init__(vin, user_id)
self.name = EventName.CHARGING
self.payload = ServiceEventCharging(**payload)


class EventAccess(BaseEvent):
name: Literal[EventName.ACCESS]
payload: ServiceEvent
type: Literal[EventType.SERVICE_EVENT] = EventType.SERVICE_EVENT
topic: Literal[ServiceEventTopic.ACCESS] = ServiceEventTopic.ACCESS
event: ServiceEvent

def __init__(self, vin: str, user_id: str, payload: dict) -> None: # noqa: D107
super().__init__(vin, user_id)
self.name = EventName.ACCESS
self.payload = ServiceEvent(**payload)


class EventLights(BaseEvent):
name: Literal[EventName.LIGHTS]
payload: ServiceEvent
type: Literal[EventType.SERVICE_EVENT] = EventType.SERVICE_EVENT
topic: Literal[ServiceEventTopic.LIGHTS] = ServiceEventTopic.LIGHTS
event: ServiceEvent

def __init__(self, vin: str, user_id: str, payload: dict) -> None: # noqa: D107
super().__init__(vin, user_id)
self.name = EventName.LIGHTS
self.payload = ServiceEvent(**payload)


class EventAccountPrivacy(BaseEvent):
name: Literal[EventName.ACCOUNT_PRIVACY]
type: Literal[EventType.ACCOUNT_EVENT] = EventType.ACCOUNT_EVENT
topic: Literal[AccountEventTopic.ACCOUNT_PRIVACY] = AccountEventTopic.ACCOUNT_PRIVACY

def __init__(self, vin: str, user_id: str, _payload: dict) -> None: # noqa: D107
super().__init__(vin, user_id)
self.name = EventName.ACCOUNT_PRIVACY


Event = (
EventAccess
| EventAccountPrivacy
EventAccountPrivacy
| EventOperation
| EventAccess
| EventAirConditioning
| EventApplyBackup
| EventApplyBackup
| EventCharging
| EventHonkAndFlash
| EventLights
| EventLockVehicle
| EventSetTargetTemperature
| EventStartStopAirConditioning
| EventStartStopCharging
| EventStartStopWindowHeating
| EventUpdateBatterySupport
| EventWakeup
)
Loading

0 comments on commit b792d88

Please sign in to comment.