WallpaperMachine
The wallpapers you love. A new home on your Mac.
A free, open-source Home Assistant panel for your desktop. Windows, macOS and Linux. Free folder profile sync, or optional paid Cloud Sync.
LifestyleJavaScript · CSSGitHub @Robertg761OnlineOwner unconfirmed
Have you tried it? One tap is enough. No sign-in needed.
Free, open-source Home Assistant desktop app and widget for Windows, macOS, and Linux. Control devices, view sensors, and pin entities to your desktop.
Taken from the repository README
Loading the full README.
Read the full README ↗A free, open-source Home Assistant desktop app and frosted-glass widget for Windows, macOS, and Linux. Control devices, view sensors, and pin entities to your desktop.
Settings are organized into six pages on the left: General (Home Assistant connection, window behavior, language), Appearance (light or dark theme, colors, window effects, readability), Dashboard (primary cards, date and time formats, weather source, media tile, custom entity icons), Hotkeys, Alerts, and Advanced (updates, profile sync, diagnostics).
Use Search settings at the top of Settings to find an option by its name or help text. Choose a result to open its page and any collapsed section. Press Escape in the search field to clear the search.
In Appearance → Readability, choose Text and control size (100%, 115%, 130%, or 150%) to enlarge the interface, dialogs, and desktop pins. Enable High contrast with opaque panels for a dark, solid background and brighter text and borders. These preferences save immediately. Desktop pins grow with it, so a pin keeps showing all of its controls.
Light, climate, fan, cover, and media tiles include a Controls button (the sliders icon in the tile corner) to open their detailed controls. Keyboard users can also focus a tile and press Shift+Enter.
See the Omarchy guide for native desktop layers, independent desktop pins, the Omarchy 4 bar plugin, Hyprland shortcut setup, live theme following, and Arch packaging. Build a local pacman package with npm run dist:arch followed by makepkg --nodeps in dist/arch.
On Sway, niri and river the widget sits on the desktop under your windows, as it does on Hyprland. To bring it forward, bind a key to its --toggle command, which shows or hides it. Settings → Hotkeys shows the command for your installation: ha-desktop-widget for the Arch package, home-assistant-widget for the .deb, and ~/.local/bin/ha-desktop-widget for the AppImage. With the .deb:
bindsym $mod+Shift+h exec home-assistant-widget --toggleMod+Shift+H { spawn "home-assistant-widget" "--toggle"; }riverctl map normal Super+Shift H spawn 'home-assistant-widget --toggle'The AppImage's file name includes its version. An update saves the new version under a new name and deletes the old file, so a key bound to the file itself stops working after the first update. Bind the link instead. Each time the AppImage starts, the widget points ~/.local/bin/ha-desktop-widget at it. niri does not expand ~, so give it the full path that Settings shows. The Copy button under it copies the command. With the AppImage:
bindsym $mod+Shift+h exec ~/.local/bin/ha-desktop-widget --toggleMod+Shift+H { spawn "/home/you/.local/bin/ha-desktop-widget" "--toggle"; }riverctl map normal Super+Shift H spawn '~/.local/bin/ha-desktop-widget --toggle'The widget only moves its own link: the one it made, or one that already led to the AppImage when the widget had no link of its own. It leaves that name alone if something else already has it, such as a file or a link of yours there (even one to another AppImage) or the Arch package's ha-desktop-widget. Settings then names the AppImage file itself. To keep that working across updates, rename the file to a name with no version number and no spaces, such as ~/Applications/ha-desktop-widget.AppImage. Updates then replace it under the same name.
Open Settings → Advanced → Settings files to export your saved pages, appearance, alerts, weather and media choices to a JSON file. Connection details and credentials, desktop pins, hotkeys, tray preferences, text size, Omarchy theme following, startup, updates, and profile sync settings stay on each computer.
Import shows the sections that will change, page names, and how many referenced devices are missing from the current Home Assistant connection. Confirming applies the file immediately and replaces unsaved Settings edits. Imported entity IDs still refer to the original Home Assistant devices; importing does not create devices or automations.
The widget saves a backup before applying an import. Restore it under Advanced → Settings files → Backups, even with profile sync disabled. Imports and restores follow your existing sync scope when sync is enabled. Settings files use a versioned format and have a 1 MB limit.
Live tray values are available only in beta builds. Stable builds retain saved tray preferences without showing the feature. Enable Receive beta updates in Settings to receive beta releases.
Right-click a Quick Access tile and choose Show in Tray, or enable Show in system tray under Edit tile settings. Each selected entity gets its own tray icon or macOS menu-bar value. Hover for its name and full value; use its menu to show the widget or remove the icon.
Tile settings also provides an optional short name. On macOS it appears before the value in the menu bar, with units and decimal precision retained. On Windows and Linux it appears in tooltips and menus; an optional icon color helps distinguish multiple readings at a glance.
An offline connection displays -- in bitmap trays or a translated Offline label on macOS.
Values return after a fresh Home Assistant snapshot. An unavailable entity uses !, and an
unknown state uses ?, with the full explanation in the tooltip. Warning colors take precedence
over custom icon colors. A renderer failure or reload clears the last value to a placeholder.
Active timers refresh each second even when the widget is hidden. The dashboard retains its normal background behavior, including pausing hidden camera previews. On Windows and Linux, activating a tray value toggles the widget; on macOS, clicking opens its menu.
Planned for a future release:
.exe installer or portable build. macOS: open the universal .dmg or .zip (older releases may be Apple Silicon-only). Linux: use the .AppImage or install the .deb package.macOS Gatekeeper notice: Current macOS artifacts are universal, but they are temporarily not Apple Developer-ID signed or notarized. macOS may block the first launch because it cannot verify the developer. For a copy downloaded from this project's official GitHub Releases page, Control-click the app and choose Open; if needed, go to System Settings > Privacy & Security and choose Open Anyway. The package's ad-hoc integrity signature is not a substitute for Developer-ID signing. Do not bypass Gatekeeper for copies obtained elsewhere.
Linux keyring: The widget remembers your Home Assistant sign-in and sync passphrase in the system keyring (a Secret Service such as GNOME Keyring or KWallet). Minimal desktops (i3, Sway, XFCE) may not run one, and then nothing is remembered after you quit. Install and start
gnome-keyring, or another Secret Service, and restart the widget. The.debrecommendsgnome-keyring, and the Arch package lists it as optional.
AppImage on Ubuntu 23.10 and later: These releases block the sandbox the AppImage needs, so a double-clicked AppImage closes without opening a window. Install the
.debinstead, or start the AppImage once from a terminal with--no-sandbox. See Running the AppImage on systems that block the Chromium sandbox.
http://homeassistant.local, a legacy http://your-ha-ip:8123 address, or https://your-ha-domain.comThe legacy long-lived access-token form remains under Settings > General > Legacy access token (advanced) for compatibility. New setups should use browser authorization.
The optional HA Desktop Widget Companion custom integration makes registered desktops visible as
native Home Assistant devices and supports show, hide, toggle, switch_page, and
apply_profile actions. The desktop uses the same authenticated Home Assistant WebSocket
connection for registration, state reporting, command delivery, and acknowledgements. It never
accepts arbitrary shell commands, JavaScript, file access, URLs, or generic Electron IPC from
Home Assistant.
apply_profile applies a named profile authored in Home Assistant: a bounded configuration
document covering appearance (theme, accent, background, opacity, frosted glass), primary cards,
Quick Access pages and tiles, comparison graphs, custom icons, and tile options. Profiles never
carry credentials, hotkeys, window geometry, desktop pins, or file-sync settings; those stay
local to each machine. The desktop reports which profile revision it last applied so Home
Assistant can flag out-of-date desktops.
[!NOTE] A Home Assistant profile overwrites the sections it contains. If profile sync is also on for the same sections, the applied profile counts as a local change and syncs to your other computers — avoid managing the same settings with both at once.
On Windows, macOS, and Linux X11, choose the widget's display in Settings > General > Window & behavior > Display, or use Move to Monitor in the tray menu. The choice stays on this computer. If the display disconnects, the widget moves to an available display and returns when the selected one reconnects. Dragging onto another connected display updates the selection. Automatic remembers the last window position without choosing a preferred display. Individual desktop pins keep their own positions. Linux desktop-layer mode continues to use its tray monitor menu.
Press Ctrl+K or Cmd+K to search entities and actions. Choose an explicit action to turn supported lights, switches, fans, or input booleans on or off, run a scene or script, or switch pages. An entity result opens its controls when it has any. A result with none, for an entity that has an action of its own in the list (a switch, an input boolean, a scene), only says what it is now and changes nothing, so a stray Enter never switches it; a button, an automation or a timer, which has no action there, still runs its own. Successful commands appear first when opening an empty search during the current session. Unavailable entities do not offer actions, and failed commands show an error.
In Settings > Alerts, configure a state change, exact state, or numeric threshold. Optional duration and cooldown fields use seconds. A duration requires the condition to remain true continuously while the app observes it; disconnecting cancels pending alerts, except the wait before an unavailable notification, which starts over when the app reconnects and the entity is still offline. Quiet hours use the computer's local time and can cross midnight. Matching updates do not repeat an alert until the condition clears and is reached again. Alerts suppressed by quiet hours or cooldown are not queued for later delivery. The app must be running and connected.
A state change alert also tells you when the entity goes unavailable or unknown, but only if it stays that way for 30 seconds, and at most once every 15 minutes per entity, so a flapping device does not flood you. An outage that begins inside those 15 minutes is not reported later, even if it lasts. Saving the rule starts a pending wait over, but does not reset the 15 minutes. The rule's own duration, cooldown and quiet hours apply as well. Coming back is not announced unless the entity returns as something other than it was before the outage. Turn off Notify when unavailable or unknown on the rule to skip these. To be told about every outage, use an exact-state alert for unavailable or unknown, which follows only its own duration and cooldown.
Click a numeric sensor tile to open its larger chart. Select 1 hour, 6 hours, 24 hours, or 7 days and use Refresh to fetch new readings. Minimum, maximum, and sample average describe the recorded numeric values. The average is not time-weighted. History availability depends on Home Assistant's recorder and retention settings; an empty period and a failed request have separate messages.
Open Settings > General > Connection diagnostics to inspect connection attempts, the last successful connection, the last received state update, and a generic issue code. Copy report copies an allowlisted report without server URLs, credentials, entity names, readings, or raw error messages. These counters cover the current app session.
Use Tab to focus a primary card or the Quick Access grid. Arrow keys move between Quick Access tiles; Enter or Space activates the focused device. Shift+Enter opens its available detail controls. With several pages, Tab reaches the page tabs once: Left and Right (or Home and End) move along them and switch page. The tabs scroll to keep the active page in view, and the mouse wheel scrolls them. Heat/cool thermostats provide separate heating and cooling targets, and on/off-only lights show power controls without a brightness slider.
--hide to the launch command to start with the widget hidden in the tray. The Omarchy guide lists the related --toggle and --show actionsGeneral: Configure Home Assistant connection, always-on-top, startup behavior, and language packs
Themes: Choose built-in or custom accent and background colors
Window Effects: Adjust opacity, toggle frosted glass, and enable subtle weather effects. On Windows, frosted glass needs Windows 11 version 22H2 or later; Windows 10 and older Windows 11 builds draw the solid panel instead
Primary Cards: Pin weather/time or any entity to the top two cards
Custom Entity Icons: Search or paste emoji/glyph overrides for entity icons
Media Tile: Select the primary media player or hide the tile
Hotkeys: Configure global entity hotkeys, action-specific shortcuts, and a popup hotkey (hold/toggle on macOS and Windows; press/toggle on Linux)
On macOS the popup hotkey needs System Settings > Privacy & Security > Accessibility access. The widget asks when you set the hotkey and says so if it is still missing. macOS may ask again after an update; if the popup hotkey stops working, switch the widget on in that list and set the hotkey once more.
Alerts: Enable desktop notifications for entity state changes or target states, and choose whether Home Assistant's own notifications (new devices, repairs, updates) also reach the desktop; they always appear in the bell
Advanced: Updates, profile sync, logs, and interaction diagnostics for troubleshooting
git clone https://github.com/Robertg761/HA-Desktop-Widget.git
cd HA-Desktop-Widget
npm install
npm run dev # Development mode (separate app and DevTools windows)
npm run dev:climate-demo # Isolated simulated Fahrenheit air-conditioner demo (no HA required)
npm start # Regular run (builds the renderer, then starts Electron)
npm run lint # Run ESLint
npm test # Run Jest tests
npm run dist # Build Windows NSIS and portable artifacts
npm run dist:win # Build Windows NSIS installer artifacts
npm run dist:mac # Build macOS distribution artifacts
npm run dist:linux # Build Linux AppImage and deb artifacts
Building the Linux artifacts additionally requires a Rust toolchain (1.98 or newer, as
pinned in .mise.toml) and python3: npm run dist:linux compiles the bundled
windowtolayer layer-shell helper from vendor/windowtolayer before packaging.
Run npm run dev:climate-demo to launch a simulated Demo Air Conditioner without a Home
Assistant server. The demo starts with a Fahrenheit AC entity in Quick Access and advertises
its HVAC, fan, and preset modes plus a 60–86°F target range with 1°F steps. Click the tile to
toggle it; press and hold it to exercise the target-temperature, mode, fan, and preset controls.
This command only works with the development --dev launch path. It creates a fresh temporary
Electron profile for that run, blocks all real Home Assistant service calls, and never reads or
writes the normal app configuration, token, desktop pins, or profile-sync data. Remove the
dev:climate-demo script and development/dev-climate-demo.js when the fixture is no longer useful.
To test the card alongside a real Home Assistant connection, run
npm run dev:climate-overlay. This development-only mode leaves the normal Electron profile and
Home Assistant connection intact, then places a renderer-local Demo Air Conditioner card at
the start of Quick Access for the current session. Its fake state and climate service calls stay
in memory and are intercepted before the WebSocket layer; the card is not saved as a favorite,
cannot be edited/removed in Quick Access, and is never written into the normal configuration or
sent to Home Assistant. The existing npm run dev:climate-demo remains the fully isolated mode.
Because the overlay uses the normal Electron profile, and only one widget runs per profile, it
cannot start while a copy of the widget is already running — quit that one first. The isolated
dev:climate-demo gets its own temporary profile and runs alongside the real widget.
package.json, package-lock.json) and a CHANGELOG.md section for the version on main, wait for CI to pass there, then run the Tag Release workflow from the Actions tab with the version. It tags main and starts the release workflow, which publishes a normal release. Windows installer and Linux AppImage users can receive it through the in-app updater; portable, macOS, and Linux deb users download it from GitHub Releases.v4.1.0-beta.1. GitHub Actions marks it as a prerelease. Only users who enable Receive beta updates in Settings → Advanced are offered these builds.main against the last successfully published beta in the active series. When unreleased changes exist, it creates the next vX.Y.Z-beta.N tag and runs the normal release workflow. The job can also be started manually from the Actions tab.The minimum planned beta version is stored in .github/beta-target. The nightly job starts the
active series at that version (for example v4.0.0-beta.1). Once that version is stable, it
automatically moves to the next patch (v4.0.1-beta.N); increasing the file starts a future minor
series instead.
Release builds align their package version from the tag. Manually prepared betas commit matching
prerelease metadata before tagging; automated nightly betas align it only inside the build.
Stable releases continue to sync package.json and package-lock.json after publishing.
The notes of a stable release are its CHANGELOG.md section, followed by a link to the full
changelog, and the release fails without a non-empty section. A beta uses its own section when the
changelog has one; otherwise GitHub generates its notes from merged pull requests, comparing against
the previous published prerelease in the same version series (or the latest stable release for the
first beta). Notes are written once, when the release is created; to replace a published release's
notes, add or correct its CHANGELOG.md section on main and run the Update Release Notes
workflow with the release's tag.
The website in website/ is not deployed by GitHub: a change merged to main goes live only when
someone publishes it with the Vercel CLI, usually before a release is tagged, so every line on it
has to read correctly on both sides of the release. Publish exactly what main holds, not a local
folder that may be stale or have uncommitted edits:
(
set -euo pipefail # any failed step, a fetch included, stops before the deploy
git fetch origin
rm -rf /tmp/ha-site # one command a line: set -e ignores a failure on the left of &&
mkdir -p /tmp/ha-site
git archive origin/main website | tar -x -C /tmp/ha-site # exactly what main holds
cp -r website/.vercel /tmp/ha-site/website/ # the link `vercel link` made
cd /tmp/ha-site/website
vercel deploy --prod
)
Then run the Website deploy check workflow, which checks out main and compares every live page,
asset and response header with it. (node scripts/check-website-deploy.cjs does the same against
your own checkout, so it only proves anything on a clean, up-to-date main.)
config.json in Electron's userData directory.
%APPDATA%\home-assistant-widget\config.json~/Library/Application Support/home-assistant-widget/config.json~/.config/home-assistant-widget/config.json (or $XDG_CONFIG_HOME/home-assistant-widget/config.json)home-assistant-widget-dev folderhomeAssistant (url and auth method; encrypted token fields only for legacy-token authentication), desktopCompanion (a random installation ID), favoriteEntities, customEntityNames,
desktopPins, customEntityIcons, quickAccessTileOptions, tileSpans, selectedWeatherEntity, primaryMediaPlayer,
globalHotkeys, entityAlerts, popupHotkey, windowPosition, windowSize, opacity, ui (theme, accent, background,
language, customColors, timeFormat, dateFormat, use24HourClock, weatherEffectsEnabled, weatherOverride, enableInteractionDebugLogs),
and customTabs. Other stored values include primaryCards, alwaysOnTop, frostedGlass,
popupHotkeyHideOnRelease, popupHotkeyToggleMode, updates, and profileSync.Release download numbers count every update, so they can't say how many people use the app.
If you turn on Settings → Advanced → Count this install (it is off by default), packaged
builds send one request a day with a random install ID, the app version, and the OS family
(Windows, macOS, or Linux). Nothing about you, your Home Assistant, or your settings is sent, and
the counter does not store IP addresses. The ID lives in usage-ping.json next to config.json
and is never synced or exported. DO_NOT_TRACK=1 stops the request even when the setting is on.
The counter service is in
services/usage-counter.
Profile sync keeps your widget set up the same way on every computer. It writes one file,
ha-widget-profile-sync.json, into a folder that Dropbox, OneDrive, iCloud Drive, Google Drive,
Syncthing, or any similar app already keeps in sync. Folder sync needs no server and no account.
You can also choose HA Desktop Widget Cloud as the sync app, which is Cloud Sync: sign in with Google or GitHub on each computer instead of choosing a folder. It is a paid service: new accounts can save settings free for 14 days, and the subscription is USD 24 a year, billed from the day you subscribe. Once the trial ends or a subscription lapses, changes from your other computers still download, and this computer's changes wait until you subscribe. Everything below about scope, merging, backups and encryption works the same way. With encryption on, the service cannot read your synced settings. Running the service yourself is covered in docs/cloud-sync-setup.md.
Set up the first computer
Add another computer
Do the same with the same folder (and the same passphrase if you encrypt). If both computers already have different settings, Settings lists which ones differ and asks which to keep: This computer (upload) or Sync file (download). The side you replace is backed up.
What syncs
Each computer chooses its own Sync scope. Changing it on one computer never changes another.
| Section | Contains |
|---|---|
| Quick Access and layout | Favorites, pages, tile options, custom names and icons, primary cards, comparison graphs, tray values |
| Appearance | Theme, colors, density, language, date and time formats, weather effects, opacity, frosted glass, always on top, hide on focus loss |
| Alerts | Entity alert notifications |
| Weather and media | Weather source and primary media player |
These always stay on each computer: the Home Assistant connection and credentials, desktop pins, hotkeys and the popup hotkey, the open Quick Access page, text and control size, Omarchy theme following, window position and size, startup, updates, and diagnostics settings.
How changes are merged
Sync runs shortly after you change a setting, on the interval you choose (1 minute to 1 hour), when the widget regains focus, after the computer wakes, and when you choose Sync now.
Each section is compared with how it looked the last time this computer synced. A section that changed on only one side is copied to the other side, whatever either computer's clock says, so changes to different sections on two computers are both kept. Only when two computers changed the same section does the newer change win. The other version is then backed up and Settings says so.
Sync up replaces the file with this computer's settings, and Sync down replaces this computer's settings with the file. Both ask first and back up what they replace.
Backups
Before sync replaces settings on this computer, or replaces another computer's settings in the
file, it saves a copy to profile-sync-backups/ in the app data folder. The last five of each
kind are kept. Restore one from Backups in Settings: it is applied here and then syncs to
your other computers, and the settings it replaces are backed up in turn.
Encryption
With encryption on, the whole file is encrypted with AES-256-GCM, using a key derived from your passphrase with scrypt. Every computer needs the same passphrase. Remember passphrase on this device stores it encrypted with the operating system's credential protection; where that is unavailable, it is kept only until the app closes. Turning encryption on or off, or changing the passphrase, rewrites the file in a way that recovers from a crash partway through. A computer whose settings do not match the file (encryption off, or an old passphrase) stops with an explanation instead of writing to it.
Troubleshooting
.sync-conflict- or Dropbox "conflicted copy" files. Profile sync
already merged the main file. Delete the copies you do not need.Compatibility
Version 4.0 writes version 3 of the sync file, which stores each section with its own timestamp. It reads files from 3.x and upgrades them the first time it saves a change. Versions 3.x cannot read the new file. They stop with an error rather than overwriting it, so update every computer to 4.0.
See the 4.0 migration guide for backups, credential recovery, and returning to 3.x.
.deb, or start the AppImage from a terminal with --no-sandbox. See Running the AppImage on systems that block the Chromium sandbox.fonts-noto-core on Debian and Ubuntu or noto-fonts on Arch.We welcome contributions! Here's how you can help:
npm run lint)HA Desktop Widget is independently developed and will remain free and open source. Sponsorship helps support ongoing development, maintenance, cross-platform testing, and other project expenses.
Become a sponsor or see the full sponsor list and recognition details.
This project is licensed under the MIT License - see the LICENSE file for details.
If you find this project useful, please give it a star on GitHub!
min/max attributes, device class, or recent history, and can be overridden