diff --git a/.gitignore b/.gitignore index 6b75623..2933378 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ .idea -venv \ No newline at end of file +venv +src/settings.dev.json \ No newline at end of file diff --git a/src/config.py b/src/config.py index 4c31b47..a7aab89 100644 --- a/src/config.py +++ b/src/config.py @@ -2,5 +2,5 @@ from dynaconf import Dynaconf settings = Dynaconf( envvar_prefix="CONF", - settings_files=["settings.json", "/data/options.json"], + settings_files=["settings.json", "/data/options.json", "settings.dev.json"], ) \ No newline at end of file diff --git a/src/config.yaml b/src/config.yaml index f52cef6..5910d1d 100644 --- a/src/config.yaml +++ b/src/config.yaml @@ -1,6 +1,6 @@ name: "Volvo2Mqtt" description: "Volvo AAOS MQTT bridge" -version: "1.1.1" +version: "1.1.2" slug: "volvo2mqtt" init: false url: "https://github.com/Dielee/volvo2mqtt" diff --git a/src/const.py b/src/const.py index 160dd3e..a63a2f9 100644 --- a/src/const.py +++ b/src/const.py @@ -1,4 +1,4 @@ -VERSION = "v1.1.1" +VERSION = "v1.1.2" OAUTH_URL = "https://volvoid.eu.volvocars.com/as/token.oauth2" VEHICLES_URL = "https://api.volvocars.com/connected-vehicle/v1/vehicles" diff --git a/src/mqtt.py b/src/mqtt.py index 6c4507c..a9163b9 100644 --- a/src/mqtt.py +++ b/src/mqtt.py @@ -6,7 +6,7 @@ from threading import Thread, Timer from datetime import datetime from babel.dates import format_datetime from config import settings -from const import VEHICLE_DETAILS_URL, CLIMATE_START_URL, CLIMATE_STOP_URL, CAR_LOCK_URL, \ +from const import CLIMATE_START_URL, CLIMATE_STOP_URL, CAR_LOCK_URL, \ CAR_UNLOCK_URL, supported_sensors, supported_buttons, supported_switches, supported_locks @@ -14,7 +14,7 @@ mqtt_client: mqtt.Client subscribed_topics = [] assumed_climate_state = {} last_data_update = None -climate_timer_thread: Thread +climate_timer: Timer def connect(): @@ -46,24 +46,22 @@ def on_message(client, userdata, msg): vin = msg.topic.split('/')[2].split('_')[0] payload = msg.payload.decode("UTF-8") if "climate_status" in msg.topic: - global assumed_climate_state + global assumed_climate_state, climate_timer if payload == "ON": api_thread = Thread(target=volvo.api_call, args=(CLIMATE_START_URL, "POST", vin)) api_thread.start() assumed_climate_state[vin] = "ON" # Starting timer to disable climate after 30 mins - global climate_timer_thread - climate_timer_thread = Timer(30 * 60, volvo.disable_climate, (vin, )) - climate_timer_thread.start() + climate_timer = Timer(1 * 60, volvo.disable_climate, (vin, )) + climate_timer.start() update_car_data() elif payload == "OFF": api_thread = Thread(target=volvo.api_call, args=(CLIMATE_STOP_URL, "POST", vin)) api_thread.start() assumed_climate_state[vin] = "OFF" # Stop timer if active - global climate_timer_thread - if climate_timer_thread.is_alive(): - climate_timer_thread.cancel() + if climate_timer.is_alive(): + climate_timer.cancel() update_car_data() elif "lock_status" in msg.topic: if payload == "LOCK": @@ -121,7 +119,7 @@ def update_car_data(): def create_ha_devices(): for vin in volvo.vins: - car_details = volvo.api_call(VEHICLE_DETAILS_URL, "GET", vin) + device = volvo.get_vehicle_details(vin) for button in supported_buttons: command_topic = f"homeassistant/button/{vin}_{button['id']}/command" @@ -132,12 +130,7 @@ def create_ha_devices(): "icon": f"mdi:{button['icon']}", "state_topic": f"homeassistant/button/{vin}_{button['id']}/state", "command_topic": command_topic, - "device": { - "identifiers": [f"volvoAAOS2mqtt_{vin}"], - "manufacturer": "Volvo", - "model": car_details['descriptions']['model'], - "name": f"{car_details['descriptions']['model']} ({car_details['modelYear']}) - {vin}", - }, + "device": device, "unique_id": f"volvoAAOS2mqtt_{vin}_{button['id']}", } mqtt_client.publish( @@ -157,12 +150,7 @@ def create_ha_devices(): "state_topic": f"homeassistant/lock/{vin}_{lock['id']}/state", "command_topic": command_topic, "optimistic": False, - "device": { - "identifiers": [f"volvoAAOS2mqtt_{vin}"], - "manufacturer": "Volvo", - "model": car_details['descriptions']['model'], - "name": f"{car_details['descriptions']['model']} ({car_details['modelYear']}) - {vin}", - }, + "device": device, "unique_id": f"volvoAAOS2mqtt_{vin}_{lock['id']}", } mqtt_client.publish( @@ -182,12 +170,7 @@ def create_ha_devices(): "state_topic": f"homeassistant/switch/{vin}_{switch['id']}/state", "command_topic": command_topic, "optimistic": False, - "device": { - "identifiers": [f"volvoAAOS2mqtt_{vin}"], - "manufacturer": "Volvo", - "model": car_details['descriptions']['model'], - "name": f"{car_details['descriptions']['model']} ({car_details['modelYear']}) - {vin}", - }, + "device": device, "unique_id": f"volvoAAOS2mqtt_{vin}_{switch['id']}", } mqtt_client.publish( @@ -204,12 +187,7 @@ def create_ha_devices(): "schema": "state", "icon": f"mdi:{sensor['icon']}", "state_topic": f"homeassistant/sensor/{vin}_{sensor['id']}/state", - "device": { - "identifiers": [f"volvoAAOS2mqtt_{vin}"], - "manufacturer": "Volvo", - "model": car_details['descriptions']['model'], - "name": f"{car_details['descriptions']['model']} ({car_details['modelYear']}) - {vin}", - }, + "device": device, "unique_id": f"volvoAAOS2mqtt_{vin}_{sensor['id']}", } if "unit" in sensor: diff --git a/src/volvo.py b/src/volvo.py index db510a4..1b05c53 100644 --- a/src/volvo.py +++ b/src/volvo.py @@ -99,6 +99,34 @@ def get_vehicles(): print("Vin: " + str(vins) + " found!") +def get_vehicle_details(vin): + response = session.get(VEHICLE_DETAILS_URL.format(vin), timeout=15) + if response.status_code == 200: + data = response.json()["data"] + if "debug" in settings: + if settings["debug"]: + print(response.text) + device = { + "identifiers": [f"volvoAAOS2mqtt_{vin}"], + "manufacturer": "Volvo", + "model": data['descriptions']['model'], + "name": f"{data['descriptions']['model']} ({data['modelYear']}) - {vin}", + } + elif response.status_code == 500 and not settings.volvoData["vin"]: + # Workaround for some cars that are not returning vehicle details + device = { + "identifiers": [f"volvoAAOS2mqtt_{vin}"], + "manufacturer": "Volvo", + "model": vin, + "name": f"Volvo - {vin}", + } + else: + raise Exception("Getting vehicle details failed. Status Code: " + str(response.status_code) + + ". Error: " + response.text) + + return device + + def initialize_climate(vins): for vin in vins: mqtt.assumed_climate_state[vin] = "OFF" @@ -157,13 +185,11 @@ def api_call(url, method, vin, sensor_id=None): else: print("API Call failed. Status Code: " + str(response.status_code) + ". Error: " + response.text) return "" - return parse_api_data(url, data, sensor_id) + return parse_api_data(data, sensor_id) -def parse_api_data(url, data, sensor_id=None): - if url == VEHICLE_DETAILS_URL: - return data["data"] - elif sensor_id == "battery_charge_level": +def parse_api_data(data, sensor_id=None): + if sensor_id == "battery_charge_level": if "batteryChargeLevel" in data["data"]: return data["data"]["batteryChargeLevel"]["value"] else: