-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
C64Axel
committed
Jul 21, 2024
1 parent
4c320db
commit 7ec5e04
Showing
23 changed files
with
1,955 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,69 @@ | ||
name: Docker | ||
|
||
# This workflow uses actions that are not certified by GitHub. | ||
# They are provided by a third-party and are governed by | ||
# separate terms of service, privacy policy, and support | ||
# documentation. | ||
|
||
on: | ||
push: | ||
branches: [ "main" ] | ||
# Publish semver tags as releases. | ||
tags: [ 'v*.*.*' ] | ||
|
||
env: | ||
# Use docker.io for Docker Hub if empty | ||
REGISTRY: ghcr.io | ||
# github.repository as <account>/<repo> | ||
IMAGE_NAME: ${{ github.repository }} | ||
|
||
|
||
jobs: | ||
build: | ||
|
||
runs-on: ubuntu-latest | ||
permissions: | ||
contents: read | ||
packages: write | ||
# This is used to complete the identity challenge | ||
# with sigstore/fulcio when running outside of PRs. | ||
id-token: write | ||
|
||
steps: | ||
- name: Checkout repository | ||
uses: actions/checkout@v3 | ||
|
||
# Workaround: https://github.com/docker/build-push-action/issues/461 | ||
- name: Setup Docker buildx | ||
uses: docker/setup-buildx-action@79abd3f86f79a9d68a23c75a09a9a85889262adf | ||
|
||
# Login against a Docker registry except on PR | ||
# https://github.com/docker/login-action | ||
- name: Log into registry ${{ env.REGISTRY }} | ||
if: github.event_name != 'pull_request' | ||
uses: docker/login-action@28218f9b04b4f3f62068d7b6ce6ca5b26e35336c | ||
with: | ||
registry: ${{ env.REGISTRY }} | ||
username: ${{ github.actor }} | ||
password: ${{ secrets.GITHUB_TOKEN }} | ||
|
||
# Extract metadata (tags, labels) for Docker | ||
# https://github.com/docker/metadata-action | ||
- name: Extract Docker metadata | ||
id: meta | ||
uses: docker/metadata-action@98669ae865ea3cffbcbaa878cf57c20bbf1c6c38 | ||
with: | ||
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} | ||
|
||
# Build and push Docker image with Buildx (don't push on PR) | ||
# https://github.com/docker/build-push-action | ||
- name: Build and push Docker image | ||
id: build-and-push | ||
uses: docker/build-push-action@ac9327eae2b366085ac7f6a2d02df8aa8ead720a | ||
with: | ||
context: . | ||
push: ${{ github.event_name != 'pull_request' }} | ||
tags: ${{ steps.meta.outputs.tags }} | ||
labels: ${{ steps.meta.outputs.labels }} | ||
cache-from: type=gha | ||
cache-to: type=gha,mode=max |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
FROM python:3.11-slim | ||
|
||
# Working directory for the application | ||
WORKDIR /usr/src/app | ||
|
||
# Set Entrypoint with hard-coded options | ||
ENTRYPOINT ["python3", "./mz2mqtt.py"] | ||
|
||
COPY requirements.txt /usr/src/app/ | ||
|
||
RUN apt update && apt install -y build-essential \ | ||
&& pip3 install --no-cache-dir -r requirements.txt \ | ||
&& apt purge -y --auto-remove build-essential && apt clean | ||
|
||
# Copy everything to the working directory (Python files, templates, config) in one go. | ||
COPY . /usr/src/app/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,65 @@ | ||
# mz2mqtt | ||
Send Car Data to mqtt | ||
**Publish all Car Data to MQTT** | ||
|
||
![Python 3.10](https://img.shields.io/badge/python-3.10-blue.svg) | ||
![Python 3.11](https://img.shields.io/badge/python-3.11-blue.svg) | ||
--- | ||
># !!! WARNING !!! !!! WARNING !!! | ||
>***A too frequent refresh of the data can discharge your 12V starter battery of the car. | ||
So use this Program at your own risk*** | ||
--- | ||
|
||
This Program based on code from bdr99, and it may stop working at any time without warning. | ||
|
||
--- | ||
|
||
Prerequisites: | ||
1. Set up your Car in the app. | ||
2. Create a second Driver for mazda2mqtt. | ||
|
||
## Installation Guide: | ||
Clone the git repository. | ||
Create a virtual environment and install the requirements: | ||
``` | ||
apt install python-virtualenv | ||
cd mz2mqtt | ||
virtualenv -p python3 ../mz2mqtt.env | ||
source ../mz2mqtt.env/bin/activate | ||
pip3 install -r requirements.txt | ||
``` | ||
Then copy config_example.yaml to config.yaml an insert your data. | ||
Start mz2mqtt: | ||
``` | ||
cd mz2mqtt | ||
source ../mz2mqtt.env/bin/activate | ||
python mz2mqtt.py | ||
``` | ||
|
||
Or download the Docker Image | ||
``` | ||
docker pull ghcr.io/c64axel/mz2mqtt:main | ||
``` | ||
Start the container with /usr/src/app/config.yaml mapped to the config file | ||
``` | ||
docker run -d --name mz2mqtt --restart unless-stopped -v <YOUR_DIR/config.yaml>:/usr/src/app/config.yaml mz2mqtt:main | ||
``` | ||
--- | ||
**MQTT-API** | ||
|
||
To trigger a manual refresh for one car, publish the following via MQTT: | ||
(replace < VIN > with the VIN of the Car) | ||
``` | ||
mz2mqtt/SET/<VIN>/refresh | ||
``` | ||
|
||
--- | ||
### History: | ||
|
||
| Date | Change | | ||
|------------|---------------------------------------------------------------------| | ||
| 26.04.2023 | Initial Version | | ||
| 03.06.2023 | only one refresh at the beginning because risk of battery discharge | | ||
| 08.06.2023 | refresh Data via MQTT | | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
mqtt: | ||
host: # your mqtthost | ||
port: 1883 # your mqtt port - default 1883 | ||
user: # your mqtt user or empty when no authentication | ||
password: # your mqtt password | ||
topic: mz2mqtt # your mqtt topic - default mz2mqtt. Leave it default for evcc | ||
clientname: mz2mqtt # your mqtt clientname - default mz2mqtt | ||
mazda: | ||
user: # email address from your Mazda account | ||
password: # password from your Mazda account | ||
region: MME # your Region (MNAO:North America, MME:Europe, MJO:Japan) - default MME | ||
status: | ||
wait: 30 # wait time in minutes getting status - default 30 | ||
refreshwait: 2 # wait time in minutes after refresh data - default 2 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,140 @@ | ||
import asyncio | ||
import logging | ||
from queue import SimpleQueue | ||
|
||
import paho.mqtt.client as mqtt_client | ||
import mzlib | ||
import yaml | ||
|
||
async def main() -> None: | ||
|
||
cmd_queue = SimpleQueue() | ||
|
||
def create_msg(object, vehicleid, mqtt_topic, indent='/'): | ||
for key in object: | ||
if type(object[key]) == dict: | ||
create_msg(object[key], vehicleid, mqtt_topic, indent + key + '/') | ||
else: | ||
mqttc.publish(mqtt_topic + '/' + str(vehicleid) + indent + key, object[key], 0, True) | ||
return | ||
|
||
async def get_and_publish(vehicle): | ||
logging.info('get and publish data for ' + vehicle['vin']) | ||
vehicle_status = await mazda_client.get_vehicle_status(vehicle['id']) | ||
create_msg(vehicle_status, vehicle['vin'], mqtt_topic) | ||
if vehicle['isElectric']: | ||
vehicle_ev_status = await mazda_client.get_ev_vehicle_status(vehicle['id']) | ||
create_msg(vehicle_ev_status, vehicle['vin'], mqtt_topic) | ||
|
||
def on_connect(client, userdata, flags, rc): | ||
if rc == 0: | ||
logging.info("MQTT connected OK") | ||
else: | ||
logging.error("Bad connection Returned code=", rc) | ||
|
||
def on_message(mosq, obj, msg): | ||
msg.payload = str(msg.payload) | ||
logging.info("received " + msg.topic + " " + msg.payload) | ||
mqtt_cmd = msg.topic.split("/") | ||
if mqtt_cmd[1].upper() == "SET": | ||
cmd_queue.put(mqtt_cmd[2] + ':' + mqtt_cmd[3] + ':' + msg.payload) | ||
|
||
logger = logging.basicConfig(format='%(asctime)s %(levelname)-8s %(message)s', | ||
datefmt='%Y-%m-%d %H:%M:%S', | ||
level=logging.INFO) | ||
|
||
# Read Config | ||
with open('config.yaml', 'r') as configfile: | ||
config = yaml.safe_load(configfile) | ||
|
||
# Connect to myMazda | ||
logging.info('Initialize myMazda') | ||
mazda_user = config['mazda']['user'] | ||
mazda_password = config['mazda']['password'] | ||
mazda_region = config['mazda']['region'] or 'MME' | ||
mazda_client = mzlib.Client(mazda_user, mazda_password, mazda_region, use_cached_vehicle_list=True) | ||
|
||
status_wait = (config['status']['wait'] or 30) * 12 | ||
status_refreshwait = (config['status']['refreshwait'] or 2) * 60 | ||
|
||
# Connect to MQTT-Broker | ||
logging.info('Initalize MQTT') | ||
mqtt_broker_address = config['mqtt']['host'] | ||
mqtt_broker_port = config['mqtt']['port'] or 1883 | ||
mqtt_broker_user = config['mqtt']['user'] or None | ||
mqtt_broker_password = config['mqtt']['password'] or None | ||
mqtt_topic = config['mqtt']['topic'] or 'mz2mqtt' | ||
mqtt_clientname = config['mqtt']['clientname'] or 'mz2mqtt' | ||
|
||
mqttc = mqtt_client.Client(mqtt_clientname) | ||
mqttc.enable_logger(logger) | ||
mqttc.on_connect = on_connect | ||
mqttc.on_message = on_message | ||
mqttc.username_pw_set(username=mqtt_broker_user, password=mqtt_broker_password) | ||
mqttc.connect(mqtt_broker_address, mqtt_broker_port, 60) | ||
mqttc.subscribe(mqtt_topic + '/' + 'SET/#', 0) | ||
mqttc.loop_start() | ||
|
||
# Get all Vehicles and publish base | ||
logging.info('Get all vehicles') | ||
try: | ||
vehicles = await mazda_client.get_vehicles() | ||
except Exception: | ||
raise Exception("Failed to get list of vehicles") | ||
|
||
# Publish vehicle data | ||
logging.info('publish all vehicles base data') | ||
for vehicle in vehicles: | ||
create_msg(vehicle,vehicle['vin'], mqtt_topic) | ||
|
||
# refresh all vehicle data at startup | ||
try: | ||
for vehicle in vehicles: | ||
logging.info('refresh data for ' + vehicle['vin']) | ||
await mazda_client.refresh_vehicle_status(vehicle['id']) | ||
except: | ||
logging.error('can not refresh all vehicles data') | ||
await mazda_client.close() | ||
|
||
logging.info('wait ' + str(status_refreshwait) + 's for data after refresh') | ||
await asyncio.sleep(status_refreshwait) | ||
|
||
# Main loop | ||
try: | ||
count = 0 | ||
while True: | ||
# look for new API input | ||
while not cmd_queue.empty(): | ||
r = cmd_queue.get_nowait() | ||
mqtt_cmd = r.split(':') | ||
match mqtt_cmd[1]: | ||
case 'refresh': | ||
found = False | ||
for vehicle in vehicles: | ||
if vehicle['vin'] == mqtt_cmd[0]: | ||
found = True | ||
logging.info('send refresh for ' + vehicle['vin'] + ' and wait' + str(status_refreshwait) + 's') | ||
await mazda_client.refresh_vehicle_status(vehicle['id']) | ||
await asyncio.sleep(status_refreshwait) | ||
await get_and_publish(vehicle) | ||
if not found: | ||
logging.error('VIN ' + mqtt_cmd[0] + ' not found') | ||
case _: | ||
logging.error("invalid command: " + mqtt_cmd[1]) | ||
|
||
# wait time reached and get data | ||
if count == 0: | ||
for vehicle in vehicles: | ||
await get_and_publish(vehicle) | ||
count = status_wait | ||
|
||
count -= 1 | ||
await asyncio.sleep(5) | ||
except: | ||
# Close the session | ||
mqttc.loop_stop() | ||
await mazda_client.close() | ||
|
||
|
||
if __name__ == "__main__": | ||
asyncio.run(main()) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
template: mz2mqtt | ||
products: | ||
- description: | ||
generic: mz2mqtt | ||
group: generic | ||
requirements: | ||
description: | ||
en: myMazda to MQTT. Required MQTT broker configuration and a mz2mqtt installation https://github.com/C64Axel/mz2mqtt. | ||
de: myMazda zu MQTT. Voraussetzung ist ein konfigurierter MQTT Broker und eine mz2mqtt Installation https://github.com/C64Axel/mz2mqtt. | ||
params: | ||
- name: title | ||
- name: vin | ||
required: true | ||
help: | ||
de: Erforderlich | ||
en: Required | ||
- name: capacity | ||
- name: phases | ||
advanced: true | ||
- name: icon | ||
default: car | ||
advanced: true | ||
- name: timeout | ||
default: 720h | ||
advanced: true | ||
- preset: vehicle-identify | ||
render: | | ||
type: custom | ||
{{- if .title }} | ||
title: {{ .title }} | ||
{{- end }} | ||
{{- if .icon }} | ||
icon: {{ .icon }} | ||
{{- end }} | ||
{{- if .capacity }} | ||
capacity: {{ .capacity }} | ||
{{- end }} | ||
{{- if .phases }} | ||
phases: {{ .phases }} | ||
{{- end }} | ||
{{- include "vehicle-identify" . }} | ||
soc: | ||
source: mqtt | ||
topic: mz2mqtt/{{ .vin }}/chargeInfo/batteryLevelPercentage | ||
timeout: {{ .timeout }} | ||
status: | ||
source: combined | ||
plugged: | ||
source: mqtt | ||
topic: mz2mqtt/{{ .vin }}/chargeInfo/pluggedIn | ||
timeout: {{ .timeout }} | ||
charging: | ||
source: mqtt | ||
topic: mz2mqtt/{{ .vin }}/chargeInfo/charging | ||
timeout: {{ .timeout }} | ||
range: | ||
source: mqtt | ||
topic: mz2mqtt/{{ .vin }}/chargeInfo/drivingRangeKm | ||
timeout: {{ .timeout }} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
from mzlib.client import Client | ||
from mzlib.exceptions import ( | ||
MazdaException, | ||
MazdaAPIEncryptionException, | ||
MazdaAuthenticationException, | ||
MazdaAccountLockedException, | ||
MazdaTokenExpiredException, | ||
MazdaLoginFailedException | ||
) |
Oops, something went wrong.