From 4671a96e3dcaf6533f59e9cd8a4c73b885f87649 Mon Sep 17 00:00:00 2001 From: Carsten Schmiemann Date: Fri, 19 Aug 2022 23:03:53 +0200 Subject: [PATCH] Update temperature script --- dbus-node-red-temp-outside/temp_outside.py | 194 ++++++++++++++------- 1 file changed, 130 insertions(+), 64 deletions(-) diff --git a/dbus-node-red-temp-outside/temp_outside.py b/dbus-node-red-temp-outside/temp_outside.py index c7f6642..b2151b8 100644 --- a/dbus-node-red-temp-outside/temp_outside.py +++ b/dbus-node-red-temp-outside/temp_outside.py @@ -1,95 +1,161 @@ #!/usr/bin/env python - -try: - import gobject # Python 2.x -except: - from gi.repository import GLib as gobject # Python 3.x -import platform + +# import normal packages +import platform import logging import sys import os +import sys +if sys.version_info.major == 2: + import gobject +else: + from gi.repository import GLib as gobject +import sys +import time import requests # for http GET -try: - import thread # for daemon = True / Python 2.x -except: - import _thread as thread # for daemon = True / Python 3.x - -# our own packages -sys.path.insert(1, os.path.join(os.path.dirname(__file__), '../ext/velib_python')) + +# our own packages from victron +sys.path.insert(1, os.path.join(os.path.dirname(__file__), '/opt/victronenergy/dbus-systemcalc-py/ext/velib_python')) from vedbus import VeDbusService -path_UpdateIndex = '/UpdateIndex' - -class NodeRedTempOutside: - def __init__(self, servicename, deviceinstance, paths, productname='External temp sensor', connection='NodeRED local'): - self._dbusservice = VeDbusService(servicename) +class NodeRedMeterTemperature: + def __init__(self, servicename, deviceinstance, paths, productname='External temp sensor', connection='Node RED HTTP JSON service'): + self._dbusservice = VeDbusService("{}.http_{:02d}".format(servicename, deviceinstance)) self._paths = paths - + logging.debug("%s /DeviceInstance = %d" % (servicename, deviceinstance)) - + # Create the management objects, as specified in the ccgx dbus-api document self._dbusservice.add_path('/Mgmt/ProcessName', __file__) + self._dbusservice.add_path('/Mgmt/ProcessVersion', 'Python ' + platform.python_version()) self._dbusservice.add_path('/Mgmt/Connection', connection) - + # Create the mandatory objects self._dbusservice.add_path('/DeviceInstance', deviceinstance) - self._dbusservice.add_path('/ProductId', 0xFFFF) # like ruvii sensors, for symbol only + self._dbusservice.add_path('/ProductId', 41314) + self._dbusservice.add_path('/FilterLength', 10) + self._dbusservice.add_path('/Offset', 0) + self._dbusservice.add_path('/Scale', 1) self._dbusservice.add_path('/ProductName', productname) + self._dbusservice.add_path('/CustomName', "Temperatur Außen") + self._dbusservice.add_path('/TemperatureType', 2) # 0=battery; 1=fridge; 2=generic self._dbusservice.add_path('/FirmwareVersion', 1.0) self._dbusservice.add_path('/HardwareVersion', 0) self._dbusservice.add_path('/Connected', 1) - + self._dbusservice.add_path('/Serial', 1337) + self._dbusservice.add_path('/UpdateIndex', 0) + + # add path values to dbus for path, settings in self._paths.items(): self._dbusservice.add_path( - path, settings['initial'], writeable=True, onchangecallback=self._handlechangedvalue) - + path, settings['initial'], gettextcallback=settings['textformat'], writeable=True, onchangecallback=self._handlechangedvalue) + + # last update + self._lastUpdate = 0 + + # add _update function 'timer' gobject.timeout_add(2000, self._update) # pause 2000ms before the next request - - def _update(self): - try: - nodered_url = "http://localhost:1880/temps" - nodered_r = requests.get(url=nodered_url) # request data from Node RED HTTP JSON API - nodered_data = nodered_r.json() # convert JSON data - nodered_temperature = nodered_data['outside'] - - self._dbusservice['/Temperature'] = float(nodered_temperature) - self._dbusservice['/TemperatureType'] = 2 # 0=battery; 1=fridge; 2=generic - self._dbusservice['/CustomName'] = "Temperatur Außen" - logging.info("Temperature reading: {:.0f}".format(nodered_temperature)) - except: - logging.info("WARNING: Could not read from Node Red, check if Node Red service is running") - index = self._dbusservice[path_UpdateIndex] + 1 # increment index - if index > 255: # maximum value of the index - index = 0 # overflow from 255 to 0 - self._dbusservice[path_UpdateIndex] = index + + # add _signOfLife 'timer' to get feedback in log every 5minutes + gobject.timeout_add(self._getSignOfLifeInterval()*60*1000, self._signOfLife) + + def _getSignOfLifeInterval(self): + value = 1 + + if not value: + value = 0 + + return int(value) + + def _getNodeRedData(self): + URL = "http://localhost:1880/temps" + temperature_r = requests.get(url = URL) + + # check for response + if not temperature_r: + raise ConnectionError("No response from NodeRed - %s" % (URL)) + + meter_data = temperature_r.json() + + # check for Json + if not meter_data: + raise ValueError("Converting response to JSON failed") + + + return meter_data + + + def _signOfLife(self): + logging.info("--- Start: sign of life ---") + logging.info("Last _update() call: %s" % (self._lastUpdate)) + logging.info("Last '/Temperature': %s" % (self._dbusservice['/Temperature'])) + logging.info("--- End: sign of life ---") return True + + def _update(self): + try: + #get data from NodeRed 3em + meter_data = self._getNodeRedData() + + #send data to DBus + self._dbusservice['/Temperature'] = meter_data['outside'] + #logging + logging.debug("Temperature Outside (/Temperature): %s" % (self._dbusservice['/Temperature'])) + logging.debug("---"); + + # increment UpdateIndex - to show that new data is available + index = self._dbusservice['/UpdateIndex'] + 1 # increment index + if index > 255: # maximum value of the index + index = 0 # overflow from 255 to 0 + self._dbusservice['/UpdateIndex'] = index + #update lastupdate vars + self._lastUpdate = time.time() + except Exception as e: + logging.critical('Error at %s', '_update', exc_info=e) + + # return true, otherwise add_timeout will be removed from GObject - see docs http://library.isr.ist.utl.pt/docs/pygtk2reference/gobject-functions.html#function-gobject--timeout-add + return True + def _handlechangedvalue(self, path, value): logging.debug("someone else updated %s to %s" % (path, value)) return True # accept the change + + def main(): - logging.basicConfig(level=logging.DEBUG) # use .INFO for less logging - thread.daemon = True # allow the program to quit - - from dbus.mainloop.glib import DBusGMainLoop - # Have a mainloop, so we can send/receive asynchronous calls to and from dbus - DBusGMainLoop(set_as_default=True) - - pvac_output = NodeRedTempOutside( - servicename='com.victronenergy.temperature', - deviceinstance=28, - paths={ - '/Temperature': {'initial': 0}, - '/TemperatureType': {'initial': 0}, - '/CustomName': {'initial': 0}, - path_UpdateIndex: {'initial': 0}, - }) - - logging.info('Connected to dbus, and switching over to gobject.MainLoop() (= event based)') - mainloop = gobject.MainLoop() - mainloop.run() - + #configure logging + logging.basicConfig( format='%(asctime)s,%(msecs)d %(name)s %(levelname)s %(message)s', + datefmt='%Y-%m-%d %H:%M:%S', + level=logging.INFO, + handlers=[ + logging.FileHandler("%s/current.log" % (os.path.dirname(os.path.realpath(__file__)))), + logging.StreamHandler() + ]) + + try: + logging.info("Start"); + + from dbus.mainloop.glib import DBusGMainLoop + # Have a mainloop, so we can send/receive asynchronous calls to and from dbus + DBusGMainLoop(set_as_default=True) + + #formatting + _celcius = lambda p, v: (str(round(v, 2)) + ' °C') + + #start our main-service + pvac_output = NodeRedMeterTemperature( + servicename='com.victronenergy.temperature', + deviceinstance=18, + paths={ + '/Temperature': {'initial': 0, 'textformat': _celcius}, + }) + + logging.info('Connected to dbus, and switching over to gobject.MainLoop() (= event based)') + mainloop = gobject.MainLoop() + mainloop.run() + except Exception as e: + logging.critical('Error at %s', 'main', exc_info=e) if __name__ == "__main__": main() \ No newline at end of file