Skip to content

Commit

Permalink
added support for Domoticz and MQTT (e.g. HomeAssistant, ioBroker)
Browse files Browse the repository at this point in the history
  • Loading branch information
ZuinigeRijder committed Nov 7, 2024
1 parent 146bf48 commit a54e9d9
Show file tree
Hide file tree
Showing 8 changed files with 552 additions and 32 deletions.
128 changes: 125 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
- [How to install python, packages and hyundai\_connect\_monitor](#how-to-install-python-packages-and-hyundai_connect_monitor)
- [configuration of gspread for "python summary.py sheetupdate" and "python dailystats.py sheetupdate"](#configuration-of-gspread-for-python-summarypy-sheetupdate-and-python-dailystatspy-sheetupdate)
- [Translations](#translations)
- [Domoticz](#domoticz)
- [MQTT Broker (e.g. HomeAssistant, ioBroker)](#mqtt-broker-eg-homeassistant-iobroker)
- [monitor.py](#monitorpy)
- [summary.py](#summarypy)
- [summary.py sheetupdate](#summarypy-sheetupdate)
Expand All @@ -28,7 +30,7 @@
---
# Introduction hyundai_kia_connect_monitor
Automatic trip administration tools for Hyundai Bluelink or Kia UVO Connect users.
Determining afterwards your private and/or business trips and information about those trips and usage of the car.
Determining afterwards your private and/or business trips and information about those trips and usage of the car. Support for Domoticz and/or MQTT Broker (e.g. HomeAssistant, ioBroker).
Best of all is the fact that it does NOT drain your 12 volt battery of the car, because it only uses the cached server information!

Examples of supported cars (including cars with newer [ccNC Infotainment](https://ifdesign.com/en/winner-ranking/project/hyundai-ccnc-infotainment-system-seon/568244)):
Expand Down Expand Up @@ -187,9 +189,83 @@ Remarks:
- Polish, Czech, Slovak, Hungarian are not translated, feel free to provide translations for those languages
- If (some) translations are not correct, please submit an issue with the proposed corrections, but be careful to provide them as unicode text, preferably using monitor.translations.xlsx

---
# Domoticz
[Domoticz](https://www.domoticz.com/) is a very light weight home automation system that lets you monitor and configure miscellaneous devices, including lights, switches, various sensors/meters like temperature, rainfall, wind, ultraviolet (UV) radiation, electricity usage/production, gas consumption, water consumption and many more. Notifications/alerts can be sent to any mobile device.

In the file "monitor.cfg" there is a configuration section for domoticz:
```
[Domoticz]
send_to_domoticz = False
domot_url = http://192.168.0.222:8081
monitor_datetime = 0
monitor_longitude = 0
monitor_latitude = 0
monitor_engineon = 0
monitor_battery12v = 0
monitor_odometer = 0
monitor_soc = 0
monitor_charging = 0
monitor_plugged = 0
monitor_address = 0
monitor_evrange = 0
trip_date = 0
trip_starttime = 0
trip_drivetime = 0
trip_idletime = 0
trip_distance = 0
trip_avgspeed = 0
trip_maxspeed = 0
dailystats_date = 0
dailystats_distance = 0
dailystats_distance_unit = 0
dailystats_total_consumed = 0
dailystats_regenerated_energy = 0
dailystats_engine_consumption = 0
dailystats_climate_consumption = 0
dailystats_onboard_electronics_consumption = 0
dailystats_battery_care_consumption = 0
```

- set send_to_domoticz to True if you want to send updates to *.csv also to Domoticz
- domot_url is the URL where to send the updates to
- the next items (e.g. monitor_odometer) you can configure the ID/IDX of each item, If the ID/IDX is 0, that item will NOT be send to Domoticz.


---
# MQTT Broker (e.g. HomeAssistant, ioBroker)
An MQTT broker is a server that receives all messages from the clients and then routes the messages to the appropriate destination clients. Information is organized in a hierarchy of topics. When hyundai_kia_connect_monitor has a new item of data to distribute, it sends a control message with the data to the connected broker. The broker then distributes the information to any clients that have subscribed to that topic. The hyundai_kia_connect_monitor does not need to have any data on the number or locations of subscribers, and subscribers, in turn, do not have to be configured with any data about the publishers.

In the file "monitor.cfg" there is a configuration section for MQTT:

```
[MQTT]
send_to_mqtt = False
mqtt_broker_hostname = localhost
mqtt_broker_port = 1883
mqtt_broker_username =
mqtt_broker_password =
mqtt_main_topic = hyundai_kia_connect_monitor
```

- set send_to_mqtt to True if you want to send updates to *.csv also to Domoticz
- mqtt_broker_hostname is the URL where to send the updates to
- mqtt_broker_port is the port where to send the updates to
- mqtt_broker_username is an optional username
- mqtt_broker_password is an optional password
- mqtt_main_topic is the main topic

When configured, the data is send to mqtt_main_topic/VIN/subtopic. Example screenshot using MQTT Explorer:

![alt text](https://raw.githubusercontent.com/ZuinigeRijder/hyundai_kia_connect_monitor/main/examples/MQTTExplorer.png)


---
# monitor.py
Simple Python3 script to monitor values using [hyundai_kia_connect_api](https://github.com/Hyundai-Kia-Connect/hyundai_kia_connect_api)
Simple Python3 script to monitor values using [hyundai_kia_connect_api](https://github.com/Hyundai-Kia-Connect/hyundai_kia_connect_api). Support for Domoticz and/or MQTT Broker (e.g. HomeAssistant, ioBroker).

Usage:
```
Expand All @@ -204,7 +280,9 @@ OUTPUTFILES:
- monitor.tripinfo.csv (appended with trip info) or monitor.tripinfo.VIN.csv
- monitor.lastrun or monitor.VIN.lastrun (rewritten with last run date/time of monitor.py and other vehicle data)

*Note: dailystats and tripinfo are only available in Europe.*
*Note 1: dailystats and tripinfo are only available in Europe.*

*Note 2: Changes to the output csv files are also send to [Domoticz](#domoticz) and/or [MQTT Broker](#mqtt-broker-eg-homeassistant-iobroker) (e.g. HomeAssistant, ioBroker) when configured.*

Make sure to configure monitor.cfg once:
```
Expand All @@ -224,6 +302,48 @@ consumption_efficiency_factor_summary = 1.0
monitor_infinite = False
monitor_infinite_interval_minutes = 60
monitor_execute_commands_when_something_written_or_error =
[Domoticz]
send_to_domoticz = False
domot_url = http://192.168.0.222:8081
monitor_datetime = 0
monitor_longitude = 0
monitor_latitude = 0
monitor_engineon = 0
monitor_battery12v = 0
monitor_odometer = 0
monitor_soc = 0
monitor_charging = 0
monitor_plugged = 0
monitor_address = 0
monitor_evrange = 0
trip_date = 0
trip_starttime = 0
trip_drivetime = 0
trip_idletime = 0
trip_distance = 0
trip_avgspeed = 0
trip_maxspeed = 0
dailystats_date = 0
dailystats_distance = 0
dailystats_distance_unit = 0
dailystats_total_consumed = 0
dailystats_regenerated_energy = 0
dailystats_engine_consumption = 0
dailystats_climate_consumption = 0
dailystats_onboard_electronics_consumption = 0
dailystats_battery_care_consumption = 0
[MQTT]
send_to_mqtt = False
mqtt_broker_hostname = localhost
mqtt_broker_port = 1883
mqtt_broker_username =
mqtt_broker_password =
mqtt_main_topic = hyundai_kia_connect_monitor
```

Explanation of the configuration items:
Expand All @@ -243,6 +363,8 @@ Explanation of the configuration items:
- monitor_infinite_interval_minutes, interval in minutes between getting cached server values
- monitor_execute_commands_when_something_written_or_error, when new cached server values are retrieved, the specified commands (separated by semicolon ;) are executed. See Note 1.
* example: monitor_execute_commands_when_something_written_or_error = python -u summary.py sheetupdate > summary.log;python -u dailystats.py sheetupdate > dailystats.log
- For configuration of Domoticz, [see here](#domoticz)
- For configuration of MQTT Broker, [see here](#mqtt-broker-eg-homeassistant-iobroker)

*Note 1: in combination with infinite (monitor_infinite = True) summary.py and dailystats.py are only run when something is changed or error occurred (or once a day). You do not need to run summary.py and dailystats.py separately and it is only run when it is needed.*

Expand Down
83 changes: 83 additions & 0 deletions domoticz_utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
# == domoticz_utils.py Author: Zuinige Rijder =========
""" domoticz utils """
# pylint:disable=logging-fstring-interpolation

import configparser
import logging
import logging.config

from monitor_utils import (
execute_request,
get,
get_bool,
get_filepath,
get_items_monitor_csv,
get_items_monitor_dailystats_csv,
get_items_monitor_tripinfo_csv,
)

PARSER = configparser.ConfigParser()
PARSER.read(get_filepath("monitor.cfg"))

# == read [Domoticz] settings in monitor.cfg ===========================
domoticz_settings = dict(PARSER.items("Domoticz"))
SEND_TO_DOMOTICZ = get_bool(domoticz_settings, "send_to_domoticz", False)
DOMOTICZ_URL = get(domoticz_settings, "domot_url")

ITEMS_MONITOR_CSV = get_items_monitor_csv()
ITEMS_MONITOR_TRIPINFO_CSV = get_items_monitor_tripinfo_csv()
ITEMS_MONITOR_DAILYSTATS_CSV = get_items_monitor_dailystats_csv()


# == send to Domoticz ========================================================
def send_to_domoticz(header: str, value: str) -> None:
"""send_to_Domoticz"""
idx = get(domoticz_settings, header, "0")
if idx == "0":
return # nothing to do

url = (
DOMOTICZ_URL
+ "/json.htm?type=command&param=udevice&idx="
+ idx
+ "&svalue="
+ value
)
logging.debug(url)
retry = 0
while True:
retry += 1
content = execute_request(url, "", {})
if content != "ERROR" or retry > 30:
if content == "ERROR":
logging.error(f"number of retries exceeded: {url}")
return


def send_line(headers: list, line: str) -> None:
"""send_line"""
splitted = line.split(",")
if len(splitted) < len(headers):
logging.info(f"line does not have all elements: {line}\n{headers}")
return

for i in range(len(splitted)): # pylint:disable=consider-using-enumerate
send_to_domoticz(headers[i], splitted[i].strip())


def send_monitor_csv_line_to_domoticz(line: str) -> None:
"""send_monitor_csv_line_to_domoticz"""
if SEND_TO_DOMOTICZ:
send_line(ITEMS_MONITOR_CSV, line)


def send_tripinfo_line_to_domoticz(line: str) -> None:
"""send_tripinfo_line_to_domoticz"""
if SEND_TO_DOMOTICZ:
send_line(ITEMS_MONITOR_TRIPINFO_CSV, line)


def send_dailystats_line_to_domoticz(line: str) -> None:
"""send_dailystats_line_to_domoticz"""
if SEND_TO_DOMOTICZ:
send_line(ITEMS_MONITOR_DAILYSTATS_CSV, line)
Binary file added examples/MQTTExplorer.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
44 changes: 43 additions & 1 deletion monitor.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,46 @@ consumption_efficiency_factor_dailystats = 1.0
consumption_efficiency_factor_summary = 1.0
monitor_infinite = False
monitor_infinite_interval_minutes = 60
monitor_execute_commands_when_something_written_or_error =
monitor_execute_commands_when_something_written_or_error =

[Domoticz]
send_to_domoticz = False
domot_url = http://192.168.0.222:8081

monitor_datetime = 0
monitor_longitude = 0
monitor_latitude = 0
monitor_engineon = 0
monitor_battery12v = 0
monitor_odometer = 0
monitor_soc = 0
monitor_charging = 0
monitor_plugged = 0
monitor_address = 0
monitor_evrange = 0

trip_date = 0
trip_starttime = 0
trip_drivetime = 0
trip_idletime = 0
trip_distance = 0
trip_avgspeed = 0
trip_maxspeed = 0

dailystats_date = 0
dailystats_distance = 0
dailystats_distance_unit = 0
dailystats_total_consumed = 0
dailystats_regenerated_energy = 0
dailystats_engine_consumption = 0
dailystats_climate_consumption = 0
dailystats_onboard_electronics_consumption = 0
dailystats_battery_care_consumption = 0

[MQTT]
send_to_mqtt = False
mqtt_broker_hostname = localhost
mqtt_broker_port = 1883
mqtt_broker_username =
mqtt_broker_password =
mqtt_main_topic = hyundai_kia_connect_monitor
30 changes: 27 additions & 3 deletions monitor.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@
arg_has,
float_to_string_no_trailing_zero,
get,
get_bool,
get_filepath,
get_last_line,
get_safe_datetime,
Expand All @@ -60,6 +61,19 @@
to_int,
)

from domoticz_utils import (
send_dailystats_line_to_domoticz,
send_monitor_csv_line_to_domoticz,
send_tripinfo_line_to_domoticz,
)
from mqtt_utils import (
send_dailystats_line_to_mqtt,
send_monitor_csv_line_to_mqtt,
send_tripinfo_line_to_mqtt,
set_vin,
stop_mqtt_loop,
)

SCRIPT_DIRNAME = path.abspath(path.dirname(__file__))
logging.config.fileConfig(f"{SCRIPT_DIRNAME}/logging_config.ini")
D = arg_has("debug")
Expand Down Expand Up @@ -88,11 +102,11 @@
USERNAME = monitor_settings["username"]
PASSWORD = monitor_settings["password"]
PIN = monitor_settings["pin"]
USE_GEOCODE = monitor_settings["use_geocode"].lower() == "true"
USE_GEOCODE_EMAIL = monitor_settings["use_geocode_email"].lower() == "true"
USE_GEOCODE = get_bool(monitor_settings, "use_geocode", False)
USE_GEOCODE_EMAIL = get_bool(monitor_settings, "use_geocode_email", False)
LANGUAGE = monitor_settings["language"]
ODO_METRIC = get(monitor_settings, "odometer_metric", "km").lower()
MONITOR_INFINITE = get(monitor_settings, "monitor_infinite", "False").lower() == "true"
MONITOR_INFINITE = get_bool(monitor_settings, "monitor_infinite", False)
MONITOR_INFINITE_INTERVAL_MINUTES = to_int(
get(monitor_settings, "monitor_infinite_interval_minutes", "60")
)
Expand Down Expand Up @@ -203,6 +217,8 @@ def handle_daily_stats(vehicle: Vehicle, number_of_vehicles: int) -> None:
file.write("\n")
MONITOR_SOMETHING_WRITTEN_OR_ERROR = True
last_line = line
send_dailystats_line_to_domoticz(full_line)
send_dailystats_line_to_mqtt(full_line)
else:
if D:
print(
Expand Down Expand Up @@ -288,6 +304,8 @@ def handle_day_trip_info(
MONITOR_SOMETHING_WRITTEN_OR_ERROR = True
last_date = yyyymmdd
last_hhmmss = hhmmss
send_tripinfo_line_to_domoticz(line)
send_tripinfo_line_to_mqtt(line)
else:
_ = D and dbg(f"Skipping trip: {yyyymmdd},{hhmmss}")
else:
Expand Down Expand Up @@ -427,7 +445,10 @@ def handle_one_vehicle(
break # finished
if not same:
_ = D and dbg(f"Writing1:\nline=[{line}]\nlast=[{last_line}]")

writeln(filename, line)
send_monitor_csv_line_to_domoticz(line)
send_monitor_csv_line_to_mqtt(line)
handle_daily_stats(vehicle, number_of_vehicles)
vehicle_stats = [
str(newest_updated_at),
Expand Down Expand Up @@ -534,6 +555,7 @@ def handle_vehicles(login: bool) -> bool:
error = False
number_of_vehicles = len(MANAGER.vehicles)
for _, vehicle in MANAGER.vehicles.items():
set_vin(vehicle.VIN)
error = handle_one_vehicle(MANAGER, vehicle, number_of_vehicles)
if error: # something gone wrong, exit vehicles loop
error_string = "Error occurred in handle_one_vehicle()"
Expand Down Expand Up @@ -619,5 +641,7 @@ def monitor():
logging.error("Too many subsequent errors occurred, exiting monitor.py")
sys.exit(-1)

stop_mqtt_loop()


monitor()
Loading

0 comments on commit a54e9d9

Please sign in to comment.