Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
4 changes: 2 additions & 2 deletions .github/workflows/build_display_theme_colors.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,10 @@ jobs:
- name: Checkout Repository
uses: actions/checkout@v4

- name: Setup Node.js (v22)
- name: Setup Node.js (v20)
uses: actions/setup-node@v4
with:
node-version: 22
node-version: 20
cache: npm
cache-dependency-path: packages/modules/display_themes/colors/source/package-lock.json

Expand Down
2 changes: 1 addition & 1 deletion docs/Ladepunkte.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ Die Einstellungen für Ladepunkte unterteilen sich in die spezifischen Einstellu

Beim Hinzufügen eines Ladepunkts muss zunächst dessen Typ ausgewählt werden. Danach wird Ladepunkt-spezifisches, wie z.B. die Anzahl angeschlossener Phasen oder eine verbaute Phasenumschaltung, konfiguriert.

In dem Ladepunkt-Profil sind die Einstellungen zum Sperren nach Uhrzeit und für die Freigabe von Ladepunkten durch ID-Tags enthalten. Ein Ladepunkt-Profil kann vom Benutzer mehreren Ladepunkte zugewiesen werden, sodass die Einstellungen für jeden Ladepunkte einzeln oder für eine Gruppe zugewiesen werden können.
In dem Ladepunkt-Profil sind die Einstellungen zum automatischen Sperren (Autolock) und für die Freigabe von Ladepunkten durch ID-Tags enthalten. Ein Ladepunkt-Profil kann vom Benutzer mehreren Ladepunkte zugewiesen werden, sodass die Einstellungen für jeden Ladepunkte einzeln oder für eine Gruppe zugewiesen werden können.

Das Erfassen eines RFID-Tags (verbauter RFID-Leser erforderlich) oder die Pin-Eingabe (Display erforderlich, Eingabefeld muss im Steuerungsmodus 'secondary' aktiviert werden) sowie die automatische
Fahrzeugerkennung der openWB Pro (muss in den Einstellungen aktiviert werden) werden gleich verarbeitet.
148 changes: 148 additions & 0 deletions docs/WiCAN.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
# WiCAN-OBD2 mit manuellem SoC Modul der openWB

Mit Hilfe des WiCAN OBD2 Dongles von meatPi Electronics können die Werte des Fahrzeugs über die OBD2-Schnittstelle ausgelesen und im WLAN per MQTT an die openWB Wallbox gesendet werden.
In Verbindung mit dem manuellem SoC Modul der openWB ist hiermit die Nutzung ohne Cloud-Dienste der Fahrzeughersteller möglich.
https://github.com/meatpiHQ/wican-fw

Die erste Umsetzung erfolgte von zut im openWB Forum, der hierfür auch eine Unterstützung von Spritmonitor umgesetzt hat. Hierfür ist ein weiteres Gerät im Netzwerk nötig, auf dem ein kleines Programm (soc_helper) laufen kann:
https://forum.openwb.de/viewtopic.php?t=7451

Im Laufe der weiteren Entwicklung wurde die Möglichkeit des automatischen Auslesen (AutoPID) in die Firmware des Gerätes integriert. Hierfür ist nun keine zusätzliche Hardware mehr nötig, da der Dongle die Daten direkt per MQTT an die openWB sendet. Die Unterstützung von Spritmonitor ist hiermit jedoch nicht möglich. Die Einrichtung dieser Funktion soll hier näher beschrieben werden.

### 1. Voraussetzungen
* WiCAN Dongle mit Firmware 3.48, 4.03 oder neuer (4.00 ist fehlerhaft).
* Konfiguration des Manuellen SoC Moduls im Fahrzeug der openWB.
* WLAN Empfang im Bereich der openWB bzw. des Fahrzeugs.
* Ggf. Beachtung einer Alarmanlage, die den OBD2 Port überwacht.

### 2. WiCAN Dongle
Die Beschaffung der Hardware ist zunächst die größte Hürde, da die Firma meatPi Electronics Ihren Sitz in Australien hat.
Es sind aktuell 2 Bezugsquellen bekannt:
https://github.com/meatpiHQ/wican-fw?tab=readme-ov-file#order-on-mouser-or-crowd-supply
Es ist hierbei darauf zu achten, dass nur der WiCAN-OBD2 oder der WiCAN PRO (Coming soon) verwendet werden kann. Bei Bestellung über Mouser erfolgt der Versand ab 50€ Versandkostenfrei, so dass sich idealerweise 2 oder mehr Besteller zu einer Sammelbestellung zusammentun können.
Achtung: Die angegeben Preise sind Nettopreise.

### 3. Konfiguration manueller SoC in openWB
* In der Fahrzeugkonfiguration der openWB muss das manuelle SoC Modul eingerichtet werden. Für die spätere Konfiguration der MQTT-Topics wird die Fahrzeug-ID benötigt. Diese kann man am besten auf der Status Seite der openWB auslesen:

![Status](pictures/WiCAN_openWB-Status.png "openWB Status")

### 4. Konfiguration WiCAN Settings

* Zunächst muss der WiCAN ins Heim-WLAN geholt werden:
https://meatpihq.github.io/wican-fw/config/wifi

* Nun werden die weiteren Parameter konfiguriert:
![WiCAN Settings](pictures/WiCAN_Settings.png "WiCAN Settings")

* Protocol AutoPID wird zur automatischen Abfrage der PIDs benötigt (Bereich Automate)
* MQTT zur openWB erfolgt mit leerem User und Passwort
* RX und Status Topic müssen mit others/ beginnen, damit sie von openWB beachtet werden. Sie dienen nur der Fehleranalyse und Überwachung mit MQTT-Explorer. Für die eigentliche Funktion werden sie nicht benötigt.
---
* Sleep Mode (Bereich Power Saving): Bei Unterschreiten der Sleep Voltage schaltet sich der WiCAN ab. Hierdurch wird ein Entladen der 12V Batterie verhindert.
![WiCAN Power Saving](pictures/WiCAN_PowerSaving.png "WiCAN Settings")

### 5. Ermittlung der PID Parameter aus dem Vehicle Profile bei meatPi

Es werden bei meatPi die benötigten Parameter verschiedener Fahrzeuge in Vehicle-Profilen gesammelt.
Die Informationen aus diesen Profilen können wir verwenden, um die Parameter, die wir für die openWB benötigen, zu ermitteln.

Die Profile sind hier einzeln verfügbar:
https://github.com/meatpiHQ/wican-fw/tree/main/vehicle_profiles

Anhand des Beispiels eines VW:ID, die wichtigen Informationen (fettgedruckt).
Wir benötigen zwingend den SoC, optional können wir auch die Reichweite (Range) gebrauchen:

---
{
"car_model":"VW: ID",
**"init":"ATST96;ATFCSD300000;ATFCSM1;",**
"pids":[
{
**"pid":"22028C1",**
**"pid_init":"ATSP7;ATCP17;ATSHFC007B;ATFCSH17FC007B;ATCRA17FE007B;",**
"parameters":[
{
"class":"battery",
**"expression":"B4\*0.4425-6.1947",**
**"name":"soc",**
"unit":"%"
}
]
},
{
**"pid":"222AB62",**
**"pid_init":"ATSP6;ATCP18;ATSH710;ATFCSH710;ATCRA77A;",**
"parameters":[
{
"class":"none",
**"expression":"[B5:B6]",**
**"name":"range",**
"unit":"km"
}
]
}

### 6. Übernahme der Werte für die Custom PIDs (Automate-Tab)

die gefundenen Werte werden nun im Bereich Automate bei den Custom PIDs eingetragen:
![WiCAN Automate Tab](pictures/WiCAN_Automate.png "WiCAN Automate")

- Das Feld "Custom Initialisation" wird aus "**init**" vom Vehicle-Profile übernommen.
- Das Feld Name muss für den SoC "manual_soc" lauten, für die Reichweite "range".
- Falls vorhanden wird das Feld "Init" aus "**pid_init**" vom Vehicle-Profile übernommen. Dieses Feld kann auch leer sein, wenn für die einzelnen PIDs keine besondere Initialisierung benötigt wird.
- Das Feld PID wird aus "**pid**" übernommen.
- Das Feld Expression wird aus "**expression**" übernommen.
- Period(ms) ist das Intervall, in denen die Werte abgefragt werden.
- Im Feld "Destination Type" muss für den SoC "MQTT_Topic", für die Reichweite jedoch "MQTT_Wallbox" eingestellt werden.
- Im Feld Send_to muss die Fahrzeug ID aus der openWB, die wir in [Punkt 3](#3-konfiguration-manueller-soc-in-openwb) ermittelt haben, eingesetzt werden (...vehicle/**x**/...):

Die Werte lauten für den SoC:
openWB/set/vehicle/**x**/soc_module/calculated_soc_state
Für die Reichweite:
openWB/set/vehicle/**x**/get/range

Das Ergebnis sieht dann z.B. so aus:

Name|Init|PID|Expression|Period(ms)|Type|Send_to
-|-|-|-|-|-|-
manual_soc|ATSP7;ATSHFC007B;ATCP17;ATCRA17FE007B;ATFCSH17FC007B;|22028C1|B4*0.4425‑6.1947|10000|MQTT_Topic|openWB/set/vehicle/**3**/soc_module/calculated_soc_state
range|ATSP6;ATSH710;ATCP18; ATCRA77A;ATFCSH710;|222AB62|[B5:B6]|10000|MQTT_Topic|openWB/set/vehicle/**3**/get/range
|
### Ergänzug zur openWB 1.9
Bei openWB 1.9 wird das SOC Modul Manuell+Berechnung am Ladepunkt konfiguriert, das Topic benötigt hier den Typ MQTT_Wallbox.
Am Beispiel Ladepunkt 1 würde dies dann so aussehen:
Name|Init|PID|Expression|Period(ms)|Type|Send_to
-|-|-|-|-|-|-
manualSoC|ATSP7;ATSHFC007B;ATCP17;ATCRA17FE007B;ATFCSH17FC007B;|22028C1|B4*0.4425‑6.1947|60000|MQTT_Wallbox|openWB/set/lp/**1**

Nachtrag: openWB 1.9 aktzeptiert nur Ganzzahlige SOC-Werte und kann daher aktuell nicht verwendet werden.

### 7. Alarmanlage des Fahrzeugs

Bei Fahrzeugen mit einer Alaramanlage wird häufig auch der OBD2-Port überwacht, so dass bei einer Abfrage die Alarmanlage auslöst.
Um dieses Problem zu umgehen sind momentan folgende Lösungen bekannt:

- Bei einigen Fahrzeugen liegt an PIN1 des OBD2-Ports nur bei eingeschalteter Zündung eine Spannung von 12V an. Diese kann dann zur Versorung des WiCAN Dongles verwendet werden, so dass dieser bei abgeschalteter Zündung gar keine Spannungsversorgung hat. Normalerweise liegt an PIN16 die Versorgungsspannung für den WiCAN an.
Es kann also in doiesem Fall mit einem entsprechend "manipuliertem" Adapterkabel z.B. PIN1 und PIN16 getauscht werden:
https://forum.openwb.de/viewtopic.php?p=115467#p115467
- Ggf. ist eine Anpassung der OBD2-Überwachung der Alarmanlage möglich:
https://www.born-forum.de/forum/thread/193-m%C3%B6gliche-codierungen-am-cupra-born-ohne-gew%C3%A4hr-auf-eigene-gefahr/?postID=77395#post77395

Beide Lösungen dienen nur zur Info und erfolgen stets auf eigene Gefahr

### 8. Übersicht erfolgreich getesteter Fahrzeuge

Abschliessend noch eine kurze Auflistung von bereits erfolgreich getesteten Fahrzeugen.
Gerne kann und sollte diese Liste erweitert werden.
Zur besseren Übersicht werden hier nur die tatsächlich verwendeten Fahrzeuge (am besten mit Modelljahr) sowie nur der SoC aufgeführt:

|Fahrzeug|Custom Initialisation|Name|Init|PID|Expression|
|-|-|-|-|-|-|
|CUPRA Born 2022|ATST96;ATFCSD300000;ATFCSM1;|manual_soc|ATSP7;ATCP17;ATSHFC007B;ATFCSH17FC007B;ATCRA17FE007B;|22028C1|B4*0.4425‑6.1947|
|Hyundai Ioniq (28 kWh) 2017|ATSP6;ATSH7E4;ATST96;|manual_soc||2105|B39/2|
|Peugeot iOn|ATSP6;ATFCSH761;ATFCSD300000;ATFCSM1;ATSH761;ATCRA762;|manual_soc||2101|(B4/2)‑5|
|

Anmerkungen zur Tabelle:
Um einen Zeilenumbruch zu verhindern, müssen Leerzeichen durch einen "no-break space character" (\ ) und ein Minuszeichen durch "no-brak hyphen" (\‑) im Markdown Quelltext ersetzt werden.
1 change: 1 addition & 0 deletions docs/_Sidebar.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
* [Ladepunkte](https://github.com/openWB/core/wiki/Ladepunkte)
* [Fahrzeuge](https://github.com/openWB/core/wiki/Fahrzeuge)
* [Manueller SoC](https://github.com/openWB/core/wiki/Manueller-SoC)
* [WiCAN OBD2 Dongle mit manuellem SoC](https://github.com/openWB/core/wiki/WiCAN)
* [Lademodi](https://github.com/openWB/core/wiki/Lademodi)
* Zähler
* [Grundsätzliches zu Zählern](https://github.com/openWB/core/wiki/Grundsätzliches-zu-Zählern)
Expand Down
Binary file added docs/pictures/WiCAN_Automate.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/pictures/WiCAN_PowerSaving.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/pictures/WiCAN_Settings.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/pictures/WiCAN_openWB-Status.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion packages/control/bat_all.py
Original file line number Diff line number Diff line change
Expand Up @@ -335,7 +335,7 @@ def get_power_limit(self):
power_limit = min(self._max_bat_power_hybrid_system(
data.data.bat_data[f"bat{bat_component.component_config.id}"])[0], remaining_power_limit)
remaining_power_limit -= power_limit
remaining_power_limit = max(remaining_power_limit, 0)
remaining_power_limit = min(remaining_power_limit, 0)

data.data.bat_data[f"bat{bat_component.component_config.id}"].data.set.power_limit = power_limit

Expand Down
6 changes: 3 additions & 3 deletions packages/control/chargepoint/chargepoint.py
Original file line number Diff line number Diff line change
Expand Up @@ -150,14 +150,14 @@ def _is_autolock_inactive(self) -> Tuple[bool, Optional[str]]:
if data.data.optional_data.data.rfid.active:
if self.data.get.rfid is None and self.data.set.rfid is None:
state = False
message = ("Keine Ladung, da der Ladepunkt durch Sperren nach Uhrzeit gesperrt ist und erst "
"per ID-Tag freigeschaltet werden muss.")
message = ("Keine Ladung, da der Ladepunkt durch Autolock gesperrt ist und erst per ID-Tag "
"freigeschaltet werden muss.")
else:
state = True
message = None
else:
state = False
message = "Keine Ladung, da Sperren nach Uhrzeit aktiv ist."
message = "Keine Ladung, da Autolock aktiv ist."
return state, message

def _is_manual_lock_inactive(self) -> Tuple[bool, Optional[str]]:
Expand Down
2 changes: 1 addition & 1 deletion packages/control/chargepoint/chargepoint_template.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ def is_locked_by_autolock(self, charge_state: bool) -> bool:
else:
return False
else:
log.info("Keine Sperrung durch Sperren nach Zeitplan, weil keine Zeitpläne konfiguriert sind.")
log.info("Keine Sperrung durch Autolock, weil keine Zeitpläne konfiguriert sind.")
return False
else:
return False
Expand Down
2 changes: 1 addition & 1 deletion packages/helpermodules/abstract_plans.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,4 +80,4 @@ class TimeChargingPlan(TimeframePlan):

@dataclass
class AutolockPlan(TimeframePlan):
name: str = "neuer Plan für Sperren nach Uhrzeit"
name: str = "neuer Autolock-Plan"
24 changes: 12 additions & 12 deletions packages/helpermodules/broker.py
Original file line number Diff line number Diff line change
@@ -1,25 +1,16 @@
import datetime
import logging
import os
import paho.mqtt.client as mqtt
import time
from typing import Callable

log = logging.getLogger(__name__)


def get_name_suffix() -> str:
with open('/proc/cpuinfo', 'r') as f:
for line in f:
if line[0:6] == 'Serial':
serial = line[10:26]
serial = "0000000000000000"
return f"{serial}-{datetime.datetime.today().timestamp()}"


class InternalBrokerClient:
def __init__(self, name: str, on_connect: Callable, on_message: Callable) -> None:
try:
self.name = f"openWB-{name}-{get_name_suffix()}"
self.name = f"openWB-{name}-{self._get_serial()}"
self.client = mqtt.Client(self.name)
self.client.on_connect = on_connect
self.client.on_message = on_message
Expand All @@ -39,11 +30,20 @@ def disconnect(self) -> None:
self.client.disconnect()
log.info(f"Verbindung von Client {self.name} geschlossen.")

def _get_serial(self) -> str:
""" Extract serial from cpuinfo file
"""
with open('/proc/cpuinfo', 'r') as f:
for line in f:
if line[0:6] == 'Serial':
return line[10:26]
return "0000000000000000"


class InternalBrokerPublisher:
def __init__(self) -> None:
try:
self.client = mqtt.Client(f"openWB-python-bulkpublisher-{get_name_suffix()}")
self.client = mqtt.Client(f"openWB-python-bulkpublisher-{os.getpid()}")
self.client.connect("localhost", 1886)
except Exception:
log.exception("Fehler beim Verbindungsaufbau zum Bulkpublisher")
Expand Down
4 changes: 2 additions & 2 deletions packages/helpermodules/command.py
Original file line number Diff line number Diff line change
Expand Up @@ -311,7 +311,7 @@ def addAutolockPlan(self, connection_id: str, payload: dict) -> None:
Pub().pub("openWB/set/command/max_id/autolock_plan", new_id)
pub_user_message(
payload, connection_id,
f'Neuer Plan für Sperren nach Uhrzeit mit ID \'{new_id}\' zu Profil '
f'Neuer Autolock-Plan mit ID \'{new_id}\' zu Profil '
f'\'{payload["data"]["template"]}\' hinzugefügt.',
MessageType.SUCCESS)

Expand All @@ -329,7 +329,7 @@ def removeAutolockPlan(self, connection_id: str, payload: dict) -> None:
"")
pub_user_message(
payload, connection_id,
f'Plan für Sperren nach Uhrzeit mit ID \'{payload["data"]["plan"]}\' vom Profil '
f'Autolock-Plan mit ID \'{payload["data"]["plan"]}\' vom Profil '
f'\'{payload["data"]["template"]}\' gelöscht.',
MessageType.SUCCESS)

Expand Down
3 changes: 1 addition & 2 deletions packages/helpermodules/create_debug.py
Original file line number Diff line number Diff line change
Expand Up @@ -203,8 +203,7 @@ def write_to_file(file_handler, func, default: Optional[Any] = None):
data = f.read()
req.get_http_session().put("https://openwb.de/tools/debug2.php",
data=data,
params={'debugemail': debug_email},
timeout=10)
params={'debugemail': debug_email})

log.info("***** cleanup...")
os.remove(debug_file)
Expand Down
2 changes: 1 addition & 1 deletion packages/helpermodules/update_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -470,7 +470,7 @@ class UpdateConfig:
("openWB/general/chargemode_config/pv_charging/bat_power_reserve", 200),
("openWB/general/chargemode_config/pv_charging/bat_power_reserve_active", True),
("openWB/general/chargemode_config/pv_charging/control_range", [0, 230]),
("openWB/general/chargemode_config/pv_charging/switch_off_threshold", 0),
("openWB/general/chargemode_config/pv_charging/switch_off_threshold", 50),
("openWB/general/chargemode_config/pv_charging/switch_off_delay", 60),
("openWB/general/chargemode_config/pv_charging/switch_on_delay", 30),
("openWB/general/chargemode_config/pv_charging/switch_on_threshold", 1500),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ def get_values(self) -> None:
chargepoint_state = self.request_values()
self.store.set(chargepoint_state)

def request_values(self) -> ChargepointState:
def request_values(self) -> None:
with self.client_error_context:
chargepoint_state = self.old_chargepoint_state
ip_address = self.config.configuration.ip_address
Expand Down
2 changes: 1 addition & 1 deletion packages/modules/chargepoints/openwb_pro/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ def __init__(self, ip_address: Optional[str] = None, duo_num: int = 0):

class OpenWBPro(SetupChargepoint[OpenWBProConfiguration]):
def __init__(self,
name: str = "openWB Pro (nicht Pro+)",
name: str = "openWB Pro",
type: str = "openwb_pro",
id: int = 0,
configuration: OpenWBProConfiguration = None) -> None:
Expand Down
3 changes: 0 additions & 3 deletions packages/modules/common/component_state.py
Original file line number Diff line number Diff line change
Expand Up @@ -141,9 +141,6 @@ def __init__(self, soc: float, range: Optional[float] = None, soc_timestamp: Opt
if soc_timestamp is None:
self.soc_timestamp = timecheck.create_timestamp()
else:
if soc_timestamp > 1e10: # Convert soc_timestamp to seconds if it is in milliseconds
log.debug(f'Zeitstempel {soc_timestamp} ist in ms, wird in s gewandelt. Modul sollte angepasst werden.')
soc_timestamp /= 1000
self.soc_timestamp = soc_timestamp


Expand Down
3 changes: 0 additions & 3 deletions packages/modules/common/configurable_vehicle.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,9 +76,6 @@ def update(self, vehicle_update_data: VehicleUpdateData):
if vehicle_update_data.soc_timestamp is None or vehicle_update_data.soc_timestamp < car_state.soc_timestamp:
# Nur wenn der SoC neuer ist als der bisherige, diesen setzen.
self.store.set(car_state)
elif vehicle_update_data.soc_timestamp > 1e10:
# car_state ist in ms geschrieben, dieser kann überschrieben werden
self.store.set(car_state)
else:
log.debug("Not updating SoC, because timestamp is older.")

Expand Down
3 changes: 1 addition & 2 deletions packages/modules/common/hardware_check.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,7 @@
METER_NO_SERIAL_NUMBER = ("Die Seriennummer des Zählers für das Ladelog kann nicht ausgelesen werden. Wenn Sie die "
"Seriennummer für Abrechnungszwecke benötigen, wenden Sie sich bitte an unseren Support. Die "
"Funktionalität wird dadurch nicht beeinträchtigt!")
EVSE_BROKEN = ("Auslesen der EVSE nicht möglich. Vermutlich ist die EVSE defekt oder hat eine unbekannte Modbus-ID. "
"(Fehlermeldung nur relevant, wenn diese auf der Startseite oder im Status angezeigt wird.)")
EVSE_BROKEN = "Auslesen der EVSE nicht möglich. Vermutlich ist die EVSE defekt oder hat eine unbekannte Modbus-ID."


def check_meter_values(voltages: List[float]) -> Optional[str]:
Expand Down
Loading