Add argparse, improve clean names function
This commit is contained in:
parent
0774871d53
commit
8213dd3c29
|
@ -4,6 +4,7 @@ import sys
|
|||
import re
|
||||
import json
|
||||
import os.path
|
||||
import argparse
|
||||
from time import sleep, localtime, strftime
|
||||
from colorama import init as colorama_init
|
||||
from colorama import Fore, Back, Style
|
||||
|
@ -13,25 +14,35 @@ from miflora.miflora_poller import MiFloraPoller, MI_BATTERY, MI_CONDUCTIVITY, M
|
|||
import paho.mqtt.client as mqtt
|
||||
import sdnotify
|
||||
|
||||
parameters = {MI_BATTERY: dict(pretty='Sensor Battery Level', typeformat='%d', unit='%'),
|
||||
MI_CONDUCTIVITY: dict(pretty='Soil Conductivity/Fertility', typeformat='%d', unit='µS/cm'),
|
||||
MI_LIGHT: dict(pretty='Sunlight Intensity', typeformat='%d', unit='lux'),
|
||||
MI_MOISTURE: dict(pretty='Soil Moisture', typeformat='%d', unit='%'),
|
||||
MI_TEMPERATURE: dict(pretty='Air Temperature', typeformat='%.1f', unit='°C')}
|
||||
project_name = 'Xiaomi Mi Flora Plant Sensor MQTT Client/Daemon'
|
||||
project_url = 'https://github.com/ThomDietrich/miflora-mqtt-daemon'
|
||||
|
||||
parameters = {MI_BATTERY: dict(name_pretty='Sensor Battery Level', typeformat='%d', unit='%'),
|
||||
MI_CONDUCTIVITY: dict(name_pretty='Soil Conductivity/Fertility', typeformat='%d', unit='µS/cm'),
|
||||
MI_LIGHT: dict(name_pretty='Sunlight Intensity', typeformat='%d', unit='lux'),
|
||||
MI_MOISTURE: dict(name_pretty='Soil Moisture', typeformat='%d', unit='%'),
|
||||
MI_TEMPERATURE: dict(name_pretty='Air Temperature', typeformat='%.1f', unit='°C')}
|
||||
|
||||
if False:
|
||||
# will be caught by python 2.7 to be illegal syntax
|
||||
print('Sorry, this script requires a python3 runtime environemt.', file=sys.stderr)
|
||||
|
||||
# Argparse
|
||||
parser = argparse.ArgumentParser(description=project_name, epilog='For further details see: ' + project_url)
|
||||
parser.add_argument('--gen-openhab', help='generate openHAB items based on configured sensors', action='store_true')
|
||||
parse_args = parser.parse_args()
|
||||
|
||||
# Intro
|
||||
colorama_init()
|
||||
print(Fore.GREEN + Style.BRIGHT)
|
||||
print('Xiaomi Mi Flora Plant Sensor MQTT Client/Daemon')
|
||||
print('Source: https://github.com/ThomDietrich/miflora-mqtt-daemon')
|
||||
print(project_name)
|
||||
print('Source:', project_url)
|
||||
print(Style.RESET_ALL)
|
||||
|
||||
if False:
|
||||
print('Sorry, this script requires a python3 runtime environemt.', file=sys.stderr)
|
||||
|
||||
# Systemd Service Notifications - https://github.com/bb4242/sdnotify
|
||||
sd_notifier = sdnotify.SystemdNotifier()
|
||||
|
||||
# Logging function
|
||||
def print_line(text, error = False, warning=False, sd_notify=False, console=True):
|
||||
timestamp = strftime('%Y-%m-%d %H:%M:%S', localtime())
|
||||
if console:
|
||||
|
@ -41,11 +52,17 @@ def print_line(text, error = False, warning=False, sd_notify=False, console=True
|
|||
print(Fore.YELLOW + '[{}] '.format(timestamp) + Style.RESET_ALL + '{}'.format(text) + Style.RESET_ALL)
|
||||
else:
|
||||
print(Fore.GREEN + '[{}] '.format(timestamp) + Style.RESET_ALL + '{}'.format(text) + Style.RESET_ALL)
|
||||
|
||||
timestamp_sd = strftime('%b %d %H:%M:%S', localtime())
|
||||
if sd_notify:
|
||||
sd_notifier.notify('STATUS={} - {}.'.format(timestamp_sd, unidecode(text)))
|
||||
|
||||
# Identifier cleanup
|
||||
def clean_identifier(name):
|
||||
clean = name.strip()
|
||||
for this, that in [[' ', '-'], ['ä', 'ae'], ['Ä', 'Ae'], ['ö', 'oe'], ['Ö', 'Oe'], ['ü', 'ue'], ['Ü', 'Ue'], ['ß', 'ss']]:
|
||||
clean = clean.replace(this, that)
|
||||
clean = unidecode(clean)
|
||||
return clean
|
||||
|
||||
# Eclipse Paho callbacks - http://www.eclipse.org/paho/clients/python/docs/#callbacks
|
||||
def on_connect(client, userdata, flags, rc):
|
||||
|
@ -64,7 +81,7 @@ def on_publish(client, userdata, mid):
|
|||
|
||||
|
||||
def flores_to_openhab_items(flores, reporting_mode):
|
||||
print_line('Generating openHAB "miflora.items" file ...')
|
||||
print_line('Generating openHAB items. Copy to your configuration and modify as needed...')
|
||||
items = list()
|
||||
items.append('// miflora.items - Generated by miflora-mqtt-daemon.')
|
||||
items.append('// Adapt to your needs! Things you probably want to modify:')
|
||||
|
@ -72,16 +89,18 @@ def flores_to_openhab_items(flores, reporting_mode):
|
|||
items.append('// "gAll", "broker", "UnknownRoom"')
|
||||
items.append('')
|
||||
items.append('// Mi Flora specific groups')
|
||||
items.append('Group gMiFlora "All Mi Flora sensors and elements" (gAll)')
|
||||
for param, param_properties in parameters.items():
|
||||
items.append('Group g{} "Mi Flora {} elements" (gAll)'.format(param.capitalize(), param_properties['pretty']))
|
||||
items.append('Group g{} "Mi Flora {} elements" (gAll, gMiFlora)'.format(param.capitalize(), param_properties['name_pretty']))
|
||||
if reporting_mode == 'mqtt-json':
|
||||
for [flora_name, flora] in flores.items():
|
||||
location = flora['location'] if flora['location'] else 'UnknownRoom'
|
||||
items.append('\n// Mi Flora "{}" ({})'.format(flora['pretty'], flora['mac']))
|
||||
location = flora['location_clean'] if flora['location_clean'] else 'UnknownRoom'
|
||||
items.append('\n// Mi Flora "{}" ({})'.format(flora['name_pretty'], flora['mac']))
|
||||
items.append('Group g{}{} "Mi Flora Sensor {}" (gMiFlora, g{})'.format(location, flora_name, flora['name_pretty'], location))
|
||||
for [param, param_properties] in parameters.items():
|
||||
basic = 'Number {}_{}_{}'.format(location, flora_name.capitalize(), param.capitalize())
|
||||
label = '"{} {} {} [{} {}]"'.format(location, flora['pretty'], param_properties['pretty'], param_properties['typeformat'], param_properties['unit'].replace('%', '%%'))
|
||||
details = '<text> (g{}, g{})'.format(location, param.capitalize())
|
||||
basic = 'Number {}_{}_{}'.format(location, flora_name, param.capitalize())
|
||||
label = '"{} {} {} [{} {}]"'.format(location, flora['name_pretty'], param_properties['name_pretty'], param_properties['typeformat'], param_properties['unit'].replace('%', '%%'))
|
||||
details = '<text> (g{}{}, g{})'.format(location, flora_name, param.capitalize())
|
||||
channel = '{{mqtt="<[broker:{}/{}:state:JSONPATH($.{})]"}}'.format(base_topic, flora_name, param)
|
||||
items.append(' '.join([basic, label, details, channel]))
|
||||
items.append('')
|
||||
|
@ -145,16 +164,12 @@ for [name, mac] in config['Sensors'].items():
|
|||
print_line('The MAC address "{}" seems to be in the wrong format. Please check your configuration'.format(mac), error=True, sd_notify=True)
|
||||
sys.exit(1)
|
||||
|
||||
location = ''
|
||||
if '@' in name:
|
||||
name, location = name.split("@")
|
||||
location = location.replace(' ', '-')
|
||||
name_pretty = name
|
||||
name_clean = name.lower()
|
||||
for o, n in [[' ', '-'], ['ä', 'ae'], ['ö', 'oe'], ['ü', 'ue'], ['ß', 'ss']]:
|
||||
name_clean = name_clean.replace(o, n)
|
||||
name_clean = unidecode(name_clean)
|
||||
|
||||
name_pretty, location_pretty = name.split('@')
|
||||
else:
|
||||
name_pretty, location_pretty = name, ''
|
||||
name_clean = clean_identifier(name_pretty)
|
||||
location_clean = clean_identifier(location_pretty)
|
||||
|
||||
flora = dict()
|
||||
print('Adding sensor to device list and testing connection ...')
|
||||
|
@ -163,17 +178,18 @@ for [name, mac] in config['Sensors'].items():
|
|||
|
||||
flora_poller = MiFloraPoller(mac=mac, cache_timeout=miflora_cache_timeout, retries=3)
|
||||
flora['poller'] = flora_poller
|
||||
flora['pretty'] = name_pretty
|
||||
flora['name_pretty'] = name_pretty
|
||||
flora['mac'] = flora_poller._mac
|
||||
flora['refresh'] = sleep_period
|
||||
flora['location'] = location
|
||||
flora['location_clean'] = location_clean
|
||||
flora['location_pretty'] = location_pretty
|
||||
flora['stats'] = {"count": 0, "success": 0, "failure": 0}
|
||||
try:
|
||||
flora_poller.fill_cache()
|
||||
flora_poller.parameter_value(MI_LIGHT)
|
||||
flora['firmware'] = flora_poller.firmware_version()
|
||||
except IOError:
|
||||
print_line('Failed to retrieve data from Mi Flora sensor "{}" ({}) during initial connection.'.format(name_pretty, mac), error=True, sd_notify=True)
|
||||
print_line('Initial connection to Mi Flora sensor "{}" ({}) failed.'.format(name_pretty, mac), error=True, sd_notify=True)
|
||||
else:
|
||||
print('Internal name: "{}"'.format(name_clean))
|
||||
print('Device name: "{}"'.format(flora_poller.name()))
|
||||
|
@ -183,6 +199,11 @@ for [name, mac] in config['Sensors'].items():
|
|||
print()
|
||||
flores[name_clean] = flora
|
||||
|
||||
# openHAB items generation
|
||||
if parse_args.gen_openhab:
|
||||
flores_to_openhab_items(flores, reporting_mode)
|
||||
sys.exit(0)
|
||||
|
||||
# Discovery Announcement
|
||||
if reporting_mode == 'mqtt-json':
|
||||
print_line('Announcing Mi Flora devices to MQTT broker for auto-discovery ...')
|
||||
|
@ -206,7 +227,7 @@ elif reporting_mode == 'mqtt-homie':
|
|||
|
||||
for [flora_name, flora] in flores.items():
|
||||
topic_path = '{}/{}/{}'.format(base_topic, device_id, flora_name)
|
||||
mqtt_client.publish('{}/$name'.format(topic_path), flora['pretty'], 1, True)
|
||||
mqtt_client.publish('{}/$name'.format(topic_path), flora['name_pretty'], 1, True)
|
||||
mqtt_client.publish('{}/$type'.format(topic_path), 'miflora', 1, True)
|
||||
mqtt_client.publish('{}/$properties'.format(topic_path), 'battery,conductivity,light,moisture,temperature', 1, True)
|
||||
mqtt_client.publish('{}/battery/$settable'.format(topic_path), 'false', 1, True)
|
||||
|
@ -234,7 +255,6 @@ elif reporting_mode == 'mqtt-homie':
|
|||
|
||||
print_line('Initialization complete, starting MQTT publish loop', console=False, sd_notify=True)
|
||||
|
||||
flores_to_openhab_items(flores, reporting_mode)
|
||||
|
||||
# Sensor data retrieval and publication
|
||||
while True:
|
||||
|
@ -244,7 +264,7 @@ while True:
|
|||
flora['poller']._cache = None
|
||||
flora['poller']._last_read = None
|
||||
flora['stats']['count'] = flora['stats']['count'] + 1
|
||||
print_line('Retrieving data from sensor "{}" ...'.format(flora['pretty']))
|
||||
print_line('Retrieving data from sensor "{}" ...'.format(flora['name_pretty']))
|
||||
while attempts != 0 and not flora['poller']._cache:
|
||||
try:
|
||||
flora['poller'].fill_cache()
|
||||
|
@ -259,7 +279,7 @@ while True:
|
|||
if not flora['poller']._cache:
|
||||
flora['stats']['failure'] = flora['stats']['failure'] + 1
|
||||
print_line('Failed to retrieve data from Mi Flora sensor "{}" ({}), success rate: {:.0%}'.format(
|
||||
flora['pretty'], flora['mac'], flora['stats']['success']/flora['stats']['count']
|
||||
flora['name_pretty'], flora['mac'], flora['stats']['success']/flora['stats']['count']
|
||||
), error = True, sd_notify = True)
|
||||
print()
|
||||
continue
|
||||
|
@ -282,7 +302,7 @@ while True:
|
|||
elif reporting_mode == 'json':
|
||||
data['timestamp'] = strftime('%Y-%m-%d %H:%M:%S', localtime())
|
||||
data['name'] = flora_name
|
||||
data['pretty_name'] = flora['pretty']
|
||||
data['name_pretty'] = flora['name_pretty']
|
||||
data['mac'] = flora['mac']
|
||||
data['firmware'] = flora['firmware']
|
||||
print('Data for "{}": {}'.format(flora_name, json.dumps(data)))
|
||||
|
|
Loading…
Reference in New Issue