Skip to content

feat: group the battery variables into one folder per device in the variable list - #34

Merged
PyFlat-JR merged 1 commit into
mainfrom
feature/variable-folders-per-device
Oct 19, 2026
Merged

PyFlat-JR merged 1 commit into
mainfrom
feature/variable-folders-per-device

Conversation

@PyFlat-JR

Copy link
Copy Markdown
Member

What does this PR do?

Groups this plugin's variables into one folder per device in Macro Deck's variable list, the way Home Assistant shows one folder per entity. Until now every variable of every device sat flat under "Unbound variables".

  • Why it was flat: the host draws a catalog as a tree only when the provider reports SupportsSearch => true. Without it, the host walks every container and lists only the leaves. The provider now reports it.
  • Folders: one container per device, id devices/<slot id>, IsContainer = true, IsBindable = false, named after the device.
    • A slot id is a slug and never contains /, so a folder id can never be a variable id.
    • TryResolve rejects a folder id, so reading or subscribing to one is unavailable. ResolveAsync returns the folder.
  • Variables: each one gets its device's folder as ParentId. Ids and names are unchanged (<slot>-<suffix>, battery_<slot>_<suffix>). The host stores a link by the variable's id, so existing bindings keep working.
  • DiscoverAsync:
    • no parent and no search: the folders only;
    • a folder id as parent: that device's variables;
    • no parent with a search: the matching variables of every device, never a folder. Every word typed (split on space, _ and .) must appear in the variable name or the device's display name, ignoring case, so phone percent finds the phone's percent variable.
    • Paging with ContinuationToken is unchanged.
  • Catalog reload: the host keeps the catalog it browsed last, even across a plugin restart, until the plugin calls IVariableSink.InvalidateCatalogAsync.
    • Called in OnAttachedAsync, and whenever the device ids or display names change, compared against the set last announced, so readings never trigger one.
    • Not IPluginCatalogNotifier.CatalogChanged: invalidating only makes the host's browser discover again, which never changes the device set, so it cannot loop the way CatalogChanged does (AGENTS.md).
  • CatalogEntryCount: still the number of variables. The SDK defines it as the number of bindable entries.
  • Tests:
    • the top level is one non-bindable container per device;
    • a folder returns exactly its device's variables, each with that ParentId;
    • a search returns matching variables and no folders, and another device's variables do not match;
    • a folder id never resolves as a variable, including for a slot id that ends in a field suffix;
    • the ids and names of one slot are pinned;
    • the catalog is invalidated on attach, when a device is added or renamed, and when a source outside the config appears, but not on readings or a re-initialize.
  • Docs: README (new Variables paragraph) and AGENTS.md (the catalog note, the catalog notification rule, the file overview and the test list).

Unchanged: Macro Deck's own battery_<name>_<hash>_* variables, which the host creates for every device published to its battery catalog, still sit beside this plugin's variables. The SDK has no way to publish a device without them.

Checklist

  • I have read AGENTS.md and CONTRIBUTING.md.
  • dotnet build and dotnet test pass locally (0 warnings, 378 tests).
  • I updated README.md / AGENTS.md if this changes what they describe.
  • If this touches a device backend: I tested it against the real hardware (see below).

Device/hardware tested against (if applicable)

Not applicable: no device backend changes.

@PyFlat-JR
PyFlat-JR merged commit 8fb5dc3 into main Oct 19, 2026
3 checks passed
@PyFlat-JR
PyFlat-JR deleted the feature/variable-folders-per-device branch October 19, 2026 17:42
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