Compare commits

..
16 Commits
Author SHA1 Message Date
Linus Dietz 96805d439d Add interior and exterior image entities #62 2023-07-09 16:06:13 +02:00
Linus DietzandGitHub 10c3e7bd97 Update README.md 2023-07-06 22:05:46 +02:00
Dielee ad691b6153 Update CHANGELOG.md 2023-07-06 13:26:56 +02:00
Dielee b6e423839a Update CHANGELOG.md 2023-07-06 11:12:31 +02:00
Dielee 044e71d862 Fix undocumented charging connection state #57 2023-07-06 11:10:58 +02:00
Linus Dietz 444579316b Fix volvo API changes #53 2023-07-05 20:19:17 +02:00
Linus DietzandGitHub e9ae0037dc Update README.md 2023-07-04 08:31:01 +02:00
Linus DietzandGitHub 4ab08ab4df Fix C40 car types 2023-07-04 08:30:10 +02:00
Linus Dietz 48cae3eee1 Added addon CHANGELOG.md 2023-07-04 08:29:09 +02:00
Linus DietzandGitHub 57a0451fda Add Ko-Fi link 2023-07-03 20:55:58 +02:00
Linus DietzandGitHub 86fbb08dcb Update bug_report.md 2023-07-03 10:46:36 +02:00
Linus DietzandGitHub 5e59c49608 Update issue templates 2023-07-03 10:46:06 +02:00
Linus DietzandGitHub 5729bf06ba Update bug_report.md 2023-07-03 10:45:00 +02:00
Linus Dietz 8fdfa269c4 Fix engine state "true" or "false" 2023-07-03 10:18:12 +02:00
Linus Dietz 71b6cd269a Move engine state to binary_sensor domain 2023-07-03 09:36:44 +02:00
Linus Dietz 4cb729beb5 Add image signage for HA Addon 2023-07-03 09:28:56 +02:00
7 changed files with 184 additions and 31 deletions
+8 -7
View File
@@ -3,28 +3,29 @@ name: Bug report
about: Create a report to help us improve
title: ''
labels: ''
assignees: Dielee
assignees: ''
---
**Describe the bug**
A clear and concise description of what the bug is.
**To Reproduce**
**To Reproduce**:<br>
Steps to reproduce the behavior:
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error
**Expected behavior**
**Expected behavior**:<br>
A clear and concise description of what you expected to happen.
**Screenshots**
**Screenshots**:<br>
If applicable, add screenshots to help explain your problem.
**Version info**
**Version info**:<br>
The version you are using and the method you are using (Docker container/HA Addon/Cloned repo)
**Additional context**
Add any other context about the problem here.
**Logs**:<br>
Enable the debug option and upload your logfile here. <br>
<b>If you are using the HA Addon the log location is `\\<Your HA Host IP>\addons\volvo2mqtt\log\volvo2mqtt.log`
+6 -3
View File
@@ -15,13 +15,16 @@ Maybe this component works also with other Volvo cars. Please try out the native
Home Assistant thread can be found [here](https://community.home-assistant.io/t/volvo2mqtt-connect-your-aaos-volvo/585699).
If you like my work:<br>
[![ko-fi](https://ko-fi.com/img/githubbutton_sm.svg)](https://ko-fi.com/U7U8MFXCF)
## Confirmed working with
- XC40 BEV (2023)
- XC40 BEV (2022)
- XC40 PHEV (2021)
- V60 T8 PHEV (2023)
- C40 PHEV (2023)
- C40 PHEV (2022)
- C40 BEV (2023)
- C40 BEV (2022)
- XC90 T8 PHEV (2023)
- XC60 PHEV (2023)
- XC60 PHEV (2022)
@@ -39,7 +42,7 @@ Please let me know if your car works with this addon so I can expand the list!<b
- Sensor "Battery Charge Level"
- Sensor "Electric Range"
- Sensor "Charging System Status"
- Sensor "Charging Connection Status"
- Sensor "Charging Connection Status" (partly broken since 2.10 car update)
- Sensor "Estimated Charging Finish Time"
- Sensor "Door Lock Status (All doors, tank lid, and engine hood)"
- Sensor "Window Lock Status (All windows and sunroof - Thanks to @navet)"
+87
View File
@@ -0,0 +1,87 @@
## v1.7.6
### 🚀 Features:
- Added support for interior and exterior images via the new [MQTT Image](https://www.home-assistant.io/integrations/image.mqtt/) entity. #62
<b>NOTE: This feature is only available using homeassistant version 2023.7.1 or greater.</b>
## v1.7.5
### 🚀 Features:
- Added support for `LOCKING` and `UNLOCKING` state (Lock entity)
## v1.7.4
### 🐛 Bug Fixes:
- Fixed undocumented charging connection state `CONNECTION_STATUS_FAULT` '#57
## v1.7.3
### 🐛 Bug Fixes:
- Fixed API changes from Volvo #53
<b>ATTENTION: Volvo optimized their API. Maybe you have to set your odometer multiplier to 1.
Also check all the other multipliers!</b>
## v1.7.2
### 🐛 Bug Fixes:
- Fixed "Engine State" can be "true" or "false", not "RUNNING" or "STOPPED" #47
## v1.7.1
### 🚀 Features:
- Moved "Engine State" from "sensor" to the "binary_sensor" domain
- Improved HA Add-On security level to 7
<b>NOTE: The old "sensor" entity will be not deleted automatically. If you want to clean up your mqtt device, just delete it and restart the volvo2mqtt addon.
</b>
## v1.7.0
### 🚀 Features:
- Added MQTT autodiscovery for HA Add-On users
- Added "Engine State" fallback endpoint for some cars (BEVs)
- Added dynamic icons for "Engine State"
- Turn off the pre-climate switch if "Engine State" changes to "RUNNING"
<b>NOTE: The mqtt broker port is now a string. Just quote your port like this `port: "1883"`</b><br>
<b>NOTE: If you want to switch to MQTT autodiscovery for the HA Add-On change your settings to: <br>
![image](https://i.imgur.com/GBamhFn.png)</b>
## v1.6.5
### 🐛 Bug Fixes:
- Fixed missing key in unit mappings for `en_US` and `en_GB` mapping
## v1.6.4
### 🚀 Features:
- Added dynamic icons for lock, battery and window state #43
## v1.6.3
### 🚀 Features:
- Moved data source for "Distance to empty" from extended api to connected api
## v1.6.2
### 🚀 Features:
- Moved print statements to logger
- Added more debug logging
- The full HA Addon log can now be found at your addon smb share `\\<Your HA IP>\addons\volvo2mqtt\log\volvo2mqtt.log`
## v1.6.1
### 🚀 Features:
- Added "Washer Fluid Level" and "Distance to Empty" sensors #37
- Expanded climate control for better state sync with HA
### 🐛 Bug Fixes:
- Fixed wrong return values for not working endpoints
- Fixed app crash on credential renewal timeout #39
## v1.6.0
### 🚀 Features:
- Added conversion from metric to imperial system for `en_US` and `en_GB` locale
+2 -1
View File
@@ -1,10 +1,11 @@
name: "Volvo2Mqtt"
description: "Volvo AAOS MQTT bridge"
version: "1.7.0"
version: "1.7.6"
slug: "volvo2mqtt"
init: false
url: "https://github.com/Dielee/volvo2mqtt"
apparmor: true
codenotary: notary@home-assistant.io
services:
- 'mqtt:need'
map:
+8 -6
View File
@@ -1,6 +1,6 @@
from config import settings
VERSION = "v1.7.0"
VERSION = "v1.7.6"
OAUTH_URL = "https://volvoid.eu.volvocars.com/as/token.oauth2"
VEHICLES_URL = "https://api.volvocars.com/connected-vehicle/v1/vehicles"
@@ -44,18 +44,20 @@ charging_system_states = {"CHARGING_SYSTEM_CHARGING": "Charging", "CHARGING_SYST
"CHARGING_SYSTEM_FAULT": "Fault", "CHARGING_SYSTEM_UNSPECIFIED": "Unspecified"}
charging_connection_states = {"CONNECTION_STATUS_DISCONNECTED": "Disconnected", "CONNECTION_STATUS_UNSPECIFIED": "Unspecified",
"CONNECTION_STATUS_CONNECTED_DC": "Connected DC", "CONNECTION_STATUS_CONNECTED_AC": "Connected AC"}
"CONNECTION_STATUS_CONNECTED_DC": "Connected DC", "CONNECTION_STATUS_CONNECTED_AC": "Connected AC",
"CONNECTION_STATUS_FAULT": "Fault"}
window_states = {"CLOSED": "OFF", "OPEN": "ON"}
door_states = {"CLOSED": "OFF", "OPEN": "ON"}
engine_states = {"RUNNING": "ON", "STOPPED": "OFF", "true": "ON", "false": "OFF"}
icon_states = {
"lock_status": {"UNLOCKED": "lock-open-alert", "LOCKED": "lock"},
"lock_status": {"UNLOCKED": "lock-open-alert", "LOCKED": "lock", "UNLOCKING": "lock-reset", "LOCKING": "lock-reset"},
"door_front_left": {"ON": "car-door", "OFF": "car-door-lock"},
"door_front_right": {"ON": "car-door", "OFF": "car-door-lock"},
"door_rear_left": {"ON": "car-door", "OFF": "car-door-lock"},
"door_rear_right": {"ON": "car-door", "OFF": "car-door-lock"},
"engine_state": {"RUNNING": "engine-outline", "STOPPED": "engine-off-outline"},
"engine_state": {"ON": "engine-outline", "OFF": "engine-off-outline"},
"battery_charge_level": [
{"from": 100, "to": 100, "icon": "battery"},
{"from": 99, "to": 90, "icon": "battery-90"},
@@ -101,8 +103,8 @@ supported_entities = [
{"name": "Tire Front Right", "domain": "sensor", "id": "tyre_front_right", "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": "Engine State", "domain": "sensor", "id": "engine_state", "icon": "engine", "url": ENGINE_STATE_URL},
{"name": "Engine State", "domain": "sensor", "id": "engine_state", "icon": "engine", "url": ENGINE_DIAGNOSTICS_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},
{"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},
+52 -8
View File
@@ -42,6 +42,43 @@ def connect():
mqtt_client = client
def send_car_images(vin, data, device):
if util.keys_exists(data, "images"):
for entity in [{"name": "Exterior Image", "id": "exterior_image"},
{"name": "Interior Image", "id": "interior_image"}]:
url_topic = f"homeassistant/image/{vin}_{entity['id']}/image_url"
config = {
"name": entity["name"],
"object_id": f"volvo_{vin}_{entity['id']}",
"schema": "state",
"icon": "mdi:image-area",
"url_topic": url_topic,
"device": device,
"unique_id": f"volvoAAOS2mqtt_{vin}_{entity['id']}",
"availability_topic": availability_topic
}
mqtt_client.publish(
f"homeassistant/image/volvoAAOS2mqtt/{vin}_{entity['id']}/config",
json.dumps(config),
retain=True
)
if entity["id"] == "exterior_image":
mqtt_client.publish(
url_topic,
data["images"]["exteriorDefaultUrl"],
retain=True
)
if entity["id"] == "interior_image":
mqtt_client.publish(
url_topic,
data["images"]["interiorDefaultUrl"],
retain=True
)
def on_connect(client, userdata, flags, rc):
send_heartbeat()
if len(subscribed_topics) > 0:
@@ -96,15 +133,21 @@ def on_message(client, userdata, msg):
update_car_data()
elif "lock_status" in msg.topic:
if payload == "LOCK":
volvo.api_call(CAR_LOCK_URL, "POST", vin)
# Start the api call in another thread for HA performance
Thread(target=volvo.api_call, args=(CAR_LOCK_URL, "POST", vin)).start()
# Force set unlocked state in HA because of slow api response
update_car_data(True, {"entity_id": "lock_status", "vin": vin, "state": "LOCKED"})
# Force set locking state
update_car_data(False, {"entity_id": "lock_status", "vin": vin, "state": "LOCKING"})
# Fetch API lock state until locking finished
Thread(target=volvo.check_lock_status, args=(vin, "UNLOCKED")).start()
elif payload == "UNLOCK":
volvo.api_call(CAR_UNLOCK_URL, "POST", vin)
# Start the api call in another thread for HA performance
Thread(target=volvo.api_call, args=(CAR_UNLOCK_URL, "POST", vin)).start()
# Force set unlocked state in HA because of slow api response
update_car_data(True, {"entity_id": "lock_status", "vin": vin, "state": "UNLOCKED"})
# Force set unlocking state
update_car_data(False, {"entity_id": "lock_status", "vin": vin, "state": "UNLOCKING"})
# Fetch API lock state until unlocking finished
Thread(target=volvo.check_lock_status, args=(vin, "LOCKED")).start()
elif "update_data" in msg.topic:
if payload == "PRESS":
update_car_data(True)
@@ -226,12 +269,13 @@ def create_ha_devices():
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"]:
elif 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)
mqtt_client.subscribe(command_topic)
elif entity.get("domain") == "image":
config["url_topic"] = f"homeassistant/{entity['domain']}/{vin}_{entity['id']}/image_url"
mqtt_client.publish(
f"homeassistant/{entity['domain']}/volvoAAOS2mqtt/{vin}_{entity['id']}/config",
+21 -6
View File
@@ -3,14 +3,14 @@ import requests
import mqtt
import util
import time
from threading import Thread, currentThread
from threading import currentThread
from datetime import datetime, timedelta
from config import settings
from babel.dates import format_datetime
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, CLIMATE_STOP_URL, ENGINE_DIAGNOSTICS_URL
STATISTICS_URL, ENGINE_DIAGNOSTICS_URL, engine_states
session = requests.Session()
session.headers = {
@@ -125,6 +125,7 @@ def get_vehicle_details(vin):
"model": data['descriptions']['model'],
"name": f"{data['descriptions']['model']} ({data['modelYear']}) - {vin}",
}
mqtt.send_car_images(vin, data, device)
elif response.status_code == 500 and not settings.volvoData["vin"]:
# Workaround for some cars that are not returning vehicle details
device = {
@@ -179,6 +180,20 @@ def disable_climate(vin):
mqtt.update_car_data()
def check_lock_status(vin, old_state):
max_runs = 10
done_runs = 0
lock_state = api_call(LOCK_STATE_URL, "GET", vin, "lock_status", True)
while lock_state == old_state:
done_runs = done_runs + 1
if done_runs >= max_runs:
break
lock_state = api_call(LOCK_STATE_URL, "GET", vin, "lock_status", True)
time.sleep(2)
mqtt.update_car_data()
def check_engine_status(vin):
endpoint_url = ""
engine_state_supported = False
@@ -194,7 +209,7 @@ def check_engine_status(vin):
t = currentThread()
while getattr(t, "do_run", True):
engine_state = api_call(endpoint_url, "GET", vin, "engine_state", True)
if engine_state == "RUNNING":
if engine_state == "ON":
mqtt.assumed_climate_state[vin] = "OFF"
mqtt.update_car_data()
break
@@ -366,10 +381,10 @@ def parse_api_data(data, sensor_id=None):
elif sensor_id == "tyre_rear_right":
return data["rearRightTyrePressure"]["value"] if util.keys_exists(data, "rearRightTyrePressure") else None
elif sensor_id == "engine_state":
return data["engineRunning"]["value"] if util.keys_exists(data, "engineRunning") else None
return engine_states[data["engineRunning"]["value"]] if util.keys_exists(data, "engineRunning") else None
elif sensor_id == "fuel_level":
if util.keys_exists(data, "fuelAmount"):
fuel_amount = int(data["fuelAmount"]["value"])
fuel_amount = float(data["fuelAmount"]["value"])
if fuel_amount > 0:
return fuel_amount
return None
@@ -388,7 +403,7 @@ def parse_api_data(data, sensor_id=None):
return None
elif sensor_id == "average_speed":
if util.keys_exists(data, "averageSpeed"):
average_speed = int(data["averageSpeed"]["value"])
average_speed = float(data["averageSpeed"]["value"])
if average_speed > 1:
divider = 1
if util.keys_exists(settings["volvoData"], "averageSpeedDivider"):