Skip to content

Releases: dckiller51/astrion-custom-dashboard

v1.3.0

Choose a tag to compare

@dckiller51 dckiller51 released this 17 Oct 07:00
9218659

[1.3.0] - 2026-10-17

Everything since 1.2.0, from the 1.2.1 to 1.2.9 betas.

⚠️ Good to know when updating from 1.2.0

  • Settings: a long press on the Menu key opens Settings when no hotkey of yours does (swiping down opens Activities).
  • New remotes start with a minimal one-page dashboard instead of the original author's layout. Existing dashboards are not touched.
  • No "Disconnected" banner without Home Assistant: a remote used only with Harmony, IR or Apple TV has no banner any more.
  • tv_remote card: without apps, it now shows Netflix, YouTube, Prime Video and Plex; without media_entity, the app buttons use media_player.<same name as remote_entity>. Set both if yours differ.
  • Select card follows your theme colours.

Added

Dashboard & builder

  • Version history of dashboard.json. The remote keeps every save from the builder or its web page (the last 30, plus pinned ones) in /sdcard/astrion/history, with an optional note. Changes made outside the web page are kept too before the next save. From the builder's 🕘 button: open a version in the editor (nothing changes until you save), restore it on the remote, download it, pin it, add a note, delete it. The device page links to it ("Version history"). New routes: GET /history, GET /history/<id>, POST /history/{restore,pin,note,delete}; POST /dashboard.json takes a note.
  • Undo / redo in the builder: ↶ ↷ above the preview, or Ctrl+Z / Ctrl+Y. Rapid changes (typing a colour, dragging a slider) count as one step.
  • The builder and the device page speak French, Italian and Spanish, following the browser's language — built on Rob Johnston's (@ClemsonJeeper) i18n work. The app itself is also translated in Spanish (thanks @enigmat14) and gets a language setting independent of Android's.
  • A form for every card type: monitor, speaker group, row (its cards edited with the normal card form), picture elements (click the floor plan to place a button), TV remote (apps, volume target, command renames). Card types are grouped (Common, Media, Grids, Remotes, Home & layout). Options written by hand are kept when a card is edited.
  • Deleting a card asks first.

Activities

  • Harmony-style Activities. With device profiles imported from the Harmony archive (power type, delays, repeats, input sequences), composed Activities start, switch and stop like a Harmony hub: shared devices stay on, inputs change only when needed.
  • IR repeats sent the way the original remote does (Harmony pressMinRepeats), locally and through IR extenders.
  • Device and Activity state remembered across restarts and updates (on/off, current input, running Activity per room). Help in Active Activities corrects what Astrion believes, then Fix replays the Activity.
  • "Off" for a room: "stopActivity": "<room>" (or "*") on tiles and hotkeys.
  • Pages that follow an Activity: "hideMode": "full" hides a page completely while its Activity is off.
  • Activity wizard: network Apple TVs (wake, sleep, app to launch) and a Home Assistant entity picker.

Remote & dock

  • Dock screen: battery screen, dim night clock (time, date, weather, now playing) or the dashboard. "Now playing" can follow the Activity of the remote's own room (dock.room, nowPlayingEntity).
  • Standby & battery (power block): Home Assistant goes quiet a while after the screen turns off (only listed entities stay live), motion-wake limited in time, screen timeout held by the app.
  • Settings reorganized into groups (Display, Connections, Web configuration, Sound & touch, Language, About), with Reload dashboard, Restart the app and Test Find my remote.
  • Find my remote can be stopped from the remote ("Found me!").
  • Logs without adb: http://<remote>:8080/logs.

Home Assistant & several remotes

  • Each remote has an identity (stable id + name, GET /info, rename from Settings or the web page), sent with every webhook push.
  • Battery pushed to Home Assistant through the webhook.
  • Hotkey action "Home Assistant event" (astrion_button, with the key, page and remote).
  • Works with the Astrion Custom Dashboard integration 2026.10.0 (devices, Activities, page select, ring, events).

Cards

  • Long press on button_grid / scene_grid tiles (long_press block).
  • apple_tv_remote: compact and full layouts, app shortcuts, long press on the trackpad (context menu, ±10 s), fast-forward / rewind, seeking — and a builder preview matching the chosen layout.
  • Hotkeys and tiles can open a page as a popup ("pageMode": "popup").
  • Monitor: vertical layout and scrolling long values.
  • Select: "dropdown" layout and text size (#93 by @dpdonovan-cyber).
  • TV remote: volume row (to the TV or a soundbar's remote).
  • Switch and picture elements: PNG icons; picture elements on_color.
  • Web page: delete icons and IR database files (with a warning when still used).

Changed

  • Settings always reachable (long press Menu, see above) and minimal starter dashboard for new remotes.
  • Connection banner: none without Home Assistant; on a new remote it shows the remote's web address; with Home Assistant unreachable it also gives that address. Tap it to reconnect.
  • Home Assistant connection, lighter: updates published as they arrive, one heartbeat, reconnects follow the network.
  • Beta updates are offered only when newer than the installed app, under their real name.
  • Hotkey lists show keys as named on the remote ("Menu (MAIN)").
  • The IR sniffer's "Send to my remote" merges models into the IR database instead of replacing the file.
  • Select card follows the theme; TV remote card defaults (see above).

Fixed

  • Apple TV: the remote-control session no longer drops every 30 s (tvOS AirPlay 2); title and artwork no longer flicker on live streams.
  • IR: devices imported from the Harmony archive work in Activities; codes captured with old IR sniffer versions are repaired on load.
  • The screen no longer wakes by itself on a still remote; D-pad focus outline no longer stays visible on bound arrow keys.
  • A Harmony Activity tile no longer stays outlined after turning it off (single hub).
  • Builder: importing a dashboard keeps popup pages' openMode.
  • The downloaded update APK no longer stays in the app cache.
  • Cards: switch #RRGGBB colours, source select icon, speaker group labels, clock & weather 24 h calendar event, climate without fan modes, fan step layout.

Internal

  • Android Gradle Plugin 8.13.2, Gradle 9.5.1.

v1.2.0

Choose a tag to compare

@dckiller51 dckiller51 released this 07 Oct 12:20
1ca17cf

[1.2.0] - 2026-10-07

Added

  • Apple TV: direct control over the network — no Home Assistant, no Harmony hub. A new native client (appletv/ package) speaks Apple's Companion protocol: mDNS discovery (_companion-link._tcp through NsdManager), HAP PIN pairing (SRP-3072 → Ed25519 long-term keys), pair-verify, and a ChaCha20-Poly1305-encrypted OPACK session. It gives remote-control keys (D-pad, Select, Menu, Home, Play/Pause, volume, Siri), wake/sleep, playback commands, the installed-app list and app launching. The Companion port is dynamic, so each connect re-resolves the device by its mDNS service name (falling back to the last known address, which is updated and saved when it changes), and a supervisor reconnects with back-off. Pairing produces credentials in the same ltpk:ltsk:atv_id:client_id format pyatv/Home Assistant use.
    • Each paired Apple TV is exposed as a Home-Assistant-shaped media_player entity (media_player.appletv_<name>, generated once and kept stable across renames) via a new local-entity overlay in HaClient (setLocalEntity, merged into entities, plus a localServiceHandler hook in callService). Its attributes mirror what HA's Apple TV integration gives: source_list (app names), supported_features, volume_level, and a playing/paused/idle/off/unavailable state derived from the controls the Apple TV currently offers. Because of that, every existing media_player, source_select, button, hotkey and picker works with it unchanged, with or without Home Assistant configured — including select_source to launch an app by name.
    • apple_tv_remote gained an appleTv option (a paired Apple TV's entity id): commands go straight to it via a new astrion_appletv.send_command service. deviceId/hub (Harmony) still work exactly as before; appleTv wins when both are present. The play/pause button uses a real toggle instead of tracking state locally.
    • Devices page: the placeholder "Apple TV — Coming soon" tile is now real: scan the network, enter the PIN shown on the TV, rename, remove. New ConfigServer endpoints /appletv-scan, /appletv-pair-start, /appletv-pair-finish, and /devices-config now includes appleTvs. /save-connection only rewrites the Apple TV list when the form carries atv_present=1, so older clients can't wipe paired TVs. Builder: the apple_tv_remote form offers "Control via → Apple TV (direct) / Harmony hub" once at least one Apple TV is paired.
    • Lint/style cleanup for the above: split AppleTvDevice's command/service dispatch into small
      lookup tables (and moved the stateless remote-button mapping into a new AppleTvCommands.kt),
      broke CompanionAuth's pair-verify/pair-setup into single-purpose steps, split CompanionLink's
      frame read/write paths, and moved ConfigServer's appleTvs JSON block into its own function —
      all to stay under this project's ktlint/detekt limits (line length, cyclomatic complexity, nested
      blocks, throw count, parameter count). AppleTvRegistry now persists an Apple TV's updated
      IP/port itself (it already has the Context it needs) instead of MainActivity doing it, which
      also keeps MainActivity's own footprint for this feature to eight lines.
    • Known limits (Companion protocol): the currently playing title/artist/artwork and the foreground app are not available over Companion (they need the separate AirPlay/MRP channel), so those attributes are absent rather than guessed. Adds the org.bouncycastle:bcprov-jdk18on dependency (X25519/Ed25519/ChaCha20-Poly1305 aren't in the Android platform until API 33, and minSdk is 26).
  • scene_grid tiles can now be highlighted off an arbitrary HA entity's state, not just the Activity-tracking already there. The existing Activity-based border-highlight (a tile with "activity" or "track"+"room") stays exactly as it was, and is now joined by a second, independent, combinable mechanism: the same "state_entity"/"state_value"/"active_color"/"active_border" fields already on button_grid (see its own class doc) — useful for a scene_grid tile that isn't an Activity at all, e.g. a plain navigation tile to a "Lights" page lit up while any light in the room is on. Unlike the Activity mechanism (border only), this one also changes the tile's background via "active_color", same as button_grid. Web builder (cards.js): the scene_grid item form gained the same State entity/Target state/Active color/Accent border fields already on button_grid's form.
  • Media player: when there's no artwork at all (not just an external-URL loading failure — see the Fixed entry above), the full-card placeholder now shows the active app's name (app_name) under the cast icon. Previously a bare, unlabeled cast icon regardless of which app was active. No change to the compact/tile header's small circular avatar (too small to fit a label). This doesn't retrieve an actual per-app icon: neither Home Assistant's Apple TV integration nor its docs appear to expose one (only app_id/app_name as plain text), so app_name is the most specific thing there currently is to show.
  • button_grid buttons couldn't close a popup after their action — "closePopup": true was silently ignored. An oversight in the Harmony/IR support added just above: fire() never read "closePopup" at all (and the web builder's button_grid form didn't even show the checkbox), even though it's exactly as applicable to a button_grid button as to a scene_grid one — arguably more so, since button_grid is the natural fit for a TV/Projector source-picker popup. fire() now checks "closePopup" last, after every other action on the button, same as scene_grid's own. Web builder: the button_grid item form gained the same "Close the open popup after this button's action" checkbox already on scene_grid's form.
  • Web builder: a page opened as a popup only by a tile (not linkedPage/openWhenEntity) can now have its own popup size/position, in the builder and on JSON import. The page dialog's popup width/height/position row, and normalizePageFromJson's JSON-import path, only ever showed/kept those fields when the page itself had linkedPageMode: "popup" or openMode: "popup" — neither of which a page that's purely opened via a tile's own "pageMode": "popup" (added above) has, since that trigger lives on a tile on some other page, invisible to this page's own dialog. Editing such a page in the builder silently ignored any popup size already in the JSON, and re-saving it from the builder would drop it outright. Added a new standalone checkbox, "Opened as a popup from a tile elsewhere on the dashboard", next to the existing "Linked page"/"Auto-open" popup-mode pickers — checking it reveals and persists the same width/height/position fields independent of the other two triggers. normalizePageFromJson (the paste-box and device-load import path) now keeps popupWidth/popupHeight/popupPosition whenever present on a page, rather than only when linkedPage+linkedPageMode: "popup" were also both set, and the dialog now pre-checks the new checkbox itself when it detects that case on open (saved geometry present with neither other trigger set to popup).
  • button_grid: a button can now fire a direct Harmony command/Activity or a local IR command, not just an HA service call. Previously button_grid was hard-wired to a plain service call (see ButtonGridCard.kt's old doc: "each firing a HA service call") while scene_grid had the richer action set — an inconsistency there was no real reason for. fire() now fires every action a button carries independently: service (unchanged, now optional), a direct Harmony device command (harmonyDevice+harmonyCommand, optionally hub), a direct Harmony Activity start (activityId, optionally hub), and/or a local IR command (irDevice+irCommand, no hub needed) — same fields, same behavior as scene_grid's own Harmony/IR options, just without scene_grid's page/composed-Activity/tracking options, which don't apply to a plain action button. A button can combine more than one (e.g. call an HA service and send a direct IR command in the same tap). Web builder (cards.js): the button_grid item form gained the same Harmony action picker and IR device/command fields already used on scene_grid, reusing the existing giHarmonyMode/giIrDevice widgets rather than duplicating them; a button now needs at least one of Service/IR/Harmony set, validated on save.
  • Open a page as a popup from a normal tile, and optionally close it again after an action. Until now the floating popup overlay (LinkedPagePopup) could only be triggered by a linkedPage swipe-up or by openWhenEntity+openMode: "popup" — a scene_grid/title tile's own "page" always did a full pager navigation. CardContext gained openPagePopup(pageName) and closePopup(), built in Dashboard() from the exact same linkedPopupPage/popupOpenedByEntityId slot the other two triggers already share (so only one popup is ever on screen, however it was opened), reusing the target page's own popupWidthFraction/popupHeightFraction/popupPosition — same as openWhenEntity. New per-tile options: "pageMode": "popup" next to scene_grid's existing "page" (and "${prefix}_pageMode" for TitleCard's title_page/subtitle_page) opens it as a popup instead of navigating; "closePopup": true (and "${prefix}_closePopup") dismisses whichever popup is currently open, fired after every other action on that tile — e.g. a small TV/Projector popup where tapping one source tile sends the IR/Harmony command and closes the popup in the same tap. closePopup is independent of page/pageMode on the same tile, so a tile elsewhere can open the popup while a different tile inside it closes it. Web builder (cards.js): the gri...
Read more

v1.1.0

Choose a tag to compare

@dckiller51 dckiller51 released this 06 Sep 20:43
e39dd96

[1.1.0] - 2026-09-06

Added

  • Devices catalog (/), rebuilt as a proper "add a device" hub instead of a connection form. Opening the remote's own IP now lands on a hero + four category tiles (Home Assistant, Harmony, IR Devices, Apple TV — the last a placeholder for direct control, not built yet, present so cards will be ready to reference it once it is), each with a live device count. Tapping a tile drills into that category's own list + "+ Add device" flow, Home-Assistant-integrations style, instead of the old single long scrolling form. ConfigServer.kt's previous serveForm() (one large server-rendered HTML string) is replaced by two static single-page apps served from assets/docs/: devices.html/js/devices-page.js (this new page, root /) and the existing index.html/js/*.js (the dashboard/card builder, /builder/) — both now share the same static-asset serving path (serveDocsAsset()), so any file dropped into assets/docs/ (styles, scripts, images) is reachable from either page without a route added per file.
  • Home Assistant device catalog. Cards that reference an HA entity (Light, Switch, Cover, Climate, Media Player, Camera, Fan, Vacuum, Weather, Select) no longer take a free-typed entity_id — pick a domain and a live entity on the Devices page, give it a name (pre-filled from HA's own friendly_name, editable), and it shows up by that name in the matching card's picker in /builder/. New haDevices array in dashboard.json (parallel to irDevices), new GET /devices-config endpoint (full HA connection + Harmony hub read-back, used to pre-fill the edit forms — GET /harmony-hubs stays id+name-only, unchanged, still used by the card/hotkey pickers). Editing an existing card that references an entity not in the catalog (an older dashboard, or one hand-edited via raw JSON) doesn't silently blank the field — an extra "(not in catalog)" option is injected so the real value round-trips even though it can't be changed without adding it to the catalog first.
  • Cache-Control: no-cache, no-store, must-revalidate on every docs/ asset response (serveDocsAsset()) — these ship inside the APK and change on every app update; a stale cached .js silently paired with a fresh .html (or vice versa) produces exactly the kind of confusing, hard-to-reproduce bug this is meant to rule out.
  • Charging screen. While the remote is genuinely sitting on its charging dock, the dashboard is replaced with a big battery percentage and a pulsing charging glyph — same idea as a phone's own charging screen. Auto-dims (window brightness down to near-zero) after 15s with no touch, undims instantly on the next one; fully restored the moment it's picked up or unplugged.
    • Docked vs. plugged into a computer for development is a real distinction here, and a non-obvious one. Both report identically through BatteryManager — confirmed on real hardware, this device's dock has two power-only charging pins, no USB data lines at all, and Android still classifies that as generic "AC" charging just like a PC's USB port would. What actually differs is whether a USB data connection exists: UsbManager's own connection state (mConnected/mConfigured in dumpsys usb) reads false on the dock and true over a real cable. That state isn't exposed through the public UsbManager SDK constants (ACTION_USB_STATE/USB_CONNECTED exist in AOSP and fire at runtime, but aren't in the public stub jar) — used as raw string literals instead, which is legitimate since both are just string values handed to fully public APIs (IntentFilter.addAction, Intent.getBooleanExtra), not a reach into a hidden class member.
    • New ChargeDockMonitor.kt: owns the detection (a BroadcastReceiver on ACTION_POWER_CONNECTED/_DISCONNECTED + the USB state action) and the dim timer, as its own class rather than inline in MainActivity.kt — which was starting to accumulate too many unrelated concerns in one place (detekt's LargeClass agreed). MainActivity.kt now just owns one instance and delegates: start()/stop() from its own lifecycle methods, onUserInteraction() (the standard Android hook that fires on any touch/key regardless of which view or Compose node consumed it — no separate touch listener needed) forwarded straight through.
    • New ChargingScreen.kt, themed via the same ProvideTheme/LocalTheme system every other screen uses. rememberBatteryState() and batteryGlyph() — both previously private to Topstatusbar.kt — are now shared between the status bar's compact battery indicator and this screen's much larger one, rather than duplicated.
    • 2 new strings (charging_screen_charging/charging_screen_full) in values//values-fr/.
  • IR devices can now reference the external ir-database picker instead of embedding pre-resolved codes. Previously every dashboard.json had to carry a fully-resolved {freq, pattern} table for every IR command, baked in once by the web builder at build time from either a hand-pasted Pronto code or a bundled ir-database/ catalog. That catalog (docs/ir-database/ and its src/main/assets/docs/ir-database/ mirror) is removed in this release — instead, pick the devices you actually own on the picker, copy the generated JSON files onto the remote's /sdcard/astrion/ir-database/, and reference them from the web builder ("2. IR Devices" → "Reference the ir-database", category + brand + model). The app resolves Pronto → {freq, pattern} itself, on-device, the first time a command is used, and caches the result — a database update or a newly-added device model no longer means re-touching or re-flashing every dashboard.json built against it, and it's no longer blocked by Logitech's own Harmony device catalog being unmaintained (most recent hardware was never going to be in there in the first place). Hand-pasting a single Pronto code for a one-off button is still supported side by side, unchanged — new devices choose either mode per device, not globally.
    • AppConfig.kt: new IrDeviceSource sealed class (Inline — the old embedded-commands shape, SdCardRef — a category/brand/model pointer). IrDeviceConfig carries a source instead of always embedding commands directly; DashboardLoader.kt's parser and encoder round-trip both shapes, so every existing dashboard.json with inline-resolved commands keeps working exactly as before.
    • New IrDatabaseRuntime.kt: a Kotlin port of the web builder's prontoToPattern() (verified value-for-value against the original on real captured codes), plus the /sdcard/astrion/ir-database/<category>.json loader/resolver, with a two-level cache (parsed file per category, resolved step per command) so repeat button presses skip both the disk read and the Pronto math.
    • Dashboard.kt (tile taps) and MainActivity.kt (hardware hotkeys) both now resolve through this same runtime instead of each reading IrDeviceConfig.commands directly — one shared IrDatabaseRuntime.resolve()/resolveFrom(), not parallel copies of the same lookup.
    • Web builder (docs/ and src/main/assets/docs/, identical patch to both): the "Add IR device" form now offers the two modes above instead of the old category→brand→model→command cascade fetched from the now-removed bundled catalog. giIrCommand (scene_grid tiles) and the Activity wizard's power/input/volume command fields are now free-text inputs with <datalist> suggestions instead of <select>s, since a referenced device's real command list only exists on the phone at runtime, not in the builder session.
  • New "IR devices" panel on the local config page (/), next to Icons. A link out to the picker, plus a same-shape-as-Icons upload form for its own curated <category>.json files straight from the device — for when you'd rather not hand-copy them onto /sdcard/astrion/ir-database/ yourself. New POST /ir-database route in ConfigServer.kt: validates the upload actually has "category"+"brands" before writing anything (a half-written or garbled file here would otherwise silently break every IR button sourced from it, not fail loudly once), then calls IrDatabaseRuntime.invalidate() — picked up on the very next command send, no restart needed. The same route also answers the picker's own "Send to my remote" button directly over the LAN (Accept: application/json switches the response from the page's usual redirect-with-flash-message to a plain JSON body); whether that button actually reaches the device depends on the calling browser's mixed-content policy, since the picker is HTTPS (GitHub Pages) and this server is plain HTTP — when it's blocked, downloading and copying the files by hand (or this new on-device upload form) works regardless. 5 new strings across values//values-fr/ for the panel's heading, description, link, button, and note.
  • The web builder can now read straight off this remote's own /sdcard/astrion/ir-database/ when opened via /builder/. New GET /ir-database (which category files are actually present) and GET /ir-database/<category>.json (raw contents) — same-origin, so none of the picker's mixed-content caveats apply here. The "Reference the ir-database" device form picks this up automatically: a category → brand → model quick-pick appears above the manual fields whenever this remote actually has matching files already, filling in category/brand/model and every real command id from that file — no more typing "known command ids" by hand for anything already copied over. The manual fields stay right below it, unchanged, for anything not on this remote yet.
  • Fixed: ir-database category filenames are matched case-insensitively (TV.json now resolves the same as tv.json). Android's filesystem is case-sensitive, and a strict match here failed silently — not a crash, just every IR button in that category quietly...
Read more

Dev build 1.3.0

Dev build 1.3.0 Pre-release
Pre-release

Choose a tag to compare

@dckiller51 dckiller51 released this 23 Aug 08:16
3eec0f5

chore(release): 1.3.0...

v1.0.0

Choose a tag to compare

@dckiller51 dckiller51 released this 22 Aug 19:25
176c464

[1.0.0] - 2026-08-22

Added

  • scene_grid: icon_fill mode. New optional icon_fill: true on a scene_grid card's options scales each tile's icon to fill the tile's full height (ContentScale.FillHeight) instead of the default fitted size (ContentScale.Fit) — useful for icon-only grids ("show_labels": false). New checkbox in the web editor's scene_grid card form, with a live preview. (#22 by @ClemsonJeeper)
  • Live HA entity data in the dashboard editor preview. Card previews in the web editor (/builder/) now render with real state, names, and attributes fetched from Home Assistant via a new /ha-states endpoint, instead of always showing the static example data — falls back to the mocks automatically when there's no HA connection or the editor is opened outside the device (e.g. GitHub Pages). Entity ID fields also get autocomplete against the live entity list. (#13 by @ClemsonJeeper)
  • Getting Started guide. New GETTING_STARTED.md — first-time setup walkthrough for a Sanytron Astrion HA100, from enabling ADB through connecting Home Assistant and loading the first dashboard.json, plus troubleshooting and update instructions. Linked from the README. (#18 by @dskudrin)
  • VacuumCard: configurable room-clean service. Room buttons used to always fire the Roborock/Xiaomi-style vacuum.send_command / app_segment_clean — silent no-op on integrations that expect their own service instead, e.g. Dreame's dreame_vacuum.vacuum_clean_segment. New optional options.room_clean_action: { domain, service, parameter } lets a card call any domain.service with the room/segment id written into a named field instead — entity_id still goes through the same target mechanism every other card's service call already uses. Leaving it unset keeps today's exact default call, so every existing dashboard.json is unaffected. New optional fields in the web editor's vacuum card form.
  • Hardware button → open Settings or Active Activities. A hotkey can now be assigned openOverlay: "settings" or openOverlay: "activities" instead of a page/service/Harmony action — same two overlays already reachable by swiping down from the top bar or up from the page dots, now also bindable to a physical remote button. New "Open overlay" action in the web editor's hotkey dialog.
  • Hardware button → return to the current Activity, per room. A hotkey can now be assigned openCurrentActivityRoom: "<room>" to jump straight to the page of whichever tracked Activity is currently active in that room — the direct, no-picker counterpart to openOverlay: "activities", for a remote/page already dedicated to one room. Resolved live via ActivityRuntime.activeActivity(room); does nothing if that room has nothing active. New "Return to current Activity" action in the web editor's hotkey dialog.
  • Configurable "return to parent" button. Child pages (PageConfig.parent) no longer hardcode the hardware BACK key for jumping back to their parent — a new optional parentKey field picks any physical button instead (defaults to "BACK", so existing configs are unaffected). If the page also binds its own hotkey on that button, the hotkey wins and the parent-jump is skipped, same precedence as before. New picker in the web editor's page dialog, shown once a parent page is selected.
  • Camera card. New camera card type for displaying a Home Assistant camera.* feed, in two modes: live stream (MJPEG, with automatic fallback to still snapshots if the stream stalls) or snapshot-only (still frame refreshed at a configurable interval). Aspect ratio and fit (cover/contain) are configurable. Tapping the card opens a fullscreen viewer with pinch-to-zoom (1x–8x) and pan, without interrupting the live stream. (#8 by @ClemsonJeeper)
  • Configurable theme colors. The app's color palette (12 semantic tokens: backgrounds, text, icons, accents, state colors) is now configurable via dashboard.json, with a new "Theme & Colors" section in the web editor to adjust it visually (swatch + hex field + per-token reset) without touching the JSON. An empty theme block reproduces the original palette. Existing color fields (SwitchCard.on_color, per-scene color in SceneGridCard) now use the same color picker. (#11 by @ClemsonJeeper)
  • Drag-to-reorder page tabs in the web editor, alongside the existing card reordering. Editor-only, no native app changes. (#10 by @ClemsonJeeper)
  • Toast notifications. The "Save to device" confirmation in the editor now uses a non-blocking, auto-dismissing toast instead of a modal alert(), matching the editor's other inline feedback (icon upload, Harmony discovery, etc.). (#9 by @ClemsonJeeper)
  • Fan card "step" style. New "style": "step" option for fan cards, for fans that report discrete speed levels rather than a continuous percentage: a single row showing the current speed name/percentage with -/+ buttons that call Home Assistant's fan.increase_speed/fan.decrease_speed services directly, instead of computing and sending a percentage via fan.set_percentage like the simple/full styles do. No power button or name row in this layout — just the stepper. Builder support in the fan card's style dropdown and a matching live preview. Existing simple/full styles and their own percentage-step config (options.step) are unaffected. (#14 by @ClemsonJeeper)

Internal

  • Migrated all hardcoded Color(0x...) literals in Compose UI to the new theme system (ThemeColors / LocalTheme), across every card and screen (dashboard, settings, status bar, overlays).
  • HaClient: new dedicated HTTP client with no timeout for long-lived MJPEG streaming, plus a raw-bytes fetch method.
  • ConfigServer: new GET /camera-snapshot endpoint proxying a single HA camera frame to the web editor (the browser has no HA token of its own).
  • ConfigServer: new GET /battery endpoint exposing the wall tablet's own battery level and charging state as JSON ({ level, charging }), for the companion Home Assistant integration.
  • HaClient.onResult(): no longer assumes every WebSocket result payload is an array — service calls (e.g. the fan step buttons' increase_speed/decrease_speed) can return result: null/{}, which is now handled safely instead of throwing during response parsing.

Fixed

  • Theme migration: several card tiles lost their button/icon contrast. CoverCard, LightCard, and MediaPlayerCard painted their outer tile background with theme.controlBackground — the same token their round buttons and icon backgrounds also use — so those buttons visually disappeared into the tile (no more "circle" around the cover up/down icons, the main icon, the light card's cycle button, or the media player's off-state avatar). Tile backgrounds now use theme.cardSurface instead, restoring the contrast the pre-theme hardcoded colors had (matching the convention already used by FanCard/ClimateCard and by MediaPlayerCard's own detail dialog).
  • Activity wizard: adding a Harmony device silently failed. In the "Devices" step, renderWizardHarmonyAddFields() rendered the Hub/Device pickers straight into the same container as the "+ Add Harmony device" button — but that picker's own render call replaces its container's entire content, wiping out the button (and the placeholder selects the code then tried, and failed, to swap in) the moment a Harmony hub was reachable. Net effect: picking a hub and device had no button to add them with, so the wizard always reported "Add at least one device." on Next. Now renders the picker into its own sub-container and reuses the same shared Hub→Device wiring as the hotkey and scene-item forms. (#16 reported by @bilbon0bud)
  • Weather forecast bar (Clock & Weather card) rendered as a flat color instead of a two-tone gradient — restored using theme.success at two alpha levels.
  • Compact Fan card layout used the same background color for on/off — on now uses theme.success at low alpha, matching the pattern already used elsewhere in the same card and in ClimateCard.
  • Connection banner no longer distinguished "connecting" from "disconnected" (both used controlBackground) — connecting now uses theme.accentSecondary, keeping danger for the failure states and controlBackground for disconnected.

uninstall

Choose a tag to compare

@dckiller51 dckiller51 released this 22 Aug 00:45

Astrion Custom - Uninstallation Tool

This package contains the automated tool to cleanly remove the Astrion Custom Dashboard from your Sanytron Astrion HA100 remote control and restore the stock environment.


What's Included

  • uninstall.bat : Automated script to completely remove the application via ADB.

How to Use

  1. Prepare your remote:
    • Make sure USB Debugging is enabled in your remote's Developer Options.
  2. Connect your device:
    • Plug your remote into your Windows PC using a data USB cable.
    • Accept the USB debugging security prompt on your screen if requested.
  3. Run the uninstaller:
    • Download and extract this .zip archive into a folder on your computer.
    • Double-click on uninstall.bat and follow the on-screen instructions.

Once completed, the remote will automatically fall back to the stock HaRemote launcher.

initial-installation-v0.9.0

Choose a tag to compare

Initial APK Installer (Bootstrap)

This package is designed for the initial installation of the application on Android devices using ADB (Android Debug Bridge).

Note: Once this initial version is installed on your device, all future updates will be handled automatically by the application itself. You will not need to use this tool again.


Prerequisites (Android Device)

Before running the installer, make sure your phone is properly configured:

  1. Enable Developer Options: Go to Settings > About phone, and tap on Build number 7 times.
  2. Enable USB Debugging: Go to Settings > Developer options and toggle on USB Debugging.
  3. Connect & Authorize: Plug your phone into your PC via a data USB cable and allow the debugging prompt that appears on your phone screen.

How to Use

  1. Extract the contents of this .zip archive into a folder on your computer.
  2. Make sure your Android device is plugged in and recognized.
  3. Double-click on install.bat.
  4. Follow the on-screen instructions. The script will automatically detect your device and install the application.

v0.9.0

Choose a tag to compare

@dckiller51 dckiller51 released this 19 Aug 10:55
605f887

[0.9.0] - 2026-08-19

Added

  • ConfigServer: new GET /activities, GET /activities/active, POST /activities/start, and POST /activities/stop routes, exposing ActivityRuntime's per-room Activity state (previously only readable on-device, via the Active Activities overlay from 0.8.0) to an external controller — built for the companion Home Assistant integration's new sensor.active_activity_<room> and select.activity_<room> entities, but usable by anything that can speak plain HTTP. /activities lists every trackable Activity (composed or track: true) as {id, name, room, icon}; /activities/active reports which one (if any) is active per room; /activities/start starts one by id; /activities/stop stops whichever is active in a given room.
  • ActivityRuntime.clear(room) — the counterpart to markActive(), explicitly marking a room as having nothing active. Only meant to be called once the room's Activity has actually been stopped (its own poweroff devices sent, or a Harmony hub's own "-1"/PowerOff confirmation arriving through bind()), never speculatively ahead of the stop action itself.
  • Dashboard.kt: stopActivity(room) — the missing counterpart to switchActivity()/startActivity(). For a composed Activity, sends each device's own powerOffCommand (same as switchActivity()'s outgoing-diff branch, just with nothing incoming) and clears the room. For a Harmony-backed Activity, sends PowerOff to that Activity's own hub only — Harmony has no per-Activity stop command, a hub always runs exactly one Activity at a time, so this is the narrowest possible "stop" and, unlike the existing PowerOff hotkey, never touches a different room sharing the same hub. This closes a real gap from 0.8.0: the Active Activities overlay could show what was running per room but had no way to end just one of them, short of a blanket "turn everything off" hotkey.
  • Active Activities overlay: each row now has a dedicated "Stop" button (R.string.stop_activity — "Arrêter" in French) wired to the new stopActivity(), so a running Activity can be ended directly from the list without hunting for its poweroff button elsewhere.
  • Dashboard() gained onActivityRuntimeReady/onStartActivityReady/onStopActivityReady callback parameters, invoked once per ActivityRuntime instance (i.e. once per dashboard.json load, same lifetime remember(config) { ActivityRuntime(config) } already had). MainActivity stores what they hand up and feeds it to ConfigServer as plain lambdas — the same hoisting pattern onSetPage already used for the page pager, needed here because ActivityRuntime/switchActivity/startActivity are created inside Compose (closing over activitiesById, harmonyRegistry, client) rather than in MainActivity itself.
  • Hierarchical pages: PageConfig gained an optional parent (another page's name), making it a child page in a navigation tree instead of a flat top-level one. Entering a child is unchanged — any card's own navigateToPage, e.g. a scene_grid tile with "page": "Apple TV" on its parent's page, exactly like flat navigation always worked. Leaving is new: the hardware BACK key, previously a no-op unless a page-specific hotkey bound it (MainActivity.dispatchKeyEvent only falls through to onBackPressed() once every hotkey handler for that key comes back empty), now jumps to the current page's parent when nothing else claims it first — so a page with its own BACK hotkey (an Apple TV's Back/Menu, say) is completely unaffected. A dashboard with no parent set anywhere behaves identically to before this existed.
  • PageIndicator reworked around siblings instead of the whole flat page list: the dots now represent only the current page's siblings (every page sharing the same parent, root pages included), windowed to at most 5 with a small "…" on a clipped edge — the fix for large dashboards' dot row growing to take up a quarter of the screen height. A child page also shows a ‹ ParentName chevron on the left (44dp min tap target), the on-screen twin of the hardware BACK key's new behavior. Swiping itself is unaffected by any of this — still one continuous pager over the full flat pages list in file order, same as always.
  • Builder (docs/js/pages.js, index.html): the page dialog gained a "Parent page" dropdown, offering every page except the one being edited and any of its own descendants (pagesUnavailableAsParentOf()) — hierarchies can't be built into a cycle from the UI. Renaming a page that others point to as their parent cascades the new name to those children automatically; deleting a page with children asks for confirmation and promotes them to top-level rather than deleting or orphaning them to a name that no longer exists. docs/js/preview.js's tab bar prefixes a child page's label with ↳ so the hierarchy is visible in the tab strip itself, not just inside each page's own settings.
  • ConfigServer: new GET /version route reporting {version, versionCode} — built for the companion Home Assistant integration's new update.app_update entity, which compares it against the latest GitHub release.
  • New SelectCard ("type": "select") for any input_select.* or select.* entity — an adaptation of Home Assistant's Mushroom select card, the same way CoverCard adapts Mushroom's cover card: icon in a colored shape, a name/state column, and a dedicated control area (the option picker itself) below/beside/stacked per layout (default/horizontal/vertical, same 3 variants as CoverCard), instead of the whole tile being one flat clickable dropdown. Reads the entity's options attribute and current state, calls <domain>.select_option on pick — the domain (input_select or select) is read straight from entity_id, so one card type covers both without extra config. Answers a GitHub feature request for exactly this (e.g. picking between "TV"/"Projector" for a room's video output) — deliberately a separate renderer from SourceSelectCard rather than a shared/branching one, since that card is specific to media_player's source_list/source attributes and media_player.select_source service, none of which exist on an input_select/select entity.
  • Builder (docs/js/cards.js, index.html, docs/js/preview.js, docs/js/mocks.js): added the "Select / dropdown (select)" card type to the picker, its own form (entity, name, icon color, layout), and a full live preview (new SELECT_MOCK, an input_select.video_output_living_room with "TV"/"Projector" options — the same example from the feature request) matching SelectCard.kt's actual rendering, reusing the same .pc-icon/.pc-namestate/.pc-slider-shaped CSS the cover/light previews already established rather than introducing new classes.

Fixed

  • MainActivity.DEBUG_KEYS was left true, showing an "Unmapped key: N" Toast on screen every time an unbound remote button was pressed. Now false; unmapped key codes are still logged via Log.i(KEY_TAG, ...) for adb logcat when actually debugging a new remote.

v0.8.0

Choose a tag to compare

@dckiller51 dckiller51 released this 17 Aug 00:15
feac8a9

[0.8.0] - 2026-08-16

Added

  • AV Activities: any scene_grid tile or hotkey can now be marked "track": true with a "room", turning it into a tracked Activity ("Watch Apple TV", "Listen to Music"...) — no separate config section, the tile's existing entity_id/activityId+hub/harmonyDevice+harmonyCommand is still what actually runs; track/room just add bookkeeping. At most one Activity is active per room at a time; different rooms are fully independent.
  • Composed Activities (ActivityConfig/ActivityDeviceConfig, AppConfig.activities): for setups where more than one device needs orchestrating — the case a Harmony hub normally handles internally — Astrion can now do it itself, mixing IR/Harmony/HA devices freely within the same Activity. On switching Activities within a room, a device used by both the outgoing and incoming Activity is left alone (no needless power cycle, just a possible input change); a device only in the outgoing one gets powered off (switchActivity() in Dashboard.kt). Referenced from a scene_grid tile's new "activity" field; always implicitly tracked.
  • IR Devices (IrDeviceConfig, AppConfig.irDevices — replaces the old IrActivityConfig/irActivities macro list): a registry of named commands per physical device ("power", "hdmi1", "volume_up"...), resolved once at build time from either a community ir-database/ entry or a hand-pasted Pronto Hex code. This is the fully-offline baseline for AV control — no Harmony hub, no Home Assistant, no cloud dependency, works even if every external service involved disappears.
  • ActivityRuntime: scans every page/hotkey for track: true items, and every entry in AppConfig.activities, once per dashboard.json load; tracks which Activity is active per room (activeByRoom), updated via trackTap() (right after SceneGridCard fires a single-action tile), markActiveById() (after a composed Activity finishes its start sequence), and bind() (mirrors a Harmony hub's live Activity state into the matching room, so an Activity started from elsewhere — another remote, an HA automation, the physical Harmony remote — shows up too).
  • HarmonyHubClient: currentActivityId/activityState (StateFlow), populated from the hub's unsolicited connect.stateDigest?notify push frames (activityId + activityStatus, distinguishing "starting" from "running" — confirmed against several real hub captures) and from a new one-shot getCurrentActivity() request/response call made right after connecting, since the push only fires on the next change.
  • HarmonyHubRegistry.clientsByLocalId — exposes every configured hub's client, not just the default one (client()), for callers like ActivityRuntime.bind() that need to wire up all of them.
  • New "Active Activities" overlay: swipe up from the bottom edge (page-indicator dots) — the mirror gesture of the existing swipe-down-for-Settings — lists every currently active Activity grouped by room; tapping one jumps straight to its page. Reached only by this gesture, like Settings, so it never appears in the page pager or its dots.
  • Builder: new "IR Devices" section — the community ir-database/ cascade picker (category → brand → model → command) now feeds a per-device command registry instead of a one-shot macro; a "Import all commands from this model" button pulls every command from a picked model in one go (ids already present are left alone, so re-importing after a manual rename doesn't clobber it); a manual Pronto Hex paste path covers devices/buttons not in the database; each saved command can be renamed inline (✎, no browser prompt(), consistent with the rest of the builder).
  • Builder: new "Activities" section — a page-by-page wizard for composed Activities, mirroring Logitech Harmony's own Activity setup flow rather than one long form: pick a type (just prefills a name) → name/room/icon/page → select devices (local IR, Harmony hub+device, or a bare HA entity id — mixable) → one screen per selected device for its power-on/power-off/input commands, on/off-cycle toggles, and a delay → which device handles volume → review (flags any device with no commands set — it would silently do nothing at runtime) → save. Editing an existing Activity jumps straight to the name/room screen. A device already added can be edited in place, not just removed and re-added.
  • Volume routing for a composed Activity: after picking a volume device, a further screen picks its volume-up/volume-down/mute commands (skipped for an HA device — it always uses the fixed media_player.volume_up/volume_down/volume_mute services). On save, if the Activity has a page, these are written as page-scoped hotkeys on it (PageConfig.hotkeys, which already override global bindings while that page is on screen — no new runtime mechanism needed). If that page already has a hotkey on VOLUME_UP/VOLUME_DOWN/MUTE, the builder confirms before overwriting rather than silently taking priority over an existing binding.
  • HotkeyConfig gained irDevice/irCommand (send a local IR command directly from a physical-button binding, same as a scene_grid tile already could) and devices (a bookkeeping hint — see next point).
  • TrackedActivity/HotkeyConfig/scene_grid items gained an optional devices list: which physical devices an Activity is known to involve, independent of whether it's composed or a lightweight track-only tile. switchActivity() in Dashboard.kt now reads this from whatever Activity was previously active in a room — Harmony-backed or composed — instead of only from a composed ActivityConfig's own device list. This closes a real gap: switching from a Harmony Activity to a composed one had no way to know a device shared between them (e.g. a soundbar with only a PowerToggle command, no discrete on/off) was already on, and could send an unwanted toggle that turned it off instead of confirming it stayed on.
  • Builder (docs/js/cards.js): a scene_grid tile with "Track as Activity" checked can now list which physical devices it involves (comma-separated ids) — mainly useful on a Harmony-backed tile, since Astrion has no other way to know which devices a Harmony Activity touches. scene_grid tiles also gained an IR device+command picker and a "Composed Activity" picker referencing the new Activities section.
  • MainActivity.kt: runHotkey() now dispatches irDevice+irCommand bindings directly through the device's own IR blaster (ConsumerIrManager) — the same local IR path scene_grid/composed Activities already had, now available on physical-button bindings too. Closes the loop for a composed Activity's volume routing when the volume device is local IR.
  • New title card type — a section header for grouping cards on a page (title + optional subtitle, independently alignable), ported from Home Assistant's Mushroom Title card. Where Mushroom uses its own title_tap_action/subtitle_tap_action (a full HA ActionConfig — call-service, navigate, more-info, url, assist...), this instead reuses the same title_/subtitle_-prefixed action fields scene_grid already has, so it also covers Harmony/IR/composed-Activity actions with nothing new to learn. Also supports an optional icon (a PNG file path, same BitmapFactory.decodeFile convention every other icon in the app uses — deliberately not a bundled/named vector set) and a divider line filling the rest of the row, Bubble-Card-style; either forces the title row left-aligned regardless of alignment, since that layout has no sensible centered/right-aligned form. An optional color (same ARGB hex convention as scene_grid's) tints the title text and, when divider is on, the line at reduced alpha to match — the subtitle and icon are untouched by it, icons are never tinted anywhere in the app. Builder: new "Title" entry under "Common" with a title/subtitle/alignment/icon/divider/color form; a tappable title needs the "Other / custom type…" raw-JSON editor instead, since re-saving through the simple form — like every card type here — rewrites the card's options from scratch.

Changed

  • Builder: editing an existing Harmony-backed tile (scene_grid or apple_tv_remote) that has no explicit hub no longer silently defaults the Hub dropdown to the first configured hub — with more than one hub, that default could be the wrong one and would get silently re-saved as correct. The dropdown now shows empty and the existing "pick a hub" validation blocks saving until one is chosen explicitly.
  • Builder: ids generated from a name (IR devices, Activities) now strip accents via Unicode NFD normalization instead of mangling them — "Xbox Série X" becomes "xbox_serie_x", not the previous "xbox_s_rie_x".
  • SettingsOverlay/ActivitiesOverlay: the swipe-to-close gesture now lives on a dedicated strip (bottom for Settings, top for Activities — mirrored) instead of on the same node as verticalScroll, which meant the scroll consumer ate every vertical drag before the close gesture ever fired. Incorporates the SettingsOverlay fix from PR #3 (see Fixed, below), applied to ActivitiesOverlay as well.

Fixed

  • Builder (docs/js/cards.js): fillGiHarmonySection() was defined twice (dead duplicate); removed.
  • Builder: dashboard.json's irDevices/activities were silently dropped every time the builder reloaded a config — both pages.js's "paste JSON" import and device.js's auto-reload-from-device path had a field whitelist that pre-dated those two sections.
  • Builder: the Activities section's IR-device picker didn't refresh after saving or removing an IR device (only the scene_grid form's picker did, via a different refresh path).
  • Dashboard.kt: SettingsOverlay called SettingsMenu(ctx, onClose = onClose), but SettingsMenu only ever declared ctx — a pre-existing compile error.
  • `HaMod...
Read more

v0.7.0

Choose a tag to compare

@dckiller51 dckiller51 released this 14 Aug 02:47

[0.7.0] - 2026-08-14

Added

  • MediaPlayerCard: full Mushroom-style rewrite of the compact layout — round album-art/icon avatar, name + state row, and a control row below that switches between transport buttons (previous/play-pause/next, plus optional power/shuffle/repeat) and volume controls (mute, -/+ buttons, or a slider) via a small swap button, matching Home Assistant's own Mushroom media-player card behavior. Tap the tile to toggle play/pause; long-press opens the new MediaPlayerDetailDialog (see below).
  • MediaPlayerCard: new MediaPlayerDetailDialog, opened by long-pressing the compact tile (same gesture LightCard/CoverCard use) — large artwork, a live progress bar, the full transport row, a volume slider with mute, and a power toggle when the entity supports it.
  • MediaPlayerCard: variant config option ("compact" (default) / "full") replaces the old fixed layout; the full variant keeps the dedicated-media-page look (large art, top_buttons) but adds a live progress bar (computed from media_position + media_position_updated_at, advancing every second while playing) and the same feature-gated transport/volume rows as the compact tile.
  • MediaPlayerCard: use_media_info (default true — shows media_title/media_artist/app_name instead of the entity's friendly name/state), show_volume_level (appends "⸱ N%" to the state line), media_controls (comma list: on_off,shuffle,previous,play_pause,next,repeat), and volume_controls (comma list: mute,buttons,set) config options — every button is checked live against the entity's supported_features bitmask, so a control requested in config but not supported by the entity never renders.
  • MdiIcons: Play, Pause, SkipPrevious, SkipNext, VolumeHigh, VolumeOff, Repeat, Shuffle, Cast, CastOff glyphs, replacing the generic Material icons the old media player card used. Cast/CastOff also become the compact tile's (and full page's) fallback avatar — device on/off — whenever the entity reports no entity_picture, instead of guessing a play/pause icon.
  • New media_state_playing/paused/idle/buffering/on/off string resources (English and French), used on the state line whenever the entity reports no media_title/media_artist/app_name.
  • Dashboard builder (docs/index.html): media_player cards now have their own dedicated form (previously only the raw-JSON fallback) — variant, name, entity, use_media_info/show_volume_level checkboxes, per-control checkboxes for media_controls/volume_controls, and a top_buttons JSON field (full variant only) — plus a real preview matching the on-device look, including the swap-button behavior and shuffle/repeat's tinted-not-swapped active state.
  • CoverCard/LightCard: new optional controls, matching Home Assistant's Mushroom cards. Covers can show open/stop/close buttons, a position slider, and a tilt slider (any combination); lights can show a brightness slider, a colour temperature slider, and colour swatches. If more than one is turned on for a card, a small button lets you flip between them. All configurable from the dashboard builder, with a matching preview. Existing dashboards aren't affected unless you turn these on.
  • Dashboard builder: the remote preview now lights up the physical buttons that already have a hotkey assigned to them (on the current page or globally), so it's easy to see at a glance what's already mapped.

Changed

  • MediaPlayerCard: the compact layout's control row now offers mute and full transport (previous/play/pause/next, optionally shuffle/repeat/power) — the previous compact layout only ever showed a fixed volume up/down pair with no way to control playback.
  • Shuffle and repeat only have one glyph each (no separate "active" variant) — MediaPlayerCard/MediaPlayerDetailDialog tint the button instead of swapping its icon when shuffle is on or repeat isn't "off".
  • Dashboard builder: the remote preview is now a proper redrawing of the physical remote instead of a photo, and much closer to true scale — icon/text sizes, how many cards fit on screen before scrolling, and button positions all now match the real device.

Fixed

  • CoverCard/LightCard: the new sliders' fill bar was centering and shrinking from both sides instead of filling left to right.
  • Dashboard builder: the zoomed-in preview rendered everything about 9% smaller than the real device.
  • Dashboard builder: some elements in the remote preview (screen, buttons) could end up slightly out of position depending on your browser.
  • Dashboard builder: two of the physical media buttons could render slightly past the edge of the remote.

Removed

  • remote.png — the static photo the dashboard builder used to overlay the preview onto, now unused: the remote preview is a fully HTML/CSS recreation of the physical device (see Changed above).