-
-
Notifications
You must be signed in to change notification settings - Fork 43
Simplify the V1 methods by using single re-usable base methods #125
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
base: master
Are you sure you want to change the base?
Changes from 17 commits
c6703ba
6e487d6
8a07ba6
cf7d7b8
aa01035
1d1e8e2
76b4548
52661fd
ce9afa5
21f9581
fd98a51
7d4db09
837c78c
f042b63
0c03dfc
e4241e9
3f3790d
9baba05
f58d8b1
48a8fc6
ae1f36e
4314600
8f75a8f
b32fd8d
c393caf
b90943c
5c75e6f
08668a3
aae9343
0006892
0125ebd
11e28ac
97b3e93
b1429e8
68dda25
1bba0a0
33b84f8
4dc88d1
a75df72
68a6317
611e983
b1cd756
dc9648f
7d183d3
4ef89a5
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 |
|---|---|---|
|
|
@@ -26,6 +26,7 @@ share/python-wheels/ | |
| .installed.cfg | ||
| *.egg | ||
| MANIFEST | ||
| *.json | ||
|
|
||
| # Symlink | ||
| examples/growattServer | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,25 @@ | ||
| # The contents of this file is based on https://github.com/home-assistant/core/blob/dev/pyproject.toml | ||
|
|
||
| target-version = "py313" | ||
|
|
||
| [lint] | ||
| select = [ | ||
| "ALL", | ||
| ] | ||
|
|
||
| ignore = [ | ||
| "ANN401", # Dynamically typed expressions (typing.Any) are disallowed | ||
| "D203", # no-blank-line-before-class (incompatible with formatter) | ||
| "D212", # multi-line-summary-first-line (incompatible with formatter) | ||
| "COM812", # incompatible with formatter | ||
| "ISC001", # incompatible with formatter | ||
| ] | ||
|
|
||
| [lint.flake8-pytest-style] | ||
| fixture-parentheses = false | ||
|
|
||
| [lint.pyupgrade] | ||
| keep-runtime-typing = true | ||
|
|
||
| [lint.mccabe] | ||
| max-complexity = 25 |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,65 @@ | ||
| # MIX/SPH Inverter Settings | ||
|
|
||
| This is part of the [OpenAPI V1 doc](../openapiv1.md). | ||
|
|
||
| For MIX/SPH systems, the public V1 API provides a way to read and write inverter settings: | ||
|
|
||
| * **Device Settings** | ||
| * function: `api.device_settings` | ||
| * parameters: | ||
| * `device_sn`: The device serial number | ||
| * `device_type`: Use `DeviceType.MIX_SPH` for MIX/SPH inverters | ||
|
|
||
| * **Read Parameter** | ||
| * function: `api.read_parameter` | ||
| * parameters: | ||
| * `device_sn`: The device serial number | ||
| * `device_type`: Use `DeviceType.MIX_SPH` for MIX/SPH inverters | ||
| * `parameter_id`: Parameter ID to read (e.g., "ac_charge", "discharge_power") | ||
|
|
||
| * **Time Segments** | ||
| * function: `api.read_time_segments` | ||
| * parameters: | ||
| * `device_sn`: The device serial number | ||
| * `device_type`: Use `DeviceType.MIX_SPH` for MIX/SPH inverters | ||
| * `settings_data`: Optional settings data to avoid redundant API calls | ||
|
|
||
| Returns: | ||
| ```python | ||
| [ | ||
| { | ||
| 'segment_id': int, # Segment number (1-9) | ||
| 'batt_mode': int, # 0=Load First, 1=Battery First, 2=Grid First | ||
| 'mode_name': str, # String representation of the mode | ||
| 'start_time': str, # Start time in format "HH:MM" | ||
| 'end_time': str, # End time in format "HH:MM" | ||
| 'enabled': bool # Whether the segment is enabled | ||
| }, | ||
| # ... (up to 9 segments) | ||
| ] | ||
| ``` | ||
|
|
||
| ## Common Usage Examples | ||
|
|
||
| ### Reading Device Settings | ||
| ```python | ||
| from growattServer import OpenApiV1, DeviceType | ||
|
|
||
| api = OpenApiV1(token="your_api_token") | ||
| settings = api.device_settings("DEVICE_SN", DeviceType.MIX_SPH) | ||
| ``` | ||
|
|
||
| ### Reading Parameters | ||
| ```python | ||
| # Read a specific parameter | ||
| value = api.read_parameter("DEVICE_SN", DeviceType.MIX_SPH, "ac_charge") | ||
| ``` | ||
|
|
||
| ### Reading Time Segments | ||
| ```python | ||
| # Option 1: Single call | ||
| segments = api.read_time_segments("DEVICE_SN", DeviceType.MIX_SPH) | ||
|
|
||
| # Option 2: Reuse settings data to avoid multiple API calls | ||
| settings = api.device_settings("DEVICE_SN", DeviceType.MIX_SPH) | ||
| segments = api.read_time_segments("DEVICE_SN", DeviceType.MIX_SPH, settings) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,10 +1,32 @@ | ||
| import growattServer | ||
| from . import growattServer | ||
| import json | ||
| import requests | ||
| import datetime | ||
| import os | ||
|
|
||
|
|
||
| def safe_float(val, default=0.0): | ||
| try: | ||
| # If already a float, return as is | ||
| if isinstance(val, float): | ||
| return val | ||
| # If it's an int, convert to float | ||
| if isinstance(val, int): | ||
| return float(val) | ||
| # If it's a string, try to parse | ||
| if isinstance(val, str): | ||
| # Remove any commas, spaces, etc. | ||
| val = val.replace(',', '').strip() | ||
| return float(val) | ||
| # If it's a type that can be cast to float (e.g., numpy.float64) | ||
| return float(val) | ||
| except (TypeError, ValueError, KeyError, AttributeError): | ||
| return default | ||
|
|
||
|
|
||
| """ | ||
| Example script fetching key power and today+total energy metrics from a Growatt MID-30KTL3-XH (TLX) + APX battery hybrid system | ||
| using the V1 API with token-based authentication. | ||
| # Example script controlling a MIX/SPH Growatt (SPH3~6k TL BL UP + battery) system using the public growatt API | ||
| # You can obtain an API token from the Growatt API documentation or developer portal. | ||
| """ | ||
|
|
||
| # Get the API token from user input or environment variable | ||
|
|
@@ -17,23 +39,21 @@ | |
| # Initialize the API with token | ||
| api = growattServer.OpenApiV1(token=api_token) | ||
|
|
||
| # Get plant list using V1 API | ||
| # Plant info | ||
| plants = api.plant_list() | ||
| print(f"Plants: Found {plants['count']} plants") | ||
| plant_id = plants['plants'][0]['plant_id'] | ||
| today = datetime.date.today() | ||
| devices = api.get_devices(plant_id) | ||
|
|
||
| # Get devices in plant | ||
| devices = api.device_list(plant_id) | ||
|
|
||
| # Iterate over all devices | ||
| energy_data = None | ||
| for device in devices['devices']: | ||
| if device['type'] == 7: # (MIN/TLX) | ||
| inverter_sn = device['device_sn'] | ||
|
|
||
| # Get energy data | ||
| energy_data = api.min_energy(device_sn=inverter_sn) | ||
| with open('energy_data.json', 'w') as f: | ||
| json.dump(energy_data, f, indent=4, sort_keys=True) | ||
| for device in devices: | ||
| # Works automatically for MIN, MIX, or any future device type! | ||
| energy_data = device.energy() | ||
| print(f"Energy: {energy_data}") | ||
|
|
||
| if energy_data is None: | ||
| raise Exception("No MIN_TLX device found to get energy data from.") | ||
|
|
||
| # energy data does not contain epvToday for some reason, so we need to calculate it | ||
| epv_today = energy_data["epv1Today"] + energy_data["epv2Today"] | ||
|
||
|
|
@@ -92,4 +112,6 @@ | |
| except requests.exceptions.RequestException as e: | ||
| print(f"Network Error: {e}") | ||
| except Exception as e: | ||
| import traceback | ||
| print(f"Unexpected error: {e}") | ||
| traceback.print_exc() | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,51 @@ | ||
| from . import growattServer | ||
| import datetime | ||
| import json | ||
| import requests | ||
| import os | ||
|
|
||
| """ | ||
| # Example script controlling a MIX/SPH Growatt (SPH3~6k TL BL UP + battery) system using the public growatt API | ||
| # You can obtain an API token from the Growatt API documentation or developer portal. | ||
| """ | ||
|
|
||
| # Get the API token from user input or environment variable | ||
| api_token = os.environ.get("GROWATT_API_TOKEN") or input("Enter your Growatt API token: ") | ||
|
|
||
| # test token from official API docs https://www.showdoc.com.cn/262556420217021/1494053950115877 | ||
| # api_token = "6eb6f069523055a339d71e5b1f6c88cc" # gitleaks:allow | ||
|
|
||
| try: | ||
| # Initialize the API with token instead of using login | ||
| api = growattServer.OpenApiV1(token=api_token) | ||
|
|
||
| # Plant info | ||
| plants = api.plant_list() | ||
| print(f"Plants: Found {plants['count']} plants") | ||
| plant_id = plants['plants'][0]['plant_id'] | ||
| today = datetime.date.today() | ||
| devices = api.get_devices(plant_id) | ||
|
|
||
| for device in devices: | ||
| # Works automatically for MIN, MIX, or any future device type! | ||
| device_type = device.device_type | ||
| device_sn = device.device_sn | ||
| print(f"Device: {device_type} SN: {device_sn}") | ||
| details = device.details() | ||
| energy = device.energy() | ||
| settings = device.settings() | ||
| history = device.energy_history(start_date=today) | ||
|
|
||
| print(f"Details: {details}") | ||
| print(f"Energy: {energy}") | ||
| print(f"Settings: {settings}") | ||
| print(f"History: {history}") | ||
|
|
||
| except growattServer.GrowattV1ApiError as e: | ||
| print(f"API Error: {e} (Code: {e.error_code}, Message: {e.error_msg})") | ||
| except growattServer.GrowattParameterError as e: | ||
| print(f"Parameter Error: {e}") | ||
| except requests.exceptions.RequestException as e: | ||
| print(f"Network Error: {e}") | ||
| except Exception as e: | ||
| print(f"Unexpected error: {e}") |
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.
passing device_type?
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.
let me check , but I believe I have 3 different ways to call the same methods , device_details() is the base method with sn and device type https://github.com/indykoning/PyPi_GrowattServer/pull/125/files#diff-12865f9986e5f28194397aae0efed2b7abc9137d18c0a09197682e6c7e65cd01R484-R511, the details() https://github.com/indykoning/PyPi_GrowattServer/pull/125/files#diff-12865f9986e5f28194397aae0efed2b7abc9137d18c0a09197682e6c7e65cd01R1225-R1227 where the sn and device type are set on the object, so not needed as params , I will roll back my changes in examples/min_example.py to use a backwards compatible min_detail(sn) which will just call device_details(sn, device_type=7 )