-
Notifications
You must be signed in to change notification settings - Fork 5
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
Adds organisation API support #30
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -4,12 +4,24 @@ This repository contains a Python module for the Charge Amps' electric vehicle c | |
|
||
The module is developed by [Kirei AB](https://www.kirei.se) and is not supported by [Charge Amps AB](https://chargeamps.com). | ||
|
||
## How to use | ||
|
||
The simplest way is to install via `pip`, as in: | ||
|
||
``` | ||
pip install chargeamps | ||
``` | ||
|
||
If you need access to the organisation API calls, you need to specify this feature upon installation: | ||
|
||
``` | ||
pip install chargeamps["organisations"] | ||
``` | ||
|
||
## External API Key | ||
|
||
You need an API key to use the Charge Amps external API. Contact [Charge Amps Support](mailto:[email protected]) for extensive API documentation and API key. | ||
|
||
|
||
## References | ||
|
||
- [Charge Amps External REST API](https://eapi.charge.space/swagger/) |
Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
@@ -19,6 +19,8 @@ | |||||||||||||||||||||||||||||||||||||||||||||||||||||||
ChargePointStatus, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
ChargingSession, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
StartAuth, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
User, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Partner, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
API_BASE_URL = "https://eapi.charge.space" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
@@ -44,6 +46,7 @@ def __init__( | |||||||||||||||||||||||||||||||||||||||||||||||||||||||
self._token = None | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
self._token_expire = 0 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
self._refresh_token = None | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
self._user: User = None | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
async def shutdown(self) -> None: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
await self._session.close() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
@@ -72,6 +75,7 @@ async def _ensure_token(self) -> None: | |||||||||||||||||||||||||||||||||||||||||||||||||||||||
except HTTPException: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
self._logger.warning("Token refresh failed") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
self._token = None | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
self._user = None | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
self._refresh_token = None | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
else: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
self._token = None | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
@@ -89,6 +93,7 @@ async def _ensure_token(self) -> None: | |||||||||||||||||||||||||||||||||||||||||||||||||||||||
except HTTPException as exc: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
self._logger.error("Login failed") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
self._token = None | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
self._user = None | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
self._refresh_token = None | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
self._token_expire = 0 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
raise exc | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
@@ -100,6 +105,7 @@ async def _ensure_token(self) -> None: | |||||||||||||||||||||||||||||||||||||||||||||||||||||||
response_payload = await response.json() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
self._token = response_payload["token"] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
self._user = response_payload["user"] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
self._refresh_token = response_payload["refreshToken"] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
token_payload = jwt.decode(self._token, options={"verify_signature": False}) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
@@ -209,6 +215,13 @@ async def set_chargepoint_connector_settings( | |||||||||||||||||||||||||||||||||||||||||||||||||||||||
request_uri = f"/api/{API_VERSION}/chargepoints/{charge_point_id}/connectors/{connector_id}/settings" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
await self._put(request_uri, json=payload) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
async def get_partner(self, charge_point_id: str) -> Partner: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
"""Get partner details""" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
request_uri = f"/api/{API_VERSION}/chargepoints/{charge_point_id}/partner" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
response = await self._get(request_uri) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
payload = await response.json() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
return Partner.model_validate(payload) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Comment on lines
+218
to
+223
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion Enhance error handling and documentation for get_partner method The method needs better error handling and documentation of possible exceptions. async def get_partner(self, charge_point_id: str) -> Partner:
- """Get partner details"""
+ """Get partner details for a specific charge point.
+
+ Args:
+ charge_point_id: The ID of the charge point
+
+ Returns:
+ Partner: The partner details
+
+ Raises:
+ HTTPException: If the API request fails
+ ValidationError: If the response data is invalid
+ """
request_uri = f"/api/{API_VERSION}/chargepoints/{charge_point_id}/partner"
- response = await self._get(request_uri)
- payload = await response.json()
- return Partner.model_validate(payload)
+ try:
+ response = await self._get(request_uri)
+ payload = await response.json()
+ return Partner.model_validate(payload)
+ except Exception as e:
+ self._logger.error(f"Failed to get partner details: {e}")
+ raise 📝 Committable suggestion
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not sure why just this function needed more detailed documentation, but similarly to above I will keep both the docstring and error handling consistent with the rest of the module. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @yarcod, understood. Maintaining consistency in docstrings and error handling across the module makes sense. Thank you for clarifying. ✏️ Learnings added
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
async def remote_start( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
self, charge_point_id: str, connector_id: int, start_auth: StartAuth | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
) -> None: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
@@ -226,3 +239,16 @@ async def reboot(self, charge_point_id) -> None: | |||||||||||||||||||||||||||||||||||||||||||||||||||||||
"""Reboot chargepoint""" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
request_uri = f"/api/{API_VERSION}/chargepoints/{charge_point_id}/reboot" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
await self._put(request_uri, json="{}") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
async def get_logged_in_user(self) -> User: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
"""Get authenticated user info""" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
if not self._user or not isinstance(self._user, dict): | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
raise ValueError("No user is currently logged in") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
user_id = self._user["id"] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
request_uri = f"/api/{API_VERSION}/users/{user_id}" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
response = await self._get(request_uri) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
payload = await response.json() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
return User.model_validate(payload) |
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.
Ensure consistent typing of
self._user
The instance variable
self._user
is annotated asUser
but is assigned a dictionary fromresponse_payload["user"]
. This can lead to type inconsistencies and potential errors.Apply this diff to validate and assign the user data correctly:
This ensures
self._user
is always aUser
instance.Additionally, in the
get_logged_in_user
method, you can returnself._user
directly: