Skip to content

Commit

Permalink
summary.py and dailystats.py now also sends data to Domoticz and/or M…
Browse files Browse the repository at this point in the history
…QTT Broker
  • Loading branch information
ZuinigeRijder committed Nov 20, 2024
1 parent 9e79b57 commit 227bba9
Show file tree
Hide file tree
Showing 19 changed files with 1,957 additions and 256 deletions.
701 changes: 631 additions & 70 deletions README.md

Large diffs are not rendered by default.

17 changes: 8 additions & 9 deletions check_monitor.py
Original file line number Diff line number Diff line change
@@ -1,20 +1,19 @@
# == check_monitor.py Author: Zuinige Rijder =========
""" Simple Python3 script to check monitor.csv """
from os import path
from datetime import datetime
from io import TextIOWrapper
import logging
import logging.config
from pathlib import Path

from monitor_utils import arg_has, split_on_comma
from monitor_utils import arg_has, dbg, set_dbg, split_on_comma

SCRIPT_DIRNAME = path.abspath(path.dirname(__file__))
logging.config.fileConfig(f"{SCRIPT_DIRNAME}/logging_config.ini")
D = arg_has("debug")


def dbg(line: str) -> bool:
"""print line if debugging"""
if D:
print(line)
return D # just to make a lazy evaluation expression possible

if D:
set_dbg()

# indexes to splitted monitor.csv items
DT = 0 # datetime
Expand Down
86 changes: 69 additions & 17 deletions dailystats.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,21 @@
import typing
from dateutil.relativedelta import relativedelta
import gspread
from domoticz_utils import (
SEND_TO_DOMOTICZ,
send_dailystats_day_line_to_domoticz,
send_dailystats_trip_line_to_domoticz,
)
from monitor_utils import (
dbg,
determine_vin,
float_to_string_no_trailing_zero,
get,
get_filepath,
arg_has,
get_vin_arg,
safe_divide,
set_dbg,
sleep_a_minute,
split_on_comma,
split_output_to_sheet_float_list,
Expand All @@ -36,25 +44,23 @@
read_reverse_order_init,
split_output_to_sheet_list,
)
from mqtt_utils import (
SEND_TO_MQTT,
send_dailystats_day_line_to_mqtt,
send_dailystats_trip_line_to_mqtt,
stop_mqtt,
)

SCRIPT_DIRNAME = path.abspath(path.dirname(__file__))
logging.config.fileConfig(f"{SCRIPT_DIRNAME}/logging_config.ini")
D = arg_has("debug")
if D:
logging.getLogger().setLevel(logging.DEBUG)
set_dbg()

# Initializing a queue for about 30 days
MAX_QUEUE_LEN = 122
PRINTED_OUTPUT_QUEUE: deque[str] = deque(maxlen=MAX_QUEUE_LEN)


def dbg(line: str) -> bool:
"""print line if debugging"""
if D:
logging.debug(line)
return D # just to make a lazy evaluation expression possible


KEYWORD_LIST = ["help", "sheetupdate", "debug"]
KEYWORD_ERROR = False
for kindex in range(1, len(sys.argv)):
Expand All @@ -74,6 +80,7 @@ def dbg(line: str) -> bool:
OUTPUT_SPREADSHEET_NAME = "monitor.dailystats"
SHEET: typing.Any = None

LASTRUN_FILENAME = Path("monitor.lastrun")
DAILYSTATS_CSV_FILE = Path("monitor.dailystats.csv")
TRIPINFO_CSV_FILE = Path("monitor.tripinfo.csv")
SUMMARY_CHARGE_CSV_FILE = Path("summary.charge.csv")
Expand All @@ -82,6 +89,7 @@ def dbg(line: str) -> bool:
LENCHECK = 1
VIN = get_vin_arg()
if VIN != "":
LASTRUN_FILENAME = Path(f"monitor.{VIN}.lastrun")
DAILYSTATS_CSV_FILE = Path(f"monitor.dailystats.{VIN}.csv")
TRIPINFO_CSV_FILE = Path(f"monitor.tripinfo.{VIN}.csv")
SUMMARY_CHARGE_CSV_FILE = Path(f"summary.charge.{VIN}.csv")
Expand Down Expand Up @@ -118,6 +126,7 @@ def dbg(line: str) -> bool:

TR_HELPER: dict[str, str] = read_translations()
COLUMN_WIDTHS = [11, 12, 14, 10, 9, 9, 8]
EMPTY_ROW = ",,,,,,"


def update_width(text: str, index_column_widths: int) -> None:
Expand Down Expand Up @@ -320,8 +329,9 @@ def print_output(output: str) -> None:
print(text, end="")
print("")

if SHEETUPDATE and len(PRINTED_OUTPUT_QUEUE) < MAX_QUEUE_LEN:
PRINTED_OUTPUT_QUEUE.append(output)
if SHEETUPDATE or SEND_TO_DOMOTICZ or SEND_TO_MQTT:
if len(PRINTED_OUTPUT_QUEUE) < MAX_QUEUE_LEN:
PRINTED_OUTPUT_QUEUE.append(output)


def get_charge_for_date(date: str) -> str:
Expand Down Expand Up @@ -580,7 +590,7 @@ def print_dailystats(
time_str = now.strftime("%H:%M")
tr_last_run = get_translation_and_update_width("Last run", 0)
print_output(f"{tr_last_run},{date_str},{time_str},{tr_weekday},,,")
print_output(",,,,,,") # empty line/row
print_output(EMPTY_ROW)

print_output(
f"{totals},{TR.recuperation},{TR.consumption},{TR.engine},{TR.climate},{TR.electronic_devices},{TR.battery_care}" # noqa
Expand Down Expand Up @@ -674,7 +684,7 @@ def summary_tripinfo() -> None:
avg_speed=f"{average_speed}",
max_speed=f"{TOTAL_TRIPINFO_MAX_SPEED}",
)
print_output(",,,,,,") # empty line/row
print_output(EMPTY_ROW)


def reverse_print_dailystats_one_line(val: list[str]) -> None:
Expand All @@ -694,7 +704,7 @@ def reverse_print_dailystats_one_line(val: list[str]) -> None:
)
if date != "Totals":
print_day_trip_info(date)
print_output(",,,,,,") # empty line/row
print_output(EMPTY_ROW)


def compute_cumulative_dailystats(val: list[str], total: list[str]) -> list[str]:
Expand Down Expand Up @@ -824,7 +834,7 @@ def print_output_queue() -> None:
array.append({"range": f"N{cd_row}", "values": [[cd_date]]})
cd_date = ""
else:
if ct_header and queue_output != ",,,,,,":
if ct_header and queue_output != EMPTY_ROW:
# tripinfo
trip = [ct_date, 0, "", 0, 0, 0, 0] # clear consumption
tmp = re.sub(r"[^0-9.,:-]", "", queue_output)
Expand Down Expand Up @@ -864,6 +874,45 @@ def print_output_queue() -> None:
SHEET.batch_format(formats)


def send_to_mqtt_domoticz() -> None:
"""send_to_mqtt_domoticz"""
idx = 0
items = []
for item in PRINTED_OUTPUT_QUEUE:
idx += 1
_ = D and dbg(f"{idx}: {item}")
items.append(item)
if idx > 13 and item == EMPTY_ROW: # no more trips
break

# send dailystats day info
totals_day = f"{items[0].split(',')[1]},{items[3]},{items[4]}"
last_day = f"{items[8].split(',')[0]},{items[9]},{items[10]}"

if SEND_TO_MQTT:
determine_vin(LASTRUN_FILENAME)
send_dailystats_day_line_to_mqtt("TOTALS", totals_day)
send_dailystats_day_line_to_mqtt("LAST_DAY", last_day)

if SEND_TO_DOMOTICZ:
send_dailystats_day_line_to_domoticz("TOTALS", totals_day)
send_dailystats_day_line_to_domoticz("LAST_DAY", last_day)

# send also the dailystats_trip info
totals_trip = f"{items[5].split(',')[0]},{items[5].split(',')[2]}, {items[6]}"
last_trip = f"{items[11].split(',')[0]},{items[11].split(',')[2]}, {items[12]}"

if SEND_TO_MQTT:
send_dailystats_trip_line_to_mqtt("TOTALS", totals_trip)
send_dailystats_trip_line_to_mqtt("LAST_DAY", last_trip)

if SEND_TO_DOMOTICZ:
send_dailystats_trip_line_to_domoticz("TOTALS", totals_trip)
send_dailystats_trip_line_to_domoticz("LAST_DAY", last_trip)

stop_mqtt()


# main program
RETRIES = -1
if SHEETUPDATE:
Expand Down Expand Up @@ -910,6 +959,9 @@ def print_output_queue() -> None:
traceback.print_exc()
RETRIES = sleep_a_minute(RETRIES)

if SEND_TO_DOMOTICZ or SEND_TO_MQTT:
send_to_mqtt_domoticz()

if RETRIES == -1:
exit(0)
exit(-1)
sys.exit(0)
sys.exit(-1)
113 changes: 97 additions & 16 deletions domoticz_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,18 @@
import logging.config

from monitor_utils import (
dbg,
execute_request,
get,
d,
get_bool,
get_filepath,
get_items_dailystat_trip,
get_items_dailystats_day,
get_items_monitor_csv,
get_items_monitor_dailystats_csv,
get_items_monitor_tripinfo_csv,
get_items_summary,
)

PARSER = configparser.ConfigParser()
Expand All @@ -25,15 +30,30 @@
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()
for IDX in range(len(ITEMS_MONITOR_CSV)): # pylint:disable=consider-using-enumerate
ITEMS_MONITOR_CSV[IDX] = f"monitor_monitor_{ITEMS_MONITOR_CSV[IDX]}"

ITEMS_TRIPINFO_CSV = get_items_monitor_tripinfo_csv()
for IDX in range(len(ITEMS_TRIPINFO_CSV)): # pylint:disable=consider-using-enumerate
ITEMS_TRIPINFO_CSV[IDX] = f"monitor_tripinfo_{ITEMS_TRIPINFO_CSV[IDX]}"

ITEMS_DAILYSTATS_CSV = get_items_monitor_dailystats_csv()
for IDX in range(len(ITEMS_DAILYSTATS_CSV)): # pylint:disable=consider-using-enumerate
ITEMS_DAILYSTATS_CSV[IDX] = f"monitor_dailystats_{ITEMS_DAILYSTATS_CSV[IDX]}"

ITEMS_SUMMARY = get_items_summary()

ITEMS_DAILYSTATS_DAY = get_items_dailystats_day()

ITEMS_DAILYSTATS_TRIP = get_items_dailystat_trip()


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

url = (
Expand All @@ -43,9 +63,12 @@ def send_to_domoticz(header: str, value: str) -> None:
+ "&svalue="
+ value
)
logging.debug(url)
if reference_test:
_ = d() and dbg(f"send_to_domoticz: {header} = {value}")
else:
_ = d() and dbg(url)
retry = 0
while True:
while not reference_test:
retry += 1
content = execute_request(url, "", {})
if content != "ERROR" or retry > 30:
Expand All @@ -54,15 +77,34 @@ def send_to_domoticz(header: str, value: str) -> None:
return


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

skipped_first = not skip_first
for i in range(len(splitted)): # pylint:disable=consider-using-enumerate
send_to_domoticz(headers[i], splitted[i].strip())
if i < len(headers):
if skipped_first:
value = splitted[i].strip()
if replace_empty_by_0 and value == "":
value = "0"
send_to_domoticz(headers[i], value)
else:
skipped_first = True


def send_line(
headers: list, line: str, replace_empty_by_0: bool = True, skip_first: bool = False
) -> None:
"""send_line"""
splitted = line.split(",")
send_splitted_line(headers, splitted, replace_empty_by_0, skip_first)


def send_monitor_csv_line_to_domoticz(line: str) -> None:
Expand All @@ -71,13 +113,52 @@ def send_monitor_csv_line_to_domoticz(line: str) -> None:
send_line(ITEMS_MONITOR_CSV, line)


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


def send_dailystats_csv_line_to_domoticz(line: str) -> None:
"""send_dailystats_csv_line_to_domoticz"""
if SEND_TO_DOMOTICZ:
send_line(ITEMS_DAILYSTATS_CSV, line)


def get_items(subtopic: str, items: list[str]) -> list[str]:
"""get_items"""
new_items: list[str] = []
for item in items:
new_items.append(f"{subtopic}_{item}")
return new_items


def send_summary_line_to_domoticz(line: str) -> None:
"""send_summary_line_to_domoticz"""
if SEND_TO_DOMOTICZ:
splitted = [x.strip() for x in line.split(",")]
period = splitted[0].replace(" ", "")
if (
period
in "TRIP, DAY, WEEK, MONTH, YEAR, TRIPAVG, DAYAVG, WEEKAVG, MONTHAVG, YEARLY" # noqa
):
send_splitted_line(
get_items(f"summary_{period}", ITEMS_SUMMARY), splitted, True, True
)


def send_dailystats_day_line_to_domoticz(postfix: str, line: str) -> None:
"""send_dailystats_day_line_to_domoticz"""
if SEND_TO_DOMOTICZ:
send_line(ITEMS_MONITOR_TRIPINFO_CSV, line)
send_line(get_items(f"dailystats_day_{postfix}", ITEMS_DAILYSTATS_DAY), line)


def send_dailystats_line_to_domoticz(line: str) -> None:
"""send_dailystats_line_to_domoticz"""
def send_dailystats_trip_line_to_domoticz(
postfix: str, line: str, skip_first_two: bool = False
) -> None:
"""send_dailystats_trip_line_to_domoticz"""
if SEND_TO_DOMOTICZ:
send_line(ITEMS_MONITOR_DAILYSTATS_CSV, line)
items = get_items(f"dailystats_trip_{postfix}", ITEMS_DAILYSTATS_TRIP)
if skip_first_two:
items = items[2:]
send_line(items, line, replace_empty_by_0=False)
Binary file removed examples/MQTTExplorer.png
Binary file not shown.
Binary file added examples/MQTTExplorer_dailystats.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added examples/MQTTExplorer_monitor.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added examples/MQTTExplorer_summary.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading

0 comments on commit 227bba9

Please sign in to comment.