mirror of
https://github.com/home-assistant/frontend.git
synced 2025-12-19 18:28:42 +00:00
Use unassigned device summary card
This commit is contained in:
15
src/data/device/unassigned_devices.ts
Normal file
15
src/data/device/unassigned_devices.ts
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
import type { DeviceRegistryEntry } from "./device_registry";
|
||||||
|
|
||||||
|
export const filterUnassignedDevices = (
|
||||||
|
devices: Record<string, DeviceRegistryEntry>
|
||||||
|
): DeviceRegistryEntry[] =>
|
||||||
|
Object.values(devices).filter(
|
||||||
|
(device) =>
|
||||||
|
device.area_id === null &&
|
||||||
|
device.disabled_by === null &&
|
||||||
|
device.entry_type !== "service"
|
||||||
|
);
|
||||||
|
|
||||||
|
export const countUnassignedDevices = (
|
||||||
|
devices: Record<string, DeviceRegistryEntry>
|
||||||
|
): number => filterUnassignedDevices(devices).length;
|
||||||
@@ -21,6 +21,7 @@ import { sortConfigEntries } from "../../../data/config_entries";
|
|||||||
import { fullEntitiesContext } from "../../../data/context";
|
import { fullEntitiesContext } from "../../../data/context";
|
||||||
import type { DeviceEntityLookup } from "../../../data/device/device_registry";
|
import type { DeviceEntityLookup } from "../../../data/device/device_registry";
|
||||||
import { updateDeviceRegistryEntry } 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 { EntityRegistryEntry } from "../../../data/entity/entity_registry";
|
||||||
import type { IntegrationManifest } from "../../../data/integration";
|
import type { IntegrationManifest } from "../../../data/integration";
|
||||||
import { showAlertDialog } from "../../../dialogs/generic/show-dialog-box";
|
import { showAlertDialog } from "../../../dialogs/generic/show-dialog-box";
|
||||||
@@ -55,6 +56,8 @@ export class HaConfigDevicesUnassigned extends LitElement {
|
|||||||
|
|
||||||
@property({ attribute: false }) public route!: Route;
|
@property({ attribute: false }) public route!: Route;
|
||||||
|
|
||||||
|
@state() private _searchParms = new URLSearchParams(window.location.search);
|
||||||
|
|
||||||
@state()
|
@state()
|
||||||
@storage({
|
@storage({
|
||||||
storage: "sessionStorage",
|
storage: "sessionStorage",
|
||||||
@@ -122,10 +125,7 @@ export class HaConfigDevicesUnassigned extends LitElement {
|
|||||||
entryLookup[entry.entry_id] = entry;
|
entryLookup[entry.entry_id] = entry;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Filter to only unassigned and enabled devices
|
const unassignedDevices = filterUnassignedDevices(devices);
|
||||||
const unassignedDevices = Object.values(devices).filter(
|
|
||||||
(device) => device.area_id === null && device.disabled_by === null
|
|
||||||
);
|
|
||||||
|
|
||||||
return unassignedDevices.map((device) => {
|
return unassignedDevices.map((device) => {
|
||||||
const deviceEntries = sortConfigEntries(
|
const deviceEntries = sortConfigEntries(
|
||||||
@@ -260,7 +260,9 @@ export class HaConfigDevicesUnassigned extends LitElement {
|
|||||||
<hass-tabs-subpage-data-table
|
<hass-tabs-subpage-data-table
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
.narrow=${this.narrow}
|
.narrow=${this.narrow}
|
||||||
back-path="/config/devices/dashboard"
|
.backPath=${this._searchParms.has("historyBack")
|
||||||
|
? undefined
|
||||||
|
: "/config/devices/dashboard"}
|
||||||
.tabs=${TABS}
|
.tabs=${TABS}
|
||||||
.route=${this.route}
|
.route=${this.route}
|
||||||
.searchLabel=${this.hass.localize(
|
.searchLabel=${this.hass.localize(
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ import "../../../components/ha-icon";
|
|||||||
import "../../../components/ha-ripple";
|
import "../../../components/ha-ripple";
|
||||||
import "../../../components/tile/ha-tile-icon";
|
import "../../../components/tile/ha-tile-icon";
|
||||||
import "../../../components/tile/ha-tile-info";
|
import "../../../components/tile/ha-tile-info";
|
||||||
|
import { countUnassignedDevices } from "../../../data/device/unassigned_devices";
|
||||||
import type { ActionHandlerEvent } from "../../../data/lovelace/action_handler";
|
import type { ActionHandlerEvent } from "../../../data/lovelace/action_handler";
|
||||||
import "../../../state-display/state-display";
|
import "../../../state-display/state-display";
|
||||||
import type { HomeAssistant } from "../../../types";
|
import type { HomeAssistant } from "../../../types";
|
||||||
@@ -35,6 +36,7 @@ const COLORS: Record<HomeSummary, string> = {
|
|||||||
climate: "deep-orange",
|
climate: "deep-orange",
|
||||||
security: "blue-grey",
|
security: "blue-grey",
|
||||||
media_players: "blue",
|
media_players: "blue",
|
||||||
|
unassigned_devices: "grey",
|
||||||
};
|
};
|
||||||
|
|
||||||
@customElement("hui-home-summary-card")
|
@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");
|
: 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 "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ export const HOME_SUMMARIES = [
|
|||||||
"climate",
|
"climate",
|
||||||
"security",
|
"security",
|
||||||
"media_players",
|
"media_players",
|
||||||
|
"unassigned_devices",
|
||||||
] as const;
|
] as const;
|
||||||
|
|
||||||
export type HomeSummary = (typeof HOME_SUMMARIES)[number];
|
export type HomeSummary = (typeof HOME_SUMMARIES)[number];
|
||||||
@@ -18,6 +19,7 @@ export const HOME_SUMMARIES_ICONS: Record<HomeSummary, string> = {
|
|||||||
climate: "mdi:home-thermometer",
|
climate: "mdi:home-thermometer",
|
||||||
security: "mdi:security",
|
security: "mdi:security",
|
||||||
media_players: "mdi:multimedia",
|
media_players: "mdi:multimedia",
|
||||||
|
unassigned_devices: "mdi:devices",
|
||||||
};
|
};
|
||||||
|
|
||||||
export const HOME_SUMMARIES_FILTERS: Record<HomeSummary, EntityFilter[]> = {
|
export const HOME_SUMMARIES_FILTERS: Record<HomeSummary, EntityFilter[]> = {
|
||||||
@@ -25,6 +27,7 @@ export const HOME_SUMMARIES_FILTERS: Record<HomeSummary, EntityFilter[]> = {
|
|||||||
climate: climateEntityFilters,
|
climate: climateEntityFilters,
|
||||||
security: securityEntityFilters,
|
security: securityEntityFilters,
|
||||||
media_players: [{ domain: "media_player", entity_category: "none" }],
|
media_players: [{ domain: "media_player", entity_category: "none" }],
|
||||||
|
unassigned_devices: [], // Device-based, not entity-based
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getSummaryLabel = (
|
export const getSummaryLabel = (
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ import type {
|
|||||||
WeatherForecastCardConfig,
|
WeatherForecastCardConfig,
|
||||||
} from "../../cards/types";
|
} from "../../cards/types";
|
||||||
import type { CommonControlSectionStrategyConfig } from "../usage_prediction/common-controls-section-strategy";
|
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 { HOME_SUMMARIES_FILTERS } from "./helpers/home-summaries";
|
||||||
import type { Condition } from "../../common/validate-condition";
|
import type { Condition } from "../../common/validate-condition";
|
||||||
|
|
||||||
@@ -170,6 +171,10 @@ export class HomeOverviewViewStrategy extends ReactiveElement {
|
|||||||
const hasClimate = findEntities(allEntities, climateFilters).length > 0;
|
const hasClimate = findEntities(allEntities, climateFilters).length > 0;
|
||||||
const hasSecurity = findEntities(allEntities, securityFilters).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[] = [
|
const summaryCards: LovelaceCardConfig[] = [
|
||||||
hasLights &&
|
hasLights &&
|
||||||
({
|
({
|
||||||
@@ -219,6 +224,18 @@ export class HomeOverviewViewStrategy extends ReactiveElement {
|
|||||||
columns: 12,
|
columns: 12,
|
||||||
},
|
},
|
||||||
} satisfies HomeSummaryCard),
|
} 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[];
|
].filter(Boolean) as LovelaceCardConfig[];
|
||||||
|
|
||||||
const forYouSection: LovelaceSectionConfig = {
|
const forYouSection: LovelaceSectionConfig = {
|
||||||
|
|||||||
@@ -212,7 +212,8 @@
|
|||||||
"count_alarms_disarmed": "{count} {count, plural,\n one {disarmed}\n other {disarmed}\n}",
|
"count_alarms_disarmed": "{count} {count, plural,\n one {disarmed}\n other {disarmed}\n}",
|
||||||
"all_secure": "All secure",
|
"all_secure": "All secure",
|
||||||
"no_media_playing": "No media playing",
|
"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": {
|
"media_player": {
|
||||||
"source": "Source",
|
"source": "Source",
|
||||||
@@ -7154,7 +7155,8 @@
|
|||||||
},
|
},
|
||||||
"home": {
|
"home": {
|
||||||
"summary_list": {
|
"summary_list": {
|
||||||
"media_players": "Media players"
|
"media_players": "Media players",
|
||||||
|
"unassigned_devices": "Unassigned devices"
|
||||||
},
|
},
|
||||||
"welcome_user": "Welcome {user}",
|
"welcome_user": "Welcome {user}",
|
||||||
"summaries": "Summaries",
|
"summaries": "Summaries",
|
||||||
|
|||||||
Reference in New Issue
Block a user