This commit is contained in:
Thomas Luther 2024-04-24 12:22:21 +02:00
parent 2afab331db
commit b41590e888
25 changed files with 301 additions and 170 deletions

View File

@ -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 "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 "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 "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 "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_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_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 "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/delete_inviting_member',
'power_service/v1/app/share_site/get_invited_list', 'power_service/v1/app/share_site/get_invited_list',
'power_service/v1/app/share_site/join_site', '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/app/get_phonecode_list',
'power_service/v1/get_message_not_disturb', # get do not disturb messages settings '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 '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 # limit the counter entries to 1 hour when adding new
self.recycle() 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.""" """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] self.elements = [x for x in self.elements if x > last_time]
@ -584,7 +586,7 @@ class AnkerSolixApi:
self._logger.error(err) self._logger.error(err)
return {} 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.""" """Save json data to given file for testing."""
if self.mask_credentials: if self.mask_credentials:
masked_filename = filename.replace( masked_filename = filename.replace(
@ -625,9 +627,9 @@ class AnkerSolixApi:
def _update_dev( # noqa: C901 def _update_dev( # noqa: C901
self, self,
devData: dict, devData: dict,
devType: str = None, devType: str | None = None,
siteId: str = None, siteId: str | None = None,
isAdmin: bool = None, isAdmin: bool | None = None,
) -> str | 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. """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}) self.devices.update({str(sn): device})
return sn 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.""" """Get or set the subfolder for local API test files."""
if not subfolder or subfolder == self._testdir: if not subfolder or subfolder == self._testdir:
return self._testdir return self._testdir
@ -869,35 +871,39 @@ class AnkerSolixApi:
self._logger.info("Set Api test folder to: %s", subfolder) self._logger.info("Set Api test folder to: %s", subfolder)
return self._testdir 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.""" """Get or set the logger log level."""
if level is not None and isinstance(level, int): if level is not None and isinstance(level, int):
self._logger.setLevel(level) self._logger.setLevel(level)
self._logger.info("Set log level to: %s", level) self._logger.info("Set log level to: %s", level)
return self._logger.getEffectiveLevel() 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.""" """Get or set the api request delay in seconds."""
if ( if (
delay is not None delay is not None
and isinstance(delay, (float,int)) and isinstance(delay, (float, int))
and delay != self._request_delay and float(delay) != float(self._request_delay)
): ):
self._request_delay = min( self._request_delay = float(
SolixDefaults.REQUEST_DELAY_MAX, min(
max(SolixDefaults.REQUEST_DELAY_MIN, delay), SolixDefaults.REQUEST_DELAY_MAX,
max(SolixDefaults.REQUEST_DELAY_MIN, delay),
)
) )
self._logger.info( self._logger.info(
"Set api request delay to %.3f seconds", self._request_delay "Set api request delay to %.3f seconds", self._request_delay
) )
return 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.""" """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)): if delay is not None and isinstance(delay, (float, int)):
delay = min( delay = float(
SolixDefaults.REQUEST_DELAY_MAX, min(
max(SolixDefaults.REQUEST_DELAY_MIN, delay), SolixDefaults.REQUEST_DELAY_MAX,
max(SolixDefaults.REQUEST_DELAY_MIN, delay),
)
) )
else: else:
delay = self._request_delay delay = self._request_delay
@ -934,15 +940,13 @@ class AnkerSolixApi:
"%s", "%s",
self.mask_values(data, "user_id", "auth_token", "email", "geo_key"), self.mask_values(data, "user_id", "auth_token", "email", "geo_key"),
) )
self._retry_attempt = ( # clear retry attempt to allow retry for authentication refresh
False # clear retry attempt to allow retry for authentication refresh self._retry_attempt = False
)
else: else:
self._logger.debug("Fetching new Login credentials from server") self._logger.debug("Fetching new Login credentials from server")
now = datetime.now().astimezone() now = datetime.now().astimezone()
self._retry_attempt = ( # set retry attempt to avoid retry on failed authentication
True # set retry attempt to avoid retry on failed authentication self._retry_attempt = True
)
auth_resp = await self.request( auth_resp = await self.request(
"post", "post",
_API_LOGIN, _API_LOGIN,
@ -1089,7 +1093,7 @@ class AnkerSolixApi:
# get first the body text for usage in error detail logging if necessary # get first the body text for usage in error detail logging if necessary
body_text = await resp.text() body_text = await resp.text()
data = {} 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: if (data := await resp.json(content_type=None)) and self.encrypt_body:
# TODO(#70): Test and Support optional encryption for body # TODO(#70): Test and Support optional encryption for body
# data dict has to be decoded when encrypted # data dict has to be decoded when encrypted
@ -1109,43 +1113,50 @@ class AnkerSolixApi:
) )
else: else:
self._logger.debug("Response Data: %s", data) 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 # valid response at this point, mark login and return data
self._loggedIn = True self._loggedIn = True
return data # noqa: TRY300 return data # noqa: TRY300
except ( # Exception from ClientSession based on standard response status codes
ClientError except ClientError as err:
) as err: # Exception from ClientSession based on standard response status codes
self._logger.error("Api Request Error: %s", err) self._logger.error("Api Request Error: %s", err)
self._logger.error("Response Text: %s", body_text) self._logger.error("Response Text: %s", body_text)
# Prepare data dict for Api error lookup # Prepare data dict for Api error lookup
if not data: if not data:
data = {} data = {}
if not hasattr(data,"code"): if not hasattr(data, "code"):
data["code"] = resp.status data["code"] = resp.status
if not hasattr(data,"msg"): if not hasattr(data, "msg"):
data["msg"] = body_text data["msg"] = body_text
if resp.status in [401,403]: if resp.status in [401, 403]:
# Unauthorized or forbidden request # Unauthorized or forbidden request
if self._retry_attempt: # reattempt autentication with same credentials if cached token was kicked out
errors.raise_error(data, prefix=f"Login failed for user {self._email}") # retry attempt is set if login response data were not cached to fail immediately
# catch error if Api code not defined if not self._retry_attempt:
raise errors.AuthorizationError( self._logger.warning("Login failed, retrying authentication")
f"Login failed for user {self._email}" if await self.async_authenticate(restart=True):
) from err return await self.request(
self._logger.warning("Login failed, retrying authentication") method, endpoint, headers=headers, json=json
if await self.async_authenticate(restart=True): )
return await self.request( self._logger.error("Re-Login failed for user %s", self._email)
method, endpoint, headers=headers, json=json errors.raise_error(
) data, prefix=f"Login failed for user {self._email}"
self._logger.error("Re-Login failed for user %s", self._email) )
elif resp.status in [429]: # 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 # 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: else:
# raise Anker Solix error if code is known # raise Anker Solix error if code is known
errors.raise_error(data) errors.raise_error(data)
@ -1349,7 +1360,7 @@ class AnkerSolixApi:
return self.sites return self.sites
async def update_site_details( async def update_site_details(
self, fromFile: bool = False, exclude: set = None self, fromFile: bool = False, exclude: set | None = None
) -> dict: ) -> dict:
"""Get the latest updates for additional site related details updated less frequently. """Get the latest updates for additional site related details updated less frequently.
@ -1374,7 +1385,7 @@ class AnkerSolixApi:
return self.sites return self.sites
async def update_device_details( async def update_device_details(
self, fromFile: bool = False, exclude: set = None self, fromFile: bool = False, exclude: set | None = None
) -> dict: ) -> dict:
"""Get the latest updates for additional device info updated less frequently. """Get the latest updates for additional device info updated less frequently.
@ -1455,7 +1466,7 @@ class AnkerSolixApi:
return self.devices 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. """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. 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 return data
async def set_site_price( 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: ) -> bool:
"""Set the power price, the unit and/or CO2 for a site. """Set the power price, the unit and/or CO2 for a site.
@ -1984,7 +1999,7 @@ class AnkerSolixApi:
self, self,
siteId: str, siteId: str,
paramType: str = SolixParmType.SOLARBANK_SCHEDULE.value, paramType: str = SolixParmType.SOLARBANK_SCHEDULE.value,
deviceSn: str = None, deviceSn: str | None = None,
fromFile: bool = False, fromFile: bool = False,
) -> dict: ) -> 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. 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, paramData: dict,
paramType: str = SolixParmType.SOLARBANK_SCHEDULE.value, paramType: str = SolixParmType.SOLARBANK_SCHEDULE.value,
command: int = 17, command: int = 17,
deviceSn: str = None, deviceSn: str | None = None,
) -> dict: ) -> dict:
"""Set device parameters (e.g. solarbank schedule). """Set device parameters (e.g. solarbank schedule).
@ -2073,9 +2088,9 @@ class AnkerSolixApi:
siteId: str, siteId: str,
deviceSn: str, deviceSn: str,
all_day: bool = False, all_day: bool = False,
preset: int = None, preset: int | None = None,
export: bool = None, export: bool | None = None,
charge_prio: int = None, charge_prio: int | None = None,
set_slot: SolarbankTimeslot = None, set_slot: SolarbankTimeslot = None,
insert_slot: SolarbankTimeslot = None, insert_slot: SolarbankTimeslot = None,
) -> bool: ) -> bool:
@ -2611,10 +2626,10 @@ class AnkerSolixApi:
) )
return resp.get("data", {}) return resp.get("data", {})
async def get_upgrade_record( async def check_upgrade_record(
self, recordType: int = 2, fromFile: bool = False self, recordType: int = 2, fromFile: bool = False
) -> dict: ) -> 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: Example data:
{"is_record": true,"device_list": [{ {"is_record": true,"device_list": [{
@ -2624,7 +2639,7 @@ class AnkerSolixApi:
data = {"type": recordType} data = {"type": recordType}
if fromFile: if fromFile:
resp = self._loadFromFile( 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: else:
resp = await self.request( resp = await self.request(
@ -2632,14 +2647,45 @@ class AnkerSolixApi:
) )
return resp.get("data", {}) 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( async def energy_analysis(
self, self,
siteId: str, siteId: str,
deviceSn: str, deviceSn: str,
rangeType: str = None, rangeType: str | None = None,
startDay: datetime = None, startDay: datetime | None = None,
endDay: datetime = None, endDay: datetime | None = None,
devType: str = None, devType: str | None = None,
) -> dict: ) -> dict:
"""Fetch Energy data for given device and optional time frame. """Fetch Energy data for given device and optional time frame.
@ -2770,7 +2816,7 @@ class AnkerSolixApi:
table.update({daystr: entry}) table.update({daystr: entry})
return table 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. """Get home load chart data.
Example data: Example data:

View File

@ -75,6 +75,8 @@ ERRORS: dict[int, type[AnkerSolixError]] = {
401: AuthorizationError, 401: AuthorizationError,
403: AuthorizationError, 403: AuthorizationError,
429: RequestLimitError, 429: RequestLimitError,
502: ConnectError,
504: ConnectError,
997: ConnectError, 997: ConnectError,
998: NetworkError, 998: NetworkError,
999: ServerError, 999: ServerError,

View File

@ -1,27 +1,27 @@
{ {
"DILM86K2GJV2NRAI": { "G55HAP9LVQO2LAPM": {
"device_sn": "DILM86K2GJV2NRAI", "device_sn": "G55HAP9LVQO2LAPM",
"type": "solarbank", "type": "solarbank",
"site_id": "bdd7d770-bcb7-d22b-cafc-d0b2eddc4e23", "site_id": "9e40dc42-adac-7fba-dead-a1bba7bff34e",
"is_admin": true, "is_admin": true,
"device_pn": "A17C0", "device_pn": "A17C0",
"battery_soc": "6", "battery_soc": "17",
"battery_capacity": "1600", "battery_capacity": "1600",
"battery_energy": "96", "battery_energy": "272",
"charging_power": "64", "charging_power": "-190",
"power_unit": "W", "power_unit": "W",
"charging_status": "3", "charging_status": "2",
"charging_status_desc": "charge_bypass", "charging_status_desc": "discharge",
"status": "1", "status": "1",
"status_desc": "online", "status_desc": "online",
"wireless_type": "1", "wireless_type": "1",
"input_power": "154", "input_power": "0",
"output_power": "90", "output_power": "190",
"set_output_power": "0", "set_output_power": "300",
"power_cutoff": 5, "power_cutoff": 5,
"alias": "SB E1600", "alias": "SB E1600",
"set_system_output_power": "0", "set_system_output_power": "300",
"bt_ble_mac": "3CEAAD9FDDF7", "bt_ble_mac": "FEDCEB33AA9A",
"name": "Solarbank E1600", "name": "Solarbank E1600",
"wifi_online": true, "wifi_online": true,
"charge": false, "charge": false,
@ -29,7 +29,7 @@
"sw_version": "v1.5.6", "sw_version": "v1.5.6",
"auto_upgrade": false, "auto_upgrade": false,
"wifi_name": "wifi-network-1", "wifi_name": "wifi-network-1",
"wifi_signal": "54", "wifi_signal": "50",
"power_cutoff_data": [ "power_cutoff_data": [
{ {
"id": 1, "id": 1,
@ -49,7 +49,7 @@
"solar_info": { "solar_info": {
"solar_brand": "ANKER", "solar_brand": "ANKER",
"solar_model": "A5143", "solar_model": "A5143",
"solar_sn": "9XK0FGWY2TWW", "solar_sn": "YTZL4ZEJ4R5B",
"solar_model_name": "MI80 Microinverter(BLE)" "solar_model_name": "MI80 Microinverter(BLE)"
}, },
"schedule": { "schedule": {
@ -57,7 +57,7 @@
{ {
"id": 0, "id": 0,
"start_time": "00:00", "start_time": "00:00",
"end_time": "06:30", "end_time": "07:20",
"turn_on": true, "turn_on": true,
"appliance_loads": [ "appliance_loads": [
{ {
@ -71,21 +71,21 @@
"power_setting_mode": 1, "power_setting_mode": 1,
"device_power_loads": [ "device_power_loads": [
{ {
"device_sn": "DILM86K2GJV2NRAI", "device_sn": "G55HAP9LVQO2LAPM",
"power": 150 "power": 150
} }
] ]
}, },
{ {
"id": 0, "id": 0,
"start_time": "06:30", "start_time": "07:20",
"end_time": "18:30", "end_time": "19:40",
"turn_on": false, "turn_on": false,
"appliance_loads": [ "appliance_loads": [
{ {
"id": 0, "id": 0,
"name": "Benutzerdefiniert", "name": "Benutzerdefiniert",
"power": 100, "power": 140,
"number": 1 "number": 1
} }
], ],
@ -93,14 +93,14 @@
"power_setting_mode": 1, "power_setting_mode": 1,
"device_power_loads": [ "device_power_loads": [
{ {
"device_sn": "DILM86K2GJV2NRAI", "device_sn": "G55HAP9LVQO2LAPM",
"power": 50 "power": 70
} }
] ]
}, },
{ {
"id": 0, "id": 0,
"start_time": "18:30", "start_time": "19:40",
"end_time": "24:00", "end_time": "24:00",
"turn_on": true, "turn_on": true,
"appliance_loads": [ "appliance_loads": [
@ -115,7 +115,7 @@
"power_setting_mode": 1, "power_setting_mode": 1,
"device_power_loads": [ "device_power_loads": [
{ {
"device_sn": "DILM86K2GJV2NRAI", "device_sn": "G55HAP9LVQO2LAPM",
"power": 150 "power": 150
} }
] ]
@ -130,8 +130,8 @@
"display_advanced_mode": 0, "display_advanced_mode": 0,
"advanced_mode_min_load": 0 "advanced_mode_min_load": 0
}, },
"preset_system_output_power": 100, "preset_system_output_power": 300,
"preset_allow_export": false, "preset_allow_export": true,
"preset_charge_priority": 10, "preset_charge_priority": 10,
"fittings": {} "fittings": {}
} }

View File

@ -1,8 +1,8 @@
{ {
"bdd7d770-bcb7-d22b-cafc-d0b2eddc4e23": { "9e40dc42-adac-7fba-dead-a1bba7bff34e": {
"type": "system", "type": "system",
"site_info": { "site_info": {
"site_id": "bdd7d770-bcb7-d22b-cafc-d0b2eddc4e23", "site_id": "9e40dc42-adac-7fba-dead-a1bba7bff34e",
"site_name": "BKW", "site_name": "BKW",
"site_img": "", "site_img": "",
"device_type_list": [ "device_type_list": [
@ -36,17 +36,17 @@
"statistics": [ "statistics": [
{ {
"type": "1", "type": "1",
"total": "135.13", "total": "183.54",
"unit": "kwh" "unit": "kwh"
}, },
{ {
"type": "2", "type": "2",
"total": "134.72", "total": "182.99",
"unit": "kg" "unit": "kg"
}, },
{ {
"type": "3", "type": "3",
"total": "44.59", "total": "60.57",
"unit": "\u20ac" "unit": "\u20ac"
} }
], ],
@ -55,40 +55,40 @@
"solarbank_list": [ "solarbank_list": [
{ {
"device_pn": "A17C0", "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", "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": "", "bind_site_status": "",
"charging_power": "64", "charging_power": "-190",
"power_unit": "W", "power_unit": "W",
"charging_status": "3", "charging_status": "2",
"status": "1", "status": "1",
"wireless_type": "1", "wireless_type": "1",
"main_version": "", "main_version": "",
"photovoltaic_power": "154", "photovoltaic_power": "0",
"output_power": "90", "output_power": "190",
"create_time": 1695392386, "create_time": 1695392386,
"set_load_power": "0", "set_load_power": "300",
"output_cutoff_data": 5, "output_cutoff_data": 5,
"is_display": true, "is_display": true,
"alias_name": "SB E1600", "alias_name": "SB E1600",
"current_home_load": "0" "current_home_load": "300"
} }
], ],
"total_charging_power": "64", "total_charging_power": "-190.0",
"power_unit": "W", "power_unit": "W",
"charging_status": "0", "charging_status": "0",
"total_battery_power": "0.06", "total_battery_power": "0.17",
"updated_time": "2024-03-27 11:49:04", "updated_time": "2024-04-24 01:33:08",
"total_photovoltaic_power": "154", "total_photovoltaic_power": "0",
"total_output_power": "90.00", "total_output_power": "190.00",
"display_set_power": false, "display_set_power": false,
"is_display_data": true "is_display_data": true
}, },
"retain_load": "0W", "retain_load": "300W",
"updated_time": "01-01-0001 00:00:00", "updated_time": "01-01-0001 00:00:00",
"power_site_type": 2, "power_site_type": 2,
"site_id": "bdd7d770-bcb7-d22b-cafc-d0b2eddc4e23", "site_id": "9e40dc42-adac-7fba-dead-a1bba7bff34e",
"powerpanel_list": [], "powerpanel_list": [],
"site_details": { "site_details": {
"has_unread_msg": false, "has_unread_msg": false,

View File

@ -5,7 +5,7 @@
"main_switch": true, "main_switch": true,
"device_list": [ "device_list": [
{ {
"device_sn": "DILM86K2GJV2NRAI", "device_sn": "G55HAP9LVQO2LAPM",
"device_name": "Solarbank E1600", "device_name": "Solarbank E1600",
"auto_upgrade": false, "auto_upgrade": false,
"alias_name": "SB E1600", "alias_name": "SB E1600",
@ -13,5 +13,5 @@
} }
] ]
}, },
"trace_id": "145dab32b452d3db2a81ce9506ee4010" "trace_id": "c6f8bd0ae3dd9eb8b3dbd8c84d2afba5"
} }

View File

@ -4,10 +4,10 @@
"data": { "data": {
"data": [ "data": [
{ {
"device_sn": "DILM86K2GJV2NRAI", "device_sn": "G55HAP9LVQO2LAPM",
"product_code": "A17C0", "product_code": "A17C0",
"bt_ble_id": "3C:EA:AD:9F:DD:F7", "bt_ble_id": "FE:DC:EB:33:AA:9A",
"bt_ble_mac": "3CEAAD9FDDF7", "bt_ble_mac": "FEDCEB33AA9A",
"device_name": "Solarbank E1600", "device_name": "Solarbank E1600",
"alias_name": "SB 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", "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"
} }

View File

@ -5,5 +5,5 @@
"device_list": null, "device_list": null,
"guide_txt": "" "guide_txt": ""
}, },
"trace_id": "c55301e2c5686c8d75bf494a91d804b6" "trace_id": "ded0b8ad19ffe254edcf876c633e4735"
} }

View File

@ -5,7 +5,7 @@
"ota_complete_status": 2, "ota_complete_status": 2,
"process_skip_type": 2, "process_skip_type": 2,
"solar_info": { "solar_info": {
"solar_sn": "9XK0FGWY2TWW", "solar_sn": "YTZL4ZEJ4R5B",
"solar_brand": "ANKER", "solar_brand": "ANKER",
"solar_model": "A5143", "solar_model": "A5143",
"brand_id": "3a9930f5-74ef-4e41-a797-04e6b33d3f0f", "brand_id": "3a9930f5-74ef-4e41-a797-04e6b33d3f0f",
@ -15,5 +15,5 @@
"solar_model_name": "MI80 Microinverter(BLE)" "solar_model_name": "MI80 Microinverter(BLE)"
} }
}, },
"trace_id": "7dbfd7538ad24749c1abfcf0fd93de5f" "trace_id": "077b4ed2b6fccac0a9dfcf8e4fdd85a6"
} }

View File

@ -4,5 +4,5 @@
"data": { "data": {
"data": [] "data": []
}, },
"trace_id": "823ef9f8a8c4fbdbe7e4abd4232f79c4" "trace_id": "a433dc2acdb4187e340cff4d6bb60f3c"
} }

View File

@ -2,11 +2,11 @@
"code": 0, "code": 0,
"msg": "success!", "msg": "success!",
"data": { "data": {
"site_id": "bdd7d770-bcb7-d22b-cafc-d0b2eddc4e23", "site_id": "9e40dc42-adac-7fba-dead-a1bba7bff34e",
"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}", "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": "0W", "current_home_load": "300W",
"parallel_home_load": "", "parallel_home_load": "",
"parallel_display": false "parallel_display": false
}, },
"trace_id": "adbafcecd3cac7f6c9feda1bcbab7aea" "trace_id": "ece0447b2ec3c1520e8ca17c1a4ce63f"
} }

View File

@ -2,7 +2,7 @@
"code": 0, "code": 0,
"msg": "success!", "msg": "success!",
"data": { "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"
} }

View File

@ -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"
}

View File

@ -4,7 +4,7 @@
"data": { "data": {
"site_list": [ "site_list": [
{ {
"site_id": "bdd7d770-bcb7-d22b-cafc-d0b2eddc4e23", "site_id": "9e40dc42-adac-7fba-dead-a1bba7bff34e",
"site_name": "BKW", "site_name": "BKW",
"site_img": "", "site_img": "",
"device_type_list": [ "device_type_list": [
@ -21,10 +21,10 @@
"solarbank_list": [ "solarbank_list": [
{ {
"device_pn": "", "device_pn": "",
"device_sn": "DILM86K2GJV2NRAI", "device_sn": "G55HAP9LVQO2LAPM",
"device_name": "SB E1600", "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", "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", "bind_site_status": "1",
"charging_power": "", "charging_power": "",
"power_unit": "", "power_unit": "",
@ -42,5 +42,5 @@
], ],
"powerpanel_list": [] "powerpanel_list": []
}, },
"trace_id": "ae8abfd363866b4cee17359fd1cd9dca" "trace_id": "d0ee3c49869cec32cf6cf251521f29dd"
} }

View File

@ -4,5 +4,5 @@
"data": { "data": {
"has_unread_msg": false "has_unread_msg": false
}, },
"trace_id": "aec8da3373cd0e59d9db5eb52e7adbdc" "trace_id": "a058ab399db309214abaabf12ddccaad"
} }

View File

@ -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"
}

View File

@ -19,5 +19,5 @@
} }
] ]
}, },
"trace_id": "7a84aead0e446bbc85fc76ebbcb2bc01" "trace_id": "8bb7ef048b69dcff25ae45bc3ec3fad2"
} }

View File

@ -2,10 +2,10 @@
"code": 0, "code": 0,
"msg": "success!", "msg": "success!",
"data": { "data": {
"site_id": "bdd7d770-bcb7-d22b-cafc-d0b2eddc4e23", "site_id": "9e40dc42-adac-7fba-dead-a1bba7bff34e",
"price": 0.33, "price": 0.33,
"site_co2": 0, "site_co2": 0,
"site_price_unit": "\u20ac" "site_price_unit": "\u20ac"
}, },
"trace_id": "7cfacbfca09e909cf9a8939ebddba1ac" "trace_id": "5aae22e5adc38af4b9fdc37aa1f89cdb"
} }

View File

@ -20,17 +20,17 @@
"statistics": [ "statistics": [
{ {
"type": "1", "type": "1",
"total": "135.13", "total": "183.54",
"unit": "kwh" "unit": "kwh"
}, },
{ {
"type": "2", "type": "2",
"total": "134.72", "total": "182.99",
"unit": "kg" "unit": "kg"
}, },
{ {
"type": "3", "type": "3",
"total": "44.59", "total": "60.57",
"unit": "\u20ac" "unit": "\u20ac"
} }
], ],
@ -39,40 +39,40 @@
"solarbank_list": [ "solarbank_list": [
{ {
"device_pn": "A17C0", "device_pn": "A17C0",
"device_sn": "DILM86K2GJV2NRAI", "device_sn": "G55HAP9LVQO2LAPM",
"device_name": "SB E1600", "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", "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": "", "bind_site_status": "",
"charging_power": "90", "charging_power": "190",
"power_unit": "W", "power_unit": "W",
"charging_status": "3", "charging_status": "2",
"status": "1", "status": "1",
"wireless_type": "1", "wireless_type": "1",
"main_version": "", "main_version": "",
"photovoltaic_power": "154", "photovoltaic_power": "0",
"output_power": "90", "output_power": "190",
"create_time": 1695392386, "create_time": 1695392386,
"set_load_power": "", "set_load_power": "",
"output_cutoff_data": 5, "output_cutoff_data": 5,
"is_display": true "is_display": true
} }
], ],
"total_charging_power": "64", "total_charging_power": "0",
"power_unit": "W", "power_unit": "W",
"charging_status": "0", "charging_status": "0",
"total_battery_power": "0.06", "total_battery_power": "0.17",
"updated_time": "2024-03-27 11:49:04", "updated_time": "2024-04-24 01:33:08",
"total_photovoltaic_power": "154", "total_photovoltaic_power": "0",
"total_output_power": "90.00", "total_output_power": "190.00",
"display_set_power": false, "display_set_power": false,
"is_display_data": true "is_display_data": true
}, },
"retain_load": "0W", "retain_load": "300W",
"updated_time": "01-01-0001 00:00:00", "updated_time": "01-01-0001 00:00:00",
"power_site_type": 2, "power_site_type": 2,
"site_id": "bdd7d770-bcb7-d22b-cafc-d0b2eddc4e23", "site_id": "9e40dc42-adac-7fba-dead-a1bba7bff34e",
"powerpanel_list": [] "powerpanel_list": []
}, },
"trace_id": "e6a2f3f712a7c2498a25ae7c0bafef3e" "trace_id": "64eeaeab8d80ee6bab68bf981b9cda1d"
} }

View File

@ -3,7 +3,7 @@
"msg": "success!", "msg": "success!",
"data": { "data": {
"site_info": { "site_info": {
"site_id": "bdd7d770-bcb7-d22b-cafc-d0b2eddc4e23", "site_id": "9e40dc42-adac-7fba-dead-a1bba7bff34e",
"site_name": "BKW", "site_name": "BKW",
"site_img": "", "site_img": "",
"device_type_list": [ "device_type_list": [
@ -23,7 +23,7 @@
"solarbank_list": [ "solarbank_list": [
{ {
"device_pn": "A17C0", "device_pn": "A17C0",
"device_sn": "DILM86K2GJV2NRAI", "device_sn": "G55HAP9LVQO2LAPM",
"device_name": "SB E1600", "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", "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": "", "battery_power": "",
@ -44,5 +44,5 @@
], ],
"powerpanel_list": [] "powerpanel_list": []
}, },
"trace_id": "84caedc36bfe9b3f0ed1c89ceb5c0ee2" "trace_id": "daee0eafcbcefbcafc9a0adb3519ea6f"
} }

View File

@ -4,7 +4,7 @@
"data": { "data": {
"site_list": [ "site_list": [
{ {
"site_id": "bdd7d770-bcb7-d22b-cafc-d0b2eddc4e23", "site_id": "9e40dc42-adac-7fba-dead-a1bba7bff34e",
"site_name": "BKW", "site_name": "BKW",
"site_img": "", "site_img": "",
"device_type_list": [ "device_type_list": [
@ -21,5 +21,5 @@
} }
] ]
}, },
"trace_id": "b07e6dfde00e7ddfb8fa4b3a2b76ac4c" "trace_id": "fe909473eeb4bd6ba428b4fda59aab74"
} }

View File

@ -142,5 +142,5 @@
} }
] ]
}, },
"trace_id": "d3dceb28da9a8f8ca2fb2ae170dc186a" "trace_id": "f8acb37f7d12aeabe646cbd5055ebabf"
} }

View File

@ -5,8 +5,8 @@
"brand_id": "3a9930f5-74ef-4e41-a797-04e6b33d3f0f", "brand_id": "3a9930f5-74ef-4e41-a797-04e6b33d3f0f",
"solar_brand": "ANKER", "solar_brand": "ANKER",
"solar_model": "A5143", "solar_model": "A5143",
"solar_sn": "9XK0FGWY2TWW", "solar_sn": "YTZL4ZEJ4R5B",
"solar_model_name": "MI80 Microinverter(BLE)" "solar_model_name": "MI80 Microinverter(BLE)"
}, },
"trace_id": "4fddaabaeaabd39fea6d7c474fb97ad2" "trace_id": "6b2dfaebd1abe931bcec042c1686fc4f"
} }

View File

@ -7,7 +7,7 @@
"solarbank_list": [ "solarbank_list": [
{ {
"device_pn": "A17C0", "device_pn": "A17C0",
"device_sn": "DILM86K2GJV2NRAI", "device_sn": "G55HAP9LVQO2LAPM",
"device_name": "SB E1600", "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", "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": "", "battery_power": "",
@ -28,5 +28,5 @@
], ],
"powerpanel_list": [] "powerpanel_list": []
}, },
"trace_id": "3607fa3ff416d93ded855c2ee9b10bea" "trace_id": "4acd422dafadcb3f2ccabbe1fb92ceda"
} }

View File

@ -5,9 +5,9 @@
"wifi_info_list": [ "wifi_info_list": [
{ {
"wifi_name": "wifi-network-1", "wifi_name": "wifi-network-1",
"wifi_signal": "54" "wifi_signal": "50"
} }
] ]
}, },
"trace_id": "38ec9a1a8d29dac96ac9adc12fbb41b8" "trace_id": "af7beabd3e81cfb62be0f4abf96298fb"
} }

View File

@ -50,7 +50,8 @@ def randomize(val, key: str = "") -> str:
if not RANDOMIZE: if not RANDOMIZE:
return str(val) return str(val)
randomstr = RANDOMDATA.get(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"]: if "_sn" in key or key in ["sn"]:
randomstr = "".join( randomstr = "".join(
random.choices(string.ascii_uppercase + string.digits, k=len(val)) random.choices(string.ascii_uppercase + string.digits, k=len(val))
@ -68,7 +69,7 @@ def randomize(val, key: str = "") -> str:
if ":" in val: if ":" in val:
RANDOMDATA.update({temp: randomstr}) # save also key value without : RANDOMDATA.update({temp: randomstr}) # save also key value without :
randomstr = ":".join( 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: elif "_id" in key:
for part in val.split("-"): for part in val.split("-"):
@ -102,7 +103,7 @@ def randomize(val, key: str = "") -> str:
# default randomize format # default randomize format
randomstr = "".join(random.choices(string.ascii_letters, k=len(val))) randomstr = "".join(random.choices(string.ascii_letters, k=len(val)))
RANDOMDATA.update({val: randomstr}) RANDOMDATA.update({val: randomstr})
return randomstr return randomstr or str(val)
def check_keys(data): def check_keys(data):
@ -125,6 +126,7 @@ def check_keys(data):
"wifi_name", "wifi_name",
"home_load_data", "home_load_data",
"param_data", "param_data",
"device_name"
] ]
) or k in ["sn"]: ) or k in ["sn"]:
data[k] = randomize(v, k) data[k] = randomize(v, k)
@ -133,7 +135,7 @@ def check_keys(data):
def export( def export(
filename: str, filename: str,
d: dict = None, d: dict | None = None,
skip_randomize: bool = False, skip_randomize: bool = False,
randomkeys: bool = False, randomkeys: bool = False,
) -> None: ) -> None:
@ -329,6 +331,22 @@ async def main() -> bool: # noqa: C901 # pylint: disable=too-many-branches,too-
except (ClientError, errors.AnkerSolixError): except (ClientError, errors.AnkerSolixError):
if not admin: if not admin:
CONSOLE.warning("Query requires account of site owner!") 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(): for sn, device in myapi.devices.items():
CONSOLE.info( CONSOLE.info(
"\nExporting device specific data for device %s SN %s...", "\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): except (ClientError, errors.AnkerSolixError):
if not admin: if not admin:
CONSOLE.warning("Query requires account of site owner!") 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...") CONSOLE.info("\nExporting site rules...")
export( export(