diff --git a/.github/workflows/python-pdm.yml b/.github/workflows/python-pdm.yml index 600f8f1..6a7f61e 100644 --- a/.github/workflows/python-pdm.yml +++ b/.github/workflows/python-pdm.yml @@ -14,7 +14,7 @@ jobs: strategy: fail-fast: false matrix: - python-version: ['3.9', '3.10', '3.11', '3.12', '3.13'] + python-version: ['3.10', '3.11', '3.12', '3.13', '3.14'] os: [ubuntu-latest, macOS-latest, windows-latest] steps: diff --git a/README.md b/README.md index 7036dbe..0248bac 100644 --- a/README.md +++ b/README.md @@ -40,7 +40,7 @@ Changes can be followed at [CHANGELOG.md](https://github.com/waldbaer/e3dc-cli/b ## Requirements ## - - [Python 3.9](https://www.python.org/) + - [Python 3.10](https://www.python.org/) - [pip](https://pip.pypa.io/) or [pipx](https://pipx.pypa.io/stable/) For development: diff --git a/pyproject.toml b/pyproject.toml index 1635c4e..d4131d3 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -6,9 +6,9 @@ authors = [{ name = "Sebastian Waldvogel", email = "sebastian@waldvogels.de" }] license = { text = "MIT" } readme = "README.md" -requires-python = ">=3.9" +requires-python = ">=3.10" dependencies = [ - "pye3dc==0.9.3", + "pye3dc==0.10.0", "tzlocal==5.3.1", "jsonargparse==4.42.0", "rich-argparse==1.7.2", diff --git a/src/e3dc_cli/argparse.py b/src/e3dc_cli/argparse.py index 9d698c3..bed33d6 100755 --- a/src/e3dc_cli/argparse.py +++ b/src/e3dc_cli/argparse.py @@ -2,7 +2,7 @@ # ---- Imports ---- import sys -from typing import Any, Dict, List, Optional +from typing import Any, Dict, List from jsonargparse import ArgumentParser, DefaultHelpFormatter from pydantic import SecretStr @@ -86,7 +86,7 @@ def parse_config(prog: str, version: str, copy_right: str, author: str, arg_list arg_parser.add_argument( "-o", "--output", - type=Optional[str], + type=str | None, help="Path of JSON output file. If not set JSON output is written to console / stdout", ) @@ -103,7 +103,7 @@ def parse_config(prog: str, version: str, copy_right: str, author: str, arg_list ) arg_parser.add_argument( "--connection.address", - type=Optional[str], + type=str | None, help="""IP or DNS address of the E3/DC system. Only relevant for connection type 'local'. """, @@ -120,14 +120,14 @@ def parse_config(prog: str, version: str, copy_right: str, author: str, arg_list ) arg_parser.add_argument( "--connection.rscp_password", - type=Optional[SecretStr], + type=SecretStr | None, help="""RSCP password. Set on the device via Main Page -> Personalize -> User profile -> RSCP password. Only relevant for connection type 'local', """, ) arg_parser.add_argument( "--connection.serial_number", - type=Optional[SecretStr], + type=SecretStr | None, help="""Serial number of the system (see 'SN' in E3/DC portal). Only relevant for connection type 'web'. """, @@ -168,7 +168,7 @@ def parse_config(prog: str, version: str, copy_right: str, author: str, arg_list # ---- Setter ---- arg_parser.add_argument( "--set.power_limits.enable", - type=Optional[bool], + type=bool | None, metavar="{true,false}", help="""true: enable manual SmartPower limits. false: Use automatic mode. Automatically set to 'true' if not explicitly set and any other manual limit @@ -177,7 +177,7 @@ def parse_config(prog: str, version: str, copy_right: str, author: str, arg_list ) arg_parser.add_argument( "--set.power_limits.max_charge", - type=Optional[int], + type=int | None, help="""SmartPower maximum charging power. Unit: Watt. Automatically set to the systems max. battery charge power limit if not explicitly set. Only relevant if set.power_limits.enable is 'true' or not explicitly configured. @@ -185,7 +185,7 @@ def parse_config(prog: str, version: str, copy_right: str, author: str, arg_list ) arg_parser.add_argument( "--set.power_limits.max_discharge", - type=Optional[int], + type=int | None, help="""SmartPower maximum discharging power. Unit: Watt. Automatically set to the systems max. battery discharge power limit if not explicitly set. Only relevant if set.power_limits.enable is 'true' or not explicitly configured. @@ -193,7 +193,7 @@ def parse_config(prog: str, version: str, copy_right: str, author: str, arg_list ) arg_parser.add_argument( "--set.power_limits.discharge_start", - type=Optional[int], + type=int | None, help="""SmartPower lower charge / discharge threshold. Unit: Watt. Automatically set to the systems discharge default threshold if not explicitly set. Only relevant if set.power_limits.enable is 'true' or not explicitly configured. @@ -202,13 +202,13 @@ def parse_config(prog: str, version: str, copy_right: str, author: str, arg_list arg_parser.add_argument( "--set.powersave", - type=Optional[bool], + type=bool | None, metavar="{true,false}", help="Enable / Disable PowerSave of the inverter (inverter switches to standby mode when not in use).", ) arg_parser.add_argument( "--set.weather_regulated_charge", - type=Optional[bool], + type=bool | None, metavar="{true,false}", help="Enabled / Disable optimized charging based on the weather forecast.", ) diff --git a/tests/test_queries.py b/tests/test_queries.py index 4792a6b..34228c3 100644 --- a/tests/test_queries.py +++ b/tests/test_queries.py @@ -2,7 +2,6 @@ import os import re -from typing import Optional import pytest from pytest_mock.plugin import MockerFixture @@ -223,7 +222,7 @@ def test_ct_query_single( @pytest.mark.parametrize("connection_type", [ConnectionType.local, ConnectionType.web]) @pytest.mark.parametrize("output_file", [None, "e3dc_cli_test.json"]) def test_ct_query_multi( - connection_type: ConnectionType, output_file: Optional[str], tmp_path: str, capsys: pytest.CaptureFixture[str] + connection_type: ConnectionType, output_file: str | None, tmp_path: str, capsys: pytest.CaptureFixture[str] ) -> None: """Test the --query option for multiple queries. diff --git a/tests/test_setter.py b/tests/test_setter.py index 5d283c8..6f6f24f 100644 --- a/tests/test_setter.py +++ b/tests/test_setter.py @@ -1,7 +1,5 @@ """Test of setter commands.""" -from typing import Optional - import pytest from e3dc_cli import setter @@ -125,7 +123,7 @@ def test_ct_setter_boolean( ], ) def test_ct_setter_power_limits( - enable: Optional[bool], + enable: bool | None, expected_enable: bool, max_charge: int, max_discharge: int, diff --git a/tests/util_runner.py b/tests/util_runner.py index 978af6a..b2f736d 100644 --- a/tests/util_runner.py +++ b/tests/util_runner.py @@ -5,7 +5,7 @@ import shlex from ast import Dict from dataclasses import dataclass -from typing import List, Optional +from typing import List import pytest @@ -22,10 +22,10 @@ class CliResult: stdout: str stderr: str stdout_lines: List[str] - stdout_as_json: Optional[dict] + stdout_as_json: dict | None fileout: str fileout_lines: List[str] - fileout_as_json: Optional[dict] + fileout_as_json: dict | None def __init__( self, @@ -33,10 +33,10 @@ def __init__( stdout: str, stderr: str, stdout_lines: List[str], - stdout_as_json: Optional[dict] = None, + stdout_as_json: dict | None = None, fileout: str = None, fileout_lines: List[str] = None, - fileout_as_json: Optional[dict] = None, + fileout_as_json: dict | None = None, ) -> None: """Construct. @@ -60,7 +60,7 @@ def __init__( self.fileout_as_json = fileout_as_json -def run_cli(cli_args: str, capsys: pytest.CaptureFixture, output_path: Optional[str] = None) -> CliResult: +def run_cli(cli_args: str, capsys: pytest.CaptureFixture, output_path: str | None = None) -> CliResult: """Run the command line util with the passed arguments. Arguments: @@ -87,7 +87,7 @@ def run_cli(cli_args: str, capsys: pytest.CaptureFixture, output_path: Optional[ ) -def run_cli_json(cli_args: str, capsys: pytest.CaptureFixture, output_path: Optional[str] = None) -> Dict: +def run_cli_json(cli_args: str, capsys: pytest.CaptureFixture, output_path: str | None = None) -> Dict: """Run the command line util with the passed arguments and capture the outputs from a JSON file. Arguments: