diff --git a/assets/css/app.css b/assets/css/app.css index 0f3da24732..1ce4a3fa56 100644 --- a/assets/css/app.css +++ b/assets/css/app.css @@ -855,6 +855,13 @@ input[list]::-webkit-list-button { } } +html.embedded { + --safe-area-inset-top: 0px; + --safe-area-inset-bottom: 0px; + --safe-area-inset-left: 0px; + --safe-area-inset-right: 0px; +} + html.app .hide-in-app { display: none !important; } diff --git a/assets/js/components/Config/CircuitsLegacyModal.vue b/assets/js/components/Config/CircuitsLegacyModal.vue index 15b2142c78..f062f5e63c 100644 --- a/assets/js/components/Config/CircuitsLegacyModal.vue +++ b/assets/js/components/Config/CircuitsLegacyModal.vue @@ -4,7 +4,7 @@ name="circuitslegacy" :title="`${$t('config.circuits.title')} (${$t('config.general.legacy')})`" :description="$t('config.circuits.description')" - docs="/reference/configuration/circuits" + docs="/features/loadmanagement" :defaultYaml="defaultYaml" endpoint="/config/circuits" removeKey="circuits" diff --git a/assets/js/components/Config/DeviceTags.vue b/assets/js/components/Config/DeviceTags.vue index 66c04692bf..a47055e844 100644 --- a/assets/js/components/Config/DeviceTags.vue +++ b/assets/js/components/Config/DeviceTags.vue @@ -72,7 +72,7 @@ import TariffChart from "../Tariff/TariffChart.vue"; import { generateRateSlots, calculateCostRange } from "@/utils/tariffSlots"; import { distanceValue, distanceUnit } from "@/units"; -const HIDDEN_TAGS = ["icon", "heating", "integratedDevice", "continuous"]; +const HIDDEN_TAGS = ["icon", "heating", "integratedDevice", "continuous", "demandPredictor"]; const PHASE_TAGS = ["phaseCurrents", "phaseVoltages", "phasePowers"]; diff --git a/assets/js/components/Config/FormRow.vue b/assets/js/components/Config/FormRow.vue index 66c8e549d3..13844dbec4 100644 --- a/assets/js/components/Config/FormRow.vue +++ b/assets/js/components/Config/FormRow.vue @@ -29,6 +29,7 @@ {{ $t("config.general.docsLink") }} + diff --git a/assets/js/components/Config/LoadpointModal.vue b/assets/js/components/Config/LoadpointModal.vue index 7b935b43b3..e7046c0a78 100644 --- a/assets/js/components/Config/LoadpointModal.vue +++ b/assets/js/components/Config/LoadpointModal.vue @@ -447,7 +447,51 @@
-
{{ $t("config.loadpoint.temperatureRangeTitle") }}
+
{{ $t("config.loadpoint.demandPredictor.title") }}
+

+ {{ $t("config.loadpoint.demandPredictor.description") }} +

+ + + + +
+ +
+
{{ $t("config.loadpoint.temperatureRangeTitle") }}

{{ $t("config.loadpoint.temperatureRangeHelp") }}

@@ -568,10 +612,13 @@ import { type VehicleOption, type ConfigCircuit, type ConfigLoadpoint, + type DemandPredictor, } from "@/types/evcc"; const nsPerMin = 60 * 1e9; +const DEMAND_PREDICTORS: DemandPredictor[] = ["daily", "weekday", "temperature"]; + const { OFF, SMART, NOW } = CHARGE_MODE; const defaultValues = { @@ -627,6 +674,7 @@ export default { }, voltage: Number, gridConnection: String as PropType, + temperatureTariffConfigured: Boolean, }, emits: ["changed", "dismissed", "disable"], data() { @@ -645,6 +693,44 @@ export default { id(): number | undefined { return getModal("loadpoint")?.id; }, + defaultDemandPredictor(): DemandPredictor { + return this.chargerStatus?.demandPredictor?.value ?? "daily"; + }, + demandPredictorGroups() { + const t = (key: string) => this.$t(`config.loadpoint.demandPredictor.${key}`); + const label = (profile: DemandPredictor) => t(`${profile}.label`); + return [ + { + label: t("defaultGroup"), + options: [{ value: "", label: label(this.defaultDemandPredictor) }], + }, + { + label: t("profilesGroup"), + options: DEMAND_PREDICTORS.map((value) => ({ value, label: label(value) })), + }, + ]; + }, + demandPredictorSelection: { + get(): string { + return this.values.demandPredictor ?? ""; + }, + set(value: string) { + this.values.demandPredictor = (value as DemandPredictor) || undefined; + }, + }, + activeDemandPredictor(): DemandPredictor { + return this.values.demandPredictor || this.defaultDemandPredictor; + }, + demandPredictorHelp(): string { + return this.$t( + `config.loadpoint.demandPredictor.${this.activeDemandPredictor}.description` + ); + }, + temperatureTariffMissing(): boolean { + return ( + this.activeDemandPredictor === "temperature" && !this.temperatureTariffConfigured + ); + }, thresholdsSet(): boolean { const { enable, disable } = this.values.thresholds; return !!(enable.threshold || disable.threshold); @@ -1020,6 +1106,9 @@ export default { this.values.charger = ""; } }, + addTemperatureTariff() { + openModal("tariff", { type: "temperature" }); + }, async editMeter() { const meter = this.meters.find((m) => m.name === this.values.meter); const result = await openModal("meter", { diff --git a/assets/js/components/Config/Markdown.test.ts b/assets/js/components/Config/Markdown.test.ts new file mode 100644 index 0000000000..2ef742d658 --- /dev/null +++ b/assets/js/components/Config/Markdown.test.ts @@ -0,0 +1,35 @@ +import { mount } from "@vue/test-utils"; +import { describe, expect, test } from "vite-plus/test"; +import Markdown from "./Markdown.vue"; + +const render = (markdown: string) => mount(Markdown, { props: { markdown } }).html(); + +describe("Markdown", () => { + test("keeps underscores inside words as text", () => { + const html = render("charge_before_export and a_b_c stay flat"); + expect(html).toContain("charge_before_export and a_b_c stay flat"); + expect(html).not.toContain(""); + }); + + test("keeps underscores in firmware versions", () => { + const html = render("requires M3W_3.11STP or M3W_4.03PTB"); + expect(html).toContain("requires M3W_3.11STP or M3W_4.03PTB"); + expect(html).not.toContain(""); + }); + + test("opens links in new window", () => { + expect(render("[docs](https://docs.evcc.io)")).toContain( + 'docs' + ); + }); + + test("still emphasises whole words", () => { + expect(render("so _early_ and __bold__")).toContain( + "so early and bold" + ); + }); + + test("keeps underscores inside code", () => { + expect(render("`charge_before_export`")).toContain("charge_before_export"); + }); +}); diff --git a/assets/js/components/Config/Markdown.vue b/assets/js/components/Config/Markdown.vue index 88514bec0e..3fa3cbc940 100644 --- a/assets/js/components/Config/Markdown.vue +++ b/assets/js/components/Config/Markdown.vue @@ -4,7 +4,7 @@