XKNX / XKNX/xknxtoolkit

[Detail Bug] Node editor: multi-receiver group address shows only one link (duplicate LinkId collapse)

Open
#77 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
Python
Stars
4
Forks
0
Avg merge
15h 38m
Merged PRs (30d)
37

Description

Detail Bug Report

https://app.detail.dev/org_62aa40f5-2c23-4914-a665-3bb2068af20e/bugs/bug_06a16365-1787-426d-a796-7084a2e76050

Introduced in bd8a99e7237a73a781580622a800f49ab4652f12 by @kewde on May 13, 2026

Summary

  • Context: NodeEditorPanel._compute_visual_links (GA-hidden branch) builds the list of (link_id, start_pin, end_pin) triples that _render_links feeds to imgui_node_editor's ed.link(ed.LinkId(link_id), ...).
  • Bug: For group addresses with one sender and multiple receivers, the GA-hidden branch reuses ga.id as the LinkId for every sender×receiver pair. Because imgui_node_editor's Link() reuses the same internal link record when called with a LinkId it has already seen this frame, only one curve (the last-submitted pair) survives — the other assignments are invisible in the editor.
  • Actual vs. expected: A 1-sender/3-receiver GA should draw 3 link curves; instead all 3 triplets share LinkId == ga.id and the editor ends up with one link carrying the last (sender, receiver) pair, hiding the other two real assignments.
  • Impact: Real assignments are invisible in the node editor (the default view), so users believe certain sensors/receivers are not on the group when they actually are.

Code with Bug

# _compute_visual_links, GA-hidden branch (apps/knx-gui/src/knx_gui/plugins/node_editor/ui.py)
else:
    if len(assignments) < 2:
        continue
    for send_pin in sending_pins:
        for recv_pin in receiving_pins:
            links.append((ga.id, send_pin, recv_pin))   # <-- BUG 🔴 reuses ga.id for every sender×receiver pair

Consumed in _render_links:

def _render_links(self) -> None:
    for ga_id, start_pin, end_pin in self._compute_visual_links():
        match = self._pins_match_quality(start_pin, end_pin)
        color = LINK_LOOSE_COLOR if match == DPTMatch.LOOSE else LINK_COLOR
        ed.link(ed.LinkId(ga_id), ed.PinId(start_pin), ed.PinId(end_pin), color)   # <-- BUG 🔴 duplicate LinkId collapses to one link

Explanation

  • In GA-hidden mode, a single GA can yield multiple visual links (one per sender×receiver pair).
  • imgui_node_editor identifies links by LinkId. Submitting multiple ed.link() calls with the same LinkId in one frame overwrites/reuses the same internal link record.
  • This behavior was reproduced against the real imgui_node_editor bindings: calling ed.link(LinkId(7), ...) three times in one frame with different pin pairs results in ed.get_link_pins(LinkId(7), ...) returning only the last pair, proving the earlier links are lost.

Recommended Fix

Make the visual link ID unique per (sender, receiver) pair in the GA-hidden branch (and maintain a mapping back to the GA for deletion logic). For example, generate a composite link_id based on ga.id plus sender/receiver indices, and keep a visual_link_id -> ga_id map so _handle_link_deletion can still remove the correct GA.

History

This bug was introduced in commit bd8a99e, which refactored link rendering to use _compute_visual_links() and used ga.id as the LinkId for every (send_pin, recv_pin) tuple under a GA. A later commit added unique composite link IDs for the GA-shown mode, but left the GA-hidden (default) branch reusing ga.id.

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.

Research direction

Start in apps/knx-gui/src/knx_gui/plugins/node_editor/ui.py, reading _compute_visual_links and _render_links to trace how GA-hidden assignments become imgui_node_editor LinkIds. Check _handle_link_deletion as well; done means each sender/receiver pair renders as a distinct curve while deletion still identifies the correct GA.

Written by the indexing model from the issue text.

Assessment

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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.