diff --git a/src/data/device_registry.ts b/src/data/device_registry.ts
index fabfe7da26..119231feec 100644
--- a/src/data/device_registry.ts
+++ b/src/data/device_registry.ts
@@ -53,6 +53,9 @@ export const fallbackDeviceName = (
return undefined;
};
+export const devicesInArea = (devices: DeviceRegistryEntry[], areaId: string) =>
+ devices.filter((device) => device.area_id === areaId);
+
export const updateDeviceRegistryEntry = (
hass: HomeAssistant,
deviceId: string,
diff --git a/src/dialogs/generic/show-dialog-box.ts b/src/dialogs/generic/show-dialog-box.ts
index 4d9975a6df..8b05578bd7 100644
--- a/src/dialogs/generic/show-dialog-box.ts
+++ b/src/dialogs/generic/show-dialog-box.ts
@@ -1,8 +1,9 @@
import { fireEvent } from "../../common/dom/fire_event";
+import { TemplateResult } from "lit-html";
interface BaseDialogParams {
confirmText?: string;
- text?: string;
+ text?: string | TemplateResult;
title?: string;
}
diff --git a/src/layouts/hass-tabs-subpage-data-table.ts b/src/layouts/hass-tabs-subpage-data-table.ts
index d12ed9e8f8..4c458569cf 100644
--- a/src/layouts/hass-tabs-subpage-data-table.ts
+++ b/src/layouts/hass-tabs-subpage-data-table.ts
@@ -88,6 +88,7 @@ export class HaTabsSubpageDataTable extends LitElement {
.route=${this.route}
.tabs=${this.tabs}
>
+
diff --git a/src/panels/config/areas/dialog-area-registry-detail.ts b/src/panels/config/areas/dialog-area-registry-detail.ts
index af7410c8c5..3bfa1a0a99 100644
--- a/src/panels/config/areas/dialog-area-registry-detail.ts
+++ b/src/panels/config/areas/dialog-area-registry-detail.ts
@@ -109,9 +109,9 @@ class DialogAreaDetail extends LitElement {
name: this._name.trim(),
};
if (this._params!.entry) {
- await this._params!.updateEntry(values);
+ await this._params!.updateEntry!(values);
} else {
- await this._params!.createEntry(values);
+ await this._params!.createEntry!(values);
}
this._params = undefined;
} catch (err) {
@@ -124,7 +124,7 @@ class DialogAreaDetail extends LitElement {
private async _deleteEntry() {
this._submitting = true;
try {
- if (await this._params!.removeEntry()) {
+ if (await this._params!.removeEntry!()) {
this._params = undefined;
}
} finally {
diff --git a/src/panels/config/areas/ha-config-area-page.ts b/src/panels/config/areas/ha-config-area-page.ts
new file mode 100644
index 0000000000..9a2762d3dd
--- /dev/null
+++ b/src/panels/config/areas/ha-config-area-page.ts
@@ -0,0 +1,397 @@
+import "@material/mwc-button";
+import "@polymer/paper-dialog-scrollable/paper-dialog-scrollable";
+import "@polymer/paper-input/paper-input";
+import {
+ css,
+ CSSResult,
+ customElement,
+ html,
+ LitElement,
+ property,
+ TemplateResult,
+} from "lit-element";
+import "../../../components/dialog/ha-paper-dialog";
+import { haStyle } from "../../../resources/styles";
+import { HomeAssistant, Route } from "../../../types";
+import memoizeOne from "memoize-one";
+import {
+ AreaRegistryEntry,
+ updateAreaRegistryEntry,
+ deleteAreaRegistryEntry,
+} from "../../../data/area_registry";
+import {
+ DeviceRegistryEntry,
+ devicesInArea,
+ computeDeviceName,
+} from "../../../data/device_registry";
+import { configSections } from "../ha-panel-config";
+import {
+ showAreaRegistryDetailDialog,
+ loadAreaRegistryDetailDialog,
+} from "./show-dialog-area-registry-detail";
+import { showConfirmationDialog } from "../../../dialogs/generic/show-dialog-box";
+import { RelatedResult, findRelated } from "../../../data/search";
+import { isComponentLoaded } from "../../../common/config/is_component_loaded";
+import { computeStateName } from "../../../common/entity/compute_state_name";
+import { ifDefined } from "lit-html/directives/if-defined";
+
+@customElement("ha-config-area-page")
+class HaConfigAreaPage extends LitElement {
+ @property() public hass!: HomeAssistant;
+ @property() public areaId!: string;
+ @property() public areas!: AreaRegistryEntry[];
+ @property() public devices!: DeviceRegistryEntry[];
+ @property({ type: Boolean, reflect: true }) public narrow!: boolean;
+ @property() public isWide!: boolean;
+ @property() public showAdvanced!: boolean;
+ @property() public route!: Route;
+ @property() private _related?: RelatedResult;
+
+ private _area = memoizeOne((areaId: string, areas: AreaRegistryEntry[]):
+ | AreaRegistryEntry
+ | undefined => areas.find((area) => area.area_id === areaId));
+
+ private _devices = memoizeOne(
+ (areaId: string, devices: DeviceRegistryEntry[]): DeviceRegistryEntry[] =>
+ devicesInArea(devices, areaId)
+ );
+
+ protected firstUpdated(changedProps) {
+ super.firstUpdated(changedProps);
+ loadAreaRegistryDetailDialog();
+ }
+
+ protected updated(changedProps) {
+ super.updated(changedProps);
+ if (changedProps.has("areaId")) {
+ this._findRelated();
+ }
+ }
+
+ protected render(): TemplateResult {
+ const area = this._area(this.areaId, this.areas);
+
+ if (!area) {
+ return html`
+
+ `;
+ }
+
+ const devices = this._devices(this.areaId, this.devices);
+
+ return html`
+
+ ${this.narrow
+ ? html`
+
+ ${area.name}
+
+ `
+ : ""}
+
+
+
+
+ ${!this.narrow
+ ? html`
+
+
${area.name}
+
+ `
+ : ""}
+
+
+ ${isComponentLoaded(this.hass, "automation")
+ ? html`
+
${this._related?.automation?.length
+ ? this._related.automation.map((automation) => {
+ const state = this.hass.states[automation];
+ return state
+ ? html`
+
+ `
+ : "";
+ })
+ : html`
+ ${this.hass.localize(
+ "ui.panel.config.devices.automation.no_automations"
+ )}
+ `}
+
+ `
+ : ""}
+
+
+ ${isComponentLoaded(this.hass, "scene")
+ ? html`
+
${this._related?.scene?.length
+ ? this._related.scene.map((scene) => {
+ const state = this.hass.states[scene];
+ return state
+ ? html`
+
+ `
+ : "";
+ })
+ : html`
+ ${this.hass.localize(
+ "ui.panel.config.devices.scene.no_scenes"
+ )}
+ `}
+
+ `
+ : ""}
+ ${isComponentLoaded(this.hass, "script")
+ ? html`
+
${this._related?.script?.length
+ ? this._related.script.map((script) => {
+ const state = this.hass.states[script];
+ return state
+ ? html`
+
+
+
+ ${computeStateName(state)}
+
+
+
+
+ `
+ : "";
+ })
+ : html`
+
+ ${this.hass.localize(
+ "ui.panel.config.devices.script.no_scripts"
+ )}
+ `}
+
+ `
+ : ""}
+
+
+
+ `;
+ }
+
+ private async _findRelated() {
+ this._related = await findRelated(this.hass, "area", this.areaId);
+ }
+
+ private _showSettings(ev: MouseEvent) {
+ const entry: AreaRegistryEntry = (ev.currentTarget! as any).entry;
+ this._openDialog(entry);
+ }
+
+ private _openDialog(entry?: AreaRegistryEntry) {
+ showAreaRegistryDetailDialog(this, {
+ entry,
+ updateEntry: async (values) =>
+ updateAreaRegistryEntry(this.hass!, entry!.area_id, values),
+ removeEntry: async () => {
+ if (
+ !(await showConfirmationDialog(this, {
+ title: this.hass.localize(
+ "ui.panel.config.areas.delete.confirmation_title"
+ ),
+ text: this.hass.localize(
+ "ui.panel.config.areas.delete.confirmation_text"
+ ),
+ dismissText: this.hass.localize("ui.common.no"),
+ confirmText: this.hass.localize("ui.common.yes"),
+ }))
+ ) {
+ return false;
+ }
+
+ try {
+ await deleteAreaRegistryEntry(this.hass!, entry!.area_id);
+ return true;
+ } catch (err) {
+ return false;
+ }
+ },
+ });
+ }
+
+ static get styles(): CSSResult[] {
+ return [
+ haStyle,
+ css`
+ h1 {
+ margin-top: 0;
+ font-family: var(--paper-font-headline_-_font-family);
+ -webkit-font-smoothing: var(
+ --paper-font-headline_-_-webkit-font-smoothing
+ );
+ font-size: var(--paper-font-headline_-_font-size);
+ font-weight: var(--paper-font-headline_-_font-weight);
+ letter-spacing: var(--paper-font-headline_-_letter-spacing);
+ line-height: var(--paper-font-headline_-_line-height);
+ opacity: var(--dark-primary-opacity);
+ }
+
+ .container {
+ display: flex;
+ flex-wrap: wrap;
+ margin: auto;
+ max-width: 1000px;
+ margin-top: 32px;
+ margin-bottom: 32px;
+ }
+ .column {
+ padding: 8px;
+ box-sizing: border-box;
+ width: 33%;
+ flex-grow: 1;
+ }
+ .fullwidth {
+ padding: 8px;
+ width: 100%;
+ }
+ .column > *:not(:first-child) {
+ margin-top: 16px;
+ }
+
+ :host([narrow]) .column {
+ width: 100%;
+ }
+
+ :host([narrow]) .container {
+ margin-top: 0;
+ }
+
+ paper-item {
+ cursor: pointer;
+ }
+
+ a {
+ text-decoration: none;
+ color: var(--primary-text-color);
+ }
+
+ paper-item.no-link {
+ cursor: default;
+ }
+ `,
+ ];
+ }
+}
+
+declare global {
+ interface HTMLElementTagNameMap {
+ "ha-config-area-page": HaConfigAreaPage;
+ }
+}
diff --git a/src/panels/config/areas/ha-config-areas-dashboard.ts b/src/panels/config/areas/ha-config-areas-dashboard.ts
new file mode 100644
index 0000000000..25abaa7a17
--- /dev/null
+++ b/src/panels/config/areas/ha-config-areas-dashboard.ts
@@ -0,0 +1,217 @@
+import {
+ LitElement,
+ TemplateResult,
+ html,
+ css,
+ CSSResult,
+ property,
+ customElement,
+} from "lit-element";
+import "@polymer/paper-item/paper-item";
+import "@polymer/paper-item/paper-item-body";
+
+import { HomeAssistant, Route } from "../../../types";
+import {
+ AreaRegistryEntry,
+ createAreaRegistryEntry,
+} from "../../../data/area_registry";
+import "../../../components/ha-fab";
+import "../../../layouts/hass-loading-screen";
+import "../../../layouts/hass-tabs-subpage-data-table";
+import "../ha-config-section";
+import {
+ showAreaRegistryDetailDialog,
+ loadAreaRegistryDetailDialog,
+} from "./show-dialog-area-registry-detail";
+import { configSections } from "../ha-panel-config";
+import memoizeOne from "memoize-one";
+import {
+ DataTableColumnContainer,
+ RowClickedEvent,
+} from "../../../components/data-table/ha-data-table";
+import {
+ devicesInArea,
+ DeviceRegistryEntry,
+} from "../../../data/device_registry";
+import { navigate } from "../../../common/navigate";
+import { HASSDomEvent } from "../../../common/dom/fire_event";
+import { showAlertDialog } from "../../../dialogs/generic/show-dialog-box";
+
+@customElement("ha-config-areas-dashboard")
+export class HaConfigAreasDashboard extends LitElement {
+ @property() public hass!: HomeAssistant;
+ @property() public isWide?: boolean;
+ @property() public narrow!: boolean;
+ @property() public route!: Route;
+ @property() public areas!: AreaRegistryEntry[];
+ @property() public devices!: DeviceRegistryEntry[];
+
+ private _areas = memoizeOne(
+ (areas: AreaRegistryEntry[], devices: DeviceRegistryEntry[]) => {
+ return areas.map((area) => {
+ return {
+ ...area,
+ devices: devicesInArea(devices, area.area_id).length,
+ };
+ });
+ }
+ );
+
+ private _columns = memoizeOne(
+ (narrow: boolean): DataTableColumnContainer =>
+ narrow
+ ? {
+ name: {
+ title: this.hass.localize(
+ "ui.panel.config.areas.data_table.area"
+ ),
+ sortable: true,
+ filterable: true,
+ grows: true,
+ direction: "asc",
+ },
+ }
+ : {
+ name: {
+ title: this.hass.localize(
+ "ui.panel.config.areas.data_table.area"
+ ),
+ sortable: true,
+ filterable: true,
+ grows: true,
+ direction: "asc",
+ },
+ devices: {
+ title: this.hass.localize(
+ "ui.panel.config.areas.data_table.devices"
+ ),
+ sortable: true,
+ type: "numeric",
+ width: "20%",
+ direction: "asc",
+ },
+ }
+ );
+
+ protected render(): TemplateResult {
+ return html`
+
+
+
+
+ `;
+ }
+
+ protected firstUpdated(changedProps) {
+ super.firstUpdated(changedProps);
+ loadAreaRegistryDetailDialog();
+ }
+
+ private _createArea() {
+ this._openDialog();
+ }
+
+ private _showHelp() {
+ showAlertDialog(this, {
+ title: this.hass.localize("ui.panel.config.areas.caption"),
+ text: html`
+ ${this.hass.localize("ui.panel.config.areas.picker.introduction")}
+
+ ${this.hass.localize("ui.panel.config.areas.picker.introduction2")}
+
+
+ ${this.hass.localize(
+ "ui.panel.config.areas.picker.integrations_page"
+ )}
+
+ `,
+ });
+ }
+
+ private _handleRowClicked(ev: HASSDomEvent
) {
+ const areaId = ev.detail.id;
+ navigate(this, `/config/areas/area/${areaId}`);
+ }
+
+ private _openDialog(entry?: AreaRegistryEntry) {
+ showAreaRegistryDetailDialog(this, {
+ entry,
+ createEntry: async (values) =>
+ createAreaRegistryEntry(this.hass!, values),
+ });
+ }
+
+ static get styles(): CSSResult {
+ return css`
+ hass-loading-screen {
+ --app-header-background-color: var(--sidebar-background-color);
+ --app-header-text-color: var(--sidebar-text-color);
+ }
+ a {
+ color: var(--primary-color);
+ }
+ ha-card {
+ max-width: 600px;
+ margin: 16px auto;
+ overflow: hidden;
+ }
+ .empty {
+ text-align: center;
+ }
+ paper-item {
+ cursor: pointer;
+ padding-top: 4px;
+ padding-bottom: 4px;
+ }
+ ha-fab {
+ position: fixed;
+ bottom: 16px;
+ right: 16px;
+ z-index: 1;
+ }
+
+ ha-fab[is-wide] {
+ bottom: 24px;
+ right: 24px;
+ }
+ ha-fab[narrow] {
+ bottom: 84px;
+ }
+ ha-fab.rtl {
+ right: auto;
+ left: 16px;
+ }
+
+ ha-fab[is-wide].rtl {
+ bottom: 24px;
+ right: auto;
+ left: 24px;
+ }
+ `;
+ }
+}
diff --git a/src/panels/config/areas/ha-config-areas.ts b/src/panels/config/areas/ha-config-areas.ts
index 7c5212bb5d..ac7c351eef 100644
--- a/src/panels/config/areas/ha-config-areas.ts
+++ b/src/panels/config/areas/ha-config-areas.ts
@@ -1,226 +1,120 @@
+import "./ha-config-areas-dashboard";
+import "./ha-config-area-page";
+import { compare } from "../../../common/string/compare";
import {
- LitElement,
- TemplateResult,
- html,
- css,
- CSSResult,
- property,
- customElement,
-} from "lit-element";
-import "@polymer/paper-item/paper-item";
-import "@polymer/paper-item/paper-item-body";
-
-import { HomeAssistant, Route } from "../../../types";
-import {
- AreaRegistryEntry,
- updateAreaRegistryEntry,
- deleteAreaRegistryEntry,
- createAreaRegistryEntry,
subscribeAreaRegistry,
+ AreaRegistryEntry,
} from "../../../data/area_registry";
-import "../../../components/ha-card";
-import "../../../components/ha-fab";
-import "../../../layouts/hass-tabs-subpage";
-import "../../../layouts/hass-loading-screen";
-import "../ha-config-section";
import {
- showAreaRegistryDetailDialog,
- loadAreaRegistryDetailDialog,
-} from "./show-dialog-area-registry-detail";
-import { classMap } from "lit-html/directives/class-map";
-import { computeRTL } from "../../../common/util/compute_rtl";
+ HassRouterPage,
+ RouterOptions,
+} from "../../../layouts/hass-router-page";
+import { property, customElement, PropertyValues } from "lit-element";
+import { HomeAssistant } from "../../../types";
+import { ConfigEntry, getConfigEntries } from "../../../data/config_entries";
+import {
+ DeviceRegistryEntry,
+ subscribeDeviceRegistry,
+} from "../../../data/device_registry";
import { UnsubscribeFunc } from "home-assistant-js-websocket";
-import { configSections } from "../ha-panel-config";
-import { showConfirmationDialog } from "../../../dialogs/generic/show-dialog-box";
@customElement("ha-config-areas")
-export class HaConfigAreas extends LitElement {
+class HaConfigAreas extends HassRouterPage {
@property() public hass!: HomeAssistant;
- @property() public isWide?: boolean;
@property() public narrow!: boolean;
- @property() public route!: Route;
- @property() private _areas?: AreaRegistryEntry[];
- private _unsubAreas?: UnsubscribeFunc;
+ @property() public isWide!: boolean;
+ @property() public showAdvanced!: boolean;
+
+ protected routerOptions: RouterOptions = {
+ defaultPage: "dashboard",
+ routes: {
+ dashboard: {
+ tag: "ha-config-areas-dashboard",
+ cache: true,
+ },
+ area: {
+ tag: "ha-config-area-page",
+ },
+ },
+ };
+
+ @property() private _configEntries: ConfigEntry[] = [];
+ @property() private _deviceRegistryEntries: DeviceRegistryEntry[] = [];
+ @property() private _areas: AreaRegistryEntry[] = [];
+
+ private _unsubs?: UnsubscribeFunc[];
+
+ public connectedCallback() {
+ super.connectedCallback();
+
+ if (!this.hass) {
+ return;
+ }
+ this._loadData();
+ }
public disconnectedCallback() {
super.disconnectedCallback();
- if (this._unsubAreas) {
- this._unsubAreas();
+ if (this._unsubs) {
+ while (this._unsubs.length) {
+ this._unsubs.pop()!();
+ }
+ this._unsubs = undefined;
}
}
- protected render(): TemplateResult {
- if (!this.hass || this._areas === undefined) {
- return html`
-
- `;
- }
- return html`
-
-
-
- ${this.hass.localize("ui.panel.config.areas.picker.header")}
-
-
- ${this.hass.localize("ui.panel.config.areas.picker.introduction")}
-
- ${this.hass.localize(
- "ui.panel.config.areas.picker.introduction2"
- )}
-
-
- ${this.hass.localize(
- "ui.panel.config.areas.picker.integrations_page"
- )}
-
-
-
- ${this._areas.map((entry) => {
- return html`
-
-
- ${entry.name}
-
-
- `;
- })}
- ${this._areas.length === 0
- ? html`
-
- ${this.hass.localize("ui.panel.config.areas.no_areas")}
-
- ${this.hass.localize("ui.panel.config.areas.create_area")}
-
-
- `
- : html``}
-
-
-
-
-
- `;
- }
-
protected firstUpdated(changedProps) {
super.firstUpdated(changedProps);
- loadAreaRegistryDetailDialog();
- }
-
- protected updated(changedProps) {
- super.updated(changedProps);
- if (!this._unsubAreas) {
- this._unsubAreas = subscribeAreaRegistry(
- this.hass.connection,
- (areas) => {
- this._areas = areas;
- }
- );
- }
- }
-
- private _createArea() {
- this._openDialog();
- }
-
- private _openEditEntry(ev: MouseEvent) {
- const entry: AreaRegistryEntry = (ev.currentTarget! as any).entry;
- this._openDialog(entry);
- }
- private _openDialog(entry?: AreaRegistryEntry) {
- showAreaRegistryDetailDialog(this, {
- entry,
- createEntry: async (values) =>
- createAreaRegistryEntry(this.hass!, values),
- updateEntry: async (values) =>
- updateAreaRegistryEntry(this.hass!, entry!.area_id, values),
- removeEntry: async () => {
- if (
- !(await showConfirmationDialog(this, {
- title: this.hass.localize(
- "ui.panel.config.areas.delete.confirmation_title"
- ),
- text: this.hass.localize(
- "ui.panel.config.areas.delete.confirmation_text"
- ),
- dismissText: this.hass.localize("ui.common.no"),
- confirmText: this.hass.localize("ui.common.yes"),
- }))
- ) {
- return false;
- }
-
- try {
- await deleteAreaRegistryEntry(this.hass!, entry!.area_id);
- return true;
- } catch (err) {
- return false;
- }
- },
+ this.addEventListener("hass-reload-entries", () => {
+ this._loadData();
});
}
- static get styles(): CSSResult {
- return css`
- hass-loading-screen {
- --app-header-background-color: var(--sidebar-background-color);
- --app-header-text-color: var(--sidebar-text-color);
- }
- a {
- color: var(--primary-color);
- }
- ha-card {
- max-width: 600px;
- margin: 16px auto;
- overflow: hidden;
- }
- .empty {
- text-align: center;
- }
- paper-item {
- cursor: pointer;
- padding-top: 4px;
- padding-bottom: 4px;
- }
- ha-fab {
- position: fixed;
- bottom: 16px;
- right: 16px;
- z-index: 1;
- }
+ protected updated(changedProps: PropertyValues) {
+ super.updated(changedProps);
+ if (!this._unsubs && changedProps.has("hass")) {
+ this._loadData();
+ }
+ }
- ha-fab[is-wide] {
- bottom: 24px;
- right: 24px;
- }
- ha-fab[narrow] {
- bottom: 84px;
- }
- ha-fab.rtl {
- right: auto;
- left: 16px;
- }
+ protected updatePageEl(pageEl) {
+ pageEl.hass = this.hass;
- ha-fab[is-wide].rtl {
- bottom: 24px;
- right: auto;
- left: 24px;
- }
- `;
+ if (this._currentPage === "area") {
+ pageEl.areaId = this.routeTail.path.substr(1);
+ }
+
+ pageEl.entries = this._configEntries;
+ pageEl.devices = this._deviceRegistryEntries;
+ pageEl.areas = this._areas;
+ pageEl.narrow = this.narrow;
+ pageEl.isWide = this.isWide;
+ pageEl.showAdvanced = this.showAdvanced;
+ pageEl.route = this.routeTail;
+ }
+
+ private _loadData() {
+ getConfigEntries(this.hass).then((configEntries) => {
+ this._configEntries = configEntries.sort((conf1, conf2) =>
+ compare(conf1.title, conf2.title)
+ );
+ });
+ if (this._unsubs) {
+ return;
+ }
+ this._unsubs = [
+ subscribeAreaRegistry(this.hass.connection, (areas) => {
+ this._areas = areas;
+ }),
+ subscribeDeviceRegistry(this.hass.connection, (entries) => {
+ this._deviceRegistryEntries = entries;
+ }),
+ ];
+ }
+}
+
+declare global {
+ interface HTMLElementTagNameMap {
+ "ha-config-areas": HaConfigAreas;
}
}
diff --git a/src/panels/config/areas/show-dialog-area-registry-detail.ts b/src/panels/config/areas/show-dialog-area-registry-detail.ts
index 4a24231981..401c88f13b 100644
--- a/src/panels/config/areas/show-dialog-area-registry-detail.ts
+++ b/src/panels/config/areas/show-dialog-area-registry-detail.ts
@@ -6,11 +6,11 @@ import {
export interface AreaRegistryDetailDialogParams {
entry?: AreaRegistryEntry;
- createEntry: (values: AreaRegistryEntryMutableParams) => Promise;
- updateEntry: (
+ createEntry?: (values: AreaRegistryEntryMutableParams) => Promise;
+ updateEntry?: (
updates: Partial
) => Promise;
- removeEntry: () => Promise;
+ removeEntry?: () => Promise;
}
export const loadAreaRegistryDetailDialog = () =>
diff --git a/src/translations/en.json b/src/translations/en.json
index 9307f0643d..0df149151d 100755
--- a/src/translations/en.json
+++ b/src/translations/en.json
@@ -785,6 +785,10 @@
"areas": {
"caption": "Areas",
"description": "Overview of all areas in your home.",
+ "data_table": {
+ "area": "Area",
+ "devices": "Devices"
+ },
"picker": {
"header": "Areas",
"introduction": "Areas are used to organize where devices are. This information will be used throughout Home Assistant to help you in organizing your interface, permissions and integrations with other systems.",
@@ -1442,6 +1446,7 @@
"unknown_error": "Unknown error",
"name": "Name",
"update": "Update",
+ "no_devices": "No devices",
"automation": {
"automations": "Automations",
"no_automations": "No automations",