Compare commits

...
19 Commits
Author SHA1 Message Date
Linus Dietz 1e071154c5 Bump version 2023-06-26 08:55:23 +02:00
Linus Dietz 6d938eab7a Filter zero data from volvo api 2023-06-26 08:50:30 +02:00
Linus Dietz 86980408e7 Filter zero data from volvo api 2023-06-26 08:43:20 +02:00
Linus Dietz a0800db907 Change average speed icon 2023-06-26 08:37:41 +02:00
Linus Dietz d5565cd52a Move windows and doors to binary_sensor domain 2023-06-26 08:36:50 +02:00
Linus DietzandGitHub 9f5b4f4017 Only build on new release 2023-06-26 08:17:51 +02:00
Linus Dietz 689d3e84e7 Add statistics endpoint to cached responses 2023-06-26 08:11:08 +02:00
Linus Dietz c095c39462 Bump version for release #30, #31, #32 2023-06-26 07:59:18 +02:00
Linus Dietz c72e99bcf8 Add average fuel consumption and average speed #30 2023-06-26 07:57:11 +02:00
Linus Dietz c5ed7b7971 Fix cached wget requirements.txt for HA Addon 2023-06-26 07:30:09 +02:00
Linus Dietz 43d09cb2f4 Send heartbeat after device creation timeout 2023-06-26 07:18:31 +02:00
Linus Dietz b7794d837b Fix offline devices after HA reboot 2023-06-26 07:16:41 +02:00
Linus Dietz 5d57979849 Add timezone option for HA Addon 2023-06-25 20:17:51 +02:00
Linus DietzandGitHub b1ef7e8e46 Add TZ link 2023-06-25 19:49:38 +02:00
Linus DietzandGitHub 07213aba18 Update README.md 2023-06-25 12:33:37 +02:00
Linus Dietz 71b8c59ba8 Fix unlockstate for multiple car usage 2023-06-25 07:22:15 +02:00
Linus Dietz 50b5eb9863 Add mqtt availability 2023-06-24 20:06:59 +02:00
Linus DietzandGitHub 02793accfa Update README.md 2023-06-24 19:53:03 +02:00
Linus Dietz 4d8b5e5006 Optimize lock/unlock behaviour 2023-06-24 19:47:02 +02:00
12 changed files with 174 additions and 96 deletions
+1 -1
View File
@@ -11,7 +11,7 @@ name: Create and publish a Docker image
on:
release:
types: [created, edited]
types: [created]
workflow_dispatch:
env:
+7 -3
View File
@@ -6,15 +6,19 @@ Maybe this component works also with other Volvo cars. Please try out the native
## Confirmed working with
- XC40 BEV (2023)
- XC40 BEV (2022)
- XC40 PHEV (2021)
- V60 T8 PHEV (2023)
- C40 PHEV (2023)
- C40 PHEV (2022)
- XC90 T8 PHEV (2023)
- XC60 PHEV (2023)
- XC60 PHEV (2022)
- V90 PHEV T8 (2019)
- V90 PHEV T8 (2019)*
*only partly working
Please let me know if your car works with this addon so I can expand the list!<br>
Please let me know if your car works with this addon so I can expand the list!
## Supported features
- Lock/unlock car
@@ -56,7 +60,7 @@ Here is what every option means:
| `CONF_mqtt` | `json` | **required** | Broker = Mqtt Broker IP / Username and Passwort are optional! Broker port can be changed. If no value is given, port 1883 will be used. |
| `CONF_volvoData` | `json` | **required** | Username and password are REQUIRED. Car vin can be a single vin or a list of multiple vins like `["vin1", "vin2"]`. If no vin is provided, <b>ALL</b> of your vehicles will be used. Vccapi key is REQUIRED. Get your Vccapi key from [here](https://developer.volvocars.com/account/). Odometer Multiplier is sometimes 10, sometimes 1. Try what's right for your car. If you leave it empty, the multiplier will be 1. |
| `CONF_debug` | `string` | | Debug option (true/false) - optional! |
| `TZ` | `string` | | Container timezone eg "Europe/Berlin" |
| `TZ` | `string` | | Container timezone eg "Europe/Berlin" from [here](https://docs.diladele.com/docker/timezones.html)|
If you like my work:
+2 -1
View File
@@ -1,4 +1,5 @@
requests~=2.29.0
dynaconf~=3.1.12
paho-mqtt~=1.6.1
Babel~=2.12.1
Babel~=2.12.1
pytz~=2023.3
+1 -1
View File
@@ -8,7 +8,7 @@ RUN apk add --no-cache python3 bash py3-pip
WORKDIR /volvoAAOS2mqtt
# download requirements.txt
RUN wget https://raw.githubusercontent.com/Dielee/volvo2mqtt/main/requirements.txt
RUN wget --no-cache https://raw.githubusercontent.com/Dielee/volvo2mqtt/main/requirements.txt
# install dependencies
RUN pip install -r requirements.txt
+3 -1
View File
@@ -1,6 +1,6 @@
name: "Volvo2Mqtt"
description: "Volvo AAOS MQTT bridge"
version: "1.4.1"
version: "1.5.2"
slug: "volvo2mqtt"
init: false
url: "https://github.com/Dielee/volvo2mqtt"
@@ -8,6 +8,7 @@ apparmor: true
options:
updateInterval: 300
babelLocale: null
TZ: null
debug: false
mqtt:
broker: null
@@ -23,6 +24,7 @@ options:
schema:
updateInterval: int(10,)
babelLocale: str
TZ: str
debug: bool
mqtt:
broker: str
+22 -13
View File
@@ -1,6 +1,6 @@
from config import settings
VERSION = "v1.4.1"
VERSION = "v1.5.2"
OAUTH_URL = "https://volvoid.eu.volvocars.com/as/token.oauth2"
VEHICLES_URL = "https://api.volvocars.com/connected-vehicle/v1/vehicles"
@@ -18,6 +18,9 @@ TYRE_STATE_URL = "https://api.volvocars.com/connected-vehicle/v2/vehicles/{0}/ty
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"
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"
availability_topic = "volvoAAOS2mqtt/availability"
charging_system_states = {"CHARGING_SYSTEM_CHARGING": "Charging", "CHARGING_SYSTEM_IDLE": "Idle",
"CHARGING_SYSTEM_FAULT": "Fault", "CHARGING_SYSTEM_UNSPECIFIED": "Unspecified"}
@@ -25,6 +28,9 @@ charging_system_states = {"CHARGING_SYSTEM_CHARGING": "Charging", "CHARGING_SYST
charging_connection_states = {"CONNECTION_STATUS_DISCONNECTED": "Disconnected", "CONNECTION_STATUS_UNSPECIFIED": "Unspecified",
"CONNECTION_STATUS_CONNECTED_DC": "Connected DC", "CONNECTION_STATUS_CONNECTED_AC": "Connected AC"}
window_states = {"CLOSED": "OFF", "OPEN": "ON"}
door_states = {"CLOSED": "OFF", "OPEN": "ON"}
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": BATTERY_CHARGE_STATE_URL},
@@ -35,17 +41,18 @@ supported_entities = [
{"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": "Last Data Update", "domain": "sensor", "id": "last_data_update", "icon": "timer", "url": ""},
{"name": "Window Front Left", "domain": "sensor", "id": "window_front_left", "icon": "car-door-lock", "url": WINDOWS_STATE_URL},
{"name": "Window Front Right", "domain": "sensor", "id": "window_front_right", "icon": "car-door-lock", "url": WINDOWS_STATE_URL},
{"name": "Window Rear Left", "domain": "sensor", "id": "window_rear_left", "icon": "car-door-lock", "url": WINDOWS_STATE_URL},
{"name": "Window Rear Right", "domain": "sensor", "id": "window_rear_right", "icon": "car-door-lock", "url": WINDOWS_STATE_URL},
{"name": "Door Front Left", "domain": "sensor", "id": "door_front_left", "icon": "car-door-lock", "url": LOCK_STATE_URL},
{"name": "Door Front Right", "domain": "sensor", "id": "door_front_right", "icon": "car-door-lock", "url": LOCK_STATE_URL},
{"name": "Door Rear Left", "domain": "sensor", "id": "door_rear_left", "icon": "car-door-lock", "url": LOCK_STATE_URL},
{"name": "Door Rear Right", "domain": "sensor", "id": "door_rear_right", "icon": "car-door-lock", "url": LOCK_STATE_URL},
{"name": "Tailgate", "domain": "sensor", "id": "tailgate", "icon": "car-door-lock", "url": LOCK_STATE_URL},
{"name": "Engine Hood", "domain": "sensor", "id": "engine_hood", "icon": "car-door-lock", "url": LOCK_STATE_URL},
{"name": "Tank Lid", "domain": "sensor", "id": "tank_lid", "icon": "car-door-lock", "url": LOCK_STATE_URL},
{"name": "Window Front Left", "domain": "binary_sensor", "device_class": "window", "id": "window_front_left", "icon": "car-door-lock", "url": WINDOWS_STATE_URL},
{"name": "Window Front Right", "domain": "binary_sensor", "device_class": "window", "id": "window_front_right", "icon": "car-door-lock", "url": WINDOWS_STATE_URL},
{"name": "Window Rear Left", "domain": "binary_sensor", "device_class": "window", "id": "window_rear_left", "icon": "car-door-lock", "url": WINDOWS_STATE_URL},
{"name": "Window Rear Right", "domain": "binary_sensor", "device_class": "window", "id": "window_rear_right", "icon": "car-door-lock", "url": WINDOWS_STATE_URL},
{"name": "Door Front Left", "domain": "binary_sensor", "device_class": "door", "id": "door_front_left", "icon": "car-door-lock", "url": LOCK_STATE_URL},
{"name": "Door Front Right", "domain": "binary_sensor", "device_class": "door", "id": "door_front_right", "icon": "car-door-lock", "url": LOCK_STATE_URL},
{"name": "Door Rear Left", "domain": "binary_sensor", "device_class": "door", "id": "door_rear_left", "icon": "car-door-lock", "url": LOCK_STATE_URL},
{"name": "Door Rear Right", "domain": "binary_sensor", "device_class": "door", "id": "door_rear_right", "icon": "car-door-lock", "url": LOCK_STATE_URL},
{"name": "Tailgate", "domain": "binary_sensor", "device_class": "door", "id": "tailgate", "icon": "car-door-lock", "url": LOCK_STATE_URL},
{"name": "Engine Hood", "domain": "binary_sensor", "device_class": "door", "id": "engine_hood", "icon": "car-door-lock", "url": LOCK_STATE_URL},
{"name": "Tank Lid", "domain": "binary_sensor", "device_class": "door", "id": "tank_lid", "icon": "car-door-lock", "url": LOCK_STATE_URL},
{"name": "Sunroof", "domain": "binary_sensor", "device_class": "door", "id": "sunroof", "icon": "car-door-lock", "url": LOCK_STATE_URL},
{"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": "Force Update Data", "domain": "button", "id": "update_data", "icon": "update", "url": ""},
@@ -55,5 +62,7 @@ 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": "sensor", "id": "engine_state", "icon": "engine", "url": ENGINE_STATE_URL},
{"name": "Fuel Level", "domain": "sensor", "id": "fuel_level", "unit": "liters", "icon": "fuel", "url": FUEL_STATE_URL}
{"name": "Fuel Level", "domain": "sensor", "id": "fuel_level", "unit": "liters", "icon": "fuel", "url": FUEL_STATE_URL},
{"name": "Average Fuel Consumption", "domain": "sensor", "id": "average_fuel_consumption", "unit": "liters", "icon": "fuel", "url": STATISTICS_URL},
{"name": "Average Speed", "domain": "sensor", "id": "average_speed", "unit": "km/h" if settings["babelLocale"] != "en_US" else "mp/h", "icon": "speedometer", "url": STATISTICS_URL}
]
+2
View File
@@ -1,10 +1,12 @@
from volvo import authorize
from mqtt import update_loop, connect
from const import VERSION
from util import set_tz
if __name__ == '__main__':
print("Starting volvo2mqtt version " + VERSION)
set_tz()
authorize()
connect()
update_loop()
+35 -12
View File
@@ -2,13 +2,13 @@ import time
import paho.mqtt.client as mqtt
import json
import volvo
from util import keys_exists
import util
from threading import Thread, Timer
from datetime import datetime
from babel.dates import format_datetime
from config import settings
from const import CLIMATE_START_URL, CLIMATE_STOP_URL, CAR_LOCK_URL, \
CAR_UNLOCK_URL
CAR_UNLOCK_URL, availability_topic
mqtt_client: mqtt.Client
@@ -20,10 +20,11 @@ climate_timer = {}
def connect():
client = mqtt.Client("volvoAAOS2mqtt")
client.will_set(availability_topic, "offline", 0, False)
if settings["mqtt"]["username"] and settings["mqtt"]["password"]:
client.username_pw_set(settings["mqtt"]["username"], settings["mqtt"]["password"])
port = 1883
if keys_exists(settings["mqtt"], "port"):
if util.keys_exists(settings["mqtt"], "port"):
conf_port = settings["mqtt"]["port"]
if isinstance(conf_port, int):
if conf_port > 0:
@@ -39,6 +40,7 @@ def connect():
def on_connect(client, userdata, flags, rc):
send_heartbeat()
if len(subscribed_topics) > 0:
for topic in subscribed_topics:
mqtt_client.subscribe(topic)
@@ -77,10 +79,10 @@ def on_message(client, userdata, msg):
elif "lock_status" in msg.topic:
if payload == "LOCK":
volvo.api_call(CAR_LOCK_URL, "POST", vin)
update_car_data(True)
update_car_data(True, {"entity_id": "lock_status", "vin": vin, "state": "LOCKED"})
elif payload == "UNLOCK":
volvo.api_call(CAR_UNLOCK_URL, "POST", vin)
update_car_data(True)
update_car_data(True, {"entity_id": "lock_status", "vin": vin, "state": "UNLOCKED"})
elif "update_data" in msg.topic:
if payload == "PRESS":
update_car_data(True)
@@ -90,25 +92,37 @@ def update_loop():
create_ha_devices()
while True:
print("Sending mqtt update...")
send_heartbeat()
update_car_data()
print("Mqtt update done. Next run in " + str(settings["updateInterval"]) + " seconds.")
time.sleep(settings["updateInterval"])
def update_car_data(force_update=False):
def update_car_data(force_update=False, overwrite={}):
global last_data_update
last_data_update = format_datetime(datetime.now(), format="medium", locale=settings["babelLocale"])
last_data_update = format_datetime(datetime.now(util.TZ), format="medium", locale=settings["babelLocale"])
for vin in volvo.vins:
for entity in volvo.supported_endpoints[vin]:
if entity["domain"] == "button":
continue
ov_entity_id = ""
ov_vin = ""
ov_state = ""
if bool(overwrite):
ov_entity_id = overwrite["entity_id"]
ov_vin = overwrite["vin"]
ov_state = overwrite["state"]
if entity["id"] == "climate_status":
state = assumed_climate_state[vin]
elif entity["id"] == "last_data_update":
state = last_data_update
else:
state = volvo.api_call(entity["url"], "GET", vin, entity["id"], force_update)
if entity["id"] == ov_entity_id and vin == ov_vin:
state = ov_state
else:
state = volvo.api_call(entity["url"], "GET", vin, entity["id"], force_update)
if entity["domain"] == "device_tracker":
topic = f"homeassistant/{entity['domain']}/{vin}_{entity['id']}/attributes"
@@ -133,15 +147,19 @@ def create_ha_devices():
"icon": f"mdi:{entity['icon']}",
"state_topic": f"homeassistant/{entity['domain']}/{vin}_{entity['id']}/state",
"device": device,
"unique_id": f"volvoAAOS2mqtt_{vin}_{entity['id']}"
"unique_id": f"volvoAAOS2mqtt_{vin}_{entity['id']}",
"availability_topic": availability_topic
}
if entity.get('unit'):
if entity.get("device_class"):
config["device_class"] = entity["device_class"]
if entity.get("unit"):
config["unit_of_measurement"] = entity["unit"]
if entity.get('domain') == "device_tracker":
if entity.get("domain") == "device_tracker":
config["json_attributes_topic"] = f"homeassistant/{entity['domain']}/{vin}_{entity['id']}/attributes"
if entity.get('domain') in ["switch", "lock", "button"]:
if entity.get("domain") in ["switch", "lock", "button"]:
command_topic = f"homeassistant/{entity['domain']}/{vin}_{entity['id']}/command"
config["command_topic"] = command_topic
subscribed_topics.append(command_topic)
@@ -153,3 +171,8 @@ def create_ha_devices():
retain=True
)
time.sleep(2)
send_heartbeat()
def send_heartbeat():
mqtt_client.publish(availability_topic, "online")
+1
View File
@@ -1,6 +1,7 @@
{
"updateInterval": 300,
"babelLocale": "de",
"TZ": "Europe/Berlin",
"mqtt": {
"broker": "",
"port": 1883,
+3
View File
@@ -5,6 +5,9 @@ configuration:
babelLocale:
name: Babel locale
description: Select your country code or take a look at the GitHub repo for more infos!
TZ:
name: Timezone
description: Set your local timezone eg. "Europe/Berlin" or "America/New_York"
debug:
name: API debug mode
description: Enable Volvo API debug, normaly this can stay off
+19
View File
@@ -1,3 +1,10 @@
import pytz
import os
from config import settings
TZ = None
def keys_exists(element, *keys):
""""
Check if *keys (nested) exists in `element` (dict).
@@ -15,3 +22,15 @@ def keys_exists(element, *keys):
except KeyError:
return False
return True
def set_tz():
global TZ
env_tz = os.environ.get("TZ")
settings_tz = settings.get("TZ")
if env_tz:
TZ = pytz.timezone(env_tz)
elif settings_tz:
TZ = pytz.timezone(settings_tz)
else:
raise Exception("No timezone setting found! Please read the README!")
+78 -64
View File
@@ -1,12 +1,13 @@
import requests
from datetime import datetime, timedelta
import mqtt
import util
from datetime import datetime, timedelta
from config import settings
from babel.dates import format_datetime
from util import keys_exists
from const import charging_system_states, charging_connection_states, CLIMATE_START_URL, \
OAUTH_URL, VEHICLES_URL, VEHICLE_DETAILS_URL, RECHARGE_STATE_URL, \
WINDOWS_STATE_URL, LOCK_STATE_URL, TYRE_STATE_URL, supported_entities, BATTERY_CHARGE_STATE_URL
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, \
STATISTICS_URL
session = requests.Session()
session.headers = {
@@ -41,7 +42,7 @@ def authorize():
session.headers.update({'authorization': "Bearer " + data["access_token"]})
global token_expires_at, refresh_token
token_expires_at = datetime.now() + timedelta(seconds=(data["expires_in"] - 30))
token_expires_at = datetime.now(util.TZ) + timedelta(seconds=(data["expires_in"] - 30))
refresh_token = data["refresh_token"]
get_vehicles()
@@ -70,7 +71,7 @@ def refresh_auth():
session.headers.update({'authorization': "Bearer " + data["access_token"]})
global token_expires_at
token_expires_at = datetime.now() + timedelta(seconds=(data["expires_in"] - 30))
token_expires_at = datetime.now(util.TZ) + timedelta(seconds=(data["expires_in"] - 30))
refresh_token = data["refresh_token"]
@@ -165,33 +166,15 @@ def disable_climate(vin):
def api_call(url, method, vin, sensor_id=None, force_update=False):
global token_expires_at
if datetime.now() >= token_expires_at:
if datetime.now(util.TZ) >= token_expires_at:
refresh_auth()
if url == RECHARGE_STATE_URL:
# Minimize API calls for recharge state
if url in [RECHARGE_STATE_URL, WINDOWS_STATE_URL, LOCK_STATE_URL, TYRE_STATE_URL, STATISTICS_URL]:
# Minimize API calls for endpoints with multiple values
response = cached_request(url, method, vin, force_update)
if response is None:
# Exception caught while getting data from volvo api, doing nothing
return None
elif url == WINDOWS_STATE_URL:
# Minimize API calls for window state
response = cached_request(url, method, vin, force_update)
if response is None:
# Exception caught while getting data from volvo api, doing nothing
return ""
elif url == LOCK_STATE_URL:
# Minimize API calls for door state
response = cached_request(url, method, vin, force_update)
if response is None:
# Exception caught while getting data from volvo api, doing nothing
return ""
elif url == TYRE_STATE_URL:
# Minimize API calls for tyre state
response = cached_request(url, method, vin, force_update)
if response is None:
# Exception caught while getting data from volvo api, doing nothing
return ""
elif method == "GET":
print("Starting " + method + " call against " + url)
try:
@@ -228,7 +211,7 @@ def api_call(url, method, vin, sensor_id=None, force_update=False):
def cached_request(url, method, vin, force_update=False):
global cached_requests
if not keys_exists(cached_requests, vin + "_" + url):
if not util.keys_exists(cached_requests, vin + "_" + url):
# No API Data cached, get fresh data from API
print("Starting " + method + " call against " + url)
try:
@@ -237,11 +220,11 @@ def cached_request(url, method, vin, force_update=False):
print("Error getting data: " + str(e))
return None
data = {"response": response, "last_update": datetime.now()}
data = {"response": response, "last_update": datetime.now(util.TZ)}
cached_requests[vin + "_" + url] = data
else:
if (datetime.now() - cached_requests[vin + "_" + url]["last_update"]).total_seconds() >= settings["updateInterval"] \
or (force_update and (datetime.now() - cached_requests[vin + "_" + url]["last_update"]).total_seconds() >= 2):
if (datetime.now(util.TZ) - cached_requests[vin + "_" + url]["last_update"]).total_seconds() >= settings["updateInterval"] \
or (force_update and (datetime.now(util.TZ) - cached_requests[vin + "_" + url]["last_update"]).total_seconds() >= 2):
# Old Data in Cache, or force mode active, updating
print("Starting " + method + " call against " + url)
try:
@@ -249,7 +232,7 @@ def cached_request(url, method, vin, force_update=False):
except requests.exceptions.RequestException as e:
print("Error getting data: " + str(e))
return None
data = {"response": response, "last_update": datetime.now()}
data = {"response": response, "last_update": datetime.now(util.TZ)}
cached_requests[vin + "_" + url] = data
else:
# Data is up do date, returning cached data
@@ -260,86 +243,117 @@ def cached_request(url, method, vin, force_update=False):
def parse_api_data(data, sensor_id=None):
data = data["data"]
if sensor_id == "battery_charge_level":
return data["batteryChargeLevel"]["value"] if keys_exists(data, "batteryChargeLevel") else None
return data["batteryChargeLevel"]["value"] if util.keys_exists(data, "batteryChargeLevel") else None
elif sensor_id == "electric_range":
return data["electricRange"]["value"] if keys_exists(data, "electricRange") else None
return data["electricRange"]["value"] if util.keys_exists(data, "electricRange") else None
elif sensor_id == "charging_system_status":
return charging_system_states[data["chargingSystemStatus"]["value"]] if keys_exists(data,
return charging_system_states[data["chargingSystemStatus"]["value"]] if util.keys_exists(data,
"chargingSystemStatus") else None
elif sensor_id == "charging_connection_status":
return charging_connection_states[data["chargingConnectionStatus"]["value"]] if keys_exists(data,
return charging_connection_states[data["chargingConnectionStatus"]["value"]] if util.keys_exists(data,
"chargingConnectionStatus") else None
elif sensor_id == "estimated_charging_time":
if keys_exists(data, "chargingSystemStatus"):
if util.keys_exists(data, "chargingSystemStatus"):
charging_system_state = charging_system_states[data["chargingSystemStatus"]["value"]]
if charging_system_state == "Charging":
return data["estimatedChargingTime"]["value"] if keys_exists(data, "estimatedChargingTime") else ""
return data["estimatedChargingTime"]["value"] if util.keys_exists(data, "estimatedChargingTime") else ""
else:
return 0
else:
return None
elif sensor_id == "estimated_charging_finish_time":
if keys_exists(data, "chargingSystemStatus"):
if util.keys_exists(data, "chargingSystemStatus"):
charging_system_state = charging_system_states[data["chargingSystemStatus"]["value"]]
if charging_system_state == "Charging":
charging_time = int(data["estimatedChargingTime"]["value"] if keys_exists(data, "estimatedChargingTime")
charging_time = int(data["estimatedChargingTime"]["value"] if util.keys_exists(data, "estimatedChargingTime")
else 0)
charging_finished = datetime.now() + timedelta(minutes=charging_time)
charging_finished = datetime.now(util.TZ) + timedelta(minutes=charging_time)
return format_datetime(charging_finished, format="medium", locale=settings["babelLocale"])
else:
return ""
else:
return None
elif sensor_id == "lock_status":
return data["carLocked"]["value"] if keys_exists(data, "carLocked") else None
return data["carLocked"]["value"] if util.keys_exists(data, "carLocked") else None
elif sensor_id == "odometer":
multiplier = 1
if keys_exists(settings["volvoData"], "odometerMultiplier"):
if util.keys_exists(settings["volvoData"], "odometerMultiplier"):
multiplier = settings["volvoData"]["odometerMultiplier"]
if isinstance(multiplier, str):
multiplier = 1
elif multiplier < 1:
multiplier = 1
return int(data["odometer"]["value"]) * multiplier if keys_exists(data, "odometer") else None
return int(data["odometer"]["value"]) * multiplier if util.keys_exists(data, "odometer") else None
elif sensor_id == "window_front_left":
return data["frontLeftWindowOpen"]["value"] if keys_exists(data, "frontLeftWindowOpen") else None
return window_states[data["frontLeftWindowOpen"]["value"]] if util.keys_exists(data, "frontLeftWindowOpen") \
else None
elif sensor_id == "window_front_right":
return data["frontRightWindowOpen"]["value"] if keys_exists(data, "frontRightWindowOpen") else None
return window_states[data["frontRightWindowOpen"]["value"]] if util.keys_exists(data, "frontRightWindowOpen") \
else None
elif sensor_id == "window_rear_left":
return data["rearLeftWindowOpen"]["value"] if keys_exists(data, "rearLeftWindowOpen") else None
return window_states[data["rearLeftWindowOpen"]["value"]] if util.keys_exists(data, "rearLeftWindowOpen") \
else None
elif sensor_id == "window_rear_right":
return data["rearRightWindowOpen"]["value"] if keys_exists(data, "rearRightWindowOpen") else None
return window_states[data["rearRightWindowOpen"]["value"]] if util.keys_exists(data, "rearRightWindowOpen") \
else None
elif sensor_id == "door_front_left":
return data["frontLeftDoorOpen"]["value"] if keys_exists(data, "frontLeftDoorOpen") else None
return door_states[data["frontLeftDoorOpen"]["value"]] if util.keys_exists(data, "frontLeftDoorOpen") else None
elif sensor_id == "door_front_right":
return data["frontRightDoorOpen"]["value"] if keys_exists(data, "frontRightDoorOpen") else None
return door_states[data["frontRightDoorOpen"]["value"]] if util.keys_exists(data, "frontRightDoorOpen") else None
elif sensor_id == "door_rear_left":
return data["rearLeftDoorOpen"]["value"] if keys_exists(data, "rearLeftDoorOpen") else None
return door_states[data["rearLeftDoorOpen"]["value"]] if util.keys_exists(data, "rearLeftDoorOpen") else None
elif sensor_id == "door_rear_right":
return data["rearRightDoorOpen"]["value"] if keys_exists(data, "rearRightDoorOpen") else None
return door_states[data["rearRightDoorOpen"]["value"]] if util.keys_exists(data, "rearRightDoorOpen") else None
elif sensor_id == "tailgate":
return data["tailGateOpen"]["value"] if keys_exists(data, "tailGateOpen") else None
return door_states[data["tailGateOpen"]["value"]] if util.keys_exists(data, "tailGateOpen") else None
elif sensor_id == "sunroof":
return door_states[data["capOpen"]["value"]] if util.keys_exists(data, "capOpen") else None
elif sensor_id == "engine_hood":
return data["hoodOpen"]["value"] if keys_exists(data, "hoodOpen") else None
return door_states[data["hoodOpen"]["value"]] if util.keys_exists(data, "hoodOpen") else None
elif sensor_id == "tank_lid":
return data["tankLidOpen"]["value"] if keys_exists(data, "tankLidOpen") else None
return door_states[data["tankLidOpen"]["value"]] if util.keys_exists(data, "tankLidOpen") else None
elif sensor_id == "tyre_front_left":
return data["frontLeftTyrePressure"]["value"] if keys_exists(data, "frontLeftTyrePressure") else None
return data["frontLeftTyrePressure"]["value"] if util.keys_exists(data, "frontLeftTyrePressure") else None
elif sensor_id == "tyre_front_right":
return data["frontRightTyrePressure"]["value"] if keys_exists(data, "frontRightTyrePressure") else None
return data["frontRightTyrePressure"]["value"] if util.keys_exists(data, "frontRightTyrePressure") else None
elif sensor_id == "tyre_rear_left":
return data["rearLeftTyrePressure"]["value"] if keys_exists(data, "rearLeftTyrePressure") else None
return data["rearLeftTyrePressure"]["value"] if util.keys_exists(data, "rearLeftTyrePressure") else None
elif sensor_id == "tyre_rear_right":
return data["rearRightTyrePressure"]["value"] if keys_exists(data, "rearRightTyrePressure") else None
return data["rearRightTyrePressure"]["value"] if util.keys_exists(data, "rearRightTyrePressure") else None
elif sensor_id == "engine_state":
return data["engineRunning"]["value"] if keys_exists(data, "engineRunning") else None
return data["engineRunning"]["value"] if util.keys_exists(data, "engineRunning") else None
elif sensor_id == "fuel_level":
return data["fuelAmount"]["value"] if keys_exists(data, "fuelAmount") else None
if util.keys_exists(data, "fuelAmount"):
fuel_amount = int(data["fuelAmount"]["value"])
if fuel_amount > 0:
return fuel_amount
else:
return None
else:
return None
elif sensor_id == "average_fuel_consumption":
if util.keys_exists(data, "averageFuelConsumption"):
average_fuel_con = float(data["averageFuelConsumption"]["value"])
if average_fuel_con > 0:
return average_fuel_con
else:
return None
else:
return None
elif sensor_id == "average_speed":
if util.keys_exists(data, "averageSpeed"):
average_speed = int(data["averageSpeed"]["value"])
if average_speed > 0:
return average_speed
else:
return None
else:
return None
elif sensor_id == "location":
coordinates = {}
if keys_exists(data, "geometry"):
if util.keys_exists(data, "geometry"):
raw_data = data["geometry"]
if keys_exists(raw_data, "coordinates"):
if util.keys_exists(raw_data, "coordinates"):
coordinates = {"longitude": raw_data["coordinates"][0],
"latitude": raw_data["coordinates"][1],
"gps_accuracy": 1}