Compare commits
2 Commits
2afab331db
...
b4c7c07b79
Author | SHA1 | Date |
---|---|---|
Thomas Luther | b4c7c07b79 | |
Thomas Luther | b41590e888 |
16
README.md
16
README.md
|
@ -63,7 +63,9 @@ async def main() -> None:
|
|||
common.user(), common.password(), common.country(), websession, _LOGGER
|
||||
)
|
||||
await myapi.update_sites()
|
||||
await myapi.update_site_details()
|
||||
await myapi.update_device_details()
|
||||
await myapi.update_device_energy()
|
||||
print("System Overview:")
|
||||
print(json.dumps(myapi.sites, indent=2))
|
||||
print("Device Overview:")
|
||||
|
@ -78,14 +80,18 @@ if __name__ == "__main__":
|
|||
print(f"{type(err)}: {err}")
|
||||
```
|
||||
|
||||
The AnkerSolixApi class provides 2 main methods:
|
||||
The AnkerSolixApi class provides 4 main methods to query data and cache them into internal dictionaries:
|
||||
|
||||
- `AnkerSolixApi.update_sites()` to query overview data for all accessible sites and store data in dictionaries `AnkerSolixApi.sites` and `AnkerSolixApi.devices` for quick access.
|
||||
This method could be run in regular intervals (30sec or more) to fetch new data of the systems
|
||||
- `AnkerSolixApi.update_device_details()` to query further settings for the device serials as found in the sites query.
|
||||
This method could be run in regular intervals (60sec or more) to fetch new data of the systems. Note that the system devices update the cloud data only once per minute, therefore less than 60 second intervals do not provide much benefit
|
||||
- `AnkerSolixApi.update_device_details()` to query further settings for the device serials as found in the sites query or for stand alone devices and store data in dictionary `AnkerSolixApi.devices`
|
||||
This method should be run less frequently since this will mostly fetch various device configuration settings and needs multiple queries.
|
||||
It currently is developped for Solarbank and Inverter devices only, further device types such as Portable Power Stations or Power Panels
|
||||
could be added once example data is available.
|
||||
- `AnkerSolixApi.update_site_details()` to query further settings for the defined site (power system) and store data in dictionary `AnkerSolixApi.sites` for quick access.
|
||||
This method should be run less frequently since this will mostly fetch various site configuration settings and needs multiple queries.
|
||||
- `AnkerSolixApi.update_device_energy()` to query further energy statistics for the devices and store data in dictionary `AnkerSolixApi.devices` for quick access.
|
||||
This method should be run less frequently since this will fetch 2-4 queries per device. Currently only solarbank devices are supported, but it was noticed, that the energy statistics endpoint (maybe each endpoint) is limited to 25-30 queries per minute.
|
||||
|
||||
Check out `test_api.py` and other python executable tools that may help to leverage and explore the Api for your Anker power system.
|
||||
The subfolder [`examples`](https://github.com/thomluther/anker-solix-api/tree/main/examples) contains actual example exports with json files using anonymized responses of the `export_system.py` module giving you an idea of how various Api responses look like. (Note that the Solarbank was switched off when the data were pulled, so some fields may be empty)
|
||||
|
@ -116,6 +122,10 @@ Optionally you can specify whether personalized information in the response data
|
|||
You can review the response files afterwards. They can be used as examples for dedicated data extraction from the devices.
|
||||
Optionally the AnkerSolixApi class can use the json files for debugging and testing on various system outputs.
|
||||
|
||||
**Note**:
|
||||
|
||||
You should preferrably run the export_system with the owner account of the site. Otherwise only limited information can be exported by shared accounts due to access permissions.
|
||||
|
||||
## solarbank_monitor.py
|
||||
|
||||
```
|
||||
|
|
180
api/api.py
180
api/api.py
|
@ -149,11 +149,11 @@ _API_ENDPOINTS = {
|
|||
"compatible_process": "power_service/v1/app/compatible/get_compatible_process", # contains solar_info plus OTA processing codes, works only with owner account
|
||||
"get_device_fittings": "power_service/v1/app/get_relate_device_fittings", # Device fittings for given site id and device sn. Shows Accessories like Solarbank 0W Switch info
|
||||
"energy_analysis": "power_service/v1/site/energy_analysis", # Fetch energy data for given time frames
|
||||
"home_load_chart": "power_service/v1/site/get_home_load_chart", # Fetch data as displayed in home load chart for given site_id and optional device SN (empty if solarbank not connected)
|
||||
"home_load_chart": "power_service/v1/site/get_home_load_chart", # Fetch data as displayed in home load chart for schedule adjustments for given site_id and optional device SN (empty if solarbank not connected)
|
||||
"get_upgrade_record": "power_service/v1/app/get_upgrade_record", # get list of firmware update history
|
||||
"check_upgrade_record": "power_service/v1/app/check_upgrade_record", # show an upgrade record for the device, types 1-3 show different info, only works for owner account
|
||||
"get_message_unread": "power_service/v1/get_message_unread", # GET method to show if there are unread messages for account
|
||||
"get_message": "power_service/v1/get_message", # get list of max Messages from certain time, last_time format unknown
|
||||
"get_upgrade_record": "power_service/v1/app/get_upgrade_record", # get list of firmware update history
|
||||
"get_mqtt_info": "app/devicemanage/get_user_mqtt_info", # get mqtt server and certificates, not explored or used
|
||||
}
|
||||
|
||||
|
@ -184,7 +184,7 @@ _API_ENDPOINTS = {
|
|||
'power_service/v1/app/share_site/delete_inviting_member',
|
||||
'power_service/v1/app/share_site/get_invited_list',
|
||||
'power_service/v1/app/share_site/join_site',
|
||||
'power_service/v1/app/upgrade_event_report',
|
||||
'power_service/v1/app/upgrade_event_report', # post an entry to upgrade event report
|
||||
'power_service/v1/app/get_phonecode_list',
|
||||
'power_service/v1/get_message_not_disturb', # get do not disturb messages settings
|
||||
'power_service/v1/message_not_disturb', # change do not disurb messages settings
|
||||
|
@ -410,7 +410,9 @@ class RequestCounter:
|
|||
# limit the counter entries to 1 hour when adding new
|
||||
self.recycle()
|
||||
|
||||
def recycle(self, last_time: datetime = datetime.now() - timedelta(hours=1)) -> None:
|
||||
def recycle(
|
||||
self, last_time: datetime = datetime.now() - timedelta(hours=1)
|
||||
) -> None:
|
||||
"""Remove oldest timestamps from beginning of counter until last_time is reached, default is 1 hour ago."""
|
||||
self.elements = [x for x in self.elements if x > last_time]
|
||||
|
||||
|
@ -584,7 +586,7 @@ class AnkerSolixApi:
|
|||
self._logger.error(err)
|
||||
return {}
|
||||
|
||||
def _saveToFile(self, filename: str, data: dict = None) -> bool:
|
||||
def _saveToFile(self, filename: str, data: dict | None = None) -> bool:
|
||||
"""Save json data to given file for testing."""
|
||||
if self.mask_credentials:
|
||||
masked_filename = filename.replace(
|
||||
|
@ -625,9 +627,9 @@ class AnkerSolixApi:
|
|||
def _update_dev( # noqa: C901
|
||||
self,
|
||||
devData: dict,
|
||||
devType: str = None,
|
||||
siteId: str = None,
|
||||
isAdmin: bool = None,
|
||||
devType: str | None = None,
|
||||
siteId: str | None = None,
|
||||
isAdmin: bool | None = None,
|
||||
) -> str | None:
|
||||
"""Update the internal device details dictionary with the given data. The device_sn key must be set in the data dict for the update to be applied.
|
||||
|
||||
|
@ -858,7 +860,7 @@ class AnkerSolixApi:
|
|||
self.devices.update({str(sn): device})
|
||||
return sn
|
||||
|
||||
def testDir(self, subfolder: str = None) -> str:
|
||||
def testDir(self, subfolder: str | None = None) -> str:
|
||||
"""Get or set the subfolder for local API test files."""
|
||||
if not subfolder or subfolder == self._testdir:
|
||||
return self._testdir
|
||||
|
@ -869,35 +871,39 @@ class AnkerSolixApi:
|
|||
self._logger.info("Set Api test folder to: %s", subfolder)
|
||||
return self._testdir
|
||||
|
||||
def logLevel(self, level: int = None) -> int:
|
||||
def logLevel(self, level: int | None = None) -> int:
|
||||
"""Get or set the logger log level."""
|
||||
if level is not None and isinstance(level, int):
|
||||
self._logger.setLevel(level)
|
||||
self._logger.info("Set log level to: %s", level)
|
||||
return self._logger.getEffectiveLevel()
|
||||
|
||||
def requestDelay(self, delay: float = None) -> float:
|
||||
def requestDelay(self, delay: float | None = None) -> float:
|
||||
"""Get or set the api request delay in seconds."""
|
||||
if (
|
||||
delay is not None
|
||||
and isinstance(delay, (float,int))
|
||||
and delay != self._request_delay
|
||||
and isinstance(delay, (float, int))
|
||||
and float(delay) != float(self._request_delay)
|
||||
):
|
||||
self._request_delay = min(
|
||||
SolixDefaults.REQUEST_DELAY_MAX,
|
||||
max(SolixDefaults.REQUEST_DELAY_MIN, delay),
|
||||
self._request_delay = float(
|
||||
min(
|
||||
SolixDefaults.REQUEST_DELAY_MAX,
|
||||
max(SolixDefaults.REQUEST_DELAY_MIN, delay),
|
||||
)
|
||||
)
|
||||
self._logger.info(
|
||||
"Set api request delay to %.3f seconds", self._request_delay
|
||||
)
|
||||
return self._request_delay
|
||||
|
||||
async def _wait_delay(self, delay: float = None) -> None:
|
||||
async def _wait_delay(self, delay: float | None = None) -> None:
|
||||
"""Wait at least for the defined Api request delay or for the provided delay in seconds since the last request occured."""
|
||||
if delay is not None and isinstance(delay, (float,int)):
|
||||
delay = min(
|
||||
SolixDefaults.REQUEST_DELAY_MAX,
|
||||
max(SolixDefaults.REQUEST_DELAY_MIN, delay),
|
||||
if delay is not None and isinstance(delay, (float, int)):
|
||||
delay = float(
|
||||
min(
|
||||
SolixDefaults.REQUEST_DELAY_MAX,
|
||||
max(SolixDefaults.REQUEST_DELAY_MIN, delay),
|
||||
)
|
||||
)
|
||||
else:
|
||||
delay = self._request_delay
|
||||
|
@ -934,15 +940,13 @@ class AnkerSolixApi:
|
|||
"%s",
|
||||
self.mask_values(data, "user_id", "auth_token", "email", "geo_key"),
|
||||
)
|
||||
self._retry_attempt = (
|
||||
False # clear retry attempt to allow retry for authentication refresh
|
||||
)
|
||||
# clear retry attempt to allow retry for authentication refresh
|
||||
self._retry_attempt = False
|
||||
else:
|
||||
self._logger.debug("Fetching new Login credentials from server")
|
||||
now = datetime.now().astimezone()
|
||||
self._retry_attempt = (
|
||||
True # set retry attempt to avoid retry on failed authentication
|
||||
)
|
||||
# set retry attempt to avoid retry on failed authentication
|
||||
self._retry_attempt = True
|
||||
auth_resp = await self.request(
|
||||
"post",
|
||||
_API_LOGIN,
|
||||
|
@ -1089,7 +1093,7 @@ class AnkerSolixApi:
|
|||
# get first the body text for usage in error detail logging if necessary
|
||||
body_text = await resp.text()
|
||||
data = {}
|
||||
resp.raise_for_status() # any response status >= 400
|
||||
resp.raise_for_status() # any response status >= 400
|
||||
if (data := await resp.json(content_type=None)) and self.encrypt_body:
|
||||
# TODO(#70): Test and Support optional encryption for body
|
||||
# data dict has to be decoded when encrypted
|
||||
|
@ -1109,43 +1113,50 @@ class AnkerSolixApi:
|
|||
)
|
||||
else:
|
||||
self._logger.debug("Response Data: %s", data)
|
||||
self._retry_attempt = False # reset retry flag only when valid token received and not another login request
|
||||
# reset retry flag only when valid token received and not another login request
|
||||
self._retry_attempt = False
|
||||
|
||||
errors.raise_error(data) # check the Api response status code in the data
|
||||
# check the Api response status code in the data
|
||||
errors.raise_error(data)
|
||||
|
||||
# valid response at this point, mark login and return data
|
||||
self._loggedIn = True
|
||||
return data # noqa: TRY300
|
||||
|
||||
except (
|
||||
ClientError
|
||||
) as err: # Exception from ClientSession based on standard response status codes
|
||||
# Exception from ClientSession based on standard response status codes
|
||||
except ClientError as err:
|
||||
self._logger.error("Api Request Error: %s", err)
|
||||
self._logger.error("Response Text: %s", body_text)
|
||||
# Prepare data dict for Api error lookup
|
||||
if not data:
|
||||
data = {}
|
||||
if not hasattr(data,"code"):
|
||||
if not hasattr(data, "code"):
|
||||
data["code"] = resp.status
|
||||
if not hasattr(data,"msg"):
|
||||
if not hasattr(data, "msg"):
|
||||
data["msg"] = body_text
|
||||
if resp.status in [401,403]:
|
||||
if resp.status in [401, 403]:
|
||||
# Unauthorized or forbidden request
|
||||
if self._retry_attempt:
|
||||
errors.raise_error(data, prefix=f"Login failed for user {self._email}")
|
||||
# catch error if Api code not defined
|
||||
raise errors.AuthorizationError(
|
||||
f"Login failed for user {self._email}"
|
||||
) from err
|
||||
self._logger.warning("Login failed, retrying authentication")
|
||||
if await self.async_authenticate(restart=True):
|
||||
return await self.request(
|
||||
method, endpoint, headers=headers, json=json
|
||||
)
|
||||
self._logger.error("Re-Login failed for user %s", self._email)
|
||||
elif resp.status in [429]:
|
||||
# reattempt autentication with same credentials if cached token was kicked out
|
||||
# retry attempt is set if login response data were not cached to fail immediately
|
||||
if not self._retry_attempt:
|
||||
self._logger.warning("Login failed, retrying authentication")
|
||||
if await self.async_authenticate(restart=True):
|
||||
return await self.request(
|
||||
method, endpoint, headers=headers, json=json
|
||||
)
|
||||
self._logger.error("Re-Login failed for user %s", self._email)
|
||||
errors.raise_error(
|
||||
data, prefix=f"Login failed for user {self._email}"
|
||||
)
|
||||
# catch error if Api code not defined
|
||||
raise errors.AuthorizationError(
|
||||
f"Login failed for user {self._email}"
|
||||
) from err
|
||||
if resp.status in [429]:
|
||||
# Too Many Requests, add stats to message
|
||||
errors.raise_error(data, prefix=f"Too Many Requests: {self.request_count}")
|
||||
errors.raise_error(
|
||||
data, prefix=f"Too Many Requests: {self.request_count}"
|
||||
)
|
||||
else:
|
||||
# raise Anker Solix error if code is known
|
||||
errors.raise_error(data)
|
||||
|
@ -1349,7 +1360,7 @@ class AnkerSolixApi:
|
|||
return self.sites
|
||||
|
||||
async def update_site_details(
|
||||
self, fromFile: bool = False, exclude: set = None
|
||||
self, fromFile: bool = False, exclude: set | None = None
|
||||
) -> dict:
|
||||
"""Get the latest updates for additional site related details updated less frequently.
|
||||
|
||||
|
@ -1374,7 +1385,7 @@ class AnkerSolixApi:
|
|||
return self.sites
|
||||
|
||||
async def update_device_details(
|
||||
self, fromFile: bool = False, exclude: set = None
|
||||
self, fromFile: bool = False, exclude: set | None = None
|
||||
) -> dict:
|
||||
"""Get the latest updates for additional device info updated less frequently.
|
||||
|
||||
|
@ -1455,7 +1466,7 @@ class AnkerSolixApi:
|
|||
|
||||
return self.devices
|
||||
|
||||
async def update_device_energy(self, exclude: set = None) -> dict:
|
||||
async def update_device_energy(self, exclude: set | None = None) -> dict:
|
||||
"""Get the energy statistics for given device types from today and yesterday.
|
||||
|
||||
Yesterday energy will be queried only once if not available yet, but not updated in subsequent refreshes.
|
||||
|
@ -1850,7 +1861,11 @@ class AnkerSolixApi:
|
|||
return data
|
||||
|
||||
async def set_site_price(
|
||||
self, siteId: str, price: float = None, unit: str = None, co2: float = None
|
||||
self,
|
||||
siteId: str,
|
||||
price: float | None = None,
|
||||
unit: str | None = None,
|
||||
co2: float | None = None,
|
||||
) -> bool:
|
||||
"""Set the power price, the unit and/or CO2 for a site.
|
||||
|
||||
|
@ -1984,7 +1999,7 @@ class AnkerSolixApi:
|
|||
self,
|
||||
siteId: str,
|
||||
paramType: str = SolixParmType.SOLARBANK_SCHEDULE.value,
|
||||
deviceSn: str = None,
|
||||
deviceSn: str | None = None,
|
||||
fromFile: bool = False,
|
||||
) -> dict:
|
||||
r"""Get device parameters (e.g. solarbank schedule). This can be queried for each siteId listed in the homepage info site_list. The paramType is always 4, but can be modified if necessary.
|
||||
|
@ -2040,7 +2055,7 @@ class AnkerSolixApi:
|
|||
paramData: dict,
|
||||
paramType: str = SolixParmType.SOLARBANK_SCHEDULE.value,
|
||||
command: int = 17,
|
||||
deviceSn: str = None,
|
||||
deviceSn: str | None = None,
|
||||
) -> dict:
|
||||
"""Set device parameters (e.g. solarbank schedule).
|
||||
|
||||
|
@ -2073,9 +2088,9 @@ class AnkerSolixApi:
|
|||
siteId: str,
|
||||
deviceSn: str,
|
||||
all_day: bool = False,
|
||||
preset: int = None,
|
||||
export: bool = None,
|
||||
charge_prio: int = None,
|
||||
preset: int | None = None,
|
||||
export: bool | None = None,
|
||||
charge_prio: int | None = None,
|
||||
set_slot: SolarbankTimeslot = None,
|
||||
insert_slot: SolarbankTimeslot = None,
|
||||
) -> bool:
|
||||
|
@ -2611,10 +2626,10 @@ class AnkerSolixApi:
|
|||
)
|
||||
return resp.get("data", {})
|
||||
|
||||
async def get_upgrade_record(
|
||||
async def check_upgrade_record(
|
||||
self, recordType: int = 2, fromFile: bool = False
|
||||
) -> dict:
|
||||
"""Get upgrade record, shows device updates with their last version. Type 0-3 work.
|
||||
"""Check upgrade record, shows device updates with their last version. Type 0-3 work.
|
||||
|
||||
Example data:
|
||||
{"is_record": true,"device_list": [{
|
||||
|
@ -2624,7 +2639,7 @@ class AnkerSolixApi:
|
|||
data = {"type": recordType}
|
||||
if fromFile:
|
||||
resp = self._loadFromFile(
|
||||
os.path.join(self._testdir, f"upgrade_record_{recordType}.json")
|
||||
os.path.join(self._testdir, f"check_upgrade_record_{recordType}.json")
|
||||
)
|
||||
else:
|
||||
resp = await self.request(
|
||||
|
@ -2632,14 +2647,45 @@ class AnkerSolixApi:
|
|||
)
|
||||
return resp.get("data", {})
|
||||
|
||||
async def get_upgrade_record(
|
||||
self, deviceSn: str | None = None, siteId: str | None = None, recordType: int | None = None, fromFile: bool = False
|
||||
) -> dict:
|
||||
"""Get upgrade record for a device serial or site ID, shows update history. Type 1 works for solarbank, type 2 for site ID.
|
||||
|
||||
Example data:
|
||||
{"device_sn": "9JVB42LJK8J0P5RY", "site_id": "", "upgrade_record_list": [
|
||||
{"upgrade_time": "2024-02-29 12:38:23","upgrade_version": "v1.5.6","pre_version": "v1.4.4","upgrade_type": "1","upgrade_desc": "",
|
||||
"device_sn": "9JVB42LJK8J0P5RY","device_name": "9JVB42LJK8J0P5RY","child_upgrade_records": null},
|
||||
{"upgrade_time": "2023-12-29 10:23:06","upgrade_version": "v1.4.4","pre_version": "v0.0.6.6","upgrade_type": "1","upgrade_desc": "",
|
||||
"device_sn": "9JVB42LJK8J0P5RY","device_name": "9JVB42LJK8J0P5RY","child_upgrade_records": null},
|
||||
{"upgrade_time": "2023-11-02 13:43:09","upgrade_version": "v1.4.1","pre_version": "v0.0.6.5","upgrade_type": "1","upgrade_desc": "",
|
||||
"device_sn": "9JVB42LJK8J0P5RY","device_name": "9JVB42LJK8J0P5RY","child_upgrade_records": null}]},
|
||||
"""
|
||||
if deviceSn:
|
||||
data = {"device_sn": deviceSn, "type": 1 if recordType is None else recordType}
|
||||
elif siteId:
|
||||
data = {"site_id": siteId, "type": 2 if recordType is None else recordType}
|
||||
else:
|
||||
recordType = 0 if recordType is None else recordType
|
||||
data = {"type": recordType}
|
||||
if fromFile:
|
||||
resp = self._loadFromFile(
|
||||
os.path.join(self._testdir, f"get_upgrade_record_{deviceSn if deviceSn else siteId if siteId else recordType}.json")
|
||||
)
|
||||
else:
|
||||
resp = await self.request(
|
||||
"post", _API_ENDPOINTS["get_upgrade_record"], json=data
|
||||
)
|
||||
return resp.get("data", {})
|
||||
|
||||
async def energy_analysis(
|
||||
self,
|
||||
siteId: str,
|
||||
deviceSn: str,
|
||||
rangeType: str = None,
|
||||
startDay: datetime = None,
|
||||
endDay: datetime = None,
|
||||
devType: str = None,
|
||||
rangeType: str | None = None,
|
||||
startDay: datetime | None = None,
|
||||
endDay: datetime | None = None,
|
||||
devType: str | None = None,
|
||||
) -> dict:
|
||||
"""Fetch Energy data for given device and optional time frame.
|
||||
|
||||
|
@ -2770,7 +2816,7 @@ class AnkerSolixApi:
|
|||
table.update({daystr: entry})
|
||||
return table
|
||||
|
||||
async def home_load_chart(self, siteId: str, deviceSn: str = None) -> dict:
|
||||
async def home_load_chart(self, siteId: str, deviceSn: str | None = None) -> dict:
|
||||
"""Get home load chart data.
|
||||
|
||||
Example data:
|
||||
|
|
|
@ -75,6 +75,8 @@ ERRORS: dict[int, type[AnkerSolixError]] = {
|
|||
401: AuthorizationError,
|
||||
403: AuthorizationError,
|
||||
429: RequestLimitError,
|
||||
502: ConnectError,
|
||||
504: ConnectError,
|
||||
997: ConnectError,
|
||||
998: NetworkError,
|
||||
999: ServerError,
|
||||
|
|
|
@ -1,27 +1,27 @@
|
|||
{
|
||||
"DILM86K2GJV2NRAI": {
|
||||
"device_sn": "DILM86K2GJV2NRAI",
|
||||
"G55HAP9LVQO2LAPM": {
|
||||
"device_sn": "G55HAP9LVQO2LAPM",
|
||||
"type": "solarbank",
|
||||
"site_id": "bdd7d770-bcb7-d22b-cafc-d0b2eddc4e23",
|
||||
"site_id": "9e40dc42-adac-7fba-dead-a1bba7bff34e",
|
||||
"is_admin": true,
|
||||
"device_pn": "A17C0",
|
||||
"battery_soc": "6",
|
||||
"battery_soc": "17",
|
||||
"battery_capacity": "1600",
|
||||
"battery_energy": "96",
|
||||
"charging_power": "64",
|
||||
"battery_energy": "272",
|
||||
"charging_power": "-190",
|
||||
"power_unit": "W",
|
||||
"charging_status": "3",
|
||||
"charging_status_desc": "charge_bypass",
|
||||
"charging_status": "2",
|
||||
"charging_status_desc": "discharge",
|
||||
"status": "1",
|
||||
"status_desc": "online",
|
||||
"wireless_type": "1",
|
||||
"input_power": "154",
|
||||
"output_power": "90",
|
||||
"set_output_power": "0",
|
||||
"input_power": "0",
|
||||
"output_power": "190",
|
||||
"set_output_power": "300",
|
||||
"power_cutoff": 5,
|
||||
"alias": "SB E1600",
|
||||
"set_system_output_power": "0",
|
||||
"bt_ble_mac": "3CEAAD9FDDF7",
|
||||
"set_system_output_power": "300",
|
||||
"bt_ble_mac": "FEDCEB33AA9A",
|
||||
"name": "Solarbank E1600",
|
||||
"wifi_online": true,
|
||||
"charge": false,
|
||||
|
@ -29,7 +29,7 @@
|
|||
"sw_version": "v1.5.6",
|
||||
"auto_upgrade": false,
|
||||
"wifi_name": "wifi-network-1",
|
||||
"wifi_signal": "54",
|
||||
"wifi_signal": "50",
|
||||
"power_cutoff_data": [
|
||||
{
|
||||
"id": 1,
|
||||
|
@ -49,7 +49,7 @@
|
|||
"solar_info": {
|
||||
"solar_brand": "ANKER",
|
||||
"solar_model": "A5143",
|
||||
"solar_sn": "9XK0FGWY2TWW",
|
||||
"solar_sn": "YTZL4ZEJ4R5B",
|
||||
"solar_model_name": "MI80 Microinverter(BLE)"
|
||||
},
|
||||
"schedule": {
|
||||
|
@ -57,7 +57,7 @@
|
|||
{
|
||||
"id": 0,
|
||||
"start_time": "00:00",
|
||||
"end_time": "06:30",
|
||||
"end_time": "07:20",
|
||||
"turn_on": true,
|
||||
"appliance_loads": [
|
||||
{
|
||||
|
@ -71,21 +71,21 @@
|
|||
"power_setting_mode": 1,
|
||||
"device_power_loads": [
|
||||
{
|
||||
"device_sn": "DILM86K2GJV2NRAI",
|
||||
"device_sn": "G55HAP9LVQO2LAPM",
|
||||
"power": 150
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 0,
|
||||
"start_time": "06:30",
|
||||
"end_time": "18:30",
|
||||
"start_time": "07:20",
|
||||
"end_time": "19:40",
|
||||
"turn_on": false,
|
||||
"appliance_loads": [
|
||||
{
|
||||
"id": 0,
|
||||
"name": "Benutzerdefiniert",
|
||||
"power": 100,
|
||||
"power": 140,
|
||||
"number": 1
|
||||
}
|
||||
],
|
||||
|
@ -93,14 +93,14 @@
|
|||
"power_setting_mode": 1,
|
||||
"device_power_loads": [
|
||||
{
|
||||
"device_sn": "DILM86K2GJV2NRAI",
|
||||
"power": 50
|
||||
"device_sn": "G55HAP9LVQO2LAPM",
|
||||
"power": 70
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 0,
|
||||
"start_time": "18:30",
|
||||
"start_time": "19:40",
|
||||
"end_time": "24:00",
|
||||
"turn_on": true,
|
||||
"appliance_loads": [
|
||||
|
@ -115,7 +115,7 @@
|
|||
"power_setting_mode": 1,
|
||||
"device_power_loads": [
|
||||
{
|
||||
"device_sn": "DILM86K2GJV2NRAI",
|
||||
"device_sn": "G55HAP9LVQO2LAPM",
|
||||
"power": 150
|
||||
}
|
||||
]
|
||||
|
@ -130,8 +130,8 @@
|
|||
"display_advanced_mode": 0,
|
||||
"advanced_mode_min_load": 0
|
||||
},
|
||||
"preset_system_output_power": 100,
|
||||
"preset_allow_export": false,
|
||||
"preset_system_output_power": 300,
|
||||
"preset_allow_export": true,
|
||||
"preset_charge_priority": 10,
|
||||
"fittings": {}
|
||||
}
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
{
|
||||
"bdd7d770-bcb7-d22b-cafc-d0b2eddc4e23": {
|
||||
"9e40dc42-adac-7fba-dead-a1bba7bff34e": {
|
||||
"type": "system",
|
||||
"site_info": {
|
||||
"site_id": "bdd7d770-bcb7-d22b-cafc-d0b2eddc4e23",
|
||||
"site_id": "9e40dc42-adac-7fba-dead-a1bba7bff34e",
|
||||
"site_name": "BKW",
|
||||
"site_img": "",
|
||||
"device_type_list": [
|
||||
|
@ -36,17 +36,17 @@
|
|||
"statistics": [
|
||||
{
|
||||
"type": "1",
|
||||
"total": "135.13",
|
||||
"total": "183.54",
|
||||
"unit": "kwh"
|
||||
},
|
||||
{
|
||||
"type": "2",
|
||||
"total": "134.72",
|
||||
"total": "182.99",
|
||||
"unit": "kg"
|
||||
},
|
||||
{
|
||||
"type": "3",
|
||||
"total": "44.59",
|
||||
"total": "60.57",
|
||||
"unit": "\u20ac"
|
||||
}
|
||||
],
|
||||
|
@ -55,40 +55,40 @@
|
|||
"solarbank_list": [
|
||||
{
|
||||
"device_pn": "A17C0",
|
||||
"device_sn": "DILM86K2GJV2NRAI",
|
||||
"device_sn": "G55HAP9LVQO2LAPM",
|
||||
"device_img": "https://public-aiot-fra-prod.s3.dualstack.eu-central-1.amazonaws.com/anker-power/public/product/anker-power/e9478c2d-e665-4d84-95d7-dd4844f82055/20230719-144818.png",
|
||||
"battery_power": "6",
|
||||
"battery_power": "17",
|
||||
"bind_site_status": "",
|
||||
"charging_power": "64",
|
||||
"charging_power": "-190",
|
||||
"power_unit": "W",
|
||||
"charging_status": "3",
|
||||
"charging_status": "2",
|
||||
"status": "1",
|
||||
"wireless_type": "1",
|
||||
"main_version": "",
|
||||
"photovoltaic_power": "154",
|
||||
"output_power": "90",
|
||||
"photovoltaic_power": "0",
|
||||
"output_power": "190",
|
||||
"create_time": 1695392386,
|
||||
"set_load_power": "0",
|
||||
"set_load_power": "300",
|
||||
"output_cutoff_data": 5,
|
||||
"is_display": true,
|
||||
"alias_name": "SB E1600",
|
||||
"current_home_load": "0"
|
||||
"current_home_load": "300"
|
||||
}
|
||||
],
|
||||
"total_charging_power": "64",
|
||||
"total_charging_power": "-190.0",
|
||||
"power_unit": "W",
|
||||
"charging_status": "0",
|
||||
"total_battery_power": "0.06",
|
||||
"updated_time": "2024-03-27 11:49:04",
|
||||
"total_photovoltaic_power": "154",
|
||||
"total_output_power": "90.00",
|
||||
"total_battery_power": "0.17",
|
||||
"updated_time": "2024-04-24 01:33:08",
|
||||
"total_photovoltaic_power": "0",
|
||||
"total_output_power": "190.00",
|
||||
"display_set_power": false,
|
||||
"is_display_data": true
|
||||
},
|
||||
"retain_load": "0W",
|
||||
"retain_load": "300W",
|
||||
"updated_time": "01-01-0001 00:00:00",
|
||||
"power_site_type": 2,
|
||||
"site_id": "bdd7d770-bcb7-d22b-cafc-d0b2eddc4e23",
|
||||
"site_id": "9e40dc42-adac-7fba-dead-a1bba7bff34e",
|
||||
"powerpanel_list": [],
|
||||
"site_details": {
|
||||
"has_unread_msg": false,
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
"main_switch": true,
|
||||
"device_list": [
|
||||
{
|
||||
"device_sn": "DILM86K2GJV2NRAI",
|
||||
"device_sn": "G55HAP9LVQO2LAPM",
|
||||
"device_name": "Solarbank E1600",
|
||||
"auto_upgrade": false,
|
||||
"alias_name": "SB E1600",
|
||||
|
@ -13,5 +13,5 @@
|
|||
}
|
||||
]
|
||||
},
|
||||
"trace_id": "145dab32b452d3db2a81ce9506ee4010"
|
||||
"trace_id": "c6f8bd0ae3dd9eb8b3dbd8c84d2afba5"
|
||||
}
|
|
@ -4,10 +4,10 @@
|
|||
"data": {
|
||||
"data": [
|
||||
{
|
||||
"device_sn": "DILM86K2GJV2NRAI",
|
||||
"device_sn": "G55HAP9LVQO2LAPM",
|
||||
"product_code": "A17C0",
|
||||
"bt_ble_id": "3C:EA:AD:9F:DD:F7",
|
||||
"bt_ble_mac": "3CEAAD9FDDF7",
|
||||
"bt_ble_id": "FE:DC:EB:33:AA:9A",
|
||||
"bt_ble_mac": "FEDCEB33AA9A",
|
||||
"device_name": "Solarbank E1600",
|
||||
"alias_name": "SB E1600",
|
||||
"img_url": "https://public-aiot-fra-prod.s3.dualstack.eu-central-1.amazonaws.com/anker-power/public/product/anker-power/e9478c2d-e665-4d84-95d7-dd4844f82055/20230719-144818.png",
|
||||
|
@ -26,5 +26,5 @@
|
|||
}
|
||||
]
|
||||
},
|
||||
"trace_id": "acff073fd7e842994b11373fd1d3449e"
|
||||
"trace_id": "8fed8fc1bba815a1efa844983cecef9e"
|
||||
}
|
|
@ -5,5 +5,5 @@
|
|||
"device_list": null,
|
||||
"guide_txt": ""
|
||||
},
|
||||
"trace_id": "c55301e2c5686c8d75bf494a91d804b6"
|
||||
"trace_id": "ded0b8ad19ffe254edcf876c633e4735"
|
||||
}
|
|
@ -5,7 +5,7 @@
|
|||
"ota_complete_status": 2,
|
||||
"process_skip_type": 2,
|
||||
"solar_info": {
|
||||
"solar_sn": "9XK0FGWY2TWW",
|
||||
"solar_sn": "YTZL4ZEJ4R5B",
|
||||
"solar_brand": "ANKER",
|
||||
"solar_model": "A5143",
|
||||
"brand_id": "3a9930f5-74ef-4e41-a797-04e6b33d3f0f",
|
||||
|
@ -15,5 +15,5 @@
|
|||
"solar_model_name": "MI80 Microinverter(BLE)"
|
||||
}
|
||||
},
|
||||
"trace_id": "7dbfd7538ad24749c1abfcf0fd93de5f"
|
||||
"trace_id": "077b4ed2b6fccac0a9dfcf8e4fdd85a6"
|
||||
}
|
|
@ -4,5 +4,5 @@
|
|||
"data": {
|
||||
"data": []
|
||||
},
|
||||
"trace_id": "823ef9f8a8c4fbdbe7e4abd4232f79c4"
|
||||
"trace_id": "a433dc2acdb4187e340cff4d6bb60f3c"
|
||||
}
|
|
@ -2,11 +2,11 @@
|
|||
"code": 0,
|
||||
"msg": "success!",
|
||||
"data": {
|
||||
"site_id": "bdd7d770-bcb7-d22b-cafc-d0b2eddc4e23",
|
||||
"home_load_data": "{\"ranges\":[{\"id\":0,\"start_time\":\"00:00\",\"end_time\":\"06:30\",\"turn_on\":true,\"appliance_loads\":[{\"id\":0,\"name\":\"Benutzerdefiniert\",\"power\":300,\"number\":1}],\"charge_priority\":10,\"power_setting_mode\":1,\"device_power_loads\":[{\"device_sn\":\"DILM86K2GJV2NRAI\",\"power\":150}]},{\"id\":0,\"start_time\":\"06:30\",\"end_time\":\"18:30\",\"turn_on\":false,\"appliance_loads\":[{\"id\":0,\"name\":\"Benutzerdefiniert\",\"power\":100,\"number\":1}],\"charge_priority\":10,\"power_setting_mode\":1,\"device_power_loads\":[{\"device_sn\":\"DILM86K2GJV2NRAI\",\"power\":50}]},{\"id\":0,\"start_time\":\"18:30\",\"end_time\":\"24:00\",\"turn_on\":true,\"appliance_loads\":[{\"id\":0,\"name\":\"Benutzerdefiniert\",\"power\":300,\"number\":1}],\"charge_priority\":10,\"power_setting_mode\":1,\"device_power_loads\":[{\"device_sn\":\"DILM86K2GJV2NRAI\",\"power\":150}]}],\"min_load\":100,\"max_load\":800,\"step\":0,\"is_charge_priority\":1,\"default_charge_priority\":80,\"is_zero_output_tips\":0,\"display_advanced_mode\":0,\"advanced_mode_min_load\":0}",
|
||||
"current_home_load": "0W",
|
||||
"site_id": "9e40dc42-adac-7fba-dead-a1bba7bff34e",
|
||||
"home_load_data": "{\"ranges\":[{\"id\":0,\"start_time\":\"00:00\",\"end_time\":\"07:20\",\"turn_on\":true,\"appliance_loads\":[{\"id\":0,\"name\":\"Benutzerdefiniert\",\"power\":300,\"number\":1}],\"charge_priority\":10,\"power_setting_mode\":1,\"device_power_loads\":[{\"device_sn\":\"G55HAP9LVQO2LAPM\",\"power\":150}]},{\"id\":0,\"start_time\":\"07:20\",\"end_time\":\"19:40\",\"turn_on\":false,\"appliance_loads\":[{\"id\":0,\"name\":\"Benutzerdefiniert\",\"power\":140,\"number\":1}],\"charge_priority\":10,\"power_setting_mode\":1,\"device_power_loads\":[{\"device_sn\":\"G55HAP9LVQO2LAPM\",\"power\":70}]},{\"id\":0,\"start_time\":\"19:40\",\"end_time\":\"24:00\",\"turn_on\":true,\"appliance_loads\":[{\"id\":0,\"name\":\"Benutzerdefiniert\",\"power\":300,\"number\":1}],\"charge_priority\":10,\"power_setting_mode\":1,\"device_power_loads\":[{\"device_sn\":\"G55HAP9LVQO2LAPM\",\"power\":150}]}],\"min_load\":100,\"max_load\":800,\"step\":0,\"is_charge_priority\":1,\"default_charge_priority\":80,\"is_zero_output_tips\":0,\"display_advanced_mode\":0,\"advanced_mode_min_load\":0}",
|
||||
"current_home_load": "300W",
|
||||
"parallel_home_load": "",
|
||||
"parallel_display": false
|
||||
},
|
||||
"trace_id": "adbafcecd3cac7f6c9feda1bcbab7aea"
|
||||
"trace_id": "ece0447b2ec3c1520e8ca17c1a4ce63f"
|
||||
}
|
|
@ -2,7 +2,7 @@
|
|||
"code": 0,
|
||||
"msg": "success!",
|
||||
"data": {
|
||||
"param_data": "{\"ranges\":[{\"id\":0,\"start_time\":\"00:00\",\"end_time\":\"06:30\",\"turn_on\":true,\"appliance_loads\":[{\"id\":0,\"name\":\"Benutzerdefiniert\",\"power\":300,\"number\":1}],\"charge_priority\":10,\"power_setting_mode\":1,\"device_power_loads\":[{\"device_sn\":\"DILM86K2GJV2NRAI\",\"power\":150}]},{\"id\":0,\"start_time\":\"06:30\",\"end_time\":\"18:30\",\"turn_on\":false,\"appliance_loads\":[{\"id\":0,\"name\":\"Benutzerdefiniert\",\"power\":100,\"number\":1}],\"charge_priority\":10,\"power_setting_mode\":1,\"device_power_loads\":[{\"device_sn\":\"DILM86K2GJV2NRAI\",\"power\":50}]},{\"id\":0,\"start_time\":\"18:30\",\"end_time\":\"24:00\",\"turn_on\":true,\"appliance_loads\":[{\"id\":0,\"name\":\"Benutzerdefiniert\",\"power\":300,\"number\":1}],\"charge_priority\":10,\"power_setting_mode\":1,\"device_power_loads\":[{\"device_sn\":\"DILM86K2GJV2NRAI\",\"power\":150}]}],\"min_load\":100,\"max_load\":800,\"step\":0,\"is_charge_priority\":1,\"default_charge_priority\":80,\"is_zero_output_tips\":0,\"display_advanced_mode\":0,\"advanced_mode_min_load\":0}"
|
||||
"param_data": "{\"ranges\":[{\"id\":0,\"start_time\":\"00:00\",\"end_time\":\"07:20\",\"turn_on\":true,\"appliance_loads\":[{\"id\":0,\"name\":\"Benutzerdefiniert\",\"power\":300,\"number\":1}],\"charge_priority\":10,\"power_setting_mode\":1,\"device_power_loads\":[{\"device_sn\":\"G55HAP9LVQO2LAPM\",\"power\":150}]},{\"id\":0,\"start_time\":\"07:20\",\"end_time\":\"19:40\",\"turn_on\":false,\"appliance_loads\":[{\"id\":0,\"name\":\"Benutzerdefiniert\",\"power\":140,\"number\":1}],\"charge_priority\":10,\"power_setting_mode\":1,\"device_power_loads\":[{\"device_sn\":\"G55HAP9LVQO2LAPM\",\"power\":70}]},{\"id\":0,\"start_time\":\"19:40\",\"end_time\":\"24:00\",\"turn_on\":true,\"appliance_loads\":[{\"id\":0,\"name\":\"Benutzerdefiniert\",\"power\":300,\"number\":1}],\"charge_priority\":10,\"power_setting_mode\":1,\"device_power_loads\":[{\"device_sn\":\"G55HAP9LVQO2LAPM\",\"power\":150}]}],\"min_load\":100,\"max_load\":800,\"step\":0,\"is_charge_priority\":1,\"default_charge_priority\":80,\"is_zero_output_tips\":0,\"display_advanced_mode\":0,\"advanced_mode_min_load\":0}"
|
||||
},
|
||||
"trace_id": "2b3a35dbdeede8bbff7fba8dfeb0a3ff"
|
||||
"trace_id": "87feb1cf10c4cb773ea4deb7f8eacb84"
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
{
|
||||
"code": 0,
|
||||
"msg": "success!",
|
||||
"data": {
|
||||
"device_sn": "",
|
||||
"site_id": "9e40dc42-adac-7fba-dead-a1bba7bff34e",
|
||||
"upgrade_record_list": [
|
||||
{
|
||||
"upgrade_time": "2024-02-29 10:48:23",
|
||||
"upgrade_version": "v1.5.6",
|
||||
"pre_version": "v1.4.4",
|
||||
"upgrade_type": "1",
|
||||
"upgrade_desc": "",
|
||||
"device_sn": "G55HAP9LVQO2LAPM",
|
||||
"device_name": "G55HAP9LVQO2LAPM",
|
||||
"child_upgrade_records": null
|
||||
},
|
||||
{
|
||||
"upgrade_time": "2023-12-28 15:57:06",
|
||||
"upgrade_version": "v1.4.4",
|
||||
"pre_version": "v0.0.6.6",
|
||||
"upgrade_type": "1",
|
||||
"upgrade_desc": "",
|
||||
"device_sn": "G55HAP9LVQO2LAPM",
|
||||
"device_name": "G55HAP9LVQO2LAPM",
|
||||
"child_upgrade_records": null
|
||||
},
|
||||
{
|
||||
"upgrade_time": "2023-11-01 09:53:09",
|
||||
"upgrade_version": "v1.4.1",
|
||||
"pre_version": "v0.0.6.5",
|
||||
"upgrade_type": "1",
|
||||
"upgrade_desc": "",
|
||||
"device_sn": "G55HAP9LVQO2LAPM",
|
||||
"device_name": "G55HAP9LVQO2LAPM",
|
||||
"child_upgrade_records": null
|
||||
}
|
||||
]
|
||||
},
|
||||
"trace_id": "9b3f6a74d73cbede9dcd7e3fa9f3c6de"
|
||||
}
|
|
@ -4,7 +4,7 @@
|
|||
"data": {
|
||||
"site_list": [
|
||||
{
|
||||
"site_id": "bdd7d770-bcb7-d22b-cafc-d0b2eddc4e23",
|
||||
"site_id": "9e40dc42-adac-7fba-dead-a1bba7bff34e",
|
||||
"site_name": "BKW",
|
||||
"site_img": "",
|
||||
"device_type_list": [
|
||||
|
@ -21,10 +21,10 @@
|
|||
"solarbank_list": [
|
||||
{
|
||||
"device_pn": "",
|
||||
"device_sn": "DILM86K2GJV2NRAI",
|
||||
"device_sn": "G55HAP9LVQO2LAPM",
|
||||
"device_name": "SB E1600",
|
||||
"device_img": "https://public-aiot-fra-prod.s3.dualstack.eu-central-1.amazonaws.com/anker-power/public/product/anker-power/e9478c2d-e665-4d84-95d7-dd4844f82055/20230719-144818.png",
|
||||
"battery_power": "6",
|
||||
"battery_power": "17",
|
||||
"bind_site_status": "1",
|
||||
"charging_power": "",
|
||||
"power_unit": "",
|
||||
|
@ -42,5 +42,5 @@
|
|||
],
|
||||
"powerpanel_list": []
|
||||
},
|
||||
"trace_id": "ae8abfd363866b4cee17359fd1cd9dca"
|
||||
"trace_id": "d0ee3c49869cec32cf6cf251521f29dd"
|
||||
}
|
|
@ -4,5 +4,5 @@
|
|||
"data": {
|
||||
"has_unread_msg": false
|
||||
},
|
||||
"trace_id": "aec8da3373cd0e59d9db5eb52e7adbdc"
|
||||
"trace_id": "a058ab399db309214abaabf12ddccaad"
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
{
|
||||
"code": 0,
|
||||
"msg": "success!",
|
||||
"data": {
|
||||
"is_ota_update": false,
|
||||
"need_retry": false,
|
||||
"retry_interval": 0,
|
||||
"device_list": null
|
||||
},
|
||||
"trace_id": "2dcfae1f2f344d0d97ac24d74ef8fba9"
|
||||
}
|
|
@ -19,5 +19,5 @@
|
|||
}
|
||||
]
|
||||
},
|
||||
"trace_id": "7a84aead0e446bbc85fc76ebbcb2bc01"
|
||||
"trace_id": "8bb7ef048b69dcff25ae45bc3ec3fad2"
|
||||
}
|
|
@ -2,10 +2,10 @@
|
|||
"code": 0,
|
||||
"msg": "success!",
|
||||
"data": {
|
||||
"site_id": "bdd7d770-bcb7-d22b-cafc-d0b2eddc4e23",
|
||||
"site_id": "9e40dc42-adac-7fba-dead-a1bba7bff34e",
|
||||
"price": 0.33,
|
||||
"site_co2": 0,
|
||||
"site_price_unit": "\u20ac"
|
||||
},
|
||||
"trace_id": "7cfacbfca09e909cf9a8939ebddba1ac"
|
||||
"trace_id": "5aae22e5adc38af4b9fdc37aa1f89cdb"
|
||||
}
|
|
@ -20,17 +20,17 @@
|
|||
"statistics": [
|
||||
{
|
||||
"type": "1",
|
||||
"total": "135.13",
|
||||
"total": "183.54",
|
||||
"unit": "kwh"
|
||||
},
|
||||
{
|
||||
"type": "2",
|
||||
"total": "134.72",
|
||||
"total": "182.99",
|
||||
"unit": "kg"
|
||||
},
|
||||
{
|
||||
"type": "3",
|
||||
"total": "44.59",
|
||||
"total": "60.57",
|
||||
"unit": "\u20ac"
|
||||
}
|
||||
],
|
||||
|
@ -39,40 +39,40 @@
|
|||
"solarbank_list": [
|
||||
{
|
||||
"device_pn": "A17C0",
|
||||
"device_sn": "DILM86K2GJV2NRAI",
|
||||
"device_sn": "G55HAP9LVQO2LAPM",
|
||||
"device_name": "SB E1600",
|
||||
"device_img": "https://public-aiot-fra-prod.s3.dualstack.eu-central-1.amazonaws.com/anker-power/public/product/anker-power/e9478c2d-e665-4d84-95d7-dd4844f82055/20230719-144818.png",
|
||||
"battery_power": "6",
|
||||
"battery_power": "17",
|
||||
"bind_site_status": "",
|
||||
"charging_power": "90",
|
||||
"charging_power": "190",
|
||||
"power_unit": "W",
|
||||
"charging_status": "3",
|
||||
"charging_status": "2",
|
||||
"status": "1",
|
||||
"wireless_type": "1",
|
||||
"main_version": "",
|
||||
"photovoltaic_power": "154",
|
||||
"output_power": "90",
|
||||
"photovoltaic_power": "0",
|
||||
"output_power": "190",
|
||||
"create_time": 1695392386,
|
||||
"set_load_power": "",
|
||||
"output_cutoff_data": 5,
|
||||
"is_display": true
|
||||
}
|
||||
],
|
||||
"total_charging_power": "64",
|
||||
"total_charging_power": "0",
|
||||
"power_unit": "W",
|
||||
"charging_status": "0",
|
||||
"total_battery_power": "0.06",
|
||||
"updated_time": "2024-03-27 11:49:04",
|
||||
"total_photovoltaic_power": "154",
|
||||
"total_output_power": "90.00",
|
||||
"total_battery_power": "0.17",
|
||||
"updated_time": "2024-04-24 01:33:08",
|
||||
"total_photovoltaic_power": "0",
|
||||
"total_output_power": "190.00",
|
||||
"display_set_power": false,
|
||||
"is_display_data": true
|
||||
},
|
||||
"retain_load": "0W",
|
||||
"retain_load": "300W",
|
||||
"updated_time": "01-01-0001 00:00:00",
|
||||
"power_site_type": 2,
|
||||
"site_id": "bdd7d770-bcb7-d22b-cafc-d0b2eddc4e23",
|
||||
"site_id": "9e40dc42-adac-7fba-dead-a1bba7bff34e",
|
||||
"powerpanel_list": []
|
||||
},
|
||||
"trace_id": "e6a2f3f712a7c2498a25ae7c0bafef3e"
|
||||
"trace_id": "64eeaeab8d80ee6bab68bf981b9cda1d"
|
||||
}
|
|
@ -3,7 +3,7 @@
|
|||
"msg": "success!",
|
||||
"data": {
|
||||
"site_info": {
|
||||
"site_id": "bdd7d770-bcb7-d22b-cafc-d0b2eddc4e23",
|
||||
"site_id": "9e40dc42-adac-7fba-dead-a1bba7bff34e",
|
||||
"site_name": "BKW",
|
||||
"site_img": "",
|
||||
"device_type_list": [
|
||||
|
@ -23,7 +23,7 @@
|
|||
"solarbank_list": [
|
||||
{
|
||||
"device_pn": "A17C0",
|
||||
"device_sn": "DILM86K2GJV2NRAI",
|
||||
"device_sn": "G55HAP9LVQO2LAPM",
|
||||
"device_name": "SB E1600",
|
||||
"device_img": "https://public-aiot-fra-prod.s3.dualstack.eu-central-1.amazonaws.com/anker-power/public/product/anker-power/e9478c2d-e665-4d84-95d7-dd4844f82055/20230719-144818.png",
|
||||
"battery_power": "",
|
||||
|
@ -44,5 +44,5 @@
|
|||
],
|
||||
"powerpanel_list": []
|
||||
},
|
||||
"trace_id": "84caedc36bfe9b3f0ed1c89ceb5c0ee2"
|
||||
"trace_id": "daee0eafcbcefbcafc9a0adb3519ea6f"
|
||||
}
|
|
@ -4,7 +4,7 @@
|
|||
"data": {
|
||||
"site_list": [
|
||||
{
|
||||
"site_id": "bdd7d770-bcb7-d22b-cafc-d0b2eddc4e23",
|
||||
"site_id": "9e40dc42-adac-7fba-dead-a1bba7bff34e",
|
||||
"site_name": "BKW",
|
||||
"site_img": "",
|
||||
"device_type_list": [
|
||||
|
@ -21,5 +21,5 @@
|
|||
}
|
||||
]
|
||||
},
|
||||
"trace_id": "b07e6dfde00e7ddfb8fa4b3a2b76ac4c"
|
||||
"trace_id": "fe909473eeb4bd6ba428b4fda59aab74"
|
||||
}
|
|
@ -142,5 +142,5 @@
|
|||
}
|
||||
]
|
||||
},
|
||||
"trace_id": "d3dceb28da9a8f8ca2fb2ae170dc186a"
|
||||
"trace_id": "f8acb37f7d12aeabe646cbd5055ebabf"
|
||||
}
|
|
@ -5,8 +5,8 @@
|
|||
"brand_id": "3a9930f5-74ef-4e41-a797-04e6b33d3f0f",
|
||||
"solar_brand": "ANKER",
|
||||
"solar_model": "A5143",
|
||||
"solar_sn": "9XK0FGWY2TWW",
|
||||
"solar_sn": "YTZL4ZEJ4R5B",
|
||||
"solar_model_name": "MI80 Microinverter(BLE)"
|
||||
},
|
||||
"trace_id": "4fddaabaeaabd39fea6d7c474fb97ad2"
|
||||
"trace_id": "6b2dfaebd1abe931bcec042c1686fc4f"
|
||||
}
|
|
@ -7,7 +7,7 @@
|
|||
"solarbank_list": [
|
||||
{
|
||||
"device_pn": "A17C0",
|
||||
"device_sn": "DILM86K2GJV2NRAI",
|
||||
"device_sn": "G55HAP9LVQO2LAPM",
|
||||
"device_name": "SB E1600",
|
||||
"device_img": "https://public-aiot-fra-prod.s3.dualstack.eu-central-1.amazonaws.com/anker-power/public/product/anker-power/e9478c2d-e665-4d84-95d7-dd4844f82055/20230719-144818.png",
|
||||
"battery_power": "",
|
||||
|
@ -28,5 +28,5 @@
|
|||
],
|
||||
"powerpanel_list": []
|
||||
},
|
||||
"trace_id": "3607fa3ff416d93ded855c2ee9b10bea"
|
||||
"trace_id": "4acd422dafadcb3f2ccabbe1fb92ceda"
|
||||
}
|
|
@ -5,9 +5,9 @@
|
|||
"wifi_info_list": [
|
||||
{
|
||||
"wifi_name": "wifi-network-1",
|
||||
"wifi_signal": "54"
|
||||
"wifi_signal": "50"
|
||||
}
|
||||
]
|
||||
},
|
||||
"trace_id": "38ec9a1a8d29dac96ac9adc12fbb41b8"
|
||||
"trace_id": "af7beabd3e81cfb62be0f4abf96298fb"
|
||||
}
|
|
@ -50,7 +50,8 @@ def randomize(val, key: str = "") -> str:
|
|||
if not RANDOMIZE:
|
||||
return str(val)
|
||||
randomstr = RANDOMDATA.get(val, "")
|
||||
if not randomstr and val:
|
||||
# generate new random string
|
||||
if not randomstr and val and key not in ["device_name"]:
|
||||
if "_sn" in key or key in ["sn"]:
|
||||
randomstr = "".join(
|
||||
random.choices(string.ascii_uppercase + string.digits, k=len(val))
|
||||
|
@ -68,7 +69,7 @@ def randomize(val, key: str = "") -> str:
|
|||
if ":" in val:
|
||||
RANDOMDATA.update({temp: randomstr}) # save also key value without :
|
||||
randomstr = ":".join(
|
||||
a + b for a, b in zip(randomstr[::2], randomstr[1::2])
|
||||
a + b for a, b in zip(randomstr[::2], randomstr[1::2], strict=False)
|
||||
)
|
||||
elif "_id" in key:
|
||||
for part in val.split("-"):
|
||||
|
@ -102,7 +103,7 @@ def randomize(val, key: str = "") -> str:
|
|||
# default randomize format
|
||||
randomstr = "".join(random.choices(string.ascii_letters, k=len(val)))
|
||||
RANDOMDATA.update({val: randomstr})
|
||||
return randomstr
|
||||
return randomstr or str(val)
|
||||
|
||||
|
||||
def check_keys(data):
|
||||
|
@ -125,6 +126,7 @@ def check_keys(data):
|
|||
"wifi_name",
|
||||
"home_load_data",
|
||||
"param_data",
|
||||
"device_name"
|
||||
]
|
||||
) or k in ["sn"]:
|
||||
data[k] = randomize(v, k)
|
||||
|
@ -133,7 +135,7 @@ def check_keys(data):
|
|||
|
||||
def export(
|
||||
filename: str,
|
||||
d: dict = None,
|
||||
d: dict | None = None,
|
||||
skip_randomize: bool = False,
|
||||
randomkeys: bool = False,
|
||||
) -> None:
|
||||
|
@ -329,6 +331,22 @@ async def main() -> bool: # noqa: C901 # pylint: disable=too-many-branches,too-
|
|||
except (ClientError, errors.AnkerSolixError):
|
||||
if not admin:
|
||||
CONSOLE.warning("Query requires account of site owner!")
|
||||
CONSOLE.info("Exporting site upgrade record...")
|
||||
try:
|
||||
export(
|
||||
os.path.join(
|
||||
folder,
|
||||
f"get_upgrade_record_{randomize(siteId,'site_id')}.json",
|
||||
),
|
||||
await myapi.request(
|
||||
"post",
|
||||
api._API_ENDPOINTS["get_upgrade_record"],
|
||||
json={"site_id": siteId, "type": 2},
|
||||
),
|
||||
) # works only for site owners
|
||||
except (ClientError, errors.AnkerSolixError):
|
||||
if not admin:
|
||||
CONSOLE.warning("Query requires account of site owner!")
|
||||
for sn, device in myapi.devices.items():
|
||||
CONSOLE.info(
|
||||
"\nExporting device specific data for device %s SN %s...",
|
||||
|
@ -414,6 +432,19 @@ async def main() -> bool: # noqa: C901 # pylint: disable=too-many-branches,too-
|
|||
except (ClientError, errors.AnkerSolixError):
|
||||
if not admin:
|
||||
CONSOLE.warning("Query requires account of site owner!")
|
||||
CONSOLE.info("Exporting OTA update info...")
|
||||
try:
|
||||
export(
|
||||
os.path.join(folder, f"ota_update_{randomize(sn,'_sn')}.json"),
|
||||
await myapi.request(
|
||||
"post",
|
||||
api._API_ENDPOINTS["get_ota_update"],
|
||||
json={"device_sn": sn, "insert_sn": ""},
|
||||
),
|
||||
) # works only for site owners
|
||||
except (ClientError, errors.AnkerSolixError):
|
||||
if not admin:
|
||||
CONSOLE.warning("Query requires account of site owner!")
|
||||
|
||||
CONSOLE.info("\nExporting site rules...")
|
||||
export(
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
[project]
|
||||
name = "Anker-Solix-Api"
|
||||
version = "1.8.0"
|
||||
version = "1.8.1"
|
||||
description = "Python library for Anker Solix Power devices (Solarbank, Inverter etc)"
|
||||
readme = "README.md"
|
||||
requires-python = ">=3.11"
|
||||
|
|
Loading…
Reference in New Issue