diff --git a/src/devices/devices-list.ts b/src/devices/devices-list.ts index 06aaa72d..bfd3ea90 100644 --- a/src/devices/devices-list.ts +++ b/src/devices/devices-list.ts @@ -19,12 +19,15 @@ import { MetadataRefresher } from "./device-metadata-refresher"; import { ESPHomeSearch } from "../components/esphome-search"; import { fireEvent } from "../util/fire-event"; +type StatusFilter = "all" | "online" | "offline"; + @customElement("esphome-devices-list") class ESPHomeDevicesList extends LitElement { @property() public showDiscoveredDevices = false; @state() private _devices?: Array; @state() private _onlineStatus: Record = {}; + @state() private _statusFilter: StatusFilter = "all"; @query("esphome-search") private _search!: ESPHomeSearch; @@ -103,6 +106,28 @@ class ESPHomeDevicesList extends LitElement { return html` this.requestUpdate()}> +
+ Status: + this._handleStatusFilterChange("all")} + > + this._handleStatusFilterChange("online")} + > + this._handleStatusFilterChange("offline")} + > +
${!this.showDiscoveredDevices && discoveredCount > 0 ? html`
@@ -126,6 +151,17 @@ class ESPHomeDevicesList extends LitElement { return false; } + // Status filter for configured devices only + if (!this._isImportable(item) && this._statusFilter !== "all") { + const isOnline = this._onlineStatus[item.configuration] || false; + if (this._statusFilter === "online" && !isOnline) { + return false; + } + if (this._statusFilter === "offline" && isOnline) { + return false; + } + } + if (this._search?.value) { const searchValue = this._search!.value.toLowerCase(); if (item.name!.toLowerCase().indexOf(searchValue) >= 0) { @@ -165,6 +201,17 @@ class ESPHomeDevicesList extends LitElement { openWizardDialog(); } + private _handleStatusFilterChange(filter: StatusFilter) { + this._statusFilter = filter; + // Save to localStorage for persistence + try { + localStorage.setItem("esphome-status-filter", filter); + } catch (e) { + // Ignore localStorage errors + } + this.requestUpdate(); + } + static styles = css` .grid { display: grid; @@ -219,6 +266,28 @@ class ESPHomeDevicesList extends LitElement { border-top: 1px solid var(--divider-color); color: var(--mdc-theme-on-primary); } + .filter-bar { + display: flex; + align-items: center; + gap: 8px; + padding: 12px 16px; + background-color: var(--primary-bg-color); + border-bottom: 1px solid var(--divider-color); + } + .filter-label { + font-weight: 500; + color: var(--primary-text-color); + margin-right: 8px; + } + .filter-button { + --mdc-theme-primary: var(--primary-text-color); + --mdc-button-outline-color: var(--divider-color); + } + .filter-button.active { + --mdc-theme-primary: var(--primary-color); + background-color: var(--primary-color); + color: white; + } `; private async _updateDevices() { @@ -237,6 +306,20 @@ class ESPHomeDevicesList extends LitElement { public connectedCallback() { super.connectedCallback(); + // Load saved filter from localStorage + try { + const savedFilter = localStorage.getItem("esphome-status-filter"); + if ( + savedFilter === "online" || + savedFilter === "offline" || + savedFilter === "all" + ) { + this._statusFilter = savedFilter; + } + } catch (e) { + // Ignore localStorage errors + } + this._devicesUnsub = subscribeDevices(async (devices) => { if (!devices) return; let newName: string | undefined;