diff --git a/meter_reader/.DS_Store b/meter_reader/.DS_Store new file mode 100644 index 0000000..5008ddf Binary files /dev/null and b/meter_reader/.DS_Store differ diff --git a/meter_reader/Dockerfile b/meter_reader/Dockerfile new file mode 100644 index 0000000..7fafa06 --- /dev/null +++ b/meter_reader/Dockerfile @@ -0,0 +1,22 @@ +ARG BUILD_FROM +FROM $BUILD_FROM + +# Add env +ENV LANG C.UTF-8 + +RUN apk add --no-cache curl +RUN apk add --no-cache python3 +RUN apk add --no-cache nano + +# install .py script dependency +RUN pip3 install boto3 +RUN pip3 install requests +RUN pip3 install paho-mqtt + +# Copy data for add-on +COPY ocr_aws.py / +COPY run.sh / + +RUN chmod a+x /run.sh + +CMD [ "/run.sh" ] diff --git a/meter_reader/README.md b/meter_reader/README.md new file mode 100644 index 0000000..d77a048 --- /dev/null +++ b/meter_reader/README.md @@ -0,0 +1,28 @@ +# Meter Reader + +Put a webcam in front of your utility meter and AWS Rekognition sends the reading over MQTT to a MQTT server of your choice. + +note: if you restart the addon you will need to update the baseline value. + + +## Config + +upd_interval: value in seconds to be used as interval between data refreshes. + +url: where to find the picture of your meter (webcam) - it will be stored temporarily in folder /config/www + +user: basic authentication for cam + +password: password to obtain webcam picture + +baseline: current reading of your meter + +under: max you expect it to go down during an update interval (where I live solar panels allow meters to run backwards) + +over: max you expect it to go up during an update interval + +aws_access_key_id, aws_secret_access_key, region: see link on how to set up an AWS account. Did not really see how to get your region, I use "us-east-2" (https://docs.aws.amazon.com/rekognition/latest/dg/setting-up.html) + +mqtt_host, mqtt_port, mqtt_user, mqtt_pwd: find and get access to your MQTT server + +mqtt_topic: topic on which meter reading gets published diff --git a/meter_reader/config.json b/meter_reader/config.json new file mode 100644 index 0000000..082b091 --- /dev/null +++ b/meter_reader/config.json @@ -0,0 +1,14 @@ +{ + "name": "Meter reader", + "version": "0.1.13", + "slug": "meterreader", + "homeassistant_api": "no", + "description": "Put a webcam in front of your utility meter and let AWS Rekognition push the reading over mqtt", + "url": "https://github.com/bvlaicu/home-assistant-addons/tree/master/meter_reader", + "startup": "application", + "boot": "auto", + "arch": ["aarch64", "amd64", "armv7", "i386"], + "options": {"upd_interval":1800,"url": "127.0.0.1", "user": null, "password": null, "baseline": 1, "under": 1, "over": 1, "aws_access_key_id": null, "aws_secret_access_key": null, "region": null, "mqtt_host":"127.0.0.1", "mqtt_port":1883, "mqtt_user":"mqtt", "mqtt_pwd":"mqtt_pwd","mqtt_topic":"home/meter"}, + "schema": {"upd_interval": "int", "url":"str", "user":"str", "password":"str", "baseline":"int", "under":"int", "over":"int", "aws_access_key_id": "str", "aws_secret_access_key": "str", "region": "str", "mqtt_host":"str", "mqtt_port":"int", "mqtt_user":"str", "mqtt_pwd":"str","mqtt_topic":"str"}, + "map": ["config:rw"] +} \ No newline at end of file diff --git a/meter_reader/icon.png b/meter_reader/icon.png new file mode 100644 index 0000000..134efd8 Binary files /dev/null and b/meter_reader/icon.png differ diff --git a/meter_reader/logo.png b/meter_reader/logo.png new file mode 100644 index 0000000..134efd8 Binary files /dev/null and b/meter_reader/logo.png differ diff --git a/meter_reader/ocr_aws.py b/meter_reader/ocr_aws.py new file mode 100644 index 0000000..17794df --- /dev/null +++ b/meter_reader/ocr_aws.py @@ -0,0 +1,135 @@ +import boto3 +import paho.mqtt.client as mqtt +import requests +import json +import time +import os +import re +import sys + +config_json = json.loads(open("/data/options.json").read()) + +path = "/root/.aws" +reading = "" + +try: + os.mkdir(path) +except OSError: + print("Creation of the directory %s failed" % path) +else: + print("Successfully created the directory %s " % path) + +f = open("/root/.aws/credentials", "w") +f.write("[default]") +f.write("\n") +f.write("aws_access_key_id = " + config_json["aws_access_key_id"]) +f.write("\n") +f.write("aws_secret_access_key = " + config_json["aws_secret_access_key"]) +f.close() +f = open("/root/.aws/config", "w") +f.write("[default]") +f.write("\n") +f.write("region = " + config_json["region"]) +f.close() + +baseline = int(config_json["baseline"]) +base_low = baseline - int(config_json["under"]) +base_up = baseline + int(config_json["over"]) + +client = boto3.client('rekognition') + + +def processor(base_low, baseline, base_up): + file = "/config/www/meter.jpg" + data_str = reader(file) + response = client.detect_text(Image={'Bytes': data_str}) + print(response) + print("") + found = (response['TextDetections']) + reading = "" + for item in found: + print("Tekst:", item['DetectedText']) + print("Type:", item['Type']) + print("Confidence", item['Confidence']) + print("Id", item['Id']) + if item['Type'] != 'LINE': + print("ParentId:", item['ParentId']) + if (item['Type'] == 'WORD') or (item['Type'] == 'LINE'): + print("value conversion") + temp = item['DetectedText'] + print("x", temp, "x") + temp = re.sub("[^0-9]", "", temp) + print("x", temp, "x") + nw = 0 + try: + nw = int(temp) + except Exception: + print("Conversion failed") + pass + print("Nw: ", nw) + if (base_low <= nw and nw <= base_up): + print("erg waarschijnlijk juist:") + reading = temp + print(temp) + print(base_low, nw, base_up) + print("-----------") + return reading + + +def reader(file): + with open(file, "rb") as image_file: + encoded_string = image_file.read() + return encoded_string + + +def takereading(): + # download the url contents in binary format + r = requests.get(config_json["url"], auth=(config_json["user"], config_json["password"])) + # open method to open a file on your system and write the contents + with open("/config/www/meter.jpg", "wb") as code: + code.write(r.content) + print("File downloaded") + + +def publishMQTT(reading): + mqttc = mqtt.Client("meter_reader") + mqttc.username_pw_set(username=config_json["mqtt_user"], password=config_json["mqtt_pwd"]) + mqttc.connect(config_json["mqtt_host"], int(config_json["mqtt_port"])) + mqttc.publish(config_json["mqtt_topic"], reading, retain=True) + mqttc.disconnect() + + +def publishhiloMQTT(low, high): + mqttc = mqtt.Client("meter_reader") + mqttc.username_pw_set(username=config_json["mqtt_user"], password=config_json["mqtt_pwd"]) + mqttc.connect(config_json["mqtt_host"], int(config_json["mqtt_port"])) + mqttc.publish("home/band/low", str(low), retain=True) + mqttc.publish("home/band/high", str(high), retain=True) + mqttc.disconnect() + + +if __name__ == '__main__': + baseline = int(config_json["baseline"]) + base_low = baseline - int(config_json["under"]) + base_up = baseline + int(config_json["over"]) + while True: + print("Start") + try: + takereading() + print("foto action finished") + reading = processor(base_low, baseline, base_up) + if reading != "": + print("Reading = ok = ", reading) + baseline = int(reading) + base_low = baseline - int(config_json["under"]) + base_up = baseline + int(config_json["over"]) + publishMQTT(reading) + publishhiloMQTT(base_low, base_up) + else: + base_low = base_low - int(config_json["under"]) + base_up = base_up + int(config_json["over"]) + publishhiloMQTT(base_low, base_up) + except Exception: + print("Unexpected error:", sys.exc_info()) + print("OCR failed. Will try again later.") + time.sleep(int(config_json["upd_interval"])) diff --git a/meter_reader/run.sh b/meter_reader/run.sh new file mode 100644 index 0000000..1ed314c --- /dev/null +++ b/meter_reader/run.sh @@ -0,0 +1,5 @@ +#!/bin/bash +set -e + +python3 ocr_aws.py +