Skip to content

feat: bump the save stamp on edits and flag silent wiring mistakes in validate - #24

Open
s31teg wants to merge 2 commits into
eisber:mainfrom
s31teg:feat/doc-stamp-and-validate-rules
Open

s31teg wants to merge 2 commits into
eisber:mainfrom
s31teg:feat/doc-stamp-and-validate-rules

Conversation

@s31teg

@s31teg s31teg commented Oct 12, 2026

Copy link
Copy Markdown

Summary

Two changes from editing a real house project with lox-cli and uploading the results to a Miniserver.

1. Bump the Document save stamp when an edit is saved

Loxone Config decides whether an opened .Loxone file is the program already on the Miniserver by the Document element's Date / DateS stamp, not by its content. lox-cli edits never touched the stamp, so on Connect Config reported the program as identical and loaded the Miniserver's old program, and the edits were silently lost.

  • ConfigEditor::stamp_document(now) sets:

    • Date to the local wall-clock time (%Y-%m-%d %H:%M:%S);
    • DateS to whole seconds since the Loxone epoch (2009-01-01 00:00:00 UTC) for the same instant.

    Only those two attributes change, in place.

  • save_edited() stamps only when the output differs from an unedited round-trip of the input. A command that ends up changing nothing (e.g. layout --only-new with nothing to place) keeps the old stamp.

  • to_bytes() stays a pure serialiser, so no-op round-trips are still byte-identical.

  • push is not stamped.

  • save_edited now takes &mut ConfigEditor, so this PR also updates its call sites.

2. Three new lox config validate warnings

Each one is wiring that Config accepts but that silently does nothing, or the wrong thing, on the Miniserver. All three caused real bugs:

  • Lighting Controller motion that does nothing in some operating modes. The rule applies to a controller with Move or Presence wired:

    • its motion table (LSConfig OpM) has no "All other times" row (778);
    • and it lacks a row for an operating mode that at least half of the other motion-wired controllers list (e.g. the bedroom has no Night row when every other room has one).

    A controller with no rows at all is also flagged. Comparing with the other controllers keeps the rule quiet on projects whose own modes cover the whole day.

  • A pulse longer than 0.2 s into a mood input (Sel1..SelN, T5/x) from an EdgeDetection (PulseTime) or Monoflop (Time). Such a pulse counts as a long-click and mixes the mood in instead of selecting it (the controller then logs e.g. "Dim All,Dim Wall"). When no Def is set, the rule uses Loxone's defaults (1 s / 2 s).

  • A wire taken from the Q of a reference to a device or virtual input. Config only uses AQ there, so the wire is neither drawn nor used; Q is valid on references to Memory flags. Wires into OutputRefLM (Caller / Mailer / Logger references) are exempt, because the sample configs use Q there on purpose.

COMMANDS.md documents both changes.

Testing

  • New unit tests:
    • DateS against values from real files;
    • only Date / DateS change, with BOM and CRLF preserved;
    • a real edit bumps the stamp, and a no-op save leaves the file byte-identical;
    • positive and quiet cases for each rule.
  • cargo fmt --check, cargo clippy -- -D warnings, cargo build --release and cargo test pass.
  • Results on a real 2.4 MB house project:
    • motion rule: 0 warnings (its controllers cover the same day modes);
    • pulse rule: 4 warnings, real leftovers (EdgeDetection PulseTime=1 into Sel1);
    • Q rule: 0 warnings.
  • On the 41 sample configs in tests/eval/golden/configs, all three rules give 0 warnings.
  • A real edit on a copy of that project produced a stamp matching the previous one plus the elapsed time; the rest of the Document element was unchanged.

Loxone Config decides whether an opened .Loxone file is the program already
on the Miniserver by the Document element's Date/DateS stamp, not by content.
lox-cli edits never touched it, so Config treated edited files as identical
and loaded the Miniserver's old program, silently dropping the edits.

Add ConfigEditor::stamp_document(now), which sets Date to the local
wall-clock time (%Y-%m-%d %H:%M:%S) and DateS to whole seconds since the
Loxone epoch (2009-01-01 00:00:00 UTC) for the same instant, changing only
those two attributes in place. save_edited() calls it when the edit actually
changed the serialised output compared with an unedited round-trip of the
input, so no-op saves keep the stamp. to_bytes() stays a pure serialiser.
…stakes

Three new `lox config validate` warnings for wiring Loxone Config accepts
but that does nothing (or the wrong thing) on the Miniserver:

- LightController2 with Move/Presence wired whose motion table (LSConfig
  OpM) has no "All other times" row (778) and lacks a row for an operating
  mode that at least half of the other motion-wired controllers list, or
  has no rows at all: motion does nothing while that mode is active.
  Comparing with the other controllers keeps the rule quiet on projects
  whose own modes cover the whole day.
- A pulse longer than 0.2 s into a mood input Sel1..SelN from an
  EdgeDetection (PulseTime) or Monoflop (Time), using Loxone's defaults
  (1 s / 2 s) when no Def is set: it counts as a long-click and mixes the
  mood in instead of selecting it.
- A wire from the Q output of an InputRef whose referenced element is not a
  Memory flag. Wires into OutputRefLM (Caller/Mailer/Logger references) are
  exempt, since sample configs use Q there legitimately.

This branch has not been deployed

No deployments
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.

1 participant