[Detail Bug] Dynamic UI: Inline parameter calculations write literal "{}" instead of clearing to defaults
Nobody has claimed this yet.
- Dominant language
- Python
- Stars
- 4
- Forks
- 0
- Avg merge
- 15h 38m
- Merged PRs (30d)
- 37
Description
Detail Bug Report
Introduced in debd9ef940657cc09e589acd992c251373d9b286 by @kewde on Jun 18, 2026
Summary
- Context:
evaluate_lr/evaluate_rlinpackages/product/src/xknxmono/product/parser_v2/calculation/__init__.pyrun a KNX product's JavaScriptParameterCalculationtransformations (via dukpy) to derive one set of parameter values from another;DynamicUI.set_parameter_refuses the returned dict to set or clear the derived parameters. - Bug: The inline transformation branch returns the literal string
"{}"for every declared output the script left unset, instead of returningNone(and thus being excluded from the returned dict). Because the caller only clears a parameter when the returned value isNone, the"{}"string is written verbatim into parameter state. - Actual vs. expected: Actual: unset inline-calc outputs become
"{}"and are persisted as overrides (and may propagate into encoded device memory). Expected: when a calculation produces no value for a declared output, the parameter is cleared so it reverts to its static default. - Impact: KNX parameter state is silently corrupted with the literal string
"{}"instead of reverting to static defaults; on thegira_2gang_button_interface.knxprodfixture, an API call sequence can produce anencode_to_memory()image that differs by two bytes compared to the contract-intended clear-to-default behavior.
Code with Bug
In packages/product/src/xknxmono/product/parser_v2/calculation/_js.py:
def _read_js_var(interp: dukpy.JSInterpreter, expr: str) -> str | None:
try:
v = interp.evaljs(expr)
except Exception:
return None
if v is None:
return None
if isinstance(v, (int, float)):
fv = float(v)
return str(int(fv)) if fv == int(fv) else str(fv)
return str(v) # <-- BUG 🔴 dukpy marshals JS `undefined` to {}, which becomes literal "{}"
def eval_inline(code, inputs, output_names) -> dict[str, str]:
interp = dukpy.JSInterpreter()
for name, val in inputs.items():
interp.evaljs(f"var {name} = {_to_js_literal(val)};")
for name in output_names:
if name not in inputs:
interp.evaljs(f"var {name};") # <-- BUG 🔴 declares output as `undefined`, later read back as "{}"
interp.evaljs(code)
return {n: v for n in output_names if (v := _read_js_var(interp, n)) is not None}
In packages/product/src/xknxmono/product/parser_v2/dynamic.py:
for pr in calc.rparameters.parameter_ref_ref:
v = r_values.get(pr.alias_name or pr.ref_id)
if v is not None:
self._state.set_instance_ref(pr.ref_id, v) # <-- BUG 🔴 inline calcs pass "{}" (non-None), so it gets written
else:
self._state.clear_instance_ref(pr.ref_id)
Explanation
- Under pinned
dukpy==0.5.1, evaluating an unassigned JS variable (var y; y) marshals to a Python emptydict({}), notNone. _read_js_varstringifies non-numeric, non-None return values viastr(v), turning that{}into the string"{}".eval_inlinedeclares each output asvar {name};, so any output the script doesn’t assign remainsundefinedand gets returned as"{}".DynamicUI.set_parameter_refonly clears parameters when the returned value isNone; since"{}"is non-None, it is persisted as a parameter override instead of clearing back to the static default.- The named-function evaluation path avoids this by initializing outputs to
null(marshals to PythonNone), so unset outputs are excluded and the caller clears them.
Recommended Fix
Pre-initialize declared inline outputs to JS null so “unset” outputs marshal to None and are excluded from the returned dict (matching the named-function path):
for name in output_names:
if name not in inputs:
interp.evaljs(f"var {name} = null;")
Also update the existing test that currently asserts the (buggy) "{}" behavior for declared-but-unset inline outputs.
History
This bug was introduced in commit debd9ef9. That commit implemented inline calculation evaluation by declaring outputs as var {name}; (JS undefined), which dukpy marshals to {} and _read_js_var stringifies as "{}"; these values have been written into parameter state since inline evaluation was introduced.
Contributor guide
No contributing guide indexed for this repository
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start in packages/product/src/xknxmono/product/parser_v2/calculation/_js.py, focusing on eval_inline and its output initialization, then find the existing test for declared-but-unset inline outputs. Compare this path with the named-function evaluation path and verify that unset outputs are omitted so DynamicUI clears them; update the test to assert the clear-to-default behavior.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- javascript, python
- Domain
- backend, testing-qa
- Issue type
- Bug
- Difficulty
- 2/5
- Estimated time
- 1-3 hours
- Activity status
- Active
- Clarity
- Clearly specified
- Newbie friendliness
- 88/100