-
Notifications
You must be signed in to change notification settings - Fork 60
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add PSA SoC module #644
Add PSA SoC module #644
Conversation
MartinRinas
commented
Nov 15, 2022
•
edited
edited
- liest SoC und Range
- Kombination mit manueller Berechnung fehlt noch, diese ist notwendig um den SoC während des Ladevorgangs bestimmen zu können da PSA keinen aktualisierten SoC bereitstellt
- Konfiguration der client_id und client_secret ist nun optional
- unterstützt Accounts mit mehreren Fahrzeugen über optionalen VIN-Filter
offen ist nur mehr die manuelle Berechnung des SoC während des Ladevorgangs, wir bekommen von PSA ja keine Aktualisierung während des Ladens sondern erst mit Ende eines Ladevorgangs. @LKuemmel hierfür suche ich im Datenmodell nach einem Zähler welcher die ans Auto gelieferte Energiemenge für den aktuellen Ladevorgang zählt. Wichtig wäre dass auch mögliche Lademoduswechsel berücksichtigt sind, konkretes Beispiel: Ich lade mit 2kWh mit PV, schalte dann auf Sofortladen um und lade weitere 10kWh. Ladevorgang wurde nie gestoppt. Der Zähler muss in diesem Fall 12kWh liefern. Es gibt imported und imported_since_modeswitch, _since_plugged & co - bin mir ad hoc aber nicht sicher ob einer dieser Zähler sich so verhält wie ich das bräuchte. |
packages/modules/psa/api.py
Outdated
# f = open('/var/www/html/openWB/ramdisk/psasoctime1', 'w') | ||
# f.write(str(int(soctime))) | ||
# f.close() | ||
return soc, range, soc_timestamp |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Es könnte einfacher sein hier ein CarState
zu erzeugen und zurückzugeben.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
das ist in den anderen Modulen ähnlich implementiert. Bin aber bei dir, das wäre schöner. Was mir leider nicht klar ist, wie kann ich den CarState in den anderen Funktionen, z.b. fetch_soc und in der soc.py direkt weiterverwenden ohne mir stets ein neues Objekt über carstate=CarState(soc, range, timestamp) zu erzeugen - das CarState Objekt muss hier ja im Grunde nur 1-1 durchgereicht werden. von PSARequest zu fetch_soc zur update in soc.py.
Es gibt einen Zähler für die Energie seit Anstecken und einen für die Energie seit dem letzten Moduswechsel. Einen Zähler seit dem letzten Ladestopp gibt es nicht. |
packages/modules/psa/config.py
Outdated
client_id: Optional[str] = None, | ||
client_secret: Optional[str] = None, | ||
manufacturer: Optional[str] = None, | ||
soccalc: bool = False, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Dann am besten noch soc_calc
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ist überall zu calculate_soc umbenannt, das trifft es m.E. noch besser.
packages/modules/psa/soc.py
Outdated
|
||
|
||
class Soc(AbstractSoc): | ||
def __init__(self, device_config: Union[dict, PSA], vehicle: int): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Bei vehicle
denke ich nicht an einen int
. Ich sehe in anderen Modulen heißt das auch so... Mhh... Das könnte man als Anhaltspunkt sehen es so zu lassen zur Standardisierung. Ich hätte es sonst eher vehicle_id
oder so genannt. Wie siehst du das @LKuemmel ?
packages/modules/psa/api.py
Outdated
def fetch_access_token(user_id: str, password: str, client_id: Optional[str], client_secret: Optional[str], | ||
manufacturer: str, session: req.Session | ||
) -> Tuple[str, str, str]: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Die Aufteilung ist so viel besser, aber die schiere Anzahl an Parametern und Rückgabewerten finde ich unschön.
Hier ein Gegenvorschlag:
def create_session(user_id: str, password: str, client_id: Optional[str], client_secret: Optional[str],
manufacturer: str) -> Session:
manufacturer_config = manufacturer_configurations[manufacturer]
brand = manufacturer_config.brand
realm = manufacturer_config.realm
if not client_id or not client_secret: # use defaults if no client_id and client_secret is specified
client_id = manufacturer_config.client_id
client_secret = manufacturer_config.client_secret
if not client_id or not client_secret:
raise Exception(
"No OAuth credentials configured and no default available for manufacturer %s" % manufacturer)
session = req.get_http_session()
data = {
'realm': realm, 'grant_type': 'password', 'password': password, 'username': user_id, 'scope': 'openid profile'
}
access_token = session.post(
"https://idpcvs." + str(brand) + "/am/oauth2/access_token", data=data, auth=(client_id, client_secret)
).json()['access_token']
session.params = {"client_id": client_id}
session.headers = {
'Accept': 'application/hal+json', 'Authorization': 'Bearer %s' % access_token, 'x-introspect-realm': realm
}
return session
Ungetestet, müsste aber meine ich funktionieren und du hast damit alle Parameter für die ganze Session gesetzt und kannst diese auch in den anderen Funktionen einsparen.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ja, das ist in der Tat schöner, danke! Ist gestestet und funktioniert. PR ist aktualisiert.
packages/modules/psa/api.py
Outdated
session = create_session( | ||
config.user_id, config.password, config.client_id, config.client_secret, config.manufacturer) | ||
|
||
vehicle = fetch_vehicle(config.vin, session) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
modules/psa/api.py:113: error: Incompatible types in assignment (expression has type "Dict[Any, Any]", variable has type "int") [assignment]
Hat es recht... Die Variable vehicle
weiter oben, die als Parameter rein kommt ist ein int
.
Das ist auch insofern nicht optimal, weil 5 Zeilen später in Zeile 118 wo die Exception geworfen wird sowohl der int
stehen kann, als auch ein vehicle-dict, je nachdem wann der Fehler im try
fliegt.
Ich wäre dafür den Parameter in vehicle_id
oder sowas umzubenennen.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ist auch angepasst.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Aber "falschrum". Die id
passt eher zu dem int
welches der Parameter ist.
packages/modules/psa/soc.py
Outdated
Soc(PSA(configuration=PSAConfiguration(charge_point, | ||
user_id, | ||
password, | ||
client_id, | ||
client_secret, | ||
manufacturer, | ||
calculate_soc, | ||
vin)), | ||
charge_point).update(False) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
modules/psa/soc.py:45: error: Too many arguments for "PSAConfiguration" [call-arg]
Hat es recht, das kann nicht funktionieren. Es fällt auf, dass charge_point
doppelt vorkommt.
Da der Konstruktor von PSAConfiguration
sehr viele (>4) Parameter hat, könnte es zur Übersicht beitragen die Namen der Parameter mit anzugeben. Damit würden Parameterfehler (Reihenfolge und Anzahl) sichtbarer werden:
Soc(
PSA(
configuration=PSAConfiguration(
user_id=user_id,
password=password,
client_id=client_id,
client_secret=client_secret,
manufacturer=manufacturer,
calculate_soc=calculate_soc,
vin=vin)
),
charge_point
).update(False)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
danke, ist korrigiert.
packages/modules/psa/soc.py
Outdated
from modules.psa.config import PSA, PSAConfiguration | ||
|
||
|
||
log = logging.getLogger("soc."+__name__) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Eine Logausgabe sieht dann zum Beispiel so aus:
2022-11-21 13:06:31,761 - {soc.modules.psa.soc:10} - {WARNING:MainThread} - Hi there
Fokus auf den Loggername: soc.modules.psa.soc
. Das soc
ist so doppelt. Es kommt einmal von __name__
, den die Datei heißt soc.py
.
Ich finde es konsequenter nur bei __name__
zu bleiben, denn da steckt alles drin. Wenn die Initialisierung aus einer anderen Datei kommt, zum Beispiel aus der api.py
, dann hieße der Logger modules.psa.api
, was soc nicht enthält, die Quelle aber trotzdem eindeutig identifiziert.
Ich sehe allerdings, dass auch einige weitere Module das Konstrukt so haben. Falls das eine Designentscheidung so ist müsste das natürlich so bleiben...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ja, die anderen Module machen das auch genau so, ggf. kann man das zukünftig in allen Modulen ändern wenn sich das Design ändert.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Der Code sieht für mich gut aus. Ich würde emphelen noch die Git-Historie aufzuräumen, also auf den aktuellen master rebasen und alle commits zusammenfassen. Die kleinen Zwischenschritt vor dem erstmaligen Merge sind nicht so interessant und die Historie und die Verwendung in Werkzeugen wie git blame wird dann leichter zu lesen.
Da du jetzt wo du den 6afe762 gemergt hast sowas machst: git reset --soft 6afe7625f80a0b7624693bba28309e544f337ee9
git commit -m "add PSA SoC-Module"
git push -f origin (unter der Annahme dass das remote für deinen fork "origin" heißt), dann hast du wieder einen übersichtlichen PR mit nurnoch einem commit. Der soft-Reset setzt den branch pointer auf den angegebenen commit, lässt aber dein Arbeitsverzeichnis unangetastet. Dann kannst du alle Änderung wieder mit einem einzelnen commit committen. |
43021d8
to
ca266b0
Compare
hm, ich weiß nicht was ich hier jetzt angestellt habe 331 geänderte Dateien sind wohl etwas viel. Ist wohl am einfachsten wenn ich einen neuen PR mit sauberer Basis einstelle. |
Du kannst auf den gleichen Branch nochmal neu pushen. Muss kein neues Issue sein. |