Team & Access

Smart-lock provider abstraction

← Back to Team & Access

All smart-lock operations (Nuki, 2N, future providers) route through one unified LockProvider facade. Operator-visible behaviour is unchanged; the abstraction lets new lock vendors plug in cleanly and makes error handling consistent.

What the operator sees (unchanged)

  • Smart Locks screen shows one card per property with a lock configured.
  • Codes editor + audit log + activity log per property.
  • Same buttons, same behaviour — Generate PIN, Revoke, Test Connection.

What changed underneath

  • One API — every lock call (generate code, revoke code, unlock, get activity) goes through lockOps.<operation> with the property's provider auto-resolved.
  • Retry policy consistent — 5 attempts on transient failures across ALL lock calls (operator-directive decision §D-5). No more silent Nuki 5xx failures (fixed in.
  • Never-silent-error — every lock error surfaces on the operator screen + writes an app_errors row. Fixed the class where Nuki 5xx were caught + dropped without notifying operator.
  • Adds new providers cleanly — adding a new lock vendor requires implementing the LockProvider interface + registering in the provider registry. Four steps, documented in docs/lock-integration-api.md.

Why this matters for the operator

  • Error visibility — when a Nuki call fails, the operator now sees it. Earlier a transient 5xx got Sentry'd + the code silently didn't provision. Now the failure surfaces + a retry runs.
  • Consistent behaviour across providers — same retry, same error UX regardless of Nuki vs 2N vs a future provider.
  • New provider path — if a tenant wants to onboard a new lock brand (Salto, August, TTLock, …), the platform can add it without rewriting every callsite.

What's still per-provider

  • Provider capabilities — each provider declares what it supports (generateCode, revokeCode, unlock, getActivity). Not every provider supports every capability. The V28 Smart Locks screen shows disabled controls with a tooltip when the property's provider doesn't support the operation (mirrors the Channel sync capability-matrix pattern).
  • Provider-specific config — Nuki API token vs 2N intercom IP address vs whatever the new provider needs. Configured per-property in Settings.

2N lifecycle: dispose

2N sessions require an explicit dispose call to release resources on the intercom side. The abstraction handles this automatically — no operator step needed. Earlier callers forgot to dispose + 2N sessions leaked over time. Fixed.

No-direct-import ratchet

A CI ratchet (server/tests/ratchets/noDirectLockClientImportRatchet.test.ts) fails PR if any file imports Nuki or 2N clients directly instead of going through lockOps. Prevents drift back to the pre-

Source: the FlatsBratislava operator manual.