[Detail Bug] Configure panel commits intermediate KNX individual addresses while editing segmented input

Open Beginner friendly
#83 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Assessment

Difficulty
2/5
Estimated time
1-3 hours
Newbie friendliness
88/100
Issue type
Bug
Clarity
Clearly specified
Activity status
Active
Tech stack
python
Domain
frontend

Research direction

Start in apps/knx-gui/src/knx_gui/plugins/project/ui/configure.py at ConfigurePanel.render and _commit_address. Run reproduce_ia_commit_bug.py to observe the current callback sequence, then verify that a chained edit commits only after the composite address is finished. Done means the 1.1.1 to 8.5.9 reproduction produces one callback for 8.5.9.

Written by the indexing model from the issue text.

Description

Detail Bug Report

https://app.detail.dev/org_62aa40f5-2c23-4914-a665-3bb2068af20e/bugs/bug_d0df1e78-ef8c-45fa-b727-c22b340865f9

Introduced in #26 by @kewde on Sep 6, 2026

Summary

  • Context: The Configure panel's segmented Individual Address input (Area / Line / Device) lets the user edit a device's KNX area.line.device address across three chained input_text widgets and, on deactivation, commits the assembled address by calling the injected on_individual_address_change callback, which the project plugin uses to optimistically mutate the in-memory Device and then push the change through the project service.
  • Bug: _commit_address(device) is fired whenever any one of the three segments reports is_item_deactivated_after_edit(), instead of only once when editing of the whole composite address is finished — so Tab/auto-advance/click through Area → Line → Device (the widget's intended edit flow) commits a chain of intermediate partial addresses, each assembled from the segment the user just reached plus the unchanged (still old-IA) octets of the other two.
  • Actual vs. expected: A user changing 1.1.18.5.9 produces three on_individual_address_change callback invocations — (1, "8.1.1"), (1, "8.5.1"), (1, "8.5.9") — instead of the single "8.5.9" the user intended.
  • Impact: Spurious intermediate commits are sprayed at the UI/plugin layer on every multi-segment IA edit. Concretely: (a) the project plugin optimistically rewrites device.individual_address to each intermediate partial address in turn; (b) the device-select combo dropdown label flickers through intermediate addresses while the user is still typing; (c) the undo history can grow by one entry per intermediate whose (area, line) exists, so an intended one-shot edit can require multiple undo presses to revert.

Code with Bug

# apps/knx-gui/src/knx_gui/plugins/project/ui/configure.py, ConfigurePanel.render
        area = render_bounded_numeric_segment("##ia_area", self._ia_area, 2, _MAX_AREA)
        self._ia_area = area.value
        self._render_address_separator()

        if area.advance:
            imgui.set_keyboard_focus_here()
        line = render_bounded_numeric_segment("##ia_line", self._ia_line, 2, _MAX_LINE)
        self._ia_line = line.value
        self._render_address_separator()

        if line.advance:
            imgui.set_keyboard_focus_here()
        dev = render_bounded_numeric_segment(
            "##ia_device", self._ia_device, 3, _MAX_DEVICE
        )
        self._ia_device = dev.value

        if area.deactivated or line.deactivated or dev.deactivated:
            self._commit_address(device)  # <-- BUG 🔴 fires on each per-segment deactivation, not on completion of the whole address
        elif (
            not (area.active or line.active or dev.active)
            and self._assembled_address() != device.individual_address
        ):
            self._sync_address_buffers(device.individual_address)
# apps/knx-gui/src/knx_gui/plugins/project/ui/configure.py
    def _commit_address(self, device: Device) -> None:
        new_address = self._assembled_address()
        if new_address and new_address != "0.0.0":
            self._on_individual_address_change(device, new_address)  # <-- BUG 🔴 one commit per segment deactivation

Explanation

Each segment (Area, Line, Device) independently reports deactivated=imgui.is_item_deactivated_after_edit(). The UI currently commits whenever any segment deactivates, which is normal when focus moves from one segment to the next during a single logical edit. Because _assembled_address() joins the three buffers as-is, the early commits combine the just-edited segment with the other two still holding the old address, producing partial-but-valid intermediate addresses (e.g. 8.1.1, 8.5.1).

A repo reproduction (reproduce_ia_commit_bug.py) confirms the callback is invoked three times for a single chained edit from 1.1.1 to 8.5.9:

on_individual_address_change calls: [(1, '8.1.1'), (1, '8.5.1'), (1, '8.5.9')]
  count = 3
  -> BUG CONFIRMED: intermediate partial addresses were committed.

Recommended Fix

Commit only when a segment deactivated on this frame and no segment is currently active (i.e. the composite edit has finished), rather than on any per-segment deactivation:

        any_just_deactivated = area.deactivated or line.deactivated or dev.deactivated
        any_still_active = area.active or line.active or dev.active
        if any_just_deactivated and not any_still_active:
            self._commit_address(device)
        elif not any_still_active and self._assembled_address() != device.individual_address:
            self._sync_address_buffers(device.individual_address)

History

This bug was introduced in commit 1ab42e3 (PR #26, segmented Individual Address input). The previous single-field input committed once on imgui.is_item_deactivated_after_edit(). The new segmented version mirrored that pattern with area.deactivated or line.deactivated or dev.deactivated, which commits on each segment boundary during a single logical edit.

Dominant language
Python
Stars
4
Forks
0
Avg merge
17h 43m
Merged PRs (30d)
39

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

More from XKNX/xknxtoolkit

All issues in XKNX/xknxtoolkit

Similar issues

More Python issues

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.