forked from peter/volvo2mqtt
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
81b2771c80 | ||
|
|
f1953e1baa | ||
|
|
459c2dc1af | ||
|
|
be09c7953e | ||
|
|
fa68c4ba2c | ||
|
|
d7a79f84a4 | ||
|
|
2c8effd746 | ||
|
|
cef28f7d0b | ||
|
|
781e1da8e8 | ||
|
|
6b419cfe3a | ||
|
|
e5ece2116b | ||
|
|
23246a464f | ||
|
|
1a5cc3db7a | ||
|
|
974ad0e454 | ||
|
|
7db02b5dc6 | ||
|
|
7c87c71ac8 | ||
|
|
3482a5f893 | ||
|
|
3f1f793c1b | ||
|
|
1039eab649 |
@@ -32,6 +32,7 @@ If you like my work:<br>
|
|||||||
- XC60 PHEV (2022)
|
- XC60 PHEV (2022)
|
||||||
- XC90 PHEV T8 (2023)
|
- XC90 PHEV T8 (2023)
|
||||||
- XC90 PHEV T8 (2024)
|
- XC90 PHEV T8 (2024)
|
||||||
|
- XC90 B5 Mildhybrid (2024)
|
||||||
- V90 PHEV T8 (2019)*
|
- V90 PHEV T8 (2019)*
|
||||||
|
|
||||||
*only partly working
|
*only partly working
|
||||||
@@ -72,7 +73,7 @@ NOTE: Energy status currently available only for cars in the Europe / Middle Eas
|
|||||||
Just install this addon with the following command.
|
Just install this addon with the following command.
|
||||||
Please note to fill in your settings inside the environment variables.
|
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": "", "backupvccapikey": "", "odometerMultiplier": 1, "averageSpeedDivider": 1, "averageFuelConsumptionMultiplier": 1}' -e TZ='Europe/Berlin' --name volvo2mqtt ghcr.io/dielee/volvo2mqtt:latest`
|
`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"], "odometerMultiplier": 1, "averageSpeedDivider": 1, "averageFuelConsumptionMultiplier": 1}' -e TZ='Europe/Berlin' --name volvo2mqtt ghcr.io/dielee/volvo2mqtt:latest`
|
||||||
|
|
||||||
<b>HA Add-On:</b><br>
|
<b>HA Add-On:</b><br>
|
||||||
|
|
||||||
|
|||||||
+64
-1
@@ -1,3 +1,66 @@
|
|||||||
|
## v1.8.12
|
||||||
|
### 🐛 Bug Fixes:
|
||||||
|
|
||||||
|
- Fix `AJAR` state for windows
|
||||||
|
- Fix deprecation from `currentThread` to `current_thread`
|
||||||
|
|
||||||
|
## 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:
|
||||||
|
|
||||||
|
- Add option to disable log completely #96
|
||||||
|
|
||||||
|
### 🐛 Bug Fixes:
|
||||||
|
|
||||||
|
- Fix regex error for some mailaddresses #97
|
||||||
|
|
||||||
|
## v1.8.6
|
||||||
|
### 🚀 Features:
|
||||||
|
|
||||||
|
- Add option to use multiple docker containers (with different logins) #93
|
||||||
|
|
||||||
|
## v1.8.5
|
||||||
|
### 🚀 Features:
|
||||||
|
|
||||||
|
- Allow phone number as username #91
|
||||||
|
|
||||||
|
## v1.8.4
|
||||||
|
### 🚀 Features:
|
||||||
|
|
||||||
|
- Optimize Addon configuration #90
|
||||||
|
|
||||||
|
## v1.8.3
|
||||||
|
### 🚀 Features:
|
||||||
|
|
||||||
|
- Add `device_class: battery` for battery state sensors from BEV and PHEV
|
||||||
|
|
||||||
|
## v1.8.2
|
||||||
|
### 🚀 Features:
|
||||||
|
|
||||||
|
- Add `updateInterval` limit to prevent abuse (60 Seconds)
|
||||||
|
- Add `vccapikey` limit to prevent abuse (3 Keys)
|
||||||
|
- Optimize vcc api key change behaviour
|
||||||
|
|
||||||
## v1.8.1
|
## v1.8.1
|
||||||
### 🐛 Bug Fixes:
|
### 🐛 Bug Fixes:
|
||||||
|
|
||||||
@@ -7,7 +70,7 @@
|
|||||||
### 🚀 Features:
|
### 🚀 Features:
|
||||||
|
|
||||||
- <b>Breaking change: The `vccapikey` setting is now a list. It is possible to
|
- <b>Breaking change: The `vccapikey` setting is now a list. It is possible to
|
||||||
add multiple VCCAPIKEYs. This will be helpful if you are extending your 10.000 call limit some times.
|
add multiple VCCAPIKEYs. This will be helpful if you are extending your 10.000 call limit sometimes.
|
||||||
Change your vccapikey config like this:
|
Change your vccapikey config like this:
|
||||||
|
|
||||||
Old:
|
Old:
|
||||||
|
|||||||
+9
-6
@@ -1,6 +1,6 @@
|
|||||||
name: "Volvo2Mqtt"
|
name: "Volvo2Mqtt"
|
||||||
description: "Volvo AAOS MQTT bridge"
|
description: "Volvo AAOS MQTT bridge"
|
||||||
version: "1.8.1"
|
version: "1.8.12"
|
||||||
slug: "volvo2mqtt"
|
slug: "volvo2mqtt"
|
||||||
init: false
|
init: false
|
||||||
url: "https://github.com/Dielee/volvo2mqtt"
|
url: "https://github.com/Dielee/volvo2mqtt"
|
||||||
@@ -15,6 +15,7 @@ options:
|
|||||||
babelLocale: null
|
babelLocale: null
|
||||||
TZ: null
|
TZ: null
|
||||||
debug: false
|
debug: false
|
||||||
|
disable_logging: false
|
||||||
mqtt:
|
mqtt:
|
||||||
broker: "auto_broker"
|
broker: "auto_broker"
|
||||||
port: "auto_port"
|
port: "auto_port"
|
||||||
@@ -24,26 +25,28 @@ options:
|
|||||||
username: null
|
username: null
|
||||||
password: null
|
password: null
|
||||||
vin: ""
|
vin: ""
|
||||||
vccapikey: null
|
vccapikey:
|
||||||
|
- null
|
||||||
odometerMultiplier: 1
|
odometerMultiplier: 1
|
||||||
averageSpeedDivider: 1
|
averageSpeedDivider: 1
|
||||||
averageFuelConsumptionMultiplier: 1
|
averageFuelConsumptionMultiplier: 1
|
||||||
schema:
|
schema:
|
||||||
updateInterval: int(10,)
|
updateInterval: int(60,)
|
||||||
babelLocale: str
|
babelLocale: str
|
||||||
TZ: str
|
TZ: match(^.+/.+$)
|
||||||
debug: bool
|
debug: bool
|
||||||
|
disable_logging: bool
|
||||||
mqtt:
|
mqtt:
|
||||||
broker: str
|
broker: str
|
||||||
port: str
|
port: str
|
||||||
username: str?
|
username: str?
|
||||||
password: str?
|
password: str?
|
||||||
volvoData:
|
volvoData:
|
||||||
username: str
|
username: match(^([a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+)|(\+\d{5,20})$)
|
||||||
password: str
|
password: str
|
||||||
vin: str?
|
vin: str?
|
||||||
vccapikey:
|
vccapikey:
|
||||||
- str
|
- match(^\b\w{32}\b$)
|
||||||
odometerMultiplier: int(1,)
|
odometerMultiplier: int(1,)
|
||||||
averageSpeedDivider: int(1,)
|
averageSpeedDivider: int(1,)
|
||||||
averageFuelConsumptionMultiplier: int(1,)
|
averageFuelConsumptionMultiplier: int(1,)
|
||||||
|
|||||||
+6
-7
@@ -1,6 +1,6 @@
|
|||||||
from config import settings
|
from config import settings
|
||||||
|
|
||||||
VERSION = "v1.8.1"
|
VERSION = "v1.8.12"
|
||||||
|
|
||||||
OAUTH_URL = "https://volvoid.eu.volvocars.com/as/token.oauth2"
|
OAUTH_URL = "https://volvoid.eu.volvocars.com/as/token.oauth2"
|
||||||
VEHICLES_URL = "https://api.volvocars.com/connected-vehicle/v1/vehicles"
|
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"
|
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"
|
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"
|
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"
|
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"
|
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"
|
ENGINE_DIAGNOSTICS_URL = "https://api.volvocars.com/connected-vehicle/v2/vehicles/{0}/engine"
|
||||||
@@ -50,8 +50,8 @@ charging_connection_states = {"CONNECTION_STATUS_DISCONNECTED": "Disconnected",
|
|||||||
"CONNECTION_STATUS_CONNECTED_DC": "Connected DC", "CONNECTION_STATUS_CONNECTED_AC": "Connected AC",
|
"CONNECTION_STATUS_CONNECTED_DC": "Connected DC", "CONNECTION_STATUS_CONNECTED_AC": "Connected AC",
|
||||||
"CONNECTION_STATUS_FAULT": "Fault"}
|
"CONNECTION_STATUS_FAULT": "Fault"}
|
||||||
|
|
||||||
window_states = {"CLOSED": "OFF", "OPEN": "ON"}
|
window_states = {"CLOSED": "OFF", "OPEN": "ON", "AJAR": "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"}
|
engine_states = {"RUNNING": "ON", "STOPPED": "OFF", "true": "ON", "false": "OFF"}
|
||||||
|
|
||||||
icon_states = {
|
icon_states = {
|
||||||
@@ -77,8 +77,8 @@ icon_states = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
supported_entities = [
|
supported_entities = [
|
||||||
{"name": "Battery Charge Level", "domain": "sensor", "id": "battery_charge_level", "unit": "%", "icon": "car-battery", "url": RECHARGE_STATE_URL, "state_class": "measurement"},
|
{"name": "Battery Charge Level", "domain": "sensor", "device_class": "battery", "id": "battery_charge_level", "unit": "%", "icon": "car-battery", "url": RECHARGE_STATE_URL, "state_class": "measurement"},
|
||||||
{"name": "Battery Charge Level", "domain": "sensor", "id": "battery_charge_level", "unit": "%", "icon": "car-battery", "url": BATTERY_CHARGE_STATE_URL, "state_class": "measurement"},
|
{"name": "Battery Charge Level", "domain": "sensor", "device_class": "battery", "id": "battery_charge_level", "unit": "%", "icon": "car-battery", "url": BATTERY_CHARGE_STATE_URL, "state_class": "measurement"},
|
||||||
{"name": "Electric Range", "domain": "sensor", "id": "electric_range", "unit": "km" if not units.get(settings["babelLocale"]) else units[settings["babelLocale"]]["electric_range"]["unit"], "icon": "map-marker-distance", "url": RECHARGE_STATE_URL, "state_class": "measurement"},
|
{"name": "Electric Range", "domain": "sensor", "id": "electric_range", "unit": "km" if not units.get(settings["babelLocale"]) else units[settings["babelLocale"]]["electric_range"]["unit"], "icon": "map-marker-distance", "url": RECHARGE_STATE_URL, "state_class": "measurement"},
|
||||||
{"name": "Estimated Charging Time", "domain": "sensor", "id": "estimated_charging_time", "unit": "minutes", "icon": "timer-sync-outline", "url": RECHARGE_STATE_URL, "state_class": "measurement"},
|
{"name": "Estimated Charging Time", "domain": "sensor", "id": "estimated_charging_time", "unit": "minutes", "icon": "timer-sync-outline", "url": RECHARGE_STATE_URL, "state_class": "measurement"},
|
||||||
{"name": "Charging System Status", "domain": "sensor", "id": "charging_system_status", "icon": "ev-station", "url": RECHARGE_STATE_URL},
|
{"name": "Charging System Status", "domain": "sensor", "id": "charging_system_status", "icon": "ev-station", "url": RECHARGE_STATE_URL},
|
||||||
@@ -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 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": "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_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": "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": "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"},
|
{"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"},
|
||||||
|
|||||||
+4
-3
@@ -2,14 +2,15 @@ import logging
|
|||||||
from volvo import authorize
|
from volvo import authorize
|
||||||
from mqtt import update_loop, connect
|
from mqtt import update_loop, connect
|
||||||
from const import VERSION
|
from const import VERSION
|
||||||
from util import set_tz, setup_logging, set_mqtt_settings
|
from util import set_tz, setup_logging, set_mqtt_settings, validate_settings
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
set_tz()
|
|
||||||
set_mqtt_settings()
|
|
||||||
setup_logging()
|
setup_logging()
|
||||||
logging.info("Starting volvo2mqtt version " + VERSION)
|
logging.info("Starting volvo2mqtt version " + VERSION)
|
||||||
|
validate_settings()
|
||||||
|
set_tz()
|
||||||
|
set_mqtt_settings()
|
||||||
connect()
|
connect()
|
||||||
authorize()
|
authorize()
|
||||||
update_loop()
|
update_loop()
|
||||||
|
|||||||
+10
-6
@@ -4,6 +4,7 @@ import paho.mqtt.client as mqtt
|
|||||||
import json
|
import json
|
||||||
import volvo
|
import volvo
|
||||||
import util
|
import util
|
||||||
|
import os
|
||||||
from threading import Thread, Timer
|
from threading import Thread, Timer
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from babel.dates import format_datetime
|
from babel.dates import format_datetime
|
||||||
@@ -23,7 +24,9 @@ active_schedules = {}
|
|||||||
|
|
||||||
|
|
||||||
def connect():
|
def connect():
|
||||||
client = mqtt.Client("volvoAAOS2mqtt")
|
client = mqtt.Client("volvoAAOS2mqtt") if os.environ.get("IS_HA_ADDON") \
|
||||||
|
else mqtt.Client("volvoAAOS2mqtt_" + settings.volvoData["username"].replace("+", ""))
|
||||||
|
|
||||||
client.will_set(availability_topic, "offline", 0, False)
|
client.will_set(availability_topic, "offline", 0, False)
|
||||||
if settings["mqtt"]["username"] and settings["mqtt"]["password"]:
|
if settings["mqtt"]["username"] and settings["mqtt"]["password"]:
|
||||||
client.username_pw_set(settings["mqtt"]["username"], settings["mqtt"]["password"])
|
client.username_pw_set(settings["mqtt"]["username"], settings["mqtt"]["password"])
|
||||||
@@ -252,11 +255,12 @@ def update_car_data(force_update=False, overwrite={}):
|
|||||||
else:
|
else:
|
||||||
topic = f"homeassistant/{entity['domain']}/{vin}_{entity['id']}/state"
|
topic = f"homeassistant/{entity['domain']}/{vin}_{entity['id']}/state"
|
||||||
|
|
||||||
mqtt_client.publish(
|
if state:
|
||||||
topic,
|
mqtt_client.publish(
|
||||||
json.dumps(state) if isinstance(state, dict) or isinstance(state, list) else state
|
topic,
|
||||||
)
|
json.dumps(state) if isinstance(state, dict) or isinstance(state, list) else state
|
||||||
update_ha_device(entity, vin, state)
|
)
|
||||||
|
update_ha_device(entity, vin, state)
|
||||||
|
|
||||||
|
|
||||||
def update_ha_device(entity, vin, state):
|
def update_ha_device(entity, vin, state):
|
||||||
|
|||||||
@@ -2,6 +2,8 @@
|
|||||||
"updateInterval": 300,
|
"updateInterval": 300,
|
||||||
"babelLocale": "de",
|
"babelLocale": "de",
|
||||||
"TZ": "Europe/Berlin",
|
"TZ": "Europe/Berlin",
|
||||||
|
"debug": false,
|
||||||
|
"disable_logging": false,
|
||||||
"mqtt": {
|
"mqtt": {
|
||||||
"broker": "",
|
"broker": "",
|
||||||
"port": 1883,
|
"port": 1883,
|
||||||
|
|||||||
@@ -11,6 +11,9 @@ configuration:
|
|||||||
debug:
|
debug:
|
||||||
name: API debug mode
|
name: API debug mode
|
||||||
description: Enable Volvo API debug, normaly this can stay off. If enabled, the complete log file can be found under \\<Your HA Host IP>\addons\volvo2mqtt\log\volvo2mqtt.log.
|
description: Enable Volvo API debug, normaly this can stay off. If enabled, the complete log file can be found under \\<Your HA Host IP>\addons\volvo2mqtt\log\volvo2mqtt.log.
|
||||||
|
disable_logging:
|
||||||
|
name: Disable logging
|
||||||
|
description: Disable logging completely to reduce IO and SD card access.
|
||||||
mqtt:
|
mqtt:
|
||||||
name: MQTT Broker settings
|
name: MQTT Broker settings
|
||||||
description: Leave the settings as they are if you are using the MQTT Mosquitto Addon. If not, take a look at the readme from volvo2mqtt.
|
description: Leave the settings as they are if you are using the MQTT Mosquitto Addon. If not, take a look at the readme from volvo2mqtt.
|
||||||
|
|||||||
+16
@@ -68,6 +68,10 @@ def setup_logging():
|
|||||||
if settings["debug"]:
|
if settings["debug"]:
|
||||||
logger.setLevel(logging.DEBUG)
|
logger.setLevel(logging.DEBUG)
|
||||||
|
|
||||||
|
if "disable_logging" in settings:
|
||||||
|
if settings["disable_logging"]:
|
||||||
|
logger.setLevel(logging.ERROR)
|
||||||
|
|
||||||
|
|
||||||
def check_existing_folder():
|
def check_existing_folder():
|
||||||
Path("/addons/volvo2mqtt/log/").mkdir(parents=True, exist_ok=True)
|
Path("/addons/volvo2mqtt/log/").mkdir(parents=True, exist_ok=True)
|
||||||
@@ -136,3 +140,15 @@ def set_mqtt_settings():
|
|||||||
config.settings["mqtt"]["port"] = broker_port
|
config.settings["mqtt"]["port"] = broker_port
|
||||||
config.settings["mqtt"]["username"] = broker_user
|
config.settings["mqtt"]["username"] = broker_user
|
||||||
config.settings["mqtt"]["password"] = broker_pass
|
config.settings["mqtt"]["password"] = broker_pass
|
||||||
|
|
||||||
|
|
||||||
|
def validate_settings():
|
||||||
|
if not os.environ.get("DEV_MODE"):
|
||||||
|
setting_keys = settings.volvoData["vccapikey"]
|
||||||
|
if isinstance(setting_keys, list):
|
||||||
|
if len(setting_keys) > 3:
|
||||||
|
raise Exception("Settings invalid! Maximum allowed vccapikeys are three!")
|
||||||
|
|
||||||
|
update_interval = settings["updateInterval"]
|
||||||
|
if update_interval < 60:
|
||||||
|
raise Exception("Settings invalid! Minimum allowed update interval is 60 seconds!")
|
||||||
|
|||||||
+59
-53
@@ -4,10 +4,11 @@ import mqtt
|
|||||||
import util
|
import util
|
||||||
import time
|
import time
|
||||||
import re
|
import re
|
||||||
from threading import currentThread
|
from threading import current_thread
|
||||||
from datetime import datetime, timedelta
|
from datetime import datetime, timedelta
|
||||||
from config import settings
|
from config import settings
|
||||||
from babel.dates import format_datetime
|
from babel.dates import format_datetime
|
||||||
|
from json import JSONDecodeError
|
||||||
from const import charging_system_states, charging_connection_states, door_states, window_states, \
|
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, \
|
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, \
|
WINDOWS_STATE_URL, LOCK_STATE_URL, TYRE_STATE_URL, supported_entities, BATTERY_CHARGE_STATE_URL, \
|
||||||
@@ -119,28 +120,32 @@ def get_vehicles():
|
|||||||
|
|
||||||
|
|
||||||
def get_vcc_api_keys(used_key=None):
|
def get_vcc_api_keys(used_key=None):
|
||||||
setting_keys = settings.volvoData["vccapikey"]
|
working_keys = None
|
||||||
if isinstance(setting_keys, str):
|
|
||||||
set_key_state(setting_keys)
|
|
||||||
elif isinstance(setting_keys, list):
|
|
||||||
for key in setting_keys:
|
|
||||||
set_key_state(key)
|
|
||||||
|
|
||||||
logging.debug(str(vcc_api_keys))
|
while not working_keys:
|
||||||
working_keys = [key["key"] for key in vcc_api_keys if not key.get("extended") and key.get('key') != used_key]
|
setting_keys = settings.volvoData["vccapikey"]
|
||||||
if len(working_keys) < 1:
|
if isinstance(setting_keys, str):
|
||||||
logging.warning("No working VCCAPIKEY found, waiting 10 minutes. Then trying again!")
|
set_key_state(setting_keys)
|
||||||
mqtt.send_offline()
|
elif isinstance(setting_keys, list):
|
||||||
time.sleep(600)
|
for key in setting_keys:
|
||||||
get_vcc_api_keys(used_key=None)
|
set_key_state(key)
|
||||||
return None
|
|
||||||
|
|
||||||
mqtt.send_heartbeat()
|
logging.debug(str(vcc_api_keys))
|
||||||
session.headers.update({"vcc-api-key": working_keys[0]})
|
working_keys = [key["key"] for key in vcc_api_keys if not key.get("extended") and key.get('key') != used_key]
|
||||||
logging.info("Using VCCAPIKEY: " + working_keys[0])
|
if len(working_keys) < 1:
|
||||||
for key_dict in vcc_api_keys:
|
used_key = None
|
||||||
if key_dict["key"] == working_keys[0]:
|
logging.warning("No working VCCAPIKEY found, waiting 10 minutes. Then trying again!")
|
||||||
key_dict["in_use"] = True
|
mqtt.send_offline()
|
||||||
|
time.sleep(600)
|
||||||
|
else:
|
||||||
|
mqtt.send_heartbeat()
|
||||||
|
session.headers.update({"vcc-api-key": working_keys[0]})
|
||||||
|
logging.info("Using VCCAPIKEY: " + working_keys[0])
|
||||||
|
for key_dict in vcc_api_keys:
|
||||||
|
if key_dict["key"] == working_keys[0]:
|
||||||
|
key_dict["in_use"] = True
|
||||||
|
|
||||||
|
logging.debug(str(vcc_api_keys))
|
||||||
|
|
||||||
|
|
||||||
def set_key_state(key):
|
def set_key_state(key):
|
||||||
@@ -160,6 +165,8 @@ def set_key_state(key):
|
|||||||
def check_vcc_api_key(test_key, extended_until=None):
|
def check_vcc_api_key(test_key, extended_until=None):
|
||||||
if extended_until:
|
if extended_until:
|
||||||
if extended_until >= datetime.now():
|
if extended_until >= datetime.now():
|
||||||
|
logging.warning("VCCAPIKEY " + test_key + " is extended and will be reusable at: "
|
||||||
|
+ format_datetime(extended_until, format="medium", locale=settings["babelLocale"]))
|
||||||
return True, extended_until
|
return True, extended_until
|
||||||
|
|
||||||
if datetime.now(util.TZ) >= token_expires_at:
|
if datetime.now(util.TZ) >= token_expires_at:
|
||||||
@@ -238,11 +245,6 @@ def check_supported_endpoints():
|
|||||||
# If battery charge level could be found in recharge-api, skip the second battery charge sensor
|
# If battery charge level could be found in recharge-api, skip the second battery charge sensor
|
||||||
continue
|
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'):
|
if entity.get('url'):
|
||||||
state = api_call(entity["url"], "GET", vin, entity["id"])
|
state = api_call(entity["url"], "GET", vin, entity["id"])
|
||||||
else:
|
else:
|
||||||
@@ -301,7 +303,7 @@ def check_engine_status(vin):
|
|||||||
# Exit thread as engine state is unsupported by car
|
# Exit thread as engine state is unsupported by car
|
||||||
return None
|
return None
|
||||||
|
|
||||||
t = currentThread()
|
t = current_thread()
|
||||||
while getattr(t, "do_run", True):
|
while getattr(t, "do_run", True):
|
||||||
engine_state = api_call(endpoint_url, "GET", vin, "engine_state", True)
|
engine_state = api_call(endpoint_url, "GET", vin, "engine_state", True)
|
||||||
if engine_state == "ON":
|
if engine_state == "ON":
|
||||||
@@ -341,7 +343,11 @@ def api_call(url, method, vin, sensor_id=None, force_update=False, key_change=Fa
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
logging.debug("Response status code: " + str(response.status_code))
|
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:
|
if response.status_code == 200:
|
||||||
logging.debug(response.text)
|
logging.debug(response.text)
|
||||||
@@ -437,7 +443,7 @@ def parse_api_data(data, sensor_id=None):
|
|||||||
return ""
|
return ""
|
||||||
return None
|
return None
|
||||||
elif sensor_id == "lock_status":
|
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":
|
elif sensor_id == "odometer":
|
||||||
multiplier = 1
|
multiplier = 1
|
||||||
if util.keys_exists(settings["volvoData"], "odometerMultiplier"):
|
if util.keys_exists(settings["volvoData"], "odometerMultiplier"):
|
||||||
@@ -449,44 +455,44 @@ def parse_api_data(data, sensor_id=None):
|
|||||||
return util.convert_metric_values(int(data["odometer"]["value"]) * multiplier) \
|
return util.convert_metric_values(int(data["odometer"]["value"]) * multiplier) \
|
||||||
if util.keys_exists(data, "odometer") else None
|
if util.keys_exists(data, "odometer") else None
|
||||||
elif sensor_id == "window_front_left":
|
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
|
else None
|
||||||
elif sensor_id == "window_front_right":
|
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
|
else None
|
||||||
elif sensor_id == "window_rear_left":
|
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
|
else None
|
||||||
elif sensor_id == "window_rear_right":
|
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
|
else None
|
||||||
elif sensor_id == "door_front_left":
|
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":
|
elif sensor_id == "door_front_right":
|
||||||
return door_states[data["frontRightDoorOpen"]["value"]] \
|
return door_states[data["frontRightDoor"]["value"]] \
|
||||||
if util.keys_exists(data, "frontRightDoorOpen") else None
|
if util.keys_exists(data, "frontRightDoor") else None
|
||||||
elif sensor_id == "door_rear_left":
|
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":
|
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":
|
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":
|
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":
|
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":
|
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":
|
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":
|
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":
|
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":
|
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":
|
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":
|
elif sensor_id == "fuel_level":
|
||||||
if util.keys_exists(data, "fuelAmount"):
|
if util.keys_exists(data, "fuelAmount"):
|
||||||
fuel_amount = float(data["fuelAmount"]["value"])
|
fuel_amount = float(data["fuelAmount"]["value"])
|
||||||
@@ -537,16 +543,16 @@ def parse_api_data(data, sensor_id=None):
|
|||||||
elif sensor_id == "hours_to_service":
|
elif sensor_id == "hours_to_service":
|
||||||
return data["engineHoursToService"]["value"] if util.keys_exists(data, "engineHoursToService") else None
|
return data["engineHoursToService"]["value"] if util.keys_exists(data, "engineHoursToService") else None
|
||||||
elif sensor_id == "km_to_service":
|
elif sensor_id == "km_to_service":
|
||||||
if util.keys_exists(data, "kmToService"):
|
if util.keys_exists(data, "distanceToService"):
|
||||||
km_to_service = int(data["kmToService"]["value"])
|
km_to_service = int(data["distanceToService"]["value"])
|
||||||
if km_to_service > 0:
|
if km_to_service > 0:
|
||||||
return util.convert_metric_values(data["kmToService"]["value"])
|
return util.convert_metric_values(data["distanceToService"]["value"])
|
||||||
return None
|
return None
|
||||||
elif sensor_id == "months_to_service":
|
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":
|
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":
|
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:
|
else:
|
||||||
return None
|
return None
|
||||||
|
|||||||
Reference in New Issue
Block a user