Skip to content

info.connection flips on every restart — instance is yellow every second start (follow-up to #135) #536

Description

@meicker

info.connection flips on every restart — instance is yellow every second start

Follow-up to #135, which was closed on 2026-05-13 with "Please open a new issue
if problem still exists with current release"
. It still exists in 2.8.2, and
this report adds the root cause.

Versions

nuki-extended 2.8.2
js-controller 7.2.2
admin 7.8.23
node / npm v22.23.2 / 10.9.8
OS Debian 12 (bookworm), aarch64

Summary

nuki-extended.0.info.connection alternates between false and true on every
restart, so the instance is yellow on every second start. The adapter itself
works perfectly in both cases — bridge reachable, Web API returns HTTP 200,
states are updated. Only the indicator is wrong.

This matches @Azathoth88's reproduction in #135 (2024-12-10) exactly.

Steps to reproduce

Deterministic, no waiting involved:

  1. iobroker restart nuki-extended.0info.connection becomes true (green)
  2. iobroker restart nuki-extended.0info.connection becomes false (yellow)
  3. Repeat — it alternates on every single restart.

Measured on a working installation:

13:36:55.969  val=true    (from system.adapter.nuki-extended.0)
13:39:01.729  restart issued
13:39:09.595  val=false   ← written by the NEW process, ~8 s after start
13:41:29      restart issued
13:41:48.035  val=true

No warning or error is logged in either case, and no reconnect happens — the
adapter is fully functional while yellow.

Root cause

Library._setValue() (lib/library.js:597) only writes to the database when the
new value differs from the internal cache:

_setValue(state, value, options = {}) {
    if (state !== undefined) {
        if (value !== undefined && (options.force || this._STATES[state] === undefined ||
            this._STATES[state] === null || this._STATES[state].val != value)) {
            this.setDeviceState(state, value);
            this._adapter.setStateAsync(state, { val: value, ts: Date.now(), ack: true });
        } else {
            this.setDeviceProperties(state);   // cache only — no write
        }
    }
}

Three things happen during startup, in this order:

  1. lib/library.js:51,55 — the constructor empties the cache
    (this._STATES = {}) and then calls this.set(Library.CONNECTION, false).
    Because the node is not in the cache yet, set() takes the _createNode()
    branch, and _setValue() sees this._STATES[state] === undefined. So this
    false is always written.

  2. nuki-extended.js:84-89 — the adapter reads its own states and feeds them
    into the cache via library.setDeviceState(...). This updates the cache only,
    without writing. The cache for info.connection is therefore seeded with
    the value the previous run left behind.

  3. nuki-extended.js:92library.set(Library.CONNECTION, true). The node is
    in the cache now, so _setValue() compares against it.

That comparison is where it breaks:

value left by previous run step 3 result
false false != true → writes true green
true true == truesuppressed as a duplicate yellow — only the false from step 1 remains

Hence the exact alternation. The state written by step 1 is not stale, and it is
not a race between the old and the new process: the false at 13:39:09.595 was
written roughly seven seconds after the old process had already exited.

The same pattern applies to the other four set(Library.CONNECTION, true) call
sites (nuki-extended.js:380, nuki-extended.js:456, lib/nuki-tools.js:25,
lib/web-api.js:64) — none of them can write once the cache already says true.

Suggested fix

_setValue() already supports an override, so the smallest change is to use it
at the connection call sites:

library.set(Library.CONNECTION, true, { force: true });

Alternatively, drop the unconditional false in the constructor
(lib/library.js:55) and set it only in terminate(), which already does so at
lib/library.js:89. Writing "not connected" before any connection has even been
attempted is arguably wrong in itself.

Workaround for users

Restart the instance until it is green, then avoid unnecessary restarts. If a
daily restartSchedule is configured, it re-rolls the dice every day — removing
it makes the green state persist.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions