This commit is contained in:
Dan Ponte 2023-01-30 17:31:31 +00:00
commit 71e70571d9
6 changed files with 212 additions and 0 deletions

11
README.md Normal file
View File

@ -0,0 +1,11 @@
This is the aquastat program. It uses ssocr to read a boiler's water temperature display and publish to MQTT.
Tested on FreeBSD. Requires raspberrypi-userland (for libbcm_host.so)
1. Create a venv.
2. Enter it.
3. `pip install -r requirements.txt`
4. Install the aquastat script to rc.d.
5. `aquastat_enable="YES"`
6. Make sure `ssocr` is installed.
7. Edit `aquastat.yaml`.

56
aquastat Executable file
View File

@ -0,0 +1,56 @@
#!/bin/sh
# PROVIDE: aquastat
# REQUIRE: NETWORKING
#
# Add the following lines to /etc/rc.conf.local or /etc/rc.conf
# to enable this service:
#
# aquastat (bool): Set to NO by default.
# Set it to YES to enable aquastat.
#
. /etc/rc.subr
name="aquastat"
rcvar=aquastat_enable
pidfile=/var/run/aquastat.pid
procname=python3
start_cmd=aquastat_start
stop_cmd=aquastat_stop
status_cmd=aquastat_status
command="/home/dponte/aquastat/start.sh"
aquastat_start() {
echo "Starting ${name}."
/usr/sbin/daemon -p ${pidfile} -f -u dponte $command
}
aquastat_stop() {
if [ -e "${pidfile}" ]; then
echo "Stopping ${name}."
kill -s TERM `cat ${pidfile}`
else
echo "${name} is not running"
fi
}
aquastat_status() {
if [ -e "${pidfile}" ]; then
echo "${name} is running as pid `cat ${pidfile}`"
else
echo "${name} is not running"
fi
}
load_rc_config $name
: ${aquastat_enable="NO"}
: ${aquastat_user="dponte"}
: ${aquastat_group="dponte"}
run_rc_command "$1"

114
aquastat.py Normal file
View File

@ -0,0 +1,114 @@
#!/usr/bin/env python3.10
class Config:
def __init__(self, configfile):
with open(configfile, 'r') as f:
conf = yaml.safe_load(f)
self.outfile = conf["outfile"]
self.broker = conf["mqtt"]["broker"]
self.port = conf["mqtt"]["port"]
self.username = conf["mqtt"]["username"]
self.password = conf["mqtt"]["password"]
self.temptopic = conf["topic"]["temp"]
self.lwttopic = conf["topic"]["lwt"]
self.thresh = conf["threshold"]
self.coords = conf["coords"]
self.sleepfast = conf["sleep"]["fast"]
self.sleepmedium = conf["sleep"]["medium"]
self.sleepslow = conf["sleep"]["slow"]
self.fastthresh = conf["fastThreshold"]
self.slowthresh = conf["slowThreshold"]
pass
from time import sleep
from picamera import PiCamera
import subprocess
import signal, os
import sys
import paho.mqtt.client as mqtt
import yaml
cfg = Config('/usr/local/etc/aquastat.yaml')
SSOCR_ARGS = ['ssocr', '--number-digits=-1', '--foreground=white', '--background=black', '-t', str(cfg.thresh), 'crop', str(cfg.coords[0]), str(cfg.coords[1]), str(cfg.coords[2]), str(cfg.coords[3]), cfg.outfile]
def mysleep(t):
# print("sleeping for {}".format(t))
sleep(t)
def on_connect(client, userdata, flags, rc):
client.publish(cfg.lwttopic, "Online")
pass
# The callback for when a PUBLISH message is received from the server.
def on_message(client, userdata, msg):
pass
def handler(signum, frame):
client.publish(cfg.lwttopic, "Offline")
sys.exit(0)
signal.signal(signal.SIGTERM, handler)
signal.signal(signal.SIGINT, handler)
client = mqtt.Client()
client.on_connect = on_connect
client.on_message = on_message
client.username_pw_set(cfg.username, cfg.password)
client.connect(cfg.broker, cfg.port, 60)
client.loop_start()
camera = PiCamera()
camera.resolution = (1024, 768)
mysleep(2)
temp = 0
prev_temp = 0
offline = False
sleep_count = 0
fast = False
slow = False
while True:
try:
prev_temp = temp
camera.capture(cfg.outfile, resize=(800, 600))
result = subprocess.run(SSOCR_ARGS, stdout=subprocess.PIPE)
if result.returncode != 0:
client.publish(cfg.lwttopic, "Offline")
offline = True
continue
if offline:
offline = False
client.publish(cfg.lwttopic, "Online")
temp = int(result.stdout.decode('utf-8'))
if temp > 200 or temp < 50:
mysleep(cfg.sleepfast)
continue
if temp != prev_temp:
client.publish(cfg.temptopic, temp)
sleep_count = 0
fast = True
slow = False
mysleep(cfg.sleepfast)
else:
if fast and sleep_count > cfg.fastthresh:
fast = False
sleep_count = 0
mysleep(cfg.sleepmedium)
elif slow or sleep_count > cfg.slowthresh:
sleep_count = 0
slow = True
mysleep(cfg.sleepslow)
else:
sleep_count += 1
if fast:
mysleep(cfg.sleepfast)
else:
mysleep(cfg.sleepmedium)
except:
mysleep(cfg.sleepfast)
continue

23
aquastat.yaml.sample Normal file
View File

@ -0,0 +1,23 @@
outfile: /tmp/cam.jpg
mqtt:
broker: xenon-iot
port: 1883
username: aquastat
password: "password"
topic:
temp: "aquastat/WATERTEMP"
lwt: "aquastat/LWT"
sleep:
fast: 1
medium: 5
slow: 15
fastThreshold: 3
slowThreshold: 10
threshold: 75
coords:
- 360
- 170
- 162
- 109

4
requirements.txt Normal file
View File

@ -0,0 +1,4 @@
colorzero==2.0
paho-mqtt==1.6.1
picamera==1.13
PyYAML==6.0

4
start.sh Executable file
View File

@ -0,0 +1,4 @@
#!/bin/sh
. /home/dponte/env/bin/activate
exec python3 /home/dponte/aquastat/aquastat.py