@cryptotaxi247 / netdata-1 / commits / 74456806d

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;