Skip to content

Commit 4b5f92b

Browse files
committed
Conflicts: config.json_dist
2 parents f0c78e7 + a761e3a commit 4b5f92b

13 files changed

+366
-33
lines changed

.gitignore

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
batrium_config.json
1+
config.json
22

33
# Logs
44
logs

README.md

Lines changed: 32 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,13 @@ WatchMon UDP Binary Listener translates to JSON and stores to influxdb or sends
77
git clone https://github.com/Batrium/WatchMonUdpListener
88
```
99
or
10+
```
11+
npm install watchmonudplistener
12+
```
13+
or
1014

1115
Download premade Raspberry PI 3 ISO with everything built in:
12-
<--link-->
16+
http://diytechandrepairs.nu/raspberry-solar/
1317

1418

1519
### Prerequisites
@@ -18,7 +22,7 @@ MQTT
1822
Binary-Parser
1923
Influx
2024

21-
How to install them:
25+
How to install them: (If you clone from GitHub)
2226
```
2327
npm install mqtt
2428
npm install binary-parser
@@ -27,11 +31,13 @@ npm install influx
2731

2832
### Installing
2933

34+
Main install in "Get started"
35+
3036
Start with copying the batrium_config.json_dist to batrium_config.json
3137

3238
Example code in linux:
3339
```
34-
cp batrium_config.json_dist batrium_config.json
40+
cp config.json_dist config.json
3541
```
3642

3743
Edit the file to suit your needs.
@@ -81,12 +87,32 @@ When all this is done its just a matter of running the application
8187

8288
## Automatic startup
8389

84-
<- information about automatic start need to be added here and files added.
90+
```
91+
cp systemctl/batrium.service /etc/systemd/system/
92+
```
93+
Be sure to update paths in the file acordingly to where you installed the Batrium UDP listener
8594

95+
```
96+
systemctl --system daemon-reload
97+
```
8698

99+
To enable:
100+
```
101+
systemctl enable batrium.service
102+
```
103+
To disable
104+
```
105+
systemctl disable batrium.service
106+
```
87107
## Contributing
88108

89-
For contributing to this project you can either add it up as an issue here on Github or to this page on the DIYPowerwalls forum:
109+
For contributing to this project you can support Batrium by getting HW from them.
110+
Supporting the creator of this script by Patreon/Paypal/Subscribing to youtube channel.
111+
Patreon: https://www.patreon.com/diytech
112+
Youtube: https://www.youtube.com/user/daromeresperyd
113+
Webpage with more ways to contribute: http://diytechandrepairs.nu/
114+
And the most important is to report any issues/bugs or features that you want to see sorted or fixed.
115+
All added contributions are much appreciated!
90116

91117

92118
## Authors
@@ -95,7 +121,7 @@ For contributing to this project you can either add it up as an issue here on Gi
95121

96122
## License
97123

98-
This project is licensed under the MIT License - see the [LICENSE.md](LICENSE.md) file for details
124+
This project is licensed under the MIT License - see the [LICENSE.md](LICENSE) file for details
99125

100126
## Acknowledgments
101127

Lines changed: 12 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -6,45 +6,39 @@
66
"influxhost" : "localhost",
77
"influxdatabase" : "batrium"
88
},
9-
"3e5a" : {
9+
"3e" : {
1010
"mqtt" : true,
1111
"influx" : true,
1212
"tag" : "general",
1313
"serie" : "generic"
1414
},
15-
"3e32" : {
16-
"mqtt" : true,
17-
"influx" : true,
18-
"tag" : "general",
19-
"serie" : "generic"
20-
},
21-
"3f33" : {
22-
"mqtt" : true,
23-
"influx" : true,
24-
"tag" : "general",
25-
"serie" : "generic"
26-
},
27-
"7857" : {
15+
"78" : {
2816
"mqtt" : false,
2917
"influx" : false,
3018
"info" : "Dont use influx on message 7857",
3119
"tag" : "general",
3220
"serie" : "generic"
3321
},
34-
"5732" : {
22+
"57" : {
3523
"mqtt" : false,
3624
"influx" : false,
3725
"tag" : "general",
3826
"serie" : "generic"
3927
},
40-
"4232" : {
28+
"42" : {
4129
"mqtt" : false,
42-
"influx" : true,
30+
"influx" : false,
4331
"tag" : "general",
4432
"serie" : "nodes",
4533
"tagID" : "ID"
4634
},
47-
"415a" : {
35+
"54" : {
36+
"mqtt" : false,
37+
"influx" : true,
38+
"tag" : "general",
39+
"serie" : "daily"
40+
},
41+
"41" : {
4842
"mqtt" : true,
4943
"influx" : false,
5044
"tag" : "general",
1.26 MB
Binary file not shown.

batrium.js renamed to index.js

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
// http://diytechandrepairs.nu
55
// https://www.youtube.com/user/daromeresperyd
66
//
7-
//
7+
// MIT Licensed
88
// creating a udp server
99
var udp = require('dgram');
1010
var mqtt = require('mqtt')
@@ -18,10 +18,10 @@ var fs = require('fs');
1818

1919
//Loading configuration file.
2020
try {
21-
var config = JSON.parse(fs.readFileSync('batrium_config.json', "utf8"));
21+
var config = JSON.parse(fs.readFileSync('config.json', "utf8"));
2222
}
2323
catch (e) {
24-
errorText('Could not load configuration file. Will therefore not send any data out. file missing is batrium_config.json. Perhaps copy the dist file?');
24+
errorText('Could not load configuration file. Will therefore not send any data out. file missing is config.json. Perhaps copy the dist file?');
2525
console.log(e);
2626
var config = {'all':{'mqtt':{},'influx':{}}, 'hej': {}};
2727
}
@@ -90,7 +90,7 @@ function sendInflux(data, tag) {
9090
tg = { systemId: data.SystemId, messageId: data.MessageId, messageType: (config[data.MessageId] && config[data.MessageId].tag ) ? config[data.MessageId].tag: 'generic' };
9191
// IF its node based we need to add the node-tag to it as well
9292
(config[data.MessageId] && config[data.MessageId].tagID ) ? tg['nodeID'] = data[config[data.MessageId].tagID] : '';
93-
93+
9494
influx.writeMeasurement((config[data.MessageId] && config[data.MessageId].serie ) ? config[data.MessageId].serie: 'generic', [
9595
{
9696
tags: tg,
@@ -154,13 +154,17 @@ var tag;
154154
// Parse new messages incomming from Batrium
155155
server.on('message',function(msg,info){
156156
payload = getPayload(msg);
157-
process.stdout.write("\x1b[34mData recieved from\x1b[0m " + payload.SystemId + " \x1b[34mand message is:\x1b[0m " + payload.MessageId + " \r");
158-
157+
//process.stdout.write("\x1b[34mData recieved from\x1b[0m " + payload.SystemId + " \x1b[34mand message is:\x1b[0m " + payload.MessageId + " \r");
158+
messageID = payload.MessageId.substring(0,2); // uggly but it just brings out the message id and removes the versiion
159159
if(payload.MessageId in messages) {
160160
// If error in message lets try/catch it so we dont rage quit
161161
try {
162162
obj = Object.assign(payload, eval(messages[payload.MessageId])(msg));
163163
if (debug) console.log(obj);
164+
// check if the message id is present in the config. This dont care what version is there if file exist
165+
if (config[messageID] && config[messageID].mqtt || config.all.mqtt) sendMqtt(payload.SystemId,payload.MessageId,obj);
166+
if (config[messageID] && config[messageID].influx || config.all.influx) sendInflux(obj, tag);
167+
// Below is used if you use messageid and version in the configuration file
164168
if (config[payload.MessageId] && config[payload.MessageId].mqtt || config.all.mqtt) sendMqtt(payload.SystemId,payload.MessageId,obj);
165169
if (config[payload.MessageId] && config[payload.MessageId].influx || config.all.influx) sendInflux(obj, tag);
166170
} catch (e) {

package.json

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
{
2+
"name": "watchmonudplistener",
3+
"version": "1.0.1",
4+
"description": "UDP listener for the Batrium Watchmon BMS system",
5+
"main": "index.js",
6+
"scripts": {
7+
"test": "echo \"Error: no test specified\" && exit 1"
8+
},
9+
10+
"keywords": [
11+
"solar",
12+
"inverter",
13+
"bms",
14+
"batrium",
15+
"watchmon"
16+
],
17+
"author": "Batrium",
18+
"contributors":
19+
{ "name" : "Daniel Römer"
20+
, "email" : "[email protected]"
21+
, "url" : "http://diytechandrepairs.nu/"
22+
},
23+
"bugs":
24+
{ "url" : "https://github.com/Batrium/WatchMonUdpListener/issues"
25+
},
26+
"license": "MIT",
27+
"dependencies": {
28+
"mqtt": "latest",
29+
"binary-parser": "latest",
30+
"influx": "latest"
31+
}
32+
}

payload/Msg_3e32_StatusRapid.js

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
module.exports = function()
2+
{
3+
var Parser = require('binary-parser').Parser;
4+
5+
// Category = Aggregated telemetry
6+
// Object = Rapid
7+
// Description = Combined status - Rapid
8+
// MsgLength = 50
9+
// Version = 2
10+
// Frequency = 300 mS
11+
// Support = Current
12+
// Valid from = SW 1.0.30
13+
this.parse_3e32 = function(msg)
14+
{
15+
var status = new Parser()
16+
.skip(8)
17+
.int16le('MinCellVolt', { formatter: (x) => {return x/1000;}})
18+
.int16le('MaxCellVolt', { formatter: (x) => {return x/1000;}})
19+
.uint8('MinCellVoltId')
20+
.uint8('MaxCellVoltId')
21+
.uint8('MinCellTemp', { formatter: (x) => {return x-40;}}) // temperature ºC
22+
.uint8('MaxCellTemp', { formatter: (x) => {return x-40;}}) // temperature ºC
23+
.uint8('MinCellTempId')
24+
.uint8('MaxCellTempId')
25+
.int16le('MinBypassAmp', { formatter: (x) => {return x/1000;}})
26+
.int16le('MaxBypassAmp', { formatter: (x) => {return x/1000;}})
27+
.uint8('MinBypassAmpId')
28+
.uint8('MaxBypassAmpId')
29+
.uint8('MinBypassTemp', { formatter: (x) => {return x-40;}}) // temperature ºC
30+
.uint8('MaxBypassTemp', { formatter: (x) => {return x-40;}}) // temperature ºC
31+
.uint8('MinBypassTempId', { formatter: (x) => {return x-40;}})
32+
.uint8('MaxBypassTempId', { formatter: (x) => {return x-40;}})
33+
.int16le('AvgCellVolt', { formatter: (x) => {return x/1000;}})
34+
.uint8('AvgCellTemp', { formatter: (x) => {return x-40;}}) // temperature ºC
35+
.uint8('NumOfCellsAboveInitialBypass')
36+
.uint8('NumOfCellsAboveFinalBypass')
37+
.uint8('NumOfCellsInBypass')
38+
.uint8('NumOfCellsOverdue')
39+
.uint8('NumOfCellsActive')
40+
.uint8('NumOfCellsInSystem')
41+
.uint8('CmuTxOpStatusId')
42+
.uint8('CmuRxOpStatusId')
43+
.uint8('CmuRxOpStatusUSN')
44+
.int16le('ShuntVoltage', { formatter: (x) => {return x/100;}}) // voltage
45+
.floatle('ShuntCurrent', { formatter: (x) => {return x/1000;}}) // amp
46+
.floatle('ShuntPowerVA', { formatter: (x) => {return x/1000;}}) // kW
47+
48+
return status.parse(msg);
49+
}
50+
}

payload/Msg_4a35_HwSystemSetup.js

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
module.exports = function()
2+
{
3+
var Parser = require('binary-parser').Parser;
4+
5+
// Category = Hardware Configuration
6+
// Object = System
7+
// MsgLength = 76
8+
// Description = System setup configuration
9+
// Version = 5
10+
// Frequency = 20 seconds
11+
// Support = Current
12+
// Valid from = SW 1.0.30
13+
this.parse_4a35 = function(msg)
14+
{
15+
var status = new Parser()
16+
.skip(8)
17+
.int16le('HwSystemSetupVers')
18+
.string('SystemCode', { encoding: 'ascii', length: 8 })
19+
.string('SysName', { encoding: 'ascii', length: 20 })
20+
.string('AssetCode', { encoding: 'ascii', length: 20 })
21+
.uint8('AllowTechAuth') // boolean 0 = Off , 1 = On
22+
.uint8('AllowQuickSession') // boolean 0 = Off , 1 = On
23+
.uint32le('QuickSessionInterval', { formatter: (x) => {return x/1000;}}) // seconds
24+
.int16le('SystemPresetId')
25+
.int16le('SystemFirmwareVersion')
26+
.int16le('SystemHardwareVersion')
27+
.uint32le('SystemSerialNo')
28+
.uint8('ShowScheduler') // boolean 0 = Off , 1 = On
29+
.uint8('ShowStripCycle') // boolean 0 = Off , 1 = On
30+
31+
return status.parse(msg);
32+
}
33+
}
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
module.exports = function()
2+
{
3+
var Parser = require('binary-parser').Parser;
4+
5+
// Category = Hardware Configuration
6+
// Object = Cellmon
7+
// MsgLength = 53
8+
// Description = Hardware cellmon setup configuration
9+
// Version = 5
10+
// Frequency = 20 seconds
11+
// Support = Current
12+
// Valid from = SW 1.0.30
13+
this.parse_4b35 = function(msg)
14+
{
15+
var status = new Parser()
16+
.skip(8)
17+
.uint8('HwCellmonSetupVers')
18+
.uint8('HwCellmonBattTypeID') /* BattTypes
19+
Custom = 0,
20+
Li-FePO4 Typical = 1,
21+
Li-Ion Performance = 2,
22+
Li-Ion LongLife = 3,
23+
Li-FePO4 Longlife = 4, */
24+
.uint8( 'HwCellmonFirstID')
25+
.uint8( 'HwCellmonLastID')
26+
.int16le('HwCellmonNomCellVolt', { formatter: (x) => {return x/1000;}})
27+
.int16le('HwCellmonLoCellVolt', { formatter: (x) => {return x/1000;}})
28+
.int16le('HwCellmonHiCellVolt', { formatter: (x) => {return x/1000;}})
29+
.int16le('HwCellmonBypassVoltLevel', { formatter: (x) => {return x/1000;}})
30+
.int16le('HwCellmonBypassAmpLimit', { formatter: (x) => {return x/1000;}})
31+
.uint8( 'HwCellmonBypassTempLimit', { formatter: (x) => {return x-40;}}) // temperature ºC
32+
.uint8( 'HwCellmonLoCellTemp', { formatter: (x) => {return x-40;}}) // temperature ºC
33+
.uint8( 'HwCellmonHiCellTemp', { formatter: (x) => {return x-40;}}) // temperature ºC
34+
.uint8( 'HwCellmonDiffNomCellsInSeries') // Boolean 0 = Off , 1 = On
35+
.uint8( 'HwCellmonNomCellsInSeries')
36+
.uint8( 'HwCellmonAllowEntireRange') // Boolean 0 = Off , 1 = On
37+
.uint8( 'HwCellmonEntireFirstID')
38+
.uint8( 'HwCellmonEntireLastID')
39+
.uint8( 'HwCellmonBypassExtraMode') /* BypassExtraModes
40+
None = 0,
41+
Idle Shunt = 1,
42+
Same CellVolt = 2,
43+
AutoLevel = 3, */
44+
.int16le('HwCellmonBypassExtraInterval', { formatter: (x) => {return x/1000;}}) // seconds
45+
.uint8( 'HwCellmonCellMonTypeID') /* CellMonTypes
46+
Custom = 0,
47+
GenMon 2W = 1,
48+
GenMon 8W = 2,
49+
LongMon = 3,
50+
BlockMonM8 = 4,
51+
BlockMonM14 = 5,
52+
EndMon = 6,
53+
ManyMon = 7, */
54+
.floatle('HwCellmonBypassImpedance', { formatter: (x) => {return x/1000;}})
55+
.int16le('HwCellmonBypassLoVoltCutout', { formatter: (x) => {return x/1000;}})
56+
.int16le('HwCellmonBypassShuntChargeLimit', { formatter: (x) => {return x/100;}}) // amps
57+
.int16le('HwCellmonBypassShuntDischgLimit', { formatter: (x) => {return x/100;}}) // amps
58+
.uint8( 'HwCellmonBypassShuntSocLo', { formatter: (x) => {return (x-5)/2;}}) // percent
59+
.int16le('HwCellmonBypassCellVoltBanding', { formatter: (x) => {return x/1000;}})
60+
.int16le('HwCellmonBypassCellVoltDiff', { formatter: (x) => {return x/1000;}})
61+
.int16le('HwCellmonBypassStableInterval', { formatter: (x) => {return x/1000;}}) // seconds
62+
.int16le('HwCellmonBypassExtraAmpLimit', { formatter: (x) => {return x/1000;}}) // amps
63+
64+
return status.parse(msg);
65+
}
66+
}

0 commit comments

Comments
 (0)