diff options
Diffstat (limited to 'node.d')
-rw-r--r-- | node.d/Makefile.am | 17 | ||||
-rw-r--r-- | node.d/Makefile.in | 67 | ||||
-rw-r--r-- | node.d/README.md | 55 | ||||
-rw-r--r-- | node.d/fronius.node.js | 311 | ||||
-rw-r--r-- | node.d/node_modules/asn1-ber.js | 6 | ||||
-rw-r--r-- | node.d/node_modules/asn1.js | 20 | ||||
-rw-r--r-- | node.d/node_modules/ber/errors.js | 13 | ||||
-rw-r--r-- | node.d/node_modules/ber/index.js | 27 | ||||
-rw-r--r-- | node.d/node_modules/ber/reader.js | 261 | ||||
-rw-r--r-- | node.d/node_modules/ber/types.js | 36 | ||||
-rw-r--r-- | node.d/node_modules/ber/writer.js | 316 | ||||
-rw-r--r-- | node.d/node_modules/lib/ber/errors.js | 9 | ||||
-rw-r--r-- | node.d/node_modules/lib/ber/index.js | 17 | ||||
-rw-r--r-- | node.d/node_modules/lib/ber/reader.js | 269 | ||||
-rw-r--r-- | node.d/node_modules/lib/ber/types.js | 34 | ||||
-rw-r--r-- | node.d/node_modules/lib/ber/writer.js | 317 | ||||
-rw-r--r-- | node.d/node_modules/net-snmp.js | 2 | ||||
-rw-r--r-- | node.d/node_modules/netdata.js | 8 | ||||
-rw-r--r-- | node.d/snmp.node.js | 37 | ||||
-rw-r--r-- | node.d/stiebeleltron.node.js | 196 |
20 files changed, 1171 insertions, 847 deletions
diff --git a/node.d/Makefile.am b/node.d/Makefile.am index 28008aeb..7f67faa6 100644 --- a/node.d/Makefile.am +++ b/node.d/Makefile.am @@ -6,6 +6,7 @@ dist_node_DATA = \ fronius.node.js \ sma_webbox.node.js \ snmp.node.js \ + stiebeleltron.node.js \ $(NULL) nodemodulesdir=$(nodedir)/node_modules @@ -14,14 +15,14 @@ dist_nodemodules_DATA = \ node_modules/extend.js \ node_modules/pixl-xml.js \ node_modules/net-snmp.js \ - node_modules/asn1.js \ + node_modules/asn1-ber.js \ $(NULL) -nodemodulesberdir=$(nodedir)/node_modules/ber -dist_nodemodulesber_DATA = \ - node_modules/ber/index.js \ - node_modules/ber/errors.js \ - node_modules/ber/reader.js \ - node_modules/ber/types.js \ - node_modules/ber/writer.js \ +nodemoduleslibberdir=$(nodedir)/node_modules/lib/ber +dist_nodemoduleslibber_DATA = \ + node_modules/lib/ber/index.js \ + node_modules/lib/ber/errors.js \ + node_modules/lib/ber/reader.js \ + node_modules/lib/ber/types.js \ + node_modules/lib/ber/writer.js \ $(NULL) diff --git a/node.d/Makefile.in b/node.d/Makefile.in index 35024cb1..dd572ee8 100644 --- a/node.d/Makefile.in +++ b/node.d/Makefile.in @@ -81,7 +81,7 @@ host_triplet = @host@ subdir = node.d DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.am \ $(dist_node_DATA) $(dist_nodemodules_DATA) \ - $(dist_nodemodulesber_DATA) + $(dist_nodemoduleslibber_DATA) ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/ax_c___atomic.m4 \ $(top_srcdir)/m4/ax_c__generic.m4 $(top_srcdir)/m4/ax_c_lto.m4 \ @@ -144,9 +144,9 @@ am__uninstall_files_from_dir = { \ $(am__cd) "$$dir" && rm -f $$files; }; \ } am__installdirs = "$(DESTDIR)$(nodedir)" "$(DESTDIR)$(nodemodulesdir)" \ - "$(DESTDIR)$(nodemodulesberdir)" + "$(DESTDIR)$(nodemoduleslibberdir)" DATA = $(dist_node_DATA) $(dist_nodemodules_DATA) \ - $(dist_nodemodulesber_DATA) + $(dist_nodemoduleslibber_DATA) am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ACLOCAL = @ACLOCAL@ @@ -300,6 +300,7 @@ dist_node_DATA = \ fronius.node.js \ sma_webbox.node.js \ snmp.node.js \ + stiebeleltron.node.js \ $(NULL) nodemodulesdir = $(nodedir)/node_modules @@ -308,16 +309,16 @@ dist_nodemodules_DATA = \ node_modules/extend.js \ node_modules/pixl-xml.js \ node_modules/net-snmp.js \ - node_modules/asn1.js \ + node_modules/asn1-ber.js \ $(NULL) -nodemodulesberdir = $(nodedir)/node_modules/ber -dist_nodemodulesber_DATA = \ - node_modules/ber/index.js \ - node_modules/ber/errors.js \ - node_modules/ber/reader.js \ - node_modules/ber/types.js \ - node_modules/ber/writer.js \ +nodemoduleslibberdir = $(nodedir)/node_modules/lib/ber +dist_nodemoduleslibber_DATA = \ + node_modules/lib/ber/index.js \ + node_modules/lib/ber/errors.js \ + node_modules/lib/ber/reader.js \ + node_modules/lib/ber/types.js \ + node_modules/lib/ber/writer.js \ $(NULL) all: all-am @@ -395,27 +396,27 @@ uninstall-dist_nodemodulesDATA: @list='$(dist_nodemodules_DATA)'; test -n "$(nodemodulesdir)" || list=; \ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ dir='$(DESTDIR)$(nodemodulesdir)'; $(am__uninstall_files_from_dir) -install-dist_nodemodulesberDATA: $(dist_nodemodulesber_DATA) +install-dist_nodemoduleslibberDATA: $(dist_nodemoduleslibber_DATA) @$(NORMAL_INSTALL) - @list='$(dist_nodemodulesber_DATA)'; test -n "$(nodemodulesberdir)" || list=; \ + @list='$(dist_nodemoduleslibber_DATA)'; test -n "$(nodemoduleslibberdir)" || list=; \ if test -n "$$list"; then \ - echo " $(MKDIR_P) '$(DESTDIR)$(nodemodulesberdir)'"; \ - $(MKDIR_P) "$(DESTDIR)$(nodemodulesberdir)" || exit 1; \ + echo " $(MKDIR_P) '$(DESTDIR)$(nodemoduleslibberdir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(nodemoduleslibberdir)" || exit 1; \ fi; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ echo "$$d$$p"; \ done | $(am__base_list) | \ while read files; do \ - echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(nodemodulesberdir)'"; \ - $(INSTALL_DATA) $$files "$(DESTDIR)$(nodemodulesberdir)" || exit $$?; \ + echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(nodemoduleslibberdir)'"; \ + $(INSTALL_DATA) $$files "$(DESTDIR)$(nodemoduleslibberdir)" || exit $$?; \ done -uninstall-dist_nodemodulesberDATA: +uninstall-dist_nodemoduleslibberDATA: @$(NORMAL_UNINSTALL) - @list='$(dist_nodemodulesber_DATA)'; test -n "$(nodemodulesberdir)" || list=; \ + @list='$(dist_nodemoduleslibber_DATA)'; test -n "$(nodemoduleslibberdir)" || list=; \ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ - dir='$(DESTDIR)$(nodemodulesberdir)'; $(am__uninstall_files_from_dir) + dir='$(DESTDIR)$(nodemoduleslibberdir)'; $(am__uninstall_files_from_dir) tags TAGS: ctags CTAGS: @@ -457,7 +458,7 @@ check-am: all-am check: check-am all-am: Makefile $(DATA) installdirs: - for dir in "$(DESTDIR)$(nodedir)" "$(DESTDIR)$(nodemodulesdir)" "$(DESTDIR)$(nodemodulesberdir)"; do \ + for dir in "$(DESTDIR)$(nodedir)" "$(DESTDIR)$(nodemodulesdir)" "$(DESTDIR)$(nodemoduleslibberdir)"; do \ test -z "$$dir" || $(MKDIR_P) "$$dir"; \ done install: install-am @@ -512,7 +513,7 @@ info: info-am info-am: install-data-am: install-dist_nodeDATA install-dist_nodemodulesDATA \ - install-dist_nodemodulesberDATA + install-dist_nodemoduleslibberDATA install-dvi: install-dvi-am @@ -557,7 +558,7 @@ ps: ps-am ps-am: uninstall-am: uninstall-dist_nodeDATA uninstall-dist_nodemodulesDATA \ - uninstall-dist_nodemodulesberDATA + uninstall-dist_nodemoduleslibberDATA .MAKE: install-am install-strip @@ -565,16 +566,16 @@ uninstall-am: uninstall-dist_nodeDATA uninstall-dist_nodemodulesDATA \ ctags-am distclean distclean-generic distdir dvi dvi-am html \ html-am info info-am install install-am install-data \ install-data-am install-dist_nodeDATA \ - install-dist_nodemodulesDATA install-dist_nodemodulesberDATA \ - install-dvi install-dvi-am install-exec install-exec-am \ - install-html install-html-am install-info install-info-am \ - install-man install-pdf install-pdf-am install-ps \ - install-ps-am install-strip installcheck installcheck-am \ - installdirs maintainer-clean maintainer-clean-generic \ - mostlyclean mostlyclean-generic pdf pdf-am ps ps-am tags-am \ - uninstall uninstall-am uninstall-dist_nodeDATA \ - uninstall-dist_nodemodulesDATA \ - uninstall-dist_nodemodulesberDATA + install-dist_nodemodulesDATA \ + install-dist_nodemoduleslibberDATA install-dvi install-dvi-am \ + install-exec install-exec-am install-html install-html-am \ + install-info install-info-am install-man install-pdf \ + install-pdf-am install-ps install-ps-am install-strip \ + installcheck installcheck-am installdirs maintainer-clean \ + maintainer-clean-generic mostlyclean mostlyclean-generic pdf \ + pdf-am ps ps-am tags-am uninstall uninstall-am \ + uninstall-dist_nodeDATA uninstall-dist_nodemodulesDATA \ + uninstall-dist_nodemoduleslibberDATA # Tell versions [3.59,3.63) of GNU make to not export all variables. diff --git a/node.d/README.md b/node.d/README.md index 3c297790..6818d34d 100644 --- a/node.d/README.md +++ b/node.d/README.md @@ -61,3 +61,58 @@ Sample: If no configuration is given, the module will be disabled. Each `update_every` is optional, the default is `5`.
---
+
+# stiebel eltron
+
+This module collects metrics from the configured heat pump and hot water installation from Stiebel Eltron ISG web.
+See `netdata/conf.d/node.d/stiebeleltron.conf.md` for more details.
+
+**Requirements**
+ * Configuration file `stiebeleltron.conf` in the node.d netdata config dir (default: `/etc/netdata/node.d/stiebeleltron.conf`)
+ * Stiebel Eltron ISG web with network access (http), without password login
+
+The charts are configurable, however, the provided default configuration collects the following:
+
+1. **General**
+ * Outside temperature in C
+ * Condenser temperature in C
+ * Heating circuit pressure in bar
+ * Flow rate in l/min
+ * Output of water and heat pumps in %
+
+2. **Heating**
+ * Heat circuit 1 temperature in C (set/actual)
+ * Heat circuit 2 temperature in C (set/actual)
+ * Flow temperature in C (set/actual)
+ * Buffer temperature in C (set/actual)
+ * Pre-flow temperature in C
+
+3. **Hot Water**
+ * Hot water temperature in C (set/actual)
+
+4. **Room Temperature**
+ * Heat circuit 1 room temperature in C (set/actual)
+ * Heat circuit 2 room temperature in C (set/actual)
+
+5. **Eletric Reheating**
+ * Dual Mode Reheating temperature in C (hot water/heating)
+
+6. **Process Data**
+ * Remaining compressor rest time in s
+
+7. **Runtime**
+ * Compressor runtime hours (hot water/heating)
+ * Reheating runtime hours (reheating 1/reheating 2)
+
+8. **Energy**
+ * Compressor today in kWh (hot water/heating)
+ * Compressor Total in kWh (hot water/heating)
+
+
+### configuration
+
+The default configuration is provided in [netdata/conf.d/node.d/stiebeleltron.conf.md](https://github.com/firehol/netdata/blob/master/conf.d/node.d/stiebeleltron.conf.md). Just change the `update_every` (if necessary) and hostnames. **You may have to adapt the configuration to suit your needs and setup** (which might be different).
+
+If no configuration is given, the module will be disabled. Each `update_every` is optional, the default is `10`.
+
+---
diff --git a/node.d/fronius.node.js b/node.d/fronius.node.js index f771f6c3..7aa2c13b 100644 --- a/node.d/fronius.node.js +++ b/node.d/fronius.node.js @@ -1,15 +1,15 @@ -'use strict'; +"use strict"; // This program will connect to one or more Fronius Symo Inverters. // to get the Solar Power Generated (current, today). // example configuration in netdata/conf.d/node.d/fronius.conf.md -var url = require('url'); -var http = require('http'); -var netdata = require('netdata'); +var url = require("url"); +var http = require("http"); +var netdata = require("netdata"); -netdata.debug('loaded ' + __filename + ' plugin'); +netdata.debug("loaded " + __filename + " plugin"); var fronius = { name: "Fronius", @@ -39,24 +39,25 @@ var fronius = { }, // Gets the site power chart. Will be created if not existing. - getSitePowerChart: function (service, id) { + getSitePowerChart: function (service, suffix) { + var id = this.getChartId(service, suffix); var chart = fronius.charts[id]; if (fronius.isDefined(chart)) return chart; var dim = {}; - dim[fronius.powerGridId] = this.createBasicDimension(fronius.powerGridId, "Grid", 1); - dim[fronius.powerPvId] = this.createBasicDimension(fronius.powerPvId, "Photovoltaics", 1); - dim[fronius.powerAccuId] = this.createBasicDimension(fronius.powerAccuId, "Accumulator", 1); + dim[fronius.powerGridId] = this.createBasicDimension(fronius.powerGridId, "grid", 1); + dim[fronius.powerPvId] = this.createBasicDimension(fronius.powerPvId, "photovoltaics", 1); + dim[fronius.powerAccuId] = this.createBasicDimension(fronius.powerAccuId, "accumulator", 1); 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 + 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.area, // the type of the chart - priority: fronius.base_priority + 1, // the priority relative to others in the same family + 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 }; @@ -67,19 +68,20 @@ var fronius = { }, // Gets the site consumption chart. Will be created if not existing. - getSiteConsumptionChart: function (service, id) { + getSiteConsumptionChart: function (service, suffix) { + var id = this.getChartId(service, suffix); var chart = fronius.charts[id]; if (fronius.isDefined(chart)) return chart; var dim = {}; - dim[fronius.consumptionLoadId] = this.createBasicDimension(fronius.consumptionLoadId, "Load", 1); + dim[fronius.consumptionLoadId] = this.createBasicDimension(fronius.consumptionLoadId, "load", 1); 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 + 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 @@ -92,20 +94,21 @@ var fronius = { }, // Gets the site consumption chart. Will be created if not existing. - getSiteAutonomyChart: function (service, id) { + getSiteAutonomyChart: function (service, suffix) { + var id = this.getChartId(service, suffix); var chart = fronius.charts[id]; if (fronius.isDefined(chart)) return chart; var dim = {}; - dim[fronius.autonomyId] = this.createBasicDimension(fronius.autonomyId, "Autonomy", 1); - dim[fronius.consumptionSelfId] = this.createBasicDimension(fronius.consumptionSelfId, "Self Consumption", 1); + dim[fronius.autonomyId] = this.createBasicDimension(fronius.autonomyId, "autonomy", 1); + dim[fronius.consumptionSelfId] = this.createBasicDimension(fronius.consumptionSelfId, "self_consumption", 1); chart = { id: id, // the unique id of the chart - name: '', // the unique name of the chart - title: service.name + ' Current Autonomy', // the title of the chart - units: '%', // the units of the chart dimensions - family: 'autonomy', // the family of the chart - context: 'fronius.autonomy', // the context of the chart + name: "", // the unique name of the chart + title: service.name + " Current Autonomy", // the title of the chart + units: "%", // the units of the chart dimensions + family: "autonomy", // the family of the chart + context: "fronius.autonomy", // the context of the chart type: netdata.chartTypes.area, // 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 @@ -118,21 +121,22 @@ var fronius = { }, // Gets the site energy chart for today. Will be created if not existing. - getSiteEnergyTodayChart: function (service, chartId) { + getSiteEnergyTodayChart: function (service, suffix) { + var chartId = this.getChartId(service, suffix); var chart = fronius.charts[chartId]; if (fronius.isDefined(chart)) return chart; var dim = {}; - dim[fronius.energyTodayId] = this.createBasicDimension(fronius.energyTodayId, "Today", 1000); + dim[fronius.energyTodayId] = this.createBasicDimension(fronius.energyTodayId, "today", 1000); chart = { id: chartId, // the unique id of the chart - name: '', // the unique name of the chart - title: service.name + ' Energy production for today', // the title of the chart - units: 'kWh', // the units of the chart dimensions - family: 'energy', // the family of the chart - context: 'fronius.energy.today', // the context of the chart - type: netdata.chartTypes.area, // 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 + name: "", // the unique name of the chart + title: service.name + " Energy production for today",// the title of the chart + units: "kWh", // the units of the chart dimensions + family: "energy", // the family of the chart + context: "fronius.energy.today", // the context of the chart + type: netdata.chartTypes.area, // 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); @@ -142,21 +146,22 @@ var fronius = { }, // Gets the site energy chart for today. Will be created if not existing. - getSiteEnergyYearChart: function (service, chartId) { + getSiteEnergyYearChart: function (service, suffix) { + var chartId = this.getChartId(service, suffix); var chart = fronius.charts[chartId]; if (fronius.isDefined(chart)) return chart; var dim = {}; - dim[fronius.energyYearId] = this.createBasicDimension(fronius.energyYearId, "Year", 1000); + dim[fronius.energyYearId] = this.createBasicDimension(fronius.energyYearId, "year", 1000); chart = { - id: chartId, // the unique id of the chart - name: '', // the unique name of the chart - title: service.name + ' Energy production for this year', // the title of the chart - units: 'kWh', // the units of the chart dimensions - family: 'energy', // the family of the chart - context: 'fronius.energy.year', // the context of the chart - type: netdata.chartTypes.area, // the type of the chart - priority: fronius.base_priority + 5, // the priority relative to others in the same family - update_every: service.update_every, // the expected update frequency of the chart + id: chartId, // the unique id of the chart + name: "", // the unique name of the chart + title: service.name + " Energy production for this year",// the title of the chart + units: "kWh", // the units of the chart dimensions + family: "energy", // the family of the chart + context: "fronius.energy.year", // the context of the chart + type: netdata.chartTypes.area, // the type of the chart + priority: fronius.base_priority + 5, // 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); @@ -167,35 +172,30 @@ var fronius = { // 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) { - + getInverterPowerChart: function (service, suffix, inverters) { + var chartId = this.getChartId(service, suffix); var chart = fronius.charts[chartId]; if (fronius.isDefined(chart)) return chart; var dim = {}; - - var inverterCount = Object.keys(inverters).length; - var inverter = inverters[inverterCount.toString()]; - var i = 1; - for (i; i <= inverterCount; i++) { - if (fronius.isUndefined(inverter)) { - netdata.error("Expected an Inverter with a numerical name! " + - "Have a look at your JSON output to verify."); - continue; + for (var key in inverters) { + if (inverters.hasOwnProperty(key)) { + var name = key; + if (!isNaN(key)) name = "inverter_" + key; + dim[key] = this.createBasicDimension("inverter_" + key, name, 1); } - dim[i.toString()] = this.createBasicDimension("inverter_" + i, "Inverter " + i, 1); } 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.output', // the context of the chart - type: netdata.chartTypes.stacked, // the type of the 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.output", // the context of the chart + type: netdata.chartTypes.stacked, // the type of the chart priority: fronius.base_priority + 6, // the priority relative to others in the same family - update_every: service.update_every, // the expected update frequency of the chart + update_every: service.update_every, // the expected update frequency of the chart dimensions: dim }; chart = service.chart(chartId, chart); @@ -205,62 +205,126 @@ var fronius = { }, processResponse: function (service, content) { - if (content === null) return; - var json = JSON.parse(content); - if (!fronius.isResponseValid(json)) return; + var json = fronius.convertToJson(content); + if (json === null) return; // add the service service.commit(); + var chartDefinitions = fronius.parseCharts(service, json); + var chartCount = chartDefinitions.length; + while (chartCount--) { + var chartObj = chartDefinitions[chartCount]; + service.begin(chartObj.chart); + var dimCount = chartObj.dimensions.length; + while (dimCount--) { + var dim = chartObj.dimensions[dimCount]; + service.set(dim.name, dim.value); + } + service.end(); + } + }, + + parseCharts: function (service, json) { var site = json.Body.Data.Site; + return [ + this.parsePowerChart(service, site), + this.parseConsumptionChart(service, site), + this.parseAutonomyChart(service, site), + this.parseEnergyTodayChart(service, site), + this.parseEnergyYearChart(service, site), + this.parseInverterChart(service, json.Body.Data.Inverters) + ]; + }, - // Site Current Power Chart - service.begin(fronius.getSitePowerChart(service, 'fronius_' + service.name + '.power')); - service.set(fronius.powerGridId, Math.round(site.P_Grid)); - service.set(fronius.powerPvId, Math.round(site.P_PV)); - service.set(fronius.powerAccuId, Math.round(site.P_Akku)); - service.end(); - - // Site Consumption Chart - service.begin(fronius.getSiteConsumptionChart(service, 'fronius_' + service.name + '.consumption')); - service.set(fronius.consumptionLoadId, Math.round(Math.abs(site.P_Load))); - service.end(); - - // Site Autonomy Chart - service.begin(fronius.getSiteAutonomyChart(service, 'fronius_' + service.name + '.autonomy')); - service.set(fronius.autonomyId, Math.round(site.rel_Autonomy)); + parsePowerChart: function (service, site) { + return this.getChart(this.getSitePowerChart(service, "power"), + [ + this.getDimension(this.powerGridId, Math.round(site.P_Grid)), + this.getDimension(this.powerPvId, Math.round(Math.max(site.P_PV, 0))), + this.getDimension(this.powerAccuId, Math.round(site.P_Akku)) + ] + ); + }, + + parseConsumptionChart: function (service, site) { + return this.getChart(this.getSiteConsumptionChart(service, "consumption"), + [this.getDimension(this.consumptionLoadId, Math.round(Math.abs(site.P_Load)))] + ); + }, + + parseAutonomyChart: function (service, site) { var selfConsumption = site.rel_SelfConsumption; - service.set(fronius.consumptionSelfId, Math.round(selfConsumption === null ? 100 : selfConsumption)); - service.end(); - - // Site Energy Today Chart - service.begin(fronius.getSiteEnergyTodayChart(service, 'fronius_' + service.name + '.energy.today')); - service.set(fronius.energyTodayId, Math.round(site.E_Day)); - service.end(); - - // Site Energy Year Chart - service.begin(fronius.getSiteEnergyYearChart(service, 'fronius_' + service.name + '.energy.year')); - service.set(fronius.energyYearId, Math.round(site.E_Year)); - service.end(); - - // Inverters - var inverters = json.Body.Data.Inverters; - var inverterCount = Object.keys(inverters).length + 1; - while (inverterCount--) { - var inverter = inverters[inverterCount]; - if (fronius.isUndefined(inverter)) continue; - service.begin(fronius.getInverterPowerChart(service, 'fronius_' + service.name + '.inverters.output', inverters)); - service.set(inverterCount.toString(), Math.round(inverter.P)); - service.end(); + return this.getChart(this.getSiteAutonomyChart(service, "autonomy"), + [ + this.getDimension(this.autonomyId, Math.round(site.rel_Autonomy)), + this.getDimension(this.consumptionSelfId, Math.round(selfConsumption === null ? 100 : selfConsumption)) + ] + ); + }, + + parseEnergyTodayChart: function (service, site) { + return this.getChart(this.getSiteEnergyTodayChart(service, "energy.today"), + [this.getDimension(this.energyTodayId, Math.round(Math.max(site.E_Day, 0)))] + ); + }, + + parseEnergyYearChart: function (service, site) { + return this.getChart(this.getSiteEnergyYearChart(service, "energy.year"), + [this.getDimension(this.energyYearId, Math.round(Math.max(site.E_Year, 0)))] + ); + }, + + parseInverterChart: function (service, inverters) { + var dimensions = []; + for (var key in inverters) { + if (inverters.hasOwnProperty(key)) { + dimensions.push(this.getDimension(key, Math.round(inverters[key].P))); + } + } + return this.getChart(this.getInverterPowerChart(service, "inverters.output", inverters), dimensions); + }, + + getDimension: function (name, value) { + return { + name: name, + value: value + }; + }, + + getChart: function (chart, dimensions) { + return { + chart: chart, + dimensions: dimensions + }; + }, + + getChartId: function (service, suffix) { + return "fronius_" + service.name + "." + suffix; + }, + + convertToJson: function (httpBody) { + if (httpBody === null) return null; + var json = httpBody; + // can't parse if it's already a json object, + // the check enables easier testing if the httpBody is already valid JSON. + if (typeof httpBody !== "object") { + try { + json = JSON.parse(httpBody); + } catch (error) { + netdata.error("fronius: Got a response, but it is not valid JSON. Ignoring. Error: " + error.message); + return null; + } } + return this.isResponseValid(json) ? json : null; }, // some basic validation isResponseValid: function (json) { - if (fronius.isUndefined(json.Body)) return false; - if (fronius.isUndefined(json.Body.Data)) return false; - if (fronius.isUndefined(json.Body.Data.Site)) return false; - return fronius.isDefined(json.Body.Data.Inverters); + if (this.isUndefined(json.Body)) return false; + if (this.isUndefined(json.Body.Data)) return false; + if (this.isUndefined(json.Body.Data.Site)) return false; + return this.isDefined(json.Body.Data.Inverters); }, // module.serviceExecute() @@ -268,11 +332,11 @@ var fronius = { // 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); + netdata.debug(this.name + ": " + name + ": url: " + uri + ", update_every: " + update_every); var service = netdata.service({ name: name, - request: netdata.requestFromURL('http://' + uri), + request: netdata.requestFromURL("http://" + uri), update_every: update_every, module: this }); @@ -287,6 +351,7 @@ var fronius = { while (len--) { var server = config.servers[len]; if (fronius.isUndefined(server.update_every)) server.update_every = this.update_every; + if (fronius.areUndefined([server.name, server.hostname, server.api_path])) continue; var url = server.hostname + server.api_path; this.serviceExecute(server.name, url, server.update_every); @@ -306,11 +371,19 @@ var fronius = { }, isUndefined: function (value) { - return typeof value === 'undefined'; + return typeof value === "undefined"; + }, + + areUndefined: function (valueArray) { + var i = 0; + for (i; i < valueArray.length; i++) { + if (this.isUndefined(valueArray[i])) return true; + } + return false; }, isDefined: function (value) { - return typeof value !== 'undefined'; + return typeof value !== "undefined"; } }; diff --git a/node.d/node_modules/asn1-ber.js b/node.d/node_modules/asn1-ber.js new file mode 100644 index 00000000..57809f48 --- /dev/null +++ b/node.d/node_modules/asn1-ber.js @@ -0,0 +1,6 @@ + +var Ber = require('./lib/ber/index') + +exports.Ber = Ber +exports.BerReader = Ber.Reader +exports.BerWriter = Ber.Writer diff --git a/node.d/node_modules/asn1.js b/node.d/node_modules/asn1.js deleted file mode 100644 index d1766e7a..00000000 --- a/node.d/node_modules/asn1.js +++ /dev/null @@ -1,20 +0,0 @@ -// Copyright 2011 Mark Cavage <mcavage@gmail.com> All rights reserved. - -// If you have no idea what ASN.1 or BER is, see this: -// ftp://ftp.rsa.com/pub/pkcs/ascii/layman.asc - -var Ber = require('./ber/index'); - - - -///--- Exported API - -module.exports = { - - Ber: Ber, - - BerReader: Ber.Reader, - - BerWriter: Ber.Writer - -}; diff --git a/node.d/node_modules/ber/errors.js b/node.d/node_modules/ber/errors.js deleted file mode 100644 index ff21d4fa..00000000 --- a/node.d/node_modules/ber/errors.js +++ /dev/null @@ -1,13 +0,0 @@ -// Copyright 2011 Mark Cavage <mcavage@gmail.com> All rights reserved. - - -module.exports = { - - newInvalidAsn1Error: function(msg) { - var e = new Error(); - e.name = 'InvalidAsn1Error'; - e.message = msg || ''; - return e; - } - -}; diff --git a/node.d/node_modules/ber/index.js b/node.d/node_modules/ber/index.js deleted file mode 100644 index 4fb90aea..00000000 --- a/node.d/node_modules/ber/index.js +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright 2011 Mark Cavage <mcavage@gmail.com> All rights reserved. - -var errors = require('./errors'); -var types = require('./types'); - -var Reader = require('./reader'); -var Writer = require('./writer'); - - -///--- Exports - -module.exports = { - - Reader: Reader, - - Writer: Writer - -}; - -for (var t in types) { - if (types.hasOwnProperty(t)) - module.exports[t] = types[t]; -} -for (var e in errors) { - if (errors.hasOwnProperty(e)) - module.exports[e] = errors[e]; -} diff --git a/node.d/node_modules/ber/reader.js b/node.d/node_modules/ber/reader.js deleted file mode 100644 index 0a00e98e..00000000 --- a/node.d/node_modules/ber/reader.js +++ /dev/null @@ -1,261 +0,0 @@ -// Copyright 2011 Mark Cavage <mcavage@gmail.com> All rights reserved. - -var assert = require('assert'); - -var ASN1 = require('./types'); -var errors = require('./errors'); - - -///--- Globals - -var newInvalidAsn1Error = errors.newInvalidAsn1Error; - - - -///--- API - -function Reader(data) { - if (!data || !Buffer.isBuffer(data)) - throw new TypeError('data must be a node Buffer'); - - this._buf = data; - this._size = data.length; - - // These hold the "current" state - this._len = 0; - this._offset = 0; -} - -Object.defineProperty(Reader.prototype, 'length', { - enumerable: true, - get: function () { return (this._len); } -}); - -Object.defineProperty(Reader.prototype, 'offset', { - enumerable: true, - get: function () { return (this._offset); } -}); - -Object.defineProperty(Reader.prototype, 'remain', { - get: function () { return (this._size - this._offset); } -}); - -Object.defineProperty(Reader.prototype, 'buffer', { - get: function () { return (this._buf.slice(this._offset)); } -}); - - -/** - * Reads a single byte and advances offset; you can pass in `true` to make this - * a "peek" operation (i.e., get the byte, but don't advance the offset). - * - * @param {Boolean} peek true means don't move offset. - * @return {Number} the next byte, null if not enough data. - */ -Reader.prototype.readByte = function(peek) { - if (this._size - this._offset < 1) - return null; - - var b = this._buf[this._offset] & 0xff; - - if (!peek) - this._offset += 1; - - return b; -}; - - -Reader.prototype.peek = function() { - return this.readByte(true); -}; - - -/** - * Reads a (potentially) variable length off the BER buffer. This call is - * not really meant to be called directly, as callers have to manipulate - * the internal buffer afterwards. - * - * As a result of this call, you can call `Reader.length`, until the - * next thing called that does a readLength. - * - * @return {Number} the amount of offset to advance the buffer. - * @throws {InvalidAsn1Error} on bad ASN.1 - */ -Reader.prototype.readLength = function(offset) { - if (offset === undefined) - offset = this._offset; - - if (offset >= this._size) - return null; - - var lenB = this._buf[offset++] & 0xff; - if (lenB === null) - return null; - - if ((lenB & 0x80) == 0x80) { - lenB &= 0x7f; - - if (lenB == 0) - throw newInvalidAsn1Error('Indefinite length not supported'); - - if (lenB > 4) - throw newInvalidAsn1Error('encoding too long'); - - if (this._size - offset < lenB) - return null; - - this._len = 0; - for (var i = 0; i < lenB; i++) - this._len = (this._len << 8) + (this._buf[offset++] & 0xff); - - } else { - // Wasn't a variable length - this._len = lenB; - } - - return offset; -}; - - -/** - * Parses the next sequence in this BER buffer. - * - * To get the length of the sequence, call `Reader.length`. - * - * @return {Number} the sequence's tag. - */ -Reader.prototype.readSequence = function(tag) { - var seq = this.peek(); - if (seq === null) - return null; - if (tag !== undefined && tag !== seq) - throw newInvalidAsn1Error('Expected 0x' + tag.toString(16) + - ': got 0x' + seq.toString(16)); - - var o = this.readLength(this._offset + 1); // stored in `length` - if (o === null) - return null; - - this._offset = o; - return seq; -}; - - -Reader.prototype.readInt = function() { - return this._readTag(ASN1.Integer); -}; - - -Reader.prototype.readBoolean = function() { - return (this._readTag(ASN1.Boolean) === 0 ? false : true); -}; - - -Reader.prototype.readEnumeration = function() { - return this._readTag(ASN1.Enumeration); -}; - - -Reader.prototype.readString = function(tag, retbuf) { - if (!tag) - tag = ASN1.OctetString; - - var b = this.peek(); - if (b === null) - return null; - - if (b !== tag) - throw newInvalidAsn1Error('Expected 0x' + tag.toString(16) + - ': got 0x' + b.toString(16)); - - var o = this.readLength(this._offset + 1); // stored in `length` - - if (o === null) - return null; - - if (this.length > this._size - o) - return null; - - this._offset = o; - - if (this.length === 0) - return retbuf ? new Buffer(0) : ''; - - var str = this._buf.slice(this._offset, this._offset + this.length); - this._offset += this.length; - - return retbuf ? str : str.toString('utf8'); -}; - -Reader.prototype.readOID = function(tag) { - if (!tag) - tag = ASN1.OID; - - var b = this.readString(tag, true); - if (b === null) - return null; - - var values = []; - var value = 0; - - for (var i = 0; i < b.length; i++) { - var byte = b[i] & 0xff; - - value <<= 7; - value += byte & 0x7f; - if ((byte & 0x80) == 0) { - values.push(value); - value = 0; - } - } - - value = values.shift(); - values.unshift(value % 40); - values.unshift((value / 40) >> 0); - - return values.join('.'); -}; - - -Reader.prototype._readTag = function(tag) { - assert.ok(tag !== undefined); - - var b = this.peek(); - - if (b === null) - return null; - - if (b !== tag) - throw newInvalidAsn1Error('Expected 0x' + tag.toString(16) + - ': got 0x' + b.toString(16)); - - var o = this.readLength(this._offset + 1); // stored in `length` - if (o === null) - return null; - - if (this.length > 4) - throw newInvalidAsn1Error('Integer too long: ' + this.length); - - if (this.length > this._size - o) - return null; - this._offset = o; - - var fb = this._buf[this._offset]; - var value = 0; - - for (var i = 0; i < this.length; i++) { - value <<= 8; - value |= (this._buf[this._offset++] & 0xff); - } - - if ((fb & 0x80) == 0x80 && i !== 4) - value -= (1 << (i * 8)); - - return value >> 0; -}; - - - -///--- Exported API - -module.exports = Reader; diff --git a/node.d/node_modules/ber/types.js b/node.d/node_modules/ber/types.js deleted file mode 100644 index 8aea0001..00000000 --- a/node.d/node_modules/ber/types.js +++ /dev/null @@ -1,36 +0,0 @@ -// Copyright 2011 Mark Cavage <mcavage@gmail.com> All rights reserved. - - -module.exports = { - EOC: 0, - Boolean: 1, - Integer: 2, - BitString: 3, - OctetString: 4, - Null: 5, - OID: 6, - ObjectDescriptor: 7, - External: 8, - Real: 9, // float - Enumeration: 10, - PDV: 11, - Utf8String: 12, - RelativeOID: 13, - Sequence: 16, - Set: 17, - NumericString: 18, - PrintableString: 19, - T61String: 20, - VideotexString: 21, - IA5String: 22, - UTCTime: 23, - GeneralizedTime: 24, - GraphicString: 25, - VisibleString: 26, - GeneralString: 28, - UniversalString: 29, - CharacterString: 30, - BMPString: 31, - Constructor: 32, - Context: 128 -}; diff --git a/node.d/node_modules/ber/writer.js b/node.d/node_modules/ber/writer.js deleted file mode 100644 index d9d99af6..00000000 --- a/node.d/node_modules/ber/writer.js +++ /dev/null @@ -1,316 +0,0 @@ -// Copyright 2011 Mark Cavage <mcavage@gmail.com> All rights reserved. - -var assert = require('assert'); -var ASN1 = require('./types'); -var errors = require('./errors'); - - -///--- Globals - -var newInvalidAsn1Error = errors.newInvalidAsn1Error; - -var DEFAULT_OPTS = { - size: 1024, - growthFactor: 8 -}; - - -///--- Helpers - -function merge(from, to) { - assert.ok(from); - assert.equal(typeof(from), 'object'); - assert.ok(to); - assert.equal(typeof(to), 'object'); - - var keys = Object.getOwnPropertyNames(from); - keys.forEach(function(key) { - if (to[key]) - return; - - var value = Object.getOwnPropertyDescriptor(from, key); - Object.defineProperty(to, key, value); - }); - - return to; -} - - - -///--- API - -function Writer(options) { - options = merge(DEFAULT_OPTS, options || {}); - - this._buf = new Buffer(options.size || 1024); - this._size = this._buf.length; - this._offset = 0; - this._options = options; - - // A list of offsets in the buffer where we need to insert - // sequence tag/len pairs. - this._seq = []; -} - -Object.defineProperty(Writer.prototype, 'buffer', { - get: function () { - if (this._seq.length) - throw new InvalidAsn1Error(this._seq.length + ' unended sequence(s)'); - - return (this._buf.slice(0, this._offset)); - } -}); - -Writer.prototype.writeByte = function(b) { - if (typeof(b) !== 'number') - throw new TypeError('argument must be a Number'); - - this._ensure(1); - this._buf[this._offset++] = b; -}; - - -Writer.prototype.writeInt = function(i, tag) { - if (typeof(i) !== 'number') - throw new TypeError('argument must be a Number'); - if (typeof(tag) !== 'number') - tag = ASN1.Integer; - - var sz = 4; - - while ((((i & 0xff800000) === 0) || ((i & 0xff800000) === 0xff800000 >> 0)) && - (sz > 1)) { - sz--; - i <<= 8; - } - - if (sz > 4) - throw new InvalidAsn1Error('BER ints cannot be > 0xffffffff'); - - this._ensure(2 + sz); - this._buf[this._offset++] = tag; - this._buf[this._offset++] = sz; - - while (sz-- > 0) { - this._buf[this._offset++] = ((i & 0xff000000) >>> 24); - i <<= 8; - } - -}; - - -Writer.prototype.writeNull = function() { - this.writeByte(ASN1.Null); - this.writeByte(0x00); -}; - - -Writer.prototype.writeEnumeration = function(i, tag) { - if (typeof(i) !== 'number') - throw new TypeError('argument must be a Number'); - if (typeof(tag) !== 'number') - tag = ASN1.Enumeration; - - return this.writeInt(i, tag); -}; - - -Writer.prototype.writeBoolean = function(b, tag) { - if (typeof(b) !== 'boolean') - throw new TypeError('argument must be a Boolean'); - if (typeof(tag) !== 'number') - tag = ASN1.Boolean; - - this._ensure(3); - this._buf[this._offset++] = tag; - this._buf[this._offset++] = 0x01; - this._buf[this._offset++] = b ? 0xff : 0x00; -}; - - -Writer.prototype.writeString = function(s, tag) { - if (typeof(s) !== 'string') - throw new TypeError('argument must be a string (was: ' + typeof(s) + ')'); - if (typeof(tag) !== 'number') - tag = ASN1.OctetString; - - var len = Buffer.byteLength(s); - this.writeByte(tag); - this.writeLength(len); - if (len) { - this._ensure(len); - this._buf.write(s, this._offset); - this._offset += len; - } -}; - - -Writer.prototype.writeBuffer = function(buf, tag) { - if (typeof(tag) !== 'number') - throw new TypeError('tag must be a number'); - if (!Buffer.isBuffer(buf)) - throw new TypeError('argument must be a buffer'); - - this.writeByte(tag); - this.writeLength(buf.length); - this._ensure(buf.length); - buf.copy(this._buf, this._offset, 0, buf.length); - this._offset += buf.length; -}; - - -Writer.prototype.writeStringArray = function(strings) { - if ((!strings instanceof Array)) - throw new TypeError('argument must be an Array[String]'); - - var self = this; - strings.forEach(function(s) { - self.writeString(s); - }); -}; - -// This is really to solve DER cases, but whatever for now -Writer.prototype.writeOID = function(s, tag) { - if (typeof(s) !== 'string') - throw new TypeError('argument must be a string'); - if (typeof(tag) !== 'number') - tag = ASN1.OID; - - if (!/^([0-9]+\.){3,}[0-9]+$/.test(s)) - throw new Error('argument is not a valid OID string'); - - function encodeOctet(bytes, octet) { - if (octet < 128) { - bytes.push(octet); - } else if (octet < 16384) { - bytes.push((octet >>> 7) | 0x80); - bytes.push(octet & 0x7F); - } else if (octet < 2097152) { - bytes.push((octet >>> 14) | 0x80); - bytes.push(((octet >>> 7) | 0x80) & 0xFF); - bytes.push(octet & 0x7F); - } else if (octet < 268435456) { - bytes.push((octet >>> 21) | 0x80); - bytes.push(((octet >>> 14) | 0x80) & 0xFF); - bytes.push(((octet >>> 7) | 0x80) & 0xFF); - bytes.push(octet & 0x7F); - } else { - bytes.push(((octet >>> 28) | 0x80) & 0xFF); - bytes.push(((octet >>> 21) | 0x80) & 0xFF); - bytes.push(((octet >>> 14) | 0x80) & 0xFF); - bytes.push(((octet >>> 7) | 0x80) & 0xFF); - bytes.push(octet & 0x7F); - } - } - - var tmp = s.split('.'); - var bytes = []; - bytes.push(parseInt(tmp[0], 10) * 40 + parseInt(tmp[1], 10)); - tmp.slice(2).forEach(function(b) { - encodeOctet(bytes, parseInt(b, 10)); - }); - - var self = this; - this._ensure(2 + bytes.length); - this.writeByte(tag); - this.writeLength(bytes.length); - bytes.forEach(function(b) { - self.writeByte(b); - }); -}; - - -Writer.prototype.writeLength = function(len) { - if (typeof(len) !== 'number') - throw new TypeError('argument must be a Number'); - - this._ensure(4); - - if (len <= 0x7f) { - this._buf[this._offset++] = len; - } else if (len <= 0xff) { - this._buf[this._offset++] = 0x81; - this._buf[this._offset++] = len; - } else if (len <= 0xffff) { - this._buf[this._offset++] = 0x82; - this._buf[this._offset++] = len >> 8; - this._buf[this._offset++] = len; - } else if (len <= 0xffffff) { - this._buf[this._offset++] = 0x83; - this._buf[this._offset++] = len >> 16; - this._buf[this._offset++] = len >> 8; - this._buf[this._offset++] = len; - } else { - throw new InvalidAsn1ERror('Length too long (> 4 bytes)'); - } -}; - -Writer.prototype.startSequence = function(tag) { - if (typeof(tag) !== 'number') - tag = ASN1.Sequence | ASN1.Constructor; - - this.writeByte(tag); - this._seq.push(this._offset); - this._ensure(3); - this._offset += 3; -}; - - -Writer.prototype.endSequence = function() { - var seq = this._seq.pop(); - var start = seq + 3; - var len = this._offset - start; - - if (len <= 0x7f) { - this._shift(start, len, -2); - this._buf[seq] = len; - } else if (len <= 0xff) { - this._shift(start, len, -1); - this._buf[seq] = 0x81; - this._buf[seq + 1] = len; - } else if (len <= 0xffff) { - this._buf[seq] = 0x82; - this._buf[seq + 1] = len >> 8; - this._buf[seq + 2] = len; - } else if (len <= 0xffffff) { - this._shift(start, len, 1); - this._buf[seq] = 0x83; - this._buf[seq + 1] = len >> 16; - this._buf[seq + 2] = len >> 8; - this._buf[seq + 3] = len; - } else { - throw new InvalidAsn1Error('Sequence too long'); - } -}; - - -Writer.prototype._shift = function(start, len, shift) { - assert.ok(start !== undefined); - assert.ok(len !== undefined); - assert.ok(shift); - - this._buf.copy(this._buf, start + shift, start, start + len); - this._offset += shift; -}; - -Writer.prototype._ensure = function(len) { - assert.ok(len); - - if (this._size - this._offset < len) { - var sz = this._size * this._options.growthFactor; - if (sz - this._offset < len) - sz += len; - - var buf = new Buffer(sz); - - this._buf.copy(buf, 0, 0, this._offset); - this._buf = buf; - this._size = sz; - } -}; - - - -///--- Exported API - -module.exports = Writer; diff --git a/node.d/node_modules/lib/ber/errors.js b/node.d/node_modules/lib/ber/errors.js new file mode 100644 index 00000000..0106747e --- /dev/null +++ b/node.d/node_modules/lib/ber/errors.js @@ -0,0 +1,9 @@ + +module.exports = { + InvalidAsn1Error: function(msg) { + var e = new Error() + e.name = 'InvalidAsn1Error' + e.message = msg || '' + return e + } +} diff --git a/node.d/node_modules/lib/ber/index.js b/node.d/node_modules/lib/ber/index.js new file mode 100644 index 00000000..65985c1e --- /dev/null +++ b/node.d/node_modules/lib/ber/index.js @@ -0,0 +1,17 @@ + +var errors = require('./errors') +var types = require('./types') + +var Reader = require('./reader') +var Writer = require('./writer') + +for (var t in types) + if (types.hasOwnProperty(t)) + exports[t] = types[t] + +for (var e in errors) + if (errors.hasOwnProperty(e)) + exports[e] = errors[e] + +exports.Reader = Reader +exports.Writer = Writer diff --git a/node.d/node_modules/lib/ber/reader.js b/node.d/node_modules/lib/ber/reader.js new file mode 100644 index 00000000..f93d829a --- /dev/null +++ b/node.d/node_modules/lib/ber/reader.js @@ -0,0 +1,269 @@ + +var assert = require('assert'); + +var ASN1 = require('./types'); +var errors = require('./errors'); + + +///--- Globals + +var InvalidAsn1Error = errors.InvalidAsn1Error; + + + +///--- API + +function Reader(data) { + if (!data || !Buffer.isBuffer(data)) + throw new TypeError('data must be a node Buffer'); + + this._buf = data; + this._size = data.length; + + // These hold the "current" state + this._len = 0; + this._offset = 0; +} + +Object.defineProperty(Reader.prototype, 'length', { + enumerable: true, + get: function () { return (this._len); } +}); + +Object.defineProperty(Reader.prototype, 'offset', { + enumerable: true, + get: function () { return (this._offset); } +}); + +Object.defineProperty(Reader.prototype, 'remain', { + get: function () { return (this._size - this._offset); } +}); + +Object.defineProperty(Reader.prototype, 'buffer', { + get: function () { return (this._buf.slice(this._offset)); } +}); + + +/** + * Reads a single byte and advances offset; you can pass in `true` to make this + * a "peek" operation (i.e., get the byte, but don't advance the offset). + * + * @param {Boolean} peek true means don't move offset. + * @return {Number} the next byte, null if not enough data. + */ +Reader.prototype.readByte = function(peek) { + if (this._size - this._offset < 1) + return null; + + var b = this._buf[this._offset] & 0xff; + + if (!peek) + this._offset += 1; + + return b; +}; + + +Reader.prototype.peek = function() { + return this.readByte(true); +}; + + +/** + * Reads a (potentially) variable length off the BER buffer. This call is + * not really meant to be called directly, as callers have to manipulate + * the internal buffer afterwards. + * + * As a result of this call, you can call `Reader.length`, until the + * next thing called that does a readLength. + * + * @return {Number} the amount of offset to advance the buffer. + * @throws {InvalidAsn1Error} on bad ASN.1 + */ +Reader.prototype.readLength = function(offset) { + if (offset === undefined) + offset = this._offset; + + if (offset >= this._size) + return null; + + var lenB = this._buf[offset++] & 0xff; + if (lenB === null) + return null; + + if ((lenB & 0x80) == 0x80) { + lenB &= 0x7f; + + if (lenB == 0) + throw InvalidAsn1Error('Indefinite length not supported'); + + if (lenB > 4) + throw InvalidAsn1Error('encoding too long'); + + if (this._size - offset < lenB) + return null; + + this._len = 0; + for (var i = 0; i < lenB; i++) + this._len = (this._len << 8) + (this._buf[offset++] & 0xff); + + } else { + // Wasn't a variable length + this._len = lenB; + } + + return offset; +}; + + +/** + * Parses the next sequence in this BER buffer. + * + * To get the length of the sequence, call `Reader.length`. + * + * @return {Number} the sequence's tag. + */ +Reader.prototype.readSequence = function(tag) { + var seq = this.peek(); + if (seq === null) + return null; + if (tag !== undefined && tag !== seq) + throw InvalidAsn1Error('Expected 0x' + tag.toString(16) + + ': got 0x' + seq.toString(16)); + + var o = this.readLength(this._offset + 1); // stored in `length` + if (o === null) + return null; + + this._offset = o; + return seq; +}; + + +Reader.prototype.readInt = function(tag) { + if (typeof(tag) !== 'number') + tag = ASN1.Integer; + + return this._readTag(ASN1.Integer); +}; + + +Reader.prototype.readBoolean = function(tag) { + if (typeof(tag) !== 'number') + tag = ASN1.Boolean; + + return (this._readTag(tag) === 0 ? false : true); +}; + + +Reader.prototype.readEnumeration = function(tag) { + if (typeof(tag) !== 'number') + tag = ASN1.Enumeration; + + return this._readTag(ASN1.Enumeration); +}; + + +Reader.prototype.readString = function(tag, retbuf) { + if (!tag) + tag = ASN1.OctetString; + + var b = this.peek(); + if (b === null) + return null; + + if (b !== tag) + throw InvalidAsn1Error('Expected 0x' + tag.toString(16) + + ': got 0x' + b.toString(16)); + + var o = this.readLength(this._offset + 1); // stored in `length` + + if (o === null) + return null; + + if (this.length > this._size - o) + return null; + + this._offset = o; + + if (this.length === 0) + return retbuf ? new Buffer(0) : ''; + + var str = this._buf.slice(this._offset, this._offset + this.length); + this._offset += this.length; + + return retbuf ? str : str.toString('utf8'); +}; + +Reader.prototype.readOID = function(tag) { + if (!tag) + tag = ASN1.OID; + + var b = this.readString(tag, true); + if (b === null) + return null; + + var values = []; + var value = 0; + + for (var i = 0; i < b.length; i++) { + var byte = b[i] & 0xff; + + value <<= 7; + value += byte & 0x7f; + if ((byte & 0x80) == 0) { + values.push(value >>> 0); + value = 0; + } + } + + value = values.shift(); + values.unshift(value % 40); + values.unshift((value / 40) >> 0); + + return values.join('.'); +}; + + +Reader.prototype._readTag = function(tag) { + assert.ok(tag !== undefined); + + var b = this.peek(); + + if (b === null) + return null; + + if (b !== tag) + throw InvalidAsn1Error('Expected 0x' + tag.toString(16) + + ': got 0x' + b.toString(16)); + + var o = this.readLength(this._offset + 1); // stored in `length` + if (o === null) + return null; + + if (this.length > 4) + throw InvalidAsn1Error('Integer too long: ' + this.length); + + if (this.length > this._size - o) + return null; + this._offset = o; + + var fb = this._buf[this._offset]; + var value = 0; + + for (var i = 0; i < this.length; i++) { + value <<= 8; + value |= (this._buf[this._offset++] & 0xff); + } + + if ((fb & 0x80) == 0x80 && i !== 4) + value -= (1 << (i * 8)); + + return value >> 0; +}; + + + +///--- Exported API + +module.exports = Reader; diff --git a/node.d/node_modules/lib/ber/types.js b/node.d/node_modules/lib/ber/types.js new file mode 100644 index 00000000..345824bb --- /dev/null +++ b/node.d/node_modules/lib/ber/types.js @@ -0,0 +1,34 @@ + +module.exports = { + EOC: 0, + Boolean: 1, + Integer: 2, + BitString: 3, + OctetString: 4, + Null: 5, + OID: 6, + ObjectDescriptor: 7, + External: 8, + Real: 9, + Enumeration: 10, + PDV: 11, + Utf8String: 12, + RelativeOID: 13, + Sequence: 16, + Set: 17, + NumericString: 18, + PrintableString: 19, + T61String: 20, + VideotexString: 21, + IA5String: 22, + UTCTime: 23, + GeneralizedTime: 24, + GraphicString: 25, + VisibleString: 26, + GeneralString: 28, + UniversalString: 29, + CharacterString: 30, + BMPString: 31, + Constructor: 32, + Context: 128 +} diff --git a/node.d/node_modules/lib/ber/writer.js b/node.d/node_modules/lib/ber/writer.js new file mode 100644 index 00000000..bf980588 --- /dev/null +++ b/node.d/node_modules/lib/ber/writer.js @@ -0,0 +1,317 @@ + +var assert = require('assert'); +var ASN1 = require('./types'); +var errors = require('./errors'); + + +///--- Globals + +var InvalidAsn1Error = errors.InvalidAsn1Error; + +var DEFAULT_OPTS = { + size: 1024, + growthFactor: 8 +}; + + +///--- Helpers + +function merge(from, to) { + assert.ok(from); + assert.equal(typeof(from), 'object'); + assert.ok(to); + assert.equal(typeof(to), 'object'); + + var keys = Object.getOwnPropertyNames(from); + keys.forEach(function(key) { + if (to[key]) + return; + + var value = Object.getOwnPropertyDescriptor(from, key); + Object.defineProperty(to, key, value); + }); + + return to; +} + + + +///--- API + +function Writer(options) { + options = merge(DEFAULT_OPTS, options || {}); + + this._buf = new Buffer(options.size || 1024); + this._size = this._buf.length; + this._offset = 0; + this._options = options; + + // A list of offsets in the buffer where we need to insert + // sequence tag/len pairs. + this._seq = []; +} + +Object.defineProperty(Writer.prototype, 'buffer', { + get: function () { + if (this._seq.length) + throw new InvalidAsn1Error(this._seq.length + ' unended sequence(s)'); + + return (this._buf.slice(0, this._offset)); + } +}); + +Writer.prototype.writeByte = function(b) { + if (typeof(b) !== 'number') + throw new TypeError('argument must be a Number'); + + this._ensure(1); + this._buf[this._offset++] = b; +}; + + +Writer.prototype.writeInt = function(i, tag) { + if (typeof(i) !== 'number') + throw new TypeError('argument must be a Number'); + if (typeof(tag) !== 'number') + tag = ASN1.Integer; + + var sz = 4; + + while ((((i & 0xff800000) === 0) || ((i & 0xff800000) === 0xff800000 >> 0)) && + (sz > 1)) { + sz--; + i <<= 8; + } + + if (sz > 4) + throw new InvalidAsn1Error('BER ints cannot be > 0xffffffff'); + + this._ensure(2 + sz); + this._buf[this._offset++] = tag; + this._buf[this._offset++] = sz; + + while (sz-- > 0) { + this._buf[this._offset++] = ((i & 0xff000000) >>> 24); + i <<= 8; + } + +}; + + +Writer.prototype.writeNull = function() { + this.writeByte(ASN1.Null); + this.writeByte(0x00); +}; + + +Writer.prototype.writeEnumeration = function(i, tag) { + if (typeof(i) !== 'number') + throw new TypeError('argument must be a Number'); + if (typeof(tag) !== 'number') + tag = ASN1.Enumeration; + + return this.writeInt(i, tag); +}; + + +Writer.prototype.writeBoolean = function(b, tag) { + if (typeof(b) !== 'boolean') + throw new TypeError('argument must be a Boolean'); + if (typeof(tag) !== 'number') + tag = ASN1.Boolean; + + this._ensure(3); + this._buf[this._offset++] = tag; + this._buf[this._offset++] = 0x01; + this._buf[this._offset++] = b ? 0xff : 0x00; +}; + + +Writer.prototype.writeString = function(s, tag) { + if (typeof(s) !== 'string') + throw new TypeError('argument must be a string (was: ' + typeof(s) + ')'); + if (typeof(tag) !== 'number') + tag = ASN1.OctetString; + + var len = Buffer.byteLength(s); + this.writeByte(tag); + this.writeLength(len); + if (len) { + this._ensure(len); + this._buf.write(s, this._offset); + this._offset += len; + } +}; + + +Writer.prototype.writeBuffer = function(buf, tag) { + if (!Buffer.isBuffer(buf)) + throw new TypeError('argument must be a buffer'); + + // If no tag is specified we will assume `buf` already contains tag and length + if (typeof(tag) === 'number') { + this.writeByte(tag); + this.writeLength(buf.length); + } + + this._ensure(buf.length); + buf.copy(this._buf, this._offset, 0, buf.length); + this._offset += buf.length; +}; + + +Writer.prototype.writeStringArray = function(strings, tag) { + if (! (strings instanceof Array)) + throw new TypeError('argument must be an Array[String]'); + + var self = this; + strings.forEach(function(s) { + self.writeString(s, tag); + }); +}; + +// This is really to solve DER cases, but whatever for now +Writer.prototype.writeOID = function(s, tag) { + if (typeof(s) !== 'string') + throw new TypeError('argument must be a string'); + if (typeof(tag) !== 'number') + tag = ASN1.OID; + + if (!/^([0-9]+\.){3,}[0-9]+$/.test(s)) + throw new Error('argument is not a valid OID string'); + + function encodeOctet(bytes, octet) { + if (octet < 128) { + bytes.push(octet); + } else if (octet < 16384) { + bytes.push((octet >>> 7) | 0x80); + bytes.push(octet & 0x7F); + } else if (octet < 2097152) { + bytes.push((octet >>> 14) | 0x80); + bytes.push(((octet >>> 7) | 0x80) & 0xFF); + bytes.push(octet & 0x7F); + } else if (octet < 268435456) { + bytes.push((octet >>> 21) | 0x80); + bytes.push(((octet >>> 14) | 0x80) & 0xFF); + bytes.push(((octet >>> 7) | 0x80) & 0xFF); + bytes.push(octet & 0x7F); + } else { + bytes.push(((octet >>> 28) | 0x80) & 0xFF); + bytes.push(((octet >>> 21) | 0x80) & 0xFF); + bytes.push(((octet >>> 14) | 0x80) & 0xFF); + bytes.push(((octet >>> 7) | 0x80) & 0xFF); + bytes.push(octet & 0x7F); + } + } + + var tmp = s.split('.'); + var bytes = []; + bytes.push(parseInt(tmp[0], 10) * 40 + parseInt(tmp[1], 10)); + tmp.slice(2).forEach(function(b) { + encodeOctet(bytes, parseInt(b, 10)); + }); + + var self = this; + this._ensure(2 + bytes.length); + this.writeByte(tag); + this.writeLength(bytes.length); + bytes.forEach(function(b) { + self.writeByte(b); + }); +}; + + +Writer.prototype.writeLength = function(len) { + if (typeof(len) !== 'number') + throw new TypeError('argument must be a Number'); + + this._ensure(4); + + if (len <= 0x7f) { + this._buf[this._offset++] = len; + } else if (len <= 0xff) { + this._buf[this._offset++] = 0x81; + this._buf[this._offset++] = len; + } else if (len <= 0xffff) { + this._buf[this._offset++] = 0x82; + this._buf[this._offset++] = len >> 8; + this._buf[this._offset++] = len; + } else if (len <= 0xffffff) { + this._buf[this._offset++] = 0x83; + this._buf[this._offset++] = len >> 16; + this._buf[this._offset++] = len >> 8; + this._buf[this._offset++] = len; + } else { + throw new InvalidAsn1Error('Length too long (> 4 bytes)'); + } +}; + +Writer.prototype.startSequence = function(tag) { + if (typeof(tag) !== 'number') + tag = ASN1.Sequence | ASN1.Constructor; + + this.writeByte(tag); + this._seq.push(this._offset); + this._ensure(3); + this._offset += 3; +}; + + +Writer.prototype.endSequence = function() { + var seq = this._seq.pop(); + var start = seq + 3; + var len = this._offset - start; + + if (len <= 0x7f) { + this._shift(start, len, -2); + this._buf[seq] = len; + } else if (len <= 0xff) { + this._shift(start, len, -1); + this._buf[seq] = 0x81; + this._buf[seq + 1] = len; + } else if (len <= 0xffff) { + this._buf[seq] = 0x82; + this._buf[seq + 1] = len >> 8; + this._buf[seq + 2] = len; + } else if (len <= 0xffffff) { + this._shift(start, len, 1); + this._buf[seq] = 0x83; + this._buf[seq + 1] = len >> 16; + this._buf[seq + 2] = len >> 8; + this._buf[seq + 3] = len; + } else { + throw new InvalidAsn1Error('Sequence too long'); + } +}; + + +Writer.prototype._shift = function(start, len, shift) { + assert.ok(start !== undefined); + assert.ok(len !== undefined); + assert.ok(shift); + + this._buf.copy(this._buf, start + shift, start, start + len); + this._offset += shift; +}; + +Writer.prototype._ensure = function(len) { + assert.ok(len); + + if (this._size - this._offset < len) { + var sz = this._size * this._options.growthFactor; + if (sz - this._offset < len) + sz += len; + + var buf = new Buffer(sz); + + this._buf.copy(buf, 0, 0, this._offset); + this._buf = buf; + this._size = sz; + } +}; + + + +///--- Exported API + +module.exports = Writer; diff --git a/node.d/node_modules/net-snmp.js b/node.d/node_modules/net-snmp.js index de592610..ac9a8d35 100644 --- a/node.d/node_modules/net-snmp.js +++ b/node.d/node_modules/net-snmp.js @@ -1,7 +1,7 @@ // Copyright 2013 Stephen Vickers <stephen.vickers.sv@gmail.com> -var ber = require ("asn1").Ber; +var ber = require ("asn1-ber").Ber; var dgram = require ("dgram"); var events = require ("events"); var util = require ("util"); diff --git a/node.d/node_modules/netdata.js b/node.d/node_modules/netdata.js index 11202061..143255d9 100644 --- a/node.d/node_modules/netdata.js +++ b/node.d/node_modules/netdata.js @@ -364,12 +364,8 @@ var netdata = { if(typeof value === 'undefined' || value === null) return false; - if(this._current_chart._dimensions_count !== 0) { - if (value instanceof Buffer) - this.queue('SET ' + dimension + ' = 0x' + value.toString('hex')); - else - this.queue('SET ' + dimension + ' = ' + value.toString()); - } + if(this._current_chart._dimensions_count !== 0) + this.queue('SET ' + dimension + ' = ' + value.toString()); return true; }; diff --git a/node.d/snmp.node.js b/node.d/snmp.node.js index 57b37ffa..3e702795 100644 --- a/node.d/snmp.node.js +++ b/node.d/snmp.node.js @@ -269,13 +269,36 @@ netdata.processors.snmp = { failed++; } else { - if(__DEBUG === true) - netdata.debug(service.module.name + ': ' + service.name + ': found ' + service.module.name + ' value of OIDs ' + varbinds[i].oid + " = " + varbinds[i].value); - - if(varbinds[i].type === net_snmp.ObjectType.OctetString && service.snmp_oids_index[varbinds[i].oid].type !== 'title') - value = parseFloat(varbinds[i].value) * 1000; - else - value = varbinds[i].value; + // test fom Counter64 + // varbinds[i].type = net_snmp.ObjectType.Counter64; + // varbinds[i].value = new Buffer([0x34, 0x49, 0x2e, 0xdc, 0xd1]); + + switch(varbinds[i].type) { + case net_snmp.ObjectType.OctetString: + if(service.snmp_oids_index[varbinds[i].oid].type !== 'title') + // parse floating point values, exposed as strings + value = parseFloat(varbinds[i].value) * 1000; + if(__DEBUG === true) netdata.debug(service.module.name + ': ' + service.name + ': found ' + service.module.name + ' value of OIDs ' + varbinds[i].oid + ", ObjectType " + net_snmp.ObjectType[varbinds[i].type] + " (" + netdata.stringify(varbinds[i].type) + "), typeof(" + typeof(varbinds[i].value) + "), in JSON: " + netdata.stringify(varbinds[i].value) + ", value = " + value.toString() + " (parsed as float in string)"); + else + // just use the string + value = varbinds[i].value; + if(__DEBUG === true) netdata.debug(service.module.name + ': ' + service.name + ': found ' + service.module.name + ' value of OIDs ' + varbinds[i].oid + ", ObjectType " + net_snmp.ObjectType[varbinds[i].type] + " (" + netdata.stringify(varbinds[i].type) + "), typeof(" + typeof(varbinds[i].value) + "), in JSON: " + netdata.stringify(varbinds[i].value) + ", value = " + value.toString() + " (parsed as string)"); + break; + + case net_snmp.ObjectType.Counter64: + // copy the buffer + value = '0x' + varbinds[i].value.toString('hex'); + if(__DEBUG === true) netdata.debug(service.module.name + ': ' + service.name + ': found ' + service.module.name + ' value of OIDs ' + varbinds[i].oid + ", ObjectType " + net_snmp.ObjectType[varbinds[i].type] + " (" + netdata.stringify(varbinds[i].type) + "), typeof(" + typeof(varbinds[i].value) + "), in JSON: " + netdata.stringify(varbinds[i].value) + ", value = " + value.toString() + " (parsed as buffer)"); + break; + + case net_snmp.ObjectType.Integer: + case net_snmp.ObjectType.Counter: + case net_snmp.ObjectType.Gauge: + default: + value = varbinds[i].value; + if(__DEBUG === true) netdata.debug(service.module.name + ': ' + service.name + ': found ' + service.module.name + ' value of OIDs ' + varbinds[i].oid + ", ObjectType " + net_snmp.ObjectType[varbinds[i].type] + " (" + netdata.stringify(varbinds[i].type) + "), typeof(" + typeof(varbinds[i].value) + "), in JSON: " + netdata.stringify(varbinds[i].value) + ", value = " + value.toString() + " (parsed as number)"); + break; + } ok++; } diff --git a/node.d/stiebeleltron.node.js b/node.d/stiebeleltron.node.js new file mode 100644 index 00000000..b0eb0aba --- /dev/null +++ b/node.d/stiebeleltron.node.js @@ -0,0 +1,196 @@ +'use strict'; + +// This program will connect to one Stiebel Eltron ISG for heatpump heating +// to get the heat pump metrics. + +// example configuration in netdata/conf.d/node.d/stiebeleltron.conf.md + +var url = require("url"); +var http = require("http"); +var netdata = require("netdata"); + +netdata.debug("loaded " + __filename + " plugin"); + +var stiebeleltron = { + name: "Stiebel Eltron", + enable_autodetect: false, + update_every: 10, + base_priority: 60000, + charts: {}, + pages: {}, + + createBasicDimension: function (id, name, multiplier, divisor) { + 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: multiplier, // the multiplier + divisor: divisor, // the divisor + hidden: false // is hidden (boolean) + }; + }, + + processResponse: function (service, html) { + if (html === null) return; + + // add the service + service.commit(); + + var page = stiebeleltron.pages[service.name]; + var categories = page.categories; + var categoriesCount = categories.length; + while (categoriesCount--) { + var context = { + html: html, + service: service, + category: categories[categoriesCount], + page: page, + chartDefinition: null, + dimension: null + }; + stiebeleltron.processCategory(context); + + } + }, + + processCategory: function (context) { + var charts = context.category.charts; + var chartCount = charts.length; + while (chartCount--) { + context.chartDefinition = charts[chartCount]; + stiebeleltron.processChart(context); + } + }, + + processChart: function (context) { + var dimensions = context.chartDefinition.dimensions; + var dimensionCount = dimensions.length; + context.service.begin(stiebeleltron.getChartFromContext(context)); + + while (dimensionCount--) { + context.dimension = dimensions[dimensionCount]; + stiebeleltron.processDimension(context); + } + context.service.end(); + }, + + processDimension: function (context) { + var dimension = context.dimension; + var match = new RegExp(dimension.regex).exec(context.html); + if (match === null) return; + var value = match[1].replace(",", "."); + // most values have a single digit by default, which requires the values to be multiplied. can be overridden. + if (stiebeleltron.isDefined(dimension.digits)) { + value *= Math.pow(10, dimension.digits); + } else { + value *= 10; + } + context.service.set(stiebeleltron.getDimensionId(context), value); + }, + + getChartFromContext: function (context) { + var chartId = this.getChartId(context); + var chart = stiebeleltron.charts[chartId]; + if (stiebeleltron.isDefined(chart)) return chart; + + var chartDefinition = context.chartDefinition; + var service = context.service; + var dimensions = {}; + + var dimCount = chartDefinition.dimensions.length; + while (dimCount--) { + var dim = chartDefinition.dimensions[dimCount]; + var multiplier = 1; + var divisor = 10; + if (stiebeleltron.isDefined(dim.digits)) divisor = Math.pow(10, Math.max(0, dim.digits)); + if (stiebeleltron.isDefined(dim.multiplier)) multiplier = dim.multiplier; + if (stiebeleltron.isDefined(dim.divisor)) divisor = dim.divisor; + context.dimension = dim; + var dimId = this.getDimensionId(context); + dimensions[dimId] = this.createBasicDimension(dimId, dim.name, multiplier, divisor); + } + + chart = { + id: chartId, + name: '', + title: chartDefinition.title, + units: chartDefinition.unit, + family: context.category.name, + context: 'stiebeleltron.' + context.page.id + "." + context.category.id, + type: chartDefinition.type, + priority: stiebeleltron.base_priority + chartDefinition.prio,// the priority relative to others in the same family + update_every: service.update_every, // the expected update frequency of the chart + dimensions: dimensions + }; + chart = service.chart(chartId, chart); + stiebeleltron.charts[chartId] = chart; + + return chart; + }, + + // 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(uri), + update_every: update_every, + module: this + }); + service.execute(this.processResponse); + }, + + + configure: function (config) { + if (stiebeleltron.isUndefined(config.pages)) return 0; + var added = 0; + var pageCount = config.pages.length; + while (pageCount--) { + var page = config.pages[pageCount]; + // some validation + if (stiebeleltron.isUndefined(page.categories) || page.categories.length < 1) { + netdata.error("Your Stiebel Eltron config is invalid. Disabling plugin."); + return 0; + } + if (stiebeleltron.isUndefined(page.update_every)) page.update_every = this.update_every; + this.pages[page.name] = page; + this.serviceExecute(page.name, page.url, page.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(); + }); + }, + + getChartId: function (context) { + return "stiebeleltron_" + context.page.id + + "." + context.category.id + + "." + context.chartDefinition.id; + }, + + getDimensionId: function (context) { + return context.dimension.id; + }, + + isUndefined: function (value) { + return typeof value === 'undefined'; + }, + + isDefined: function (value) { + return typeof value !== 'undefined'; + } +}; + +module.exports = stiebeleltron; |