Hallo,
ich wollte mich schon seit Jahren mal etwas ernsthafter mit python
beschäftigen und hier ist mein Erstlingswerk signifikanter Länge.
pylint hat nichts auszusetzen, an manchen Stellen habe ich pylint aber
überstimmt.
Ist da irgendetwas drin, was nicht pythonesk genug formuliert ist?
Laufen tut das Programm jedenfalls.
Ich würde mich über Eure Kommentare freuen.
Grüße
Marc
#!/usr/bin/python
"""
Monitor the MQTT messages from an electric meter
and send out information when an applicance has started and ended
its run
"""
import sys
import argparse
import json
import time
import pprint
import threading
import yaml
import paho.mqtt.client as mqtt
pp = pprint.PrettyPrinter(indent=4)
argparser=argparse.ArgumentParser(
prog = 'appliance',
description = 'monitor appliance power consumption and'
+ 'send status messages',
)
argparser.add_argument('-c',
'--config',
help='path to configuration file',
)
argparser.add_argument('-d',
'--debug',
action='store_true',
help='activate debugging',
)
args=argparser.parse_args()
# pylint issue 2166: all module-level assignments get flagged with
this
# how would I properly define a global variable?
# pylint: disable-next=invalid-name
debug = 0
if args.debug:
# pylint: disable-next=invalid-name
debug = 1
if debug > 0:
print('parsed arguments:')
pp.pprint(args)
try:
with open(args.config, encoding="utf-8") as c:
config=yaml.safe_load(c)
except FileNotFoundError:
sys.exit('configuration file '+args.config+' not found or
unreadable')
if debug > 0:
print('read config:')
pp.pprint(config)
if 'debug' in config:
debug = config['debug']
table=[
{
'timestamp': time.time(),
'leistungRounded': 0,
'leistung': 0,
}
]
# pylint: disable-next=invalid-name
status = 'inaktiv'
status_change = time.time()
# pylint:
disable-next=redefined-outer-name,unused-argument,invalid-name
def on_connect(client, userdata, flags, rc):
"""handle successful connect callback"""
if debug > 0:
print('Connected to ',
config['config']['host'],
'port',
config['config']['port'],
'with result code',
str(rc),
)
print('subscribe to topic '+config['config']['topic'])
client.subscribe(config['config']['topic'])
def print_table():
""" print table for debugging """
if debug > 0:
now=time.time()
for element in table:
print(int(now-element['timestamp']),
element['leistungRounded'],
round(element['leistung'],6),
element.get('meter',''),
)
print('length:',
len(table),
'max:',
max_leistung(table),
)
print('-----------')
cleanuplock=threading.Lock()
def cleanup_table():
"""clean up table, removing data that is older than cutoff time"""
with cleanuplock:
while (
len(table) > 1
and table[0]['timestamp']+config['config']['cutoff_time']
< time.time()
):
if debug > 0:
print('remove from beginning')
table.pop(0)
def add_element(newdata):
"""create new data record and add to table"""
rounded = int(newdata['leistung']*1000)
element={
'leistung': newdata['leistung'],
'leistungRounded': rounded,
'timestamp': time.time(),
}
if 'meter' in newdata:
element['meter']=newdata.get('meter')
table.append(element)
if debug > 0:
print(
'add:',
int(element['timestamp']),
element['leistungRounded'],
round(element['leistung'],6),
)
# pylint: disable-next=redefined-outer-name
def max_leistung(table):
"""return maximum "leistung" found in table if recent enough"""
maxvalue=0
elementfound=0
for element in table:
if (
element['timestamp']+config['config']['cutoff_time']
< time.time()
):
continue
elementfound=1
if element['leistungRounded']>maxvalue:
maxvalue=element['leistungRounded']
if elementfound==0:
maxvalue=table[len(table)-1]['leistungRounded']
return maxvalue
# pylint: disable-next=redefined-outer-name↲
def report_status_change(status):
"""report status change. Plug in messenger code here"""
print('statuswechsel:',
config['config']['friendlyname'],
'ist',
status,
)
evaluatelock=threading.Lock()
def evaluate():
"""
Evaluate the current table, handle internal state machine and
call function to act on relevant status change
"""
# pylint: disable-next=global-statement,invalid-name
global status
with evaluatelock:
maxl=max_leistung(table)
if (
status=='fertig'
and status_change+config['config']['inactive_time'] <
time.time()
):
status='inaktiv'
if status=='aktiv':
if maxl < config['config']['on_threshold']:
status='fertig'
report_status_change(status)
if (status!='aktiv' and maxl >=
config['config']['on_threshold']):
status='aktiv'
report_status_change(status)
# pylint: disable-next=unused-argument,redefined-outer-name
def on_message(client, userdata, msg):
"""
Callback function acting on an incoming MQTT message
"""
if debug > 0:
print('receive MQTT message to',
msg.topic,
':',
str(msg.payload),
)
jsonobject=json.loads(msg.payload)
cleanup_table()
add_element(jsonobject)
if debug > 0:
print_table()
evaluate()
client = mqtt.Client()
client.on_connect = on_connect
client.on_message = on_message
client.connect(config['config']['host'], config['config']['port'], 60)
client.loop_start()
while True:
time.sleep(30)
if debug > 0:
print('run evaluate() and cleanup_table() from main loop')
cleanup_table()
if debug > 0:
print_table()
evaluate()
# end of file
--
-------------------------------------- !! No courtesy copies, please !! -----
Marc Haber | " Questions are the | Mailadresse im Header
Mannheim, Germany | Beginning of Wisdom " |
Nordisch by Nature | Lt. Worf, TNG "Rightful Heir" | Fon: *49 621 72739834