From 9b68edd31c12a3e9dd1a08ed2f84e4d1ce6d3fc1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Viktor=20Markst=C3=A4dter?= Date: Fri, 1 Apr 2022 10:05:18 +0200 Subject: [PATCH] remove acload postfix, add new files --- README.md | 8 +- config.ini | 8 + dbus-goecharger.py | 250 ++++++++++++++++++++++++++++ docs/go-eCharger-status-sample.json | 98 +++++++++++ restart.sh | 2 +- service/run | 2 +- uninstall.sh | 2 +- 7 files changed, 365 insertions(+), 5 deletions(-) create mode 100644 config.ini create mode 100755 dbus-goecharger.py create mode 100644 docs/go-eCharger-status-sample.json diff --git a/README.md b/README.md index f57f016..b016932 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,6 @@ -# dbus-goecharger-acload -Integrate go-eCharger into Victron Energies Venus OS as a acload +# dbus-goecharger +Integrate go-eCharger into Victron Energies Venus OS + +# Usefull links +- https://github.com/goecharger/go-eCharger-API-v1 +- https://github.com/trixing/venus.dbus-twc3 \ No newline at end of file diff --git a/config.ini b/config.ini new file mode 100644 index 0000000..012d702 --- /dev/null +++ b/config.ini @@ -0,0 +1,8 @@ +[DEFAULT] +AccessType = OnPremise +SignOfLifeLog = 1 + +[ONPREMISE] +Host=192.168.178.97 +Username= +Password= diff --git a/dbus-goecharger.py b/dbus-goecharger.py new file mode 100755 index 0000000..dec7bbc --- /dev/null +++ b/dbus-goecharger.py @@ -0,0 +1,250 @@ +#!/usr/bin/env python + +# 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 +import configparser # for config/ini file + +# 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 + + +class DbusGoeChargerService: + def __init__(self, servicename, paths, productname='go-eCharger', connection='go-eCharger HTTP JSON service'): + config = self._getConfig() + deviceinstance = int(config['DEFAULT']['Deviceinstance']) + + self._dbusservice = VeDbusService("{}.http_{:02d}".format(servicename, deviceinstance)) + self._paths = paths + + logging.debug("%s /DeviceInstance = %d" % (servicename, deviceinstance)) + + paths_wo_unit = [ + '/Ac/Frequency', + '/Status', + '/Mode', + '/ChargingTime', + '/PCB/Temperature', + '/MCU/Temperature', + '/History/ChargingCycles', + '/History/ConnectorCycles', + '/History/Ac/Energy/Forward', + '/History/Uptime', + '/History/ChargingTime', + '/History/Alerts', + '/History/AverageStartupTemperature', + '/History/AbortedChargingCycles', + '/History/ThermalFoldbacks' + ] + + # 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', 'Unkown version, and running on 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) # + self._dbusservice.add_path('/ProductName', productname) + self._dbusservice.add_path('/CustomName', productname) + self._dbusservice.add_path('/FirmwareVersion', 0.1) + self._dbusservice.add_path('/HardwareVersion', 0) + self._dbusservice.add_path('/Connected', 1) + self._dbusservice.add_path('/UpdateIndex', 0) + + # add paths without units + for path in paths_wo_unit: + self._dbusservice.add_path(path, None) + + # add path values to dbus + for path, settings in self._paths.items(): + self._dbusservice.add_path( + path, settings['initial'], gettextcallback=settings['textformat'], writeable=True, onchangecallback=self._handlechangedvalue) + + # last update + self._lastUpdate = 0 + + # add _update function 'timer' + gobject.timeout_add(250, self._update) # pause 250ms before the next request + + # add _signOfLife 'timer' to get feedback in log every 5minutes + gobject.timeout_add(self._getSignOfLifeInterval()*60*1000, self._signOfLife) + + def _getConfig(self): + config = configparser.ConfigParser() + config.read("%s/config.ini" % (os.path.dirname(os.path.realpath(__file__)))) + return config; + + + def _getSignOfLifeInterval(self): + config = self._getConfig() + value = config['DEFAULT']['SignOfLifeLog'] + + if not value: + value = 0 + + return int(value) + + + def _getGoeChargerStatusUrl(self): + config = self._getConfig() + accessType = config['DEFAULT']['AccessType'] + + if accessType == 'OnPremise': + URL = "http://%s/status" % (config['ONPREMISE']['Host']) + else: + raise ValueError("AccessType %s is not supported" % (config['DEFAULT']['AccessType'])) + + return URL + + + def _getGoeChargerData(self): + URL = self._getGoeChargerStatusUrl() + request_data = requests.get(url = URL) + + # check for response + if not request_data: + raise ConnectionError("No response from go-eCharger - %s" % (URL)) + + json_data = request_data.json() + + # check for Json + if not json_data: + raise ValueError("Converting response to JSON failed") + + + return json_data + + + def _signOfLife(self): + logging.info("--- Start: sign of life ---") + logging.info("Last _update() call: %s" % (self._lastUpdate)) + logging.info("Last '/Ac/Power': %s" % (self._dbusservice['/Ac/Power'])) + logging.info("--- End: sign of life ---") + return True + + def _update(self): + try: + #get data from go-eCharger + data = self._getGoeChargerData() + + config = self._getConfig() + + #send data to DBus + self._dbusservice['/Ac/L1/Power'] = int(data['nrg'][7] * 0.1 * 1000) + self._dbusservice['/Ac/L2/Power'] = int(data['nrg'][8] * 0.1 * 1000) + self._dbusservice['/Ac/L3/Power'] = int(data['nrg'][9] * 0.1 * 1000) + self._dbusservice['/Ac/Power'] = int(data['nrg'][11] * 0.01 * 1000) + self._dbusservice['/Ac/Frequency'] = 0 + self._dbusservice['/Ac/Voltage'] = data['grid_v'] + self._dbusservice['/Current'] = data['vehicle_current_a'] + self._dbusservice['/SetCurrent'] = 16 # static for now + self._dbusservice['/MaxCurrent'] = 16 # data['vehicle_current_a'] + # self._dbusservice['/Ac/Energy/Forward'] = float(data['session_energy_wh']) / 1000.0 + self._dbusservice['/Ac/Energy/Forward'] = float(lt['energy_wh']) / 1000.0 + self._dbusservice['/ChargingTime'] = data['session_s'] + + self._dbusservice['/Ac/Power'] = self._dbusservice['/Ac/' + pvinverter_phase + '/Power'] + self._dbusservice['/Ac/Energy/Forward'] = self._dbusservice['/Ac/' + pvinverter_phase + '/Energy/Forward'] + + #logging + logging.debug("Wallbox Consumption (/Ac/Power): %s" % (self._dbusservice['/Ac/Power'])) + logging.debug("Wallbox Forward (/Ac/Energy/Forward): %s" % (self._dbusservice['/Ac/Energy/Forward'])) + 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(): + #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 + _kwh = lambda p, v: (str(round(v, 2)) + 'KWh') + _a = lambda p, v: (str(round(v, 1)) + 'A') + _w = lambda p, v: (str(round(v, 1)) + 'W') + _v = lambda p, v: (str(round(v, 1)) + 'V') + + #start our main-service + pvac_output = DbusGoeChargerService( + servicename='com.victronenergy.evcharger', + paths=[ + '/Ac/Power': {'initial': 0, 'textformat': _w}, + '/Ac/L1/Power': {'initial': 0, 'textformat': _w}, + '/Ac/L2/Power': {'initial': 0, 'textformat': _w}, + '/Ac/L3/Power': {'initial': 0, 'textformat': _w}, + '/Ac/Energy/Forward': {'initial': 0, 'textformat': _kwh}, + + '/Ac/Voltage': {'initial': 0, 'textformat': _v}, + '/Current': {'initial': 0, 'textformat': _a}, + '/MaxCurrent': {'initial': 0, 'textformat': _a} + ], + paths={ + '/Ac/Energy/Forward': {'initial': 0, 'textformat': _kwh}, + '/Ac/Power': {'initial': 0, 'textformat': _w}, + + '/Ac/Current': {'initial': 0, 'textformat': _a}, + '/Ac/Voltage': {'initial': 0, 'textformat': _v}, + + '/Ac/L1/Voltage': {'initial': 0, 'textformat': _v}, + '/Ac/L2/Voltage': {'initial': 0, 'textformat': _v}, + '/Ac/L3/Voltage': {'initial': 0, 'textformat': _v}, + '/Ac/L1/Current': {'initial': 0, 'textformat': _a}, + '/Ac/L2/Current': {'initial': 0, 'textformat': _a}, + '/Ac/L3/Current': {'initial': 0, 'textformat': _a}, + '/Ac/L1/Power': {'initial': 0, 'textformat': _w}, + '/Ac/L2/Power': {'initial': 0, 'textformat': _w}, + '/Ac/L3/Power': {'initial': 0, 'textformat': _w}, + '/Ac/L1/Energy/Forward': {'initial': 0, 'textformat': _kwh}, + '/Ac/L2/Energy/Forward': {'initial': 0, 'textformat': _kwh}, + '/Ac/L3/Energy/Forward': {'initial': 0, 'textformat': _kwh}, + }) + + 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() diff --git a/docs/go-eCharger-status-sample.json b/docs/go-eCharger-status-sample.json new file mode 100644 index 0000000..b529e7f --- /dev/null +++ b/docs/go-eCharger-status-sample.json @@ -0,0 +1,98 @@ +{"version":"B", + "tme":"2903222009", + "rbc":"205", + "rbt":"1422673530", + "car":"4", + "amp":"6", + "err":"0", + "ast":"0", + "alw":"0", + "stp":"0", + "cbl":"20", + "pha":"8", + "tmp":"22", + "tma":[16.13,16.00,15.75,16.13,-0.13,-0.13], + "amt":"32", + "dws":"4320841", + "dwo":"0", + "adi":"1", + "uby":"0", + "eto":"3660", + "wst":"3", + "txi":"0", + "nrg":[220,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0], + "fwv":"041.0", + "sse":"002918", + "wss":"chargerWLAN", + "wke":"********", + "wen":"1", + "cdi":"0", + "tof":"101", + "tds":"1", + "lbr":"255", + "aho":"3", + "afi":"7", + "azo":"1", + "ama":"32", + "al1":"10", + "al2":"16", + "al3":"20", + "al4":"24", + "al5":"32", + "cid":"255", + "cch":"65535", + "cfi":"65280", + "lse":"1", + "ust":"2", + "wak":"cd328163cd", + "r1x":"2", + "dto":"0", + "nmo":"0", + "sch":"AAAAAAAAAAAAAAAA", + "sdp":"0", + "eca":"0", + "ecr":"0", + "ecd":"0", + "ec4":"0", + "ec5":"0", + "ec6":"0", + "ec7":"0", + "ec8":"0", + "ec9":"0", + "ec1":"0", + "rca":"045C543A", + "rcr":"", + "rcd":"", + "rc4":"", + "rc5":"", + "rc6":"", + "rc7":"", + "rc8":"", + "rc9":"", + "rc1":"", + "rna":"", + "rnm":"", + "rne":"", + "rn4":"", + "rn5":"", + "rn6":"", + "rn7":"", + "rn8":"", + "rn9":"", + "rn1":"", + "loe":0, + "lot":0, + "lom":0, + "lop":0, + "log":"", + "lon":0, + "lof":0, + "loa":0, + "lch":13989, + "mce":0, + "mcs":"test.mosquitto.org", + "mcp":1883, + "mcu":"", + "mck":"", + "mcc":0 +} \ No newline at end of file diff --git a/restart.sh b/restart.sh index 7e8f6bd..4ae4dd9 100644 --- a/restart.sh +++ b/restart.sh @@ -1,4 +1,4 @@ #!/bin/bash SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd ) -kill $(pgrep -f "python $SCRIPT_DIR/dbus-goecharger-acload.py") +kill $(pgrep -f "python $SCRIPT_DIR/dbus-goecharger.py") diff --git a/service/run b/service/run index 22d6fe4..7e80bd2 100644 --- a/service/run +++ b/service/run @@ -2,4 +2,4 @@ SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd ) exec 2>&1 -python $(realpath $SCRIPT_DIR/../dbus-goecharger-acload.py) +python $(realpath $SCRIPT_DIR/../dbus-goecharger.py) diff --git a/uninstall.sh b/uninstall.sh index e220065..962352a 100644 --- a/uninstall.sh +++ b/uninstall.sh @@ -3,6 +3,6 @@ SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd ) SERVICE_NAME=$(basename $SCRIPT_DIR) rm /service/$SERVICE_NAME -kill $(pgrep -f 'supervise dbus-goecharger-acload') +kill $(pgrep -f 'supervise dbus-goecharger') chmod a-x $SCRIPT_DIR/service/run ./restart.sh