A custom Fire TV launcher with switchable and seasonal themes, homelab status, and a command
rail built for a remote that only sends arrows, OK, Back and play/pause (a TCL TV remote over
HDMI-CEC). Built for a Fire TV Stick 4K (AFTKRT, Fire OS 8 / Android 11).
![]() |
![]() |
| The command rail: everything the remote can't reach directly | New Year |
![]() |
![]() |
| Date Night | Christmas |
Hearth is licensed under the GNU General Public License v3.0 or later (see LICENSE).
Fire OS pins Amazon's launcher as the system Home app, so a third-party launcher cannot
register as Home the normal way. Hearth's Home-redirect mechanism
(homekey/HomeRedirectService.kt, homekey/HomeKeyWatcher.kt, homekey/BootReceiver.kt) is
ported from Homestead (GPL-3.0) by Sarg338, which
solved this with an accessibility service plus a READ_LOGS logcat watcher. Hearth reuses
that approach and its security posture (canRetrieveWindowContent=false, no key filtering,
the logcat watcher trusts only lines the log daemon stamps as written by the system's own
uid) almost verbatim, retargeted to Hearth's package and Kotlin. Because Homestead is
GPL-3.0, Hearth is too.
Requires JDK 21, the Android SDK (compileSdk 35, platform-tools, build-tools 34.0.0),
and network access for Gradle/Maven dependencies the first time.
./gradlew testReleaseUnitTest assembleReleaseThe build copies every theme folder from themes/<id>/ into the APK's
assets/themes/<id>/ and fails with a clear error if themes/default/theme.json is
missing, since the theme contract requires a default theme to always exist.
Your devices, name, weather location and service addresses live in one file,
config/site.env (gitignored). Every script reads it.
make setup # copies config/site.env.example to config/site.env; then edit it
make keystore # creates ~/.hearth-keys/hearth-release.keystore if missing
make install # builds, installs, grants READ_LOGS and media access, sets Hearth as the Home redirect
make push-config # renders config/hearth.local.json from site.env and pushes itmake lists the rest: logs, screenshots, theme art (make themes), the theme catalog
(make catalog, into dist/catalog) and a local test stack of AdGuard, Uptime Kuma and the
catalog (make stack-up).
install.sh points enabled_accessibility_services at Hearth's HomeRedirectService only,
and allows media/MediaListener as a notification listener so Home can show what's playing.
It also compiles Hearth ahead of time (cmd package compile -m speed): a sideloaded APK is
otherwise mostly interpreted, and cold start drops from about 1.2 s to 0.9 s. Unset widgets
(weather, guild events, Uptime Kuma, AdGuard, TV controls) stay hidden.
Because the Home redirect is an accessibility service, Compose always keeps its accessibility tree live, and each Text that changes or graphicsLayer transform that moves makes it rebuild the whole tree (about 100 ms on the stick). Anything that changes every frame or every second is drawn instead: the wallpaper drift, the particles, the Now Playing time.
When another app is playing music or video (Spotify in the background, say), the strip under the header shows its cover, title, artist and app, a time bar, and previous / play-pause / next. It shares the strip with the guild's next quest (one quest instead of two), so the app grid doesn't move. OK on the cover or title opens the player. A paused session stays until its app releases it, so it can be resumed from Home. Up from the grid reaches it.
homekey/RemoteBridge.kt listens on TCP 47800 (LAN clients only) for one word per connection
from the TV (tcl-tv/bridge/tcl-keys.sh
in the homelab repo): jellyfin, netflix, prime, youtube, hbo,
disney, hearth, settings. It wakes the stick and opens that app. It lives in the
accessibility service, so it starts at boot and its activity starts are not blocked.
widgets/TvLink.kt talks the other way, to the TV's listener on TCP 47801 (config tv).
homekey/-- the Home redirect (ported from Homestead, see above), the remote bridge, andCoverSnapshot, the picture of the home screen the redirect shows while Hearth comes up.theme/-- theme.json model, validation, season resolution ("Automatic"), particle effects, bundled/downloaded theme loading.catalog/-- remote theme catalog client: fetch, sha256/size verification, zip-slip protection, atomic install.config/-- the pushed-config contract: data model, app-private storage, and the write-onlyConfigProvider(content://dev.jacobcoffee.hearth.config/config).widgets/-- weather (Open-Meteo), guild events (SideQuest), Uptime Kuma, AdGuard Home, and the TV link.media/-- Now Playing: follows other apps' media sessions (NowPlayingSource) throughMediaListener, a notification listener that exists only to unlockMediaSessionManager.getActiveSessionsand reads no notifications.apps/-- the app lists (TV apps and user-installed apps for the grid, Fire OS's own launchers for the Apps panel), ordering, hiding, pushed layouts, Fire TV settings.ui/-- Compose for TV:home/(header, quest log, command rail, panels, search, sigils, chamber, ornaments), the adaptive app grid with per-theme tile frames, and settings.
app/src/test covers the edge logic that is worth testing in isolation: season-window
resolution (wrap-around, priority, ties, no-match), theme.json validation, the catalog
installer's zip-slip and sha256-mismatch rejection, and Now Playing's position clock.
Everything else (UI, network widgets,
the home-redirect service) is exercised on-device.
Developed in JacobCoffee/homelab under
fire-tv/hearth/ and mirrored here on every push, so send changes there.




