Skip to content
Merged
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
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ jobs:
matrix:
python-version:
- "3.13"
- "3.14"

steps:
- uses: actions/checkout@v4
Expand All @@ -35,6 +36,7 @@ jobs:
- name: Test with tox
run: tox
- name: Upload coverage data
if: ${{ matrix.python-version == '3.14' }}
uses: "actions/upload-artifact@v4"
with:
name: coverage-data
Expand Down
52 changes: 10 additions & 42 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -1,51 +1,23 @@
default_install_hook_types: [pre-commit, pre-push]
repos:
- repo: https://github.com/asottile/pyupgrade
rev: v2.31.0
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.9.7
hooks:
- id: pyupgrade
args: ["--py39-plus"]
- repo: https://github.com/ambv/black
rev: 22.3.0
hooks:
- id: black
language_version: python3
- id: ruff
args:
- --safe
- --quiet
files: ^(openevsehttp/.+)?[^/]+\.py$
- --fix
- id: ruff-format
- repo: https://github.com/codespell-project/codespell
rev: v1.17.1
rev: v2.4.1
hooks:
- id: codespell
args:
- --ignore-words-list=hass,alot,datas,dof,dur,farenheit,hist,iff,ines,ist,lightsensor,mut,nd,pres,referer,ser,serie,te,technik,ue,uint,visability,wan,wanna,withing,iam,incomfort
- --skip="./.*,*.csv,*.json"
- --quiet-level=2
exclude_types: [csv, json]
- repo: https://gitlab.com/pycqa/flake8
rev: 4.0.1
hooks:
- id: flake8
additional_dependencies:
- flake8-docstrings==1.5.0
files: ^openevsehttp/.+\.py$
args:
- --max-line-length=500
- --ignore=E203,E266,E501,W503
- --max-complexity=18
- --select=B,C,E,F,W,T4,B9
- repo: https://github.com/pre-commit/mirrors-isort
rev: v5.10.1
hooks:
- id: isort
args:
- --multi-line=3
- --trailing-comma
- --force-grid-wrap=0
- --use-parentheses
- --line-width=88
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.1.0
rev: v5.0.0
hooks:
- id: check-executables-have-shebangs
stages: [manual]
Expand All @@ -57,12 +29,8 @@ repos:
- id: check-docstring-first
- id: check-yaml
- id: debug-statements
- repo: https://github.com/PyCQA/pydocstyle
rev: 6.1.1
hooks:
- id: pydocstyle
- repo: https://github.com/adrienverge/yamllint.git
rev: v1.24.2
rev: v1.35.1
hooks:
- id: yamllint
- repo: https://github.com/prettier/prettier
Expand All @@ -71,7 +39,7 @@ repos:
- id: prettier
stages: [manual]
- repo: https://github.com/pre-commit/mirrors-mypy
rev: "v0.930"
rev: "v1.15.0"
hooks:
- id: mypy
files: ^openevsehttp/.+\.py$
12 changes: 6 additions & 6 deletions EXTERNAL_SESSION.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,11 @@ async def main():
async with aiohttp.ClientSession(timeout=timeout) as session:
# Pass the session to OpenEVSE
charger = OpenEVSE("openevse.local", session=session)

# Use the charger normally
await charger.update()
print(f"Status: {charger.status}")

# Clean up
await charger.ws_disconnect()
# Session will be closed by the context manager
Expand All @@ -43,11 +43,11 @@ from openevsehttp import OpenEVSE
async def main():
# The library creates and manages its own sessions
charger = OpenEVSE("openevse.local")

# Use the charger normally
await charger.update()
print(f"Status: {charger.status}")

await charger.ws_disconnect()
```

Expand All @@ -62,11 +62,11 @@ async def main():
# Use the same session for multiple chargers
charger1 = OpenEVSE("charger1.local", session=session)
charger2 = OpenEVSE("charger2.local", session=session)

# Both chargers use the same session
await charger1.update()
await charger2.update()

await charger1.ws_disconnect()
await charger2.ws_disconnect()
```
Expand Down
6 changes: 3 additions & 3 deletions example_external_session.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@


async def example_with_external_session():
"""Example using an external session."""
"""Demonstrate using an external session."""
# Create your own session with custom settings
timeout = aiohttp.ClientTimeout(total=30)
async with aiohttp.ClientSession(timeout=timeout) as session:
Expand All @@ -32,7 +32,7 @@ async def example_with_external_session():


async def example_without_external_session():
"""Example without external session (backward compatible)."""
"""Demonstrate without external session (backward compatible)."""
# The library will create and manage its own sessions
charger = OpenEVSE("openevse.local")

Expand All @@ -45,7 +45,7 @@ async def example_without_external_session():


async def example_shared_session():
"""Example sharing a session between multiple clients."""
"""Demonstrate sharing a session between multiple clients."""
async with aiohttp.ClientSession() as session:
# Use the same session for multiple chargers
charger1 = OpenEVSE("charger1.local", session=session)
Expand Down
61 changes: 21 additions & 40 deletions openevsehttp/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,9 @@
import json
import logging
import re
from collections.abc import Callable
from datetime import datetime, timedelta, timezone
from typing import Any, Callable, Dict, Union
from typing import Any

import aiohttp # type: ignore
from aiohttp.client_exceptions import ContentTypeError, ServerTimeoutError
Expand Down Expand Up @@ -151,12 +152,10 @@ async def _process_request_with_session(
method,
)
try:
async with http_method(
url,
data=rapi,
json=data,
auth=auth,
) as resp:
kwargs = {"data": rapi, "auth": auth}
if data is not None:
kwargs["json"] = data
async with http_method(url, **kwargs) as resp:
try:
message = await resp.text()
except UnicodeDecodeError:
Expand Down Expand Up @@ -356,7 +355,7 @@ async def repeat(self, interval, func, *args, **kwargs):
await asyncio.sleep(interval)
await func(*args, **kwargs)

async def get_schedule(self) -> Union[Dict[str, str], Dict[str, Any]]:
async def get_schedule(self) -> dict[str, str] | dict[str, Any]:
"""Return the current schedule."""
url = f"{self.url}schedule"

Expand All @@ -375,9 +374,7 @@ async def set_charge_mode(self, mode: str = "fast") -> None:
data = {"charge_mode": mode}

_LOGGER.debug("Setting charge mode to %s", mode)
response = await self.process_request(
url=url, method="post", data=data
) # noqa: E501
response = await self.process_request(url=url, method="post", data=data)
result = response["msg"]
if result not in ["done", "no change"]:
_LOGGER.error("Problem issuing command: %s", response["msg"])
Expand All @@ -402,13 +399,11 @@ async def divert_mode(self) -> dict[str, str] | dict[str, Any]:
data = {"divert_enabled": mode}

_LOGGER.debug("Toggling divert: %s", mode)
response = await self.process_request(
url=url, method="post", data=data
) # noqa: E501
response = await self.process_request(url=url, method="post", data=data)
_LOGGER.debug("divert_mode response: %s", response)
return response

async def get_override(self) -> Union[Dict[str, str], Dict[str, Any]]:
async def get_override(self) -> dict[str, str] | dict[str, Any]:
"""Get the manual override status."""
if not self._version_check("4.0.1"):
_LOGGER.debug("Feature not supported for older firmware.")
Expand Down Expand Up @@ -455,9 +450,7 @@ async def set_override(

_LOGGER.debug("Override data: %s", data)
_LOGGER.debug("Setting override config on %s", url)
response = await self.process_request(
url=url, method="post", data=data
) # noqa: E501
response = await self.process_request(url=url, method="post", data=data)
return response

async def toggle_override(self) -> None:
Expand Down Expand Up @@ -528,9 +521,7 @@ async def set_service_level(self, level: int = 2) -> None:
data = {"service": level}

_LOGGER.debug("Set service level to: %s", level)
response = await self.process_request(
url=url, method="post", data=data
) # noqa: E501
response = await self.process_request(url=url, method="post", data=data)
_LOGGER.debug("service response: %s", response)
result = response["msg"]
if result not in ["done", "no change"]:
Expand Down Expand Up @@ -800,7 +791,7 @@ async def set_limit(
raise UnsupportedFeature

url = f"{self.url}limit"
data: Dict[str, Any] = await self.get_limit()
data: dict[str, Any] = await self.get_limit()
valid_types = ["time", "energy", "soc", "range"]

if limit_type not in valid_types:
Expand All @@ -813,9 +804,7 @@ async def set_limit(

_LOGGER.debug("Limit data: %s", data)
_LOGGER.debug("Setting limit config on %s", url)
response = await self.process_request(
url=url, method="post", data=data
) # noqa: E501
response = await self.process_request(url=url, method="post", data=data)
return response

async def clear_limit(self) -> Any:
Expand All @@ -825,12 +814,9 @@ async def clear_limit(self) -> Any:
raise UnsupportedFeature

url = f"{self.url}limit"
data: Dict[str, Any] = {}

_LOGGER.debug("Clearing limit config on %s", url)
response = await self.process_request(
url=url, method="delete", data=data
) # noqa: E501
response = await self.process_request(url=url, method="delete")
return response

async def get_limit(self) -> Any:
Expand All @@ -840,12 +826,9 @@ async def get_limit(self) -> Any:
raise UnsupportedFeature

url = f"{self.url}limit"
data: Dict[str, Any] = {}

_LOGGER.debug("Getting limit config on %s", url)
response = await self.process_request(
url=url, method="get", data=data
) # noqa: E501
response = await self.process_request(url=url, method="get")
return response

async def make_claim(
Expand Down Expand Up @@ -880,9 +863,7 @@ async def make_claim(

_LOGGER.debug("Claim data: %s", data)
_LOGGER.debug("Setting up claim on %s", url)
response = await self.process_request(
url=url, method="post", data=data
) # noqa: E501
response = await self.process_request(url=url, method="post", data=data)
return response

async def release_claim(self, client: int = CLIENT) -> Any:
Expand All @@ -894,7 +875,7 @@ async def release_claim(self, client: int = CLIENT) -> Any:
url = f"{self.url}claims/{client}"

_LOGGER.debug("Releasing claim on %s", url)
response = await self.process_request(url=url, method="delete") # noqa: E501
response = await self.process_request(url=url, method="delete")
return response

async def list_claims(self, target: bool | None = None) -> Any:
Expand All @@ -910,7 +891,7 @@ async def list_claims(self, target: bool | None = None) -> Any:
url = f"{self.url}claims{target_check}"

_LOGGER.debug("Getting claims on %s", url)
response = await self.process_request(url=url, method="get") # noqa: E501
response = await self.process_request(url=url, method="get")
return response

async def set_led_brightness(self, level: int) -> None:
Expand All @@ -924,7 +905,7 @@ async def set_led_brightness(self, level: int) -> None:

data["led_brightness"] = level
_LOGGER.debug("Setting LED brightness to %s", level)
await self.process_request(url=url, method="post", data=data) # noqa: E501
await self.process_request(url=url, method="post", data=data)

async def set_divert_mode(self, mode: str = "fast") -> None:
"""Set the divert mode."""
Expand Down Expand Up @@ -1387,7 +1368,7 @@ def freeram(self) -> int | None:
# Safety counts
@property
def checks_count(self) -> dict:
"""Return the saftey checks counts."""
"""Return the safety checks counts."""
attributes = ("gfcicount", "nogndcount", "stuckcount")
counts = {}
if self._status is not None and set(attributes).issubset(self._status.keys()):
Expand Down
2 changes: 1 addition & 1 deletion openevsehttp/websocket.py
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,7 @@ async def keepalive(self):
if self._ping and self._pong:
time_delta = self._pong - self._ping
if time_delta < datetime.timedelta(0):
# Negitive time should indicate no pong reply so consider the
# Negative time should indicate no pong reply so consider the
# websocket disconnected.
self._error_reason = ERROR_PING_TIMEOUT
await self._set_state(STATE_DISCONNECTED)
Expand Down
41 changes: 0 additions & 41 deletions pylintrc

This file was deleted.

Loading