diff --git a/src/const.py b/src/const.py index 60a9c4c..97f29ff 100644 --- a/src/const.py +++ b/src/const.py @@ -8,7 +8,7 @@ VEHICLE_DETAILS_URL = "https://api.volvocars.com/connected-vehicle/v1/vehicles/{ WINDOWS_STATE_URL = "https://api.volvocars.com/connected-vehicle/v2/vehicles/{0}/windows" CLIMATE_START_URL = "https://api.volvocars.com/connected-vehicle/v2/vehicles/{0}/commands/climatization-start" CLIMATE_STOP_URL = "https://api.volvocars.com/connected-vehicle/v2/vehicles/{0}/commands/climatization-stop" -CAR_LOCK_STATE_URL = "https://api.volvocars.com/connected-vehicle/v2/vehicles/{0}/doors" +LOCK_STATE_URL = "https://api.volvocars.com/connected-vehicle/v2/vehicles/{0}/doors" CAR_LOCK_URL = "https://api.volvocars.com/connected-vehicle/v2/vehicles/{0}/commands/lock" CAR_UNLOCK_URL = "https://api.volvocars.com/connected-vehicle/v2/vehicles/{0}/commands/unlock" RECHARGE_STATE_URL = "https://api.volvocars.com/energy/v1/vehicles/{0}/recharge-status" @@ -28,7 +28,11 @@ supported_sensors = [ {"name": "Window Front Left", "id": "window_front_left", "icon": "car-door-lock", "url": WINDOWS_STATE_URL}, {"name": "Window Front Right", "id": "window_front_right", "icon": "car-door-lock", "url": WINDOWS_STATE_URL}, {"name": "Window Rear Left", "id": "window_rear_left", "icon": "car-door-lock", "url": WINDOWS_STATE_URL}, - {"name": "Window Rear Right", "id": "window_rear_right", "icon": "car-door-lock", "url": WINDOWS_STATE_URL} + {"name": "Window Rear Right", "id": "window_rear_right", "icon": "car-door-lock", "url": WINDOWS_STATE_URL}, + {"name": "Door Front Left", "id": "door_front_left", "icon": "car-door-lock", "url": LOCK_STATE_URL}, + {"name": "Door Front Right", "id": "door_front_right", "icon": "car-door-lock", "url": LOCK_STATE_URL}, + {"name": "Door Rear Left", "id": "door_rear_left", "icon": "car-door-lock", "url": LOCK_STATE_URL}, + {"name": "Door Rear Right", "id": "door_rear_right", "icon": "car-door-lock", "url": LOCK_STATE_URL} ] supported_switches = [ @@ -36,9 +40,9 @@ supported_switches = [ ] supported_locks = [ - {"name": "Lock state", "id": "lock_status", "icon": "lock", "url": CAR_LOCK_STATE_URL} + {"name": "Lock state", "id": "lock_status", "icon": "lock", "url": LOCK_STATE_URL} ] supported_buttons = [ - {"name": "Update Data", "id": "update_data", "icon": "update", "url": ""} + {"name": "Force Update Data", "id": "update_data", "icon": "update", "url": ""} ] diff --git a/src/mqtt.py b/src/mqtt.py index a9163b9..9de116b 100644 --- a/src/mqtt.py +++ b/src/mqtt.py @@ -72,7 +72,7 @@ def on_message(client, userdata, msg): update_car_data() elif "update_data" in msg.topic: if payload == "PRESS": - update_car_data() + update_car_data(True) def update_loop(): @@ -84,12 +84,12 @@ def update_loop(): time.sleep(settings["updateInterval"]) -def update_car_data(): +def update_car_data(force_update=False): global last_data_update last_data_update = format_datetime(datetime.now(), format="medium", locale=settings["babelLocale"]) for vin in volvo.vins: for lock in supported_locks: - state = volvo.api_call(lock["url"], "GET", vin, lock["id"]) + state = volvo.api_call(lock["url"], "GET", vin, lock["id"], force_update) mqtt_client.publish( f"homeassistant/lock/{vin}_{lock['id']}/state", state @@ -110,7 +110,7 @@ def update_car_data(): if sensor["id"] == "last_data_update": state = last_data_update else: - state = volvo.api_call(sensor["url"], "GET", vin, sensor["id"]) + state = volvo.api_call(sensor["url"], "GET", vin, sensor["id"], force_update) mqtt_client.publish( f"homeassistant/sensor/{vin}_{sensor['id']}/state", state diff --git a/src/volvo.py b/src/volvo.py index 59ceba5..dc2a52b 100644 --- a/src/volvo.py +++ b/src/volvo.py @@ -5,7 +5,7 @@ from config import settings from babel.dates import format_datetime from const import charging_system_states, CLIMATE_START_URL, \ OAUTH_URL, VEHICLES_URL, VEHICLE_DETAILS_URL, RECHARGE_STATE_URL, \ - WINDOWS_STATE_URL + WINDOWS_STATE_URL, LOCK_STATE_URL session = requests.Session() session.headers = { @@ -21,6 +21,8 @@ recharge_cached_api_response = {} recharge_api_last_update = {} window_cached_api_response = {} window_api_last_update = {} +door_cached_api_response = {} +door_api_last_update = {} def authorize(): @@ -141,17 +143,20 @@ def disable_climate(vin): mqtt.update_car_data() -def api_call(url, method, vin, sensor_id=None): +def api_call(url, method, vin, sensor_id=None, force_update=False): global token_expires_at if datetime.now() >= token_expires_at: refresh_auth() if url == RECHARGE_STATE_URL: # Minimize API calls for recharge state - response = pull_recharge_api(url, method, vin) + response = pull_recharge_api(url, method, vin, force_update) elif url == WINDOWS_STATE_URL: # Minimize API calls for window state - response = pull_window_api(url, method, vin) + response = pull_window_api(url, method, vin, force_update) + elif url == LOCK_STATE_URL: + # Minimize API calls for door state + response = pull_door_api(url, method, vin, force_update) elif method == "GET": print("Starting " + method + " call against " + url) response = session.get(url.format(vin), timeout=15) @@ -178,9 +183,30 @@ def api_call(url, method, vin, sensor_id=None): return parse_api_data(data, sensor_id) -def pull_window_api(url, method, vin): +def pull_door_api(url, method, vin, force_update=False): + global door_cached_api_response, door_api_last_update + if not vin in door_cached_api_response or force_update: + # No API Data for vin cached, get fresh data from API + print("Starting " + method + " call against " + url) + response = session.get(url.format(vin), timeout=15) + door_cached_api_response[vin] = response + door_api_last_update[vin] = datetime.now() + else: + if (datetime.now() - door_api_last_update[vin]).total_seconds() >= settings["updateInterval"]: + # Old Data in Cache, updating + print("Starting " + method + " call against " + url) + response = session.get(url.format(vin), timeout=15) + door_cached_api_response[vin] = response + door_api_last_update[vin] = datetime.now() + else: + # Data is up do date, returning cached data + response = door_cached_api_response[vin] + return response + + +def pull_window_api(url, method, vin, force_update=False): global window_cached_api_response, window_api_last_update - if not vin in window_cached_api_response: + if not vin in window_cached_api_response or force_update: # No API Data for vin cached, get fresh data from API print("Starting " + method + " call against " + url) response = session.get(url.format(vin), timeout=15) @@ -199,9 +225,9 @@ def pull_window_api(url, method, vin): return response -def pull_recharge_api(url, method, vin): +def pull_recharge_api(url, method, vin, force_update=False): global recharge_cached_api_response, recharge_api_last_update - if not vin in recharge_cached_api_response: + if not vin in recharge_cached_api_response or force_update: # No API Data for vin cached, get fresh data from API print("Starting " + method + " call against " + url) response = session.get(url.format(vin), timeout=15) @@ -268,5 +294,13 @@ def parse_api_data(data, sensor_id=None): return data["data"]["rearLeftWindowOpen"]["value"] elif sensor_id == "window_rear_right": return data["data"]["rearRightWindowOpen"]["value"] + elif sensor_id == "door_front_left": + return data["data"]["frontLeftDoorOpen"]["value"] + elif sensor_id == "door_front_right": + return data["data"]["frontRightDoorOpen"]["value"] + elif sensor_id == "door_rear_left": + return data["data"]["rearLeftDoorOpen"]["value"] + elif sensor_id == "door_rear_right": + return data["data"]["rearRightDoorOpen"]["value"] else: return ""