mirror of
https://github.com/home-assistant/frontend.git
synced 2025-08-25 17:19:27 +00:00
Compare commits
22 Commits
fix-search
...
20220214.0
Author | SHA1 | Date | |
---|---|---|---|
![]() |
901677bbdf | ||
![]() |
8bb2374b1b | ||
![]() |
520896a3c2 | ||
![]() |
92db272759 | ||
![]() |
fc654d86c6 | ||
![]() |
523afe2f6f | ||
![]() |
460b9003fc | ||
![]() |
2ac0ad1d98 | ||
![]() |
a321432175 | ||
![]() |
63c9b3f830 | ||
![]() |
806b1296b0 | ||
![]() |
7f90ffa82f | ||
![]() |
db33c38e21 | ||
![]() |
a8c1fdd21e | ||
![]() |
d86a18b80b | ||
![]() |
bef6591548 | ||
![]() |
e1c07f109c | ||
![]() |
fb66d224ae | ||
![]() |
ee1fd3e865 | ||
![]() |
a9bfea233c | ||
![]() |
35cc291118 | ||
![]() |
db7cac5782 |
@@ -16,6 +16,9 @@
|
|||||||
"runem.lit-plugin",
|
"runem.lit-plugin",
|
||||||
"ms-python.vscode-pylance"
|
"ms-python.vscode-pylance"
|
||||||
],
|
],
|
||||||
|
"containerEnv": {
|
||||||
|
"WORKSPACE_DIRECTORY": "${containerWorkspaceFolder}"
|
||||||
|
},
|
||||||
"settings": {
|
"settings": {
|
||||||
"terminal.integrated.shell.linux": "/bin/bash",
|
"terminal.integrated.shell.linux": "/bin/bash",
|
||||||
"files.eol": "\n",
|
"files.eol": "\n",
|
||||||
|
@@ -33,6 +33,10 @@ module.exports.emptyPackages = ({ latestBuild, isHassioBuild }) =>
|
|||||||
require.resolve(
|
require.resolve(
|
||||||
path.resolve(paths.polymer_dir, "src/components/ha-icon.ts")
|
path.resolve(paths.polymer_dir, "src/components/ha-icon.ts")
|
||||||
),
|
),
|
||||||
|
isHassioBuild &&
|
||||||
|
require.resolve(
|
||||||
|
path.resolve(paths.polymer_dir, "src/components/ha-icon-picker.ts")
|
||||||
|
),
|
||||||
].filter(Boolean);
|
].filter(Boolean);
|
||||||
|
|
||||||
module.exports.definedVars = ({ isProdBuild, latestBuild, defineOverlay }) => ({
|
module.exports.definedVars = ({ isProdBuild, latestBuild, defineOverlay }) => ({
|
||||||
|
@@ -61,6 +61,12 @@ const SCHEMAS: {
|
|||||||
select: { options: ["Everyone Home", "Some Home", "All gone"] },
|
select: { options: ["Everyone Home", "Some Home", "All gone"] },
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "icon",
|
||||||
|
selector: {
|
||||||
|
icon: {},
|
||||||
|
},
|
||||||
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@@ -72,6 +72,7 @@ const SCHEMAS: {
|
|||||||
name: "Select",
|
name: "Select",
|
||||||
selector: { select: { options: ["Option 1", "Option 2"] } },
|
selector: { select: { options: ["Option 1", "Option 2"] } },
|
||||||
},
|
},
|
||||||
|
icon: { name: "Icon", selector: { icon: {} } },
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
@@ -221,13 +221,14 @@ class HassioAddonStore extends LitElement {
|
|||||||
margin-top: 24px;
|
margin-top: 24px;
|
||||||
}
|
}
|
||||||
.search {
|
.search {
|
||||||
padding: 0 16px;
|
position: sticky;
|
||||||
background: var(--sidebar-background-color);
|
top: 0;
|
||||||
border-bottom: 1px solid var(--divider-color);
|
z-index: 2;
|
||||||
}
|
}
|
||||||
.search search-input {
|
search-input {
|
||||||
position: relative;
|
display: block;
|
||||||
top: 2px;
|
--mdc-text-field-fill-color: var(--sidebar-background-color);
|
||||||
|
--mdc-text-field-idle-line-color: var(--divider-color);
|
||||||
}
|
}
|
||||||
.advanced {
|
.advanced {
|
||||||
padding: 12px;
|
padding: 12px;
|
||||||
|
@@ -1,7 +1,6 @@
|
|||||||
import "@material/mwc-button";
|
import "@material/mwc-button";
|
||||||
import "@polymer/paper-dropdown-menu/paper-dropdown-menu";
|
import "@material/mwc-select";
|
||||||
import "@polymer/paper-item/paper-item";
|
import "@material/mwc-list/mwc-list-item";
|
||||||
import "@polymer/paper-listbox/paper-listbox";
|
|
||||||
import {
|
import {
|
||||||
css,
|
css,
|
||||||
CSSResultGroup,
|
CSSResultGroup,
|
||||||
@@ -11,7 +10,7 @@ import {
|
|||||||
TemplateResult,
|
TemplateResult,
|
||||||
} from "lit";
|
} from "lit";
|
||||||
import { customElement, property, state } from "lit/decorators";
|
import { customElement, property, state } from "lit/decorators";
|
||||||
import "web-animations-js/web-animations-next-lite.min";
|
import { stopPropagation } from "../../../../src/common/dom/stop_propagation";
|
||||||
import "../../../../src/components/buttons/ha-progress-button";
|
import "../../../../src/components/buttons/ha-progress-button";
|
||||||
import "../../../../src/components/ha-alert";
|
import "../../../../src/components/ha-alert";
|
||||||
import "../../../../src/components/ha-card";
|
import "../../../../src/components/ha-card";
|
||||||
@@ -57,49 +56,44 @@ class HassioAddonAudio extends LitElement {
|
|||||||
${this._error
|
${this._error
|
||||||
? html`<ha-alert alert-type="error">${this._error}</ha-alert>`
|
? html`<ha-alert alert-type="error">${this._error}</ha-alert>`
|
||||||
: ""}
|
: ""}
|
||||||
|
${this._inputDevices &&
|
||||||
<paper-dropdown-menu
|
html`<mwc-select
|
||||||
.label=${this.supervisor.localize(
|
.label=${this.supervisor.localize(
|
||||||
"addon.configuration.audio.input"
|
"addon.configuration.audio.input"
|
||||||
)}
|
)}
|
||||||
@iron-select=${this._setInputDevice}
|
@selected=${this._setInputDevice}
|
||||||
|
@closed=${stopPropagation}
|
||||||
|
fixedMenuPosition
|
||||||
|
naturalMenuWidth
|
||||||
|
.value=${this._selectedInput!}
|
||||||
>
|
>
|
||||||
<paper-listbox
|
${this._inputDevices.map(
|
||||||
slot="dropdown-content"
|
(item) => html`
|
||||||
attr-for-selected="device"
|
<mwc-list-item .value=${item.device || ""}>
|
||||||
.selected=${this._selectedInput!}
|
${item.name}
|
||||||
>
|
</mwc-list-item>
|
||||||
${this._inputDevices &&
|
`
|
||||||
this._inputDevices.map(
|
)}
|
||||||
(item) => html`
|
</mwc-select>`}
|
||||||
<paper-item device=${item.device || ""}>
|
${this._outputDevices &&
|
||||||
${item.name}
|
html`<mwc-select
|
||||||
</paper-item>
|
|
||||||
`
|
|
||||||
)}
|
|
||||||
</paper-listbox>
|
|
||||||
</paper-dropdown-menu>
|
|
||||||
<paper-dropdown-menu
|
|
||||||
.label=${this.supervisor.localize(
|
.label=${this.supervisor.localize(
|
||||||
"addon.configuration.audio.output"
|
"addon.configuration.audio.output"
|
||||||
)}
|
)}
|
||||||
@iron-select=${this._setOutputDevice}
|
@selected=${this._setOutputDevice}
|
||||||
|
@closed=${stopPropagation}
|
||||||
|
fixedMenuPosition
|
||||||
|
naturalMenuWidth
|
||||||
|
.value=${this._selectedOutput!}
|
||||||
>
|
>
|
||||||
<paper-listbox
|
${this._outputDevices.map(
|
||||||
slot="dropdown-content"
|
(item) => html`
|
||||||
attr-for-selected="device"
|
<mwc-list-item .value=${item.device || ""}
|
||||||
.selected=${this._selectedOutput!}
|
>${item.name}</mwc-list-item
|
||||||
>
|
>
|
||||||
${this._outputDevices &&
|
`
|
||||||
this._outputDevices.map(
|
)}
|
||||||
(item) => html`
|
</mwc-select>`}
|
||||||
<paper-item device=${item.device || ""}
|
|
||||||
>${item.name}</paper-item
|
|
||||||
>
|
|
||||||
`
|
|
||||||
)}
|
|
||||||
</paper-listbox>
|
|
||||||
</paper-dropdown-menu>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="card-actions">
|
<div class="card-actions">
|
||||||
<ha-progress-button @click=${this._saveSettings}>
|
<ha-progress-button @click=${this._saveSettings}>
|
||||||
@@ -126,24 +120,30 @@ class HassioAddonAudio extends LitElement {
|
|||||||
.card-actions {
|
.card-actions {
|
||||||
text-align: right;
|
text-align: right;
|
||||||
}
|
}
|
||||||
|
mwc-select {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
mwc-select:last-child {
|
||||||
|
margin-top: 8px;
|
||||||
|
}
|
||||||
`,
|
`,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
protected update(changedProperties: PropertyValues): void {
|
protected willUpdate(changedProperties: PropertyValues): void {
|
||||||
super.update(changedProperties);
|
super.willUpdate(changedProperties);
|
||||||
if (changedProperties.has("addon")) {
|
if (changedProperties.has("addon")) {
|
||||||
this._addonChanged();
|
this._addonChanged();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private _setInputDevice(ev): void {
|
private _setInputDevice(ev): void {
|
||||||
const device = ev.detail.item.getAttribute("device");
|
const device = ev.target.value;
|
||||||
this._selectedInput = device;
|
this._selectedInput = device;
|
||||||
}
|
}
|
||||||
|
|
||||||
private _setOutputDevice(ev): void {
|
private _setOutputDevice(ev): void {
|
||||||
const device = ev.detail.item.getAttribute("device");
|
const device = ev.target.value;
|
||||||
this._selectedOutput = device;
|
this._selectedOutput = device;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -148,7 +148,6 @@ export class HassioUpdate extends LitElement {
|
|||||||
}
|
}
|
||||||
ha-settings-row {
|
ha-settings-row {
|
||||||
padding: 0;
|
padding: 0;
|
||||||
--paper-item-body-two-line-min-height: 32px;
|
|
||||||
}
|
}
|
||||||
`,
|
`,
|
||||||
];
|
];
|
||||||
|
@@ -1,6 +1,5 @@
|
|||||||
import "@polymer/paper-dropdown-menu/paper-dropdown-menu";
|
import "@material/mwc-list/mwc-list-item";
|
||||||
import "@polymer/paper-item/paper-item";
|
import "@material/mwc-select";
|
||||||
import "@polymer/paper-listbox/paper-listbox";
|
|
||||||
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
||||||
import { customElement, property, state } from "lit/decorators";
|
import { customElement, property, state } from "lit/decorators";
|
||||||
import memoizeOne from "memoize-one";
|
import memoizeOne from "memoize-one";
|
||||||
@@ -90,18 +89,19 @@ class HassioDatadiskDialog extends LitElement {
|
|||||||
)}
|
)}
|
||||||
<br /><br />
|
<br /><br />
|
||||||
|
|
||||||
<paper-dropdown-menu
|
<mwc-select
|
||||||
.label=${this.dialogParams.supervisor.localize(
|
.label=${this.dialogParams.supervisor.localize(
|
||||||
"dialog.datadisk_move.select_device"
|
"dialog.datadisk_move.select_device"
|
||||||
)}
|
)}
|
||||||
@value-changed=${this._select_device}
|
@selected=${this._select_device}
|
||||||
>
|
>
|
||||||
<paper-listbox slot="dropdown-content">
|
${this.devices.map(
|
||||||
${this.devices.map(
|
(device) =>
|
||||||
(device) => html`<paper-item>${device}</paper-item>`
|
html`<mwc-list-item .value=${device}
|
||||||
)}
|
>${device}</mwc-list-item
|
||||||
</paper-listbox>
|
>`
|
||||||
</paper-dropdown-menu>
|
)}
|
||||||
|
</mwc-select>
|
||||||
`
|
`
|
||||||
: this.devices === undefined
|
: this.devices === undefined
|
||||||
? this.dialogParams.supervisor.localize(
|
? this.dialogParams.supervisor.localize(
|
||||||
@@ -130,8 +130,8 @@ class HassioDatadiskDialog extends LitElement {
|
|||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
private _select_device(event) {
|
private _select_device(ev) {
|
||||||
this.selectedDevice = event.detail.value;
|
this.selectedDevice = ev.target.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async _moveDatadisk() {
|
private async _moveDatadisk() {
|
||||||
|
@@ -178,7 +178,7 @@ class HassioHardwareDialog extends LitElement {
|
|||||||
padding: 0.2em 0.4em;
|
padding: 0.2em 0.4em;
|
||||||
}
|
}
|
||||||
search-input {
|
search-input {
|
||||||
margin: 0 16px;
|
margin: 8px 16px 0;
|
||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
.device-property {
|
.device-property {
|
||||||
|
@@ -1,7 +1,4 @@
|
|||||||
import "@material/mwc-button";
|
import "@material/mwc-button";
|
||||||
import "@polymer/paper-dropdown-menu/paper-dropdown-menu";
|
|
||||||
import "@polymer/paper-item/paper-item";
|
|
||||||
import "@polymer/paper-listbox/paper-listbox";
|
|
||||||
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
||||||
import { customElement, property, state } from "lit/decorators";
|
import { customElement, property, state } from "lit/decorators";
|
||||||
import "../../../src/components/buttons/ha-progress-button";
|
import "../../../src/components/buttons/ha-progress-button";
|
||||||
@@ -73,24 +70,19 @@ class HassioSupervisorLog extends LitElement {
|
|||||||
: ""}
|
: ""}
|
||||||
${this.hass.userData?.showAdvanced
|
${this.hass.userData?.showAdvanced
|
||||||
? html`
|
? html`
|
||||||
<paper-dropdown-menu
|
<mwc-select
|
||||||
.label=${this.supervisor.localize("system.log.log_provider")}
|
.label=${this.supervisor.localize("system.log.log_provider")}
|
||||||
@iron-select=${this._setLogProvider}
|
@selected=${this._setLogProvider}
|
||||||
|
.value=${this._selectedLogProvider}
|
||||||
>
|
>
|
||||||
<paper-listbox
|
${logProviders.map(
|
||||||
slot="dropdown-content"
|
(provider) => html`
|
||||||
attr-for-selected="provider"
|
<mwc-list-item .value=${provider.key}>
|
||||||
.selected=${this._selectedLogProvider}
|
${provider.name}
|
||||||
>
|
</mwc-list-item>
|
||||||
${logProviders.map(
|
`
|
||||||
(provider) => html`
|
)}
|
||||||
<paper-item provider=${provider.key}>
|
</mwc-select>
|
||||||
${provider.name}
|
|
||||||
</paper-item>
|
|
||||||
`
|
|
||||||
)}
|
|
||||||
</paper-listbox>
|
|
||||||
</paper-dropdown-menu>
|
|
||||||
`
|
`
|
||||||
: ""}
|
: ""}
|
||||||
|
|
||||||
@@ -110,7 +102,7 @@ class HassioSupervisorLog extends LitElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private async _setLogProvider(ev): Promise<void> {
|
private async _setLogProvider(ev): Promise<void> {
|
||||||
const provider = ev.detail.item.getAttribute("provider");
|
const provider = ev.target.value;
|
||||||
this._selectedLogProvider = provider;
|
this._selectedLogProvider = provider;
|
||||||
this._loadData();
|
this._loadData();
|
||||||
}
|
}
|
||||||
@@ -153,9 +145,9 @@ class HassioSupervisorLog extends LitElement {
|
|||||||
pre {
|
pre {
|
||||||
white-space: pre-wrap;
|
white-space: pre-wrap;
|
||||||
}
|
}
|
||||||
paper-dropdown-menu {
|
mwc-select {
|
||||||
padding: 0 2%;
|
width: 100%;
|
||||||
width: 96%;
|
margin-bottom: 4px;
|
||||||
}
|
}
|
||||||
`,
|
`,
|
||||||
];
|
];
|
||||||
|
@@ -10,7 +10,6 @@ import {
|
|||||||
import { customElement, property, state } from "lit/decorators";
|
import { customElement, property, state } from "lit/decorators";
|
||||||
import memoizeOne from "memoize-one";
|
import memoizeOne from "memoize-one";
|
||||||
import { fireEvent } from "../../../src/common/dom/fire_event";
|
import { fireEvent } from "../../../src/common/dom/fire_event";
|
||||||
import "../../../src/common/search/search-input";
|
|
||||||
import "../../../src/components/buttons/ha-progress-button";
|
import "../../../src/components/buttons/ha-progress-button";
|
||||||
import "../../../src/components/ha-alert";
|
import "../../../src/components/ha-alert";
|
||||||
import "../../../src/components/ha-button-menu";
|
import "../../../src/components/ha-button-menu";
|
||||||
|
18
script/core
18
script/core
@@ -4,6 +4,8 @@
|
|||||||
# Stop on errors
|
# Stop on errors
|
||||||
set -e
|
set -e
|
||||||
|
|
||||||
|
WD="${WORKSPACE_DIRECTORY:=/workspaces/frontend}"
|
||||||
|
|
||||||
if [ -z "${DEVCONTAINER}" ]; then
|
if [ -z "${DEVCONTAINER}" ]; then
|
||||||
echo "This task should only run inside a devcontainer, for local install HA Core in a venv."
|
echo "This task should only run inside a devcontainer, for local install HA Core in a venv."
|
||||||
exit 1
|
exit 1
|
||||||
@@ -16,9 +18,9 @@ if [ -z $(which hass) ]; then
|
|||||||
git+git://github.com/home-assistant/home-assistant.git@dev
|
git+git://github.com/home-assistant/home-assistant.git@dev
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ ! -d "/workspaces/frontend/config" ]; then
|
if [ ! -d "${WD}/config" ]; then
|
||||||
echo "Creating default configuration."
|
echo "Creating default configuration."
|
||||||
mkdir -p "/workspaces/frontend/config";
|
mkdir -p "${WD}/config";
|
||||||
hass --script ensure_config -c config
|
hass --script ensure_config -c config
|
||||||
echo "demo:
|
echo "demo:
|
||||||
|
|
||||||
@@ -26,24 +28,24 @@ logger:
|
|||||||
default: info
|
default: info
|
||||||
logs:
|
logs:
|
||||||
homeassistant.components.frontend: debug
|
homeassistant.components.frontend: debug
|
||||||
" >> /workspaces/frontend/config/configuration.yaml
|
" >> "${WD}/config/configuration.yaml"
|
||||||
|
|
||||||
if [ ! -z "${HASSIO}" ]; then
|
if [ ! -z "${HASSIO}" ]; then
|
||||||
echo "
|
echo "
|
||||||
# frontend:
|
# frontend:
|
||||||
# development_repo: /workspaces/frontend
|
# development_repo: ${WD}
|
||||||
|
|
||||||
hassio:
|
hassio:
|
||||||
development_repo: /workspaces/frontend" >> /workspaces/frontend/config/configuration.yaml
|
development_repo: ${WD}" >> "${WD}/config/configuration.yaml"
|
||||||
else
|
else
|
||||||
echo "
|
echo "
|
||||||
frontend:
|
frontend:
|
||||||
development_repo: /workspaces/frontend
|
development_repo: ${WD}
|
||||||
|
|
||||||
# hassio:
|
# hassio:
|
||||||
# development_repo: /workspaces/frontend" >> /workspaces/frontend/config/configuration.yaml
|
# development_repo: ${WD}" >> "${WD}/config/configuration.yaml"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
hass -c /workspaces/frontend/config
|
hass -c "${WD}/config"
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
[metadata]
|
[metadata]
|
||||||
name = home-assistant-frontend
|
name = home-assistant-frontend
|
||||||
version = 20220203.0
|
version = 20220214.0
|
||||||
author = The Home Assistant Authors
|
author = The Home Assistant Authors
|
||||||
author_email = hello@home-assistant.io
|
author_email = hello@home-assistant.io
|
||||||
license = Apache-2.0
|
license = Apache-2.0
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
import { HomeAssistant } from "../../types";
|
import type { HomeAssistant } from "../../types";
|
||||||
|
|
||||||
export const canToggleDomain = (hass: HomeAssistant, domain: string) => {
|
export const canToggleDomain = (hass: HomeAssistant, domain: string) => {
|
||||||
const services = hass.services[domain];
|
const services = hass.services[domain];
|
||||||
|
@@ -1,14 +1,30 @@
|
|||||||
import { HassEntity } from "home-assistant-js-websocket";
|
import type { HassEntity } from "home-assistant-js-websocket";
|
||||||
import { HomeAssistant } from "../../types";
|
import type { HomeAssistant } from "../../types";
|
||||||
import { canToggleDomain } from "./can_toggle_domain";
|
import { canToggleDomain } from "./can_toggle_domain";
|
||||||
import { computeStateDomain } from "./compute_state_domain";
|
import { computeStateDomain } from "./compute_state_domain";
|
||||||
import { supportsFeature } from "./supports-feature";
|
import { supportsFeature } from "./supports-feature";
|
||||||
|
|
||||||
export const canToggleState = (hass: HomeAssistant, stateObj: HassEntity) => {
|
export const canToggleState = (hass: HomeAssistant, stateObj: HassEntity) => {
|
||||||
const domain = computeStateDomain(stateObj);
|
const domain = computeStateDomain(stateObj);
|
||||||
|
|
||||||
if (domain === "group") {
|
if (domain === "group") {
|
||||||
return stateObj.state === "on" || stateObj.state === "off";
|
if (
|
||||||
|
stateObj.attributes?.entity_id?.some((entity) => {
|
||||||
|
const entityStateObj = hass.states[entity];
|
||||||
|
if (!entityStateObj) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const entityDomain = computeStateDomain(entityStateObj);
|
||||||
|
return canToggleDomain(hass, entityDomain);
|
||||||
|
})
|
||||||
|
) {
|
||||||
|
return stateObj.state === "on" || stateObj.state === "off";
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (domain === "climate") {
|
if (domain === "climate") {
|
||||||
return supportsFeature(stateObj, 4096);
|
return supportsFeature(stateObj, 4096);
|
||||||
}
|
}
|
||||||
|
@@ -123,7 +123,11 @@ export const computeStateDisplay = (
|
|||||||
domain === "scene" ||
|
domain === "scene" ||
|
||||||
(domain === "sensor" && stateObj.attributes.device_class === "timestamp")
|
(domain === "sensor" && stateObj.attributes.device_class === "timestamp")
|
||||||
) {
|
) {
|
||||||
return formatDateTime(new Date(compareState), locale);
|
try {
|
||||||
|
return formatDateTime(new Date(compareState), locale);
|
||||||
|
} catch (_err) {
|
||||||
|
return compareState;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@@ -14,6 +14,9 @@ class SearchInput extends LitElement {
|
|||||||
|
|
||||||
@property() public filter?: string;
|
@property() public filter?: string;
|
||||||
|
|
||||||
|
@property({ type: Boolean })
|
||||||
|
public suffix = false;
|
||||||
|
|
||||||
@property({ type: Boolean })
|
@property({ type: Boolean })
|
||||||
public autofocus = false;
|
public autofocus = false;
|
||||||
|
|
||||||
@@ -33,7 +36,7 @@ class SearchInput extends LitElement {
|
|||||||
.label=${this.label || "Search"}
|
.label=${this.label || "Search"}
|
||||||
.value=${this.filter || ""}
|
.value=${this.filter || ""}
|
||||||
.icon=${true}
|
.icon=${true}
|
||||||
.iconTrailing=${this.filter}
|
.iconTrailing=${this.filter || this.suffix}
|
||||||
@input=${this._filterInputChanged}
|
@input=${this._filterInputChanged}
|
||||||
>
|
>
|
||||||
<slot name="prefix" slot="leadingIcon">
|
<slot name="prefix" slot="leadingIcon">
|
||||||
@@ -43,16 +46,18 @@ class SearchInput extends LitElement {
|
|||||||
.path=${mdiMagnify}
|
.path=${mdiMagnify}
|
||||||
></ha-svg-icon>
|
></ha-svg-icon>
|
||||||
</slot>
|
</slot>
|
||||||
${this.filter &&
|
<div class="trailing" slot="trailingIcon">
|
||||||
html`
|
${this.filter &&
|
||||||
<ha-icon-button
|
html`
|
||||||
slot="trailingIcon"
|
<ha-icon-button
|
||||||
@click=${this._clearSearch}
|
@click=${this._clearSearch}
|
||||||
.label=${this.hass.localize("ui.common.clear")}
|
.label=${this.hass.localize("ui.common.clear")}
|
||||||
.path=${mdiClose}
|
.path=${mdiClose}
|
||||||
class="clear-button"
|
class="clear-button"
|
||||||
></ha-icon-button>
|
></ha-icon-button>
|
||||||
`}
|
`}
|
||||||
|
<slot name="suffix"></slot>
|
||||||
|
</div>
|
||||||
</ha-textfield>
|
</ha-textfield>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
@@ -81,15 +86,16 @@ class SearchInput extends LitElement {
|
|||||||
ha-svg-icon {
|
ha-svg-icon {
|
||||||
outline: none;
|
outline: none;
|
||||||
}
|
}
|
||||||
ha-icon-button {
|
|
||||||
--mdc-icon-button-size: 24px;
|
|
||||||
}
|
|
||||||
.clear-button {
|
.clear-button {
|
||||||
--mdc-icon-size: 20px;
|
--mdc-icon-size: 20px;
|
||||||
}
|
}
|
||||||
ha-textfield {
|
ha-textfield {
|
||||||
display: inherit;
|
display: inherit;
|
||||||
}
|
}
|
||||||
|
.trailing {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,6 +1,4 @@
|
|||||||
import "@polymer/paper-input/paper-input";
|
import "@polymer/paper-input/paper-input";
|
||||||
import "@polymer/paper-item/paper-icon-item";
|
|
||||||
import "@polymer/paper-item/paper-item-body";
|
|
||||||
import { HassEntity } from "home-assistant-js-websocket";
|
import { HassEntity } from "home-assistant-js-websocket";
|
||||||
import { html, LitElement, PropertyValues, TemplateResult } from "lit";
|
import { html, LitElement, PropertyValues, TemplateResult } from "lit";
|
||||||
import { ComboBoxLitRenderer } from "lit-vaadin-helpers";
|
import { ComboBoxLitRenderer } from "lit-vaadin-helpers";
|
||||||
|
@@ -4,6 +4,7 @@ import { mdiFilterVariant } from "@mdi/js";
|
|||||||
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
||||||
import { customElement, property, state } from "lit/decorators";
|
import { customElement, property, state } from "lit/decorators";
|
||||||
import { fireEvent } from "../common/dom/fire_event";
|
import { fireEvent } from "../common/dom/fire_event";
|
||||||
|
import { stopPropagation } from "../common/dom/stop_propagation";
|
||||||
import { computeStateName } from "../common/entity/compute_state_name";
|
import { computeStateName } from "../common/entity/compute_state_name";
|
||||||
import { computeDeviceName } from "../data/device_registry";
|
import { computeDeviceName } from "../data/device_registry";
|
||||||
import { findRelated, RelatedResult } from "../data/search";
|
import { findRelated, RelatedResult } from "../data/search";
|
||||||
@@ -65,6 +66,7 @@ export class HaRelatedFilterButtonMenu extends LitElement {
|
|||||||
.fullwidth=${this.narrow}
|
.fullwidth=${this.narrow}
|
||||||
.corner=${this.corner}
|
.corner=${this.corner}
|
||||||
@closed=${this._onClosed}
|
@closed=${this._onClosed}
|
||||||
|
@input=${stopPropagation}
|
||||||
>
|
>
|
||||||
<ha-area-picker
|
<ha-area-picker
|
||||||
.label=${this.hass.localize(
|
.label=${this.hass.localize(
|
||||||
@@ -74,6 +76,7 @@ export class HaRelatedFilterButtonMenu extends LitElement {
|
|||||||
.value=${this.value?.area}
|
.value=${this.value?.area}
|
||||||
no-add
|
no-add
|
||||||
@value-changed=${this._areaPicked}
|
@value-changed=${this._areaPicked}
|
||||||
|
@click=${this._preventDefault}
|
||||||
></ha-area-picker>
|
></ha-area-picker>
|
||||||
<ha-device-picker
|
<ha-device-picker
|
||||||
.label=${this.hass.localize(
|
.label=${this.hass.localize(
|
||||||
@@ -82,6 +85,7 @@ export class HaRelatedFilterButtonMenu extends LitElement {
|
|||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
.value=${this.value?.device}
|
.value=${this.value?.device}
|
||||||
@value-changed=${this._devicePicked}
|
@value-changed=${this._devicePicked}
|
||||||
|
@click=${this._preventDefault}
|
||||||
></ha-device-picker>
|
></ha-device-picker>
|
||||||
<ha-entity-picker
|
<ha-entity-picker
|
||||||
.label=${this.hass.localize(
|
.label=${this.hass.localize(
|
||||||
@@ -91,6 +95,7 @@ export class HaRelatedFilterButtonMenu extends LitElement {
|
|||||||
.value=${this.value?.entity}
|
.value=${this.value?.entity}
|
||||||
.excludeDomains=${this.excludeDomains}
|
.excludeDomains=${this.excludeDomains}
|
||||||
@value-changed=${this._entityPicked}
|
@value-changed=${this._entityPicked}
|
||||||
|
@click=${this._preventDefault}
|
||||||
></ha-entity-picker>
|
></ha-entity-picker>
|
||||||
</mwc-menu-surface>
|
</mwc-menu-surface>
|
||||||
`;
|
`;
|
||||||
@@ -103,11 +108,17 @@ export class HaRelatedFilterButtonMenu extends LitElement {
|
|||||||
this._open = true;
|
this._open = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private _onClosed(): void {
|
private _onClosed(ev): void {
|
||||||
|
ev.stopPropagation();
|
||||||
this._open = false;
|
this._open = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private _preventDefault(ev) {
|
||||||
|
ev.preventDefault();
|
||||||
|
}
|
||||||
|
|
||||||
private async _entityPicked(ev: CustomEvent) {
|
private async _entityPicked(ev: CustomEvent) {
|
||||||
|
ev.stopPropagation();
|
||||||
const entityId = ev.detail.value;
|
const entityId = ev.detail.value;
|
||||||
if (!entityId) {
|
if (!entityId) {
|
||||||
fireEvent(this, "related-changed", { value: undefined });
|
fireEvent(this, "related-changed", { value: undefined });
|
||||||
@@ -127,6 +138,7 @@ export class HaRelatedFilterButtonMenu extends LitElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private async _devicePicked(ev: CustomEvent) {
|
private async _devicePicked(ev: CustomEvent) {
|
||||||
|
ev.stopPropagation();
|
||||||
const deviceId = ev.detail.value;
|
const deviceId = ev.detail.value;
|
||||||
if (!deviceId) {
|
if (!deviceId) {
|
||||||
fireEvent(this, "related-changed", { value: undefined });
|
fireEvent(this, "related-changed", { value: undefined });
|
||||||
@@ -150,6 +162,7 @@ export class HaRelatedFilterButtonMenu extends LitElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private async _areaPicked(ev: CustomEvent) {
|
private async _areaPicked(ev: CustomEvent) {
|
||||||
|
ev.stopPropagation();
|
||||||
const areaId = ev.detail.value;
|
const areaId = ev.detail.value;
|
||||||
if (!areaId) {
|
if (!areaId) {
|
||||||
fireEvent(this, "related-changed", { value: undefined });
|
fireEvent(this, "related-changed", { value: undefined });
|
||||||
@@ -173,9 +186,7 @@ export class HaRelatedFilterButtonMenu extends LitElement {
|
|||||||
:host {
|
:host {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
position: relative;
|
position: relative;
|
||||||
}
|
--mdc-menu-min-width: 250px;
|
||||||
:host([narrow]) {
|
|
||||||
position: static;
|
|
||||||
}
|
}
|
||||||
ha-area-picker,
|
ha-area-picker,
|
||||||
ha-device-picker,
|
ha-device-picker,
|
||||||
@@ -185,8 +196,15 @@ export class HaRelatedFilterButtonMenu extends LitElement {
|
|||||||
padding: 4px 16px;
|
padding: 4px 16px;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
}
|
}
|
||||||
|
ha-area-picker {
|
||||||
|
padding-top: 16px;
|
||||||
|
}
|
||||||
|
ha-entity-picker {
|
||||||
|
padding-bottom: 16px;
|
||||||
|
}
|
||||||
:host([narrow]) ha-area-picker,
|
:host([narrow]) ha-area-picker,
|
||||||
:host([narrow]) ha-device-picker {
|
:host([narrow]) ha-device-picker,
|
||||||
|
:host([narrow]) ha-entity-picker {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
@@ -32,7 +32,12 @@ export class HaForm extends LitElement implements HaFormElement {
|
|||||||
|
|
||||||
@property() public computeError?: (schema: HaFormSchema, error) => string;
|
@property() public computeError?: (schema: HaFormSchema, error) => string;
|
||||||
|
|
||||||
@property() public computeLabel?: (schema: HaFormSchema) => string;
|
@property() public computeLabel?: (
|
||||||
|
schema: HaFormSchema,
|
||||||
|
data?: HaFormDataContainer
|
||||||
|
) => string;
|
||||||
|
|
||||||
|
@property() public computeHelper?: (schema: HaFormSchema) => string;
|
||||||
|
|
||||||
public focus() {
|
public focus() {
|
||||||
const root = this.shadowRoot?.querySelector(".root");
|
const root = this.shadowRoot?.querySelector(".root");
|
||||||
@@ -71,6 +76,7 @@ export class HaForm extends LitElement implements HaFormElement {
|
|||||||
: ""}
|
: ""}
|
||||||
${this.schema.map((item) => {
|
${this.schema.map((item) => {
|
||||||
const error = getValue(this.error, item);
|
const error = getValue(this.error, item);
|
||||||
|
|
||||||
return html`
|
return html`
|
||||||
${error
|
${error
|
||||||
? html`
|
? html`
|
||||||
@@ -85,14 +91,15 @@ export class HaForm extends LitElement implements HaFormElement {
|
|||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
.selector=${item.selector}
|
.selector=${item.selector}
|
||||||
.value=${getValue(this.data, item)}
|
.value=${getValue(this.data, item)}
|
||||||
.label=${this._computeLabel(item)}
|
.label=${this._computeLabel(item, this.data)}
|
||||||
.disabled=${this.disabled}
|
.disabled=${this.disabled}
|
||||||
|
.helper=${this._computeHelper(item)}
|
||||||
.required=${item.required || false}
|
.required=${item.required || false}
|
||||||
></ha-selector>`
|
></ha-selector>`
|
||||||
: dynamicElement(`ha-form-${item.type}`, {
|
: dynamicElement(`ha-form-${item.type}`, {
|
||||||
schema: item,
|
schema: item,
|
||||||
data: getValue(this.data, item),
|
data: getValue(this.data, item),
|
||||||
label: this._computeLabel(item),
|
label: this._computeLabel(item, this.data),
|
||||||
disabled: this.disabled,
|
disabled: this.disabled,
|
||||||
})}
|
})}
|
||||||
`;
|
`;
|
||||||
@@ -107,6 +114,7 @@ export class HaForm extends LitElement implements HaFormElement {
|
|||||||
root.addEventListener("value-changed", (ev) => {
|
root.addEventListener("value-changed", (ev) => {
|
||||||
ev.stopPropagation();
|
ev.stopPropagation();
|
||||||
const schema = (ev.target as HaFormElement).schema as HaFormSchema;
|
const schema = (ev.target as HaFormElement).schema as HaFormSchema;
|
||||||
|
|
||||||
fireEvent(this, "value-changed", {
|
fireEvent(this, "value-changed", {
|
||||||
value: { ...this.data, [schema.name]: ev.detail.value },
|
value: { ...this.data, [schema.name]: ev.detail.value },
|
||||||
});
|
});
|
||||||
@@ -114,14 +122,18 @@ export class HaForm extends LitElement implements HaFormElement {
|
|||||||
return root;
|
return root;
|
||||||
}
|
}
|
||||||
|
|
||||||
private _computeLabel(schema: HaFormSchema) {
|
private _computeLabel(schema: HaFormSchema, data: HaFormDataContainer) {
|
||||||
return this.computeLabel
|
return this.computeLabel
|
||||||
? this.computeLabel(schema)
|
? this.computeLabel(schema, data)
|
||||||
: schema
|
: schema
|
||||||
? schema.name
|
? schema.name
|
||||||
: "";
|
: "";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private _computeHelper(schema: HaFormSchema) {
|
||||||
|
return this.computeHelper ? this.computeHelper(schema) : "";
|
||||||
|
}
|
||||||
|
|
||||||
private _computeError(error, schema: HaFormSchema | HaFormSchema[]) {
|
private _computeError(error, schema: HaFormSchema | HaFormSchema[]) {
|
||||||
return this.computeError ? this.computeError(error, schema) : error;
|
return this.computeError ? this.computeError(error, schema) : error;
|
||||||
}
|
}
|
||||||
|
@@ -43,6 +43,8 @@ class HaHLSPlayer extends LitElement {
|
|||||||
|
|
||||||
@state() private _error?: string;
|
@state() private _error?: string;
|
||||||
|
|
||||||
|
@state() private _errorIsFatal = false;
|
||||||
|
|
||||||
private _hlsPolyfillInstance?: HlsLite;
|
private _hlsPolyfillInstance?: HlsLite;
|
||||||
|
|
||||||
private _exoPlayer = false;
|
private _exoPlayer = false;
|
||||||
@@ -53,6 +55,7 @@ class HaHLSPlayer extends LitElement {
|
|||||||
super.connectedCallback();
|
super.connectedCallback();
|
||||||
HaHLSPlayer.streamCount += 1;
|
HaHLSPlayer.streamCount += 1;
|
||||||
if (this.hasUpdated) {
|
if (this.hasUpdated) {
|
||||||
|
this._resetError();
|
||||||
this._startHls();
|
this._startHls();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -64,16 +67,23 @@ class HaHLSPlayer extends LitElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected render(): TemplateResult {
|
protected render(): TemplateResult {
|
||||||
if (this._error) {
|
|
||||||
return html`<ha-alert alert-type="error">${this._error}</ha-alert>`;
|
|
||||||
}
|
|
||||||
return html`
|
return html`
|
||||||
<video
|
${this._error
|
||||||
?autoplay=${this.autoPlay}
|
? html`<ha-alert
|
||||||
.muted=${this.muted}
|
alert-type="error"
|
||||||
?playsinline=${this.playsInline}
|
class=${this._errorIsFatal ? "fatal" : "retry"}
|
||||||
?controls=${this.controls}
|
>
|
||||||
></video>
|
${this._error}
|
||||||
|
</ha-alert>`
|
||||||
|
: ""}
|
||||||
|
${!this._errorIsFatal
|
||||||
|
? html`<video
|
||||||
|
?autoplay=${this.autoPlay}
|
||||||
|
.muted=${this.muted}
|
||||||
|
?playsinline=${this.playsInline}
|
||||||
|
?controls=${this.controls}
|
||||||
|
></video>`
|
||||||
|
: ""}
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -87,12 +97,11 @@ class HaHLSPlayer extends LitElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this._cleanUp();
|
this._cleanUp();
|
||||||
|
this._resetError();
|
||||||
this._startHls();
|
this._startHls();
|
||||||
}
|
}
|
||||||
|
|
||||||
private async _startHls(): Promise<void> {
|
private async _startHls(): Promise<void> {
|
||||||
this._error = undefined;
|
|
||||||
|
|
||||||
const masterPlaylistPromise = fetch(this.url);
|
const masterPlaylistPromise = fetch(this.url);
|
||||||
|
|
||||||
const Hls: typeof HlsType = (await import("hls.js/dist/hls.light.min"))
|
const Hls: typeof HlsType = (await import("hls.js/dist/hls.light.min"))
|
||||||
@@ -110,8 +119,8 @@ class HaHLSPlayer extends LitElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!hlsSupported) {
|
if (!hlsSupported) {
|
||||||
this._error = this.hass.localize(
|
this._setFatalError(
|
||||||
"ui.components.media-browser.video_not_supported"
|
this.hass.localize("ui.components.media-browser.video_not_supported")
|
||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -219,9 +228,16 @@ class HaHLSPlayer extends LitElement {
|
|||||||
this._hlsPolyfillInstance = hls;
|
this._hlsPolyfillInstance = hls;
|
||||||
hls.attachMedia(videoEl);
|
hls.attachMedia(videoEl);
|
||||||
hls.on(Hls.Events.MEDIA_ATTACHED, () => {
|
hls.on(Hls.Events.MEDIA_ATTACHED, () => {
|
||||||
|
this._resetError();
|
||||||
hls.loadSource(url);
|
hls.loadSource(url);
|
||||||
});
|
});
|
||||||
hls.on(Hls.Events.ERROR, (_, data: any) => {
|
hls.on(Hls.Events.FRAG_LOADED, (_event, _data: any) => {
|
||||||
|
this._resetError();
|
||||||
|
});
|
||||||
|
hls.on(Hls.Events.ERROR, (_event, data: any) => {
|
||||||
|
// Some errors are recovered automatically by the hls player itself, and the others handled
|
||||||
|
// in this function require special actions to recover. Errors retried in this function
|
||||||
|
// are done with backoff to not cause unecessary failures.
|
||||||
if (!data.fatal) {
|
if (!data.fatal) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -241,22 +257,22 @@ class HaHLSPlayer extends LitElement {
|
|||||||
error += " (" + data.response.code + ")";
|
error += " (" + data.response.code + ")";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this._error = error;
|
this._setRetryableError(error);
|
||||||
return;
|
break;
|
||||||
}
|
}
|
||||||
case Hls.ErrorDetails.MANIFEST_LOAD_TIMEOUT:
|
case Hls.ErrorDetails.MANIFEST_LOAD_TIMEOUT:
|
||||||
this._error = "Timeout while starting stream";
|
this._setRetryableError("Timeout while starting stream");
|
||||||
return;
|
break;
|
||||||
default:
|
default:
|
||||||
this._error = "Unknown stream network error (" + data.details + ")";
|
this._setRetryableError("Stream network error");
|
||||||
return;
|
break;
|
||||||
}
|
}
|
||||||
this._error = "Error with media stream contents (" + data.details + ")";
|
hls.startLoad();
|
||||||
} else if (data.type === Hls.ErrorTypes.MEDIA_ERROR) {
|
} else if (data.type === Hls.ErrorTypes.MEDIA_ERROR) {
|
||||||
this._error = "Error with media stream contents (" + data.details + ")";
|
this._setRetryableError("Error with media stream contents");
|
||||||
|
hls.recoverMediaError();
|
||||||
} else {
|
} else {
|
||||||
this._error =
|
this._setFatalError("Error playing stream");
|
||||||
"Unknown error with stream (" + data.type + ", " + data.details + ")";
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -284,6 +300,21 @@ class HaHLSPlayer extends LitElement {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private _resetError() {
|
||||||
|
this._error = undefined;
|
||||||
|
this._errorIsFatal = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _setFatalError(errorMessage: string) {
|
||||||
|
this._error = errorMessage;
|
||||||
|
this._errorIsFatal = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _setRetryableError(errorMessage: string) {
|
||||||
|
this._error = errorMessage;
|
||||||
|
this._errorIsFatal = false;
|
||||||
|
}
|
||||||
|
|
||||||
static get styles(): CSSResultGroup {
|
static get styles(): CSSResultGroup {
|
||||||
return css`
|
return css`
|
||||||
:host,
|
:host,
|
||||||
@@ -296,10 +327,14 @@ class HaHLSPlayer extends LitElement {
|
|||||||
max-height: var(--video-max-height, calc(100vh - 97px));
|
max-height: var(--video-max-height, calc(100vh - 97px));
|
||||||
}
|
}
|
||||||
|
|
||||||
ha-alert {
|
.fatal {
|
||||||
display: block;
|
display: block;
|
||||||
padding: 100px 16px;
|
padding: 100px 16px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.retry {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,28 +0,0 @@
|
|||||||
import "@polymer/paper-dropdown-menu/paper-dropdown-menu";
|
|
||||||
import { PolymerElement } from "@polymer/polymer";
|
|
||||||
import { Constructor } from "../types";
|
|
||||||
|
|
||||||
const paperDropdownClass = customElements.get(
|
|
||||||
"paper-dropdown-menu"
|
|
||||||
) as Constructor<PolymerElement>;
|
|
||||||
|
|
||||||
// patches paper drop down to properly support RTL - https://github.com/PolymerElements/paper-dropdown-menu/issues/183
|
|
||||||
export class HaPaperDropdownClass extends paperDropdownClass {
|
|
||||||
public ready() {
|
|
||||||
super.ready();
|
|
||||||
// wait to check for direction since otherwise direction is wrong even though top level is RTL
|
|
||||||
setTimeout(() => {
|
|
||||||
if (window.getComputedStyle(this).direction === "rtl") {
|
|
||||||
this.style.textAlign = "right";
|
|
||||||
}
|
|
||||||
}, 100);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
declare global {
|
|
||||||
interface HTMLElementTagNameMap {
|
|
||||||
"ha-paper-dropdown-menu": HaPaperDropdownClass;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
customElements.define("ha-paper-dropdown-menu", HaPaperDropdownClass);
|
|
39
src/components/ha-selector/ha-selector-icon.ts
Normal file
39
src/components/ha-selector/ha-selector-icon.ts
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
import "../ha-icon-picker";
|
||||||
|
import { html, LitElement } from "lit";
|
||||||
|
import { customElement, property } from "lit/decorators";
|
||||||
|
import { HomeAssistant } from "../../types";
|
||||||
|
import { IconSelector } from "../../data/selector";
|
||||||
|
import { fireEvent } from "../../common/dom/fire_event";
|
||||||
|
|
||||||
|
@customElement("ha-selector-icon")
|
||||||
|
export class HaIconSelector extends LitElement {
|
||||||
|
@property() public hass!: HomeAssistant;
|
||||||
|
|
||||||
|
@property() public selector!: IconSelector;
|
||||||
|
|
||||||
|
@property() public value?: string;
|
||||||
|
|
||||||
|
@property() public label?: string;
|
||||||
|
|
||||||
|
@property({ type: Boolean, reflect: true }) public disabled = false;
|
||||||
|
|
||||||
|
protected render() {
|
||||||
|
return html`
|
||||||
|
<ha-icon-picker
|
||||||
|
.label=${this.label}
|
||||||
|
.value=${this.value}
|
||||||
|
@value-changed=${this._valueChanged}
|
||||||
|
></ha-icon-picker>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _valueChanged(ev: CustomEvent) {
|
||||||
|
fireEvent(this, "value-changed", { value: ev.detail.value });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface HTMLElementTagNameMap {
|
||||||
|
"ha-selector-icon": HaIconSelector;
|
||||||
|
}
|
||||||
|
}
|
@@ -2,7 +2,7 @@ import { css, CSSResultGroup, html, LitElement } from "lit";
|
|||||||
import { customElement, property } from "lit/decorators";
|
import { customElement, property } from "lit/decorators";
|
||||||
import { fireEvent } from "../../common/dom/fire_event";
|
import { fireEvent } from "../../common/dom/fire_event";
|
||||||
import { stopPropagation } from "../../common/dom/stop_propagation";
|
import { stopPropagation } from "../../common/dom/stop_propagation";
|
||||||
import { SelectSelector } from "../../data/selector";
|
import { SelectOption, SelectSelector } from "../../data/selector";
|
||||||
import { HomeAssistant } from "../../types";
|
import { HomeAssistant } from "../../types";
|
||||||
import "@material/mwc-select/mwc-select";
|
import "@material/mwc-select/mwc-select";
|
||||||
import "@material/mwc-list/mwc-list-item";
|
import "@material/mwc-list/mwc-list-item";
|
||||||
@@ -17,6 +17,8 @@ export class HaSelectSelector extends LitElement {
|
|||||||
|
|
||||||
@property() public label?: string;
|
@property() public label?: string;
|
||||||
|
|
||||||
|
@property() public helper?: string;
|
||||||
|
|
||||||
@property({ type: Boolean }) public disabled = false;
|
@property({ type: Boolean }) public disabled = false;
|
||||||
|
|
||||||
protected render() {
|
protected render() {
|
||||||
@@ -25,15 +27,17 @@ export class HaSelectSelector extends LitElement {
|
|||||||
naturalMenuWidth
|
naturalMenuWidth
|
||||||
.label=${this.label}
|
.label=${this.label}
|
||||||
.value=${this.value}
|
.value=${this.value}
|
||||||
|
.helper=${this.helper}
|
||||||
.disabled=${this.disabled}
|
.disabled=${this.disabled}
|
||||||
@closed=${stopPropagation}
|
@closed=${stopPropagation}
|
||||||
@selected=${this._valueChanged}
|
@selected=${this._valueChanged}
|
||||||
>
|
>
|
||||||
${this.selector.select.options.map(
|
${this.selector.select.options.map((item: string | SelectOption) => {
|
||||||
(item: string) => html`
|
const value = typeof item === "object" ? item.value : item;
|
||||||
<mwc-list-item .value=${item}>${item}</mwc-list-item>
|
const label = typeof item === "object" ? item.label : item;
|
||||||
`
|
|
||||||
)}
|
return html`<mwc-list-item .value=${value}>${label}</mwc-list-item>`;
|
||||||
|
})}
|
||||||
</mwc-select>`;
|
</mwc-select>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -17,6 +17,7 @@ import "./ha-selector-select";
|
|||||||
import "./ha-selector-target";
|
import "./ha-selector-target";
|
||||||
import "./ha-selector-text";
|
import "./ha-selector-text";
|
||||||
import "./ha-selector-time";
|
import "./ha-selector-time";
|
||||||
|
import "./ha-selector-icon";
|
||||||
|
|
||||||
@customElement("ha-selector")
|
@customElement("ha-selector")
|
||||||
export class HaSelector extends LitElement {
|
export class HaSelector extends LitElement {
|
||||||
@@ -28,6 +29,8 @@ export class HaSelector extends LitElement {
|
|||||||
|
|
||||||
@property() public label?: string;
|
@property() public label?: string;
|
||||||
|
|
||||||
|
@property() public helper?: string;
|
||||||
|
|
||||||
@property() public placeholder?: any;
|
@property() public placeholder?: any;
|
||||||
|
|
||||||
@property({ type: Boolean }) public disabled = false;
|
@property({ type: Boolean }) public disabled = false;
|
||||||
@@ -52,6 +55,7 @@ export class HaSelector extends LitElement {
|
|||||||
placeholder: this.placeholder,
|
placeholder: this.placeholder,
|
||||||
disabled: this.disabled,
|
disabled: this.disabled,
|
||||||
required: this.required,
|
required: this.required,
|
||||||
|
helper: this.helper,
|
||||||
id: "selector",
|
id: "selector",
|
||||||
})}
|
})}
|
||||||
`;
|
`;
|
||||||
|
@@ -68,6 +68,14 @@ export class HaTextField extends TextFieldBase {
|
|||||||
:host([no-spinner]) input[type="number"] {
|
:host([no-spinner]) input[type="number"] {
|
||||||
-moz-appearance: textfield;
|
-moz-appearance: textfield;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.mdc-text-field__ripple {
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mdc-text-field {
|
||||||
|
overflow: var(--text-field-overflow);
|
||||||
|
}
|
||||||
`,
|
`,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
230
src/components/media-player/ha-browse-media-tts.ts
Normal file
230
src/components/media-player/ha-browse-media-tts.ts
Normal file
@@ -0,0 +1,230 @@
|
|||||||
|
import "@material/mwc-select";
|
||||||
|
import "@material/mwc-list/mwc-list-item";
|
||||||
|
import { css, html, LitElement, PropertyValues } from "lit";
|
||||||
|
import { customElement, property, state } from "lit/decorators";
|
||||||
|
import memoizeOne from "memoize-one";
|
||||||
|
import { fireEvent } from "../../common/dom/fire_event";
|
||||||
|
import { fetchCloudStatus, updateCloudPref } from "../../data/cloud";
|
||||||
|
import {
|
||||||
|
CloudTTSInfo,
|
||||||
|
getCloudTTSInfo,
|
||||||
|
getCloudTtsLanguages,
|
||||||
|
getCloudTtsSupportedGenders,
|
||||||
|
} from "../../data/cloud/tts";
|
||||||
|
import { MediaPlayerBrowseAction } from "../../data/media-player";
|
||||||
|
import { HomeAssistant } from "../../types";
|
||||||
|
import "../ha-textarea";
|
||||||
|
import { buttonLinkStyle } from "../../resources/styles";
|
||||||
|
import { showAlertDialog } from "../../dialogs/generic/show-dialog-box";
|
||||||
|
import { LocalStorage } from "../../common/decorators/local-storage";
|
||||||
|
|
||||||
|
@customElement("ha-browse-media-tts")
|
||||||
|
class BrowseMediaTTS extends LitElement {
|
||||||
|
@property() public hass!: HomeAssistant;
|
||||||
|
|
||||||
|
@property() public item;
|
||||||
|
|
||||||
|
@property() public action!: MediaPlayerBrowseAction;
|
||||||
|
|
||||||
|
@state() private _cloudDefaultOptions?: [string, string];
|
||||||
|
|
||||||
|
@state() private _cloudOptions?: [string, string];
|
||||||
|
|
||||||
|
@state() private _cloudTTSInfo?: CloudTTSInfo;
|
||||||
|
|
||||||
|
@LocalStorage("cloudTtsTryMessage", false, false) private _message!: string;
|
||||||
|
|
||||||
|
protected render() {
|
||||||
|
return html`
|
||||||
|
<ha-textarea
|
||||||
|
autogrow
|
||||||
|
.label=${this.hass.localize("ui.panel.media-browser.tts.message")}
|
||||||
|
.value=${this._message ||
|
||||||
|
this.hass.localize("ui.panel.media-browser.tts.example_message", {
|
||||||
|
name: this.hass.user?.name || "",
|
||||||
|
})}
|
||||||
|
>
|
||||||
|
</ha-textarea>
|
||||||
|
${this._cloudDefaultOptions ? this._renderCloudOptions() : ""}
|
||||||
|
<div class="actions">
|
||||||
|
${this._cloudDefaultOptions &&
|
||||||
|
(this._cloudDefaultOptions![0] !== this._cloudOptions![0] ||
|
||||||
|
this._cloudDefaultOptions![1] !== this._cloudOptions![1])
|
||||||
|
? html`
|
||||||
|
<button class="link" @click=${this._storeDefaults}>
|
||||||
|
${this.hass.localize(
|
||||||
|
"ui.panel.media-browser.tts.set_as_default"
|
||||||
|
)}
|
||||||
|
</button>
|
||||||
|
`
|
||||||
|
: html`<span></span>`}
|
||||||
|
<mwc-button raised label="Say" @click=${this._ttsClicked}></mwc-button>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _renderCloudOptions() {
|
||||||
|
const languages = this.getLanguages(this._cloudTTSInfo);
|
||||||
|
const selectedVoice = this._cloudOptions!;
|
||||||
|
const genders = this.getSupportedGenders(
|
||||||
|
selectedVoice[0],
|
||||||
|
this._cloudTTSInfo,
|
||||||
|
this.hass.localize
|
||||||
|
);
|
||||||
|
|
||||||
|
return html`
|
||||||
|
<div class="cloud-options">
|
||||||
|
<mwc-select
|
||||||
|
fixedMenuPosition
|
||||||
|
naturalMenuWidth
|
||||||
|
.label=${this.hass.localize("ui.panel.media-browser.tts.language")}
|
||||||
|
.value=${selectedVoice[0]}
|
||||||
|
@selected=${this._handleLanguageChange}
|
||||||
|
>
|
||||||
|
${languages.map(
|
||||||
|
([key, label]) =>
|
||||||
|
html`<mwc-list-item .value=${key}>${label}</mwc-list-item>`
|
||||||
|
)}
|
||||||
|
</mwc-select>
|
||||||
|
|
||||||
|
<mwc-select
|
||||||
|
fixedMenuPosition
|
||||||
|
naturalMenuWidth
|
||||||
|
.label=${this.hass.localize("ui.panel.media-browser.tts.gender")}
|
||||||
|
.value=${selectedVoice[1]}
|
||||||
|
@selected=${this._handleGenderChange}
|
||||||
|
>
|
||||||
|
${genders.map(
|
||||||
|
([key, label]) =>
|
||||||
|
html`<mwc-list-item .value=${key}>${label}</mwc-list-item>`
|
||||||
|
)}
|
||||||
|
</mwc-select>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override willUpdate(changedProps: PropertyValues): void {
|
||||||
|
super.willUpdate(changedProps);
|
||||||
|
|
||||||
|
if (changedProps.has("message")) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Re-rendering can reset message because textarea content is newer than local storage.
|
||||||
|
// But we don't want to write every keystroke to local storage.
|
||||||
|
// So instead we just do it when we're going to render.
|
||||||
|
const message = this.shadowRoot!.querySelector("ha-textarea")?.value;
|
||||||
|
if (message !== undefined && message !== this._message) {
|
||||||
|
this._message = message;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async _handleLanguageChange(ev) {
|
||||||
|
if (ev.target.value === this._cloudOptions![0]) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this._cloudOptions = [ev.target.value, this._cloudOptions![1]];
|
||||||
|
}
|
||||||
|
|
||||||
|
async _handleGenderChange(ev) {
|
||||||
|
if (ev.target.value === this._cloudOptions![1]) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this._cloudOptions = [this._cloudOptions![0], ev.target.value];
|
||||||
|
}
|
||||||
|
|
||||||
|
protected updated(changedProps: PropertyValues): void {
|
||||||
|
super.updated(changedProps);
|
||||||
|
|
||||||
|
if (changedProps.has("item")) {
|
||||||
|
if (this.isCloudItem && !this._cloudTTSInfo) {
|
||||||
|
getCloudTTSInfo(this.hass).then((info) => {
|
||||||
|
this._cloudTTSInfo = info;
|
||||||
|
});
|
||||||
|
fetchCloudStatus(this.hass).then((status) => {
|
||||||
|
if (status.logged_in) {
|
||||||
|
this._cloudDefaultOptions = status.prefs.tts_default_voice;
|
||||||
|
this._cloudOptions = { ...this._cloudDefaultOptions };
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private getLanguages = memoizeOne(getCloudTtsLanguages);
|
||||||
|
|
||||||
|
private getSupportedGenders = memoizeOne(getCloudTtsSupportedGenders);
|
||||||
|
|
||||||
|
private get isCloudItem(): boolean {
|
||||||
|
return this.item.media_content_id === "media-source://tts/cloud";
|
||||||
|
}
|
||||||
|
|
||||||
|
private async _ttsClicked(): Promise<void> {
|
||||||
|
const message = this.shadowRoot!.querySelector("ha-textarea")!.value;
|
||||||
|
this._message = message;
|
||||||
|
const item = { ...this.item };
|
||||||
|
const query = new URLSearchParams();
|
||||||
|
query.append("message", message);
|
||||||
|
if (this._cloudOptions) {
|
||||||
|
query.append("language", this._cloudOptions[0]);
|
||||||
|
query.append("gender", this._cloudOptions[1]);
|
||||||
|
}
|
||||||
|
item.media_content_id += `?${query.toString()}`;
|
||||||
|
item.can_play = true;
|
||||||
|
fireEvent(this, "media-picked", { item });
|
||||||
|
}
|
||||||
|
|
||||||
|
private async _storeDefaults() {
|
||||||
|
const oldDefaults = this._cloudDefaultOptions!;
|
||||||
|
this._cloudDefaultOptions = [...this._cloudOptions!];
|
||||||
|
try {
|
||||||
|
await updateCloudPref(this.hass, {
|
||||||
|
tts_default_voice: this._cloudDefaultOptions,
|
||||||
|
});
|
||||||
|
} catch (err: any) {
|
||||||
|
this._cloudDefaultOptions = oldDefaults;
|
||||||
|
showAlertDialog(this, {
|
||||||
|
text: this.hass.localize(
|
||||||
|
"ui.panel.media-browser.tts.faild_to_store_defaults",
|
||||||
|
{ error: err.message || err }
|
||||||
|
),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static override styles = [
|
||||||
|
buttonLinkStyle,
|
||||||
|
css`
|
||||||
|
:host {
|
||||||
|
margin: 16px auto;
|
||||||
|
padding: 0 8px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
max-width: 400px;
|
||||||
|
}
|
||||||
|
.cloud-options {
|
||||||
|
margin-top: 16px;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
.cloud-options mwc-select {
|
||||||
|
width: 48%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.actions {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
margin-top: 16px;
|
||||||
|
}
|
||||||
|
button.link {
|
||||||
|
color: var(--primary-color);
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface HTMLElementTagNameMap {
|
||||||
|
"ha-browse-media-tts": BrowseMediaTTS;
|
||||||
|
}
|
||||||
|
}
|
@@ -1,8 +1,7 @@
|
|||||||
import "@material/mwc-button/mwc-button";
|
import "@material/mwc-button/mwc-button";
|
||||||
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 { mdiPlay, mdiPlus } from "@mdi/js";
|
import { mdiArrowUpRight, mdiPlay, mdiPlus } from "@mdi/js";
|
||||||
import "@polymer/paper-item/paper-item";
|
|
||||||
import "@polymer/paper-tooltip/paper-tooltip";
|
import "@polymer/paper-tooltip/paper-tooltip";
|
||||||
import {
|
import {
|
||||||
css,
|
css,
|
||||||
@@ -49,11 +48,20 @@ import "../ha-icon-button";
|
|||||||
import "../ha-svg-icon";
|
import "../ha-svg-icon";
|
||||||
import "../ha-fab";
|
import "../ha-fab";
|
||||||
import { browseLocalMediaPlayer } from "../../data/media_source";
|
import { browseLocalMediaPlayer } from "../../data/media_source";
|
||||||
|
import { isTTSMediaSource } from "../../data/tts";
|
||||||
|
import "./ha-browse-media-tts";
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
interface HASSDomEvents {
|
interface HASSDomEvents {
|
||||||
"media-picked": MediaPickedEvent;
|
"media-picked": MediaPickedEvent;
|
||||||
"media-browsed": { ids: MediaPlayerItemId[]; current?: MediaPlayerItem };
|
"media-browsed": {
|
||||||
|
// Items of the new browse stack
|
||||||
|
ids: MediaPlayerItemId[];
|
||||||
|
// Current fetched item for this browse stack
|
||||||
|
current?: MediaPlayerItem;
|
||||||
|
// If the new stack should replace the old stack
|
||||||
|
replace?: boolean;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -246,160 +254,160 @@ export class HaMediaPlayerBrowse extends LitElement {
|
|||||||
${this._renderError(this._error)}
|
${this._renderError(this._error)}
|
||||||
</div>
|
</div>
|
||||||
`
|
`
|
||||||
: currentItem.children?.length
|
: isTTSMediaSource(currentItem.media_content_id)
|
||||||
? childrenMediaClass.layout === "grid"
|
? html`
|
||||||
? html`
|
<ha-browse-media-tts
|
||||||
<div
|
.item=${currentItem}
|
||||||
class="children ${classMap({
|
.hass=${this.hass}
|
||||||
portrait:
|
.action=${this.action}
|
||||||
childrenMediaClass.thumbnail_ratio === "portrait",
|
></ha-browse-media-tts>
|
||||||
})}"
|
`
|
||||||
>
|
: !currentItem.children?.length
|
||||||
${currentItem.children.map(
|
? html`
|
||||||
(child) => html`
|
|
||||||
<div
|
|
||||||
class="child"
|
|
||||||
.item=${child}
|
|
||||||
@click=${this._childClicked}
|
|
||||||
>
|
|
||||||
<ha-card outlined>
|
|
||||||
<div class="thumbnail">
|
|
||||||
${child.thumbnail
|
|
||||||
? html`
|
|
||||||
<div
|
|
||||||
class="${[
|
|
||||||
"app",
|
|
||||||
"directory",
|
|
||||||
].includes(child.media_class)
|
|
||||||
? "centered-image"
|
|
||||||
: ""} image lazythumbnail"
|
|
||||||
data-src=${child.thumbnail}
|
|
||||||
></div>
|
|
||||||
`
|
|
||||||
: html`
|
|
||||||
<div class="icon-holder image">
|
|
||||||
<ha-svg-icon
|
|
||||||
class="folder"
|
|
||||||
.path=${MediaClassBrowserSettings[
|
|
||||||
child.media_class === "directory"
|
|
||||||
? child.children_media_class ||
|
|
||||||
child.media_class
|
|
||||||
: child.media_class
|
|
||||||
].icon}
|
|
||||||
></ha-svg-icon>
|
|
||||||
</div>
|
|
||||||
`}
|
|
||||||
${child.can_play
|
|
||||||
? html`
|
|
||||||
<ha-icon-button
|
|
||||||
class="play ${classMap({
|
|
||||||
can_expand: child.can_expand,
|
|
||||||
})}"
|
|
||||||
.item=${child}
|
|
||||||
.label=${this.hass.localize(
|
|
||||||
`ui.components.media-browser.${this.action}-media`
|
|
||||||
)}
|
|
||||||
.path=${this.action === "play"
|
|
||||||
? mdiPlay
|
|
||||||
: mdiPlus}
|
|
||||||
@click=${this._actionClicked}
|
|
||||||
></ha-icon-button>
|
|
||||||
`
|
|
||||||
: ""}
|
|
||||||
</div>
|
|
||||||
<div class="title">
|
|
||||||
${child.title}
|
|
||||||
<paper-tooltip
|
|
||||||
fitToVisibleBounds
|
|
||||||
position="top"
|
|
||||||
offset="4"
|
|
||||||
>${child.title}</paper-tooltip
|
|
||||||
>
|
|
||||||
</div>
|
|
||||||
</ha-card>
|
|
||||||
</div>
|
|
||||||
`
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
`
|
|
||||||
: html`
|
|
||||||
<mwc-list>
|
|
||||||
${currentItem.children.map(
|
|
||||||
(child) => html`
|
|
||||||
<mwc-list-item
|
|
||||||
@click=${this._childClicked}
|
|
||||||
.item=${child}
|
|
||||||
.graphic=${mediaClass.show_list_images
|
|
||||||
? "medium"
|
|
||||||
: "avatar"}
|
|
||||||
dir=${computeRTLDirection(this.hass)}
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
class=${classMap({
|
|
||||||
graphic: true,
|
|
||||||
lazythumbnail:
|
|
||||||
mediaClass.show_list_images === true,
|
|
||||||
})}
|
|
||||||
data-src=${ifDefined(
|
|
||||||
mediaClass.show_list_images && child.thumbnail
|
|
||||||
? child.thumbnail
|
|
||||||
: undefined
|
|
||||||
)}
|
|
||||||
slot="graphic"
|
|
||||||
>
|
|
||||||
<ha-icon-button
|
|
||||||
class="play ${classMap({
|
|
||||||
show:
|
|
||||||
!mediaClass.show_list_images ||
|
|
||||||
!child.thumbnail,
|
|
||||||
})}"
|
|
||||||
.item=${child}
|
|
||||||
.label=${this.hass.localize(
|
|
||||||
`ui.components.media-browser.${this.action}-media`
|
|
||||||
)}
|
|
||||||
.path=${this.action === "play"
|
|
||||||
? mdiPlay
|
|
||||||
: mdiPlus}
|
|
||||||
@click=${this._actionClicked}
|
|
||||||
></ha-icon-button>
|
|
||||||
</div>
|
|
||||||
<span class="title">${child.title}</span>
|
|
||||||
</mwc-list-item>
|
|
||||||
<li divider role="separator"></li>
|
|
||||||
`
|
|
||||||
)}
|
|
||||||
</mwc-list>
|
|
||||||
`
|
|
||||||
: html`
|
|
||||||
<div class="container no-items">
|
<div class="container no-items">
|
||||||
${this.hass.localize(
|
|
||||||
"ui.components.media-browser.no_items"
|
|
||||||
)}
|
|
||||||
<br />
|
|
||||||
${currentItem.media_content_id ===
|
${currentItem.media_content_id ===
|
||||||
"media-source://media_source/local/."
|
"media-source://media_source/local/."
|
||||||
? html`<br />${this.hass.localize(
|
? html`
|
||||||
"ui.components.media-browser.learn_adding_local_media",
|
<div class="highlight-add-button">
|
||||||
"documentation",
|
<span>
|
||||||
html`<a
|
<ha-svg-icon
|
||||||
href=${documentationUrl(
|
.path=${mdiArrowUpRight}
|
||||||
this.hass,
|
></ha-svg-icon>
|
||||||
"/more-info/local-media/add-media"
|
</span>
|
||||||
|
<span>
|
||||||
|
${this.hass.localize(
|
||||||
|
"ui.components.media-browser.file_management.highlight_button"
|
||||||
)}
|
)}
|
||||||
target="_blank"
|
</span>
|
||||||
rel="noreferrer"
|
</div>
|
||||||
>${this.hass.localize(
|
`
|
||||||
"ui.components.media-browser.documentation"
|
: this.hass.localize(
|
||||||
)}</a
|
"ui.components.media-browser.no_items"
|
||||||
>`
|
)}
|
||||||
)}
|
|
||||||
<br />
|
|
||||||
${this.hass.localize(
|
|
||||||
"ui.components.media-browser.local_media_files"
|
|
||||||
)}`
|
|
||||||
: ""}
|
|
||||||
</div>
|
</div>
|
||||||
`
|
`
|
||||||
|
: childrenMediaClass.layout === "grid"
|
||||||
|
? html`
|
||||||
|
<div
|
||||||
|
class="children ${classMap({
|
||||||
|
portrait:
|
||||||
|
childrenMediaClass.thumbnail_ratio === "portrait",
|
||||||
|
})}"
|
||||||
|
>
|
||||||
|
${currentItem.children.map(
|
||||||
|
(child) => html`
|
||||||
|
<div
|
||||||
|
class="child"
|
||||||
|
.item=${child}
|
||||||
|
@click=${this._childClicked}
|
||||||
|
>
|
||||||
|
<ha-card outlined>
|
||||||
|
<div class="thumbnail">
|
||||||
|
${child.thumbnail
|
||||||
|
? html`
|
||||||
|
<div
|
||||||
|
class="${["app", "directory"].includes(
|
||||||
|
child.media_class
|
||||||
|
)
|
||||||
|
? "centered-image"
|
||||||
|
: ""} image lazythumbnail"
|
||||||
|
data-src=${child.thumbnail}
|
||||||
|
></div>
|
||||||
|
`
|
||||||
|
: html`
|
||||||
|
<div class="icon-holder image">
|
||||||
|
<ha-svg-icon
|
||||||
|
class="folder"
|
||||||
|
.path=${MediaClassBrowserSettings[
|
||||||
|
child.media_class === "directory"
|
||||||
|
? child.children_media_class ||
|
||||||
|
child.media_class
|
||||||
|
: child.media_class
|
||||||
|
].icon}
|
||||||
|
></ha-svg-icon>
|
||||||
|
</div>
|
||||||
|
`}
|
||||||
|
${child.can_play
|
||||||
|
? html`
|
||||||
|
<ha-icon-button
|
||||||
|
class="play ${classMap({
|
||||||
|
can_expand: child.can_expand,
|
||||||
|
})}"
|
||||||
|
.item=${child}
|
||||||
|
.label=${this.hass.localize(
|
||||||
|
`ui.components.media-browser.${this.action}-media`
|
||||||
|
)}
|
||||||
|
.path=${this.action === "play"
|
||||||
|
? mdiPlay
|
||||||
|
: mdiPlus}
|
||||||
|
@click=${this._actionClicked}
|
||||||
|
></ha-icon-button>
|
||||||
|
`
|
||||||
|
: ""}
|
||||||
|
</div>
|
||||||
|
<div class="title">
|
||||||
|
${child.title}
|
||||||
|
<paper-tooltip
|
||||||
|
fitToVisibleBounds
|
||||||
|
position="top"
|
||||||
|
offset="4"
|
||||||
|
>${child.title}</paper-tooltip
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
</ha-card>
|
||||||
|
</div>
|
||||||
|
`
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
`
|
||||||
|
: html`
|
||||||
|
<mwc-list>
|
||||||
|
${currentItem.children.map(
|
||||||
|
(child) => html`
|
||||||
|
<mwc-list-item
|
||||||
|
@click=${this._childClicked}
|
||||||
|
.item=${child}
|
||||||
|
.graphic=${mediaClass.show_list_images
|
||||||
|
? "medium"
|
||||||
|
: "avatar"}
|
||||||
|
dir=${computeRTLDirection(this.hass)}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class=${classMap({
|
||||||
|
graphic: true,
|
||||||
|
lazythumbnail:
|
||||||
|
mediaClass.show_list_images === true,
|
||||||
|
})}
|
||||||
|
data-src=${ifDefined(
|
||||||
|
mediaClass.show_list_images && child.thumbnail
|
||||||
|
? child.thumbnail
|
||||||
|
: undefined
|
||||||
|
)}
|
||||||
|
slot="graphic"
|
||||||
|
>
|
||||||
|
<ha-icon-button
|
||||||
|
class="play ${classMap({
|
||||||
|
show:
|
||||||
|
!mediaClass.show_list_images ||
|
||||||
|
!child.thumbnail,
|
||||||
|
})}"
|
||||||
|
.item=${child}
|
||||||
|
.label=${this.hass.localize(
|
||||||
|
`ui.components.media-browser.${this.action}-media`
|
||||||
|
)}
|
||||||
|
.path=${this.action === "play"
|
||||||
|
? mdiPlay
|
||||||
|
: mdiPlus}
|
||||||
|
@click=${this._actionClicked}
|
||||||
|
></ha-icon-button>
|
||||||
|
</div>
|
||||||
|
<span class="title">${child.title}</span>
|
||||||
|
</mwc-list-item>
|
||||||
|
<li divider role="separator"></li>
|
||||||
|
`
|
||||||
|
)}
|
||||||
|
</mwc-list>
|
||||||
|
`
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -425,8 +433,8 @@ export class HaMediaPlayerBrowse extends LitElement {
|
|||||||
|
|
||||||
if (changedProps.has("entityId")) {
|
if (changedProps.has("entityId")) {
|
||||||
this._setError(undefined);
|
this._setError(undefined);
|
||||||
}
|
} else if (!changedProps.has("navigateIds")) {
|
||||||
if (!changedProps.has("navigateIds")) {
|
// Neither entity ID or navigateIDs changed, nothing to fetch
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -435,6 +443,7 @@ export class HaMediaPlayerBrowse extends LitElement {
|
|||||||
const oldNavigateIds = changedProps.get("navigateIds") as
|
const oldNavigateIds = changedProps.get("navigateIds") as
|
||||||
| this["navigateIds"]
|
| this["navigateIds"]
|
||||||
| undefined;
|
| undefined;
|
||||||
|
const navigateIds = this.navigateIds;
|
||||||
|
|
||||||
// We're navigating. Reset the shizzle.
|
// We're navigating. Reset the shizzle.
|
||||||
this._content?.scrollTo(0, 0);
|
this._content?.scrollTo(0, 0);
|
||||||
@@ -443,11 +452,9 @@ export class HaMediaPlayerBrowse extends LitElement {
|
|||||||
const oldParentItem = this._parentItem;
|
const oldParentItem = this._parentItem;
|
||||||
this._currentItem = undefined;
|
this._currentItem = undefined;
|
||||||
this._parentItem = undefined;
|
this._parentItem = undefined;
|
||||||
const currentId = this.navigateIds[this.navigateIds.length - 1];
|
const currentId = navigateIds[navigateIds.length - 1];
|
||||||
const parentId =
|
const parentId =
|
||||||
this.navigateIds.length > 1
|
navigateIds.length > 1 ? navigateIds[navigateIds.length - 2] : undefined;
|
||||||
? this.navigateIds[this.navigateIds.length - 2]
|
|
||||||
: undefined;
|
|
||||||
let currentProm: Promise<MediaPlayerItem> | undefined;
|
let currentProm: Promise<MediaPlayerItem> | undefined;
|
||||||
let parentProm: Promise<MediaPlayerItem> | undefined;
|
let parentProm: Promise<MediaPlayerItem> | undefined;
|
||||||
|
|
||||||
@@ -456,9 +463,9 @@ export class HaMediaPlayerBrowse extends LitElement {
|
|||||||
if (
|
if (
|
||||||
// Check if we navigated to a child
|
// Check if we navigated to a child
|
||||||
oldNavigateIds &&
|
oldNavigateIds &&
|
||||||
this.navigateIds.length > oldNavigateIds.length &&
|
navigateIds.length === oldNavigateIds.length + 1 &&
|
||||||
oldNavigateIds.every((oldVal, idx) => {
|
oldNavigateIds.every((oldVal, idx) => {
|
||||||
const curVal = this.navigateIds[idx];
|
const curVal = navigateIds[idx];
|
||||||
return (
|
return (
|
||||||
curVal.media_content_id === oldVal.media_content_id &&
|
curVal.media_content_id === oldVal.media_content_id &&
|
||||||
curVal.media_content_type === oldVal.media_content_type
|
curVal.media_content_type === oldVal.media_content_type
|
||||||
@@ -469,8 +476,8 @@ export class HaMediaPlayerBrowse extends LitElement {
|
|||||||
} else if (
|
} else if (
|
||||||
// Check if we navigated to a parent
|
// Check if we navigated to a parent
|
||||||
oldNavigateIds &&
|
oldNavigateIds &&
|
||||||
this.navigateIds.length < oldNavigateIds.length &&
|
navigateIds.length === oldNavigateIds.length - 1 &&
|
||||||
this.navigateIds.every((curVal, idx) => {
|
navigateIds.every((curVal, idx) => {
|
||||||
const oldVal = oldNavigateIds[idx];
|
const oldVal = oldNavigateIds[idx];
|
||||||
return (
|
return (
|
||||||
curVal.media_content_id === oldVal.media_content_id &&
|
curVal.media_content_id === oldVal.media_content_id &&
|
||||||
@@ -493,11 +500,33 @@ export class HaMediaPlayerBrowse extends LitElement {
|
|||||||
(item) => {
|
(item) => {
|
||||||
this._currentItem = item;
|
this._currentItem = item;
|
||||||
fireEvent(this, "media-browsed", {
|
fireEvent(this, "media-browsed", {
|
||||||
ids: this.navigateIds,
|
ids: navigateIds,
|
||||||
current: item,
|
current: item,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
(err) => this._setError(err)
|
(err) => {
|
||||||
|
// When we change entity ID, we will first try to see if the new entity is
|
||||||
|
// able to resolve the new path. If that results in an error, browse the root.
|
||||||
|
const isNewEntityWithSamePath =
|
||||||
|
oldNavigateIds &&
|
||||||
|
changedProps.has("entityId") &&
|
||||||
|
navigateIds.length === oldNavigateIds.length &&
|
||||||
|
oldNavigateIds.every(
|
||||||
|
(oldItem, idx) =>
|
||||||
|
navigateIds[idx].media_content_id === oldItem.media_content_id &&
|
||||||
|
navigateIds[idx].media_content_type === oldItem.media_content_type
|
||||||
|
);
|
||||||
|
if (isNewEntityWithSamePath) {
|
||||||
|
fireEvent(this, "media-browsed", {
|
||||||
|
ids: [
|
||||||
|
{ media_content_id: undefined, media_content_type: undefined },
|
||||||
|
],
|
||||||
|
replace: true,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
this._setError(err);
|
||||||
|
}
|
||||||
|
}
|
||||||
);
|
);
|
||||||
// Fetch parent
|
// Fetch parent
|
||||||
if (!parentProm && parentId !== undefined) {
|
if (!parentProm && parentId !== undefined) {
|
||||||
@@ -736,6 +765,18 @@ export class HaMediaPlayerBrowse extends LitElement {
|
|||||||
padding-left: 32px;
|
padding-left: 32px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.highlight-add-button {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row-reverse;
|
||||||
|
margin-right: 48px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.highlight-add-button ha-svg-icon {
|
||||||
|
position: relative;
|
||||||
|
top: -0.5em;
|
||||||
|
margin-left: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
.content {
|
.content {
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
|
@@ -186,10 +186,3 @@ export const updateCloudAlexaEntityConfig = (
|
|||||||
entity_id: entityId,
|
entity_id: entityId,
|
||||||
...values,
|
...values,
|
||||||
});
|
});
|
||||||
|
|
||||||
export interface CloudTTSInfo {
|
|
||||||
languages: Array<[string, string]>;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const getCloudTTSInfo = (hass: HomeAssistant) =>
|
|
||||||
hass.callWS<CloudTTSInfo>({ type: "cloud/tts/info" });
|
|
||||||
|
70
src/data/cloud/tts.ts
Normal file
70
src/data/cloud/tts.ts
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
import { caseInsensitiveStringCompare } from "../../common/string/compare";
|
||||||
|
import { LocalizeFunc } from "../../common/translations/localize";
|
||||||
|
import { translationMetadata } from "../../resources/translations-metadata";
|
||||||
|
import { HomeAssistant } from "../../types";
|
||||||
|
|
||||||
|
export interface CloudTTSInfo {
|
||||||
|
languages: Array<[string, string]>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getCloudTTSInfo = (hass: HomeAssistant) =>
|
||||||
|
hass.callWS<CloudTTSInfo>({ type: "cloud/tts/info" });
|
||||||
|
|
||||||
|
export const getCloudTtsLanguages = (info?: CloudTTSInfo) => {
|
||||||
|
const languages: Array<[string, string]> = [];
|
||||||
|
|
||||||
|
if (!info) {
|
||||||
|
return languages;
|
||||||
|
}
|
||||||
|
|
||||||
|
const seen = new Set<string>();
|
||||||
|
for (const [lang] of info.languages) {
|
||||||
|
if (seen.has(lang)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
seen.add(lang);
|
||||||
|
|
||||||
|
let label = lang;
|
||||||
|
|
||||||
|
if (lang in translationMetadata.translations) {
|
||||||
|
label = translationMetadata.translations[lang].nativeName;
|
||||||
|
} else {
|
||||||
|
const [langFamily, dialect] = lang.split("-");
|
||||||
|
if (langFamily in translationMetadata.translations) {
|
||||||
|
label = `${translationMetadata.translations[langFamily].nativeName}`;
|
||||||
|
|
||||||
|
if (langFamily.toLowerCase() !== dialect.toLowerCase()) {
|
||||||
|
label += ` (${dialect})`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
languages.push([lang, label]);
|
||||||
|
}
|
||||||
|
return languages.sort((a, b) => caseInsensitiveStringCompare(a[1], b[1]));
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getCloudTtsSupportedGenders = (
|
||||||
|
language: string,
|
||||||
|
info: CloudTTSInfo | undefined,
|
||||||
|
localize: LocalizeFunc
|
||||||
|
) => {
|
||||||
|
const genders: Array<[string, string]> = [];
|
||||||
|
|
||||||
|
if (!info) {
|
||||||
|
return genders;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const [curLang, gender] of info.languages) {
|
||||||
|
if (curLang === language) {
|
||||||
|
genders.push([
|
||||||
|
gender,
|
||||||
|
localize(`ui.panel.media-browser.tts.gender_${gender}`) ||
|
||||||
|
localize(`ui.panel.config.cloud.account.tts.${gender}`) ||
|
||||||
|
gender,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return genders.sort((a, b) => caseInsensitiveStringCompare(a[1], b[1]));
|
||||||
|
};
|
@@ -261,8 +261,10 @@ export const computeMediaControls = (
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const assumedState = stateObj.attributes.assumed_state === true;
|
||||||
|
|
||||||
if (
|
if (
|
||||||
(state === "playing" || state === "paused") &&
|
(state === "playing" || state === "paused" || assumedState) &&
|
||||||
supportsFeature(stateObj, SUPPORT_PREVIOUS_TRACK)
|
supportsFeature(stateObj, SUPPORT_PREVIOUS_TRACK)
|
||||||
) {
|
) {
|
||||||
buttons.push({
|
buttons.push({
|
||||||
@@ -272,14 +274,15 @@ export const computeMediaControls = (
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (
|
if (
|
||||||
(state === "playing" &&
|
!assumedState &&
|
||||||
|
((state === "playing" &&
|
||||||
(supportsFeature(stateObj, SUPPORT_PAUSE) ||
|
(supportsFeature(stateObj, SUPPORT_PAUSE) ||
|
||||||
supportsFeature(stateObj, SUPPORT_STOP))) ||
|
supportsFeature(stateObj, SUPPORT_STOP))) ||
|
||||||
((state === "paused" || state === "idle") &&
|
((state === "paused" || state === "idle") &&
|
||||||
supportsFeature(stateObj, SUPPORT_PLAY)) ||
|
supportsFeature(stateObj, SUPPORT_PLAY)) ||
|
||||||
(state === "on" &&
|
(state === "on" &&
|
||||||
(supportsFeature(stateObj, SUPPORT_PLAY) ||
|
(supportsFeature(stateObj, SUPPORT_PLAY) ||
|
||||||
supportsFeature(stateObj, SUPPORT_PAUSE)))
|
supportsFeature(stateObj, SUPPORT_PAUSE))))
|
||||||
) {
|
) {
|
||||||
buttons.push({
|
buttons.push({
|
||||||
icon:
|
icon:
|
||||||
@@ -299,8 +302,29 @@ export const computeMediaControls = (
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (assumedState && supportsFeature(stateObj, SUPPORT_PLAY)) {
|
||||||
|
buttons.push({
|
||||||
|
icon: mdiPlay,
|
||||||
|
action: "media_play",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (assumedState && supportsFeature(stateObj, SUPPORT_PAUSE)) {
|
||||||
|
buttons.push({
|
||||||
|
icon: mdiPause,
|
||||||
|
action: "media_pause",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (assumedState && supportsFeature(stateObj, SUPPORT_STOP)) {
|
||||||
|
buttons.push({
|
||||||
|
icon: mdiStop,
|
||||||
|
action: "media_stop",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
if (
|
if (
|
||||||
(state === "playing" || state === "paused") &&
|
(state === "playing" || state === "paused" || assumedState) &&
|
||||||
supportsFeature(stateObj, SUPPORT_NEXT_TRACK)
|
supportsFeature(stateObj, SUPPORT_NEXT_TRACK)
|
||||||
) {
|
) {
|
||||||
buttons.push({
|
buttons.push({
|
||||||
|
@@ -12,7 +12,8 @@ export type Selector =
|
|||||||
| ActionSelector
|
| ActionSelector
|
||||||
| StringSelector
|
| StringSelector
|
||||||
| ObjectSelector
|
| ObjectSelector
|
||||||
| SelectSelector;
|
| SelectSelector
|
||||||
|
| IconSelector;
|
||||||
|
|
||||||
export interface EntitySelector {
|
export interface EntitySelector {
|
||||||
entity: {
|
entity: {
|
||||||
@@ -133,8 +134,18 @@ export interface ObjectSelector {
|
|||||||
object: {};
|
object: {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface SelectOption {
|
||||||
|
value: string;
|
||||||
|
label: string;
|
||||||
|
}
|
||||||
|
|
||||||
export interface SelectSelector {
|
export interface SelectSelector {
|
||||||
select: {
|
select: {
|
||||||
options: string[];
|
options: string[] | SelectOption[];
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface IconSelector {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/ban-types
|
||||||
|
icon: {};
|
||||||
|
}
|
||||||
|
@@ -10,3 +10,11 @@ export const convertTextToSpeech = (
|
|||||||
options?: Record<string, unknown>;
|
options?: Record<string, unknown>;
|
||||||
}
|
}
|
||||||
) => hass.callApi<{ url: string; path: string }>("POST", "tts_get_url", data);
|
) => hass.callApi<{ url: string; path: string }>("POST", "tts_get_url", data);
|
||||||
|
|
||||||
|
const TTS_MEDIA_SOURCE_PREFIX = "media-source://tts/";
|
||||||
|
|
||||||
|
export const isTTSMediaSource = (mediaContentId: string) =>
|
||||||
|
mediaContentId.startsWith(TTS_MEDIA_SOURCE_PREFIX);
|
||||||
|
|
||||||
|
export const getProviderFromTTSMediaSource = (mediaContentId: string) =>
|
||||||
|
mediaContentId.substring(TTS_MEDIA_SOURCE_PREFIX.length);
|
||||||
|
@@ -126,6 +126,7 @@ export interface ZWaveJSNodeStatus {
|
|||||||
is_routing: boolean | null;
|
is_routing: boolean | null;
|
||||||
zwave_plus_version: number | null;
|
zwave_plus_version: number | null;
|
||||||
highest_security_class: SecurityClass | null;
|
highest_security_class: SecurityClass | null;
|
||||||
|
is_controller_node: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ZwaveJSNodeMetadata {
|
export interface ZwaveJSNodeMetadata {
|
||||||
|
@@ -83,6 +83,7 @@ class DialogConfigEntrySystemOptions extends LitElement {
|
|||||||
.checked=${!this._disableNewEntities}
|
.checked=${!this._disableNewEntities}
|
||||||
@change=${this._disableNewEntitiesChanged}
|
@change=${this._disableNewEntitiesChanged}
|
||||||
.disabled=${this._submitting}
|
.disabled=${this._submitting}
|
||||||
|
dialogInitialFocus
|
||||||
></ha-switch>
|
></ha-switch>
|
||||||
</ha-formfield>
|
</ha-formfield>
|
||||||
${this._allowUpdatePolling()
|
${this._allowUpdatePolling()
|
||||||
|
@@ -1,23 +1,27 @@
|
|||||||
import "@polymer/paper-item/paper-icon-item";
|
import "@material/mwc-list/mwc-list";
|
||||||
import "@polymer/paper-item/paper-item-body";
|
import "@material/mwc-list/mwc-list-item";
|
||||||
import Fuse from "fuse.js";
|
import Fuse from "fuse.js";
|
||||||
import {
|
import {
|
||||||
css,
|
css,
|
||||||
CSSResultGroup,
|
CSSResultGroup,
|
||||||
html,
|
html,
|
||||||
LitElement,
|
LitElement,
|
||||||
TemplateResult,
|
|
||||||
PropertyValues,
|
PropertyValues,
|
||||||
|
TemplateResult,
|
||||||
} from "lit";
|
} from "lit";
|
||||||
import { customElement, property, state } from "lit/decorators";
|
import { customElement, property, state } from "lit/decorators";
|
||||||
import { styleMap } from "lit/directives/style-map";
|
import { styleMap } from "lit/directives/style-map";
|
||||||
import memoizeOne from "memoize-one";
|
import memoizeOne from "memoize-one";
|
||||||
|
import { isComponentLoaded } from "../../common/config/is_component_loaded";
|
||||||
import { fireEvent } from "../../common/dom/fire_event";
|
import { fireEvent } from "../../common/dom/fire_event";
|
||||||
|
import { navigate } from "../../common/navigate";
|
||||||
import "../../common/search/search-input";
|
import "../../common/search/search-input";
|
||||||
import { caseInsensitiveStringCompare } from "../../common/string/compare";
|
import { caseInsensitiveStringCompare } from "../../common/string/compare";
|
||||||
import { LocalizeFunc } from "../../common/translations/localize";
|
import { LocalizeFunc } from "../../common/translations/localize";
|
||||||
import "../../components/ha-icon-next";
|
import "../../components/ha-icon-next";
|
||||||
|
import { getConfigEntries } from "../../data/config_entries";
|
||||||
import { domainToName } from "../../data/integration";
|
import { domainToName } from "../../data/integration";
|
||||||
|
import { showZWaveJSAddNodeDialog } from "../../panels/config/integrations/integration-panels/zwave_js/show-dialog-zwave_js-add-node";
|
||||||
import { HomeAssistant } from "../../types";
|
import { HomeAssistant } from "../../types";
|
||||||
import { brandsUrl } from "../../util/brands-url";
|
import { brandsUrl } from "../../util/brands-url";
|
||||||
import { documentationUrl } from "../../util/documentation-url";
|
import { documentationUrl } from "../../util/documentation-url";
|
||||||
@@ -26,6 +30,7 @@ import { configFlowContentStyles } from "./styles";
|
|||||||
interface HandlerObj {
|
interface HandlerObj {
|
||||||
name: string;
|
name: string;
|
||||||
slug: string;
|
slug: string;
|
||||||
|
is_add?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
@@ -77,6 +82,17 @@ class StepFlowPickHandler extends LitElement {
|
|||||||
protected render(): TemplateResult {
|
protected render(): TemplateResult {
|
||||||
const handlers = this._getHandlers();
|
const handlers = this._getHandlers();
|
||||||
|
|
||||||
|
const addDeviceRows: HandlerObj[] = ["zha", "zwave_js"]
|
||||||
|
.filter((domain) => isComponentLoaded(this.hass, domain))
|
||||||
|
.map((domain) => ({
|
||||||
|
name: this.hass.localize(
|
||||||
|
`ui.panel.config.integrations.add_${domain}_device`
|
||||||
|
),
|
||||||
|
slug: domain,
|
||||||
|
is_add: true,
|
||||||
|
}))
|
||||||
|
.sort((a, b) => caseInsensitiveStringCompare(a.name, b.name));
|
||||||
|
|
||||||
return html`
|
return html`
|
||||||
<h2>${this.hass.localize("ui.panel.config.integrations.new")}</h2>
|
<h2>${this.hass.localize("ui.panel.config.integrations.new")}</h2>
|
||||||
<search-input
|
<search-input
|
||||||
@@ -87,37 +103,20 @@ class StepFlowPickHandler extends LitElement {
|
|||||||
.label=${this.hass.localize("ui.panel.config.integrations.search")}
|
.label=${this.hass.localize("ui.panel.config.integrations.search")}
|
||||||
@keypress=${this._maybeSubmit}
|
@keypress=${this._maybeSubmit}
|
||||||
></search-input>
|
></search-input>
|
||||||
<div
|
<mwc-list
|
||||||
style=${styleMap({
|
style=${styleMap({
|
||||||
width: `${this._width}px`,
|
width: `${this._width}px`,
|
||||||
height: `${this._height}px`,
|
height: `${this._height}px`,
|
||||||
})}
|
})}
|
||||||
>
|
>
|
||||||
|
${addDeviceRows.length
|
||||||
|
? html`
|
||||||
|
${addDeviceRows.map((handler) => this._renderRow(handler))}
|
||||||
|
<li divider padded class="divider" role="separator"></li>
|
||||||
|
`
|
||||||
|
: ""}
|
||||||
${handlers.length
|
${handlers.length
|
||||||
? handlers.map(
|
? handlers.map((handler) => this._renderRow(handler))
|
||||||
(handler: HandlerObj) =>
|
|
||||||
html`
|
|
||||||
<paper-icon-item
|
|
||||||
@click=${this._handlerPicked}
|
|
||||||
.handler=${handler}
|
|
||||||
>
|
|
||||||
<img
|
|
||||||
slot="item-icon"
|
|
||||||
loading="lazy"
|
|
||||||
src=${brandsUrl({
|
|
||||||
domain: handler.slug,
|
|
||||||
type: "icon",
|
|
||||||
useFallback: true,
|
|
||||||
darkOptimized: this.hass.themes?.darkMode,
|
|
||||||
})}
|
|
||||||
referrerpolicy="no-referrer"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<paper-item-body> ${handler.name} </paper-item-body>
|
|
||||||
<ha-icon-next></ha-icon-next>
|
|
||||||
</paper-icon-item>
|
|
||||||
`
|
|
||||||
)
|
|
||||||
: html`
|
: html`
|
||||||
<p>
|
<p>
|
||||||
${this.hass.localize(
|
${this.hass.localize(
|
||||||
@@ -140,15 +139,56 @@ class StepFlowPickHandler extends LitElement {
|
|||||||
>.
|
>.
|
||||||
</p>
|
</p>
|
||||||
`}
|
`}
|
||||||
</div>
|
</mwc-list>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _renderRow(handler: HandlerObj) {
|
||||||
|
return html`
|
||||||
|
<mwc-list-item
|
||||||
|
graphic="medium"
|
||||||
|
.hasMeta=${!handler.is_add}
|
||||||
|
.handler=${handler}
|
||||||
|
@click=${this._handlerPicked}
|
||||||
|
>
|
||||||
|
<img
|
||||||
|
slot="graphic"
|
||||||
|
loading="lazy"
|
||||||
|
src=${brandsUrl({
|
||||||
|
domain: handler.slug,
|
||||||
|
type: "icon",
|
||||||
|
useFallback: true,
|
||||||
|
darkOptimized: this.hass.themes?.darkMode,
|
||||||
|
})}
|
||||||
|
referrerpolicy="no-referrer"
|
||||||
|
/>
|
||||||
|
<span>${handler.name}</span>
|
||||||
|
${handler.is_add ? "" : html`<ha-icon-next slot="meta"></ha-icon-next>`}
|
||||||
|
</mwc-list-item>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
public willUpdate(changedProps: PropertyValues): void {
|
public willUpdate(changedProps: PropertyValues): void {
|
||||||
|
super.willUpdate(changedProps);
|
||||||
if (this._filter === undefined && this.initialFilter !== undefined) {
|
if (this._filter === undefined && this.initialFilter !== undefined) {
|
||||||
this._filter = this.initialFilter;
|
this._filter = this.initialFilter;
|
||||||
}
|
}
|
||||||
super.willUpdate(changedProps);
|
if (this.initialFilter !== undefined && this._filter === "") {
|
||||||
|
this.initialFilter = undefined;
|
||||||
|
this._filter = "";
|
||||||
|
this._width = undefined;
|
||||||
|
this._height = undefined;
|
||||||
|
} else if (
|
||||||
|
this.hasUpdated &&
|
||||||
|
changedProps.has("_filter") &&
|
||||||
|
(!this._width || !this._height)
|
||||||
|
) {
|
||||||
|
// Store the width and height so that when we search, box doesn't jump
|
||||||
|
const boundingRect =
|
||||||
|
this.shadowRoot!.querySelector("mwc-list")!.getBoundingClientRect();
|
||||||
|
this._width = boundingRect.width;
|
||||||
|
this._height = boundingRect.height;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected firstUpdated(changedProps) {
|
protected firstUpdated(changedProps) {
|
||||||
@@ -159,24 +199,6 @@ class StepFlowPickHandler extends LitElement {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected updated(changedProps) {
|
|
||||||
super.updated(changedProps);
|
|
||||||
// Store the width and height so that when we search, box doesn't jump
|
|
||||||
const div = this.shadowRoot!.querySelector("div")!;
|
|
||||||
if (!this._width) {
|
|
||||||
const width = div.clientWidth;
|
|
||||||
if (width) {
|
|
||||||
this._width = width;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!this._height) {
|
|
||||||
const height = div.clientHeight;
|
|
||||||
if (height) {
|
|
||||||
this._height = height;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private _getHandlers() {
|
private _getHandlers() {
|
||||||
return this._filterHandlers(
|
return this._filterHandlers(
|
||||||
this.handlers,
|
this.handlers,
|
||||||
@@ -190,8 +212,31 @@ class StepFlowPickHandler extends LitElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private async _handlerPicked(ev) {
|
private async _handlerPicked(ev) {
|
||||||
|
const handler: HandlerObj = ev.currentTarget.handler;
|
||||||
|
|
||||||
|
if (handler.is_add) {
|
||||||
|
if (handler.slug === "zwave_js") {
|
||||||
|
const entries = await getConfigEntries(this.hass);
|
||||||
|
const entry = entries.find((ent) => ent.domain === "zwave_js");
|
||||||
|
|
||||||
|
if (!entry) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
showZWaveJSAddNodeDialog(this, {
|
||||||
|
entry_id: entry.entry_id,
|
||||||
|
});
|
||||||
|
} else if (handler.slug === "zha") {
|
||||||
|
navigate("/config/zha/add");
|
||||||
|
}
|
||||||
|
|
||||||
|
// This closes dialog.
|
||||||
|
fireEvent(this, "flow-update");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
fireEvent(this, "handler-picked", {
|
fireEvent(this, "handler-picked", {
|
||||||
handler: ev.currentTarget.handler.slug,
|
handler: handler.slug,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -219,27 +264,26 @@ class StepFlowPickHandler extends LitElement {
|
|||||||
}
|
}
|
||||||
search-input {
|
search-input {
|
||||||
display: block;
|
display: block;
|
||||||
margin: -12px 16px 0;
|
margin: 16px 16px 0;
|
||||||
}
|
}
|
||||||
ha-icon-next {
|
ha-icon-next {
|
||||||
margin-right: 8px;
|
margin-right: 8px;
|
||||||
}
|
}
|
||||||
div {
|
mwc-list {
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
max-height: 600px;
|
max-height: 600px;
|
||||||
}
|
}
|
||||||
|
.divider {
|
||||||
|
border-bottom-color: var(--divider-color);
|
||||||
|
}
|
||||||
h2 {
|
h2 {
|
||||||
padding-right: 66px;
|
padding-right: 66px;
|
||||||
}
|
}
|
||||||
@media all and (max-height: 900px) {
|
@media all and (max-height: 900px) {
|
||||||
div {
|
mwc-list {
|
||||||
max-height: calc(100vh - 134px);
|
max-height: calc(100vh - 134px);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
paper-icon-item {
|
|
||||||
cursor: pointer;
|
|
||||||
margin-bottom: 4px;
|
|
||||||
}
|
|
||||||
p {
|
p {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
padding: 16px;
|
padding: 16px;
|
||||||
|
@@ -615,10 +615,6 @@ class MoreInfoLight extends LitElement {
|
|||||||
color: var(--secondary-text-color);
|
color: var(--secondary-text-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
paper-item {
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
hr {
|
hr {
|
||||||
border-color: var(--divider-color);
|
border-color: var(--divider-color);
|
||||||
border-bottom: none;
|
border-bottom: none;
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
import { html, LitElement, TemplateResult } from "lit";
|
||||||
import { customElement, property } from "lit/decorators";
|
import { customElement, property } from "lit/decorators";
|
||||||
import { supportsFeature } from "../../../common/entity/supports-feature";
|
import { supportsFeature } from "../../../common/entity/supports-feature";
|
||||||
import "../../../components/ha-attributes";
|
import "../../../components/ha-attributes";
|
||||||
@@ -66,14 +66,6 @@ class MoreInfoRemote extends LitElement {
|
|||||||
activity: newVal,
|
activity: newVal,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
static get styles(): CSSResultGroup {
|
|
||||||
return css`
|
|
||||||
paper-item {
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
|
@@ -241,9 +241,6 @@ class MoreInfoVacuum extends LitElement {
|
|||||||
.status-subtitle {
|
.status-subtitle {
|
||||||
color: var(--secondary-text-color);
|
color: var(--secondary-text-color);
|
||||||
}
|
}
|
||||||
paper-item {
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
.flex-horizontal {
|
.flex-horizontal {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
|
@@ -338,7 +338,9 @@ export class MoreInfoDialog extends LitElement {
|
|||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
|
.content {
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
@media all and (max-width: 450px), all and (max-height: 500px) {
|
@media all and (max-width: 450px), all and (max-height: 500px) {
|
||||||
ha-header-bar {
|
ha-header-bar {
|
||||||
--mdc-theme-primary: var(--app-header-background-color);
|
--mdc-theme-primary: var(--app-header-background-color);
|
||||||
|
@@ -1,5 +1,4 @@
|
|||||||
import "@material/mwc-button/mwc-button";
|
import "@material/mwc-button/mwc-button";
|
||||||
import { mdiFilterVariant } from "@mdi/js";
|
|
||||||
import "@polymer/paper-tooltip/paper-tooltip";
|
import "@polymer/paper-tooltip/paper-tooltip";
|
||||||
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
||||||
import { customElement, property, query } from "lit/decorators";
|
import { customElement, property, query } from "lit/decorators";
|
||||||
@@ -157,30 +156,31 @@ export class HaTabsSubpageDataTable extends LitElement {
|
|||||||
: hiddenLabel;
|
: hiddenLabel;
|
||||||
|
|
||||||
const headerToolbar = html`<search-input
|
const headerToolbar = html`<search-input
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
.filter=${this.filter}
|
.filter=${this.filter}
|
||||||
@value-changed=${this._handleSearchChange}
|
.suffix=${!this.narrow}
|
||||||
.label=${this.searchLabel ||
|
@value-changed=${this._handleSearchChange}
|
||||||
this.hass.localize("ui.components.data-table.search")}
|
.label=${this.searchLabel ||
|
||||||
>
|
this.hass.localize("ui.components.data-table.search")}
|
||||||
</search-input>
|
>
|
||||||
<div class="filters">
|
${!this.narrow
|
||||||
${filterInfo
|
? html`<div
|
||||||
? html`<div class="active-filters">
|
class="filters"
|
||||||
${this.narrow
|
slot="suffix"
|
||||||
? html`<div>
|
@click=${this._preventDefault}
|
||||||
<ha-svg-icon .path=${mdiFilterVariant}></ha-svg-icon>
|
>
|
||||||
<paper-tooltip animation-delay="0" position="left">
|
${filterInfo
|
||||||
${filterInfo}
|
? html`<div class="active-filters">
|
||||||
</paper-tooltip>
|
${filterInfo}
|
||||||
</div>`
|
<mwc-button @click=${this._clearFilter}>
|
||||||
: filterInfo}
|
${this.hass.localize("ui.components.data-table.clear")}
|
||||||
<mwc-button @click=${this._clearFilter}>
|
</mwc-button>
|
||||||
${this.hass.localize("ui.components.data-table.clear")}
|
</div>`
|
||||||
</mwc-button>
|
: ""}
|
||||||
</div>`
|
<slot name="filter-menu"></slot>
|
||||||
: ""}<slot name="filter-menu"></slot>
|
</div>`
|
||||||
</div>`;
|
: ""}
|
||||||
|
</search-input>`;
|
||||||
|
|
||||||
return html`
|
return html`
|
||||||
<hass-tabs-subpage
|
<hass-tabs-subpage
|
||||||
@@ -195,7 +195,16 @@ export class HaTabsSubpageDataTable extends LitElement {
|
|||||||
.mainPage=${this.mainPage}
|
.mainPage=${this.mainPage}
|
||||||
.supervisor=${this.supervisor}
|
.supervisor=${this.supervisor}
|
||||||
>
|
>
|
||||||
<div slot="toolbar-icon"><slot name="toolbar-icon"></slot></div>
|
<div slot="toolbar-icon">
|
||||||
|
${this.narrow
|
||||||
|
? html`<div class="filter-menu">
|
||||||
|
${this.numHidden || this.activeFilters
|
||||||
|
? html`<span class="badge">${this.numHidden || "!"}</span>`
|
||||||
|
: ""}
|
||||||
|
<slot name="filter-menu"></slot>
|
||||||
|
</div>`
|
||||||
|
: ""}<slot name="toolbar-icon"></slot>
|
||||||
|
</div>
|
||||||
${this.narrow
|
${this.narrow
|
||||||
? html`
|
? html`
|
||||||
<div slot="header">
|
<div slot="header">
|
||||||
@@ -233,6 +242,10 @@ export class HaTabsSubpageDataTable extends LitElement {
|
|||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private _preventDefault(ev) {
|
||||||
|
ev.preventDefault();
|
||||||
|
}
|
||||||
|
|
||||||
private _handleSearchChange(ev: CustomEvent) {
|
private _handleSearchChange(ev: CustomEvent) {
|
||||||
if (this.filter === ev.detail.value) {
|
if (this.filter === ev.detail.value) {
|
||||||
return;
|
return;
|
||||||
@@ -267,6 +280,12 @@ export class HaTabsSubpageDataTable extends LitElement {
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
color: var(--secondary-text-color);
|
color: var(--secondary-text-color);
|
||||||
}
|
}
|
||||||
|
search-input {
|
||||||
|
--mdc-text-field-fill-color: var(--sidebar-background-color);
|
||||||
|
--mdc-text-field-idle-line-color: var(--divider-color);
|
||||||
|
--text-field-overflow: visible;
|
||||||
|
z-index: 5;
|
||||||
|
}
|
||||||
.table-header search-input {
|
.table-header search-input {
|
||||||
display: block;
|
display: block;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
@@ -276,16 +295,19 @@ export class HaTabsSubpageDataTable extends LitElement {
|
|||||||
}
|
}
|
||||||
.search-toolbar search-input {
|
.search-toolbar search-input {
|
||||||
display: block;
|
display: block;
|
||||||
|
width: 100%;
|
||||||
color: var(--secondary-text-color);
|
color: var(--secondary-text-color);
|
||||||
--mdc-text-field-fill-color: transparant;
|
|
||||||
--mdc-text-field-idle-line-color: var(--divider-color);
|
|
||||||
--mdc-ripple-color: transparant;
|
--mdc-ripple-color: transparant;
|
||||||
}
|
}
|
||||||
.filters {
|
.filters {
|
||||||
|
--mdc-text-field-fill-color: var(--input-fill-color);
|
||||||
|
--mdc-text-field-idle-line-color: var(--input-idle-line-color);
|
||||||
|
--mdc-shape-small: 4px;
|
||||||
|
--text-field-overflow: initial;
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: flex-end;
|
justify-content: flex-end;
|
||||||
width: 100%;
|
|
||||||
margin-right: 8px;
|
margin-right: 8px;
|
||||||
|
color: var(--primary-text-color);
|
||||||
}
|
}
|
||||||
.active-filters {
|
.active-filters {
|
||||||
color: var(--primary-text-color);
|
color: var(--primary-text-color);
|
||||||
@@ -295,6 +317,8 @@ export class HaTabsSubpageDataTable extends LitElement {
|
|||||||
padding: 2px 2px 2px 8px;
|
padding: 2px 2px 2px 8px;
|
||||||
margin-left: 4px;
|
margin-left: 4px;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
|
width: max-content;
|
||||||
|
cursor: initial;
|
||||||
}
|
}
|
||||||
.active-filters ha-svg-icon {
|
.active-filters ha-svg-icon {
|
||||||
color: var(--primary-color);
|
color: var(--primary-color);
|
||||||
@@ -313,6 +337,24 @@ export class HaTabsSubpageDataTable extends LitElement {
|
|||||||
left: 0;
|
left: 0;
|
||||||
content: "";
|
content: "";
|
||||||
}
|
}
|
||||||
|
.badge {
|
||||||
|
min-width: 20px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
border-radius: 50%;
|
||||||
|
font-weight: 400;
|
||||||
|
background-color: var(--primary-color);
|
||||||
|
line-height: 20px;
|
||||||
|
text-align: center;
|
||||||
|
padding: 0px 4px;
|
||||||
|
color: var(--text-primary-color);
|
||||||
|
position: absolute;
|
||||||
|
right: 0;
|
||||||
|
top: 4px;
|
||||||
|
font-size: 0.65em;
|
||||||
|
}
|
||||||
|
.filter-menu {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -272,6 +272,7 @@ class HassTabsSubpage extends LitElement {
|
|||||||
ha-menu-button,
|
ha-menu-button,
|
||||||
ha-icon-button-arrow-prev,
|
ha-icon-button-arrow-prev,
|
||||||
::slotted([slot="toolbar-icon"]) {
|
::slotted([slot="toolbar-icon"]) {
|
||||||
|
display: flex;
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
pointer-events: auto;
|
pointer-events: auto;
|
||||||
color: var(--sidebar-icon-color);
|
color: var(--sidebar-icon-color);
|
||||||
|
@@ -1,7 +1,6 @@
|
|||||||
import { ActionDetail } from "@material/mwc-list/mwc-list-foundation";
|
import { ActionDetail } from "@material/mwc-list/mwc-list-foundation";
|
||||||
import "@material/mwc-list/mwc-list-item";
|
import "@material/mwc-list/mwc-list-item";
|
||||||
import { mdiDotsVertical } from "@mdi/js";
|
import { mdiDotsVertical } from "@mdi/js";
|
||||||
import "@polymer/paper-item/paper-item";
|
|
||||||
import { css, CSSResultGroup, html, LitElement } from "lit";
|
import { css, CSSResultGroup, html, LitElement } from "lit";
|
||||||
import { customElement, property, state } from "lit/decorators";
|
import { customElement, property, state } from "lit/decorators";
|
||||||
import { fireEvent } from "../../../../common/dom/fire_event";
|
import { fireEvent } from "../../../../common/dom/fire_event";
|
||||||
|
@@ -1,4 +1,5 @@
|
|||||||
import "@polymer/paper-dropdown-menu/paper-dropdown-menu-light";
|
import "@material/mwc-list/mwc-list-item";
|
||||||
|
import "@material/mwc-select";
|
||||||
import { UnsubscribeFunc } from "home-assistant-js-websocket";
|
import { UnsubscribeFunc } from "home-assistant-js-websocket";
|
||||||
import { html, LitElement } from "lit";
|
import { html, LitElement } from "lit";
|
||||||
import { customElement, property, state } from "lit/decorators";
|
import { customElement, property, state } from "lit/decorators";
|
||||||
@@ -48,41 +49,35 @@ export class HaTriggerCondition extends LitElement {
|
|||||||
"ui.panel.config.automation.editor.conditions.type.trigger.no_triggers"
|
"ui.panel.config.automation.editor.conditions.type.trigger.no_triggers"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return html`<paper-dropdown-menu-light
|
return html`<mwc-select
|
||||||
.label=${this.hass.localize(
|
.label=${this.hass.localize(
|
||||||
"ui.panel.config.automation.editor.conditions.type.trigger.id"
|
"ui.panel.config.automation.editor.conditions.type.trigger.id"
|
||||||
)}
|
)}
|
||||||
no-animations
|
.value=${id}
|
||||||
|
@selected=${this._triggerPicked}
|
||||||
>
|
>
|
||||||
<paper-listbox
|
${ensureArray(this._triggers).map((trigger) =>
|
||||||
slot="dropdown-content"
|
trigger.id
|
||||||
.selected=${id}
|
? html`
|
||||||
attr-for-selected="data-trigger-id"
|
<mwc-list-item .value=${trigger.id}>
|
||||||
@selected-item-changed=${this._triggerPicked}
|
${trigger.id}
|
||||||
>
|
</mwc-list-item>
|
||||||
${ensureArray(this._triggers).map((trigger) =>
|
`
|
||||||
trigger.id
|
: ""
|
||||||
? html`
|
)}
|
||||||
<paper-item data-trigger-id=${trigger.id}>
|
</mwc-select>`;
|
||||||
${trigger.id}
|
|
||||||
</paper-item>
|
|
||||||
`
|
|
||||||
: ""
|
|
||||||
)}
|
|
||||||
</paper-listbox>
|
|
||||||
</paper-dropdown-menu-light>`;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private _automationUpdated(config?: AutomationConfig) {
|
private _automationUpdated(config?: AutomationConfig) {
|
||||||
this._triggers = config?.trigger;
|
this._triggers = config?.trigger;
|
||||||
}
|
}
|
||||||
|
|
||||||
private _triggerPicked(ev: CustomEvent) {
|
private _triggerPicked(ev) {
|
||||||
ev.stopPropagation();
|
ev.stopPropagation();
|
||||||
if (!ev.detail.value) {
|
if (!ev.target.value) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const newTrigger = ev.detail.value.dataset.triggerId;
|
const newTrigger = ev.target.value;
|
||||||
if (this.condition.id === newTrigger) {
|
if (this.condition.id === newTrigger) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@@ -1,15 +1,13 @@
|
|||||||
import "@polymer/paper-input/paper-input";
|
import "../../../../../components/ha-form/ha-form";
|
||||||
import "@polymer/paper-input/paper-textarea";
|
|
||||||
import { html, LitElement, PropertyValues } from "lit";
|
import { html, LitElement, PropertyValues } from "lit";
|
||||||
import { customElement, property } from "lit/decorators";
|
import { customElement, property } from "lit/decorators";
|
||||||
|
import memoizeOne from "memoize-one";
|
||||||
|
import type { HaFormSchema } from "../../../../../components/ha-form/types";
|
||||||
import { createDurationData } from "../../../../../common/datetime/create_duration_data";
|
import { createDurationData } from "../../../../../common/datetime/create_duration_data";
|
||||||
import { fireEvent } from "../../../../../common/dom/fire_event";
|
import { fireEvent } from "../../../../../common/dom/fire_event";
|
||||||
import { hasTemplate } from "../../../../../common/string/has-template";
|
import { hasTemplate } from "../../../../../common/string/has-template";
|
||||||
import "../../../../../components/entity/ha-entity-picker";
|
import type { NumericStateTrigger } from "../../../../../data/automation";
|
||||||
import { NumericStateTrigger } from "../../../../../data/automation";
|
import type { HomeAssistant } from "../../../../../types";
|
||||||
import { HomeAssistant } from "../../../../../types";
|
|
||||||
import { handleChangeEvent } from "../ha-automation-trigger-row";
|
|
||||||
import "../../../../../components/ha-duration-input";
|
|
||||||
|
|
||||||
@customElement("ha-automation-trigger-numeric_state")
|
@customElement("ha-automation-trigger-numeric_state")
|
||||||
export class HaNumericStateTrigger extends LitElement {
|
export class HaNumericStateTrigger extends LitElement {
|
||||||
@@ -17,6 +15,22 @@ export class HaNumericStateTrigger extends LitElement {
|
|||||||
|
|
||||||
@property() public trigger!: NumericStateTrigger;
|
@property() public trigger!: NumericStateTrigger;
|
||||||
|
|
||||||
|
private _schema = memoizeOne((entityId): HaFormSchema[] => [
|
||||||
|
{ name: "entity_id", selector: { entity: {} } },
|
||||||
|
{
|
||||||
|
name: "attribute",
|
||||||
|
selector: { attribute: { entity_id: entityId } },
|
||||||
|
},
|
||||||
|
{ name: "above", required: false, selector: { text: {} } },
|
||||||
|
{ name: "below", required: false, selector: { text: {} } },
|
||||||
|
{
|
||||||
|
name: "value_template",
|
||||||
|
required: false,
|
||||||
|
selector: { text: { multiline: true } },
|
||||||
|
},
|
||||||
|
{ name: "for", required: false, selector: { duration: {} } },
|
||||||
|
]);
|
||||||
|
|
||||||
public willUpdate(changedProperties: PropertyValues) {
|
public willUpdate(changedProperties: PropertyValues) {
|
||||||
if (!changedProperties.has("trigger")) {
|
if (!changedProperties.has("trigger")) {
|
||||||
return;
|
return;
|
||||||
@@ -38,67 +52,46 @@ export class HaNumericStateTrigger extends LitElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public render() {
|
public render() {
|
||||||
const { value_template, entity_id, attribute, below, above } = this.trigger;
|
|
||||||
const trgFor = createDurationData(this.trigger.for);
|
const trgFor = createDurationData(this.trigger.for);
|
||||||
|
|
||||||
|
const data = { ...this.trigger, for: trgFor };
|
||||||
|
const schema = this._schema(this.trigger.entity_id);
|
||||||
|
|
||||||
return html`
|
return html`
|
||||||
<ha-entity-picker
|
<ha-form
|
||||||
.value=${entity_id}
|
|
||||||
@value-changed=${this._valueChanged}
|
|
||||||
.name=${"entity_id"}
|
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
allow-custom-entity
|
.data=${data}
|
||||||
></ha-entity-picker>
|
.schema=${schema}
|
||||||
<ha-entity-attribute-picker
|
|
||||||
.hass=${this.hass}
|
|
||||||
.entityId=${entity_id}
|
|
||||||
.value=${attribute}
|
|
||||||
.name=${"attribute"}
|
|
||||||
.label=${this.hass.localize(
|
|
||||||
"ui.panel.config.automation.editor.triggers.type.state.attribute"
|
|
||||||
)}
|
|
||||||
@value-changed=${this._valueChanged}
|
@value-changed=${this._valueChanged}
|
||||||
allow-custom-value
|
.computeLabel=${this._computeLabelCallback}
|
||||||
></ha-entity-attribute-picker>
|
></ha-form>
|
||||||
<paper-input
|
|
||||||
.label=${this.hass.localize(
|
|
||||||
"ui.panel.config.automation.editor.triggers.type.numeric_state.above"
|
|
||||||
)}
|
|
||||||
name="above"
|
|
||||||
.value=${above}
|
|
||||||
@value-changed=${this._valueChanged}
|
|
||||||
></paper-input>
|
|
||||||
<paper-input
|
|
||||||
.label=${this.hass.localize(
|
|
||||||
"ui.panel.config.automation.editor.triggers.type.numeric_state.below"
|
|
||||||
)}
|
|
||||||
name="below"
|
|
||||||
.value=${below}
|
|
||||||
@value-changed=${this._valueChanged}
|
|
||||||
></paper-input>
|
|
||||||
<paper-textarea
|
|
||||||
.label=${this.hass.localize(
|
|
||||||
"ui.panel.config.automation.editor.triggers.type.numeric_state.value_template"
|
|
||||||
)}
|
|
||||||
name="value_template"
|
|
||||||
.value=${value_template}
|
|
||||||
@value-changed=${this._valueChanged}
|
|
||||||
dir="ltr"
|
|
||||||
></paper-textarea>
|
|
||||||
<ha-duration-input
|
|
||||||
.label=${this.hass.localize(
|
|
||||||
"ui.panel.config.automation.editor.triggers.type.state.for"
|
|
||||||
)}
|
|
||||||
.name=${"for"}
|
|
||||||
.data=${trgFor}
|
|
||||||
@value-changed=${this._valueChanged}
|
|
||||||
></ha-duration-input>
|
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
private _valueChanged(ev: CustomEvent): void {
|
private _valueChanged(ev: CustomEvent): void {
|
||||||
handleChangeEvent(this, ev);
|
ev.stopPropagation();
|
||||||
|
const newTrigger = ev.detail.value;
|
||||||
|
fireEvent(this, "value-changed", { value: newTrigger });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private _computeLabelCallback = (schema: HaFormSchema): string => {
|
||||||
|
switch (schema.name) {
|
||||||
|
case "entity_id":
|
||||||
|
return this.hass.localize("ui.components.entity.entity-picker.entity");
|
||||||
|
case "attribute":
|
||||||
|
return this.hass.localize(
|
||||||
|
"ui.components.entity.entity-attribute-picker.attribute"
|
||||||
|
);
|
||||||
|
case "for":
|
||||||
|
return this.hass.localize(
|
||||||
|
`ui.panel.config.automation.editor.triggers.type.state.for`
|
||||||
|
);
|
||||||
|
default:
|
||||||
|
return this.hass.localize(
|
||||||
|
`ui.panel.config.automation.editor.triggers.type.numeric_state.${schema.name}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
|
@@ -1,13 +1,14 @@
|
|||||||
|
import "@material/mwc-list/mwc-list-item";
|
||||||
|
import "@material/mwc-select";
|
||||||
import "@polymer/paper-input/paper-input";
|
import "@polymer/paper-input/paper-input";
|
||||||
import { html, LitElement, PropertyValues } from "lit";
|
import { html, LitElement, PropertyValues } from "lit";
|
||||||
import { customElement, property, state } from "lit/decorators";
|
import { customElement, property, state } from "lit/decorators";
|
||||||
import { fireEvent } from "../../../../../common/dom/fire_event";
|
import { fireEvent } from "../../../../../common/dom/fire_event";
|
||||||
|
import { caseInsensitiveStringCompare } from "../../../../../common/string/compare";
|
||||||
import { TagTrigger } from "../../../../../data/automation";
|
import { TagTrigger } from "../../../../../data/automation";
|
||||||
import { fetchTags, Tag } from "../../../../../data/tag";
|
import { fetchTags, Tag } from "../../../../../data/tag";
|
||||||
import { HomeAssistant } from "../../../../../types";
|
import { HomeAssistant } from "../../../../../types";
|
||||||
import { TriggerElement } from "../ha-automation-trigger-row";
|
import { TriggerElement } from "../ha-automation-trigger-row";
|
||||||
import "../../../../../components/ha-paper-dropdown-menu";
|
|
||||||
import { caseInsensitiveStringCompare } from "../../../../../common/string/compare";
|
|
||||||
|
|
||||||
@customElement("ha-automation-trigger-tag")
|
@customElement("ha-automation-trigger-tag")
|
||||||
export class HaTagTrigger extends LitElement implements TriggerElement {
|
export class HaTagTrigger extends LitElement implements TriggerElement {
|
||||||
@@ -29,27 +30,22 @@ export class HaTagTrigger extends LitElement implements TriggerElement {
|
|||||||
protected render() {
|
protected render() {
|
||||||
const { tag_id } = this.trigger;
|
const { tag_id } = this.trigger;
|
||||||
return html`
|
return html`
|
||||||
<ha-paper-dropdown-menu
|
<mwc-select
|
||||||
.label=${this.hass.localize(
|
.label=${this.hass.localize(
|
||||||
"ui.panel.config.automation.editor.triggers.type.tag.label"
|
"ui.panel.config.automation.editor.triggers.type.tag.label"
|
||||||
)}
|
)}
|
||||||
?disabled=${this._tags.length === 0}
|
.disabled=${this._tags.length === 0}
|
||||||
|
.value=${tag_id}
|
||||||
|
@selected=${this._tagChanged}
|
||||||
>
|
>
|
||||||
<paper-listbox
|
${this._tags.map(
|
||||||
slot="dropdown-content"
|
(tag) => html`
|
||||||
.selected=${tag_id}
|
<mwc-list-item .value=${tag.id}>
|
||||||
attr-for-selected="tag_id"
|
${tag.name || tag.id}
|
||||||
@iron-select=${this._tagChanged}
|
</mwc-list-item>
|
||||||
>
|
`
|
||||||
${this._tags.map(
|
)}
|
||||||
(tag) => html`
|
</mwc-select>
|
||||||
<paper-item tag_id=${tag.id} .tag=${tag}>
|
|
||||||
${tag.name || tag.id}
|
|
||||||
</paper-item>
|
|
||||||
`
|
|
||||||
)}
|
|
||||||
</paper-listbox>
|
|
||||||
</ha-paper-dropdown-menu>
|
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -64,8 +60,14 @@ export class HaTagTrigger extends LitElement implements TriggerElement {
|
|||||||
fireEvent(this, "value-changed", {
|
fireEvent(this, "value-changed", {
|
||||||
value: {
|
value: {
|
||||||
...this.trigger,
|
...this.trigger,
|
||||||
tag_id: ev.detail.item.tag.id,
|
tag_id: ev.target.value,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface HTMLElementTagNameMap {
|
||||||
|
"ha-automation-trigger-tag": HaTagTrigger;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@@ -1,5 +1,4 @@
|
|||||||
import "@material/mwc-button";
|
import "@material/mwc-button";
|
||||||
import "@polymer/paper-item/paper-item-body";
|
|
||||||
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
||||||
import { customElement, property } from "lit/decorators";
|
import { customElement, property } from "lit/decorators";
|
||||||
import { fireEvent } from "../../../../common/dom/fire_event";
|
import { fireEvent } from "../../../../common/dom/fire_event";
|
||||||
|
@@ -5,18 +5,17 @@ import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
|||||||
import { customElement, property, state } from "lit/decorators";
|
import { customElement, property, state } from "lit/decorators";
|
||||||
import memoizeOne from "memoize-one";
|
import memoizeOne from "memoize-one";
|
||||||
import { fireEvent } from "../../../../common/dom/fire_event";
|
import { fireEvent } from "../../../../common/dom/fire_event";
|
||||||
import { caseInsensitiveStringCompare } from "../../../../common/string/compare";
|
|
||||||
import "../../../../components/ha-card";
|
import "../../../../components/ha-card";
|
||||||
import "../../../../components/ha-svg-icon";
|
import "../../../../components/ha-svg-icon";
|
||||||
import "../../../../components/ha-switch";
|
import "../../../../components/ha-switch";
|
||||||
|
import { CloudStatusLoggedIn, updateCloudPref } from "../../../../data/cloud";
|
||||||
import {
|
import {
|
||||||
CloudStatusLoggedIn,
|
|
||||||
CloudTTSInfo,
|
CloudTTSInfo,
|
||||||
getCloudTTSInfo,
|
getCloudTTSInfo,
|
||||||
updateCloudPref,
|
getCloudTtsLanguages,
|
||||||
} from "../../../../data/cloud";
|
getCloudTtsSupportedGenders,
|
||||||
|
} from "../../../../data/cloud/tts";
|
||||||
import { showAlertDialog } from "../../../../dialogs/generic/show-dialog-box";
|
import { showAlertDialog } from "../../../../dialogs/generic/show-dialog-box";
|
||||||
import { translationMetadata } from "../../../../resources/translations-metadata";
|
|
||||||
import type { HomeAssistant } from "../../../../types";
|
import type { HomeAssistant } from "../../../../types";
|
||||||
import { showTryTtsDialog } from "./show-dialog-cloud-tts-try";
|
import { showTryTtsDialog } from "./show-dialog-cloud-tts-try";
|
||||||
|
|
||||||
@@ -37,7 +36,11 @@ export class CloudTTSPref extends LitElement {
|
|||||||
|
|
||||||
const languages = this.getLanguages(this.ttsInfo);
|
const languages = this.getLanguages(this.ttsInfo);
|
||||||
const defaultVoice = this.cloudStatus.prefs.tts_default_voice;
|
const defaultVoice = this.cloudStatus.prefs.tts_default_voice;
|
||||||
const genders = this.getSupportedGenders(defaultVoice[0], this.ttsInfo);
|
const genders = this.getSupportedGenders(
|
||||||
|
defaultVoice[0],
|
||||||
|
this.ttsInfo,
|
||||||
|
this.hass.localize
|
||||||
|
);
|
||||||
|
|
||||||
return html`
|
return html`
|
||||||
<ha-card
|
<ha-card
|
||||||
@@ -100,61 +103,9 @@ export class CloudTTSPref extends LitElement {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private getLanguages = memoizeOne((info?: CloudTTSInfo) => {
|
private getLanguages = memoizeOne(getCloudTtsLanguages);
|
||||||
const languages: Array<[string, string]> = [];
|
|
||||||
|
|
||||||
if (!info) {
|
private getSupportedGenders = memoizeOne(getCloudTtsSupportedGenders);
|
||||||
return languages;
|
|
||||||
}
|
|
||||||
|
|
||||||
const seen = new Set<string>();
|
|
||||||
for (const [lang] of info.languages) {
|
|
||||||
if (seen.has(lang)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
seen.add(lang);
|
|
||||||
|
|
||||||
let label = lang;
|
|
||||||
|
|
||||||
if (lang in translationMetadata.translations) {
|
|
||||||
label = translationMetadata.translations[lang].nativeName;
|
|
||||||
} else {
|
|
||||||
const [langFamily, dialect] = lang.split("-");
|
|
||||||
if (langFamily in translationMetadata.translations) {
|
|
||||||
label = `${translationMetadata.translations[langFamily].nativeName}`;
|
|
||||||
|
|
||||||
if (langFamily.toLowerCase() !== dialect.toLowerCase()) {
|
|
||||||
label += ` (${dialect})`;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
languages.push([lang, label]);
|
|
||||||
}
|
|
||||||
return languages.sort((a, b) => caseInsensitiveStringCompare(a[1], b[1]));
|
|
||||||
});
|
|
||||||
|
|
||||||
private getSupportedGenders = memoizeOne(
|
|
||||||
(language: string, info?: CloudTTSInfo) => {
|
|
||||||
const genders: Array<[string, string]> = [];
|
|
||||||
|
|
||||||
if (!info) {
|
|
||||||
return genders;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const [curLang, gender] of info.languages) {
|
|
||||||
if (curLang === language) {
|
|
||||||
genders.push([
|
|
||||||
gender,
|
|
||||||
this.hass.localize(`ui.panel.config.cloud.account.tts.${gender}`) ||
|
|
||||||
gender,
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return genders.sort((a, b) => caseInsensitiveStringCompare(a[1], b[1]));
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
private _openTryDialog() {
|
private _openTryDialog() {
|
||||||
showTryTtsDialog(this, {
|
showTryTtsDialog(this, {
|
||||||
@@ -170,7 +121,11 @@ export class CloudTTSPref extends LitElement {
|
|||||||
const language = ev.target.value;
|
const language = ev.target.value;
|
||||||
|
|
||||||
const curGender = this.cloudStatus!.prefs.tts_default_voice[1];
|
const curGender = this.cloudStatus!.prefs.tts_default_voice[1];
|
||||||
const genders = this.getSupportedGenders(language, this.ttsInfo);
|
const genders = this.getSupportedGenders(
|
||||||
|
language,
|
||||||
|
this.ttsInfo,
|
||||||
|
this.hass.localize
|
||||||
|
);
|
||||||
const newGender = genders.find((item) => item[0] === curGender)
|
const newGender = genders.find((item) => item[0] === curGender)
|
||||||
? curGender
|
? curGender
|
||||||
: genders[0][0];
|
: genders[0][0];
|
||||||
|
@@ -1,5 +1,3 @@
|
|||||||
import "@polymer/paper-item/paper-item";
|
|
||||||
import "@polymer/paper-item/paper-item-body";
|
|
||||||
import { css, CSSResultGroup, html, LitElement, PropertyValues } from "lit";
|
import { css, CSSResultGroup, html, LitElement, PropertyValues } from "lit";
|
||||||
import { customElement, property, state } from "lit/decorators";
|
import { customElement, property, state } from "lit/decorators";
|
||||||
import { isComponentLoaded } from "../../../../common/config/is_component_loaded";
|
import { isComponentLoaded } from "../../../../common/config/is_component_loaded";
|
||||||
|
@@ -266,9 +266,6 @@ export class CloudRegister extends LitElement {
|
|||||||
a {
|
a {
|
||||||
color: var(--primary-color);
|
color: var(--primary-color);
|
||||||
}
|
}
|
||||||
paper-item {
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
h1 {
|
h1 {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
}
|
}
|
||||||
|
@@ -71,6 +71,7 @@ class DialogMQTTDeviceDebugInfo extends LitElement {
|
|||||||
<ha-switch
|
<ha-switch
|
||||||
.checked=${this._showDeserialized}
|
.checked=${this._showDeserialized}
|
||||||
@change=${this._showDeserializedChanged}
|
@change=${this._showDeserializedChanged}
|
||||||
|
dialogInitialFocus
|
||||||
>
|
>
|
||||||
</ha-switch>
|
</ha-switch>
|
||||||
</ha-formfield>
|
</ha-formfield>
|
||||||
|
@@ -10,8 +10,10 @@ import {
|
|||||||
import { customElement, property, state } from "lit/decorators";
|
import { customElement, property, state } from "lit/decorators";
|
||||||
import { DeviceRegistryEntry } from "../../../../../../data/device_registry";
|
import { DeviceRegistryEntry } from "../../../../../../data/device_registry";
|
||||||
import {
|
import {
|
||||||
|
fetchZwaveNodeStatus,
|
||||||
getZwaveJsIdentifiersFromDevice,
|
getZwaveJsIdentifiersFromDevice,
|
||||||
ZWaveJSNodeIdentifiers,
|
ZWaveJSNodeIdentifiers,
|
||||||
|
ZWaveJSNodeStatus,
|
||||||
} from "../../../../../../data/zwave_js";
|
} from "../../../../../../data/zwave_js";
|
||||||
import { haStyle } from "../../../../../../resources/styles";
|
import { haStyle } from "../../../../../../resources/styles";
|
||||||
import { HomeAssistant } from "../../../../../../types";
|
import { HomeAssistant } from "../../../../../../types";
|
||||||
@@ -29,43 +31,67 @@ export class HaDeviceActionsZWaveJS extends LitElement {
|
|||||||
|
|
||||||
@state() private _nodeId?: number;
|
@state() private _nodeId?: number;
|
||||||
|
|
||||||
|
@state() private _node?: ZWaveJSNodeStatus;
|
||||||
|
|
||||||
protected updated(changedProperties: PropertyValues) {
|
protected updated(changedProperties: PropertyValues) {
|
||||||
if (changedProperties.has("device")) {
|
if (changedProperties.has("device")) {
|
||||||
this._entryId = this.device.config_entries[0];
|
|
||||||
|
|
||||||
const identifiers: ZWaveJSNodeIdentifiers | undefined =
|
const identifiers: ZWaveJSNodeIdentifiers | undefined =
|
||||||
getZwaveJsIdentifiersFromDevice(this.device);
|
getZwaveJsIdentifiersFromDevice(this.device);
|
||||||
if (!identifiers) {
|
if (!identifiers) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this._nodeId = identifiers.node_id;
|
this._nodeId = identifiers.node_id;
|
||||||
|
this._entryId = this.device.config_entries[0];
|
||||||
|
|
||||||
|
this._fetchNodeDetails();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected async _fetchNodeDetails() {
|
||||||
|
if (!this._nodeId || !this._entryId) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this._node = await fetchZwaveNodeStatus(
|
||||||
|
this.hass,
|
||||||
|
this._entryId,
|
||||||
|
this._nodeId
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
protected render(): TemplateResult {
|
protected render(): TemplateResult {
|
||||||
|
if (!this._node) {
|
||||||
|
return html``;
|
||||||
|
}
|
||||||
return html`
|
return html`
|
||||||
<a
|
${!this._node.is_controller_node
|
||||||
.href=${`/config/zwave_js/node_config/${this.device.id}?config_entry=${this._entryId}`}
|
? html`
|
||||||
>
|
<a
|
||||||
<mwc-button>
|
.href=${`/config/zwave_js/node_config/${this.device.id}?config_entry=${this._entryId}`}
|
||||||
${this.hass.localize(
|
>
|
||||||
"ui.panel.config.zwave_js.device_info.device_config"
|
<mwc-button>
|
||||||
)}
|
${this.hass.localize(
|
||||||
</mwc-button>
|
"ui.panel.config.zwave_js.device_info.device_config"
|
||||||
</a>
|
)}
|
||||||
<mwc-button @click=${this._reinterviewClicked}>
|
</mwc-button>
|
||||||
${this.hass.localize(
|
</a>
|
||||||
"ui.panel.config.zwave_js.device_info.reinterview_device"
|
<mwc-button @click=${this._reinterviewClicked}>
|
||||||
)}
|
${this.hass.localize(
|
||||||
</mwc-button>
|
"ui.panel.config.zwave_js.device_info.reinterview_device"
|
||||||
<mwc-button @click=${this._healNodeClicked}>
|
)}
|
||||||
${this.hass.localize("ui.panel.config.zwave_js.device_info.heal_node")}
|
</mwc-button>
|
||||||
</mwc-button>
|
<mwc-button @click=${this._healNodeClicked}>
|
||||||
<mwc-button @click=${this._removeFailedNode}>
|
${this.hass.localize(
|
||||||
${this.hass.localize(
|
"ui.panel.config.zwave_js.device_info.heal_node"
|
||||||
"ui.panel.config.zwave_js.device_info.remove_failed"
|
)}
|
||||||
)}
|
</mwc-button>
|
||||||
</mwc-button>
|
<mwc-button @click=${this._removeFailedNode}>
|
||||||
|
${this.hass.localize(
|
||||||
|
"ui.panel.config.zwave_js.device_info.remove_failed"
|
||||||
|
)}
|
||||||
|
</mwc-button>
|
||||||
|
`
|
||||||
|
: ""}
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -103,52 +103,58 @@ export class HaDeviceInfoZWaveJS extends LitElement {
|
|||||||
${this.hass.localize("ui.panel.config.zwave_js.common.node_id")}:
|
${this.hass.localize("ui.panel.config.zwave_js.common.node_id")}:
|
||||||
${this._node.node_id}
|
${this._node.node_id}
|
||||||
</div>
|
</div>
|
||||||
<div>
|
${!this._node.is_controller_node
|
||||||
${this.hass.localize(
|
? html`
|
||||||
"ui.panel.config.zwave_js.device_info.node_status"
|
<div>
|
||||||
)}:
|
${this.hass.localize(
|
||||||
${this.hass.localize(
|
"ui.panel.config.zwave_js.device_info.node_status"
|
||||||
`ui.panel.config.zwave_js.node_status.${
|
)}:
|
||||||
nodeStatus[this._node.status]
|
${this.hass.localize(
|
||||||
}`
|
`ui.panel.config.zwave_js.node_status.${
|
||||||
)}
|
nodeStatus[this._node.status]
|
||||||
</div>
|
}`
|
||||||
<div>
|
)}
|
||||||
${this.hass.localize(
|
</div>
|
||||||
"ui.panel.config.zwave_js.device_info.node_ready"
|
<div>
|
||||||
)}:
|
${this.hass.localize(
|
||||||
${this._node.ready
|
"ui.panel.config.zwave_js.device_info.node_ready"
|
||||||
? this.hass.localize("ui.common.yes")
|
)}:
|
||||||
: this.hass.localize("ui.common.no")}
|
${this._node.ready
|
||||||
</div>
|
? this.hass.localize("ui.common.yes")
|
||||||
<div>
|
: this.hass.localize("ui.common.no")}
|
||||||
${this.hass.localize(
|
</div>
|
||||||
"ui.panel.config.zwave_js.device_info.highest_security"
|
<div>
|
||||||
)}:
|
${this.hass.localize(
|
||||||
${this._node.highest_security_class !== null
|
"ui.panel.config.zwave_js.device_info.highest_security"
|
||||||
? this.hass.localize(
|
)}:
|
||||||
`ui.panel.config.zwave_js.security_classes.${
|
${this._node.highest_security_class !== null
|
||||||
SecurityClass[this._node.highest_security_class]
|
? this.hass.localize(
|
||||||
}.title`
|
`ui.panel.config.zwave_js.security_classes.${
|
||||||
)
|
SecurityClass[this._node.highest_security_class]
|
||||||
: this._node.is_secure === false
|
}.title`
|
||||||
? this.hass.localize(
|
)
|
||||||
"ui.panel.config.zwave_js.security_classes.none.title"
|
: this._node.is_secure === false
|
||||||
)
|
? this.hass.localize(
|
||||||
: this.hass.localize("ui.panel.config.zwave_js.device_info.unknown")}
|
"ui.panel.config.zwave_js.security_classes.none.title"
|
||||||
</div>
|
)
|
||||||
<div>
|
: this.hass.localize(
|
||||||
${this.hass.localize(
|
"ui.panel.config.zwave_js.device_info.unknown"
|
||||||
"ui.panel.config.zwave_js.device_info.zwave_plus"
|
)}
|
||||||
)}:
|
</div>
|
||||||
${this._node.zwave_plus_version
|
<div>
|
||||||
? this.hass.localize(
|
${this.hass.localize(
|
||||||
"ui.panel.config.zwave_js.device_info.zwave_plus_version",
|
"ui.panel.config.zwave_js.device_info.zwave_plus"
|
||||||
"version",
|
)}:
|
||||||
this._node.zwave_plus_version
|
${this._node.zwave_plus_version
|
||||||
)
|
? this.hass.localize(
|
||||||
: this.hass.localize("ui.common.no")}
|
"ui.panel.config.zwave_js.device_info.zwave_plus_version",
|
||||||
</div>
|
"version",
|
||||||
|
this._node.zwave_plus_version
|
||||||
|
)
|
||||||
|
: this.hass.localize("ui.common.no")}
|
||||||
|
</div>
|
||||||
|
`
|
||||||
|
: ""}
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -1,6 +1,7 @@
|
|||||||
import "@material/mwc-button/mwc-button";
|
import "@material/mwc-button/mwc-button";
|
||||||
|
import "@material/mwc-list/mwc-list-item";
|
||||||
|
import "@material/mwc-select/mwc-select";
|
||||||
import "@polymer/paper-input/paper-input";
|
import "@polymer/paper-input/paper-input";
|
||||||
import type { PaperItemElement } from "@polymer/paper-item/paper-item";
|
|
||||||
import { HassEntity, UnsubscribeFunc } from "home-assistant-js-websocket";
|
import { HassEntity, UnsubscribeFunc } from "home-assistant-js-websocket";
|
||||||
import {
|
import {
|
||||||
css,
|
css,
|
||||||
@@ -17,7 +18,6 @@ import { domainIcon } from "../../../common/entity/domain_icon";
|
|||||||
import "../../../components/ha-area-picker";
|
import "../../../components/ha-area-picker";
|
||||||
import "../../../components/ha-expansion-panel";
|
import "../../../components/ha-expansion-panel";
|
||||||
import "../../../components/ha-icon-picker";
|
import "../../../components/ha-icon-picker";
|
||||||
import "../../../components/ha-paper-dropdown-menu";
|
|
||||||
import "../../../components/ha-switch";
|
import "../../../components/ha-switch";
|
||||||
import type { HaSwitch } from "../../../components/ha-switch";
|
import type { HaSwitch } from "../../../components/ha-switch";
|
||||||
import {
|
import {
|
||||||
@@ -158,28 +158,23 @@ export class EntityRegistrySettings extends SubscribeMixin(LitElement) {
|
|||||||
></ha-icon-picker>
|
></ha-icon-picker>
|
||||||
${OVERRIDE_DEVICE_CLASSES[domain]?.includes(this._deviceClass) ||
|
${OVERRIDE_DEVICE_CLASSES[domain]?.includes(this._deviceClass) ||
|
||||||
(domain === "cover" && this.entry.original_device_class === null)
|
(domain === "cover" && this.entry.original_device_class === null)
|
||||||
? html`<ha-paper-dropdown-menu
|
? html`<mwc-select
|
||||||
.label=${this.hass.localize(
|
.label=${this.hass.localize(
|
||||||
"ui.dialogs.entity_registry.editor.device_class"
|
"ui.dialogs.entity_registry.editor.device_class"
|
||||||
)}
|
)}
|
||||||
|
.value=${this._deviceClass}
|
||||||
|
@selected=${this._deviceClassChanged}
|
||||||
>
|
>
|
||||||
<paper-listbox
|
${OVERRIDE_DEVICE_CLASSES[domain].map(
|
||||||
slot="dropdown-content"
|
(deviceClass: string) => html`
|
||||||
attr-for-selected="item-value"
|
<mwc-list-item .value=${deviceClass}>
|
||||||
.selected=${this._deviceClass}
|
${this.hass.localize(
|
||||||
@selected-item-changed=${this._deviceClassChanged}
|
`ui.dialogs.entity_registry.editor.device_classes.${domain}.${deviceClass}`
|
||||||
>
|
)}
|
||||||
${OVERRIDE_DEVICE_CLASSES[domain].map(
|
</mwc-list-item>
|
||||||
(deviceClass: string) => html`
|
`
|
||||||
<paper-item .itemValue=${deviceClass}>
|
)}
|
||||||
${this.hass.localize(
|
</mwc-select>`
|
||||||
`ui.dialogs.entity_registry.editor.device_classes.${domain}.${deviceClass}`
|
|
||||||
)}
|
|
||||||
</paper-item>
|
|
||||||
`
|
|
||||||
)}
|
|
||||||
</paper-listbox>
|
|
||||||
</ha-paper-dropdown-menu>`
|
|
||||||
: ""}
|
: ""}
|
||||||
<paper-input
|
<paper-input
|
||||||
.value=${this._entityId}
|
.value=${this._entityId}
|
||||||
@@ -302,12 +297,9 @@ export class EntityRegistrySettings extends SubscribeMixin(LitElement) {
|
|||||||
this._entityId = ev.detail.value;
|
this._entityId = ev.detail.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
private _deviceClassChanged(ev: PolymerChangedEvent<PaperItemElement>): void {
|
private _deviceClassChanged(ev): void {
|
||||||
this._error = undefined;
|
this._error = undefined;
|
||||||
if (ev.detail.value === null) {
|
this._deviceClass = ev.target.value;
|
||||||
return;
|
|
||||||
}
|
|
||||||
this._deviceClass = (ev.detail.value as any).itemValue;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private _areaPicked(ev: CustomEvent) {
|
private _areaPicked(ev: CustomEvent) {
|
||||||
@@ -425,7 +417,7 @@ export class EntityRegistrySettings extends SubscribeMixin(LitElement) {
|
|||||||
padding-bottom: max(env(safe-area-inset-bottom), 8px);
|
padding-bottom: max(env(safe-area-inset-bottom), 8px);
|
||||||
background-color: var(--mdc-theme-surface, #fff);
|
background-color: var(--mdc-theme-surface, #fff);
|
||||||
}
|
}
|
||||||
ha-paper-dropdown-menu {
|
mwc-select {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
ha-switch {
|
ha-switch {
|
||||||
|
@@ -22,10 +22,6 @@ documentContainer.innerHTML = `<dom-module id="ha-form-style">
|
|||||||
@apply --layout-vertical;
|
@apply --layout-vertical;
|
||||||
@apply --layout-start;
|
@apply --layout-start;
|
||||||
}
|
}
|
||||||
|
|
||||||
paper-dropdown-menu.form-control {
|
|
||||||
margin: -9px 0;
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
</template>
|
</template>
|
||||||
</dom-module>`;
|
</dom-module>`;
|
||||||
|
@@ -21,8 +21,6 @@ import {
|
|||||||
mdiTools,
|
mdiTools,
|
||||||
mdiViewDashboard,
|
mdiViewDashboard,
|
||||||
} from "@mdi/js";
|
} from "@mdi/js";
|
||||||
import "@polymer/paper-item/paper-item";
|
|
||||||
import "@polymer/paper-item/paper-item-body";
|
|
||||||
import { PolymerElement } from "@polymer/polymer";
|
import { PolymerElement } from "@polymer/polymer";
|
||||||
import { PropertyValues } from "lit";
|
import { PropertyValues } from "lit";
|
||||||
import { customElement, property, state } from "lit/decorators";
|
import { customElement, property, state } from "lit/decorators";
|
||||||
|
@@ -2,7 +2,7 @@ import "@material/mwc-button/mwc-button";
|
|||||||
import "@polymer/paper-item/paper-icon-item";
|
import "@polymer/paper-item/paper-icon-item";
|
||||||
import "@polymer/paper-tooltip/paper-tooltip";
|
import "@polymer/paper-tooltip/paper-tooltip";
|
||||||
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
||||||
import { customElement, property, state, query } from "lit/decorators";
|
import { customElement, property, query, state } from "lit/decorators";
|
||||||
import { classMap } from "lit/directives/class-map";
|
import { classMap } from "lit/directives/class-map";
|
||||||
import { isComponentLoaded } from "../../../common/config/is_component_loaded";
|
import { isComponentLoaded } from "../../../common/config/is_component_loaded";
|
||||||
import { dynamicElement } from "../../../common/dom/dynamic-element-directive";
|
import { dynamicElement } from "../../../common/dom/dynamic-element-directive";
|
||||||
|
@@ -297,29 +297,36 @@ class HaConfigIntegrations extends SubscribeMixin(LitElement) {
|
|||||||
this._filter
|
this._filter
|
||||||
);
|
);
|
||||||
|
|
||||||
const filterMenu = html`<ha-button-menu
|
const filterMenu = html`<div
|
||||||
corner="BOTTOM_START"
|
slot=${ifDefined(this.narrow ? "toolbar-icon" : "suffix")}
|
||||||
multi
|
|
||||||
slot=${ifDefined(this.narrow ? "toolbar-icon" : undefined)}
|
|
||||||
@action=${this._handleMenuAction}
|
|
||||||
>
|
>
|
||||||
<ha-icon-button
|
${!this._showDisabled && this.narrow && disabledCount
|
||||||
slot="trigger"
|
? html`<span class="badge">${disabledCount}</span>`
|
||||||
.label=${this.hass.localize("ui.common.menu")}
|
: ""}
|
||||||
.path=${mdiFilterVariant}
|
<ha-button-menu
|
||||||
|
corner="BOTTOM_START"
|
||||||
|
multi
|
||||||
|
@action=${this._handleMenuAction}
|
||||||
|
@click=${this._preventDefault}
|
||||||
>
|
>
|
||||||
</ha-icon-button>
|
<ha-icon-button
|
||||||
<ha-check-list-item left .selected=${this._showIgnored}>
|
slot="trigger"
|
||||||
${this.hass.localize(
|
.label=${this.hass.localize("ui.common.menu")}
|
||||||
"ui.panel.config.integrations.ignore.show_ignored"
|
.path=${mdiFilterVariant}
|
||||||
)}
|
>
|
||||||
</ha-check-list-item>
|
</ha-icon-button>
|
||||||
<ha-check-list-item left .selected=${this._showDisabled}>
|
<ha-check-list-item left .selected=${this._showIgnored}>
|
||||||
${this.hass.localize(
|
${this.hass.localize(
|
||||||
"ui.panel.config.integrations.disable.show_disabled"
|
"ui.panel.config.integrations.ignore.show_ignored"
|
||||||
)}
|
)}
|
||||||
</ha-check-list-item>
|
</ha-check-list-item>
|
||||||
</ha-button-menu>`;
|
<ha-check-list-item left .selected=${this._showDisabled}>
|
||||||
|
${this.hass.localize(
|
||||||
|
"ui.panel.config.integrations.disable.show_disabled"
|
||||||
|
)}
|
||||||
|
</ha-check-list-item>
|
||||||
|
</ha-button-menu>
|
||||||
|
</div>`;
|
||||||
|
|
||||||
return html`
|
return html`
|
||||||
<hass-tabs-subpage
|
<hass-tabs-subpage
|
||||||
@@ -336,8 +343,6 @@ class HaConfigIntegrations extends SubscribeMixin(LitElement) {
|
|||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
.filter=${this._filter}
|
.filter=${this._filter}
|
||||||
class="header"
|
class="header"
|
||||||
no-label-float
|
|
||||||
no-underline
|
|
||||||
@value-changed=${this._handleSearchChange}
|
@value-changed=${this._handleSearchChange}
|
||||||
.label=${this.hass.localize(
|
.label=${this.hass.localize(
|
||||||
"ui.panel.config.integrations.search"
|
"ui.panel.config.integrations.search"
|
||||||
@@ -350,29 +355,33 @@ class HaConfigIntegrations extends SubscribeMixin(LitElement) {
|
|||||||
<div class="search">
|
<div class="search">
|
||||||
<search-input
|
<search-input
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
no-label-float
|
suffix
|
||||||
no-underline
|
|
||||||
.filter=${this._filter}
|
.filter=${this._filter}
|
||||||
@value-changed=${this._handleSearchChange}
|
@value-changed=${this._handleSearchChange}
|
||||||
.label=${this.hass.localize(
|
.label=${this.hass.localize(
|
||||||
"ui.panel.config.integrations.search"
|
"ui.panel.config.integrations.search"
|
||||||
)}
|
)}
|
||||||
></search-input>
|
>
|
||||||
${!this._showDisabled && disabledCount
|
${!this._showDisabled && disabledCount
|
||||||
? html`<div class="active-filters">
|
? html`<div
|
||||||
${this.hass.localize(
|
class="active-filters"
|
||||||
"ui.panel.config.integrations.disable.disabled_integrations",
|
slot="suffix"
|
||||||
{ number: disabledCount }
|
@click=${this._preventDefault}
|
||||||
)}
|
>
|
||||||
<mwc-button
|
${this.hass.localize(
|
||||||
@click=${this._toggleShowDisabled}
|
"ui.panel.config.integrations.disable.disabled_integrations",
|
||||||
.label=${this.hass.localize(
|
{ number: disabledCount }
|
||||||
"ui.panel.config.integrations.disable.show"
|
|
||||||
)}
|
)}
|
||||||
></mwc-button>
|
<mwc-button
|
||||||
</div>`
|
@click=${this._toggleShowDisabled}
|
||||||
: ""}
|
.label=${this.hass.localize(
|
||||||
${filterMenu}
|
"ui.panel.config.integrations.disable.show"
|
||||||
|
)}
|
||||||
|
></mwc-button>
|
||||||
|
</div>`
|
||||||
|
: ""}
|
||||||
|
${filterMenu}
|
||||||
|
</search-input>
|
||||||
</div>
|
</div>
|
||||||
`}
|
`}
|
||||||
|
|
||||||
@@ -503,6 +512,10 @@ class HaConfigIntegrations extends SubscribeMixin(LitElement) {
|
|||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private _preventDefault(ev) {
|
||||||
|
ev.preventDefault();
|
||||||
|
}
|
||||||
|
|
||||||
private _loadConfigEntries() {
|
private _loadConfigEntries() {
|
||||||
getConfigEntries(this.hass).then((configEntries) => {
|
getConfigEntries(this.hass).then((configEntries) => {
|
||||||
this._configEntries = configEntries
|
this._configEntries = configEntries
|
||||||
@@ -683,13 +696,15 @@ class HaConfigIntegrations extends SubscribeMixin(LitElement) {
|
|||||||
.empty-message h1 {
|
.empty-message h1 {
|
||||||
margin-bottom: 0;
|
margin-bottom: 0;
|
||||||
}
|
}
|
||||||
|
search-input {
|
||||||
|
--mdc-text-field-fill-color: var(--sidebar-background-color);
|
||||||
|
--mdc-text-field-idle-line-color: var(--divider-color);
|
||||||
|
--text-field-overflow: visible;
|
||||||
|
}
|
||||||
search-input.header {
|
search-input.header {
|
||||||
display: block;
|
display: block;
|
||||||
color: var(--secondary-text-color);
|
color: var(--secondary-text-color);
|
||||||
margin-left: 8px;
|
margin-left: 8px;
|
||||||
--mdc-text-field-fill-color: transparant;
|
|
||||||
--mdc-text-field-idle-line-color: var(--divider-color);
|
|
||||||
--mdc-ripple-color: transparant;
|
--mdc-ripple-color: transparant;
|
||||||
}
|
}
|
||||||
.search {
|
.search {
|
||||||
@@ -717,6 +732,8 @@ class HaConfigIntegrations extends SubscribeMixin(LitElement) {
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
padding: 2px 2px 2px 8px;
|
padding: 2px 2px 2px 8px;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
|
width: max-content;
|
||||||
|
cursor: initial;
|
||||||
}
|
}
|
||||||
.active-filters mwc-button {
|
.active-filters mwc-button {
|
||||||
margin-left: 8px;
|
margin-left: 8px;
|
||||||
@@ -732,6 +749,24 @@ class HaConfigIntegrations extends SubscribeMixin(LitElement) {
|
|||||||
left: 0;
|
left: 0;
|
||||||
content: "";
|
content: "";
|
||||||
}
|
}
|
||||||
|
.badge {
|
||||||
|
min-width: 20px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
border-radius: 50%;
|
||||||
|
font-weight: 400;
|
||||||
|
background-color: var(--primary-color);
|
||||||
|
line-height: 20px;
|
||||||
|
text-align: center;
|
||||||
|
padding: 0px 4px;
|
||||||
|
color: var(--text-primary-color);
|
||||||
|
position: absolute;
|
||||||
|
right: 14px;
|
||||||
|
top: 8px;
|
||||||
|
font-size: 0.65em;
|
||||||
|
}
|
||||||
|
ha-button-menu {
|
||||||
|
color: var(--primary-text-color);
|
||||||
|
}
|
||||||
`,
|
`,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
@@ -1,7 +1,5 @@
|
|||||||
import "@material/mwc-button/mwc-button";
|
import "@material/mwc-button/mwc-button";
|
||||||
import { mdiFolderMultipleOutline, mdiLan, mdiNetwork, mdiPlus } from "@mdi/js";
|
import { mdiFolderMultipleOutline, mdiLan, mdiNetwork, mdiPlus } from "@mdi/js";
|
||||||
import "@polymer/paper-item/paper-item";
|
|
||||||
import "@polymer/paper-item/paper-item-body";
|
|
||||||
import {
|
import {
|
||||||
css,
|
css,
|
||||||
CSSResultGroup,
|
CSSResultGroup,
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
|
import "@material/mwc-list/mwc-list-item";
|
||||||
|
import "@material/mwc-select/mwc-select";
|
||||||
import { mdiDownload } from "@mdi/js";
|
import { mdiDownload } from "@mdi/js";
|
||||||
import "@polymer/paper-dropdown-menu/paper-dropdown-menu";
|
|
||||||
import "@polymer/paper-listbox/paper-listbox";
|
|
||||||
import { UnsubscribeFunc } from "home-assistant-js-websocket";
|
import { UnsubscribeFunc } from "home-assistant-js-websocket";
|
||||||
import { css, CSSResultArray, html, LitElement } from "lit";
|
import { css, CSSResultArray, html, LitElement } from "lit";
|
||||||
import { customElement, property, query, state } from "lit/decorators";
|
import { customElement, property, query, state } from "lit/decorators";
|
||||||
@@ -77,26 +77,20 @@ class ZWaveJSLogs extends SubscribeMixin(LitElement) {
|
|||||||
<div class="card-content">
|
<div class="card-content">
|
||||||
${this._logConfig
|
${this._logConfig
|
||||||
? html`
|
? html`
|
||||||
<paper-dropdown-menu
|
<mwc-select
|
||||||
dynamic-align
|
|
||||||
.label=${this.hass.localize(
|
.label=${this.hass.localize(
|
||||||
"ui.panel.config.zwave_js.logs.log_level"
|
"ui.panel.config.zwave_js.logs.log_level"
|
||||||
)}
|
)}
|
||||||
|
.value=${this._logConfig.level}
|
||||||
|
@selected=${this._dropdownSelected}
|
||||||
>
|
>
|
||||||
<paper-listbox
|
<mwc-list-item value="error">Error</mwc-list-item>
|
||||||
slot="dropdown-content"
|
<mwc-list-item value="warn">Warn</mwc-list-item>
|
||||||
.selected=${this._logConfig.level}
|
<mwc-list-item value="info">Info</mwc-list-item>
|
||||||
attr-for-selected="value"
|
<mwc-list-item value="verbose">Verbose</mwc-list-item>
|
||||||
@iron-select=${this._dropdownSelected}
|
<mwc-list-item value="debug">Debug</mwc-list-item>
|
||||||
>
|
<mwc-list-item value="silly">Silly</mwc-list-item>
|
||||||
<paper-item value="error">Error</paper-item>
|
</mwc-select>
|
||||||
<paper-item value="warn">Warn</paper-item>
|
|
||||||
<paper-item value="info">Info</paper-item>
|
|
||||||
<paper-item value="verbose">Verbose</paper-item>
|
|
||||||
<paper-item value="debug">Debug</paper-item>
|
|
||||||
<paper-item value="silly">Silly</paper-item>
|
|
||||||
</paper-listbox>
|
|
||||||
</paper-dropdown-menu>
|
|
||||||
`
|
`
|
||||||
: ""}
|
: ""}
|
||||||
</div>
|
</div>
|
||||||
@@ -142,7 +136,7 @@ class ZWaveJSLogs extends SubscribeMixin(LitElement) {
|
|||||||
if (ev.target === undefined || this._logConfig === undefined) {
|
if (ev.target === undefined || this._logConfig === undefined) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const selected = ev.target.selected;
|
const selected = ev.target.value;
|
||||||
if (this._logConfig.level === selected) {
|
if (this._logConfig.level === selected) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@@ -1,13 +1,12 @@
|
|||||||
import "@material/mwc-button/mwc-button";
|
import "@material/mwc-button/mwc-button";
|
||||||
|
import "@material/mwc-list/mwc-list-item";
|
||||||
|
import "@material/mwc-select/mwc-select";
|
||||||
import {
|
import {
|
||||||
mdiCheckCircle,
|
mdiCheckCircle,
|
||||||
mdiCircle,
|
mdiCircle,
|
||||||
mdiCloseCircle,
|
mdiCloseCircle,
|
||||||
mdiProgressClock,
|
mdiProgressClock,
|
||||||
} from "@mdi/js";
|
} from "@mdi/js";
|
||||||
import "@polymer/paper-dropdown-menu/paper-dropdown-menu";
|
|
||||||
import "@polymer/paper-item/paper-item";
|
|
||||||
import "@polymer/paper-listbox/paper-listbox";
|
|
||||||
import { UnsubscribeFunc } from "home-assistant-js-websocket";
|
import { UnsubscribeFunc } from "home-assistant-js-websocket";
|
||||||
import {
|
import {
|
||||||
css,
|
css,
|
||||||
@@ -287,26 +286,20 @@ class ZWaveJSNodeConfig extends SubscribeMixin(LitElement) {
|
|||||||
return html`
|
return html`
|
||||||
${labelAndDescription}
|
${labelAndDescription}
|
||||||
<div class="flex">
|
<div class="flex">
|
||||||
<paper-dropdown-menu
|
<mwc-select
|
||||||
dynamic-align
|
|
||||||
.disabled=${!item.metadata.writeable}
|
.disabled=${!item.metadata.writeable}
|
||||||
|
.value=${item.value}
|
||||||
|
.key=${id}
|
||||||
|
.property=${item.property}
|
||||||
|
.propertyKey=${item.property_key}
|
||||||
|
@selected=${this._dropdownSelected}
|
||||||
>
|
>
|
||||||
<paper-listbox
|
${Object.entries(item.metadata.states).map(
|
||||||
slot="dropdown-content"
|
([key, entityState]) => html`
|
||||||
.selected=${item.value}
|
<mwc-list-item .value=${key}>${entityState}</mwc-list-item>
|
||||||
attr-for-selected="value"
|
`
|
||||||
.key=${id}
|
)}
|
||||||
.property=${item.property}
|
</mwc-select>
|
||||||
.propertyKey=${item.property_key}
|
|
||||||
@iron-select=${this._dropdownSelected}
|
|
||||||
>
|
|
||||||
${Object.entries(item.metadata.states).map(
|
|
||||||
([key, entityState]) => html`
|
|
||||||
<paper-item .value=${key}>${entityState}</paper-item>
|
|
||||||
`
|
|
||||||
)}
|
|
||||||
</paper-listbox>
|
|
||||||
</paper-dropdown-menu>
|
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
@@ -351,12 +344,12 @@ class ZWaveJSNodeConfig extends SubscribeMixin(LitElement) {
|
|||||||
if (ev.target === undefined || this._config![ev.target.key] === undefined) {
|
if (ev.target === undefined || this._config![ev.target.key] === undefined) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (this._config![ev.target.key].value === ev.target.selected) {
|
if (this._config![ev.target.key].value === ev.target.value) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.setResult(ev.target.key, undefined);
|
this.setResult(ev.target.key, undefined);
|
||||||
|
|
||||||
this._updateConfigParameter(ev.target, Number(ev.target.selected));
|
this._updateConfigParameter(ev.target, Number(ev.target.value));
|
||||||
}
|
}
|
||||||
|
|
||||||
private debouncedUpdate = debounce((target, value) => {
|
private debouncedUpdate = debounce((target, value) => {
|
||||||
@@ -462,7 +455,7 @@ class ZWaveJSNodeConfig extends SubscribeMixin(LitElement) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.flex .config-label,
|
.flex .config-label,
|
||||||
.flex paper-dropdown-menu {
|
.flex mwc-select {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -104,7 +104,7 @@ class DialogSystemLogDetail extends LitElement {
|
|||||||
)}
|
)}
|
||||||
</ha-alert>`
|
</ha-alert>`
|
||||||
: ""}
|
: ""}
|
||||||
<div class="contents">
|
<div class="contents" tabindex="-1" dialogInitialFocus>
|
||||||
<p>
|
<p>
|
||||||
Logger: ${item.name}<br />
|
Logger: ${item.name}<br />
|
||||||
Source: ${item.source.join(":")}
|
Source: ${item.source.join(":")}
|
||||||
@@ -227,6 +227,7 @@ class DialogSystemLogDetail extends LitElement {
|
|||||||
}
|
}
|
||||||
.contents {
|
.contents {
|
||||||
padding: 16px;
|
padding: 16px;
|
||||||
|
outline: none;
|
||||||
}
|
}
|
||||||
.error {
|
.error {
|
||||||
color: var(--error-color);
|
color: var(--error-color);
|
||||||
|
@@ -105,10 +105,10 @@ export class HaConfigLogs extends LitElement {
|
|||||||
}
|
}
|
||||||
search-input {
|
search-input {
|
||||||
display: block;
|
display: block;
|
||||||
|
--mdc-text-field-fill-color: var(--sidebar-background-color);
|
||||||
|
--mdc-text-field-idle-line-color: var(--divider-color);
|
||||||
}
|
}
|
||||||
search-input.header {
|
search-input.header {
|
||||||
--mdc-text-field-fill-color: transparant;
|
|
||||||
--mdc-text-field-idle-line-color: var(--divider-color);
|
|
||||||
--mdc-ripple-color: transparant;
|
--mdc-ripple-color: transparant;
|
||||||
}
|
}
|
||||||
.content {
|
.content {
|
||||||
|
@@ -1,11 +1,8 @@
|
|||||||
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 "@polymer/paper-item/paper-item";
|
|
||||||
import "@polymer/paper-listbox/paper-listbox";
|
|
||||||
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
||||||
import { customElement, property, state } from "lit/decorators";
|
import { customElement, property, state } from "lit/decorators";
|
||||||
import { createCloseHeading } from "../../../../components/ha-dialog";
|
import { createCloseHeading } from "../../../../components/ha-dialog";
|
||||||
import "../../../../components/ha-paper-dropdown-menu";
|
|
||||||
import {
|
import {
|
||||||
LovelaceResource,
|
LovelaceResource,
|
||||||
LovelaceResourcesMutableParams,
|
LovelaceResourcesMutableParams,
|
||||||
@@ -14,6 +11,9 @@ import { PolymerChangedEvent } from "../../../../polymer-types";
|
|||||||
import { haStyleDialog } from "../../../../resources/styles";
|
import { haStyleDialog } from "../../../../resources/styles";
|
||||||
import { HomeAssistant } from "../../../../types";
|
import { HomeAssistant } from "../../../../types";
|
||||||
import { LovelaceResourceDetailsDialogParams } from "./show-dialog-lovelace-resource-detail";
|
import { LovelaceResourceDetailsDialogParams } from "./show-dialog-lovelace-resource-detail";
|
||||||
|
import "@material/mwc-list/mwc-list-item";
|
||||||
|
import "@material/mwc-select/mwc-select";
|
||||||
|
import { stopPropagation } from "../../../../common/dom/stop_propagation";
|
||||||
|
|
||||||
const detectResourceType = (url: string) => {
|
const detectResourceType = (url: string) => {
|
||||||
const ext = url.split(".").pop() || "";
|
const ext = url.split(".").pop() || "";
|
||||||
@@ -102,48 +102,44 @@ export class DialogLovelaceResourceDetail extends LitElement {
|
|||||||
dialogInitialFocus
|
dialogInitialFocus
|
||||||
></paper-input>
|
></paper-input>
|
||||||
<br />
|
<br />
|
||||||
<ha-paper-dropdown-menu
|
<mwc-select
|
||||||
.label=${this.hass!.localize(
|
.label=${this.hass!.localize(
|
||||||
"ui.panel.config.lovelace.resources.detail.type"
|
"ui.panel.config.lovelace.resources.detail.type"
|
||||||
)}
|
)}
|
||||||
|
.value=${this._type}
|
||||||
|
@selected=${this._typeChanged}
|
||||||
|
@closed=${stopPropagation}
|
||||||
|
.invalid=${!this._type}
|
||||||
>
|
>
|
||||||
<paper-listbox
|
<mwc-list-item value="module">
|
||||||
slot="dropdown-content"
|
${this.hass!.localize(
|
||||||
.selected=${this._type}
|
"ui.panel.config.lovelace.resources.types.module"
|
||||||
@iron-select=${this._typeChanged}
|
)}
|
||||||
attr-for-selected="type"
|
</mwc-list-item>
|
||||||
.invalid=${!this._type}
|
${this._type === "js"
|
||||||
>
|
? html`
|
||||||
<paper-item type="module">
|
<mwc-list-item value="js">
|
||||||
${this.hass!.localize(
|
${this.hass!.localize(
|
||||||
"ui.panel.config.lovelace.resources.types.module"
|
"ui.panel.config.lovelace.resources.types.js"
|
||||||
)}
|
)}
|
||||||
</paper-item>
|
</mwc-list-item>
|
||||||
${this._type === "js"
|
`
|
||||||
? html`
|
: ""}
|
||||||
<paper-item type="js">
|
<mwc-list-item value="css">
|
||||||
${this.hass!.localize(
|
${this.hass!.localize(
|
||||||
"ui.panel.config.lovelace.resources.types.js"
|
"ui.panel.config.lovelace.resources.types.css"
|
||||||
)}
|
)}
|
||||||
</paper-item>
|
</mwc-list-item>
|
||||||
`
|
${this._type === "html"
|
||||||
: ""}
|
? html`
|
||||||
<paper-item type="css">
|
<mwc-list-item value="html">
|
||||||
${this.hass!.localize(
|
${this.hass!.localize(
|
||||||
"ui.panel.config.lovelace.resources.types.css"
|
"ui.panel.config.lovelace.resources.types.html"
|
||||||
)}
|
)}
|
||||||
</paper-item>
|
</mwc-list-item>
|
||||||
${this._type === "html"
|
`
|
||||||
? html`
|
: ""}
|
||||||
<paper-item type="html">
|
</mwc-select>
|
||||||
${this.hass!.localize(
|
|
||||||
"ui.panel.config.lovelace.resources.types.html"
|
|
||||||
)}
|
|
||||||
</paper-item>
|
|
||||||
`
|
|
||||||
: ""}
|
|
||||||
</paper-listbox>
|
|
||||||
</ha-paper-dropdown-menu>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
${this._params.resource
|
${this._params.resource
|
||||||
@@ -185,8 +181,8 @@ export class DialogLovelaceResourceDetail extends LitElement {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private _typeChanged(ev: CustomEvent) {
|
private _typeChanged(ev) {
|
||||||
this._type = ev.detail.item.getAttribute("type");
|
this._type = ev.target.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async _updateResource() {
|
private async _updateResource() {
|
||||||
|
@@ -1,6 +1,4 @@
|
|||||||
import { mdiPlus } from "@mdi/js";
|
import { mdiPlus } from "@mdi/js";
|
||||||
import "@polymer/paper-dropdown-menu/paper-dropdown-menu";
|
|
||||||
import "@polymer/paper-item/paper-icon-item";
|
|
||||||
import { html, LitElement, PropertyValues, TemplateResult } from "lit";
|
import { html, LitElement, PropertyValues, TemplateResult } from "lit";
|
||||||
import { customElement, property, state } from "lit/decorators";
|
import { customElement, property, state } from "lit/decorators";
|
||||||
import memoize from "memoize-one";
|
import memoize from "memoize-one";
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
import { ActionDetail } from "@material/mwc-list/mwc-list-foundation";
|
import type { ActionDetail } from "@material/mwc-list/mwc-list-foundation";
|
||||||
import "@material/mwc-list/mwc-list-item";
|
import "@material/mwc-list/mwc-list-item";
|
||||||
import {
|
import {
|
||||||
mdiCheck,
|
mdiCheck,
|
||||||
@@ -9,8 +9,6 @@ import {
|
|||||||
} from "@mdi/js";
|
} from "@mdi/js";
|
||||||
import "@polymer/app-layout/app-header/app-header";
|
import "@polymer/app-layout/app-header/app-header";
|
||||||
import "@polymer/app-layout/app-toolbar/app-toolbar";
|
import "@polymer/app-layout/app-toolbar/app-toolbar";
|
||||||
import "@polymer/paper-dropdown-menu/paper-dropdown-menu-light";
|
|
||||||
import { PaperListboxElement } from "@polymer/paper-listbox";
|
|
||||||
import {
|
import {
|
||||||
css,
|
css,
|
||||||
CSSResultGroup,
|
CSSResultGroup,
|
||||||
@@ -21,6 +19,7 @@ import {
|
|||||||
} from "lit";
|
} from "lit";
|
||||||
import { property, state, query } from "lit/decorators";
|
import { property, state, query } from "lit/decorators";
|
||||||
import { classMap } from "lit/directives/class-map";
|
import { classMap } from "lit/directives/class-map";
|
||||||
|
import memoizeOne from "memoize-one";
|
||||||
import { computeObjectId } from "../../../common/entity/compute_object_id";
|
import { computeObjectId } from "../../../common/entity/compute_object_id";
|
||||||
import { navigate } from "../../../common/navigate";
|
import { navigate } from "../../../common/navigate";
|
||||||
import { slugify } from "../../../common/string/slugify";
|
import { slugify } from "../../../common/string/slugify";
|
||||||
@@ -29,8 +28,12 @@ import { copyToClipboard } from "../../../common/util/copy-clipboard";
|
|||||||
import "../../../components/ha-button-menu";
|
import "../../../components/ha-button-menu";
|
||||||
import "../../../components/ha-card";
|
import "../../../components/ha-card";
|
||||||
import "../../../components/ha-fab";
|
import "../../../components/ha-fab";
|
||||||
|
import type {
|
||||||
|
HaFormDataContainer,
|
||||||
|
HaFormSchema,
|
||||||
|
HaFormSelector,
|
||||||
|
} from "../../../components/ha-form/types";
|
||||||
import "../../../components/ha-icon-button";
|
import "../../../components/ha-icon-button";
|
||||||
import "../../../components/ha-icon-picker";
|
|
||||||
import "../../../components/ha-svg-icon";
|
import "../../../components/ha-svg-icon";
|
||||||
import "../../../components/ha-yaml-editor";
|
import "../../../components/ha-yaml-editor";
|
||||||
import type { HaYamlEditor } from "../../../components/ha-yaml-editor";
|
import type { HaYamlEditor } from "../../../components/ha-yaml-editor";
|
||||||
@@ -49,10 +52,9 @@ import { showConfirmationDialog } from "../../../dialogs/generic/show-dialog-box
|
|||||||
import "../../../layouts/ha-app-layout";
|
import "../../../layouts/ha-app-layout";
|
||||||
import { KeyboardShortcutMixin } from "../../../mixins/keyboard-shortcut-mixin";
|
import { KeyboardShortcutMixin } from "../../../mixins/keyboard-shortcut-mixin";
|
||||||
import { haStyle } from "../../../resources/styles";
|
import { haStyle } from "../../../resources/styles";
|
||||||
import { HomeAssistant, Route } from "../../../types";
|
import type { HomeAssistant, Route } from "../../../types";
|
||||||
import { documentationUrl } from "../../../util/documentation-url";
|
import { documentationUrl } from "../../../util/documentation-url";
|
||||||
import { showToast } from "../../../util/toast";
|
import { showToast } from "../../../util/toast";
|
||||||
import "../automation/action/ha-automation-action";
|
|
||||||
import { HaDeviceAction } from "../automation/action/types/ha-automation-action-device_id";
|
import { HaDeviceAction } from "../automation/action/types/ha-automation-action-device_id";
|
||||||
import "../ha-config-section";
|
import "../ha-config-section";
|
||||||
import { configSections } from "../ha-panel-config";
|
import { configSections } from "../ha-panel-config";
|
||||||
@@ -83,7 +85,91 @@ export class HaScriptEditor extends KeyboardShortcutMixin(LitElement) {
|
|||||||
|
|
||||||
@query("ha-yaml-editor", true) private _editor?: HaYamlEditor;
|
@query("ha-yaml-editor", true) private _editor?: HaYamlEditor;
|
||||||
|
|
||||||
|
private _schema = memoizeOne(
|
||||||
|
(hasID: boolean, useBluePrint?: boolean, currentMode?: string) => {
|
||||||
|
const schema: HaFormSchema[] = [
|
||||||
|
{
|
||||||
|
name: "alias",
|
||||||
|
selector: {
|
||||||
|
text: {
|
||||||
|
type: "text",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "icon",
|
||||||
|
selector: {
|
||||||
|
icon: {},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
if (!hasID) {
|
||||||
|
schema.push({
|
||||||
|
name: "id",
|
||||||
|
selector: {
|
||||||
|
text: {},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!useBluePrint) {
|
||||||
|
schema.push({
|
||||||
|
name: "mode",
|
||||||
|
selector: {
|
||||||
|
select: {
|
||||||
|
options: MODES.map((mode) => ({
|
||||||
|
label: `
|
||||||
|
${
|
||||||
|
this.hass.localize(
|
||||||
|
`ui.panel.config.script.editor.modes.${mode}`
|
||||||
|
) || mode
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
value: mode,
|
||||||
|
})),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (currentMode && MODES_MAX.includes(currentMode)) {
|
||||||
|
schema.push({
|
||||||
|
name: "max",
|
||||||
|
selector: {
|
||||||
|
text: {
|
||||||
|
type: "number",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return schema;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
protected render(): TemplateResult {
|
protected render(): TemplateResult {
|
||||||
|
if (!this._config) {
|
||||||
|
return html``;
|
||||||
|
}
|
||||||
|
|
||||||
|
const schema = this._schema(
|
||||||
|
!!this.scriptEntityId,
|
||||||
|
"use_blueprint" in this._config,
|
||||||
|
this._config.mode
|
||||||
|
);
|
||||||
|
|
||||||
|
const data = {
|
||||||
|
mode: MODES[0],
|
||||||
|
max:
|
||||||
|
this._config.mode && MODES_MAX.includes(this._config.mode)
|
||||||
|
? 10
|
||||||
|
: undefined,
|
||||||
|
icon: undefined,
|
||||||
|
...this._config,
|
||||||
|
id: this._entityId,
|
||||||
|
};
|
||||||
|
|
||||||
return html`
|
return html`
|
||||||
<hass-tabs-subpage
|
<hass-tabs-subpage
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
@@ -113,11 +199,13 @@ export class HaScriptEditor extends KeyboardShortcutMixin(LitElement) {
|
|||||||
>
|
>
|
||||||
${this.hass.localize("ui.panel.config.automation.editor.edit_ui")}
|
${this.hass.localize("ui.panel.config.automation.editor.edit_ui")}
|
||||||
${this._mode === "gui"
|
${this._mode === "gui"
|
||||||
? html` <ha-svg-icon
|
? html`
|
||||||
class="selected_menu_item"
|
<ha-svg-icon
|
||||||
slot="graphic"
|
class="selected_menu_item"
|
||||||
.path=${mdiCheck}
|
slot="graphic"
|
||||||
></ha-svg-icon>`
|
.path=${mdiCheck}
|
||||||
|
></ha-svg-icon>
|
||||||
|
`
|
||||||
: ``}
|
: ``}
|
||||||
</mwc-list-item>
|
</mwc-list-item>
|
||||||
<mwc-list-item
|
<mwc-list-item
|
||||||
@@ -129,11 +217,13 @@ export class HaScriptEditor extends KeyboardShortcutMixin(LitElement) {
|
|||||||
>
|
>
|
||||||
${this.hass.localize("ui.panel.config.automation.editor.edit_yaml")}
|
${this.hass.localize("ui.panel.config.automation.editor.edit_yaml")}
|
||||||
${this._mode === "yaml"
|
${this._mode === "yaml"
|
||||||
? html` <ha-svg-icon
|
? html`
|
||||||
class="selected_menu_item"
|
<ha-svg-icon
|
||||||
slot="graphic"
|
class="selected_menu_item"
|
||||||
.path=${mdiCheck}
|
slot="graphic"
|
||||||
></ha-svg-icon>`
|
.path=${mdiCheck}
|
||||||
|
></ha-svg-icon>
|
||||||
|
`
|
||||||
: ``}
|
: ``}
|
||||||
</mwc-list-item>
|
</mwc-list-item>
|
||||||
|
|
||||||
@@ -173,16 +263,14 @@ export class HaScriptEditor extends KeyboardShortcutMixin(LitElement) {
|
|||||||
</mwc-list-item>
|
</mwc-list-item>
|
||||||
</ha-button-menu>
|
</ha-button-menu>
|
||||||
${this.narrow
|
${this.narrow
|
||||||
? html` <span slot="header">${this._config?.alias}</span> `
|
? html`<span slot="header">${this._config?.alias}</span>`
|
||||||
: ""}
|
: ""}
|
||||||
<div
|
<div
|
||||||
class="content ${classMap({
|
class="content ${classMap({
|
||||||
"yaml-mode": this._mode === "yaml",
|
"yaml-mode": this._mode === "yaml",
|
||||||
})}"
|
})}"
|
||||||
>
|
>
|
||||||
${this._errors
|
${this._errors ? html`<div class="errors">${this._errors}</div>` : ""}
|
||||||
? html` <div class="errors">${this._errors}</div> `
|
|
||||||
: ""}
|
|
||||||
${this._mode === "gui"
|
${this._mode === "gui"
|
||||||
? html`
|
? html`
|
||||||
<div
|
<div
|
||||||
@@ -205,95 +293,14 @@ export class HaScriptEditor extends KeyboardShortcutMixin(LitElement) {
|
|||||||
</span>
|
</span>
|
||||||
<ha-card>
|
<ha-card>
|
||||||
<div class="card-content">
|
<div class="card-content">
|
||||||
<paper-input
|
<ha-form
|
||||||
.label=${this.hass.localize(
|
.schema=${schema}
|
||||||
"ui.panel.config.script.editor.alias"
|
.data=${data}
|
||||||
)}
|
.hass=${this.hass}
|
||||||
name="alias"
|
.computeLabel=${this._computeLabelCallback}
|
||||||
.value=${this._config.alias}
|
.computeHelper=${this._computeHelperCallback}
|
||||||
@value-changed=${this._valueChanged}
|
@value-changed=${this._valueChanged}
|
||||||
@change=${this._aliasChanged}
|
></ha-form>
|
||||||
>
|
|
||||||
</paper-input>
|
|
||||||
<ha-icon-picker
|
|
||||||
.label=${this.hass.localize(
|
|
||||||
"ui.panel.config.script.editor.icon"
|
|
||||||
)}
|
|
||||||
.name=${"icon"}
|
|
||||||
.value=${this._config.icon}
|
|
||||||
@value-changed=${this._valueChanged}
|
|
||||||
>
|
|
||||||
</ha-icon-picker>
|
|
||||||
${!this.scriptEntityId
|
|
||||||
? html`<paper-input
|
|
||||||
.label=${this.hass.localize(
|
|
||||||
"ui.panel.config.script.editor.id"
|
|
||||||
)}
|
|
||||||
.errorMessage=${this.hass.localize(
|
|
||||||
"ui.panel.config.script.editor.id_already_exists"
|
|
||||||
)}
|
|
||||||
.invalid=${this._idError}
|
|
||||||
.value=${this._entityId}
|
|
||||||
@value-changed=${this._idChanged}
|
|
||||||
>
|
|
||||||
</paper-input>`
|
|
||||||
: ""}
|
|
||||||
${"use_blueprint" in this._config
|
|
||||||
? ""
|
|
||||||
: html`<p>
|
|
||||||
${this.hass.localize(
|
|
||||||
"ui.panel.config.script.editor.modes.description",
|
|
||||||
"documentation_link",
|
|
||||||
html`<a
|
|
||||||
href=${documentationUrl(
|
|
||||||
this.hass,
|
|
||||||
"/integrations/script/#script-modes"
|
|
||||||
)}
|
|
||||||
target="_blank"
|
|
||||||
rel="noreferrer"
|
|
||||||
>${this.hass.localize(
|
|
||||||
"ui.panel.config.script.editor.modes.documentation"
|
|
||||||
)}</a
|
|
||||||
>`
|
|
||||||
)}
|
|
||||||
</p>
|
|
||||||
<paper-dropdown-menu-light
|
|
||||||
.label=${this.hass.localize(
|
|
||||||
"ui.panel.config.script.editor.modes.label"
|
|
||||||
)}
|
|
||||||
no-animations
|
|
||||||
>
|
|
||||||
<paper-listbox
|
|
||||||
slot="dropdown-content"
|
|
||||||
.selected=${this._config.mode
|
|
||||||
? MODES.indexOf(this._config.mode)
|
|
||||||
: 0}
|
|
||||||
@iron-select=${this._modeChanged}
|
|
||||||
>
|
|
||||||
${MODES.map(
|
|
||||||
(mode) => html`
|
|
||||||
<paper-item .mode=${mode}>
|
|
||||||
${this.hass.localize(
|
|
||||||
`ui.panel.config.script.editor.modes.${mode}`
|
|
||||||
) || mode}
|
|
||||||
</paper-item>
|
|
||||||
`
|
|
||||||
)}
|
|
||||||
</paper-listbox>
|
|
||||||
</paper-dropdown-menu-light>
|
|
||||||
${this._config.mode &&
|
|
||||||
MODES_MAX.includes(this._config.mode)
|
|
||||||
? html`<paper-input
|
|
||||||
.label=${this.hass.localize(
|
|
||||||
`ui.panel.config.script.editor.max.${this._config.mode}`
|
|
||||||
)}
|
|
||||||
type="number"
|
|
||||||
name="max"
|
|
||||||
.value=${this._config.max || "10"}
|
|
||||||
@value-changed=${this._valueChanged}
|
|
||||||
>
|
|
||||||
</paper-input>`
|
|
||||||
: html``} `}
|
|
||||||
</div>
|
</div>
|
||||||
${this.scriptEntityId
|
${this.scriptEntityId
|
||||||
? html`
|
? html`
|
||||||
@@ -328,47 +335,51 @@ export class HaScriptEditor extends KeyboardShortcutMixin(LitElement) {
|
|||||||
</ha-config-section>
|
</ha-config-section>
|
||||||
|
|
||||||
${"use_blueprint" in this._config
|
${"use_blueprint" in this._config
|
||||||
? html`<blueprint-script-editor
|
? html`
|
||||||
.hass=${this.hass}
|
<blueprint-script-editor
|
||||||
.narrow=${this.narrow}
|
|
||||||
.isWide=${this.isWide}
|
|
||||||
.config=${this._config}
|
|
||||||
@value-changed=${this._configChanged}
|
|
||||||
></blueprint-script-editor>`
|
|
||||||
: html`<ha-config-section
|
|
||||||
vertical
|
|
||||||
.isWide=${this.isWide}
|
|
||||||
>
|
|
||||||
<span slot="header">
|
|
||||||
${this.hass.localize(
|
|
||||||
"ui.panel.config.script.editor.sequence"
|
|
||||||
)}
|
|
||||||
</span>
|
|
||||||
<span slot="introduction">
|
|
||||||
<p>
|
|
||||||
${this.hass.localize(
|
|
||||||
"ui.panel.config.script.editor.sequence_sentence"
|
|
||||||
)}
|
|
||||||
</p>
|
|
||||||
<a
|
|
||||||
href=${documentationUrl(
|
|
||||||
this.hass,
|
|
||||||
"/docs/scripts/"
|
|
||||||
)}
|
|
||||||
target="_blank"
|
|
||||||
rel="noreferrer"
|
|
||||||
>
|
|
||||||
${this.hass.localize(
|
|
||||||
"ui.panel.config.script.editor.link_available_actions"
|
|
||||||
)}
|
|
||||||
</a>
|
|
||||||
</span>
|
|
||||||
<ha-automation-action
|
|
||||||
.actions=${this._config.sequence}
|
|
||||||
@value-changed=${this._sequenceChanged}
|
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
></ha-automation-action>
|
.narrow=${this.narrow}
|
||||||
</ha-config-section>`}
|
.isWide=${this.isWide}
|
||||||
|
.config=${this._config}
|
||||||
|
@value-changed=${this._configChanged}
|
||||||
|
></blueprint-script-editor>
|
||||||
|
`
|
||||||
|
: html`
|
||||||
|
<ha-config-section
|
||||||
|
vertical
|
||||||
|
.isWide=${this.isWide}
|
||||||
|
>
|
||||||
|
<span slot="header">
|
||||||
|
${this.hass.localize(
|
||||||
|
"ui.panel.config.script.editor.sequence"
|
||||||
|
)}
|
||||||
|
</span>
|
||||||
|
<span slot="introduction">
|
||||||
|
<p>
|
||||||
|
${this.hass.localize(
|
||||||
|
"ui.panel.config.script.editor.sequence_sentence"
|
||||||
|
)}
|
||||||
|
</p>
|
||||||
|
<a
|
||||||
|
href=${documentationUrl(
|
||||||
|
this.hass,
|
||||||
|
"/docs/scripts/"
|
||||||
|
)}
|
||||||
|
target="_blank"
|
||||||
|
rel="noreferrer"
|
||||||
|
>
|
||||||
|
${this.hass.localize(
|
||||||
|
"ui.panel.config.script.editor.link_available_actions"
|
||||||
|
)}
|
||||||
|
</a>
|
||||||
|
</span>
|
||||||
|
<ha-automation-action
|
||||||
|
.actions=${this._config.sequence}
|
||||||
|
@value-changed=${this._sequenceChanged}
|
||||||
|
.hass=${this.hass}
|
||||||
|
></ha-automation-action>
|
||||||
|
</ha-config-section>
|
||||||
|
`}
|
||||||
`
|
`
|
||||||
: ""}
|
: ""}
|
||||||
</div>
|
</div>
|
||||||
@@ -495,7 +506,50 @@ export class HaScriptEditor extends KeyboardShortcutMixin(LitElement) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async _runScript(ev) {
|
private _computeLabelCallback = (
|
||||||
|
schema: HaFormSelector,
|
||||||
|
data: HaFormDataContainer
|
||||||
|
): string => {
|
||||||
|
switch (schema.name) {
|
||||||
|
case "mode":
|
||||||
|
return this.hass.localize("ui.panel.config.script.editor.modes.label");
|
||||||
|
case "max":
|
||||||
|
return this.hass.localize(
|
||||||
|
`ui.panel.config.script.editor.max.${data.mode}`
|
||||||
|
);
|
||||||
|
default:
|
||||||
|
return this.hass.localize(
|
||||||
|
`ui.panel.config.script.editor.${schema.name}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
private _computeHelperCallback = (
|
||||||
|
schema: HaFormSelector
|
||||||
|
): string | undefined => {
|
||||||
|
if (schema.name === "mode") {
|
||||||
|
return this.hass.localize(
|
||||||
|
"ui.panel.config.script.editor.modes.description",
|
||||||
|
"documentation_link",
|
||||||
|
html`
|
||||||
|
<a
|
||||||
|
href=${documentationUrl(
|
||||||
|
this.hass,
|
||||||
|
"/integrations/script/#script-modes"
|
||||||
|
)}
|
||||||
|
target="_blank"
|
||||||
|
rel="noreferrer"
|
||||||
|
>${this.hass.localize(
|
||||||
|
"ui.panel.config.script.editor.modes.documentation"
|
||||||
|
)}</a
|
||||||
|
>
|
||||||
|
`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
|
};
|
||||||
|
|
||||||
|
private async _runScript(ev: CustomEvent) {
|
||||||
ev.stopPropagation();
|
ev.stopPropagation();
|
||||||
await triggerScript(this.hass, this.scriptEntityId as string);
|
await triggerScript(this.hass, this.scriptEntityId as string);
|
||||||
showToast(this, {
|
showToast(this, {
|
||||||
@@ -507,14 +561,7 @@ export class HaScriptEditor extends KeyboardShortcutMixin(LitElement) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private _modeChanged(ev: CustomEvent) {
|
private _modeChanged(mode) {
|
||||||
const mode = ((ev.target as PaperListboxElement)?.selectedItem as any)
|
|
||||||
?.mode;
|
|
||||||
|
|
||||||
if (mode === this._config!.mode) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this._config = { ...this._config!, mode };
|
this._config = { ...this._config!, mode };
|
||||||
if (!MODES_MAX.includes(mode)) {
|
if (!MODES_MAX.includes(mode)) {
|
||||||
delete this._config.max;
|
delete this._config.max;
|
||||||
@@ -522,23 +569,23 @@ export class HaScriptEditor extends KeyboardShortcutMixin(LitElement) {
|
|||||||
this._dirty = true;
|
this._dirty = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private _aliasChanged(ev: CustomEvent) {
|
private _aliasChanged(alias: string) {
|
||||||
if (this.scriptEntityId || this._entityId) {
|
if (this.scriptEntityId || this._entityId) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const aliasSlugify = slugify((ev.target as any).value);
|
const aliasSlugify = slugify(alias);
|
||||||
let id = aliasSlugify;
|
let id = aliasSlugify;
|
||||||
let i = 2;
|
let i = 2;
|
||||||
while (this.hass.states[`script.${id}`]) {
|
while (this.hass.states[`script.${id}`]) {
|
||||||
id = `${aliasSlugify}_${i}`;
|
id = `${aliasSlugify}_${i}`;
|
||||||
i++;
|
i++;
|
||||||
}
|
}
|
||||||
|
|
||||||
this._entityId = id;
|
this._entityId = id;
|
||||||
}
|
}
|
||||||
|
|
||||||
private _idChanged(ev: CustomEvent) {
|
private _idChanged(id: string) {
|
||||||
ev.stopPropagation();
|
this._entityId = id;
|
||||||
this._entityId = (ev.target as any).value;
|
|
||||||
if (this.hass.states[`script.${this._entityId}`]) {
|
if (this.hass.states[`script.${this._entityId}`]) {
|
||||||
this._idError = true;
|
this._idError = true;
|
||||||
} else {
|
} else {
|
||||||
@@ -548,24 +595,39 @@ export class HaScriptEditor extends KeyboardShortcutMixin(LitElement) {
|
|||||||
|
|
||||||
private _valueChanged(ev: CustomEvent) {
|
private _valueChanged(ev: CustomEvent) {
|
||||||
ev.stopPropagation();
|
ev.stopPropagation();
|
||||||
const target = ev.target as any;
|
const values = ev.detail.value as any;
|
||||||
const name = target.name;
|
|
||||||
if (!name) {
|
for (const key of Object.keys(values)) {
|
||||||
return;
|
if (key === "sequence") {
|
||||||
}
|
continue;
|
||||||
let newVal = ev.detail.value;
|
}
|
||||||
if (target.type === "number") {
|
|
||||||
newVal = Number(newVal);
|
const value = values[key];
|
||||||
}
|
|
||||||
if ((this._config![name] || "") === newVal) {
|
if (value === this._config![key]) {
|
||||||
return;
|
continue;
|
||||||
}
|
}
|
||||||
if (!newVal) {
|
|
||||||
delete this._config![name];
|
switch (key) {
|
||||||
this._config = { ...this._config! };
|
case "id":
|
||||||
} else {
|
this._idChanged(value);
|
||||||
this._config = { ...this._config!, [name]: newVal };
|
return;
|
||||||
|
case "alias":
|
||||||
|
this._aliasChanged(value);
|
||||||
|
break;
|
||||||
|
case "mode":
|
||||||
|
this._modeChanged(value);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (values[key] === undefined) {
|
||||||
|
delete this._config![key];
|
||||||
|
this._config = { ...this._config! };
|
||||||
|
} else {
|
||||||
|
this._config = { ...this._config!, [key]: value };
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this._dirty = true;
|
this._dirty = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -575,7 +637,10 @@ export class HaScriptEditor extends KeyboardShortcutMixin(LitElement) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private _sequenceChanged(ev: CustomEvent): void {
|
private _sequenceChanged(ev: CustomEvent): void {
|
||||||
this._config = { ...this._config!, sequence: ev.detail.value as Action[] };
|
this._config = {
|
||||||
|
...this._config!,
|
||||||
|
sequence: ev.detail.value as Action[],
|
||||||
|
};
|
||||||
this._errors = undefined;
|
this._errors = undefined;
|
||||||
this._dirty = true;
|
this._dirty = true;
|
||||||
}
|
}
|
||||||
|
@@ -111,6 +111,7 @@ export class DialogAddUser extends LitElement {
|
|||||||
.errorMessage=${this.hass.localize("ui.common.error_required")}
|
.errorMessage=${this.hass.localize("ui.common.error_required")}
|
||||||
@value-changed=${this._handleValueChanged}
|
@value-changed=${this._handleValueChanged}
|
||||||
@blur=${this._maybePopulateUsername}
|
@blur=${this._maybePopulateUsername}
|
||||||
|
dialogInitialFocus
|
||||||
></paper-input>`
|
></paper-input>`
|
||||||
: ""}
|
: ""}
|
||||||
<paper-input
|
<paper-input
|
||||||
@@ -125,6 +126,7 @@ export class DialogAddUser extends LitElement {
|
|||||||
autocapitalize="none"
|
autocapitalize="none"
|
||||||
@value-changed=${this._handleValueChanged}
|
@value-changed=${this._handleValueChanged}
|
||||||
.errorMessage=${this.hass.localize("ui.common.error_required")}
|
.errorMessage=${this.hass.localize("ui.common.error_required")}
|
||||||
|
dialogInitialFocus
|
||||||
></paper-input>
|
></paper-input>
|
||||||
|
|
||||||
<paper-input
|
<paper-input
|
||||||
|
@@ -65,6 +65,7 @@ export class DialogStatisticsFixUnitsChanged extends LitElement {
|
|||||||
name="action"
|
name="action"
|
||||||
.checked=${this._action === "update"}
|
.checked=${this._action === "update"}
|
||||||
@change=${this._handleActionChanged}
|
@change=${this._handleActionChanged}
|
||||||
|
dialogInitialFocus
|
||||||
></ha-radio>
|
></ha-radio>
|
||||||
</ha-formfield>
|
</ha-formfield>
|
||||||
<ha-formfield
|
<ha-formfield
|
||||||
|
@@ -47,7 +47,11 @@ export class DialogStatisticsFixUnsupportedUnitMetadata extends LitElement {
|
|||||||
${this._params.issue.data.supported_unit}?
|
${this._params.issue.data.supported_unit}?
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<mwc-button slot="primaryAction" @click=${this._fixIssue}>
|
<mwc-button
|
||||||
|
slot="primaryAction"
|
||||||
|
@click=${this._fixIssue}
|
||||||
|
dialogInitialFocus
|
||||||
|
>
|
||||||
Fix
|
Fix
|
||||||
</mwc-button>
|
</mwc-button>
|
||||||
<mwc-button slot="secondaryAction" @click=${this.closeDialog}>
|
<mwc-button slot="secondaryAction" @click=${this.closeDialog}>
|
||||||
|
@@ -1,11 +1,11 @@
|
|||||||
|
import "@material/mwc-list/mwc-list-item";
|
||||||
|
import "@material/mwc-select/mwc-select";
|
||||||
import { mdiClose } from "@mdi/js";
|
import { mdiClose } from "@mdi/js";
|
||||||
import "@polymer/paper-dropdown-menu/paper-dropdown-menu";
|
|
||||||
import "@polymer/paper-item/paper-item";
|
|
||||||
import "@polymer/paper-listbox/paper-listbox";
|
|
||||||
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
||||||
import { customElement, property, state } from "lit/decorators";
|
import { customElement, property, state } from "lit/decorators";
|
||||||
import { array, assert, assign, object, optional, string } from "superstruct";
|
import { array, assert, assign, object, optional, string } from "superstruct";
|
||||||
import { fireEvent } from "../../../../common/dom/fire_event";
|
import { fireEvent } from "../../../../common/dom/fire_event";
|
||||||
|
import { stopPropagation } from "../../../../common/dom/stop_propagation";
|
||||||
import "../../../../components/entity/ha-entity-picker";
|
import "../../../../components/entity/ha-entity-picker";
|
||||||
import "../../../../components/ha-svg-icon";
|
import "../../../../components/ha-svg-icon";
|
||||||
import { HomeAssistant } from "../../../../types";
|
import { HomeAssistant } from "../../../../types";
|
||||||
@@ -109,18 +109,20 @@ export class HuiAlarmPanelCardEditor
|
|||||||
</div>
|
</div>
|
||||||
`
|
`
|
||||||
)}
|
)}
|
||||||
<paper-dropdown-menu
|
<mwc-select
|
||||||
.label=${this.hass.localize(
|
.label=${this.hass.localize(
|
||||||
"ui.panel.lovelace.editor.card.alarm-panel.available_states"
|
"ui.panel.lovelace.editor.card.alarm-panel.available_states"
|
||||||
)}
|
)}
|
||||||
@value-changed=${this._stateAdded}
|
@selected=${this._stateAdded}
|
||||||
|
@closed=${stopPropagation}
|
||||||
|
fixedMenuPosition
|
||||||
|
naturalMenuWidth
|
||||||
>
|
>
|
||||||
<paper-listbox slot="dropdown-content">
|
${states.map(
|
||||||
${states.map(
|
(entityState) =>
|
||||||
(entityState) => html` <paper-item>${entityState}</paper-item> `
|
html`<mwc-list-item>${entityState}</mwc-list-item> `
|
||||||
)}
|
)}
|
||||||
</paper-listbox>
|
</mwc-select>
|
||||||
</paper-dropdown-menu>
|
|
||||||
<hui-theme-select-editor
|
<hui-theme-select-editor
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
.value=${this._theme}
|
.value=${this._theme}
|
||||||
|
@@ -1,3 +1,5 @@
|
|||||||
|
import "@material/mwc-list/mwc-list-item";
|
||||||
|
import "@material/mwc-select/mwc-select";
|
||||||
import { CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
import { CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
||||||
import { customElement, property, state } from "lit/decorators";
|
import { customElement, property, state } from "lit/decorators";
|
||||||
import {
|
import {
|
||||||
@@ -11,15 +13,16 @@ import {
|
|||||||
union,
|
union,
|
||||||
} from "superstruct";
|
} from "superstruct";
|
||||||
import { fireEvent } from "../../../../common/dom/fire_event";
|
import { fireEvent } from "../../../../common/dom/fire_event";
|
||||||
|
import { stopPropagation } from "../../../../common/dom/stop_propagation";
|
||||||
import "../../../../components/entity/ha-entities-picker";
|
import "../../../../components/entity/ha-entities-picker";
|
||||||
import type { HomeAssistant } from "../../../../types";
|
import type { HomeAssistant } from "../../../../types";
|
||||||
import type { CalendarCardConfig } from "../../cards/types";
|
import type { CalendarCardConfig } from "../../cards/types";
|
||||||
import "../../components/hui-entity-editor";
|
import "../../components/hui-entity-editor";
|
||||||
import "../../components/hui-theme-select-editor";
|
import "../../components/hui-theme-select-editor";
|
||||||
import type { LovelaceCardEditor } from "../../types";
|
import type { LovelaceCardEditor } from "../../types";
|
||||||
|
import { baseLovelaceCardConfig } from "../structs/base-card-struct";
|
||||||
import type { EditorTarget, EntitiesEditorEvent } from "../types";
|
import type { EditorTarget, EntitiesEditorEvent } from "../types";
|
||||||
import { configElementStyle } from "./config-elements-style";
|
import { configElementStyle } from "./config-elements-style";
|
||||||
import { baseLovelaceCardConfig } from "../structs/base-card-struct";
|
|
||||||
|
|
||||||
const cardConfigStruct = assign(
|
const cardConfigStruct = assign(
|
||||||
baseLovelaceCardConfig,
|
baseLovelaceCardConfig,
|
||||||
@@ -80,29 +83,25 @@ export class HuiCalendarCardEditor
|
|||||||
.configValue=${"title"}
|
.configValue=${"title"}
|
||||||
@value-changed=${this._valueChanged}
|
@value-changed=${this._valueChanged}
|
||||||
></paper-input>
|
></paper-input>
|
||||||
<paper-dropdown-menu
|
<mwc-select
|
||||||
.label=${this.hass.localize(
|
.label=${this.hass.localize(
|
||||||
"ui.panel.lovelace.editor.card.calendar.inital_view"
|
"ui.panel.lovelace.editor.card.calendar.inital_view"
|
||||||
)}
|
)}
|
||||||
|
.value=${this._initial_view}
|
||||||
|
.configValue=${"initial_view"}
|
||||||
|
@selected=${this._viewChanged}
|
||||||
|
@closed=${stopPropagation}
|
||||||
>
|
>
|
||||||
<paper-listbox
|
${views.map(
|
||||||
slot="dropdown-content"
|
(view) => html`
|
||||||
attr-for-selected="view"
|
<mwc-list-item .value=${view}
|
||||||
.selected=${this._initial_view}
|
>${this.hass!.localize(
|
||||||
.configValue=${"initial_view"}
|
`ui.panel.lovelace.editor.card.calendar.views.${view}`
|
||||||
@iron-select=${this._viewChanged}
|
)}
|
||||||
>
|
</mwc-list-item>
|
||||||
${views.map(
|
`
|
||||||
(view) => html`
|
)}
|
||||||
<paper-item .view=${view}
|
</mwc-select>
|
||||||
>${this.hass!.localize(
|
|
||||||
`ui.panel.lovelace.editor.card.calendar.views.${view}`
|
|
||||||
)}
|
|
||||||
</paper-item>
|
|
||||||
`
|
|
||||||
)}
|
|
||||||
</paper-listbox>
|
|
||||||
</paper-dropdown-menu>
|
|
||||||
</div>
|
</div>
|
||||||
<hui-theme-select-editor
|
<hui-theme-select-editor
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
@@ -157,18 +156,18 @@ export class HuiCalendarCardEditor
|
|||||||
fireEvent(this, "config-changed", { config: this._config });
|
fireEvent(this, "config-changed", { config: this._config });
|
||||||
}
|
}
|
||||||
|
|
||||||
private _viewChanged(ev: CustomEvent): void {
|
private _viewChanged(ev): void {
|
||||||
if (!this._config || !this.hass) {
|
if (!this._config || !this.hass) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ev.detail.item.view === "") {
|
if (ev.target.value === "") {
|
||||||
this._config = { ...this._config };
|
this._config = { ...this._config };
|
||||||
delete this._config.initial_view;
|
delete this._config.initial_view;
|
||||||
} else {
|
} else {
|
||||||
this._config = {
|
this._config = {
|
||||||
...this._config,
|
...this._config,
|
||||||
initial_view: ev.detail.item.view,
|
initial_view: ev.target.value,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
fireEvent(this, "config-changed", { config: this._config });
|
fireEvent(this, "config-changed", { config: this._config });
|
||||||
|
@@ -1,8 +1,10 @@
|
|||||||
|
import "@material/mwc-list/mwc-list-item";
|
||||||
|
import "@material/mwc-select/mwc-select";
|
||||||
import "@material/mwc-tab-bar/mwc-tab-bar";
|
import "@material/mwc-tab-bar/mwc-tab-bar";
|
||||||
import "@material/mwc-tab/mwc-tab";
|
import "@material/mwc-tab/mwc-tab";
|
||||||
import type { MDCTabBarActivatedEvent } from "@material/tab-bar";
|
import type { MDCTabBarActivatedEvent } from "@material/tab-bar";
|
||||||
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
||||||
import { customElement, property, state, query } from "lit/decorators";
|
import { customElement, property, query, state } from "lit/decorators";
|
||||||
import {
|
import {
|
||||||
any,
|
any,
|
||||||
array,
|
array,
|
||||||
@@ -13,6 +15,7 @@ import {
|
|||||||
string,
|
string,
|
||||||
} from "superstruct";
|
} from "superstruct";
|
||||||
import { fireEvent, HASSDomEvent } from "../../../../common/dom/fire_event";
|
import { fireEvent, HASSDomEvent } from "../../../../common/dom/fire_event";
|
||||||
|
import { stopPropagation } from "../../../../common/dom/stop_propagation";
|
||||||
import "../../../../components/entity/ha-entity-picker";
|
import "../../../../components/entity/ha-entity-picker";
|
||||||
import { LovelaceCardConfig, LovelaceConfig } from "../../../../data/lovelace";
|
import { LovelaceCardConfig, LovelaceConfig } from "../../../../data/lovelace";
|
||||||
import { HomeAssistant } from "../../../../types";
|
import { HomeAssistant } from "../../../../types";
|
||||||
@@ -142,33 +145,33 @@ export class HuiConditionalCardEditor
|
|||||||
<ha-entity-picker
|
<ha-entity-picker
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
.value=${cond.entity}
|
.value=${cond.entity}
|
||||||
.index=${idx}
|
.idx=${idx}
|
||||||
.configValue=${"entity"}
|
.configValue=${"entity"}
|
||||||
@change=${this._changeCondition}
|
@change=${this._changeCondition}
|
||||||
allow-custom-entity
|
allow-custom-entity
|
||||||
></ha-entity-picker>
|
></ha-entity-picker>
|
||||||
</div>
|
</div>
|
||||||
<div class="state">
|
<div class="state">
|
||||||
<paper-dropdown-menu>
|
<mwc-select
|
||||||
<paper-listbox
|
.value=${cond.state_not !== undefined
|
||||||
.selected=${cond.state_not !== undefined ? 1 : 0}
|
? "true"
|
||||||
slot="dropdown-content"
|
: "false"}
|
||||||
.index=${idx}
|
.idx=${idx}
|
||||||
.configValue=${"invert"}
|
.configValue=${"invert"}
|
||||||
@selected-item-changed=${this._changeCondition}
|
@selected=${this._changeCondition}
|
||||||
>
|
@closed=${stopPropagation}
|
||||||
<paper-item
|
>
|
||||||
>${this.hass!.localize(
|
<mwc-list-item value="false">
|
||||||
"ui.panel.lovelace.editor.card.conditional.state_equal"
|
${this.hass!.localize(
|
||||||
)}</paper-item
|
"ui.panel.lovelace.editor.card.conditional.state_equal"
|
||||||
>
|
)}
|
||||||
<paper-item
|
</mwc-list-item>
|
||||||
>${this.hass!.localize(
|
<mwc-list-item value="true">
|
||||||
"ui.panel.lovelace.editor.card.conditional.state_not_equal"
|
${this.hass!.localize(
|
||||||
)}</paper-item
|
"ui.panel.lovelace.editor.card.conditional.state_not_equal"
|
||||||
>
|
)}
|
||||||
</paper-listbox>
|
</mwc-list-item>
|
||||||
</paper-dropdown-menu>
|
</mwc-select>
|
||||||
<paper-input
|
<paper-input
|
||||||
.label="${this.hass!.localize(
|
.label="${this.hass!.localize(
|
||||||
"ui.panel.lovelace.editor.card.generic.state"
|
"ui.panel.lovelace.editor.card.generic.state"
|
||||||
@@ -178,7 +181,7 @@ export class HuiConditionalCardEditor
|
|||||||
.value=${cond.state_not !== undefined
|
.value=${cond.state_not !== undefined
|
||||||
? cond.state_not
|
? cond.state_not
|
||||||
: cond.state}
|
: cond.state}
|
||||||
.index=${idx}
|
.idx=${idx}
|
||||||
.configValue=${"state"}
|
.configValue=${"state"}
|
||||||
@value-changed=${this._changeCondition}
|
@value-changed=${this._changeCondition}
|
||||||
></paper-input>
|
></paper-input>
|
||||||
@@ -274,9 +277,9 @@ export class HuiConditionalCardEditor
|
|||||||
}
|
}
|
||||||
const conditions = [...this._config.conditions];
|
const conditions = [...this._config.conditions];
|
||||||
if (target.configValue === "entity" && target.value === "") {
|
if (target.configValue === "entity" && target.value === "") {
|
||||||
conditions.splice(target.index, 1);
|
conditions.splice(target.idx, 1);
|
||||||
} else {
|
} else {
|
||||||
const condition = { ...conditions[target.index] };
|
const condition = { ...conditions[target.idx] };
|
||||||
if (target.configValue === "entity") {
|
if (target.configValue === "entity") {
|
||||||
condition.entity = target.value;
|
condition.entity = target.value;
|
||||||
} else if (target.configValue === "state") {
|
} else if (target.configValue === "state") {
|
||||||
@@ -286,7 +289,7 @@ export class HuiConditionalCardEditor
|
|||||||
condition.state = target.value;
|
condition.state = target.value;
|
||||||
}
|
}
|
||||||
} else if (target.configValue === "invert") {
|
} else if (target.configValue === "invert") {
|
||||||
if (target.selected === 1) {
|
if (target.value === "true") {
|
||||||
if (condition.state) {
|
if (condition.state) {
|
||||||
condition.state_not = condition.state;
|
condition.state_not = condition.state;
|
||||||
delete condition.state;
|
delete condition.state;
|
||||||
@@ -296,7 +299,7 @@ export class HuiConditionalCardEditor
|
|||||||
delete condition.state_not;
|
delete condition.state_not;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
conditions[target.index] = condition;
|
conditions[target.idx] = condition;
|
||||||
}
|
}
|
||||||
this._config = { ...this._config, conditions };
|
this._config = { ...this._config, conditions };
|
||||||
fireEvent(this, "config-changed", { config: this._config });
|
fireEvent(this, "config-changed", { config: this._config });
|
||||||
@@ -321,7 +324,7 @@ export class HuiConditionalCardEditor
|
|||||||
display: flex;
|
display: flex;
|
||||||
align-items: flex-end;
|
align-items: flex-end;
|
||||||
}
|
}
|
||||||
.condition .state paper-dropdown-menu {
|
.condition .state mwc-select {
|
||||||
margin-right: 16px;
|
margin-right: 16px;
|
||||||
}
|
}
|
||||||
.condition .state paper-input {
|
.condition .state paper-input {
|
||||||
|
@@ -1,6 +1,4 @@
|
|||||||
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 { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
||||||
import { customElement, property, state } from "lit/decorators";
|
import { customElement, property, state } from "lit/decorators";
|
||||||
import {
|
import {
|
||||||
|
@@ -1,3 +1,5 @@
|
|||||||
|
import "@material/mwc-list/mwc-list-item";
|
||||||
|
import "@material/mwc-select/mwc-select";
|
||||||
import "@polymer/paper-input/paper-input";
|
import "@polymer/paper-input/paper-input";
|
||||||
import { CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
import { CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
||||||
import { customElement, property, state } from "lit/decorators";
|
import { customElement, property, state } from "lit/decorators";
|
||||||
@@ -99,37 +101,34 @@ export class HuiGenericEntityRowEditor
|
|||||||
@value-changed=${this._valueChanged}
|
@value-changed=${this._valueChanged}
|
||||||
></ha-icon-picker>
|
></ha-icon-picker>
|
||||||
</div>
|
</div>
|
||||||
<paper-dropdown-menu .label=${"Secondary Info"}>
|
<mwc-select
|
||||||
<paper-listbox
|
label="Secondary Info"
|
||||||
slot="dropdown-content"
|
.selected=${this._config.secondary_info || "none"}
|
||||||
attr-for-selected="value"
|
.configValue=${"secondary_info"}
|
||||||
.selected=${this._config.secondary_info || "none"}
|
@selected=${this._valueChanged}
|
||||||
.configValue=${"secondary_info"}
|
>
|
||||||
@iron-select=${this._valueChanged}
|
<mwc-list-item value=""
|
||||||
|
>${this.hass!.localize(
|
||||||
|
"ui.panel.lovelace.editor.card.entities.secondary_info_values.none"
|
||||||
|
)}</mwc-list-item
|
||||||
>
|
>
|
||||||
<paper-item value=""
|
${Object.keys(SecondaryInfoValues).map((info) => {
|
||||||
>${this.hass!.localize(
|
if (
|
||||||
"ui.panel.lovelace.editor.card.entities.secondary_info_values.none"
|
!("domains" in SecondaryInfoValues[info]) ||
|
||||||
)}</paper-item
|
("domains" in SecondaryInfoValues[info] &&
|
||||||
>
|
SecondaryInfoValues[info].domains!.includes(domain))
|
||||||
${Object.keys(SecondaryInfoValues).map((info) => {
|
) {
|
||||||
if (
|
return html`
|
||||||
!("domains" in SecondaryInfoValues[info]) ||
|
<mwc-list-item .value=${info}>
|
||||||
("domains" in SecondaryInfoValues[info] &&
|
${this.hass!.localize(
|
||||||
SecondaryInfoValues[info].domains!.includes(domain))
|
`ui.panel.lovelace.editor.card.entities.secondary_info_values.${info}`
|
||||||
) {
|
)}
|
||||||
return html`
|
</mwc-list-item>
|
||||||
<paper-item .value=${info}
|
`;
|
||||||
>${this.hass!.localize(
|
}
|
||||||
`ui.panel.lovelace.editor.card.entities.secondary_info_values.${info}`
|
return "";
|
||||||
)}</paper-item
|
})}
|
||||||
>
|
</mwc-select>
|
||||||
`;
|
|
||||||
}
|
|
||||||
return "";
|
|
||||||
})}
|
|
||||||
</paper-listbox>
|
|
||||||
</paper-dropdown-menu>
|
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
@@ -1,11 +1,11 @@
|
|||||||
import "@polymer/paper-dropdown-menu/paper-dropdown-menu";
|
import "@material/mwc-list/mwc-list-item";
|
||||||
|
import "@material/mwc-select/mwc-select";
|
||||||
import "@polymer/paper-input/paper-input";
|
import "@polymer/paper-input/paper-input";
|
||||||
import "@polymer/paper-item/paper-item";
|
|
||||||
import "@polymer/paper-listbox/paper-listbox";
|
|
||||||
import { CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
import { CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
||||||
import { customElement, property, state } from "lit/decorators";
|
import { customElement, property, state } from "lit/decorators";
|
||||||
import { assert, boolean, object, optional, string, assign } from "superstruct";
|
import { assert, assign, boolean, object, optional, string } from "superstruct";
|
||||||
import { fireEvent } from "../../../../common/dom/fire_event";
|
import { fireEvent } from "../../../../common/dom/fire_event";
|
||||||
|
import { stopPropagation } from "../../../../common/dom/stop_propagation";
|
||||||
import { computeRTLDirection } from "../../../../common/util/compute_rtl";
|
import { computeRTLDirection } from "../../../../common/util/compute_rtl";
|
||||||
import "../../../../components/ha-formfield";
|
import "../../../../components/ha-formfield";
|
||||||
import "../../../../components/ha-switch";
|
import "../../../../components/ha-switch";
|
||||||
@@ -17,9 +17,9 @@ import "../../components/hui-entity-editor";
|
|||||||
import "../../components/hui-theme-select-editor";
|
import "../../components/hui-theme-select-editor";
|
||||||
import { LovelaceCardEditor } from "../../types";
|
import { LovelaceCardEditor } from "../../types";
|
||||||
import { actionConfigStruct } from "../structs/action-struct";
|
import { actionConfigStruct } from "../structs/action-struct";
|
||||||
|
import { baseLovelaceCardConfig } from "../structs/base-card-struct";
|
||||||
import { EditorTarget } from "../types";
|
import { EditorTarget } from "../types";
|
||||||
import { configElementStyle } from "./config-elements-style";
|
import { configElementStyle } from "./config-elements-style";
|
||||||
import { baseLovelaceCardConfig } from "../structs/base-card-struct";
|
|
||||||
|
|
||||||
const cardConfigStruct = assign(
|
const cardConfigStruct = assign(
|
||||||
baseLovelaceCardConfig,
|
baseLovelaceCardConfig,
|
||||||
@@ -155,22 +155,24 @@ export class HuiPictureEntityCardEditor
|
|||||||
allow-custom-entity
|
allow-custom-entity
|
||||||
></ha-entity-picker>
|
></ha-entity-picker>
|
||||||
<div class="side-by-side">
|
<div class="side-by-side">
|
||||||
<paper-dropdown-menu
|
<mwc-select
|
||||||
.label="${this.hass.localize(
|
.label="${this.hass.localize(
|
||||||
"ui.panel.lovelace.editor.card.generic.camera_view"
|
"ui.panel.lovelace.editor.card.generic.camera_view"
|
||||||
)} (${this.hass.localize(
|
)} (${this.hass.localize(
|
||||||
"ui.panel.lovelace.editor.card.config.optional"
|
"ui.panel.lovelace.editor.card.config.optional"
|
||||||
)})"
|
)})"
|
||||||
.configValue=${"camera_view"}
|
.configValue=${"camera_view"}
|
||||||
@value-changed=${this._valueChanged}
|
@selected=${this._valueChanged}
|
||||||
|
@closed=${stopPropagation}
|
||||||
|
fixedMenuPosition
|
||||||
|
naturalMenuWidth
|
||||||
|
.value=${views.indexOf(this._camera_view)}
|
||||||
>
|
>
|
||||||
<paper-listbox
|
${views.map(
|
||||||
slot="dropdown-content"
|
(view) =>
|
||||||
.selected=${views.indexOf(this._camera_view)}
|
html`<mwc-list-item .value=${view}>${view}</mwc-list-item> `
|
||||||
>
|
)}
|
||||||
${views.map((view) => html` <paper-item>${view}</paper-item> `)}
|
</mwc-select>
|
||||||
</paper-listbox>
|
|
||||||
</paper-dropdown-menu>
|
|
||||||
<paper-input
|
<paper-input
|
||||||
.label="${this.hass.localize(
|
.label="${this.hass.localize(
|
||||||
"ui.panel.lovelace.editor.card.generic.aspect_ratio"
|
"ui.panel.lovelace.editor.card.generic.aspect_ratio"
|
||||||
|
@@ -1,11 +1,11 @@
|
|||||||
import "@polymer/paper-dropdown-menu/paper-dropdown-menu";
|
import "@material/mwc-list/mwc-list-item";
|
||||||
|
import "@material/mwc-select/mwc-select";
|
||||||
import "@polymer/paper-input/paper-input";
|
import "@polymer/paper-input/paper-input";
|
||||||
import "@polymer/paper-item/paper-item";
|
|
||||||
import "@polymer/paper-listbox/paper-listbox";
|
|
||||||
import { CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
import { CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
||||||
import { customElement, property, state } from "lit/decorators";
|
import { customElement, property, state } from "lit/decorators";
|
||||||
import { array, assert, object, optional, string, assign } from "superstruct";
|
import { array, assert, assign, object, optional, string } from "superstruct";
|
||||||
import { fireEvent } from "../../../../common/dom/fire_event";
|
import { fireEvent } from "../../../../common/dom/fire_event";
|
||||||
|
import { stopPropagation } from "../../../../common/dom/stop_propagation";
|
||||||
import "../../../../components/entity/ha-entity-picker";
|
import "../../../../components/entity/ha-entity-picker";
|
||||||
import { ActionConfig } from "../../../../data/lovelace";
|
import { ActionConfig } from "../../../../data/lovelace";
|
||||||
import { HomeAssistant } from "../../../../types";
|
import { HomeAssistant } from "../../../../types";
|
||||||
@@ -17,10 +17,10 @@ import { EntityConfig } from "../../entity-rows/types";
|
|||||||
import { LovelaceCardEditor } from "../../types";
|
import { LovelaceCardEditor } from "../../types";
|
||||||
import { processEditorEntities } from "../process-editor-entities";
|
import { processEditorEntities } from "../process-editor-entities";
|
||||||
import { actionConfigStruct } from "../structs/action-struct";
|
import { actionConfigStruct } from "../structs/action-struct";
|
||||||
|
import { baseLovelaceCardConfig } from "../structs/base-card-struct";
|
||||||
import { entitiesConfigStruct } from "../structs/entities-struct";
|
import { entitiesConfigStruct } from "../structs/entities-struct";
|
||||||
import { EditorTarget } from "../types";
|
import { EditorTarget } from "../types";
|
||||||
import { configElementStyle } from "./config-elements-style";
|
import { configElementStyle } from "./config-elements-style";
|
||||||
import { baseLovelaceCardConfig } from "../structs/base-card-struct";
|
|
||||||
|
|
||||||
const cardConfigStruct = assign(
|
const cardConfigStruct = assign(
|
||||||
baseLovelaceCardConfig,
|
baseLovelaceCardConfig,
|
||||||
@@ -146,22 +146,24 @@ export class HuiPictureGlanceCardEditor
|
|||||||
.includeDomains=${includeDomains}
|
.includeDomains=${includeDomains}
|
||||||
></ha-entity-picker>
|
></ha-entity-picker>
|
||||||
<div class="side-by-side">
|
<div class="side-by-side">
|
||||||
<paper-dropdown-menu
|
<mwc-select
|
||||||
.label="${this.hass.localize(
|
.label="${this.hass.localize(
|
||||||
"ui.panel.lovelace.editor.card.generic.camera_view"
|
"ui.panel.lovelace.editor.card.generic.camera_view"
|
||||||
)} (${this.hass.localize(
|
)} (${this.hass.localize(
|
||||||
"ui.panel.lovelace.editor.card.config.optional"
|
"ui.panel.lovelace.editor.card.config.optional"
|
||||||
)})"
|
)})"
|
||||||
.configValue=${"camera_view"}
|
.configValue=${"camera_view"}
|
||||||
@value-changed=${this._valueChanged}
|
@selected=${this._valueChanged}
|
||||||
|
@closed=${stopPropagation}
|
||||||
|
fixedMenuPosition
|
||||||
|
naturalMenuWidth
|
||||||
|
.value=${this._camera_view}
|
||||||
>
|
>
|
||||||
<paper-listbox
|
${views.map(
|
||||||
slot="dropdown-content"
|
(view) =>
|
||||||
.selected=${views.indexOf(this._camera_view)}
|
html`<mwc-list-item .value=${view}>${view}</mwc-list-item> `
|
||||||
>
|
)}
|
||||||
${views.map((view) => html` <paper-item>${view}</paper-item> `)}
|
</mwc-select>
|
||||||
</paper-listbox>
|
|
||||||
</paper-dropdown-menu>
|
|
||||||
<paper-input
|
<paper-input
|
||||||
.label="${this.hass.localize(
|
.label="${this.hass.localize(
|
||||||
"ui.panel.lovelace.editor.card.generic.aspect_ratio"
|
"ui.panel.lovelace.editor.card.generic.aspect_ratio"
|
||||||
|
@@ -1,7 +1,6 @@
|
|||||||
import "@polymer/paper-dropdown-menu/paper-dropdown-menu";
|
import "@material/mwc-list/mwc-list-item";
|
||||||
|
import "@material/mwc-select/mwc-select";
|
||||||
import "@polymer/paper-input/paper-input";
|
import "@polymer/paper-input/paper-input";
|
||||||
import "@polymer/paper-item/paper-item";
|
|
||||||
import "@polymer/paper-listbox/paper-listbox";
|
|
||||||
import { CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
import { CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
||||||
import { customElement, property, state } from "lit/decorators";
|
import { customElement, property, state } from "lit/decorators";
|
||||||
import {
|
import {
|
||||||
@@ -15,6 +14,7 @@ import {
|
|||||||
union,
|
union,
|
||||||
} from "superstruct";
|
} from "superstruct";
|
||||||
import { fireEvent } from "../../../../common/dom/fire_event";
|
import { fireEvent } from "../../../../common/dom/fire_event";
|
||||||
|
import { stopPropagation } from "../../../../common/dom/stop_propagation";
|
||||||
import { computeDomain } from "../../../../common/entity/compute_domain";
|
import { computeDomain } from "../../../../common/entity/compute_domain";
|
||||||
import { domainIcon } from "../../../../common/entity/domain_icon";
|
import { domainIcon } from "../../../../common/entity/domain_icon";
|
||||||
import "../../../../components/entity/ha-entity-picker";
|
import "../../../../components/entity/ha-entity-picker";
|
||||||
@@ -142,22 +142,24 @@ export class HuiSensorCardEditor
|
|||||||
.configValue=${"icon"}
|
.configValue=${"icon"}
|
||||||
@value-changed=${this._valueChanged}
|
@value-changed=${this._valueChanged}
|
||||||
></ha-icon-picker>
|
></ha-icon-picker>
|
||||||
<paper-dropdown-menu
|
<mwc-select
|
||||||
.label="${this.hass.localize(
|
.label="${this.hass.localize(
|
||||||
"ui.panel.lovelace.editor.card.sensor.graph_type"
|
"ui.panel.lovelace.editor.card.sensor.graph_type"
|
||||||
)} (${this.hass.localize(
|
)} (${this.hass.localize(
|
||||||
"ui.panel.lovelace.editor.card.config.optional"
|
"ui.panel.lovelace.editor.card.config.optional"
|
||||||
)})"
|
)})"
|
||||||
.configValue=${"graph"}
|
.configValue=${"graph"}
|
||||||
@value-changed=${this._valueChanged}
|
@selected=${this._valueChanged}
|
||||||
|
@closed=${stopPropagation}
|
||||||
|
fixedMenuPosition
|
||||||
|
naturalMenuWidth
|
||||||
|
.value=${this._graph}
|
||||||
>
|
>
|
||||||
<paper-listbox
|
${graphs.map(
|
||||||
slot="dropdown-content"
|
(graph) =>
|
||||||
.selected=${graphs.indexOf(this._graph)}
|
html`<mwc-list-item .value=${graph}>${graph}</mwc-list-item>`
|
||||||
>
|
)}
|
||||||
${graphs.map((graph) => html`<paper-item>${graph}</paper-item>`)}
|
</mwc-select>
|
||||||
</paper-listbox>
|
|
||||||
</paper-dropdown-menu>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="side-by-side">
|
<div class="side-by-side">
|
||||||
<paper-input
|
<paper-input
|
||||||
|
@@ -1,33 +1,35 @@
|
|||||||
|
import "@material/mwc-list/mwc-list-item";
|
||||||
|
import "@material/mwc-select/mwc-select";
|
||||||
import "@polymer/paper-input/paper-input";
|
import "@polymer/paper-input/paper-input";
|
||||||
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
||||||
import { customElement, property, state } from "lit/decorators";
|
import { customElement, property, state } from "lit/decorators";
|
||||||
import {
|
import {
|
||||||
array,
|
array,
|
||||||
assert,
|
assert,
|
||||||
|
assign,
|
||||||
literal,
|
literal,
|
||||||
number,
|
number,
|
||||||
object,
|
object,
|
||||||
optional,
|
optional,
|
||||||
string,
|
string,
|
||||||
union,
|
union,
|
||||||
assign,
|
|
||||||
} from "superstruct";
|
} from "superstruct";
|
||||||
import { fireEvent } from "../../../../common/dom/fire_event";
|
import { fireEvent } from "../../../../common/dom/fire_event";
|
||||||
|
import { stopPropagation } from "../../../../common/dom/stop_propagation";
|
||||||
|
import "../../../../components/entity/ha-statistics-picker";
|
||||||
|
import "../../../../components/ha-checkbox";
|
||||||
|
import "../../../../components/ha-formfield";
|
||||||
|
import "../../../../components/ha-radio";
|
||||||
|
import type { HaRadio } from "../../../../components/ha-radio";
|
||||||
|
import { StatisticType } from "../../../../data/history";
|
||||||
import { HomeAssistant } from "../../../../types";
|
import { HomeAssistant } from "../../../../types";
|
||||||
import { StatisticsGraphCardConfig } from "../../cards/types";
|
import { StatisticsGraphCardConfig } from "../../cards/types";
|
||||||
|
import { processConfigEntities } from "../../common/process-config-entities";
|
||||||
import { LovelaceCardEditor } from "../../types";
|
import { LovelaceCardEditor } from "../../types";
|
||||||
|
import { baseLovelaceCardConfig } from "../structs/base-card-struct";
|
||||||
import { entitiesConfigStruct } from "../structs/entities-struct";
|
import { entitiesConfigStruct } from "../structs/entities-struct";
|
||||||
import { EditorTarget } from "../types";
|
import { EditorTarget } from "../types";
|
||||||
import { configElementStyle } from "./config-elements-style";
|
import { configElementStyle } from "./config-elements-style";
|
||||||
import "../../../../components/entity/ha-statistics-picker";
|
|
||||||
import { processConfigEntities } from "../../common/process-config-entities";
|
|
||||||
import "../../../../components/ha-formfield";
|
|
||||||
import "../../../../components/ha-checkbox";
|
|
||||||
import { StatisticType } from "../../../../data/history";
|
|
||||||
import "../../../../components/ha-radio";
|
|
||||||
import type { HaRadio } from "../../../../components/ha-radio";
|
|
||||||
import { baseLovelaceCardConfig } from "../structs/base-card-struct";
|
|
||||||
import "@polymer/paper-dropdown-menu/paper-dropdown-menu";
|
|
||||||
|
|
||||||
const statTypeStruct = union([
|
const statTypeStruct = union([
|
||||||
literal("sum"),
|
literal("sum"),
|
||||||
@@ -118,30 +120,28 @@ export class HuiStatisticsGraphCardEditor
|
|||||||
@value-changed=${this._valueChanged}
|
@value-changed=${this._valueChanged}
|
||||||
></paper-input>
|
></paper-input>
|
||||||
<div class="side-by-side">
|
<div class="side-by-side">
|
||||||
<paper-dropdown-menu
|
<mwc-select
|
||||||
.label="${this.hass.localize(
|
.label="${this.hass.localize(
|
||||||
"ui.panel.lovelace.editor.card.statistics-graph.period"
|
"ui.panel.lovelace.editor.card.statistics-graph.period"
|
||||||
)} (${this.hass.localize(
|
)} (${this.hass.localize(
|
||||||
"ui.panel.lovelace.editor.card.config.optional"
|
"ui.panel.lovelace.editor.card.config.optional"
|
||||||
)})"
|
)})"
|
||||||
.configValue=${"period"}
|
.configValue=${"period"}
|
||||||
@iron-select=${this._periodSelected}
|
@selected=${this._periodSelected}
|
||||||
|
@closed=${stopPropagation}
|
||||||
|
fixedMenuPosition
|
||||||
|
naturalMenuWidth
|
||||||
|
.value=${this._period}
|
||||||
>
|
>
|
||||||
<paper-listbox
|
${periods.map(
|
||||||
slot="dropdown-content"
|
(period) =>
|
||||||
attr-for-selected="period"
|
html`<mwc-list-item .value=${period}>
|
||||||
.selected=${this._period}
|
${this.hass!.localize(
|
||||||
>
|
`ui.panel.lovelace.editor.card.statistics-graph.periods.${period}`
|
||||||
${periods.map(
|
)}
|
||||||
(period) =>
|
</mwc-list-item>`
|
||||||
html`<paper-item .period=${period}>
|
)}
|
||||||
${this.hass!.localize(
|
</mwc-select>
|
||||||
`ui.panel.lovelace.editor.card.statistics-graph.periods.${period}`
|
|
||||||
)}
|
|
||||||
</paper-item>`
|
|
||||||
)}
|
|
||||||
</paper-listbox>
|
|
||||||
</paper-dropdown-menu>
|
|
||||||
<paper-input
|
<paper-input
|
||||||
type="number"
|
type="number"
|
||||||
.label="${this.hass.localize(
|
.label="${this.hass.localize(
|
||||||
@@ -242,8 +242,8 @@ export class HuiStatisticsGraphCardEditor
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private _periodSelected(ev: CustomEvent) {
|
private _periodSelected(ev) {
|
||||||
const newPeriod = ev.detail.item
|
const newPeriod = ev.target.value
|
||||||
.period as StatisticsGraphCardConfig["period"];
|
.period as StatisticsGraphCardConfig["period"];
|
||||||
if (newPeriod === this._period) {
|
if (newPeriod === this._period) {
|
||||||
return;
|
return;
|
||||||
|
@@ -1,5 +1,4 @@
|
|||||||
import { mdiClose, mdiPencil, mdiPlus } from "@mdi/js";
|
import { mdiClose, mdiPencil, mdiPlus } from "@mdi/js";
|
||||||
import "@polymer/paper-item/paper-item";
|
|
||||||
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
||||||
import { customElement, property } from "lit/decorators";
|
import { customElement, property } from "lit/decorators";
|
||||||
import { fireEvent } from "../../../../common/dom/fire_event";
|
import { fireEvent } from "../../../../common/dom/fire_event";
|
||||||
|
@@ -1,13 +1,13 @@
|
|||||||
import "@material/mwc-list/mwc-list";
|
import "@material/mwc-list/mwc-list";
|
||||||
|
import "@material/mwc-list/mwc-list-item";
|
||||||
import "@material/mwc-list/mwc-radio-list-item";
|
import "@material/mwc-list/mwc-radio-list-item";
|
||||||
import "@polymer/paper-item/paper-item";
|
import "@material/mwc-select/mwc-select";
|
||||||
import "@polymer/paper-listbox/paper-listbox";
|
|
||||||
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
||||||
import { customElement, state } from "lit/decorators";
|
import { customElement, state } from "lit/decorators";
|
||||||
import { fireEvent } from "../../../../common/dom/fire_event";
|
import { fireEvent } from "../../../../common/dom/fire_event";
|
||||||
|
import { stopPropagation } from "../../../../common/dom/stop_propagation";
|
||||||
import { createCloseHeading } from "../../../../components/ha-dialog";
|
import { createCloseHeading } from "../../../../components/ha-dialog";
|
||||||
import "../../../../components/ha-icon";
|
import "../../../../components/ha-icon";
|
||||||
import "../../../../components/ha-paper-dropdown-menu";
|
|
||||||
import {
|
import {
|
||||||
fetchConfig,
|
fetchConfig,
|
||||||
fetchDashboards,
|
fetchDashboards,
|
||||||
@@ -69,40 +69,37 @@ export class HuiDialogSelectView extends LitElement {
|
|||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
${this._params.allowDashboardChange
|
${this._params.allowDashboardChange
|
||||||
? html`<ha-paper-dropdown-menu
|
? html`<mwc-select
|
||||||
.label=${this.hass.localize(
|
.label=${this.hass.localize(
|
||||||
"ui.panel.lovelace.editor.select_view.dashboard_label"
|
"ui.panel.lovelace.editor.select_view.dashboard_label"
|
||||||
)}
|
)}
|
||||||
dynamic-align
|
|
||||||
.disabled=${!this._dashboards.length}
|
.disabled=${!this._dashboards.length}
|
||||||
|
.value=${this._urlPath || this.hass.defaultPanel}
|
||||||
|
@selected=${this._dashboardChanged}
|
||||||
|
@closed=${stopPropagation}
|
||||||
|
fixedMenuPosition
|
||||||
|
naturalMenuWidth
|
||||||
>
|
>
|
||||||
<paper-listbox
|
<mwc-list-item
|
||||||
slot="dropdown-content"
|
value="lovelace"
|
||||||
.selected=${this._urlPath || this.hass.defaultPanel}
|
.disabled=${(this.hass.panels.lovelace?.config as any)?.mode ===
|
||||||
@iron-select=${this._dashboardChanged}
|
"yaml"}
|
||||||
attr-for-selected="url-path"
|
|
||||||
>
|
>
|
||||||
<paper-item
|
Default
|
||||||
.urlPath=${"lovelace"}
|
</mwc-list-item>
|
||||||
.disabled=${(this.hass.panels.lovelace?.config as any)
|
${this._dashboards.map((dashboard) => {
|
||||||
?.mode === "yaml"}
|
if (!this.hass.user!.is_admin && dashboard.require_admin) {
|
||||||
>
|
return "";
|
||||||
Default
|
}
|
||||||
</paper-item>
|
return html`
|
||||||
${this._dashboards.map((dashboard) => {
|
<mwc-list-item
|
||||||
if (!this.hass.user!.is_admin && dashboard.require_admin) {
|
.disabled=${dashboard.mode !== "storage"}
|
||||||
return "";
|
.value=${dashboard.url_path}
|
||||||
}
|
>${dashboard.title}</mwc-list-item
|
||||||
return html`
|
>
|
||||||
<paper-item
|
`;
|
||||||
.disabled=${dashboard.mode !== "storage"}
|
})}
|
||||||
.urlPath=${dashboard.url_path}
|
</mwc-select>`
|
||||||
>${dashboard.title}</paper-item
|
|
||||||
>
|
|
||||||
`;
|
|
||||||
})}
|
|
||||||
</paper-listbox>
|
|
||||||
</ha-paper-dropdown-menu>`
|
|
||||||
: ""}
|
: ""}
|
||||||
${this._config
|
${this._config
|
||||||
? this._config.views.length > 1
|
? this._config.views.length > 1
|
||||||
@@ -111,7 +108,7 @@ export class HuiDialogSelectView extends LitElement {
|
|||||||
${this._config.views.map(
|
${this._config.views.map(
|
||||||
(view, idx) => html`
|
(view, idx) => html`
|
||||||
<mwc-radio-list-item
|
<mwc-radio-list-item
|
||||||
graphic=${this._config?.views.some(({ icon }) => icon)
|
.graphic=${this._config?.views.some(({ icon }) => icon)
|
||||||
? "icon"
|
? "icon"
|
||||||
: null}
|
: null}
|
||||||
@click=${this._viewChanged}
|
@click=${this._viewChanged}
|
||||||
@@ -142,8 +139,8 @@ export class HuiDialogSelectView extends LitElement {
|
|||||||
this._params!.dashboards || (await fetchDashboards(this.hass));
|
this._params!.dashboards || (await fetchDashboards(this.hass));
|
||||||
}
|
}
|
||||||
|
|
||||||
private async _dashboardChanged(ev: CustomEvent) {
|
private async _dashboardChanged(ev) {
|
||||||
let urlPath: string | null = ev.detail.item.urlPath;
|
let urlPath: string | null = ev.target.value;
|
||||||
if (urlPath === this._urlPath) {
|
if (urlPath === this._urlPath) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -181,7 +178,7 @@ export class HuiDialogSelectView extends LitElement {
|
|||||||
return [
|
return [
|
||||||
haStyleDialog,
|
haStyleDialog,
|
||||||
css`
|
css`
|
||||||
ha-paper-dropdown-menu {
|
mwc-select {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
`,
|
`,
|
||||||
|
@@ -1,7 +1,10 @@
|
|||||||
|
import "@material/mwc-list/mwc-list-item";
|
||||||
|
import "@material/mwc-select/mwc-select";
|
||||||
import "@polymer/paper-input/paper-input";
|
import "@polymer/paper-input/paper-input";
|
||||||
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
||||||
import { customElement, property, state } from "lit/decorators";
|
import { customElement, property, state } from "lit/decorators";
|
||||||
import { fireEvent } from "../../../../common/dom/fire_event";
|
import { fireEvent } from "../../../../common/dom/fire_event";
|
||||||
|
import { stopPropagation } from "../../../../common/dom/stop_propagation";
|
||||||
import { slugify } from "../../../../common/string/slugify";
|
import { slugify } from "../../../../common/string/slugify";
|
||||||
import "../../../../components/ha-formfield";
|
import "../../../../components/ha-formfield";
|
||||||
import "../../../../components/ha-icon-picker";
|
import "../../../../components/ha-icon-picker";
|
||||||
@@ -121,26 +124,24 @@ export class HuiViewEditor extends LitElement {
|
|||||||
.configValue=${"theme"}
|
.configValue=${"theme"}
|
||||||
@value-changed=${this._valueChanged}
|
@value-changed=${this._valueChanged}
|
||||||
></hui-theme-select-editor>
|
></hui-theme-select-editor>
|
||||||
<paper-dropdown-menu
|
<mwc-select
|
||||||
.label=${this.hass.localize(
|
.label=${this.hass.localize(
|
||||||
"ui.panel.lovelace.editor.edit_view.type"
|
"ui.panel.lovelace.editor.edit_view.type"
|
||||||
)}
|
)}
|
||||||
|
.value=${this._type}
|
||||||
|
@selected=${this._typeChanged}
|
||||||
|
@closed=${stopPropagation}
|
||||||
|
fixedMenuPosition
|
||||||
|
naturalMenuWidth
|
||||||
>
|
>
|
||||||
<paper-listbox
|
${[DEFAULT_VIEW_LAYOUT, SIDEBAR_VIEW_LAYOUT, PANEL_VIEW_LAYOUT].map(
|
||||||
slot="dropdown-content"
|
(type) => html`<mwc-list-item .value=${type}>
|
||||||
.selected=${this._type}
|
${this.hass.localize(
|
||||||
attr-for-selected="type"
|
`ui.panel.lovelace.editor.edit_view.types.${type}`
|
||||||
@iron-select=${this._typeChanged}
|
)}
|
||||||
>
|
</mwc-list-item>`
|
||||||
${[DEFAULT_VIEW_LAYOUT, SIDEBAR_VIEW_LAYOUT, PANEL_VIEW_LAYOUT].map(
|
)}
|
||||||
(type) => html`<paper-item .type=${type}>
|
</mwc-select>
|
||||||
${this.hass.localize(
|
|
||||||
`ui.panel.lovelace.editor.edit_view.types.${type}`
|
|
||||||
)}
|
|
||||||
</paper-item>`
|
|
||||||
)}
|
|
||||||
</paper-listbox>
|
|
||||||
</paper-dropdown-menu>
|
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
@@ -166,7 +167,7 @@ export class HuiViewEditor extends LitElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private _typeChanged(ev): void {
|
private _typeChanged(ev): void {
|
||||||
const selected = ev.target.selected;
|
const selected = ev.target.value;
|
||||||
if (selected === "") {
|
if (selected === "") {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
import "@polymer/paper-item/paper-icon-item";
|
import "@material/mwc-list/mwc-list-item";
|
||||||
import "@polymer/paper-item/paper-item-body";
|
import "@material/mwc-select/mwc-select";
|
||||||
import {
|
import {
|
||||||
css,
|
css,
|
||||||
CSSResultGroup,
|
CSSResultGroup,
|
||||||
@@ -68,19 +68,20 @@ export class HuiViewVisibilityEditor extends LitElement {
|
|||||||
</p>
|
</p>
|
||||||
${this._sortedUsers(this._users).map(
|
${this._sortedUsers(this._users).map(
|
||||||
(user) => html`
|
(user) => html`
|
||||||
<paper-icon-item>
|
<mwc-list-item graphic="avatar" hasMeta>
|
||||||
<ha-user-badge
|
<ha-user-badge
|
||||||
slot="item-icon"
|
slot="graphic"
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
.user=${user}
|
.user=${user}
|
||||||
></ha-user-badge>
|
></ha-user-badge>
|
||||||
<paper-item-body>${user.name}</paper-item-body>
|
<span>${user.name}</span>
|
||||||
<ha-switch
|
<ha-switch
|
||||||
|
slot="meta"
|
||||||
.userId=${user.id}
|
.userId=${user.id}
|
||||||
@change=${this.valChange}
|
@change=${this.valChange}
|
||||||
.checked=${this.checkUser(user.id)}
|
.checked=${this.checkUser(user.id)}
|
||||||
></ha-switch>
|
></ha-switch>
|
||||||
</paper-icon-item>
|
</mwc-list-item>
|
||||||
`
|
`
|
||||||
)}
|
)}
|
||||||
`;
|
`;
|
||||||
|
@@ -110,10 +110,11 @@ class HuiMediaPlayerEntityRow extends LitElement implements LovelaceRow {
|
|||||||
|
|
||||||
const entityState = stateObj.state;
|
const entityState = stateObj.state;
|
||||||
const controlButton = this._computeControlButton(stateObj);
|
const controlButton = this._computeControlButton(stateObj);
|
||||||
|
const assumedState = stateObj.attributes.assumed_state === true;
|
||||||
|
|
||||||
const buttons = html`
|
const buttons = html`
|
||||||
${!this._narrow &&
|
${!this._narrow &&
|
||||||
entityState === "playing" &&
|
(entityState === "playing" || assumedState) &&
|
||||||
supportsFeature(stateObj, SUPPORT_PREVIOUS_TRACK)
|
supportsFeature(stateObj, SUPPORT_PREVIOUS_TRACK)
|
||||||
? html`
|
? html`
|
||||||
<ha-icon-button
|
<ha-icon-button
|
||||||
@@ -125,14 +126,15 @@ class HuiMediaPlayerEntityRow extends LitElement implements LovelaceRow {
|
|||||||
></ha-icon-button>
|
></ha-icon-button>
|
||||||
`
|
`
|
||||||
: ""}
|
: ""}
|
||||||
${(entityState === "playing" &&
|
${!assumedState &&
|
||||||
|
((entityState === "playing" &&
|
||||||
(supportsFeature(stateObj, SUPPORT_PAUSE) ||
|
(supportsFeature(stateObj, SUPPORT_PAUSE) ||
|
||||||
supportsFeature(stateObj, SUPPORT_STOP))) ||
|
supportsFeature(stateObj, SUPPORT_STOP))) ||
|
||||||
((entityState === "paused" || entityState === "idle") &&
|
((entityState === "paused" || entityState === "idle") &&
|
||||||
supportsFeature(stateObj, SUPPORT_PLAY)) ||
|
supportsFeature(stateObj, SUPPORT_PLAY)) ||
|
||||||
(entityState === "on" &&
|
(entityState === "on" &&
|
||||||
(supportsFeature(stateObj, SUPPORT_PLAY) ||
|
(supportsFeature(stateObj, SUPPORT_PLAY) ||
|
||||||
supportsFeature(stateObj, SUPPORT_PAUSE)))
|
supportsFeature(stateObj, SUPPORT_PAUSE))))
|
||||||
? html`
|
? html`
|
||||||
<ha-icon-button
|
<ha-icon-button
|
||||||
.path=${controlButton.icon}
|
.path=${controlButton.icon}
|
||||||
@@ -143,7 +145,34 @@ class HuiMediaPlayerEntityRow extends LitElement implements LovelaceRow {
|
|||||||
></ha-icon-button>
|
></ha-icon-button>
|
||||||
`
|
`
|
||||||
: ""}
|
: ""}
|
||||||
${entityState === "playing" &&
|
${assumedState && supportsFeature(stateObj, SUPPORT_PLAY)
|
||||||
|
? html`
|
||||||
|
<ha-icon-button
|
||||||
|
.path=${mdiPlay}
|
||||||
|
.label=${this.hass.localize(`ui.card.media_player.media_play`)}
|
||||||
|
@click=${this._play}
|
||||||
|
></ha-icon-button>
|
||||||
|
`
|
||||||
|
: ""}
|
||||||
|
${assumedState && supportsFeature(stateObj, SUPPORT_PAUSE)
|
||||||
|
? html`
|
||||||
|
<ha-icon-button
|
||||||
|
.path=${mdiPause}
|
||||||
|
.label=${this.hass.localize(`ui.card.media_player.media_pause`)}
|
||||||
|
@click=${this._pause}
|
||||||
|
></ha-icon-button>
|
||||||
|
`
|
||||||
|
: ""}
|
||||||
|
${assumedState && supportsFeature(stateObj, SUPPORT_STOP)
|
||||||
|
? html`
|
||||||
|
<ha-icon-button
|
||||||
|
.path=${mdiStop}
|
||||||
|
.label=${this.hass.localize(`ui.card.media_player.media_stop`)}
|
||||||
|
@click=${this._stop}
|
||||||
|
></ha-icon-button>
|
||||||
|
`
|
||||||
|
: ""}
|
||||||
|
${(entityState === "playing" || assumedState) &&
|
||||||
supportsFeature(stateObj, SUPPORT_NEXT_TRACK)
|
supportsFeature(stateObj, SUPPORT_NEXT_TRACK)
|
||||||
? html`
|
? html`
|
||||||
<ha-icon-button
|
<ha-icon-button
|
||||||
@@ -312,6 +341,24 @@ class HuiMediaPlayerEntityRow extends LitElement implements LovelaceRow {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private _play(): void {
|
||||||
|
this.hass!.callService("media_player", "media_play", {
|
||||||
|
entity_id: this._config!.entity,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private _pause(): void {
|
||||||
|
this.hass!.callService("media_player", "media_pause", {
|
||||||
|
entity_id: this._config!.entity,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private _stop(): void {
|
||||||
|
this.hass!.callService("media_player", "media_stop", {
|
||||||
|
entity_id: this._config!.entity,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
private _previousTrack(): void {
|
private _previousTrack(): void {
|
||||||
this.hass!.callService("media_player", "media_previous_track", {
|
this.hass!.callService("media_player", "media_previous_track", {
|
||||||
entity_id: this._config!.entity,
|
entity_id: this._config!.entity,
|
||||||
|
@@ -276,137 +276,153 @@ class HUIRoot extends LitElement {
|
|||||||
></ha-icon-button>
|
></ha-icon-button>
|
||||||
`
|
`
|
||||||
: ""}
|
: ""}
|
||||||
<ha-button-menu corner="BOTTOM_START">
|
${this._showButtonMenu
|
||||||
<ha-icon-button
|
? html`
|
||||||
slot="trigger"
|
<ha-button-menu corner="BOTTOM_START">
|
||||||
.label=${this.hass!.localize(
|
<ha-icon-button
|
||||||
"ui.panel.lovelace.editor.menu.open"
|
slot="trigger"
|
||||||
)}
|
|
||||||
.path=${mdiDotsVertical}
|
|
||||||
></ha-icon-button>
|
|
||||||
${this.narrow &&
|
|
||||||
this._conversation(this.hass.config.components)
|
|
||||||
? html`
|
|
||||||
<mwc-list-item
|
|
||||||
.label=${this.hass!.localize(
|
.label=${this.hass!.localize(
|
||||||
"ui.panel.lovelace.menu.start_conversation"
|
"ui.panel.lovelace.editor.menu.open"
|
||||||
)}
|
)}
|
||||||
graphic="icon"
|
.path=${mdiDotsVertical}
|
||||||
@request-selected=${this._showVoiceCommandDialog}
|
></ha-icon-button>
|
||||||
>
|
${this.narrow &&
|
||||||
<span
|
this._conversation(this.hass.config.components)
|
||||||
>${this.hass!.localize(
|
? html`
|
||||||
"ui.panel.lovelace.menu.start_conversation"
|
<mwc-list-item
|
||||||
)}</span
|
.label=${this.hass!.localize(
|
||||||
>
|
"ui.panel.lovelace.menu.start_conversation"
|
||||||
<ha-svg-icon
|
)}
|
||||||
slot="graphic"
|
graphic="icon"
|
||||||
.path=${mdiMicrophone}
|
@request-selected=${this
|
||||||
></ha-svg-icon>
|
._showVoiceCommandDialog}
|
||||||
</mwc-list-item>
|
>
|
||||||
`
|
<span
|
||||||
: ""}
|
>${this.hass!.localize(
|
||||||
${this._yamlMode
|
"ui.panel.lovelace.menu.start_conversation"
|
||||||
? html`
|
)}</span
|
||||||
<mwc-list-item
|
>
|
||||||
aria-label=${this.hass!.localize(
|
<ha-svg-icon
|
||||||
"ui.common.refresh"
|
slot="graphic"
|
||||||
)}
|
.path=${mdiMicrophone}
|
||||||
graphic="icon"
|
></ha-svg-icon>
|
||||||
@request-selected=${this._handleRefresh}
|
</mwc-list-item>
|
||||||
>
|
`
|
||||||
<span
|
: ""}
|
||||||
>${this.hass!.localize("ui.common.refresh")}</span
|
${this._yamlMode
|
||||||
>
|
? html`
|
||||||
<ha-svg-icon
|
<mwc-list-item
|
||||||
slot="graphic"
|
aria-label=${this.hass!.localize(
|
||||||
.path=${mdiRefresh}
|
"ui.common.refresh"
|
||||||
></ha-svg-icon>
|
)}
|
||||||
</mwc-list-item>
|
graphic="icon"
|
||||||
<mwc-list-item
|
@request-selected=${this._handleRefresh}
|
||||||
aria-label=${this.hass!.localize(
|
>
|
||||||
"ui.panel.lovelace.unused_entities.title"
|
<span
|
||||||
)}
|
>${this.hass!.localize(
|
||||||
graphic="icon"
|
"ui.common.refresh"
|
||||||
@request-selected=${this._handleUnusedEntities}
|
)}</span
|
||||||
>
|
>
|
||||||
<span
|
<ha-svg-icon
|
||||||
>${this.hass!.localize(
|
slot="graphic"
|
||||||
"ui.panel.lovelace.unused_entities.title"
|
.path=${mdiRefresh}
|
||||||
)}</span
|
></ha-svg-icon>
|
||||||
>
|
</mwc-list-item>
|
||||||
<ha-svg-icon
|
<mwc-list-item
|
||||||
slot="graphic"
|
aria-label=${this.hass!.localize(
|
||||||
.path=${mdiShape}
|
"ui.panel.lovelace.unused_entities.title"
|
||||||
></ha-svg-icon>
|
)}
|
||||||
</mwc-list-item>
|
graphic="icon"
|
||||||
`
|
@request-selected=${this
|
||||||
: ""}
|
._handleUnusedEntities}
|
||||||
${(this.hass.panels.lovelace?.config as LovelacePanelConfig)
|
>
|
||||||
?.mode === "yaml"
|
<span
|
||||||
? html`
|
>${this.hass!.localize(
|
||||||
<mwc-list-item
|
"ui.panel.lovelace.unused_entities.title"
|
||||||
graphic="icon"
|
)}</span
|
||||||
aria-label=${this.hass!.localize(
|
>
|
||||||
"ui.panel.lovelace.menu.reload_resources"
|
<ha-svg-icon
|
||||||
)}
|
slot="graphic"
|
||||||
@request-selected=${this._handleReloadResources}
|
.path=${mdiShape}
|
||||||
>
|
></ha-svg-icon>
|
||||||
${this.hass!.localize(
|
</mwc-list-item>
|
||||||
"ui.panel.lovelace.menu.reload_resources"
|
`
|
||||||
)}
|
: ""}
|
||||||
<ha-svg-icon
|
${(
|
||||||
slot="graphic"
|
this.hass.panels.lovelace
|
||||||
.path=${mdiRefresh}
|
?.config as LovelacePanelConfig
|
||||||
></ha-svg-icon>
|
)?.mode === "yaml"
|
||||||
</mwc-list-item>
|
? html`
|
||||||
`
|
<mwc-list-item
|
||||||
: ""}
|
graphic="icon"
|
||||||
${this.hass!.user?.is_admin && !this.hass!.config.safe_mode
|
aria-label=${this.hass!.localize(
|
||||||
? html`
|
"ui.panel.lovelace.menu.reload_resources"
|
||||||
<mwc-list-item
|
)}
|
||||||
graphic="icon"
|
@request-selected=${this
|
||||||
aria-label=${this.hass!.localize(
|
._handleReloadResources}
|
||||||
"ui.panel.lovelace.menu.configure_ui"
|
>
|
||||||
)}
|
${this.hass!.localize(
|
||||||
@request-selected=${this._handleEnableEditMode}
|
"ui.panel.lovelace.menu.reload_resources"
|
||||||
>
|
)}
|
||||||
${this.hass!.localize(
|
<ha-svg-icon
|
||||||
"ui.panel.lovelace.menu.configure_ui"
|
slot="graphic"
|
||||||
)}
|
.path=${mdiRefresh}
|
||||||
<ha-svg-icon
|
></ha-svg-icon>
|
||||||
slot="graphic"
|
</mwc-list-item>
|
||||||
.path=${mdiPencil}
|
`
|
||||||
></ha-svg-icon>
|
: ""}
|
||||||
</mwc-list-item>
|
${this.hass!.user?.is_admin &&
|
||||||
`
|
!this.hass!.config.safe_mode
|
||||||
: ""}
|
? html`
|
||||||
${this._editMode
|
<mwc-list-item
|
||||||
? html`
|
graphic="icon"
|
||||||
<a
|
aria-label=${this.hass!.localize(
|
||||||
href=${documentationUrl(this.hass, "/lovelace/")}
|
"ui.panel.lovelace.menu.configure_ui"
|
||||||
rel="noreferrer"
|
)}
|
||||||
class="menu-link"
|
@request-selected=${this
|
||||||
target="_blank"
|
._handleEnableEditMode}
|
||||||
>
|
>
|
||||||
<mwc-list-item
|
${this.hass!.localize(
|
||||||
graphic="icon"
|
"ui.panel.lovelace.menu.configure_ui"
|
||||||
aria-label=${this.hass!.localize(
|
)}
|
||||||
"ui.panel.lovelace.menu.help"
|
<ha-svg-icon
|
||||||
)}
|
slot="graphic"
|
||||||
>
|
.path=${mdiPencil}
|
||||||
${this.hass!.localize(
|
></ha-svg-icon>
|
||||||
"ui.panel.lovelace.menu.help"
|
</mwc-list-item>
|
||||||
)}
|
`
|
||||||
<ha-svg-icon
|
: ""}
|
||||||
slot="graphic"
|
${this._editMode
|
||||||
.path=${mdiHelp}
|
? html`
|
||||||
></ha-svg-icon>
|
<a
|
||||||
</mwc-list-item>
|
href=${documentationUrl(
|
||||||
</a>
|
this.hass,
|
||||||
`
|
"/lovelace/"
|
||||||
: ""}
|
)}
|
||||||
</ha-button-menu>
|
rel="noreferrer"
|
||||||
|
class="menu-link"
|
||||||
|
target="_blank"
|
||||||
|
>
|
||||||
|
<mwc-list-item
|
||||||
|
graphic="icon"
|
||||||
|
aria-label=${this.hass!.localize(
|
||||||
|
"ui.panel.lovelace.menu.help"
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
${this.hass!.localize(
|
||||||
|
"ui.panel.lovelace.menu.help"
|
||||||
|
)}
|
||||||
|
<ha-svg-icon
|
||||||
|
slot="graphic"
|
||||||
|
.path=${mdiHelp}
|
||||||
|
></ha-svg-icon>
|
||||||
|
</mwc-list-item>
|
||||||
|
</a>
|
||||||
|
`
|
||||||
|
: ""}
|
||||||
|
</ha-button-menu>
|
||||||
|
`
|
||||||
|
: ""}
|
||||||
</app-toolbar>
|
</app-toolbar>
|
||||||
`}
|
`}
|
||||||
${this._editMode
|
${this._editMode
|
||||||
@@ -621,6 +637,17 @@ class HUIRoot extends LitElement {
|
|||||||
return this.shadowRoot!.getElementById("view") as HTMLDivElement;
|
return this.shadowRoot!.getElementById("view") as HTMLDivElement;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private get _showButtonMenu(): boolean {
|
||||||
|
return (
|
||||||
|
(this.narrow && this._conversation(this.hass.config.components)) ||
|
||||||
|
this._editMode ||
|
||||||
|
(this.hass!.user?.is_admin && !this.hass!.config.safe_mode) ||
|
||||||
|
(this.hass.panels.lovelace?.config as LovelacePanelConfig)?.mode ===
|
||||||
|
"yaml" ||
|
||||||
|
this._yamlMode
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
private _handleRefresh(ev: CustomEvent<RequestSelectedDetail>): void {
|
private _handleRefresh(ev: CustomEvent<RequestSelectedDetail>): void {
|
||||||
if (!shouldHandleRequestSelectedEvent(ev)) {
|
if (!shouldHandleRequestSelectedEvent(ev)) {
|
||||||
return;
|
return;
|
||||||
|
@@ -25,7 +25,6 @@ import { computeStateDomain } from "../../common/entity/compute_state_domain";
|
|||||||
import { computeStateName } from "../../common/entity/compute_state_name";
|
import { computeStateName } from "../../common/entity/compute_state_name";
|
||||||
import { domainIcon } from "../../common/entity/domain_icon";
|
import { domainIcon } from "../../common/entity/domain_icon";
|
||||||
import { supportsFeature } from "../../common/entity/supports-feature";
|
import { supportsFeature } from "../../common/entity/supports-feature";
|
||||||
import { navigate } from "../../common/navigate";
|
|
||||||
import "../../components/ha-button-menu";
|
import "../../components/ha-button-menu";
|
||||||
import "../../components/ha-icon-button";
|
import "../../components/ha-icon-button";
|
||||||
import { UNAVAILABLE_STATES } from "../../data/entity";
|
import { UNAVAILABLE_STATES } from "../../data/entity";
|
||||||
@@ -47,6 +46,12 @@ import type { HomeAssistant } from "../../types";
|
|||||||
import "../lovelace/components/hui-marquee";
|
import "../lovelace/components/hui-marquee";
|
||||||
import { BrowserMediaPlayer } from "./browser-media-player";
|
import { BrowserMediaPlayer } from "./browser-media-player";
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface HASSDomEvents {
|
||||||
|
"player-picked": { entityId: string };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@customElement("ha-bar-media-player")
|
@customElement("ha-bar-media-player")
|
||||||
class BarMediaPlayer extends LitElement {
|
class BarMediaPlayer extends LitElement {
|
||||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||||
@@ -399,7 +404,7 @@ class BarMediaPlayer extends LitElement {
|
|||||||
|
|
||||||
private _selectPlayer(ev: CustomEvent): void {
|
private _selectPlayer(ev: CustomEvent): void {
|
||||||
const entityId = (ev.currentTarget as any).player;
|
const entityId = (ev.currentTarget as any).player;
|
||||||
navigate(`/media-browser/${entityId}`, { replace: true });
|
fireEvent(this, "player-picked", { entityId });
|
||||||
}
|
}
|
||||||
|
|
||||||
static get styles(): CSSResultGroup {
|
static get styles(): CSSResultGroup {
|
||||||
|
@@ -15,6 +15,7 @@ import { LocalStorage } from "../../common/decorators/local-storage";
|
|||||||
import { fireEvent, HASSDomEvent } from "../../common/dom/fire_event";
|
import { fireEvent, HASSDomEvent } from "../../common/dom/fire_event";
|
||||||
import { navigate } from "../../common/navigate";
|
import { navigate } from "../../common/navigate";
|
||||||
import "../../components/ha-menu-button";
|
import "../../components/ha-menu-button";
|
||||||
|
import "../../components/ha-circular-progress";
|
||||||
import "../../components/ha-icon-button";
|
import "../../components/ha-icon-button";
|
||||||
import "../../components/ha-svg-icon";
|
import "../../components/ha-svg-icon";
|
||||||
import "../../components/media-player/ha-media-player-browse";
|
import "../../components/media-player/ha-media-player-browse";
|
||||||
@@ -43,6 +44,16 @@ import {
|
|||||||
isCameraMediaSource,
|
isCameraMediaSource,
|
||||||
} from "../../data/camera";
|
} from "../../data/camera";
|
||||||
|
|
||||||
|
const createMediaPanelUrl = (entityId: string, items: MediaPlayerItemId[]) => {
|
||||||
|
let path = `/media-browser/${entityId}`;
|
||||||
|
for (const item of items.slice(1)) {
|
||||||
|
path +=
|
||||||
|
"/" +
|
||||||
|
encodeURIComponent(`${item.media_content_type},${item.media_content_id}`);
|
||||||
|
}
|
||||||
|
return path;
|
||||||
|
};
|
||||||
|
|
||||||
@customElement("ha-panel-media-browser")
|
@customElement("ha-panel-media-browser")
|
||||||
class PanelMediaBrowser extends LitElement {
|
class PanelMediaBrowser extends LitElement {
|
||||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||||
@@ -54,6 +65,8 @@ class PanelMediaBrowser extends LitElement {
|
|||||||
|
|
||||||
@state() _currentItem?: MediaPlayerItem;
|
@state() _currentItem?: MediaPlayerItem;
|
||||||
|
|
||||||
|
@state() _uploading = 0;
|
||||||
|
|
||||||
private _navigateIds: MediaPlayerItemId[] = [
|
private _navigateIds: MediaPlayerItemId[] = [
|
||||||
{
|
{
|
||||||
media_content_id: undefined,
|
media_content_id: undefined,
|
||||||
@@ -97,12 +110,34 @@ class PanelMediaBrowser extends LitElement {
|
|||||||
)
|
)
|
||||||
? html`
|
? html`
|
||||||
<mwc-button
|
<mwc-button
|
||||||
.label=${this.hass.localize(
|
.label=${this._uploading > 0
|
||||||
"ui.components.media-browser.file_management.add_media"
|
? this.hass.localize(
|
||||||
)}
|
"ui.components.media-browser.file_management.uploading",
|
||||||
|
{
|
||||||
|
count: this._uploading,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
: this.hass.localize(
|
||||||
|
"ui.components.media-browser.file_management.add_media"
|
||||||
|
)}
|
||||||
|
.disabled=${this._uploading > 0}
|
||||||
@click=${this._startUpload}
|
@click=${this._startUpload}
|
||||||
>
|
>
|
||||||
<ha-svg-icon .path=${mdiUpload} slot="icon"></ha-svg-icon>
|
${this._uploading > 0
|
||||||
|
? html`
|
||||||
|
<ha-circular-progress
|
||||||
|
size="tiny"
|
||||||
|
active
|
||||||
|
alt=""
|
||||||
|
slot="icon"
|
||||||
|
></ha-circular-progress>
|
||||||
|
`
|
||||||
|
: html`
|
||||||
|
<ha-svg-icon
|
||||||
|
.path=${mdiUpload}
|
||||||
|
slot="icon"
|
||||||
|
></ha-svg-icon>
|
||||||
|
`}
|
||||||
</mwc-button>
|
</mwc-button>
|
||||||
`
|
`
|
||||||
: ""}
|
: ""}
|
||||||
@@ -120,6 +155,7 @@ class PanelMediaBrowser extends LitElement {
|
|||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
.entityId=${this._entityId}
|
.entityId=${this._entityId}
|
||||||
.narrow=${this.narrow}
|
.narrow=${this.narrow}
|
||||||
|
@player-picked=${this._playerPicked}
|
||||||
></ha-bar-media-player>
|
></ha-bar-media-player>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
@@ -179,7 +215,9 @@ class PanelMediaBrowser extends LitElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private _goBack() {
|
private _goBack() {
|
||||||
history.back();
|
navigate(
|
||||||
|
createMediaPanelUrl(this._entityId, this._navigateIds.slice(0, -1))
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private _mediaBrowsed(ev: { detail: HASSDomEvents["media-browsed"] }) {
|
private _mediaBrowsed(ev: { detail: HASSDomEvents["media-browsed"] }) {
|
||||||
@@ -188,15 +226,9 @@ class PanelMediaBrowser extends LitElement {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let path = "";
|
navigate(createMediaPanelUrl(this._entityId, ev.detail.ids), {
|
||||||
for (const item of ev.detail.ids.slice(1)) {
|
replace: ev.detail.replace,
|
||||||
path +=
|
});
|
||||||
"/" +
|
|
||||||
encodeURIComponent(
|
|
||||||
`${item.media_content_type},${item.media_content_id}`
|
|
||||||
);
|
|
||||||
}
|
|
||||||
navigate(`/media-browser/${this._entityId}${path}`);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private async _mediaPicked(
|
private async _mediaPicked(
|
||||||
@@ -219,18 +251,18 @@ class PanelMediaBrowser extends LitElement {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (item.media_content_type.startsWith("audio/")) {
|
const resolvedUrl = await resolveMediaSource(
|
||||||
|
this.hass,
|
||||||
|
item.media_content_id
|
||||||
|
);
|
||||||
|
|
||||||
|
if (resolvedUrl.mime_type.startsWith("audio/")) {
|
||||||
await this.shadowRoot!.querySelector("ha-bar-media-player")!.playItem(
|
await this.shadowRoot!.querySelector("ha-bar-media-player")!.playItem(
|
||||||
item
|
item
|
||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const resolvedUrl: any = await resolveMediaSource(
|
|
||||||
this.hass,
|
|
||||||
item.media_content_id
|
|
||||||
);
|
|
||||||
|
|
||||||
showWebBrowserPlayMediaDialog(this, {
|
showWebBrowserPlayMediaDialog(this, {
|
||||||
sourceUrl: resolvedUrl.url,
|
sourceUrl: resolvedUrl.url,
|
||||||
sourceType: resolvedUrl.mime_type,
|
sourceType: resolvedUrl.mime_type,
|
||||||
@@ -239,29 +271,50 @@ class PanelMediaBrowser extends LitElement {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private _playerPicked(ev) {
|
||||||
|
const entityId: string = ev.detail.entityId;
|
||||||
|
if (entityId === this._entityId) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
navigate(createMediaPanelUrl(entityId, this._navigateIds));
|
||||||
|
}
|
||||||
|
|
||||||
private async _startUpload() {
|
private async _startUpload() {
|
||||||
|
if (this._uploading > 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
const input = document.createElement("input");
|
const input = document.createElement("input");
|
||||||
input.type = "file";
|
input.type = "file";
|
||||||
input.addEventListener("change", async () => {
|
input.accept = "audio/*,video/*,image/*";
|
||||||
try {
|
input.multiple = true;
|
||||||
await uploadLocalMedia(
|
input.addEventListener(
|
||||||
this.hass,
|
"change",
|
||||||
this._currentItem!.media_content_id!,
|
async () => {
|
||||||
input.files![0]
|
const files = input.files!;
|
||||||
);
|
const target = this._currentItem!.media_content_id!;
|
||||||
} catch (err: any) {
|
|
||||||
showAlertDialog(this, {
|
for (let i = 0; i < files.length; i++) {
|
||||||
text: this.hass.localize(
|
this._uploading = files.length - i;
|
||||||
"ui.components.media-browser.file_management.upload_failed",
|
try {
|
||||||
{
|
// eslint-disable-next-line no-await-in-loop
|
||||||
reason: err.message || err,
|
await uploadLocalMedia(this.hass, target, files[i]);
|
||||||
}
|
} catch (err: any) {
|
||||||
),
|
showAlertDialog(this, {
|
||||||
});
|
text: this.hass.localize(
|
||||||
return;
|
"ui.components.media-browser.file_management.upload_failed",
|
||||||
}
|
{
|
||||||
await this._browser.refresh();
|
reason: err.message || err,
|
||||||
});
|
}
|
||||||
|
),
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this._uploading = 0;
|
||||||
|
await this._browser.refresh();
|
||||||
|
},
|
||||||
|
{ once: true }
|
||||||
|
);
|
||||||
input.click();
|
input.click();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -269,8 +322,10 @@ class PanelMediaBrowser extends LitElement {
|
|||||||
return [
|
return [
|
||||||
haStyle,
|
haStyle,
|
||||||
css`
|
css`
|
||||||
:host {
|
app-toolbar mwc-button {
|
||||||
--mdc-theme-primary: var(--app-header-text-color);
|
--mdc-theme-primary: var(--app-header-text-color);
|
||||||
|
/* We use icon + text to show disabled state */
|
||||||
|
--mdc-button-disabled-ink-color: var(--app-header-text-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
ha-media-player-browse {
|
ha-media-player-browse {
|
||||||
@@ -288,7 +343,8 @@ class PanelMediaBrowser extends LitElement {
|
|||||||
right: 0;
|
right: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
ha-svg-icon[slot="icon"] {
|
ha-svg-icon[slot="icon"],
|
||||||
|
ha-circular-progress[slot="icon"] {
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
}
|
}
|
||||||
`,
|
`,
|
||||||
|
@@ -1,8 +1,6 @@
|
|||||||
import "@material/mwc-button";
|
import "@material/mwc-button";
|
||||||
import "@polymer/app-layout/app-header/app-header";
|
import "@polymer/app-layout/app-header/app-header";
|
||||||
import "@polymer/app-layout/app-toolbar/app-toolbar";
|
import "@polymer/app-layout/app-toolbar/app-toolbar";
|
||||||
import "@polymer/paper-item/paper-item";
|
|
||||||
import "@polymer/paper-item/paper-item-body";
|
|
||||||
import { UnsubscribeFunc } from "home-assistant-js-websocket";
|
import { UnsubscribeFunc } from "home-assistant-js-websocket";
|
||||||
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
||||||
import { property, state } from "lit/decorators";
|
import { property, state } from "lit/decorators";
|
||||||
|
@@ -1,12 +1,11 @@
|
|||||||
import "@polymer/paper-item/paper-item";
|
|
||||||
import "@polymer/paper-listbox/paper-listbox";
|
|
||||||
import { html, LitElement, PropertyValues, TemplateResult } from "lit";
|
import { html, LitElement, PropertyValues, TemplateResult } from "lit";
|
||||||
import { customElement, property, state } from "lit/decorators";
|
import { customElement, property, state } from "lit/decorators";
|
||||||
import "../../components/ha-paper-dropdown-menu";
|
|
||||||
import "../../components/ha-settings-row";
|
import "../../components/ha-settings-row";
|
||||||
import { fetchDashboards, LovelaceDashboard } from "../../data/lovelace";
|
import { fetchDashboards, LovelaceDashboard } from "../../data/lovelace";
|
||||||
import { setDefaultPanel } from "../../data/panel";
|
import { setDefaultPanel } from "../../data/panel";
|
||||||
import { HomeAssistant } from "../../types";
|
import { HomeAssistant } from "../../types";
|
||||||
|
import "@material/mwc-list/mwc-list-item";
|
||||||
|
import "@material/mwc-select/mwc-select";
|
||||||
|
|
||||||
@customElement("ha-pick-dashboard-row")
|
@customElement("ha-pick-dashboard-row")
|
||||||
class HaPickDashboardRow extends LitElement {
|
class HaPickDashboardRow extends LitElement {
|
||||||
@@ -30,36 +29,30 @@ class HaPickDashboardRow extends LitElement {
|
|||||||
<span slot="description">
|
<span slot="description">
|
||||||
${this.hass.localize("ui.panel.profile.dashboard.description")}
|
${this.hass.localize("ui.panel.profile.dashboard.description")}
|
||||||
</span>
|
</span>
|
||||||
<ha-paper-dropdown-menu
|
<mwc-select
|
||||||
.label=${this.hass.localize(
|
.label=${this.hass.localize(
|
||||||
"ui.panel.profile.dashboard.dropdown_label"
|
"ui.panel.profile.dashboard.dropdown_label"
|
||||||
)}
|
)}
|
||||||
dynamic-align
|
|
||||||
.disabled=${!this._dashboards.length}
|
.disabled=${!this._dashboards.length}
|
||||||
|
.value=${this.hass.defaultPanel}
|
||||||
|
@selected=${this._dashboardChanged}
|
||||||
>
|
>
|
||||||
<paper-listbox
|
<mwc-list-item value="lovelace">
|
||||||
slot="dropdown-content"
|
${this.hass.localize(
|
||||||
.selected=${this.hass.defaultPanel}
|
"ui.panel.profile.dashboard.default_dashboard_label"
|
||||||
@iron-select=${this._dashboardChanged}
|
)}
|
||||||
attr-for-selected="url-path"
|
</mwc-list-item>
|
||||||
>
|
${this._dashboards.map((dashboard) => {
|
||||||
<paper-item url-path="lovelace"
|
if (!this.hass.user!.is_admin && dashboard.require_admin) {
|
||||||
>${this.hass.localize(
|
return "";
|
||||||
"ui.panel.profile.dashboard.default_dashboard_label"
|
}
|
||||||
)}</paper-item
|
return html`
|
||||||
>
|
<mwc-list-item .value=${dashboard.url_path}>
|
||||||
${this._dashboards.map((dashboard) => {
|
${dashboard.title}
|
||||||
if (!this.hass.user!.is_admin && dashboard.require_admin) {
|
</mwc-list-item>
|
||||||
return "";
|
`;
|
||||||
}
|
})}
|
||||||
return html`
|
</mwc-select>
|
||||||
<paper-item url-path=${dashboard.url_path}
|
|
||||||
>${dashboard.title}</paper-item
|
|
||||||
>
|
|
||||||
`;
|
|
||||||
})}
|
|
||||||
</paper-listbox>
|
|
||||||
</ha-paper-dropdown-menu>
|
|
||||||
</ha-settings-row>
|
</ha-settings-row>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
@@ -68,8 +61,8 @@ class HaPickDashboardRow extends LitElement {
|
|||||||
this._dashboards = await fetchDashboards(this.hass);
|
this._dashboards = await fetchDashboards(this.hass);
|
||||||
}
|
}
|
||||||
|
|
||||||
private _dashboardChanged(ev: CustomEvent) {
|
private _dashboardChanged(ev) {
|
||||||
const urlPath = ev.detail.item.getAttribute("url-path");
|
const urlPath = ev.target.value;
|
||||||
if (!urlPath || urlPath === this.hass.defaultPanel) {
|
if (!urlPath || urlPath === this.hass.defaultPanel) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@@ -1,111 +0,0 @@
|
|||||||
import "@polymer/paper-item/paper-item";
|
|
||||||
import "@polymer/paper-listbox/paper-listbox";
|
|
||||||
import { html } from "@polymer/polymer/lib/utils/html-tag";
|
|
||||||
/* eslint-plugin-disable lit */
|
|
||||||
import { PolymerElement } from "@polymer/polymer/polymer-element";
|
|
||||||
import "../../components/ha-paper-dropdown-menu";
|
|
||||||
import "../../components/ha-settings-row";
|
|
||||||
import { EventsMixin } from "../../mixins/events-mixin";
|
|
||||||
import LocalizeMixin from "../../mixins/localize-mixin";
|
|
||||||
|
|
||||||
/*
|
|
||||||
* @appliesMixin LocalizeMixin
|
|
||||||
* @appliesMixin EventsMixin
|
|
||||||
*/
|
|
||||||
class HaPickLanguageRow extends LocalizeMixin(EventsMixin(PolymerElement)) {
|
|
||||||
static get template() {
|
|
||||||
return html`
|
|
||||||
<style>
|
|
||||||
a {
|
|
||||||
color: var(--primary-color);
|
|
||||||
}
|
|
||||||
paper-item {
|
|
||||||
direction: ltr;
|
|
||||||
}
|
|
||||||
paper-item[is-rtl] {
|
|
||||||
direction: rtl;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
<ha-settings-row narrow="[[narrow]]">
|
|
||||||
<span slot="heading"
|
|
||||||
>[[localize('ui.panel.profile.language.header')]]</span
|
|
||||||
>
|
|
||||||
<span slot="description">
|
|
||||||
<a
|
|
||||||
href="https://developers.home-assistant.io/docs/en/internationalization_translation.html"
|
|
||||||
target="_blank"
|
|
||||||
rel="noreferrer"
|
|
||||||
>[[localize('ui.panel.profile.language.link_promo')]]</a
|
|
||||||
>
|
|
||||||
</span>
|
|
||||||
<ha-paper-dropdown-menu
|
|
||||||
label="[[localize('ui.panel.profile.language.dropdown_label')]]"
|
|
||||||
dynamic-align=""
|
|
||||||
>
|
|
||||||
<paper-listbox
|
|
||||||
slot="dropdown-content"
|
|
||||||
attr-for-selected="language-tag"
|
|
||||||
selected="{{languageSelection}}"
|
|
||||||
>
|
|
||||||
<template is="dom-repeat" items="[[languages]]">
|
|
||||||
<paper-item language-tag$="[[item.key]]" is-rtl$="[[item.isRTL]]">
|
|
||||||
[[item.nativeName]]
|
|
||||||
</paper-item>
|
|
||||||
</template>
|
|
||||||
</paper-listbox>
|
|
||||||
</ha-paper-dropdown-menu>
|
|
||||||
</ha-settings-row>
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
|
|
||||||
static get properties() {
|
|
||||||
return {
|
|
||||||
hass: Object,
|
|
||||||
narrow: Boolean,
|
|
||||||
languageSelection: {
|
|
||||||
type: String,
|
|
||||||
observer: "languageSelectionChanged",
|
|
||||||
},
|
|
||||||
languages: {
|
|
||||||
type: Array,
|
|
||||||
computed: "computeLanguages(hass)",
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
static get observers() {
|
|
||||||
return ["setLanguageSelection(language)"];
|
|
||||||
}
|
|
||||||
|
|
||||||
computeLanguages(hass) {
|
|
||||||
if (!hass || !hass.translationMetadata) {
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
const translations = hass.translationMetadata.translations;
|
|
||||||
return Object.keys(translations).map((key) => ({
|
|
||||||
key,
|
|
||||||
...translations[key],
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
setLanguageSelection(language) {
|
|
||||||
this.languageSelection = language;
|
|
||||||
}
|
|
||||||
|
|
||||||
languageSelectionChanged(newVal) {
|
|
||||||
// Only fire event if language was changed. This prevents select updates when
|
|
||||||
// responding to hass changes.
|
|
||||||
if (newVal !== this.hass.language) {
|
|
||||||
this.fire("hass-language-select", newVal);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ready() {
|
|
||||||
super.ready();
|
|
||||||
if (this.hass && this.hass.locale && this.hass.locale.language) {
|
|
||||||
this.setLanguageSelection(this.hass.locale.language);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
customElements.define("ha-pick-language-row", HaPickLanguageRow);
|
|
87
src/panels/profile/ha-pick-language-row.ts
Normal file
87
src/panels/profile/ha-pick-language-row.ts
Normal file
@@ -0,0 +1,87 @@
|
|||||||
|
import { css, html, LitElement, PropertyValues } from "lit";
|
||||||
|
import { customElement, property, state } from "lit/decorators";
|
||||||
|
import { fireEvent } from "../../common/dom/fire_event";
|
||||||
|
import "../../components/ha-settings-row";
|
||||||
|
import { HomeAssistant, Translation } from "../../types";
|
||||||
|
import "@material/mwc-select/mwc-select";
|
||||||
|
import "@material/mwc-list/mwc-list-item";
|
||||||
|
|
||||||
|
@customElement("ha-pick-language-row")
|
||||||
|
export class HaPickLanguageRow extends LitElement {
|
||||||
|
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||||
|
|
||||||
|
@property() public narrow!: boolean;
|
||||||
|
|
||||||
|
@state() private _languages: (Translation & { key: string })[] = [];
|
||||||
|
|
||||||
|
protected firstUpdated(changedProps: PropertyValues) {
|
||||||
|
super.firstUpdated(changedProps);
|
||||||
|
this._computeLanguages();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected render() {
|
||||||
|
return html`
|
||||||
|
<ha-settings-row .narrow=${this.narrow}>
|
||||||
|
<span slot="heading"
|
||||||
|
>${this.hass.localize("ui.panel.profile.language.header")}</span
|
||||||
|
>
|
||||||
|
<span slot="description">
|
||||||
|
<a
|
||||||
|
href="https://developers.home-assistant.io/docs/en/internationalization_translation.html"
|
||||||
|
target="_blank"
|
||||||
|
rel="noreferrer"
|
||||||
|
>${this.hass.localize("ui.panel.profile.language.link_promo")}</a
|
||||||
|
>
|
||||||
|
</span>
|
||||||
|
<mwc-select
|
||||||
|
.label=${this.hass.localize(
|
||||||
|
"ui.panel.profile.language.dropdown_label"
|
||||||
|
)}
|
||||||
|
.value=${this.hass.locale.language}
|
||||||
|
@selected=${this._languageSelectionChanged}
|
||||||
|
>
|
||||||
|
${this._languages.map(
|
||||||
|
(language) => html`<mwc-list-item
|
||||||
|
.value=${language.key}
|
||||||
|
rtl=${language.isRTL}
|
||||||
|
>
|
||||||
|
${language.nativeName}
|
||||||
|
</mwc-list-item>`
|
||||||
|
)}
|
||||||
|
</mwc-select>
|
||||||
|
</ha-settings-row>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _computeLanguages() {
|
||||||
|
if (!this.hass.translationMetadata?.translations) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this._languages = Object.keys(
|
||||||
|
this.hass.translationMetadata.translations
|
||||||
|
).map((key) => ({
|
||||||
|
key,
|
||||||
|
...this.hass.translationMetadata.translations[key],
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
private _languageSelectionChanged(ev) {
|
||||||
|
// Only fire event if language was changed. This prevents select updates when
|
||||||
|
// responding to hass changes.
|
||||||
|
if (ev.target.value !== this.hass.language) {
|
||||||
|
fireEvent(this, "hass-language-select", ev.target.value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static styles = css`
|
||||||
|
a {
|
||||||
|
color: var(--primary-color);
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface HTMLElementTagNameMap {
|
||||||
|
"ha-pick-language-row": HaPickLanguageRow;
|
||||||
|
}
|
||||||
|
}
|
@@ -1,11 +1,10 @@
|
|||||||
import "@polymer/paper-item/paper-item";
|
import "@material/mwc-list/mwc-list-item";
|
||||||
import "@polymer/paper-listbox/paper-listbox";
|
import "@material/mwc-select/mwc-select";
|
||||||
import { html, LitElement, TemplateResult } from "lit";
|
import { html, LitElement, TemplateResult } from "lit";
|
||||||
import { customElement, property } from "lit/decorators";
|
import { customElement, property } from "lit/decorators";
|
||||||
import { fireEvent } from "../../common/dom/fire_event";
|
import { fireEvent } from "../../common/dom/fire_event";
|
||||||
import { formatNumber } from "../../common/number/format_number";
|
import { formatNumber } from "../../common/number/format_number";
|
||||||
import "../../components/ha-card";
|
import "../../components/ha-card";
|
||||||
import "../../components/ha-paper-dropdown-menu";
|
|
||||||
import "../../components/ha-settings-row";
|
import "../../components/ha-settings-row";
|
||||||
import { NumberFormat } from "../../data/translation";
|
import { NumberFormat } from "../../data/translation";
|
||||||
import { HomeAssistant } from "../../types";
|
import { HomeAssistant } from "../../types";
|
||||||
@@ -25,47 +24,39 @@ class NumberFormatRow extends LitElement {
|
|||||||
<span slot="description">
|
<span slot="description">
|
||||||
${this.hass.localize("ui.panel.profile.number_format.description")}
|
${this.hass.localize("ui.panel.profile.number_format.description")}
|
||||||
</span>
|
</span>
|
||||||
<ha-paper-dropdown-menu
|
<mwc-select
|
||||||
label=${this.hass.localize(
|
.label=${this.hass.localize(
|
||||||
"ui.panel.profile.number_format.dropdown_label"
|
"ui.panel.profile.number_format.dropdown_label"
|
||||||
)}
|
)}
|
||||||
dynamic-align
|
|
||||||
.disabled=${this.hass.locale === undefined}
|
.disabled=${this.hass.locale === undefined}
|
||||||
|
.value=${this.hass.locale.number_format}
|
||||||
|
@selected=${this._handleFormatSelection}
|
||||||
>
|
>
|
||||||
<paper-listbox
|
${Object.values(NumberFormat).map((format) => {
|
||||||
slot="dropdown-content"
|
const formattedNumber = formatNumber(1234567.89, {
|
||||||
.selected=${this.hass.locale.number_format}
|
...this.hass.locale,
|
||||||
@iron-select=${this._handleFormatSelection}
|
number_format: format,
|
||||||
attr-for-selected="format"
|
});
|
||||||
>
|
const value = this.hass.localize(
|
||||||
${Object.values(NumberFormat).map((format) => {
|
`ui.panel.profile.number_format.formats.${format}`
|
||||||
const formattedNumber = formatNumber(1234567.89, {
|
);
|
||||||
...this.hass.locale,
|
const twoLine = value.slice(value.length - 2) !== "89"; // Display explicit number formats on one line
|
||||||
number_format: format,
|
return html`
|
||||||
});
|
<mwc-list-item .value=${format} .twoline=${twoLine}>
|
||||||
const value = this.hass.localize(
|
<span>${value}</span>
|
||||||
`ui.panel.profile.number_format.formats.${format}`
|
${twoLine
|
||||||
);
|
? html`<span slot="secondary">${formattedNumber}</span>`
|
||||||
const twoLine = value.slice(value.length - 2) !== "89"; // Display explicit number formats on one line
|
: ""}
|
||||||
return html`
|
</mwc-list-item>
|
||||||
<paper-item .format=${format} .label=${value}>
|
`;
|
||||||
<paper-item-body ?two-line=${twoLine}>
|
})}
|
||||||
<div>${value}</div>
|
</mwc-select>
|
||||||
${twoLine
|
|
||||||
? html`<div secondary>${formattedNumber}</div>`
|
|
||||||
: ""}
|
|
||||||
</paper-item-body>
|
|
||||||
</paper-item>
|
|
||||||
`;
|
|
||||||
})}
|
|
||||||
</paper-listbox>
|
|
||||||
</ha-paper-dropdown-menu>
|
|
||||||
</ha-settings-row>
|
</ha-settings-row>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async _handleFormatSelection(ev: CustomEvent) {
|
private async _handleFormatSelection(ev) {
|
||||||
fireEvent(this, "hass-number-format-select", ev.detail.item.format);
|
fireEvent(this, "hass-number-format-select", ev.target.value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -1,7 +1,5 @@
|
|||||||
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 "@polymer/paper-item/paper-item";
|
|
||||||
import "@polymer/paper-listbox/paper-listbox";
|
|
||||||
import {
|
import {
|
||||||
css,
|
css,
|
||||||
CSSResultGroup,
|
CSSResultGroup,
|
||||||
@@ -13,7 +11,6 @@ import {
|
|||||||
import { customElement, property, state } from "lit/decorators";
|
import { customElement, property, state } from "lit/decorators";
|
||||||
import { fireEvent } from "../../common/dom/fire_event";
|
import { fireEvent } from "../../common/dom/fire_event";
|
||||||
import "../../components/ha-formfield";
|
import "../../components/ha-formfield";
|
||||||
import "../../components/ha-paper-dropdown-menu";
|
|
||||||
import "../../components/ha-radio";
|
import "../../components/ha-radio";
|
||||||
import type { HaRadio } from "../../components/ha-radio";
|
import type { HaRadio } from "../../components/ha-radio";
|
||||||
import "../../components/ha-settings-row";
|
import "../../components/ha-settings-row";
|
||||||
@@ -23,6 +20,8 @@ import {
|
|||||||
} from "../../resources/ha-style";
|
} from "../../resources/ha-style";
|
||||||
import { HomeAssistant } from "../../types";
|
import { HomeAssistant } from "../../types";
|
||||||
import { documentationUrl } from "../../util/documentation-url";
|
import { documentationUrl } from "../../util/documentation-url";
|
||||||
|
import "@material/mwc-select/mwc-select";
|
||||||
|
import "@material/mwc-list/mwc-list-item";
|
||||||
|
|
||||||
@customElement("ha-pick-theme-row")
|
@customElement("ha-pick-theme-row")
|
||||||
export class HaPickThemeRow extends LitElement {
|
export class HaPickThemeRow extends LitElement {
|
||||||
@@ -63,22 +62,17 @@ export class HaPickThemeRow extends LitElement {
|
|||||||
${this.hass.localize("ui.panel.profile.themes.link_promo")}
|
${this.hass.localize("ui.panel.profile.themes.link_promo")}
|
||||||
</a>
|
</a>
|
||||||
</span>
|
</span>
|
||||||
<ha-paper-dropdown-menu
|
<mwc-select
|
||||||
.label=${this.hass.localize("ui.panel.profile.themes.dropdown_label")}
|
.label=${this.hass.localize("ui.panel.profile.themes.dropdown_label")}
|
||||||
dynamic-align
|
|
||||||
.disabled=${!hasThemes}
|
.disabled=${!hasThemes}
|
||||||
|
.value=${this.hass.selectedTheme?.theme || "Backend-selected"}
|
||||||
|
@selected=${this._handleThemeSelection}
|
||||||
>
|
>
|
||||||
<paper-listbox
|
${this._themeNames.map(
|
||||||
slot="dropdown-content"
|
(theme) =>
|
||||||
.selected=${this.hass.selectedTheme?.theme || "Backend-selected"}
|
html`<mwc-list-item .value=${theme}>${theme}</mwc-list-item>`
|
||||||
attr-for-selected="theme"
|
)}
|
||||||
@iron-select=${this._handleThemeSelection}
|
</mwc-select>
|
||||||
>
|
|
||||||
${this._themeNames.map(
|
|
||||||
(theme) => html`<paper-item .theme=${theme}>${theme}</paper-item>`
|
|
||||||
)}
|
|
||||||
</paper-listbox>
|
|
||||||
</ha-paper-dropdown-menu>
|
|
||||||
</ha-settings-row>
|
</ha-settings-row>
|
||||||
${curTheme === "default" || this._supportsModeSelection(curTheme)
|
${curTheme === "default" || this._supportsModeSelection(curTheme)
|
||||||
? html` <div class="inputs">
|
? html` <div class="inputs">
|
||||||
@@ -91,7 +85,7 @@ export class HaPickThemeRow extends LitElement {
|
|||||||
@change=${this._handleDarkMode}
|
@change=${this._handleDarkMode}
|
||||||
name="dark_mode"
|
name="dark_mode"
|
||||||
value="auto"
|
value="auto"
|
||||||
?checked=${themeSettings?.dark === undefined}
|
.checked=${themeSettings?.dark === undefined}
|
||||||
></ha-radio>
|
></ha-radio>
|
||||||
</ha-formfield>
|
</ha-formfield>
|
||||||
<ha-formfield
|
<ha-formfield
|
||||||
@@ -103,7 +97,7 @@ export class HaPickThemeRow extends LitElement {
|
|||||||
@change=${this._handleDarkMode}
|
@change=${this._handleDarkMode}
|
||||||
name="dark_mode"
|
name="dark_mode"
|
||||||
value="light"
|
value="light"
|
||||||
?checked=${themeSettings?.dark === false}
|
.checked=${themeSettings?.dark === false}
|
||||||
>
|
>
|
||||||
</ha-radio>
|
</ha-radio>
|
||||||
</ha-formfield>
|
</ha-formfield>
|
||||||
@@ -116,7 +110,7 @@ export class HaPickThemeRow extends LitElement {
|
|||||||
@change=${this._handleDarkMode}
|
@change=${this._handleDarkMode}
|
||||||
name="dark_mode"
|
name="dark_mode"
|
||||||
value="dark"
|
value="dark"
|
||||||
?checked=${themeSettings?.dark === true}
|
.checked=${themeSettings?.dark === true}
|
||||||
>
|
>
|
||||||
</ha-radio>
|
</ha-radio>
|
||||||
</ha-formfield>
|
</ha-formfield>
|
||||||
@@ -195,8 +189,8 @@ export class HaPickThemeRow extends LitElement {
|
|||||||
fireEvent(this, "settheme", { dark });
|
fireEvent(this, "settheme", { dark });
|
||||||
}
|
}
|
||||||
|
|
||||||
private _handleThemeSelection(ev: CustomEvent) {
|
private _handleThemeSelection(ev) {
|
||||||
const theme = ev.detail.item.theme;
|
const theme = ev.target.value;
|
||||||
if (theme === "Backend-selected") {
|
if (theme === "Backend-selected") {
|
||||||
if (this.hass.selectedTheme?.theme) {
|
if (this.hass.selectedTheme?.theme) {
|
||||||
fireEvent(this, "settheme", {
|
fireEvent(this, "settheme", {
|
||||||
|
@@ -1,14 +1,13 @@
|
|||||||
import "@polymer/paper-item/paper-item";
|
|
||||||
import "@polymer/paper-listbox/paper-listbox";
|
|
||||||
import { html, LitElement, TemplateResult } from "lit";
|
import { html, LitElement, TemplateResult } from "lit";
|
||||||
import { customElement, property } from "lit/decorators";
|
import { customElement, property } from "lit/decorators";
|
||||||
import { formatTime } from "../../common/datetime/format_time";
|
import { formatTime } from "../../common/datetime/format_time";
|
||||||
import { fireEvent } from "../../common/dom/fire_event";
|
import { fireEvent } from "../../common/dom/fire_event";
|
||||||
import "../../components/ha-card";
|
import "../../components/ha-card";
|
||||||
import "../../components/ha-paper-dropdown-menu";
|
|
||||||
import "../../components/ha-settings-row";
|
import "../../components/ha-settings-row";
|
||||||
import { TimeFormat } from "../../data/translation";
|
import { TimeFormat } from "../../data/translation";
|
||||||
import { HomeAssistant } from "../../types";
|
import { HomeAssistant } from "../../types";
|
||||||
|
import "@material/mwc-list/mwc-list-item";
|
||||||
|
import "@material/mwc-select/mwc-select";
|
||||||
|
|
||||||
@customElement("ha-pick-time-format-row")
|
@customElement("ha-pick-time-format-row")
|
||||||
class TimeFormatRow extends LitElement {
|
class TimeFormatRow extends LitElement {
|
||||||
@@ -26,42 +25,34 @@ class TimeFormatRow extends LitElement {
|
|||||||
<span slot="description">
|
<span slot="description">
|
||||||
${this.hass.localize("ui.panel.profile.time_format.description")}
|
${this.hass.localize("ui.panel.profile.time_format.description")}
|
||||||
</span>
|
</span>
|
||||||
<ha-paper-dropdown-menu
|
<mwc-select
|
||||||
.label=${this.hass.localize(
|
.label=${this.hass.localize(
|
||||||
"ui.panel.profile.time_format.dropdown_label"
|
"ui.panel.profile.time_format.dropdown_label"
|
||||||
)}
|
)}
|
||||||
dynamic-align
|
|
||||||
.disabled=${this.hass.locale === undefined}
|
.disabled=${this.hass.locale === undefined}
|
||||||
|
.value=${this.hass.locale.time_format}
|
||||||
|
@selected=${this._handleFormatSelection}
|
||||||
>
|
>
|
||||||
<paper-listbox
|
${Object.values(TimeFormat).map((format) => {
|
||||||
slot="dropdown-content"
|
const formattedTime = formatTime(date, {
|
||||||
.selected=${this.hass.locale.time_format}
|
...this.hass.locale,
|
||||||
@iron-select=${this._handleFormatSelection}
|
time_format: format,
|
||||||
attr-for-selected="format"
|
});
|
||||||
>
|
const value = this.hass.localize(
|
||||||
${Object.values(TimeFormat).map((format) => {
|
`ui.panel.profile.time_format.formats.${format}`
|
||||||
const formattedTime = formatTime(date, {
|
);
|
||||||
...this.hass.locale,
|
return html`<mwc-list-item .value=${format} twoline>
|
||||||
time_format: format,
|
<span>${value}</span>
|
||||||
});
|
<span slot="secondary">${formattedTime}</span>
|
||||||
const value = this.hass.localize(
|
</mwc-list-item>`;
|
||||||
`ui.panel.profile.time_format.formats.${format}`
|
})}
|
||||||
);
|
</mwc-select>
|
||||||
return html` <paper-item .format=${format} .label=${value}>
|
|
||||||
<paper-item-body two-line>
|
|
||||||
<div>${value}</div>
|
|
||||||
<div secondary>${formattedTime}</div>
|
|
||||||
</paper-item-body>
|
|
||||||
</paper-item>`;
|
|
||||||
})}
|
|
||||||
</paper-listbox>
|
|
||||||
</ha-paper-dropdown-menu>
|
|
||||||
</ha-settings-row>
|
</ha-settings-row>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async _handleFormatSelection(ev: CustomEvent) {
|
private async _handleFormatSelection(ev) {
|
||||||
fireEvent(this, "hass-time-format-select", ev.detail.item.format);
|
fireEvent(this, "hass-time-format-select", ev.target.value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -525,8 +525,10 @@
|
|||||||
"no_media_folder": "It looks like you have not yet created a media directory.",
|
"no_media_folder": "It looks like you have not yet created a media directory.",
|
||||||
"setup_local_help": "Check the {documentation} on how to setup local media.",
|
"setup_local_help": "Check the {documentation} on how to setup local media.",
|
||||||
"file_management": {
|
"file_management": {
|
||||||
|
"highlight_button": "Click here to upload your first media",
|
||||||
"upload_failed": "Upload failed: {reason}",
|
"upload_failed": "Upload failed: {reason}",
|
||||||
"add_media": "Add Media"
|
"add_media": "Add Media",
|
||||||
|
"uploading": "Uploading {count} {count, plural,\n one {file}\n other {files}\n}"
|
||||||
},
|
},
|
||||||
"class": {
|
"class": {
|
||||||
"album": "Album",
|
"album": "Album",
|
||||||
@@ -2478,6 +2480,8 @@
|
|||||||
"rename_dialog": "Edit the name of this config entry",
|
"rename_dialog": "Edit the name of this config entry",
|
||||||
"rename_input_label": "Entry name",
|
"rename_input_label": "Entry name",
|
||||||
"search": "Search integrations",
|
"search": "Search integrations",
|
||||||
|
"add_zwave_js_device": "Add Z-Wave device",
|
||||||
|
"add_zha_device": "Add Zigbee device",
|
||||||
"disable": {
|
"disable": {
|
||||||
"show_disabled": "Show disabled integrations",
|
"show_disabled": "Show disabled integrations",
|
||||||
"disabled_integrations": "{number} disabled",
|
"disabled_integrations": "{number} disabled",
|
||||||
@@ -3687,6 +3691,18 @@
|
|||||||
"media-browser": {
|
"media-browser": {
|
||||||
"error": {
|
"error": {
|
||||||
"player_not_exist": "Media player {name} does not exist"
|
"player_not_exist": "Media player {name} does not exist"
|
||||||
|
},
|
||||||
|
"tts": {
|
||||||
|
"message": "Message",
|
||||||
|
"example_message": "Hello {name}, you can play any text on any supported media player!",
|
||||||
|
"language": "Language",
|
||||||
|
"gender": "Gender",
|
||||||
|
"gender_male": "Male",
|
||||||
|
"gender_female": "Female",
|
||||||
|
"action_play": "Say",
|
||||||
|
"action_pick": "Select",
|
||||||
|
"set_as_default": "Set as default options",
|
||||||
|
"faild_to_store_defaults": "Failed to store defaults: {error}"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"map": {
|
"map": {
|
||||||
|
@@ -10,6 +10,10 @@ describe("canToggleState", () => {
|
|||||||
turn_off: null,
|
turn_off: null,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
states: {
|
||||||
|
"light.bla": { entity_id: "light.bla" },
|
||||||
|
"light.test": { entity_id: "light.test" },
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
it("Detects lights toggle", () => {
|
it("Detects lights toggle", () => {
|
||||||
@@ -24,7 +28,11 @@ describe("canToggleState", () => {
|
|||||||
const stateObj: any = {
|
const stateObj: any = {
|
||||||
entity_id: "group.bla",
|
entity_id: "group.bla",
|
||||||
state: "on",
|
state: "on",
|
||||||
|
attributes: {
|
||||||
|
entity_id: ["light.bla", "light.test"],
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
assert.isTrue(canToggleState(hass, stateObj));
|
assert.isTrue(canToggleState(hass, stateObj));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user