From bf1c08f27947e146563c17053e8032fa286424a8 Mon Sep 17 00:00:00 2001 From: cshagen Date: Tue, 16 Feb 2021 08:50:05 +0100 Subject: [PATCH] Color theme - day selection for graph, variable arc length in power meter (#1058) * added dayily graph to color theme * show previous days in graph * reduce arch length in powermeter * Fix heading of powergraph --- web/themes/colors/daygraph.js | 420 ------------------------- web/themes/colors/powerdata.js | 14 +- web/themes/colors/powergraph.js | 262 ++++++++++----- web/themes/colors/powermeter.js | 100 +++++- web/themes/colors/processAllMqttMsg.js | 16 +- web/themes/colors/setupMqttServices.js | 2 +- web/themes/colors/theme.html | 30 +- 7 files changed, 323 insertions(+), 521 deletions(-) delete mode 100644 web/themes/colors/daygraph.js diff --git a/web/themes/colors/daygraph.js b/web/themes/colors/daygraph.js deleted file mode 100644 index 6e3aceb6d..000000000 --- a/web/themes/colors/daygraph.js +++ /dev/null @@ -1,420 +0,0 @@ -class DayGraph { - svg; - - constructor() { - this.graphData = []; - this.initCounter = 0; - this.staging = []; - this.rawData = []; - this.colors = []; - this.gridColors = []; - this.bgcolor = ""; - this.axiscolor = ""; - this.chargeColor = ""; - this.lp1color = ""; - this.lp2color = ""; - this.batteryColor = ""; - this.graphRefreshCounter = 0; - this.width = 500; - this.height = 500; - this.margin = { top: 10, right: 20, bottom: 20, left: 25 }; - } - - init() { - var style = getComputedStyle(document.body); - this.colors[18] = style.getPropertyValue('--color-house'); - this.colors[19] = style.getPropertyValue('--color-battery'); - this.colors[20] = style.getPropertyValue('--color-pv'); - this.gridColors[0] = style.getPropertyValue('--color-battery'); - this.gridColors[1] = style.getPropertyValue('--color-pv'); - this.gridColors[2] = style.getPropertyValue('--color-export'); - this.gridColors[3] = style.getPropertyValue('--color-evu'); - this.bgcolor = style.getPropertyValue('--color-bg'); - this.chargeColor = style.getPropertyValue('--color-charging'); - this.axiscolor = style.getPropertyValue('--color-axis'); - this.lp1color = style.getPropertyValue('--color-lp1'); - this.lp2color = style.getPropertyValue('--color-lp2'); - this.batteryColor = style.getPropertyValue('--color-battery'); - - var i; - for (i = 0; i < 8; i++) { - this.colors[i] = wbdata.chargePoint[i].color; - } - for (i = 0; i < 8; i++) { - this.colors[8 + i] = wbdata.shDevice[i].color; - } - this.colors[16] = style.getPropertyValue('--color-co1'); - this.colors[17] = style.getPropertyValue('--color-co2'); - var figure = d3.select("figure#daygraph"); - this.svg = figure.append("svg") - .attr("viewBox", `0 0 500 500`); - } - - activate() { - if (!wbdata.showLiveGraph) { - this.reset(); - try { - subscribeDayGraph(); - } catch (err) { - //on initial run of activate, subscribeDayGraph is not yet initialized. - // the error can be ignored - } - d3.select("figure#daygraph").classed("hide", false); - } - } - - deactivate() { - d3.select("figure#daygraph").classed("hide", true); - } - - reset() { - this.staging = []; - this.rawData = []; - this.graphData = []; - } - update(topic, payload) { - if (payload != 'empty') { - var segment = payload.toString().split("\n"); - if (segment.length <= 1) { - segment = []; - } - const serialNo = topic.substring(26, topic.length); - if (serialNo != "") { - if (typeof (this.staging[+serialNo - 1]) === 'undefined') { - this.staging[+serialNo - 1] = segment; - this.initCounter++; - } - } - if (this.initCounter == 12) {// Initialization complete - unsubscribeDayGraph(); - this.initCounter = 0; - this.staging.map(segment => - segment.map(line => this.rawData.push(line)) - ) - this.rawData.map((line, i, a) => { - if (i > 0) { - const values = this.extractValues(line, a[i - 1]); - this.graphData.push(values); - } else { - // const values = this.extractValues(line, []); - } - }); - this.updateGraph(); - setTimeout(() => this.activate(), 300000) - } - } - } - - extractValues(payload, oldPayload) { - const elements = payload.split(","); - const oldElements = oldPayload.split(","); - var values = {}; - values.date = new Date(d3.timeParse("%H%M")(elements[0])); - // evu - values.gridPull = this.calcValue(1, elements, oldElements); - values.gridPush = this.calcValue(2, elements, oldElements); - // pv - values.solarPower = this.calcValue(3, elements, oldElements); - values.inverter = 0; - // charge points - values.charging = this.calcValue(7, elements, oldElements); - var i; - for (i = 0; i < 3; i++) { - values["lp" + i] = this.calcValue(4 + i, elements, oldElements); - } - for (i = 3; i < 8; i++) { - values["lp" + i] = this.calcValue(12 + i, elements, oldElements); - } - values.soc1 = +elements[21]; - values.soc2 = +elements[22]; - // smart home - for (i = 0; i < 10; i++) { - values["sh" + i] = this.calcValue(26 + i, elements, oldElements); - } - //consumers - values.co0 = this.calcValue(10, elements, oldElements); - values.co1 = this.calcValue(12, elements, oldElements); - //battery - values.batIn = this.calcValue(8, elements, oldElements); - values.batOut = this.calcValue(9, elements, oldElements); - values.batterySoc = +elements[20]; - // calculated values - values.housePower = values.gridPull + values.solarPower + values.batOut - - values.gridPush - values.batIn - values.charging - values.co0 - values.co1 - - values.sh0 - values.sh1 - values.sh2 - values.sh3 - values.sh4 - values.sh5 - values.sh6 - values.sh7 - values.sh8 - values.sh9; - if (values.housePower < 0) {values.housePower = 0;}; - values.selfUsage = values.solarPower - values.gridPush; - if (values.selfUsage < 0) {values.selfUsage = 0;}; - return values; - } - - calcValue(i, array, oldArray) { - var val = (array[i] - oldArray[i]) * 12; - if (val < 0 || val > 150000) { - val = 0; - } - return val; - } - - updateGraph() { - const svg = this.createOrUpdateSvg(); - this.drawChart(svg); - }; - - createOrUpdateSvg() { - this.svg.selectAll("*").remove(); - - - this.g = this.svg - .append("g") - .attr( - "transform", - "translate(" + this.margin.left + "," + this.margin.top + ")" - ); - return this.g; - } - - drawChart(svg) { - const height = this.height - this.margin.top - this.margin.bottom; - const width = this.width - this.margin.left - this.margin.right; - - this.drawSourceGraph(svg, width, height / 2); - this.drawUsageGraph(svg, width, height / 2); - this.drawXAxis(svg, width, height); - this.drawSoc(svg, width, height / 2); - svg.append("text") - .attr("x", width + this.margin.right) - .attr("y", height + this.margin.top) - .attr("text-anchor", "end") - .attr("font-size", 12) - .attr("fill", this.axiscolor) - .text("Heute") - } - - drawSourceGraph(svg, width, height) { - const keys = ["batOut", "selfUsage", "gridPush", "gridPull"]; - const xScale = d3.scaleTime().range([0, width - this.margin.right]); - const yScale = d3.scaleLinear().range([height - 10, 0]); - const extent = d3.extent(this.graphData, (d) => - Math.max(d.solarPower + d.gridPull, d.selfUsage + d.gridPush)); - xScale.domain(d3.extent(this.graphData, (d) => d.date)); - yScale.domain([0, extent[1]]); - const stackGen = d3.stack().keys(keys); - const stackedSeries = stackGen(this.graphData); - svg.selectAll(".sourceareas") - .data(stackedSeries) - .join("path") - .attr("d", d3.area() - .x((d, i) => xScale(this.graphData[i].date)) - .y0((d) => yScale(d[0])) - .y1((d) => yScale(d[1])) - ) - .attr("fill", (d, i) => this.gridColors[i]); - - const yAxis = svg.append("g") - .attr("class", "axis") - .call(d3.axisLeft(yScale) - .tickSizeInner(-width) - .ticks(6) - .tickFormat((d, i) => (d == 0) ? "" : (Math.round(d / 100) / 10))) - ; - yAxis.selectAll(".tick") - .attr("font-size", 12); - yAxis.selectAll(".tick line").attr("stroke", this.bgcolor); - yAxis.select(".domain") - .attr("stroke", this.bgcolor) - ; - } - - drawUsageGraph(svg, width, height) { - const xScale = d3.scaleTime().range([0, width - this.margin.right]); - const yScale = d3.scaleLinear().range([height + 10, 2 * height + 15]); - - xScale.domain(d3.extent(this.graphData, (d) => d.date)); - const extent = d3.extent(this.graphData, (d) => - (d.housePower + d.lp0 + d.lp1 + d.lp2 + d.lp3 + d.lp4 - + d.lp5 + d.lp6 + d.lp7 + d.sh0 + d.sh1 + d.sh2 + d.sh3 + d.sh4 - + d.sh5 + d.sh6 + d.sh7 + d.co0 + d.co1 + d.batIn + d.inverter) - ); - yScale.domain([0, (extent[1])]); - const keys = ["lp0", "lp1", "lp2", "lp3", "lp4", - "lp5", "lp6", "lp7", - "sh0", "sh1", "sh2", "sh3", "sh4", - "sh5", "sh6", "sh7", "co0", "co1", "housePower", "batIn", "inverter"]; - - const stackGen = d3.stack().keys(keys); - const stackedSeries = stackGen(this.graphData); - - svg.selectAll(".targetareas") - .data(stackedSeries) - .join("path") - .attr("d", d3.area() - .x((d, i) => xScale(this.graphData[i].date)) - .y0((d) => yScale(d[0])) - .y1((d) => yScale(d[1])) - ) - .attr("fill", (d, i) => this.colors[i]); - - const yAxis = svg.append("g") - .attr("class", "axis") - .call(d3.axisLeft(yScale) - .tickSizeInner(-width) - .ticks(6) - .tickFormat((d, i) => (d == 0) ? "" : (Math.round(d / 100) / 10)) - ); - yAxis.selectAll(".tick") - .attr("font-size", 12); - yAxis.selectAll(".tick line").attr("stroke", this.bgcolor); - yAxis.select(".domain") - .attr("stroke", this.bgcolor) - ; - } - - drawXAxis(svg, width, height) { - const fontsize = 12; - const xScale = d3.scaleTime().range([0, width - this.margin.right]); - xScale.domain(d3.extent(this.graphData, (d) => d.date)); - - const xAxisGenerator = d3 - .axisBottom(xScale) - .ticks(6) - .tickSizeInner(-10) - .tickFormat(d3.timeFormat("%H:%M")); - - const xAxis = svg.append("g").attr("class", "axis") - .call(xAxisGenerator); - xAxis.attr("transform", "translate(0," + (height / 2 - 6) + ")"); - xAxis.selectAll(".tick") - .attr("color", this.axiscolor) - .attr("font-size", fontsize) - xAxis.selectAll(".tick line").attr("stroke", this.bgcolor); - xAxis.select(".domain") - .attr("stroke", this.bgcolor) - ; - svg.append("text") - .attr("x", - this.margin.left) - .attr("y", height / 2 + 5) - .attr("fill", this.axiscolor) - .attr("font-size", fontsize) - .text("kW") - } - - drawSoc(svg, width, height) { - const xScale = d3.scaleTime().range([0, width - this.margin.right]); - const yScale = d3.scaleLinear().range([height - 10, 0]); - xScale.domain(d3.extent(this.graphData, (d) => d.date)); - yScale.domain([0, 100]); - - // Chargepoint 1 - if (wbdata.chargePoint[0].isSocConfigured) { - svg.append("path") - .datum(this.graphData) - .attr("stroke", this.bgcolor) - .attr("stroke-width", 1) - .attr("fill", "none") - //.style("stroke-dasharray", ("3, 3")) - .attr("d", d3.line() - .x((d, i) => xScale(this.graphData[i].date)) - .y(d => yScale(d.soc1)) - ); - svg.append("path") - .datum(this.graphData) - .attr("stroke", this.lp1color) - .attr("stroke-width", 1) - .attr("fill", "none") - .style("stroke-dasharray", ("3, 3")) - .attr("d", d3.line() - .x((d, i) => xScale(this.graphData[i].date)) - .y(d => yScale(d.soc1)) - ); - - svg.append("text") - .attr("x", width - this.margin.right - 3) - .attr("y", yScale(this.graphData[this.graphData.length - 1].soc1 + 2)) - .text(wbdata.chargePoint[0].name) - .attr("fill", this.lp1color) - .style("font-size", 10) - .attr("text-anchor", "end"); - } - // Chargepoint 2 - if (wbdata.chargePoint[1].isSocConfigured) { - svg.append("path") - .datum(this.graphData) - .attr("stroke", this.bgcolor) - .attr("stroke-width", 1) - .attr("fill", "none") - // .style("stroke-dasharray", ("3, 3")) - .attr("d", d3.line() - .x((d, i) => xScale(this.graphData[i].date)) - .y(d => yScale(d.soc2)) - ); - svg.append("path") - .datum(this.graphData) - .attr("stroke", this.lp2color) - .attr("stroke-width", 1) - .attr("fill", "none") - .style("stroke-dasharray", ("3, 3")) - .attr("d", d3.line() - .x((d, i) => xScale(this.graphData[i].date)) - .y(d => yScale(d.soc2)) - ); - svg.append("text") - .attr("x", 3) - .attr("y", yScale(this.graphData[this.graphData.length - 1].soc2 + 2)) - .text(wbdata.chargePoint[1].name) - .attr("fill", this.lp2color) - .style("font-size", 10) - .attr("text-anchor", "start"); - } - - // Battery - if (wbdata.isBatteryConfigured) { - svg.append("path") - .datum(this.graphData) - .attr("stroke", this.bgcolor) - .attr("stroke-width", 1) - .attr("fill", "none") - //.style("stroke-dasharray", ("3, 3")) - .attr("d", d3.line() - .x((d, i) => xScale(this.graphData[i].date)) - .y(d => yScale(d.batterySoc)) - ); - svg.append("path") - .datum(this.graphData) - .attr("stroke", this.batteryColor) - .attr("stroke-width", 1) - .attr("fill", "none") - .style("stroke-dasharray", ("3, 3")) - .attr("d", d3.line() - .x((d, i) => xScale(this.graphData[i].date)) - .y(d => yScale(d.batterySoc)) - ); - svg.append("text") - .attr("x", (width - this.margin.right) / 2) - .attr("y", yScale(this.graphData[this.graphData.length - 1].batterySoc + 2)) - .text("Speicher") - .attr("fill", this.batteryColor) - .style("background-color", "black") - .style("font-size", 10) - .attr("text-anchor", "middle"); - - } - const socAxis = svg.append("g") - .attr("class", "axis") - .attr("transform", "translate(" + (width - 20) + ",0)") - .call(d3.axisRight(yScale) - .ticks(5) - .tickFormat((d) => (d + "%"))) - - ; - socAxis.selectAll(".tick").attr("font-size", 12); - socAxis.selectAll(".tick line").attr("stroke", this.bgcolor); - socAxis.select(".domain") - .attr("stroke", this.bgcolor) - ; - } -} - - -var dayGraph = new DayGraph(); - diff --git a/web/themes/colors/powerdata.js b/web/themes/colors/powerdata.js index eca4e85e0..26866304e 100644 --- a/web/themes/colors/powerdata.js +++ b/web/themes/colors/powerdata.js @@ -77,13 +77,19 @@ class WbData { if ('showLG' in this.prefs) { this.showLiveGraph = this.prefs.showLG; } + if ('maxPow' in this.prefs) { + powerMeter.maxPower = +this.prefs.maxPow; + } + if ('relPM' in this.prefs) { + powerMeter.showRelativeArcs = this.prefs.relPM; + } } if (this.showLiveGraph) { - dayGraph.deactivate(); - powerGraph.activate(); + powerGraph.deactivateDay(); + powerGraph.activateLive(); } else { - powerGraph.deactivate(); - dayGraph.activate(); + powerGraph.deactivateLive(); + powerGraph.activateDay(); } } diff --git a/web/themes/colors/powergraph.js b/web/themes/colors/powergraph.js index 94e498f6e..d305164e6 100644 --- a/web/themes/colors/powergraph.js +++ b/web/themes/colors/powergraph.js @@ -4,6 +4,8 @@ class PowerGraph { constructor() { this.graphData = []; this.initCounter = 0; + this.staging = []; + this.rawData = []; this.graphdata = [] this.initialGraphData = []; this.initialized = false; @@ -19,6 +21,7 @@ class PowerGraph { this.width = 500; this.height = 500; this.margin = { top: 10, right: 20, bottom: 20, left: 25 }; + this.graphDate = new Date(); } init() { @@ -49,23 +52,24 @@ class PowerGraph { this.svg = figure.append("svg") .attr("viewBox", `0 0 500 500`); - d3.select("button#graphModeButton") - .on("click", switchGraph) + d3.select("button#graphLeftButton") + .on("click", shiftLeft) + d3.select("button#graphRightButton") + .on("click", shiftRight) } - activate() { + activateLive() { try { - this.reset(); + this.resetLiveGraph(); subscribeMqttGraphSegments(); subscribeGraphUpdates(); } catch (err) { // on initial invocation this method is not existing } - d3.select("figure#powergraph").classed("hide", false); + d3.select("h3#graphheading").text("Leistung / Ladestand") } - - deactivate() { - d3.select("figure#powergraph").classed("hide",true); + + deactivateLive() { try { unsubscribeMqttGraphSegments(); unsubscribeGraphUpdates(); @@ -73,62 +77,169 @@ class PowerGraph { // on intial run this method is not existing } } - - update(topic, payload) { - if (this.initialized) { // steady state - - if (topic === "openWB/graph/lastlivevalues") { - const values = this.extractValues(payload.toString()); - this.graphRefreshCounter++; - this.graphData.push(values); - - this.updateGraph(); - - if (this.graphRefreshCounter > 60) { - this.reset(); - subscribeMqttGraphSegments(); - } + activateDay() { + if (!wbdata.showLiveGraph) { + this.resetDayGraph(); + try { + subscribeDayGraph(this.graphDate); + } catch (err) { + //on initial run of activate, subscribeDayGraph is not yet initialized. + // the error can be ignored } - } else { // init phase - const t = topic; - if (t.substring(t.length - 13, t.length) === "alllivevalues") { - // init message - const serialNo = t.substring(13, t.length - 13); - var bulkdata = payload.toString().split("\n"); - if (bulkdata.length <= 1) { - bulkdata = []; - } - if (serialNo != "") { - if (typeof (this.initialGraphData[+serialNo - 1]) === 'undefined') { - this.initialGraphData[+serialNo - 1] = bulkdata; - this.initCounter++; + var heading = "Leistung / Ladestand "; + const today = new Date(); + if (today.getDate() == this.graphDate.getDate() && today.getMonth() == this.graphDate.getMonth() && today.getFullYear() == this.graphDate.getFullYear()) { + heading = heading + "heute"; + } else { + heading = heading + this.graphDate.getDate() + "." + (this.graphDate.getMonth() + 1) + "."; + } + d3.select("h3#graphheading").text(heading); + } + } + + deactivateDay() { + } + updateLive(topic, payload) { + if (wbdata.showLiveGraph) { // only udpdate if live graph is active + if (this.initialized) { // steady state + if (topic === "openWB/graph/lastlivevalues") { + const values = this.extractLiveValues(payload.toString()); + this.graphRefreshCounter++; + this.graphData.push(values); + this.updateGraph(); + if (this.graphRefreshCounter > 60) { + this.resetLiveGraph(); + subscribeMqttGraphSegments(); } } - if (this.initCounter == 16) {// Initialization complete - this.initialized = true; - this.initialGraphData.map(bulkdata => { - bulkdata.map((line) => { - const values = this.extractValues(line); - this.graphData.push(values); + } else { // init phase + const t = topic; + if (t.substring(t.length - 13, t.length) === "alllivevalues") { + // init message + const serialNo = t.substring(13, t.length - 13); + var bulkdata = payload.toString().split("\n"); + if (bulkdata.length <= 1) { + bulkdata = []; + } + if (serialNo != "") { + if (typeof (this.initialGraphData[+serialNo - 1]) === 'undefined') { + this.initialGraphData[+serialNo - 1] = bulkdata; + this.initCounter++; + } + } + if (this.initCounter == 16) {// Initialization complete + this.initialized = true; + this.initialGraphData.map(bulkdata => { + bulkdata.map((line) => { + const values = this.extractLiveValues(line); + this.graphData.push(values); + }); }); - }); - this.updateGraph(); - unsubscribeMqttGraphSegments(); + this.updateGraph(); + unsubscribeMqttGraphSegments(); + } + } + } + } + } + + updateDay(topic, payload) { + if (payload != 'empty') { + var segment = payload.toString().split("\n"); + if (segment.length <= 1) { + segment = []; + } + const serialNo = topic.substring(26, topic.length); + if (serialNo != "") { + if (typeof (this.staging[+serialNo - 1]) === 'undefined') { + this.staging[+serialNo - 1] = segment; + this.initCounter++; } } + if (this.initCounter == 12) {// Initialization complete + unsubscribeDayGraph(); + + this.initCounter = 0; + this.staging.map(segment => + segment.map(line => this.rawData.push(line)) + ) + this.rawData.map((line, i, a) => { + if (i > 0) { + const values = this.extractDayValues(line, a[i - 1]); + this.graphData.push(values); + } else { + // const values = this.extractValues(line, []); + } + }); + this.updateGraph(); + setTimeout(() => this.activateLive(), 300000) + } + } + } + + extractDayValues(payload, oldPayload) { + const elements = payload.split(","); + const oldElements = oldPayload.split(","); + var values = {}; + values.date = new Date(d3.timeParse("%H%M")(elements[0])); + // evu + values.gridPull = this.calcValue(1, elements, oldElements); + values.gridPush = this.calcValue(2, elements, oldElements); + // pv + values.solarPower = this.calcValue(3, elements, oldElements); + values.inverter = 0; + // charge points + values.charging = this.calcValue(7, elements, oldElements); + var i; + for (i = 0; i < 3; i++) { + values["lp" + i] = this.calcValue(4 + i, elements, oldElements); } + for (i = 3; i < 8; i++) { + values["lp" + i] = this.calcValue(12 + i, elements, oldElements); + } + values.soc1 = +elements[21]; + values.soc2 = +elements[22]; + // smart home + for (i = 0; i < 10; i++) { + values["sh" + i] = this.calcValue(26 + i, elements, oldElements); + } + //consumers + values.co0 = this.calcValue(10, elements, oldElements); + values.co1 = this.calcValue(12, elements, oldElements); + //battery + values.batIn = this.calcValue(8, elements, oldElements); + values.batOut = this.calcValue(9, elements, oldElements); + values.batterySoc = +elements[20]; + // calculated values + values.housePower = values.gridPull + values.solarPower + values.batOut + - values.gridPush - values.batIn - values.charging - values.co0 - values.co1 + - values.sh0 - values.sh1 - values.sh2 - values.sh3 - values.sh4 - values.sh5 - values.sh6 - values.sh7 - values.sh8 - values.sh9; + if (values.housePower < 0) { values.housePower = 0; }; + values.selfUsage = values.solarPower - values.gridPush; + if (values.selfUsage < 0) { values.selfUsage = 0; }; + return values; } reset() { - // fresh reloas of the graph + this.resetLiveGraph(); + this.resetDayGraph(); + } + resetLiveGraph() { + // fresh reload of the graph this.initialized = false; this.initCounter = 0; this.initialGraphData = []; this.graphData = []; this.graphRefreshCounter = 0; } - - extractValues(payload) { + resetDayGraph() { + this.initialized = false; + this.initCounter = 0; + this.staging = []; + this.rawData = []; + this.graphData = []; + } + extractLiveValues(payload) { const elements = payload.split(","); var values = {}; values.date = new Date(d3.timeParse("%H:%M:%S")(elements[0])); @@ -187,6 +298,13 @@ class PowerGraph { return values; } + calcValue(i, array, oldArray) { + var val = (array[i] - oldArray[i]) * 12; + if (val < 0 || val > 150000) { + val = 0; + } + return val; + } @@ -216,13 +334,6 @@ class PowerGraph { this.drawUsageGraph(svg, width, height / 2); this.drawXAxis(svg, width, height); this.drawSoc(svg, width, height / 2); - svg.append("text") - .attr("x", width+this.margin.right) - .attr("y", height + this.margin.top) - .attr("text-anchor", "end") - .attr("font-size", 12) - .attr("fill", this.axiscolor) - .text("Kürzlich") } drawSourceGraph(svg, width, height) { @@ -341,7 +452,6 @@ class PowerGraph { const yScale = d3.scaleLinear().range([height - 10, 0]); xScale.domain(d3.extent(this.graphData, (d) => d.date)); yScale.domain([0, 100]); - // Chargepoint 1 if (wbdata.chargePoint[0].isSocConfigured) { svg.append("path") @@ -364,7 +474,6 @@ class PowerGraph { .x((d, i) => xScale(this.graphData[i].date)) .y(d => yScale(d.soc1)) ); - svg.append("text") .attr("x", width - this.margin.right - 3) .attr("y", yScale(this.graphData[this.graphData.length - 1].soc1 + 2)) @@ -403,7 +512,6 @@ class PowerGraph { .style("font-size", 10) .attr("text-anchor", "start"); } - // Battery if (wbdata.isBatteryConfigured) { svg.append("path") @@ -434,7 +542,6 @@ class PowerGraph { .style("background-color", "black") .style("font-size", 10) .attr("text-anchor", "middle"); - } const socAxis = svg.append("g") .attr("class", "axis") @@ -442,8 +549,7 @@ class PowerGraph { .call(d3.axisRight(yScale) .ticks(5) .tickFormat((d) => (d + "%"))) - - ; + ; socAxis.selectAll(".tick").attr("font-size", 12); socAxis.selectAll(".tick line").attr("stroke", this.bgcolor); socAxis.select(".domain") @@ -452,19 +558,35 @@ class PowerGraph { } } -function switchGraph() { +function shiftLeft() { if (wbdata.showLiveGraph) { wbdata.showLiveGraph = false; - powerGraph.deactivate(); - dayGraph.activate(); + powerGraph.deactivateLive(); + powerGraph.activateDay(); wbdata.prefs.showLG = false; wbdata.persistGraphPreferences(); -} else { - wbdata.showLiveGraph = true; - dayGraph.deactivate(); - powerGraph.activate(); - wbdata.prefs.showLG = true; - wbdata.persistGraphPreferences(); + d3.select("button#graphRightButton").classed("disabled", false) + } else { + powerGraph.graphDate.setTime(powerGraph.graphDate.getTime() - 86400000); + powerGraph.activateDay(); + } +} +function shiftRight() { + today = new Date(); + d = powerGraph.graphDate; + if (d.getDate() == today.getDate() && d.getMonth() == today.getMonth() && d.getFullYear() == today.getFullYear()) { + if (!wbdata.showLiveGraph) { + wbdata.showLiveGraph = true; + powerGraph.deactivateDay(); + powerGraph.activateLive(); + wbdata.prefs.showLG = true; + wbdata.persistGraphPreferences(); + d3.select("button#graphLeftButton").classed("disabled", false) + d3.select("button#graphRightButton").classed("disabled", true) + } + } else { + powerGraph.graphDate.setTime(powerGraph.graphDate.getTime() + 86400000); + powerGraph.activateDay(); } } diff --git a/web/themes/colors/powermeter.js b/web/themes/colors/powermeter.js index 1922ab618..378612a66 100644 --- a/web/themes/colors/powermeter.js +++ b/web/themes/colors/powermeter.js @@ -12,6 +12,9 @@ class PowerMeter { this.radius = this.width / 2 - this.margin; this.cornerRadius = 1; this.circleGapSize = (Math.PI / 40); + this.maxPower = 4000; + this.showRelativeArcs = false; + this.emptyPower = 0; } // public method to initialize @@ -26,6 +29,9 @@ class PowerMeter { this.gridColor = style.getPropertyValue('--color-evu'); this.bgColor = style.getPropertyValue('--color-bg'); this.chargeColor = style.getPropertyValue('--color-charging'); + this.axisColor = style.getPropertyValue('--color-axis'); + d3.select("button#powerMeterButton") + .on("click", switchDisplay) } // public method to update the graph @@ -43,10 +49,29 @@ class PowerMeter { "transform", "translate(" + this.width / 2 + "," + this.height / 2 + ")" ); + + if (this.showRelativeArcs) { + this.svg.append("g") + + .append("text") + .attr("x", this.width) + .attr("y", this.height) + .attr("fill", this.axisColor) + .attr("text-anchor", "end") + .attr("font-size", 20) + .attr("id", "powerMeterReset") + .text("RESET") + .on("click", resetButtonClicked); + } + return g; - } + + } + drawGraph(svg) { + this.updateDisplayRatio(); + this.drawSourceArc(svg); this.drawUsageArc(svg); @@ -86,7 +111,27 @@ class PowerMeter { wbdata.usageSummary[3].color); } + if (this.showRelativeArcs) { svg.append("text") + .attr("x", 0) + .attr("y", 5) + .text("Verbrauch: " + formatWatt(wbdata.housePower + wbdata.usageSummary[1].power + wbdata.usageSummary[2].power + wbdata.usageSummary[3].power)) + .attr("fill", "white") + .attr("backgroundcolor", this.bgColor) + .style("text-anchor", "middle") + .style("font-size", "22") + ; + svg.append("text") + .attr("x", this.width / 2 -42) + .attr("y", 2) + .text("Max: " + formatWatt(this.maxPower)) + .attr("fill", this.axisColor) + .attr("backgroundcolor", this.bgColor) + .style("text-anchor", "middle") + .style("font-size", "12") + ; + } else { + svg.append("text") .attr("x", 0) .attr("y", 0) .text("Aktueller Verbrauch: " + formatWatt(wbdata.housePower + wbdata.usageSummary[1].power + wbdata.usageSummary[2].power + wbdata.usageSummary[3].power)) @@ -95,6 +140,9 @@ class PowerMeter { .style("text-anchor", "middle") .style("font-size", "22") ; + } + + } drawSourceArc(svg) { @@ -112,20 +160,23 @@ class PowerMeter { .cornerRadius(this.cornerRadius); // Add the chart to the svg + const arcCount = Object.values(wbdata.sourceSummary).length; + svg.selectAll("sources") - .data(pieGenerator(Object.values (wbdata.sourceSummary))).enter() + .data(pieGenerator(Object.values (wbdata.sourceSummary).concat ([{"power": this.emptyPower, "color": this.bgColor}]))).enter() .append("path") .attr("d", arc) - .attr("fill", (d) => d.data.color); + .attr("fill", (d) => d.data.color) + .attr("stroke", (d,i) => (i==arcCount && d.data.power > 0) ? this.axisColor : "null"); } drawUsageArc(svg) { - + // Define the generator for the segments const pieGenerator = d3.pie() .value((record) => Number(record.power)) .startAngle(Math.PI * 1.5 - this.circleGapSize) - .endAngle(Math.PI / 2 + this.circleGapSize) + .endAngle(Math.PI / 2 + this.circleGapSize ) .sort(null); // Generator for the pie chart @@ -135,11 +186,13 @@ class PowerMeter { .cornerRadius(this.cornerRadius); // Add the chart to the svg + const arcCount = wbdata.usageSummary.length; svg.selectAll("consumers") - .data(pieGenerator(wbdata.usageSummary)).enter() + .data(pieGenerator(wbdata.usageSummary.concat ([{"power": this.emptyPower, "color": this.bgColor}]))).enter() .append("path") .attr("d", arc) - .attr("fill", (d) => d.data.color); + .attr("fill", (d) => d.data.color) + .attr("stroke", (d,i) => (i==arcCount && d.data.power > 0) ? this.axisColor : "null"); } addLabel(svg, x, y, anchor, data) { @@ -174,6 +227,39 @@ class PowerMeter { calcColor(row) { return ("color:" + row.color + "; text-align:center"); } + + updateDisplayRatio() { + if (this.showRelativeArcs) { + this.displayRatio = (+wbdata.sourceSummary.pv.power + wbdata.sourceSummary.evuIn.power + wbdata.sourceSummary.batOut.power) / this.maxPower; + this.emptyPower = this.maxPower - (+wbdata.sourceSummary.pv.power + wbdata.sourceSummary.evuIn.power + wbdata.sourceSummary.batOut.power); + if (this.emptyPower < 0) { + this.maxPower = +wbdata.sourceSummary.pv.power + wbdata.sourceSummary.evuIn.power + wbdata.sourceSummary.batOut.power; + this.emptyPower = 0; + wbdata.prefs.maxPow = this.maxPower; + wbdata.persistGraphPreferences(); + } + } else { + this.emptyPower = 0; + } + } + + resetDisplayRatio() { + this.maxPower = +wbdata.sourceSummary.pv.power + wbdata.sourceSummary.evuIn.power + wbdata.sourceSummary.batOut.power; + this.emptyPower = 0; + wbdata.prefs.maxPow = this.maxPower; + wbdata.persistGraphPreferences(); + } } +function switchDisplay () { + powerMeter.showRelativeArcs = powerMeter.showRelativeArcs ? false : true; + wbdata.prefs.relPM = powerMeter.showRelativeArcs; + wbdata.persistGraphPreferences(); + powerMeter.update(); +} + +function resetButtonClicked() { + powerMeter.resetDisplayRatio(); + powerMeter.update(); +} var powerMeter = new PowerMeter(); diff --git a/web/themes/colors/processAllMqttMsg.js b/web/themes/colors/processAllMqttMsg.js index 98ee9b71f..0288887fc 100644 --- a/web/themes/colors/processAllMqttMsg.js +++ b/web/themes/colors/processAllMqttMsg.js @@ -255,7 +255,7 @@ function processGraphMessages(mqttmsg, mqttpayload) { //checkgraphload(); } else if (mqttmsg.match(/^openwb\/graph\/[1-9][0-9]*alllivevalues$/i)) { - powerGraph.update(mqttmsg, mqttpayload); + powerGraph.updateLive(mqttmsg, mqttpayload); /* var index = mqttmsg.match(/(\d+)(?!.*\d)/g)[0]; // extract last match = number from mqttmsg // now call functions or set variables corresponding to the index if (initialread == 0) { @@ -265,7 +265,7 @@ function processGraphMessages(mqttmsg, mqttpayload) { }*/ } else if (mqttmsg == 'openWB/graph/lastlivevalues') { - powerGraph.update(mqttmsg, mqttpayload); + powerGraph.updateLive(mqttmsg, mqttpayload); /* if ( initialread > 0) { //updateGraph(mqttpayload); } @@ -628,7 +628,7 @@ function processSystemMessages(mqttmsg, mqttpayload) { $('#date').text(date); } else if (mqttmsg.match(/^openwb\/system\/daygraphdata[1-9][0-9]*$/i)) { - dayGraph.update(mqttmsg, mqttpayload); + powerGraph.updateDay(mqttmsg, mqttpayload); } } @@ -1216,11 +1216,11 @@ function unsubscribeGraphUpdates() { topic = "openWB/graph/lastlivevalues"; client.unsubscribe(topic); } -function subscribeDayGraph() { - var today = new Date(); - var dd = String(today.getDate()).padStart(2, '0'); - var mm = String(today.getMonth() + 1).padStart(2, '0'); //January is 0! - var yyyy = today.getFullYear(); +function subscribeDayGraph(date) { + // var today = new Date(); + var dd = String(date.getDate()).padStart(2, '0'); + var mm = String(date.getMonth() + 1).padStart(2, '0'); //January is 0! + var yyyy = date.getFullYear(); graphdate = yyyy + mm + dd; for (var segment = 1; segment < 13; segment++) { var topic = "openWB/system/DayGraphData" + segment; diff --git a/web/themes/colors/setupMqttServices.js b/web/themes/colors/setupMqttServices.js index 5da740384..28960e1b2 100644 --- a/web/themes/colors/setupMqttServices.js +++ b/web/themes/colors/setupMqttServices.js @@ -385,7 +385,7 @@ var options = { topicsToSubscribe.forEach((topic) => { client.subscribe(topic[0], { qos: 0 }); }); - subscribeDayGraph(); + subscribeDayGraph(new Date()); }, //Gets Called if the connection could not be established onFailure: function (message) { diff --git a/web/themes/colors/theme.html b/web/themes/colors/theme.html index f8f3d3c79..624a456b8 100644 --- a/web/themes/colors/theme.html +++ b/web/themes/colors/theme.html @@ -111,7 +111,6 @@ - @@ -166,39 +165,49 @@
+
-

Aktuelle Leistung

+
+
+
+

Aktuelle Leistung

+
+
+ +
+
+
+
-

Leistung / Ladestand

+

Leistung / Ladestand

- + +
-
+

Energie heute

+
-

Ladepunkte

-
-
@@ -206,8 +215,6 @@

Ladepunkte

Minimal Stromstärke

- -
@@ -685,12 +692,14 @@

Lademengenbegrenzung

+

Geräte

+

Speicher

@@ -898,7 +907,6 @@ wbdata.init(); powerGraph.init(); - dayGraph.init(); powerMeter.init(); yieldMeter.init(); chargePointList.init();