diff options
author | BrainDoctor <github.account@chrigel.net> | 2017-07-04 21:17:56 +0200 |
---|---|---|
committer | BrainDoctor <github.account@chrigel.net> | 2017-07-05 02:11:28 +0200 |
commit | 66d8f45e39a9d884ca900dee57d7044989fc52a9 (patch) | |
tree | a697e028e4632b6af48768adee191bafbe36209e /node.d | |
parent | d808708cab09a35e48fc6770a1eae03202db9385 (diff) |
First implementation
Diffstat (limited to 'node.d')
-rw-r--r-- | node.d/fronius.node.js | 302 |
1 files changed, 302 insertions, 0 deletions
diff --git a/node.d/fronius.node.js b/node.d/fronius.node.js new file mode 100644 index 0000000000..3200f89bbc --- /dev/null +++ b/node.d/fronius.node.js @@ -0,0 +1,302 @@ +'use strict'; + +// This program will connect to one or more Fronius Symo Inverters. +// to get the Solar Power Generated (current, today). + +// example configuration in /etc/netdata/node.d/fronius.conf +/* + { + "enable_autodetect": false, + "update_every": 5, + "servers": [ + { + "name": "plant1", + "hostname": "10.0.1.1", + "update_every": 10, + "api_path": "/solar_api/v1/GetPowerFlowRealtimeData.fcgi" + }, + { + "name": "plant2", + "hostname": "10.0.2.1", + "update_every": 15, + "api_path": "/solar_api/v1/GetPowerFlowRealtimeData.fcgi" + } + ] + } + */ + + +var url = require('url'); +var http = require('http'); +var netdata = require('netdata'); + +netdata.debug('loaded ' + __filename + ' plugin'); + +const power_grid_id = 'p_grid'; +const power_pv_id = 'p_pv'; +const power_accu_id = 'p_akku'; // not my typo! Using the ID from the API +const consumption_load_id = 'p_load'; + +var fronius = { + name: __filename, + enable_autodetect: false, + update_every: 3, + base_priority: 60000, + charts: {}, + + createBasicDimension: function (id, name) { + return { + id: id, // the unique id of the dimension + name: name, // the name of the dimension + algorithm: netdata.chartAlgorithms.absolute,// the id of the netdata algorithm + multiplier: 1, // the multiplier + divisor: 1, // the divisor + hidden: false // is hidden (boolean) + } + }, + + // Gets the site power chart. Will be created if not existing. + getSitePowerChart: function (service, id) { + + var chart = fronius.charts[id]; + if (fronius.isDefined(chart)) return chart; + + var dim = {}; + dim[power_grid_id] = this.createBasicDimension(power_grid_id, "Grid"); + dim[power_pv_id] = this.createBasicDimension(power_pv_id, "Photovoltaics"); + dim[power_accu_id] = this.createBasicDimension(power_accu_id, "Accumulator"); + + chart = { + id: id, // the unique id of the chart + name: '', // the unique name of the chart + title: service.name + ' Current Site Power', // the title of the chart + units: 'W', // the units of the chart dimensions + family: 'Power', // the family of the chart + context: 'fronius.power', // the context of the chart + type: netdata.chartTypes.line, // the type of the chart + priority: fronius.base_priority + 1, // the priority relative to others in the same family + update_every: service.update_every, // the expected update frequency of the chart + dimensions: dim + }; + chart = service.chart(id, chart); + fronius.charts[id] = chart; + + return chart; + }, + + // Gets the site consumption chart. Will be created if not existing. + getSiteConsumptionChart: function (service, id) { + + var chart = fronius.charts[id]; + if (fronius.isDefined(chart)) return chart; + var dim = {}; + dim[consumption_load_id] = this.createBasicDimension(consumption_load_id, "Load"); + + chart = { + id: id, // the unique id of the chart + name: '', // the unique name of the chart + title: service.name + ' Current Load', // the title of the chart + units: 'W', // the units of the chart dimensions + family: 'Consumption', // the family of the chart + context: 'fronius.consumption', // the context of the chart + type: netdata.chartTypes.area, // the type of the chart + priority: fronius.base_priority + 2, // the priority relative to others in the same family + update_every: service.update_every, // the expected update frequency of the chart + dimensions: dim + }; + chart = service.chart(id, chart); + fronius.charts[id] = chart; + + return chart; + }, + + // Gets the inverter power chart. Will be created if not existing. + // Needs the array of inverters in order to create a chart with all inverters as dimensions + getInverterPowerChart: function (service, chartId, inverters) { + + var chart = fronius.charts[chartId]; + if (fronius.isDefined(chart)) return chart; + + var dim = {}; + + var inverter_count = Object.keys(inverters).length; + var inverter = inverters[inverter_count.toString()]; + var i = 1; + for (i; i <= inverter_count; i++) { + if (fronius.isUndefined(inverter)) { + netdata.error("Expected an Inverter with a numerical name! " + + "Have a look at your JSON output to verify."); + continue; + } + dim[i.toString()] = this.createBasicDimension("inverter_" + i, "Inverter " + i); + } + + chart = { + id: chartId, // the unique id of the chart + name: '', // the unique name of the chart + title: service.name + ' Current Inverter Output', // the title of the chart + units: 'W', // the units of the chart dimensions + family: 'Inverters', // the family of the chart + context: 'fronius.inverter', // the context of the chart + type: netdata.chartTypes.stacked, // the type of the chart + priority: fronius.base_priority + 3, // the priority relative to others in the same family + update_every: service.update_every, // the expected update frequency of the chart + dimensions: dim + }; + chart = service.chart(chartId, chart); + fronius.charts[chartId] = chart; + + return chart; + }, + + // Gets the inverter energy production chart for today. Will be created if not existing. + // Needs the array of inverters in order to create a chart with all inverters as dimensions + getInverterEnergyTodayChart: function (service, chartId, inverters) { + + var chart = fronius.charts[chartId]; + if (fronius.isDefined(chart)) return chart; + + var dim = {}; + + var inverter_count = Object.keys(inverters).length; + var inverter = inverters[inverter_count.toString()]; + var i = 1; + for (i; i <= inverter_count; i++) { + if (fronius.isUndefined(inverter)) { + netdata.error("Expected an Inverter with a numerical name! " + + "Have a look at your JSON output to verify."); + continue; + } + dim[i.toString()] = { + id: 'inverter_' + i, // the unique id of the dimension + name: 'Inverter ' + i, // the name of the dimension + algorithm: netdata.chartAlgorithms.absolute,// the id of the netdata algorithm + multiplier: 1, // the multiplier + divisor: 1000, // the divisor + hidden: false // is hidden (boolean) + }; + } + + chart = { + id: chartId, // the unique id of the chart + name: '', // the unique name of the chart + title: service.name + ' Inverter Energy production for today', // the title of the chart + units: 'kWh', // the units of the chart dimensions + family: 'Inverters', // the family of the chart + context: 'fronius.inverter', // the context of the chart + type: netdata.chartTypes.stacked, // the type of the chart + priority: fronius.base_priority + 4, // the priority relative to others in the same family + update_every: service.update_every, // the expected update frequency of the chart + dimensions: dim + }; + chart = service.chart(chartId, chart); + fronius.charts[chartId] = chart; + + return chart; + }, + + + processResponse: function (service, content) { + if (content === null) return; + var json = JSON.parse(content); + // validating response + if (fronius.isUndefined(json.Body)) return; + if (fronius.isUndefined(json.Body.Data)) return; + if (fronius.isUndefined(json.Body.Data.Site)) return; + if (fronius.isUndefined(json.Body.Data.Inverters)) return; + + // add the service + if (service.added !== true) + service.commit(); + + var site = json.Body.Data.Site; + + // Site Current Power Chart + service.begin(fronius.getSitePowerChart(service, 'fronius_' + service.name + '.power')); + service.set(power_grid_id, Math.round(site.P_Grid)); + service.set(power_pv_id, Math.round(site.P_PV)); + service.set(power_accu_id, Math.round(site.P_Akku)); + service.end(); + + // Site Consumption Chart + var consumption = site.P_Load; + if (consumption === null) consumption = 0; + consumption *= -1; + + service.begin(fronius.getSiteConsumptionChart(service, 'fronius_' + service.name + '.consumption')); + service.set(consumption_load_id, Math.round(consumption)); + service.end(); + + // Inverters + var inverters = json.Body.Data.Inverters; + var inverter_count = Object.keys(inverters).length; + if (inverter_count <= 0) return; + var i = 1; + for (i; i <= inverter_count; i++) { + var inverter = inverters[i]; + if (fronius.isUndefined(inverter)) continue; + netdata.debug("Setting values"); + service.begin(fronius.getInverterPowerChart(service, 'fronius_' + service.name + '.inverters.output', inverters)); + service.set(i.toString(), Math.round(inverter.P)); + service.end(); + service.begin(fronius.getInverterEnergyTodayChart(service, 'fronius_' + service.name + '.inverters.today', inverters)); + service.set(i.toString(), Math.round(inverter.E_Day)); + service.end(); + } + }, + + // module.serviceExecute() + // this function is called only from this module + // its purpose is to prepare the request and call + // netdata.serviceExecute() + serviceExecute: function (name, uri, update_every) { + netdata.debug(this.name + ': ' + name + ': url: ' + uri + ', update_every: ' + update_every); + + var service = netdata.service({ + name: name, + request: netdata.requestFromURL('http://' + uri), + update_every: update_every, + module: this + }); + service.request.method = 'GET'; + service.execute(this.processResponse); + }, + + + configure: function (config) { + if (fronius.isUndefined(config.servers)) return 0; + var added = 0; + var len = config.servers.length; + while (len--) { + var server = config.servers[len]; + if (fronius.isUndefined(server.update_every)) server.update_every = this.update_every; + + var url = server.hostname + server.api_path; + this.serviceExecute(server.name, url, server.update_every); + added++; + } + return added; + }, + + // module.update() + // this is called repeatedly to collect data, by calling + // netdata.serviceExecute() + update: function (service, callback) { + service.execute(function (serv, data) { + service.module.processResponse(serv, data); + callback(); + }); + }, + + isUndefined: function (value) { + return typeof value === 'undefined'; + }, + + isDefined: function (value) { + return typeof value !== 'undefined'; + } + + +}; + +module.exports = fronius; |