Skip to content

fix(config_edit): byte-clean writes matching Loxone's format - #13

Open
tobsch wants to merge 1 commit into
eisber:mainfrom
tobsch:fix/write-minimal-diff
Open

fix(config_edit): byte-clean writes matching Loxone's format#13
tobsch wants to merge 1 commit into
eisber:mainfrom
tobsch:fix/write-minimal-diff

Conversation

@tobsch

@tobsch tobsch commented Aug 25, 2026

Copy link
Copy Markdown

Fixes #7.

Problem

Every write re-serialized the whole file. A raw diff on the resulting .Loxone reported ~11,500 changed lines for a single added block — the real change was drowned in formatting noise, so changes couldn't be reviewed before uploading to a live house.

The churn was purely emitter formatting that differs from Loxone's own output — the content was identical, just re-emitted differently.

Fix

Make to_bytes match Loxone's on-disk conventions so a round-trip touches only what actually changed:

  1. pad_self_closing(false) — Loxone writes <C/>, xml-rs padded to <C />. This alone was ~99.5% of the diff (every self-closing element).
  2. Expand attribute-less empty tags — Loxone writes <IoData></IoData>, never <IoData/>; attributed empties like <Co K="I" U="…"/> stay self-closed. (In the test config: 45 <IoData></IoData>, zero attribute-less <X/> — a consistent rule.)
  3. Un-escape &#xA; → literal newline in attribute values (multi-line PicoC code, notification/alarm texts). Loxone keeps literal newlines and never emits &#xA;, so this only reverses xml-rs's own escaping.
  4. Trailing newline.

Result

# before: adding one block
$ diff config.Loxone out.Loxone | wc -l
11542
# after
$ diff config.Loxone out.Loxone
7716a7717,7722
> 				</C>
> 				<C Type="AMemory" … Title="__difftest__" …>
> 					<Co K="Input" …/>
> 					…

A diff of exactly the added block (+6/-0) — raw .Loxone diffs are now reviewable.

  • All 78 config_edit tests pass; adds a formatting round-trip test.
  • No semantic change to the DOM — only serialization.

🤖 Generated with Claude Code

Every write re-serialized the whole file, so a raw `diff` on a `.Loxone`
reported ~11.5k changed lines for a single added block — real changes
drowned in formatting noise, making review impossible.

The churn was purely emitter formatting that differs from Loxone's own
output. Match it so a round-trip touches only what actually changed:

- `pad_self_closing(false)` — Loxone writes `<C/>`, xml-rs padded to
  `<C />` (this alone caused ~99.5% of the diff).
- Expand attribute-less empty tags: Loxone writes `<IoData></IoData>`,
  never `<IoData/>` (attributed empties like `<Co K="I" U="…"/>` stay
  self-closed). Verified: the config has 45 `<IoData></IoData>` and zero
  attribute-less `<X/>`.
- Un-escape `&#xA;` → literal newline in attribute values (multi-line
  PicoC code, notification texts). Loxone keeps literal newlines and
  never emits `&#xA;`, so this only reverses xml-rs's own escaping.
- Restore the trailing newline.

Result: adding one block now produces a diff of exactly that block
(+6/-0) instead of 11.5k lines. All 78 config_edit tests pass; adds a
formatting round-trip test.

Fixes eisber#7

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01V1TT6BSmf3uXakmtfexfDt
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Writes re-serialize the whole file → raw diffs are unreviewable

1 participant