#!/usr/bin/env python try: import gobject # Python 2.x except: from gi.repository import GLib as gobject # Python 3.x import platform import logging import sys import os 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')) from vedbus import VeDbusService path_UpdateIndex = '/UpdateIndex' class NodeRedMeterKeller: def __init__(self, servicename, deviceinstance, paths, productname='Keller', connection='NodeRED local'): self._dbusservice = VeDbusService(servicename) 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/Connection', connection) # Create the mandatory objects self._dbusservice.add_path('/DeviceInstance', deviceinstance) self._dbusservice.add_path('/ProductId', 0x10) self._dbusservice.add_path('/ProductName', productname) self._dbusservice.add_path('/FirmwareVersion', 1.0) self._dbusservice.add_path('/HardwareVersion', 0) self._dbusservice.add_path('/Connected', 1) for path, settings in self._paths.items(): self._dbusservice.add_path( path, settings['initial'], writeable=True, onchangecallback=self._handlechangedvalue) gobject.timeout_add(5000, self._update) # pause 5000ms before the next request def _update(self): try: self._dbusservice['/ErrorCode'] = 0 self._dbusservice['/DeviceType'] = 103 nodered_url = "http://localhost:1880/meters" nodered_r = requests.get(url=nodered_url) # request data from the Fronius PV inverter nodered_data = nodered_r.json() # convert JSON data self._dbusservice['/Ac/L1/Voltage'] = nodered_data['keller']['l1_voltage'] self._dbusservice['/Ac/L1/Current'] = nodered_data['keller']['l1_current'] self._dbusservice['/Ac/L1/Power'] = nodered_data['keller']['l1_power'] self._dbusservice['/Ac/L1/Energy/Forward'] = nodered_data['keller']['l1_import'] except: logging.info("WARNING: Could not read from Node Red, check if Node Red service is running") self._dbusservice['/ErrorCode'] = 1 self._dbusservice['/Ac/L1/Voltage'] = 0 self._dbusservice['/Ac/L1/Current'] = 0 self._dbusservice['/Ac/L1/Power'] = 0 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 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 = NodeRedMeterKeller( servicename='com.victronenergy.grid', deviceinstance=51, paths={ '/ErrorCode': {'initial': 0}, '/DeviceType': {'initial': 103}, '/Ac/L1/Voltage': {'initial': 0}, '/Ac/L1/Current': {'initial': 0}, '/Ac/L1/Power': {'initial': 0}, '/Ac/L1/Energy/Forward': {'initial': 0}, path_UpdateIndex: {'initial': 0}, }) logging.info('Connected to dbus, and switching over to gobject.MainLoop() (= event based)') mainloop = gobject.MainLoop() mainloop.run() if __name__ == "__main__": main()