First implementation
BrainDoctor committed
Jul 5, 2017 at 19:16 UTC
74456806d18d22b57911be316a5573f89a32f7c8
1 file changed
+82
-208
node.d/stiebeleltron.node.js
+82
-208
@@ -1,31 +1,25 @@
1
'use strict';
2
3
-// This program will connect to one or more Fronius Symo Inverters.
4
-// to get the Solar Power Generated (current, today).
3
+// This program will connect to one Stiebel Eltron ISG for heatpump heating.
4
+// to get the heat pump metrics.
5
6
-// example configuration in netdata/conf.d/node.d/fronius.conf.md
6
+// example configuration in netdata/conf.d/node.d/stiebeleltron.conf.md
7
8
-var url = require('url');
9
-var http = require('http');
10
-var netdata = require('netdata');
8
+var url = require("url");
9
+var http = require("http");
10
+var netdata = require("netdata");
11
12
-netdata.debug('loaded ' + __filename + ' plugin');
12
+netdata.debug("loaded " + __filename + " plugin");
13
14
-const power_grid_id = 'p_grid';
15
-const power_pv_id = 'p_pv';
16
-const power_accu_id = 'p_akku'; // not my typo! Using the ID from the API
17
-const consumption_load_id = 'p_load';
18
-const autonomy_id = 'rel_autonomy';
19
-const consumption_self_id = 'rel_selfconsumption';
20
-
21
-var fronius = {
22
- name: "Fronius",
14
+var stiebeleltron = {
15
+ name: "Stiebel Eltron",
16
enable_autodetect: false,
24
- update_every: 5,
17
+ update_every: 10,
18
base_priority: 60000,
19
charts: {},
20
+ pages: {},
21
28
- createBasicDimension: function (id, name) {
22
+ createBasicDimension(id, name) {
23
return {
24
id: id, // the unique id of the dimension
25
name: name, // the name of the dimension
@@ -33,136 +27,60 @@ var fronius = {
27
multiplier: 1, // the multiplier
28
divisor: 1, // the divisor
29
hidden: false // is hidden (boolean)
36
- }
37
- },
38
-
39
- // Gets the site power chart. Will be created if not existing.
40
- getSitePowerChart: function (service, id) {
41
-
42
- var chart = fronius.charts[id];
43
- if (fronius.isDefined(chart)) return chart;
44
-
45
- var dim = {};
46
- dim[power_grid_id] = this.createBasicDimension(power_grid_id, "Grid");
47
- dim[power_pv_id] = this.createBasicDimension(power_pv_id, "Photovoltaics");
48
- dim[power_accu_id] = this.createBasicDimension(power_accu_id, "Accumulator");
49
-
50
- chart = {
51
- id: id, // the unique id of the chart
52
- name: '', // the unique name of the chart
53
- title: service.name + ' Current Site Power', // the title of the chart
54
- units: 'W', // the units of the chart dimensions
55
- family: 'Power', // the family of the chart
56
- context: 'fronius.power', // the context of the chart
57
- type: netdata.chartTypes.area, // the type of the chart
58
- priority: fronius.base_priority + 1, // the priority relative to others in the same family
59
- update_every: service.update_every, // the expected update frequency of the chart
60
- dimensions: dim
30
};
62
- chart = service.chart(id, chart);
63
- fronius.charts[id] = chart;
64
-
65
- return chart;
31
},
32
68
- // Gets the site consumption chart. Will be created if not existing.
69
- getSiteConsumptionChart: function (service, id) {
33
+ processResponse(service, html) {
34
+ if (html === null) return;
35
71
- var chart = fronius.charts[id];
72
- if (fronius.isDefined(chart)) return chart;
73
- var dim = {};
74
- dim[consumption_load_id] = this.createBasicDimension(consumption_load_id, "Load");
75
-
76
- chart = {
77
- id: id, // the unique id of the chart
78
- name: '', // the unique name of the chart
79
- title: service.name + ' Current Load', // the title of the chart
80
- units: 'W', // the units of the chart dimensions
81
- family: 'Consumption', // the family of the chart
82
- context: 'fronius.consumption', // the context of the chart
83
- type: netdata.chartTypes.area, // the type of the chart
84
- priority: fronius.base_priority + 2, // the priority relative to others in the same family
85
- update_every: service.update_every, // the expected update frequency of the chart
86
- dimensions: dim
87
- };
88
- chart = service.chart(id, chart);
89
- fronius.charts[id] = chart;
36
+ // add the service
37
+ service.commit();
38
+
39
+ var page = stiebeleltron.pages[service.name];
40
+ var categories = page.categories;
41
+ var categoriesCount = categories.length;
42
+ while (categoriesCount--) {
43
+ var category = categories[categoriesCount];
44
+ var context = {
45
+ html: html,
46
+ service: service,
47
+ category: category,
48
+ page: page
49
+ };
50
+ stiebeleltron.processCategory(context);
51
91
- return chart;
52
+ }
53
},
54
94
-
95
- // Gets the site consumption chart. Will be created if not existing.
96
- getSiteAutonomyChart: function (service, id) {
97
- var chart = fronius.charts[id];
98
- if (fronius.isDefined(chart)) return chart;
99
- var dim = {};
100
- dim[autonomy_id] = this.createBasicDimension(autonomy_id, "Autonomy");
101
- dim[consumption_self_id] = this.createBasicDimension(consumption_self_id, "Self Consumption");
102
-
103
- chart = {
104
- id: id, // the unique id of the chart
105
- name: '', // the unique name of the chart
106
- title: service.name + ' Current Autonomy', // the title of the chart
107
- units: '%', // the units of the chart dimensions
108
- family: 'Autonomy', // the family of the chart
109
- context: 'fronius.autonomy', // the context of the chart
110
- type: netdata.chartTypes.area, // the type of the chart
111
- priority: fronius.base_priority + 3, // the priority relative to others in the same family
112
- update_every: service.update_every, // the expected update frequency of the chart
113
- dimensions: dim
114
- };
115
- chart = service.chart(id, chart);
116
- fronius.charts[id] = chart;
117
-
118
- return chart;
55
+ processCategory(context) {
56
+ var charts = context.category.charts;
57
+ var chartCount = charts.length;
58
+ while (chartCount--) {
59
+ var chart = charts[chartCount];
60
+ context.chartDefinition = chart;
61
+ stiebeleltron.processChart(context);
62
+ }
63
},
64
121
- // Gets the inverter power chart. Will be created if not existing.
122
- // Needs the array of inverters in order to create a chart with all inverters as dimensions
123
- getInverterPowerChart: function (service, chartId, inverters) {
65
125
- var chart = fronius.charts[chartId];
126
- if (fronius.isDefined(chart)) return chart;
127
-
128
- var dim = {};
66
+ processChart(context) {
67
+ var dimensions = context.chartDefinition.dimensions;
68
+ var dimensionCount = dimensions.length;
69
+ context.service.begin(stiebeleltron.getChartUsing(context));
70
130
- var inverter_count = Object.keys(inverters).length;
131
- var inverter = inverters[inverter_count.toString()];
132
- var i = 1;
133
- for (i; i <= inverter_count; i++) {
134
- if (fronius.isUndefined(inverter)) {
135
- netdata.error("Expected an Inverter with a numerical name! " +
136
- "Have a look at your JSON output to verify.");
137
- continue;
138
- }
139
- dim[i.toString()] = this.createBasicDimension("inverter_" + i, "Inverter " + i);
71
+ while(dimensionCount--) {
72
+ var dimension = dimensions[dimensionCount];
73
+ stiebeleltron.processDimension(dimension, context);
74
}
141
-
142
- chart = {
143
- id: chartId, // the unique id of the chart
144
- name: '', // the unique name of the chart
145
- title: service.name + ' Current Inverter Output', // the title of the chart
146
- units: 'W', // the units of the chart dimensions
147
- family: 'Inverters', // the family of the chart
148
- context: 'fronius.inverter', // the context of the chart
149
- type: netdata.chartTypes.stacked, // the type of the chart
150
- priority: fronius.base_priority + 4, // the priority relative to others in the same family
151
- update_every: service.update_every, // the expected update frequency of the chart
152
- dimensions: dim
153
- };
154
- chart = service.chart(chartId, chart);
155
- fronius.charts[chartId] = chart;
156
-
157
- return chart;
75
+ context.service.end();
76
},
77
160
- // Gets the inverter energy production chart for today. Will be created if not existing.
161
- // Needs the array of inverters in order to create a chart with all inverters as dimensions
162
- getInverterEnergyTodayChart: function (service, chartId, inverters) {
163
-
164
- var chart = fronius.charts[chartId];
165
- if (fronius.isDefined(chart)) return chart;
78
+ getChartUsing(context) {
79
+ var chartId = "stiebeleltron_" + context.page.id +
80
+ "." + context.category.id +
81
+ "." + context.chartDefinition.id;
82
+ var chart = stiebeleltron.charts[chartId];
83
+ if (stiebeleltron.isDefined(chart)) return chart;
84
85
var dim = {};
86
@@ -170,7 +88,7 @@ var fronius = {
88
var inverter = inverters[inverter_count.toString()];
89
var i = 1;
90
for (i; i <= inverter_count; i++) {
173
- if (fronius.isUndefined(inverter)) {
91
+ if (stiebeleltron.isUndefined(inverter)) {
92
netdata.error("Expected an Inverter with a numerical name! " +
93
"Have a look at your JSON output to verify.");
94
continue;
@@ -188,81 +106,35 @@ var fronius = {
106
chart = {
107
id: chartId, // the unique id of the chart
108
name: '', // the unique name of the chart
191
- title: service.name + ' Inverter Energy production for today', // the title of the chart
192
- units: 'kWh', // the units of the chart dimensions
109
+ title: service.name + " " + chartDefinition.title, // the title of the chart
110
+ units: chartDefinition.unit, // the units of the chart dimensions
111
family: 'Inverters', // the family of the chart
194
- context: 'fronius.inverter', // the context of the chart
112
+ context: 'stiebeleltron.inverter', // the context of the chart
113
type: netdata.chartTypes.stacked, // the type of the chart
196
- priority: fronius.base_priority + 5, // the priority relative to others in the same family
114
+ priority: stiebeleltron.base_priority + 5, // the priority relative to others in the same family
115
update_every: service.update_every, // the expected update frequency of the chart
116
dimensions: dim
117
};
118
chart = service.chart(chartId, chart);
201
- fronius.charts[chartId] = chart;
119
+ stiebeleltron.charts[chartId] = chart;
120
121
return chart;
122
},
123
124
+ processDimension(dimension, context) {
125
+ var value = stiebeleltron.parseRegex(dimension.regex, context.html);
126
+ context.service.set(dimension.name, Math.round(value));
127
+ },
128
207
- processResponse: function (service, content) {
208
- if (content === null) return;
209
- var json = JSON.parse(content);
210
- // validating response
211
- if (fronius.isUndefined(json.Body)) return;
212
- if (fronius.isUndefined(json.Body.Data)) return;
213
- if (fronius.isUndefined(json.Body.Data.Site)) return;
214
- if (fronius.isUndefined(json.Body.Data.Inverters)) return;
215
-
216
- // add the service
217
- if (service.added !== true) service.commit();
218
-
219
- var site = json.Body.Data.Site;
220
-
221
- // Site Current Power Chart
222
- service.begin(fronius.getSitePowerChart(service, 'fronius_' + service.name + '.power'));
223
- service.set(power_grid_id, Math.round(site.P_Grid));
224
- service.set(power_pv_id, Math.round(site.P_PV));
225
- service.set(power_accu_id, Math.round(site.P_Akku));
226
- service.end();
227
-
228
- // Site Consumption Chart
229
- var consumption = site.P_Load;
230
- if (consumption === null) consumption = 0;
231
- consumption *= -1;
232
-
233
- service.begin(fronius.getSiteConsumptionChart(service, 'fronius_' + service.name + '.consumption'));
234
- service.set(consumption_load_id, Math.round(consumption));
235
- service.end();
236
-
237
- // Site Autonomy Chart
238
- service.begin(fronius.getSiteAutonomyChart(service, 'fronius_' + service.name + '.autonomy'));
239
- service.set(autonomy_id, Math.round(site.rel_Autonomy));
240
- service.set(consumption_self_id, Math.round(site.rel_SelfConsumption));
241
- service.end();
129
+ parseRegex(regex, html) {
130
243
- // Inverters
244
- var inverters = json.Body.Data.Inverters;
245
- var inverter_count = Object.keys(inverters).length;
246
- if (inverter_count <= 0) return;
247
- var i = 1;
248
- for (i; i <= inverter_count; i++) {
249
- var inverter = inverters[i];
250
- if (fronius.isUndefined(inverter)) continue;
251
- netdata.debug("Setting values");
252
- service.begin(fronius.getInverterPowerChart(service, 'fronius_' + service.name + '.inverters.output', inverters));
253
- service.set(i.toString(), Math.round(inverter.P));
254
- service.end();
255
- service.begin(fronius.getInverterEnergyTodayChart(service, 'fronius_' + service.name + '.inverters.today', inverters));
256
- service.set(i.toString(), Math.round(inverter.E_Day));
257
- service.end();
258
- }
131
},
132
133
// module.serviceExecute()
134
// this function is called only from this module
135
// its purpose is to prepare the request and call
136
// netdata.serviceExecute()
265
- serviceExecute: function (name, uri, update_every) {
137
+ serviceExecute(name, uri, update_every) {
138
netdata.debug(this.name + ': ' + name + ': url: ' + uri + ', update_every: ' + update_every);
139
140
var service = netdata.service({
@@ -276,16 +148,20 @@ var fronius = {
148
},
149
150
279
- configure: function (config) {
280
- if (fronius.isUndefined(config.servers)) return 0;
151
+ configure(config) {
152
+ if (stiebeleltron.isUndefined(config.pages)) return 0;
153
var added = 0;
282
- var len = config.servers.length;
283
- while (len--) {
284
- var server = config.servers[len];
285
- if (fronius.isUndefined(server.update_every)) server.update_every = this.update_every;
286
-
287
- var url = server.hostname + server.api_path;
288
- this.serviceExecute(server.name, url, server.update_every);
154
+ var pageCount = config.pages.length;
155
+ while (pageCount--) {
156
+ var page = config.pages[pageCount];
157
+ // some validation
158
+ if (stiebeleltron.isUndefined(page.categories) || page.categories.length < 1) {
159
+ netdata.error("Your Stiebel Eltron config is invalid. Disabling plugin.");
160
+ return 0;
161
+ }
162
+ if (stiebeleltron.isUndefined(page.update_every)) page.update_every = this.update_every;
163
+ this.pages[page.id] = page;
164
+ this.serviceExecute(page.name, page.url, page.update_every);
165
added++;
166
}
167
return added;
@@ -294,22 +170,20 @@ var fronius = {
170
// module.update()
171
// this is called repeatedly to collect data, by calling
172
// netdata.serviceExecute()
297
- update: function (service, callback) {
173
+ update(service, callback) {
174
service.execute(function (serv, data) {
175
service.module.processResponse(serv, data);
176
callback();
177
});
178
},
179
304
- isUndefined: function (value) {
180
+ isUndefined(value) {
181
return typeof value === 'undefined';
182
},
183
308
- isDefined: function (value) {
184
+ isDefined(value) {
185
return typeof value !== 'undefined';
310
- }
311
-
312
-
186
+ },
187
};
188
315
-module.exports = fronius;
189
+module.exports = stiebeleltron;