forked from peter/volvo2mqtt
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bf6568d945 | ||
|
|
f8b6f9cf53 | ||
|
|
7c32ca59be | ||
|
|
67948b0900 | ||
|
|
48663d2e0d | ||
|
|
76cfa21db7 | ||
|
|
9d11dff24b | ||
|
|
3a4160451c | ||
|
|
515e1a8d27 | ||
|
|
4961f7bfad | ||
|
|
a68216bdd5 | ||
|
|
98d8dd7e65 | ||
|
|
7acfdea1d8 | ||
|
|
f95348769b | ||
|
|
4673f14a1c | ||
|
|
1a2f35f1b5 | ||
|
|
33ee258f3f | ||
|
|
54db72df94 | ||
|
|
70b027fb70 |
@@ -129,7 +129,17 @@ The following steps are required for authentication in exactly this order:
|
||||
Just install this addon with the following command.
|
||||
Please note to fill in your settings inside the environment variables.
|
||||
|
||||
`docker run -d --pull=always -e CONF_updateInterval=300 -e CONF_babelLocale='de' -e CONF_mqtt='@json {"broker": "", "username": "", "password": "", "port": 1883}' -e CONF_volvoData='@json {"username": "", "password": "", "vin": "", "vccapikey": ["key1", "key2"]}' -e TZ='Europe/Berlin' --name volvo2mqtt ghcr.io/dielee/volvo2mqtt:latest`
|
||||
<pre>
|
||||
<code>docker run -d --pull=always \
|
||||
-e CONF_updateInterval=300 \
|
||||
-e CONF_babelLocale='de' \
|
||||
-e CONF_mqtt='@json {"broker": "", "username": "", "password": "", "port": 1883}' \
|
||||
-e CONF_volvoData='@json {"username": "", "password": "", "vin": "", "vccapikey": ["key1", "key2"]}' \
|
||||
-e TZ='Europe/Berlin' \
|
||||
--name volvo2mqtt \
|
||||
ghcr.io/dielee/volvo2mqtt:latest
|
||||
</code>
|
||||
</pre>
|
||||
|
||||
<b>HA Add-On:</b><br>
|
||||
|
||||
@@ -161,6 +171,6 @@ Here is what every option means:
|
||||
[i386-shield]: https://img.shields.io/badge/i386-yes-green.svg
|
||||
[releases]: https://github.com/Dielee/volvo2mqtt/releases
|
||||
[releases-shield]: https://img.shields.io/github/release/Dielee/volvo2mqtt.svg
|
||||
[maintenance-shield]: https://img.shields.io/maintenance/yes/2025.svg
|
||||
[maintenance-shield]: https://img.shields.io/maintenance/yes/2026.svg
|
||||
[commits-shield]: https://img.shields.io/github/commit-activity/y/Dielee/volvo2mqtt.svg
|
||||
[commits]: https://github.com/Dielee/volvo2mqtt/commits/main
|
||||
|
||||
+4
-4
@@ -1,6 +1,6 @@
|
||||
requests~=2.32.0
|
||||
dynaconf~=3.1.12
|
||||
paho-mqtt~=1.6.1
|
||||
Babel~=2.12.1
|
||||
pytz~=2023.3
|
||||
dynaconf~=3.2.13
|
||||
paho-mqtt~=2.1.0
|
||||
Babel~=2.18.0
|
||||
pytz~=2026.1.post1
|
||||
google_play_scraper~=1.2.6
|
||||
@@ -1,3 +1,53 @@
|
||||
## v1.13.5
|
||||
|
||||
### 🐛 Bug Fixes:
|
||||
|
||||
- Add build.yaml for homeassistant version > 2026.04.X
|
||||
|
||||
## v1.13.4
|
||||
|
||||
### 🐛 Bug Fixes:
|
||||
|
||||
- Fix `AttributeError: module 'paho.mqtt.client' has no attribute 'CallbackAPIVersion'` when paho-mqtt < 2.0 is installed
|
||||
|
||||
## v1.13.3
|
||||
|
||||
### 🐛 Bug Fixes:
|
||||
|
||||
- Bump pypi packages
|
||||
|
||||
## v1.13.2
|
||||
|
||||
### 🐛 Bug Fixes:
|
||||
|
||||
- Fix KeyError: 'authorizeResponse' in auth flow #318
|
||||
|
||||
## v1.13.1
|
||||
|
||||
### 🐛 Bug Fixes:
|
||||
|
||||
- Fix missing charging system state `error` #314
|
||||
- Move `object_id` to `default_entity_id` for mqtt entities according to #312
|
||||
|
||||
## v1.13.0
|
||||
|
||||
### 🚀 Features:
|
||||
|
||||
- Move energy api to v2 (https://developer.volvocars.com/apis/energy/v2/release-notes/)
|
||||
|
||||
## v1.12.2
|
||||
|
||||
### 🚀 Features:
|
||||
|
||||
- Add more logging for refresh process
|
||||
- Update refresh token, if volvo API returns a new one
|
||||
|
||||
## v1.12.1
|
||||
|
||||
### 🐛 Bug Fixes:
|
||||
|
||||
- Fix `trip_speed` and `trip_distance` unit for `en_US` and `en_GB` #301
|
||||
|
||||
## v1.12.0
|
||||
|
||||
### 🚀 Features:
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
build_from:
|
||||
amd64: ghcr.io/home-assistant/base-python:latest
|
||||
aarch64: ghcr.io/home-assistant/base-python:latest
|
||||
armv7: ghcr.io/home-assistant/base-python:latest
|
||||
armhf: ghcr.io/home-assistant/base-python:latest
|
||||
i386: ghcr.io/home-assistant/base-python:latest
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
name: "Volvo2Mqtt"
|
||||
description: "Volvo AAOS MQTT bridge"
|
||||
version: "1.12.0"
|
||||
version: "1.13.5"
|
||||
slug: "volvo2mqtt"
|
||||
init: false
|
||||
url: "https://github.com/Dielee/volvo2mqtt"
|
||||
|
||||
+14
-10
@@ -1,6 +1,6 @@
|
||||
from config import settings
|
||||
|
||||
VERSION = "v1.12.0"
|
||||
VERSION = "v1.13.5"
|
||||
|
||||
OAUTH_TOKEN_URL = "https://volvoid.eu.volvocars.com/as/token.oauth2"
|
||||
OAUTH_AUTH_URL = "https://volvoid.eu.volvocars.com/as/authorization.oauth2"
|
||||
@@ -12,7 +12,7 @@ CLIMATE_STOP_URL = "https://api.volvocars.com/connected-vehicle/v2/vehicles/{0}/
|
||||
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"
|
||||
RECHARGE_STATE_URL = "https://api.volvocars.com/energy/v2/vehicles/{0}/state"
|
||||
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"
|
||||
TYRE_STATE_URL = "https://api.volvocars.com/connected-vehicle/v2/vehicles/{0}/tyres"
|
||||
@@ -41,7 +41,9 @@ units = {
|
||||
"odometer": {"unit": LENGTH_MILES},
|
||||
"average_speed": {"unit": SPEED_MILES_PER_HOUR},
|
||||
"distance_to_empty": {"unit": LENGTH_MILES},
|
||||
"distance_to_service": {"unit": LENGTH_MILES}
|
||||
"distance_to_service": {"unit": LENGTH_MILES},
|
||||
"trip_distance": {"unit": LENGTH_MILES},
|
||||
"trip_speed": {"unit": SPEED_MILES_PER_HOUR}
|
||||
},
|
||||
"en_US": {
|
||||
"divider": 1.60934,
|
||||
@@ -49,19 +51,21 @@ units = {
|
||||
"odometer": {"unit": LENGTH_MILES},
|
||||
"average_speed": {"unit": SPEED_MILES_PER_HOUR},
|
||||
"distance_to_empty": {"unit": LENGTH_MILES},
|
||||
"distance_to_service": {"unit": LENGTH_MILES}
|
||||
"distance_to_service": {"unit": LENGTH_MILES},
|
||||
"trip_distance": {"unit": LENGTH_MILES},
|
||||
"trip_speed": {"unit": SPEED_MILES_PER_HOUR}
|
||||
}
|
||||
}
|
||||
|
||||
availability_topic = "volvoAAOS2mqtt/availability"
|
||||
|
||||
charging_system_states = {"CHARGING_SYSTEM_CHARGING": "Charging", "CHARGING_SYSTEM_IDLE": "Idle",
|
||||
"CHARGING_SYSTEM_FAULT": "Fault", "CHARGING_SYSTEM_UNSPECIFIED": "Unspecified",
|
||||
"CHARGING_SYSTEM_DONE": "Done", "CHARGING_SYSTEM_SCHEDULED": "Scheduled"}
|
||||
charging_system_states = {"CHARGING": "Charging", "IDLE": "Idle",
|
||||
"FAULT": "Fault", "UNSPECIFIED": "Unspecified",
|
||||
"DONE": "Done", "SCHEDULED": "Scheduled", "ERROR": "Error",
|
||||
"DISCHARGING": "Discharging"}
|
||||
|
||||
charging_connection_states = {"CONNECTION_STATUS_DISCONNECTED": "Disconnected", "CONNECTION_STATUS_UNSPECIFIED": "Unspecified",
|
||||
"CONNECTION_STATUS_CONNECTED_DC": "Connected DC", "CONNECTION_STATUS_CONNECTED_AC": "Connected AC",
|
||||
"CONNECTION_STATUS_FAULT": "Fault"}
|
||||
charging_connection_states = {"DISCONNECTED": "Disconnected", "UNSPECIFIED": "Unspecified",
|
||||
"FAULT": "Fault", "CONNECTED": "Connected"}
|
||||
|
||||
window_states = {"CLOSED": "OFF", "OPEN": "ON", "UNSPECIFIED": "UNKNOWN", "AJAR": "ON"}
|
||||
door_states = {"CLOSED": "OFF", "OPEN": "ON", "UNSPECIFIED": "UNKNOWN", "AJAR": "ON"}
|
||||
|
||||
+31
-9
@@ -25,8 +25,13 @@ active_schedules = {}
|
||||
otp_code = None
|
||||
|
||||
def connect():
|
||||
client = mqtt.Client("volvoAAOS2mqtt") if os.environ.get("IS_HA_ADDON") \
|
||||
else mqtt.Client("volvoAAOS2mqtt_" + settings.volvoData["username"].replace("+", ""))
|
||||
client_id = "volvoAAOS2mqtt" if os.environ.get("IS_HA_ADDON") \
|
||||
else "volvoAAOS2mqtt_" + settings.volvoData["username"].replace("+", "")
|
||||
try:
|
||||
client = mqtt.Client(mqtt.CallbackAPIVersion.VERSION1, client_id, clean_session=False)
|
||||
except AttributeError:
|
||||
# paho-mqtt < 2.0 does not have CallbackAPIVersion
|
||||
client = mqtt.Client(client_id)
|
||||
|
||||
if "logging" in settings["mqtt"] and settings["mqtt"]["logging"]:
|
||||
mqtt_logger = logging.getLogger("mqtt")
|
||||
@@ -35,6 +40,7 @@ def connect():
|
||||
client.on_message = safe_on_message
|
||||
client.on_disconnect = on_disconnect
|
||||
client.on_connect = on_connect
|
||||
client.reconnect_delay_set(min_delay=1, max_delay=30)
|
||||
|
||||
client.will_set(availability_topic, "offline", 0, False)
|
||||
if settings["mqtt"]["username"] and settings["mqtt"]["password"]:
|
||||
@@ -45,10 +51,9 @@ def connect():
|
||||
if isinstance(conf_port, int):
|
||||
if conf_port > 0:
|
||||
port = settings["mqtt"]["port"]
|
||||
client.connect(settings["mqtt"]["broker"], port)
|
||||
client.connect(settings["mqtt"]["broker"], port, 120)
|
||||
|
||||
client.loop_start()
|
||||
client.subscribe("volvoAAOS2mqtt/otp_code")
|
||||
|
||||
global mqtt_client
|
||||
mqtt_client = client
|
||||
@@ -58,7 +63,7 @@ def create_otp_input():
|
||||
state_topic = otp_mqtt_topic + "/state"
|
||||
config = {
|
||||
"name": "Volvo OTP",
|
||||
"object_id": "volvo_otp",
|
||||
"default_entity_id": "volvo_otp",
|
||||
"schema": "state",
|
||||
"command_topic": otp_mqtt_topic,
|
||||
"state_topic": state_topic,
|
||||
@@ -95,7 +100,7 @@ def send_car_images(vin, data, device):
|
||||
image_topic = f"homeassistant/image/{vin}_{entity['id']}/image_topic"
|
||||
config = {
|
||||
"name": entity["name"],
|
||||
"object_id": f"volvo_{vin}_{entity['id']}",
|
||||
"default_entity_id": f"volvo_{vin}_{entity['id']}",
|
||||
"schema": "state",
|
||||
"icon": "mdi:image-area",
|
||||
"content_type": "image/png",
|
||||
@@ -140,6 +145,7 @@ def on_connect(client, userdata, flags, rc):
|
||||
logging.info("MQTT connected")
|
||||
|
||||
send_heartbeat()
|
||||
mqtt_client.subscribe("volvoAAOS2mqtt/otp_code")
|
||||
if len(subscribed_topics) > 0:
|
||||
for topic in subscribed_topics:
|
||||
mqtt_client.subscribe(topic)
|
||||
@@ -176,16 +182,24 @@ def on_message(client, userdata, msg):
|
||||
if "climate_status" in msg.topic:
|
||||
if payload == "ON":
|
||||
start_climate(vin)
|
||||
return None
|
||||
elif payload == "OFF":
|
||||
stop_climate(vin)
|
||||
return None
|
||||
return None
|
||||
elif "lock_status" in msg.topic:
|
||||
if payload == "LOCK":
|
||||
lock_car(vin)
|
||||
return None
|
||||
elif payload == "UNLOCK":
|
||||
unlock_car(vin)
|
||||
return None
|
||||
return None
|
||||
elif "update_data" in msg.topic:
|
||||
if payload == "PRESS":
|
||||
update_car_data(True)
|
||||
return None
|
||||
return None
|
||||
elif "update_interval" in msg.topic:
|
||||
try:
|
||||
update_interval = int(payload)
|
||||
@@ -194,8 +208,10 @@ def on_message(client, userdata, msg):
|
||||
else:
|
||||
logging.warning(f"Interval {update_interval} seconds is to low. Doing nothing!")
|
||||
update_car_data()
|
||||
return None
|
||||
except ValueError as error:
|
||||
logging.error(f"Unable to change update_interval {error} payload={payload}")
|
||||
logging.error(f"Unable to change update_interval {error} payload={payload}")
|
||||
return None
|
||||
elif "schedule" in msg.topic:
|
||||
try:
|
||||
d = json.loads(payload)
|
||||
@@ -205,8 +221,11 @@ def on_message(client, userdata, msg):
|
||||
|
||||
if d["mode"] == "timer":
|
||||
start_climate_timer(d, vin)
|
||||
return None
|
||||
else:
|
||||
logging.warning("No schedule mode found, doing nothing")
|
||||
return None
|
||||
return None
|
||||
|
||||
|
||||
def start_climate_timer(d, vin):
|
||||
@@ -392,7 +411,7 @@ def update_ha_device(entity, vin, state):
|
||||
logging.debug("Updating icon to " + icon + " for " + entity["id"])
|
||||
config = {
|
||||
"name": entity['name'],
|
||||
"object_id": f"volvo_{vin}_{entity['id']}",
|
||||
"default_entity_id": f"volvo_{vin}_{entity['id']}",
|
||||
"schema": "state",
|
||||
"icon": f"mdi:{icon}" if icon else f"mdi:{entity['icon']}",
|
||||
"state_topic": f"homeassistant/{entity['domain']}/{vin}_{entity['id']}/state",
|
||||
@@ -421,6 +440,7 @@ def update_ha_device(entity, vin, state):
|
||||
mqtt_client.publish(
|
||||
f"homeassistant/{entity['domain']}/volvoAAOS2mqtt/{vin}_{entity['id']}/config",
|
||||
json.dumps(config),
|
||||
qos=1,
|
||||
retain=True
|
||||
)
|
||||
|
||||
@@ -433,7 +453,7 @@ def create_ha_devices():
|
||||
for entity in volvo.supported_endpoints[vin]:
|
||||
config = {
|
||||
"name": entity['name'],
|
||||
"object_id": f"volvo_{vin}_{entity['id']}",
|
||||
"default_entity_id": f"volvo_{vin}_{entity['id']}",
|
||||
"schema": "state",
|
||||
"icon": f"mdi:{entity['icon']}",
|
||||
"state_topic": f"homeassistant/{entity['domain']}/{vin}_{entity['id']}/state",
|
||||
@@ -471,8 +491,10 @@ def create_ha_devices():
|
||||
mqtt_client.publish(
|
||||
f"homeassistant/{entity['domain']}/volvoAAOS2mqtt/{vin}_{entity['id']}/config",
|
||||
json.dumps(config),
|
||||
qos=1,
|
||||
retain=True
|
||||
)
|
||||
time.sleep(0.05)
|
||||
time.sleep(2)
|
||||
send_heartbeat()
|
||||
|
||||
|
||||
+214
-195
@@ -48,6 +48,10 @@ def authorize(renew_tokenfile=False):
|
||||
logging.warning("Detected corrupted token file, restarting auth process")
|
||||
authorize(True)
|
||||
return
|
||||
except KeyError:
|
||||
logging.warning("Token file missing refresh_token, restarting auth process")
|
||||
authorize(True)
|
||||
return
|
||||
else:
|
||||
logging.info("Starting login with OTP")
|
||||
auth_session = requests.session()
|
||||
@@ -110,7 +114,7 @@ def authorize(renew_tokenfile=False):
|
||||
|
||||
def continue_auth(auth_session, data):
|
||||
next_url = data["_links"]["continueAuthentication"]["href"].replace("http://", "https://") + "?action=continueAuthentication"
|
||||
auth = auth_session.get(next_url)
|
||||
auth = auth_session.post(next_url, data="{}")
|
||||
|
||||
if auth.status_code == 200:
|
||||
return auth.json()
|
||||
@@ -171,8 +175,9 @@ def send_otp(auth_session, data):
|
||||
|
||||
|
||||
def refresh_auth():
|
||||
logging.info("Refreshing credentials")
|
||||
global refresh_token
|
||||
logging.info("Refreshing credentials with token " + refresh_token)
|
||||
|
||||
headers = {
|
||||
"authorization": "Basic aDRZZjBiOlU4WWtTYlZsNnh3c2c1WVFxWmZyZ1ZtSWFEcGhPc3kxUENhVXNpY1F0bzNUUjVrd2FKc2U0QVpkZ2ZJZmNMeXc=",
|
||||
"content-type": "application/x-www-form-urlencoded",
|
||||
@@ -192,16 +197,22 @@ def refresh_auth():
|
||||
|
||||
if auth.status_code == 200:
|
||||
token_path = util.get_token_path()
|
||||
if os.path.exists(token_path):
|
||||
with open(token_path) as f:
|
||||
data = json.load(f)
|
||||
|
||||
refresh_data = auth.json()
|
||||
|
||||
f = open(token_path)
|
||||
try:
|
||||
data = json.load(f)
|
||||
refresh_token = data["refresh_token"]
|
||||
except ValueError:
|
||||
raise Exception("Cannot refresh token!")
|
||||
if not "refresh_token" in refresh_data:
|
||||
f = open(token_path)
|
||||
try:
|
||||
data = json.load(f)
|
||||
refresh_data["refresh_token"] = data["refresh_token"]
|
||||
except ValueError:
|
||||
raise Exception("Cannot refresh token!")
|
||||
else:
|
||||
logging.info("New refresh token found, updating!")
|
||||
|
||||
refresh_data["refresh_token"] = refresh_token
|
||||
util.save_to_json(refresh_data, token_path)
|
||||
session.headers.update({"authorization": "Bearer " + refresh_data["access_token"]})
|
||||
|
||||
@@ -451,7 +462,9 @@ def check_engine_status(vin):
|
||||
mqtt.assumed_climate_state[vin] = "OFF"
|
||||
mqtt.update_car_data()
|
||||
break
|
||||
|
||||
time.sleep(5)
|
||||
return None
|
||||
|
||||
|
||||
def backend_status_loop():
|
||||
@@ -587,196 +600,202 @@ def cached_request(url, method, vin, force_update=False, key_change=False):
|
||||
|
||||
|
||||
def parse_api_data(data, sensor_id=None):
|
||||
if sensor_id != "api_backend_status":
|
||||
data = data["data"]
|
||||
if sensor_id == "battery_charge_level":
|
||||
return data["batteryChargeLevel"]["value"] if util.keys_exists(data, "batteryChargeLevel") else None
|
||||
elif sensor_id == "battery_capacity":
|
||||
return data["batteryCapacityKWH"] if util.keys_exists(data, "batteryCapacityKWH") else None
|
||||
elif sensor_id == "electric_range":
|
||||
return util.convert_metric_values(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 util.keys_exists(data, "chargingSystemStatus") else None
|
||||
elif sensor_id == "charging_connection_status":
|
||||
return charging_connection_states[data["chargingConnectionStatus"]["value"]] \
|
||||
if util.keys_exists(data, "chargingConnectionStatus") else None
|
||||
elif sensor_id == "estimated_charging_time":
|
||||
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 util.keys_exists(data, "estimatedChargingTime") else ""
|
||||
if sensor_id in ["battery_charge_level", "electric_range", "charging_system_status",
|
||||
"charging_connection_status", "estimated_charging_time", "estimated_charging_finish_time"]:
|
||||
|
||||
if sensor_id == "battery_charge_level":
|
||||
return data["batteryChargeLevel"]["value"] if util.keys_exists(data, "batteryChargeLevel") else None
|
||||
elif sensor_id == "electric_range":
|
||||
return util.convert_metric_values(data["electricRange"]["value"]) \
|
||||
if util.keys_exists(data, "electricRange") else None
|
||||
elif sensor_id == "charging_system_status":
|
||||
return charging_system_states[data["chargingStatus"]["value"]] \
|
||||
if util.keys_exists(data, "chargingStatus") else None
|
||||
elif sensor_id == "charging_connection_status":
|
||||
return charging_connection_states[data["chargerConnectionStatus"]["value"]] \
|
||||
if util.keys_exists(data, "chargerConnectionStatus") else None
|
||||
elif sensor_id == "estimated_charging_time":
|
||||
if util.keys_exists(data, "chargingStatus"):
|
||||
charging_system_state = charging_system_states[data["chargingStatus"]["value"]]
|
||||
if charging_system_state == "Charging":
|
||||
return data["estimatedChargingTimeToTargetBatteryChargeLevel"]["value"] if util.keys_exists(data,
|
||||
"estimatedChargingTimeToTargetBatteryChargeLevel") else ""
|
||||
else:
|
||||
return 0
|
||||
return None
|
||||
elif sensor_id == "estimated_charging_finish_time":
|
||||
if util.keys_exists(data, "estimatedChargingTimeToTargetBatteryChargeLevel"):
|
||||
charging_system_state = charging_system_states[data["chargingStatus"]["value"]]
|
||||
if charging_system_state == "Charging":
|
||||
charging_time = int(
|
||||
data["estimatedChargingTimeToTargetBatteryChargeLevel"]["value"] if util.keys_exists(data, "estimatedChargingTimeToTargetBatteryChargeLevel")
|
||||
else 0)
|
||||
charging_finished = datetime.now(util.TZ) + timedelta(minutes=charging_time)
|
||||
return format_datetime(charging_finished, format="medium", locale=settings["babelLocale"])
|
||||
else:
|
||||
return ""
|
||||
return None
|
||||
return None
|
||||
else:
|
||||
if sensor_id != "api_backend_status":
|
||||
data = data["data"]
|
||||
|
||||
if sensor_id == "lock_status":
|
||||
return data["centralLock"]["value"] if util.keys_exists(data, "centralLock") else None
|
||||
elif sensor_id == "battery_capacity":
|
||||
return data["batteryCapacityKWH"] if util.keys_exists(data, "batteryCapacityKWH") else None
|
||||
elif sensor_id == "odometer":
|
||||
return util.convert_metric_values(int(data["odometer"]["value"])) \
|
||||
if util.keys_exists(data, "odometer") else None
|
||||
elif sensor_id == "window_front_left":
|
||||
return window_states[data["frontLeftWindow"]["value"]] if util.keys_exists(data, "frontLeftWindow") \
|
||||
else None
|
||||
elif sensor_id == "window_front_right":
|
||||
return window_states[data["frontRightWindow"]["value"]] if util.keys_exists(data, "frontRightWindow") \
|
||||
else None
|
||||
elif sensor_id == "window_rear_left":
|
||||
return window_states[data["rearLeftWindow"]["value"]] if util.keys_exists(data, "rearLeftWindow") \
|
||||
else None
|
||||
elif sensor_id == "window_rear_right":
|
||||
return window_states[data["rearRightWindow"]["value"]] if util.keys_exists(data, "rearRightWindow") \
|
||||
else None
|
||||
elif sensor_id == "door_front_left":
|
||||
return door_states[data["frontLeftDoor"]["value"]] if util.keys_exists(data, "frontLeftDoor") else None
|
||||
elif sensor_id == "door_front_right":
|
||||
return door_states[data["frontRightDoor"]["value"]] \
|
||||
if util.keys_exists(data, "frontRightDoor") else None
|
||||
elif sensor_id == "door_rear_left":
|
||||
return door_states[data["rearLeftDoor"]["value"]] if util.keys_exists(data, "rearLeftDoor") else None
|
||||
elif sensor_id == "door_rear_right":
|
||||
return door_states[data["rearRightDoor"]["value"]] if util.keys_exists(data, "rearRightDoor") else None
|
||||
elif sensor_id == "tailgate":
|
||||
return door_states[data["tailgate"]["value"]] if util.keys_exists(data, "tailgate") else None
|
||||
elif sensor_id == "sunroof":
|
||||
return door_states[data["sunroof"]["value"]] if util.keys_exists(data, "sunroof") else None
|
||||
elif sensor_id == "engine_hood":
|
||||
return door_states[data["hood"]["value"]] if util.keys_exists(data, "hood") else None
|
||||
elif sensor_id == "tank_lid":
|
||||
return door_states[data["tankLid"]["value"]] if util.keys_exists(data, "tankLid") else None
|
||||
elif sensor_id == "tyre_front_left":
|
||||
return data["frontLeft"]["value"] if util.keys_exists(data, "frontLeft") else None
|
||||
elif sensor_id == "tyre_front_right":
|
||||
return data["frontRight"]["value"] if util.keys_exists(data, "frontRight") else None
|
||||
elif sensor_id == "tyre_rear_left":
|
||||
return data["rearLeft"]["value"] if util.keys_exists(data, "rearLeft") else None
|
||||
elif sensor_id == "tyre_rear_right":
|
||||
return data["rearRight"]["value"] if util.keys_exists(data, "rearRight") else None
|
||||
elif sensor_id == "engine_state":
|
||||
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"])
|
||||
if fuel_amount > 0:
|
||||
return fuel_amount
|
||||
return None
|
||||
elif sensor_id == "average_fuel_consumption":
|
||||
average_fuel_con = 0
|
||||
if util.keys_exists(data, "averageFuelConsumption"):
|
||||
average_fuel_con = float(data["averageFuelConsumption"]["value"])
|
||||
elif util.keys_exists(data, "averageFuelConsumptionAutomatic"):
|
||||
average_fuel_con = float(data["averageFuelConsumptionAutomatic"]["value"])
|
||||
|
||||
if average_fuel_con > 0:
|
||||
return average_fuel_con
|
||||
return None
|
||||
elif sensor_id == "average_speed":
|
||||
average_speed = 0
|
||||
if util.keys_exists(data, "averageSpeed"):
|
||||
average_speed = float(data["averageSpeed"]["value"])
|
||||
elif util.keys_exists(data, "averageSpeedAutomatic"):
|
||||
average_speed = float(data["averageSpeedAutomatic"]["value"])
|
||||
|
||||
if average_speed != 0:
|
||||
return util.convert_metric_values(average_speed)
|
||||
|
||||
return None
|
||||
elif sensor_id == "location":
|
||||
coordinates = {}
|
||||
if util.keys_exists(data, "geometry"):
|
||||
raw_data = data["geometry"]
|
||||
if util.keys_exists(raw_data, "coordinates"):
|
||||
coordinates = {"longitude": raw_data["coordinates"][0],
|
||||
"latitude": raw_data["coordinates"][1],
|
||||
"gps_accuracy": 1}
|
||||
return coordinates
|
||||
elif sensor_id == "distance_to_empty_tank":
|
||||
if util.keys_exists(data, "distanceToEmptyTank"):
|
||||
distance_to_empty = int(data["distanceToEmptyTank"]["value"])
|
||||
if distance_to_empty > 0:
|
||||
return util.convert_metric_values(data["distanceToEmptyTank"]["value"])
|
||||
return None
|
||||
elif sensor_id == "distance_to_empty_battery":
|
||||
if util.keys_exists(data, "distanceToEmptyBattery"):
|
||||
distance_to_empty = int(data["distanceToEmptyBattery"]["value"])
|
||||
if distance_to_empty > 0:
|
||||
return util.convert_metric_values(data["distanceToEmptyBattery"]["value"])
|
||||
return 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, "distanceToService"):
|
||||
km_to_service = int(data["distanceToService"]["value"])
|
||||
if km_to_service > 0:
|
||||
return util.convert_metric_values(data["distanceToService"]["value"])
|
||||
return None
|
||||
elif sensor_id == "time_to_service":
|
||||
if util.keys_exists(data, "timeToService"):
|
||||
return str(data["timeToService"]["value"]) + " " + data["timeToService"]["unit"]
|
||||
else:
|
||||
return 0
|
||||
return None
|
||||
elif sensor_id == "estimated_charging_finish_time":
|
||||
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 util.keys_exists(data, "estimatedChargingTime")
|
||||
else 0)
|
||||
charging_finished = datetime.now(util.TZ) + timedelta(minutes=charging_time)
|
||||
return format_datetime(charging_finished, format="medium", locale=settings["babelLocale"])
|
||||
else:
|
||||
return ""
|
||||
return None
|
||||
elif sensor_id == "lock_status":
|
||||
return data["centralLock"]["value"] if util.keys_exists(data, "centralLock") else None
|
||||
elif sensor_id == "odometer":
|
||||
return util.convert_metric_values(int(data["odometer"]["value"])) \
|
||||
if util.keys_exists(data, "odometer") else None
|
||||
elif sensor_id == "window_front_left":
|
||||
return window_states[data["frontLeftWindow"]["value"]] if util.keys_exists(data, "frontLeftWindow") \
|
||||
else None
|
||||
elif sensor_id == "window_front_right":
|
||||
return window_states[data["frontRightWindow"]["value"]] if util.keys_exists(data, "frontRightWindow") \
|
||||
else None
|
||||
elif sensor_id == "window_rear_left":
|
||||
return window_states[data["rearLeftWindow"]["value"]] if util.keys_exists(data, "rearLeftWindow") \
|
||||
else None
|
||||
elif sensor_id == "window_rear_right":
|
||||
return window_states[data["rearRightWindow"]["value"]] if util.keys_exists(data, "rearRightWindow") \
|
||||
else None
|
||||
elif sensor_id == "door_front_left":
|
||||
return door_states[data["frontLeftDoor"]["value"]] if util.keys_exists(data, "frontLeftDoor") else None
|
||||
elif sensor_id == "door_front_right":
|
||||
return door_states[data["frontRightDoor"]["value"]] \
|
||||
if util.keys_exists(data, "frontRightDoor") else None
|
||||
elif sensor_id == "door_rear_left":
|
||||
return door_states[data["rearLeftDoor"]["value"]] if util.keys_exists(data, "rearLeftDoor") else None
|
||||
elif sensor_id == "door_rear_right":
|
||||
return door_states[data["rearRightDoor"]["value"]] if util.keys_exists(data, "rearRightDoor") else None
|
||||
elif sensor_id == "tailgate":
|
||||
return door_states[data["tailgate"]["value"]] if util.keys_exists(data, "tailgate") else None
|
||||
elif sensor_id == "sunroof":
|
||||
return door_states[data["sunroof"]["value"]] if util.keys_exists(data, "sunroof") else None
|
||||
elif sensor_id == "engine_hood":
|
||||
return door_states[data["hood"]["value"]] if util.keys_exists(data, "hood") else None
|
||||
elif sensor_id == "tank_lid":
|
||||
return door_states[data["tankLid"]["value"]] if util.keys_exists(data, "tankLid") else None
|
||||
elif sensor_id == "tyre_front_left":
|
||||
return data["frontLeft"]["value"] if util.keys_exists(data, "frontLeft") else None
|
||||
elif sensor_id == "tyre_front_right":
|
||||
return data["frontRight"]["value"] if util.keys_exists(data, "frontRight") else None
|
||||
elif sensor_id == "tyre_rear_left":
|
||||
return data["rearLeft"]["value"] if util.keys_exists(data, "rearLeft") else None
|
||||
elif sensor_id == "tyre_rear_right":
|
||||
return data["rearRight"]["value"] if util.keys_exists(data, "rearRight") else None
|
||||
elif sensor_id == "engine_state":
|
||||
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"])
|
||||
if fuel_amount > 0:
|
||||
return fuel_amount
|
||||
return None
|
||||
elif sensor_id == "average_fuel_consumption":
|
||||
average_fuel_con = 0
|
||||
if util.keys_exists(data, "averageFuelConsumption"):
|
||||
average_fuel_con = float(data["averageFuelConsumption"]["value"])
|
||||
elif util.keys_exists(data, "averageFuelConsumptionAutomatic"):
|
||||
average_fuel_con = float(data["averageFuelConsumptionAutomatic"]["value"])
|
||||
return None
|
||||
elif sensor_id == "service_warning_status":
|
||||
return data["serviceWarning"]["value"] if util.keys_exists(data, "serviceWarning") else None
|
||||
elif sensor_id == "average_energy_consumption":
|
||||
average_energy_con = 0
|
||||
if util.keys_exists(data, "averageEnergyConsumption"):
|
||||
average_energy_con = data["averageEnergyConsumption"]["value"]
|
||||
elif util.keys_exists(data, "averageEnergyConsumptionAutomatic"):
|
||||
average_energy_con = data["averageEnergyConsumptionAutomatic"]["value"]
|
||||
|
||||
if average_fuel_con > 0:
|
||||
return average_fuel_con
|
||||
return None
|
||||
elif sensor_id == "average_speed":
|
||||
average_speed = 0
|
||||
if util.keys_exists(data, "averageSpeed"):
|
||||
average_speed = float(data["averageSpeed"]["value"])
|
||||
elif util.keys_exists(data, "averageSpeedAutomatic"):
|
||||
average_speed = float(data["averageSpeedAutomatic"]["value"])
|
||||
if average_energy_con != 0:
|
||||
return average_energy_con
|
||||
return None
|
||||
elif sensor_id == "washer_fluid_warning":
|
||||
return data["washerFluidLevelWarning"]["value"] if util.keys_exists(data, "washerFluidLevelWarning") else None
|
||||
elif sensor_id == "warnings":
|
||||
warnings = 0
|
||||
cleaned_data = {}
|
||||
for key, dicts in data.items():
|
||||
contains_data = sum(value == "NO_WARNING" or value == "FAILURE" for value in dicts.values())
|
||||
if contains_data:
|
||||
warnings = warnings + 1
|
||||
cleaned_data[key] = dicts["value"]
|
||||
|
||||
if average_speed != 0:
|
||||
return util.convert_metric_values(average_speed)
|
||||
|
||||
return None
|
||||
elif sensor_id == "location":
|
||||
coordinates = {}
|
||||
if util.keys_exists(data, "geometry"):
|
||||
raw_data = data["geometry"]
|
||||
if util.keys_exists(raw_data, "coordinates"):
|
||||
coordinates = {"longitude": raw_data["coordinates"][0],
|
||||
"latitude": raw_data["coordinates"][1],
|
||||
"gps_accuracy": 1}
|
||||
return coordinates
|
||||
elif sensor_id == "distance_to_empty_tank":
|
||||
if util.keys_exists(data, "distanceToEmptyTank"):
|
||||
distance_to_empty = int(data["distanceToEmptyTank"]["value"])
|
||||
if distance_to_empty > 0:
|
||||
return util.convert_metric_values(data["distanceToEmptyTank"]["value"])
|
||||
return None
|
||||
elif sensor_id == "distance_to_empty_battery":
|
||||
if util.keys_exists(data, "distanceToEmptyBattery"):
|
||||
distance_to_empty = int(data["distanceToEmptyBattery"]["value"])
|
||||
if distance_to_empty > 0:
|
||||
return util.convert_metric_values(data["distanceToEmptyBattery"]["value"])
|
||||
return 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, "distanceToService"):
|
||||
km_to_service = int(data["distanceToService"]["value"])
|
||||
if km_to_service > 0:
|
||||
return util.convert_metric_values(data["distanceToService"]["value"])
|
||||
return None
|
||||
elif sensor_id == "time_to_service":
|
||||
if util.keys_exists(data, "timeToService"):
|
||||
return str(data["timeToService"]["value"]) + " " + data["timeToService"]["unit"]
|
||||
return cleaned_data if warnings > 0 else None
|
||||
elif sensor_id == "trip_fuel_consumption":
|
||||
trip_fuel_con = 0
|
||||
if util.keys_exists(data, "averageFuelConsumptionAutomatic"):
|
||||
trip_fuel_con = float(data["averageFuelConsumptionAutomatic"]["value"])
|
||||
if trip_fuel_con > 0:
|
||||
return trip_fuel_con
|
||||
return None
|
||||
elif sensor_id == "trip_distance":
|
||||
return util.convert_metric_values(int(data["tripMeterAutomatic"]["value"])) \
|
||||
if util.keys_exists(data, "tripMeterAutomatic") else None
|
||||
elif sensor_id == "trip_energy_consumption":
|
||||
trip_energy_con = 0
|
||||
if util.keys_exists(data, "averageEnergyConsumptionAutomatic"):
|
||||
trip_energy_con = data["averageEnergyConsumptionAutomatic"]["value"]
|
||||
if trip_energy_con != 0:
|
||||
return trip_energy_con
|
||||
return None
|
||||
elif sensor_id == "trip_speed":
|
||||
trip_speed = 0
|
||||
if util.keys_exists(data, "averageSpeedAutomatic"):
|
||||
trip_speed = float(data["averageSpeedAutomatic"]["value"])
|
||||
if trip_speed != 0:
|
||||
return util.convert_metric_values(trip_speed)
|
||||
return None
|
||||
else:
|
||||
return None
|
||||
elif sensor_id == "service_warning_status":
|
||||
return data["serviceWarning"]["value"] if util.keys_exists(data, "serviceWarning") else None
|
||||
elif sensor_id == "average_energy_consumption":
|
||||
average_energy_con = 0
|
||||
if util.keys_exists(data, "averageEnergyConsumption"):
|
||||
average_energy_con = data["averageEnergyConsumption"]["value"]
|
||||
elif util.keys_exists(data, "averageEnergyConsumptionAutomatic"):
|
||||
average_energy_con = data["averageEnergyConsumptionAutomatic"]["value"]
|
||||
|
||||
if average_energy_con != 0:
|
||||
return average_energy_con
|
||||
return None
|
||||
elif sensor_id == "washer_fluid_warning":
|
||||
return data["washerFluidLevelWarning"]["value"] if util.keys_exists(data, "washerFluidLevelWarning") else None
|
||||
elif sensor_id == "warnings":
|
||||
warnings = 0
|
||||
cleaned_data = {}
|
||||
for key, dicts in data.items():
|
||||
contains_data = sum(value == "NO_WARNING" or value == "FAILURE" for value in dicts.values())
|
||||
if contains_data:
|
||||
warnings = warnings + 1
|
||||
cleaned_data[key] = dicts["value"]
|
||||
|
||||
return cleaned_data if warnings > 0 else None
|
||||
elif sensor_id == "trip_fuel_consumption":
|
||||
trip_fuel_con = 0
|
||||
if util.keys_exists(data, "averageFuelConsumptionAutomatic"):
|
||||
trip_fuel_con = float(data["averageFuelConsumptionAutomatic"]["value"])
|
||||
if trip_fuel_con > 0:
|
||||
return trip_fuel_con
|
||||
return None
|
||||
elif sensor_id == "trip_distance":
|
||||
return util.convert_metric_values(int(data["tripMeterAutomatic"]["value"])) \
|
||||
if util.keys_exists(data, "tripMeterAutomatic") else None
|
||||
elif sensor_id == "trip_energy_consumption":
|
||||
trip_energy_con = 0
|
||||
if util.keys_exists(data, "averageEnergyConsumptionAutomatic"):
|
||||
trip_energy_con = data["averageEnergyConsumptionAutomatic"]["value"]
|
||||
if trip_energy_con != 0:
|
||||
return trip_energy_con
|
||||
return None
|
||||
elif sensor_id == "trip_speed":
|
||||
trip_speed = 0
|
||||
if util.keys_exists(data, "averageSpeedAutomatic"):
|
||||
trip_speed = float(data["averageSpeedAutomatic"]["value"])
|
||||
if trip_speed != 0:
|
||||
return util.convert_metric_values(trip_speed)
|
||||
return None
|
||||
|
||||
else:
|
||||
return None
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user