Summary
In coordinator._async_poll, the operation-log fetch runs after the state
query and before the return. If it raises, the state and battery values that
were already read successfully are thrown away, and _async_update_data
substitutes {"locked": None, "battery_level": None}.
Reading the operation log is auxiliary — it feeds the event entity. It should
not be able to invalidate the lock's state.
async def _async_poll(self, connection):
result = await connection.async_query_state()
if result is None:
return {"locked": None, "battery_level": None}
raw_state, battery = result # <-- state already in hand
await connection.async_get_operation_log() # <-- raises
return { # <-- never reached
"locked": _parse_lock_state(raw_state),
"battery_level": battery,
}
Why it matters
With DEFAULT_SCAN_INTERVAL_SECONDS = 3600 this is the only mechanism that
refreshes the entity from the device. When it is broken, the entity's state
only ever changes from HA's own optimistic writes in lock.py.
Everything done outside HA — fingerprint, PIN, NFC card, the TTLock app, the
keypad long-press — becomes invisible, not for an hour but indefinitely.
Measured on my install: I unlocked from the TTLock app and HA still reported
locked 40+ minutes later, including after a forced
homeassistant.update_entity.
That is worse than an unavailable entity, because it reports a confident wrong
value. A state-based automation such as "lock the door if it isn't locked when
everyone leaves" silently does nothing: the condition reads a stale locked
and the automation aborts without acting and without warning. I hit exactly
this before tracing it back here.
Environment
|
|
ha-ttlock-ble |
3.4.0 |
ttlock-ble |
0.1.10 |
| Home Assistant |
2026.7.2 (Docker) |
| Lock |
LL609, DLock-XP V3 |
Suggested fix
Isolate the auxiliary read:
raw_state, battery = result
try:
await connection.async_get_operation_log()
except Exception:
LOGGER.debug("Operation log fetch failed for %s", connection.mac, exc_info=True)
return {
"locked": _parse_lock_state(raw_state),
"battery_level": battery,
}
The lock state stays fresh; only the event entity lags.
Secondary: the failure is reported as a success
_async_update_data catches the exception and returns a None state, so the
coordinator logs:
WARNING [custom_components.ttlock_ble] Failed to poll <MAC>: The length of the provided data is not a multiple of the block length.
DEBUG [custom_components.ttlock_ble] Finished fetching ttlock_ble data in 39.262 seconds (success: True)
success: True on a poll that produced no data hides the problem in exactly
the log a user would check. Worth flagging the update as failed so HA's own
coordinator machinery reflects it.
Related, but not duplicates
Three different paths to "HA reports the wrong lock state" may be worth
triaging together, since the consequence is the same and it's a bad one: a
confidently wrong locked silently disables any state-based automation built
on it.
Summary
In
coordinator._async_poll, the operation-log fetch runs after the statequery and before the return. If it raises, the state and battery values that
were already read successfully are thrown away, and
_async_update_datasubstitutes
{"locked": None, "battery_level": None}.Reading the operation log is auxiliary — it feeds the
evententity. It shouldnot be able to invalidate the lock's state.
Why it matters
With
DEFAULT_SCAN_INTERVAL_SECONDS = 3600this is the only mechanism thatrefreshes the entity from the device. When it is broken, the entity's state
only ever changes from HA's own optimistic writes in
lock.py.Everything done outside HA — fingerprint, PIN, NFC card, the TTLock app, the
keypad long-press — becomes invisible, not for an hour but indefinitely.
Measured on my install: I unlocked from the TTLock app and HA still reported
locked40+ minutes later, including after a forcedhomeassistant.update_entity.That is worse than an unavailable entity, because it reports a confident wrong
value. A state-based automation such as "lock the door if it isn't locked when
everyone leaves" silently does nothing: the condition reads a stale
lockedand the automation aborts without acting and without warning. I hit exactly
this before tracing it back here.
Environment
ha-ttlock-blettlock-bleSuggested fix
Isolate the auxiliary read:
The lock state stays fresh; only the event entity lags.
Secondary: the failure is reported as a success
_async_update_datacatches the exception and returns aNonestate, so thecoordinator logs:
success: Trueon a poll that produced no data hides the problem in exactlythe log a user would check. Worth flagging the update as failed so HA's own
coordinator machinery reflects it.
Related, but not duplicates
It's deterministic per lock, so on affected locks the entity state is
permanently stale rather than occasionally. The two are independent
though: any failure of
async_get_operation_log, transient BLE included,currently invalidates a good state read.
lockedshown while the door is open), buta different mechanism: there the state changes on disconnect, and those logs
show
Fetched 1 log entr(ies), so the log read is working.Three different paths to "HA reports the wrong lock state" may be worth
triaging together, since the consequence is the same and it's a bad one: a
confidently wrong
lockedsilently disables any state-based automation builton it.