Skip to content

Commit b3cea97

Browse files
raman325claude
andcommitted
Address review feedback: use internal wrappers and add conflict detection
- Use async_internal_get_usercodes/async_internal_clear_usercode instead of direct provider calls for rate limiting and availability checks - Add comment noting provider limitation for slot detection - Add PIN conflict detection in adopt step: first-seen PIN wins, skip conflicting values with warning log Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Entire-Checkpoint: 732e178917ee
1 parent 394b8ba commit b3cea97

2 files changed

Lines changed: 27 additions & 8 deletions

File tree

custom_components/lock_code_manager/config_flow.py

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -146,14 +146,18 @@ async def _async_get_unmanaged_codes(
146146
hass, dev_reg, ent_reg, lock_config_entry, lock_entry
147147
)
148148
try:
149-
usercodes = await lock_instance.async_get_usercodes()
149+
usercodes = await lock_instance.async_internal_get_usercodes()
150150
except Exception: # noqa: BLE001
151151
_LOGGER.debug(
152152
"Failed to get usercodes from %s during lock reset check",
153153
lock_entity_id,
154154
exc_info=True,
155155
)
156156
continue
157+
# Note: some providers (Matter, Virtual) only return slots already
158+
# configured in a Lock Code Manager config entry, so unmanaged codes
159+
# on those providers will not be detected here. This reset step is
160+
# most useful for Z-Wave locks which return all occupied slots.
157161
unmanaged = {
158162
slot: code
159163
for slot, code in usercodes.items()
@@ -266,7 +270,9 @@ async def async_step_lock_reset_clear(
266270
continue
267271
for slot in codes:
268272
try:
269-
await lock_instance.async_clear_usercode(slot)
273+
await lock_instance.async_internal_clear_usercode(
274+
slot, source="direct"
275+
)
270276
except Exception: # noqa: BLE001
271277
_LOGGER.warning(
272278
"Failed to clear slot %s on %s",
@@ -283,11 +289,22 @@ async def async_step_lock_reset_adopt(
283289
) -> dict[str, Any]:
284290
"""Adopt readable unmanaged codes into the Lock Code Manager configuration."""
285291
self.data.setdefault(CONF_SLOTS, {})
286-
for codes in self._unmanaged_codes.values():
292+
for lock_entity_id, codes in self._unmanaged_codes.items():
287293
for slot, code in codes.items():
288294
if code is SlotCode.UNKNOWN:
289295
continue
290-
self.data[CONF_SLOTS][int(slot)] = {
296+
int_slot = int(slot)
297+
if int_slot in self.data[CONF_SLOTS]:
298+
existing_pin = self.data[CONF_SLOTS][int_slot].get(CONF_PIN)
299+
if existing_pin != str(code):
300+
_LOGGER.warning(
301+
"Slot %s has conflicting PINs across locks "
302+
"(keeping first seen PIN, skipping %s)",
303+
slot,
304+
lock_entity_id,
305+
)
306+
continue
307+
self.data[CONF_SLOTS][int_slot] = {
291308
CONF_NAME: f"Slot {slot}",
292309
CONF_PIN: str(code),
293310
CONF_ENABLED: True,
@@ -303,7 +320,9 @@ async def async_step_lock_reset_adopt(
303320
continue
304321
for slot in masked_slots:
305322
try:
306-
await lock_instance.async_clear_usercode(slot)
323+
await lock_instance.async_internal_clear_usercode(
324+
slot, source="direct"
325+
)
307326
except Exception: # noqa: BLE001
308327
_LOGGER.warning(
309328
"Failed to clear masked slot %s on %s",

tests/test_config_flow.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -319,7 +319,7 @@ async def test_lock_reset_readable_codes_clear(hass: HomeAssistant):
319319
"""Test clearing unmanaged readable codes."""
320320
mock_clear = AsyncMock(return_value=True)
321321
mock_lock = AsyncMock()
322-
mock_lock.async_clear_usercode = mock_clear
322+
mock_lock.async_internal_clear_usercode = mock_clear
323323
unmanaged = {LOCK_1_ENTITY_ID: {3: "9999", 4: "8888"}}
324324
lock_instances = {LOCK_1_ENTITY_ID: mock_lock}
325325

@@ -439,7 +439,7 @@ async def test_lock_reset_mixed_codes_adopt_clears_masked(hass: HomeAssistant):
439439
"""Test that adopt with mixed codes adopts readable and clears masked."""
440440
mock_clear = AsyncMock(return_value=True)
441441
mock_lock = AsyncMock()
442-
mock_lock.async_clear_usercode = mock_clear
442+
mock_lock.async_internal_clear_usercode = mock_clear
443443
unmanaged = {LOCK_1_ENTITY_ID: {3: "9999", 4: SlotCode.UNKNOWN}}
444444
lock_instances = {LOCK_1_ENTITY_ID: mock_lock}
445445

@@ -461,4 +461,4 @@ async def test_lock_reset_mixed_codes_adopt_clears_masked(hass: HomeAssistant):
461461
assert result["type"] == "menu"
462462
assert result["step_id"] == "choose_path"
463463
# Only the masked slot (4) should have been cleared
464-
mock_clear.assert_called_once_with(4)
464+
mock_clear.assert_called_once_with(4, source="direct")

0 commit comments

Comments
 (0)