Add floors to hass (#21960)

This commit is contained in:
Paul Bottein 2024-09-13 11:07:04 +02:00 committed by GitHub
parent 68a0d04f04
commit 32feab6a70
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
11 changed files with 87 additions and 153 deletions

View File

@ -1,6 +1,6 @@
import { mdiTextureBox } from "@mdi/js";
import { ComboBoxLitRenderer } from "@vaadin/combo-box/lit";
import { HassEntity, UnsubscribeFunc } from "home-assistant-js-websocket";
import { HassEntity } from "home-assistant-js-websocket";
import { LitElement, PropertyValues, TemplateResult, html, nothing } from "lit";
import { customElement, property, query, state } from "lit/decorators";
import { styleMap } from "lit/directives/style-map";
@ -20,12 +20,7 @@ import {
getDeviceEntityDisplayLookup,
} from "../data/device_registry";
import { EntityRegistryDisplayEntry } from "../data/entity_registry";
import {
FloorRegistryEntry,
getFloorAreaLookup,
subscribeFloorRegistry,
} from "../data/floor_registry";
import { SubscribeMixin } from "../mixins/subscribe-mixin";
import { FloorRegistryEntry, getFloorAreaLookup } from "../data/floor_registry";
import { HomeAssistant, ValueChangedEvent } from "../types";
import type { HaDevicePickerDeviceFilterFunc } from "./device/ha-device-picker";
import "./ha-combo-box";
@ -50,7 +45,7 @@ interface FloorAreaEntry {
}
@customElement("ha-area-floor-picker")
export class HaAreaFloorPicker extends SubscribeMixin(LitElement) {
export class HaAreaFloorPicker extends LitElement {
@property({ attribute: false }) public hass!: HomeAssistant;
@property() public label?: string;
@ -111,22 +106,12 @@ export class HaAreaFloorPicker extends SubscribeMixin(LitElement) {
@property({ type: Boolean }) public required = false;
@state() private _floors?: FloorRegistryEntry[];
@state() private _opened?: boolean;
@query("ha-combo-box", true) public comboBox!: HaComboBox;
private _init = false;
protected hassSubscribe(): (UnsubscribeFunc | Promise<UnsubscribeFunc>)[] {
return [
subscribeFloorRegistry(this.hass.connection, (floors) => {
this._floors = floors;
}),
];
}
public async open() {
await this.updateComplete;
await this.comboBox?.open();
@ -431,12 +416,12 @@ export class HaAreaFloorPicker extends SubscribeMixin(LitElement) {
protected updated(changedProps: PropertyValues) {
if (
(!this._init && this.hass && this._floors) ||
(!this._init && this.hass) ||
(this._init && changedProps.has("_opened") && this._opened)
) {
this._init = true;
const areas = this._getAreas(
this._floors!,
Object.values(this.hass.floors),
Object.values(this.hass.areas),
Object.values(this.hass.devices),
Object.values(this.hass.entities),

View File

@ -1,6 +1,5 @@
import "@material/mwc-menu/mwc-menu-surface";
import { mdiFilterVariantRemove, mdiTextureBox } from "@mdi/js";
import { UnsubscribeFunc } from "home-assistant-js-websocket";
import {
CSSResultGroup,
LitElement,
@ -15,13 +14,8 @@ import { repeat } from "lit/directives/repeat";
import memoizeOne from "memoize-one";
import { fireEvent } from "../common/dom/fire_event";
import { computeRTL } from "../common/util/compute_rtl";
import {
FloorRegistryEntry,
getFloorAreaLookup,
subscribeFloorRegistry,
} from "../data/floor_registry";
import { getFloorAreaLookup } from "../data/floor_registry";
import { RelatedResult, findRelated } from "../data/search";
import { SubscribeMixin } from "../mixins/subscribe-mixin";
import { haStyleScrollbar } from "../resources/styles";
import type { HomeAssistant } from "../types";
import "./ha-check-list-item";
@ -31,7 +25,7 @@ import "./ha-svg-icon";
import "./ha-tree-indicator";
@customElement("ha-filter-floor-areas")
export class HaFilterFloorAreas extends SubscribeMixin(LitElement) {
export class HaFilterFloorAreas extends LitElement {
@property({ attribute: false }) public hass!: HomeAssistant;
@property({ attribute: false }) public value?: {
@ -47,8 +41,6 @@ export class HaFilterFloorAreas extends SubscribeMixin(LitElement) {
@state() private _shouldRender = false;
@state() private _floors?: FloorRegistryEntry[];
public willUpdate(properties: PropertyValues) {
super.willUpdate(properties);
@ -60,7 +52,7 @@ export class HaFilterFloorAreas extends SubscribeMixin(LitElement) {
}
protected render() {
const areas = this._areas(this.hass.areas, this._floors);
const areas = this._areas(this.hass.areas, this.hass.floors);
return html`
<ha-expansion-panel
@ -189,14 +181,6 @@ export class HaFilterFloorAreas extends SubscribeMixin(LitElement) {
this._findRelated();
}
protected hassSubscribe(): (UnsubscribeFunc | Promise<UnsubscribeFunc>)[] {
return [
subscribeFloorRegistry(this.hass.connection, (floors) => {
this._floors = floors;
}),
];
}
protected updated(changed) {
if (changed.has("expanded") && this.expanded) {
setTimeout(() => {
@ -220,9 +204,9 @@ export class HaFilterFloorAreas extends SubscribeMixin(LitElement) {
}
private _areas = memoizeOne(
(areaReg: HomeAssistant["areas"], floors?: FloorRegistryEntry[]) => {
(areaReg: HomeAssistant["areas"], floorReg: HomeAssistant["floors"]) => {
const areas = Object.values(areaReg);
const floors = Object.values(floorReg);
const floorAreaLookup = getFloorAreaLookup(areas);
const unassisgnedAreas = areas.filter(

View File

@ -1,5 +1,5 @@
import { ComboBoxLitRenderer } from "@vaadin/combo-box/lit";
import { HassEntity, UnsubscribeFunc } from "home-assistant-js-websocket";
import { HassEntity } from "home-assistant-js-websocket";
import { LitElement, PropertyValues, TemplateResult, html } from "lit";
import { customElement, property, query, state } from "lit/decorators";
import { classMap } from "lit/directives/class-map";
@ -24,10 +24,8 @@ import {
FloorRegistryEntry,
createFloorRegistryEntry,
getFloorAreaLookup,
subscribeFloorRegistry,
} from "../data/floor_registry";
import { showAlertDialog } from "../dialogs/generic/show-dialog-box";
import { SubscribeMixin } from "../mixins/subscribe-mixin";
import { showFloorRegistryDetailDialog } from "../panels/config/areas/show-dialog-floor-registry-detail";
import { HomeAssistant, ValueChangedEvent } from "../types";
import type { HaDevicePickerDeviceFilterFunc } from "./device/ha-device-picker";
@ -53,7 +51,7 @@ const rowRenderer: ComboBoxLitRenderer<FloorRegistryEntry> = (item) =>
</ha-list-item>`;
@customElement("ha-floor-picker")
export class HaFloorPicker extends SubscribeMixin(LitElement) {
export class HaFloorPicker extends LitElement {
@property({ attribute: false }) public hass!: HomeAssistant;
@property() public label?: string;
@ -111,8 +109,6 @@ export class HaFloorPicker extends SubscribeMixin(LitElement) {
@state() private _opened?: boolean;
@state() private _floors?: FloorRegistryEntry[];
@query("ha-combo-box", true) public comboBox!: HaComboBox;
private _suggestion?: string;
@ -129,14 +125,6 @@ export class HaFloorPicker extends SubscribeMixin(LitElement) {
await this.comboBox?.focus();
}
protected hassSubscribe(): (UnsubscribeFunc | Promise<UnsubscribeFunc>)[] {
return [
subscribeFloorRegistry(this.hass.connection, (floors) => {
this._floors = floors;
}),
];
}
private _getFloors = memoizeOne(
(
floors: FloorRegistryEntry[],
@ -320,12 +308,12 @@ export class HaFloorPicker extends SubscribeMixin(LitElement) {
protected updated(changedProps: PropertyValues) {
if (
(!this._init && this.hass && this._floors) ||
(!this._init && this.hass) ||
(this._init && changedProps.has("_opened") && this._opened)
) {
this._init = true;
const floors = this._getFloors(
this._floors!,
Object.values(this.hass.floors),
Object.values(this.hass.areas),
Object.values(this.hass.devices),
Object.values(this.hass.entities),
@ -360,8 +348,7 @@ export class HaFloorPicker extends SubscribeMixin(LitElement) {
? this.hass.localize("ui.components.floor-picker.floor")
: this.label}
.placeholder=${this.placeholder
? this._floors?.find((floor) => floor.floor_id === this.placeholder)
?.name
? this.hass.floors[this.placeholder]?.name
: undefined}
.renderer=${rowRenderer}
@filter-changed=${this._filterChanged}
@ -460,7 +447,7 @@ export class HaFloorPicker extends SubscribeMixin(LitElement) {
floor_id: floor.floor_id,
});
});
const floors = [...this._floors!, floor];
const floors = [...Object.values(this.hass.floors), floor];
this.comboBox.filteredItems = this._getFloors(
floors,
Object.values(this.hass.areas)!,

View File

@ -35,10 +35,6 @@ import {
computeDeviceName,
} from "../data/device_registry";
import { EntityRegistryDisplayEntry } from "../data/entity_registry";
import {
FloorRegistryEntry,
subscribeFloorRegistry,
} from "../data/floor_registry";
import {
LabelRegistryEntry,
subscribeLabelRegistry,
@ -103,17 +99,12 @@ export class HaTargetPicker extends SubscribeMixin(LitElement) {
@query(".add-container", true) private _addContainer?: HTMLDivElement;
@state() private _floors?: FloorRegistryEntry[];
@state() private _labels?: LabelRegistryEntry[];
private _opened = false;
protected hassSubscribe(): (UnsubscribeFunc | Promise<UnsubscribeFunc>)[] {
return [
subscribeFloorRegistry(this.hass.connection, (floors) => {
this._floors = floors;
}),
subscribeLabelRegistry(this.hass.connection, (labels) => {
this._labels = labels;
}),
@ -132,9 +123,7 @@ export class HaTargetPicker extends SubscribeMixin(LitElement) {
<div class="mdc-chip-set items">
${this.value?.floor_id
? ensureArray(this.value.floor_id).map((floor_id) => {
const floor = this._floors?.find(
(flr) => flr.floor_id === floor_id
);
const floor = this.hass.floors[floor_id];
return this._renderChip(
"floor_id",
floor_id,

View File

@ -1,7 +1,4 @@
import { Connection, createCollection } from "home-assistant-js-websocket";
import { Store } from "home-assistant-js-websocket/dist/store";
import { stringCompare } from "../common/string/compare";
import { debounce } from "../common/util/debounce";
import { HomeAssistant } from "../types";
import { AreaRegistryEntry } from "./area_registry";
import { RegistryEntry } from "./registry";
@ -27,48 +24,6 @@ export interface FloorRegistryEntryMutableParams {
aliases?: string[];
}
const fetchFloorRegistry = (conn: Connection) =>
conn
.sendMessagePromise({
type: "config/floor_registry/list",
})
.then((floors) =>
(floors as FloorRegistryEntry[]).sort((ent1, ent2) => {
if (ent1.level !== ent2.level) {
return (ent1.level ?? 9999) - (ent2.level ?? 9999);
}
return stringCompare(ent1.name, ent2.name);
})
);
const subscribeFloorRegistryUpdates = (
conn: Connection,
store: Store<FloorRegistryEntry[]>
) =>
conn.subscribeEvents(
debounce(
() =>
fetchFloorRegistry(conn).then((areas: FloorRegistryEntry[]) =>
store.setState(areas, true)
),
500,
true
),
"floor_registry_updated"
);
export const subscribeFloorRegistry = (
conn: Connection,
onChange: (floors: FloorRegistryEntry[]) => void
) =>
createCollection<FloorRegistryEntry[]>(
"_floorRegistry",
fetchFloorRegistry,
subscribeFloorRegistryUpdates,
conn,
onChange
);
export const createFloorRegistryEntry = (
hass: HomeAssistant,
values: FloorRegistryEntryMutableParams

View File

@ -0,0 +1,47 @@
import { Connection, createCollection } from "home-assistant-js-websocket";
import { Store } from "home-assistant-js-websocket/dist/store";
import { stringCompare } from "../common/string/compare";
import { debounce } from "../common/util/debounce";
import { FloorRegistryEntry } from "./floor_registry";
const fetchFloorRegistry = (conn: Connection) =>
conn
.sendMessagePromise({
type: "config/floor_registry/list",
})
.then((floors) =>
(floors as FloorRegistryEntry[]).sort((ent1, ent2) => {
if (ent1.level !== ent2.level) {
return (ent1.level ?? 9999) - (ent2.level ?? 9999);
}
return stringCompare(ent1.name, ent2.name);
})
);
const subscribeFloorRegistryUpdates = (
conn: Connection,
store: Store<FloorRegistryEntry[]>
) =>
conn.subscribeEvents(
debounce(
() =>
fetchFloorRegistry(conn).then((areas: FloorRegistryEntry[]) =>
store.setState(areas, true)
),
500,
true
),
"floor_registry_updated"
);
export const subscribeFloorRegistry = (
conn: Connection,
onChange: (floors: FloorRegistryEntry[]) => void
) =>
createCollection<FloorRegistryEntry[]>(
"_floorRegistry",
fetchFloorRegistry,
subscribeFloorRegistryUpdates,
conn,
onChange
);

View File

@ -21,6 +21,7 @@ import { subscribeRepairsIssueRegistry } from "../data/repairs";
import { subscribeAreaRegistry } from "../data/ws-area_registry";
import { subscribeDeviceRegistry } from "../data/ws-device_registry";
import { subscribeEntityRegistryDisplay } from "../data/ws-entity_registry_display";
import { subscribeFloorRegistry } from "../data/ws-floor_registry";
import { subscribePanels } from "../data/ws-panels";
import { subscribeThemes } from "../data/ws-themes";
import { subscribeUser } from "../data/ws-user";
@ -117,6 +118,7 @@ window.hassConnection.then(({ conn }) => {
subscribeEntityRegistryDisplay(conn, noop);
subscribeDeviceRegistry(conn, noop);
subscribeAreaRegistry(conn, noop);
subscribeFloorRegistry(conn, noop);
subscribeConfig(conn, noop);
subscribeServices(conn, noop);
subscribePanels(conn, noop);

View File

@ -6,7 +6,6 @@ import {
mdiPencil,
mdiPlus,
} from "@mdi/js";
import { UnsubscribeFunc } from "home-assistant-js-websocket";
import {
CSSResultGroup,
LitElement,
@ -15,15 +14,15 @@ import {
html,
nothing,
} from "lit";
import { customElement, property, state } from "lit/decorators";
import { customElement, property } from "lit/decorators";
import { styleMap } from "lit/directives/style-map";
import memoizeOne from "memoize-one";
import { formatListWithAnds } from "../../../common/string/format-list";
import "../../../components/ha-fab";
import "../../../components/ha-floor-icon";
import "../../../components/ha-icon-button";
import "../../../components/ha-svg-icon";
import "../../../components/ha-sortable";
import "../../../components/ha-svg-icon";
import {
AreaRegistryEntry,
createAreaRegistryEntry,
@ -34,7 +33,6 @@ import {
createFloorRegistryEntry,
deleteFloorRegistryEntry,
getFloorAreaLookup,
subscribeFloorRegistry,
updateFloorRegistryEntry,
} from "../../../data/floor_registry";
import {
@ -42,7 +40,6 @@ import {
showConfirmationDialog,
} from "../../../dialogs/generic/show-dialog-box";
import "../../../layouts/hass-tabs-subpage";
import { SubscribeMixin } from "../../../mixins/subscribe-mixin";
import { HomeAssistant, Route } from "../../../types";
import "../ha-config-section";
import { configSections } from "../ha-panel-config";
@ -57,7 +54,7 @@ const UNASSIGNED_PATH = ["__unassigned__"];
const SORT_OPTIONS = { sort: false, delay: 500, delayOnTouchOnly: true };
@customElement("ha-config-areas-dashboard")
export class HaConfigAreasDashboard extends SubscribeMixin(LitElement) {
export class HaConfigAreasDashboard extends LitElement {
@property({ attribute: false }) public hass!: HomeAssistant;
@property({ type: Boolean }) public isWide = false;
@ -66,14 +63,12 @@ export class HaConfigAreasDashboard extends SubscribeMixin(LitElement) {
@property({ attribute: false }) public route!: Route;
@state() private _floors?: FloorRegistryEntry[];
private _processAreas = memoizeOne(
(
areas: HomeAssistant["areas"],
devices: HomeAssistant["devices"],
entities: HomeAssistant["entities"],
floors: FloorRegistryEntry[]
floors: HomeAssistant["floors"]
) => {
const processArea = (area: AreaRegistryEntry) => {
let noDevicesInArea = 0;
@ -109,7 +104,7 @@ export class HaConfigAreasDashboard extends SubscribeMixin(LitElement) {
(area) => !area.floor_id || !floorAreaLookup[area.floor_id]
);
return {
floors: floors.map((floor) => ({
floors: Object.values(floors).map((floor) => ({
...floor,
areas: (floorAreaLookup[floor.floor_id] || []).map(processArea),
})),
@ -118,26 +113,18 @@ export class HaConfigAreasDashboard extends SubscribeMixin(LitElement) {
}
);
protected hassSubscribe(): (UnsubscribeFunc | Promise<UnsubscribeFunc>)[] {
return [
subscribeFloorRegistry(this.hass.connection, (floors) => {
this._floors = floors;
}),
];
}
protected render(): TemplateResult {
const areasAndFloors =
!this.hass.areas ||
!this.hass.devices ||
!this.hass.entities ||
!this._floors
!this.hass.floors
? undefined
: this._processAreas(
this.hass.areas,
this.hass.devices,
this.hass.entities,
this._floors
this.hass.floors
);
return html`
@ -327,7 +314,7 @@ export class HaConfigAreasDashboard extends SubscribeMixin(LitElement) {
this.hass.areas,
this.hass.devices,
this.hass.entities,
this._floors!
this.hass.floors
);
let area: AreaRegistryEntry;
if (ev.detail.oldPath === UNASSIGNED_PATH) {

View File

@ -35,22 +35,17 @@ import { customElement, property, state } from "lit/decorators";
import { isComponentLoaded } from "../../common/config/is_component_loaded";
import { listenMediaQuery } from "../../common/dom/media_query";
import { CloudStatus, fetchCloudStatus } from "../../data/cloud";
import {
floorsContext,
fullEntitiesContext,
labelsContext,
} from "../../data/context";
import { fullEntitiesContext, labelsContext } from "../../data/context";
import {
entityRegistryByEntityId,
entityRegistryById,
subscribeEntityRegistry,
} from "../../data/entity_registry";
import { subscribeLabelRegistry } from "../../data/label_registry";
import { HassRouterPage, RouterOptions } from "../../layouts/hass-router-page";
import { PageNavigation } from "../../layouts/hass-tabs-subpage";
import { SubscribeMixin } from "../../mixins/subscribe-mixin";
import { HomeAssistant, Route } from "../../types";
import { subscribeLabelRegistry } from "../../data/label_registry";
import { subscribeFloorRegistry } from "../../data/floor_registry";
declare global {
// for fire event
@ -390,11 +385,6 @@ class HaPanelConfig extends SubscribeMixin(HassRouterPage) {
initialValue: [],
});
private _floorsContext = new ContextProvider(this, {
context: floorsContext,
initialValue: [],
});
public hassSubscribe(): UnsubscribeFunc[] {
return [
subscribeEntityRegistry(this.hass.connection!, (entities) => {
@ -403,9 +393,6 @@ class HaPanelConfig extends SubscribeMixin(HassRouterPage) {
subscribeLabelRegistry(this.hass.connection!, (labels) => {
this._labelsContext.setValue(labels);
}),
subscribeFloorRegistry(this.hass.connection!, (floors) => {
this._floorsContext.setValue(floors);
}),
];
}

View File

@ -34,6 +34,7 @@ import { getState } from "../util/ha-pref-storage";
import hassCallApi from "../util/hass-call-api";
import { HassBaseEl } from "./hass-base-mixin";
import { promiseTimeout } from "../common/util/promise-timeout";
import { subscribeFloorRegistry } from "../data/ws-floor_registry";
export const connectionMixin = <T extends Constructor<HassBaseEl>>(
superClass: T
@ -52,6 +53,7 @@ export const connectionMixin = <T extends Constructor<HassBaseEl>>(
entities: null as any,
devices: null as any,
areas: null as any,
floors: null as any,
config: null as any,
themes: null as any,
selectedTheme: null,
@ -266,6 +268,13 @@ export const connectionMixin = <T extends Constructor<HassBaseEl>>(
}
this._updateHass({ areas });
});
subscribeFloorRegistry(conn, (floorReg) => {
const floors: HomeAssistant["floors"] = {};
for (const floor of floorReg) {
floors[floor.floor_id] = floor;
}
this._updateHass({ floors });
});
subscribeConfig(conn, (config) => this._updateHass({ config }));
subscribeServices(conn, (services) => this._updateHass({ services }));
subscribePanels(conn, (panels) => this._updateHass({ panels }));

View File

@ -12,6 +12,7 @@ import { LocalizeFunc } from "./common/translations/localize";
import { AreaRegistryEntry } from "./data/area_registry";
import { DeviceRegistryEntry } from "./data/device_registry";
import { EntityRegistryDisplayEntry } from "./data/entity_registry";
import { FloorRegistryEntry } from "./data/floor_registry";
import { CoreFrontendUserData } from "./data/frontend";
import { FrontendLocaleData, getHassTranslations } from "./data/translation";
import { Themes } from "./data/ws-themes";
@ -210,6 +211,7 @@ export interface HomeAssistant {
entities: { [id: string]: EntityRegistryDisplayEntry };
devices: { [id: string]: DeviceRegistryEntry };
areas: { [id: string]: AreaRegistryEntry };
floors: { [id: string]: FloorRegistryEntry };
services: HassServices;
config: HassConfig;
themes: Themes;