diff --git a/src/data/device/unassigned_devices.ts b/src/data/device/unassigned_devices.ts new file mode 100644 index 0000000000..7ed6bfc6e4 --- /dev/null +++ b/src/data/device/unassigned_devices.ts @@ -0,0 +1,15 @@ +import type { DeviceRegistryEntry } from "./device_registry"; + +export const filterUnassignedDevices = ( + devices: Record +): DeviceRegistryEntry[] => + Object.values(devices).filter( + (device) => + device.area_id === null && + device.disabled_by === null && + device.entry_type !== "service" + ); + +export const countUnassignedDevices = ( + devices: Record +): number => filterUnassignedDevices(devices).length; diff --git a/src/panels/config/devices/ha-config-devices-unassigned.ts b/src/panels/config/devices/ha-config-devices-unassigned.ts index 53db616dfc..79afe8cb70 100644 --- a/src/panels/config/devices/ha-config-devices-unassigned.ts +++ b/src/panels/config/devices/ha-config-devices-unassigned.ts @@ -21,6 +21,7 @@ import { sortConfigEntries } from "../../../data/config_entries"; import { fullEntitiesContext } from "../../../data/context"; import type { DeviceEntityLookup } from "../../../data/device/device_registry"; import { updateDeviceRegistryEntry } from "../../../data/device/device_registry"; +import { filterUnassignedDevices } from "../../../data/device/unassigned_devices"; import type { EntityRegistryEntry } from "../../../data/entity/entity_registry"; import type { IntegrationManifest } from "../../../data/integration"; import { showAlertDialog } from "../../../dialogs/generic/show-dialog-box"; @@ -55,6 +56,8 @@ export class HaConfigDevicesUnassigned extends LitElement { @property({ attribute: false }) public route!: Route; + @state() private _searchParms = new URLSearchParams(window.location.search); + @state() @storage({ storage: "sessionStorage", @@ -122,10 +125,7 @@ export class HaConfigDevicesUnassigned extends LitElement { entryLookup[entry.entry_id] = entry; } - // Filter to only unassigned and enabled devices - const unassignedDevices = Object.values(devices).filter( - (device) => device.area_id === null && device.disabled_by === null - ); + const unassignedDevices = filterUnassignedDevices(devices); return unassignedDevices.map((device) => { const deviceEntries = sortConfigEntries( @@ -260,7 +260,9 @@ export class HaConfigDevicesUnassigned extends LitElement { = { climate: "deep-orange", security: "blue-grey", media_players: "blue", + unassigned_devices: "grey", }; @customElement("hui-home-summary-card") @@ -214,6 +216,13 @@ export class HuiHomeSummaryCard extends LitElement implements LovelaceCard { }) : this.hass.localize("ui.card.home-summary.no_media_playing"); } + case "unassigned_devices": { + const count = countUnassignedDevices(this.hass.devices); + return this.hass.localize( + "ui.card.home-summary.count_unassigned_devices", + { count } + ); + } } return ""; } diff --git a/src/panels/lovelace/strategies/home/helpers/home-summaries.ts b/src/panels/lovelace/strategies/home/helpers/home-summaries.ts index 5f3c9557a1..489d07d4ce 100644 --- a/src/panels/lovelace/strategies/home/helpers/home-summaries.ts +++ b/src/panels/lovelace/strategies/home/helpers/home-summaries.ts @@ -9,6 +9,7 @@ export const HOME_SUMMARIES = [ "climate", "security", "media_players", + "unassigned_devices", ] as const; export type HomeSummary = (typeof HOME_SUMMARIES)[number]; @@ -18,6 +19,7 @@ export const HOME_SUMMARIES_ICONS: Record = { climate: "mdi:home-thermometer", security: "mdi:security", media_players: "mdi:multimedia", + unassigned_devices: "mdi:devices", }; export const HOME_SUMMARIES_FILTERS: Record = { @@ -25,6 +27,7 @@ export const HOME_SUMMARIES_FILTERS: Record = { climate: climateEntityFilters, security: securityEntityFilters, media_players: [{ domain: "media_player", entity_category: "none" }], + unassigned_devices: [], // Device-based, not entity-based }; export const getSummaryLabel = ( diff --git a/src/panels/lovelace/strategies/home/home-overview-view-strategy.ts b/src/panels/lovelace/strategies/home/home-overview-view-strategy.ts index 2d69f42957..0b6a49deef 100644 --- a/src/panels/lovelace/strategies/home/home-overview-view-strategy.ts +++ b/src/panels/lovelace/strategies/home/home-overview-view-strategy.ts @@ -24,6 +24,7 @@ import type { WeatherForecastCardConfig, } from "../../cards/types"; import type { CommonControlSectionStrategyConfig } from "../usage_prediction/common-controls-section-strategy"; +import { countUnassignedDevices } from "../../../../data/device/unassigned_devices"; import { HOME_SUMMARIES_FILTERS } from "./helpers/home-summaries"; import type { Condition } from "../../common/validate-condition"; @@ -170,6 +171,10 @@ export class HomeOverviewViewStrategy extends ReactiveElement { const hasClimate = findEntities(allEntities, climateFilters).length > 0; const hasSecurity = findEntities(allEntities, securityFilters).length > 0; + const unassignedDevicesCount = countUnassignedDevices(hass.devices); + const hasUnassignedDevices = + hass.user?.is_admin && unassignedDevicesCount > 0; + const summaryCards: LovelaceCardConfig[] = [ hasLights && ({ @@ -219,6 +224,18 @@ export class HomeOverviewViewStrategy extends ReactiveElement { columns: 12, }, } satisfies HomeSummaryCard), + hasUnassignedDevices && + ({ + type: "home-summary", + summary: "unassigned_devices", + tap_action: { + action: "navigate", + navigation_path: "/config/devices/unassigned?historyBack=1", + }, + grid_options: { + columns: 12, + }, + } satisfies HomeSummaryCard), ].filter(Boolean) as LovelaceCardConfig[]; const forYouSection: LovelaceSectionConfig = { diff --git a/src/translations/en.json b/src/translations/en.json index 047524481f..32a3fd0cb2 100644 --- a/src/translations/en.json +++ b/src/translations/en.json @@ -212,7 +212,8 @@ "count_alarms_disarmed": "{count} {count, plural,\n one {disarmed}\n other {disarmed}\n}", "all_secure": "All secure", "no_media_playing": "No media playing", - "count_media_playing": "{count} {count, plural,\n one {playing}\n other {playing}\n}" + "count_media_playing": "{count} {count, plural,\n one {playing}\n other {playing}\n}", + "count_unassigned_devices": "{count} {count, plural,\n one {device}\n other {devices}\n}" }, "media_player": { "source": "Source", @@ -7154,7 +7155,8 @@ }, "home": { "summary_list": { - "media_players": "Media players" + "media_players": "Media players", + "unassigned_devices": "Unassigned devices" }, "welcome_user": "Welcome {user}", "summaries": "Summaries",