Skip to content

Commit

Permalink
Release 2.0.3 (#54)
Browse files Browse the repository at this point in the history
* Fixed MQTT incomplete message bug

* Fixed MQTT Publishing

* Added new Logger with "the loop"

* cleaned up formatting

* Tidied up publish loop

* Fixed tzlocal install bug

* Changed timestamp on magtest logfile

* Small tweaks

* Ready for release
  • Loading branch information
CharlesGodwin authored Nov 1, 2023
1 parent 3752db0 commit 597da34
Show file tree
Hide file tree
Showing 15 changed files with 236 additions and 52 deletions.
7 changes: 7 additions & 0 deletions CHANGES.rst
Original file line number Diff line number Diff line change
@@ -1,6 +1,13 @@
=======================
pyMagnum Release Notes
=======================
Version 2.0.3 2023/10/20
------------------------
- Fixed problem with installing tzlocal on some system
- Changed minimum Python version to 3.7
- Added warning about use of ``--break-system-packages`` with Python 3.11 and higher.
- Improved how to build information.

Version 2.0.2 2022/11/09
------------------------
- Fixed bug in issue #44 https://github.com/CharlesGodwin/pymagnum/issues/44
Expand Down
12 changes: 7 additions & 5 deletions build_draft.sh
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
#!/bin/bash
rm -R -f dist
rm -R -f pymagnum.egg-info
python setup.py clean build
python setup.py sdist --formats=zip
#!/bin/bash
# readhow_to_build.md
# this assumes python 3.7 or higher is installed
rm -R -f dist
rm -R -f pymagnum.egg-info
python setup.py clean build
python setup.py sdist --formats=zip
10 changes: 3 additions & 7 deletions docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,6 @@
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
#
import os
import sys
from datetime import date
# sys.path.insert(0, os.path.abspath('../magnum'))
# sys.path.insert(0, os.path.abspath('../'))
Expand All @@ -21,21 +19,19 @@
# -- Project information -----------------------------------------------------
# BUILDINFO
project = 'Magnum Energy'
year = date.today().year
copyright = f"2018-{year}, Charles Godwin [email protected]"
copyright = f"2018-{date.today().year}, Charles Godwin [email protected]"
author = 'Charles Godwin'

# The full version, including alpha/beta/rc tags
release = '2.0'
version = '2.0.2'
version = '2.0.3'

# -- General configuration ---------------------------------------------------

# Add any Sphinx extension module names here, as strings. They can be
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
# ones.
extensions = [
]
extensions = []

# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']
Expand Down
4 changes: 4 additions & 0 deletions docs/installation.rst
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,10 @@ version 3.7 of Python.
| Then install or upgrade this software package using:
| ``sudo pip install --upgrade pymagnum``
If you are using Python 3.11 or higher, you may be greeted by ``error: externally-managed-environment``.
You can install ``pymagnum`` in a virtual environment to avoid this message, but then you can only use magnum program when you enable this virtual environment first.
If you want to ignore this annoyance, you can just add the ``--break-system-packages`` flag and go on with your day.

If you want to check which version is have installed on your system, run this command:
``sudo pip show pymagnum``

Expand Down
10 changes: 3 additions & 7 deletions examples/magsample.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,13 +44,9 @@ def sigint_handler(signal, frame):
help="Suppress clean up of unknown packets (default: False)")
args = parser.magnum_parse_args()
if args.verbose:
print('Magnum Sample Version:{0}'.format(magnum.__version__))
print("Options:{0}".format(str(args)
.replace("Namespace(", "")
.replace(")", "")
.replace('[', '')
.replace("'", "")
.replace(']', '')))
print(f"Magnum Sample Version:{magnum.__version__}")
print(f"Options:{str(args)[10:-1]}")

magnumReaders = dict()
for device in args.device:
try:
Expand Down
17 changes: 9 additions & 8 deletions examples/magserver.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
import sys

from collections import OrderedDict
from datetime import datetime
from datetime import datetime, timezone
from http import HTTPStatus
from http.server import BaseHTTPRequestHandler, HTTPServer

Expand Down Expand Up @@ -38,8 +38,9 @@ def log_request(self, code='-', size='-'):
#
if isinstance(code, HTTPStatus):
code = code.value
if code < 200 or code >= 300:
super().log_request(code=code, size=size)
if type(code) is int:
if code < 200 or code >= 300:
super().log_request(code=code, size=size)

def _set_headers(self, contenttype="text/html"):
self.send_response(200)
Expand All @@ -50,8 +51,8 @@ def do_GET(self):
response = []
code = None
message = None
timestamp = datetime.now(get_localzone()).replace(
microsecond=0).isoformat()
timestamp = datetime.now(timezone.utc).replace(
microsecond=0).astimezone().isoformat()
devices = []
for comm_device, magnumReader in magnumReaders.items():
try:
Expand All @@ -66,7 +67,7 @@ def do_GET(self):
device['data'] = data
response.append(device)
else:
message = "No data avaliable"
message = "No data available"
code = HTTPStatus.NO_CONTENT
except Exception as e:
message = "Exception detected attempting to read network data - {0} {1}".format(comm_device, str(e))
Expand All @@ -75,7 +76,7 @@ def do_GET(self):
jsonString = json.dumps(response)
self.wfile.write(jsonString.encode("utf8"))
return
self.send_error(code, message=message)
self.send_error(code, message=message) # type: ignore


def run(server_class=HTTPServer, handler_class=magServer, addr="", port=17223):
Expand Down Expand Up @@ -109,7 +110,7 @@ def run(server_class=HTTPServer, handler_class=magServer, addr="", port=17223):
args = parser.magnum_parse_args()
if args.listen.upper() == 'ALL':
args.listen = ''
print("Options:{}".format(str(args).replace("Namespace(", "").replace(")", "")))
print(f"Options:{str(args)[10:-1]}")
for device in args.device:
try:
magnumReader = Magnum(device=device, packets=args.packets, trace=args.trace,
Expand Down
179 changes: 179 additions & 0 deletions examples/mqttlogger-2.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,179 @@
#
# Copyright (c) 2018-2023 Charles Godwin <[email protected]>
#
# SPDX-License-Identifier: BSD-3-Clause
#
# This code is provided as an example of a JSON logger that writes to MQTT
# The data is published at every interval seconds
# If you publish the subtopic 'refresh' (i.e magnum/refresh) this will publish data immediately
# run the program with --help for details of options.
# Each device is a separate JSON record new-line delimited
# The JSON Record is like this:
# datetime is a timestamp for local time
# timestamp is the same time but as a Unix Epoch second as UTC time - this is useful for time series software
# the data object is pertinent to each device
# The current devices are INVERTER, REMOTE, AGS, BMK, and PT100
# topic = magnum/inverter
# payload
# {
# "datetime": "2019-10-08 12:49:14-04:00",
# "device": "INVERTER",
# "data": {
# "revision": "5.1",
# "mode": 64,
# "mode_text": "INVERT",
# "fault": 0,
# "fault_text": "None",
# "vdc": 24.6,
# "adc": 2,
# "VACout": 119,
# "VACin": 0,
# "invled": 1,
# "invled_text": "On",
# "chgled": 0,
# "chgled_text": "Off",
# "bat": 17,
# "tfmr": 36,
# "fet": 30,
# "model": 107,
# "model_text": "MS4024PAE",
# "stackmode": 0,
# "stackmode_text": "Stand Alone",
# "AACin": 0,
# "AACout": 1,
# "Hz": 60.0
# }
# }

import json
import signal
import sys
import time
import uuid
from collections import OrderedDict
from datetime import datetime, timezone

import paho.mqtt.client as mqtt
from magnum.magnum import Magnum
from magnum.magparser import MagnumArgumentParser
# from tzlocal import get_localzone


def on_connect(client, userdata, flags, rc):
if rc == 0:
global args
client.subscribe(f"{args.topic}refresh")
if args.trace:
print("Connected")
else:
raise Exception("Connection failed.")


def on_message(client, userdata, msg):
global args
if msg.topic == f"{args.topic}refresh":
publish_data()


def on_publish(client, userdata, result):
print(f"Message {result} published\n")


def sigint_handler(signal, frame):
print('Interrupted. Shutting down.')
global client
client.disconnect()
client.loop_stop()
sys.exit(0)


def publish_data():
global magnumReaders, client, args
try:
for comm_device, magnumReader in magnumReaders.items():
try:
devices = magnumReader.getDevices()
if len(devices) != 0:
data = OrderedDict()
now = int(time.time())
data["datetime"] = datetime.now(timezone.utc).replace(
microsecond=0).astimezone().isoformat()
data['comm_device'] = comm_device
for device in devices:
topic = args.topic + device["device"].lower()
data["device"] = device["device"]
data["data"] = device["data"]
payload = json.dumps(data, indent=None, ensure_ascii=True, allow_nan=True, separators=(',', ':'))
client.publish(topic, payload=payload)
except Exception as e:
print(f"{comm_device} {str(e)}")
except Exception as e:
print(str(e))


signal.signal(signal.SIGINT, sigint_handler)
parser = MagnumArgumentParser(description="Magnum Data MQTT Publisher", fromfile_prefix_chars='@',
epilog="Refer to https://github.com/CharlesGodwin/pymagnum for details")
logger = parser.add_argument_group("MQTT publish")
reader = parser.add_argument_group("Magnum reader")
logger.add_argument("-t", "--topic", default='magnum/',
help="Topic prefix (default: %(default)s)")
logger.add_argument("-b", "--broker", default='localhost:1883',
help="MQTT Broker address and (optional port)(default: %(default)s)")
logger.add_argument("-i", "--interval", default=60, type=int, dest='interval',
help="Interval, in seconds, between publishing (default: %(default)s)")
logger.add_argument("--username", default='None',
help="MQTT User name, if needed (default: %(default)s)")
logger.add_argument("--password", default='None',
help="MQTT User password, if needed (default: %(default)s)")
reader.add_argument("-d", "--device", nargs='*', action='append', default=[],
help="Serial device name (default: /dev/ttyUSB0). You can specify more than one.")
reader.add_argument("--packets", default=50, type=int,
help="Number of packets to generate in reader (default: %(default)s)")
reader.add_argument("--timeout", default=0.005, type=float,
help="Timeout for serial read (default: %(default)s)")
reader.add_argument("--trace", action="store_true",
help="Add raw packet info to data (default: %(default)s)")
reader.add_argument("--nocleanup", action="store_true", default=False, dest='cleanpackets',
help="Suppress clean up of unknown packets (default: False)")
args = parser.magnum_parse_args()
if args.interval < 10 or args.interval > (60*60):
parser.error(
"argument -i/--interval: must be between 10 seconds and 3600 (1 hour)")
if args.topic[-1] != "/":
args.topic += "/"
print(f"Options:{str(args)[10:-1]}")
magnumReaders = dict()
for device in args.device:
try:
magnumReader = Magnum(device=device, packets=args.packets, trace=args.trace,
timeout=args.timeout, cleanpackets=args.cleanpackets)
magnumReader.getDevices() # test read to see if all's good
magnumReaders[magnumReader.getComm_Device()] = magnumReader
except Exception as e:
print(f"{device} {device}")
if len(magnumReaders) == 0:
print("Error: There are no usable devices connected.")
exit(2)

print(
f"Publishing to broker:{args.broker} Every:{args.interval} seconds. Using: {list(magnumReaders.keys())}")
uuidstr = str(uuid.uuid1())
brokerinfo = args.broker.split(':')
if len(brokerinfo) == 1:
brokerinfo.append(1883)
client = mqtt.Client(client_id=uuidstr, clean_session=False)
if args.username != 'None':
client.username_pw_set(username=args.username, password=args.password)
client.on_connect = on_connect
client.on_message = on_message
if args.trace:
client.on_publish = on_publish
client.connect(brokerinfo[0], port=int(brokerinfo[1]))
client.loop_start()
while True:
start = time.time()
publish_data()
sleep = args.interval - (time.time() - start)
if sleep > 0:
time.sleep(sleep)
8 changes: 4 additions & 4 deletions examples/mqttlogger.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,12 +49,12 @@
import time
import uuid
from collections import OrderedDict
from datetime import datetime
from datetime import datetime, timezone

import paho.mqtt.client as mqtt
from magnum.magnum import Magnum
from magnum.magparser import MagnumArgumentParser
from tzlocal import get_localzone
# from tzlocal import get_localzone

def on_connect(client, userdata, flags, rc):
if rc == 0:
Expand Down Expand Up @@ -148,8 +148,8 @@ def sigint_handler(signal, frame):
if len(devices) != 0:
data = OrderedDict()
now = int(time.time())
data["datetime"] = datetime.now(
get_localzone()).replace(microsecond=0).isoformat()
data["datetime"] = datetime.now(timezone.utc).replace(
microsecond=0).astimezone().isoformat()
data['comm_device'] = comm_device
for device in devices:
topic = args.topic + device["device"].lower()
Expand Down
6 changes: 5 additions & 1 deletion how_to_build.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ __How to Build pyMagnum for Testing__

This is a simple description of the steps needed to build a version locally.

**NOTE** you must be using Python 3 for all this. In windows use `pip` and `python` and in Linux use `sudo pip3` and `python3`. If you have experience with Python Virtual Environments, you can use one of those too.
**NOTE** you must be using Python 3 for all this. In windows use `pip` and `python` and in Linux use `sudo pip3` and `python3` **. If you have experience with Python Virtual Environments, you can use one of those too.

Clone the repository from https://github.com/CharlesGodwin/pymagnum

Expand All @@ -23,3 +23,7 @@ If you are developing in VS Code, You need to do this to ensure the debug enviro

`python setup.py develop` or
`sudo python setup.py develop`

** __NOTE:__ Some linux versions don't have a `python` or `pip` defined, just `python3` and `pip3`. In that case use the commands to get things working:
`alias python=python3`
`alias pip=pip3`
2 changes: 1 addition & 1 deletion magnum/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
# SPDX-License-Identifier: BSD-3-Clause
#
# BUILDINFO
__version__="2.0.2"
__version__="2.0.3"
#
# names of devices
#
Expand Down
Loading

0 comments on commit 597da34

Please sign in to comment.