forked from peter/volvo2mqtt
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f1953e1baa | ||
|
|
459c2dc1af | ||
|
|
be09c7953e | ||
|
|
fa68c4ba2c | ||
|
|
d7a79f84a4 | ||
|
|
2c8effd746 | ||
|
|
cef28f7d0b | ||
|
|
781e1da8e8 |
@@ -32,6 +32,7 @@ If you like my work:<br>
|
||||
- XC60 PHEV (2022)
|
||||
- XC90 PHEV T8 (2023)
|
||||
- XC90 PHEV T8 (2024)
|
||||
- XC90 B5 Mildhybrid (2024)
|
||||
- V90 PHEV T8 (2019)*
|
||||
|
||||
*only partly working
|
||||
|
||||
@@ -1,3 +1,24 @@
|
||||
## v1.8.11
|
||||
### 🐛 Bug Fixes:
|
||||
|
||||
- Remove old code from checks for running engine sensor
|
||||
- Move battery charge level sensor from v2 to v1, as v2 returns 404
|
||||
|
||||
## v1.8.10
|
||||
### 🐛 Bug Fixes:
|
||||
|
||||
- Add `AJAR` state for sunroof #123
|
||||
|
||||
## v1.8.9
|
||||
### 🐛 Bug Fixes:
|
||||
|
||||
- Fix api changes from volvo #121
|
||||
|
||||
## v1.8.8
|
||||
### 🐛 Bug Fixes:
|
||||
|
||||
- Fix json decode error, if volvo API returns a simple string #104
|
||||
|
||||
## v1.8.7
|
||||
### 🚀 Features:
|
||||
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
name: "Volvo2Mqtt"
|
||||
description: "Volvo AAOS MQTT bridge"
|
||||
version: "1.8.7"
|
||||
version: "1.8.11"
|
||||
slug: "volvo2mqtt"
|
||||
init: false
|
||||
url: "https://github.com/Dielee/volvo2mqtt"
|
||||
|
||||
+3
-4
@@ -1,6 +1,6 @@
|
||||
from config import settings
|
||||
|
||||
VERSION = "v1.8.7"
|
||||
VERSION = "v1.8.11"
|
||||
|
||||
OAUTH_URL = "https://volvoid.eu.volvocars.com/as/token.oauth2"
|
||||
VEHICLES_URL = "https://api.volvocars.com/connected-vehicle/v1/vehicles"
|
||||
@@ -16,7 +16,7 @@ ODOMETER_STATE_URL = "https://api.volvocars.com/connected-vehicle/v2/vehicles/{0
|
||||
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"
|
||||
BATTERY_CHARGE_STATE_URL = "https://api.volvocars.com/connected-vehicle/v2/vehicles/{0}/battery-charge-level"
|
||||
BATTERY_CHARGE_STATE_URL = "https://api.volvocars.com/connected-vehicle/v1/vehicles/{0}/battery-charge-level"
|
||||
FUEL_STATE_URL = "https://api.volvocars.com/connected-vehicle/v2/vehicles/{0}/fuel"
|
||||
STATISTICS_URL = "https://api.volvocars.com/connected-vehicle/v2/vehicles/{0}/statistics"
|
||||
ENGINE_DIAGNOSTICS_URL = "https://api.volvocars.com/connected-vehicle/v2/vehicles/{0}/engine"
|
||||
@@ -51,7 +51,7 @@ charging_connection_states = {"CONNECTION_STATUS_DISCONNECTED": "Disconnected",
|
||||
"CONNECTION_STATUS_FAULT": "Fault"}
|
||||
|
||||
window_states = {"CLOSED": "OFF", "OPEN": "ON"}
|
||||
door_states = {"CLOSED": "OFF", "OPEN": "ON"}
|
||||
door_states = {"CLOSED": "OFF", "OPEN": "ON", "UNSPECIFIED": "UNKNOWN", "AJAR": "ON"}
|
||||
engine_states = {"RUNNING": "ON", "STOPPED": "OFF", "true": "ON", "false": "OFF"}
|
||||
|
||||
icon_states = {
|
||||
@@ -108,7 +108,6 @@ supported_entities = [
|
||||
{"name": "Tire Rear Left", "domain": "sensor", "id": "tyre_rear_left", "icon": "car-tire-alert", "url": TYRE_STATE_URL},
|
||||
{"name": "Tire Rear Right", "domain": "sensor", "id": "tyre_rear_right", "icon": "car-tire-alert", "url": TYRE_STATE_URL},
|
||||
{"name": "Engine State", "domain": "binary_sensor", "device_class": "running", "id": "engine_state", "icon": "engine", "url": ENGINE_STATE_URL},
|
||||
{"name": "Engine State", "domain": "binary_sensor", "device_class": "running", "id": "engine_state", "icon": "engine", "url": ENGINE_DIAGNOSTICS_URL},
|
||||
{"name": "Fuel Level", "domain": "sensor", "id": "fuel_level", "unit": "liters", "icon": "fuel", "url": FUEL_STATE_URL, "state_class": "measurement"},
|
||||
{"name": "Average Fuel Consumption", "domain": "sensor", "id": "average_fuel_consumption", "unit": "liters", "icon": "fuel", "url": STATISTICS_URL},
|
||||
{"name": "Distance to Empty", "domain": "sensor", "id": "distance_to_empty", "unit": "km" if not units.get(settings["babelLocale"]) else units[settings["babelLocale"]]["distance_to_empty"]["unit"], "icon": "map-marker-distance", "url": STATISTICS_URL, "state_class": "measurement"},
|
||||
|
||||
+7
-6
@@ -25,7 +25,7 @@ active_schedules = {}
|
||||
|
||||
def connect():
|
||||
client = mqtt.Client("volvoAAOS2mqtt") if os.environ.get("IS_HA_ADDON") \
|
||||
else mqtt.Client("volvoAAOS2mqtt_" + settings.volvoData["username"])
|
||||
else mqtt.Client("volvoAAOS2mqtt_" + settings.volvoData["username"].replace("+", ""))
|
||||
|
||||
client.will_set(availability_topic, "offline", 0, False)
|
||||
if settings["mqtt"]["username"] and settings["mqtt"]["password"]:
|
||||
@@ -255,11 +255,12 @@ def update_car_data(force_update=False, overwrite={}):
|
||||
else:
|
||||
topic = f"homeassistant/{entity['domain']}/{vin}_{entity['id']}/state"
|
||||
|
||||
mqtt_client.publish(
|
||||
topic,
|
||||
json.dumps(state) if isinstance(state, dict) or isinstance(state, list) else state
|
||||
)
|
||||
update_ha_device(entity, vin, state)
|
||||
if state:
|
||||
mqtt_client.publish(
|
||||
topic,
|
||||
json.dumps(state) if isinstance(state, dict) or isinstance(state, list) else state
|
||||
)
|
||||
update_ha_device(entity, vin, state)
|
||||
|
||||
|
||||
def update_ha_device(entity, vin, state):
|
||||
|
||||
+31
-31
@@ -8,6 +8,7 @@ from threading import currentThread
|
||||
from datetime import datetime, timedelta
|
||||
from config import settings
|
||||
from babel.dates import format_datetime
|
||||
from json import JSONDecodeError
|
||||
from const import charging_system_states, charging_connection_states, door_states, window_states, \
|
||||
OAUTH_URL, VEHICLES_URL, VEHICLE_DETAILS_URL, RECHARGE_STATE_URL, CLIMATE_START_URL, \
|
||||
WINDOWS_STATE_URL, LOCK_STATE_URL, TYRE_STATE_URL, supported_entities, BATTERY_CHARGE_STATE_URL, \
|
||||
@@ -244,11 +245,6 @@ def check_supported_endpoints():
|
||||
# If battery charge level could be found in recharge-api, skip the second battery charge sensor
|
||||
continue
|
||||
|
||||
if entity["id"] == "engine_state" and entity["url"] == ENGINE_DIAGNOSTICS_URL \
|
||||
and any("engine_state" in d["id"] for d in supported_endpoints[vin]):
|
||||
# If engine state could be found in engine state endpoint, skip the second engine running sensor
|
||||
continue
|
||||
|
||||
if entity.get('url'):
|
||||
state = api_call(entity["url"], "GET", vin, entity["id"])
|
||||
else:
|
||||
@@ -347,7 +343,11 @@ def api_call(url, method, vin, sensor_id=None, force_update=False, key_change=Fa
|
||||
return None
|
||||
|
||||
logging.debug("Response status code: " + str(response.status_code))
|
||||
data = response.json()
|
||||
try:
|
||||
data = response.json()
|
||||
except JSONDecodeError as e:
|
||||
logging.error("Fetched json decode error, Volvo API seems to return garbage. Skipping update. Error: " + str(e))
|
||||
return None
|
||||
|
||||
if response.status_code == 200:
|
||||
logging.debug(response.text)
|
||||
@@ -443,7 +443,7 @@ def parse_api_data(data, sensor_id=None):
|
||||
return ""
|
||||
return None
|
||||
elif sensor_id == "lock_status":
|
||||
return data["carLocked"]["value"] if util.keys_exists(data, "carLocked") else None
|
||||
return data["centralLock"]["value"] if util.keys_exists(data, "centralLock") else None
|
||||
elif sensor_id == "odometer":
|
||||
multiplier = 1
|
||||
if util.keys_exists(settings["volvoData"], "odometerMultiplier"):
|
||||
@@ -455,44 +455,44 @@ def parse_api_data(data, sensor_id=None):
|
||||
return util.convert_metric_values(int(data["odometer"]["value"]) * multiplier) \
|
||||
if util.keys_exists(data, "odometer") else None
|
||||
elif sensor_id == "window_front_left":
|
||||
return window_states[data["frontLeftWindowOpen"]["value"]] if util.keys_exists(data, "frontLeftWindowOpen") \
|
||||
return window_states[data["frontLeftWindow"]["value"]] if util.keys_exists(data, "frontLeftWindow") \
|
||||
else None
|
||||
elif sensor_id == "window_front_right":
|
||||
return window_states[data["frontRightWindowOpen"]["value"]] if util.keys_exists(data, "frontRightWindowOpen") \
|
||||
return window_states[data["frontRightWindow"]["value"]] if util.keys_exists(data, "frontRightWindow") \
|
||||
else None
|
||||
elif sensor_id == "window_rear_left":
|
||||
return window_states[data["rearLeftWindowOpen"]["value"]] if util.keys_exists(data, "rearLeftWindowOpen") \
|
||||
return window_states[data["rearLeftWindow"]["value"]] if util.keys_exists(data, "rearLeftWindow") \
|
||||
else None
|
||||
elif sensor_id == "window_rear_right":
|
||||
return window_states[data["rearRightWindowOpen"]["value"]] if util.keys_exists(data, "rearRightWindowOpen") \
|
||||
return window_states[data["rearRightWindow"]["value"]] if util.keys_exists(data, "rearRightWindow") \
|
||||
else None
|
||||
elif sensor_id == "door_front_left":
|
||||
return door_states[data["frontLeftDoorOpen"]["value"]] if util.keys_exists(data, "frontLeftDoorOpen") else None
|
||||
return door_states[data["frontLeftDoor"]["value"]] if util.keys_exists(data, "frontLeftDoor") else None
|
||||
elif sensor_id == "door_front_right":
|
||||
return door_states[data["frontRightDoorOpen"]["value"]] \
|
||||
if util.keys_exists(data, "frontRightDoorOpen") else None
|
||||
return door_states[data["frontRightDoor"]["value"]] \
|
||||
if util.keys_exists(data, "frontRightDoor") else None
|
||||
elif sensor_id == "door_rear_left":
|
||||
return door_states[data["rearLeftDoorOpen"]["value"]] if util.keys_exists(data, "rearLeftDoorOpen") else None
|
||||
return door_states[data["rearLeftDoor"]["value"]] if util.keys_exists(data, "rearLeftDoor") else None
|
||||
elif sensor_id == "door_rear_right":
|
||||
return door_states[data["rearRightDoorOpen"]["value"]] if util.keys_exists(data, "rearRightDoorOpen") else None
|
||||
return door_states[data["rearRightDoor"]["value"]] if util.keys_exists(data, "rearRightDoor") else None
|
||||
elif sensor_id == "tailgate":
|
||||
return door_states[data["tailGateOpen"]["value"]] if util.keys_exists(data, "tailGateOpen") else None
|
||||
return door_states[data["tailgate"]["value"]] if util.keys_exists(data, "tailgate") else None
|
||||
elif sensor_id == "sunroof":
|
||||
return door_states[data["sunRoofOpen"]["value"]] if util.keys_exists(data, "sunRoofOpen") else None
|
||||
return door_states[data["sunroof"]["value"]] if util.keys_exists(data, "sunroof") else None
|
||||
elif sensor_id == "engine_hood":
|
||||
return door_states[data["hoodOpen"]["value"]] if util.keys_exists(data, "hoodOpen") else None
|
||||
return door_states[data["hood"]["value"]] if util.keys_exists(data, "hood") else None
|
||||
elif sensor_id == "tank_lid":
|
||||
return door_states[data["tankLidOpen"]["value"]] if util.keys_exists(data, "tankLidOpen") else None
|
||||
return door_states[data["tankLid"]["value"]] if util.keys_exists(data, "tankLid") else None
|
||||
elif sensor_id == "tyre_front_left":
|
||||
return data["frontLeftTyrePressure"]["value"] if util.keys_exists(data, "frontLeftTyrePressure") else None
|
||||
return data["frontLeft"]["value"] if util.keys_exists(data, "frontLeft") else None
|
||||
elif sensor_id == "tyre_front_right":
|
||||
return data["frontRightTyrePressure"]["value"] if util.keys_exists(data, "frontRightTyrePressure") else None
|
||||
return data["frontRight"]["value"] if util.keys_exists(data, "frontRight") else None
|
||||
elif sensor_id == "tyre_rear_left":
|
||||
return data["rearLeftTyrePressure"]["value"] if util.keys_exists(data, "rearLeftTyrePressure") else None
|
||||
return data["rearLeft"]["value"] if util.keys_exists(data, "rearLeft") else None
|
||||
elif sensor_id == "tyre_rear_right":
|
||||
return data["rearRightTyrePressure"]["value"] if util.keys_exists(data, "rearRightTyrePressure") else None
|
||||
return data["rearRight"]["value"] if util.keys_exists(data, "rearRight") else None
|
||||
elif sensor_id == "engine_state":
|
||||
return engine_states[data["engineRunning"]["value"]] if util.keys_exists(data, "engineRunning") else None
|
||||
return engine_states[data["engineStatus"]["value"]] if util.keys_exists(data, "engineStatus") else None
|
||||
elif sensor_id == "fuel_level":
|
||||
if util.keys_exists(data, "fuelAmount"):
|
||||
fuel_amount = float(data["fuelAmount"]["value"])
|
||||
@@ -543,16 +543,16 @@ def parse_api_data(data, sensor_id=None):
|
||||
elif sensor_id == "hours_to_service":
|
||||
return data["engineHoursToService"]["value"] if util.keys_exists(data, "engineHoursToService") else None
|
||||
elif sensor_id == "km_to_service":
|
||||
if util.keys_exists(data, "kmToService"):
|
||||
km_to_service = int(data["kmToService"]["value"])
|
||||
if util.keys_exists(data, "distanceToService"):
|
||||
km_to_service = int(data["distanceToService"]["value"])
|
||||
if km_to_service > 0:
|
||||
return util.convert_metric_values(data["kmToService"]["value"])
|
||||
return util.convert_metric_values(data["distanceToService"]["value"])
|
||||
return None
|
||||
elif sensor_id == "months_to_service":
|
||||
return data["monthsToService"]["value"] if util.keys_exists(data, "monthsToService") else None
|
||||
return data["timeToService"]["value"] if util.keys_exists(data, "timeToService") else None
|
||||
elif sensor_id == "service_warning_status":
|
||||
return data["serviceWarningStatus"]["value"] if util.keys_exists(data, "serviceWarningStatus") else None
|
||||
return data["serviceWarning"]["value"] if util.keys_exists(data, "serviceWarning") else None
|
||||
elif sensor_id == "service_warning_trigger":
|
||||
return data["serviceWarningTrigger"]["value"] if util.keys_exists(data, "serviceWarningTrigger") else None
|
||||
return data["serviceTrigger"]["value"] if util.keys_exists(data, "serviceTrigger") else None
|
||||
else:
|
||||
return None
|
||||
|
||||
Reference in New Issue
Block a user