From 4ff0b9a5fd2d8b4d336a6c51b54d0aa71f0f497d Mon Sep 17 00:00:00 2001 From: Michael Geers Date: Mon, 6 Feb 2023 21:57:00 +0100 Subject: [PATCH] Add option to show current price and co2 info (#6048) --- .../Energyflow/Energyflow.story.vue | 4 + .../js/components/Energyflow/Energyflow.vue | 44 +++++++++ .../components/Energyflow/EnergyflowEntry.vue | 61 +++++++++++- assets/js/components/GlobalSettingsModal.vue | 30 ++++++ assets/js/components/SelectGroup.vue | 1 + assets/js/components/Site.vue | 12 ++- assets/js/components/TargetChargePlan.vue | 4 +- assets/js/components/TopNavigation.vue | 9 +- assets/js/gridDetails.js | 22 +++++ assets/js/mixins/formatter.js | 15 ++- assets/js/settings.js | 3 + cmd/demo.yaml | 1 + core/savings.go | 95 ++++++------------- core/savings_test.go | 93 +++++------------- core/site.go | 84 +++++++++++++++- core/site_test.go | 45 +++++++++ histoire.config.js | 12 +++ i18n/de.toml | 6 ++ i18n/en.toml | 6 ++ tariff/electricitymaps.go | 2 +- tariff/gruenstromindex.go | 2 +- tariff/tariffs.go | 32 +++++++ util/telemetry/charge.go | 6 +- 23 files changed, 430 insertions(+), 159 deletions(-) create mode 100644 assets/js/gridDetails.js diff --git a/assets/js/components/Energyflow/Energyflow.story.vue b/assets/js/components/Energyflow/Energyflow.story.vue index 2323003435..c8b30c8251 100644 --- a/assets/js/components/Energyflow/Energyflow.story.vue +++ b/assets/js/components/Energyflow/Energyflow.story.vue @@ -27,6 +27,10 @@ import Energyflow from "./Energyflow.vue"; :batteryPower="800" :batterySoc="77" siteTitle="Home" + :battery="[ + { soc: 44, capacity: 13.3 }, + { soc: 82, capacity: 21 }, + ]" /> diff --git a/assets/js/components/Energyflow/Energyflow.vue b/assets/js/components/Energyflow/Energyflow.vue index 7a2636591a..8dce4c9247 100644 --- a/assets/js/components/Energyflow/Energyflow.vue +++ b/assets/js/components/Energyflow/Energyflow.vue @@ -65,12 +65,17 @@ :soc="batterySoc" :power="batteryDischarge" :valuesInKw="valuesInKw" + :tooltip="batteryTooltip" /> @@ -89,6 +94,10 @@ icon="home" :power="homePower" :valuesInKw="valuesInKw" + :price="tariffEffectivePrice" + :currency="currency" + :co2="tariffEffectiveCo2" + :tooltip="detailsTooltip(tariffEffectivePrice, tariffEffectiveCo2)" /> @@ -143,9 +160,16 @@ export default { loadpointsPower: { type: Number, default: 0 }, activeLoadpointsCount: { type: Number, default: 0 }, batteryConfigured: Boolean, + battery: { type: Array }, batteryPower: { type: Number, default: 0 }, batterySoc: { type: Number, default: 0 }, vehicleIcons: { type: Array }, + tariffGrid: { type: Number }, + tariffFeedIn: { type: Number }, + tariffEffectivePrice: { type: Number }, + tariffCo2: { type: Number }, + tariffEffectiveCo2: { type: Number }, + currency: { type: String }, }, data: () => { return { detailsOpen: false, detailsCompleteHeight: null }; @@ -187,6 +211,16 @@ export default { detailsHeight: function () { return this.detailsOpen ? this.detailsCompleteHeight + "px" : 0; }, + batteryTooltip() { + if (!Array.isArray(this.battery)) { + return; + } + return this.battery.map(({ soc, capacity }) => { + const energy = this.fmtKWh((capacity / 100) * soc * 1e3, true, false, 1); + const total = this.fmtKWh(capacity * 1e3, true, true, 1); + return this.$t("main.energyflow.batteryTooltip", { energy, total, soc }); + }); + }, }, mounted() { window.addEventListener("resize", this.updateHeight); @@ -199,6 +233,16 @@ export default { window.removeEventListener("resize", this.updateHeight); }, methods: { + detailsTooltip(price, co2) { + const result = []; + if (co2 !== undefined) { + result.push(`${this.fmtCo2Long(co2)}`); + } + if (price !== undefined) { + result.push(`${this.fmtPricePerKWh(price, this.currency)}`); + } + return result; + }, kw: function (watt) { return this.fmtKw(watt, this.valuesInKw); }, diff --git a/assets/js/components/Energyflow/EnergyflowEntry.vue b/assets/js/components/Energyflow/EnergyflowEntry.vue index 3cc8fb17a4..1efd3290b7 100644 --- a/assets/js/components/Energyflow/EnergyflowEntry.vue +++ b/assets/js/components/Energyflow/EnergyflowEntry.vue @@ -5,10 +5,22 @@ - {{ name }} - {{ soc }}% / - + + {{ name }} + + +
+ + + {{ soc }}% +
+
@@ -17,10 +29,12 @@ import "@h2d2/shopicons/es/regular/powersupply"; import "@h2d2/shopicons/es/regular/sun"; import "@h2d2/shopicons/es/regular/home"; +import Tooltip from "bootstrap/js/dist/tooltip"; import BatteryIcon from "./BatteryIcon.vue"; import formatter from "../../mixins/formatter"; import AnimatedNumber from "../AnimatedNumber.vue"; import VehicleIcon from "../VehicleIcon"; +import { showGridPrice, showGridCo2 } from "../../gridDetails"; export default { name: "EnergyflowEntry", @@ -31,8 +45,15 @@ export default { icon: { type: String }, power: { type: Number }, soc: { type: Number }, + price: { type: Number }, + co2: { type: Number }, valuesInKw: { type: Boolean }, vehicleIcons: { type: Array }, + currency: { type: String }, + tooltip: { type: Array }, + }, + data() { + return { tooltipInstance: null }; }, computed: { active: function () { @@ -48,10 +69,39 @@ export default { return this.isBattery && !isNaN(this.soc); }, }, + watch: { + tooltip(newVal, oldVal) { + if (JSON.stringify(newVal) !== JSON.stringify(oldVal)) { + this.updateTooltip(); + } + }, + }, + mounted: function () { + this.updateTooltip(); + }, methods: { + showPrice() { + return showGridPrice() && !isNaN(this.price); + }, + showCo2() { + return showGridCo2() && !isNaN(this.co2); + }, kw: function (watt) { return this.fmtKw(watt, this.valuesInKw); }, + fmtPrice: function (price) { + return this.fmtPricePerKWh(price, this.currency, true); + }, + updateTooltip: function () { + if (!Array.isArray(this.tooltip) || !this.tooltip.length) { + return; + } + if (!this.tooltipInstance) { + this.tooltipInstance = new Tooltip(this.$refs.details, { html: true }); + } + const html = `
${this.tooltip.join("
")}
`; + this.tooltipInstance.setContent({ ".tooltip-inner": html }); + }, }, }; @@ -59,4 +109,7 @@ export default { .entry { transition: color var(--evcc-transition-medium) linear; } +.power { + min-width: 75px; +} diff --git a/assets/js/components/GlobalSettingsModal.vue b/assets/js/components/GlobalSettingsModal.vue index b04c053434..c03a3dfb7c 100644 --- a/assets/js/components/GlobalSettingsModal.vue +++ b/assets/js/components/GlobalSettingsModal.vue @@ -63,6 +63,23 @@ " /> + + + @@ -81,20 +98,25 @@ import SelectGroup from "./SelectGroup.vue"; import { getLocalePreference, setLocalePreference, LOCALES, removeLocalePreference } from "../i18n"; import { getThemePreference, setThemePreference, THEMES } from "../theme"; import { getUnits, setUnits, UNITS } from "../units"; +import { getGridDetails, setGridDetails, GRID_DETAILS } from "../gridDetails"; export default { name: "GlobalSettingsModal", components: { TelemetrySettings, FormRow, SelectGroup }, props: { sponsor: String, + hasPrice: Boolean, + hasCo2: Boolean, }, data: function () { return { theme: getThemePreference(), language: getLocalePreference() || "", unit: getUnits(), + gridDetails: getGridDetails(), THEMES, UNITS, + GRID_DETAILS, }; }, computed: { @@ -111,6 +133,9 @@ export default { unit(value) { setUnits(value); }, + gridDetails(value) { + setGridDetails(value); + }, theme(value) { setThemePreference(value); }, @@ -123,6 +148,11 @@ export default { } }, }, + methods: { + isDisabled(option) { + return (option === "co2" && !this.hasCo2) || (option === "price" && !this.hasPrice); + }, + }, };