Files
aaisp2mqtt/aaisp-to-mqtt.py

165 lines
5.8 KiB
Python
Executable File

#!/usr/bin/env python
import os
import sys
import logging
import json
import time
import configparser
import paho.mqtt.client as mqtt
import humanfriendly
import requests
import argparse
LOG = logging.getLogger(__name__)
VERSION = 0.2
AAISP_INFO_URL = 'https://chaos2.aa.net.uk/broadband/info'
def main():
logging.basicConfig(level=logging.INFO,
format='%(levelname)8s [%(asctime)s] %(message)s')
if len(sys.argv) > 1:
cfgfile = sys.argv[1]
# load the config
config = configparser.ConfigParser()
config.read(cfgfile)
# check it has the correct sections
for section in ['aaisp', 'mqtt']:
if section not in config.sections():
LOG.fatal('%s section not found in config file %s',
section, cfgfile)
aaisp_username = config.get('aaisp', 'username')
aaisp_password = config.get('aaisp', 'password')
mqtt_broker = config.get('mqtt', 'broker')
mqtt_port = int(config.get('mqtt', 'port', fallback='1883'))
mqtt_username = config.get('mqtt', 'username', fallback=None)
mqtt_password = config.get('mqtt', 'password', fallback=None)
mqtt_topic_prefix = config.get('mqtt', 'topic_prefix', fallback='aaisp')
else:
# Use the environment
aaisp_username = os.environ.get('AAISP_USERNAME')
aaisp_password = os.environ.get('AAISP_PASSWORD')
mqtt_broker = os.environ.get('MQTT_BROKER') or 'localhost'
mqtt_port = int(os.environ.get('MQTT_PORT') or '1883')
mqtt_username = os.environ.get('MQTT_USERNAME')
mqtt_password = os.environ.get('MQTT_PASSWORD')
mqtt_topic_prefix = os.environ.get('MQTT_TOPIC_PREFIX') or 'aaisp'
if aaisp_username is None or aaisp_password is None:
LOG.fatal('Username or Password missing for AAISP')
sys.exit(1)
# attempt to get details from aaisp
LOG.info('Connecting to AAISP CHAOSv2 endpoint')
response = requests.get(AAISP_INFO_URL, params={
'control_login': aaisp_username,
'control_password': aaisp_password
})
if not response.status_code == requests.codes.ok:
LOG.error('Error connecting to AAISP CHAOSv2 endpoint: %s' % response.body)
sys.exit(1)
data = response.json()
if 'info' not in data:
LOG.fatal('info section not found in AAISP CHAOSv2 response')
sys.exit(1)
circuits = data['info']
LOG.info('Got %s circuits', len(circuits))
if len(circuits) == 0:
LOG.fatal('No circuits returned from AAISP CHAOSv2')
# work out unique line IDs and logins
logins = []
lines = []
for circuit in circuits:
if circuit['login'] not in logins:
logins.append(circuit['login'])
if circuit['ID'] not in lines:
lines.append(circuit['ID'])
LOG.info('* Lines: %s', ', '.join(lines))
LOG.info('* Logins: %s', ', '.join(logins))
# connect to the broker
LOG.info('Connecting to MQTT broker %s:%s', mqtt_broker, mqtt_port)
client = mqtt.Client()
# do auth?
if mqtt_username is not None and mqtt_password is not None:
client.username_pw_set(mqtt_username, mqtt_password)
client.max_inflight_messages_set(100)
try:
client.connect(mqtt_broker, mqtt_port, 60)
except Exception:
LOG.exception('Error connecting to MQTT')
sys.exit(1)
LOG.info('Connected OK to MQTT')
# version and indexes
publish(client=client, topic='%s/$version' %
(mqtt_topic_prefix), payload=VERSION)
publish(client=client, topic='%s/$lines' %
(mqtt_topic_prefix), payload=','.join(lines))
publish(client=client, topic='%s/$logins' %
(mqtt_topic_prefix), payload=','.join(logins))
LOG.info('Published version and index messages')
# publish per circuit
for circuit in circuits:
publish_per_circuit(client=client, circuit=circuit,
mqtt_topic_prefix=mqtt_topic_prefix)
LOG.info('Published details for %s circuits', len(circuits))
# disconnect
LOG.info('Disconnecting from MQTT')
client.disconnect()
sys.exit(0)
def publish_per_circuit(client, circuit, mqtt_topic_prefix):
quota_remaining = int(circuit['quota_remaining'])
quota_remaining_gb = quota_remaining / 1000000000
quota_monthly = int(circuit['quota_monthly'])
quota_monthly_gb = quota_monthly / 1000000000
up = float(circuit['rx_rate'])
up_mb = round(up / 1000000, 2)
down = float(circuit['tx_rate'])
down_mb = round(down / 1000000, 2)
# line_prefix = '%s/line/%s' % (mqtt_topic_prefix, circuit['ID'])
login_prefix = '%s/login/%s' % (mqtt_topic_prefix, circuit['login'])
for prefix in [login_prefix]: # , line_prefix]:
for metric in [
('quota/remaining', quota_remaining),
('quota/remaining/gb', quota_remaining_gb),
('quota/remaining/human', humanfriendly.format_size(quota_remaining)),
('quota/monthly', quota_monthly),
('quota/monthly/gb', quota_monthly_gb),
('quota/monthly/human', humanfriendly.format_size(quota_monthly)),
('syncrate/up', up),
('syncrate/up/mb', up_mb),
('syncrate/up/human', humanfriendly.format_size(up)),
('syncrate/down', down),
('syncrate/down/mb', down_mb),
('syncrate/down/human', humanfriendly.format_size(down)),
('postcode', str(circuit['postcode'].strip()))
]:
topic = '%s/%s' % (prefix, metric[0])
publish(client=client, topic=topic, payload=metric[1])
return
def publish(client, topic, payload):
time.sleep(0.1)
result = client.publish(topic=topic, payload=payload, qos=1)
if result[0] != 0:
LOG.fail('MQTT publish failure: %s %s', topic, payload)
if __name__ == '__main__':
main()