forked from peter/volvo2mqtt
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
710ca2fcbc | ||
|
|
c1998f8938 | ||
|
|
693e0b0048 | ||
|
|
f23f56e45e | ||
|
|
17eaa8dde2 | ||
|
|
8f3bf5f3b1 | ||
|
|
9d4f8fab0c | ||
|
|
414ac503b1 | ||
|
|
18926b67e4 | ||
|
|
f650ea9247 | ||
|
|
a7a2e1182c | ||
|
|
3d3227de0d | ||
|
|
44509529ff | ||
|
|
0dd5852d81 |
@@ -87,8 +87,36 @@ The following steps are required for authentication in exactly this order:
|
|||||||
```etc ...```
|
```etc ...```
|
||||||
4. Now, open your mailbox and copy your OTP Code
|
4. Now, open your mailbox and copy your OTP Code
|
||||||
5. Open HomeAssistant and search for the entity ID ```text.volvo_otp```
|
5. Open HomeAssistant and search for the entity ID ```text.volvo_otp```
|
||||||
6. Paste your OTP into the text entity and **<ins>press Enter</ins>**
|
6. Paste your OTP into the text entity and **<ins>press Enter</ins>**<br>
|
||||||
7. If everything has worked, your addon is now authenticated. In the future, OTP authentication only needs to be done when updating, not when restarting the container.
|
6a. (optional) If your OTP cannot be safed, try to paste your OTP code via MQTT to the following topic: `volvoAAOS2mqtt/otp_code` <p>
|
||||||
|
<details>
|
||||||
|
<summary>OTP Post Script</summary>
|
||||||
|
<div>
|
||||||
|
<p>
|
||||||
|
1. Safe the following script in your script section <br>
|
||||||
|
2. Go to https://yourHAUrl/developer-tools/service<br>
|
||||||
|
3. Search for "Post Volvo OTP"<br>
|
||||||
|
4. Tick "OTP" and fill in your OTP<br>
|
||||||
|
5. Run this service<br>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
alias: Post Volvo OTP
|
||||||
|
sequence:
|
||||||
|
- service: mqtt.publish
|
||||||
|
metadata: {}
|
||||||
|
data:
|
||||||
|
topic: volvoAAOS2mqtt/otp_code
|
||||||
|
payload: "{{ otp }}"
|
||||||
|
mode: single
|
||||||
|
fields:
|
||||||
|
otp:
|
||||||
|
selector:
|
||||||
|
text: null
|
||||||
|
name: OTP
|
||||||
|
description: Volvo OTP
|
||||||
|
|
||||||
|
</details>
|
||||||
|
8. If everything has worked, your addon is now authenticated. In the future, OTP authentication only needs to be done when updating, not when restarting the container.
|
||||||
|
|
||||||
## Setup
|
## Setup
|
||||||
<b>Docker:</b>
|
<b>Docker:</b>
|
||||||
|
|||||||
@@ -1,3 +1,60 @@
|
|||||||
|
## v1.9.6
|
||||||
|
### 🐛 Bug Fixes:
|
||||||
|
|
||||||
|
- Fixed interior and exterior Images #219
|
||||||
|
- Take care of broken token files #220
|
||||||
|
|
||||||
|
## v1.9.5
|
||||||
|
### 🚀 Features:
|
||||||
|
|
||||||
|
- Added "retain" states to survive HA reboot
|
||||||
|
|
||||||
|
## v1.9.4
|
||||||
|
### 🐛 Bug Fixes:
|
||||||
|
|
||||||
|
- Fix backend state API url
|
||||||
|
|
||||||
|
## v1.9.3
|
||||||
|
### 🐛 Bug Fixes:
|
||||||
|
|
||||||
|
- Ignore retained OTP messages #193
|
||||||
|
|
||||||
|
## v1.9.2
|
||||||
|
### 🐛 Bug Fixes:
|
||||||
|
|
||||||
|
- Try to fix OTP Input
|
||||||
|
- Fix `SyntaxWarning: invalid escape sequence '\d'` #196
|
||||||
|
|
||||||
|
## v1.9.1
|
||||||
|
### 🚀 Features:
|
||||||
|
|
||||||
|
- The token file now survives an HA addon update/restart
|
||||||
|
|
||||||
|
### 🐛 Bug Fixes:
|
||||||
|
|
||||||
|
- Extend OTP retry time to 120 seconds
|
||||||
|
|
||||||
|
## v1.9.0
|
||||||
|
### ⚠️ Breaking change
|
||||||
|
### 🚀 Features:
|
||||||
|
|
||||||
|
- Add "Battery Capacity" sensor. Thanks to @gurtjun!
|
||||||
|
- OTP Auth
|
||||||
|
This addon uses the same OTP authentication as the Volvo app, now.
|
||||||
|
The following steps are required for authentication in exactly this order:
|
||||||
|
|
||||||
|
1. Setup volvo2Mqtt, either via Docker, or via HA addon (take a look at the "Setup" section)
|
||||||
|
2. Fill in your settings and start volvo2Mqtt
|
||||||
|
3. Your log will show the following lines
|
||||||
|
Waiting for otp code... Please check your mailbox and post your otp code to the following mqtt topic "volvoAAOS2mqtt/otp_code". Retry 0/15
|
||||||
|
Waiting for otp code... Please check your mailbox and post your otp code to the following mqtt topic "volvoAAOS2mqtt/otp_code". Retry 1/15
|
||||||
|
etc ...
|
||||||
|
4. Now, open your mailbox and copy your OTP Code
|
||||||
|
5. Open HomeAssistant and search for the entity ID text.volvo_otp
|
||||||
|
6. Paste your OTP into the text entity and press Enter
|
||||||
|
7. If everything has worked, your addon is now authenticated. In the future, OTP authentication only needs to be done when updating, not when restarting the container.
|
||||||
|
|
||||||
|
|
||||||
## v1.8.27
|
## v1.8.27
|
||||||
### 🚀 Features:
|
### 🚀 Features:
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
name: "Volvo2Mqtt"
|
name: "Volvo2Mqtt"
|
||||||
description: "Volvo AAOS MQTT bridge"
|
description: "Volvo AAOS MQTT bridge"
|
||||||
version: "1.9.2"
|
version: "1.9.6"
|
||||||
slug: "volvo2mqtt"
|
slug: "volvo2mqtt"
|
||||||
init: false
|
init: false
|
||||||
url: "https://github.com/Dielee/volvo2mqtt"
|
url: "https://github.com/Dielee/volvo2mqtt"
|
||||||
|
|||||||
+2
-2
@@ -1,6 +1,6 @@
|
|||||||
from config import settings
|
from config import settings
|
||||||
|
|
||||||
VERSION = "v1.9.2"
|
VERSION = "v1.9.6"
|
||||||
|
|
||||||
OAUTH_TOKEN_URL = "https://volvoid.eu.volvocars.com/as/token.oauth2"
|
OAUTH_TOKEN_URL = "https://volvoid.eu.volvocars.com/as/token.oauth2"
|
||||||
OAUTH_AUTH_URL = "https://volvoid.eu.volvocars.com/as/authorization.oauth2"
|
OAUTH_AUTH_URL = "https://volvoid.eu.volvocars.com/as/authorization.oauth2"
|
||||||
@@ -22,7 +22,7 @@ STATISTICS_URL = "https://api.volvocars.com/connected-vehicle/v2/vehicles/{0}/st
|
|||||||
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"
|
||||||
VEHICLE_DIAGNOSTICS_URL = "https://api.volvocars.com/connected-vehicle/v2/vehicles/{0}/diagnostics"
|
VEHICLE_DIAGNOSTICS_URL = "https://api.volvocars.com/connected-vehicle/v2/vehicles/{0}/diagnostics"
|
||||||
WARNINGS_URL = "https://api.volvocars.com/connected-vehicle/v2/vehicles/{0}/warnings"
|
WARNINGS_URL = "https://api.volvocars.com/connected-vehicle/v2/vehicles/{0}/warnings"
|
||||||
API_BACKEND_STATUS = "https://oip-dev-bff.euwest1.production.volvo.care/api/v1/backend-status"
|
API_BACKEND_STATUS = "https://public-developer-portal-bff.weu-prod.ecpaz.volvocars.biz/api/v1/backend-status"
|
||||||
|
|
||||||
LENGTH_KILOMETERS = "km"
|
LENGTH_KILOMETERS = "km"
|
||||||
SPEED_KILOMETERS_PER_HOUR = "km/h"
|
SPEED_KILOMETERS_PER_HOUR = "km/h"
|
||||||
|
|||||||
+48
-18
@@ -5,6 +5,7 @@ import json
|
|||||||
import volvo
|
import volvo
|
||||||
import util
|
import util
|
||||||
import os
|
import os
|
||||||
|
import requests
|
||||||
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
|
||||||
@@ -84,13 +85,14 @@ def send_car_images(vin, data, device):
|
|||||||
if util.keys_exists(data, "images"):
|
if util.keys_exists(data, "images"):
|
||||||
for entity in [{"name": "Exterior Image", "id": "exterior_image"},
|
for entity in [{"name": "Exterior Image", "id": "exterior_image"},
|
||||||
{"name": "Interior Image", "id": "interior_image"}]:
|
{"name": "Interior Image", "id": "interior_image"}]:
|
||||||
url_topic = f"homeassistant/image/{vin}_{entity['id']}/image_url"
|
image_topic = f"homeassistant/image/{vin}_{entity['id']}/image_topic"
|
||||||
config = {
|
config = {
|
||||||
"name": entity["name"],
|
"name": entity["name"],
|
||||||
"object_id": f"volvo_{vin}_{entity['id']}",
|
"object_id": f"volvo_{vin}_{entity['id']}",
|
||||||
"schema": "state",
|
"schema": "state",
|
||||||
"icon": "mdi:image-area",
|
"icon": "mdi:image-area",
|
||||||
"url_topic": url_topic,
|
"content_type": "image/png",
|
||||||
|
"image_topic": image_topic,
|
||||||
"device": device,
|
"device": device,
|
||||||
"unique_id": f"volvoAAOS2mqtt_{vin}_{entity['id']}",
|
"unique_id": f"volvoAAOS2mqtt_{vin}_{entity['id']}",
|
||||||
"availability_topic": availability_topic
|
"availability_topic": availability_topic
|
||||||
@@ -102,19 +104,41 @@ def send_car_images(vin, data, device):
|
|||||||
retain=True
|
retain=True
|
||||||
)
|
)
|
||||||
|
|
||||||
|
headers = {"User-Agent": "(Windows NT 10.0; Win64; x64)",
|
||||||
|
"sec-ch-ua-platform": "\"Windows\"", "sec-fetch-dest": "document", "Accept-Encoding": "gzip, deflate, br, zstd",
|
||||||
|
"sec-fetch-mode": "navigate", "sec-fetch-site": "none", "sec-fetch-user": "?1", "upgrade-insecure-requests": "1"}
|
||||||
|
|
||||||
|
# Post exterior image
|
||||||
if entity["id"] == "exterior_image":
|
if entity["id"] == "exterior_image":
|
||||||
mqtt_client.publish(
|
if not os.path.exists("exterior_image.png"):
|
||||||
url_topic,
|
response = requests.get(data["images"]["exteriorImageUrl"], headers=headers)
|
||||||
data["images"]["exteriorImageUrl"],
|
if response.status_code == 200:
|
||||||
retain=True
|
with open("exterior_image.png", 'wb') as image:
|
||||||
)
|
image.write(response.content)
|
||||||
|
|
||||||
|
if os.path.exists("exterior_image.png"):
|
||||||
|
ext_image = open("exterior_image.png", mode="rb").read()
|
||||||
|
mqtt_client.publish(
|
||||||
|
image_topic,
|
||||||
|
ext_image,
|
||||||
|
retain=True
|
||||||
|
)
|
||||||
|
|
||||||
if entity["id"] == "interior_image":
|
if entity["id"] == "interior_image":
|
||||||
mqtt_client.publish(
|
# Post interior Image
|
||||||
url_topic,
|
if not os.path.exists("interior_image.png"):
|
||||||
data["images"]["internalImageUrl"],
|
response = requests.get(data["images"]["internalImageUrl"], headers=headers)
|
||||||
retain=True
|
if response.status_code == 200:
|
||||||
)
|
with open("interior_image.png", 'wb') as image:
|
||||||
|
image.write(response.content)
|
||||||
|
|
||||||
|
if os.path.exists("interior_image.png"):
|
||||||
|
int_image = open("interior_image.png", mode="rb").read()
|
||||||
|
mqtt_client.publish(
|
||||||
|
image_topic,
|
||||||
|
int_image,
|
||||||
|
retain=True
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def on_connect(client, userdata, flags, rc):
|
def on_connect(client, userdata, flags, rc):
|
||||||
@@ -131,8 +155,12 @@ def on_disconnect(client, userdata, rc):
|
|||||||
def on_message(client, userdata, msg):
|
def on_message(client, userdata, msg):
|
||||||
payload = msg.payload.decode("UTF-8")
|
payload = msg.payload.decode("UTF-8")
|
||||||
if msg.topic == otp_mqtt_topic:
|
if msg.topic == otp_mqtt_topic:
|
||||||
global otp_code
|
if msg.retain == 0:
|
||||||
otp_code = payload
|
global otp_code
|
||||||
|
otp_code = payload
|
||||||
|
set_otp_state()
|
||||||
|
else:
|
||||||
|
logging.warning("Found retained OTP, this can't work! Please clean retained messages!")
|
||||||
return None
|
return None
|
||||||
else:
|
else:
|
||||||
try:
|
try:
|
||||||
@@ -312,7 +340,8 @@ def update_car_data(force_update=False, overwrite={}):
|
|||||||
elif entity["id"] == "warnings":
|
elif entity["id"] == "warnings":
|
||||||
mqtt_client.publish(
|
mqtt_client.publish(
|
||||||
f"homeassistant/{entity['domain']}/{vin}_{entity['id']}/attributes",
|
f"homeassistant/{entity['domain']}/{vin}_{entity['id']}/attributes",
|
||||||
json.dumps(state)
|
json.dumps(state),
|
||||||
|
retain=True
|
||||||
)
|
)
|
||||||
if state:
|
if state:
|
||||||
state = sum(value == "FAILURE" for value in state.values())
|
state = sum(value == "FAILURE" for value in state.values())
|
||||||
@@ -326,7 +355,8 @@ def update_car_data(force_update=False, overwrite={}):
|
|||||||
if state or state == 0:
|
if state or state == 0:
|
||||||
mqtt_client.publish(
|
mqtt_client.publish(
|
||||||
topic,
|
topic,
|
||||||
json.dumps(state) if isinstance(state, dict) or isinstance(state, list) else state
|
json.dumps(state) if isinstance(state, dict) or isinstance(state, list) else state,
|
||||||
|
retain=True
|
||||||
)
|
)
|
||||||
update_ha_device(entity, vin, state)
|
update_ha_device(entity, vin, state)
|
||||||
|
|
||||||
@@ -437,11 +467,11 @@ def create_ha_devices():
|
|||||||
|
|
||||||
|
|
||||||
def send_heartbeat():
|
def send_heartbeat():
|
||||||
mqtt_client.publish(availability_topic, "online")
|
mqtt_client.publish(availability_topic, "online", retain=True)
|
||||||
|
|
||||||
|
|
||||||
def send_offline():
|
def send_offline():
|
||||||
mqtt_client.publish(availability_topic, "offline")
|
mqtt_client.publish(availability_topic, "offline", retain=True)
|
||||||
|
|
||||||
|
|
||||||
def delete_old_entities():
|
def delete_old_entities():
|
||||||
|
|||||||
+7
-4
@@ -40,9 +40,13 @@ def authorize(renew_tokenfile=False):
|
|||||||
if os.path.isfile(token_path) and not renew_tokenfile:
|
if os.path.isfile(token_path) and not renew_tokenfile:
|
||||||
logging.info("Using login from token file")
|
logging.info("Using login from token file")
|
||||||
f = open(token_path)
|
f = open(token_path)
|
||||||
data = json.load(f)
|
try:
|
||||||
refresh_token = data["refresh_token"]
|
data = json.load(f)
|
||||||
refresh_auth()
|
refresh_token = data["refresh_token"]
|
||||||
|
refresh_auth()
|
||||||
|
except ValueError:
|
||||||
|
logging.warning("Detected corrupted token file, restarting auth process")
|
||||||
|
authorize(True)
|
||||||
else:
|
else:
|
||||||
logging.info("Starting login with OTP")
|
logging.info("Starting login with OTP")
|
||||||
auth_session = requests.session()
|
auth_session = requests.session()
|
||||||
@@ -156,7 +160,6 @@ def send_otp(auth_session, data):
|
|||||||
if not mqtt.otp_code:
|
if not mqtt.otp_code:
|
||||||
raise Exception ("No OTP found, exting...")
|
raise Exception ("No OTP found, exting...")
|
||||||
|
|
||||||
mqtt.set_otp_state()
|
|
||||||
auth = auth_session.post(next_url, data=json.dumps(body))
|
auth = auth_session.post(next_url, data=json.dumps(body))
|
||||||
if auth.status_code == 200:
|
if auth.status_code == 200:
|
||||||
return auth.json()
|
return auth.json()
|
||||||
|
|||||||
Reference in New Issue
Block a user