Skip to content

Commit

Permalink
Add option to show current price and co2 info (evcc-io#6048)
Browse files Browse the repository at this point in the history
  • Loading branch information
naltatis authored Feb 6, 2023
1 parent d019854 commit 4ff0b9a
Show file tree
Hide file tree
Showing 23 changed files with 430 additions and 159 deletions.
4 changes: 4 additions & 0 deletions assets/js/components/Energyflow/Energyflow.story.vue
Original file line number Diff line number Diff line change
Expand Up @@ -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 },
]"
/>
</Variant>
<Variant title="battery charging">
Expand Down
44 changes: 44 additions & 0 deletions assets/js/components/Energyflow/Energyflow.vue
Original file line number Diff line number Diff line change
Expand Up @@ -65,12 +65,17 @@
:soc="batterySoc"
:power="batteryDischarge"
:valuesInKw="valuesInKw"
:tooltip="batteryTooltip"
/>
<EnergyflowEntry
:name="$t('main.energyflow.gridImport')"
icon="powersupply"
:power="gridImport"
:valuesInKw="valuesInKw"
:price="tariffGrid"
:currency="currency"
:co2="tariffCo2"
:tooltip="detailsTooltip(tariffGrid, tariffCo2)"
/>
</div>
</div>
Expand All @@ -89,6 +94,10 @@
icon="home"
:power="homePower"
:valuesInKw="valuesInKw"
:price="tariffEffectivePrice"
:currency="currency"
:co2="tariffEffectiveCo2"
:tooltip="detailsTooltip(tariffEffectivePrice, tariffEffectiveCo2)"
/>
<EnergyflowEntry
:name="
Expand All @@ -100,6 +109,10 @@
:vehicleIcons="vehicleIcons"
:power="loadpointsPower"
:valuesInKw="valuesInKw"
:price="tariffEffectivePrice"
:currency="currency"
:co2="tariffEffectiveCo2"
:tooltip="detailsTooltip(tariffEffectivePrice, tariffEffectiveCo2)"
/>
<EnergyflowEntry
v-if="batteryConfigured"
Expand All @@ -108,12 +121,16 @@
:soc="batterySoc"
:power="batteryCharge"
:valuesInKw="valuesInKw"
:tooltip="batteryTooltip"
/>
<EnergyflowEntry
:name="$t('main.energyflow.pvExport')"
icon="powersupply"
:power="pvExport"
:valuesInKw="valuesInKw"
:price="-tariffFeedIn"
:currency="currency"
:tooltip="detailsTooltip(-tariffFeedIn)"
/>
</div>
</div>
Expand Down Expand Up @@ -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 };
Expand Down Expand Up @@ -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);
Expand All @@ -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);
},
Expand Down
61 changes: 57 additions & 4 deletions assets/js/components/Energyflow/EnergyflowEntry.vue
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,22 @@
<VehicleIcon v-else-if="isVehicle" :names="vehicleIcons" />
<component :is="`shopicon-regular-${icon}`" v-else></component>
</span>
<span class="text-nowrap flex-grow-1 ms-3">{{ name }}</span>
<span class="text-end text-nowrap ps-1 fw-bold"
><span v-if="hasSoc">{{ soc }}% / </span>
<AnimatedNumber :to="power" :format="kw" />
<span class="text-nowrap flex-grow-1 ms-3">
{{ name }}
</span>
<span class="text-end text-nowrap ps-1 fw-bold d-flex">
<div
ref="details"
class="evcc-gray fw-normal"
data-bs-toggle="tooltip"
title=" "
@click.stop=""
>
<span v-if="showPrice()"><AnimatedNumber :to="price" :format="fmtPrice" /></span>
<span v-if="showCo2()"><AnimatedNumber :to="co2" :format="fmtCo2Short" /></span>
<span v-if="hasSoc">{{ soc }}%</span>
</div>
<AnimatedNumber :to="power" :format="kw" class="power" />
</span>
</div>
</template>
Expand All @@ -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",
Expand All @@ -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 () {
Expand All @@ -48,15 +69,47 @@ 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 = `<div class="text-end">${this.tooltip.join("<br/>")}</div>`;
this.tooltipInstance.setContent({ ".tooltip-inner": html });
},
},
};
</script>
<style scoped>
.entry {
transition: color var(--evcc-transition-medium) linear;
}
.power {
min-width: 75px;
}
</style>
30 changes: 30 additions & 0 deletions assets/js/components/GlobalSettingsModal.vue
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,23 @@
"
/>
</FormRow>
<FormRow
id="settingsGridDetails"
:label="$t('settings.gridDetails.label')"
>
<SelectGroup
id="settingsGridDetails"
v-model="gridDetails"
class="w-100"
:options="
GRID_DETAILS.map((value) => ({
value,
name: $t(`settings.gridDetails.${value}`),
disabled: isDisabled(value),
}))
"
/>
</FormRow>
<FormRow id="telemetryEnabled" :label="$t('settings.telemetry.label')">
<TelemetrySettings :sponsor="sponsor" class="mt-1 mb-0" />
</FormRow>
Expand All @@ -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: {
Expand All @@ -111,6 +133,9 @@ export default {
unit(value) {
setUnits(value);
},
gridDetails(value) {
setGridDetails(value);
},
theme(value) {
setThemePreference(value);
},
Expand All @@ -123,6 +148,11 @@ export default {
}
},
},
methods: {
isDisabled(option) {
return (option === "co2" && !this.hasCo2) || (option === "price" && !this.hasPrice);
},
},
};
</script>
<style scoped>
Expand Down
1 change: 1 addition & 0 deletions assets/js/components/SelectGroup.vue
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
type="button"
class="btn btn-sm flex-grow-1 flex-shrink-1"
:class="{ active: option.value === modelValue }"
:disabled="option.disabled"
@click="$emit('update:modelValue', option.value)"
>
{{ option.name }}
Expand Down
12 changes: 11 additions & 1 deletion assets/js/components/Site.vue
Original file line number Diff line number Diff line change
Expand Up @@ -61,14 +61,14 @@ export default {
batteryConfigured: Boolean,
batteryPower: Number,
batterySoc: Number,
battery: Array,
gridCurrents: Array,
prioritySoc: Number,
siteTitle: String,
vehicles: Array,
auth: Object,
// footer
currency: String,
savingsAmount: Number,
savingsEffectivePrice: Number,
Expand All @@ -77,8 +77,12 @@ export default {
savingsSelfConsumptionPercent: Number,
savingsSince: String,
savingsTotalCharged: Number,
greenShare: Number,
tariffFeedIn: Number,
tariffGrid: Number,
tariffEffectivePrice: Number,
tariffCo2: Number,
tariffEffectiveCo2: Number,
availableVersion: String,
releaseNotes: String,
Expand Down Expand Up @@ -113,6 +117,12 @@ export default {
const vehicleLogins = this.auth ? this.auth.vehicles : {};
return { vehicleLogins, ...this.collectProps(TopNavigation) };
},
hasPrice: function () {
return !isNaN(this.tariffGrid);
},
hasCo2: function () {
return !isNaN(this.tariffCo2);
},
showParkingLot: function () {
// work in progess
return false;
Expand Down
4 changes: 1 addition & 3 deletions assets/js/components/TargetChargePlan.vue
Original file line number Diff line number Diff line change
Expand Up @@ -94,9 +94,7 @@ export default {
if (price === undefined) {
return this.$t("main.targetChargePlan.unknownPrice");
}
return this.isCo2
? `${Math.round(price)} g/kWh`
: this.fmtPricePerKWh(price, this.unit);
return this.isCo2 ? this.fmtCo2Medium(price) : this.fmtPricePerKWh(price, this.unit);
},
activeSlot() {
return this.slots[this.activeIndex];
Expand Down
9 changes: 8 additions & 1 deletion assets/js/components/TopNavigation.vue
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@
</a>
</li>
</ul>
<GlobalSettingsModal :sponsor="sponsor" />
<GlobalSettingsModal v-bind="globalSettingsModalProps" />
</div>
</template>

Expand All @@ -104,12 +104,14 @@ import "@h2d2/shopicons/es/regular/moonstars";
import "@h2d2/shopicons/es/regular/menu";
import "@h2d2/shopicons/es/regular/newtab";
import GlobalSettingsModal from "./GlobalSettingsModal.vue";
import collector from "../mixins/collector";
import baseAPI from "../baseapi";
export default {
name: "TopNavigation",
components: { GlobalSettingsModal },
mixins: [collector],
props: {
vehicleLogins: {
type: Object,
Expand All @@ -118,8 +120,13 @@ export default {
},
},
sponsor: String,
hasPrice: Boolean,
hasCo2: Boolean,
},
computed: {
globalSettingsModalProps: function () {
return this.collectProps(GlobalSettingsModal);
},
logoutCount() {
return this.providerLogins.filter((login) => !login.loggedIn).length;
},
Expand Down
Loading

0 comments on commit 4ff0b9a

Please sign in to comment.