From 1617f4ef544b767705b727f48af544f6c0b65cf0 Mon Sep 17 00:00:00 2001 From: Copilot <198982749+Copilot@users.noreply.github.com> Date: Sun, 19 Apr 2026 19:09:36 +0000 Subject: [PATCH 0001/1224] Remove YAML-specific device template input hints (#29259) --- templates/definition/charger/evbox-livo.yaml | 4 ++-- templates/definition/meter/solax-hybrid-cloud.yaml | 4 ++-- templates/definition/meter/solax-inverter-cloud.yaml | 4 ++-- util/templates/defaults.yaml | 8 ++++---- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/templates/definition/charger/evbox-livo.yaml b/templates/definition/charger/evbox-livo.yaml index cb6d119e7..5c5f35cc6 100644 --- a/templates/definition/charger/evbox-livo.yaml +++ b/templates/definition/charger/evbox-livo.yaml @@ -6,8 +6,8 @@ products: requirements: evcc: ["eebus"] description: - de: Das Gerät benötigt eine feste IP Adresse. Es ist wichtig, zuerst EEBus einzurichten. Danach erkennt das Ladegerät evcc als HEMS-Gerät im Netzwerk. Verwende das Installationstool, um evcc als HEMS auszuwählen. Kopiere anschließend den angegebenen SKI aus der Installations-App und fügen ihn zu Ihrem YAML hinzu. - en: The device requires a fixed IP addres. It's important to set up EEBus first. After setting up EEBus the charger will recognize evcc as a HEMS device on the network. Please use the installer tool to select evcc as HEMS. After this has been done, copy the given SKI from the Install app and add it to your yaml. + de: Das Gerät benötigt eine feste IP Adresse. Es ist wichtig, zuerst EEBus einzurichten. Danach erkennt das Ladegerät evcc als HEMS-Gerät im Netzwerk. Verwende das Installationstool, um evcc als HEMS auszuwählen. Kopiere anschließend den angegebenen SKI aus der Installations-App und füge ihn zur Konfiguration hinzu. + en: The device requires a fixed IP address. It's important to set up EEBus first. After setting up EEBus the charger will recognize evcc as a HEMS device on the network. Please use the installer tool to select evcc as HEMS. After this has been done, copy the given SKI from the Install app and add it to the configuration. params: - preset: eebus render: | diff --git a/templates/definition/meter/solax-hybrid-cloud.yaml b/templates/definition/meter/solax-hybrid-cloud.yaml index 680d2565c..693f13833 100644 --- a/templates/definition/meter/solax-hybrid-cloud.yaml +++ b/templates/definition/meter/solax-hybrid-cloud.yaml @@ -22,8 +22,8 @@ params: description: generic: SolaxCloud TokenID help: - de: "[solaxcloud.com](https://www.solaxcloud.com/) -> Support -> Drittanbieter-Ökosystem (alte Website) oder Dienst -> API (neue Website), den Wert von `tokenID` hier in Anführungszeichen eintragen (Beispiel: '20241028488283838')" - en: "[solaxcloud.com](https://www.solaxcloud.com/) -> Support -> Third-party Ecology (old site) or Service -> API (new site), put the value of `tokenID` here in single quotes (Example: '20241028488283838')" + de: "[solaxcloud.com](https://www.solaxcloud.com/) -> Support -> Drittanbieter-Ökosystem (alte Website) oder Dienst -> API (neue Website), den Wert von `tokenID` hier eintragen (Beispiel: 20241028488283838)" + en: "[solaxcloud.com](https://www.solaxcloud.com/) -> Support -> Third-party Ecology (old site) or Service -> API (new site), enter the value of `tokenID` here (Example: 20241028488283838)" - name: serial required: true description: diff --git a/templates/definition/meter/solax-inverter-cloud.yaml b/templates/definition/meter/solax-inverter-cloud.yaml index d43952b51..86d5c8b1d 100644 --- a/templates/definition/meter/solax-inverter-cloud.yaml +++ b/templates/definition/meter/solax-inverter-cloud.yaml @@ -22,8 +22,8 @@ params: description: generic: SolaxCloud TokenID help: - de: "[solaxcloud.com](https://www.solaxcloud.com/) -> Support -> Drittanbieter-Ökosystem (alte Website) oder Dienst -> API (neue Website), den Wert von `tokenID` hier in Anführungszeichen eintragen (Beispiel: '20241028488283838')" - en: "[solaxcloud.com](https://www.solaxcloud.com/) -> Support -> Third-party Ecology (old site) or Service -> API (new site), put the value of `tokenID` here in single quotes (Example: '20241028488283838')" + de: "[solaxcloud.com](https://www.solaxcloud.com/) -> Support -> Drittanbieter-Ökosystem (alte Website) oder Dienst -> API (neue Website), den Wert von `tokenID` hier eintragen (Beispiel: 20241028488283838)" + en: "[solaxcloud.com](https://www.solaxcloud.com/) -> Support -> Third-party Ecology (old site) or Service -> API (new site), enter the value of `tokenID` here (Example: 20241028488283838)" - name: serial required: true description: diff --git a/util/templates/defaults.yaml b/util/templates/defaults.yaml index 576df08c3..3b98dec06 100644 --- a/util/templates/defaults.yaml +++ b/util/templates/defaults.yaml @@ -59,8 +59,8 @@ params: de: Passwort en: Password help: - de: Bei führenden Nullen bitte in einfache Hochkommata setzen - en: Use single quotes in case of leading zeros + de: Zugangspasswort des Dienstes + en: Service account password mask: true - name: capacity unit: kWh @@ -292,8 +292,8 @@ params: en: Actor Identification Number (AIN) de: Aktoridentifikationsnummer (AIN) help: - en: Printed on the type label on the back of the device. Embed it in double quotes in case of leading zeroes. - de: Ist auf dem Typenschild auf der Geräterückseite aufgedruckt. Bei führenden Nullen bitte in doppelte Hochkommata setzen. + en: Printed on the type label on the back of the device. + de: Ist auf dem Typenschild auf der Geräterückseite aufgedruckt. - name: coarsecurrent type: bool description: From 63459e18e99a16eac473b49625425d820cd318d8 Mon Sep 17 00:00:00 2001 From: Michael Geers Date: Mon, 20 Apr 2026 09:02:53 +0200 Subject: [PATCH 0002/1224] Forecast: temporarily remove adjustment (2) (#29260) --- assets/js/settings.ts | 2 +- assets/js/views/Forecast.vue | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/assets/js/settings.ts b/assets/js/settings.ts index 9ea7fcff8..1657c0bc1 100644 --- a/assets/js/settings.ts +++ b/assets/js/settings.ts @@ -151,7 +151,7 @@ const settings: Settings = reactive({ savingsIndicator: read(SAVINGS_INDICATOR), sessionsGroup: read(SESSIONS_GROUP), sessionsType: read(SESSIONS_TYPE), - solarAdjusted: readBool(SETTINGS_SOLAR_ADJUSTED), + solarAdjusted: false, //readBool(SETTINGS_SOLAR_ADJUSTED), # temporarily disable, https://github.com/evcc-io/evcc/issues/29165 priceZoom: readBool(SETTINGS_PRICE_ZOOM), hideFeedin: readBool(SETTINGS_HIDE_FEEDIN), loadpoints: readJSON(LOADPOINTS), diff --git a/assets/js/views/Forecast.vue b/assets/js/views/Forecast.vue index c1cd0b682..d15863a4a 100644 --- a/assets/js/views/Forecast.vue +++ b/assets/js/views/Forecast.vue @@ -243,7 +243,8 @@ export default defineComponent({ return true; }, showSolarAdjust() { - return !!this.forecast.solar && this.experimental; + // TODO fix https://github.com/evcc-io/evcc/issues/29165 + return !!this.forecast.solar && this.experimental && false; }, solar() { return this.showSolarAdjust && this.solarAdjusted From 0918c62b44ecb7f698a5ae946f04af247ce7779a Mon Sep 17 00:00:00 2001 From: Copilot <198982749+Copilot@users.noreply.github.com> Date: Mon, 20 Apr 2026 09:16:02 +0200 Subject: [PATCH 0003/1224] Sungrow iHomeManager: fix voltage and per-phase active power decoding (#27985) --- templates/definition/meter/sungrow-ihm.yaml | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/templates/definition/meter/sungrow-ihm.yaml b/templates/definition/meter/sungrow-ihm.yaml index 06cb6d664..d58e1f131 100644 --- a/templates/definition/meter/sungrow-ihm.yaml +++ b/templates/definition/meter/sungrow-ihm.yaml @@ -36,21 +36,21 @@ render: | - source: modbus {{- include "modbus" . | indent 2 }} register: - address: 8554 # Register 8555 (A-phase voltage) + address: 4356 # Register 4357 (Phase A voltage, phase-to-neutral) type: input decode: uint16 scale: 0.1 # Factor 0.1 V - source: modbus {{- include "modbus" . | indent 2 }} register: - address: 8555 # Register 8556 (B-phase voltage) + address: 4358 # Register 4359 (Phase B voltage, phase-to-neutral) type: input decode: uint16 scale: 0.1 - source: modbus {{- include "modbus" . | indent 2 }} register: - address: 8556 # Register 8557 (C-phase voltage) + address: 4360 # Register 4361 (Phase C voltage, phase-to-neutral) type: input decode: uint16 scale: 0.1 @@ -58,24 +58,21 @@ render: | - source: modbus {{- include "modbus" . | indent 2 }} register: - address: 8558 # Register 8559 (Phase A active power, U32) + address: 8558 # Register 8559 (Phase A active power, S32, signed import/export) type: input - decode: uint32s - scale: 1 # Factor 1 W + decode: int32s - source: modbus {{- include "modbus" . | indent 2 }} register: address: 8560 # Register 8561 (Phase B active power) type: input - decode: uint32s - scale: 1 + decode: int32s - source: modbus {{- include "modbus" . | indent 2 }} register: address: 8562 # Register 8563 (Phase C active power) type: input - decode: uint32s - scale: 1 + decode: int32s {{- end }} {{- if eq .usage "pv" }} type: custom From 4bfc1ba6e9b95b6be6546bcdcf25e0c437e9d6ea Mon Sep 17 00:00:00 2001 From: andig Date: Mon, 20 Apr 2026 16:33:33 +0200 Subject: [PATCH 0004/1224] myPV AC Thor: support extra heater on relay (#29266) --- charger/mypv.go | 67 +++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 59 insertions(+), 8 deletions(-) diff --git a/charger/mypv.go b/charger/mypv.go index cf6fc386b..67982419f 100644 --- a/charger/mypv.go +++ b/charger/mypv.go @@ -46,13 +46,18 @@ type MyPv struct { } const ( - elwaRegSetPower = 1000 - elwaRegTempLimit = 1002 - elwaRegStatus = 1003 - elwaRegLoadState = 1059 - elwaRegPower = 1000 // https://github.com/evcc-io/evcc/issues/18020#issuecomment-2585300804 - elwaRegOperationState = 1077 - elwaERegOperationState = elwaRegStatus // same register for elwa-e operation state + elwaRegSetPower = 1000 + elwaRegTempLimit = 1002 + elwaRegStatus = 1003 + elwaRegLoadState = 1059 + elwaRegPower = 1000 // https://github.com/evcc-io/evcc/issues/18020#issuecomment-2585300804 + elwaRegOperationState = 1077 + elwaERegOperationState = elwaRegStatus // same register for elwa-e operation state + elwaRegRelayState = 1058 + elwaRegVoltage = 1061 + elwaRegOperationMode = 1065 // https://github.com/evcc-io/evcc/discussions/23708 + elwaRegMaxControlledPower = 1014 // max. power for linear controlled output + elwaRegMaxCombinedPower = 1071 // (max. power for linear controlled output + configured relais power) * 1.10 ) var elwaTemp = []uint16{1001, 1030, 1031} @@ -266,6 +271,7 @@ func (wb *MyPv) MaxCurrentMillis(current float64) error { phases = p } } + power := uint16(voltage * current * float64(phases)) err := wb.setPower(power) @@ -285,7 +291,52 @@ func (wb *MyPv) CurrentPower() (float64, error) { return 0, err } - return float64(binary.BigEndian.Uint16(b)), nil + res := float64(binary.BigEndian.Uint16(b)) + if wb.name != "ac-thor" { + return res, nil + } + + c, err := wb.conn.ReadHoldingRegisters(elwaRegOperationMode, 1) + if err != nil { + return 0, err + } + wb.log.TRACE.Printf("operation mode %d", binary.BigEndian.Uint16(c)) + + // AC Thor operation mode != 3 + if binary.BigEndian.Uint16(c) != 3 { + return res, nil + } + + // AC Thor operation mode == 3 "Warm water 9 + 9kW" + // with extra heater on internal relay + // see https://github.com/evcc-io/evcc/discussions/23708 + f, err := wb.conn.ReadHoldingRegisters(elwaRegRelayState, 1) + if err != nil { + return 0, err + } + + // relay inactive + if binary.BigEndian.Uint16(f) != 1 { + return res, nil + } + + // get power of heater on relay as set in web interface + // (scale factor must be used for correct setting in web interface) + d, err := wb.conn.ReadHoldingRegisters(elwaRegMaxControlledPower, 1) + if err != nil { + return 0, err + } + + e, err := wb.conn.ReadHoldingRegisters(elwaRegMaxCombinedPower, 1) + if err != nil { + return 0, err + } + wb.log.TRACE.Printf("max. power: controlled %.0f W / combined %.0f W", float64(binary.BigEndian.Uint16(d)), float64(binary.BigEndian.Uint16(e))) + + // relay power = combined power - controlled power, finally corrected with 110% factor + res += float64(int(binary.BigEndian.Uint16(e))-int(binary.BigEndian.Uint16(d))) / wb.scale / 1.1 + + return res, nil } var _ api.Battery = (*MyPv)(nil) From 7d2f507f3a15647446a0270d406b680a222cce3c Mon Sep 17 00:00:00 2001 From: Mathias Walter Date: Tue, 21 Apr 2026 05:54:45 +0200 Subject: [PATCH 0005/1224] Log UI: sort the log area multiselect naturally (#29289) --- assets/js/views/Log.vue | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/assets/js/views/Log.vue b/assets/js/views/Log.vue index 601be30af..436e8e0a7 100644 --- a/assets/js/views/Log.vue +++ b/assets/js/views/Log.vue @@ -171,7 +171,10 @@ export default defineComponent({ }); }, areaOptions() { - return this.availableAreas.map((area) => ({ name: area, value: area })); + return this.availableAreas + .slice() + .sort((a, b) => a.localeCompare(b, undefined, { numeric: true })) + .map((area) => ({ name: area, value: area })); }, areasLabel() { if (this.areas.length === 0) { From 1d3a963dc82d3f8fd540daa147b7b8f4e71d90fb Mon Sep 17 00:00:00 2001 From: VallahDieWaldfee <164005630+VallahDieWaldfee@users.noreply.github.com> Date: Tue, 21 Apr 2026 08:35:57 +0200 Subject: [PATCH 0006/1224] Nibe S-series: update docs (#29293) --- .../definition/charger/nibe-s-series.yaml | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/templates/definition/charger/nibe-s-series.yaml b/templates/definition/charger/nibe-s-series.yaml index c9cae6d39..d943e48ce 100644 --- a/templates/definition/charger/nibe-s-series.yaml +++ b/templates/definition/charger/nibe-s-series.yaml @@ -7,16 +7,21 @@ group: heating requirements: description: de: | - Modbus Verbindung zur NIBE S Wärmepumpe. Firmware 4.7.5 oder neuer benötigt. - In der Inneneinheit muss im Menü 7.5.9 Modbus aktiviert sein. - Zur Verwendung der Leistungssteuerung muss im Menü 7.4 unter dem Punkt AUX von Modbus ext. Leistungsbegrenzung ausgewählt sein. - Es muss einmalig im Holding Register 3032 der Wert `1` (uint8) geschrieben werden. + Modbus Verbindung zur NIBE S Wärmepumpe. + Firmware 4.7.5 oder neuer benötigt. + Benötigte Einstellungen in der Steuereinheit: + - In der Inneneinheit muss im Menü 7.5.9 Modbus aktiviert sein. + - Zur Verwendung der Leistungssteuerung muss im Menü 7.4 unter dem Punkt AUX von Modbus ext. Leistungsbegrenzung ausgewählt sein. In den Ladepunkteinstellungen muss eine minimale Leistung von 1,2kW (1-phasig) eingestellt werden. Sollte der Normalverbrauch höher sein, muss dieser als minimale Leistung eingestellt werden. + - Im Menü 7.4 dürfen die Optionen SG-Ready A und SG-Ready B nicht vergeben sein. + - Es muss einmalig im Holding Register 3032 der Wert `1` (uint8) geschrieben werden. en: | Modbus connection to the NIBE S heat pump. Firmware version 4.7.5 or newer is required. - In the indoor unit, Modbus must be enabled in menu 7.5.9 - To use the power control function, external power limitation must be selected under Menu 7.4, item AUX from modbus. - The value `1` (uint8) must be written once to holding register 3032. + Required settings in the control unit: + - In the indoor unit, Modbus must be enabled in menu 7.5.9 + - To use power control, “external power limitation via Modbus” must be selected under AUX in menu 7.4. In the charging point settings, a minimum power of 1.2 kW (single-phase) must be configured. If the base load is higher, it must be set as the minimum power. + - In menu 7.4, the options “SG-Ready A” and “SG-Ready B” must not be assigned. + - The value `1` (uint8) must be written once to holding register 3032. params: - name: modbus choice: ["tcpip"] From 2fb900c8eae7dc06482384a64d7c3dd30c4d2390 Mon Sep 17 00:00:00 2001 From: Michael Geers Date: Tue, 21 Apr 2026 10:31:19 +0200 Subject: [PATCH 0007/1224] History UI: small fixes (#29138) --- assets/js/components/History/Chart.vue | 14 +++++++---- assets/js/views/History.vue | 4 ++++ core/metrics/db.go | 7 +++--- core/metrics/db_test.go | 33 ++++++++++++++++++++++++++ 4 files changed, 50 insertions(+), 8 deletions(-) diff --git a/assets/js/components/History/Chart.vue b/assets/js/components/History/Chart.vue index 874388cf4..4e71801fc 100644 --- a/assets/js/components/History/Chart.vue +++ b/assets/js/components/History/Chart.vue @@ -72,10 +72,14 @@ export default defineComponent({ const color = colors.palette[i % colors.palette.length]; const fill = dimColor(color); - const points = s.data.map((slot) => ({ - x: new Date(slot.start).getTime(), - y: slotPower(slot, "import") - slotPower(slot, "export"), - })); + const points = s.data.map((slot) => { + const start = new Date(slot.start).getTime(); + const end = new Date(slot.end).getTime(); + return { + x: (start + end) / 2, + y: slotPower(slot, "import") - slotPower(slot, "export"), + }; + }); return { label: s.name, @@ -85,7 +89,7 @@ export default defineComponent({ fill: true, pointRadius: 0, borderWidth: 1.5, - tension: 0.3, + tension: 0.05, }; }); diff --git a/assets/js/views/History.vue b/assets/js/views/History.vue index 4d818ebca..a346f2d97 100644 --- a/assets/js/views/History.vue +++ b/assets/js/views/History.vue @@ -1,6 +1,10 @@ @@ -31,6 +32,8 @@ import Item from "./Item.vue"; import MoreIcon from "../MaterialIcon/More.vue"; import MoreMenu from "./MoreMenu.vue"; import { isUserConfigError } from "@/utils/fatal"; +import { isNewVersionAvailable, isNewVersionUnacknowledged } from "@/utils/version"; +import settings from "@/settings"; import type { FatalError, Sponsor, EvOpt, AuthProviders } from "@/types/evcc"; export default defineComponent({ @@ -46,6 +49,7 @@ export default defineComponent({ evopt: { type: Object as PropType, required: false }, installed: String, commit: String, + availableVersion: String, }, data() { return { open: false }; @@ -69,14 +73,27 @@ export default defineComponent({ showConfigBadge() { return this.sponsorExpires || isUserConfigError(this.fatal); }, + newVersionAvailable() { + return isNewVersionAvailable(this.installed, this.availableVersion); + }, + showVersionBadge() { + return isNewVersionUnacknowledged( + this.installed, + this.availableVersion, + settings.lastAcknowledgedVersion + ); + }, showRootBadge() { - return this.authorizationRequired || this.showConfigBadge; + return this.authorizationRequired || this.showConfigBadge || this.showVersionBadge; }, badgeClass() { if (this.fatal.length > 0) { return "bg-danger"; } - return "bg-warning"; + if (this.authorizationRequired || this.showConfigBadge) { + return "bg-warning"; + } + return "bg-darker-green"; }, }, methods: { diff --git a/assets/js/components/BottomTabs/MoreMenu.vue b/assets/js/components/BottomTabs/MoreMenu.vue index f27d2704d..1749ae2d3 100644 --- a/assets/js/components/BottomTabs/MoreMenu.vue +++ b/assets/js/components/BottomTabs/MoreMenu.vue @@ -11,8 +11,14 @@ class="dropdown-item d-flex align-items-center" @click="openAboutModal" > + evcc {{ versionLabel }} + +
+ + +
@@ -68,11 +94,21 @@ import "@h2d2/shopicons/es/filled/lightning"; import Loadpoint from "./Loadpoint.vue"; import { defineComponent, type PropType } from "vue"; -import type { UiLoadpoint, SMART_COST_TYPE, Timeout, Vehicle, BATTERY_MODE } from "@/types/evcc"; +import type { + UiLoadpoint, + SMART_COST_TYPE, + Timeout, + Vehicle, + BATTERY_MODE, + Forecast, + CURRENCY, +} from "@/types/evcc"; +import ChargingPlanModal from "../ChargingPlans/ChargingPlanModal.vue"; +import SettingsModal from "../Loadpoints/SettingsModal.vue"; export default defineComponent({ name: "Loadpoints", - components: { Loadpoint }, + components: { Loadpoint, ChargingPlanModal, SettingsModal }, props: { loadpoints: { type: Array as PropType, default: () => [] }, vehicles: { type: Array as PropType }, @@ -82,14 +118,14 @@ export default defineComponent({ tariffGrid: Number, tariffCo2: Number, tariffFeedIn: Number, - currency: String, + currency: String as PropType, selectedId: String, gridConfigured: Boolean, pvConfigured: Boolean, batteryConfigured: Boolean, batterySoc: Number, batteryMode: String as PropType, - forecast: Object, // as PropType, + forecast: Object as PropType, }, emits: ["id-changed"], data() { @@ -188,6 +224,25 @@ export default defineComponent({ } }, 1000); }, + openChargingPlanModal(loadpointId: string, openArrivalTab = false) { + const modal = this.$refs["chargingPlanModal"] as + | InstanceType + | undefined; + + if (openArrivalTab) { + modal?.showArrivalTab(); + } else { + modal?.showDepartureTab(); + } + + modal?.open(loadpointId); + }, + openSettingsModal(loadpointId: string) { + const modal = this.$refs["settingsModal"] as + | InstanceType + | undefined; + modal?.open(loadpointId); + }, }, }); diff --git a/assets/js/components/Loadpoints/SettingsModal.vue b/assets/js/components/Loadpoints/SettingsModal.vue index 34d254b93..e3a11a253 100644 --- a/assets/js/components/Loadpoints/SettingsModal.vue +++ b/assets/js/components/Loadpoints/SettingsModal.vue @@ -1,30 +1,31 @@ + + diff --git a/assets/js/components/Sessions/EnergyGroupedChart.vue b/assets/js/components/Sessions/EnergyGroupedChart.vue index 18c39eb21..31e69bd9c 100644 --- a/assets/js/components/Sessions/EnergyGroupedChart.vue +++ b/assets/js/components/Sessions/EnergyGroupedChart.vue @@ -1,7 +1,7 @@