forked from peter/volvo2mqtt
Add support for charging connection state, tyre state, and engine state
This commit is contained in:
+13
-2
@@ -14,15 +14,21 @@ CAR_UNLOCK_URL = "https://api.volvocars.com/connected-vehicle/v2/vehicles/{0}/co
|
|||||||
RECHARGE_STATE_URL = "https://api.volvocars.com/energy/v1/vehicles/{0}/recharge-status"
|
RECHARGE_STATE_URL = "https://api.volvocars.com/energy/v1/vehicles/{0}/recharge-status"
|
||||||
ODOMETER_STATE_URL = "https://api.volvocars.com/connected-vehicle/v2/vehicles/{0}/odometer"
|
ODOMETER_STATE_URL = "https://api.volvocars.com/connected-vehicle/v2/vehicles/{0}/odometer"
|
||||||
LOCATION_STATE_URL = "https://api.volvocars.com/location/v1/vehicles/{0}/location"
|
LOCATION_STATE_URL = "https://api.volvocars.com/location/v1/vehicles/{0}/location"
|
||||||
|
TYRE_STATE_URL = "https://api.volvocars.com/connected-vehicle/v2/vehicles/{0}/tyres"
|
||||||
|
ENGINE_STATE_URL = "https://api.volvocars.com/connected-vehicle/v2/vehicles/{0}/engine-status"
|
||||||
|
|
||||||
charging_system_states = {"CHARGING_SYSTEM_CHARGING": "Charging", "CHARGING_SYSTEM_IDLE": "Idle",
|
charging_system_states = {"CHARGING_SYSTEM_CHARGING": "Charging", "CHARGING_SYSTEM_IDLE": "Idle",
|
||||||
"CHARGING_SYSTEM_FAULT": "Fault", "CHARGING_SYSTEM_UNSPECIFIED": "Unspecified"}
|
"CHARGING_SYSTEM_FAULT": "Fault", "CHARGING_SYSTEM_UNSPECIFIED": "Unspecified"}
|
||||||
|
|
||||||
|
charging_connection_states = {"CONNECTION_STATUS_DISCONNECTED": "Disconnected", "CONNECTION_STATUS_UNSPECIFIED": "Unspecified",
|
||||||
|
"CONNECTION_STATUS_CONNECTED_DC": "Connected DC", "CONNECTION_STATUS_CONNECTED_AC": "Connected AC"}
|
||||||
|
|
||||||
supported_entities = [
|
supported_entities = [
|
||||||
{"name": "Battery Charge Level", "domain": "sensor", "id": "battery_charge_level", "unit": "%", "icon": "car-battery", "url": RECHARGE_STATE_URL},
|
{"name": "Battery Charge Level", "domain": "sensor", "id": "battery_charge_level", "unit": "%", "icon": "car-battery", "url": RECHARGE_STATE_URL},
|
||||||
{"name": "Electric Range", "domain": "sensor", "id": "electric_range", "unit": "km" if settings["babelLocale"] != "en_US" else "mi", "icon": "map-marker-distance", "url": RECHARGE_STATE_URL},
|
{"name": "Electric Range", "domain": "sensor", "id": "electric_range", "unit": "km" if settings["babelLocale"] != "en_US" else "mi", "icon": "map-marker-distance", "url": RECHARGE_STATE_URL},
|
||||||
{"name": "Estimated Charging Time", "domain": "sensor", "id": "estimated_charging_time", "unit": "minutes", "icon": "timer-sync-outline", "url": RECHARGE_STATE_URL},
|
{"name": "Estimated Charging Time", "domain": "sensor", "id": "estimated_charging_time", "unit": "minutes", "icon": "timer-sync-outline", "url": RECHARGE_STATE_URL},
|
||||||
{"name": "Charging System Status", "domain": "sensor", "id": "charging_system_status", "icon": "ev-plug-ccs2", "url": RECHARGE_STATE_URL},
|
{"name": "Charging System Status", "domain": "sensor", "id": "charging_system_status", "icon": "ev-station", "url": RECHARGE_STATE_URL},
|
||||||
|
{"name": "Charging Connection Status", "domain": "sensor", "id": "charging_connection_status", "icon": "ev-plug-ccs2", "url": RECHARGE_STATE_URL},
|
||||||
{"name": "Estimated Charging Finish Time", "domain": "sensor", "id": "estimated_charging_finish_time", "icon": "timer-sync-outline", "url": RECHARGE_STATE_URL},
|
{"name": "Estimated Charging Finish Time", "domain": "sensor", "id": "estimated_charging_finish_time", "icon": "timer-sync-outline", "url": RECHARGE_STATE_URL},
|
||||||
{"name": "Odometer", "domain": "sensor", "id": "odometer", "unit": "km" if settings["babelLocale"] != "en_US" else "mi", "icon": "counter", "url": ODOMETER_STATE_URL},
|
{"name": "Odometer", "domain": "sensor", "id": "odometer", "unit": "km" if settings["babelLocale"] != "en_US" else "mi", "icon": "counter", "url": ODOMETER_STATE_URL},
|
||||||
{"name": "Last Data Update", "domain": "sensor", "id": "last_data_update", "icon": "timer", "url": ""},
|
{"name": "Last Data Update", "domain": "sensor", "id": "last_data_update", "icon": "timer", "url": ""},
|
||||||
@@ -40,5 +46,10 @@ supported_entities = [
|
|||||||
{"name": "Air Conditioning", "domain": "switch", "id": "climate_status", "icon": "air-conditioner"},
|
{"name": "Air Conditioning", "domain": "switch", "id": "climate_status", "icon": "air-conditioner"},
|
||||||
{"name": "Lock state", "domain": "lock", "id": "lock_status", "icon": "lock", "url": LOCK_STATE_URL},
|
{"name": "Lock state", "domain": "lock", "id": "lock_status", "icon": "lock", "url": LOCK_STATE_URL},
|
||||||
{"name": "Force Update Data", "domain": "button", "id": "update_data", "icon": "update", "url": ""},
|
{"name": "Force Update Data", "domain": "button", "id": "update_data", "icon": "update", "url": ""},
|
||||||
{"name": "Location", "domain": "device_tracker", "id": "location", "icon": "map-marker-radius", "url": LOCATION_STATE_URL}
|
{"name": "Location", "domain": "device_tracker", "id": "location", "icon": "map-marker-radius", "url": LOCATION_STATE_URL},
|
||||||
|
{"name": "Tyre Front Left", "domain": "sensor", "id": "tyre_front_left", "icon": "car-tire-alert", "url": TYRE_STATE_URL},
|
||||||
|
{"name": "Tyre Front Right", "domain": "sensor", "id": "tyre_front_right", "icon": "car-tire-alert", "url": TYRE_STATE_URL},
|
||||||
|
{"name": "Tyre Rear Left", "domain": "sensor", "id": "tyre_rear_left", "icon": "car-tire-alert", "url": TYRE_STATE_URL},
|
||||||
|
{"name": "Tyre Rear Right", "domain": "sensor", "id": "tyre_rear_right", "icon": "car-tire-alert", "url": TYRE_STATE_URL},
|
||||||
|
{"name": "Engine State", "domain": "sensor", "id": "engine_state", "icon": "engine", "url": ENGINE_STATE_URL}
|
||||||
]
|
]
|
||||||
|
|||||||
+44
-2
@@ -4,9 +4,10 @@ import mqtt
|
|||||||
from config import settings
|
from config import settings
|
||||||
from babel.dates import format_datetime
|
from babel.dates import format_datetime
|
||||||
from util import keys_exists
|
from util import keys_exists
|
||||||
from const import charging_system_states, CLIMATE_START_URL, \
|
from const import charging_system_states, charging_connection_states, CLIMATE_START_URL, \
|
||||||
OAUTH_URL, VEHICLES_URL, VEHICLE_DETAILS_URL, RECHARGE_STATE_URL, \
|
OAUTH_URL, VEHICLES_URL, VEHICLE_DETAILS_URL, RECHARGE_STATE_URL, \
|
||||||
WINDOWS_STATE_URL, LOCK_STATE_URL, supported_entities
|
WINDOWS_STATE_URL, LOCK_STATE_URL, TYRE_STATE_URL, supported_entities
|
||||||
|
|
||||||
|
|
||||||
session = requests.Session()
|
session = requests.Session()
|
||||||
session.headers = {
|
session.headers = {
|
||||||
@@ -24,6 +25,8 @@ window_cached_api_response = {}
|
|||||||
window_api_last_update = {}
|
window_api_last_update = {}
|
||||||
door_cached_api_response = {}
|
door_cached_api_response = {}
|
||||||
door_api_last_update = {}
|
door_api_last_update = {}
|
||||||
|
tyre_cached_api_response = {}
|
||||||
|
tyre_api_last_update = {}
|
||||||
supported_endpoints = {}
|
supported_endpoints = {}
|
||||||
|
|
||||||
|
|
||||||
@@ -146,7 +149,10 @@ def check_supported_endpoints():
|
|||||||
state = ""
|
state = ""
|
||||||
|
|
||||||
if state is not None:
|
if state is not None:
|
||||||
|
print("Success! " + entity["name"] + " is supported by your vehicle.")
|
||||||
supported_endpoints[vin].append(entity)
|
supported_endpoints[vin].append(entity)
|
||||||
|
else:
|
||||||
|
print("Failed, " + entity["name"] + " is unfortunately not supported by your vehicle.")
|
||||||
|
|
||||||
|
|
||||||
def initialize_climate(vins):
|
def initialize_climate(vins):
|
||||||
@@ -174,6 +180,9 @@ def api_call(url, method, vin, sensor_id=None, force_update=False):
|
|||||||
elif url == LOCK_STATE_URL:
|
elif url == LOCK_STATE_URL:
|
||||||
# Minimize API calls for door state
|
# Minimize API calls for door state
|
||||||
response = pull_door_api(url, method, vin, force_update)
|
response = pull_door_api(url, method, vin, force_update)
|
||||||
|
elif url == TYRE_STATE_URL:
|
||||||
|
# Minimize API calls for tyre state
|
||||||
|
response = pull_tyre_api(url, method, vin, force_update)
|
||||||
elif method == "GET":
|
elif method == "GET":
|
||||||
print("Starting " + method + " call against " + url)
|
print("Starting " + method + " call against " + url)
|
||||||
response = session.get(url.format(vin), timeout=15)
|
response = session.get(url.format(vin), timeout=15)
|
||||||
@@ -200,6 +209,27 @@ def api_call(url, method, vin, sensor_id=None, force_update=False):
|
|||||||
return parse_api_data(data, sensor_id)
|
return parse_api_data(data, sensor_id)
|
||||||
|
|
||||||
|
|
||||||
|
def pull_tyre_api(url, method, vin, force_update=False):
|
||||||
|
global tyre_cached_api_response, tyre_api_last_update
|
||||||
|
if not vin in tyre_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)
|
||||||
|
tyre_cached_api_response[vin] = response
|
||||||
|
tyre_api_last_update[vin] = datetime.now()
|
||||||
|
else:
|
||||||
|
if (datetime.now() - tyre_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)
|
||||||
|
tyre_cached_api_response[vin] = response
|
||||||
|
tyre_api_last_update[vin] = datetime.now()
|
||||||
|
else:
|
||||||
|
# Data is up do date, returning cached data
|
||||||
|
response = tyre_cached_api_response[vin]
|
||||||
|
return response
|
||||||
|
|
||||||
|
|
||||||
def pull_door_api(url, method, vin, force_update=False):
|
def pull_door_api(url, method, vin, force_update=False):
|
||||||
global door_cached_api_response, door_api_last_update
|
global door_cached_api_response, door_api_last_update
|
||||||
if not vin in door_cached_api_response or force_update:
|
if not vin in door_cached_api_response or force_update:
|
||||||
@@ -271,6 +301,8 @@ def parse_api_data(data, sensor_id=None):
|
|||||||
return data["electricRange"]["value"] if keys_exists(data, "electricRange") else None
|
return data["electricRange"]["value"] if keys_exists(data, "electricRange") else None
|
||||||
elif sensor_id == "charging_system_status":
|
elif sensor_id == "charging_system_status":
|
||||||
return charging_system_states[data["chargingSystemStatus"]["value"]] if keys_exists(data, "chargingSystemStatus") else None
|
return charging_system_states[data["chargingSystemStatus"]["value"]] if keys_exists(data, "chargingSystemStatus") else None
|
||||||
|
elif sensor_id == "charging_connection_status":
|
||||||
|
return charging_connection_states[data["chargingConnectionStatus"]["value"]] if keys_exists(data, "chargingConnectionStatus") else None
|
||||||
elif sensor_id == "estimated_charging_time":
|
elif sensor_id == "estimated_charging_time":
|
||||||
if keys_exists(data, "chargingSystemStatus"):
|
if keys_exists(data, "chargingSystemStatus"):
|
||||||
charging_system_state = charging_system_states[data["chargingSystemStatus"]["value"]]
|
charging_system_state = charging_system_states[data["chargingSystemStatus"]["value"]]
|
||||||
@@ -318,6 +350,16 @@ def parse_api_data(data, sensor_id=None):
|
|||||||
return data["hoodOpen"]["value"] if keys_exists(data, "hoodOpen") else None
|
return data["hoodOpen"]["value"] if keys_exists(data, "hoodOpen") else None
|
||||||
elif sensor_id == "tank_lid":
|
elif sensor_id == "tank_lid":
|
||||||
return data["tankLidOpen"]["value"] if keys_exists(data, "tankLidOpen") else None
|
return data["tankLidOpen"]["value"] if keys_exists(data, "tankLidOpen") else None
|
||||||
|
elif sensor_id == "tyre_front_left":
|
||||||
|
return data["frontLeftTyrePressure"]["value"] if keys_exists(data, "frontLeftTyrePressure") else None
|
||||||
|
elif sensor_id == "tyre_front_right":
|
||||||
|
return data["frontRightTyrePressure"]["value"] if keys_exists(data, "frontRightTyrePressure") else None
|
||||||
|
elif sensor_id == "tyre_rear_left":
|
||||||
|
return data["rearLeftTyrePressure"]["value"] if keys_exists(data, "rearLeftTyrePressure") else None
|
||||||
|
elif sensor_id == "tyre_rear_right":
|
||||||
|
return data["rearRightTyrePressure"]["value"] if keys_exists(data, "rearRightTyrePressure") else None
|
||||||
|
elif sensor_id == "engine_state":
|
||||||
|
return data["engineRunning"]["value"] if keys_exists(data, "engineRunning") else None
|
||||||
elif sensor_id == "location":
|
elif sensor_id == "location":
|
||||||
coordinates = {}
|
coordinates = {}
|
||||||
if keys_exists(data, "geometry"):
|
if keys_exists(data, "geometry"):
|
||||||
|
|||||||
Reference in New Issue
Block a user