Compare commits

...

15 Commits

Author SHA1 Message Date
Carsten Schmiemann edc90b79cb Rename grid-meter to grid_meter 2023-09-20 21:54:53 +02:00
Carsten Schmiemann e261a24a17 Add node-red flow for reading Tibber node locally 2023-08-08 23:37:15 +02:00
Carsten Schmiemann 5abff3afa6 Update reamde 2023-08-08 23:36:53 +02:00
Carsten Schmiemann c7f41413b7 Save daily usage stats in victrom settings as well 2023-07-19 22:22:21 +02:00
Carsten Schmiemann 0771aacf20 Stop UpdateIndex if there is an error 2023-07-16 13:14:29 +02:00
Carsten Schmiemann 4897387ab0 Disable pv-inveter file logging for production usage 2023-07-15 11:56:48 +02:00
Carsten Schmiemann 02b5503270 Set initial values to None, tidier for single phase inverter 2023-07-15 11:56:09 +02:00
Carsten Schmiemann d02c67e3d3 Add error handler for left side single phase pvinverter meter too 2023-07-15 11:55:14 +02:00
Carsten Schmiemann 88fbc49fcb Disable file logging for production use 2023-07-15 11:49:58 +02:00
Carsten Schmiemann fdc54769d6 grid meter py Clean up a bit 2023-07-15 11:47:45 +02:00
Carsten Schmiemann 41d57160be Improve grid-meter disconnect or parse error, stop ess 2023-07-15 11:44:22 +02:00
Carsten Schmiemann bbf202b74d Add standalone serial (vedirect) bmv700 and mppt node red flow 2023-07-02 23:38:51 +02:00
Carsten Schmiemann 01746e83fe Remove batrium native 2.0 service due incompleteness 2023-07-02 23:37:50 +02:00
Carsten Schmiemann 27830903d9 Fix readme lines 2023-06-24 13:01:13 +02:00
Carsten Schmiemann d58adfd99b Update readme for rsync backup to microSD 2023-06-24 12:59:46 +02:00
10 changed files with 528 additions and 582 deletions

View File

@ -37,13 +37,9 @@ I try to integrate every datapoint from Batriums BMS to fill Victrons registers.
- capacity to full or to empty is not transmitted by Batrium (transmission of 0x00111900 is missing against documentation)
- Ah, kWh charged and discharged is calcualated by script (see above)
- Capacity of battery must be entered into script batrium.py (see above)
- dbus script is started by rc.local which gets executed last, so all mppts and multiplus' showing temporarily "BMS missing" and need to be cleared after (re-)boot of Venus OS
- <del>dbus script is started by rc.local which gets executed last, so all mppts and multiplus' showing temporarily "BMS missing" and need to be cleared after (re-)boot of Venus OS</del> fixed
- include calculation of State Of Health, based on Ah discharge and SoC State after full charge, because Batrium does not have
### Victrons datapoints
This script will emulate one of Victrons own BMS, therefore are many registers to fill, but I dont know how to fill all.
For example what means average Ah discharged. I implemented datapoints to my best knowledge and it is working for me.
# dbus-node-red-XXX Service files
### Configuration
@ -109,6 +105,16 @@ If you want to restart the script, for example after changing it, kill running s
The daemon-tools will restart the scriptwithin a few seconds.
# Backup
You can insert a SD card with FAT32 filesystem, or a blank one and format it with the GX device itself.
Simply add to cron following command, to copy data folder with some exceptions for node-red node modules every day at 2 am
`crontab -e`
`0 2 * * * /usr/bin/rsync rsync -rlptD --progress --exclude /data/home/nodered/.cache/ --exclude /data/home/nodered/.npm --exclude /data/home/nodered/.node-red/node_modules /data /run/media/mmcblk0p1/data-backup`
### Sources
Used https://github.com/victronenergy/velib_python/blob/master/dbusdummyservice.py as basis for my custom services.

View File

@ -100,6 +100,8 @@ class DbusBatteryService:
'MinCellVoltage': ['/Settings/Batrium/MinCellVoltage', 0.0,0.0,0.0],
'MaxCellVoltage': ['/Settings/Batrium/MaxCellVoltage', 0.0,0.0,0.0],
'TargetChargeVoltage': ['/Settings/Batrium/TargetChargeVoltage', 0.0,0.0,0.0],
'HistoryChargedEnergy': ['/Settings/Batrium/HistoryChargedEnergy', 0.0,0.0,0.0],
'HistoryDischargedEnergy': ['/Settings/Batrium/HistoryDischargedEnergy', 0.0,0.0,0.0],
'interval': ['/Settings/Batrium/Interval', 200, 200, 200]
},
eventCallback=handle_changed_setting)
@ -133,6 +135,10 @@ class DbusBatteryService:
## Load settings from Venus OS config xml ##
self._dbusservice['/History/AverageDischarge'] = self._settings['AvgDischarge']
self._dbusservice['/History/TotalAhDrawn'] = self._settings['TotalAhDrawn']
self._dbusservice['/History/ChargedEnergy'] = self._settings['HistoryChargedEnergy']
self._dbusservice['/History/DischargedEnergy'] = self._settings['HistoryDischargedEnergy']
self.ChargedEnergy = self._settings['HistoryChargedEnergy']
self.DischargedEnergy = self._settings['HistoryDischargedEnergy'] * -1
self._dbusservice.add_path('/History/TimeSinceLastFullCharge', 0)
self._dbusservice.add_path('/History/MinCellVoltage', self._settings['MinCellVoltage'])
self._dbusservice.add_path('/History/MaxCellVoltage', self._settings['MaxCellVoltage'])
@ -170,6 +176,8 @@ class DbusBatteryService:
self._settings['MinCellVoltage'] = self._dbusservice['/History/MinCellVoltage']
self._settings['MaxCellVoltage'] = self._dbusservice['/History/MaxCellVoltage']
self._settings['TargetChargeVoltage'] = self._dbusservice['/Info/MaxChargeVoltage']
self._settings['HistoryChargedEnergy'] = self._dbusservice['/History/ChargedEnergy']
self._settings['HistoryDischargedEnergy'] = self._dbusservice['/History/DischargedEnergy']
def _daily_stats(self):
## Update daily statistics and reset energy counters

View File

@ -1,208 +0,0 @@
#!/usr/bin/env python3
import sys
import os
import logging
import itertools
from time import time
from datetime import datetime
import can
import struct
from argparse import ArgumentParser
VERSION = '1.0'
class BatriumBattery(can.Listener):
def __init__(self, connection):
self.hw_version = 0
self.sw_version = 0
self.capacity = 2.2
self.maxChargeVoltage = 0
self.numberOfModules = 3
self.soc = 0
self.soh = 0
self.voltage = 0
self.current = 0
self.temperature = 0
self.maxCellTemperature = 0
self.maxCellTemperatureId = 0
self.minCellTemperature = 0
self.minCellTemperatureId = 0
#self.cellVoltages_min = dict()
#self.cellVoltages_max = dict()
#self.cellTemperatures = dict()
#self.cellBypassTemperatures = dict()
#self.cellBypassPWM = dict()
self.maxCellVoltage = 0
self.maxCellVoltageId = 0
self.minCellVoltage = 0
self.minCellVoltageId = 0
self.maxChargeCurrent = 0
self.maxDischargeCurrent = 0
self.updated = -1
#self.TimeToEmpty = 0
#self.TimeToFull = 0
self.AhToEmpty = 0
#self.AhToFull = 0
self.NumberInBypass = 0
#self.alarm_high_temperature = 0
#self.alarm_high_voltage = 0
#self.alarm_low_temperature = 0
#self.alarm_low_voltage = 0
#self.alarm_low_soc = 0
#self.alarm_high_charge_current = 0
#self.alarm_high_discharge_current = 0
self._ci = can.interface.Bus(channel=connection, bustype='socketcan')
# check connection and that reported system voltage roughly matches configuration
found = False
msg = None
while True:
try:
msg = self._ci.recv(timeout=10)
except can.CanError:
logging.error("Canbus error")
if msg == None:
#timeout no system connected
logging.error("No messages on canbus %s received. Check connection and speed." % connection)
break;
elif msg.arbitration_id == 0x01:
logging.info("Found Batrium BMS on %s" % connection)
found = True
break;
def on_message_received(self, msg):
self.updated = msg.timestamp
if msg.arbitration_id == 0x00:
self.hw_version = (((msg.data[1] * 256 + (1 << 8)) + msg.data[0] + (1 << 8)) )
self.sw_version = (((msg.data[3] * 256 + (1 << 8)) + msg.data[2] + (1 << 8)) )
self.serial = ((msg.data[7] * 256) + (msg.data[6] * 256) + (msg.data[5] * 256) + msg.data[4]) + (1 << 32)
logging.debug("HW Version: %d, SW Version: %d, Serialnumber: %d", self.hw_version, self.sw_version, self.serial)
elif msg.arbitration_id == 0x01:
self.minCellVoltage = ((msg.data[1] * 256) + msg.data[0]) * 0.001
self.maxCellVoltage = ((msg.data[3] * 256) + msg.data[2]) * 0.001
#self.avgCellVoltage = ((msg.data[5] * 256) + msg.data[4]) * 0.01
self.minCellVoltageId = msg.data[6]
self.maxCellVoltageId = msg.data[7]
logging.debug("MinCellVolt %1.3fV (ID: %d), MaxCellVolt %1.3fV (ID: %d)", self.minCellVoltage, self.minCellVoltageId, self.maxCellVoltage, self.maxCellVoltageId)
elif msg.arbitration_id == 0x02:
self.minCellTemperature = msg.data[0] - 40
self.maxCellTemperature = msg.data[1] - 40
self.temperature = msg.data[2] - 40
self.minCellTemperatureId = msg.data[3]
self.maxCellTemperatureId = msg.data[4]
logging.debug("MinCellTemp: %d (ID: %d), MaxCellTemp: %d (ID: %d), AverageTemp: %d", self.minCellTemperature, self.minCellTemperatureId, self.maxCellTemperature, self.maxCellTemperatureId, self.temperature)
elif msg.arbitration_id == 0x03:
self.NumberInBypass = msg.data[0]
logging.debug("Cell number in bypass: %d", self.NumberInBypass)
if msg.arbitration_id == 0x04:
logging.debug(msg)
self.voltage = ((msg.data[1] * 256) + msg.data[0]) * 0.1
self.current = int(((msg.data[3] * 256) + msg.data[2]) * 0.001)
self.power = int(((msg.data[5] * 256) + msg.data[4]) * 0.1)
logging.debug("Voltage: %2.2fV, Current: %2.2fA, Power: %2.2fW", self.voltage, self.current, self.power)
if msg.arbitration_id == 0x05:
self.soc = (msg.data[1] * 256 + msg.data[0]) * 0.01
self.soh = (msg.data[3] * 256 + msg.data[2]) * 0.01
self.AhToEmpty = (msg.data[5] * 256 + msg.data[4]) * 0.1
self.capacity = (msg.data[7] * 256 + msg.data[6]) * 0.1
logging.debug("SOC: %2.2f%%, SOH: %2.2f%%, Ah Remain: %2.2fAh, Capacity: %2.2fAh", self.soc, self.soh, self.AhToEmpty, self.capacity)
elif msg.arbitration_id == 0x06:
self.maxChargeVoltage = ((msg.data[1] * 256) + msg.data[0]) * 0.1
self.maxChargeCurrent = ((msg.data[3] * 256) + msg.data[2]) * 0.1
#self.maxDischargeVoltage = ((msg.data[5] * 256) + msg.data[4]) * 0.01
self.maxDischargeCurrent = ((msg.data[7] * 256) + msg.data[6]) * 0.1
logging.debug("MaxChargeVoltage (CVL) %2.2fA, MaxChargeCurrent (CCL) %2.2fV, MaxDischargeCurrent %2.2fA", self.maxChargeCurrent, self.maxChargeVoltage, self.maxDischargeCurrent)
#elif msg.arbitration_id == 0x00111900:
# logging.debug("Ah 0x00111900 received")
# message never arrived, batrium fw bug?
#self.AhToFull = round((struct.unpack('>f', bytearray([msg.data[3], msg.data[2], msg.data[1], msg.data[0]])))[0] * 0.001, 2)
#self.AhToEmpty = round((struct.unpack('>f', bytearray([msg.data[7], msg.data[6], msg.data[5], msg.data[4]])))[0] * 0.001, 2)
#self.capacity = self.AhToEmpty + self.AhToFull
#logging.debug("Ah to Full: %2.2f", self.AhToFull)
#logging.debug("Ah to Empty: %2.2f", self.AhToEmpty)
#logging.debug("Capacity: %2.2f", self.capacity)
#elif msg.arbitration_id == 0x00140100:
# self.alarm_high_temperature = (msg.data[2] >> 3) & 1
# logging.debug("High Temp Alarm: %d", self.alarm_high_temperature)
# self.alarm_high_voltage = (msg.data[2] >> 1) & 1
# logging.debug("High Voltage Alarm: %d", self.alarm_high_voltage)
# self.alarm_low_temperature = (msg.data[2] >> 2) & 1
# logging.debug("Low Temperature Alarm: %d", self.alarm_low_temperature)
# self.alarm_low_voltage = (msg.data[2] >> 0) & 1
# logging.debug("Low Voltage Alarm: %d", self.alarm_low_voltage)
# self.alarm_high_charge_current = (msg.data[3] >> 3) & 1
# logging.debug("High Charge Current Alarm: %d", self.alarm_high_charge_current)
# self.alarm_high_discharge_current = (msg.data[3] >> 4) & 1
# logging.debug("High Discharge Current Alarm: %d", self.alarm_high_discharge_current)
#elif msg.arbitration_id == 0x00111800:
# self.TimeToEmpty = ((msg.data[5] * 256) + msg.data[4] * 60)
# logging.debug("Time to empty: %dmin", self.TimeToEmpty)
#elif msg.arbitration_id == 0x00111700:
# self.TimeToFull = ((msg.data[5] * 256) + msg.data[4] * 60)
# logging.debug("Time to full: %dmin", self.TimeToFull)
#elif ((msg.arbitration_id >= 0x001D1101) and (msg.arbitration_id <= 0x001D11F9)):
# cell_id = msg.arbitration_id & 0xff
#logging.debug("Cell ID: %d, Voltage_min: %2.2f, Voltage_max: %2.2f, Temperature: %d, Bypass_Temperature: %d, BypassPWM: %d", cell_id, ((msg.data[1] * 256) + msg.data[0]) * 0.001, ((msg.data[3] * 256) + msg.data[2]) * 0.001, msg.data[4] - 40, msg.data[5] - 40, msg.data[6])
# self.cellVoltages_min[cell_id] = ((msg.data[1] * 256) + msg.data[0]) * 0.001
# self.cellVoltages_max[cell_id] = ((msg.data[3] * 256) + msg.data[2]) * 0.001
# self.cellTemperatures[cell_id] = msg.data[4] - 40
# self.cellBypassTemperatures[cell_id] = msg.data[5] - 40
# self.cellBypassPWM[cell_id] = msg.data[6]
# self.numberOfModules = len(self.cellVoltages_min.keys())
def prnt(self):
print("SOC: %2d, I: %2.2fA, U: %2.2fV, T:%2.1fC" % (self.soc, self.current, self.voltage, self.maxCellTemperature))
print("MinCellVolt: %1.2f, MaxCellVolt: %1.2f CellVoltDelta: %1.2f" % (self.minCellVoltage, self.maxCellVoltage, self.maxCellVoltage-self.minCellVoltage))
print("MaxChargeCurrent (CCL): %4.1f A MaxChargeVoltage (CVL): %2.2f V" % (self.maxChargeCurrent, self.maxChargeVoltage))
print("Number of modules found: %1.0f" % (self.numberOfModules))
# === All code below is to simply run it from the commandline for debugging purposes ===
def main():
logging.basicConfig( format='%(asctime)s,%(msecs)d %(name)s %(levelname)s %(message)s',
datefmt='%Y-%m-%d %H:%M:%S',
level=logging.DEBUG,
stream=sys.stdout,
)
logging.info('Starting dbus_batrium_native listener')
logger = can.Logger('logfile.asc')
bat = BatriumBattery(connection='can0')
listeners = [
logger, # Regular Listener object
bat
]
notifier = can.Notifier(bat._ci, listeners)
for msg in bat._ci:
if msg.arbitration_id == 0x001:
bat.prnt()
# Clean-up
notifier.stop()
bat._ci.shutdown()
if __name__ == "__main__":
main()

View File

@ -1,308 +0,0 @@
#!/usr/bin/env python3
"""
A class to put a battery service on the dbus, according to victron standards, with constantly updating
paths.
"""
from gi.repository import GLib
import platform
import argparse
import logging
import sys
import os
import dbus
import itertools
import math
from time import time
from datetime import datetime
import struct
from argparse import ArgumentParser
from batrium import *
# our own packages
sys.path.insert(1, os.path.join(os.path.dirname(__file__), '/opt/victronenergy/dbus-systemcalc-py/ext/velib_python'))
from vedbus import VeDbusService
from ve_utils import exit_on_error
from settingsdevice import SettingsDevice
VERSION = '1.0'
def handle_changed_setting(setting, oldvalue, newvalue):
logging.debug('setting changed, setting: %s, old: %s, new: %s' % (setting, oldvalue, newvalue))
class DbusBatteryService:
def __init__(self, servicename, deviceinstance, productname='Batrium BMS', connection='can0'):
self.minUpdateDone = 0
self.secUpdateDone = 0
self.dailyResetDone = 0
self.lastUpdated = 0
self.cell_balanced = 0
self.ChargedEnergy = 0
self.DischargedEnergy = 0
self._bat = BatriumBattery(connection=connection)
self.notifier = can.Notifier(self._bat._ci, [self._bat])
try:
self._dbusservice = VeDbusService(servicename+'.socketcan_'+connection+'_di'+str(deviceinstance))
except:
exit
# 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', VERSION + ' 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', 0xB038)
self._dbusservice.add_path('/ProductName', productname)
self._dbusservice.add_path('/FirmwareVersion', None, writeable=True)
self._dbusservice.add_path('/HardwareVersion', None, writeable=True)
self._dbusservice.add_path('/Connected', 0)
# Create battery specific objects
self._dbusservice.add_path('/Status', None, writeable=True)
self._dbusservice.add_path('/Dc/0/Temperature', None, writeable=True)
self._dbusservice.add_path('/Info/BatteryLowVoltage', None, writeable=True)
self._dbusservice.add_path('/Alarms/CellImbalance', None, writeable=True)
self._dbusservice.add_path('/Alarms/LowVoltage', None, writeable=True)
self._dbusservice.add_path('/Alarms/HighVoltage', None, writeable=True)
self._dbusservice.add_path('/Alarms/HighDischargeCurrent', None, writeable=True)
self._dbusservice.add_path('/Alarms/HighChargeCurrent', None, writeable=True)
self._dbusservice.add_path('/Alarms/LowSoc', None, writeable=True)
self._dbusservice.add_path('/Alarms/LowTemperature', None, writeable=True)
self._dbusservice.add_path('/Alarms/HighTemperature', None, writeable=True)
self._dbusservice.add_path('/Balancing', None, writeable=True)
self._dbusservice.add_path('/System/HasTemperature', 1)
self._dbusservice.add_path('/System/NrOfBatteries', 1)
self._dbusservice.add_path('/System/NrOfModulesOnline', None, writeable=True)
self._dbusservice.add_path('/System/NrOfModulesOffline', 0)
self._dbusservice.add_path('/System/NrOfModulesBlockingDischarge', 0)
self._dbusservice.add_path('/System/NrOfModulesBlockingCharge', 0)
self._dbusservice.add_path('/System/NrOfBatteriesBalancing', None, writeable=True)
self._dbusservice.add_path('/System/BatteriesSeries', None, writeable=True)
self._dbusservice.add_path('/System/MinVoltageCellId', None, writeable=True)
self._dbusservice.add_path('/System/MaxVoltageCellId', None, writeable=True)
self._dbusservice.add_path("/System/MinTemperatureCellId", None, writeable=True)
self._dbusservice.add_path("/System/MaxTemperatureCellId", None, writeable=True)
self._settings = SettingsDevice(
bus=dbus.SystemBus() if (platform.machine() == 'armv7l') else dbus.SessionBus(),
supportedSettings={
'AvgDischarge': ['/Settings/Batrium/AvgerageDischarge', 0.0,0,0],
'TotalAhDrawn': ['/Settings/Batrium/TotalAhDrawn', 0.0,0,0],
'TimeLastFull': ['/Settings/Batrium/TimeLastFull', 0.0 ,0,0],
'MinCellVoltage': ['/Settings/Batrium/MinCellVoltage', 0.0,0.0,0.0],
'MaxCellVoltage': ['/Settings/Batrium/MaxCellVoltage', 0.0,0.0,0.0],
'interval': ['/Settings/Batrium/Interval', 200, 200, 200]
},
eventCallback=handle_changed_setting)
self._summeditems = {
'/System/MaxCellVoltage': {'gettext': '%.3F V'},
'/System/MinCellVoltage': {'gettext': '%.3F V'},
'/Dc/0/Voltage': {'gettext': '%.2F V'},
'/Dc/0/Current': {'gettext': '%.2F A'},
'/Dc/0/Power': {'gettext': '%.0F W'},
'/Soc': {'gettext': '%.0F%%'},
'/Soh': {'gettext': '%.0F%%'},
'/History/TotalAhDrawn': {'gettext': '%.0F Ah'},
'/History/DischargedEnergy': {'gettext': '%.2F kWh'},
'/History/ChargedEnergy': {'gettext': '%.2F kWh'},
'/History/AverageDischarge': {'gettext': '%.2F kWh'},
'/TimeToGo': {'gettext': '%.0F s'},
'/ConsumedAmphours': {'gettext': '%.1F Ah'},
'/System/MinCellTemperature': {'gettext': '%.1F °C'},
'/System/MaxCellTemperature': {'gettext': '%.1F °C'},
'/Capacity': {'gettext': '%.1F Ah'},
'/InstalledCapacity': {'gettext': '%.1F Ah'},
'/Info/MaxChargeVoltage': {'gettext': '%.1F V'},
'/Info/MaxChargeCurrent': {'gettext': '%.1F A'},
'/Info/MaxDischargeCurrent': {'gettext': '%.1F A'}
}
for path in self._summeditems.keys():
self._dbusservice.add_path(path, value=None, gettextcallback=self._gettext)
self._dbusservice['/History/AverageDischarge'] = self._settings['AvgDischarge']
self._dbusservice['/History/TotalAhDrawn'] = self._settings['TotalAhDrawn']
self._dbusservice.add_path('/History/TimeSinceLastFullCharge', 0)
self._dbusservice.add_path('/History/MinCellVoltage', self._settings['MinCellVoltage'])
self._dbusservice.add_path('/History/MaxCellVoltage', self._settings['MaxCellVoltage'])
self._dbusservice['/Status'] = 0
logging.info("History cell voltage min: %.3f, max: %.3f, totalAhDrawn: %d",
self._settings['MinCellVoltage'], self._settings['MaxCellVoltage'], self._settings['TotalAhDrawn'])
if self._bat.soc <= 99:
self._dbusservice['/Soc'] = self._bat.soc
elif self._bat.soc > 100:
self._dbusservice['/Soc'] = 100
GLib.timeout_add( self._settings['interval'], exit_on_error, self._update)
def _gettext(self, path, value):
item = self._summeditems.get(path)
if item is not None:
return item['gettext'] % value
return str(value)
def __del__(self):
self._safe_history()
logging.info('Stopping dbus_Batrium')
def _safe_history(self):
logging.debug('Saving history to localsettings')
self._settings['AvgDischarge'] = self._dbusservice['/History/AverageDischarge']
self._settings['TotalAhDrawn'] = self._dbusservice['/History/TotalAhDrawn']
self._settings['MinCellVoltage'] = self._dbusservice['/History/MinCellVoltage']
self._settings['MaxCellVoltage'] = self._dbusservice['/History/MaxCellVoltage']
def _daily_stats(self):
if (self._dbusservice['/History/DischargedEnergy'] == 0): return
logging.info("Updating stats, SOC: %d, Discharged: %.2f, Charged: %.2f ",self._bat.soc, self._dbusservice['/History/DischargedEnergy'], self._dbusservice['/History/ChargedEnergy'])
self._dbusservice['/History/AverageDischarge'] = (6*self._dbusservice['/History/AverageDischarge'] + self._dbusservice['/History/DischargedEnergy'])/7 #rolling week
self._dbusservice['/History/ChargedEnergy'] = 0
self.ChargedEnergy = 0
self._dbusservice['/History/DischargedEnergy'] = 0
self.DischargedEnergy = 0
dt = datetime.now() - datetime.fromtimestamp( float(self._settings['TimeLastFull']) )
self.dailyResetDone = datetime.now().day
def _update(self):
#logging.debug("Update CAN %d, Time Now: %d", self._bat.updated, self.lastUpdated)
time = datetime.now()
self.lastUpdated = time.timestamp()
if (self._bat.updated != -1 and self.lastUpdated == 0) or ((self.lastUpdated - self._bat.updated) < 10):
self._dbusservice['/Connected'] = 1
else:
self._dbusservice['/Connected'] = 0
if self._bat.soc <= 99:
self._dbusservice['/Soc'] = self._bat.soc
elif (self._bat.soc > 99) and (self.cell_balanced):
self._dbusservice['/Soc'] = 100
self._dbusservice['/Soh'] = self._bat.soh
if self._bat.NumberInBypass != 0:
self._dbusservice['/Balancing'] = 1
else:
self._dbusservice['/Balancing'] = 0
self._dbusservice['/FirmwareVersion'] = self._bat.sw_version
self._dbusservice['/HardwareVersion'] = self._bat.hw_version
self._dbusservice['/ConsumedAmphours'] = ((100 - self._dbusservice['/Soc']) / 100) * self._bat.capacity
self._dbusservice['/InstalledCapacity'] = self._bat.capacity
self._dbusservice['/Capacity'] = self._bat.capacity
self._dbusservice['/Dc/0/Current'] = self._bat.current
self._dbusservice['/Dc/0/Voltage'] = self._bat.voltage
self._dbusservice['/Dc/0/Power'] = self._bat.power
self._dbusservice['/Dc/0/Temperature'] = self._bat.maxCellTemperature
self._dbusservice['/Info/MaxChargeCurrent'] = self._bat.maxChargeCurrent
self._dbusservice['/Info/MaxDischargeCurrent'] = self._bat.maxDischargeCurrent
if self._bat.maxChargeVoltage != 0:
self._dbusservice['/Info/MaxChargeVoltage'] = self._bat.maxChargeVoltage
self._dbusservice['/System/NrOfModulesOnline'] = self._bat.numberOfModules
self._dbusservice['/System/NrOfBatteriesBalancing'] = self._bat.NumberInBypass
self._dbusservice['/System/BatteriesSeries'] = self._bat.numberOfModules
#update energy statistics daily at 6:00,
if datetime.now().hour == 6 and datetime.now().minute == 0 and datetime.now().day != self.dailyResetDone :
self._daily_stats()
now = datetime.now().time()
if now.minute != self.minUpdateDone:
self.minUpdateDone = now.minute
self._dbusservice['/System/MinCellTemperature'] = self._bat.minCellTemperature
self._dbusservice['/System/MaxCellTemperature'] = self._bat.maxCellTemperature
self._dbusservice['/System/MinTemperatureCellId'] = self._bat.minCellTemperatureId
self._dbusservice['/System/MaxTemperatureCellId'] = self._bat.maxCellTemperatureId
self._dbusservice['/System/MaxCellVoltage'] = self._bat.maxCellVoltage
if (self._bat.maxCellVoltage > self._dbusservice['/History/MaxCellVoltage'] ):
self._dbusservice['/History/MaxCellVoltage'] = self._bat.maxCellVoltage
logging.info("New maximum cell voltage: %f", self._bat.maxCellVoltage)
self._dbusservice['/System/MinCellVoltage'] = self._bat.minCellVoltage
if (0 < self._bat.minCellVoltage < self._dbusservice['/History/MinCellVoltage'] ):
self._dbusservice['/History/MinCellVoltage'] = self._bat.minCellVoltage
logging.info("New minimum cell voltage: %f", self._bat.minCellVoltage)
self._dbusservice['/System/MaxVoltageCellId'] = self._bat.maxCellVoltageId
self._dbusservice['/System/MinVoltageCellId'] = self._bat.minCellVoltageId
if self._bat.NumberInBypass == self._bat.numberOfModules:
self.cell_balanced = True
elif self._bat.NumberInBypass == 0:
self.cell_balanced = False
self._safe_history()
if now.second != self.secUpdateDone:
self.secUpdateDone = now.second
#Workaround because of missing messages of Batrium BMS
if self._bat.power > 0:
self.ChargedEnergy += (self._bat.power / 3600.0) / 1000
if self._bat.power < 0:
self.DischargedEnergy += (self._bat.power / 3600.0) / 1000
self._dbusservice['/History/ChargedEnergy'] = abs(self.ChargedEnergy)
self._dbusservice['/History/DischargedEnergy'] = abs(self.DischargedEnergy)
return True
def main():
parser = ArgumentParser(description='dbus_batrium', add_help=True)
parser.add_argument('-d', '--debug', help='enable debug logging',
action='store_true')
parser.add_argument('-i', '--interface', help='CAN interface')
parser.add_argument('-p', '--print', help='print only')
args = parser.parse_args()
logging.basicConfig( format='%(asctime)s,%(msecs)d %(name)s %(levelname)s %(message)s',
datefmt='%Y-%m-%d %H:%M:%S',
level=(logging.DEBUG if args.debug else logging.INFO),
handlers=[
logging.FileHandler("%s/current.log" % (os.path.dirname(os.path.realpath(__file__)))),
logging.StreamHandler()
])
if not args.interface:
logging.info('No CAN interface specified, using default can0')
args.interface = 'can0'
logging.info('Starting dbus_batrium %s on %s ' %
(VERSION, args.interface))
logging.debug('Setup main loop')
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)
logging.debug('Setup dbus service')
battery_output = DbusBatteryService(
servicename='com.victronenergy.battery',
connection = args.interface,
deviceinstance = 0,
)
logging.debug('Connected to dbus, and switching over to GLib.MainLoop() (= event based)')
mainloop = GLib.MainLoop()
mainloop.run()
if __name__ == "__main__":
main()

View File

@ -1,3 +0,0 @@
opkg update
opkg install python3-pip
pip3 install python-can

View File

@ -1,3 +0,0 @@
#!/bin/sh
exec 2>&1
exec softlimit -d 100000000 -s 1000000 -a 100000000 python /data/dbus-batrium-native2/dbus_batrium_native2.py

View File

@ -19,8 +19,8 @@ sys.path.insert(1, os.path.join(os.path.dirname(__file__), '/opt/victronenergy/d
from vedbus import VeDbusService
class NodeRedMetergrid-meter:
def __init__(self, servicename, deviceinstance, paths, productname='grid-meter', connection='Node RED HTTP JSON service'):
class NodeRedGridMeter:
def __init__(self, servicename, deviceinstance, paths, productname='Zähler Einspeisung', connection='Node RED HTTP JSON service'):
self._dbusservice = VeDbusService("{}.http_{:02d}".format(servicename, deviceinstance))
self._paths = paths
@ -68,21 +68,38 @@ class NodeRedMetergrid-meter:
return int(value)
# if there is a grid meter disconnect or some other error, set mandatory values to None to stop ESS and set MPs to passthrough
def _errorState(self):
self._dbusservice['/Connected'] = 0
self._dbusservice['/Ac/Power'] = None
self._dbusservice['/Ac/Current'] = None
self._dbusservice['/Ac/L1/Voltage'] = None
self._dbusservice['/Ac/L2/Voltage'] = None
self._dbusservice['/Ac/L3/Voltage'] = None
self._dbusservice['/Ac/L1/Current'] = None
self._dbusservice['/Ac/L2/Current'] = None
self._dbusservice['/Ac/L3/Current'] = None
self._dbusservice['/Ac/L1/Power'] = None
self._dbusservice['/Ac/L2/Power'] = None
self._dbusservice['/Ac/L3/Power'] = None
return True
def _getNodeRedData(self):
URL = "http://localhost:1880/meters"
try:
meter_r = requests.get(url = URL, timeout=5)
except requests.exceptions.RequestException as e:
self._dbusservice['/Connected'] = 0
self._errorState()
raise ConnectionError("No response from NodeRed - %s" % (URL))
meter_data = meter_r.json()
# check for Json
if not meter_data:
self._dbusservice['/Connected'] = 0
self._errorState()
raise ValueError("Converting response to JSON failed")
# if there is not error parsing data, set connected to true
self._dbusservice['/Connected'] = 1
return meter_data
@ -94,29 +111,29 @@ class NodeRedMetergrid-meter:
def _update(self):
try:
#get data from NodeRed 3em
#get parsed data from NodeRed fuction
meter_data = self._getNodeRedData()
#send data to DBus
self._dbusservice['/Ac/Power'] = meter_data['grid-meter']['total_power'] # positive: consumption, negative: feed into grid
self._dbusservice['/Ac/Current'] = meter_data['grid-meter']['total_current']
self._dbusservice['/Ac/L1/Voltage'] = meter_data['grid-meter']['l1_voltage']
self._dbusservice['/Ac/L2/Voltage'] = meter_data['grid-meter']['l2_voltage']
self._dbusservice['/Ac/L3/Voltage'] = meter_data['grid-meter']['l3_voltage']
self._dbusservice['/Ac/L1/Current'] = meter_data['grid-meter']['l1_current']
self._dbusservice['/Ac/L2/Current'] = meter_data['grid-meter']['l2_current']
self._dbusservice['/Ac/L3/Current'] = meter_data['grid-meter']['l3_current']
self._dbusservice['/Ac/L1/Power'] = meter_data['grid-meter']['l1_power']
self._dbusservice['/Ac/L2/Power'] = meter_data['grid-meter']['l2_power']
self._dbusservice['/Ac/L3/Power'] = meter_data['grid-meter']['l3_power']
self._dbusservice['/Ac/L1/Energy/Forward'] = meter_data['grid-meter']['l1_import']
self._dbusservice['/Ac/L2/Energy/Forward'] = meter_data['grid-meter']['l2_import']
self._dbusservice['/Ac/L3/Energy/Forward'] = meter_data['grid-meter']['l3_import']
self._dbusservice['/Ac/L1/Energy/Reverse'] = meter_data['grid-meter']['l1_export']
self._dbusservice['/Ac/L2/Energy/Reverse'] = meter_data['grid-meter']['l2_export']
self._dbusservice['/Ac/L3/Energy/Reverse'] = meter_data['grid-meter']['l3_export']
self._dbusservice['/Ac/Energy/Forward'] = meter_data['grid-meter']['total_import']
self._dbusservice['/Ac/Energy/Reverse'] = meter_data['grid-meter']['total_export']
self._dbusservice['/Ac/Power'] = meter_data['grid_meter']['total_power'] # positive: consumption, negative: feed into grid
self._dbusservice['/Ac/Current'] = meter_data['grid_meter']['total_current']
self._dbusservice['/Ac/L1/Voltage'] = meter_data['grid_meter']['l1_voltage']
self._dbusservice['/Ac/L2/Voltage'] = meter_data['grid_meter']['l2_voltage']
self._dbusservice['/Ac/L3/Voltage'] = meter_data['grid_meter']['l3_voltage']
self._dbusservice['/Ac/L1/Current'] = meter_data['grid_meter']['l1_current']
self._dbusservice['/Ac/L2/Current'] = meter_data['grid_meter']['l2_current']
self._dbusservice['/Ac/L3/Current'] = meter_data['grid_meter']['l3_current']
self._dbusservice['/Ac/L1/Power'] = meter_data['grid_meter']['l1_power']
self._dbusservice['/Ac/L2/Power'] = meter_data['grid_meter']['l2_power']
self._dbusservice['/Ac/L3/Power'] = meter_data['grid_meter']['l3_power']
self._dbusservice['/Ac/L1/Energy/Forward'] = meter_data['grid_meter']['l1_import']
self._dbusservice['/Ac/L2/Energy/Forward'] = meter_data['grid_meter']['l2_import']
self._dbusservice['/Ac/L3/Energy/Forward'] = meter_data['grid_meter']['l3_import']
self._dbusservice['/Ac/L1/Energy/Reverse'] = meter_data['grid_meter']['l1_export']
self._dbusservice['/Ac/L2/Energy/Reverse'] = meter_data['grid_meter']['l2_export']
self._dbusservice['/Ac/L3/Energy/Reverse'] = meter_data['grid_meter']['l3_export']
self._dbusservice['/Ac/Energy/Forward'] = meter_data['grid_meter']['total_import']
self._dbusservice['/Ac/Energy/Reverse'] = meter_data['grid_meter']['total_export']
#logging
logging.debug("House Consumption (/Ac/Power): %s" % (self._dbusservice['/Ac/Power']))
@ -125,14 +142,17 @@ class NodeRedMetergrid-meter:
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
if self._dbusservice['/Connected'] == 1 :
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()
self._lastUpdate = time.time()
except Exception as e:
self._errorState()
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
@ -141,18 +161,17 @@ class NodeRedMetergrid-meter:
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()
])
#handlers=[
# logging.FileHandler("%s/current.log" % (os.path.dirname(os.path.realpath(__file__)))),
# logging.StreamHandler()
#]
)
try:
logging.info("Start");
@ -168,7 +187,7 @@ def main():
_v = lambda p, v: (str(round(v, 1)) + ' V')
#start our main-service
pvac_output = NodeRedMetergrid-meter(
pvac_output = NodeRedGridMeter(
servicename='com.victronenergy.grid',
deviceinstance=40,
paths={

View File

@ -38,7 +38,7 @@ class NodeRedPVinverter:
self._dbusservice.add_path('/CustomName', productname)
self._dbusservice.add_path('/FirmwareVersion', 1.0)
self._dbusservice.add_path('/HardwareVersion', 0)
self._dbusservice.add_path('/Connected', 1)
self._dbusservice.add_path('/Connected', 0)
self._dbusservice.add_path('/Position', 0)
self._dbusservice.add_path('/Serial', 12345678)
self._dbusservice.add_path('/UpdateIndex', 0)
@ -66,21 +66,35 @@ class NodeRedPVinverter:
return int(value)
# if there is a pv meter disconnect or some other error, set mandatory values to None to tidy up vrm view
def _errorState(self):
self._dbusservice['/Connected'] = 0
self._dbusservice['/Ac/Voltage'] = None
self._dbusservice['/Ac/L1/Voltage'] = None
self._dbusservice['/Ac/Current'] = None
self._dbusservice['/Ac/L1/Current'] = None
self._dbusservice['/Ac/Power'] = None
self._dbusservice['/Ac/L1/Power'] = None
return True
def _getNodeRedData(self):
URL = "http://localhost:1880/meters"
meter_r = requests.get(url = URL)
# check for response
if not meter_r:
self._errorState()
raise ConnectionError("No response from NodeRed - %s" % (URL))
meter_data = meter_r.json()
# check for Json
if not meter_data:
self._errorState()
raise ValueError("Converting response to JSON failed")
# if there is no error parsing data, set connected to true
self._dbusservice['/Connected'] = 1
return meter_data
@ -91,7 +105,7 @@ class NodeRedPVinverter:
def _update(self):
try:
#get data from NodeRed 3em
#get data from NodeRed
meter_data = self._getNodeRedData()
#send data to DBus
@ -118,6 +132,7 @@ class NodeRedPVinverter:
#update lastupdate vars
self._lastUpdate = time.time()
except Exception as e:
self._errorState()
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
@ -134,10 +149,11 @@ def main():
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()
])
#handlers=[
# logging.FileHandler("%s/current.log" % (os.path.dirname(os.path.realpath(__file__)))),
# logging.StreamHandler()
#])
)
try:
logging.info("Start");
@ -158,20 +174,20 @@ def main():
deviceinstance=46,
paths={
'/Ac/Energy/Forward': {'initial': None, 'textformat': _kwh}, # energy produced by pv inverter
'/Ac/Power': {'initial': 0, 'textformat': _w},
'/Ac/Power': {'initial': None, 'textformat': _w},
'/Ac/Current': {'initial': 0, 'textformat': _a},
'/Ac/Voltage': {'initial': 0, 'textformat': _v},
'/Ac/Current': {'initial': None, 'textformat': _a},
'/Ac/Voltage': {'initial': None, '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/Voltage': {'initial': None, 'textformat': _v},
'/Ac/L2/Voltage': {'initial': None, 'textformat': _v},
'/Ac/L3/Voltage': {'initial': None, 'textformat': _v},
'/Ac/L1/Current': {'initial': None, 'textformat': _a},
'/Ac/L2/Current': {'initial': None, 'textformat': _a},
'/Ac/L3/Current': {'initial': None, 'textformat': _a},
'/Ac/L1/Power': {'initial': None, 'textformat': _w},
'/Ac/L2/Power': {'initial': None, 'textformat': _w},
'/Ac/L3/Power': {'initial': None, 'textformat': _w},
'/Ac/L1/Energy/Forward': {'initial': None, 'textformat': _kwh},
'/Ac/L2/Energy/Forward': {'initial': None, 'textformat': _kwh},
'/Ac/L3/Energy/Forward': {'initial': None, 'textformat': _kwh},

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,418 @@
[
{
"id": "8d1576fc0be96b4a",
"type": "http request",
"z": "154ae691bb56f6e7",
"name": "http req. Tibber Pulse SML",
"method": "GET",
"ret": "bin",
"paytoqs": "ignore",
"url": "http://10.1.0.11/data.json?node_id=1",
"tls": "",
"persist": false,
"proxy": "",
"insecureHTTPParser": false,
"authType": "basic",
"senderr": false,
"headers": [],
"credentials": {},
"x": 420,
"y": 300,
"wires": [
[
"a82fbeb90cddaba3"
]
]
},
{
"id": "66b56bf77c1e2db4",
"type": "inject",
"z": "154ae691bb56f6e7",
"name": "Request every 3s",
"props": [
{
"p": "payload"
},
{
"p": "topic",
"vt": "str"
}
],
"repeat": "3",
"crontab": "",
"once": false,
"onceDelay": 0.1,
"topic": "",
"payload": "",
"payloadType": "date",
"x": 130,
"y": 300,
"wires": [
[
"8d1576fc0be96b4a"
]
]
},
{
"id": "af078be60e1168a2",
"type": "smartmeter",
"z": "154ae691bb56f6e7",
"name": "parse SML",
"datasource": "2cc5d556b9271b80",
"protocol": "SmlProtocol",
"transport": "LocalFileTransport",
"requestInterval": "99999",
"d0WakeupCharacters": "",
"d0SignOnMessage": "",
"d0BaudrateChangeoverOverwrite": "",
"protocolSmlIgnoreInvalidCRC": false,
"debugging": false,
"x": 870,
"y": 300,
"wires": [
[
"bcb656baff6e2422",
"90e11ee613d8a8d8"
]
]
},
{
"id": "a82fbeb90cddaba3",
"type": "file",
"z": "154ae691bb56f6e7",
"name": "",
"filename": "/tmp/tibber_sml.data",
"filenameType": "str",
"appendNewline": false,
"createDir": false,
"overwriteFile": "true",
"encoding": "none",
"x": 680,
"y": 300,
"wires": [
[
"af078be60e1168a2"
]
]
},
{
"id": "bcb656baff6e2422",
"type": "function",
"z": "154ae691bb56f6e7",
"name": "filter 1.8.0",
"func": "msg.payload = msg.payload[\"1-0:1.8.0*255\"].values[0].value;\nreturn msg;",
"outputs": 1,
"noerr": 0,
"initialize": "",
"finalize": "",
"libs": [],
"x": 800,
"y": 380,
"wires": [
[
"99439952c9f0698e"
]
]
},
{
"id": "90e11ee613d8a8d8",
"type": "function",
"z": "154ae691bb56f6e7",
"name": "filter 16.7.0",
"func": "msg.payload = msg.payload[\"1-0:16.7.0*255\"].values[0].value;\nreturn msg;",
"outputs": 1,
"noerr": 0,
"initialize": "",
"finalize": "",
"libs": [],
"x": 810,
"y": 420,
"wires": [
[
"3416a1bf3d6e4db5"
]
]
},
{
"id": "609c6f6c087622eb",
"type": "influxdb out",
"z": "154ae691bb56f6e7",
"influxdb": "dee207f7.f8e2b8",
"name": "",
"measurement": "evu_1_8_0",
"precision": "",
"retentionPolicy": "",
"database": "database",
"precisionV18FluxV20": "ms",
"retentionPolicyV18Flux": "",
"org": "Klotz",
"bucket": "energy_meter",
"x": 1300,
"y": 380,
"wires": []
},
{
"id": "99439952c9f0698e",
"type": "delay",
"z": "154ae691bb56f6e7",
"name": "",
"pauseType": "rate",
"timeout": "5",
"timeoutUnits": "seconds",
"rate": "1",
"nbRateUnits": "60",
"rateUnits": "second",
"randomFirst": "1",
"randomLast": "5",
"randomUnits": "seconds",
"drop": true,
"allowrate": false,
"outputs": 1,
"x": 1030,
"y": 380,
"wires": [
[
"609c6f6c087622eb"
]
]
},
{
"id": "cf1e5fe6d9e2b8cb",
"type": "influxdb out",
"z": "154ae691bb56f6e7",
"influxdb": "dee207f7.f8e2b8",
"name": "",
"measurement": "evu_16_7_0",
"precision": "",
"retentionPolicy": "",
"database": "database",
"precisionV18FluxV20": "ms",
"retentionPolicyV18Flux": "",
"org": "Klotz",
"bucket": "energy_meter",
"x": 1310,
"y": 420,
"wires": []
},
{
"id": "7d112572b3df1bc9",
"type": "http request",
"z": "154ae691bb56f6e7",
"name": "http req. Tibber Pulse Metric",
"method": "GET",
"ret": "obj",
"paytoqs": "ignore",
"url": "http://10.1.0.11/metrics.json?node_id=1",
"tls": "",
"persist": false,
"proxy": "",
"insecureHTTPParser": false,
"authType": "basic",
"senderr": false,
"headers": [],
"credentials": {},
"x": 420,
"y": 600,
"wires": [
[
"9de9ed09efa9fc09",
"dc55cf9772b06948",
"128089ad6b44d136"
]
]
},
{
"id": "1a000ee3584d95a9",
"type": "inject",
"z": "154ae691bb56f6e7",
"name": "Request every 1m",
"props": [
{
"p": "payload"
},
{
"p": "topic",
"vt": "str"
}
],
"repeat": "60",
"crontab": "",
"once": false,
"onceDelay": 0.1,
"topic": "",
"payload": "",
"payloadType": "date",
"x": 140,
"y": 600,
"wires": [
[
"7d112572b3df1bc9"
]
]
},
{
"id": "9de9ed09efa9fc09",
"type": "function",
"z": "154ae691bb56f6e7",
"name": "battery voltage",
"func": "msg.payload = msg.payload.node_status.node_battery_voltage;\nreturn msg;",
"outputs": 1,
"noerr": 0,
"initialize": "",
"finalize": "",
"libs": [],
"x": 680,
"y": 600,
"wires": [
[
"3418ac83e0994fd7"
]
]
},
{
"id": "dc55cf9772b06948",
"type": "function",
"z": "154ae691bb56f6e7",
"name": "temperature",
"func": "msg.payload = msg.payload.node_status.node_temperature;\nreturn msg;",
"outputs": 1,
"noerr": 0,
"initialize": "",
"finalize": "",
"libs": [],
"x": 670,
"y": 640,
"wires": [
[
"edd18b7ad90a3bb5"
]
]
},
{
"id": "128089ad6b44d136",
"type": "function",
"z": "154ae691bb56f6e7",
"name": "RSSI",
"func": "msg.payload = msg.payload.node_status.node_avg_rssi;\nreturn msg;",
"outputs": 1,
"noerr": 0,
"initialize": "",
"finalize": "",
"libs": [],
"x": 650,
"y": 680,
"wires": [
[
"0af0bfae91209540"
]
]
},
{
"id": "3418ac83e0994fd7",
"type": "influxdb out",
"z": "154ae691bb56f6e7",
"influxdb": "dee207f7.f8e2b8",
"name": "",
"measurement": "node_battery_voltage",
"precision": "",
"retentionPolicy": "",
"database": "database",
"precisionV18FluxV20": "ms",
"retentionPolicyV18Flux": "",
"org": "Klotz",
"bucket": "tibber",
"x": 1080,
"y": 600,
"wires": []
},
{
"id": "edd18b7ad90a3bb5",
"type": "influxdb out",
"z": "154ae691bb56f6e7",
"influxdb": "dee207f7.f8e2b8",
"name": "",
"measurement": "node_temperature",
"precision": "",
"retentionPolicy": "",
"database": "database",
"precisionV18FluxV20": "ms",
"retentionPolicyV18Flux": "",
"org": "Klotz",
"bucket": "tibber",
"x": 1070,
"y": 640,
"wires": []
},
{
"id": "0af0bfae91209540",
"type": "influxdb out",
"z": "154ae691bb56f6e7",
"influxdb": "dee207f7.f8e2b8",
"name": "",
"measurement": "node_rssi",
"precision": "",
"retentionPolicy": "",
"database": "database",
"precisionV18FluxV20": "ms",
"retentionPolicyV18Flux": "",
"org": "Klotz",
"bucket": "tibber",
"x": 1040,
"y": 680,
"wires": []
},
{
"id": "3416a1bf3d6e4db5",
"type": "delay",
"z": "154ae691bb56f6e7",
"name": "",
"pauseType": "rate",
"timeout": "5",
"timeoutUnits": "seconds",
"rate": "1",
"nbRateUnits": "3",
"rateUnits": "second",
"randomFirst": "1",
"randomLast": "5",
"randomUnits": "seconds",
"drop": true,
"allowrate": false,
"outputs": 1,
"x": 1020,
"y": 420,
"wires": [
[
"cf1e5fe6d9e2b8cb"
]
]
},
{
"id": "2cc5d556b9271b80",
"type": "smartmeter-connection",
"sourcetype": "file",
"serialport": "/dev/null",
"serialbaud": "9600",
"databits": "8",
"parity": "none",
"stopbits": "1",
"httphost": "localhost",
"httpport": "80",
"tcphost": "localhost",
"tcpport": "502",
"filepath": "/tmp/tibber_sml.data"
},
{
"id": "dee207f7.f8e2b8",
"type": "influxdb",
"hostname": "127.0.0.1",
"port": "8086",
"protocol": "http",
"database": "database",
"name": "Influx Devil",
"usetls": false,
"tls": "",
"influxdbVersion": "2.0",
"url": "http://10.1.0.36:8086",
"rejectUnauthorized": false
}
]