Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions custom_components/hotata/config_flow.py
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,12 @@ async def async_step_user(
CONF_DESCENT_TIME, DEFAULT_DESCENT_TIME
)
),
CONF_BACKUP_USERNAME: user_input.get(
CONF_BACKUP_USERNAME, ""
).strip(),
CONF_BACKUP_PASSWORD: user_input.get(
CONF_BACKUP_PASSWORD, ""
),
**creds,
},
)
Expand All @@ -154,6 +160,8 @@ async def async_step_user(
vol.Required(
CONF_DESCENT_TIME, default=DEFAULT_DESCENT_TIME
): vol.All(vol.Coerce(int), vol.Range(min=0, max=20)),
vol.Optional(CONF_BACKUP_USERNAME): str,
vol.Optional(CONF_BACKUP_PASSWORD): str,
}
)
return self.async_show_form(
Expand Down
61 changes: 52 additions & 9 deletions custom_components/hotata/hub.py
Original file line number Diff line number Diff line change
Expand Up @@ -477,32 +477,75 @@ async def _switch_back_to_primary(self) -> bool:
if not self._using_backup:
return False
try:
# Probe: account-level read (listBindings). A fresh IoT
# credential does NOT lift a throttled account's penalty,
# so verify with a real request before switching.
await self._primary_api.async_list_devices()
# Round 1: account-level read. A fresh IoT credential
# does NOT lift a throttled account's penalty, so
# verify the account is free before trying devices.
devices = await self._primary_api.async_list_devices()
except HotataRateLimited:
self._primary_penalty_until = (
time.time() + SWITCH_BACK_PROBE_DELAY
)
self._switch_back_failures += 1
self._async_save_runtime_state()
self._schedule_switch_back()
_LOGGER.info("Primary still rate-limited, staying on backup")
_LOGGER.info("Primary still rate-limited (account probe), staying on backup")
return False
except HotataAuthError as err:
# Credentials rejected — refresh was already attempted inside
# the client; retry later on the probe schedule.
self._switch_back_failures += 1
self._schedule_switch_back()
_LOGGER.warning("Primary probe auth failed: %s", err)
return False
except HotataError as err:
self._switch_back_failures += 1
self._schedule_switch_back()
_LOGGER.warning("Primary probe failed: %s", err)
_LOGGER.warning("Primary account probe failed: %s", err)
return False
# Probe OK — activate the primary identity.

# Round 2: device-level probe. Account probe alone may pass
# when throttling only applies to device-level requests.
# Query properties of the first discovered device — this is
# the same heavy request the coordinator polls with, so if
# it 403s here we know the primary is still unfit for
# real traffic and we keep the backup.
if devices:
try:
await self._primary_api.async_get_properties(
devices[0].iot_id
)
except HotataRateLimited:
self._primary_penalty_until = (
time.time() + SWITCH_BACK_PROBE_DELAY
)
self._switch_back_failures += 1
self._async_save_runtime_state()
self._schedule_switch_back()
_LOGGER.info(
"Primary still rate-limited (device probe on %s), "
"staying on backup",
devices[0].iot_id,
)
return False
except HotataAuthError as err:
self._switch_back_failures += 1
self._schedule_switch_back()
_LOGGER.warning(
"Primary device probe auth failed on %s: %s",
devices[0].iot_id,
err,
)
return False
except HotataError as err:
self._switch_back_failures += 1
self._schedule_switch_back()
_LOGGER.warning(
"Primary device probe failed on %s: %s",
devices[0].iot_id,
err,
)
return False

# Heavy probe OK — the primary account can actually serve
# real traffic. Activate the primary identity.
self._using_backup = False
self._primary_penalty_until = 0.0
self._switch_back_failures = 0
Expand Down
3 changes: 2 additions & 1 deletion custom_components/hotata/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,6 @@
"requirements": [
"cryptography>=42.0.0"
],
"version": "4.0.1"
"version": "4.0.2"
}

2 changes: 1 addition & 1 deletion custom_components/hotata/sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ def _sensor(
),
_sensor(
"DeviceModelType",
"设备型号",
"机型功能配置",
value_map={
0: "照明、消毒、风干、烘干",
1: "照明",
Expand Down
Loading