From 2897b0e6ee55fefc7243c04b18dda2832eb53589 Mon Sep 17 00:00:00 2001 From: Maxime Boissonneault Date: Thu, 20 Oct 2022 17:06:40 -0400 Subject: [PATCH 1/3] convert water leak info to 1/0 --- app.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/app.py b/app.py index 8e93006..21829ad 100644 --- a/app.py +++ b/app.py @@ -57,6 +57,11 @@ def metrics(): attrib["currentValue"] = 1 else: attrib["currentValue"] = 0 + if attrib["name"] == "water": + if attrib["currentValue"] == "dry": + attrib["currentValue"] = 1 + else: + attrib["currentValue"] = 0 if attrib["name"] == "power": if attrib["currentValue"] == "on": attrib["currentValue"] = 1 From d9348fb75c5b2c9a9e8e80ae8e1ddf8dbd41ac24 Mon Sep 17 00:00:00 2001 From: Maxime Boissonneault Date: Thu, 20 Oct 2022 17:28:43 -0400 Subject: [PATCH 2/3] added more details, based on TheChrisTech --- app.py | 20 ++++++++++++++------ templates/base.txt | 2 +- 2 files changed, 15 insertions(+), 7 deletions(-) diff --git a/app.py b/app.py index 21829ad..def6d09 100644 --- a/app.py +++ b/app.py @@ -2,6 +2,7 @@ import os import requests import time +import re from flask import render_template, Flask, make_response, Response @@ -70,13 +71,20 @@ def metrics(): else: attrib["currentValue"] = attrib["currentValue"] - # Sanitise the device name as it will appear in the label - device_name = device_details['label'].lower().replace(' ','_').replace('-','_') - # Sanitise the metric name - metric_name = attrib['name'].lower().replace(' ','_').replace('-','_') + # Sanitize to allow Prometheus Ingestion + device_name = sanitize(device_details['name']) + device_label = sanitize(device_details['label']) + device_human_label = device_details['label'] + device_type = sanitize(device_details['type']) + device_id = sanitize(device_details['id']) + metric_name = sanitize(attrib['name']) # Create the dict that holds the data device_attributes.append({ "device_name": f"{device_name}", + "device_label": f"{device_label}", + "device_human_label": f"{device_human_label}", + "device_type": f"{device_type}", + "device_id": f"{device_id}", "metric_name": f"{metric_name}", "metric_value": f"{attrib['currentValue']}", "metric_timestamp": time.time()}) @@ -88,5 +96,5 @@ def metrics(): response.mimetype = "text/plain" return response - - +def sanitize(inputValue): + return re.sub('[^a-z0-9]+', '_', inputValue.lower()) diff --git a/templates/base.txt b/templates/base.txt index 58a99f9..5876661 100644 --- a/templates/base.txt +++ b/templates/base.txt @@ -1,3 +1,3 @@ {% for device in device_details %} -{{ device["metric_name"] }}{device_name="{{device['device_name']}}"} {{ device["metric_value"] }} +{{ device["metric_name"] }}{device_name="{{device['device_name']}}",device_human_label="{{device['device_human_label']}}",device_label="{{device['device_label']}}",device_type="{{device['device_type']}}",device_id="{{device['device_id']}}"} {{ device["metric_value"] }} {% endfor %} From 7bbab0dfb7e5c962bba137bde1c9542a1d986066 Mon Sep 17 00:00:00 2001 From: Maxime Boissonneault Date: Thu, 20 Oct 2022 17:42:43 -0400 Subject: [PATCH 3/3] highly speed up the check by querying the maker API just once for all devices --- app.py | 110 ++++++++++++++++++++++++++++++--------------------------- 1 file changed, 58 insertions(+), 52 deletions(-) diff --git a/app.py b/app.py index def6d09..8d5204f 100644 --- a/app.py +++ b/app.py @@ -18,14 +18,19 @@ except KeyError as e: print(f"Could not read the environment variable - {e}") +def get_all_devices(): + return requests.get(f"{base_uri}/all?access_token={access_token}") + def get_devices(): return requests.get(f"{base_uri}?access_token={access_token}") @app.route("/info") def info(): + result = get_devices() res = { "status": { - "CONNECTION": "ONLINE" if get_devices().status_code == 200 else "OFFLINE" + "CONNECTION": "ONLINE" if result.status_code == 200 else "OFFLINE", + "CODE": result.status_code }, "config": { "HE_URI": base_uri, @@ -42,58 +47,59 @@ def info(): @app.route("/metrics") def metrics(): - devices = get_devices() - device_attributes = [] - - for device in devices.json(): - device_details = requests.get(f"{base_uri}/{device['id']}?access_token={access_token}").json() - for attrib in device_details['attributes']: - # Is this a metric we should be collecting? - if attrib["name"] in collected_metrics: - # Does it have a "proper" value? - if attrib["currentValue"] is not None: - # If it's a switch, then change from text to binary values - if attrib["name"] == "switch": - if attrib["currentValue"] == "on": - attrib["currentValue"] = 1 - else: - attrib["currentValue"] = 0 - if attrib["name"] == "water": - if attrib["currentValue"] == "dry": - attrib["currentValue"] = 1 - else: - attrib["currentValue"] = 0 - if attrib["name"] == "power": - if attrib["currentValue"] == "on": - attrib["currentValue"] = 1 - elif attrib["currentValue"] == "off": - attrib["currentValue"] = 0 - else: - attrib["currentValue"] = attrib["currentValue"] + devices = get_all_devices() + if devices.status_code == 200: + device_attributes = [] - # Sanitize to allow Prometheus Ingestion - device_name = sanitize(device_details['name']) - device_label = sanitize(device_details['label']) - device_human_label = device_details['label'] - device_type = sanitize(device_details['type']) - device_id = sanitize(device_details['id']) - metric_name = sanitize(attrib['name']) - # Create the dict that holds the data - device_attributes.append({ - "device_name": f"{device_name}", - "device_label": f"{device_label}", - "device_human_label": f"{device_human_label}", - "device_type": f"{device_type}", - "device_id": f"{device_id}", - "metric_name": f"{metric_name}", - "metric_value": f"{attrib['currentValue']}", - "metric_timestamp": time.time()}) - # Create the response - response = make_response(render_template('base.txt', - device_details=device_attributes - )) - # Make sure we return plain text otherwise Prometheus complains - response.mimetype = "text/plain" + for device in devices.json(): + for attrib in device['attributes']: + # Is this a metric we should be collecting? + if attrib in collected_metrics: + value = device['attributes'][attrib] + # Does it have a "proper" value? + if value is not None: + # If it's a switch, then change from text to binary values + if attrib == "switch": + if value == "on": + value = 1 + else: + value = 0 + if attrib == "water": + if value == "dry": + value = 1 + else: + value = 0 + if attrib == "power": + if value == "on": + value = 1 + elif value == "off": + value = 0 + + # Sanitize to allow Prometheus Ingestion + device_name = sanitize(device['name']) + device_label = sanitize(device['label']) + device_human_label = device['label'] + device_type = sanitize(device['type']) + device_id = sanitize(device['id']) + metric_name = sanitize(attrib) + # Create the dict that holds the data + device_attributes.append({ + "device_name": f"{device_name}", + "device_label": f"{device_label}", + "device_human_label": f"{device_human_label}", + "device_type": f"{device_type}", + "device_id": f"{device_id}", + "metric_name": f"{metric_name}", + "metric_value": f"{value}", + "metric_timestamp": time.time()}) + # Create the response + response = make_response(render_template('base.txt', + device_details=device_attributes + )) + # Make sure we return plain text otherwise Prometheus complains + response.mimetype = "text/plain" + else: + response = devices return response def sanitize(inputValue):