From 208d7d05d7a6aa25cc449e8c39578fec43d4237f Mon Sep 17 00:00:00 2001 From: Jarno Rankinen Date: Wed, 15 Mar 2023 21:17:59 +0200 Subject: [PATCH] Moved Python stuff to dedicated repo, preparing to clean up packagenaming --- enervent-ctrl-python/.gitignore | 6 - enervent-ctrl-python/html/coils/index.html | 1 - enervent-ctrl-python/html/index.html | 16 -- .../html/registers/index.html | 1 - .../html/static/tabledata.css | 17 -- enervent-ctrl-python/html/static/tabledata.js | 52 ----- enervent-ctrl-python/nginx-server-block.conf | 59 ------ enervent-ctrl-python/pyvenv.cfg | 3 - enervent-ctrl-python/requirements.txt | 11 - enervent-ctrl-python/src/PingvinKL.py | 188 ------------------ enervent-ctrl-python/src/enervent-logger.py | 47 ----- 11 files changed, 401 deletions(-) delete mode 100644 enervent-ctrl-python/.gitignore delete mode 120000 enervent-ctrl-python/html/coils/index.html delete mode 100644 enervent-ctrl-python/html/index.html delete mode 120000 enervent-ctrl-python/html/registers/index.html delete mode 100644 enervent-ctrl-python/html/static/tabledata.css delete mode 100644 enervent-ctrl-python/html/static/tabledata.js delete mode 100644 enervent-ctrl-python/nginx-server-block.conf delete mode 100644 enervent-ctrl-python/pyvenv.cfg delete mode 100644 enervent-ctrl-python/requirements.txt delete mode 100644 enervent-ctrl-python/src/PingvinKL.py delete mode 100755 enervent-ctrl-python/src/enervent-logger.py diff --git a/enervent-ctrl-python/.gitignore b/enervent-ctrl-python/.gitignore deleted file mode 100644 index 9e4c91c..0000000 --- a/enervent-ctrl-python/.gitignore +++ /dev/null @@ -1,6 +0,0 @@ -*/__pycache__/ -bin/ -include/ -lib/ -lib64 -share/ diff --git a/enervent-ctrl-python/html/coils/index.html b/enervent-ctrl-python/html/coils/index.html deleted file mode 120000 index 79c5d6f..0000000 --- a/enervent-ctrl-python/html/coils/index.html +++ /dev/null @@ -1 +0,0 @@ -../index.html \ No newline at end of file diff --git a/enervent-ctrl-python/html/index.html b/enervent-ctrl-python/html/index.html deleted file mode 100644 index eeb0a81..0000000 --- a/enervent-ctrl-python/html/index.html +++ /dev/null @@ -1,16 +0,0 @@ - - - - - - - Enervent Pingvin Kotilämpö - - - - - - -
Coil values at
AddressValueSymbolDescription
- - \ No newline at end of file diff --git a/enervent-ctrl-python/html/registers/index.html b/enervent-ctrl-python/html/registers/index.html deleted file mode 120000 index 79c5d6f..0000000 --- a/enervent-ctrl-python/html/registers/index.html +++ /dev/null @@ -1 +0,0 @@ -../index.html \ No newline at end of file diff --git a/enervent-ctrl-python/html/static/tabledata.css b/enervent-ctrl-python/html/static/tabledata.css deleted file mode 100644 index 3f7a99f..0000000 --- a/enervent-ctrl-python/html/static/tabledata.css +++ /dev/null @@ -1,17 +0,0 @@ -.addr { - text-align: center; -} -.val { - text-align: center; -} -#data { - padding: 2pt; - border-collapse: collapse; -} -thead { - border-bottom: 1px solid; - text-align: left; -} -td { - padding: 2pt; -} \ No newline at end of file diff --git a/enervent-ctrl-python/html/static/tabledata.js b/enervent-ctrl-python/html/static/tabledata.js deleted file mode 100644 index 769fcf8..0000000 --- a/enervent-ctrl-python/html/static/tabledata.js +++ /dev/null @@ -1,52 +0,0 @@ -function zeroPad(number) { - return ("0" + number).slice(-2) -} -function getData() { - now = new Date() - Y = now.getFullYear() - m = now.getMonth() - d = now.getDate() - H = zeroPad(now.getHours()) - M = zeroPad(now.getMinutes()) - S = zeroPad(now.getSeconds()) - document.getElementById('time').innerHTML = `${Y}-${m}-${d} ${H}:${M}:${S}` - - error = false - // The same index.html is used for both coil and register data, - // change api url based on which we're looking at - if (document.location.pathname == "/coils/") { - url = "/api/v1/coils" - } - else if (document.location.pathname == "/registers/") { - url = "/api/v1/registers" - } - else { - document.getElementById("data").innerHTML = 'Page not found' - error = true - } - if (!error) { - // Fetch data from API - fetch(url) - .then((response) => { - if (!response.ok) { - throw new Error(`Error fetching data: ${response.status}`) - } - return response.json() - }) - .then((data) => { - // Populate table - document.getElementById('coildata').innerHTML = ""; - for (n=0; n${data[n].address}\ - ${Number(data[n].value)}\ - ${data[n].symbol}\ - ${data[n].description}` - document.getElementById('coildata').innerHTML += tablerow - } - }); - } - - // Using setTimeout instead of setInterval to avoid possible connection issues - // There's no need to update exactly every 5 seconds, the skew is fine - setTimeout(getData, 1*1000); -} \ No newline at end of file diff --git a/enervent-ctrl-python/nginx-server-block.conf b/enervent-ctrl-python/nginx-server-block.conf deleted file mode 100644 index 20a41c3..0000000 --- a/enervent-ctrl-python/nginx-server-block.conf +++ /dev/null @@ -1,59 +0,0 @@ -upstream enervent-ctrl { - server localhost:8888; -} - -server { - listen 80 default_server; - listen [::]:80 default_server; - - # SSL configuration - # - # listen 443 ssl default_server; - # listen [::]:443 ssl default_server; - # - # Note: You should disable gzip for SSL traffic. - # See: https://bugs.debian.org/773332 - # - # Read up on ssl_ciphers to ensure a secure configuration. - # See: https://bugs.debian.org/765782 - # - # Self signed certs generated by the ssl-cert package - # Don't use them in a production server! - # - # include snippets/snakeoil.conf; - - root /home/jarno/enervent-ctrl/enervent-ctrl-python/html; - index index.html; - - server_name _; - - location / { - # First attempt to serve request as file, then - # as directory, then fall back to displaying a 404. - if ($http_user_agent ~* "^curl") { - proxy_set_header X-Forwarded-Proto $scheme; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Host $host:$server_port; - proxy_set_header X-Forwarded-Port $server_port; - proxy_pass http://enervent-ctrl; - } - try_files $uri $uri/ =404; - } - - #location ~ /static|/coils|/registers { - # root /home/jarno/enervent-ctrl/enervent-ctrl-python/html; - #} - - location ~ /api { - proxy_set_header X-Forwarded-Proto $scheme; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Host $host:$server_port; - proxy_set_header X-Forwarded-Port $server_port; - proxy_pass http://enervent-ctrl; - } - - - - - -} diff --git a/enervent-ctrl-python/pyvenv.cfg b/enervent-ctrl-python/pyvenv.cfg deleted file mode 100644 index 5c102c1..0000000 --- a/enervent-ctrl-python/pyvenv.cfg +++ /dev/null @@ -1,3 +0,0 @@ -home = /usr/bin -include-system-site-packages = false -version = 3.9.2 diff --git a/enervent-ctrl-python/requirements.txt b/enervent-ctrl-python/requirements.txt deleted file mode 100644 index bff09ad..0000000 --- a/enervent-ctrl-python/requirements.txt +++ /dev/null @@ -1,11 +0,0 @@ -click==8.1.3 -Flask==2.2.2 -importlib-metadata==6.0.0 -itsdangerous==2.1.2 -Jinja2==3.1.2 -MarkupSafe==2.1.1 -minimalmodbus==2.0.1 -pyserial==3.5 -waitress==2.1.2 -Werkzeug==2.2.3 -zipp==3.11.0 diff --git a/enervent-ctrl-python/src/PingvinKL.py b/enervent-ctrl-python/src/PingvinKL.py deleted file mode 100644 index b67fe09..0000000 --- a/enervent-ctrl-python/src/PingvinKL.py +++ /dev/null @@ -1,188 +0,0 @@ -import minimalmodbus -import logging -from flask import jsonify -from threading import Lock -from time import sleep - -class PingvinCoil(): - """Single coil data structure""" - def __init__(self, symbol="-", description="-"): - self.symbol = symbol - self.value = False - self.description = description - self.reserved = symbol == "-" and description == "-" - - def serialize(self): - return { - "value": self.value, - "symbol": self.symbol, - "description": self.description, - "reserved": self.reserved - } - - def get(self): - return jsonify(self.serialize()) - - def flip(self): - self.value = not self.value - -class PingvinCoils(): - """Class for handling Modbus coils""" - ## coil descriptions and symbols courtesy of Ensto Enervent - ## https://doc.enervent.com/out/out.ViewDocument.php?documentid=59 - coils = [ - PingvinCoil("COIL_STOP", "Stop"), - PingvinCoil("COIL_AWAY", "Away mode"), - PingvinCoil("COIL_AWAY_L", "Away Long mode"), - PingvinCoil(), - PingvinCoil(), - PingvinCoil(), - PingvinCoil("COIL_MAX_H", "Max Heating"), - PingvinCoil("COIL_MAX_C", "Max Cooling"), - PingvinCoil("COIL_CO_BOOST_EN", "CO2 boost"), - PingvinCoil("COIL_RH_BOOST_EN", "Relative humidity boost"), - PingvinCoil("COIL_M_BOOST", "Manual boost 100%"), - PingvinCoil("COIL_TEMP_BOOST_EN", "Temperature boost"), - PingvinCoil("COIL_SNC", "Summer night cooling"), - PingvinCoil(), - PingvinCoil(), - PingvinCoil(), - PingvinCoil(), - PingvinCoil(), - PingvinCoil("COIL_AWAY_H", "Heating enabled/disabled in AWAY mode"), - PingvinCoil("COIL_AWAY_C", "Cooling enabled/disabled in AWAY mode"), - PingvinCoil(), - PingvinCoil(), - PingvinCoil(), - PingvinCoil(), - PingvinCoil(), - PingvinCoil(), - PingvinCoil(), - PingvinCoil(), - PingvinCoil(), - PingvinCoil(), - PingvinCoil("COIL_LTO_ON", "Heat recycler state (running=1, stopped = 0)"), - PingvinCoil(), - PingvinCoil("COIL_HEAT_ON", "After heater element state (On = 1, Off = 0)"), - PingvinCoil(), - PingvinCoil(), - PingvinCoil(), - PingvinCoil("COIL_TEMP_DECREASE", "Temperature decrease function"), - PingvinCoil("COIL_OVERTIME", "Programmatic equivalent of OVERTIME digital input"), - PingvinCoil(), - PingvinCoil(), - PingvinCoil("COIL_ECO_MODE", "Eco mode"), - PingvinCoil("COIL_ALARM_A", "Alarm of class A active"), - PingvinCoil("COIL_ALARM_B", "Alarm of class B active"), - PingvinCoil("COIL_CLK_PROG", "Clock program is currently active"), - PingvinCoil(), - PingvinCoil(), - PingvinCoil(), - PingvinCoil("COIL_SILENT_MODE", "Silent mode"), - PingvinCoil("COIL_STOP_SLP_COOLING", "Electrical heater cool-off function enabled when the machine has stopped"), - PingvinCoil("COIL_SERVICE_EN", "Service reminder"), - PingvinCoil(), - PingvinCoil(), - PingvinCoil("COIL_COOLING_EN", "Active cooling function enabled"), - PingvinCoil("COIL_LTO_EN", "N/A"), - PingvinCoil("COIL_HEATING_EN", "Active heating function enabled"), - PingvinCoil("COIL_LTO_DEFROST_EN", "HRC defrosting function enabled during winter season"), - PingvinCoil(), - PingvinCoil(), - PingvinCoil(), - PingvinCoil(), - PingvinCoil(), - PingvinCoil(), - PingvinCoil(), - PingvinCoil(), - PingvinCoil(), - PingvinCoil(), - PingvinCoil(), - PingvinCoil(), - PingvinCoil(), - PingvinCoil(), - PingvinCoil(), - PingvinCoil() - ] - - def __init__(self, device, semaphore, debug=False): - self.pingvin = device - self.semaphore = semaphore - - def __getitem__(self, item): - return self.coils[item] - - def update(self, debug=False): - """Fetch all coils values from device""" - self.pingvin.serial.timeout = 0.2 - self.pingvin.debug = debug - if debug: logging.info(f"{len(self.coils)} coils registered") - self.semaphore.acquire() - curvalues = self.pingvin.read_bits(0,len(self.coils),1) - self.semaphore.release() - for i, coil in enumerate(self.coils): - self.coils[i].value = bool(curvalues[i]) - if debug: logging.info("Coil values read succesfully\n") - - def fetchValue(self, address, debug=False): - """Update single coil value from device and return it""" - self.pingvin.debug = debug - if debug: logging.debug("Updating coil value from device to cache") - self.semaphore.acquire() - self.coils[address].value = bool(self.pingvin.read_bit(address, 1)) - self.semaphore.release() - return self.value(address, debug) - - def value(self, address, debug=False): - """Get single local coil value""" - if debug: logging.debug("Reading coil value from cache") - return self.coils[address].value - - def print(self, debug=False): - """Human-readable print of all coil values""" - coilvals = "" - for i, coil in enumerate(self.coils): - coilvals = coilvals + f"Coil {i : <{4}}{coil.value : <{2}} {coil.symbol : <{25}}{coil.description}\n" - return coilvals - - def serialize(self, include_reserved=False): - """Returns coil values as parseable Python object""" - coilvals = [] - for i, coil in enumerate(self.coils): - if not coil.reserved or include_reserved: - coil = coil.serialize() - coil['address'] = i - coilvals.append(coil) - return coilvals - - def get(self, include_reserved=False, live=False, debug=False): - """Return all coil values in JSON format""" - if live: self.update(debug) - return jsonify(self.serialize(include_reserved)) - - def write(self, address): - self.semaphore.acquire() - self.pingvin.write_bit(address, int(not self.coils[address].value)) - if self.pingvin.read_bit(address, 1) != self.coils[address].value: - self.coils[address].flip() - self.semaphore.release() - return True - self.semaphore.release() - return False - -class PingvinKL(): - """Class for communicating with an Enervent Pinvin Kotilämpö ventilation/heating unit""" - def __init__(self, serialdevice='/dev/ttyS0', modbusaddr=1, debug=False): - self.semaphore = Lock() - self.pingvin = minimalmodbus.Instrument(serialdevice, modbusaddr) - self.coils = PingvinCoils(self.pingvin, self.semaphore, debug) - self.run = False - - def monitor(self, interval=15, debug=False): - if not self.run: # Prevent starting two monitor threads - self.run = True - logging.info("Starting data monitor loop") - while self.run: - if debug: logging.info("Data monitor updating coil data") - self.coils.update(debug) - sleep(interval) \ No newline at end of file diff --git a/enervent-ctrl-python/src/enervent-logger.py b/enervent-ctrl-python/src/enervent-logger.py deleted file mode 100755 index 8cef774..0000000 --- a/enervent-ctrl-python/src/enervent-logger.py +++ /dev/null @@ -1,47 +0,0 @@ -#!/usr/bin/env python -import logging -from PingvinKL import PingvinKL -from flask import Flask, request -import threading -from waitress import serve - -VERSION = "0.0.1" -DEBUG = False - -## Logging configuration -log = logging.getLogger(__name__) -if DEBUG: - dbglevel = logging.DEBUG -else: - dbglevel = logging.INFO -logging.basicConfig( - level=dbglevel, - format='%(asctime)s %(message)s', - datefmt='%y/%m/%d %H:%M:%S' - ) - -pingvin = PingvinKL('/dev/ttyS0',1,debug=DEBUG) -app = Flask(__name__) - -@app.route('/api/v1/coils') -def get_all(): - return pingvin.coils.get(include_reserved=request.args.get('include_reserved'),live=request.args.get('live'),debug=DEBUG) - -@app.route('/api/v1/coils/', methods=["GET","PUT"]) -def coil(address): - if request.method == 'GET': - coil = pingvin.coils[address].get() - return coil - elif request.method == 'PUT': - return {"success": pingvin.coils.write(address)} - -@app.route('/') -def dump(): - return pingvin.coils.print(debug=DEBUG) - -if __name__ == "__main__": - log.info(f"Starting enervent-logger {VERSION}") - datathread = threading.Thread(target=pingvin.monitor, kwargs={"interval": 2, "debug": DEBUG}) - datathread.start() - # app.run(host='0.0.0.0', port=8888) - serve(app, listen='*:8888', trusted_proxy='127.0.0.1', trusted_proxy_headers="x-forwarded-for x-forwarded-host x-forwarded-proto x-forwarded-port") \ No newline at end of file