forked from TonyApuzzo/amridm2mqtt
-
Notifications
You must be signed in to change notification settings - Fork 1
/
amrscm2mqtt
executable file
·98 lines (83 loc) · 3.1 KB
/
amrscm2mqtt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
#!/usr/bin/env python3
'''
Runs rtlamr to watch for SDM broadcasts from utility meters. If the meter id
is in the list, usage is sent to an MQTT broker under a
'meters/{meter id}/reading' topic. All required settings should be specified in
settings.py.
'''
import backoff
import os
import paho.mqtt.client as mqtt
import settings
import signal
import ssl
import subprocess
import sys
import time
# Use signal to shut down gracefully
def shutdown(signum, frame):
print('shutting down')
rtlamr.terminate()
@backoff.on_exception(
backoff.expo,
(ConnectionRefusedError, ssl.SSLEOFError),
max_time=300)
def try_reconnect(client):
print('attempting to reconnect to broker...')
client.reconnect()
print('reconnected successfully')
signal.signal(signal.SIGTERM, shutdown)
# Start the rtl_tcp program
with subprocess.Popen([settings.RTL_TCP + " > /dev/null 2>&1 &"], shell=True, stdin=None, stdout=None, stderr=None, close_fds=True):
time.sleep(10)
print('started rtl_tcp')
# Start the rtlamr program
filter_ids = ','.join(map(str, settings.WATCHED_METERS))
rtlamr_cmd = [settings.RTLAMR, '-msgtype=scm', '-format=csv', '-filterid=' + filter_ids, '-symbollength=' + str(settings.SYMBOL_LENGTH)]
with subprocess.Popen(rtlamr_cmd, stdout=subprocess.PIPE, universal_newlines=True) as rtlamr:
print('started rtlamr')
# Configure the MQTT client
client = mqtt.Client('rtlamr')
if settings.MQTT_TLS:
client.tls_set()
if len(settings.MQTT_USER) and len(settings.MQTT_PASSWORD):
client.username_pw_set(settings.MQTT_USER, settings.MQTT_PASSWORD)
client.will_set('meters/availability', 'offline', retain=True)
# Connect to the broker
print('connecting to the broker...')
client.connect(settings.MQTT_HOST, settings.MQTT_PORT)
client.loop_start()
client.publish('meters/availability', 'online', retain=True)
print('connected successfully')
# Process messages in a loop
try:
last_readings = {}
for line in rtlamr.stdout:
fields = line.strip().split(',')
# Valid SCM results have 9 fields
if len(fields) != 9:
continue
# Parse the results
meter_id = int(fields[3])
reading = int(fields[7])
# Don't send a reading if the value hasn't changed
if meter_id in last_readings.keys() and reading == last_readings[meter_id]:
continue
print('sending meter {} reading: {}'.format(meter_id, reading))
result = client.publish('meters/{}/reading'.format(meter_id), reading, retain=True)
if result.rc == mqtt.MQTT_ERR_SUCCESS:
last_readings[meter_id] = reading
elif result.rc == mqtt.MQTT_ERR_NO_CONN:
try_reconnect(client)
# If the broker restarted, LWT may have taken effect and marked us as offline
client.publish('meters/availability', 'online', retain=True)
except:
print(sys.exc_info())
raise
finally:
# Clean up after rtlamr exits
print('disconnecting from broker...')
result = client.publish('meters/availability', 'offline', retain=True)
result.wait_for_publish()
client.disconnect()
client.loop_stop()