Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
f4221b4
invert bat power sign (#1973)
ndrsnhs Oct 25, 2024
63cfa95
fix default value if serial number is missing (#1976)
LKuemmel Oct 28, 2024
e91a1c6
fix deviating evse current only if chargepoint is in charging state (…
LKuemmel Oct 28, 2024
d695246
fix the deviation only for vehicles with a larger deviation (#1978)
LKuemmel Oct 28, 2024
c5f7b93
build UI (#1979)
LKuemmel Oct 28, 2024
51bc880
fix pv simcounter (#1980)
LKuemmel Oct 28, 2024
f6438f2
Überarbeitung Strompreisbasiertes Laden (#1981)
LKuemmel Oct 28, 2024
2c41711
update soc if new soc from cp is available (#1983)
LKuemmel Oct 30, 2024
0b39104
Wki: Hausverbauch (#1984)
LKuemmel Oct 30, 2024
56492b0
internal cp: don't set heartbeat if one cp is in error state (#1982)
LKuemmel Oct 30, 2024
3575fbf
build UI (#1985)
LKuemmel Oct 30, 2024
b30c8df
fix missing soc timestamps (#1986)
LKuemmel Oct 30, 2024
b2fc2a0
voltage check for 1phase Zoe (#1987)
LKuemmel Oct 31, 2024
76afce2
series 2 satellit: : check failed telnet connection every iteration (…
LKuemmel Oct 31, 2024
8e7c610
vehicle-specific token to allow multiple vehiclles with ovms module (…
rleidner Nov 4, 2024
12aaad3
soc_ovms: define server_url (#1996)
rleidner Nov 5, 2024
69b0f3d
fix phases scheduled charging (#1998)
LKuemmel Nov 5, 2024
556ea61
fix null in daily log data (#1993)
LKuemmel Nov 5, 2024
ee2d3de
use SingleComponentUpdateContext
benderl Nov 6, 2024
348f4de
Merge pull request #1999 from benderl/fix-fronius-component-update
benderl Nov 6, 2024
1e13bed
pv charging: range offset based on range mid point (#1992)
vuffiraa72 Nov 6, 2024
14ac8f3
fix solis (#1989)
LKuemmel Nov 6, 2024
efba441
fix growatt and goodwe enum comparison (#2001)
ndrsnhs Nov 6, 2024
99a5aa6
Ändere Filterkriterium in der Protokolldatenabfrage von RFID zu Tag
benderl Nov 7, 2024
cc06ba1
fix destroying price modal in standard legacy theme
benderl Nov 7, 2024
03e2786
build
benderl Nov 7, 2024
ec7be99
Merge pull request #2003 from benderl/build-ui
benderl Nov 7, 2024
478d2c1
Merge pull request #2002 from benderl/fix-charge-log-id-tag-filter
benderl Nov 7, 2024
ccfac12
update cards display theme dependencies
benderl Nov 7, 2024
2053164
Merge pull request #2004 from benderl/dependencies
benderl Nov 7, 2024
6751a09
Build Display Theme: Cards
benderl Nov 7, 2024
ec23648
Fix Solis (#1990)
the2masters Nov 7, 2024
7cd120d
Update version 2.1.6-RC.1
LKuemmel Nov 7, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 11 additions & 7 deletions docs/Hausverbrauch.md
Original file line number Diff line number Diff line change
@@ -1,17 +1,21 @@
Der Hausverbrauch kann in der openWB auf zwei verschiedenen Wegen ermittelt werden: die openWB berechnet den Hausverbrauch oder du gibst in den Einstellungen einen physischen Zähler an, der den Hausverbrauch misst.
Der Hausverbrauch kann in der openWB auf zwei verschiedenen Wegen ermittelt werden: die openWB berechnet den Hausverbrauch oder du gibst in den Einstellungen einen Zähler an, der den Hausverbrauch misst.
Eine Mischung der beiden Möglichkeiten, also nicht gemessener, berechneter Hausverbrauch und gemessener Hausverbrauch kann in der openWB nicht abgebildet werden.
Es muss in der Anlage einen Zähler geben, der alle Verbräuche erfasst. Dies kann entweder der EVU-Zähler sein (dieser erfasst auch Wechselrichter und Speicher) oder der Hausverbrauchszähler (Wechselrichter und Zähler werden separat erfasst).

### Berechnung des Hausverbrauchs durch die openWB
### Möglichkeit 1: Berechnung des Hausverbrauchs durch die openWB

Der Hausverbrauch entspricht der Summer aller nicht erfassten Verbraucher. Üblicherweise ist am EVU-Punkt ein Zähler installiert. Außerdem kennt die openWB die Leistungen des Wechselrichters, des Speichers und der Ladepunkte. Aus der Differenz ergibt sich der Hausverbrauch.
Der Hausverbrauch entspricht der Summer aller nicht gemessenen Verbraucher. Üblicherweise ist am EVU-Punkt ein Zähler installiert. Außerdem kennt die openWB die Leistungen des Wechselrichters, des Speichers und der Ladepunkte. Aus der Differenz ergibt sich der Hausverbrauch.

### Hausverbrauchszähler
### Möglichkeit 2: Hausverbrauchszähler

Unter `Einstellungen→Konfiguration→Lastmanagement` kann bei Hausverbrauch ein Zähler ausgewählt werden. Diese Einstellung ist nur dann richtig, wenn in der Anlage ein Zähler verbaut ist, der den Hausverbrauch misst. Dies ist bei manchen Systemherstellern wie Kostal(?) üblich. Der Hausverbrauchszähler kann die Ladepunkte messen oder nicht. Dann müssen diese in der Hierachie entsprechend hinter oder neben dem Zähler angeordnet werden.
Bezug und Einspeisung ins öffentliche Netz werden dann aus den Werten des Zählers, Wechselrichter und Speicher berechnet. Dazu gibt es in openWB einen virtuellen Zähler. Dieser addiert die Werte aller in der Struktur dahinter angeordneten Komponenten.
Unter `Einstellungen→Konfiguration→Lastmanagement` kann bei Hausverbrauch ein Zähler ausgewählt werden. Diese Einstellung ist nur dann richtig, wenn in der Anlage ein Zähler verbaut ist, der den Hausverbrauch misst. Dies ist bei manchen Systemherstellern wie Kostal(?) üblich. Der Hausverbrauchszähler kann die Ladepunkte messen oder nicht. Dann müssen diese in der Hierachie entsprechend hinter oder neben dem Zähler angeordnet werden.
Bezug und Einspeisung ins öffentliche Netz werden dann mit einem virtuellen Zähler aus den Werten des Hausverauchszählers, Wechselrichter und Speicher berechnet. Der virtuelle Zähler addiert die Werte aller in der Struktur dahinter angeordneten Komponenten.

Zunächst ein Virtuelles Gerät mit einem virtuellen Zähler anlegen. Die Komponenten müssen in der Hierarchie wie in den Abbildungen unten angeordnet werden. In den Einstellungen für das Lastmanagement beim Punkt Hausverbrauch den Hausverbrauchs-Zähler auswählen.

Wenn der Hausverbrauch die Summer mehrerer Zähler in der Anlage ist, müssen diese in einem virtuellen Zähler zusammengefasst werden und dieser als Hausverbrauchs-Zähler ausgewählt werden. Dieser darf nicht der Zähler an der Spitze (EVU-Zähler) sein. Eine Mischung aus nicht erfasstem, berechnetem Hausverbrauch und gezähltem Hausverbrauch, kann in der openWB nicht abgebildet werden.
### Hausverbrauch bei mehreren Zählern
Wenn es einen Zähler am EVU-Punkt und einen Zähler im Hausverbrauchszweig gibt, dann wie unter `Möglichkeit 2` beschrieben, den Zähler, der den Hausverbrauch misst unter `Einstellungen→Konfiguration→Lastmanagement→Hausverbauch` auswählen.
Wenn der Hausverbrauch die Summer mehrerer Zähler in der Anlage ist, müssen diese in einem virtuellen Zähler zusammengefasst werden und dieser wie unter `Möglichkeit 2` als Hausverbrauchs-Zähler ausgewählt werden. Dieser kann nicht der Zähler an der Spitze (EVU-Zähler) sein, da in diesem Zähler immer auch Speicher und PV miteingerechnet werden müssen, um den Überschuss für PV-Laden am EVU-Punkt zu kennen.

Misst der Zähler den Hausverbrauch, ergibt sich folgende Anordnung:

Expand Down
7 changes: 4 additions & 3 deletions docs/Strompreisbasiertes Laden.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ Unter `Einstellungen → Ladeeinstellungen → Übergreifendes` muss der Stroman

Im Ladeprofil des Fahrzeugs muss das strompreisbasierte Laden aktiviert werden. Die Berücksichtigung des Strompreises in den verschiedenen Lademodi erfolgt folgendermaßen:

Sofort- und Zeitladen: Es wird nur geladen, wenn der Strompreis unter dem maximalen angegeben Strompreis liegt.
Zielladen: Es wird die Ladedauer ermittelt und dann zu den günstigsten Stunden geladen.
PV-Laden: keine Berücksichtigung des Strompreises
* Sofort- und Zeitladen: Es wird nur geladen, wenn der Strompreis unter dem maximalen angegeben Strompreis liegt.
* Zielladen: Die openWB berechnet anhand der eingestellten Stromstärke und Phasenanzahl für diesen Lademodus, wie lange geladen werden, muss um den konfigurierten SoC oder die konfigurierte Energiemenge zu erreichen. Dieses Ladefenster wird automatisch auf die günstigsten Stunden des Stromtarifs gelegt. Ist PV-Überschuss vorhanden, wird dieser immer zuerst genutzt und verkürzt das besagte Ladefenster.
* PV-Laden: keine Berücksichtigung des Strompreises

Wenn keine Preise abgefragt werden können, wird bei Sofort- und Zeitladen immer geladen und bei Zielladen zunächst mit PV-Überschuss und zum Erreichen des Zieltermins mit Netzstrom.
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ def test_start_pv_delay(all_cp_pv_charging_3p, all_cp_not_charging, monkeypatch)
assert data.data.cp_data[
"cp5"].data.control_parameter.timestamp_switch_on_off is None
assert data.data.counter_data["counter0"].data.set.raw_power_left == 31775
assert data.data.counter_data["counter0"].data.set.surplus_power_left == 9660
assert data.data.counter_data["counter0"].data.set.surplus_power_left == 9890
assert data.data.counter_data["counter0"].data.set.reserved_surplus == 9000


Expand Down Expand Up @@ -176,7 +176,7 @@ def test_pv_delay_expired(all_cp_pv_charging_3p, all_cp_not_charging, monkeypatc
assert data.data.cp_data[
"cp5"].data.control_parameter.timestamp_switch_on_off is None
assert data.data.counter_data["counter0"].data.set.raw_power_left == 24300
assert data.data.counter_data["counter0"].data.set.surplus_power_left == 2185
assert data.data.counter_data["counter0"].data.set.surplus_power_left == 2415
assert data.data.counter_data["counter0"].data.set.reserved_surplus == 0


Expand Down Expand Up @@ -214,7 +214,7 @@ def test_pv_delay_expired(all_cp_pv_charging_3p, all_cp_not_charging, monkeypatc
expected_current_cp4=6,
expected_current_cp5=6,
expected_raw_power_left=5635,
expected_surplus_power_left=-16480.0,
expected_surplus_power_left=-16250.0,
expected_reserved_surplus=0,
expected_released_surplus=11040),
]
Expand Down Expand Up @@ -250,7 +250,7 @@ def test_surplus(params: ParamsSurplus, all_cp_pv_charging_3p, all_cp_charging_3
expected_current_cp4=6,
expected_current_cp5=6,
expected_raw_power_left=17400,
expected_surplus_power_left=-4715,
expected_surplus_power_left=-4485,
expected_reserved_surplus=0,
expected_released_surplus=0),
ParamsPhaseSwitch(name="phase switch 1p->3p",
Expand Down
17 changes: 10 additions & 7 deletions packages/control/algorithm/surplus_controlled.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,8 @@ def set_surplus_current(self) -> None:
for cp in preferenced_cps_without_set_current:
cp.data.set.current = cp.data.set.target_current
for cp in get_chargepoints_by_chargemodes(CONSIDERED_CHARGE_MODES_SURPLUS):
self._fix_deviating_evse_current(cp)
if cp.data.control_parameter.state in CHARGING_STATES:
self._fix_deviating_evse_current(cp)

def _set(self,
chargepoints: List[Chargepoint],
Expand Down Expand Up @@ -138,15 +139,17 @@ def _fix_deviating_evse_current(self, chargepoint: Chargepoint) -> float:
Wenn die Soll-Stromstärke nicht angepasst worden ist, nicht den ungenutzten EVSE-Strom aufschlagen. Wenn das
Auto nur in 1A-Schritten regeln kann, rundet es und lädt immer etwas mehr oder weniger als Soll-Strom. Schlägt
man den EVSE-Strom auf, pendelt die Regelung um diesen 1A-Schritt."""
MAX_DEVIATION = 1.1
evse_current = chargepoint.data.get.evse_current
if evse_current and chargepoint.data.set.current != chargepoint.set_current_prev:
formatted_evse_current = evse_current if evse_current < 32 else evse_current / 100
current_with_offset = chargepoint.data.set.current + \
formatted_evse_current - max(chargepoint.data.get.currents)
current = min(current_with_offset, chargepoint.data.control_parameter.required_current)
if current != chargepoint.data.set.current:
log.debug(f"Ungenutzten Soll-Strom aufschlagen ergibt {current}A.")
chargepoint.data.set.current = current
offset = formatted_evse_current - max(chargepoint.data.get.currents)
if abs(offset) >= MAX_DEVIATION:
current_with_offset = chargepoint.data.set.current + offset
current = min(current_with_offset, chargepoint.data.control_parameter.required_current)
if current != chargepoint.data.set.current:
log.debug(f"Ungenutzten Soll-Strom aufschlagen ergibt {current}A.")
chargepoint.data.set.current = current

def check_submode_pv_charging(self) -> None:
evu_counter = data.data.counter_all_data.get_evu_counter()
Expand Down
18 changes: 9 additions & 9 deletions packages/control/algorithm/surplus_controlled_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -101,24 +101,24 @@ def test_set_required_current_to_max(phases: int,


@pytest.mark.parametrize(
"evse_current, limited_current, required_current, expected_current",
"evse_current, limited_current, expected_current",
[
pytest.param(None, 6, 16, 6, id="Kein Soll-Strom aus der EVSE ausgelesen"),
pytest.param(15, 15, 16, 15, id="Auto lädt mit Soll-Stromstärke"),
pytest.param(14.5, 14.5, 14.5, 14, id="Auto lädt mit mehr als Soll-Stromstärke"),
pytest.param(15.5, 15.5, 16, 16, id="Auto lädt mit weniger als Soll-Stromstärke"),
pytest.param(16, 16, 16, 16,
pytest.param(None, 6, 6, id="Kein Soll-Strom aus der EVSE ausgelesen"),
pytest.param(13, 13, 13, id="Auto lädt mit Soll-Stromstärke"),
pytest.param(12.5, 12.5, 12.5, id="Auto lädt mit 0.5A Abweichung von der Soll-Stromstärke"),
pytest.param(11.8, 11.8, 10.600000000000001, id="Auto lädt mit mehr als Soll-Stromstärke"),
pytest.param(14.2, 14.2, 15.399999999999999, id="Auto lädt mit weniger als Soll-Stromstärke"),
pytest.param(15, 15, 16,
id="Auto lädt mit weniger als Soll-Stromstärke, aber EVSE-Begrenzung ist erreicht.")
])
def test_add_unused_evse_current(evse_current: float,
limited_current: float,
required_current: float,
expected_current: float):
# setup
c = Chargepoint(0, None)
c.data.get.currents = [15]*3
c.data.get.currents = [13]*3
c.data.get.evse_current = evse_current
c.data.control_parameter.required_current = required_current
c.data.control_parameter.required_current = 16
c.data.set.current = limited_current

# execution
Expand Down
8 changes: 4 additions & 4 deletions packages/control/chargelog/chargelog.py
Original file line number Diff line number Diff line change
Expand Up @@ -313,13 +313,13 @@ def get_log_data(request: Dict):
)
continue
if (
"rfid" in request["filter"]["vehicle"] and
len(request["filter"]["vehicle"]["rfid"]) > 0 and
entry["vehicle"]["rfid"] not in request["filter"]["vehicle"]["rfid"]
"tag" in request["filter"]["vehicle"] and
len(request["filter"]["vehicle"]["tag"]) > 0 and
entry["vehicle"]["rfid"] not in request["filter"]["vehicle"]["tag"]
):
log.debug(
"Verwerfe Eintrag wegen ID Tag: %s != %s" %
(str(entry["vehicle"]["rfid"]), str(request["filter"]["vehicle"]["rfid"]))
(str(entry["vehicle"]["rfid"]), str(request["filter"]["vehicle"]["tag"]))
)
continue
if (
Expand Down
4 changes: 3 additions & 1 deletion packages/control/chargepoint/chargepoint.py
Original file line number Diff line number Diff line change
Expand Up @@ -734,7 +734,9 @@ def update(self, ev_list: Dict[str, Ev]) -> None:
"/set/ocpp_transaction_id", self.data.set.ocpp_transaction_id)
# SoC nach Anstecken aktualisieren
if ((self.data.get.plug_state and self.data.set.plug_state_prev is False) or
(self.data.get.plug_state is False and self.data.set.plug_state_prev)):
(self.data.get.plug_state is False and self.data.set.plug_state_prev) or
(self.data.get.soc_timestamp and self.data.set.charging_ev_data.data.get.soc_timestamp and
self.data.get.soc_timestamp > self.data.set.charging_ev_data.data.get.soc_timestamp)):
Pub().pub(f"openWB/set/vehicle/{self.data.config.ev}/get/force_soc_update", True)
log.debug("SoC nach Anstecken")
self.set_state_and_log(message)
Expand Down
8 changes: 1 addition & 7 deletions packages/control/counter.py
Original file line number Diff line number Diff line change
Expand Up @@ -243,13 +243,7 @@ def _control_range_offset(self):
control_range_low = data.data.general_data.data.chargemode_config.pv_charging.control_range[0]
control_range_high = data.data.general_data.data.chargemode_config.pv_charging.control_range[1]
control_range_center = control_range_high - (control_range_high - control_range_low) / 2
control_range_state = self.get_control_range_state(0)
if control_range_state == ControlRangeState.BELOW:
range_offset = abs(control_range_center)
elif control_range_state == ControlRangeState.ABOVE:
range_offset = - abs(control_range_center)
else:
range_offset = 0
range_offset = control_range_center
log.debug(f"Anpassen des Regelbereichs {range_offset}W")
return range_offset

Expand Down
8 changes: 4 additions & 4 deletions packages/control/counter_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -156,12 +156,12 @@ def test_switch_on_threshold_reached(params: Params, caplog, general_data_fixtur


@pytest.mark.parametrize("control_range, evu_power, expected_range_offset",
[pytest.param([0, 230], 200, 0, id="Bezug, im Regelbereich"),
pytest.param([0, 230], 290, -115, id="Bezug, über Regelbereich"),
[pytest.param([0, 230], 200, 115, id="Bezug, im Regelbereich"),
pytest.param([0, 230], 290, 115, id="Bezug, über Regelbereich"),
pytest.param([0, 230], -100, 115, id="Bezug, unter Regelbereich"),
pytest.param([-230, 0], -104, 0, id="Einspeisung, im Regelbereich"),
pytest.param([-230, 0], -104, -115, id="Einspeisung, im Regelbereich"),
pytest.param([-230, 0], 80, -115, id="Einspeisung, über Regelbereich"),
pytest.param([-230, 0], -300, 115, id="Einspeisung, unter Regelbereich"),
pytest.param([-230, 0], -300, -115, id="Einspeisung, unter Regelbereich"),
],
)
def test_control_range(control_range, evu_power, expected_range_offset, general_data_fixture, monkeypatch):
Expand Down
30 changes: 19 additions & 11 deletions packages/control/ev.py
Original file line number Diff line number Diff line change
Expand Up @@ -739,21 +739,29 @@ def scheduled_charging_recent_plan(self,
Ladestrom ein. Um etwas mehr Puffer zu haben, wird bis 20 Min nach dem Zieltermin noch geladen, wenn dieser
nicht eingehalten werden konnte.
"""
if phase_switch_supported and data.data.general_data.get_phases_chargemode("scheduled_charging",
"instant_charging") == 0:
if phase_switch_supported:
if charging_type == ChargingType.AC.value:
max_current = ev_template.data.max_current_multi_phases
else:
max_current = ev_template.data.dc_max_current
plan_data = self.search_plan(max_current, soc, ev_template, max_phases, used_amount, charging_type)
if plan_data and charging_type == ChargingType.AC.value:
if plan_data.remaining_time > 300 and self.data.et.active is False:
max_current = ev_template.data.max_current_single_phase
plan_data_single_phase = self.search_plan(
max_current, soc, ev_template, 1, used_amount, charging_type)
if plan_data_single_phase:
if plan_data_single_phase.remaining_time > 300:
plan_data = plan_data_single_phase
instant_phases = data.data.general_data.get_phases_chargemode("scheduled_charging", "instant_charging")
if instant_phases == 0:
planned_phases = 3
else:
planned_phases = instant_phases
planned_phases = min(planned_phases, max_phases)
plan_data = self.search_plan(max_current, soc, ev_template, planned_phases, used_amount, charging_type)
if (plan_data and
charging_type == ChargingType.AC.value and
instant_phases == 0 and
plan_data.remaining_time > 300 and
self.data.et.active is False):
max_current = ev_template.data.max_current_single_phase
plan_data_single_phase = self.search_plan(
max_current, soc, ev_template, 1, used_amount, charging_type)
if plan_data_single_phase:
if plan_data_single_phase.remaining_time > 300:
plan_data = plan_data_single_phase
else:
if charging_type == ChargingType.AC.value:
if phases == 1:
Expand Down
2 changes: 1 addition & 1 deletion packages/control/ev_charge_template_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@ def test_pv_charging(min_soc: int, min_current: int, current_soc: float,
search_plan=None, phases=1, max_phases=3, expected_max_current=32, expected_phases=1),
Params(name="no phase switch, multi phase", phase_switch_supported=False, chargemode_phases=0,
search_plan=None, phases=3, max_phases=3, expected_max_current=16, expected_phases=3),
Params(name="no automatic mode, multi phase", phase_switch_supported=True, chargemode_phases=1,
Params(name="no automatic mode, multi phase", phase_switch_supported=True, chargemode_phases=2,
search_plan=None, phases=2, max_phases=2, expected_max_current=16, expected_phases=2),
Params(name="select phases, not enough time", phase_switch_supported=True, chargemode_phases=0, search_plan=Mock(
spec=SelectedPlan, remaining_time=300), phases=1, max_phases=3, expected_max_current=16, expected_phases=3),
Expand Down
9 changes: 8 additions & 1 deletion packages/helpermodules/hardware_configuration.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,9 +50,16 @@ def exists_hardware_configuration_setting(name: str) -> bool:


def get_serial_number() -> Optional[str]:
# Inhalt der Datei snnumber:
# Seriennummer vorhanden: snnumber=sn1234
# keine Seriennummer: snnumber= oder Datei nicht vorhanden
try:
with open("/home/openwb/snnumber", "r") as file:
return file.read().replace("snnumber=", "").replace("\n", "")
serial_number = file.read().replace("snnumber=", "").replace("\n", "")
if serial_number == "":
return None
else:
return serial_number
except FileNotFoundError:
return None

Expand Down
Loading