mirror of
https://github.com/home-assistant/frontend.git
synced 2025-07-31 13:07:49 +00:00
Merge pull request #7875 from home-assistant/dev
This commit is contained in:
commit
b7ccf3e0e5
@ -7,8 +7,8 @@ export const createMediaPlayerEntities = () => [
|
|||||||
media_title: "I Wanna Be A Hippy (Flamman & Abraxas Radio Mix)",
|
media_title: "I Wanna Be A Hippy (Flamman & Abraxas Radio Mix)",
|
||||||
media_artist: "Technohead",
|
media_artist: "Technohead",
|
||||||
// Pause + Seek + Volume Set + Volume Mute + Previous Track + Next Track + Play Media +
|
// Pause + Seek + Volume Set + Volume Mute + Previous Track + Next Track + Play Media +
|
||||||
// Select Source + Stop + Clear + Play + Shuffle Set + Browse Media
|
// Select Source + Stop + Clear + Play + Shuffle Set
|
||||||
supported_features: 195135,
|
supported_features: 64063,
|
||||||
entity_picture: "/images/album_cover_2.jpg",
|
entity_picture: "/images/album_cover_2.jpg",
|
||||||
media_duration: 300,
|
media_duration: 300,
|
||||||
media_position: 50,
|
media_position: 50,
|
||||||
@ -24,8 +24,8 @@ export const createMediaPlayerEntities = () => [
|
|||||||
media_title: "I Wanna Be A Hippy (Flamman & Abraxas Radio Mix)",
|
media_title: "I Wanna Be A Hippy (Flamman & Abraxas Radio Mix)",
|
||||||
media_artist: "Technohead",
|
media_artist: "Technohead",
|
||||||
// Pause + Seek + Volume Set + Volume Mute + Previous Track + Next Track + Play Media +
|
// Pause + Seek + Volume Set + Volume Mute + Previous Track + Next Track + Play Media +
|
||||||
// Select Source + Stop + Clear + Play + Shuffle Set
|
// Select Source + Stop + Clear + Play + Shuffle Set + Browse Media
|
||||||
supported_features: 64063,
|
supported_features: 195135,
|
||||||
entity_picture: "/images/album_cover.jpg",
|
entity_picture: "/images/album_cover.jpg",
|
||||||
media_duration: 300,
|
media_duration: 300,
|
||||||
media_position: 0,
|
media_position: 0,
|
||||||
|
@ -146,6 +146,16 @@ const CONFIGS = [
|
|||||||
entity: media_player.receiver_off
|
entity: media_player.receiver_off
|
||||||
`,
|
`,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
heading: "Grid Full Size",
|
||||||
|
config: `
|
||||||
|
- type: grid
|
||||||
|
columns: 1
|
||||||
|
cards:
|
||||||
|
- type: media-control
|
||||||
|
entity: media_player.music_paused
|
||||||
|
`,
|
||||||
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
class DemoHuiMediControlCard extends PolymerElement {
|
class DemoHuiMediControlCard extends PolymerElement {
|
||||||
|
@ -74,9 +74,7 @@ export class HassioUpdate extends LitElement {
|
|||||||
"Supervisor",
|
"Supervisor",
|
||||||
this.supervisor.supervisor,
|
this.supervisor.supervisor,
|
||||||
"hassio/supervisor/update",
|
"hassio/supervisor/update",
|
||||||
`https://github.com//home-assistant/hassio/releases/tag/${
|
`https://github.com//home-assistant/hassio/releases/tag/${this.supervisor.supervisor.version_latest}`
|
||||||
this.supervisor.supervisor.version_latest
|
|
||||||
}`
|
|
||||||
)}
|
)}
|
||||||
${this.supervisor.host.features.includes("hassos")
|
${this.supervisor.host.features.includes("hassos")
|
||||||
? this._renderUpdateCard(
|
? this._renderUpdateCard(
|
||||||
|
@ -137,8 +137,7 @@ export class DialogHassioNetwork extends LitElement
|
|||||||
)}
|
)}
|
||||||
${this._interface?.type === "wireless"
|
${this._interface?.type === "wireless"
|
||||||
? html`
|
? html`
|
||||||
<ha-expansion-panel outlined>
|
<ha-expansion-panel header="Wi-Fi" outlined>
|
||||||
<span slot="title">Wi-Fi</span>
|
|
||||||
${this._interface?.wifi?.ssid
|
${this._interface?.wifi?.ssid
|
||||||
? html`<p>Connected to: ${this._interface?.wifi?.ssid}</p>`
|
? html`<p>Connected to: ${this._interface?.wifi?.ssid}</p>`
|
||||||
: ""}
|
: ""}
|
||||||
@ -281,8 +280,10 @@ export class DialogHassioNetwork extends LitElement
|
|||||||
|
|
||||||
private _renderIPConfiguration(version: string) {
|
private _renderIPConfiguration(version: string) {
|
||||||
return html`
|
return html`
|
||||||
<ha-expansion-panel outlined>
|
<ha-expansion-panel
|
||||||
<span slot="title">IPv${version.charAt(version.length - 1)}</span>
|
.header=${`IPv${version.charAt(version.length - 1)}`}
|
||||||
|
outlined
|
||||||
|
>
|
||||||
<div class="radio-row">
|
<div class="radio-row">
|
||||||
<ha-formfield label="DHCP">
|
<ha-formfield label="DHCP">
|
||||||
<ha-radio
|
<ha-radio
|
||||||
@ -591,6 +592,7 @@ export class DialogHassioNetwork extends LitElement
|
|||||||
}
|
}
|
||||||
|
|
||||||
ha-expansion-panel {
|
ha-expansion-panel {
|
||||||
|
--expansion-panel-summary-padding: 0 16px;
|
||||||
margin: 4px 0;
|
margin: 4px 0;
|
||||||
}
|
}
|
||||||
paper-input {
|
paper-input {
|
||||||
|
2
setup.py
2
setup.py
@ -2,7 +2,7 @@ from setuptools import setup, find_packages
|
|||||||
|
|
||||||
setup(
|
setup(
|
||||||
name="home-assistant-frontend",
|
name="home-assistant-frontend",
|
||||||
version="20201126.0",
|
version="20201202.0",
|
||||||
description="The Home Assistant frontend",
|
description="The Home Assistant frontend",
|
||||||
url="https://github.com/home-assistant/home-assistant-polymer",
|
url="https://github.com/home-assistant/home-assistant-polymer",
|
||||||
author="The Home Assistant Authors",
|
author="The Home Assistant Authors",
|
||||||
|
6
src/common/ensure-array.ts
Normal file
6
src/common/ensure-array.ts
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
export const ensureArray = (value?: any) => {
|
||||||
|
if (!value || Array.isArray(value)) {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
return [value];
|
||||||
|
};
|
@ -67,6 +67,10 @@ export const computeStateDisplay = (
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (domain === "counter") {
|
||||||
|
return formatNumber(compareState, language);
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
// Return device class translation
|
// Return device class translation
|
||||||
(stateObj.attributes.device_class &&
|
(stateObj.attributes.device_class &&
|
||||||
|
@ -139,7 +139,7 @@ export class HaAreaDevicesPicker extends SubscribeMixin(LitElement) {
|
|||||||
|
|
||||||
private _filteredDevices: DeviceRegistryEntry[] = [];
|
private _filteredDevices: DeviceRegistryEntry[] = [];
|
||||||
|
|
||||||
private _getDevices = memoizeOne(
|
private _getAreasWithDevices = memoizeOne(
|
||||||
(
|
(
|
||||||
devices: DeviceRegistryEntry[],
|
devices: DeviceRegistryEntry[],
|
||||||
areas: AreaRegistryEntry[],
|
areas: AreaRegistryEntry[],
|
||||||
@ -277,7 +277,7 @@ export class HaAreaDevicesPicker extends SubscribeMixin(LitElement) {
|
|||||||
if (!this._devices || !this._areas || !this._entities) {
|
if (!this._devices || !this._areas || !this._entities) {
|
||||||
return html``;
|
return html``;
|
||||||
}
|
}
|
||||||
const areas = this._getDevices(
|
const areas = this._getAreasWithDevices(
|
||||||
this._devices,
|
this._devices,
|
||||||
this._areas,
|
this._areas,
|
||||||
this._entities,
|
this._entities,
|
||||||
|
@ -111,6 +111,18 @@ export class HaDevicePicker extends SubscribeMixin(LitElement) {
|
|||||||
@property({ type: Boolean })
|
@property({ type: Boolean })
|
||||||
private _opened?: boolean;
|
private _opened?: boolean;
|
||||||
|
|
||||||
|
public open() {
|
||||||
|
this.updateComplete.then(() => {
|
||||||
|
(this.shadowRoot?.querySelector("vaadin-combo-box-light") as any)?.open();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public focus() {
|
||||||
|
this.updateComplete.then(() => {
|
||||||
|
this.shadowRoot?.querySelector("paper-input")?.focus();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
private _getDevices = memoizeOne(
|
private _getDevices = memoizeOne(
|
||||||
(
|
(
|
||||||
devices: DeviceRegistryEntry[],
|
devices: DeviceRegistryEntry[],
|
||||||
@ -126,6 +138,8 @@ export class HaDevicePicker extends SubscribeMixin(LitElement) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const deviceEntityLookup: DeviceEntityLookup = {};
|
const deviceEntityLookup: DeviceEntityLookup = {};
|
||||||
|
|
||||||
|
if (includeDomains || excludeDomains || includeDeviceClasses) {
|
||||||
for (const entity of entities) {
|
for (const entity of entities) {
|
||||||
if (!entity.device_id) {
|
if (!entity.device_id) {
|
||||||
continue;
|
continue;
|
||||||
@ -135,13 +149,16 @@ export class HaDevicePicker extends SubscribeMixin(LitElement) {
|
|||||||
}
|
}
|
||||||
deviceEntityLookup[entity.device_id].push(entity);
|
deviceEntityLookup[entity.device_id].push(entity);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const areaLookup: { [areaId: string]: AreaRegistryEntry } = {};
|
const areaLookup: { [areaId: string]: AreaRegistryEntry } = {};
|
||||||
for (const area of areas) {
|
for (const area of areas) {
|
||||||
areaLookup[area.area_id] = area;
|
areaLookup[area.area_id] = area;
|
||||||
}
|
}
|
||||||
|
|
||||||
let inputDevices = [...devices];
|
let inputDevices = devices.filter(
|
||||||
|
(device) => device.id === this.value || !device.disabled_by
|
||||||
|
);
|
||||||
|
|
||||||
if (includeDomains) {
|
if (includeDomains) {
|
||||||
inputDevices = inputDevices.filter((device) => {
|
inputDevices = inputDevices.filter((device) => {
|
||||||
|
@ -101,6 +101,18 @@ export class HaEntityPicker extends LitElement {
|
|||||||
|
|
||||||
@query("vaadin-combo-box-light", true) private _comboBox!: HTMLElement;
|
@query("vaadin-combo-box-light", true) private _comboBox!: HTMLElement;
|
||||||
|
|
||||||
|
public open() {
|
||||||
|
this.updateComplete.then(() => {
|
||||||
|
(this.shadowRoot?.querySelector("vaadin-combo-box-light") as any)?.open();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public focus() {
|
||||||
|
this.updateComplete.then(() => {
|
||||||
|
this.shadowRoot?.querySelector("paper-input")?.focus();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
private _initedStates = false;
|
private _initedStates = false;
|
||||||
|
|
||||||
private _states: HassEntity[] = [];
|
private _states: HassEntity[] = [];
|
||||||
|
@ -28,6 +28,18 @@ import {
|
|||||||
import { SubscribeMixin } from "../mixins/subscribe-mixin";
|
import { SubscribeMixin } from "../mixins/subscribe-mixin";
|
||||||
import { PolymerChangedEvent } from "../polymer-types";
|
import { PolymerChangedEvent } from "../polymer-types";
|
||||||
import { HomeAssistant } from "../types";
|
import { HomeAssistant } from "../types";
|
||||||
|
import memoizeOne from "memoize-one";
|
||||||
|
import {
|
||||||
|
DeviceEntityLookup,
|
||||||
|
DeviceRegistryEntry,
|
||||||
|
subscribeDeviceRegistry,
|
||||||
|
} from "../data/device_registry";
|
||||||
|
import {
|
||||||
|
EntityRegistryEntry,
|
||||||
|
subscribeEntityRegistry,
|
||||||
|
} from "../data/entity_registry";
|
||||||
|
import { computeDomain } from "../common/entity/compute_domain";
|
||||||
|
import type { HaDevicePickerDeviceFilterFunc } from "./device/ha-device-picker";
|
||||||
|
|
||||||
const rowRenderer = (
|
const rowRenderer = (
|
||||||
root: HTMLElement,
|
root: HTMLElement,
|
||||||
@ -68,39 +80,227 @@ export class HaAreaPicker extends SubscribeMixin(LitElement) {
|
|||||||
|
|
||||||
@property() public value?: string;
|
@property() public value?: string;
|
||||||
|
|
||||||
@property() public _areas?: AreaRegistryEntry[];
|
@property() public placeholder?: string;
|
||||||
|
|
||||||
@property({ type: Boolean, attribute: "no-add" })
|
@property({ type: Boolean, attribute: "no-add" })
|
||||||
public noAdd?: boolean;
|
public noAdd?: boolean;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Show only areas with entities from specific domains.
|
||||||
|
* @type {Array}
|
||||||
|
* @attr include-domains
|
||||||
|
*/
|
||||||
|
@property({ type: Array, attribute: "include-domains" })
|
||||||
|
public includeDomains?: string[];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Show no areas with entities of these domains.
|
||||||
|
* @type {Array}
|
||||||
|
* @attr exclude-domains
|
||||||
|
*/
|
||||||
|
@property({ type: Array, attribute: "exclude-domains" })
|
||||||
|
public excludeDomains?: string[];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Show only areas with entities of these device classes.
|
||||||
|
* @type {Array}
|
||||||
|
* @attr include-device-classes
|
||||||
|
*/
|
||||||
|
@property({ type: Array, attribute: "include-device-classes" })
|
||||||
|
public includeDeviceClasses?: string[];
|
||||||
|
|
||||||
|
@property() public deviceFilter?: HaDevicePickerDeviceFilterFunc;
|
||||||
|
|
||||||
|
@property() public entityFilter?: (entity: EntityRegistryEntry) => boolean;
|
||||||
|
|
||||||
|
@internalProperty() private _areas?: AreaRegistryEntry[];
|
||||||
|
|
||||||
|
@internalProperty() private _devices?: DeviceRegistryEntry[];
|
||||||
|
|
||||||
|
@internalProperty() private _entities?: EntityRegistryEntry[];
|
||||||
|
|
||||||
@internalProperty() private _opened?: boolean;
|
@internalProperty() private _opened?: boolean;
|
||||||
|
|
||||||
public hassSubscribe(): UnsubscribeFunc[] {
|
public hassSubscribe(): UnsubscribeFunc[] {
|
||||||
return [
|
return [
|
||||||
subscribeAreaRegistry(this.hass.connection!, (areas) => {
|
subscribeAreaRegistry(this.hass.connection!, (areas) => {
|
||||||
this._areas = this.noAdd
|
this._areas = areas;
|
||||||
? areas
|
}),
|
||||||
|
subscribeDeviceRegistry(this.hass.connection!, (devices) => {
|
||||||
|
this._devices = devices;
|
||||||
|
}),
|
||||||
|
subscribeEntityRegistry(this.hass.connection!, (entities) => {
|
||||||
|
this._entities = entities;
|
||||||
|
}),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public open() {
|
||||||
|
this.updateComplete.then(() => {
|
||||||
|
(this.shadowRoot?.querySelector("vaadin-combo-box-light") as any)?.open();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public focus() {
|
||||||
|
this.updateComplete.then(() => {
|
||||||
|
this.shadowRoot?.querySelector("paper-input")?.focus();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private _getAreas = memoizeOne(
|
||||||
|
(
|
||||||
|
areas: AreaRegistryEntry[],
|
||||||
|
devices: DeviceRegistryEntry[],
|
||||||
|
entities: EntityRegistryEntry[],
|
||||||
|
includeDomains: this["includeDomains"],
|
||||||
|
excludeDomains: this["excludeDomains"],
|
||||||
|
includeDeviceClasses: this["includeDeviceClasses"],
|
||||||
|
deviceFilter: this["deviceFilter"],
|
||||||
|
entityFilter: this["entityFilter"],
|
||||||
|
noAdd: this["noAdd"]
|
||||||
|
): AreaRegistryEntry[] => {
|
||||||
|
const deviceEntityLookup: DeviceEntityLookup = {};
|
||||||
|
let inputDevices: DeviceRegistryEntry[] | undefined;
|
||||||
|
let inputEntities: EntityRegistryEntry[] | undefined;
|
||||||
|
|
||||||
|
if (includeDomains || excludeDomains || includeDeviceClasses) {
|
||||||
|
for (const entity of entities) {
|
||||||
|
if (!entity.device_id) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (!(entity.device_id in deviceEntityLookup)) {
|
||||||
|
deviceEntityLookup[entity.device_id] = [];
|
||||||
|
}
|
||||||
|
deviceEntityLookup[entity.device_id].push(entity);
|
||||||
|
}
|
||||||
|
inputDevices = devices;
|
||||||
|
inputEntities = entities.filter((entity) => entity.area_id);
|
||||||
|
} else if (deviceFilter) {
|
||||||
|
inputDevices = devices;
|
||||||
|
} else if (entityFilter) {
|
||||||
|
inputEntities = entities.filter((entity) => entity.area_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (includeDomains) {
|
||||||
|
inputDevices = inputDevices!.filter((device) => {
|
||||||
|
const devEntities = deviceEntityLookup[device.id];
|
||||||
|
if (!devEntities || !devEntities.length) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return deviceEntityLookup[device.id].some((entity) =>
|
||||||
|
includeDomains.includes(computeDomain(entity.entity_id))
|
||||||
|
);
|
||||||
|
});
|
||||||
|
inputEntities = inputEntities!.filter((entity) =>
|
||||||
|
includeDomains.includes(computeDomain(entity.entity_id))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (excludeDomains) {
|
||||||
|
inputDevices = inputDevices!.filter((device) => {
|
||||||
|
const devEntities = deviceEntityLookup[device.id];
|
||||||
|
if (!devEntities || !devEntities.length) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return entities.every(
|
||||||
|
(entity) =>
|
||||||
|
!excludeDomains.includes(computeDomain(entity.entity_id))
|
||||||
|
);
|
||||||
|
});
|
||||||
|
inputEntities = inputEntities!.filter(
|
||||||
|
(entity) => !excludeDomains.includes(computeDomain(entity.entity_id))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (includeDeviceClasses) {
|
||||||
|
inputDevices = inputDevices!.filter((device) => {
|
||||||
|
const devEntities = deviceEntityLookup[device.id];
|
||||||
|
if (!devEntities || !devEntities.length) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return deviceEntityLookup[device.id].some((entity) => {
|
||||||
|
const stateObj = this.hass.states[entity.entity_id];
|
||||||
|
if (!stateObj) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
stateObj.attributes.device_class &&
|
||||||
|
includeDeviceClasses.includes(stateObj.attributes.device_class)
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
inputEntities = inputEntities!.filter((entity) => {
|
||||||
|
const stateObj = this.hass.states[entity.entity_id];
|
||||||
|
return (
|
||||||
|
stateObj.attributes.device_class &&
|
||||||
|
includeDeviceClasses.includes(stateObj.attributes.device_class)
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (deviceFilter) {
|
||||||
|
inputDevices = inputDevices!.filter((device) => deviceFilter!(device));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (entityFilter) {
|
||||||
|
entities = entities.filter((entity) => entityFilter!(entity));
|
||||||
|
}
|
||||||
|
|
||||||
|
let outputAreas = areas;
|
||||||
|
|
||||||
|
let areaIds: string[] | undefined;
|
||||||
|
|
||||||
|
if (inputDevices) {
|
||||||
|
areaIds = inputDevices
|
||||||
|
.filter((device) => device.area_id)
|
||||||
|
.map((device) => device.area_id!);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (inputEntities) {
|
||||||
|
areaIds = (areaIds ?? []).concat(
|
||||||
|
inputEntities
|
||||||
|
.filter((entity) => entity.area_id)
|
||||||
|
.map((entity) => entity.area_id!)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (areaIds) {
|
||||||
|
outputAreas = areas.filter((area) => areaIds!.includes(area.area_id));
|
||||||
|
}
|
||||||
|
|
||||||
|
return noAdd
|
||||||
|
? outputAreas
|
||||||
: [
|
: [
|
||||||
...areas,
|
...outputAreas,
|
||||||
{
|
{
|
||||||
area_id: "add_new",
|
area_id: "add_new",
|
||||||
name: this.hass.localize("ui.components.area-picker.add_new"),
|
name: this.hass.localize("ui.components.area-picker.add_new"),
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
}),
|
|
||||||
];
|
|
||||||
}
|
}
|
||||||
|
);
|
||||||
|
|
||||||
protected render(): TemplateResult {
|
protected render(): TemplateResult {
|
||||||
if (!this._areas) {
|
if (!this._devices || !this._areas || !this._entities) {
|
||||||
return html``;
|
return html``;
|
||||||
}
|
}
|
||||||
|
const areas = this._getAreas(
|
||||||
|
this._areas,
|
||||||
|
this._devices,
|
||||||
|
this._entities,
|
||||||
|
this.includeDomains,
|
||||||
|
this.excludeDomains,
|
||||||
|
this.includeDeviceClasses,
|
||||||
|
this.deviceFilter,
|
||||||
|
this.entityFilter,
|
||||||
|
this.noAdd
|
||||||
|
);
|
||||||
return html`
|
return html`
|
||||||
<vaadin-combo-box-light
|
<vaadin-combo-box-light
|
||||||
item-value-path="area_id"
|
item-value-path="area_id"
|
||||||
item-id-path="area_id"
|
item-id-path="area_id"
|
||||||
item-label-path="name"
|
item-label-path="name"
|
||||||
.items=${this._areas}
|
.items=${areas}
|
||||||
.value=${this._value}
|
.value=${this._value}
|
||||||
.renderer=${rowRenderer}
|
.renderer=${rowRenderer}
|
||||||
@opened-changed=${this._openedChanged}
|
@opened-changed=${this._openedChanged}
|
||||||
@ -110,6 +310,9 @@ export class HaAreaPicker extends SubscribeMixin(LitElement) {
|
|||||||
.label=${this.label === undefined && this.hass
|
.label=${this.label === undefined && this.hass
|
||||||
? this.hass.localize("ui.components.area-picker.area")
|
? this.hass.localize("ui.components.area-picker.area")
|
||||||
: this.label}
|
: this.label}
|
||||||
|
.placeholder=${this.placeholder
|
||||||
|
? this._area(this.placeholder)?.name
|
||||||
|
: undefined}
|
||||||
class="input"
|
class="input"
|
||||||
autocapitalize="none"
|
autocapitalize="none"
|
||||||
autocomplete="off"
|
autocomplete="off"
|
||||||
@ -132,7 +335,7 @@ export class HaAreaPicker extends SubscribeMixin(LitElement) {
|
|||||||
</ha-icon-button>
|
</ha-icon-button>
|
||||||
`
|
`
|
||||||
: ""}
|
: ""}
|
||||||
${this._areas.length > 0
|
${areas.length > 0
|
||||||
? html`
|
? html`
|
||||||
<ha-icon-button
|
<ha-icon-button
|
||||||
aria-label=${this.hass.localize(
|
aria-label=${this.hass.localize(
|
||||||
@ -151,6 +354,12 @@ export class HaAreaPicker extends SubscribeMixin(LitElement) {
|
|||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private _area = memoizeOne((areaId: string):
|
||||||
|
| AreaRegistryEntry
|
||||||
|
| undefined => {
|
||||||
|
return this._areas?.find((area) => area.area_id === areaId);
|
||||||
|
});
|
||||||
|
|
||||||
private _clearValue(ev: Event) {
|
private _clearValue(ev: Event) {
|
||||||
ev.stopPropagation();
|
ev.stopPropagation();
|
||||||
this._setValue("");
|
this._setValue("");
|
||||||
|
@ -11,6 +11,7 @@ import {
|
|||||||
import { fireEvent } from "../common/dom/fire_event";
|
import { fireEvent } from "../common/dom/fire_event";
|
||||||
import type { ToggleButton } from "../types";
|
import type { ToggleButton } from "../types";
|
||||||
import "./ha-svg-icon";
|
import "./ha-svg-icon";
|
||||||
|
import "@material/mwc-button/mwc-button";
|
||||||
|
|
||||||
@customElement("ha-button-toggle-group")
|
@customElement("ha-button-toggle-group")
|
||||||
export class HaButtonToggleGroup extends LitElement {
|
export class HaButtonToggleGroup extends LitElement {
|
||||||
@ -21,17 +22,22 @@ export class HaButtonToggleGroup extends LitElement {
|
|||||||
protected render(): TemplateResult {
|
protected render(): TemplateResult {
|
||||||
return html`
|
return html`
|
||||||
<div>
|
<div>
|
||||||
${this.buttons.map(
|
${this.buttons.map((button) =>
|
||||||
(button) => html`
|
button.iconPath
|
||||||
<mwc-icon-button
|
? html`<mwc-icon-button
|
||||||
.label=${button.label}
|
.label=${button.label}
|
||||||
.value=${button.value}
|
.value=${button.value}
|
||||||
?active=${this.active === button.value}
|
?active=${this.active === button.value}
|
||||||
@click=${this._handleClick}
|
@click=${this._handleClick}
|
||||||
>
|
>
|
||||||
<ha-svg-icon .path=${button.iconPath}></ha-svg-icon>
|
<ha-svg-icon .path=${button.iconPath}></ha-svg-icon>
|
||||||
</mwc-icon-button>
|
</mwc-icon-button>`
|
||||||
`
|
: html`<mwc-button
|
||||||
|
.value=${button.value}
|
||||||
|
?active=${this.active === button.value}
|
||||||
|
@click=${this._handleClick}
|
||||||
|
>${button.label}</mwc-button
|
||||||
|
>`
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
@ -49,13 +55,15 @@ export class HaButtonToggleGroup extends LitElement {
|
|||||||
--mdc-icon-button-size: var(--button-toggle-size, 36px);
|
--mdc-icon-button-size: var(--button-toggle-size, 36px);
|
||||||
--mdc-icon-size: var(--button-toggle-icon-size, 20px);
|
--mdc-icon-size: var(--button-toggle-icon-size, 20px);
|
||||||
}
|
}
|
||||||
mwc-icon-button {
|
mwc-icon-button,
|
||||||
|
mwc-button {
|
||||||
border: 1px solid var(--primary-color);
|
border: 1px solid var(--primary-color);
|
||||||
border-right-width: 0px;
|
border-right-width: 0px;
|
||||||
position: relative;
|
position: relative;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
mwc-icon-button::before {
|
mwc-icon-button::before,
|
||||||
|
mwc-button::before {
|
||||||
top: 0;
|
top: 0;
|
||||||
left: 0;
|
left: 0;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
@ -67,17 +75,21 @@ export class HaButtonToggleGroup extends LitElement {
|
|||||||
content: "";
|
content: "";
|
||||||
transition: opacity 15ms linear, background-color 15ms linear;
|
transition: opacity 15ms linear, background-color 15ms linear;
|
||||||
}
|
}
|
||||||
mwc-icon-button[active]::before {
|
mwc-icon-button[active]::before,
|
||||||
|
mwc-button[active]::before {
|
||||||
opacity: var(--mdc-icon-button-ripple-opacity, 0.12);
|
opacity: var(--mdc-icon-button-ripple-opacity, 0.12);
|
||||||
}
|
}
|
||||||
mwc-icon-button:first-child {
|
mwc-icon-button:first-child,
|
||||||
|
mwc-button:first-child {
|
||||||
border-radius: 4px 0 0 4px;
|
border-radius: 4px 0 0 4px;
|
||||||
}
|
}
|
||||||
mwc-icon-button:last-child {
|
mwc-icon-button:last-child,
|
||||||
|
mwc-button:last-child {
|
||||||
border-radius: 0 4px 4px 0;
|
border-radius: 0 4px 4px 0;
|
||||||
border-right-width: 1px;
|
border-right-width: 1px;
|
||||||
}
|
}
|
||||||
mwc-icon-button:only-child {
|
mwc-icon-button:only-child,
|
||||||
|
mwc-button:only-child {
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
border-right-width: 1px;
|
border-right-width: 1px;
|
||||||
}
|
}
|
||||||
|
@ -19,12 +19,14 @@ class HaExpansionPanel extends LitElement {
|
|||||||
|
|
||||||
@property({ type: Boolean, reflect: true }) outlined = false;
|
@property({ type: Boolean, reflect: true }) outlined = false;
|
||||||
|
|
||||||
|
@property() header?: string;
|
||||||
|
|
||||||
@query(".container") private _container!: HTMLDivElement;
|
@query(".container") private _container!: HTMLDivElement;
|
||||||
|
|
||||||
protected render(): TemplateResult {
|
protected render(): TemplateResult {
|
||||||
return html`
|
return html`
|
||||||
<div class="summary" @click=${this._toggleContainer}>
|
<div class="summary" @click=${this._toggleContainer}>
|
||||||
<slot name="title"></slot>
|
<slot name="header">${this.header}</slot>
|
||||||
<ha-svg-icon
|
<ha-svg-icon
|
||||||
.path=${mdiChevronDown}
|
.path=${mdiChevronDown}
|
||||||
class="summary-icon ${classMap({ expanded: this.expanded })}"
|
class="summary-icon ${classMap({ expanded: this.expanded })}"
|
||||||
@ -76,7 +78,7 @@ class HaExpansionPanel extends LitElement {
|
|||||||
|
|
||||||
.summary {
|
.summary {
|
||||||
display: flex;
|
display: flex;
|
||||||
padding: var(--expansion-panel-summary-padding, 0px 16px);
|
padding: var(--expansion-panel-summary-padding, 0);
|
||||||
min-height: 48px;
|
min-height: 48px;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
20
src/components/ha-fab.ts
Normal file
20
src/components/ha-fab.ts
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
import type { Fab } from "@material/mwc-fab";
|
||||||
|
import "@material/mwc-fab";
|
||||||
|
import { customElement } from "lit-element";
|
||||||
|
import { Constructor } from "../types";
|
||||||
|
|
||||||
|
const MwcFab = customElements.get("mwc-fab") as Constructor<Fab>;
|
||||||
|
|
||||||
|
@customElement("ha-fab")
|
||||||
|
export class HaFab extends MwcFab {
|
||||||
|
protected firstUpdated(changedProperties) {
|
||||||
|
super.firstUpdated(changedProperties);
|
||||||
|
this.style.setProperty("--mdc-theme-secondary", "var(--primary-color)");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface HTMLElementTagNameMap {
|
||||||
|
"ha-fab": HaFab;
|
||||||
|
}
|
||||||
|
}
|
@ -60,8 +60,9 @@ export class HaIconInput extends LitElement {
|
|||||||
static get styles() {
|
static get styles() {
|
||||||
return css`
|
return css`
|
||||||
ha-icon {
|
ha-icon {
|
||||||
position: relative;
|
position: absolute;
|
||||||
bottom: 4px;
|
bottom: 2px;
|
||||||
|
right: 0;
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
45
src/components/ha-selector/ha-selector-action.ts
Normal file
45
src/components/ha-selector/ha-selector-action.ts
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
import {
|
||||||
|
css,
|
||||||
|
CSSResult,
|
||||||
|
customElement,
|
||||||
|
html,
|
||||||
|
LitElement,
|
||||||
|
property,
|
||||||
|
} from "lit-element";
|
||||||
|
import { HomeAssistant } from "../../types";
|
||||||
|
import { ActionSelector } from "../../data/selector";
|
||||||
|
import { Action } from "../../data/script";
|
||||||
|
import "../../panels/config/automation/action/ha-automation-action";
|
||||||
|
|
||||||
|
@customElement("ha-selector-action")
|
||||||
|
export class HaActionSelector extends LitElement {
|
||||||
|
@property() public hass!: HomeAssistant;
|
||||||
|
|
||||||
|
@property() public selector!: ActionSelector;
|
||||||
|
|
||||||
|
@property() public value?: Action;
|
||||||
|
|
||||||
|
@property() public label?: string;
|
||||||
|
|
||||||
|
protected render() {
|
||||||
|
return html`<ha-automation-action
|
||||||
|
.actions=${this.value || []}
|
||||||
|
.hass=${this.hass}
|
||||||
|
></ha-automation-action>`;
|
||||||
|
}
|
||||||
|
|
||||||
|
static get styles(): CSSResult {
|
||||||
|
return css`
|
||||||
|
ha-automation-action {
|
||||||
|
display: block;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface HTMLElementTagNameMap {
|
||||||
|
"ha-selector-action": HaActionSelector;
|
||||||
|
}
|
||||||
|
}
|
@ -1,7 +1,16 @@
|
|||||||
import { customElement, html, LitElement, property } from "lit-element";
|
import {
|
||||||
|
customElement,
|
||||||
|
html,
|
||||||
|
internalProperty,
|
||||||
|
LitElement,
|
||||||
|
property,
|
||||||
|
} from "lit-element";
|
||||||
import { HomeAssistant } from "../../types";
|
import { HomeAssistant } from "../../types";
|
||||||
import { AreaSelector } from "../../data/selector";
|
import { AreaSelector } from "../../data/selector";
|
||||||
import "../ha-area-picker";
|
import "../ha-area-picker";
|
||||||
|
import { ConfigEntry, getConfigEntries } from "../../data/config_entries";
|
||||||
|
import { DeviceRegistryEntry } from "../../data/device_registry";
|
||||||
|
import { EntityRegistryEntry } from "../../data/entity_registry";
|
||||||
|
|
||||||
@customElement("ha-selector-area")
|
@customElement("ha-selector-area")
|
||||||
export class HaAreaSelector extends LitElement {
|
export class HaAreaSelector extends LitElement {
|
||||||
@ -13,14 +22,76 @@ export class HaAreaSelector extends LitElement {
|
|||||||
|
|
||||||
@property() public label?: string;
|
@property() public label?: string;
|
||||||
|
|
||||||
|
@internalProperty() public _configEntries?: ConfigEntry[];
|
||||||
|
|
||||||
|
protected updated(changedProperties) {
|
||||||
|
if (changedProperties.has("selector")) {
|
||||||
|
const oldSelector = changedProperties.get("selector");
|
||||||
|
if (
|
||||||
|
oldSelector !== this.selector &&
|
||||||
|
this.selector.area.device?.integration
|
||||||
|
) {
|
||||||
|
this._loadConfigEntries();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
protected render() {
|
protected render() {
|
||||||
return html`<ha-area-picker
|
return html`<ha-area-picker
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
.value=${this.value}
|
.value=${this.value}
|
||||||
.label=${this.label}
|
.label=${this.label}
|
||||||
no-add
|
no-add
|
||||||
|
.deviceFilter=${(device) => this._filterDevices(device)}
|
||||||
|
.entityFilter=${(entity) => this._filterEntities(entity)}
|
||||||
|
.includeDeviceClasses=${this.selector.area.entity?.device_class
|
||||||
|
? [this.selector.area.entity.device_class]
|
||||||
|
: undefined}
|
||||||
|
.includeDomains=${this.selector.area.entity?.domain
|
||||||
|
? [this.selector.area.entity.domain]
|
||||||
|
: undefined}
|
||||||
></ha-area-picker>`;
|
></ha-area-picker>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private _filterEntities(entity: EntityRegistryEntry): boolean {
|
||||||
|
if (this.selector.area.entity?.integration) {
|
||||||
|
if (entity.platform !== this.selector.area.entity.integration) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _filterDevices(device: DeviceRegistryEntry): boolean {
|
||||||
|
if (
|
||||||
|
this.selector.area.device?.manufacturer &&
|
||||||
|
device.manufacturer !== this.selector.area.device.manufacturer
|
||||||
|
) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
this.selector.area.device?.model &&
|
||||||
|
device.model !== this.selector.area.device.model
|
||||||
|
) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (this.selector.area.device?.integration) {
|
||||||
|
if (
|
||||||
|
!this._configEntries?.some((entry) =>
|
||||||
|
device.config_entries.includes(entry.entry_id)
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async _loadConfigEntries() {
|
||||||
|
this._configEntries = (await getConfigEntries(this.hass)).filter(
|
||||||
|
(entry) => entry.domain === this.selector.area.device?.integration
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
|
@ -19,7 +19,7 @@ export class HaEntitySelector extends SubscribeMixin(LitElement) {
|
|||||||
|
|
||||||
@property() public selector!: EntitySelector;
|
@property() public selector!: EntitySelector;
|
||||||
|
|
||||||
@internalProperty() private _entities?: Record<string, string>;
|
@internalProperty() private _entityPlaformLookup?: Record<string, string>;
|
||||||
|
|
||||||
@property() public value?: any;
|
@property() public value?: any;
|
||||||
|
|
||||||
@ -45,7 +45,7 @@ export class HaEntitySelector extends SubscribeMixin(LitElement) {
|
|||||||
}
|
}
|
||||||
entityLookup[confEnt.entity_id] = confEnt.platform;
|
entityLookup[confEnt.entity_id] = confEnt.platform;
|
||||||
}
|
}
|
||||||
this._entities = entityLookup;
|
this._entityPlaformLookup = entityLookup;
|
||||||
}),
|
}),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
@ -66,8 +66,9 @@ export class HaEntitySelector extends SubscribeMixin(LitElement) {
|
|||||||
}
|
}
|
||||||
if (this.selector.entity.integration) {
|
if (this.selector.entity.integration) {
|
||||||
if (
|
if (
|
||||||
!this._entities ||
|
!this._entityPlaformLookup ||
|
||||||
this._entities[entity.entity_id] !== this.selector.entity.integration
|
this._entityPlaformLookup[entity.entity_id] !==
|
||||||
|
this.selector.entity.integration
|
||||||
) {
|
) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
153
src/components/ha-selector/ha-selector-target.ts
Normal file
153
src/components/ha-selector/ha-selector-target.ts
Normal file
@ -0,0 +1,153 @@
|
|||||||
|
import {
|
||||||
|
css,
|
||||||
|
CSSResult,
|
||||||
|
customElement,
|
||||||
|
html,
|
||||||
|
internalProperty,
|
||||||
|
LitElement,
|
||||||
|
property,
|
||||||
|
} from "lit-element";
|
||||||
|
import { HomeAssistant } from "../../types";
|
||||||
|
import { TargetSelector } from "../../data/selector";
|
||||||
|
import { ConfigEntry, getConfigEntries } from "../../data/config_entries";
|
||||||
|
import { DeviceRegistryEntry } from "../../data/device_registry";
|
||||||
|
import "../ha-target-picker";
|
||||||
|
import "@material/mwc-list/mwc-list-item";
|
||||||
|
import "@polymer/paper-input/paper-input";
|
||||||
|
import "@material/mwc-list/mwc-list";
|
||||||
|
import {
|
||||||
|
EntityRegistryEntry,
|
||||||
|
subscribeEntityRegistry,
|
||||||
|
} from "../../data/entity_registry";
|
||||||
|
import { Target } from "../../data/target";
|
||||||
|
import "@material/mwc-tab-bar/mwc-tab-bar";
|
||||||
|
import "@material/mwc-tab/mwc-tab";
|
||||||
|
import { HassEntity, UnsubscribeFunc } from "home-assistant-js-websocket";
|
||||||
|
import { SubscribeMixin } from "../../mixins/subscribe-mixin";
|
||||||
|
|
||||||
|
@customElement("ha-selector-target")
|
||||||
|
export class HaTargetSelector extends SubscribeMixin(LitElement) {
|
||||||
|
@property() public hass!: HomeAssistant;
|
||||||
|
|
||||||
|
@property() public selector!: TargetSelector;
|
||||||
|
|
||||||
|
@property() public value?: Target;
|
||||||
|
|
||||||
|
@property() public label?: string;
|
||||||
|
|
||||||
|
@internalProperty() private _entityPlaformLookup?: Record<string, string>;
|
||||||
|
|
||||||
|
@internalProperty() private _configEntries?: ConfigEntry[];
|
||||||
|
|
||||||
|
public hassSubscribe(): UnsubscribeFunc[] {
|
||||||
|
return [
|
||||||
|
subscribeEntityRegistry(this.hass.connection!, (entities) => {
|
||||||
|
const entityLookup = {};
|
||||||
|
for (const confEnt of entities) {
|
||||||
|
if (!confEnt.platform) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
entityLookup[confEnt.entity_id] = confEnt.platform;
|
||||||
|
}
|
||||||
|
this._entityPlaformLookup = entityLookup;
|
||||||
|
}),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
protected updated(changedProperties) {
|
||||||
|
if (changedProperties.has("selector")) {
|
||||||
|
const oldSelector = changedProperties.get("selector");
|
||||||
|
if (
|
||||||
|
oldSelector !== this.selector &&
|
||||||
|
this.selector.target.device?.integration
|
||||||
|
) {
|
||||||
|
this._loadConfigEntries();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected render() {
|
||||||
|
return html`<ha-target-picker
|
||||||
|
.hass=${this.hass}
|
||||||
|
.value=${this.value}
|
||||||
|
.deviceFilter=${(device) => this._filterDevices(device)}
|
||||||
|
.entityRegFilter=${(entity: EntityRegistryEntry) =>
|
||||||
|
this._filterRegEntities(entity)}
|
||||||
|
.entityFilter=${(entity: HassEntity) => this._filterEntities(entity)}
|
||||||
|
.includeDeviceClasses=${this.selector.target.entity?.device_class
|
||||||
|
? [this.selector.target.entity.device_class]
|
||||||
|
: undefined}
|
||||||
|
.includeDomains=${this.selector.target.entity?.domain
|
||||||
|
? [this.selector.target.entity.domain]
|
||||||
|
: undefined}
|
||||||
|
></ha-target-picker>`;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _filterEntities(entity: HassEntity): boolean {
|
||||||
|
if (this.selector.target.entity?.integration) {
|
||||||
|
if (
|
||||||
|
!this._entityPlaformLookup ||
|
||||||
|
this._entityPlaformLookup[entity.entity_id] !==
|
||||||
|
this.selector.target.entity.integration
|
||||||
|
) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _filterRegEntities(entity: EntityRegistryEntry): boolean {
|
||||||
|
if (this.selector.target.entity?.integration) {
|
||||||
|
if (entity.platform !== this.selector.target.entity.integration) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _filterDevices(device: DeviceRegistryEntry): boolean {
|
||||||
|
if (
|
||||||
|
this.selector.target.device?.manufacturer &&
|
||||||
|
device.manufacturer !== this.selector.target.device.manufacturer
|
||||||
|
) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
this.selector.target.device?.model &&
|
||||||
|
device.model !== this.selector.target.device.model
|
||||||
|
) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (this.selector.target.device?.integration) {
|
||||||
|
if (
|
||||||
|
!this._configEntries?.some((entry) =>
|
||||||
|
device.config_entries.includes(entry.entry_id)
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async _loadConfigEntries() {
|
||||||
|
this._configEntries = (await getConfigEntries(this.hass)).filter(
|
||||||
|
(entry) => entry.domain === this.selector.target.device?.integration
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
static get styles(): CSSResult {
|
||||||
|
return css`
|
||||||
|
ha-target-picker {
|
||||||
|
margin: 0 -8px;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface HTMLElementTagNameMap {
|
||||||
|
"ha-selector-target": HaTargetSelector;
|
||||||
|
}
|
||||||
|
}
|
@ -5,9 +5,11 @@ import { HomeAssistant } from "../../types";
|
|||||||
import "./ha-selector-entity";
|
import "./ha-selector-entity";
|
||||||
import "./ha-selector-device";
|
import "./ha-selector-device";
|
||||||
import "./ha-selector-area";
|
import "./ha-selector-area";
|
||||||
|
import "./ha-selector-target";
|
||||||
import "./ha-selector-number";
|
import "./ha-selector-number";
|
||||||
import "./ha-selector-boolean";
|
import "./ha-selector-boolean";
|
||||||
import "./ha-selector-time";
|
import "./ha-selector-time";
|
||||||
|
import "./ha-selector-action";
|
||||||
import { Selector } from "../../data/selector";
|
import { Selector } from "../../data/selector";
|
||||||
|
|
||||||
@customElement("ha-selector")
|
@customElement("ha-selector")
|
||||||
|
595
src/components/ha-target-picker.ts
Normal file
595
src/components/ha-target-picker.ts
Normal file
@ -0,0 +1,595 @@
|
|||||||
|
import {
|
||||||
|
css,
|
||||||
|
CSSResult,
|
||||||
|
customElement,
|
||||||
|
html,
|
||||||
|
internalProperty,
|
||||||
|
LitElement,
|
||||||
|
property,
|
||||||
|
query,
|
||||||
|
unsafeCSS,
|
||||||
|
} from "lit-element";
|
||||||
|
import { HomeAssistant } from "../types";
|
||||||
|
// @ts-ignore
|
||||||
|
import chipStyles from "@material/chips/dist/mdc.chips.min.css";
|
||||||
|
import {
|
||||||
|
mdiSofa,
|
||||||
|
mdiDevices,
|
||||||
|
mdiClose,
|
||||||
|
mdiPlus,
|
||||||
|
mdiUnfoldMoreVertical,
|
||||||
|
} from "@mdi/js";
|
||||||
|
import "./ha-svg-icon";
|
||||||
|
import "./ha-icon";
|
||||||
|
import "@material/mwc-icon-button/mwc-icon-button";
|
||||||
|
import { classMap } from "lit-html/directives/class-map";
|
||||||
|
import "@material/mwc-button/mwc-button";
|
||||||
|
import { UnsubscribeFunc } from "home-assistant-js-websocket";
|
||||||
|
import {
|
||||||
|
AreaRegistryEntry,
|
||||||
|
subscribeAreaRegistry,
|
||||||
|
} from "../data/area_registry";
|
||||||
|
import {
|
||||||
|
DeviceRegistryEntry,
|
||||||
|
subscribeDeviceRegistry,
|
||||||
|
} from "../data/device_registry";
|
||||||
|
import {
|
||||||
|
EntityRegistryEntry,
|
||||||
|
subscribeEntityRegistry,
|
||||||
|
} from "../data/entity_registry";
|
||||||
|
import { SubscribeMixin } from "../mixins/subscribe-mixin";
|
||||||
|
import { computeStateName } from "../common/entity/compute_state_name";
|
||||||
|
import { stateIcon } from "../common/entity/state_icon";
|
||||||
|
import { fireEvent } from "../common/dom/fire_event";
|
||||||
|
import type { HaDevicePickerDeviceFilterFunc } from "./device/ha-device-picker";
|
||||||
|
import { computeDomain } from "../common/entity/compute_domain";
|
||||||
|
import { Target } from "../data/target";
|
||||||
|
import { ensureArray } from "../common/ensure-array";
|
||||||
|
import "./entity/ha-entity-picker";
|
||||||
|
import "./device/ha-device-picker";
|
||||||
|
import "./ha-area-picker";
|
||||||
|
import type { HaEntityPickerEntityFilterFunc } from "./entity/ha-entity-picker";
|
||||||
|
import "@polymer/paper-tooltip/paper-tooltip";
|
||||||
|
|
||||||
|
@customElement("ha-target-picker")
|
||||||
|
export class HaTargetPicker extends SubscribeMixin(LitElement) {
|
||||||
|
@property() public hass!: HomeAssistant;
|
||||||
|
|
||||||
|
@property() public value?: Target;
|
||||||
|
|
||||||
|
@property() public label?: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Show only targets with entities from specific domains.
|
||||||
|
* @type {Array}
|
||||||
|
* @attr include-domains
|
||||||
|
*/
|
||||||
|
@property({ type: Array, attribute: "include-domains" })
|
||||||
|
public includeDomains?: string[];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Show only targets with entities of these device classes.
|
||||||
|
* @type {Array}
|
||||||
|
* @attr include-device-classes
|
||||||
|
*/
|
||||||
|
@property({ type: Array, attribute: "include-device-classes" })
|
||||||
|
public includeDeviceClasses?: string[];
|
||||||
|
|
||||||
|
@property() public deviceFilter?: HaDevicePickerDeviceFilterFunc;
|
||||||
|
|
||||||
|
@property() public entityRegFilter?: (entity: EntityRegistryEntry) => boolean;
|
||||||
|
|
||||||
|
@property() public entityFilter?: HaEntityPickerEntityFilterFunc;
|
||||||
|
|
||||||
|
@internalProperty() private _areas?: { [areaId: string]: AreaRegistryEntry };
|
||||||
|
|
||||||
|
@internalProperty() private _devices?: {
|
||||||
|
[deviceId: string]: DeviceRegistryEntry;
|
||||||
|
};
|
||||||
|
|
||||||
|
@internalProperty() private _entities?: EntityRegistryEntry[];
|
||||||
|
|
||||||
|
@internalProperty() private _addMode?: "area_id" | "entity_id" | "device_id";
|
||||||
|
|
||||||
|
@query("#input") private _inputElement?;
|
||||||
|
|
||||||
|
public hassSubscribe(): UnsubscribeFunc[] {
|
||||||
|
return [
|
||||||
|
subscribeAreaRegistry(this.hass.connection!, (areas) => {
|
||||||
|
const areaLookup: { [areaId: string]: AreaRegistryEntry } = {};
|
||||||
|
for (const area of areas) {
|
||||||
|
areaLookup[area.area_id] = area;
|
||||||
|
}
|
||||||
|
this._areas = areaLookup;
|
||||||
|
}),
|
||||||
|
subscribeDeviceRegistry(this.hass.connection!, (devices) => {
|
||||||
|
const deviceLookup: { [deviceId: string]: DeviceRegistryEntry } = {};
|
||||||
|
for (const device of devices) {
|
||||||
|
deviceLookup[device.id] = device;
|
||||||
|
}
|
||||||
|
this._devices = deviceLookup;
|
||||||
|
}),
|
||||||
|
subscribeEntityRegistry(this.hass.connection!, (entities) => {
|
||||||
|
this._entities = entities;
|
||||||
|
}),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
protected render() {
|
||||||
|
if (!this._areas || !this._devices || !this._entities) {
|
||||||
|
return html``;
|
||||||
|
}
|
||||||
|
return html`<div class="mdc-chip-set items">
|
||||||
|
${ensureArray(this.value?.area_id)?.map((area_id) => {
|
||||||
|
const area = this._areas![area_id];
|
||||||
|
return this._renderChip(
|
||||||
|
"area_id",
|
||||||
|
area_id,
|
||||||
|
area?.name || area_id,
|
||||||
|
undefined,
|
||||||
|
mdiSofa
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
${ensureArray(this.value?.device_id)?.map((device_id) => {
|
||||||
|
const device = this._devices![device_id];
|
||||||
|
return this._renderChip(
|
||||||
|
"device_id",
|
||||||
|
device_id,
|
||||||
|
device?.name || device_id,
|
||||||
|
undefined,
|
||||||
|
mdiDevices
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
${ensureArray(this.value?.entity_id)?.map((entity_id) => {
|
||||||
|
const entity = this.hass.states[entity_id];
|
||||||
|
return this._renderChip(
|
||||||
|
"entity_id",
|
||||||
|
entity_id,
|
||||||
|
entity ? computeStateName(entity) : entity_id,
|
||||||
|
entity ? stateIcon(entity) : undefined
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
${this._renderPicker()}
|
||||||
|
<div class="mdc-chip-set">
|
||||||
|
<div
|
||||||
|
class="mdc-chip area_id add"
|
||||||
|
.type=${"area_id"}
|
||||||
|
@click=${this._showPicker}
|
||||||
|
>
|
||||||
|
<div class="mdc-chip__ripple"></div>
|
||||||
|
<ha-svg-icon
|
||||||
|
class="mdc-chip__icon mdc-chip__icon--leading"
|
||||||
|
.path=${mdiPlus}
|
||||||
|
></ha-svg-icon>
|
||||||
|
<span role="gridcell">
|
||||||
|
<span role="button" tabindex="0" class="mdc-chip__primary-action">
|
||||||
|
<span class="mdc-chip__text"
|
||||||
|
>${this.hass.localize(
|
||||||
|
"ui.components.target-picker.add_area_id"
|
||||||
|
)}</span
|
||||||
|
>
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="mdc-chip device_id add"
|
||||||
|
.type=${"device_id"}
|
||||||
|
@click=${this._showPicker}
|
||||||
|
>
|
||||||
|
<div class="mdc-chip__ripple"></div>
|
||||||
|
<ha-svg-icon
|
||||||
|
class="mdc-chip__icon mdc-chip__icon--leading"
|
||||||
|
.path=${mdiPlus}
|
||||||
|
></ha-svg-icon>
|
||||||
|
<span role="gridcell">
|
||||||
|
<span role="button" tabindex="0" class="mdc-chip__primary-action">
|
||||||
|
<span class="mdc-chip__text"
|
||||||
|
>${this.hass.localize(
|
||||||
|
"ui.components.target-picker.add_device_id"
|
||||||
|
)}</span
|
||||||
|
>
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="mdc-chip entity_id add"
|
||||||
|
.type=${"entity_id"}
|
||||||
|
@click=${this._showPicker}
|
||||||
|
>
|
||||||
|
<div class="mdc-chip__ripple"></div>
|
||||||
|
<ha-svg-icon
|
||||||
|
class="mdc-chip__icon mdc-chip__icon--leading"
|
||||||
|
.path=${mdiPlus}
|
||||||
|
></ha-svg-icon>
|
||||||
|
<span role="gridcell">
|
||||||
|
<span role="button" tabindex="0" class="mdc-chip__primary-action">
|
||||||
|
<span class="mdc-chip__text"
|
||||||
|
>${this.hass.localize(
|
||||||
|
"ui.components.target-picker.add_entity_id"
|
||||||
|
)}</span
|
||||||
|
>
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>`;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async _showPicker(ev) {
|
||||||
|
this._addMode = ev.currentTarget.type;
|
||||||
|
await this.updateComplete;
|
||||||
|
setTimeout(() => {
|
||||||
|
this._inputElement?.open();
|
||||||
|
this._inputElement?.focus();
|
||||||
|
}, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
private _renderChip(
|
||||||
|
type: string,
|
||||||
|
id: string,
|
||||||
|
name: string,
|
||||||
|
icon?: string,
|
||||||
|
iconPath?: string
|
||||||
|
) {
|
||||||
|
return html`
|
||||||
|
<div
|
||||||
|
class="mdc-chip ${classMap({
|
||||||
|
[type]: true,
|
||||||
|
})}"
|
||||||
|
>
|
||||||
|
${iconPath
|
||||||
|
? html`<ha-svg-icon
|
||||||
|
class="mdc-chip__icon mdc-chip__icon--leading"
|
||||||
|
.path=${iconPath}
|
||||||
|
></ha-svg-icon>`
|
||||||
|
: ""}
|
||||||
|
${icon
|
||||||
|
? html`<ha-icon
|
||||||
|
class="mdc-chip__icon mdc-chip__icon--leading"
|
||||||
|
.icon=${icon}
|
||||||
|
></ha-icon>`
|
||||||
|
: ""}
|
||||||
|
<span role="gridcell">
|
||||||
|
<span role="button" tabindex="0" class="mdc-chip__primary-action">
|
||||||
|
<span class="mdc-chip__text">${name}</span>
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
${type === "entity_id"
|
||||||
|
? ""
|
||||||
|
: html` <span role="gridcell">
|
||||||
|
<mwc-icon-button
|
||||||
|
class="expand-btn mdc-chip__icon mdc-chip__icon--trailing"
|
||||||
|
tabindex="-1"
|
||||||
|
role="button"
|
||||||
|
.label=${"Expand"}
|
||||||
|
.id=${id}
|
||||||
|
.type=${type}
|
||||||
|
@click=${this._handleExpand}
|
||||||
|
>
|
||||||
|
<ha-svg-icon .path=${mdiUnfoldMoreVertical}></ha-svg-icon>
|
||||||
|
</mwc-icon-button>
|
||||||
|
<paper-tooltip class="expand" animation-delay="0"
|
||||||
|
>${this.hass.localize(
|
||||||
|
`ui.components.target-picker.expand_${type}`
|
||||||
|
)}</paper-tooltip
|
||||||
|
>
|
||||||
|
</span>`}
|
||||||
|
<span role="gridcell">
|
||||||
|
<mwc-icon-button
|
||||||
|
class="mdc-chip__icon mdc-chip__icon--trailing"
|
||||||
|
tabindex="-1"
|
||||||
|
role="button"
|
||||||
|
.label=${"Remove"}
|
||||||
|
.id=${id}
|
||||||
|
.type=${type}
|
||||||
|
@click=${this._handleRemove}
|
||||||
|
>
|
||||||
|
<ha-svg-icon .path=${mdiClose}></ha-svg-icon>
|
||||||
|
</mwc-icon-button>
|
||||||
|
<paper-tooltip animation-delay="0"
|
||||||
|
>${this.hass.localize(
|
||||||
|
`ui.components.target-picker.remove_${type}`
|
||||||
|
)}</paper-tooltip
|
||||||
|
>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _renderPicker() {
|
||||||
|
switch (this._addMode) {
|
||||||
|
case "area_id":
|
||||||
|
return html`<ha-area-picker
|
||||||
|
.hass=${this.hass}
|
||||||
|
id="input"
|
||||||
|
.type=${"area_id"}
|
||||||
|
.label=${this.hass.localize(
|
||||||
|
"ui.components.target-picker.add_area_id"
|
||||||
|
)}
|
||||||
|
no-add
|
||||||
|
.deviceFilter=${this.deviceFilter}
|
||||||
|
.entityFilter=${this.entityRegFilter}
|
||||||
|
.includeDeviceClasses=${this.includeDeviceClasses}
|
||||||
|
.includeDomains=${this.includeDomains}
|
||||||
|
@value-changed=${this._targetPicked}
|
||||||
|
></ha-area-picker>`;
|
||||||
|
case "device_id":
|
||||||
|
return html`<ha-device-picker
|
||||||
|
.hass=${this.hass}
|
||||||
|
id="input"
|
||||||
|
.type=${"device_id"}
|
||||||
|
.label=${this.hass.localize(
|
||||||
|
"ui.components.target-picker.add_device_id"
|
||||||
|
)}
|
||||||
|
.deviceFilter=${this.deviceFilter}
|
||||||
|
.entityFilter=${this.entityRegFilter}
|
||||||
|
.includeDeviceClasses=${this.includeDeviceClasses}
|
||||||
|
.includeDomains=${this.includeDomains}
|
||||||
|
@value-changed=${this._targetPicked}
|
||||||
|
></ha-device-picker>`;
|
||||||
|
case "entity_id":
|
||||||
|
return html`<ha-entity-picker
|
||||||
|
.hass=${this.hass}
|
||||||
|
id="input"
|
||||||
|
.type=${"entity_id"}
|
||||||
|
.label=${this.hass.localize(
|
||||||
|
"ui.components.target-picker.add_entity_id"
|
||||||
|
)}
|
||||||
|
.entityFilter=${this.entityFilter}
|
||||||
|
.includeDeviceClasses=${this.includeDeviceClasses}
|
||||||
|
.includeDomains=${this.includeDomains}
|
||||||
|
@value-changed=${this._targetPicked}
|
||||||
|
></ha-entity-picker>`;
|
||||||
|
}
|
||||||
|
return html``;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _targetPicked(ev) {
|
||||||
|
ev.stopPropagation();
|
||||||
|
if (!ev.detail.value) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const value = ev.detail.value;
|
||||||
|
const target = ev.currentTarget;
|
||||||
|
target.value = "";
|
||||||
|
this._addMode = undefined;
|
||||||
|
fireEvent(this, "value-changed", {
|
||||||
|
value: this.value
|
||||||
|
? {
|
||||||
|
...this.value,
|
||||||
|
[target.type]: this.value[target.type]
|
||||||
|
? [...ensureArray(this.value[target.type]), value]
|
||||||
|
: value,
|
||||||
|
}
|
||||||
|
: { [target.type]: value },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private _handleExpand(ev) {
|
||||||
|
const target = ev.currentTarget as any;
|
||||||
|
const newDevices: string[] = [];
|
||||||
|
const newEntities: string[] = [];
|
||||||
|
if (target.type === "area_id") {
|
||||||
|
Object.values(this._devices!).forEach((device) => {
|
||||||
|
if (
|
||||||
|
device.area_id === target.id &&
|
||||||
|
!this.value!.device_id?.includes(device.id) &&
|
||||||
|
this._deviceMeetsFilter(device)
|
||||||
|
) {
|
||||||
|
newDevices.push(device.id);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
this._entities!.forEach((entity) => {
|
||||||
|
if (
|
||||||
|
entity.area_id === target.id &&
|
||||||
|
!this.value!.entity_id?.includes(entity.entity_id) &&
|
||||||
|
this._entityRegMeetsFilter(entity)
|
||||||
|
) {
|
||||||
|
newEntities.push(entity.entity_id);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else if (target.type === "device_id") {
|
||||||
|
this._entities!.forEach((entity) => {
|
||||||
|
if (
|
||||||
|
entity.device_id === target.id &&
|
||||||
|
!this.value!.entity_id?.includes(entity.entity_id) &&
|
||||||
|
this._entityRegMeetsFilter(entity)
|
||||||
|
) {
|
||||||
|
newEntities.push(entity.entity_id);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let value = this.value;
|
||||||
|
if (newEntities.length) {
|
||||||
|
value = this._addItems(value, "entity_id", newEntities);
|
||||||
|
}
|
||||||
|
if (newDevices.length) {
|
||||||
|
value = this._addItems(value, "device_id", newDevices);
|
||||||
|
}
|
||||||
|
value = this._removeItem(value, target.type, target.id);
|
||||||
|
fireEvent(this, "value-changed", { value });
|
||||||
|
}
|
||||||
|
|
||||||
|
private _handleRemove(ev) {
|
||||||
|
const target = ev.currentTarget as any;
|
||||||
|
fireEvent(this, "value-changed", {
|
||||||
|
value: this._removeItem(this.value, target.type, target.id),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private _addItems(
|
||||||
|
value: this["value"],
|
||||||
|
type: string,
|
||||||
|
ids: string[]
|
||||||
|
): this["value"] {
|
||||||
|
return {
|
||||||
|
...value,
|
||||||
|
[type]: value![type] ? ensureArray(value![type])!.concat(ids) : ids,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private _removeItem(
|
||||||
|
value: this["value"],
|
||||||
|
type: string,
|
||||||
|
id: string
|
||||||
|
): this["value"] {
|
||||||
|
return {
|
||||||
|
...value,
|
||||||
|
[type]: ensureArray(value![type])!.filter((val) => val !== id),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private _deviceMeetsFilter(device: DeviceRegistryEntry): boolean {
|
||||||
|
const devEntities = this._entities?.filter(
|
||||||
|
(entity) => entity.device_id === device.id
|
||||||
|
);
|
||||||
|
if (this.includeDomains) {
|
||||||
|
if (!devEntities || !devEntities.length) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
!devEntities.some((entity) =>
|
||||||
|
this.includeDomains!.includes(computeDomain(entity.entity_id))
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.includeDeviceClasses) {
|
||||||
|
if (!devEntities || !devEntities.length) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
!devEntities.some((entity) => {
|
||||||
|
const stateObj = this.hass.states[entity.entity_id];
|
||||||
|
if (!stateObj) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
stateObj.attributes.device_class &&
|
||||||
|
this.includeDeviceClasses!.includes(
|
||||||
|
stateObj.attributes.device_class
|
||||||
|
)
|
||||||
|
);
|
||||||
|
})
|
||||||
|
) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.deviceFilter) {
|
||||||
|
return this.deviceFilter(device);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _entityRegMeetsFilter(entity: EntityRegistryEntry): boolean {
|
||||||
|
if (
|
||||||
|
this.includeDomains &&
|
||||||
|
!this.includeDomains.includes(computeDomain(entity.entity_id))
|
||||||
|
) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (this.includeDeviceClasses) {
|
||||||
|
const stateObj = this.hass.states[entity.entity_id];
|
||||||
|
if (!stateObj) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
!stateObj.attributes.device_class ||
|
||||||
|
!this.includeDeviceClasses!.includes(stateObj.attributes.device_class)
|
||||||
|
) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (this.entityRegFilter) {
|
||||||
|
return this.entityRegFilter(entity);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static get styles(): CSSResult {
|
||||||
|
return css`
|
||||||
|
${unsafeCSS(chipStyles)}
|
||||||
|
.mdc-chip {
|
||||||
|
color: var(--primary-text-color);
|
||||||
|
}
|
||||||
|
.items {
|
||||||
|
z-index: 2;
|
||||||
|
}
|
||||||
|
.mdc-chip.add {
|
||||||
|
color: rgba(0, 0, 0, 0.87);
|
||||||
|
}
|
||||||
|
.mdc-chip:not(.add) {
|
||||||
|
cursor: default;
|
||||||
|
}
|
||||||
|
.mdc-chip mwc-icon-button {
|
||||||
|
--mdc-icon-button-size: 24px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
.mdc-chip mwc-icon-button ha-svg-icon {
|
||||||
|
border-radius: 50%;
|
||||||
|
background: var(--secondary-text-color);
|
||||||
|
}
|
||||||
|
.mdc-chip__icon.mdc-chip__icon--trailing {
|
||||||
|
width: 16px;
|
||||||
|
height: 16px;
|
||||||
|
--mdc-icon-size: 14px;
|
||||||
|
color: var(--card-background-color);
|
||||||
|
}
|
||||||
|
.mdc-chip__icon--leading {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
--mdc-icon-size: 20px;
|
||||||
|
border-radius: 50%;
|
||||||
|
padding: 6px;
|
||||||
|
margin-left: -14px !important;
|
||||||
|
}
|
||||||
|
.expand-btn {
|
||||||
|
margin-right: 0;
|
||||||
|
}
|
||||||
|
.mdc-chip.area_id:not(.add) {
|
||||||
|
border: 2px solid #fed6a4;
|
||||||
|
background: var(--card-background-color);
|
||||||
|
}
|
||||||
|
.mdc-chip.area_id:not(.add) .mdc-chip__icon--leading,
|
||||||
|
.mdc-chip.area_id.add {
|
||||||
|
background: #fed6a4;
|
||||||
|
}
|
||||||
|
.mdc-chip.device_id:not(.add) {
|
||||||
|
border: 2px solid #a8e1fb;
|
||||||
|
background: var(--card-background-color);
|
||||||
|
}
|
||||||
|
.mdc-chip.device_id:not(.add) .mdc-chip__icon--leading,
|
||||||
|
.mdc-chip.device_id.add {
|
||||||
|
background: #a8e1fb;
|
||||||
|
}
|
||||||
|
.mdc-chip.entity_id:not(.add) {
|
||||||
|
border: 2px solid #d2e7b9;
|
||||||
|
background: var(--card-background-color);
|
||||||
|
}
|
||||||
|
.mdc-chip.entity_id:not(.add) .mdc-chip__icon--leading,
|
||||||
|
.mdc-chip.entity_id.add {
|
||||||
|
background: #d2e7b9;
|
||||||
|
}
|
||||||
|
.mdc-chip:hover {
|
||||||
|
z-index: 5;
|
||||||
|
}
|
||||||
|
paper-tooltip.expand {
|
||||||
|
min-width: 200px;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface HTMLElementTagNameMap {
|
||||||
|
"ha-target-picker": HaTargetPicker;
|
||||||
|
}
|
||||||
|
}
|
@ -1,5 +1,5 @@
|
|||||||
import "@material/mwc-button/mwc-button";
|
import "@material/mwc-button/mwc-button";
|
||||||
import "@material/mwc-fab/mwc-fab";
|
import "../ha-fab";
|
||||||
import "@material/mwc-list/mwc-list";
|
import "@material/mwc-list/mwc-list";
|
||||||
import "@material/mwc-list/mwc-list-item";
|
import "@material/mwc-list/mwc-list-item";
|
||||||
import { mdiArrowLeft, mdiClose, mdiPlay, mdiPlus } from "@mdi/js";
|
import { mdiArrowLeft, mdiClose, mdiPlay, mdiPlus } from "@mdi/js";
|
||||||
@ -170,7 +170,7 @@ export class HaMediaPlayerBrowse extends LitElement {
|
|||||||
>
|
>
|
||||||
${this._narrow && currentItem?.can_play
|
${this._narrow && currentItem?.can_play
|
||||||
? html`
|
? html`
|
||||||
<mwc-fab
|
<ha-fab
|
||||||
mini
|
mini
|
||||||
.item=${currentItem}
|
.item=${currentItem}
|
||||||
@click=${this._actionClicked}
|
@click=${this._actionClicked}
|
||||||
@ -185,7 +185,7 @@ export class HaMediaPlayerBrowse extends LitElement {
|
|||||||
${this.hass.localize(
|
${this.hass.localize(
|
||||||
`ui.components.media-browser.${this.action}`
|
`ui.components.media-browser.${this.action}`
|
||||||
)}
|
)}
|
||||||
</mwc-fab>
|
</ha-fab>
|
||||||
`
|
`
|
||||||
: ""}
|
: ""}
|
||||||
</div>
|
</div>
|
||||||
@ -927,7 +927,7 @@ export class HaMediaPlayerBrowse extends LitElement {
|
|||||||
transition: width 0.4s, height 0.4s, padding-bottom 0.4s;
|
transition: width 0.4s, height 0.4s, padding-bottom 0.4s;
|
||||||
}
|
}
|
||||||
|
|
||||||
mwc-fab {
|
ha-fab {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
--mdc-theme-secondary: var(--primary-color);
|
--mdc-theme-secondary: var(--primary-color);
|
||||||
bottom: -20px;
|
bottom: -20px;
|
||||||
@ -1011,7 +1011,7 @@ export class HaMediaPlayerBrowse extends LitElement {
|
|||||||
margin-bottom: 0;
|
margin-bottom: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
:host([scroll]) mwc-fab {
|
:host([scroll]) ha-fab {
|
||||||
bottom: 4px;
|
bottom: 4px;
|
||||||
right: 4px;
|
right: 4px;
|
||||||
--mdc-fab-box-shadow: none;
|
--mdc-fab-box-shadow: none;
|
||||||
|
@ -6,7 +6,7 @@ import { navigate } from "../common/navigate";
|
|||||||
import { Context, HomeAssistant } from "../types";
|
import { Context, HomeAssistant } from "../types";
|
||||||
import { BlueprintInput } from "./blueprint";
|
import { BlueprintInput } from "./blueprint";
|
||||||
import { DeviceCondition, DeviceTrigger } from "./device_automation";
|
import { DeviceCondition, DeviceTrigger } from "./device_automation";
|
||||||
import { Action } from "./script";
|
import { Action, MODES } from "./script";
|
||||||
|
|
||||||
export interface AutomationEntity extends HassEntityBase {
|
export interface AutomationEntity extends HassEntityBase {
|
||||||
attributes: HassEntityAttributeBase & {
|
attributes: HassEntityAttributeBase & {
|
||||||
@ -26,7 +26,7 @@ export interface ManualAutomationConfig {
|
|||||||
trigger: Trigger[];
|
trigger: Trigger[];
|
||||||
condition?: Condition[];
|
condition?: Condition[];
|
||||||
action: Action[];
|
action: Action[];
|
||||||
mode?: "single" | "restart" | "queued" | "parallel";
|
mode?: typeof MODES[number];
|
||||||
max?: number;
|
max?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -17,6 +17,7 @@ export interface DeviceRegistryEntry {
|
|||||||
area_id?: string;
|
area_id?: string;
|
||||||
name_by_user?: string;
|
name_by_user?: string;
|
||||||
entry_type: "service" | null;
|
entry_type: "service" | null;
|
||||||
|
disabled_by: string | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface DeviceEntityLookup {
|
export interface DeviceEntityLookup {
|
||||||
@ -26,6 +27,7 @@ export interface DeviceEntityLookup {
|
|||||||
export interface DeviceRegistryEntryMutableParams {
|
export interface DeviceRegistryEntryMutableParams {
|
||||||
area_id?: string | null;
|
area_id?: string | null;
|
||||||
name_by_user?: string | null;
|
name_by_user?: string | null;
|
||||||
|
disabled_by?: string | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const fallbackDeviceName = (
|
export const fallbackDeviceName = (
|
||||||
|
@ -10,6 +10,7 @@ export interface EntityRegistryEntry {
|
|||||||
platform: string;
|
platform: string;
|
||||||
config_entry_id?: string;
|
config_entry_id?: string;
|
||||||
device_id?: string;
|
device_id?: string;
|
||||||
|
area_id?: string;
|
||||||
disabled_by: string | null;
|
disabled_by: string | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -29,6 +30,7 @@ export interface UpdateEntityRegistryEntryResult {
|
|||||||
export interface EntityRegistryEntryUpdateParams {
|
export interface EntityRegistryEntryUpdateParams {
|
||||||
name?: string | null;
|
name?: string | null;
|
||||||
icon?: string | null;
|
icon?: string | null;
|
||||||
|
area_id?: string | null;
|
||||||
disabled_by?: string | null;
|
disabled_by?: string | null;
|
||||||
new_entity_id?: string;
|
new_entity_id?: string;
|
||||||
}
|
}
|
||||||
|
@ -7,13 +7,13 @@ import { navigate } from "../common/navigate";
|
|||||||
import { HomeAssistant } from "../types";
|
import { HomeAssistant } from "../types";
|
||||||
import { Condition, Trigger } from "./automation";
|
import { Condition, Trigger } from "./automation";
|
||||||
|
|
||||||
export const MODES = ["single", "restart", "queued", "parallel"];
|
export const MODES = ["single", "restart", "queued", "parallel"] as const;
|
||||||
export const MODES_MAX = ["queued", "parallel"];
|
export const MODES_MAX = ["queued", "parallel"];
|
||||||
|
|
||||||
export interface ScriptEntity extends HassEntityBase {
|
export interface ScriptEntity extends HassEntityBase {
|
||||||
attributes: HassEntityAttributeBase & {
|
attributes: HassEntityAttributeBase & {
|
||||||
last_triggered: string;
|
last_triggered: string;
|
||||||
mode: "single" | "restart" | "queued" | "parallel";
|
mode: typeof MODES[number];
|
||||||
current?: number;
|
current?: number;
|
||||||
max?: number;
|
max?: number;
|
||||||
};
|
};
|
||||||
@ -23,7 +23,7 @@ export interface ScriptConfig {
|
|||||||
alias: string;
|
alias: string;
|
||||||
sequence: Action[];
|
sequence: Action[];
|
||||||
icon?: string;
|
icon?: string;
|
||||||
mode?: "single" | "restart" | "queued" | "parallel";
|
mode?: typeof MODES[number];
|
||||||
max?: number;
|
max?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,9 +2,11 @@ export type Selector =
|
|||||||
| EntitySelector
|
| EntitySelector
|
||||||
| DeviceSelector
|
| DeviceSelector
|
||||||
| AreaSelector
|
| AreaSelector
|
||||||
|
| TargetSelector
|
||||||
| NumberSelector
|
| NumberSelector
|
||||||
| BooleanSelector
|
| BooleanSelector
|
||||||
| TimeSelector;
|
| TimeSelector
|
||||||
|
| ActionSelector;
|
||||||
|
|
||||||
export interface EntitySelector {
|
export interface EntitySelector {
|
||||||
entity: {
|
entity: {
|
||||||
@ -19,13 +21,41 @@ export interface DeviceSelector {
|
|||||||
integration?: string;
|
integration?: string;
|
||||||
manufacturer?: string;
|
manufacturer?: string;
|
||||||
model?: string;
|
model?: string;
|
||||||
entity?: EntitySelector["entity"];
|
entity?: {
|
||||||
|
domain?: EntitySelector["entity"]["domain"];
|
||||||
|
device_class?: EntitySelector["entity"]["device_class"];
|
||||||
|
};
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface AreaSelector {
|
export interface AreaSelector {
|
||||||
// eslint-disable-next-line @typescript-eslint/ban-types
|
area: {
|
||||||
area: {};
|
entity?: {
|
||||||
|
integration?: EntitySelector["entity"]["integration"];
|
||||||
|
domain?: EntitySelector["entity"]["domain"];
|
||||||
|
device_class?: EntitySelector["entity"]["device_class"];
|
||||||
|
};
|
||||||
|
device?: {
|
||||||
|
integration?: DeviceSelector["device"]["integration"];
|
||||||
|
manufacturer?: DeviceSelector["device"]["manufacturer"];
|
||||||
|
model?: DeviceSelector["device"]["model"];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface TargetSelector {
|
||||||
|
target: {
|
||||||
|
entity?: {
|
||||||
|
integration?: EntitySelector["entity"]["integration"];
|
||||||
|
domain?: EntitySelector["entity"]["domain"];
|
||||||
|
device_class?: EntitySelector["entity"]["device_class"];
|
||||||
|
};
|
||||||
|
device?: {
|
||||||
|
integration?: DeviceSelector["device"]["integration"];
|
||||||
|
manufacturer?: DeviceSelector["device"]["manufacturer"];
|
||||||
|
model?: DeviceSelector["device"]["model"];
|
||||||
|
};
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface NumberSelector {
|
export interface NumberSelector {
|
||||||
@ -47,3 +77,8 @@ export interface TimeSelector {
|
|||||||
// eslint-disable-next-line @typescript-eslint/ban-types
|
// eslint-disable-next-line @typescript-eslint/ban-types
|
||||||
time: {};
|
time: {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface ActionSelector {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/ban-types
|
||||||
|
action: {};
|
||||||
|
}
|
||||||
|
5
src/data/target.ts
Normal file
5
src/data/target.ts
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
export interface Target {
|
||||||
|
entity_id?: string[];
|
||||||
|
device_id?: string[];
|
||||||
|
area_id?: string[];
|
||||||
|
}
|
@ -9,6 +9,7 @@ export const GROUPS = [SYSTEM_GROUP_ID_USER, SYSTEM_GROUP_ID_ADMIN];
|
|||||||
|
|
||||||
export interface User {
|
export interface User {
|
||||||
id: string;
|
id: string;
|
||||||
|
username: string | null;
|
||||||
name: string;
|
name: string;
|
||||||
is_owner: boolean;
|
is_owner: boolean;
|
||||||
is_active: boolean;
|
is_active: boolean;
|
||||||
@ -19,6 +20,7 @@ export interface User {
|
|||||||
|
|
||||||
export interface UpdateUserParams {
|
export interface UpdateUserParams {
|
||||||
name?: User["name"];
|
name?: User["name"];
|
||||||
|
is_active?: User["is_active"];
|
||||||
group_ids?: User["group_ids"];
|
group_ids?: User["group_ids"];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -27,6 +27,7 @@ export interface ZHADevice {
|
|||||||
device_type: string;
|
device_type: string;
|
||||||
signature: any;
|
signature: any;
|
||||||
neighbors: Neighbor[];
|
neighbors: Neighbor[];
|
||||||
|
pairing_status?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Neighbor {
|
export interface Neighbor {
|
||||||
@ -270,3 +271,23 @@ export const addGroup = (
|
|||||||
group_name: groupName,
|
group_name: groupName,
|
||||||
members: membersToAdd,
|
members: membersToAdd,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
export const INITIALIZED = "INITIALIZED";
|
||||||
|
export const INTERVIEW_COMPLETE = "INTERVIEW_COMPLETE";
|
||||||
|
export const CONFIGURED = "CONFIGURED";
|
||||||
|
export const PAIRED = "PAIRED";
|
||||||
|
export const INCOMPLETE_PAIRING_STATUSES = [
|
||||||
|
PAIRED,
|
||||||
|
CONFIGURED,
|
||||||
|
INTERVIEW_COMPLETE,
|
||||||
|
];
|
||||||
|
|
||||||
|
export const DEVICE_JOINED = "device_joined";
|
||||||
|
export const RAW_DEVICE_INITIALIZED = "raw_device_initialized";
|
||||||
|
export const DEVICE_FULLY_INITIALIZED = "device_fully_initialized";
|
||||||
|
export const DEVICE_MESSAGE_TYPES = [
|
||||||
|
DEVICE_JOINED,
|
||||||
|
RAW_DEVICE_INITIALIZED,
|
||||||
|
DEVICE_FULLY_INITIALIZED,
|
||||||
|
];
|
||||||
|
export const LOG_OUTPUT = "log_output";
|
||||||
|
@ -46,7 +46,7 @@ export class HuiNotificationItemTemplate extends LitElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.actions {
|
.actions {
|
||||||
border-top: 1px solid #e8e8e8;
|
border-top: 1px solid var(--divider-color, #e8e8e8);
|
||||||
padding: 5px 16px;
|
padding: 5px 16px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -42,7 +42,7 @@
|
|||||||
#ha-init-skeleton::before {
|
#ha-init-skeleton::before {
|
||||||
display: block;
|
display: block;
|
||||||
content: "";
|
content: "";
|
||||||
height: 112px;
|
height: 56px;
|
||||||
background-color: #THEMEC;
|
background-color: #THEMEC;
|
||||||
}
|
}
|
||||||
html {
|
html {
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import "@material/mwc-fab";
|
import "../../../components/ha-fab";
|
||||||
import { mdiPlus } from "@mdi/js";
|
import { mdiPlus } from "@mdi/js";
|
||||||
import "@polymer/paper-item/paper-item";
|
import "@polymer/paper-item/paper-item";
|
||||||
import "@polymer/paper-item/paper-item-body";
|
import "@polymer/paper-item/paper-item-body";
|
||||||
@ -124,7 +124,7 @@ export class HaConfigAreasDashboard extends LitElement {
|
|||||||
icon="hass:help-circle"
|
icon="hass:help-circle"
|
||||||
@click=${this._showHelp}
|
@click=${this._showHelp}
|
||||||
></ha-icon-button>
|
></ha-icon-button>
|
||||||
<mwc-fab
|
<ha-fab
|
||||||
slot="fab"
|
slot="fab"
|
||||||
.label=${this.hass.localize(
|
.label=${this.hass.localize(
|
||||||
"ui.panel.config.areas.picker.create_area"
|
"ui.panel.config.areas.picker.create_area"
|
||||||
@ -133,7 +133,7 @@ export class HaConfigAreasDashboard extends LitElement {
|
|||||||
@click=${this._createArea}
|
@click=${this._createArea}
|
||||||
>
|
>
|
||||||
<ha-svg-icon slot="icon" .path=${mdiPlus}></ha-svg-icon>
|
<ha-svg-icon slot="icon" .path=${mdiPlus}></ha-svg-icon>
|
||||||
</mwc-fab>
|
</ha-fab>
|
||||||
</hass-tabs-subpage-data-table>
|
</hass-tabs-subpage-data-table>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
@ -39,7 +39,7 @@ export class HaWaitForTriggerAction extends LitElement
|
|||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<ha-switch
|
<ha-switch
|
||||||
.checked=${continue_on_timeout}
|
.checked=${continue_on_timeout ?? true}
|
||||||
@change=${this._continueChanged}
|
@change=${this._continueChanged}
|
||||||
></ha-switch>
|
></ha-switch>
|
||||||
</ha-formfield>
|
</ha-formfield>
|
||||||
|
@ -63,7 +63,7 @@ export class HaBlueprintAutomationEditor extends LitElement {
|
|||||||
|
|
||||||
protected render() {
|
protected render() {
|
||||||
const blueprint = this._blueprint;
|
const blueprint = this._blueprint;
|
||||||
return html`<ha-config-section .isWide=${this.isWide}>
|
return html`<ha-config-section vertical .isWide=${this.isWide}>
|
||||||
${!this.narrow
|
${!this.narrow
|
||||||
? html` <span slot="header">${this.config.alias}</span> `
|
? html` <span slot="header">${this.config.alias}</span> `
|
||||||
: ""}
|
: ""}
|
||||||
@ -119,7 +119,7 @@ export class HaBlueprintAutomationEditor extends LitElement {
|
|||||||
</ha-card>
|
</ha-card>
|
||||||
</ha-config-section>
|
</ha-config-section>
|
||||||
|
|
||||||
<ha-config-section .isWide=${this.isWide}>
|
<ha-config-section vertical .isWide=${this.isWide}>
|
||||||
<span slot="header"
|
<span slot="header"
|
||||||
>${this.hass.localize(
|
>${this.hass.localize(
|
||||||
"ui.panel.config.automation.editor.blueprint.header"
|
"ui.panel.config.automation.editor.blueprint.header"
|
||||||
@ -185,6 +185,7 @@ export class HaBlueprintAutomationEditor extends LitElement {
|
|||||||
></ha-selector>`
|
></ha-selector>`
|
||||||
: html`<paper-input
|
: html`<paper-input
|
||||||
.key=${key}
|
.key=${key}
|
||||||
|
required
|
||||||
.value=${this.config.use_blueprint.input &&
|
.value=${this.config.use_blueprint.input &&
|
||||||
this.config.use_blueprint.input[key]}
|
this.config.use_blueprint.input[key]}
|
||||||
@value-changed=${this._inputChanged}
|
@value-changed=${this._inputChanged}
|
||||||
@ -275,20 +276,9 @@ export class HaBlueprintAutomationEditor extends LitElement {
|
|||||||
return [
|
return [
|
||||||
haStyle,
|
haStyle,
|
||||||
css`
|
css`
|
||||||
ha-card {
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
.errors {
|
|
||||||
padding: 20px;
|
|
||||||
font-weight: bold;
|
|
||||||
color: var(--error-color);
|
|
||||||
}
|
|
||||||
.padding {
|
.padding {
|
||||||
padding: 16px;
|
padding: 16px;
|
||||||
}
|
}
|
||||||
.content {
|
|
||||||
padding-bottom: 20px;
|
|
||||||
}
|
|
||||||
.blueprint-picker-container {
|
.blueprint-picker-container {
|
||||||
padding: 16px;
|
padding: 16px;
|
||||||
display: flex;
|
display: flex;
|
||||||
@ -312,24 +302,10 @@ export class HaBlueprintAutomationEditor extends LitElement {
|
|||||||
border-top: 1px solid var(--divider-color);
|
border-top: 1px solid var(--divider-color);
|
||||||
}
|
}
|
||||||
:host(:not([narrow])) ha-settings-row paper-input {
|
:host(:not([narrow])) ha-settings-row paper-input {
|
||||||
width: 50%;
|
width: 60%;
|
||||||
}
|
}
|
||||||
:host(:not([narrow])) ha-settings-row ha-selector {
|
:host(:not([narrow])) ha-settings-row ha-selector {
|
||||||
width: 50%;
|
width: 60%;
|
||||||
}
|
|
||||||
mwc-fab {
|
|
||||||
position: relative;
|
|
||||||
bottom: calc(-80px - env(safe-area-inset-bottom));
|
|
||||||
transition: bottom 0.3s;
|
|
||||||
}
|
|
||||||
mwc-fab.dirty {
|
|
||||||
bottom: 0;
|
|
||||||
}
|
|
||||||
.selected_menu_item {
|
|
||||||
color: var(--primary-color);
|
|
||||||
}
|
|
||||||
li[role="separator"] {
|
|
||||||
border-bottom-color: var(--divider-color);
|
|
||||||
}
|
}
|
||||||
`,
|
`,
|
||||||
];
|
];
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import "@material/mwc-fab";
|
import "../../../components/ha-fab";
|
||||||
import {
|
import {
|
||||||
mdiCheck,
|
mdiCheck,
|
||||||
mdiContentDuplicate,
|
mdiContentDuplicate,
|
||||||
@ -206,6 +206,7 @@ export class HaAutomationEditor extends KeyboardShortcutMixin(LitElement) {
|
|||||||
? html`<blueprint-automation-editor
|
? html`<blueprint-automation-editor
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
.narrow=${this.narrow}
|
.narrow=${this.narrow}
|
||||||
|
.isWide=${this.isWide}
|
||||||
.stateObj=${stateObj}
|
.stateObj=${stateObj}
|
||||||
.config=${this._config}
|
.config=${this._config}
|
||||||
@value-changed=${this._valueChanged}
|
@value-changed=${this._valueChanged}
|
||||||
@ -213,6 +214,7 @@ export class HaAutomationEditor extends KeyboardShortcutMixin(LitElement) {
|
|||||||
: html`<manual-automation-editor
|
: html`<manual-automation-editor
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
.narrow=${this.narrow}
|
.narrow=${this.narrow}
|
||||||
|
.isWide=${this.isWide}
|
||||||
.stateObj=${stateObj}
|
.stateObj=${stateObj}
|
||||||
.config=${this._config}
|
.config=${this._config}
|
||||||
@value-changed=${this._valueChanged}
|
@value-changed=${this._valueChanged}
|
||||||
@ -271,7 +273,7 @@ export class HaAutomationEditor extends KeyboardShortcutMixin(LitElement) {
|
|||||||
</div>
|
</div>
|
||||||
`
|
`
|
||||||
: ""}
|
: ""}
|
||||||
<mwc-fab
|
<ha-fab
|
||||||
slot="fab"
|
slot="fab"
|
||||||
class=${classMap({ dirty: this._dirty })}
|
class=${classMap({ dirty: this._dirty })}
|
||||||
.label=${this.hass.localize("ui.panel.config.automation.editor.save")}
|
.label=${this.hass.localize("ui.panel.config.automation.editor.save")}
|
||||||
@ -279,7 +281,7 @@ export class HaAutomationEditor extends KeyboardShortcutMixin(LitElement) {
|
|||||||
@click=${this._saveAutomation}
|
@click=${this._saveAutomation}
|
||||||
>
|
>
|
||||||
<ha-svg-icon slot="icon" .path=${mdiContentSave}></ha-svg-icon>
|
<ha-svg-icon slot="icon" .path=${mdiContentSave}></ha-svg-icon>
|
||||||
</mwc-fab>
|
</ha-fab>
|
||||||
</hass-tabs-subpage>
|
</hass-tabs-subpage>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
@ -524,21 +526,18 @@ export class HaAutomationEditor extends KeyboardShortcutMixin(LitElement) {
|
|||||||
.content {
|
.content {
|
||||||
padding-bottom: 20px;
|
padding-bottom: 20px;
|
||||||
}
|
}
|
||||||
span[slot="introduction"] a {
|
|
||||||
color: var(--primary-color);
|
|
||||||
}
|
|
||||||
p {
|
p {
|
||||||
margin-bottom: 0;
|
margin-bottom: 0;
|
||||||
}
|
}
|
||||||
ha-entity-toggle {
|
ha-entity-toggle {
|
||||||
margin-right: 8px;
|
margin-right: 8px;
|
||||||
}
|
}
|
||||||
mwc-fab {
|
ha-fab {
|
||||||
position: relative;
|
position: relative;
|
||||||
bottom: calc(-80px - env(safe-area-inset-bottom));
|
bottom: calc(-80px - env(safe-area-inset-bottom));
|
||||||
transition: bottom 0.3s;
|
transition: bottom 0.3s;
|
||||||
}
|
}
|
||||||
mwc-fab.dirty {
|
ha-fab.dirty {
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
}
|
}
|
||||||
.selected_menu_item {
|
.selected_menu_item {
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import "@material/mwc-fab";
|
import "../../../components/ha-fab";
|
||||||
import "@material/mwc-icon-button";
|
import "@material/mwc-icon-button";
|
||||||
import { mdiPlus, mdiHelpCircle } from "@mdi/js";
|
import { mdiPlus, mdiHelpCircle } from "@mdi/js";
|
||||||
import "@polymer/paper-tooltip/paper-tooltip";
|
import "@polymer/paper-tooltip/paper-tooltip";
|
||||||
@ -170,7 +170,7 @@ class HaAutomationPicker extends LitElement {
|
|||||||
<mwc-icon-button slot="toolbar-icon" @click=${this._showHelp}>
|
<mwc-icon-button slot="toolbar-icon" @click=${this._showHelp}>
|
||||||
<ha-svg-icon .path=${mdiHelpCircle}></ha-svg-icon>
|
<ha-svg-icon .path=${mdiHelpCircle}></ha-svg-icon>
|
||||||
</mwc-icon-button>
|
</mwc-icon-button>
|
||||||
<mwc-fab
|
<ha-fab
|
||||||
slot="fab"
|
slot="fab"
|
||||||
.label=${this.hass.localize(
|
.label=${this.hass.localize(
|
||||||
"ui.panel.config.automation.picker.add_automation"
|
"ui.panel.config.automation.picker.add_automation"
|
||||||
@ -179,7 +179,7 @@ class HaAutomationPicker extends LitElement {
|
|||||||
@click=${this._createNew}
|
@click=${this._createNew}
|
||||||
>
|
>
|
||||||
<ha-svg-icon slot="icon" .path=${mdiPlus}></ha-svg-icon>
|
<ha-svg-icon slot="icon" .path=${mdiPlus}></ha-svg-icon>
|
||||||
</mwc-fab>
|
</ha-fab>
|
||||||
</hass-tabs-subpage-data-table>
|
</hass-tabs-subpage-data-table>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
@ -309,14 +309,6 @@ export class HaManualAutomationEditor extends LitElement {
|
|||||||
ha-card {
|
ha-card {
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
.errors {
|
|
||||||
padding: 20px;
|
|
||||||
font-weight: bold;
|
|
||||||
color: var(--error-color);
|
|
||||||
}
|
|
||||||
.content {
|
|
||||||
padding-bottom: 20px;
|
|
||||||
}
|
|
||||||
span[slot="introduction"] a {
|
span[slot="introduction"] a {
|
||||||
color: var(--primary-color);
|
color: var(--primary-color);
|
||||||
}
|
}
|
||||||
@ -326,20 +318,6 @@ export class HaManualAutomationEditor extends LitElement {
|
|||||||
ha-entity-toggle {
|
ha-entity-toggle {
|
||||||
margin-right: 8px;
|
margin-right: 8px;
|
||||||
}
|
}
|
||||||
mwc-fab {
|
|
||||||
position: relative;
|
|
||||||
bottom: calc(-80px - env(safe-area-inset-bottom));
|
|
||||||
transition: bottom 0.3s;
|
|
||||||
}
|
|
||||||
mwc-fab.dirty {
|
|
||||||
bottom: 0;
|
|
||||||
}
|
|
||||||
.selected_menu_item {
|
|
||||||
color: var(--primary-color);
|
|
||||||
}
|
|
||||||
li[role="separator"] {
|
|
||||||
border-bottom-color: var(--divider-color);
|
|
||||||
}
|
|
||||||
`,
|
`,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
@ -4,7 +4,6 @@ import "@polymer/paper-input/paper-input";
|
|||||||
import type { PaperInputElement } from "@polymer/paper-input/paper-input";
|
import type { PaperInputElement } from "@polymer/paper-input/paper-input";
|
||||||
import "../../../components/ha-circular-progress";
|
import "../../../components/ha-circular-progress";
|
||||||
import {
|
import {
|
||||||
css,
|
|
||||||
CSSResult,
|
CSSResult,
|
||||||
customElement,
|
customElement,
|
||||||
html,
|
html,
|
||||||
@ -97,11 +96,10 @@ class DialogImportBlueprint extends LitElement {
|
|||||||
)}
|
)}
|
||||||
></paper-input>
|
></paper-input>
|
||||||
`}
|
`}
|
||||||
<ha-expansion-panel>
|
<ha-expansion-panel
|
||||||
<span slot="title"
|
.header=${this.hass.localize(
|
||||||
>${this.hass.localize(
|
|
||||||
"ui.panel.config.blueprint.add.raw_blueprint"
|
"ui.panel.config.blueprint.add.raw_blueprint"
|
||||||
)}</span
|
)}
|
||||||
>
|
>
|
||||||
<pre>${this._result.raw_data}</pre>
|
<pre>${this._result.raw_data}</pre>
|
||||||
</ha-expansion-panel>`
|
</ha-expansion-panel>`
|
||||||
@ -201,15 +199,8 @@ class DialogImportBlueprint extends LitElement {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static get styles(): CSSResult[] {
|
static get styles(): CSSResult {
|
||||||
return [
|
return haStyleDialog;
|
||||||
haStyleDialog,
|
|
||||||
css`
|
|
||||||
ha-expansion-panel {
|
|
||||||
--expansion-panel-summary-padding: 0;
|
|
||||||
}
|
|
||||||
`,
|
|
||||||
];
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import "@material/mwc-fab";
|
import "../../../components/ha-fab";
|
||||||
import "@material/mwc-icon-button";
|
import "@material/mwc-icon-button";
|
||||||
import { mdiPlus, mdiHelpCircle, mdiDelete, mdiRobot } from "@mdi/js";
|
import { mdiPlus, mdiHelpCircle, mdiDelete, mdiRobot } from "@mdi/js";
|
||||||
import "@polymer/paper-tooltip/paper-tooltip";
|
import "@polymer/paper-tooltip/paper-tooltip";
|
||||||
@ -174,7 +174,7 @@ class HaBlueprintOverview extends LitElement {
|
|||||||
<mwc-icon-button slot="toolbar-icon" @click=${this._showHelp}>
|
<mwc-icon-button slot="toolbar-icon" @click=${this._showHelp}>
|
||||||
<ha-svg-icon .path=${mdiHelpCircle}></ha-svg-icon>
|
<ha-svg-icon .path=${mdiHelpCircle}></ha-svg-icon>
|
||||||
</mwc-icon-button>
|
</mwc-icon-button>
|
||||||
<mwc-fab
|
<ha-fab
|
||||||
slot="fab"
|
slot="fab"
|
||||||
.label=${this.hass.localize(
|
.label=${this.hass.localize(
|
||||||
"ui.panel.config.blueprint.overview.add_blueprint"
|
"ui.panel.config.blueprint.overview.add_blueprint"
|
||||||
@ -183,7 +183,7 @@ class HaBlueprintOverview extends LitElement {
|
|||||||
@click=${this._addBlueprint}
|
@click=${this._addBlueprint}
|
||||||
>
|
>
|
||||||
<ha-svg-icon slot="icon" .path=${mdiPlus}></ha-svg-icon>
|
<ha-svg-icon slot="icon" .path=${mdiPlus}></ha-svg-icon>
|
||||||
</mwc-fab>
|
</ha-fab>
|
||||||
</hass-tabs-subpage-data-table>
|
</hass-tabs-subpage-data-table>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
@ -8,7 +8,6 @@ import {
|
|||||||
html,
|
html,
|
||||||
LitElement,
|
LitElement,
|
||||||
property,
|
property,
|
||||||
internalProperty,
|
|
||||||
PropertyValues,
|
PropertyValues,
|
||||||
TemplateResult,
|
TemplateResult,
|
||||||
} from "lit-element";
|
} from "lit-element";
|
||||||
@ -31,7 +30,7 @@ export class HaDeviceEntitiesCard extends LitElement {
|
|||||||
|
|
||||||
@property() public entities!: EntityRegistryStateEntry[];
|
@property() public entities!: EntityRegistryStateEntry[];
|
||||||
|
|
||||||
@internalProperty() private _showDisabled = false;
|
@property() public showDisabled = false;
|
||||||
|
|
||||||
private _entityRows: Array<LovelaceRow | HuiErrorCard> = [];
|
private _entityRows: Array<LovelaceRow | HuiErrorCard> = [];
|
||||||
|
|
||||||
@ -68,7 +67,7 @@ export class HaDeviceEntitiesCard extends LitElement {
|
|||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
${disabledEntities.length
|
${disabledEntities.length
|
||||||
? !this._showDisabled
|
? !this.showDisabled
|
||||||
? html`
|
? html`
|
||||||
<button
|
<button
|
||||||
class="show-more"
|
class="show-more"
|
||||||
@ -119,7 +118,7 @@ export class HaDeviceEntitiesCard extends LitElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private _toggleShowDisabled() {
|
private _toggleShowDisabled() {
|
||||||
this._showDisabled = !this._showDisabled;
|
this.showDisabled = !this.showDisabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
private _renderEntity(entry: EntityRegistryStateEntry): TemplateResult {
|
private _renderEntity(entry: EntityRegistryStateEntry): TemplateResult {
|
||||||
@ -227,3 +226,9 @@ export class HaDeviceEntitiesCard extends LitElement {
|
|||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface HTMLElementTagNameMap {
|
||||||
|
"ha-device-entities-card": HaDeviceEntitiesCard;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -12,7 +12,7 @@ import {
|
|||||||
computeDeviceName,
|
computeDeviceName,
|
||||||
DeviceRegistryEntry,
|
DeviceRegistryEntry,
|
||||||
} from "../../../../data/device_registry";
|
} from "../../../../data/device_registry";
|
||||||
import { loadDeviceRegistryDetailDialog } from "../../../../dialogs/device-registry-detail/show-dialog-device-registry-detail";
|
import { loadDeviceRegistryDetailDialog } from "../device-registry-detail/show-dialog-device-registry-detail";
|
||||||
import { HomeAssistant } from "../../../../types";
|
import { HomeAssistant } from "../../../../types";
|
||||||
|
|
||||||
@customElement("ha-device-info-card")
|
@customElement("ha-device-info-card")
|
||||||
|
@ -3,8 +3,8 @@ import "@polymer/paper-dropdown-menu/paper-dropdown-menu";
|
|||||||
import "@polymer/paper-input/paper-input";
|
import "@polymer/paper-input/paper-input";
|
||||||
import "@polymer/paper-item/paper-item";
|
import "@polymer/paper-item/paper-item";
|
||||||
import "@polymer/paper-listbox/paper-listbox";
|
import "@polymer/paper-listbox/paper-listbox";
|
||||||
import "../../components/ha-dialog";
|
import "../../../../components/ha-dialog";
|
||||||
import "../../components/ha-area-picker";
|
import "../../../../components/ha-area-picker";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
CSSResult,
|
CSSResult,
|
||||||
@ -18,11 +18,12 @@ import {
|
|||||||
} from "lit-element";
|
} from "lit-element";
|
||||||
|
|
||||||
import { DeviceRegistryDetailDialogParams } from "./show-dialog-device-registry-detail";
|
import { DeviceRegistryDetailDialogParams } from "./show-dialog-device-registry-detail";
|
||||||
import { HomeAssistant } from "../../types";
|
import { HomeAssistant } from "../../../../types";
|
||||||
import { PolymerChangedEvent } from "../../polymer-types";
|
import type { HaSwitch } from "../../../../components/ha-switch";
|
||||||
import { computeDeviceName } from "../../data/device_registry";
|
import { PolymerChangedEvent } from "../../../../polymer-types";
|
||||||
import { fireEvent } from "../../common/dom/fire_event";
|
import { computeDeviceName } from "../../../../data/device_registry";
|
||||||
import { haStyleDialog } from "../../resources/styles";
|
import { fireEvent } from "../../../../common/dom/fire_event";
|
||||||
|
import { haStyle, haStyleDialog } from "../../../../resources/styles";
|
||||||
|
|
||||||
@customElement("dialog-device-registry-detail")
|
@customElement("dialog-device-registry-detail")
|
||||||
class DialogDeviceRegistryDetail extends LitElement {
|
class DialogDeviceRegistryDetail extends LitElement {
|
||||||
@ -36,6 +37,8 @@ class DialogDeviceRegistryDetail extends LitElement {
|
|||||||
|
|
||||||
@internalProperty() private _areaId?: string;
|
@internalProperty() private _areaId?: string;
|
||||||
|
|
||||||
|
@internalProperty() private _disabledBy!: string | null;
|
||||||
|
|
||||||
@internalProperty() private _submitting?: boolean;
|
@internalProperty() private _submitting?: boolean;
|
||||||
|
|
||||||
public async showDialog(
|
public async showDialog(
|
||||||
@ -45,6 +48,7 @@ class DialogDeviceRegistryDetail extends LitElement {
|
|||||||
this._error = undefined;
|
this._error = undefined;
|
||||||
this._nameByUser = this._params.device.name_by_user || "";
|
this._nameByUser = this._params.device.name_by_user || "";
|
||||||
this._areaId = this._params.device.area_id;
|
this._areaId = this._params.device.area_id;
|
||||||
|
this._disabledBy = this._params.device.disabled_by;
|
||||||
await this.updateComplete;
|
await this.updateComplete;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -80,6 +84,32 @@ class DialogDeviceRegistryDetail extends LitElement {
|
|||||||
.value=${this._areaId}
|
.value=${this._areaId}
|
||||||
@value-changed=${this._areaPicked}
|
@value-changed=${this._areaPicked}
|
||||||
></ha-area-picker>
|
></ha-area-picker>
|
||||||
|
<div class="row">
|
||||||
|
<ha-switch
|
||||||
|
.checked=${!this._disabledBy}
|
||||||
|
@change=${this._disabledByChanged}
|
||||||
|
>
|
||||||
|
</ha-switch>
|
||||||
|
<div>
|
||||||
|
<div>
|
||||||
|
${this.hass.localize("ui.panel.config.devices.enabled_label")}
|
||||||
|
</div>
|
||||||
|
<div class="secondary">
|
||||||
|
${this._disabledBy && this._disabledBy !== "user"
|
||||||
|
? this.hass.localize(
|
||||||
|
"ui.panel.config.devices.enabled_cause",
|
||||||
|
"cause",
|
||||||
|
this.hass.localize(
|
||||||
|
`config_entry.disabled_by.${this._disabledBy}`
|
||||||
|
)
|
||||||
|
)
|
||||||
|
: ""}
|
||||||
|
${this.hass.localize(
|
||||||
|
"ui.panel.config.devices.enabled_description"
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<mwc-button
|
<mwc-button
|
||||||
@ -109,12 +139,17 @@ class DialogDeviceRegistryDetail extends LitElement {
|
|||||||
this._areaId = event.detail.value;
|
this._areaId = event.detail.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private _disabledByChanged(ev: Event): void {
|
||||||
|
this._disabledBy = (ev.target as HaSwitch).checked ? null : "user";
|
||||||
|
}
|
||||||
|
|
||||||
private async _updateEntry(): Promise<void> {
|
private async _updateEntry(): Promise<void> {
|
||||||
this._submitting = true;
|
this._submitting = true;
|
||||||
try {
|
try {
|
||||||
await this._params!.updateEntry({
|
await this._params!.updateEntry({
|
||||||
name_by_user: this._nameByUser.trim() || null,
|
name_by_user: this._nameByUser.trim() || null,
|
||||||
area_id: this._areaId || null,
|
area_id: this._areaId || null,
|
||||||
|
disabled_by: this._disabledBy || null,
|
||||||
});
|
});
|
||||||
this._params = undefined;
|
this._params = undefined;
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
@ -128,6 +163,7 @@ class DialogDeviceRegistryDetail extends LitElement {
|
|||||||
|
|
||||||
static get styles(): CSSResult[] {
|
static get styles(): CSSResult[] {
|
||||||
return [
|
return [
|
||||||
|
haStyle,
|
||||||
haStyleDialog,
|
haStyleDialog,
|
||||||
css`
|
css`
|
||||||
.form {
|
.form {
|
||||||
@ -139,6 +175,15 @@ class DialogDeviceRegistryDetail extends LitElement {
|
|||||||
.error {
|
.error {
|
||||||
color: var(--error-color);
|
color: var(--error-color);
|
||||||
}
|
}
|
||||||
|
ha-switch {
|
||||||
|
margin-right: 16px;
|
||||||
|
}
|
||||||
|
.row {
|
||||||
|
margin-top: 8px;
|
||||||
|
color: var(--primary-text-color);
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
`,
|
`,
|
||||||
];
|
];
|
||||||
}
|
}
|
@ -1,8 +1,8 @@
|
|||||||
import { fireEvent } from "../../common/dom/fire_event";
|
import { fireEvent } from "../../../../common/dom/fire_event";
|
||||||
import {
|
import {
|
||||||
DeviceRegistryEntry,
|
DeviceRegistryEntry,
|
||||||
DeviceRegistryEntryMutableParams,
|
DeviceRegistryEntryMutableParams,
|
||||||
} from "../../data/device_registry";
|
} from "../../../../data/device_registry";
|
||||||
|
|
||||||
export interface DeviceRegistryDetailDialogParams {
|
export interface DeviceRegistryDetailDialogParams {
|
||||||
device: DeviceRegistryEntry;
|
device: DeviceRegistryEntry;
|
@ -35,7 +35,7 @@ import { findRelated, RelatedResult } from "../../../data/search";
|
|||||||
import {
|
import {
|
||||||
loadDeviceRegistryDetailDialog,
|
loadDeviceRegistryDetailDialog,
|
||||||
showDeviceRegistryDetailDialog,
|
showDeviceRegistryDetailDialog,
|
||||||
} from "../../../dialogs/device-registry-detail/show-dialog-device-registry-detail";
|
} from "./device-registry-detail/show-dialog-device-registry-detail";
|
||||||
import { showConfirmationDialog } from "../../../dialogs/generic/show-dialog-box";
|
import { showConfirmationDialog } from "../../../dialogs/generic/show-dialog-box";
|
||||||
import "../../../layouts/hass-error-screen";
|
import "../../../layouts/hass-error-screen";
|
||||||
import "../../../layouts/hass-tabs-subpage";
|
import "../../../layouts/hass-tabs-subpage";
|
||||||
@ -46,6 +46,7 @@ import "./device-detail/ha-device-entities-card";
|
|||||||
import "./device-detail/ha-device-info-card";
|
import "./device-detail/ha-device-info-card";
|
||||||
import { showDeviceAutomationDialog } from "./device-detail/show-dialog-device-automation";
|
import { showDeviceAutomationDialog } from "./device-detail/show-dialog-device-automation";
|
||||||
import { brandsUrl } from "../../../util/brands-url";
|
import { brandsUrl } from "../../../util/brands-url";
|
||||||
|
import { haStyle } from "../../../resources/styles";
|
||||||
|
|
||||||
export interface EntityRegistryStateEntry extends EntityRegistryEntry {
|
export interface EntityRegistryStateEntry extends EntityRegistryEntry {
|
||||||
stateName?: string | null;
|
stateName?: string | null;
|
||||||
@ -246,6 +247,28 @@ export class HaConfigDevicePage extends LitElement {
|
|||||||
.devices=${this.devices}
|
.devices=${this.devices}
|
||||||
.device=${device}
|
.device=${device}
|
||||||
>
|
>
|
||||||
|
${
|
||||||
|
device.disabled_by
|
||||||
|
? html`
|
||||||
|
<div>
|
||||||
|
<p class="warning">
|
||||||
|
${this.hass.localize(
|
||||||
|
"ui.panel.config.devices.enabled_cause",
|
||||||
|
"cause",
|
||||||
|
this.hass.localize(
|
||||||
|
`ui.panel.config.devices.disabled_by.${device.disabled_by}`
|
||||||
|
)
|
||||||
|
)}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div class="card-actions" slot="actions">
|
||||||
|
<mwc-button unelevated @click=${this._enableDevice}>
|
||||||
|
${this.hass.localize("ui.common.enable")}
|
||||||
|
</mwc-button>
|
||||||
|
</div>
|
||||||
|
`
|
||||||
|
: html``
|
||||||
|
}
|
||||||
${this._renderIntegrationInfo(device, integrations)}
|
${this._renderIntegrationInfo(device, integrations)}
|
||||||
</ha-device-info-card>
|
</ha-device-info-card>
|
||||||
|
|
||||||
@ -255,6 +278,7 @@ export class HaConfigDevicePage extends LitElement {
|
|||||||
<ha-device-entities-card
|
<ha-device-entities-card
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
.entities=${entities}
|
.entities=${entities}
|
||||||
|
.showDisabled=${device.disabled_by !== null}
|
||||||
>
|
>
|
||||||
</ha-device-entities-card>
|
</ha-device-entities-card>
|
||||||
`
|
`
|
||||||
@ -272,7 +296,12 @@ export class HaConfigDevicePage extends LitElement {
|
|||||||
)}
|
)}
|
||||||
<ha-icon-button
|
<ha-icon-button
|
||||||
@click=${this._showAutomationDialog}
|
@click=${this._showAutomationDialog}
|
||||||
title=${this.hass.localize(
|
.disabled=${device.disabled_by}
|
||||||
|
title=${device.disabled_by
|
||||||
|
? this.hass.localize(
|
||||||
|
"ui.panel.config.devices.automation.create_disabled"
|
||||||
|
)
|
||||||
|
: this.hass.localize(
|
||||||
"ui.panel.config.devices.automation.create"
|
"ui.panel.config.devices.automation.create"
|
||||||
)}
|
)}
|
||||||
icon="hass:plus-circle"
|
icon="hass:plus-circle"
|
||||||
@ -342,9 +371,16 @@ export class HaConfigDevicePage extends LitElement {
|
|||||||
|
|
||||||
<ha-icon-button
|
<ha-icon-button
|
||||||
@click=${this._createScene}
|
@click=${this._createScene}
|
||||||
title=${this.hass.localize(
|
.disabled=${device.disabled_by}
|
||||||
|
title=${
|
||||||
|
device.disabled_by
|
||||||
|
? this.hass.localize(
|
||||||
|
"ui.panel.config.devices.scene.create_disabled"
|
||||||
|
)
|
||||||
|
: this.hass.localize(
|
||||||
"ui.panel.config.devices.scene.create"
|
"ui.panel.config.devices.scene.create"
|
||||||
)}
|
)
|
||||||
|
}
|
||||||
icon="hass:plus-circle"
|
icon="hass:plus-circle"
|
||||||
></ha-icon-button>
|
></ha-icon-button>
|
||||||
</h1>
|
</h1>
|
||||||
@ -415,7 +451,12 @@ export class HaConfigDevicePage extends LitElement {
|
|||||||
)}
|
)}
|
||||||
<ha-icon-button
|
<ha-icon-button
|
||||||
@click=${this._showScriptDialog}
|
@click=${this._showScriptDialog}
|
||||||
title=${this.hass.localize(
|
.disabled=${device.disabled_by}
|
||||||
|
title=${device.disabled_by
|
||||||
|
? this.hass.localize(
|
||||||
|
"ui.panel.config.devices.script.create_disabled"
|
||||||
|
)
|
||||||
|
: this.hass.localize(
|
||||||
"ui.panel.config.devices.script.create"
|
"ui.panel.config.devices.script.create"
|
||||||
)}
|
)}
|
||||||
icon="hass:plus-circle"
|
icon="hass:plus-circle"
|
||||||
@ -632,8 +673,16 @@ export class HaConfigDevicePage extends LitElement {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
static get styles(): CSSResult {
|
private async _enableDevice(): Promise<void> {
|
||||||
return css`
|
await updateDeviceRegistryEntry(this.hass, this.deviceId, {
|
||||||
|
disabled_by: null,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
static get styles(): CSSResult[] {
|
||||||
|
return [
|
||||||
|
haStyle,
|
||||||
|
css`
|
||||||
.container {
|
.container {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
@ -754,6 +803,7 @@ export class HaConfigDevicePage extends LitElement {
|
|||||||
ha-card a {
|
ha-card a {
|
||||||
color: var(--primary-text-color);
|
color: var(--primary-text-color);
|
||||||
}
|
}
|
||||||
`;
|
`,
|
||||||
|
];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,9 @@
|
|||||||
|
import { mdiPlus, mdiFilterVariant, mdiCancel } from "@mdi/js";
|
||||||
|
import "@material/mwc-list/mwc-list-item";
|
||||||
|
import "@polymer/paper-tooltip/paper-tooltip";
|
||||||
import {
|
import {
|
||||||
|
css,
|
||||||
|
CSSResult,
|
||||||
customElement,
|
customElement,
|
||||||
html,
|
html,
|
||||||
internalProperty,
|
internalProperty,
|
||||||
@ -6,16 +11,20 @@ import {
|
|||||||
property,
|
property,
|
||||||
TemplateResult,
|
TemplateResult,
|
||||||
} from "lit-element";
|
} from "lit-element";
|
||||||
|
import { classMap } from "lit-html/directives/class-map";
|
||||||
import memoizeOne from "memoize-one";
|
import memoizeOne from "memoize-one";
|
||||||
|
import type { RequestSelectedDetail } from "@material/mwc-list/mwc-list-item";
|
||||||
import { HASSDomEvent } from "../../../common/dom/fire_event";
|
import { HASSDomEvent } from "../../../common/dom/fire_event";
|
||||||
import { navigate } from "../../../common/navigate";
|
import { navigate } from "../../../common/navigate";
|
||||||
import { LocalizeFunc } from "../../../common/translations/localize";
|
import { LocalizeFunc } from "../../../common/translations/localize";
|
||||||
|
import { computeRTL } from "../../../common/util/compute_rtl";
|
||||||
import {
|
import {
|
||||||
DataTableColumnContainer,
|
DataTableColumnContainer,
|
||||||
DataTableRowData,
|
DataTableRowData,
|
||||||
RowClickedEvent,
|
RowClickedEvent,
|
||||||
} from "../../../components/data-table/ha-data-table";
|
} from "../../../components/data-table/ha-data-table";
|
||||||
import "../../../components/entity/ha-battery-icon";
|
import "../../../components/entity/ha-battery-icon";
|
||||||
|
import "../../../components/ha-button-menu";
|
||||||
import { AreaRegistryEntry } from "../../../data/area_registry";
|
import { AreaRegistryEntry } from "../../../data/area_registry";
|
||||||
import { ConfigEntry } from "../../../data/config_entries";
|
import { ConfigEntry } from "../../../data/config_entries";
|
||||||
import {
|
import {
|
||||||
@ -32,6 +41,7 @@ import { domainToName } from "../../../data/integration";
|
|||||||
import "../../../layouts/hass-tabs-subpage-data-table";
|
import "../../../layouts/hass-tabs-subpage-data-table";
|
||||||
import { HomeAssistant, Route } from "../../../types";
|
import { HomeAssistant, Route } from "../../../types";
|
||||||
import { configSections } from "../ha-panel-config";
|
import { configSections } from "../ha-panel-config";
|
||||||
|
import { haStyle } from "../../../resources/styles";
|
||||||
|
|
||||||
interface DeviceRowData extends DeviceRegistryEntry {
|
interface DeviceRowData extends DeviceRegistryEntry {
|
||||||
device?: DeviceRowData;
|
device?: DeviceRowData;
|
||||||
@ -62,6 +72,12 @@ export class HaConfigDeviceDashboard extends LitElement {
|
|||||||
window.location.search
|
window.location.search
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@internalProperty() private _showDisabled = false;
|
||||||
|
|
||||||
|
@internalProperty() private _filter = "";
|
||||||
|
|
||||||
|
@internalProperty() private _numHiddenDevices = 0;
|
||||||
|
|
||||||
private _activeFilters = memoizeOne(
|
private _activeFilters = memoizeOne(
|
||||||
(
|
(
|
||||||
entries: ConfigEntry[],
|
entries: ConfigEntry[],
|
||||||
@ -72,6 +88,10 @@ export class HaConfigDeviceDashboard extends LitElement {
|
|||||||
filters.forEach((value, key) => {
|
filters.forEach((value, key) => {
|
||||||
switch (key) {
|
switch (key) {
|
||||||
case "config_entry": {
|
case "config_entry": {
|
||||||
|
// If we are requested to show the devices for a given config entry,
|
||||||
|
// also show the disabled ones by default.
|
||||||
|
this._showDisabled = true;
|
||||||
|
|
||||||
const configEntry = entries.find(
|
const configEntry = entries.find(
|
||||||
(entry) => entry.entry_id === value
|
(entry) => entry.entry_id === value
|
||||||
);
|
);
|
||||||
@ -96,13 +116,14 @@ export class HaConfigDeviceDashboard extends LitElement {
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
private _devices = memoizeOne(
|
private _devicesAndFilterDomains = memoizeOne(
|
||||||
(
|
(
|
||||||
devices: DeviceRegistryEntry[],
|
devices: DeviceRegistryEntry[],
|
||||||
entries: ConfigEntry[],
|
entries: ConfigEntry[],
|
||||||
entities: EntityRegistryEntry[],
|
entities: EntityRegistryEntry[],
|
||||||
areas: AreaRegistryEntry[],
|
areas: AreaRegistryEntry[],
|
||||||
filters: URLSearchParams,
|
filters: URLSearchParams,
|
||||||
|
showDisabled: boolean,
|
||||||
localize: LocalizeFunc
|
localize: LocalizeFunc
|
||||||
) => {
|
) => {
|
||||||
// Some older installations might have devices pointing at invalid entryIDs
|
// Some older installations might have devices pointing at invalid entryIDs
|
||||||
@ -115,6 +136,9 @@ export class HaConfigDeviceDashboard extends LitElement {
|
|||||||
deviceLookup[device.id] = device;
|
deviceLookup[device.id] = device;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If nothing gets filtered, this is our correct count of devices
|
||||||
|
let startLength = outputDevices.length;
|
||||||
|
|
||||||
const deviceEntityLookup: DeviceEntityLookup = {};
|
const deviceEntityLookup: DeviceEntityLookup = {};
|
||||||
for (const entity of entities) {
|
for (const entity of entities) {
|
||||||
if (!entity.device_id) {
|
if (!entity.device_id) {
|
||||||
@ -136,16 +160,25 @@ export class HaConfigDeviceDashboard extends LitElement {
|
|||||||
areaLookup[area.area_id] = area;
|
areaLookup[area.area_id] = area;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const filterDomains: string[] = [];
|
||||||
|
|
||||||
filters.forEach((value, key) => {
|
filters.forEach((value, key) => {
|
||||||
switch (key) {
|
if (key === "config_entry") {
|
||||||
case "config_entry":
|
|
||||||
outputDevices = outputDevices.filter((device) =>
|
outputDevices = outputDevices.filter((device) =>
|
||||||
device.config_entries.includes(value)
|
device.config_entries.includes(value)
|
||||||
);
|
);
|
||||||
break;
|
startLength = outputDevices.length;
|
||||||
|
const configEntry = entries.find((entry) => entry.entry_id === value);
|
||||||
|
if (configEntry) {
|
||||||
|
filterDomains.push(configEntry.domain);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (!showDisabled) {
|
||||||
|
outputDevices = outputDevices.filter((device) => !device.disabled_by);
|
||||||
|
}
|
||||||
|
|
||||||
outputDevices = outputDevices.map((device) => {
|
outputDevices = outputDevices.map((device) => {
|
||||||
return {
|
return {
|
||||||
...device,
|
...device,
|
||||||
@ -176,16 +209,19 @@ export class HaConfigDeviceDashboard extends LitElement {
|
|||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
return outputDevices;
|
this._numHiddenDevices = startLength - outputDevices.length;
|
||||||
|
return { devicesOutput: outputDevices, filteredDomains: filterDomains };
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
private _columns = memoizeOne(
|
private _columns = memoizeOne(
|
||||||
(narrow: boolean): DataTableColumnContainer => {
|
(narrow: boolean, showDisabled: boolean): DataTableColumnContainer => {
|
||||||
const columns: DataTableColumnContainer = narrow
|
const columns: DataTableColumnContainer = narrow
|
||||||
? {
|
? {
|
||||||
name: {
|
name: {
|
||||||
title: "Device",
|
title: this.hass.localize(
|
||||||
|
"ui.panel.config.devices.data_table.device"
|
||||||
|
),
|
||||||
sortable: true,
|
sortable: true,
|
||||||
filterable: true,
|
filterable: true,
|
||||||
direction: "asc",
|
direction: "asc",
|
||||||
@ -271,6 +307,24 @@ export class HaConfigDeviceDashboard extends LitElement {
|
|||||||
: html` - `;
|
: html` - `;
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
if (showDisabled) {
|
||||||
|
columns.disabled_by = {
|
||||||
|
title: "",
|
||||||
|
type: "icon",
|
||||||
|
template: (disabled_by) =>
|
||||||
|
disabled_by
|
||||||
|
? html`<div
|
||||||
|
tabindex="0"
|
||||||
|
style="display:inline-block; position: relative;"
|
||||||
|
>
|
||||||
|
<ha-svg-icon .path=${mdiCancel}></ha-svg-icon>
|
||||||
|
<paper-tooltip animation-delay="0" position="left">
|
||||||
|
${this.hass.localize("ui.panel.config.devices.disabled")}
|
||||||
|
</paper-tooltip>
|
||||||
|
</div>`
|
||||||
|
: "",
|
||||||
|
};
|
||||||
|
}
|
||||||
return columns;
|
return columns;
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
@ -286,6 +340,126 @@ export class HaConfigDeviceDashboard extends LitElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected render(): TemplateResult {
|
protected render(): TemplateResult {
|
||||||
|
const { devicesOutput, filteredDomains } = this._devicesAndFilterDomains(
|
||||||
|
this.devices,
|
||||||
|
this.entries,
|
||||||
|
this.entities,
|
||||||
|
this.areas,
|
||||||
|
this._searchParms,
|
||||||
|
this._showDisabled,
|
||||||
|
this.hass.localize
|
||||||
|
);
|
||||||
|
const includeZHAFab = filteredDomains.includes("zha");
|
||||||
|
const activeFilters = this._activeFilters(
|
||||||
|
this.entries,
|
||||||
|
this._searchParms,
|
||||||
|
this.hass.localize
|
||||||
|
);
|
||||||
|
|
||||||
|
const headerToolbar = html`
|
||||||
|
<search-input
|
||||||
|
no-label-float
|
||||||
|
no-underline
|
||||||
|
@value-changed=${this._handleSearchChange}
|
||||||
|
.filter=${this._filter}
|
||||||
|
.label=${this.hass.localize("ui.panel.config.devices.picker.search")}
|
||||||
|
></search-input
|
||||||
|
>${activeFilters
|
||||||
|
? html`<div class="active-filters">
|
||||||
|
${this.narrow
|
||||||
|
? html` <div>
|
||||||
|
<ha-icon icon="hass:filter-variant"></ha-icon>
|
||||||
|
<paper-tooltip animation-delay="0" position="left">
|
||||||
|
${this.hass.localize(
|
||||||
|
"ui.panel.config.filtering.filtering_by"
|
||||||
|
)}
|
||||||
|
${activeFilters.join(", ")}
|
||||||
|
${this._numHiddenDevices
|
||||||
|
? "(" +
|
||||||
|
this.hass.localize(
|
||||||
|
"ui.panel.config.devices.picker.filter.hidden_devices",
|
||||||
|
"number",
|
||||||
|
this._numHiddenDevices
|
||||||
|
) +
|
||||||
|
")"
|
||||||
|
: ""}
|
||||||
|
</paper-tooltip>
|
||||||
|
</div>`
|
||||||
|
: `${this.hass.localize(
|
||||||
|
"ui.panel.config.filtering.filtering_by"
|
||||||
|
)} ${activeFilters.join(", ")}
|
||||||
|
${
|
||||||
|
this._numHiddenDevices
|
||||||
|
? "(" +
|
||||||
|
this.hass.localize(
|
||||||
|
"ui.panel.config.devices.picker.filter.hidden_devices",
|
||||||
|
"number",
|
||||||
|
this._numHiddenDevices
|
||||||
|
) +
|
||||||
|
")"
|
||||||
|
: ""
|
||||||
|
}
|
||||||
|
`}
|
||||||
|
<mwc-button @click=${this._clearFilter}
|
||||||
|
>${this.hass.localize(
|
||||||
|
"ui.panel.config.filtering.clear"
|
||||||
|
)}</mwc-button
|
||||||
|
>
|
||||||
|
</div>`
|
||||||
|
: ""}
|
||||||
|
${this._numHiddenDevices && !activeFilters
|
||||||
|
? html`<div class="active-filters">
|
||||||
|
${this.narrow
|
||||||
|
? html` <div>
|
||||||
|
<ha-icon icon="hass:filter-variant"></ha-icon>
|
||||||
|
<paper-tooltip animation-delay="0" position="left">
|
||||||
|
${this.hass.localize(
|
||||||
|
"ui.panel.config.devices.picker.filter.hidden_devices",
|
||||||
|
"number",
|
||||||
|
this._numHiddenDevices
|
||||||
|
)}
|
||||||
|
</paper-tooltip>
|
||||||
|
</div>`
|
||||||
|
: `${this.hass.localize(
|
||||||
|
"ui.panel.config.devices.picker.filter.hidden_devices",
|
||||||
|
"number",
|
||||||
|
this._numHiddenDevices
|
||||||
|
)}`}
|
||||||
|
<mwc-button @click=${this._showAll}
|
||||||
|
>${this.hass.localize(
|
||||||
|
"ui.panel.config.devices.picker.filter.show_all"
|
||||||
|
)}</mwc-button
|
||||||
|
>
|
||||||
|
</div>`
|
||||||
|
: ""}
|
||||||
|
<ha-button-menu corner="BOTTOM_START" multi>
|
||||||
|
<mwc-icon-button
|
||||||
|
slot="trigger"
|
||||||
|
.label=${this.hass!.localize(
|
||||||
|
"ui.panel.config.devices.picker.filter.filter"
|
||||||
|
)}
|
||||||
|
.title=${this.hass!.localize(
|
||||||
|
"ui.panel.config.devices.picker.filter.filter"
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<ha-svg-icon .path=${mdiFilterVariant}></ha-svg-icon>
|
||||||
|
</mwc-icon-button>
|
||||||
|
<mwc-list-item
|
||||||
|
@request-selected="${this._showDisabledChanged}"
|
||||||
|
graphic="control"
|
||||||
|
.selected=${this._showDisabled}
|
||||||
|
>
|
||||||
|
<ha-checkbox
|
||||||
|
slot="graphic"
|
||||||
|
.checked=${this._showDisabled}
|
||||||
|
></ha-checkbox>
|
||||||
|
${this.hass!.localize(
|
||||||
|
"ui.panel.config.devices.picker.filter.show_disabled"
|
||||||
|
)}
|
||||||
|
</mwc-list-item>
|
||||||
|
</ha-button-menu>
|
||||||
|
`;
|
||||||
|
|
||||||
return html`
|
return html`
|
||||||
<hass-tabs-subpage-data-table
|
<hass-tabs-subpage-data-table
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
@ -295,23 +469,33 @@ export class HaConfigDeviceDashboard extends LitElement {
|
|||||||
: "/config"}
|
: "/config"}
|
||||||
.tabs=${configSections.integrations}
|
.tabs=${configSections.integrations}
|
||||||
.route=${this.route}
|
.route=${this.route}
|
||||||
.columns=${this._columns(this.narrow)}
|
.columns=${this._columns(this.narrow, this._showDisabled)}
|
||||||
.data=${this._devices(
|
.data=${devicesOutput}
|
||||||
this.devices,
|
.filter=${this._filter}
|
||||||
this.entries,
|
|
||||||
this.entities,
|
|
||||||
this.areas,
|
|
||||||
this._searchParms,
|
|
||||||
this.hass.localize
|
|
||||||
)}
|
|
||||||
.activeFilters=${this._activeFilters(
|
|
||||||
this.entries,
|
|
||||||
this._searchParms,
|
|
||||||
this.hass.localize
|
|
||||||
)}
|
|
||||||
@row-click=${this._handleRowClicked}
|
@row-click=${this._handleRowClicked}
|
||||||
clickable
|
clickable
|
||||||
|
.hasFab=${includeZHAFab}
|
||||||
>
|
>
|
||||||
|
${includeZHAFab
|
||||||
|
? html`<a href="/config/zha/add" slot="fab">
|
||||||
|
<ha-fab
|
||||||
|
.label=${this.hass.localize("ui.panel.config.zha.add_device")}
|
||||||
|
extended
|
||||||
|
?rtl=${computeRTL(this.hass)}
|
||||||
|
>
|
||||||
|
<ha-svg-icon slot="icon" .path=${mdiPlus}></ha-svg-icon>
|
||||||
|
</ha-fab>
|
||||||
|
</a>`
|
||||||
|
: html``}
|
||||||
|
<div
|
||||||
|
class=${classMap({
|
||||||
|
"search-toolbar": this.narrow,
|
||||||
|
"table-header": !this.narrow,
|
||||||
|
})}
|
||||||
|
slot="header"
|
||||||
|
>
|
||||||
|
${headerToolbar}
|
||||||
|
</div>
|
||||||
</hass-tabs-subpage-data-table>
|
</hass-tabs-subpage-data-table>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
@ -342,6 +526,136 @@ export class HaConfigDeviceDashboard extends LitElement {
|
|||||||
const deviceId = ev.detail.id;
|
const deviceId = ev.detail.id;
|
||||||
navigate(this, `/config/devices/device/${deviceId}`);
|
navigate(this, `/config/devices/device/${deviceId}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private _showDisabledChanged(ev: CustomEvent<RequestSelectedDetail>) {
|
||||||
|
if (ev.detail.source !== "property") {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this._showDisabled = ev.detail.selected;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _handleSearchChange(ev: CustomEvent) {
|
||||||
|
this._filter = ev.detail.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _clearFilter() {
|
||||||
|
navigate(this, window.location.pathname, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private _showAll() {
|
||||||
|
this._showDisabled = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static get styles(): CSSResult[] {
|
||||||
|
return [
|
||||||
|
haStyle,
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
h2 {
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
p {
|
||||||
|
font-family: var(--paper-font-subhead_-_font-family);
|
||||||
|
-webkit-font-smoothing: var(
|
||||||
|
--paper-font-subhead_-_-webkit-font-smoothing
|
||||||
|
);
|
||||||
|
font-weight: var(--paper-font-subhead_-_font-weight);
|
||||||
|
line-height: var(--paper-font-subhead_-_line-height);
|
||||||
|
}
|
||||||
|
ha-data-table {
|
||||||
|
width: 100%;
|
||||||
|
--data-table-border-width: 0;
|
||||||
|
}
|
||||||
|
:host(:not([narrow])) ha-data-table {
|
||||||
|
height: calc(100vh - 1px - var(--header-height));
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
ha-button-menu {
|
||||||
|
margin-right: 8px;
|
||||||
|
}
|
||||||
|
.table-header {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
border-bottom: 1px solid rgba(var(--rgb-primary-text-color), 0.12);
|
||||||
|
}
|
||||||
|
search-input {
|
||||||
|
margin-left: 16px;
|
||||||
|
flex-grow: 1;
|
||||||
|
position: relative;
|
||||||
|
top: 2px;
|
||||||
|
}
|
||||||
|
.search-toolbar search-input {
|
||||||
|
margin-left: 8px;
|
||||||
|
top: 1px;
|
||||||
|
}
|
||||||
|
.search-toolbar {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
color: var(--secondary-text-color);
|
||||||
|
}
|
||||||
|
.search-toolbar ha-button-menu {
|
||||||
|
position: static;
|
||||||
|
}
|
||||||
|
.selected-txt {
|
||||||
|
font-weight: bold;
|
||||||
|
padding-left: 16px;
|
||||||
|
}
|
||||||
|
.table-header .selected-txt {
|
||||||
|
margin-top: 20px;
|
||||||
|
}
|
||||||
|
.search-toolbar .selected-txt {
|
||||||
|
font-size: 16px;
|
||||||
|
}
|
||||||
|
.header-btns > mwc-button,
|
||||||
|
.header-btns > ha-icon-button {
|
||||||
|
margin: 8px;
|
||||||
|
}
|
||||||
|
.active-filters {
|
||||||
|
color: var(--primary-text-color);
|
||||||
|
position: relative;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
padding: 2px 2px 2px 8px;
|
||||||
|
margin-left: 4px;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
.active-filters ha-icon {
|
||||||
|
color: var(--primary-color);
|
||||||
|
}
|
||||||
|
.active-filters mwc-button {
|
||||||
|
margin-left: 8px;
|
||||||
|
}
|
||||||
|
.active-filters::before {
|
||||||
|
background-color: var(--primary-color);
|
||||||
|
opacity: 0.12;
|
||||||
|
border-radius: 4px;
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
content: "";
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
|
@ -20,9 +20,16 @@ import {
|
|||||||
import { showAlertDialog } from "../../../dialogs/generic/show-dialog-box";
|
import { showAlertDialog } from "../../../dialogs/generic/show-dialog-box";
|
||||||
import type { PolymerChangedEvent } from "../../../polymer-types";
|
import type { PolymerChangedEvent } from "../../../polymer-types";
|
||||||
import type { HomeAssistant } from "../../../types";
|
import type { HomeAssistant } from "../../../types";
|
||||||
|
import "../../../components/ha-area-picker";
|
||||||
|
import { UnsubscribeFunc } from "home-assistant-js-websocket";
|
||||||
|
import {
|
||||||
|
DeviceRegistryEntry,
|
||||||
|
subscribeDeviceRegistry,
|
||||||
|
} from "../../../data/device_registry";
|
||||||
|
import { SubscribeMixin } from "../../../mixins/subscribe-mixin";
|
||||||
|
|
||||||
@customElement("ha-registry-basic-editor")
|
@customElement("ha-registry-basic-editor")
|
||||||
export class HaEntityRegistryBasicEditor extends LitElement {
|
export class HaEntityRegistryBasicEditor extends SubscribeMixin(LitElement) {
|
||||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||||
|
|
||||||
@property() public entry!: ExtEntityRegistryEntry;
|
@property() public entry!: ExtEntityRegistryEntry;
|
||||||
@ -31,16 +38,26 @@ export class HaEntityRegistryBasicEditor extends LitElement {
|
|||||||
|
|
||||||
@internalProperty() private _entityId!: string;
|
@internalProperty() private _entityId!: string;
|
||||||
|
|
||||||
|
@internalProperty() private _areaId?: string;
|
||||||
|
|
||||||
@internalProperty() private _disabledBy!: string | null;
|
@internalProperty() private _disabledBy!: string | null;
|
||||||
|
|
||||||
|
private _deviceLookup?: Record<string, DeviceRegistryEntry>;
|
||||||
|
|
||||||
|
@internalProperty() private _device?: DeviceRegistryEntry;
|
||||||
|
|
||||||
@internalProperty() private _submitting?: boolean;
|
@internalProperty() private _submitting?: boolean;
|
||||||
|
|
||||||
public async updateEntry(): Promise<void> {
|
public async updateEntry(): Promise<void> {
|
||||||
this._submitting = true;
|
this._submitting = true;
|
||||||
const params: Partial<EntityRegistryEntryUpdateParams> = {
|
const params: Partial<EntityRegistryEntryUpdateParams> = {
|
||||||
new_entity_id: this._entityId.trim(),
|
new_entity_id: this._entityId.trim(),
|
||||||
|
area_id: this._areaId || null,
|
||||||
};
|
};
|
||||||
if (this._disabledBy === null || this._disabledBy === "user") {
|
if (
|
||||||
|
this.entry.disabled_by !== this._disabledBy &&
|
||||||
|
(this._disabledBy === null || this._disabledBy === "user")
|
||||||
|
) {
|
||||||
params.disabled_by = this._disabledBy;
|
params.disabled_by = this._disabledBy;
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
@ -70,6 +87,20 @@ export class HaEntityRegistryBasicEditor extends LitElement {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public hassSubscribe(): UnsubscribeFunc[] {
|
||||||
|
return [
|
||||||
|
subscribeDeviceRegistry(this.hass.connection!, (devices) => {
|
||||||
|
this._deviceLookup = {};
|
||||||
|
for (const device of devices) {
|
||||||
|
this._deviceLookup[device.id] = device;
|
||||||
|
}
|
||||||
|
if (!this._device && this.entry.device_id) {
|
||||||
|
this._device = this._deviceLookup[this.entry.device_id];
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
protected updated(changedProperties: PropertyValues) {
|
protected updated(changedProperties: PropertyValues) {
|
||||||
super.updated(changedProperties);
|
super.updated(changedProperties);
|
||||||
if (!changedProperties.has("entry")) {
|
if (!changedProperties.has("entry")) {
|
||||||
@ -79,6 +110,11 @@ export class HaEntityRegistryBasicEditor extends LitElement {
|
|||||||
this._origEntityId = this.entry.entity_id;
|
this._origEntityId = this.entry.entity_id;
|
||||||
this._entityId = this.entry.entity_id;
|
this._entityId = this.entry.entity_id;
|
||||||
this._disabledBy = this.entry.disabled_by;
|
this._disabledBy = this.entry.disabled_by;
|
||||||
|
this._areaId = this.entry.area_id;
|
||||||
|
this._device =
|
||||||
|
this.entry.device_id && this._deviceLookup
|
||||||
|
? this._deviceLookup[this.entry.device_id]
|
||||||
|
: undefined;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -105,6 +141,12 @@ export class HaEntityRegistryBasicEditor extends LitElement {
|
|||||||
.invalid=${invalidDomainUpdate}
|
.invalid=${invalidDomainUpdate}
|
||||||
.disabled=${this._submitting}
|
.disabled=${this._submitting}
|
||||||
></paper-input>
|
></paper-input>
|
||||||
|
<ha-area-picker
|
||||||
|
.hass=${this.hass}
|
||||||
|
.value=${this._areaId}
|
||||||
|
.placeholder=${this._device?.area_id}
|
||||||
|
@value-changed=${this._areaPicked}
|
||||||
|
></ha-area-picker>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<ha-switch
|
<ha-switch
|
||||||
.checked=${!this._disabledBy}
|
.checked=${!this._disabledBy}
|
||||||
@ -139,6 +181,10 @@ export class HaEntityRegistryBasicEditor extends LitElement {
|
|||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private _areaPicked(ev: CustomEvent) {
|
||||||
|
this._areaId = ev.detail.value;
|
||||||
|
}
|
||||||
|
|
||||||
private _entityIdChanged(ev: PolymerChangedEvent<string>): void {
|
private _entityIdChanged(ev: PolymerChangedEvent<string>): void {
|
||||||
this._entityId = ev.detail.value;
|
this._entityId = ev.detail.value;
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import "@material/mwc-button/mwc-button";
|
import "@material/mwc-button/mwc-button";
|
||||||
import "@polymer/paper-input/paper-input";
|
import "@polymer/paper-input/paper-input";
|
||||||
import { HassEntity } from "home-assistant-js-websocket";
|
import { HassEntity, UnsubscribeFunc } from "home-assistant-js-websocket";
|
||||||
import {
|
import {
|
||||||
css,
|
css,
|
||||||
CSSResult,
|
CSSResult,
|
||||||
@ -31,9 +31,18 @@ import type { PolymerChangedEvent } from "../../../polymer-types";
|
|||||||
import { haStyle } from "../../../resources/styles";
|
import { haStyle } from "../../../resources/styles";
|
||||||
import type { HomeAssistant } from "../../../types";
|
import type { HomeAssistant } from "../../../types";
|
||||||
import { domainIcon } from "../../../common/entity/domain_icon";
|
import { domainIcon } from "../../../common/entity/domain_icon";
|
||||||
|
import "../../../components/ha-area-picker";
|
||||||
|
import {
|
||||||
|
DeviceRegistryEntry,
|
||||||
|
subscribeDeviceRegistry,
|
||||||
|
updateDeviceRegistryEntry,
|
||||||
|
} from "../../../data/device_registry";
|
||||||
|
import { SubscribeMixin } from "../../../mixins/subscribe-mixin";
|
||||||
|
import "../../../components/ha-expansion-panel";
|
||||||
|
import { showDeviceRegistryDetailDialog } from "../devices/device-registry-detail/show-dialog-device-registry-detail";
|
||||||
|
|
||||||
@customElement("entity-registry-settings")
|
@customElement("entity-registry-settings")
|
||||||
export class EntityRegistrySettings extends LitElement {
|
export class EntityRegistrySettings extends SubscribeMixin(LitElement) {
|
||||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||||
|
|
||||||
@property() public entry!: ExtEntityRegistryEntry;
|
@property() public entry!: ExtEntityRegistryEntry;
|
||||||
@ -44,14 +53,34 @@ export class EntityRegistrySettings extends LitElement {
|
|||||||
|
|
||||||
@internalProperty() private _entityId!: string;
|
@internalProperty() private _entityId!: string;
|
||||||
|
|
||||||
|
@internalProperty() private _areaId?: string | null;
|
||||||
|
|
||||||
@internalProperty() private _disabledBy!: string | null;
|
@internalProperty() private _disabledBy!: string | null;
|
||||||
|
|
||||||
|
private _deviceLookup?: Record<string, DeviceRegistryEntry>;
|
||||||
|
|
||||||
|
@internalProperty() private _device?: DeviceRegistryEntry;
|
||||||
|
|
||||||
@internalProperty() private _error?: string;
|
@internalProperty() private _error?: string;
|
||||||
|
|
||||||
@internalProperty() private _submitting?: boolean;
|
@internalProperty() private _submitting?: boolean;
|
||||||
|
|
||||||
private _origEntityId!: string;
|
private _origEntityId!: string;
|
||||||
|
|
||||||
|
public hassSubscribe(): UnsubscribeFunc[] {
|
||||||
|
return [
|
||||||
|
subscribeDeviceRegistry(this.hass.connection!, (devices) => {
|
||||||
|
this._deviceLookup = {};
|
||||||
|
for (const device of devices) {
|
||||||
|
this._deviceLookup[device.id] = device;
|
||||||
|
}
|
||||||
|
if (this.entry.device_id) {
|
||||||
|
this._device = this._deviceLookup[this.entry.device_id];
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
protected updated(changedProperties: PropertyValues) {
|
protected updated(changedProperties: PropertyValues) {
|
||||||
super.updated(changedProperties);
|
super.updated(changedProperties);
|
||||||
if (changedProperties.has("entry")) {
|
if (changedProperties.has("entry")) {
|
||||||
@ -59,8 +88,13 @@ export class EntityRegistrySettings extends LitElement {
|
|||||||
this._name = this.entry.name || "";
|
this._name = this.entry.name || "";
|
||||||
this._icon = this.entry.icon || "";
|
this._icon = this.entry.icon || "";
|
||||||
this._origEntityId = this.entry.entity_id;
|
this._origEntityId = this.entry.entity_id;
|
||||||
|
this._areaId = this.entry.area_id;
|
||||||
this._entityId = this.entry.entity_id;
|
this._entityId = this.entry.entity_id;
|
||||||
this._disabledBy = this.entry.disabled_by;
|
this._disabledBy = this.entry.disabled_by;
|
||||||
|
this._device =
|
||||||
|
this.entry.device_id && this._deviceLookup
|
||||||
|
? this._deviceLookup[this.entry.device_id]
|
||||||
|
: undefined;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -77,10 +111,19 @@ export class EntityRegistrySettings extends LitElement {
|
|||||||
return html`
|
return html`
|
||||||
${!stateObj
|
${!stateObj
|
||||||
? html`
|
? html`
|
||||||
<div class="container">
|
<div class="container warning">
|
||||||
${this.hass!.localize(
|
${this.hass!.localize(
|
||||||
"ui.dialogs.entity_registry.editor.unavailable"
|
"ui.dialogs.entity_registry.editor.unavailable"
|
||||||
)}
|
)}
|
||||||
|
${this._device?.disabled_by
|
||||||
|
? html`<br />${this.hass!.localize(
|
||||||
|
"ui.dialogs.entity_registry.editor.device_disabled"
|
||||||
|
)}<br /><mwc-button @click=${this._openDeviceSettings}>
|
||||||
|
${this.hass!.localize(
|
||||||
|
"ui.dialogs.entity_registry.editor.open_device_settings"
|
||||||
|
)}
|
||||||
|
</mwc-button>`
|
||||||
|
: ""}
|
||||||
</div>
|
</div>
|
||||||
`
|
`
|
||||||
: ""}
|
: ""}
|
||||||
@ -117,9 +160,17 @@ export class EntityRegistrySettings extends LitElement {
|
|||||||
.invalid=${invalidDomainUpdate}
|
.invalid=${invalidDomainUpdate}
|
||||||
.disabled=${this._submitting}
|
.disabled=${this._submitting}
|
||||||
></paper-input>
|
></paper-input>
|
||||||
|
${!this.entry.device_id
|
||||||
|
? html`<ha-area-picker
|
||||||
|
.hass=${this.hass}
|
||||||
|
.value=${this._areaId}
|
||||||
|
@value-changed=${this._areaPicked}
|
||||||
|
></ha-area-picker>`
|
||||||
|
: ""}
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<ha-switch
|
<ha-switch
|
||||||
.checked=${!this._disabledBy}
|
.checked=${!this._disabledBy}
|
||||||
|
.disabled=${this._device?.disabled_by}
|
||||||
@change=${this._disabledByChanged}
|
@change=${this._disabledByChanged}
|
||||||
>
|
>
|
||||||
</ha-switch>
|
</ha-switch>
|
||||||
@ -148,6 +199,31 @@ export class EntityRegistrySettings extends LitElement {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
${this.entry.device_id
|
||||||
|
? html`<ha-expansion-panel .header=${"Advanced"}>
|
||||||
|
<p>
|
||||||
|
By default the entities of a device are in the same area as the
|
||||||
|
device. If you change the area of this entity, it will no longer
|
||||||
|
follow the area of the device.
|
||||||
|
</p>
|
||||||
|
${this._areaId
|
||||||
|
? html`<mwc-button @click=${this._clearArea}
|
||||||
|
>Follow device area</mwc-button
|
||||||
|
>`
|
||||||
|
: this._device
|
||||||
|
? html`<mwc-button @click=${this._openDeviceSettings}
|
||||||
|
>Change device area</mwc-button
|
||||||
|
>`
|
||||||
|
: ""}
|
||||||
|
<ha-area-picker
|
||||||
|
.hass=${this.hass}
|
||||||
|
.value=${this._areaId}
|
||||||
|
.placeholder=${this._device?.area_id}
|
||||||
|
@value-changed=${this._areaPicked}
|
||||||
|
></ha-area-picker
|
||||||
|
></ha-expansion-panel>`
|
||||||
|
: ""}
|
||||||
</div>
|
</div>
|
||||||
<div class="buttons">
|
<div class="buttons">
|
||||||
<mwc-button
|
<mwc-button
|
||||||
@ -183,14 +259,37 @@ export class EntityRegistrySettings extends LitElement {
|
|||||||
this._entityId = ev.detail.value;
|
this._entityId = ev.detail.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private _areaPicked(ev: CustomEvent) {
|
||||||
|
this._error = undefined;
|
||||||
|
this._areaId = ev.detail.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _clearArea() {
|
||||||
|
this._error = undefined;
|
||||||
|
this._areaId = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _openDeviceSettings() {
|
||||||
|
showDeviceRegistryDetailDialog(this, {
|
||||||
|
device: this._device!,
|
||||||
|
updateEntry: async (updates) => {
|
||||||
|
await updateDeviceRegistryEntry(this.hass, this._device!.id, updates);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
private async _updateEntry(): Promise<void> {
|
private async _updateEntry(): Promise<void> {
|
||||||
this._submitting = true;
|
this._submitting = true;
|
||||||
const params: Partial<EntityRegistryEntryUpdateParams> = {
|
const params: Partial<EntityRegistryEntryUpdateParams> = {
|
||||||
name: this._name.trim() || null,
|
name: this._name.trim() || null,
|
||||||
icon: this._icon.trim() || null,
|
icon: this._icon.trim() || null,
|
||||||
|
area_id: this._areaId || null,
|
||||||
new_entity_id: this._entityId.trim(),
|
new_entity_id: this._entityId.trim(),
|
||||||
};
|
};
|
||||||
if (this._disabledBy === null || this._disabledBy === "user") {
|
if (
|
||||||
|
this.entry.disabled_by !== this._disabledBy &&
|
||||||
|
(this._disabledBy === null || this._disabledBy === "user")
|
||||||
|
) {
|
||||||
params.disabled_by = this._disabledBy;
|
params.disabled_by = this._disabledBy;
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import "@material/mwc-list/mwc-list-item";
|
import "@material/mwc-list/mwc-list-item";
|
||||||
import type { RequestSelectedDetail } from "@material/mwc-list/mwc-list-item";
|
import type { RequestSelectedDetail } from "@material/mwc-list/mwc-list-item";
|
||||||
import { mdiFilterVariant } from "@mdi/js";
|
import { mdiFilterVariant, mdiPlus } from "@mdi/js";
|
||||||
import "@polymer/paper-checkbox/paper-checkbox";
|
import "@polymer/paper-checkbox/paper-checkbox";
|
||||||
import "@polymer/paper-dropdown-menu/paper-dropdown-menu";
|
import "@polymer/paper-dropdown-menu/paper-dropdown-menu";
|
||||||
import "@polymer/paper-item/paper-icon-item";
|
import "@polymer/paper-item/paper-icon-item";
|
||||||
@ -62,6 +62,15 @@ import {
|
|||||||
} from "./show-dialog-entity-editor";
|
} from "./show-dialog-entity-editor";
|
||||||
import { haStyle } from "../../../resources/styles";
|
import { haStyle } from "../../../resources/styles";
|
||||||
import { UNAVAILABLE } from "../../../data/entity";
|
import { UNAVAILABLE } from "../../../data/entity";
|
||||||
|
import {
|
||||||
|
DeviceRegistryEntry,
|
||||||
|
subscribeDeviceRegistry,
|
||||||
|
} from "../../../data/device_registry";
|
||||||
|
import {
|
||||||
|
AreaRegistryEntry,
|
||||||
|
subscribeAreaRegistry,
|
||||||
|
} from "../../../data/area_registry";
|
||||||
|
import { computeRTL } from "../../../common/util/compute_rtl";
|
||||||
|
|
||||||
export interface StateEntity extends EntityRegistryEntry {
|
export interface StateEntity extends EntityRegistryEntry {
|
||||||
readonly?: boolean;
|
readonly?: boolean;
|
||||||
@ -73,6 +82,7 @@ export interface EntityRow extends StateEntity {
|
|||||||
unavailable: boolean;
|
unavailable: boolean;
|
||||||
restored: boolean;
|
restored: boolean;
|
||||||
status: string;
|
status: string;
|
||||||
|
area?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
@customElement("ha-config-entities")
|
@customElement("ha-config-entities")
|
||||||
@ -87,6 +97,10 @@ export class HaConfigEntities extends SubscribeMixin(LitElement) {
|
|||||||
|
|
||||||
@internalProperty() private _entities?: EntityRegistryEntry[];
|
@internalProperty() private _entities?: EntityRegistryEntry[];
|
||||||
|
|
||||||
|
@internalProperty() private _devices?: DeviceRegistryEntry[];
|
||||||
|
|
||||||
|
@internalProperty() private _areas: AreaRegistryEntry[] = [];
|
||||||
|
|
||||||
@internalProperty() private _stateEntities: StateEntity[] = [];
|
@internalProperty() private _stateEntities: StateEntity[] = [];
|
||||||
|
|
||||||
@property() public _entries?: ConfigEntry[];
|
@property() public _entries?: ConfigEntry[];
|
||||||
@ -175,9 +189,11 @@ export class HaConfigEntities extends SubscribeMixin(LitElement) {
|
|||||||
? (name, entity: any) =>
|
? (name, entity: any) =>
|
||||||
html`
|
html`
|
||||||
${name}<br />
|
${name}<br />
|
||||||
|
<div class="secondary">
|
||||||
${entity.entity_id} |
|
${entity.entity_id} |
|
||||||
${this.hass.localize(`component.${entity.platform}.title`) ||
|
${this.hass.localize(`component.${entity.platform}.title`) ||
|
||||||
entity.platform}
|
entity.platform}
|
||||||
|
</div>
|
||||||
`
|
`
|
||||||
: undefined,
|
: undefined,
|
||||||
},
|
},
|
||||||
@ -201,6 +217,15 @@ export class HaConfigEntities extends SubscribeMixin(LitElement) {
|
|||||||
template: (platform) =>
|
template: (platform) =>
|
||||||
this.hass.localize(`component.${platform}.title`) || platform,
|
this.hass.localize(`component.${platform}.title`) || platform,
|
||||||
},
|
},
|
||||||
|
area: {
|
||||||
|
title: this.hass.localize(
|
||||||
|
"ui.panel.config.entities.picker.headers.area"
|
||||||
|
),
|
||||||
|
sortable: true,
|
||||||
|
hidden: narrow,
|
||||||
|
filterable: true,
|
||||||
|
width: "15%",
|
||||||
|
},
|
||||||
status: {
|
status: {
|
||||||
title: this.hass.localize(
|
title: this.hass.localize(
|
||||||
"ui.panel.config.entities.picker.headers.status"
|
"ui.panel.config.entities.picker.headers.status"
|
||||||
@ -252,48 +277,87 @@ export class HaConfigEntities extends SubscribeMixin(LitElement) {
|
|||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
private _filteredEntities = memoize(
|
private _filteredEntitiesAndDomains = memoize(
|
||||||
(
|
(
|
||||||
entities: EntityRegistryEntry[],
|
entities: EntityRegistryEntry[],
|
||||||
|
devices: DeviceRegistryEntry[] | undefined,
|
||||||
|
areas: AreaRegistryEntry[] | undefined,
|
||||||
stateEntities: StateEntity[],
|
stateEntities: StateEntity[],
|
||||||
filters: URLSearchParams,
|
filters: URLSearchParams,
|
||||||
showDisabled: boolean,
|
showDisabled: boolean,
|
||||||
showUnavailable: boolean,
|
showUnavailable: boolean,
|
||||||
showReadOnly: boolean
|
showReadOnly: boolean,
|
||||||
): EntityRow[] => {
|
entries?: ConfigEntry[]
|
||||||
|
) => {
|
||||||
const result: EntityRow[] = [];
|
const result: EntityRow[] = [];
|
||||||
|
|
||||||
// If nothing gets filtered, this is our correct count of entities
|
// If nothing gets filtered, this is our correct count of entities
|
||||||
let startLength = entities.length + stateEntities.length;
|
let startLength = entities.length + stateEntities.length;
|
||||||
|
|
||||||
entities = showReadOnly ? entities.concat(stateEntities) : entities;
|
const areaLookup: { [areaId: string]: AreaRegistryEntry } = {};
|
||||||
|
const deviceLookup: { [deviceId: string]: DeviceRegistryEntry } = {};
|
||||||
|
|
||||||
|
if (areas) {
|
||||||
|
for (const area of areas) {
|
||||||
|
areaLookup[area.area_id] = area;
|
||||||
|
}
|
||||||
|
if (devices) {
|
||||||
|
for (const device of devices) {
|
||||||
|
deviceLookup[device.id] = device;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
entities.forEach((entity) => {
|
||||||
|
return entity;
|
||||||
|
});
|
||||||
|
|
||||||
|
let filteredEntities = showReadOnly
|
||||||
|
? entities.concat(stateEntities)
|
||||||
|
: entities;
|
||||||
|
|
||||||
|
const filteredDomains: string[] = [];
|
||||||
|
|
||||||
filters.forEach((value, key) => {
|
filters.forEach((value, key) => {
|
||||||
switch (key) {
|
if (key === "config_entry") {
|
||||||
case "config_entry":
|
filteredEntities = filteredEntities.filter(
|
||||||
entities = entities.filter(
|
|
||||||
(entity) => entity.config_entry_id === value
|
(entity) => entity.config_entry_id === value
|
||||||
);
|
);
|
||||||
// If we have an active filter and `showReadOnly` is true, the length of `entities` is correct.
|
// If we have an active filter and `showReadOnly` is true, the length of `entities` is correct.
|
||||||
// If however, the read-only entities were not added before, we need to check how many would
|
// If however, the read-only entities were not added before, we need to check how many would
|
||||||
// have matched the active filter and add that number to the count.
|
// have matched the active filter and add that number to the count.
|
||||||
startLength = entities.length;
|
startLength = filteredEntities.length;
|
||||||
if (!showReadOnly) {
|
if (!showReadOnly) {
|
||||||
startLength += stateEntities.filter(
|
startLength += stateEntities.filter(
|
||||||
(entity) => entity.config_entry_id === value
|
(entity) => entity.config_entry_id === value
|
||||||
).length;
|
).length;
|
||||||
}
|
}
|
||||||
break;
|
|
||||||
|
if (!entries) {
|
||||||
|
this._loadConfigEntries();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const configEntry = entries.find((entry) => entry.entry_id === value);
|
||||||
|
|
||||||
|
if (configEntry) {
|
||||||
|
filteredDomains.push(configEntry.domain);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!showDisabled) {
|
if (!showDisabled) {
|
||||||
entities = entities.filter((entity) => !entity.disabled_by);
|
filteredEntities = filteredEntities.filter(
|
||||||
|
(entity) => !entity.disabled_by
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const entry of entities) {
|
for (const entry of filteredEntities) {
|
||||||
const entity = this.hass.states[entry.entity_id];
|
const entity = this.hass.states[entry.entity_id];
|
||||||
const unavailable = entity?.state === UNAVAILABLE;
|
const unavailable = entity?.state === UNAVAILABLE;
|
||||||
const restored = entity?.attributes.restored;
|
const restored = entity?.attributes.restored;
|
||||||
|
const areaId = entry.area_id ?? deviceLookup[entry.device_id!]?.area_id;
|
||||||
|
const area = areaId ? areaLookup[areaId] : undefined;
|
||||||
|
|
||||||
if (!showUnavailable && unavailable) {
|
if (!showUnavailable && unavailable) {
|
||||||
continue;
|
continue;
|
||||||
@ -309,6 +373,7 @@ export class HaConfigEntities extends SubscribeMixin(LitElement) {
|
|||||||
this.hass.localize("state.default.unavailable"),
|
this.hass.localize("state.default.unavailable"),
|
||||||
unavailable,
|
unavailable,
|
||||||
restored,
|
restored,
|
||||||
|
area: area ? area.name : undefined,
|
||||||
status: restored
|
status: restored
|
||||||
? this.hass.localize(
|
? this.hass.localize(
|
||||||
"ui.panel.config.entities.picker.status.restored"
|
"ui.panel.config.entities.picker.status.restored"
|
||||||
@ -326,7 +391,7 @@ export class HaConfigEntities extends SubscribeMixin(LitElement) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this._numHiddenEntities = startLength - result.length;
|
this._numHiddenEntities = startLength - result.length;
|
||||||
return result;
|
return { filteredEntities: result, filteredDomains: filteredDomains };
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -345,6 +410,12 @@ export class HaConfigEntities extends SubscribeMixin(LitElement) {
|
|||||||
subscribeEntityRegistry(this.hass.connection!, (entities) => {
|
subscribeEntityRegistry(this.hass.connection!, (entities) => {
|
||||||
this._entities = entities;
|
this._entities = entities;
|
||||||
}),
|
}),
|
||||||
|
subscribeDeviceRegistry(this.hass.connection!, (devices) => {
|
||||||
|
this._devices = devices;
|
||||||
|
}),
|
||||||
|
subscribeAreaRegistry(this.hass.connection, (areas) => {
|
||||||
|
this._areas = areas;
|
||||||
|
}),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -370,15 +441,22 @@ export class HaConfigEntities extends SubscribeMixin(LitElement) {
|
|||||||
this._entries
|
this._entries
|
||||||
);
|
);
|
||||||
|
|
||||||
const entityData = this._filteredEntities(
|
const {
|
||||||
|
filteredEntities,
|
||||||
|
filteredDomains,
|
||||||
|
} = this._filteredEntitiesAndDomains(
|
||||||
this._entities,
|
this._entities,
|
||||||
|
this._devices,
|
||||||
|
this._areas,
|
||||||
this._stateEntities,
|
this._stateEntities,
|
||||||
this._searchParms,
|
this._searchParms,
|
||||||
this._showDisabled,
|
this._showDisabled,
|
||||||
this._showUnavailable,
|
this._showUnavailable,
|
||||||
this._showReadOnly
|
this._showReadOnly,
|
||||||
|
this._entries
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const includeZHAFab = filteredDomains.includes("zha");
|
||||||
const headerToolbar = this._selectedEntities.length
|
const headerToolbar = this._selectedEntities.length
|
||||||
? html`
|
? html`
|
||||||
<p class="selected-txt">
|
<p class="selected-txt">
|
||||||
@ -584,13 +662,14 @@ export class HaConfigEntities extends SubscribeMixin(LitElement) {
|
|||||||
.route=${this.route}
|
.route=${this.route}
|
||||||
.tabs=${configSections.integrations}
|
.tabs=${configSections.integrations}
|
||||||
.columns=${this._columns(this.narrow, this.hass.language)}
|
.columns=${this._columns(this.narrow, this.hass.language)}
|
||||||
.data=${entityData}
|
.data=${filteredEntities}
|
||||||
.filter=${this._filter}
|
.filter=${this._filter}
|
||||||
selectable
|
selectable
|
||||||
clickable
|
clickable
|
||||||
@selection-changed=${this._handleSelectionChanged}
|
@selection-changed=${this._handleSelectionChanged}
|
||||||
@row-click=${this._openEditEntry}
|
@row-click=${this._openEditEntry}
|
||||||
id="entity_id"
|
id="entity_id"
|
||||||
|
.hasFab=${includeZHAFab}
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class=${classMap({
|
class=${classMap({
|
||||||
@ -601,6 +680,17 @@ export class HaConfigEntities extends SubscribeMixin(LitElement) {
|
|||||||
>
|
>
|
||||||
${headerToolbar}
|
${headerToolbar}
|
||||||
</div>
|
</div>
|
||||||
|
${includeZHAFab
|
||||||
|
? html`<a href="/config/zha/add" slot="fab">
|
||||||
|
<ha-fab
|
||||||
|
.label=${this.hass.localize("ui.panel.config.zha.add_device")}
|
||||||
|
extended
|
||||||
|
?rtl=${computeRTL(this.hass)}
|
||||||
|
>
|
||||||
|
<ha-svg-icon slot="icon" .path=${mdiPlus}></ha-svg-icon>
|
||||||
|
</ha-fab>
|
||||||
|
</a>`
|
||||||
|
: html``}
|
||||||
</hass-tabs-subpage-data-table>
|
</hass-tabs-subpage-data-table>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
@ -5,6 +5,8 @@ import { classMap } from "lit-html/directives/class-map";
|
|||||||
export class HaConfigSection extends LitElement {
|
export class HaConfigSection extends LitElement {
|
||||||
@property() public isWide = false;
|
@property() public isWide = false;
|
||||||
|
|
||||||
|
@property({ type: Boolean }) public vertical = false;
|
||||||
|
|
||||||
protected render() {
|
protected render() {
|
||||||
return html`
|
return html`
|
||||||
<div
|
<div
|
||||||
@ -16,8 +18,8 @@ export class HaConfigSection extends LitElement {
|
|||||||
<div
|
<div
|
||||||
class="together layout ${classMap({
|
class="together layout ${classMap({
|
||||||
narrow: !this.isWide,
|
narrow: !this.isWide,
|
||||||
vertical: !this.isWide,
|
vertical: this.vertical || !this.isWide,
|
||||||
horizontal: this.isWide,
|
horizontal: !this.vertical && this.isWide,
|
||||||
})}"
|
})}"
|
||||||
>
|
>
|
||||||
<div class="intro"><slot name="introduction"></slot></div>
|
<div class="intro"><slot name="introduction"></slot></div>
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import "@material/mwc-fab";
|
import "../../../components/ha-fab";
|
||||||
import { mdiPlus } from "@mdi/js";
|
import { mdiPlus } from "@mdi/js";
|
||||||
import "@polymer/paper-checkbox/paper-checkbox";
|
import "@polymer/paper-checkbox/paper-checkbox";
|
||||||
import "@polymer/paper-dropdown-menu/paper-dropdown-menu";
|
import "@polymer/paper-dropdown-menu/paper-dropdown-menu";
|
||||||
@ -158,7 +158,7 @@ export class HaConfigHelpers extends LitElement {
|
|||||||
"ui.panel.config.helpers.picker.no_helpers"
|
"ui.panel.config.helpers.picker.no_helpers"
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<mwc-fab
|
<ha-fab
|
||||||
slot="fab"
|
slot="fab"
|
||||||
.label=${this.hass.localize(
|
.label=${this.hass.localize(
|
||||||
"ui.panel.config.helpers.picker.add_helper"
|
"ui.panel.config.helpers.picker.add_helper"
|
||||||
@ -167,7 +167,7 @@ export class HaConfigHelpers extends LitElement {
|
|||||||
@click=${this._createHelpler}
|
@click=${this._createHelpler}
|
||||||
>
|
>
|
||||||
<ha-svg-icon slot="icon" .path=${mdiPlus}></ha-svg-icon>
|
<ha-svg-icon slot="icon" .path=${mdiPlus}></ha-svg-icon>
|
||||||
</mwc-fab>
|
</ha-fab>
|
||||||
</hass-tabs-subpage-data-table>
|
</hass-tabs-subpage-data-table>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import "@material/mwc-fab";
|
import "../../../components/ha-fab";
|
||||||
import "@material/mwc-icon-button";
|
import "@material/mwc-icon-button";
|
||||||
import "@material/mwc-list/mwc-list-item";
|
import "@material/mwc-list/mwc-list-item";
|
||||||
import { mdiDotsVertical, mdiPlus } from "@mdi/js";
|
import { mdiDotsVertical, mdiPlus } from "@mdi/js";
|
||||||
@ -474,7 +474,7 @@ class HaConfigIntegrations extends SubscribeMixin(LitElement) {
|
|||||||
`
|
`
|
||||||
: ""}
|
: ""}
|
||||||
</div>
|
</div>
|
||||||
<mwc-fab
|
<ha-fab
|
||||||
slot="fab"
|
slot="fab"
|
||||||
.label=${this.hass.localize(
|
.label=${this.hass.localize(
|
||||||
"ui.panel.config.integrations.add_integration"
|
"ui.panel.config.integrations.add_integration"
|
||||||
@ -483,7 +483,7 @@ class HaConfigIntegrations extends SubscribeMixin(LitElement) {
|
|||||||
@click=${this._createFlow}
|
@click=${this._createFlow}
|
||||||
>
|
>
|
||||||
<ha-svg-icon slot="icon" .path=${mdiPlus}></ha-svg-icon>
|
<ha-svg-icon slot="icon" .path=${mdiPlus}></ha-svg-icon>
|
||||||
</mwc-fab>
|
</ha-fab>
|
||||||
</hass-tabs-subpage>
|
</hass-tabs-subpage>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
import "@material/mwc-button/mwc-button";
|
import "@material/mwc-button/mwc-button";
|
||||||
import "@material/mwc-fab";
|
|
||||||
import { mdiCheckCircle, mdiCircle, mdiCloseCircle, mdiZWave } from "@mdi/js";
|
import { mdiCheckCircle, mdiCircle, mdiCloseCircle, mdiZWave } from "@mdi/js";
|
||||||
import "@polymer/paper-item/paper-icon-item";
|
import "@polymer/paper-item/paper-icon-item";
|
||||||
import "@polymer/paper-item/paper-item-body";
|
import "@polymer/paper-item/paper-item-body";
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
import "@material/mwc-button/mwc-button";
|
import "@material/mwc-button/mwc-button";
|
||||||
import "@material/mwc-fab";
|
|
||||||
import { mdiCheckCircle, mdiCircle, mdiCloseCircle } from "@mdi/js";
|
import { mdiCheckCircle, mdiCircle, mdiCloseCircle } from "@mdi/js";
|
||||||
import {
|
import {
|
||||||
css,
|
css,
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
import "@material/mwc-button/mwc-button";
|
import "@material/mwc-button/mwc-button";
|
||||||
import "@material/mwc-fab";
|
|
||||||
import { mdiAlert, mdiCheck } from "@mdi/js";
|
import { mdiAlert, mdiCheck } from "@mdi/js";
|
||||||
import {
|
import {
|
||||||
CSSResult,
|
CSSResult,
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
import "@material/mwc-button/mwc-button";
|
import "@material/mwc-button/mwc-button";
|
||||||
import "@material/mwc-fab";
|
|
||||||
import {
|
import {
|
||||||
css,
|
css,
|
||||||
CSSResultArray,
|
CSSResultArray,
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
import "@material/mwc-button/mwc-button";
|
import "@material/mwc-button/mwc-button";
|
||||||
import "@material/mwc-fab";
|
|
||||||
import {
|
import {
|
||||||
css,
|
css,
|
||||||
CSSResultArray,
|
CSSResultArray,
|
||||||
|
@ -14,13 +14,17 @@ import {
|
|||||||
} from "lit-element";
|
} from "lit-element";
|
||||||
import "../../../../../components/ha-service-description";
|
import "../../../../../components/ha-service-description";
|
||||||
import "@polymer/paper-input/paper-textarea";
|
import "@polymer/paper-input/paper-textarea";
|
||||||
import { ZHADevice } from "../../../../../data/zha";
|
|
||||||
import "../../../../../layouts/hass-tabs-subpage";
|
import "../../../../../layouts/hass-tabs-subpage";
|
||||||
import { haStyle } from "../../../../../resources/styles";
|
import { haStyle } from "../../../../../resources/styles";
|
||||||
import { HomeAssistant, Route } from "../../../../../types";
|
import { HomeAssistant, Route } from "../../../../../types";
|
||||||
import "./zha-device-card";
|
import "./zha-device-pairing-status-card";
|
||||||
import { zhaTabs } from "./zha-config-dashboard";
|
import { zhaTabs } from "./zha-config-dashboard";
|
||||||
import { IronAutogrowTextareaElement } from "@polymer/iron-autogrow-textarea";
|
import { IronAutogrowTextareaElement } from "@polymer/iron-autogrow-textarea";
|
||||||
|
import {
|
||||||
|
DEVICE_MESSAGE_TYPES,
|
||||||
|
LOG_OUTPUT,
|
||||||
|
ZHADevice,
|
||||||
|
} from "../../../../../data/zha";
|
||||||
|
|
||||||
@customElement("zha-add-devices-page")
|
@customElement("zha-add-devices-page")
|
||||||
class ZHAAddDevicesPage extends LitElement {
|
class ZHAAddDevicesPage extends LitElement {
|
||||||
@ -34,7 +38,10 @@ class ZHAAddDevicesPage extends LitElement {
|
|||||||
|
|
||||||
@internalProperty() private _error?: string;
|
@internalProperty() private _error?: string;
|
||||||
|
|
||||||
@internalProperty() private _discoveredDevices: ZHADevice[] = [];
|
@internalProperty() private _discoveredDevices: Record<
|
||||||
|
string,
|
||||||
|
ZHADevice
|
||||||
|
> = {};
|
||||||
|
|
||||||
@internalProperty() private _formattedEvents = "";
|
@internalProperty() private _formattedEvents = "";
|
||||||
|
|
||||||
@ -64,7 +71,7 @@ class ZHAAddDevicesPage extends LitElement {
|
|||||||
super.disconnectedCallback();
|
super.disconnectedCallback();
|
||||||
this._unsubscribe();
|
this._unsubscribe();
|
||||||
this._error = undefined;
|
this._error = undefined;
|
||||||
this._discoveredDevices = [];
|
this._discoveredDevices = {};
|
||||||
this._formattedEvents = "";
|
this._formattedEvents = "";
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -115,7 +122,7 @@ class ZHAAddDevicesPage extends LitElement {
|
|||||||
</div>
|
</div>
|
||||||
${this._error ? html` <div class="error">${this._error}</div> ` : ""}
|
${this._error ? html` <div class="error">${this._error}</div> ` : ""}
|
||||||
<div class="content">
|
<div class="content">
|
||||||
${this._discoveredDevices.length < 1
|
${Object.keys(this._discoveredDevices).length < 1
|
||||||
? html`
|
? html`
|
||||||
<div class="discovery-text">
|
<div class="discovery-text">
|
||||||
<h4>
|
<h4>
|
||||||
@ -133,15 +140,15 @@ class ZHAAddDevicesPage extends LitElement {
|
|||||||
</div>
|
</div>
|
||||||
`
|
`
|
||||||
: html`
|
: html`
|
||||||
${this._discoveredDevices.map(
|
${Object.values(this._discoveredDevices).map(
|
||||||
(device) => html`
|
(device) => html`
|
||||||
<zha-device-card
|
<zha-device-pairing-status-card
|
||||||
class="card"
|
class="card"
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
.device=${device}
|
.device=${device}
|
||||||
.narrow=${this.narrow}
|
.narrow=${this.narrow}
|
||||||
.showHelp=${this._showHelp}
|
.showHelp=${this._showHelp}
|
||||||
></zha-device-card>
|
></zha-device-pairing-status-card>
|
||||||
`
|
`
|
||||||
)}
|
)}
|
||||||
`}
|
`}
|
||||||
@ -164,7 +171,7 @@ class ZHAAddDevicesPage extends LitElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private _handleMessage(message: any): void {
|
private _handleMessage(message: any): void {
|
||||||
if (message.type === "log_output") {
|
if (message.type === LOG_OUTPUT) {
|
||||||
this._formattedEvents += message.log_entry.message + "\n";
|
this._formattedEvents += message.log_entry.message + "\n";
|
||||||
if (this.shadowRoot) {
|
if (this.shadowRoot) {
|
||||||
const paperTextArea = this.shadowRoot.querySelector("paper-textarea");
|
const paperTextArea = this.shadowRoot.querySelector("paper-textarea");
|
||||||
@ -175,8 +182,8 @@ class ZHAAddDevicesPage extends LitElement {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (message.type && message.type === "device_fully_initialized") {
|
if (message.type && DEVICE_MESSAGE_TYPES.includes(message.type)) {
|
||||||
this._discoveredDevices.push(message.device_info);
|
this._discoveredDevices[message.device_info.ieee] = message.device_info;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import "@polymer/paper-item/paper-item";
|
import "@polymer/paper-item/paper-item";
|
||||||
import "@polymer/paper-item/paper-item-body";
|
import "@polymer/paper-item/paper-item-body";
|
||||||
import "@material/mwc-fab";
|
import "../../../../../components/ha-fab";
|
||||||
import {
|
import {
|
||||||
css,
|
css,
|
||||||
CSSResultArray,
|
CSSResultArray,
|
||||||
@ -88,13 +88,13 @@ class ZHAConfigDashboard extends LitElement {
|
|||||||
: ""}
|
: ""}
|
||||||
</ha-card>
|
</ha-card>
|
||||||
<a href="/config/zha/add" slot="fab">
|
<a href="/config/zha/add" slot="fab">
|
||||||
<mwc-fab
|
<ha-fab
|
||||||
.label=${this.hass.localize("ui.panel.config.zha.add_device")}
|
.label=${this.hass.localize("ui.panel.config.zha.add_device")}
|
||||||
extended
|
extended
|
||||||
?rtl=${computeRTL(this.hass)}
|
?rtl=${computeRTL(this.hass)}
|
||||||
>
|
>
|
||||||
<ha-svg-icon slot="icon" .path=${mdiPlus}></ha-svg-icon>
|
<ha-svg-icon slot="icon" .path=${mdiPlus}></ha-svg-icon>
|
||||||
</mwc-fab>
|
</ha-fab>
|
||||||
</a>
|
</a>
|
||||||
</hass-tabs-subpage>
|
</hass-tabs-subpage>
|
||||||
`;
|
`;
|
||||||
|
@ -0,0 +1,147 @@
|
|||||||
|
import "@polymer/paper-input/paper-input";
|
||||||
|
import "@polymer/paper-listbox/paper-listbox";
|
||||||
|
import {
|
||||||
|
css,
|
||||||
|
CSSResult,
|
||||||
|
customElement,
|
||||||
|
html,
|
||||||
|
LitElement,
|
||||||
|
property,
|
||||||
|
internalProperty,
|
||||||
|
TemplateResult,
|
||||||
|
} from "lit-element";
|
||||||
|
import "../../../../../components/buttons/ha-call-service-button";
|
||||||
|
import "../../../../../components/entity/state-badge";
|
||||||
|
import "../../../../../components/ha-card";
|
||||||
|
import "../../../../../components/ha-service-description";
|
||||||
|
import {
|
||||||
|
CONFIGURED,
|
||||||
|
INCOMPLETE_PAIRING_STATUSES,
|
||||||
|
INITIALIZED,
|
||||||
|
INTERVIEW_COMPLETE,
|
||||||
|
ZHADevice,
|
||||||
|
} from "../../../../../data/zha";
|
||||||
|
import { haStyle } from "../../../../../resources/styles";
|
||||||
|
import { HomeAssistant } from "../../../../../types";
|
||||||
|
import "../../../../../components/ha-area-picker";
|
||||||
|
import { formatAsPaddedHex } from "./functions";
|
||||||
|
import "./zha-device-card";
|
||||||
|
import { classMap } from "lit-html/directives/class-map";
|
||||||
|
|
||||||
|
@customElement("zha-device-pairing-status-card")
|
||||||
|
class ZHADevicePairingStatusCard extends LitElement {
|
||||||
|
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||||
|
|
||||||
|
@property() public device?: ZHADevice;
|
||||||
|
|
||||||
|
@property({ type: Boolean }) public narrow?: boolean;
|
||||||
|
|
||||||
|
@internalProperty() private _showHelp = false;
|
||||||
|
|
||||||
|
protected render(): TemplateResult {
|
||||||
|
if (!this.hass || !this.device) {
|
||||||
|
return html``;
|
||||||
|
}
|
||||||
|
|
||||||
|
return html`
|
||||||
|
<ha-card
|
||||||
|
outlined
|
||||||
|
class="discovered ${classMap({
|
||||||
|
initialized: this.device.pairing_status === INITIALIZED,
|
||||||
|
})}"
|
||||||
|
><div
|
||||||
|
class="header"
|
||||||
|
>
|
||||||
|
<h1>
|
||||||
|
${this.hass!.localize(
|
||||||
|
`ui.panel.config.zha.device_pairing_card.${this.device.pairing_status}`
|
||||||
|
)}
|
||||||
|
</h1>
|
||||||
|
<h4>
|
||||||
|
${this.hass!.localize(
|
||||||
|
`ui.panel.config.zha.device_pairing_card.${this.device.pairing_status}_status_text`
|
||||||
|
)}
|
||||||
|
</h4>
|
||||||
|
</div>
|
||||||
|
<div class="card-content">
|
||||||
|
${[INTERVIEW_COMPLETE, CONFIGURED].includes(
|
||||||
|
this.device.pairing_status!
|
||||||
|
)
|
||||||
|
? html`
|
||||||
|
<div class="model">${this.device.model}</div>
|
||||||
|
<div class="manuf">
|
||||||
|
${this.hass.localize(
|
||||||
|
"ui.dialogs.zha_device_info.manuf",
|
||||||
|
"manufacturer",
|
||||||
|
this.device.manufacturer
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
`
|
||||||
|
: html``}
|
||||||
|
<div class="info">
|
||||||
|
${INCOMPLETE_PAIRING_STATUSES.includes(this.device.pairing_status!)
|
||||||
|
? html`
|
||||||
|
<div class="text">IEEE: ${this.device.ieee}</div>
|
||||||
|
<div class="text">
|
||||||
|
NWK: ${formatAsPaddedHex(this.device.nwk)}
|
||||||
|
</div>
|
||||||
|
`
|
||||||
|
: html``}
|
||||||
|
</div>
|
||||||
|
${this.device.pairing_status === INITIALIZED
|
||||||
|
? html`
|
||||||
|
<zha-device-card
|
||||||
|
class="card"
|
||||||
|
.hass=${this.hass}
|
||||||
|
.device=${this.device}
|
||||||
|
.narrow=${this.narrow}
|
||||||
|
.showHelp=${this._showHelp}
|
||||||
|
></zha-device-card>
|
||||||
|
`
|
||||||
|
: html``}
|
||||||
|
</div>
|
||||||
|
</ha-card>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
static get styles(): CSSResult[] {
|
||||||
|
return [
|
||||||
|
haStyle,
|
||||||
|
css`
|
||||||
|
.discovered {
|
||||||
|
--ha-card-border-color: var(--primary-color);
|
||||||
|
}
|
||||||
|
.discovered.initialized {
|
||||||
|
--ha-card-border-color: var(--success-color);
|
||||||
|
}
|
||||||
|
.discovered .header {
|
||||||
|
background: var(--primary-color);
|
||||||
|
color: var(--text-primary-color);
|
||||||
|
padding: 8px;
|
||||||
|
text-align: center;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
.discovered.initialized .header {
|
||||||
|
background: var(--success-color);
|
||||||
|
}
|
||||||
|
h1 {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
h4 {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
.text,
|
||||||
|
.manuf,
|
||||||
|
.model {
|
||||||
|
color: var(--secondary-text-color);
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface HTMLElementTagNameMap {
|
||||||
|
"zha-device-pairing-status-card": ZHADevicePairingStatusCard;
|
||||||
|
}
|
||||||
|
}
|
@ -1,5 +1,5 @@
|
|||||||
import "@material/mwc-button";
|
import "@material/mwc-button";
|
||||||
import "@material/mwc-fab";
|
import "../../../../../components/ha-fab";
|
||||||
import "../../../../../components/ha-icon-button";
|
import "../../../../../components/ha-icon-button";
|
||||||
import memoizeOne from "memoize-one";
|
import memoizeOne from "memoize-one";
|
||||||
import {
|
import {
|
||||||
@ -127,14 +127,14 @@ export class ZHAGroupsDashboard extends LitElement {
|
|||||||
clickable
|
clickable
|
||||||
>
|
>
|
||||||
<a href="/config/zha/group-add" slot="fab">
|
<a href="/config/zha/group-add" slot="fab">
|
||||||
<mwc-fab
|
<ha-fab
|
||||||
.label=${this.hass!.localize(
|
.label=${this.hass!.localize(
|
||||||
"ui.panel.config.zha.groups.add_group"
|
"ui.panel.config.zha.groups.add_group"
|
||||||
)}
|
)}
|
||||||
extended
|
extended
|
||||||
>
|
>
|
||||||
<ha-svg-icon slot="icon" .path=${mdiPlus}></ha-svg-icon>
|
<ha-svg-icon slot="icon" .path=${mdiPlus}></ha-svg-icon>
|
||||||
</mwc-fab>
|
</ha-fab>
|
||||||
</a>
|
</a>
|
||||||
</hass-tabs-subpage-data-table>
|
</hass-tabs-subpage-data-table>
|
||||||
`;
|
`;
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import "@material/mwc-fab";
|
import "../../../../components/ha-fab";
|
||||||
import { mdiPlus } from "@mdi/js";
|
import { mdiPlus } from "@mdi/js";
|
||||||
import "@polymer/paper-tooltip/paper-tooltip";
|
import "@polymer/paper-tooltip/paper-tooltip";
|
||||||
import {
|
import {
|
||||||
@ -223,7 +223,7 @@ export class HaConfigLovelaceDashboards extends LitElement {
|
|||||||
hasFab
|
hasFab
|
||||||
clickable
|
clickable
|
||||||
>
|
>
|
||||||
<mwc-fab
|
<ha-fab
|
||||||
slot="fab"
|
slot="fab"
|
||||||
.label=${this.hass.localize(
|
.label=${this.hass.localize(
|
||||||
"ui.panel.config.lovelace.dashboards.picker.add_dashboard"
|
"ui.panel.config.lovelace.dashboards.picker.add_dashboard"
|
||||||
@ -232,7 +232,7 @@ export class HaConfigLovelaceDashboards extends LitElement {
|
|||||||
@click=${this._addDashboard}
|
@click=${this._addDashboard}
|
||||||
>
|
>
|
||||||
<ha-svg-icon slot="icon" .path=${mdiPlus}></ha-svg-icon>
|
<ha-svg-icon slot="icon" .path=${mdiPlus}></ha-svg-icon>
|
||||||
</mwc-fab>
|
</ha-fab>
|
||||||
</hass-tabs-subpage-data-table>
|
</hass-tabs-subpage-data-table>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import "@material/mwc-fab";
|
import "../../../../components/ha-fab";
|
||||||
import { mdiPlus } from "@mdi/js";
|
import { mdiPlus } from "@mdi/js";
|
||||||
import "@polymer/paper-checkbox/paper-checkbox";
|
import "@polymer/paper-checkbox/paper-checkbox";
|
||||||
import "@polymer/paper-dropdown-menu/paper-dropdown-menu";
|
import "@polymer/paper-dropdown-menu/paper-dropdown-menu";
|
||||||
@ -103,7 +103,7 @@ export class HaConfigLovelaceRescources extends LitElement {
|
|||||||
hasFab
|
hasFab
|
||||||
clickable
|
clickable
|
||||||
>
|
>
|
||||||
<mwc-fab
|
<ha-fab
|
||||||
slot="fab"
|
slot="fab"
|
||||||
.label=${this.hass.localize(
|
.label=${this.hass.localize(
|
||||||
"ui.panel.config.lovelace.resources.picker.add_resource"
|
"ui.panel.config.lovelace.resources.picker.add_resource"
|
||||||
@ -112,7 +112,7 @@ export class HaConfigLovelaceRescources extends LitElement {
|
|||||||
@click=${this._addResource}
|
@click=${this._addResource}
|
||||||
>
|
>
|
||||||
<ha-svg-icon slot="icon" .path=${mdiPlus}></ha-svg-icon>
|
<ha-svg-icon slot="icon" .path=${mdiPlus}></ha-svg-icon>
|
||||||
</mwc-fab>
|
</ha-fab>
|
||||||
</hass-tabs-subpage-data-table>
|
</hass-tabs-subpage-data-table>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
@ -68,6 +68,8 @@ class DialogPersonDetail extends LitElement {
|
|||||||
|
|
||||||
@internalProperty() private _submitting = false;
|
@internalProperty() private _submitting = false;
|
||||||
|
|
||||||
|
@internalProperty() private _personExists = false;
|
||||||
|
|
||||||
private _deviceTrackersAvailable = memoizeOne((hass) => {
|
private _deviceTrackersAvailable = memoizeOne((hass) => {
|
||||||
return Object.keys(hass.states).some(
|
return Object.keys(hass.states).some(
|
||||||
(entityId) =>
|
(entityId) =>
|
||||||
@ -79,6 +81,7 @@ class DialogPersonDetail extends LitElement {
|
|||||||
this._params = params;
|
this._params = params;
|
||||||
this._error = undefined;
|
this._error = undefined;
|
||||||
if (this._params.entry) {
|
if (this._params.entry) {
|
||||||
|
this._personExists = true;
|
||||||
this._name = this._params.entry.name || "";
|
this._name = this._params.entry.name || "";
|
||||||
this._userId = this._params.entry.user_id || undefined;
|
this._userId = this._params.entry.user_id || undefined;
|
||||||
this._deviceTrackers = this._params.entry.device_trackers || [];
|
this._deviceTrackers = this._params.entry.device_trackers || [];
|
||||||
@ -88,6 +91,7 @@ class DialogPersonDetail extends LitElement {
|
|||||||
: undefined;
|
: undefined;
|
||||||
this._isAdmin = this._user?.group_ids.includes(SYSTEM_GROUP_ID_ADMIN);
|
this._isAdmin = this._user?.group_ids.includes(SYSTEM_GROUP_ID_ADMIN);
|
||||||
} else {
|
} else {
|
||||||
|
this._personExists = false;
|
||||||
this._name = "";
|
this._name = "";
|
||||||
this._userId = undefined;
|
this._userId = undefined;
|
||||||
this._user = undefined;
|
this._user = undefined;
|
||||||
@ -398,6 +402,7 @@ class DialogPersonDetail extends LitElement {
|
|||||||
await this._params!.updateEntry(values);
|
await this._params!.updateEntry(values);
|
||||||
} else {
|
} else {
|
||||||
await this._params!.createEntry(values);
|
await this._params!.createEntry(values);
|
||||||
|
this._personExists = true;
|
||||||
}
|
}
|
||||||
this._params = undefined;
|
this._params = undefined;
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
@ -422,6 +427,14 @@ class DialogPersonDetail extends LitElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private _close(): void {
|
private _close(): void {
|
||||||
|
// If we do not have a person ID yet (= person creation dialog was just cancelled), but
|
||||||
|
// we already created a user ID for it, delete it now to not have it "free floating".
|
||||||
|
if (!this._personExists && this._userId) {
|
||||||
|
deleteUser(this.hass, this._userId);
|
||||||
|
this._params?.refreshUsers();
|
||||||
|
this._userId = undefined;
|
||||||
|
}
|
||||||
|
|
||||||
this._params = undefined;
|
this._params = undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import "@material/mwc-fab";
|
import "../../../components/ha-fab";
|
||||||
import { mdiPlus } from "@mdi/js";
|
import { mdiPlus } from "@mdi/js";
|
||||||
import "@polymer/paper-item/paper-icon-item";
|
import "@polymer/paper-item/paper-icon-item";
|
||||||
import "@polymer/paper-item/paper-item-body";
|
import "@polymer/paper-item/paper-item-body";
|
||||||
@ -146,14 +146,14 @@ class HaConfigPerson extends LitElement {
|
|||||||
`
|
`
|
||||||
: ""}
|
: ""}
|
||||||
</ha-config-section>
|
</ha-config-section>
|
||||||
<mwc-fab
|
<ha-fab
|
||||||
slot="fab"
|
slot="fab"
|
||||||
.label=${hass.localize("ui.panel.config.person.add_person")}
|
.label=${hass.localize("ui.panel.config.person.add_person")}
|
||||||
extended
|
extended
|
||||||
@click=${this._createPerson}
|
@click=${this._createPerson}
|
||||||
>
|
>
|
||||||
<ha-svg-icon slot="icon" .path=${mdiPlus}></ha-svg-icon>
|
<ha-svg-icon slot="icon" .path=${mdiPlus}></ha-svg-icon>
|
||||||
</mwc-fab>
|
</ha-fab>
|
||||||
</hass-tabs-subpage>
|
</hass-tabs-subpage>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import "@material/mwc-fab";
|
import "../../../components/ha-fab";
|
||||||
import "@material/mwc-icon-button";
|
import "@material/mwc-icon-button";
|
||||||
import { mdiPlus, mdiHelpCircle } from "@mdi/js";
|
import { mdiPlus, mdiHelpCircle } from "@mdi/js";
|
||||||
import "@polymer/paper-tooltip/paper-tooltip";
|
import "@polymer/paper-tooltip/paper-tooltip";
|
||||||
@ -152,14 +152,14 @@ class HaSceneDashboard extends LitElement {
|
|||||||
<ha-svg-icon .path=${mdiHelpCircle}></ha-svg-icon>
|
<ha-svg-icon .path=${mdiHelpCircle}></ha-svg-icon>
|
||||||
</mwc-icon-button>
|
</mwc-icon-button>
|
||||||
<a href="/config/scene/edit/new" slot="fab">
|
<a href="/config/scene/edit/new" slot="fab">
|
||||||
<mwc-fab
|
<ha-fab
|
||||||
.label=${this.hass.localize(
|
.label=${this.hass.localize(
|
||||||
"ui.panel.config.scene.picker.add_scene"
|
"ui.panel.config.scene.picker.add_scene"
|
||||||
)}
|
)}
|
||||||
extended
|
extended
|
||||||
>
|
>
|
||||||
<ha-svg-icon slot="icon" .path=${mdiPlus}></ha-svg-icon>
|
<ha-svg-icon slot="icon" .path=${mdiPlus}></ha-svg-icon>
|
||||||
</mwc-fab>
|
</ha-fab>
|
||||||
</a>
|
</a>
|
||||||
</hass-tabs-subpage-data-table>
|
</hass-tabs-subpage-data-table>
|
||||||
`;
|
`;
|
||||||
|
@ -25,7 +25,7 @@ import "../../../components/device/ha-device-picker";
|
|||||||
import "../../../components/entity/ha-entities-picker";
|
import "../../../components/entity/ha-entities-picker";
|
||||||
import "../../../components/ha-card";
|
import "../../../components/ha-card";
|
||||||
import "../../../components/ha-icon-input";
|
import "../../../components/ha-icon-input";
|
||||||
import "@material/mwc-fab";
|
import "../../../components/ha-fab";
|
||||||
import {
|
import {
|
||||||
computeDeviceName,
|
computeDeviceName,
|
||||||
DeviceRegistryEntry,
|
DeviceRegistryEntry,
|
||||||
@ -403,7 +403,7 @@ export class HaSceneEditor extends SubscribeMixin(
|
|||||||
`
|
`
|
||||||
: ""}
|
: ""}
|
||||||
</div>
|
</div>
|
||||||
<mwc-fab
|
<ha-fab
|
||||||
slot="fab"
|
slot="fab"
|
||||||
.label=${this.hass.localize("ui.panel.config.scene.editor.save")}
|
.label=${this.hass.localize("ui.panel.config.scene.editor.save")}
|
||||||
extended
|
extended
|
||||||
@ -411,7 +411,7 @@ export class HaSceneEditor extends SubscribeMixin(
|
|||||||
class=${classMap({ dirty: this._dirty })}
|
class=${classMap({ dirty: this._dirty })}
|
||||||
>
|
>
|
||||||
<ha-svg-icon slot="icon" .path=${mdiContentSave}></ha-svg-icon>
|
<ha-svg-icon slot="icon" .path=${mdiContentSave}></ha-svg-icon>
|
||||||
</mwc-fab>
|
</ha-fab>
|
||||||
</hass-tabs-subpage>
|
</hass-tabs-subpage>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
@ -786,12 +786,12 @@ export class HaSceneEditor extends SubscribeMixin(
|
|||||||
span[slot="introduction"] a {
|
span[slot="introduction"] a {
|
||||||
color: var(--primary-color);
|
color: var(--primary-color);
|
||||||
}
|
}
|
||||||
mwc-fab {
|
ha-fab {
|
||||||
position: relative;
|
position: relative;
|
||||||
bottom: calc(-80px - env(safe-area-inset-bottom));
|
bottom: calc(-80px - env(safe-area-inset-bottom));
|
||||||
transition: bottom 0.3s;
|
transition: bottom 0.3s;
|
||||||
}
|
}
|
||||||
mwc-fab.dirty {
|
ha-fab.dirty {
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
}
|
}
|
||||||
`,
|
`,
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import "@material/mwc-fab";
|
import "../../../components/ha-fab";
|
||||||
import {
|
import {
|
||||||
mdiCheck,
|
mdiCheck,
|
||||||
mdiContentSave,
|
mdiContentSave,
|
||||||
@ -388,7 +388,7 @@ export class HaScriptEditor extends KeyboardShortcutMixin(LitElement) {
|
|||||||
`
|
`
|
||||||
: ``}
|
: ``}
|
||||||
</div>
|
</div>
|
||||||
<mwc-fab
|
<ha-fab
|
||||||
slot="fab"
|
slot="fab"
|
||||||
.label=${this.hass.localize(
|
.label=${this.hass.localize(
|
||||||
"ui.panel.config.script.editor.save_script"
|
"ui.panel.config.script.editor.save_script"
|
||||||
@ -400,7 +400,7 @@ export class HaScriptEditor extends KeyboardShortcutMixin(LitElement) {
|
|||||||
})}
|
})}
|
||||||
>
|
>
|
||||||
<ha-svg-icon slot="icon" .path=${mdiContentSave}></ha-svg-icon>
|
<ha-svg-icon slot="icon" .path=${mdiContentSave}></ha-svg-icon>
|
||||||
</mwc-fab>
|
</ha-fab>
|
||||||
</hass-tabs-subpage>
|
</hass-tabs-subpage>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
@ -690,12 +690,12 @@ export class HaScriptEditor extends KeyboardShortcutMixin(LitElement) {
|
|||||||
span[slot="introduction"] a {
|
span[slot="introduction"] a {
|
||||||
color: var(--primary-color);
|
color: var(--primary-color);
|
||||||
}
|
}
|
||||||
mwc-fab {
|
ha-fab {
|
||||||
position: relative;
|
position: relative;
|
||||||
bottom: calc(-80px - env(safe-area-inset-bottom));
|
bottom: calc(-80px - env(safe-area-inset-bottom));
|
||||||
transition: bottom 0.3s;
|
transition: bottom 0.3s;
|
||||||
}
|
}
|
||||||
mwc-fab.dirty {
|
ha-fab.dirty {
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
}
|
}
|
||||||
.selected_menu_item {
|
.selected_menu_item {
|
||||||
|
@ -16,7 +16,7 @@ import { fireEvent } from "../../../common/dom/fire_event";
|
|||||||
import { computeStateName } from "../../../common/entity/compute_state_name";
|
import { computeStateName } from "../../../common/entity/compute_state_name";
|
||||||
import { computeRTL } from "../../../common/util/compute_rtl";
|
import { computeRTL } from "../../../common/util/compute_rtl";
|
||||||
import { DataTableColumnContainer } from "../../../components/data-table/ha-data-table";
|
import { DataTableColumnContainer } from "../../../components/data-table/ha-data-table";
|
||||||
import "@material/mwc-fab";
|
import "../../../components/ha-fab";
|
||||||
import { triggerScript } from "../../../data/script";
|
import { triggerScript } from "../../../data/script";
|
||||||
import { showAlertDialog } from "../../../dialogs/generic/show-dialog-box";
|
import { showAlertDialog } from "../../../dialogs/generic/show-dialog-box";
|
||||||
import "../../../layouts/hass-tabs-subpage-data-table";
|
import "../../../layouts/hass-tabs-subpage-data-table";
|
||||||
@ -147,7 +147,7 @@ class HaScriptPicker extends LitElement {
|
|||||||
<ha-svg-icon .path=${mdiHelpCircle}></ha-svg-icon>
|
<ha-svg-icon .path=${mdiHelpCircle}></ha-svg-icon>
|
||||||
</mwc-icon-button>
|
</mwc-icon-button>
|
||||||
<a href="/config/script/edit/new" slot="fab">
|
<a href="/config/script/edit/new" slot="fab">
|
||||||
<mwc-fab
|
<ha-fab
|
||||||
?is-wide=${this.isWide}
|
?is-wide=${this.isWide}
|
||||||
?narrow=${this.narrow}
|
?narrow=${this.narrow}
|
||||||
.label=${this.hass.localize(
|
.label=${this.hass.localize(
|
||||||
@ -157,7 +157,7 @@ class HaScriptPicker extends LitElement {
|
|||||||
?rtl=${computeRTL(this.hass)}
|
?rtl=${computeRTL(this.hass)}
|
||||||
>
|
>
|
||||||
<ha-svg-icon slot="icon" .path=${mdiPlus}></ha-svg-icon>
|
<ha-svg-icon slot="icon" .path=${mdiPlus}></ha-svg-icon>
|
||||||
</mwc-fab>
|
</ha-fab>
|
||||||
</a>
|
</a>
|
||||||
</hass-tabs-subpage-data-table>
|
</hass-tabs-subpage-data-table>
|
||||||
`;
|
`;
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import "@material/mwc-fab";
|
import "../../../components/ha-fab";
|
||||||
import "@material/mwc-icon-button";
|
import "@material/mwc-icon-button";
|
||||||
import {
|
import {
|
||||||
mdiCog,
|
mdiCog,
|
||||||
@ -207,14 +207,14 @@ export class HaConfigTags extends SubscribeMixin(LitElement) {
|
|||||||
<mwc-icon-button slot="toolbar-icon" @click=${this._showHelp}>
|
<mwc-icon-button slot="toolbar-icon" @click=${this._showHelp}>
|
||||||
<ha-svg-icon .path=${mdiHelpCircle}></ha-svg-icon>
|
<ha-svg-icon .path=${mdiHelpCircle}></ha-svg-icon>
|
||||||
</mwc-icon-button>
|
</mwc-icon-button>
|
||||||
<mwc-fab
|
<ha-fab
|
||||||
slot="fab"
|
slot="fab"
|
||||||
.label=${this.hass.localize("ui.panel.config.tags.add_tag")}
|
.label=${this.hass.localize("ui.panel.config.tags.add_tag")}
|
||||||
extended
|
extended
|
||||||
@click=${this._addTag}
|
@click=${this._addTag}
|
||||||
>
|
>
|
||||||
<ha-svg-icon slot="icon" .path=${mdiPlus}></ha-svg-icon>
|
<ha-svg-icon slot="icon" .path=${mdiPlus}></ha-svg-icon>
|
||||||
</mwc-fab>
|
</ha-fab>
|
||||||
</hass-tabs-subpage-data-table>
|
</hass-tabs-subpage-data-table>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
@ -98,7 +98,7 @@ export class DialogAddUser extends LitElement {
|
|||||||
class="name"
|
class="name"
|
||||||
name="name"
|
name="name"
|
||||||
.label=${this.hass.localize(
|
.label=${this.hass.localize(
|
||||||
"ui.panel.config.users.add_user.name"
|
"ui.panel.config.users.editor.name"
|
||||||
)}
|
)}
|
||||||
.value=${this._name}
|
.value=${this._name}
|
||||||
required
|
required
|
||||||
@ -113,7 +113,7 @@ export class DialogAddUser extends LitElement {
|
|||||||
class="username"
|
class="username"
|
||||||
name="username"
|
name="username"
|
||||||
.label=${this.hass.localize(
|
.label=${this.hass.localize(
|
||||||
"ui.panel.config.users.add_user.username"
|
"ui.panel.config.users.editor.username"
|
||||||
)}
|
)}
|
||||||
.value=${this._username}
|
.value=${this._username}
|
||||||
required
|
required
|
||||||
@ -241,7 +241,7 @@ export class DialogAddUser extends LitElement {
|
|||||||
user = userResponse.user;
|
user = userResponse.user;
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
this._loading = false;
|
this._loading = false;
|
||||||
this._error = err.code;
|
this._error = err.message;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -255,10 +255,11 @@ export class DialogAddUser extends LitElement {
|
|||||||
} catch (err) {
|
} catch (err) {
|
||||||
await deleteUser(this.hass, user.id);
|
await deleteUser(this.hass, user.id);
|
||||||
this._loading = false;
|
this._loading = false;
|
||||||
this._error = err.code;
|
this._error = err.message;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
user.username = this._username;
|
||||||
this._params!.userAddedCallback(user);
|
this._params!.userAddedCallback(user);
|
||||||
this._close();
|
this._close();
|
||||||
}
|
}
|
||||||
|
@ -13,6 +13,7 @@ import {
|
|||||||
} from "lit-element";
|
} from "lit-element";
|
||||||
import { computeRTLDirection } from "../../../common/util/compute_rtl";
|
import { computeRTLDirection } from "../../../common/util/compute_rtl";
|
||||||
import { createCloseHeading } from "../../../components/ha-dialog";
|
import { createCloseHeading } from "../../../components/ha-dialog";
|
||||||
|
import "../../../components/ha-help-tooltip";
|
||||||
import "../../../components/ha-formfield";
|
import "../../../components/ha-formfield";
|
||||||
import "../../../components/ha-switch";
|
import "../../../components/ha-switch";
|
||||||
import { adminChangePassword } from "../../../data/auth";
|
import { adminChangePassword } from "../../../data/auth";
|
||||||
@ -37,6 +38,8 @@ class DialogUserDetail extends LitElement {
|
|||||||
|
|
||||||
@internalProperty() private _isAdmin?: boolean;
|
@internalProperty() private _isAdmin?: boolean;
|
||||||
|
|
||||||
|
@internalProperty() private _isActive?: boolean;
|
||||||
|
|
||||||
@internalProperty() private _error?: string;
|
@internalProperty() private _error?: string;
|
||||||
|
|
||||||
@internalProperty() private _params?: UserDetailDialogParams;
|
@internalProperty() private _params?: UserDetailDialogParams;
|
||||||
@ -48,6 +51,7 @@ class DialogUserDetail extends LitElement {
|
|||||||
this._error = undefined;
|
this._error = undefined;
|
||||||
this._name = params.entry.name || "";
|
this._name = params.entry.name || "";
|
||||||
this._isAdmin = params.entry.group_ids.includes(SYSTEM_GROUP_ID_ADMIN);
|
this._isAdmin = params.entry.group_ids.includes(SYSTEM_GROUP_ID_ADMIN);
|
||||||
|
this._isActive = params.entry.is_active;
|
||||||
await this.updateComplete;
|
await this.updateComplete;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -67,7 +71,10 @@ class DialogUserDetail extends LitElement {
|
|||||||
<div>
|
<div>
|
||||||
${this._error ? html` <div class="error">${this._error}</div> ` : ""}
|
${this._error ? html` <div class="error">${this._error}</div> ` : ""}
|
||||||
<div class="secondary">
|
<div class="secondary">
|
||||||
${this.hass.localize("ui.panel.config.users.editor.id")}: ${user.id}
|
${this.hass.localize("ui.panel.config.users.editor.id")}:
|
||||||
|
${user.id}<br />
|
||||||
|
${this.hass.localize("ui.panel.config.users.editor.username")}:
|
||||||
|
${user.username}
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
${user.is_owner
|
${user.is_owner
|
||||||
@ -88,15 +95,6 @@ class DialogUserDetail extends LitElement {
|
|||||||
</span>
|
</span>
|
||||||
`
|
`
|
||||||
: ""}
|
: ""}
|
||||||
${user.is_active
|
|
||||||
? html`
|
|
||||||
<span class="state"
|
|
||||||
>${this.hass.localize(
|
|
||||||
"ui.panel.config.users.editor.active"
|
|
||||||
)}</span
|
|
||||||
>
|
|
||||||
`
|
|
||||||
: ""}
|
|
||||||
</div>
|
</div>
|
||||||
<div class="form">
|
<div class="form">
|
||||||
<paper-input
|
<paper-input
|
||||||
@ -107,8 +105,11 @@ class DialogUserDetail extends LitElement {
|
|||||||
"ui.panel.config.users.editor.name"
|
"ui.panel.config.users.editor.name"
|
||||||
)}"
|
)}"
|
||||||
></paper-input>
|
></paper-input>
|
||||||
|
<div class="row">
|
||||||
<ha-formfield
|
<ha-formfield
|
||||||
.label=${this.hass.localize("ui.panel.config.users.editor.admin")}
|
.label=${this.hass.localize(
|
||||||
|
"ui.panel.config.users.editor.admin"
|
||||||
|
)}
|
||||||
.dir=${computeRTLDirection(this.hass)}
|
.dir=${computeRTLDirection(this.hass)}
|
||||||
>
|
>
|
||||||
<ha-switch
|
<ha-switch
|
||||||
@ -118,6 +119,7 @@ class DialogUserDetail extends LitElement {
|
|||||||
>
|
>
|
||||||
</ha-switch>
|
</ha-switch>
|
||||||
</ha-formfield>
|
</ha-formfield>
|
||||||
|
</div>
|
||||||
${!this._isAdmin
|
${!this._isAdmin
|
||||||
? html`
|
? html`
|
||||||
<br />
|
<br />
|
||||||
@ -126,6 +128,27 @@ class DialogUserDetail extends LitElement {
|
|||||||
)}
|
)}
|
||||||
`
|
`
|
||||||
: ""}
|
: ""}
|
||||||
|
<div class="row">
|
||||||
|
<ha-formfield
|
||||||
|
.label=${this.hass.localize(
|
||||||
|
"ui.panel.config.users.editor.active"
|
||||||
|
)}
|
||||||
|
.dir=${computeRTLDirection(this.hass)}
|
||||||
|
>
|
||||||
|
<ha-switch
|
||||||
|
.disabled=${user.system_generated || user.is_owner}
|
||||||
|
.checked=${this._isActive}
|
||||||
|
@change=${this._activeChanged}
|
||||||
|
>
|
||||||
|
</ha-switch>
|
||||||
|
</ha-formfield>
|
||||||
|
<ha-help-tooltip
|
||||||
|
.label=${this.hass.localize(
|
||||||
|
"ui.panel.config.users.editor.active_tooltip"
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
</ha-help-tooltip>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -189,11 +212,16 @@ class DialogUserDetail extends LitElement {
|
|||||||
this._isAdmin = ev.target.checked;
|
this._isAdmin = ev.target.checked;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async _activeChanged(ev): Promise<void> {
|
||||||
|
this._isActive = ev.target.checked;
|
||||||
|
}
|
||||||
|
|
||||||
private async _updateEntry() {
|
private async _updateEntry() {
|
||||||
this._submitting = true;
|
this._submitting = true;
|
||||||
try {
|
try {
|
||||||
await this._params!.updateEntry({
|
await this._params!.updateEntry({
|
||||||
name: this._name.trim(),
|
name: this._name.trim(),
|
||||||
|
is_active: this._isActive,
|
||||||
group_ids: [
|
group_ids: [
|
||||||
this._isAdmin ? SYSTEM_GROUP_ID_ADMIN : SYSTEM_GROUP_ID_USER,
|
this._isAdmin ? SYSTEM_GROUP_ID_ADMIN : SYSTEM_GROUP_ID_USER,
|
||||||
],
|
],
|
||||||
@ -290,8 +318,13 @@ class DialogUserDetail extends LitElement {
|
|||||||
.state:not(:first-child) {
|
.state:not(:first-child) {
|
||||||
margin-left: 8px;
|
margin-left: 8px;
|
||||||
}
|
}
|
||||||
ha-switch {
|
.row {
|
||||||
margin-top: 8px;
|
display: flex;
|
||||||
|
padding: 8px 0;
|
||||||
|
}
|
||||||
|
ha-help-tooltip {
|
||||||
|
margin-left: 4px;
|
||||||
|
position: relative;
|
||||||
}
|
}
|
||||||
`,
|
`,
|
||||||
];
|
];
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import "@material/mwc-fab";
|
import "../../../components/ha-fab";
|
||||||
import { mdiPlus } from "@mdi/js";
|
import { mdiPlus } from "@mdi/js";
|
||||||
import {
|
import {
|
||||||
customElement,
|
customElement,
|
||||||
@ -35,18 +35,40 @@ export class HaConfigUsers extends LitElement {
|
|||||||
@property() public route!: Route;
|
@property() public route!: Route;
|
||||||
|
|
||||||
private _columns = memoizeOne(
|
private _columns = memoizeOne(
|
||||||
(_language): DataTableColumnContainer => {
|
(narrow: boolean, _language): DataTableColumnContainer => {
|
||||||
return {
|
const columns: DataTableColumnContainer = {
|
||||||
name: {
|
name: {
|
||||||
title: this.hass.localize(
|
title: this.hass.localize(
|
||||||
"ui.panel.config.users.picker.headers.name"
|
"ui.panel.config.users.picker.headers.name"
|
||||||
),
|
),
|
||||||
sortable: true,
|
sortable: true,
|
||||||
filterable: true,
|
filterable: true,
|
||||||
|
width: "25%",
|
||||||
direction: "asc",
|
direction: "asc",
|
||||||
grows: true,
|
grows: true,
|
||||||
template: (name) => html`
|
template: (name, user: any) =>
|
||||||
${name ||
|
narrow
|
||||||
|
? html` ${name}<br />
|
||||||
|
<div class="secondary">
|
||||||
|
${user.username} |
|
||||||
|
${this.hass.localize(`groups.${user.group_ids[0]}`)}
|
||||||
|
</div>`
|
||||||
|
: html` ${name ||
|
||||||
|
this.hass!.localize(
|
||||||
|
"ui.panel.config.users.editor.unnamed_user"
|
||||||
|
)}`,
|
||||||
|
},
|
||||||
|
username: {
|
||||||
|
title: this.hass.localize(
|
||||||
|
"ui.panel.config.users.picker.headers.username"
|
||||||
|
),
|
||||||
|
sortable: true,
|
||||||
|
filterable: true,
|
||||||
|
width: "20%",
|
||||||
|
direction: "asc",
|
||||||
|
hidden: narrow,
|
||||||
|
template: (username) => html`
|
||||||
|
${username ||
|
||||||
this.hass!.localize("ui.panel.config.users.editor.unnamed_user")}
|
this.hass!.localize("ui.panel.config.users.editor.unnamed_user")}
|
||||||
`,
|
`,
|
||||||
},
|
},
|
||||||
@ -56,26 +78,38 @@ export class HaConfigUsers extends LitElement {
|
|||||||
),
|
),
|
||||||
sortable: true,
|
sortable: true,
|
||||||
filterable: true,
|
filterable: true,
|
||||||
width: "30%",
|
width: "20%",
|
||||||
|
direction: "asc",
|
||||||
|
hidden: narrow,
|
||||||
template: (groupIds) => html`
|
template: (groupIds) => html`
|
||||||
${this.hass.localize(`groups.${groupIds[0]}`)}
|
${this.hass.localize(`groups.${groupIds[0]}`)}
|
||||||
`,
|
`,
|
||||||
},
|
},
|
||||||
|
is_active: {
|
||||||
|
title: this.hass.localize(
|
||||||
|
"ui.panel.config.users.picker.headers.is_active"
|
||||||
|
),
|
||||||
|
type: "icon",
|
||||||
|
sortable: true,
|
||||||
|
filterable: true,
|
||||||
|
width: "80px",
|
||||||
|
template: (is_active) =>
|
||||||
|
is_active ? html`<ha-icon icon="hass:check"> </ha-icon>` : "",
|
||||||
|
},
|
||||||
system_generated: {
|
system_generated: {
|
||||||
title: this.hass.localize(
|
title: this.hass.localize(
|
||||||
"ui.panel.config.users.picker.headers.system"
|
"ui.panel.config.users.picker.headers.system"
|
||||||
),
|
),
|
||||||
type: "icon",
|
type: "icon",
|
||||||
width: "80px",
|
|
||||||
sortable: true,
|
sortable: true,
|
||||||
filterable: true,
|
filterable: true,
|
||||||
template: (generated) => html`
|
width: "160px",
|
||||||
${generated
|
template: (generated) =>
|
||||||
? html` <ha-icon icon="hass:check-circle-outline"></ha-icon> `
|
generated ? html`<ha-icon icon="hass:check"> </ha-icon>` : "",
|
||||||
: ""}
|
|
||||||
`,
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
return columns;
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -92,26 +126,32 @@ export class HaConfigUsers extends LitElement {
|
|||||||
.route=${this.route}
|
.route=${this.route}
|
||||||
backPath="/config"
|
backPath="/config"
|
||||||
.tabs=${configSections.persons}
|
.tabs=${configSections.persons}
|
||||||
.columns=${this._columns(this.hass.language)}
|
.columns=${this._columns(this.narrow, this.hass.language)}
|
||||||
.data=${this._users}
|
.data=${this._users}
|
||||||
@row-click=${this._editUser}
|
@row-click=${this._editUser}
|
||||||
hasFab
|
hasFab
|
||||||
clickable
|
clickable
|
||||||
>
|
>
|
||||||
<mwc-fab
|
<ha-fab
|
||||||
slot="fab"
|
slot="fab"
|
||||||
.label=${this.hass.localize("ui.panel.config.users.picker.add_user")}
|
.label=${this.hass.localize("ui.panel.config.users.picker.add_user")}
|
||||||
extended
|
extended
|
||||||
@click=${this._addUser}
|
@click=${this._addUser}
|
||||||
>
|
>
|
||||||
<ha-svg-icon slot="icon" .path=${mdiPlus}></ha-svg-icon>
|
<ha-svg-icon slot="icon" .path=${mdiPlus}></ha-svg-icon>
|
||||||
</mwc-fab>
|
</ha-fab>
|
||||||
</hass-tabs-subpage-data-table>
|
</hass-tabs-subpage-data-table>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async _fetchUsers() {
|
private async _fetchUsers() {
|
||||||
this._users = await fetchUsers(this.hass);
|
this._users = await fetchUsers(this.hass);
|
||||||
|
|
||||||
|
this._users.forEach(function (user) {
|
||||||
|
if (user.is_owner) {
|
||||||
|
user.group_ids.unshift("owner");
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private _editUser(ev: HASSDomEvent<RowClickedEvent>) {
|
private _editUser(ev: HASSDomEvent<RowClickedEvent>) {
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import "@material/mwc-fab";
|
import "../../../components/ha-fab";
|
||||||
import "@material/mwc-icon-button";
|
import "@material/mwc-icon-button";
|
||||||
import { mdiPencil, mdiPencilOff, mdiPlus } from "@mdi/js";
|
import { mdiPencil, mdiPencilOff, mdiPlus } from "@mdi/js";
|
||||||
import "@polymer/paper-item/paper-icon-item";
|
import "@polymer/paper-item/paper-icon-item";
|
||||||
@ -255,14 +255,14 @@ export class HaConfigZone extends SubscribeMixin(LitElement) {
|
|||||||
</div>
|
</div>
|
||||||
`
|
`
|
||||||
: ""}
|
: ""}
|
||||||
<mwc-fab
|
<ha-fab
|
||||||
slot="fab"
|
slot="fab"
|
||||||
.label=${hass.localize("ui.panel.config.zone.add_zone")}
|
.label=${hass.localize("ui.panel.config.zone.add_zone")}
|
||||||
extended
|
extended
|
||||||
@click=${this._createZone}
|
@click=${this._createZone}
|
||||||
>
|
>
|
||||||
<ha-svg-icon slot="icon" .path=${mdiPlus}></ha-svg-icon>
|
<ha-svg-icon slot="icon" .path=${mdiPlus}></ha-svg-icon>
|
||||||
</mwc-fab>
|
</ha-fab>
|
||||||
</hass-tabs-subpage>
|
</hass-tabs-subpage>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
@ -109,7 +109,7 @@ class PanelDeveloperTools extends LitElement {
|
|||||||
ha-tabs {
|
ha-tabs {
|
||||||
margin-left: max(env(safe-area-inset-left), 24px);
|
margin-left: max(env(safe-area-inset-left), 24px);
|
||||||
margin-right: max(env(safe-area-inset-right), 24px);
|
margin-right: max(env(safe-area-inset-right), 24px);
|
||||||
--paper-tabs-selection-bar-color: #fff;
|
--paper-tabs-selection-bar-color: var(--text-primary-color, #fff);
|
||||||
text-transform: uppercase;
|
text-transform: uppercase;
|
||||||
}
|
}
|
||||||
`,
|
`,
|
||||||
|
@ -109,7 +109,7 @@ class HuiEntitiesCard extends LitElement implements LovelaceCard {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public setConfig(config: EntitiesCardConfig): void {
|
public setConfig(config: EntitiesCardConfig): void {
|
||||||
if (!config || !config.entities.length) {
|
if (!config.entities || !Array.isArray(config.entities)) {
|
||||||
throw new Error("Entities must be specified");
|
throw new Error("Entities must be specified");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -16,7 +16,6 @@ import "../../../components/state-history-charts";
|
|||||||
import { CacheConfig, getRecentWithCache } from "../../../data/cached-history";
|
import { CacheConfig, getRecentWithCache } from "../../../data/cached-history";
|
||||||
import { HistoryResult } from "../../../data/history";
|
import { HistoryResult } from "../../../data/history";
|
||||||
import { HomeAssistant } from "../../../types";
|
import { HomeAssistant } from "../../../types";
|
||||||
import { findEntities } from "../common/find-entites";
|
|
||||||
import { hasConfigOrEntitiesChanged } from "../common/has-changed";
|
import { hasConfigOrEntitiesChanged } from "../common/has-changed";
|
||||||
import { processConfigEntities } from "../common/process-config-entities";
|
import { processConfigEntities } from "../common/process-config-entities";
|
||||||
import { EntityConfig } from "../entity-rows/types";
|
import { EntityConfig } from "../entity-rows/types";
|
||||||
@ -30,22 +29,9 @@ export class HuiHistoryGraphCard extends LitElement implements LovelaceCard {
|
|||||||
return document.createElement("hui-history-graph-card-editor");
|
return document.createElement("hui-history-graph-card-editor");
|
||||||
}
|
}
|
||||||
|
|
||||||
public static getStubConfig(
|
public static getStubConfig(): HistoryGraphCardConfig {
|
||||||
hass: HomeAssistant,
|
// Hard coded to sun.sun to prevent high server load when it would pick an entity with a lot of state changes
|
||||||
entities: string[],
|
return { type: "history-graph", entities: ["sun.sun"] };
|
||||||
entitiesFallback: string[]
|
|
||||||
): HistoryGraphCardConfig {
|
|
||||||
const includeDomains = ["sensor"];
|
|
||||||
const maxEntities = 1;
|
|
||||||
const foundEntities = findEntities(
|
|
||||||
hass,
|
|
||||||
maxEntities,
|
|
||||||
entities,
|
|
||||||
entitiesFallback,
|
|
||||||
includeDomains
|
|
||||||
);
|
|
||||||
|
|
||||||
return { type: "history-graph", entities: foundEntities };
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@property({ attribute: false }) public hass?: HomeAssistant;
|
@property({ attribute: false }) public hass?: HomeAssistant;
|
||||||
@ -71,12 +57,12 @@ export class HuiHistoryGraphCard extends LitElement implements LovelaceCard {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public setConfig(config: HistoryGraphCardConfig): void {
|
public setConfig(config: HistoryGraphCardConfig): void {
|
||||||
if (!config.entities.length) {
|
if (!config.entities || !Array.isArray(config.entities)) {
|
||||||
throw new Error("Entities must be specified");
|
throw new Error("Entities need to be an array");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (config.entities && !Array.isArray(config.entities)) {
|
if (!config.entities.length) {
|
||||||
throw new Error("Entities need to be an array");
|
throw new Error("You must include at least one entity");
|
||||||
}
|
}
|
||||||
|
|
||||||
this._config = config;
|
this._config = config;
|
||||||
|
@ -29,7 +29,7 @@ class HuiHorizontalStackCard extends HuiStackCard {
|
|||||||
}
|
}
|
||||||
#root > * {
|
#root > * {
|
||||||
flex: 1 1 0;
|
flex: 1 1 0;
|
||||||
margin: 0 4px;
|
margin: var(--horizontal-stack-card-margin, var(--stack-card-margin, 0 4px));
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
}
|
}
|
||||||
#root > *:first-child {
|
#root > *:first-child {
|
||||||
|
@ -246,14 +246,8 @@ export class HuiMediaControlCard extends LitElement implements LovelaceCard {
|
|||||||
${!isUnavailable &&
|
${!isUnavailable &&
|
||||||
(mediaDescription || stateObj.attributes.media_title || showControls)
|
(mediaDescription || stateObj.attributes.media_title || showControls)
|
||||||
? html`
|
? html`
|
||||||
<div
|
<div>
|
||||||
class="title-controls"
|
<div class="title-controls">
|
||||||
style=${styleMap({
|
|
||||||
paddingRight: isOffState
|
|
||||||
? "0"
|
|
||||||
: `${this._cardHeight - 40}px`,
|
|
||||||
})}
|
|
||||||
>
|
|
||||||
${!mediaDescription && !stateObj.attributes.media_title
|
${!mediaDescription && !stateObj.attributes.media_title
|
||||||
? ""
|
? ""
|
||||||
: html`
|
: html`
|
||||||
@ -318,6 +312,7 @@ export class HuiMediaControlCard extends LitElement implements LovelaceCard {
|
|||||||
@click=${this._handleSeek}
|
@click=${this._handleSeek}
|
||||||
></paper-progress>
|
></paper-progress>
|
||||||
`}
|
`}
|
||||||
|
</div>
|
||||||
`
|
`
|
||||||
: ""}
|
: ""}
|
||||||
</div>
|
</div>
|
||||||
@ -635,6 +630,11 @@ export class HuiMediaControlCard extends LitElement implements LovelaceCard {
|
|||||||
.player {
|
.player {
|
||||||
position: relative;
|
position: relative;
|
||||||
padding: 16px;
|
padding: 16px;
|
||||||
|
height: 100%;
|
||||||
|
box-sizing: border-box;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: space-between;
|
||||||
color: var(--text-primary-color);
|
color: var(--text-primary-color);
|
||||||
transition-property: color, padding;
|
transition-property: color, padding;
|
||||||
transition-duration: 0.4s;
|
transition-duration: 0.4s;
|
||||||
@ -671,7 +671,7 @@ export class HuiMediaControlCard extends LitElement implements LovelaceCard {
|
|||||||
|
|
||||||
mwc-icon-button.browse-media {
|
mwc-icon-button.browse-media {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
right: 0;
|
right: 4px;
|
||||||
--mdc-icon-size: 24px;
|
--mdc-icon-size: 24px;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -693,7 +693,7 @@ export class HuiMediaControlCard extends LitElement implements LovelaceCard {
|
|||||||
.more-info {
|
.more-info {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 4px;
|
top: 4px;
|
||||||
right: 0px;
|
right: 4px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.media-info {
|
.media-info {
|
||||||
|
@ -29,7 +29,7 @@ class HuiVerticalStackCard extends HuiStackCard {
|
|||||||
height: 100%;
|
height: 100%;
|
||||||
}
|
}
|
||||||
#root > * {
|
#root > * {
|
||||||
margin: 4px 0 4px 0;
|
margin: var(--vertical-stack-card-margin, var(--stack-card-margin, 4px 0));
|
||||||
}
|
}
|
||||||
#root > *:first-child {
|
#root > *:first-child {
|
||||||
margin-top: 0;
|
margin-top: 0;
|
||||||
|
@ -72,7 +72,7 @@ class HuiMarquee extends LitElement {
|
|||||||
display: flex;
|
display: flex;
|
||||||
position: relative;
|
position: relative;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
height: 1em;
|
height: 1.2em;
|
||||||
contain: strict;
|
contain: strict;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -353,11 +353,9 @@ export class HuiCardPicker extends LitElement {
|
|||||||
max-width: 500px;
|
max-width: 500px;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
border-radius: 4px;
|
border-radius: var(--ha-card-border-radius, 4px);
|
||||||
border: 1px solid var(--divider-color);
|
|
||||||
background: var(--primary-background-color, #fafafa);
|
background: var(--primary-background-color, #fafafa);
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
box-sizing: border-box;
|
|
||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -375,7 +373,6 @@ export class HuiCardPicker extends LitElement {
|
|||||||
--ha-card-background,
|
--ha-card-background,
|
||||||
var(--card-background-color, white)
|
var(--card-background-color, white)
|
||||||
);
|
);
|
||||||
border-radius: 0 0 4px 4px;
|
|
||||||
border-bottom: 1px solid var(--divider-color);
|
border-bottom: 1px solid var(--divider-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -408,6 +405,10 @@ export class HuiCardPicker extends LitElement {
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
z-index: 1;
|
z-index: 1;
|
||||||
|
box-sizing: border-box;
|
||||||
|
border: var(--ha-card-border-width, 1px) solid
|
||||||
|
var(--ha-card-border-color, var(--divider-color));
|
||||||
|
border-radius: var(--ha-card-border-radius, 4px);
|
||||||
}
|
}
|
||||||
|
|
||||||
.manual {
|
.manual {
|
||||||
|
@ -450,6 +450,10 @@ export class HuiDialogEditCard extends LitElement
|
|||||||
}
|
}
|
||||||
.element-preview {
|
.element-preview {
|
||||||
position: relative;
|
position: relative;
|
||||||
|
height: max-content;
|
||||||
|
background: var(--primary-background-color);
|
||||||
|
padding: 4px;
|
||||||
|
border-radius: 4px;
|
||||||
}
|
}
|
||||||
.element-preview ha-circular-progress {
|
.element-preview ha-circular-progress {
|
||||||
top: 50%;
|
top: 50%;
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import "@material/mwc-fab";
|
import "../../../../components/ha-fab";
|
||||||
import {
|
import {
|
||||||
css,
|
css,
|
||||||
CSSResult,
|
CSSResult,
|
||||||
@ -108,13 +108,13 @@ export class HuiUnusedEntities extends LitElement {
|
|||||||
selected: this._selectedEntities.length,
|
selected: this._selectedEntities.length,
|
||||||
})}"
|
})}"
|
||||||
>
|
>
|
||||||
<mwc-fab
|
<ha-fab
|
||||||
.label=${this.hass.localize("ui.panel.lovelace.editor.edit_card.add")}
|
.label=${this.hass.localize("ui.panel.lovelace.editor.edit_card.add")}
|
||||||
extended
|
extended
|
||||||
@click=${this._addToLovelaceView}
|
@click=${this._addToLovelaceView}
|
||||||
>
|
>
|
||||||
<ha-svg-icon slot="icon" .path=${mdiPlus}></ha-svg-icon>
|
<ha-svg-icon slot="icon" .path=${mdiPlus}></ha-svg-icon>
|
||||||
</mwc-fab>
|
</ha-fab>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
@ -189,12 +189,12 @@ export class HuiUnusedEntities extends LitElement {
|
|||||||
padding-right: 16px;
|
padding-right: 16px;
|
||||||
padding-left: calc(16px + env(safe-area-inset-left));
|
padding-left: calc(16px + env(safe-area-inset-left));
|
||||||
}
|
}
|
||||||
mwc-fab {
|
ha-fab {
|
||||||
position: relative;
|
position: relative;
|
||||||
bottom: calc(-80px - env(safe-area-inset-bottom));
|
bottom: calc(-80px - env(safe-area-inset-bottom));
|
||||||
transition: bottom 0.3s;
|
transition: bottom 0.3s;
|
||||||
}
|
}
|
||||||
.fab.selected mwc-fab {
|
.fab.selected ha-fab {
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
@ -25,7 +25,7 @@ export class HuiPictureHeaderFooter extends LitElement
|
|||||||
public static getStubConfig(): Record<string, unknown> {
|
public static getStubConfig(): Record<string, unknown> {
|
||||||
return {
|
return {
|
||||||
image:
|
image:
|
||||||
"https://www.home-assistant.io/images/merchandise/shirt-frontpage.png",
|
"https://www.home-assistant.io/images/lovelace/header-footer/balloons-header.png",
|
||||||
tap_action: { action: "none" },
|
tap_action: { action: "none" },
|
||||||
hold_action: { action: "none" },
|
hold_action: { action: "none" },
|
||||||
};
|
};
|
||||||
|
@ -793,10 +793,6 @@ class HUIRoot extends LitElement {
|
|||||||
|
|
||||||
ha-app-layout {
|
ha-app-layout {
|
||||||
min-height: 100%;
|
min-height: 100%;
|
||||||
background: var(
|
|
||||||
--lovelace-background,
|
|
||||||
var(--primary-background-color)
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
ha-tabs {
|
ha-tabs {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
@ -884,6 +880,12 @@ class HUIRoot extends LitElement {
|
|||||||
.menu-link {
|
.menu-link {
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
}
|
}
|
||||||
|
hui-view {
|
||||||
|
background: var(
|
||||||
|
--lovelace-background,
|
||||||
|
var(--primary-background-color)
|
||||||
|
);
|
||||||
|
}
|
||||||
`,
|
`,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
@ -57,7 +57,8 @@ class HuiAttributeRow extends LitElement implements LovelaceRow {
|
|||||||
return html`
|
return html`
|
||||||
<hui-generic-entity-row .hass=${this.hass} .config=${this._config}>
|
<hui-generic-entity-row .hass=${this.hass} .config=${this._config}>
|
||||||
<div>
|
<div>
|
||||||
${this._config.prefix} ${attribute || "-"} ${this._config.suffix}
|
${this._config.prefix} ${attribute ?? "-"}
|
||||||
|
${this._config.suffix}
|
||||||
</div>
|
</div>
|
||||||
</hui-generic-entity-row>
|
</hui-generic-entity-row>
|
||||||
`;
|
`;
|
||||||
|
@ -1,3 +1,3 @@
|
|||||||
// hui-view dependencies for when in edit mode.
|
// hui-view dependencies for when in edit mode.
|
||||||
import "@material/mwc-fab";
|
import "../../../components/ha-fab";
|
||||||
import "../components/hui-card-options";
|
import "../components/hui-card-options";
|
||||||
|
@ -83,7 +83,7 @@ export class MasonryView extends LitElement implements LovelaceViewElement {
|
|||||||
<div id="columns"></div>
|
<div id="columns"></div>
|
||||||
${this.lovelace?.editMode
|
${this.lovelace?.editMode
|
||||||
? html`
|
? html`
|
||||||
<mwc-fab
|
<ha-fab
|
||||||
.label=${this.hass!.localize(
|
.label=${this.hass!.localize(
|
||||||
"ui.panel.lovelace.editor.edit_card.add"
|
"ui.panel.lovelace.editor.edit_card.add"
|
||||||
)}
|
)}
|
||||||
@ -94,7 +94,7 @@ export class MasonryView extends LitElement implements LovelaceViewElement {
|
|||||||
})}
|
})}
|
||||||
>
|
>
|
||||||
<ha-svg-icon slot="icon" .path=${mdiPlus}></ha-svg-icon>
|
<ha-svg-icon slot="icon" .path=${mdiPlus}></ha-svg-icon>
|
||||||
</mwc-fab>
|
</ha-fab>
|
||||||
`
|
`
|
||||||
: ""}
|
: ""}
|
||||||
`;
|
`;
|
||||||
@ -293,10 +293,10 @@ export class MasonryView extends LitElement implements LovelaceViewElement {
|
|||||||
|
|
||||||
.column > * {
|
.column > * {
|
||||||
display: block;
|
display: block;
|
||||||
margin: 4px 4px 8px;
|
margin: var(--masonry-view-card-margin, 4px 4px 8px);
|
||||||
}
|
}
|
||||||
|
|
||||||
mwc-fab {
|
ha-fab {
|
||||||
position: sticky;
|
position: sticky;
|
||||||
float: right;
|
float: right;
|
||||||
right: calc(16px + env(safe-area-inset-right));
|
right: calc(16px + env(safe-area-inset-right));
|
||||||
@ -304,7 +304,7 @@ export class MasonryView extends LitElement implements LovelaceViewElement {
|
|||||||
z-index: 1;
|
z-index: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
mwc-fab.rtl {
|
ha-fab.rtl {
|
||||||
float: left;
|
float: left;
|
||||||
right: auto;
|
right: auto;
|
||||||
left: calc(16px + env(safe-area-inset-left));
|
left: calc(16px + env(safe-area-inset-left));
|
||||||
|
@ -75,7 +75,7 @@ export class PanelView extends LitElement implements LovelaceViewElement {
|
|||||||
${this._card}
|
${this._card}
|
||||||
${this.lovelace?.editMode && this.cards.length === 0
|
${this.lovelace?.editMode && this.cards.length === 0
|
||||||
? html`
|
? html`
|
||||||
<mwc-fab
|
<ha-fab
|
||||||
.label=${this.hass!.localize(
|
.label=${this.hass!.localize(
|
||||||
"ui.panel.lovelace.editor.edit_card.add"
|
"ui.panel.lovelace.editor.edit_card.add"
|
||||||
)}
|
)}
|
||||||
@ -86,7 +86,7 @@ export class PanelView extends LitElement implements LovelaceViewElement {
|
|||||||
})}
|
})}
|
||||||
>
|
>
|
||||||
<ha-svg-icon slot="icon" .path=${mdiPlus}></ha-svg-icon>
|
<ha-svg-icon slot="icon" .path=${mdiPlus}></ha-svg-icon>
|
||||||
</mwc-fab>
|
</ha-fab>
|
||||||
`
|
`
|
||||||
: ""}
|
: ""}
|
||||||
`;
|
`;
|
||||||
@ -137,7 +137,7 @@ export class PanelView extends LitElement implements LovelaceViewElement {
|
|||||||
height: 100%;
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
mwc-fab {
|
ha-fab {
|
||||||
position: sticky;
|
position: sticky;
|
||||||
float: right;
|
float: right;
|
||||||
right: calc(16px + env(safe-area-inset-right));
|
right: calc(16px + env(safe-area-inset-right));
|
||||||
@ -145,7 +145,7 @@ export class PanelView extends LitElement implements LovelaceViewElement {
|
|||||||
z-index: 1;
|
z-index: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
mwc-fab.rtl {
|
ha-fab.rtl {
|
||||||
float: left;
|
float: left;
|
||||||
right: auto;
|
right: auto;
|
||||||
left: calc(16px + env(safe-area-inset-left));
|
left: calc(16px + env(safe-area-inset-left));
|
||||||
|
@ -1,154 +0,0 @@
|
|||||||
import "@material/mwc-button";
|
|
||||||
import "@polymer/paper-dialog/paper-dialog";
|
|
||||||
import "../../components/ha-circular-progress";
|
|
||||||
import { html } from "@polymer/polymer/lib/utils/html-tag";
|
|
||||||
/* eslint-plugin-disable lit */
|
|
||||||
import { PolymerElement } from "@polymer/polymer/polymer-element";
|
|
||||||
import "../../components/ha-card";
|
|
||||||
import LocalizeMixin from "../../mixins/localize-mixin";
|
|
||||||
import "../../styles/polymer-ha-style";
|
|
||||||
|
|
||||||
/*
|
|
||||||
* @appliesMixin LocalizeMixin
|
|
||||||
*/
|
|
||||||
class HaChangePasswordCard extends LocalizeMixin(PolymerElement) {
|
|
||||||
static get template() {
|
|
||||||
return html`
|
|
||||||
<style include="ha-style">
|
|
||||||
.error {
|
|
||||||
color: red;
|
|
||||||
}
|
|
||||||
.status {
|
|
||||||
color: var(--primary-color);
|
|
||||||
}
|
|
||||||
.error,
|
|
||||||
.status {
|
|
||||||
position: absolute;
|
|
||||||
top: -4px;
|
|
||||||
}
|
|
||||||
.currentPassword {
|
|
||||||
margin-top: -4px;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
<div>
|
|
||||||
<ha-card
|
|
||||||
header="[[localize('ui.panel.profile.change_password.header')]]"
|
|
||||||
>
|
|
||||||
<div class="card-content">
|
|
||||||
<template is="dom-if" if="[[_errorMsg]]">
|
|
||||||
<div class="error">[[_errorMsg]]</div>
|
|
||||||
</template>
|
|
||||||
<template is="dom-if" if="[[_statusMsg]]">
|
|
||||||
<div class="status">[[_statusMsg]]</div>
|
|
||||||
</template>
|
|
||||||
<paper-input
|
|
||||||
class="currentPassword"
|
|
||||||
label="[[localize('ui.panel.profile.change_password.current_password')]]"
|
|
||||||
type="password"
|
|
||||||
value="{{_currentPassword}}"
|
|
||||||
required
|
|
||||||
auto-validate
|
|
||||||
error-message="[[localize('ui.panel.profile.change_password.error_required')]]"
|
|
||||||
></paper-input>
|
|
||||||
<template is="dom-if" if="[[_currentPassword]]">
|
|
||||||
<paper-input
|
|
||||||
label="[[localize('ui.panel.profile.change_password.new_password')]]"
|
|
||||||
type="password"
|
|
||||||
value="{{_password1}}"
|
|
||||||
required
|
|
||||||
auto-validate
|
|
||||||
error-message="[[localize('ui.panel.profile.change_password.error_required')]]"
|
|
||||||
></paper-input>
|
|
||||||
<paper-input
|
|
||||||
label="[[localize('ui.panel.profile.change_password.confirm_new_password')]]"
|
|
||||||
type="password"
|
|
||||||
value="{{_password2}}"
|
|
||||||
required
|
|
||||||
auto-validate
|
|
||||||
error-message="[[localize('ui.panel.profile.change_password.error_required')]]"
|
|
||||||
></paper-input>
|
|
||||||
</template>
|
|
||||||
</div>
|
|
||||||
<div class="card-actions">
|
|
||||||
<template is="dom-if" if="[[_loading]]">
|
|
||||||
<div><ha-circular-progress active></ha-circular-progress></div>
|
|
||||||
</template>
|
|
||||||
<template is="dom-if" if="[[!_loading]]">
|
|
||||||
<mwc-button on-click="_changePassword"
|
|
||||||
>[[localize('ui.panel.profile.change_password.submit')]]</mwc-button
|
|
||||||
>
|
|
||||||
</template>
|
|
||||||
</div>
|
|
||||||
</ha-card>
|
|
||||||
</div>
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
|
|
||||||
static get properties() {
|
|
||||||
return {
|
|
||||||
hass: Object,
|
|
||||||
|
|
||||||
_loading: {
|
|
||||||
type: Boolean,
|
|
||||||
value: false,
|
|
||||||
},
|
|
||||||
|
|
||||||
// Error message when can't talk to server etc
|
|
||||||
_statusMsg: String,
|
|
||||||
_errorMsg: String,
|
|
||||||
|
|
||||||
_currentPassword: String,
|
|
||||||
_password1: String,
|
|
||||||
_password2: String,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
ready() {
|
|
||||||
super.ready();
|
|
||||||
this.addEventListener("keypress", (ev) => {
|
|
||||||
this._statusMsg = null;
|
|
||||||
if (ev.keyCode === 13) {
|
|
||||||
this._changePassword();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
async _changePassword() {
|
|
||||||
this._statusMsg = null;
|
|
||||||
if (!this._currentPassword || !this._password1 || !this._password2) return;
|
|
||||||
|
|
||||||
if (this._password1 !== this._password2) {
|
|
||||||
this._errorMsg = "New password confirmation doesn't match";
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this._currentPassword === this._password1) {
|
|
||||||
this._errorMsg = "New password must be different than current password";
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this._loading = true;
|
|
||||||
this._errorMsg = null;
|
|
||||||
|
|
||||||
try {
|
|
||||||
await this.hass.callWS({
|
|
||||||
type: "config/auth_provider/homeassistant/change_password",
|
|
||||||
current_password: this._currentPassword,
|
|
||||||
new_password: this._password1,
|
|
||||||
});
|
|
||||||
|
|
||||||
this.setProperties({
|
|
||||||
_statusMsg: "Password changed successfully",
|
|
||||||
_currentPassword: null,
|
|
||||||
_password1: null,
|
|
||||||
_password2: null,
|
|
||||||
});
|
|
||||||
} catch (err) {
|
|
||||||
this._errorMsg = err.message;
|
|
||||||
}
|
|
||||||
|
|
||||||
this._loading = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
customElements.define("ha-change-password-card", HaChangePasswordCard);
|
|
195
src/panels/profile/ha-change-password-card.ts
Normal file
195
src/panels/profile/ha-change-password-card.ts
Normal file
@ -0,0 +1,195 @@
|
|||||||
|
import "@polymer/paper-input/paper-input";
|
||||||
|
import "@polymer/paper-dialog/paper-dialog";
|
||||||
|
import {
|
||||||
|
css,
|
||||||
|
CSSResult,
|
||||||
|
customElement,
|
||||||
|
html,
|
||||||
|
internalProperty,
|
||||||
|
LitElement,
|
||||||
|
property,
|
||||||
|
PropertyValues,
|
||||||
|
TemplateResult,
|
||||||
|
} from "lit-element";
|
||||||
|
import "@material/mwc-button";
|
||||||
|
import "../../components/ha-circular-progress";
|
||||||
|
import "../../components/ha-card";
|
||||||
|
import { haStyle } from "../../resources/styles";
|
||||||
|
import type { HomeAssistant } from "../../types";
|
||||||
|
|
||||||
|
@customElement("ha-change-password-card")
|
||||||
|
class HaChangePasswordCard extends LitElement {
|
||||||
|
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||||
|
|
||||||
|
@internalProperty() private _loading = false;
|
||||||
|
|
||||||
|
@internalProperty() private _statusMsg?: string;
|
||||||
|
|
||||||
|
@internalProperty() private _errorMsg?: string;
|
||||||
|
|
||||||
|
@internalProperty() private _currentPassword?: string;
|
||||||
|
|
||||||
|
@internalProperty() private _password?: string;
|
||||||
|
|
||||||
|
@internalProperty() private _passwordConfirm?: string;
|
||||||
|
|
||||||
|
protected render(): TemplateResult {
|
||||||
|
return html`
|
||||||
|
<div>
|
||||||
|
<ha-card
|
||||||
|
.header=${this.hass.localize(
|
||||||
|
"ui.panel.profile.change_password.header"
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<div class="card-content">
|
||||||
|
${this._errorMsg
|
||||||
|
? html` <div class="error">${this._errorMsg}</div> `
|
||||||
|
: ""}
|
||||||
|
${this._statusMsg
|
||||||
|
? html` <div class="status">${this._statusMsg}</div> `
|
||||||
|
: ""}
|
||||||
|
|
||||||
|
<paper-input
|
||||||
|
id="currentPassword"
|
||||||
|
.label=${this.hass.localize(
|
||||||
|
"ui.panel.profile.change_password.current_password"
|
||||||
|
)}
|
||||||
|
type="password"
|
||||||
|
.value=${this._currentPassword}
|
||||||
|
@value-changed=${this._currentPasswordChanged}
|
||||||
|
required
|
||||||
|
></paper-input>
|
||||||
|
|
||||||
|
${this._currentPassword
|
||||||
|
? html` <paper-input
|
||||||
|
.label=${this.hass.localize(
|
||||||
|
"ui.panel.profile.change_password.new_password"
|
||||||
|
)}
|
||||||
|
name="password"
|
||||||
|
type="password"
|
||||||
|
.value=${this._password}
|
||||||
|
@value-changed=${this._newPasswordChanged}
|
||||||
|
required
|
||||||
|
auto-validate
|
||||||
|
></paper-input>
|
||||||
|
<paper-input
|
||||||
|
.label=${this.hass.localize(
|
||||||
|
"ui.panel.profile.change_password.confirm_new_password"
|
||||||
|
)}
|
||||||
|
name="passwordConfirm"
|
||||||
|
type="password"
|
||||||
|
.value=${this._passwordConfirm}
|
||||||
|
@value-changed=${this._newPasswordConfirmChanged}
|
||||||
|
required
|
||||||
|
auto-validate
|
||||||
|
></paper-input>`
|
||||||
|
: ""}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card-actions">
|
||||||
|
${this._loading
|
||||||
|
? html`<div>
|
||||||
|
<ha-circular-progress active></ha-circular-progress>
|
||||||
|
</div>`
|
||||||
|
: html`<mwc-button
|
||||||
|
@click=${this._changePassword}
|
||||||
|
.disabled=${!this._passwordConfirm}
|
||||||
|
>${this.hass.localize(
|
||||||
|
"ui.panel.profile.change_password.submit"
|
||||||
|
)}</mwc-button
|
||||||
|
>`}
|
||||||
|
</div>
|
||||||
|
</ha-card>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _currentPasswordChanged(ev: CustomEvent) {
|
||||||
|
this._currentPassword = ev.detail.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _newPasswordChanged(ev: CustomEvent) {
|
||||||
|
this._password = ev.detail.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _newPasswordConfirmChanged(ev: CustomEvent) {
|
||||||
|
this._passwordConfirm = ev.detail.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected firstUpdated(changedProps: PropertyValues) {
|
||||||
|
super.firstUpdated(changedProps);
|
||||||
|
this.addEventListener("keypress", (ev) => {
|
||||||
|
this._statusMsg = undefined;
|
||||||
|
if (ev.keyCode === 13) {
|
||||||
|
this._changePassword();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private async _changePassword() {
|
||||||
|
this._statusMsg = undefined;
|
||||||
|
if (!this._currentPassword || !this._password || !this._passwordConfirm) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this._password !== this._passwordConfirm) {
|
||||||
|
this._errorMsg = this.hass.localize(
|
||||||
|
"ui.panel.profile.change_password.error_new_mismatch"
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this._currentPassword === this._password) {
|
||||||
|
this._errorMsg = this.hass.localize(
|
||||||
|
"ui.panel.profile.change_password.error_new_is_old"
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this._loading = true;
|
||||||
|
this._errorMsg = undefined;
|
||||||
|
|
||||||
|
try {
|
||||||
|
await this.hass.callWS({
|
||||||
|
type: "config/auth_provider/homeassistant/change_password",
|
||||||
|
current_password: this._currentPassword,
|
||||||
|
new_password: this._password,
|
||||||
|
});
|
||||||
|
} catch (err) {
|
||||||
|
this._errorMsg = err.message;
|
||||||
|
return;
|
||||||
|
} finally {
|
||||||
|
this._loading = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
this._statusMsg = this.hass.localize(
|
||||||
|
"ui.panel.profile.change_password.success"
|
||||||
|
);
|
||||||
|
this._currentPassword = undefined;
|
||||||
|
this._password = undefined;
|
||||||
|
this._passwordConfirm = undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
static get styles(): CSSResult[] {
|
||||||
|
return [
|
||||||
|
haStyle,
|
||||||
|
css`
|
||||||
|
.error {
|
||||||
|
color: var(--error-color);
|
||||||
|
}
|
||||||
|
.status {
|
||||||
|
color: var(--primary-color);
|
||||||
|
}
|
||||||
|
#currentPassword {
|
||||||
|
margin-top: -8px;
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface HTMLElementTagNameMap {
|
||||||
|
"ha-change-password-card": HaChangePasswordCard;
|
||||||
|
}
|
||||||
|
}
|
@ -25,7 +25,7 @@ class HaPushNotificationsRow extends LocalizeMixin(PolymerElement) {
|
|||||||
>[[localize('ui.panel.profile.push_notifications.header')]]</span
|
>[[localize('ui.panel.profile.push_notifications.header')]]</span
|
||||||
>
|
>
|
||||||
<span slot="description">
|
<span slot="description">
|
||||||
[[_description(_platformLoaded, _pushSupported)]]
|
[[localize(_descrLocalizeKey)]]
|
||||||
<a
|
<a
|
||||||
href="[[_computeDocumentationUrl(hass)]]"
|
href="[[_computeDocumentationUrl(hass)]]"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
@ -45,6 +45,10 @@ class HaPushNotificationsRow extends LocalizeMixin(PolymerElement) {
|
|||||||
return {
|
return {
|
||||||
hass: Object,
|
hass: Object,
|
||||||
narrow: Boolean,
|
narrow: Boolean,
|
||||||
|
_descrLocalizeKey: {
|
||||||
|
type: String,
|
||||||
|
computed: "_descriptionKey(_platformLoaded, _pushSupported)",
|
||||||
|
},
|
||||||
_platformLoaded: {
|
_platformLoaded: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
computed: "_compPlatformLoaded(hass)",
|
computed: "_compPlatformLoaded(hass)",
|
||||||
@ -72,7 +76,7 @@ class HaPushNotificationsRow extends LocalizeMixin(PolymerElement) {
|
|||||||
return !platformLoaded || !pushSupported_;
|
return !platformLoaded || !pushSupported_;
|
||||||
}
|
}
|
||||||
|
|
||||||
_description(platformLoaded, pushSupported_) {
|
_descriptionKey(platformLoaded, pushSupported_) {
|
||||||
let key;
|
let key;
|
||||||
if (!pushSupported_) {
|
if (!pushSupported_) {
|
||||||
key = "error_use_https";
|
key = "error_use_https";
|
||||||
@ -81,7 +85,7 @@ class HaPushNotificationsRow extends LocalizeMixin(PolymerElement) {
|
|||||||
} else {
|
} else {
|
||||||
key = "description";
|
key = "description";
|
||||||
}
|
}
|
||||||
return this.localize(`ui.panel.profile.push_notifications.${key}`);
|
return `ui.panel.profile.push_notifications.${key}`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -89,6 +89,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"groups": {
|
"groups": {
|
||||||
|
"owner": "Owner",
|
||||||
"system-admin": "Administrators",
|
"system-admin": "Administrators",
|
||||||
"system-users": "Users",
|
"system-users": "Users",
|
||||||
"system-read-only": "Read-Only Users"
|
"system-read-only": "Read-Only Users"
|
||||||
@ -97,7 +98,8 @@
|
|||||||
"disabled_by": {
|
"disabled_by": {
|
||||||
"user": "User",
|
"user": "User",
|
||||||
"integration": "Integration",
|
"integration": "Integration",
|
||||||
"config_entry": "Config Entry"
|
"config_entry": "Config Entry",
|
||||||
|
"device": "Device"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"ui": {
|
"ui": {
|
||||||
@ -333,6 +335,16 @@
|
|||||||
"show_attributes": "Show attributes"
|
"show_attributes": "Show attributes"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"target-picker": {
|
||||||
|
"expand_area_id": "Expand this area in the seperate devices and entities that it contains. After expanding it will not update the devices and entities when the area changes.",
|
||||||
|
"expand_device_id": "Expand this device in seperate entities. After expanding it will not update the entities when the device changes.",
|
||||||
|
"remove_area_id": "Remove area",
|
||||||
|
"remove_device_id": "Remove device",
|
||||||
|
"remove_entity_id": "Remove entity",
|
||||||
|
"add_area_id": "Pick area",
|
||||||
|
"add_device_id": "Pick device",
|
||||||
|
"add_entity_id": "Pick entity"
|
||||||
|
},
|
||||||
"user-picker": {
|
"user-picker": {
|
||||||
"no_user": "No user",
|
"no_user": "No user",
|
||||||
"add_user": "Add user",
|
"add_user": "Add user",
|
||||||
@ -611,6 +623,8 @@
|
|||||||
"unavailable": "This entity is not currently available.",
|
"unavailable": "This entity is not currently available.",
|
||||||
"enabled_label": "Enable entity",
|
"enabled_label": "Enable entity",
|
||||||
"enabled_cause": "Disabled by {cause}.",
|
"enabled_cause": "Disabled by {cause}.",
|
||||||
|
"device_disabled": "The device of this entity is disabled.",
|
||||||
|
"open_device_settings": "Open device settings",
|
||||||
"enabled_description": "Disabled entities will not be added to Home Assistant.",
|
"enabled_description": "Disabled entities will not be added to Home Assistant.",
|
||||||
"enabled_delay_confirm": "The enabled entities will be added to Home Assistant in {delay} seconds",
|
"enabled_delay_confirm": "The enabled entities will be added to Home Assistant in {delay} seconds",
|
||||||
"enabled_restart_confirm": "Restart Home Assistant to finish enabling the entities",
|
"enabled_restart_confirm": "Restart Home Assistant to finish enabling the entities",
|
||||||
@ -786,7 +800,7 @@
|
|||||||
},
|
},
|
||||||
"areas": {
|
"areas": {
|
||||||
"caption": "Areas",
|
"caption": "Areas",
|
||||||
"description": "Manage areas in your home",
|
"description": "Group devices into areas",
|
||||||
"data_table": {
|
"data_table": {
|
||||||
"area": "Area",
|
"area": "Area",
|
||||||
"devices": "Devices"
|
"devices": "Devices"
|
||||||
@ -816,7 +830,7 @@
|
|||||||
},
|
},
|
||||||
"tags": {
|
"tags": {
|
||||||
"caption": "Tags",
|
"caption": "Tags",
|
||||||
"description": "Manage tags",
|
"description": "Trigger automations when a NFC tag, QR code, etc. is scanned",
|
||||||
"learn_more": "Learn more about tags",
|
"learn_more": "Learn more about tags",
|
||||||
"no_tags": "No tags",
|
"no_tags": "No tags",
|
||||||
"add_tag": "Add tag",
|
"add_tag": "Add tag",
|
||||||
@ -847,7 +861,7 @@
|
|||||||
},
|
},
|
||||||
"helpers": {
|
"helpers": {
|
||||||
"caption": "Helpers",
|
"caption": "Helpers",
|
||||||
"description": "Manage elements that help build automations",
|
"description": "Elements that help build automations",
|
||||||
"types": {
|
"types": {
|
||||||
"input_text": "Text",
|
"input_text": "Text",
|
||||||
"input_number": "Number",
|
"input_number": "Number",
|
||||||
@ -875,7 +889,7 @@
|
|||||||
},
|
},
|
||||||
"core": {
|
"core": {
|
||||||
"caption": "General",
|
"caption": "General",
|
||||||
"description": "Change your general Home Assistant configuration",
|
"description": "Unit system, location, time zone & other general parameters",
|
||||||
"section": {
|
"section": {
|
||||||
"core": {
|
"core": {
|
||||||
"header": "General Configuration",
|
"header": "General Configuration",
|
||||||
@ -904,7 +918,7 @@
|
|||||||
"caption": "Info",
|
"caption": "Info",
|
||||||
"copy_raw": "Raw Text",
|
"copy_raw": "Raw Text",
|
||||||
"copy_github": "For GitHub",
|
"copy_github": "For GitHub",
|
||||||
"description": "View info about your Home Assistant installation",
|
"description": "Version, system health and links to documentation",
|
||||||
"home_assistant_logo": "Home Assistant logo",
|
"home_assistant_logo": "Home Assistant logo",
|
||||||
"path_configuration": "Path to configuration.yaml: {path}",
|
"path_configuration": "Path to configuration.yaml: {path}",
|
||||||
"developed_by": "Developed by a bunch of awesome people.",
|
"developed_by": "Developed by a bunch of awesome people.",
|
||||||
@ -939,7 +953,7 @@
|
|||||||
},
|
},
|
||||||
"lovelace": {
|
"lovelace": {
|
||||||
"caption": "Lovelace Dashboards",
|
"caption": "Lovelace Dashboards",
|
||||||
"description": "Manage your Lovelace Dashboards",
|
"description": "Create customized sets of cards to control your home",
|
||||||
"dashboards": {
|
"dashboards": {
|
||||||
"default_dashboard": "This is the default dashboard",
|
"default_dashboard": "This is the default dashboard",
|
||||||
"caption": "Dashboards",
|
"caption": "Dashboards",
|
||||||
@ -1094,7 +1108,7 @@
|
|||||||
},
|
},
|
||||||
"automation": {
|
"automation": {
|
||||||
"caption": "Automations",
|
"caption": "Automations",
|
||||||
"description": "Manage automations",
|
"description": "Create custom behavior rules for your home",
|
||||||
"picker": {
|
"picker": {
|
||||||
"header": "Automation Editor",
|
"header": "Automation Editor",
|
||||||
"introduction": "The automation editor allows you to create and edit automations. Please follow the link below to read the instructions to make sure that you have configured Home Assistant correctly.",
|
"introduction": "The automation editor allows you to create and edit automations. Please follow the link below to read the instructions to make sure that you have configured Home Assistant correctly.",
|
||||||
@ -1474,7 +1488,7 @@
|
|||||||
},
|
},
|
||||||
"script": {
|
"script": {
|
||||||
"caption": "Scripts",
|
"caption": "Scripts",
|
||||||
"description": "Manage scripts",
|
"description": "Execute a sequence of actions",
|
||||||
"picker": {
|
"picker": {
|
||||||
"header": "Script Editor",
|
"header": "Script Editor",
|
||||||
"introduction": "The script editor allows you to create and edit scripts. Please follow the link below to read the instructions to make sure that you have configured Home Assistant correctly.",
|
"introduction": "The script editor allows you to create and edit scripts. Please follow the link below to read the instructions to make sure that you have configured Home Assistant correctly.",
|
||||||
@ -1524,7 +1538,7 @@
|
|||||||
},
|
},
|
||||||
"scene": {
|
"scene": {
|
||||||
"caption": "Scenes",
|
"caption": "Scenes",
|
||||||
"description": "Manage scenes",
|
"description": "Capture device states and easily recall them later",
|
||||||
"activated": "Activated scene {name}.",
|
"activated": "Activated scene {name}.",
|
||||||
"picker": {
|
"picker": {
|
||||||
"header": "Scene Editor",
|
"header": "Scene Editor",
|
||||||
@ -1570,7 +1584,7 @@
|
|||||||
"cloud": {
|
"cloud": {
|
||||||
"description_login": "Logged in as {email}",
|
"description_login": "Logged in as {email}",
|
||||||
"description_not_login": "Not logged in",
|
"description_not_login": "Not logged in",
|
||||||
"description_features": "Control away from home, integrate with Alexa and Google Assistant.",
|
"description_features": "Control home when away and integrate with Alexa and Google Assistant",
|
||||||
"login": {
|
"login": {
|
||||||
"title": "Cloud Login",
|
"title": "Cloud Login",
|
||||||
"introduction": "Home Assistant Cloud provides you with a secure remote connection to your instance while away from home. It also allows you to connect with cloud-only services: Amazon Alexa and Google Assistant.",
|
"introduction": "Home Assistant Cloud provides you with a secure remote connection to your instance while away from home. It also allows you to connect with cloud-only services: Amazon Alexa and Google Assistant.",
|
||||||
@ -1737,18 +1751,27 @@
|
|||||||
"devices": {
|
"devices": {
|
||||||
"add_prompt": "No {name} have been added using this device yet. You can add one by clicking the + button above.",
|
"add_prompt": "No {name} have been added using this device yet. You can add one by clicking the + button above.",
|
||||||
"caption": "Devices",
|
"caption": "Devices",
|
||||||
"description": "Manage connected devices",
|
"description": "Manage configured devices",
|
||||||
"device_info": "Device info",
|
"device_info": "Device info",
|
||||||
"unnamed_device": "Unnamed device",
|
"unnamed_device": "Unnamed device",
|
||||||
"unknown_error": "Unknown error",
|
"unknown_error": "Unknown error",
|
||||||
"name": "Name",
|
"name": "Name",
|
||||||
"update": "Update",
|
"update": "Update",
|
||||||
"no_devices": "No devices",
|
"no_devices": "No devices",
|
||||||
|
"enabled_label": "Enable device",
|
||||||
|
"enabled_cause": "The device is disabled by {cause}.",
|
||||||
|
"disabled_by": {
|
||||||
|
"user": "User",
|
||||||
|
"integration": "Integration",
|
||||||
|
"config_entry": "Config Entry"
|
||||||
|
},
|
||||||
|
"enabled_description": "Disabled devices will not be shown and entities belonging to the device will be disabled and not added to Home Assistant.",
|
||||||
"automation": {
|
"automation": {
|
||||||
"automations": "Automations",
|
"automations": "Automations",
|
||||||
"no_automations": "No automations",
|
"no_automations": "No automations",
|
||||||
"unknown_automation": "Unknown automation",
|
"unknown_automation": "Unknown automation",
|
||||||
"create": "Create automation with device",
|
"create": "Create automation with device",
|
||||||
|
"create_disable": "Can't create automation with disabled device",
|
||||||
"triggers": {
|
"triggers": {
|
||||||
"caption": "Do something when...",
|
"caption": "Do something when...",
|
||||||
"no_triggers": "No triggers",
|
"no_triggers": "No triggers",
|
||||||
@ -1769,12 +1792,14 @@
|
|||||||
"script": {
|
"script": {
|
||||||
"scripts": "Scripts",
|
"scripts": "Scripts",
|
||||||
"no_scripts": "No scripts",
|
"no_scripts": "No scripts",
|
||||||
"create": "Create script with device"
|
"create": "Create script with device",
|
||||||
|
"create_disable": "Can't create script with disabled device"
|
||||||
},
|
},
|
||||||
"scene": {
|
"scene": {
|
||||||
"scenes": "Scenes",
|
"scenes": "Scenes",
|
||||||
"no_scenes": "No scenes",
|
"no_scenes": "No scenes",
|
||||||
"create": "Create scene with device"
|
"create": "Create scene with device",
|
||||||
|
"create_disable": "Can't create scene with disabled device"
|
||||||
},
|
},
|
||||||
"cant_edit": "You can only edit items that are created in the UI.",
|
"cant_edit": "You can only edit items that are created in the UI.",
|
||||||
"device_not_found": "Device not found.",
|
"device_not_found": "Device not found.",
|
||||||
@ -1789,6 +1814,7 @@
|
|||||||
"scenes": "Scenes",
|
"scenes": "Scenes",
|
||||||
"confirm_rename_entity_ids": "Do you also want to rename the entity IDs of your entities?",
|
"confirm_rename_entity_ids": "Do you also want to rename the entity IDs of your entities?",
|
||||||
"confirm_rename_entity_ids_warning": "This will not change any configuration (like automations, scripts, scenes, dashboards) that is currently using these entities! You will have to update them yourself to use the new entity IDs!",
|
"confirm_rename_entity_ids_warning": "This will not change any configuration (like automations, scripts, scenes, dashboards) that is currently using these entities! You will have to update them yourself to use the new entity IDs!",
|
||||||
|
"disabled": "Disabled",
|
||||||
"data_table": {
|
"data_table": {
|
||||||
"device": "Device",
|
"device": "Device",
|
||||||
"manufacturer": "Manufacturer",
|
"manufacturer": "Manufacturer",
|
||||||
@ -1800,7 +1826,16 @@
|
|||||||
"no_area": "No area"
|
"no_area": "No area"
|
||||||
},
|
},
|
||||||
"delete": "Delete",
|
"delete": "Delete",
|
||||||
"confirm_delete": "Are you sure you want to delete this device?"
|
"confirm_delete": "Are you sure you want to delete this device?",
|
||||||
|
"picker": {
|
||||||
|
"search": "Search devices",
|
||||||
|
"filter": {
|
||||||
|
"filter": "Filter",
|
||||||
|
"show_disabled": "Show disabled devices",
|
||||||
|
"hidden_devices": "{number} hidden {number, plural,\n one {device}\n other {devices}\n}",
|
||||||
|
"show_all": "Show all"
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"entities": {
|
"entities": {
|
||||||
"caption": "Entities",
|
"caption": "Entities",
|
||||||
@ -1829,6 +1864,7 @@
|
|||||||
"name": "Name",
|
"name": "Name",
|
||||||
"entity_id": "Entity ID",
|
"entity_id": "Entity ID",
|
||||||
"integration": "Integration",
|
"integration": "Integration",
|
||||||
|
"area": "Area",
|
||||||
"status": "Status"
|
"status": "Status"
|
||||||
},
|
},
|
||||||
"selected": "{number} selected",
|
"selected": "{number} selected",
|
||||||
@ -1914,7 +1950,7 @@
|
|||||||
},
|
},
|
||||||
"integrations": {
|
"integrations": {
|
||||||
"caption": "Integrations",
|
"caption": "Integrations",
|
||||||
"description": "Manage integrations",
|
"description": "Manage integrations with services, devices, ...",
|
||||||
"integration": "integration",
|
"integration": "integration",
|
||||||
"discovered": "Discovered",
|
"discovered": "Discovered",
|
||||||
"attention": "Attention required",
|
"attention": "Attention required",
|
||||||
@ -1991,15 +2027,19 @@
|
|||||||
"users_privileges_note": "The user group feature is a work in progress. The user will be unable to administer the instance via the UI. We're still auditing all management API endpoints to ensure that they correctly limit access to administrators.",
|
"users_privileges_note": "The user group feature is a work in progress. The user will be unable to administer the instance via the UI. We're still auditing all management API endpoints to ensure that they correctly limit access to administrators.",
|
||||||
"picker": {
|
"picker": {
|
||||||
"headers": {
|
"headers": {
|
||||||
"name": "Name",
|
"name": "Display name",
|
||||||
|
"username": "Username",
|
||||||
"group": "Group",
|
"group": "Group",
|
||||||
"system": "System"
|
"system": "System generated",
|
||||||
|
"is_active": "Active",
|
||||||
|
"is_owner": "Owner"
|
||||||
},
|
},
|
||||||
"add_user": "Add user"
|
"add_user": "Add user"
|
||||||
},
|
},
|
||||||
"editor": {
|
"editor": {
|
||||||
"caption": "View user",
|
"caption": "View user",
|
||||||
"name": "Name",
|
"name": "Display name",
|
||||||
|
"username": "Username",
|
||||||
"change_password": "Change password",
|
"change_password": "Change password",
|
||||||
"new_password": "New Password",
|
"new_password": "New Password",
|
||||||
"password_changed": "Password was changed successfully",
|
"password_changed": "Password was changed successfully",
|
||||||
@ -2016,12 +2056,11 @@
|
|||||||
"system_generated_users_not_removable": "Unable to remove system generated users.",
|
"system_generated_users_not_removable": "Unable to remove system generated users.",
|
||||||
"system_generated_users_not_editable": "Unable to update system generated users.",
|
"system_generated_users_not_editable": "Unable to update system generated users.",
|
||||||
"unnamed_user": "Unnamed User",
|
"unnamed_user": "Unnamed User",
|
||||||
"confirm_user_deletion": "Are you sure you want to delete {name}?"
|
"confirm_user_deletion": "Are you sure you want to delete {name}?",
|
||||||
|
"active_tooltip": "Controls if user can login"
|
||||||
},
|
},
|
||||||
"add_user": {
|
"add_user": {
|
||||||
"caption": "Add user",
|
"caption": "Add user",
|
||||||
"name": "Name",
|
|
||||||
"username": "Username",
|
|
||||||
"password": "Password",
|
"password": "Password",
|
||||||
"password_confirm": "Confirm Password",
|
"password_confirm": "Confirm Password",
|
||||||
"password_not_match": "Passwords don't match",
|
"password_not_match": "Passwords don't match",
|
||||||
@ -2194,6 +2233,16 @@
|
|||||||
"issue_zigbee_command": "Issue Zigbee Command",
|
"issue_zigbee_command": "Issue Zigbee Command",
|
||||||
"help_command_dropdown": "Select a command to interact with."
|
"help_command_dropdown": "Select a command to interact with."
|
||||||
},
|
},
|
||||||
|
"device_pairing_card": {
|
||||||
|
"PAIRED": "Device Found",
|
||||||
|
"PAIRED_status_text": "Starting Interview",
|
||||||
|
"INTERVIEW_COMPLETE": "Interview Complete",
|
||||||
|
"INTERVIEW_COMPLETE_status_text": "Configuring",
|
||||||
|
"CONFIGURED": "Configuration Complete",
|
||||||
|
"CONFIGURED_status_text": "Initializing",
|
||||||
|
"INITIALIZED": "Initialization Complete",
|
||||||
|
"INITIALIZED_status_text": "The device is ready to use"
|
||||||
|
},
|
||||||
"network": {
|
"network": {
|
||||||
"caption": "Network"
|
"caption": "Network"
|
||||||
},
|
},
|
||||||
@ -2857,7 +2906,10 @@
|
|||||||
"new_password": "New Password",
|
"new_password": "New Password",
|
||||||
"confirm_new_password": "Confirm New Password",
|
"confirm_new_password": "Confirm New Password",
|
||||||
"error_required": "Required",
|
"error_required": "Required",
|
||||||
"submit": "Submit"
|
"submit": "Submit",
|
||||||
|
"error_new_mismatch": "Entered new password values do not match",
|
||||||
|
"error_new_is_old": "New password must be different than current password",
|
||||||
|
"success": "Password changed successfully"
|
||||||
},
|
},
|
||||||
"mfa": {
|
"mfa": {
|
||||||
"header": "Multi-factor Authentication Modules",
|
"header": "Multi-factor Authentication Modules",
|
||||||
|
@ -7,6 +7,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"groups": {
|
"groups": {
|
||||||
|
"owner": "Vlastník",
|
||||||
"system-admin": "Správci",
|
"system-admin": "Správci",
|
||||||
"system-read-only": "Uživatelé jen pro čtení",
|
"system-read-only": "Uživatelé jen pro čtení",
|
||||||
"system-users": "Uživatelé"
|
"system-users": "Uživatelé"
|
||||||
@ -587,7 +588,7 @@
|
|||||||
"by_service": "službou",
|
"by_service": "službou",
|
||||||
"entries_not_found": "Nenalezeny žádné záznamy.",
|
"entries_not_found": "Nenalezeny žádné záznamy.",
|
||||||
"messages": {
|
"messages": {
|
||||||
"became_unavailable": "stalo se nedostupným",
|
"became_unavailable": "bylo nedostupné",
|
||||||
"changed_to_state": "změněno na {state}",
|
"changed_to_state": "změněno na {state}",
|
||||||
"cleared_device_class": "zrušeno (nebylo zjištěno {device_class})",
|
"cleared_device_class": "zrušeno (nebylo zjištěno {device_class})",
|
||||||
"detected_device_class": "zjištěno {device_class}",
|
"detected_device_class": "zjištěno {device_class}",
|
||||||
@ -1068,7 +1069,7 @@
|
|||||||
"delete_confirm": "Opravdu smazat?",
|
"delete_confirm": "Opravdu smazat?",
|
||||||
"duplicate": "Duplikovat",
|
"duplicate": "Duplikovat",
|
||||||
"header": "Akce",
|
"header": "Akce",
|
||||||
"introduction": "Akce jsou to, co Home Assistant provede při spuštění automatizace. \n\n [Více informací o akcích.] (Https://home-assistant.io/docs/automation/action/)",
|
"introduction": "Akce jsou to, co Home Assistant provede při spuštění automatizace.",
|
||||||
"learn_more": "Další informace o akcích",
|
"learn_more": "Další informace o akcích",
|
||||||
"name": "Akce",
|
"name": "Akce",
|
||||||
"type_select": "Typ akce",
|
"type_select": "Typ akce",
|
||||||
@ -1150,7 +1151,7 @@
|
|||||||
"delete_confirm": "Opravdu smazat?",
|
"delete_confirm": "Opravdu smazat?",
|
||||||
"duplicate": "Duplikovat",
|
"duplicate": "Duplikovat",
|
||||||
"header": "Podmínky",
|
"header": "Podmínky",
|
||||||
"introduction": "Podmínky jsou volitelnou součástí automatizačního pravidla a mohou být použity k zabránění spuštění akce. Podmínky vypadají velmi podobně jako spouštěče, ale jsou velmi odlišné. Spouštěč se podívá na události, ke kterým dochází v systému, zatímco podmínka se zabývá pouze tím, jak systém vypadá právě teď. Pro spouštěč se může jevit že je spínač zapnutý. Stav může vidět pouze tehdy, je-li přepínač aktuálně zapnutý nebo vypnutý. \n\n [Další informace o podmínkách.] (Https://home-assistant.io/docs/scripts/conditions/)",
|
"introduction": "Podmínky jsou volitelné a pokud nejsou všechny splněny, zabrání dalšímu provádění automatizace.",
|
||||||
"learn_more": "Další informace o podmínkách",
|
"learn_more": "Další informace o podmínkách",
|
||||||
"name": "Podmínka",
|
"name": "Podmínka",
|
||||||
"type_select": "Typ podmínky",
|
"type_select": "Typ podmínky",
|
||||||
@ -1229,7 +1230,7 @@
|
|||||||
"edit_ui": "Upravit pomocí uživatelského rozhraní",
|
"edit_ui": "Upravit pomocí uživatelského rozhraní",
|
||||||
"edit_yaml": "Upravit jako YAML",
|
"edit_yaml": "Upravit jako YAML",
|
||||||
"enable_disable": "Povolit / zakázat automatizaci",
|
"enable_disable": "Povolit / zakázat automatizaci",
|
||||||
"introduction": "Použijte automatizace k oživení svého domova",
|
"introduction": "Pomocí automatizací oživte svůj domov.",
|
||||||
"load_error_not_editable": "Lze upravovat pouze automatizace v automations.yaml.",
|
"load_error_not_editable": "Lze upravovat pouze automatizace v automations.yaml.",
|
||||||
"load_error_unknown": "Chyba při načítání automatizace ({err_no}).",
|
"load_error_unknown": "Chyba při načítání automatizace ({err_no}).",
|
||||||
"max": {
|
"max": {
|
||||||
@ -1254,7 +1255,7 @@
|
|||||||
"delete_confirm": "Opravdu smazat?",
|
"delete_confirm": "Opravdu smazat?",
|
||||||
"duplicate": "Duplikovat",
|
"duplicate": "Duplikovat",
|
||||||
"header": "Spouštěče",
|
"header": "Spouštěče",
|
||||||
"introduction": "Spouštěče spouštějí zpracování automatizačního pravidla. Pro stejné pravidlo je možné zadat více spouštěčů. Po spuštění spouštěče ověří Home Assistant případné podmínky a zavolá akci. \n\n [Další informace o spouštěčích.] (Https://home-assistant.io/docs/automation/trigger/)",
|
"introduction": "Spouštěče spouštějí automatizaci. Pro jednu automatizaci je možné zadat více spouštěčů. Po spuštění ověří Home Assistant případné podmínky a zavolá akci.",
|
||||||
"learn_more": "Další informace o spouštěčích",
|
"learn_more": "Další informace o spouštěčích",
|
||||||
"name": "Spouštěč",
|
"name": "Spouštěč",
|
||||||
"type_select": "Typ spouštěče",
|
"type_select": "Typ spouštěče",
|
||||||
@ -1391,7 +1392,7 @@
|
|||||||
"error_no_url": "Zadejte adresu URL šablonky konfigurace",
|
"error_no_url": "Zadejte adresu URL šablonky konfigurace",
|
||||||
"header": "Přidat novou šablonku konfigurace",
|
"header": "Přidat novou šablonku konfigurace",
|
||||||
"import_btn": "Importovat šablonku konfigurace",
|
"import_btn": "Importovat šablonku konfigurace",
|
||||||
"import_header": "Importovat {name} ({domain})",
|
"import_header": "Importovat \"{name}\" (typ: {domain})",
|
||||||
"import_introduction": "Z Githubu a komunitních fór můžete importovat šablonky konfigurace sdílené ostatními uživateli. Zadejte adresu URL šablonky konfigurace.",
|
"import_introduction": "Z Githubu a komunitních fór můžete importovat šablonky konfigurace sdílené ostatními uživateli. Zadejte adresu URL šablonky konfigurace.",
|
||||||
"importing": "Importuje se šablonka konfigurace",
|
"importing": "Importuje se šablonka konfigurace",
|
||||||
"save_btn": "Uložit šablonku konfigurace",
|
"save_btn": "Uložit šablonku konfigurace",
|
||||||
@ -1399,13 +1400,14 @@
|
|||||||
"url": "URL šablonky konfigurace"
|
"url": "URL šablonky konfigurace"
|
||||||
},
|
},
|
||||||
"overview": {
|
"overview": {
|
||||||
"add_blueprint": "Přidat šablonku konfigurace",
|
|
||||||
"confirm_delete_header": "Odstranit tuto šablonku konfigurace?",
|
"confirm_delete_header": "Odstranit tuto šablonku konfigurace?",
|
||||||
"confirm_delete_text": "Opravdu chcete smazat tuto šablonku konfigurace?",
|
|
||||||
"headers": {
|
"headers": {
|
||||||
|
"domain": "Doména",
|
||||||
|
"file_name": "Název souboru",
|
||||||
"name": "Jméno"
|
"name": "Jméno"
|
||||||
},
|
},
|
||||||
"learn_more": "Další informace o šablonkách konfigurace"
|
"learn_more": "Další informace o šablonkách konfigurace",
|
||||||
|
"use_blueprint": "Vytvořit automatizaci"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"cloud": {
|
"cloud": {
|
||||||
@ -1722,6 +1724,7 @@
|
|||||||
},
|
},
|
||||||
"header": "Entity",
|
"header": "Entity",
|
||||||
"headers": {
|
"headers": {
|
||||||
|
"area": "Oblast",
|
||||||
"entity_id": "ID entity",
|
"entity_id": "ID entity",
|
||||||
"integration": "Integrace",
|
"integration": "Integrace",
|
||||||
"name": "Název",
|
"name": "Název",
|
||||||
@ -2378,7 +2381,7 @@
|
|||||||
"delete_user": "Odstranit uživatele",
|
"delete_user": "Odstranit uživatele",
|
||||||
"group": "Skupina",
|
"group": "Skupina",
|
||||||
"id": "ID",
|
"id": "ID",
|
||||||
"name": "Jméno",
|
"name": "Zobrazované jméno",
|
||||||
"new_password": "Nové heslo",
|
"new_password": "Nové heslo",
|
||||||
"owner": "Vlastník",
|
"owner": "Vlastník",
|
||||||
"password_changed": "Heslo bylo změněno",
|
"password_changed": "Heslo bylo změněno",
|
||||||
@ -2386,19 +2389,24 @@
|
|||||||
"system_generated_users_not_editable": "Nelze aktualizovat uživatele generované systémem.",
|
"system_generated_users_not_editable": "Nelze aktualizovat uživatele generované systémem.",
|
||||||
"system_generated_users_not_removable": "Nelze odebrat uživatele generované systémem.",
|
"system_generated_users_not_removable": "Nelze odebrat uživatele generované systémem.",
|
||||||
"unnamed_user": "Nepojmenovaný uživatel",
|
"unnamed_user": "Nepojmenovaný uživatel",
|
||||||
"update_user": "Aktualizovat"
|
"update_user": "Aktualizovat",
|
||||||
|
"username": "Uživatelské jméno"
|
||||||
},
|
},
|
||||||
"picker": {
|
"picker": {
|
||||||
"add_user": "Přidat uživatele",
|
"add_user": "Přidat uživatele",
|
||||||
"headers": {
|
"headers": {
|
||||||
"group": "Skupina",
|
"group": "Skupina",
|
||||||
"name": "Název",
|
"is_active": "Aktivní",
|
||||||
"system": "Systémový"
|
"is_owner": "Vlastník",
|
||||||
|
"name": "Zobrazované jméno",
|
||||||
|
"system": "Vygenerovaný systémem",
|
||||||
|
"username": "Uživatelské jméno"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"users_privileges_note": "Skupiny uživatelů jsou v přípravě. Uživatel je nebude moci spravovat prostřednictvím uživatelského rozhraní. Stále kontrolujeme API pro správu, abychom zajistili, že správně omezuje přístup pouze pro administrátory."
|
"users_privileges_note": "Skupiny uživatelů jsou v přípravě. Uživatel je nebude moci spravovat prostřednictvím uživatelského rozhraní. Stále kontrolujeme API pro správu, abychom zajistili, že správně omezuje přístup pouze pro administrátory."
|
||||||
},
|
},
|
||||||
"zha": {
|
"zha": {
|
||||||
|
"add_device": "Přidat zařízení",
|
||||||
"add_device_page": {
|
"add_device_page": {
|
||||||
"discovered_text": "Jakmile se objeví nalezne, zobrazí se zde.",
|
"discovered_text": "Jakmile se objeví nalezne, zobrazí se zde.",
|
||||||
"discovery_text": "Zde se objeví nalezená zařízení. Postupujte dle pokynů pro vaše zařízení a uveďte je do režimu párování.",
|
"discovery_text": "Zde se objeví nalezená zařízení. Postupujte dle pokynů pro vaše zařízení a uveďte je do režimu párování.",
|
||||||
@ -2444,6 +2452,16 @@
|
|||||||
"value": "Hodnota"
|
"value": "Hodnota"
|
||||||
},
|
},
|
||||||
"description": "Správa Zigbee Home Automation",
|
"description": "Správa Zigbee Home Automation",
|
||||||
|
"device_pairing_card": {
|
||||||
|
"CONFIGURED": "Nastavení dokončeno",
|
||||||
|
"CONFIGURED_status_text": "Inicializuji",
|
||||||
|
"INITIALIZED": "Inicializace dokončena",
|
||||||
|
"INITIALIZED_status_text": "Zařízení je připraveno k použití",
|
||||||
|
"INTERVIEW_COMPLETE": "Dotazování dokončeno",
|
||||||
|
"INTERVIEW_COMPLETE_status_text": "Nastavuji",
|
||||||
|
"PAIRED": "Zařízení nalezeno",
|
||||||
|
"PAIRED_status_text": "Začínám dotazování"
|
||||||
|
},
|
||||||
"devices": {
|
"devices": {
|
||||||
"header": "Zigbee Home Automation - Zařízení"
|
"header": "Zigbee Home Automation - Zařízení"
|
||||||
},
|
},
|
||||||
@ -2459,6 +2477,7 @@
|
|||||||
"unbind_button_label": "Odpojit skupinu"
|
"unbind_button_label": "Odpojit skupinu"
|
||||||
},
|
},
|
||||||
"groups": {
|
"groups": {
|
||||||
|
"add_group": "Přidat do skupiny",
|
||||||
"add_members": "Přidání členů",
|
"add_members": "Přidání členů",
|
||||||
"adding_members": "Přidání členů",
|
"adding_members": "Přidání členů",
|
||||||
"caption": "Skupiny",
|
"caption": "Skupiny",
|
||||||
@ -2501,7 +2520,11 @@
|
|||||||
"hint_wakeup": "Některá zařízení, jako jsou senzory Xiaomi, mají tlačítko probuzení, které můžete stisknout v intervalu ~ 5 sekund, které udržuje zařízení probuzené, když s nimi komunikujete.",
|
"hint_wakeup": "Některá zařízení, jako jsou senzory Xiaomi, mají tlačítko probuzení, které můžete stisknout v intervalu ~ 5 sekund, které udržuje zařízení probuzené, když s nimi komunikujete.",
|
||||||
"introduction": "Spouštějte ZHA příkazy, které ovlivňují specifické zařízení. Vyberte zařízení pro zobrazení seznamu dostupných příkazů."
|
"introduction": "Spouštějte ZHA příkazy, které ovlivňují specifické zařízení. Vyberte zařízení pro zobrazení seznamu dostupných příkazů."
|
||||||
},
|
},
|
||||||
"title": "Zigbee Home Automation"
|
"title": "Zigbee Home Automation",
|
||||||
|
"visualization": {
|
||||||
|
"caption": "Vizualizace",
|
||||||
|
"header": "Vizualizace sítě"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"zone": {
|
"zone": {
|
||||||
"add_zone": "Přidat zónu",
|
"add_zone": "Přidat zónu",
|
||||||
|
@ -7,6 +7,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"groups": {
|
"groups": {
|
||||||
|
"owner": "Besitzer",
|
||||||
"system-admin": "Administratoren",
|
"system-admin": "Administratoren",
|
||||||
"system-read-only": "Nur-Lesen Benutzer",
|
"system-read-only": "Nur-Lesen Benutzer",
|
||||||
"system-users": "Benutzer"
|
"system-users": "Benutzer"
|
||||||
@ -932,7 +933,7 @@
|
|||||||
"telegram": "Telegram-Benachrichtigungsdienst neu laden",
|
"telegram": "Telegram-Benachrichtigungsdienst neu laden",
|
||||||
"template": "Templates neu laden",
|
"template": "Templates neu laden",
|
||||||
"trend": "Trend Entitäten neu laden",
|
"trend": "Trend Entitäten neu laden",
|
||||||
"universal": "Universelle Medien Player Entitäten neu laden ",
|
"universal": "Universelle Medien Player Entitäten neu laden",
|
||||||
"zone": "Zonen neu laden"
|
"zone": "Zonen neu laden"
|
||||||
},
|
},
|
||||||
"server_control": {
|
"server_control": {
|
||||||
@ -1239,7 +1240,7 @@
|
|||||||
"edit_ui": "Mit der Benutzeroberfläche bearbeiten",
|
"edit_ui": "Mit der Benutzeroberfläche bearbeiten",
|
||||||
"edit_yaml": "Als YAML bearbeiten",
|
"edit_yaml": "Als YAML bearbeiten",
|
||||||
"enable_disable": "Automatisierung aktivieren / deaktivieren",
|
"enable_disable": "Automatisierung aktivieren / deaktivieren",
|
||||||
"introduction": "Benutze Automatisierungen, um deinem Zuhause Leben einzuhauchen",
|
"introduction": "Benutze Automatisierungen, um deinem Zuhause Leben einzuhauchen.",
|
||||||
"load_error_not_editable": "Nur Automatisierungen in automations.yaml sind editierbar.",
|
"load_error_not_editable": "Nur Automatisierungen in automations.yaml sind editierbar.",
|
||||||
"load_error_unknown": "Fehler beim Laden der Automatisierung ({err_no}).",
|
"load_error_unknown": "Fehler beim Laden der Automatisierung ({err_no}).",
|
||||||
"max": {
|
"max": {
|
||||||
@ -1398,7 +1399,7 @@
|
|||||||
},
|
},
|
||||||
"blueprint": {
|
"blueprint": {
|
||||||
"add": {
|
"add": {
|
||||||
"error_no_url": "Bitte gebe die URL des Blueprints ein.",
|
"error_no_url": "Bitte gebe die URL des Bauplans ein.",
|
||||||
"header": "Neuen Bauplan hinzufügen",
|
"header": "Neuen Bauplan hinzufügen",
|
||||||
"import_btn": "Bauplan importieren",
|
"import_btn": "Bauplan importieren",
|
||||||
"import_header": "{name}({domain}) importieren",
|
"import_header": "{name}({domain}) importieren",
|
||||||
@ -1407,7 +1408,7 @@
|
|||||||
"save_btn": "Bauplan speichern",
|
"save_btn": "Bauplan speichern",
|
||||||
"saving": "Bauplan wir gespeichert...",
|
"saving": "Bauplan wir gespeichert...",
|
||||||
"unsupported_blueprint": "Dieser Bauplan wird nicht unterstützt",
|
"unsupported_blueprint": "Dieser Bauplan wird nicht unterstützt",
|
||||||
"url": "URL des Blueprints"
|
"url": "URL des Bauplans"
|
||||||
},
|
},
|
||||||
"caption": "Baupläne",
|
"caption": "Baupläne",
|
||||||
"description": "Baupläne verwalten",
|
"description": "Baupläne verwalten",
|
||||||
@ -1417,10 +1418,13 @@
|
|||||||
"confirm_delete_text": "Bist du sicher, dass du diesen Bauplan löschen möchtest?",
|
"confirm_delete_text": "Bist du sicher, dass du diesen Bauplan löschen möchtest?",
|
||||||
"header": "Bauplan-Editor",
|
"header": "Bauplan-Editor",
|
||||||
"headers": {
|
"headers": {
|
||||||
|
"domain": "Domain",
|
||||||
|
"file_name": "Dateiname",
|
||||||
"name": "Name"
|
"name": "Name"
|
||||||
},
|
},
|
||||||
"introduction": "Mit dem Bauplan-Editor kannst du Baupläne erstellen und bearbeiten.",
|
"introduction": "Mit dem Bauplan-Editor kannst du Baupläne erstellen und bearbeiten.",
|
||||||
"learn_more": "Erfahre mehr über Baupläne"
|
"learn_more": "Erfahre mehr über Baupläne",
|
||||||
|
"use_blueprint": "Automatisierung erstellen"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"cloud": {
|
"cloud": {
|
||||||
@ -1737,6 +1741,7 @@
|
|||||||
},
|
},
|
||||||
"header": "Entitäten",
|
"header": "Entitäten",
|
||||||
"headers": {
|
"headers": {
|
||||||
|
"area": "Bereich",
|
||||||
"entity_id": "Entitäts-ID",
|
"entity_id": "Entitäts-ID",
|
||||||
"integration": "Integration",
|
"integration": "Integration",
|
||||||
"name": "Name",
|
"name": "Name",
|
||||||
@ -2401,19 +2406,24 @@
|
|||||||
"system_generated_users_not_editable": "Systemgenerierte Benutzer können nicht aktualisiert werden.",
|
"system_generated_users_not_editable": "Systemgenerierte Benutzer können nicht aktualisiert werden.",
|
||||||
"system_generated_users_not_removable": "Vom System generierte Benutzer können nicht entfernt werden.",
|
"system_generated_users_not_removable": "Vom System generierte Benutzer können nicht entfernt werden.",
|
||||||
"unnamed_user": "Unbenannter Benutzer",
|
"unnamed_user": "Unbenannter Benutzer",
|
||||||
"update_user": "Aktualisieren"
|
"update_user": "Aktualisieren",
|
||||||
|
"username": "Benutzername"
|
||||||
},
|
},
|
||||||
"picker": {
|
"picker": {
|
||||||
"add_user": "Benutzer hinzufügen",
|
"add_user": "Benutzer hinzufügen",
|
||||||
"headers": {
|
"headers": {
|
||||||
"group": "Gruppe",
|
"group": "Gruppe",
|
||||||
|
"is_active": "Aktiv",
|
||||||
|
"is_owner": "Besitzer",
|
||||||
"name": "Name",
|
"name": "Name",
|
||||||
"system": "System"
|
"system": "System",
|
||||||
|
"username": "Benutzername"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"users_privileges_note": "Benutzergruppen sind derzeit noch in Entwicklung. Der Benutzer wird nicht in der Lage sein, Änderungen an der Instanz über das UI vorzunehmen. Derzeit überprüfen wir noch alle API Endpunkte um sicherzustellen dass diese nur von Administratoren genutzt werden können."
|
"users_privileges_note": "Benutzergruppen sind derzeit noch in Entwicklung. Der Benutzer wird nicht in der Lage sein, Änderungen an der Instanz über das UI vorzunehmen. Derzeit überprüfen wir noch alle API Endpunkte um sicherzustellen dass diese nur von Administratoren genutzt werden können."
|
||||||
},
|
},
|
||||||
"zha": {
|
"zha": {
|
||||||
|
"add_device": "Gerät hinzufügen",
|
||||||
"add_device_page": {
|
"add_device_page": {
|
||||||
"discovered_text": "Geräte werden hier angezeigt sobald sie erkannt worden sind.",
|
"discovered_text": "Geräte werden hier angezeigt sobald sie erkannt worden sind.",
|
||||||
"discovery_text": "Erkannte Geräte werden hier angezeigt. Befolgen Sie die Anweisungen für Ihr Gerät und versetzen Sie das Gerät in den Pairing-Modus.",
|
"discovery_text": "Erkannte Geräte werden hier angezeigt. Befolgen Sie die Anweisungen für Ihr Gerät und versetzen Sie das Gerät in den Pairing-Modus.",
|
||||||
@ -2459,6 +2469,14 @@
|
|||||||
"value": "Wert"
|
"value": "Wert"
|
||||||
},
|
},
|
||||||
"description": "Zigbee Home Automation Netzwerkmanagement",
|
"description": "Zigbee Home Automation Netzwerkmanagement",
|
||||||
|
"device_pairing_card": {
|
||||||
|
"CONFIGURED": "Konfiguration abgeschlossen",
|
||||||
|
"CONFIGURED_status_text": "Initialisieren",
|
||||||
|
"INITIALIZED": "Initialisierung abgeschlossen",
|
||||||
|
"INITIALIZED_status_text": "Das Gerät ist einsatzbereit",
|
||||||
|
"INTERVIEW_COMPLETE_status_text": "Konfigurieren",
|
||||||
|
"PAIRED": "Gerät gefunden"
|
||||||
|
},
|
||||||
"devices": {
|
"devices": {
|
||||||
"header": "Zigbee Home Automation - Gerät"
|
"header": "Zigbee Home Automation - Gerät"
|
||||||
},
|
},
|
||||||
@ -2474,6 +2492,7 @@
|
|||||||
"unbind_button_label": "Gruppe auflösen"
|
"unbind_button_label": "Gruppe auflösen"
|
||||||
},
|
},
|
||||||
"groups": {
|
"groups": {
|
||||||
|
"add_group": "Gruppe hinzufügen",
|
||||||
"add_members": "Mitglieder hinzufügen",
|
"add_members": "Mitglieder hinzufügen",
|
||||||
"adding_members": "Füge Mitglieder hinzu",
|
"adding_members": "Füge Mitglieder hinzu",
|
||||||
"caption": "Gruppen",
|
"caption": "Gruppen",
|
||||||
@ -2516,7 +2535,11 @@
|
|||||||
"hint_wakeup": "Einige Geräte, z. B. Xiaomi-Sensoren, verfügen über eine Wecktaste, die in Intervallen von ~5 Sekunden gedrückt werden kann, um die Geräte wach zu halten, während mit ihnen interagiert wird.",
|
"hint_wakeup": "Einige Geräte, z. B. Xiaomi-Sensoren, verfügen über eine Wecktaste, die in Intervallen von ~5 Sekunden gedrückt werden kann, um die Geräte wach zu halten, während mit ihnen interagiert wird.",
|
||||||
"introduction": "ZHA-Befehle ausführen, die sich auf ein einzelnes Gerät auswirken. Wählen Sie ein Gerät aus, um eine Liste der verfügbaren Befehle anzuzeigen."
|
"introduction": "ZHA-Befehle ausführen, die sich auf ein einzelnes Gerät auswirken. Wählen Sie ein Gerät aus, um eine Liste der verfügbaren Befehle anzuzeigen."
|
||||||
},
|
},
|
||||||
"title": "Zigbee Home Automation"
|
"title": "Zigbee Home Automation",
|
||||||
|
"visualization": {
|
||||||
|
"caption": "Visualisierung",
|
||||||
|
"header": "Netzwerkvisualisierung"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"zone": {
|
"zone": {
|
||||||
"add_zone": "Zone hinzufügen",
|
"add_zone": "Zone hinzufügen",
|
||||||
@ -3401,7 +3424,7 @@
|
|||||||
},
|
},
|
||||||
"enable_shortcuts": {
|
"enable_shortcuts": {
|
||||||
"description": "Aktiviere oder deaktiviere Tastaturkürzel, um verschiedene Aktionen in der Benutzeroberfläche auszuführen.",
|
"description": "Aktiviere oder deaktiviere Tastaturkürzel, um verschiedene Aktionen in der Benutzeroberfläche auszuführen.",
|
||||||
"header": "Tastatürkürzel"
|
"header": "Tastaturkürzel"
|
||||||
},
|
},
|
||||||
"force_narrow": {
|
"force_narrow": {
|
||||||
"description": "Dies blendet die Seitenleiste standardmäßig aus, ähnlich der Nutzung auf Mobilgeräten.",
|
"description": "Dies blendet die Seitenleiste standardmäßig aus, ähnlich der Nutzung auf Mobilgeräten.",
|
||||||
|
@ -7,6 +7,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"groups": {
|
"groups": {
|
||||||
|
"owner": "Owner",
|
||||||
"system-admin": "Administrators",
|
"system-admin": "Administrators",
|
||||||
"system-read-only": "Read-Only Users",
|
"system-read-only": "Read-Only Users",
|
||||||
"system-users": "Users"
|
"system-users": "Users"
|
||||||
@ -1239,7 +1240,7 @@
|
|||||||
"edit_ui": "Edit with UI",
|
"edit_ui": "Edit with UI",
|
||||||
"edit_yaml": "Edit as YAML",
|
"edit_yaml": "Edit as YAML",
|
||||||
"enable_disable": "Enable/Disable automation",
|
"enable_disable": "Enable/Disable automation",
|
||||||
"introduction": "Use automations to bring your home alive.",
|
"introduction": "Use automations to bring your home to live.",
|
||||||
"load_error_not_editable": "Only automations in automations.yaml are editable.",
|
"load_error_not_editable": "Only automations in automations.yaml are editable.",
|
||||||
"load_error_unknown": "Error loading automation ({err_no}).",
|
"load_error_unknown": "Error loading automation ({err_no}).",
|
||||||
"max": {
|
"max": {
|
||||||
@ -1399,11 +1400,13 @@
|
|||||||
"blueprint": {
|
"blueprint": {
|
||||||
"add": {
|
"add": {
|
||||||
"error_no_url": "Please enter the URL of the blueprint.",
|
"error_no_url": "Please enter the URL of the blueprint.",
|
||||||
|
"file_name": "Local blueprint file name",
|
||||||
"header": "Add new blueprint",
|
"header": "Add new blueprint",
|
||||||
"import_btn": "Import blueprint",
|
"import_btn": "Import blueprint",
|
||||||
"import_header": "Import {name} ({domain})",
|
"import_header": "Import \"{name}\" (type: {domain})",
|
||||||
"import_introduction": "You can import blueprints of other users from Github and the community forums. Enter the URL of the blueprint below.",
|
"import_introduction": "You can import blueprints of other users from Github and the community forums. Enter the URL of the blueprint below.",
|
||||||
"importing": "Importing blueprint...",
|
"importing": "Importing blueprint...",
|
||||||
|
"raw_blueprint": "Blueprint content",
|
||||||
"save_btn": "Save blueprint",
|
"save_btn": "Save blueprint",
|
||||||
"saving": "Saving blueprint...",
|
"saving": "Saving blueprint...",
|
||||||
"unsupported_blueprint": "This blueprint is not supported",
|
"unsupported_blueprint": "This blueprint is not supported",
|
||||||
@ -1412,15 +1415,19 @@
|
|||||||
"caption": "Blueprints",
|
"caption": "Blueprints",
|
||||||
"description": "Manage blueprints",
|
"description": "Manage blueprints",
|
||||||
"overview": {
|
"overview": {
|
||||||
"add_blueprint": "Add blueprint",
|
"add_blueprint": "Import blueprint",
|
||||||
"confirm_delete_header": "Delete this blueprint?",
|
"confirm_delete_header": "Delete this blueprint?",
|
||||||
"confirm_delete_text": "Are you sure you want to delete this blueprint",
|
"confirm_delete_text": "Are you sure you want to delete this blueprint?",
|
||||||
|
"delete_blueprint": "Delete blueprint",
|
||||||
"header": "Blueprint Editor",
|
"header": "Blueprint Editor",
|
||||||
"headers": {
|
"headers": {
|
||||||
|
"domain": "Domain",
|
||||||
|
"file_name": "File name",
|
||||||
"name": "Name"
|
"name": "Name"
|
||||||
},
|
},
|
||||||
"introduction": "The blueprint editor allows you to create and edit blueprints.",
|
"introduction": "The blueprint editor allows you to create and edit blueprints.",
|
||||||
"learn_more": "Learn more about blueprints"
|
"learn_more": "Learn more about blueprints",
|
||||||
|
"use_blueprint": "Create automation"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"cloud": {
|
"cloud": {
|
||||||
@ -1737,6 +1744,7 @@
|
|||||||
},
|
},
|
||||||
"header": "Entities",
|
"header": "Entities",
|
||||||
"headers": {
|
"headers": {
|
||||||
|
"area": "Area",
|
||||||
"entity_id": "Entity ID",
|
"entity_id": "Entity ID",
|
||||||
"integration": "Integration",
|
"integration": "Integration",
|
||||||
"name": "Name",
|
"name": "Name",
|
||||||
@ -2209,7 +2217,7 @@
|
|||||||
"without_device": "Entities without device"
|
"without_device": "Entities without device"
|
||||||
},
|
},
|
||||||
"icon": "Icon",
|
"icon": "Icon",
|
||||||
"introduction": "Use scenes to bring your home alive.",
|
"introduction": "Use scenes to bring your home to live.",
|
||||||
"load_error_not_editable": "Only scenes in scenes.yaml are editable.",
|
"load_error_not_editable": "Only scenes in scenes.yaml are editable.",
|
||||||
"load_error_unknown": "Error loading scene ({err_no}).",
|
"load_error_unknown": "Error loading scene ({err_no}).",
|
||||||
"name": "Name",
|
"name": "Name",
|
||||||
@ -2393,7 +2401,7 @@
|
|||||||
"delete_user": "Delete user",
|
"delete_user": "Delete user",
|
||||||
"group": "Group",
|
"group": "Group",
|
||||||
"id": "ID",
|
"id": "ID",
|
||||||
"name": "Name",
|
"name": "Display name",
|
||||||
"new_password": "New Password",
|
"new_password": "New Password",
|
||||||
"owner": "Owner",
|
"owner": "Owner",
|
||||||
"password_changed": "Password was changed successfully",
|
"password_changed": "Password was changed successfully",
|
||||||
@ -2401,19 +2409,24 @@
|
|||||||
"system_generated_users_not_editable": "Unable to update system generated users.",
|
"system_generated_users_not_editable": "Unable to update system generated users.",
|
||||||
"system_generated_users_not_removable": "Unable to remove system generated users.",
|
"system_generated_users_not_removable": "Unable to remove system generated users.",
|
||||||
"unnamed_user": "Unnamed User",
|
"unnamed_user": "Unnamed User",
|
||||||
"update_user": "Update"
|
"update_user": "Update",
|
||||||
|
"username": "Username"
|
||||||
},
|
},
|
||||||
"picker": {
|
"picker": {
|
||||||
"add_user": "Add user",
|
"add_user": "Add user",
|
||||||
"headers": {
|
"headers": {
|
||||||
"group": "Group",
|
"group": "Group",
|
||||||
"name": "Name",
|
"is_active": "Active",
|
||||||
"system": "System"
|
"is_owner": "Owner",
|
||||||
|
"name": "Display name",
|
||||||
|
"system": "System generated",
|
||||||
|
"username": "Username"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"users_privileges_note": "The user group feature is a work in progress. The user will be unable to administer the instance via the UI. We're still auditing all management API endpoints to ensure that they correctly limit access to administrators."
|
"users_privileges_note": "The user group feature is a work in progress. The user will be unable to administer the instance via the UI. We're still auditing all management API endpoints to ensure that they correctly limit access to administrators."
|
||||||
},
|
},
|
||||||
"zha": {
|
"zha": {
|
||||||
|
"add_device": "Add Device",
|
||||||
"add_device_page": {
|
"add_device_page": {
|
||||||
"discovered_text": "Devices will show up here once discovered.",
|
"discovered_text": "Devices will show up here once discovered.",
|
||||||
"discovery_text": "Discovered devices will show up here. Follow the instructions for your device(s) and place the device(s) in pairing mode.",
|
"discovery_text": "Discovered devices will show up here. Follow the instructions for your device(s) and place the device(s) in pairing mode.",
|
||||||
@ -2459,6 +2472,16 @@
|
|||||||
"value": "Value"
|
"value": "Value"
|
||||||
},
|
},
|
||||||
"description": "Zigbee Home Automation network management",
|
"description": "Zigbee Home Automation network management",
|
||||||
|
"device_pairing_card": {
|
||||||
|
"CONFIGURED": "Configuration Complete",
|
||||||
|
"CONFIGURED_status_text": "Initializing",
|
||||||
|
"INITIALIZED": "Initialization Complete",
|
||||||
|
"INITIALIZED_status_text": "The device is ready to use",
|
||||||
|
"INTERVIEW_COMPLETE": "Interview Complete",
|
||||||
|
"INTERVIEW_COMPLETE_status_text": "Configuring",
|
||||||
|
"PAIRED": "Device Found",
|
||||||
|
"PAIRED_status_text": "Starting Interview"
|
||||||
|
},
|
||||||
"devices": {
|
"devices": {
|
||||||
"header": "Zigbee Home Automation - Device"
|
"header": "Zigbee Home Automation - Device"
|
||||||
},
|
},
|
||||||
@ -2474,6 +2497,7 @@
|
|||||||
"unbind_button_label": "Unbind Group"
|
"unbind_button_label": "Unbind Group"
|
||||||
},
|
},
|
||||||
"groups": {
|
"groups": {
|
||||||
|
"add_group": "Add Group",
|
||||||
"add_members": "Add Members",
|
"add_members": "Add Members",
|
||||||
"adding_members": "Adding Members",
|
"adding_members": "Adding Members",
|
||||||
"caption": "Groups",
|
"caption": "Groups",
|
||||||
@ -2516,7 +2540,11 @@
|
|||||||
"hint_wakeup": "Some devices such as Xiaomi sensors have a wake up button that you can press at ~5 second intervals that keep devices awake while you interact with them.",
|
"hint_wakeup": "Some devices such as Xiaomi sensors have a wake up button that you can press at ~5 second intervals that keep devices awake while you interact with them.",
|
||||||
"introduction": "Run ZHA commands that affect a single device. Pick a device to see a list of available commands."
|
"introduction": "Run ZHA commands that affect a single device. Pick a device to see a list of available commands."
|
||||||
},
|
},
|
||||||
"title": "Zigbee Home Automation"
|
"title": "Zigbee Home Automation",
|
||||||
|
"visualization": {
|
||||||
|
"caption": "Visualization",
|
||||||
|
"header": "Network Visualization"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"zone": {
|
"zone": {
|
||||||
"add_zone": "Add Zone",
|
"add_zone": "Add Zone",
|
||||||
|
@ -7,6 +7,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"groups": {
|
"groups": {
|
||||||
|
"owner": "Propietario",
|
||||||
"system-admin": "Administradores",
|
"system-admin": "Administradores",
|
||||||
"system-read-only": "Usuarios de solo lectura",
|
"system-read-only": "Usuarios de solo lectura",
|
||||||
"system-users": "Usuarios"
|
"system-users": "Usuarios"
|
||||||
@ -1147,7 +1148,7 @@
|
|||||||
},
|
},
|
||||||
"alias": "Nombre",
|
"alias": "Nombre",
|
||||||
"blueprint": {
|
"blueprint": {
|
||||||
"blueprint_to_use": "Plano a utilizar",
|
"blueprint_to_use": "Plano a usar",
|
||||||
"header": "Plano",
|
"header": "Plano",
|
||||||
"inputs": "Entradas",
|
"inputs": "Entradas",
|
||||||
"manage_blueprints": "Administrar planos",
|
"manage_blueprints": "Administrar planos",
|
||||||
@ -1239,7 +1240,7 @@
|
|||||||
"edit_ui": "Editar con la interfaz de usuario",
|
"edit_ui": "Editar con la interfaz de usuario",
|
||||||
"edit_yaml": "Editar como YAML",
|
"edit_yaml": "Editar como YAML",
|
||||||
"enable_disable": "Activar/Desactivar automatización",
|
"enable_disable": "Activar/Desactivar automatización",
|
||||||
"introduction": "Utiliza automatizaciones para darle vida a tu hogar.",
|
"introduction": "Usa automatizaciones para darle vida a tu hogar.",
|
||||||
"load_error_not_editable": "Solo las automatizaciones en automations.yaml son editables.",
|
"load_error_not_editable": "Solo las automatizaciones en automations.yaml son editables.",
|
||||||
"load_error_unknown": "Error al cargar la automatización ({err_no}).",
|
"load_error_unknown": "Error al cargar la automatización ({err_no}).",
|
||||||
"max": {
|
"max": {
|
||||||
@ -1399,11 +1400,13 @@
|
|||||||
"blueprint": {
|
"blueprint": {
|
||||||
"add": {
|
"add": {
|
||||||
"error_no_url": "Por favor, introduce la URL del plano.",
|
"error_no_url": "Por favor, introduce la URL del plano.",
|
||||||
|
"file_name": "Nombre de archivo del plano local",
|
||||||
"header": "Añadir nuevo plano",
|
"header": "Añadir nuevo plano",
|
||||||
"import_btn": "Importar plano",
|
"import_btn": "Importar plano",
|
||||||
"import_header": "Importar {name} ({domain})",
|
"import_header": "Importar {name} (tipo: {domain})",
|
||||||
"import_introduction": "Puedes importar planos de otros usuarios desde Github y los foros de la comunidad. Introduce la URL del plano a continuación.",
|
"import_introduction": "Puedes importar planos de otros usuarios desde Github y los foros de la comunidad. Introduce la URL del plano a continuación.",
|
||||||
"importing": "Importando plano...",
|
"importing": "Importando plano...",
|
||||||
|
"raw_blueprint": "Contenido del plano",
|
||||||
"save_btn": "Guardar plano",
|
"save_btn": "Guardar plano",
|
||||||
"saving": "Guardando plano...",
|
"saving": "Guardando plano...",
|
||||||
"unsupported_blueprint": "Este plano no es compatible",
|
"unsupported_blueprint": "Este plano no es compatible",
|
||||||
@ -1412,15 +1415,19 @@
|
|||||||
"caption": "Planos",
|
"caption": "Planos",
|
||||||
"description": "Administrar planos",
|
"description": "Administrar planos",
|
||||||
"overview": {
|
"overview": {
|
||||||
"add_blueprint": "Añadir plano",
|
"add_blueprint": "Importar plano",
|
||||||
"confirm_delete_header": "¿Eliminar este plano?",
|
"confirm_delete_header": "¿Eliminar este plano?",
|
||||||
"confirm_delete_text": "¿Estás seguro de que quieres borrar este plano?",
|
"confirm_delete_text": "¿Estás seguro de que quieres borrar este plano?",
|
||||||
|
"delete_blueprint": "Eliminar plano",
|
||||||
"header": "Editor de planos",
|
"header": "Editor de planos",
|
||||||
"headers": {
|
"headers": {
|
||||||
|
"domain": "Dominio",
|
||||||
|
"file_name": "Nombre de archivo",
|
||||||
"name": "Nombre"
|
"name": "Nombre"
|
||||||
},
|
},
|
||||||
"introduction": "El editor de planos te permite crear y editar planos.",
|
"introduction": "El editor de planos te permite crear y editar planos.",
|
||||||
"learn_more": "Más información sobre planos"
|
"learn_more": "Más información sobre planos",
|
||||||
|
"use_blueprint": "Crear automatización"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"cloud": {
|
"cloud": {
|
||||||
@ -1737,6 +1744,7 @@
|
|||||||
},
|
},
|
||||||
"header": "Entidades",
|
"header": "Entidades",
|
||||||
"headers": {
|
"headers": {
|
||||||
|
"area": "Área",
|
||||||
"entity_id": "ID de entidad",
|
"entity_id": "ID de entidad",
|
||||||
"integration": "Integración",
|
"integration": "Integración",
|
||||||
"name": "Nombre",
|
"name": "Nombre",
|
||||||
@ -2209,7 +2217,7 @@
|
|||||||
"without_device": "Entidades sin dispositivo"
|
"without_device": "Entidades sin dispositivo"
|
||||||
},
|
},
|
||||||
"icon": "Icono",
|
"icon": "Icono",
|
||||||
"introduction": "Usa escenas para dar vida a tu hogar.",
|
"introduction": "Usa escenas para darle vida a tu hogar.",
|
||||||
"load_error_not_editable": "Solo las escenas de scenes.yaml son editables.",
|
"load_error_not_editable": "Solo las escenas de scenes.yaml son editables.",
|
||||||
"load_error_unknown": "Error al cargar la escena ({err_no}).",
|
"load_error_unknown": "Error al cargar la escena ({err_no}).",
|
||||||
"name": "Nombre",
|
"name": "Nombre",
|
||||||
@ -2393,7 +2401,7 @@
|
|||||||
"delete_user": "Eliminar usuario",
|
"delete_user": "Eliminar usuario",
|
||||||
"group": "Grupo",
|
"group": "Grupo",
|
||||||
"id": "ID",
|
"id": "ID",
|
||||||
"name": "Nombre",
|
"name": "Nombre para mostrar",
|
||||||
"new_password": "Nueva contraseña",
|
"new_password": "Nueva contraseña",
|
||||||
"owner": "Propietario",
|
"owner": "Propietario",
|
||||||
"password_changed": "La contraseña se ha cambiado correctamente",
|
"password_changed": "La contraseña se ha cambiado correctamente",
|
||||||
@ -2401,19 +2409,24 @@
|
|||||||
"system_generated_users_not_editable": "No se pueden actualizar los usuarios generados por el sistema.",
|
"system_generated_users_not_editable": "No se pueden actualizar los usuarios generados por el sistema.",
|
||||||
"system_generated_users_not_removable": "No se pueden eliminar los usuarios generados por el sistema.",
|
"system_generated_users_not_removable": "No se pueden eliminar los usuarios generados por el sistema.",
|
||||||
"unnamed_user": "Usuario sin nombre",
|
"unnamed_user": "Usuario sin nombre",
|
||||||
"update_user": "Actualizar"
|
"update_user": "Actualizar",
|
||||||
|
"username": "Nombre de usuario"
|
||||||
},
|
},
|
||||||
"picker": {
|
"picker": {
|
||||||
"add_user": "Añadir usuario",
|
"add_user": "Añadir usuario",
|
||||||
"headers": {
|
"headers": {
|
||||||
"group": "Grupo",
|
"group": "Grupo",
|
||||||
"name": "Nombre",
|
"is_active": "Activo",
|
||||||
"system": "Sistema"
|
"is_owner": "Propietario",
|
||||||
|
"name": "Nombre para mostrar",
|
||||||
|
"system": "Generado por el sistema",
|
||||||
|
"username": "Nombre de usuario"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"users_privileges_note": "El grupo de usuarios es un trabajo en progreso. El usuario no podrá administrar la instancia a través de la IU. Todavía estamos auditando todos los endpoints de la API de administración para garantizar que se limita correctamente el acceso sólo a los administradores."
|
"users_privileges_note": "El grupo de usuarios es un trabajo en progreso. El usuario no podrá administrar la instancia a través de la IU. Todavía estamos auditando todos los endpoints de la API de administración para garantizar que se limita correctamente el acceso sólo a los administradores."
|
||||||
},
|
},
|
||||||
"zha": {
|
"zha": {
|
||||||
|
"add_device": "Añadir dispositivo",
|
||||||
"add_device_page": {
|
"add_device_page": {
|
||||||
"discovered_text": "Los dispositivos aparecerán aquí una vez descubiertos.",
|
"discovered_text": "Los dispositivos aparecerán aquí una vez descubiertos.",
|
||||||
"discovery_text": "Los dispositivos detectados aparecerán aquí. Ponlos en modo emparejamiento siguiendo sus instrucciones.",
|
"discovery_text": "Los dispositivos detectados aparecerán aquí. Ponlos en modo emparejamiento siguiendo sus instrucciones.",
|
||||||
@ -2459,6 +2472,16 @@
|
|||||||
"value": "Valor"
|
"value": "Valor"
|
||||||
},
|
},
|
||||||
"description": "Administración de red de Domótica Zigbee",
|
"description": "Administración de red de Domótica Zigbee",
|
||||||
|
"device_pairing_card": {
|
||||||
|
"CONFIGURED": "Configuración completada",
|
||||||
|
"CONFIGURED_status_text": "Inicializando",
|
||||||
|
"INITIALIZED": "Inicialización completada",
|
||||||
|
"INITIALIZED_status_text": "El dispositivo está listo para ser usado",
|
||||||
|
"INTERVIEW_COMPLETE": "Entrevista completada",
|
||||||
|
"INTERVIEW_COMPLETE_status_text": "Configurando",
|
||||||
|
"PAIRED": "Dispositivo encontrado",
|
||||||
|
"PAIRED_status_text": "Iniciando entrevista"
|
||||||
|
},
|
||||||
"devices": {
|
"devices": {
|
||||||
"header": "Domótica Zigbee - Dispositivo"
|
"header": "Domótica Zigbee - Dispositivo"
|
||||||
},
|
},
|
||||||
@ -2474,6 +2497,7 @@
|
|||||||
"unbind_button_label": "Desvincular grupo"
|
"unbind_button_label": "Desvincular grupo"
|
||||||
},
|
},
|
||||||
"groups": {
|
"groups": {
|
||||||
|
"add_group": "Añadir grupo",
|
||||||
"add_members": "Añadir miembros",
|
"add_members": "Añadir miembros",
|
||||||
"adding_members": "Agregar miembros",
|
"adding_members": "Agregar miembros",
|
||||||
"caption": "Grupos",
|
"caption": "Grupos",
|
||||||
@ -2516,7 +2540,11 @@
|
|||||||
"hint_wakeup": "Algunos dispositivos, como los sensores Xiaomi, tienen un botón de activación que puedes presionar a intervalos de ~ 5 segundos para mantener los dispositivos despiertos mientras interactúas con ellos.",
|
"hint_wakeup": "Algunos dispositivos, como los sensores Xiaomi, tienen un botón de activación que puedes presionar a intervalos de ~ 5 segundos para mantener los dispositivos despiertos mientras interactúas con ellos.",
|
||||||
"introduction": "Ejecuta comandos ZHA que afecten a un único dispositivo. Elije un dispositivo para ver una lista de comandos disponibles."
|
"introduction": "Ejecuta comandos ZHA que afecten a un único dispositivo. Elije un dispositivo para ver una lista de comandos disponibles."
|
||||||
},
|
},
|
||||||
"title": "Domótica Zigbee"
|
"title": "Domótica Zigbee",
|
||||||
|
"visualization": {
|
||||||
|
"caption": "Visualización",
|
||||||
|
"header": "Visualización de la red"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"zone": {
|
"zone": {
|
||||||
"add_zone": "Añadir zona",
|
"add_zone": "Añadir zona",
|
||||||
|
@ -7,6 +7,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"groups": {
|
"groups": {
|
||||||
|
"owner": "Omanik",
|
||||||
"system-admin": "Administraatorid",
|
"system-admin": "Administraatorid",
|
||||||
"system-read-only": "Ainult lugemisõigusega kasutajad",
|
"system-read-only": "Ainult lugemisõigusega kasutajad",
|
||||||
"system-users": "Kasutajad"
|
"system-users": "Kasutajad"
|
||||||
@ -967,7 +968,7 @@
|
|||||||
"manuf": "{manufacturer} järgi",
|
"manuf": "{manufacturer} järgi",
|
||||||
"no_area": "Ala puudub",
|
"no_area": "Ala puudub",
|
||||||
"power_source": "Toiteallikas",
|
"power_source": "Toiteallikas",
|
||||||
"quirk": "Erim",
|
"quirk": "Pistikäpp",
|
||||||
"services": {
|
"services": {
|
||||||
"reconfigure": "Taasseadista (tervenda) ZHA seade. Kasuta seda, kui seadmega on probleeme. Kui seade on akutoitega, siis veendu, et see oleks ärkvel ja oleks valmis käske vastu võtma.",
|
"reconfigure": "Taasseadista (tervenda) ZHA seade. Kasuta seda, kui seadmega on probleeme. Kui seade on akutoitega, siis veendu, et see oleks ärkvel ja oleks valmis käske vastu võtma.",
|
||||||
"remove": "Eemalda seade Zigbee võrgust.",
|
"remove": "Eemalda seade Zigbee võrgust.",
|
||||||
@ -1399,11 +1400,13 @@
|
|||||||
"blueprint": {
|
"blueprint": {
|
||||||
"add": {
|
"add": {
|
||||||
"error_no_url": "Sisesta kavandi URL.",
|
"error_no_url": "Sisesta kavandi URL.",
|
||||||
|
"file_name": "Kohaliku kavandifaili nimi",
|
||||||
"header": "Uue kavandi lisamine",
|
"header": "Uue kavandi lisamine",
|
||||||
"import_btn": "Impordi kavand",
|
"import_btn": "Impordi kavand",
|
||||||
"import_header": "Impordi {name} ( {domain} )",
|
"import_header": "Impordi {name} ( type:{domain} )",
|
||||||
"import_introduction": "Teiste kasutajate kavandeid saad importida Githubist ja kogukonna foorumitest. Sisesta alloleva kavandi URL.",
|
"import_introduction": "Teiste kasutajate kavandeid saad importida Githubist ja kogukonna foorumitest. Sisesta alloleva kavandi URL.",
|
||||||
"importing": "Kavandi importimine ...",
|
"importing": "Kavandi importimine ...",
|
||||||
|
"raw_blueprint": "Kavandi sisu",
|
||||||
"save_btn": "Salvesta kavand",
|
"save_btn": "Salvesta kavand",
|
||||||
"saving": "Kavandi salvestamine ...",
|
"saving": "Kavandi salvestamine ...",
|
||||||
"unsupported_blueprint": "Seda kavandit ei toetata",
|
"unsupported_blueprint": "Seda kavandit ei toetata",
|
||||||
@ -1412,15 +1415,19 @@
|
|||||||
"caption": "",
|
"caption": "",
|
||||||
"description": "Kavandite haldamine",
|
"description": "Kavandite haldamine",
|
||||||
"overview": {
|
"overview": {
|
||||||
"add_blueprint": "Lisa kavand",
|
"add_blueprint": "Laadi kavand",
|
||||||
"confirm_delete_header": "Kas kustutada see kavand?",
|
"confirm_delete_header": "Kas kustutada see kavand?",
|
||||||
"confirm_delete_text": "Kas soovid kindlasti selle kavandi kustutada?",
|
"confirm_delete_text": "Kas soovid kindlasti selle kavandi kustutada?",
|
||||||
|
"delete_blueprint": "Kustuta kavand",
|
||||||
"header": "Kavandi redaktor",
|
"header": "Kavandi redaktor",
|
||||||
"headers": {
|
"headers": {
|
||||||
|
"domain": "Domeen",
|
||||||
|
"file_name": "Faili nimi",
|
||||||
"name": "Nimi"
|
"name": "Nimi"
|
||||||
},
|
},
|
||||||
"introduction": "Kavandite redaktor võimaldab luua ja muuta kavandeid.",
|
"introduction": "Kavandite redaktor võimaldab luua ja muuta kavandeid.",
|
||||||
"learn_more": "Lisateave kavandite kohta"
|
"learn_more": "Lisateave kavandite kohta",
|
||||||
|
"use_blueprint": "Loo automatiseering"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"cloud": {
|
"cloud": {
|
||||||
@ -1737,6 +1744,7 @@
|
|||||||
},
|
},
|
||||||
"header": "Olemid",
|
"header": "Olemid",
|
||||||
"headers": {
|
"headers": {
|
||||||
|
"area": "Ala",
|
||||||
"entity_id": "Olemi ID",
|
"entity_id": "Olemi ID",
|
||||||
"integration": "Sidumine",
|
"integration": "Sidumine",
|
||||||
"name": "Nimi",
|
"name": "Nimi",
|
||||||
@ -2393,7 +2401,7 @@
|
|||||||
"delete_user": "Kustuta kasutaja",
|
"delete_user": "Kustuta kasutaja",
|
||||||
"group": "Grupp",
|
"group": "Grupp",
|
||||||
"id": "ID",
|
"id": "ID",
|
||||||
"name": "Nimi",
|
"name": "Kuvatav nimi",
|
||||||
"new_password": "Uus salasõna",
|
"new_password": "Uus salasõna",
|
||||||
"owner": "Omanik",
|
"owner": "Omanik",
|
||||||
"password_changed": "Salasõna muutmine õnnestus",
|
"password_changed": "Salasõna muutmine õnnestus",
|
||||||
@ -2401,19 +2409,24 @@
|
|||||||
"system_generated_users_not_editable": "Süsteemi loodud kasutajaid ei saa värskendada.",
|
"system_generated_users_not_editable": "Süsteemi loodud kasutajaid ei saa värskendada.",
|
||||||
"system_generated_users_not_removable": "Süsteemi loodud kasutajaid ei saa eemaldada.",
|
"system_generated_users_not_removable": "Süsteemi loodud kasutajaid ei saa eemaldada.",
|
||||||
"unnamed_user": "Nimetu kasutaja",
|
"unnamed_user": "Nimetu kasutaja",
|
||||||
"update_user": "Uuenda"
|
"update_user": "Uuenda",
|
||||||
|
"username": "Kasutajanimi"
|
||||||
},
|
},
|
||||||
"picker": {
|
"picker": {
|
||||||
"add_user": "Lisa kasutaja",
|
"add_user": "Lisa kasutaja",
|
||||||
"headers": {
|
"headers": {
|
||||||
"group": "Grupp",
|
"group": "Grupp",
|
||||||
"name": "Nimi",
|
"is_active": "Aktiivne",
|
||||||
"system": "Süsteem"
|
"is_owner": "Omanik",
|
||||||
|
"name": "Kuvatav nimi",
|
||||||
|
"system": "Süsteemi poolt loodud",
|
||||||
|
"username": "Kasutajanimi"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"users_privileges_note": "Kasutajarühma funktsioon on pooleli. Kasutajal ei ole võimalik kasutajaliidese kaudu Home Assistanti hallata. Auditeerime endiselt kõiki haldus-API lõpp-punkte tagamaks, et need lubaks juurdepääsu ainult administraatoritele."
|
"users_privileges_note": "Kasutajarühma funktsioon on pooleli. Kasutajal ei ole võimalik kasutajaliidese kaudu Home Assistanti hallata. Auditeerime endiselt kõiki haldus-API lõpp-punkte tagamaks, et need lubaks juurdepääsu ainult administraatoritele."
|
||||||
},
|
},
|
||||||
"zha": {
|
"zha": {
|
||||||
|
"add_device": "Lisa seade",
|
||||||
"add_device_page": {
|
"add_device_page": {
|
||||||
"discovered_text": "Seadmed ilmuvad siia kui avastatakse.",
|
"discovered_text": "Seadmed ilmuvad siia kui avastatakse.",
|
||||||
"discovery_text": "Leitud seadmed kuvatakse siin. Järgige seadme(te) juhiseid ja pange seade või seadmed sidumisrežiimile.",
|
"discovery_text": "Leitud seadmed kuvatakse siin. Järgige seadme(te) juhiseid ja pange seade või seadmed sidumisrežiimile.",
|
||||||
@ -2459,6 +2472,16 @@
|
|||||||
"value": "Väärtus"
|
"value": "Väärtus"
|
||||||
},
|
},
|
||||||
"description": "Zigbee Home Automation võrgu haldamine",
|
"description": "Zigbee Home Automation võrgu haldamine",
|
||||||
|
"device_pairing_card": {
|
||||||
|
"CONFIGURED": "Seadistamine on lõpetatud",
|
||||||
|
"CONFIGURED_status_text": "Lähtestan",
|
||||||
|
"INITIALIZED": "Lähtestamine on lõpetatud",
|
||||||
|
"INITIALIZED_status_text": "Seade on kasutamiseks valmis",
|
||||||
|
"INTERVIEW_COMPLETE": "Küsitlemine on lõpetatud",
|
||||||
|
"INTERVIEW_COMPLETE_status_text": "Seadistan",
|
||||||
|
"PAIRED": "Seade on leitud",
|
||||||
|
"PAIRED_status_text": "Alustan küsitlemist"
|
||||||
|
},
|
||||||
"devices": {
|
"devices": {
|
||||||
"header": "Zigbee Home Automation - Seade"
|
"header": "Zigbee Home Automation - Seade"
|
||||||
},
|
},
|
||||||
@ -2474,6 +2497,7 @@
|
|||||||
"unbind_button_label": "Sidesta grupp lahti"
|
"unbind_button_label": "Sidesta grupp lahti"
|
||||||
},
|
},
|
||||||
"groups": {
|
"groups": {
|
||||||
|
"add_group": "Lisa grupp",
|
||||||
"add_members": "Lisa liikmeid",
|
"add_members": "Lisa liikmeid",
|
||||||
"adding_members": "Liikmete lisamine",
|
"adding_members": "Liikmete lisamine",
|
||||||
"caption": "Grupid",
|
"caption": "Grupid",
|
||||||
@ -2516,7 +2540,11 @@
|
|||||||
"hint_wakeup": "Mõnel seadmel (näiteks Xiaomi anduritel) on äratusnupp mida saate vajutada ~ 5-sekundiliste intervallidega ja mis hoiab seadmeid ärkvel kui te nendega suhtlete.",
|
"hint_wakeup": "Mõnel seadmel (näiteks Xiaomi anduritel) on äratusnupp mida saate vajutada ~ 5-sekundiliste intervallidega ja mis hoiab seadmeid ärkvel kui te nendega suhtlete.",
|
||||||
"introduction": "Käivitage ZHA käsud, mis mõjutavad ühte seadet. Saadaolevate käskude loendi kuvamiseks valige seade."
|
"introduction": "Käivitage ZHA käsud, mis mõjutavad ühte seadet. Saadaolevate käskude loendi kuvamiseks valige seade."
|
||||||
},
|
},
|
||||||
"title": "Zigbee Home Automation"
|
"title": "Zigbee Home Automation",
|
||||||
|
"visualization": {
|
||||||
|
"caption": "Visualiseerimine",
|
||||||
|
"header": "Võrgu visualiseerimine"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"zone": {
|
"zone": {
|
||||||
"add_zone": "Lisa tsoon",
|
"add_zone": "Lisa tsoon",
|
||||||
|
@ -1171,7 +1171,7 @@
|
|||||||
"trigger": "Laukaisin"
|
"trigger": "Laukaisin"
|
||||||
},
|
},
|
||||||
"event": {
|
"event": {
|
||||||
"context_user_pick": "Lisää käyttäjä",
|
"context_user_pick": "Valitse käyttäjä",
|
||||||
"context_user_picked": "Käyttäjän laukaisutapahtuma",
|
"context_user_picked": "Käyttäjän laukaisutapahtuma",
|
||||||
"context_users": "Rajoita tapahtumiin, jotka ovat käynnistäneet",
|
"context_users": "Rajoita tapahtumiin, jotka ovat käynnistäneet",
|
||||||
"event_data": "Tapahtuman tietosisältö",
|
"event_data": "Tapahtuman tietosisältö",
|
||||||
@ -2113,6 +2113,7 @@
|
|||||||
"person": "Lataa henkilöt uudelleen",
|
"person": "Lataa henkilöt uudelleen",
|
||||||
"ping": "Lataa ping-binaarianturin entiteetit uudelleen",
|
"ping": "Lataa ping-binaarianturin entiteetit uudelleen",
|
||||||
"reload": "Lataa uudelleen {domain}",
|
"reload": "Lataa uudelleen {domain}",
|
||||||
|
"rest": "Uudelleenlataa rest-entiteetit ja ilmoita palveluiden kuuntelijoille",
|
||||||
"rpi_gpio": "Lataa Raspberry Pi GPIO -entiteetit uudelleen",
|
"rpi_gpio": "Lataa Raspberry Pi GPIO -entiteetit uudelleen",
|
||||||
"scene": "Lataa tilanteet uudelleen",
|
"scene": "Lataa tilanteet uudelleen",
|
||||||
"script": "Lataa skriptit uudelleen",
|
"script": "Lataa skriptit uudelleen",
|
||||||
@ -2862,7 +2863,7 @@
|
|||||||
},
|
},
|
||||||
"raw_editor": {
|
"raw_editor": {
|
||||||
"confirm_remove_config_text": "Lovelace käyttöliittymän näkymät luodaan automaattisesti alueistasi ja laitteistasi, jos poistat nykyisen määrityksen.",
|
"confirm_remove_config_text": "Lovelace käyttöliittymän näkymät luodaan automaattisesti alueistasi ja laitteistasi, jos poistat nykyisen määrityksen.",
|
||||||
"confirm_remove_config_title": "Haluatko varmasti poistaa Lovelace-käyttöliittymän asetukset? Lovelace käyttöliittymän asetukset luodaan automaattisesti alueistasi ja laitteistasi.",
|
"confirm_remove_config_title": "Haluatko varmasti poistaa Lovelace-käyttöliittymän asetukset?",
|
||||||
"confirm_unsaved_changes": "Sinulla on tallentamattomia muutoksia. Haluatko varmasti poistua?",
|
"confirm_unsaved_changes": "Sinulla on tallentamattomia muutoksia. Haluatko varmasti poistua?",
|
||||||
"confirm_unsaved_comments": "Asetuksesi sisältää kommentoituja rivejä. Kommentoituja rivejä ei tallenneta. Haluatko jatkaa?",
|
"confirm_unsaved_comments": "Asetuksesi sisältää kommentoituja rivejä. Kommentoituja rivejä ei tallenneta. Haluatko jatkaa?",
|
||||||
"error_invalid_config": "Asetuksesi ovat virheelliset: {error}",
|
"error_invalid_config": "Asetuksesi ovat virheelliset: {error}",
|
||||||
@ -2906,7 +2907,7 @@
|
|||||||
},
|
},
|
||||||
"menu": {
|
"menu": {
|
||||||
"close": "Sulje",
|
"close": "Sulje",
|
||||||
"configure_ui": "Määrittele käyttöliittymä",
|
"configure_ui": "Muokkaa käyttöliittymää",
|
||||||
"exit_edit_mode": "Poistu käyttöliittymän muokkaustilasta",
|
"exit_edit_mode": "Poistu käyttöliittymän muokkaustilasta",
|
||||||
"help": "Apua",
|
"help": "Apua",
|
||||||
"refresh": "Päivitä",
|
"refresh": "Päivitä",
|
||||||
@ -2915,7 +2916,7 @@
|
|||||||
},
|
},
|
||||||
"reload_lovelace": "Lataa Lovelace uudelleen",
|
"reload_lovelace": "Lataa Lovelace uudelleen",
|
||||||
"reload_resources": {
|
"reload_resources": {
|
||||||
"refresh_body": "Sinun täytyy päivittää sivu viimeistelläksesi uudelleenlatauksen, haluatko päivittää nyt?",
|
"refresh_body": "Sinun täytyy päivittää sivu viimeistelläksesi uudelleenlatauksen. Haluatko päivittää nyt?",
|
||||||
"refresh_header": "Haluatko päivittää?"
|
"refresh_header": "Haluatko päivittää?"
|
||||||
},
|
},
|
||||||
"unused_entities": {
|
"unused_entities": {
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user