Skip to content

Commit

Permalink
Implement Logger for kamstrup.py (#7)
Browse files Browse the repository at this point in the history
  • Loading branch information
Mausy5043 authored Jul 28, 2024
2 parents 914f6b6 + 8d8b12d commit 6ba8327
Show file tree
Hide file tree
Showing 11 changed files with 126 additions and 86 deletions.
1 change: 0 additions & 1 deletion .github/dependabot.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,3 @@ updates:
directory: "/" # Location of package manifests
schedule:
interval: "weekly"

21 changes: 21 additions & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# These checks must pass before you may commit changes
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.6.0
hooks:
- id: check-case-conflict
- id: check-docstring-first
- id: check-executables-have-shebangs
- id: check-json
- id: check-toml
- id: check-yaml
- id: end-of-file-fixer
- id: mixed-line-ending
- id: pretty-format-json
- id: requirements-txt-fixer
- id: trailing-whitespace
args: [--markdown-linebreak-ext=md]
- repo: https://github.com/psf/black
rev: 24.4.2
hooks:
- id: black
9 changes: 5 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
![Static Badge](https://img.shields.io/badge/release-rolling-lightgreen)
[![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black)
[![pre-commit.ci status](https://results.pre-commit.ci/badge/github/Mausy5043/lektrix/master.svg)](https://results.pre-commit.ci/latest/github/Mausy5043/lektrix/master)

# lektrix

Interface
with [KAMSTRUP smart electricity meter](https://www.kamstrup.com/), [SolarEdge API](https://www.solaredge.com/)
and [ZAPPI v2](https://myenergi.com/) API using Raspberry Pi

### What is does
### What is does

- Read data from a KAMSTRUP smart meter.
- Read data from solarpanels via SolarEdge API.
Expand All @@ -18,15 +19,15 @@ and [ZAPPI v2](https://myenergi.com/) API using Raspberry Pi

### Installing

Clone the repository somewhere handy. Adding the `lektrix` directory to your `$PATH` may help.
To install run `lektrix --install`.
Clone the repository somewhere handy. Adding the `lektrix` directory to your `$PATH` may help.
To install run `lektrix --install`.
Use `lektrix --uninstall` to uninstall.

### Usage

`lektrix <options>`

`--hours HOURS`, `--days DAYS`, `--months MONTHS`, `--years YEARS`
`--hours HOURS`, `--days DAYS`, `--months MONTHS`, `--years YEARS`
Create a bar graph of the given number of HOURS/DAYS/MONTHS/YEARS

`--balance`, `--balances`
Expand Down
72 changes: 41 additions & 31 deletions bin/kamstrup.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,19 +6,34 @@
"""

import argparse
import logging
import logging.handlers
import os
import shutil
import sys
import syslog
import time
import traceback

import mausy5043_common.funfile as mf
import mausy5043_common.libsqlite3 as m3

import constants
import libkamstrup as kl
import GracefulKiller as gk

logging.basicConfig(
level=logging.INFO,
format="%(module)s.%(funcName)s [%(levelname)s] - %(message)s",
datefmt="%Y-%m-%d %H:%M:%S",
handlers=[
logging.handlers.SysLogHandler(
address="/dev/log",
facility=logging.handlers.SysLogHandler.LOG_DAEMON,
)
],
)
LOGGER: logging.Logger = logging.getLogger(__name__)

# fmt: off
parser = argparse.ArgumentParser(description="Execute the kamstrup daemon.")
parser_group = parser.add_mutually_exclusive_group(required=True)
Expand Down Expand Up @@ -70,47 +85,35 @@ def main():
set_led("mains", "green")
except Exception: # noqa
set_led("mains", "red")
mf.syslog_trace(
"Unexpected error while trying to do some work!",
syslog.LOG_CRIT,
DEBUG,
)
mf.syslog_trace(traceback.format_exc(), syslog.LOG_DEBUG, DEBUG)
LOGGER.critical("Unexpected error while trying to do some work!")
LOGGER.error(traceback.format_exc())
raise
if not succes:
set_led("mains", "orange")
mf.syslog_trace("Getting telegram failed", syslog.LOG_WARNING, DEBUG)
LOGGER.warning("Getting telegram failed")
# check if we already need to report the result data
if time.time() > rprt_time:
mf.syslog_trace("Reporting", False, DEBUG)
mf.syslog_trace(f"Result : {API_KL.list_data}", False, DEBUG)
LOGGER.debug("Reporting")
LOGGER.debug(f"Result : {API_KL.list_data}")
# resample to 15m entries
data, API_KL.list_data = API_KL.compact_data(API_KL.list_data)
mf.syslog_trace(f"Remainder: {API_KL.list_data}", False, DEBUG)
try:
mf.syslog_trace(f"Data to add (first) : {data[0]}", False, DEBUG)
mf.syslog_trace(f" (last) : {data[-1]}", False, DEBUG)
for element in data:
# LOGGER.debug(f"{element}") # is already logged by sql_db.queue()
sql_db.queue(element)
except Exception: # noqa
set_led("mains", "red")
mf.syslog_trace(
"Unexpected error while trying to queue the data",
syslog.LOG_ALERT,
DEBUG,
)
mf.syslog_trace(traceback.format_exc(), syslog.LOG_ALERT, DEBUG)
LOGGER.critical("Unexpected error while trying to queue the data")
LOGGER.error(traceback.format_exc())
raise # may be changed to pass if errors can be corrected.
try:
sql_db.insert(method="replace")
except Exception: # noqa
set_led("mains", "red")
mf.syslog_trace(
"Unexpected error while trying to commit the data to the database",
syslog.LOG_ALERT,
DEBUG,
LOGGER.critical(
"Unexpected error while trying to commit the data to the database"
)
mf.syslog_trace(traceback.format_exc(), syslog.LOG_ALERT, DEBUG)
LOGGER.error(traceback.format_exc())
raise # may be changed to pass if errors can be corrected.

# electricity meter determines the tempo, so no need to wait.
Expand All @@ -121,15 +124,15 @@ def main():
) # gives the actual time when the next loop should start
# determine moment of next report
rprt_time = time.time() + (report_interval - (time.time() % report_interval))
mf.syslog_trace(f"Spent {time.time() - start_time:.1f}s getting data", False, DEBUG)
mf.syslog_trace(f"Report in {rprt_time - time.time():.0f}s", False, DEBUG)
mf.syslog_trace("................................", False, DEBUG)
LOGGER.debug(f"Spent {time.time() - start_time:.1f}s getting data")
LOGGER.debug(f"Report in {rprt_time - time.time():.0f}s")
LOGGER.debug("................................")
else:
time.sleep(1.0) # 1s resolution is enough


def set_led(dev, colour):
mf.syslog_trace(f"{dev} is {colour}", False, DEBUG)
LOGGER.debug(f"{dev} is {colour}")

in_dirfile = f"{APPROOT}/www/{colour}.png"
out_dirfile = f'{constants.TREND["website"]}/{dev}.png'
Expand All @@ -138,15 +141,22 @@ def set_led(dev, colour):

if __name__ == "__main__":
# initialise logging
syslog.openlog(ident=f'{MYAPP}.{MYID.split(".")[0]}', facility=syslog.LOG_LOCAL0)
syslog.openlog(
ident=f'{MYAPP}.{MYID.split(".")[0]}',
facility=syslog.LOG_LOCAL0,
)

if OPTION.debug:
DEBUG = True
print(OPTION)
mf.syslog_trace("Debug-mode started.", syslog.LOG_DEBUG, DEBUG)
if len(LOGGER.handlers) == 0:
LOGGER.addHandler(logging.StreamHandler(sys.stdout))
LOGGER.level = logging.DEBUG
LOGGER.debug("Debugging on.")
LOGGER.debug("Debug-mode started.")
print("Use <Ctrl>+C to stop.")

# OPTION.start only executes this next line, we don't need to test for it.
main()

print("And it's goodnight from him")
LOGGER.info("And it's goodnight from him")
35 changes: 20 additions & 15 deletions bin/libkamstrup.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@
"""Common functions for use with the KAMSTRUP electricity meter"""

import datetime as dt
import logging
import re
import sys
import syslog
import traceback

Expand All @@ -14,6 +16,8 @@

import constants

LOGGER: logging.Logger = logging.getLogger(__name__)


class Kamstrup: # pylint: disable=too-many-instance-attributes
"""Class to interact with the P1-port."""
Expand Down Expand Up @@ -46,7 +50,11 @@ def __init__(self, debug=False): # pylint: disable=too-many-instance-attributes
self.list_data = []

self.debug = debug
if self.debug:
if debug:
if len(LOGGER.handlers) == 0:
LOGGER.addHandler(logging.StreamHandler(sys.stdout))
LOGGER.level = logging.DEBUG
LOGGER.debug("Debugging on.")
self.telegram = []

def get_telegram(self):
Expand Down Expand Up @@ -76,13 +84,13 @@ def get_telegram(self):
# remember meaningful content
telegram.append(line)
except serial.SerialException:
mf.syslog_trace("*** Serialport read error:", syslog.LOG_CRIT, self.debug)
mf.syslog_trace(traceback.format_exc(), syslog.LOG_CRIT, self.debug)
LOGGER.critical("*** Serialport read error:")
LOGGER.error(traceback.format_exc())
valid_telegram = False
receiving = False
except UnicodeDecodeError:
mf.syslog_trace("*** Unicode Decode error:", syslog.LOG_CRIT, self.debug)
mf.syslog_trace(traceback.format_exc(), syslog.LOG_CRIT, self.debug)
LOGGER.critical("*** Unicode Decode error:")
LOGGER.error(traceback.format_exc())
valid_telegram = False
receiving = False

Expand Down Expand Up @@ -112,6 +120,7 @@ def _translate_telegram(self, telegram):
Returns:
(dict): data converted to a dict.
"""
LOGGER.debug(f" {telegram}")
for element in telegram:
try:
line = re.split(r"[\(\*\)]", element)
Expand Down Expand Up @@ -149,15 +158,10 @@ def _translate_telegram(self, telegram):
# ['0-0:96.13.0', '', ''] text message
# not recorded
except ValueError:
if self.debug:
mf.syslog_trace(
"*** Conversion not possible for element:",
syslog.LOG_INFO,
self.debug,
)
mf.syslog_trace(f" {element}", syslog.LOG_INFO, self.debug)
mf.syslog_trace("*** Extracted from telegram:", syslog.LOG_INFO, self.debug)
mf.syslog_trace(f" {telegram}", syslog.LOG_INFO, self.debug)
LOGGER.critical("*** Conversion not possible for element:")
LOGGER.error(f" {element}")
LOGGER.error("*** Extracted from telegram:")
LOGGER.error(f" {telegram}")
idx_dt = dt.datetime.now()
epoch = int(idx_dt.timestamp())

Expand Down Expand Up @@ -206,9 +210,10 @@ def _convert_time_to_text(date_to_convert):

# recalculate 'sample_epoch'
df_out["sample_epoch"] = df_out["sample_time"].apply(_convert_time_to_epoch)
mf.syslog_trace(f"{df_out}", False, self.debug)
result_data = df_out.to_dict("records") # list of dicts

df = df[df["sample_epoch"] > np.max(df_out["sample_epoch"])] # pylint: disable=E1136
remain_data = df.to_dict("records")
LOGGER.debug(f"Result: {result_data}")
LOGGER.debug(f"Remain: {remain_data}\n")
return result_data, remain_data
2 changes: 1 addition & 1 deletion docs/HARVI.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# HARVI

## Payload cgi-jstatus-H...
Call: `https://s__.myenergi.net/cgi-jstatus-H########`
Call: `https://s__.myenergi.net/cgi-jstatus-H########`

Where:
* `s__` is the server-id
Expand Down
18 changes: 9 additions & 9 deletions docs/ZAPPI.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@
// C1 = EV ready to receive charge
// C2 = Charging
// F = Fault
'pwm': 4100, // Pulse Width Modulation [% * 100]
'pwm': 4100, // Pulse Width Modulation [% * 100]
// percentage of 60A that can be delivered
// 4100 = 24.6 A
'rac': 4, // Residual AC
Expand Down Expand Up @@ -93,7 +93,7 @@ Note: Energy is in Joules; divide by 60 get average Watts; divide by 3 600 000 t
'exp': 16560, // Exported period total [J]
'frq': 5006, // Line frequency [centiHertz]
'gen': 480 // Energy consumed by inverter (usually at night) [J]
'gep': 2409600, // Energy production (solar panels) period total [J]
'gep': 2409600, // Energy production (solar panels) period total [J]
'h1b': 123, // Imported energy used for EV period total [J]
'h1d': 14706420, // Produced energy diverted to EV (?) period total [J]
'hr': 6, // Hour of the Day (UTC!) (if applicable and non-zero)
Expand All @@ -107,11 +107,11 @@ Note: Energy is in Joules; divide by 60 get average Watts; divide by 3 600 000 t
truncated
```

`gep` should match hourly PV totaliser
`imp` imported from grid; should match hourly P1 totaliser (T1,in + T2,in; KAMSTRUP)
`exp` exported to grid; should match hourly P1 totaliser (T1,out + T2,out; KAMSTRUP)
`gep` should match hourly PV totaliser
`imp` imported from grid; should match hourly P1 totaliser (T1,in + T2,in; KAMSTRUP)
`exp` exported to grid; should match hourly P1 totaliser (T1,out + T2,out; KAMSTRUP)
`h1d` should match EV consumption
`h1b`: device 1 boost
`h1d`: device 1 divert
`h2b`: device 2 boost
`h2d`: device 2 divert
`h1b`: device 1 boost
`h1d`: device 1 divert
`h2b`: device 2 boost
`h2d`: device 2 divert
Loading

0 comments on commit 6ba8327

Please sign in to comment.