mirror of
https://github.com/home-assistant/frontend.git
synced 2025-09-14 15:39:46 +00:00
Compare commits
40 Commits
allow-part
...
background
Author | SHA1 | Date | |
---|---|---|---|
![]() |
536fe6a23c | ||
![]() |
d8ddbefcf6 | ||
![]() |
0364c5e493 | ||
![]() |
d1ad72c6ff | ||
![]() |
723b3844ac | ||
![]() |
54f8d33b1f | ||
![]() |
4c702ac7c2 | ||
![]() |
c7b3c4df27 | ||
![]() |
56cc4e8d4d | ||
![]() |
0e8c280763 | ||
![]() |
ff715c6cb7 | ||
![]() |
81ebdf1448 | ||
![]() |
c640d9edf2 | ||
![]() |
6d29b1cfb8 | ||
![]() |
e784205e1e | ||
![]() |
1596578f96 | ||
![]() |
28304bb1dc | ||
![]() |
32bbc9421b | ||
![]() |
6e35f841cc | ||
![]() |
99e6547807 | ||
![]() |
9764a0f23f | ||
![]() |
12918580ac | ||
![]() |
61a7652ae1 | ||
![]() |
c885d08a32 | ||
![]() |
6068d5e5cd | ||
![]() |
890be2c177 | ||
![]() |
423709dd23 | ||
![]() |
4b73baa098 | ||
![]() |
dba16edabc | ||
![]() |
975f371ba8 | ||
![]() |
36b2a1bca3 | ||
![]() |
fdf36adc3c | ||
![]() |
b506791535 | ||
![]() |
206574eb9f | ||
![]() |
19ab29d130 | ||
![]() |
f61f0e4e52 | ||
![]() |
a32e6a9ac9 | ||
![]() |
d7b8823234 | ||
![]() |
d9b0d5765a | ||
![]() |
6eb3fb1076 |
@@ -8,6 +8,7 @@ const gulp = require("gulp");
|
|||||||
const jszip = require("jszip");
|
const jszip = require("jszip");
|
||||||
const tar = require("tar");
|
const tar = require("tar");
|
||||||
const { Octokit } = require("@octokit/rest");
|
const { Octokit } = require("@octokit/rest");
|
||||||
|
const { retry } = require("@octokit/plugin-retry");
|
||||||
const { createOAuthDeviceAuth } = require("@octokit/auth-oauth-device");
|
const { createOAuthDeviceAuth } = require("@octokit/auth-oauth-device");
|
||||||
|
|
||||||
const MAX_AGE = 24; // hours
|
const MAX_AGE = 24; // hours
|
||||||
@@ -95,7 +96,7 @@ gulp.task("fetch-nightly-translations", async function () {
|
|||||||
|
|
||||||
// Authenticate with token and request workflow runs from GitHub
|
// Authenticate with token and request workflow runs from GitHub
|
||||||
console.log("Fetching new translations...");
|
console.log("Fetching new translations...");
|
||||||
const octokit = new Octokit({
|
const octokit = new (Octokit.plugin(retry))({
|
||||||
userAgent: "Fetch Nightly Translations",
|
userAgent: "Fetch Nightly Translations",
|
||||||
auth: tokenAuth.token,
|
auth: tokenAuth.token,
|
||||||
});
|
});
|
||||||
|
@@ -92,11 +92,7 @@ export class HassioAddonStore extends LitElement {
|
|||||||
.route=${this.route}
|
.route=${this.route}
|
||||||
.header=${this.supervisor.localize("panel.store")}
|
.header=${this.supervisor.localize("panel.store")}
|
||||||
>
|
>
|
||||||
<ha-button-menu
|
<ha-button-menu slot="toolbar-icon" @action=${this._handleAction}>
|
||||||
corner="BOTTOM_START"
|
|
||||||
slot="toolbar-icon"
|
|
||||||
@action=${this._handleAction}
|
|
||||||
>
|
|
||||||
<ha-icon-button
|
<ha-icon-button
|
||||||
.label=${this.supervisor.localize("common.menu")}
|
.label=${this.supervisor.localize("common.menu")}
|
||||||
.path=${mdiDotsVertical}
|
.path=${mdiDotsVertical}
|
||||||
|
@@ -168,7 +168,7 @@ class HassioAddonConfig extends LitElement {
|
|||||||
${this.supervisor.localize("addon.configuration.options.header")}
|
${this.supervisor.localize("addon.configuration.options.header")}
|
||||||
</h2>
|
</h2>
|
||||||
<div class="card-menu">
|
<div class="card-menu">
|
||||||
<ha-button-menu corner="BOTTOM_START" @action=${this._handleAction}>
|
<ha-button-menu @action=${this._handleAction}>
|
||||||
<ha-icon-button
|
<ha-icon-button
|
||||||
.label=${this.supervisor.localize("common.menu")}
|
.label=${this.supervisor.localize("common.menu")}
|
||||||
.path=${mdiDotsVertical}
|
.path=${mdiDotsVertical}
|
||||||
|
@@ -195,11 +195,7 @@ export class HassioBackups extends LitElement {
|
|||||||
: "/config"}
|
: "/config"}
|
||||||
supervisor
|
supervisor
|
||||||
>
|
>
|
||||||
<ha-button-menu
|
<ha-button-menu slot="toolbar-icon" @action=${this._handleAction}>
|
||||||
corner="BOTTOM_START"
|
|
||||||
slot="toolbar-icon"
|
|
||||||
@action=${this._handleAction}
|
|
||||||
>
|
|
||||||
<ha-icon-button
|
<ha-icon-button
|
||||||
.label=${this.supervisor?.localize("common.menu")}
|
.label=${this.supervisor?.localize("common.menu")}
|
||||||
.path=${mdiDotsVertical}
|
.path=${mdiDotsVertical}
|
||||||
|
@@ -184,7 +184,7 @@ class HassioHostInfo extends LitElement {
|
|||||||
`
|
`
|
||||||
: ""}
|
: ""}
|
||||||
|
|
||||||
<ha-button-menu corner="BOTTOM_START">
|
<ha-button-menu>
|
||||||
<ha-icon-button
|
<ha-icon-button
|
||||||
.label=${this.supervisor.localize("common.menu")}
|
.label=${this.supervisor.localize("common.menu")}
|
||||||
.path=${mdiDotsVertical}
|
.path=${mdiDotsVertical}
|
||||||
|
13
package.json
13
package.json
@@ -96,8 +96,8 @@
|
|||||||
"@vibrant/core": "3.2.1-alpha.1",
|
"@vibrant/core": "3.2.1-alpha.1",
|
||||||
"@vibrant/quantizer-mmcq": "3.2.1-alpha.1",
|
"@vibrant/quantizer-mmcq": "3.2.1-alpha.1",
|
||||||
"@vue/web-component-wrapper": "1.3.0",
|
"@vue/web-component-wrapper": "1.3.0",
|
||||||
"@webcomponents/scoped-custom-element-registry": "0.0.8",
|
"@webcomponents/scoped-custom-element-registry": "0.0.9",
|
||||||
"@webcomponents/webcomponentsjs": "2.7.0",
|
"@webcomponents/webcomponentsjs": "2.8.0",
|
||||||
"app-datepicker": "5.1.1",
|
"app-datepicker": "5.1.1",
|
||||||
"chart.js": "3.3.2",
|
"chart.js": "3.3.2",
|
||||||
"comlink": "4.4.1",
|
"comlink": "4.4.1",
|
||||||
@@ -148,7 +148,7 @@
|
|||||||
"xss": "1.0.14"
|
"xss": "1.0.14"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/core": "7.21.3",
|
"@babel/core": "7.21.4",
|
||||||
"@babel/plugin-external-helpers": "7.18.6",
|
"@babel/plugin-external-helpers": "7.18.6",
|
||||||
"@babel/plugin-proposal-class-properties": "7.18.6",
|
"@babel/plugin-proposal-class-properties": "7.18.6",
|
||||||
"@babel/plugin-proposal-decorators": "7.21.0",
|
"@babel/plugin-proposal-decorators": "7.21.0",
|
||||||
@@ -158,10 +158,11 @@
|
|||||||
"@babel/plugin-syntax-dynamic-import": "7.8.3",
|
"@babel/plugin-syntax-dynamic-import": "7.8.3",
|
||||||
"@babel/plugin-syntax-import-meta": "7.10.4",
|
"@babel/plugin-syntax-import-meta": "7.10.4",
|
||||||
"@babel/plugin-syntax-top-level-await": "7.14.5",
|
"@babel/plugin-syntax-top-level-await": "7.14.5",
|
||||||
"@babel/preset-env": "7.20.2",
|
"@babel/preset-env": "7.21.4",
|
||||||
"@babel/preset-typescript": "7.21.0",
|
"@babel/preset-typescript": "7.21.4",
|
||||||
"@koa/cors": "4.0.0",
|
"@koa/cors": "4.0.0",
|
||||||
"@octokit/auth-oauth-device": "4.0.4",
|
"@octokit/auth-oauth-device": "4.0.4",
|
||||||
|
"@octokit/plugin-retry": "4.1.3",
|
||||||
"@octokit/rest": "19.0.7",
|
"@octokit/rest": "19.0.7",
|
||||||
"@open-wc/dev-server-hmr": "0.1.4",
|
"@open-wc/dev-server-hmr": "0.1.4",
|
||||||
"@rollup/plugin-babel": "6.0.3",
|
"@rollup/plugin-babel": "6.0.3",
|
||||||
@@ -244,7 +245,7 @@
|
|||||||
"vinyl-source-stream": "2.0.0",
|
"vinyl-source-stream": "2.0.0",
|
||||||
"webpack": "=5.72.1",
|
"webpack": "=5.72.1",
|
||||||
"webpack-cli": "5.0.1",
|
"webpack-cli": "5.0.1",
|
||||||
"webpack-dev-server": "4.13.1",
|
"webpack-dev-server": "4.13.2",
|
||||||
"webpack-manifest-plugin": "5.0.0",
|
"webpack-manifest-plugin": "5.0.0",
|
||||||
"webpackbar": "5.0.2",
|
"webpackbar": "5.0.2",
|
||||||
"workbox-build": "6.5.4"
|
"workbox-build": "6.5.4"
|
||||||
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|||||||
|
|
||||||
[project]
|
[project]
|
||||||
name = "home-assistant-frontend"
|
name = "home-assistant-frontend"
|
||||||
version = "20230401.0"
|
version = "20230406.0"
|
||||||
license = {text = "Apache-2.0"}
|
license = {text = "Apache-2.0"}
|
||||||
description = "The Home Assistant frontend"
|
description = "The Home Assistant frontend"
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
|
@@ -1,17 +0,0 @@
|
|||||||
import { refine, string } from "superstruct";
|
|
||||||
|
|
||||||
const isEntityId = (value: string): boolean => value.includes(".");
|
|
||||||
|
|
||||||
export const entityId = () =>
|
|
||||||
refine(string(), "entity ID (domain.entity)", isEntityId);
|
|
||||||
|
|
||||||
const isEntityIdOrAll = (value: string): boolean => {
|
|
||||||
if (value === "all") {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return isEntityId(value);
|
|
||||||
};
|
|
||||||
|
|
||||||
export const entityIdOrAll = () =>
|
|
||||||
refine(string(), "entity ID (domain.entity or all)", isEntityIdOrAll);
|
|
@@ -276,7 +276,11 @@ export default class HaChartBase extends LitElement {
|
|||||||
top: this.chart!.canvas.offsetTop + context.tooltip.caretY + 12 + "px",
|
top: this.chart!.canvas.offsetTop + context.tooltip.caretY + 12 + "px",
|
||||||
left:
|
left:
|
||||||
this.chart!.canvas.offsetLeft +
|
this.chart!.canvas.offsetLeft +
|
||||||
clamp(context.tooltip.caretX, 100, this.clientWidth - 100) -
|
clamp(
|
||||||
|
context.tooltip.caretX,
|
||||||
|
100,
|
||||||
|
this.clientWidth - 100 - this.paddingYAxis
|
||||||
|
) -
|
||||||
100 +
|
100 +
|
||||||
"px",
|
"px",
|
||||||
};
|
};
|
||||||
@@ -302,7 +306,7 @@ export default class HaChartBase extends LitElement {
|
|||||||
return css`
|
return css`
|
||||||
:host {
|
:host {
|
||||||
display: block;
|
display: block;
|
||||||
position: relative;
|
position: var(--chart-base-position, relative);
|
||||||
}
|
}
|
||||||
.chartContainer {
|
.chartContainer {
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
@@ -2,9 +2,9 @@ import "@lrnwebcomponents/simple-tooltip/simple-tooltip";
|
|||||||
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";
|
||||||
|
import { LocalizeFunc } from "../common/translations/localize";
|
||||||
import type { Analytics, AnalyticsPreferences } from "../data/analytics";
|
import type { Analytics, AnalyticsPreferences } from "../data/analytics";
|
||||||
import { haStyle } from "../resources/styles";
|
import { haStyle } from "../resources/styles";
|
||||||
import type { HomeAssistant } from "../types";
|
|
||||||
import "./ha-settings-row";
|
import "./ha-settings-row";
|
||||||
import "./ha-switch";
|
import "./ha-switch";
|
||||||
import type { HaSwitch } from "./ha-switch";
|
import type { HaSwitch } from "./ha-switch";
|
||||||
@@ -19,7 +19,7 @@ declare global {
|
|||||||
|
|
||||||
@customElement("ha-analytics")
|
@customElement("ha-analytics")
|
||||||
export class HaAnalytics extends LitElement {
|
export class HaAnalytics extends LitElement {
|
||||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
@property({ attribute: false }) public localize!: LocalizeFunc;
|
||||||
|
|
||||||
@property({ attribute: false }) public analytics?: Analytics;
|
@property({ attribute: false }) public analytics?: Analytics;
|
||||||
|
|
||||||
@@ -34,12 +34,12 @@ export class HaAnalytics extends LitElement {
|
|||||||
return html`
|
return html`
|
||||||
<ha-settings-row>
|
<ha-settings-row>
|
||||||
<span slot="heading" data-for="base">
|
<span slot="heading" data-for="base">
|
||||||
${this.hass.localize(
|
${this.localize(
|
||||||
`ui.panel.${this.translationKeyPanel}.analytics.preferences.base.title`
|
`ui.panel.${this.translationKeyPanel}.analytics.preferences.base.title`
|
||||||
)}
|
)}
|
||||||
</span>
|
</span>
|
||||||
<span slot="description" data-for="base">
|
<span slot="description" data-for="base">
|
||||||
${this.hass.localize(
|
${this.localize(
|
||||||
`ui.panel.${this.translationKeyPanel}.analytics.preferences.base.description`
|
`ui.panel.${this.translationKeyPanel}.analytics.preferences.base.description`
|
||||||
)}
|
)}
|
||||||
</span>
|
</span>
|
||||||
@@ -57,12 +57,12 @@ export class HaAnalytics extends LitElement {
|
|||||||
html`
|
html`
|
||||||
<ha-settings-row>
|
<ha-settings-row>
|
||||||
<span slot="heading" data-for=${preference}>
|
<span slot="heading" data-for=${preference}>
|
||||||
${this.hass.localize(
|
${this.localize(
|
||||||
`ui.panel.${this.translationKeyPanel}.analytics.preferences.${preference}.title`
|
`ui.panel.${this.translationKeyPanel}.analytics.preferences.${preference}.title`
|
||||||
)}
|
)}
|
||||||
</span>
|
</span>
|
||||||
<span slot="description" data-for=${preference}>
|
<span slot="description" data-for=${preference}>
|
||||||
${this.hass.localize(
|
${this.localize(
|
||||||
`ui.panel.${this.translationKeyPanel}.analytics.preferences.${preference}.description`
|
`ui.panel.${this.translationKeyPanel}.analytics.preferences.${preference}.description`
|
||||||
)}
|
)}
|
||||||
</span>
|
</span>
|
||||||
@@ -77,7 +77,7 @@ export class HaAnalytics extends LitElement {
|
|||||||
${!baseEnabled
|
${!baseEnabled
|
||||||
? html`
|
? html`
|
||||||
<simple-tooltip animation-delay="0" position="right">
|
<simple-tooltip animation-delay="0" position="right">
|
||||||
${this.hass.localize(
|
${this.localize(
|
||||||
`ui.panel.${this.translationKeyPanel}.analytics.need_base_enabled`
|
`ui.panel.${this.translationKeyPanel}.analytics.need_base_enabled`
|
||||||
)}
|
)}
|
||||||
</simple-tooltip>
|
</simple-tooltip>
|
||||||
@@ -89,12 +89,12 @@ export class HaAnalytics extends LitElement {
|
|||||||
)}
|
)}
|
||||||
<ha-settings-row>
|
<ha-settings-row>
|
||||||
<span slot="heading" data-for="diagnostics">
|
<span slot="heading" data-for="diagnostics">
|
||||||
${this.hass.localize(
|
${this.localize(
|
||||||
`ui.panel.${this.translationKeyPanel}.analytics.preferences.diagnostics.title`
|
`ui.panel.${this.translationKeyPanel}.analytics.preferences.diagnostics.title`
|
||||||
)}
|
)}
|
||||||
</span>
|
</span>
|
||||||
<span slot="description" data-for="diagnostics">
|
<span slot="description" data-for="diagnostics">
|
||||||
${this.hass.localize(
|
${this.localize(
|
||||||
`ui.panel.${this.translationKeyPanel}.analytics.preferences.diagnostics.description`
|
`ui.panel.${this.translationKeyPanel}.analytics.preferences.diagnostics.description`
|
||||||
)}
|
)}
|
||||||
</span>
|
</span>
|
||||||
|
@@ -10,7 +10,7 @@ import type { HaIconButton } from "./ha-icon-button";
|
|||||||
export class HaButtonMenu extends LitElement {
|
export class HaButtonMenu extends LitElement {
|
||||||
protected readonly [FOCUS_TARGET];
|
protected readonly [FOCUS_TARGET];
|
||||||
|
|
||||||
@property() public corner: Corner = "TOP_START";
|
@property() public corner: Corner = "BOTTOM_START";
|
||||||
|
|
||||||
@property() public menuCorner: MenuCorner = "START";
|
@property() public menuCorner: MenuCorner = "START";
|
||||||
|
|
||||||
|
@@ -35,7 +35,7 @@ interface FilterValue {
|
|||||||
export class HaRelatedFilterButtonMenu extends LitElement {
|
export class HaRelatedFilterButtonMenu extends LitElement {
|
||||||
@property() public hass!: HomeAssistant;
|
@property() public hass!: HomeAssistant;
|
||||||
|
|
||||||
@property() public corner: Corner = "TOP_START";
|
@property() public corner: Corner = "BOTTOM_START";
|
||||||
|
|
||||||
@property({ type: Boolean, reflect: true }) public narrow = false;
|
@property({ type: Boolean, reflect: true }) public narrow = false;
|
||||||
|
|
||||||
|
@@ -310,6 +310,8 @@ export class HaControlSelect extends LitElement {
|
|||||||
.option .content span {
|
.option .content span {
|
||||||
display: block;
|
display: block;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
-webkit-hyphens: auto;
|
||||||
|
-moz-hyphens: auto;
|
||||||
hyphens: auto;
|
hyphens: auto;
|
||||||
}
|
}
|
||||||
:host([vertical]) {
|
:host([vertical]) {
|
||||||
|
@@ -1,18 +1,74 @@
|
|||||||
|
import {
|
||||||
|
DIRECTION_LEFT,
|
||||||
|
DIRECTION_RIGHT,
|
||||||
|
Manager,
|
||||||
|
Swipe,
|
||||||
|
} from "@egjs/hammerjs";
|
||||||
import { DrawerBase } from "@material/mwc-drawer/mwc-drawer-base";
|
import { DrawerBase } from "@material/mwc-drawer/mwc-drawer-base";
|
||||||
import { styles } from "@material/mwc-drawer/mwc-drawer.css";
|
import { styles } from "@material/mwc-drawer/mwc-drawer.css";
|
||||||
import { css } from "lit";
|
import { css, PropertyValues } from "lit";
|
||||||
import { customElement } from "lit/decorators";
|
import { customElement, property } from "lit/decorators";
|
||||||
|
import { fireEvent } from "../common/dom/fire_event";
|
||||||
|
|
||||||
|
const blockingElements = (document as any).$blockingElements;
|
||||||
|
|
||||||
@customElement("ha-drawer")
|
@customElement("ha-drawer")
|
||||||
export class HaDrawer extends DrawerBase {
|
export class HaDrawer extends DrawerBase {
|
||||||
|
@property() public direction: "ltr" | "rtl" = "ltr";
|
||||||
|
|
||||||
|
private _mc?: HammerManager;
|
||||||
|
|
||||||
|
protected createAdapter() {
|
||||||
|
return {
|
||||||
|
...super.createAdapter(),
|
||||||
|
trapFocus: () => {
|
||||||
|
blockingElements.push(this);
|
||||||
|
this.appContent.inert = true;
|
||||||
|
document.body.style.overflow = "hidden";
|
||||||
|
},
|
||||||
|
releaseFocus: () => {
|
||||||
|
blockingElements.remove(this);
|
||||||
|
this.appContent.inert = false;
|
||||||
|
document.body.style.overflow = "";
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
protected updated(changedProps: PropertyValues) {
|
||||||
|
super.updated(changedProps);
|
||||||
|
if (changedProps.has("direction")) {
|
||||||
|
this.mdcRoot.dir = this.direction;
|
||||||
|
}
|
||||||
|
if (changedProps.has("open") && this.open && this.type === "modal") {
|
||||||
|
this._mc = new Manager(document, {
|
||||||
|
touchAction: "pan-y",
|
||||||
|
});
|
||||||
|
this._mc.add(
|
||||||
|
new Swipe({
|
||||||
|
direction:
|
||||||
|
this.direction === "rtl" ? DIRECTION_RIGHT : DIRECTION_LEFT,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
this._mc.on("swipeleft swiperight", () => {
|
||||||
|
fireEvent(this, "hass-toggle-menu", { open: false });
|
||||||
|
});
|
||||||
|
} else if (this._mc) {
|
||||||
|
this._mc.destroy();
|
||||||
|
this._mc = undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static override styles = [
|
static override styles = [
|
||||||
styles,
|
styles,
|
||||||
css`
|
css`
|
||||||
.mdc-drawer {
|
.mdc-drawer {
|
||||||
top: 0;
|
top: 0;
|
||||||
}
|
}
|
||||||
.mdc-drawer--modal.mdc-drawer--open {
|
.mdc-drawer.mdc-drawer--modal.mdc-drawer--open {
|
||||||
left: min(0px, var(--drawer-modal-left-offset));
|
z-index: 200;
|
||||||
|
}
|
||||||
|
.mdc-drawer-app-content {
|
||||||
|
transform: translateZ(0);
|
||||||
}
|
}
|
||||||
`,
|
`,
|
||||||
];
|
];
|
||||||
|
@@ -82,7 +82,6 @@ export class HaFormMultiSelect extends LitElement implements HaFormElement {
|
|||||||
<ha-button-menu
|
<ha-button-menu
|
||||||
.disabled=${this.disabled}
|
.disabled=${this.disabled}
|
||||||
fixed
|
fixed
|
||||||
corner="BOTTOM_START"
|
|
||||||
@opened=${this._handleOpen}
|
@opened=${this._handleOpen}
|
||||||
@closed=${this._handleClose}
|
@closed=${this._handleClose}
|
||||||
multi
|
multi
|
||||||
|
@@ -38,7 +38,6 @@ export class HaIconOverflowMenu extends LitElement {
|
|||||||
@click=${this._handleIconOverflowMenuOpened}
|
@click=${this._handleIconOverflowMenuOpened}
|
||||||
@closed=${this._handleIconOverflowMenuClosed}
|
@closed=${this._handleIconOverflowMenuClosed}
|
||||||
class="ha-icon-overflow-menu-overflow"
|
class="ha-icon-overflow-menu-overflow"
|
||||||
corner="BOTTOM_START"
|
|
||||||
absolute
|
absolute
|
||||||
>
|
>
|
||||||
<ha-icon-button
|
<ha-icon-button
|
||||||
|
@@ -70,11 +70,7 @@ class HaQrScanner extends LitElement {
|
|||||||
? html`<video></video>
|
? html`<video></video>
|
||||||
<div id="canvas-container">
|
<div id="canvas-container">
|
||||||
${this._cameras && this._cameras.length > 1
|
${this._cameras && this._cameras.length > 1
|
||||||
? html`<ha-button-menu
|
? html`<ha-button-menu fixed @closed=${stopPropagation}>
|
||||||
corner="BOTTOM_START"
|
|
||||||
fixed
|
|
||||||
@closed=${stopPropagation}
|
|
||||||
>
|
|
||||||
<ha-icon-button
|
<ha-icon-button
|
||||||
slot="trigger"
|
slot="trigger"
|
||||||
.label=${this.localize(
|
.label=${this.localize(
|
||||||
|
@@ -28,8 +28,8 @@ import {
|
|||||||
CSSResultGroup,
|
CSSResultGroup,
|
||||||
html,
|
html,
|
||||||
LitElement,
|
LitElement,
|
||||||
PropertyValues,
|
|
||||||
nothing,
|
nothing,
|
||||||
|
PropertyValues,
|
||||||
} from "lit";
|
} from "lit";
|
||||||
import { customElement, eventOptions, property, state } from "lit/decorators";
|
import { customElement, eventOptions, property, state } from "lit/decorators";
|
||||||
import { classMap } from "lit/directives/class-map";
|
import { classMap } from "lit/directives/class-map";
|
||||||
|
@@ -283,7 +283,6 @@ export class HaTargetPicker extends LitElement {
|
|||||||
return html`<mwc-menu-surface
|
return html`<mwc-menu-surface
|
||||||
open
|
open
|
||||||
.anchor=${this._addContainer}
|
.anchor=${this._addContainer}
|
||||||
.corner=${"BOTTOM_START"}
|
|
||||||
@closed=${this._onClosed}
|
@closed=${this._onClosed}
|
||||||
@opened=${this._onOpened}
|
@opened=${this._onOpened}
|
||||||
@opened-changed=${this._openedChanged}
|
@opened-changed=${this._openedChanged}
|
||||||
|
@@ -1,19 +1,43 @@
|
|||||||
import { TopAppBarFixedBase } from "@material/mwc-top-app-bar-fixed/mwc-top-app-bar-fixed-base";
|
import { TopAppBarFixedBase } from "@material/mwc-top-app-bar-fixed/mwc-top-app-bar-fixed-base";
|
||||||
import { styles } from "@material/mwc-top-app-bar/mwc-top-app-bar.css";
|
import { styles } from "@material/mwc-top-app-bar/mwc-top-app-bar.css";
|
||||||
import { css } from "lit";
|
import { css } from "lit";
|
||||||
import { customElement } from "lit/decorators";
|
import { customElement, property } from "lit/decorators";
|
||||||
|
|
||||||
|
let drawerContent: HTMLElement | undefined;
|
||||||
|
|
||||||
@customElement("ha-top-app-bar-fixed")
|
@customElement("ha-top-app-bar-fixed")
|
||||||
export class HaTopAppBarFixed extends TopAppBarFixedBase {
|
export class HaTopAppBarFixed extends TopAppBarFixedBase {
|
||||||
|
private get _drawerContent() {
|
||||||
|
if (!drawerContent) {
|
||||||
|
drawerContent = document
|
||||||
|
.querySelector("home-assistant")!
|
||||||
|
.renderRoot.querySelector("home-assistant-main")!
|
||||||
|
.renderRoot.querySelector("ha-drawer")!
|
||||||
|
.renderRoot.querySelector(".mdc-drawer-app-content") as HTMLElement;
|
||||||
|
}
|
||||||
|
return drawerContent;
|
||||||
|
}
|
||||||
|
|
||||||
|
@property({ type: Object })
|
||||||
|
get scrollTarget() {
|
||||||
|
return this._scrollTarget || this._drawerContent || window;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected updateRootPosition() {}
|
||||||
|
|
||||||
static override styles = [
|
static override styles = [
|
||||||
styles,
|
styles,
|
||||||
css`
|
css`
|
||||||
|
.mdc-top-app-bar {
|
||||||
|
position: sticky;
|
||||||
|
top: 0;
|
||||||
|
}
|
||||||
.mdc-top-app-bar__row {
|
.mdc-top-app-bar__row {
|
||||||
height: var(--header-height);
|
height: var(--header-height);
|
||||||
border-bottom: var(--app-header-border-bottom);
|
border-bottom: var(--app-header-border-bottom);
|
||||||
}
|
}
|
||||||
.mdc-top-app-bar--fixed-adjust {
|
.mdc-top-app-bar--fixed-adjust {
|
||||||
padding-top: var(--header-height);
|
padding-top: 0;
|
||||||
}
|
}
|
||||||
.mdc-top-app-bar {
|
.mdc-top-app-bar {
|
||||||
--mdc-typography-headline6-font-weight: 400;
|
--mdc-typography-headline6-font-weight: 400;
|
||||||
|
@@ -53,53 +53,46 @@ export const callAlarmAction = (
|
|||||||
};
|
};
|
||||||
|
|
||||||
export type AlarmMode =
|
export type AlarmMode =
|
||||||
| "away"
|
| "armed_home"
|
||||||
| "home"
|
| "armed_away"
|
||||||
| "night"
|
| "armed_night"
|
||||||
| "vacation"
|
| "armed_vacation"
|
||||||
| "custom_bypass"
|
| "armed_custom_bypass"
|
||||||
| "disarmed";
|
| "disarmed";
|
||||||
|
|
||||||
type AlarmConfig = {
|
type AlarmConfig = {
|
||||||
service: string;
|
service: string;
|
||||||
feature?: AlarmControlPanelEntityFeature;
|
feature?: AlarmControlPanelEntityFeature;
|
||||||
state: string;
|
|
||||||
path: string;
|
path: string;
|
||||||
};
|
};
|
||||||
export const ALARM_MODES: Record<AlarmMode, AlarmConfig> = {
|
export const ALARM_MODES: Record<AlarmMode, AlarmConfig> = {
|
||||||
away: {
|
armed_home: {
|
||||||
feature: AlarmControlPanelEntityFeature.ARM_AWAY,
|
|
||||||
service: "alarm_arm_away",
|
|
||||||
state: "armed_away",
|
|
||||||
path: mdiLock,
|
|
||||||
},
|
|
||||||
home: {
|
|
||||||
feature: AlarmControlPanelEntityFeature.ARM_HOME,
|
feature: AlarmControlPanelEntityFeature.ARM_HOME,
|
||||||
service: "alarm_arm_home",
|
service: "alarm_arm_home",
|
||||||
state: "armed_home",
|
|
||||||
path: mdiHome,
|
path: mdiHome,
|
||||||
},
|
},
|
||||||
custom_bypass: {
|
armed_away: {
|
||||||
feature: AlarmControlPanelEntityFeature.ARM_CUSTOM_BYPASS,
|
feature: AlarmControlPanelEntityFeature.ARM_AWAY,
|
||||||
service: "alarm_arm_custom_bypass",
|
service: "alarm_arm_away",
|
||||||
state: "armed_custom_bypass",
|
path: mdiLock,
|
||||||
path: mdiShield,
|
|
||||||
},
|
},
|
||||||
night: {
|
armed_night: {
|
||||||
feature: AlarmControlPanelEntityFeature.ARM_NIGHT,
|
feature: AlarmControlPanelEntityFeature.ARM_NIGHT,
|
||||||
service: "alarm_arm_night",
|
service: "alarm_arm_night",
|
||||||
state: "armed_night",
|
|
||||||
path: mdiMoonWaningCrescent,
|
path: mdiMoonWaningCrescent,
|
||||||
},
|
},
|
||||||
vacation: {
|
armed_vacation: {
|
||||||
feature: AlarmControlPanelEntityFeature.ARM_VACATION,
|
feature: AlarmControlPanelEntityFeature.ARM_VACATION,
|
||||||
service: "alarm_arm_vacation",
|
service: "alarm_arm_vacation",
|
||||||
state: "armed_vacation",
|
|
||||||
path: mdiAirplane,
|
path: mdiAirplane,
|
||||||
},
|
},
|
||||||
|
armed_custom_bypass: {
|
||||||
|
feature: AlarmControlPanelEntityFeature.ARM_CUSTOM_BYPASS,
|
||||||
|
service: "alarm_arm_custom_bypass",
|
||||||
|
path: mdiShield,
|
||||||
|
},
|
||||||
disarmed: {
|
disarmed: {
|
||||||
service: "alarm_disarm",
|
service: "alarm_disarm",
|
||||||
state: "disarmed",
|
|
||||||
path: mdiShieldOff,
|
path: mdiShieldOff,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
@@ -2,6 +2,7 @@ import {
|
|||||||
HassEntityAttributeBase,
|
HassEntityAttributeBase,
|
||||||
HassEntityBase,
|
HassEntityBase,
|
||||||
} from "home-assistant-js-websocket";
|
} from "home-assistant-js-websocket";
|
||||||
|
import { stateActive } from "../common/entity/state_active";
|
||||||
import { supportsFeature } from "../common/entity/supports-feature";
|
import { supportsFeature } from "../common/entity/supports-feature";
|
||||||
import { blankBeforePercent } from "../common/translations/blank_before_percent";
|
import { blankBeforePercent } from "../common/translations/blank_before_percent";
|
||||||
import { UNAVAILABLE } from "./entity";
|
import { UNAVAILABLE } from "./entity";
|
||||||
@@ -114,10 +115,12 @@ export function computeCoverPositionStateDisplay(
|
|||||||
locale: FrontendLocaleData,
|
locale: FrontendLocaleData,
|
||||||
position?: number
|
position?: number
|
||||||
) {
|
) {
|
||||||
const currentPosition =
|
const statePosition = stateActive(stateObj)
|
||||||
position ??
|
? stateObj.attributes.current_position ??
|
||||||
stateObj.attributes.current_position ??
|
stateObj.attributes.current_tilt_position
|
||||||
stateObj.attributes.current_tilt_position;
|
: undefined;
|
||||||
|
|
||||||
|
const currentPosition = position ?? statePosition;
|
||||||
|
|
||||||
return currentPosition && currentPosition !== 100
|
return currentPosition && currentPosition !== 100
|
||||||
? `${Math.round(currentPosition)}${blankBeforePercent(locale)}%`
|
? `${Math.round(currentPosition)}${blankBeforePercent(locale)}%`
|
||||||
|
@@ -9,6 +9,7 @@ import {
|
|||||||
HassEntityAttributeBase,
|
HassEntityAttributeBase,
|
||||||
HassEntityBase,
|
HassEntityBase,
|
||||||
} from "home-assistant-js-websocket";
|
} from "home-assistant-js-websocket";
|
||||||
|
import { stateActive } from "../common/entity/state_active";
|
||||||
import { blankBeforePercent } from "../common/translations/blank_before_percent";
|
import { blankBeforePercent } from "../common/translations/blank_before_percent";
|
||||||
import { FrontendLocaleData } from "./translation";
|
import { FrontendLocaleData } from "./translation";
|
||||||
|
|
||||||
@@ -69,7 +70,7 @@ export function fanSpeedToPercentage(
|
|||||||
if (speedValue === -1) {
|
if (speedValue === -1) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
return Math.round(speedValue * step);
|
return Math.floor(speedValue * step);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function computeFanSpeedCount(stateObj: FanEntity): number {
|
export function computeFanSpeedCount(stateObj: FanEntity): number {
|
||||||
@@ -99,9 +100,12 @@ export function computeFanSpeedStateDisplay(
|
|||||||
locale: FrontendLocaleData,
|
locale: FrontendLocaleData,
|
||||||
speed?: number
|
speed?: number
|
||||||
) {
|
) {
|
||||||
const currentSpeed = speed ?? stateObj.attributes.percentage;
|
const percentage = stateActive(stateObj)
|
||||||
|
? stateObj.attributes.percentage
|
||||||
|
: undefined;
|
||||||
|
const currentSpeed = speed ?? percentage;
|
||||||
|
|
||||||
return currentSpeed
|
return currentSpeed
|
||||||
? `${Math.round(currentSpeed)}${blankBeforePercent(locale)}%`
|
? `${Math.floor(currentSpeed)}${blankBeforePercent(locale)}%`
|
||||||
: "";
|
: "";
|
||||||
}
|
}
|
||||||
|
@@ -28,8 +28,19 @@ export interface HassioHassOSInfo {
|
|||||||
data_disk: string;
|
data_disk: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface Datadisk {
|
||||||
|
name: string;
|
||||||
|
vendor: string;
|
||||||
|
model: string;
|
||||||
|
serial: string;
|
||||||
|
size: number;
|
||||||
|
id: string;
|
||||||
|
dev_path: string;
|
||||||
|
}
|
||||||
|
|
||||||
export interface DatadiskList {
|
export interface DatadiskList {
|
||||||
devices: string[];
|
devices: string[];
|
||||||
|
disks: Datadisk[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export const fetchHassioHostInfo = async (
|
export const fetchHassioHostInfo = async (
|
||||||
|
@@ -389,8 +389,8 @@ export const filterSelectorEntities = (
|
|||||||
|
|
||||||
if (filterSupportedFeature) {
|
if (filterSupportedFeature) {
|
||||||
if (
|
if (
|
||||||
ensureArray(filterSupportedFeature).some(
|
!ensureArray(filterSupportedFeature).some((feature) =>
|
||||||
(feature) => !supportsFeature(entity, feature)
|
supportsFeature(entity, feature)
|
||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
return false;
|
return false;
|
||||||
|
@@ -18,6 +18,7 @@ export interface ThreadDataSet {
|
|||||||
network_name: string;
|
network_name: string;
|
||||||
extended_pan_id?: string;
|
extended_pan_id?: string;
|
||||||
pan_id?: string;
|
pan_id?: string;
|
||||||
|
channel?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ThreadRouterDiscoveryEvent {
|
export interface ThreadRouterDiscoveryEvent {
|
||||||
|
@@ -84,7 +84,7 @@ type PipelineRunEvent =
|
|||||||
| PipelineTTSStartEvent
|
| PipelineTTSStartEvent
|
||||||
| PipelineTTSEndEvent;
|
| PipelineTTSEndEvent;
|
||||||
|
|
||||||
interface PipelineRunOptions {
|
export interface PipelineRunOptions {
|
||||||
start_stage: "stt" | "intent" | "tts";
|
start_stage: "stt" | "intent" | "tts";
|
||||||
end_stage: "stt" | "intent" | "tts";
|
end_stage: "stt" | "intent" | "tts";
|
||||||
language?: string;
|
language?: string;
|
||||||
@@ -99,13 +99,15 @@ export interface PipelineRun {
|
|||||||
stage: "ready" | "stt" | "intent" | "tts" | "done" | "error";
|
stage: "ready" | "stt" | "intent" | "tts" | "done" | "error";
|
||||||
run: PipelineRunStartEvent["data"];
|
run: PipelineRunStartEvent["data"];
|
||||||
error?: PipelineErrorEvent["data"];
|
error?: PipelineErrorEvent["data"];
|
||||||
stt?: PipelineSTTStartEvent["data"] & Partial<PipelineSTTEndEvent["data"]>;
|
stt?: PipelineSTTStartEvent["data"] &
|
||||||
|
Partial<PipelineSTTEndEvent["data"]> & { done: boolean };
|
||||||
intent?: PipelineIntentStartEvent["data"] &
|
intent?: PipelineIntentStartEvent["data"] &
|
||||||
Partial<PipelineIntentEndEvent["data"]>;
|
Partial<PipelineIntentEndEvent["data"]> & { done: boolean };
|
||||||
tts?: PipelineTTSStartEvent["data"] & Partial<PipelineTTSEndEvent["data"]>;
|
tts?: PipelineTTSStartEvent["data"] &
|
||||||
|
Partial<PipelineTTSEndEvent["data"]> & { done: boolean };
|
||||||
}
|
}
|
||||||
|
|
||||||
export const runPipelineFromText = (
|
export const runVoiceAssistantPipeline = (
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
callback: (event: PipelineRun) => void,
|
callback: (event: PipelineRun) => void,
|
||||||
options: PipelineRunOptions
|
options: PipelineRunOptions
|
||||||
@@ -139,17 +141,38 @@ export const runPipelineFromText = (
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (updateEvent.type === "stt-start") {
|
if (updateEvent.type === "stt-start") {
|
||||||
run = { ...run, stage: "stt", stt: updateEvent.data };
|
run = {
|
||||||
|
...run,
|
||||||
|
stage: "stt",
|
||||||
|
stt: { ...updateEvent.data, done: false },
|
||||||
|
};
|
||||||
} else if (updateEvent.type === "stt-end") {
|
} else if (updateEvent.type === "stt-end") {
|
||||||
run = { ...run, stt: { ...run.stt!, ...updateEvent.data } };
|
run = {
|
||||||
|
...run,
|
||||||
|
stt: { ...run.stt!, ...updateEvent.data, done: true },
|
||||||
|
};
|
||||||
} else if (updateEvent.type === "intent-start") {
|
} else if (updateEvent.type === "intent-start") {
|
||||||
run = { ...run, stage: "intent", intent: updateEvent.data };
|
run = {
|
||||||
|
...run,
|
||||||
|
stage: "intent",
|
||||||
|
intent: { ...updateEvent.data, done: false },
|
||||||
|
};
|
||||||
} else if (updateEvent.type === "intent-end") {
|
} else if (updateEvent.type === "intent-end") {
|
||||||
run = { ...run, intent: { ...run.intent!, ...updateEvent.data } };
|
run = {
|
||||||
|
...run,
|
||||||
|
intent: { ...run.intent!, ...updateEvent.data, done: true },
|
||||||
|
};
|
||||||
} else if (updateEvent.type === "tts-start") {
|
} else if (updateEvent.type === "tts-start") {
|
||||||
run = { ...run, stage: "tts", tts: updateEvent.data };
|
run = {
|
||||||
|
...run,
|
||||||
|
stage: "tts",
|
||||||
|
tts: { ...updateEvent.data, done: false },
|
||||||
|
};
|
||||||
} else if (updateEvent.type === "tts-end") {
|
} else if (updateEvent.type === "tts-end") {
|
||||||
run = { ...run, tts: { ...run.tts!, ...updateEvent.data } };
|
run = {
|
||||||
|
...run,
|
||||||
|
tts: { ...run.tts!, ...updateEvent.data, done: true },
|
||||||
|
};
|
||||||
} else if (updateEvent.type === "run-end") {
|
} else if (updateEvent.type === "run-end") {
|
||||||
run = { ...run, stage: "done" };
|
run = { ...run, stage: "done" };
|
||||||
unsubProm.then((unsub) => unsub());
|
unsubProm.then((unsub) => unsub());
|
||||||
|
@@ -62,7 +62,7 @@ export const showDialog = async (
|
|||||||
dialogParams: unknown,
|
dialogParams: unknown,
|
||||||
dialogImport?: () => Promise<unknown>,
|
dialogImport?: () => Promise<unknown>,
|
||||||
addHistory = true
|
addHistory = true
|
||||||
) => {
|
): Promise<boolean> => {
|
||||||
if (!(dialogTag in LOADED)) {
|
if (!(dialogTag in LOADED)) {
|
||||||
if (!dialogImport) {
|
if (!dialogImport) {
|
||||||
if (__DEV__) {
|
if (__DEV__) {
|
||||||
@@ -71,7 +71,7 @@ export const showDialog = async (
|
|||||||
"Asked to show dialog that's not loaded and can't be imported"
|
"Asked to show dialog that's not loaded and can't be imported"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
LOADED[dialogTag] = {
|
LOADED[dialogTag] = {
|
||||||
element: dialogImport().then(() => {
|
element: dialogImport().then(() => {
|
||||||
@@ -128,6 +128,8 @@ export const showDialog = async (
|
|||||||
// so it's guaranteed to be on top of the other elements
|
// so it's guaranteed to be on top of the other elements
|
||||||
root.appendChild(dialogElement);
|
root.appendChild(dialogElement);
|
||||||
dialogElement.showDialog(dialogParams);
|
dialogElement.showDialog(dialogParams);
|
||||||
|
|
||||||
|
return true;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const replaceDialog = (dialogElement: HassDialog) => {
|
export const replaceDialog = (dialogElement: HassDialog) => {
|
||||||
|
@@ -40,9 +40,7 @@ export class HaMoreInfoAlarmControlPanelModes extends LitElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private _getCurrentMode(stateObj: AlarmControlPanelEntity) {
|
private _getCurrentMode(stateObj: AlarmControlPanelEntity) {
|
||||||
return this._modes(stateObj).find(
|
return this._modes(stateObj).find((mode) => mode === stateObj.state);
|
||||||
(mode) => ALARM_MODES[mode].state === stateObj.state
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private async _setMode(mode: AlarmMode) {
|
private async _setMode(mode: AlarmMode) {
|
||||||
@@ -86,7 +84,7 @@ export class HaMoreInfoAlarmControlPanelModes extends LitElement {
|
|||||||
private async _valueChanged(ev: CustomEvent) {
|
private async _valueChanged(ev: CustomEvent) {
|
||||||
const mode = (ev.detail as any).value as AlarmMode;
|
const mode = (ev.detail as any).value as AlarmMode;
|
||||||
|
|
||||||
if (ALARM_MODES[mode].state === this.stateObj!.state) return;
|
if (mode === this.stateObj!.state) return;
|
||||||
|
|
||||||
const oldMode = this._getCurrentMode(this.stateObj!);
|
const oldMode = this._getCurrentMode(this.stateObj!);
|
||||||
this._currentMode = mode;
|
this._currentMode = mode;
|
||||||
|
@@ -3,6 +3,7 @@ import { customElement, property, state } from "lit/decorators";
|
|||||||
import { styleMap } from "lit/directives/style-map";
|
import { styleMap } from "lit/directives/style-map";
|
||||||
import { computeAttributeNameDisplay } from "../../../../common/entity/compute_attribute_display";
|
import { computeAttributeNameDisplay } from "../../../../common/entity/compute_attribute_display";
|
||||||
import { computeStateDisplay } from "../../../../common/entity/compute_state_display";
|
import { computeStateDisplay } from "../../../../common/entity/compute_state_display";
|
||||||
|
import { stateActive } from "../../../../common/entity/state_active";
|
||||||
import { stateColorCss } from "../../../../common/entity/state_color";
|
import { stateColorCss } from "../../../../common/entity/state_color";
|
||||||
import "../../../../components/ha-control-select";
|
import "../../../../components/ha-control-select";
|
||||||
import type { ControlSelectOption } from "../../../../components/ha-control-select";
|
import type { ControlSelectOption } from "../../../../components/ha-control-select";
|
||||||
@@ -26,20 +27,25 @@ export class HaMoreInfoFanSpeed extends LitElement {
|
|||||||
|
|
||||||
@property({ attribute: false }) public stateObj!: FanEntity;
|
@property({ attribute: false }) public stateObj!: FanEntity;
|
||||||
|
|
||||||
@state() value?: number;
|
@state() sliderValue?: number;
|
||||||
|
|
||||||
|
@state() speedValue?: FanSpeed;
|
||||||
|
|
||||||
protected updated(changedProp: Map<string | number | symbol, unknown>): void {
|
protected updated(changedProp: Map<string | number | symbol, unknown>): void {
|
||||||
if (changedProp.has("stateObj")) {
|
if (changedProp.has("stateObj")) {
|
||||||
this.value =
|
const percentage = stateActive(this.stateObj)
|
||||||
this.stateObj.attributes.percentage != null
|
? this.stateObj.attributes.percentage ?? 0
|
||||||
? Math.max(Math.round(this.stateObj.attributes.percentage), 1)
|
: 0;
|
||||||
: undefined;
|
this.sliderValue = Math.max(Math.round(percentage), 0);
|
||||||
|
this.speedValue = fanPercentageToSpeed(this.stateObj, percentage);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private _speedValueChanged(ev: CustomEvent) {
|
private _speedValueChanged(ev: CustomEvent) {
|
||||||
const speed = (ev.detail as any).value as FanSpeed;
|
const speed = (ev.detail as any).value as FanSpeed;
|
||||||
|
|
||||||
|
this.speedValue = speed;
|
||||||
|
|
||||||
const percentage = fanSpeedToPercentage(this.stateObj, speed);
|
const percentage = fanSpeedToPercentage(this.stateObj, speed);
|
||||||
|
|
||||||
this.hass.callService("fan", "set_percentage", {
|
this.hass.callService("fan", "set_percentage", {
|
||||||
@@ -52,6 +58,8 @@ export class HaMoreInfoFanSpeed extends LitElement {
|
|||||||
const value = (ev.detail as any).value;
|
const value = (ev.detail as any).value;
|
||||||
if (isNaN(value)) return;
|
if (isNaN(value)) return;
|
||||||
|
|
||||||
|
this.sliderValue = value;
|
||||||
|
|
||||||
this.hass.callService("fan", "set_percentage", {
|
this.hass.callService("fan", "set_percentage", {
|
||||||
entity_id: this.stateObj!.entity_id,
|
entity_id: this.stateObj!.entity_id,
|
||||||
percentage: value,
|
percentage: value,
|
||||||
@@ -88,16 +96,11 @@ export class HaMoreInfoFanSpeed extends LitElement {
|
|||||||
})
|
})
|
||||||
).reverse();
|
).reverse();
|
||||||
|
|
||||||
const speed = fanPercentageToSpeed(
|
|
||||||
this.stateObj,
|
|
||||||
this.stateObj.attributes.percentage ?? 0
|
|
||||||
);
|
|
||||||
|
|
||||||
return html`
|
return html`
|
||||||
<ha-control-select
|
<ha-control-select
|
||||||
vertical
|
vertical
|
||||||
.options=${options}
|
.options=${options}
|
||||||
.value=${speed}
|
.value=${this.speedValue}
|
||||||
@value-changed=${this._speedValueChanged}
|
@value-changed=${this._speedValueChanged}
|
||||||
.ariaLabel=${computeAttributeNameDisplay(
|
.ariaLabel=${computeAttributeNameDisplay(
|
||||||
this.hass.localize,
|
this.hass.localize,
|
||||||
@@ -119,7 +122,7 @@ export class HaMoreInfoFanSpeed extends LitElement {
|
|||||||
vertical
|
vertical
|
||||||
min="0"
|
min="0"
|
||||||
max="100"
|
max="100"
|
||||||
.value=${this.value}
|
.value=${this.sliderValue}
|
||||||
.step=${this.stateObj.attributes.percentage_step ?? 1}
|
.step=${this.stateObj.attributes.percentage_step ?? 1}
|
||||||
@value-changed=${this._valueChanged}
|
@value-changed=${this._valueChanged}
|
||||||
.ariaLabel=${computeAttributeNameDisplay(
|
.ariaLabel=${computeAttributeNameDisplay(
|
||||||
|
@@ -23,6 +23,7 @@ import {
|
|||||||
computeAttributeValueDisplay,
|
computeAttributeValueDisplay,
|
||||||
} from "../../../common/entity/compute_attribute_display";
|
} from "../../../common/entity/compute_attribute_display";
|
||||||
import { computeStateDisplay } from "../../../common/entity/compute_state_display";
|
import { computeStateDisplay } from "../../../common/entity/compute_state_display";
|
||||||
|
import { stateActive } from "../../../common/entity/state_active";
|
||||||
import { supportsFeature } from "../../../common/entity/supports-feature";
|
import { supportsFeature } from "../../../common/entity/supports-feature";
|
||||||
import "../../../components/ha-attributes";
|
import "../../../components/ha-attributes";
|
||||||
import { UNAVAILABLE } from "../../../data/entity";
|
import { UNAVAILABLE } from "../../../data/entity";
|
||||||
@@ -119,7 +120,7 @@ class MoreInfoFan extends LitElement {
|
|||||||
const liveValue = this._liveSpeed;
|
const liveValue = this._liveSpeed;
|
||||||
|
|
||||||
const forcedState =
|
const forcedState =
|
||||||
this._liveSpeed != null ? (this._liveSpeed ? "on" : "off") : undefined;
|
liveValue != null ? (liveValue ? "on" : "off") : undefined;
|
||||||
|
|
||||||
const stateDisplay = computeStateDisplay(
|
const stateDisplay = computeStateDisplay(
|
||||||
this.hass.localize,
|
this.hass.localize,
|
||||||
@@ -135,7 +136,7 @@ class MoreInfoFan extends LitElement {
|
|||||||
liveValue
|
liveValue
|
||||||
);
|
);
|
||||||
|
|
||||||
if (positionStateDisplay) {
|
if (positionStateDisplay && (stateActive(this.stateObj!) || liveValue)) {
|
||||||
return positionStateDisplay;
|
return positionStateDisplay;
|
||||||
}
|
}
|
||||||
return stateDisplay;
|
return stateDisplay;
|
||||||
@@ -273,7 +274,6 @@ class MoreInfoFan extends LitElement {
|
|||||||
supportsPresetMode && this.stateObj.attributes.preset_modes
|
supportsPresetMode && this.stateObj.attributes.preset_modes
|
||||||
? html`
|
? html`
|
||||||
<ha-button-menu
|
<ha-button-menu
|
||||||
corner="BOTTOM_START"
|
|
||||||
@action=${this._handlePresetMode}
|
@action=${this._handlePresetMode}
|
||||||
@closed=${stopPropagation}
|
@closed=${stopPropagation}
|
||||||
fixed
|
fixed
|
||||||
|
@@ -173,7 +173,6 @@ class MoreInfoLight extends LitElement {
|
|||||||
${supportsEffects && this.stateObj.attributes.effect_list
|
${supportsEffects && this.stateObj.attributes.effect_list
|
||||||
? html`
|
? html`
|
||||||
<ha-button-menu
|
<ha-button-menu
|
||||||
corner="BOTTOM_START"
|
|
||||||
@action=${this._handleEffectButton}
|
@action=${this._handleEffectButton}
|
||||||
@closed=${stopPropagation}
|
@closed=${stopPropagation}
|
||||||
fixed
|
fixed
|
||||||
|
@@ -12,6 +12,8 @@ import {
|
|||||||
import { css, CSSResultGroup, html, LitElement, nothing } from "lit";
|
import { css, CSSResultGroup, html, LitElement, nothing } from "lit";
|
||||||
import { customElement, property } from "lit/decorators";
|
import { customElement, property } from "lit/decorators";
|
||||||
import { stopPropagation } from "../../../common/dom/stop_propagation";
|
import { stopPropagation } from "../../../common/dom/stop_propagation";
|
||||||
|
import { computeAttributeValueDisplay } from "../../../common/entity/compute_attribute_display";
|
||||||
|
import { computeStateDisplay } from "../../../common/entity/compute_state_display";
|
||||||
import { supportsFeature } from "../../../common/entity/supports-feature";
|
import { supportsFeature } from "../../../common/entity/supports-feature";
|
||||||
import "../../../components/ha-attributes";
|
import "../../../components/ha-attributes";
|
||||||
import "../../../components/ha-icon";
|
import "../../../components/ha-icon";
|
||||||
@@ -113,11 +115,19 @@ class MoreInfoVacuum extends LitElement {
|
|||||||
</span>
|
</span>
|
||||||
<span>
|
<span>
|
||||||
<strong>
|
<strong>
|
||||||
${stateObj.attributes.status ||
|
${computeAttributeValueDisplay(
|
||||||
this.hass.localize(
|
this.hass.localize,
|
||||||
`component.vacuum.entity_component._.state.${stateObj.state}`
|
stateObj,
|
||||||
|
this.hass.locale,
|
||||||
|
this.hass.entities,
|
||||||
|
"status"
|
||||||
) ||
|
) ||
|
||||||
stateObj.state}
|
computeStateDisplay(
|
||||||
|
this.hass.localize,
|
||||||
|
stateObj,
|
||||||
|
this.hass.locale,
|
||||||
|
this.hass.entities
|
||||||
|
)}
|
||||||
</strong>
|
</strong>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
@@ -451,6 +451,7 @@ export class MoreInfoDialog extends LitElement {
|
|||||||
--dialog-surface-position: static;
|
--dialog-surface-position: static;
|
||||||
--dialog-content-position: static;
|
--dialog-content-position: static;
|
||||||
--dialog-content-padding: 0;
|
--dialog-content-padding: 0;
|
||||||
|
--chart-base-position: static;
|
||||||
}
|
}
|
||||||
|
|
||||||
ha-header-bar {
|
ha-header-bar {
|
||||||
|
@@ -6,6 +6,7 @@ This is the entry point for providing external app stuff from app entrypoint.
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { fireEvent } from "../common/dom/fire_event";
|
import { fireEvent } from "../common/dom/fire_event";
|
||||||
|
import { mainWindow } from "../common/dom/get_main_window";
|
||||||
import { HomeAssistantMain } from "../layouts/home-assistant-main";
|
import { HomeAssistantMain } from "../layouts/home-assistant-main";
|
||||||
import type { EMIncomingMessageCommands } from "./external_messaging";
|
import type { EMIncomingMessageCommands } from "./external_messaging";
|
||||||
|
|
||||||
@@ -45,6 +46,15 @@ const handleExternalMessage = (
|
|||||||
result: null,
|
result: null,
|
||||||
});
|
});
|
||||||
} else if (msg.command === "sidebar/toggle") {
|
} else if (msg.command === "sidebar/toggle") {
|
||||||
|
if (mainWindow.history.state?.open) {
|
||||||
|
bus.fireMessage({
|
||||||
|
id: msg.id,
|
||||||
|
type: "result",
|
||||||
|
success: false,
|
||||||
|
error: { code: "not_allowed", message: "dialog open" },
|
||||||
|
});
|
||||||
|
return true;
|
||||||
|
}
|
||||||
fireEvent(hassMainEl, "hass-toggle-menu");
|
fireEvent(hassMainEl, "hass-toggle-menu");
|
||||||
bus.fireMessage({
|
bus.fireMessage({
|
||||||
id: msg.id,
|
id: msg.id,
|
||||||
@@ -53,10 +63,16 @@ const handleExternalMessage = (
|
|||||||
result: null,
|
result: null,
|
||||||
});
|
});
|
||||||
} else if (msg.command === "sidebar/show") {
|
} else if (msg.command === "sidebar/show") {
|
||||||
fireEvent(hassMainEl, "hass-toggle-menu", {
|
if (mainWindow.history.state?.open) {
|
||||||
open: true,
|
bus.fireMessage({
|
||||||
screenPercentage: msg.data?.screenPercentage,
|
id: msg.id,
|
||||||
});
|
type: "result",
|
||||||
|
success: false,
|
||||||
|
error: { code: "not_allowed", message: "dialog open" },
|
||||||
|
});
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
fireEvent(hassMainEl, "hass-toggle-menu", { open: true });
|
||||||
bus.fireMessage({
|
bus.fireMessage({
|
||||||
id: msg.id,
|
id: msg.id,
|
||||||
type: "result",
|
type: "result",
|
||||||
|
@@ -131,7 +131,6 @@ interface EMIncomingMessageShowSidebar {
|
|||||||
id: number;
|
id: number;
|
||||||
type: "command";
|
type: "command";
|
||||||
command: "sidebar/show";
|
command: "sidebar/show";
|
||||||
data?: { screenPercentage: number };
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export type EMIncomingMessageCommands =
|
export type EMIncomingMessageCommands =
|
||||||
|
@@ -99,6 +99,8 @@ class HassSubpage extends LitElement {
|
|||||||
display: block;
|
display: block;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
background-color: var(--primary-background-color);
|
background-color: var(--primary-background-color);
|
||||||
|
overflow: hidden;
|
||||||
|
position: relative;
|
||||||
}
|
}
|
||||||
|
|
||||||
:host([narrow]) {
|
:host([narrow]) {
|
||||||
@@ -152,7 +154,7 @@ class HassSubpage extends LitElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#fab {
|
#fab {
|
||||||
position: fixed;
|
position: absolute;
|
||||||
right: calc(16px + env(safe-area-inset-right));
|
right: calc(16px + env(safe-area-inset-right));
|
||||||
bottom: calc(16px + env(safe-area-inset-bottom));
|
bottom: calc(16px + env(safe-area-inset-bottom));
|
||||||
z-index: 1;
|
z-index: 1;
|
||||||
|
@@ -11,6 +11,7 @@ import { customElement, property, state } from "lit/decorators";
|
|||||||
import { fireEvent, HASSDomEvent } from "../common/dom/fire_event";
|
import { fireEvent, HASSDomEvent } from "../common/dom/fire_event";
|
||||||
import { listenMediaQuery } from "../common/dom/media_query";
|
import { listenMediaQuery } from "../common/dom/media_query";
|
||||||
import { toggleAttribute } from "../common/dom/toggle_attribute";
|
import { toggleAttribute } from "../common/dom/toggle_attribute";
|
||||||
|
import { computeRTLDirection } from "../common/util/compute_rtl";
|
||||||
import "../components/ha-drawer";
|
import "../components/ha-drawer";
|
||||||
import { showNotificationDrawer } from "../dialogs/notifications/show-notification-drawer";
|
import { showNotificationDrawer } from "../dialogs/notifications/show-notification-drawer";
|
||||||
import type { HomeAssistant, Route } from "../types";
|
import type { HomeAssistant, Route } from "../types";
|
||||||
@@ -19,9 +20,7 @@ import "./partial-panel-resolver";
|
|||||||
declare global {
|
declare global {
|
||||||
// for fire event
|
// for fire event
|
||||||
interface HASSDomEvents {
|
interface HASSDomEvents {
|
||||||
"hass-toggle-menu":
|
"hass-toggle-menu": undefined | { open?: boolean };
|
||||||
| undefined
|
|
||||||
| { open?: boolean; screenPercentage?: number };
|
|
||||||
"hass-edit-sidebar": EditSideBarEvent;
|
"hass-edit-sidebar": EditSideBarEvent;
|
||||||
"hass-show-notifications": undefined;
|
"hass-show-notifications": undefined;
|
||||||
}
|
}
|
||||||
@@ -63,6 +62,7 @@ export class HomeAssistantMain extends LitElement {
|
|||||||
<ha-drawer
|
<ha-drawer
|
||||||
.type=${sidebarNarrow ? "modal" : ""}
|
.type=${sidebarNarrow ? "modal" : ""}
|
||||||
.open=${sidebarNarrow ? this._drawerOpen : undefined}
|
.open=${sidebarNarrow ? this._drawerOpen : undefined}
|
||||||
|
.direction=${computeRTLDirection(this.hass)}
|
||||||
@MDCDrawer:closed=${this._drawerClosed}
|
@MDCDrawer:closed=${this._drawerClosed}
|
||||||
>
|
>
|
||||||
<ha-sidebar
|
<ha-sidebar
|
||||||
@@ -122,10 +122,6 @@ export class HomeAssistantMain extends LitElement {
|
|||||||
}
|
}
|
||||||
if (this._sidebarNarrow) {
|
if (this._sidebarNarrow) {
|
||||||
this._drawerOpen = ev.detail?.open ?? !this._drawerOpen;
|
this._drawerOpen = ev.detail?.open ?? !this._drawerOpen;
|
||||||
const offset = ev.detail?.screenPercentage
|
|
||||||
? -256 + screen.width * (ev.detail.screenPercentage / 100)
|
|
||||||
: 0;
|
|
||||||
this.style.setProperty("--drawer-modal-left-offset", `${offset}px`);
|
|
||||||
} else {
|
} else {
|
||||||
fireEvent(this, "hass-dock-sidebar", {
|
fireEvent(this, "hass-dock-sidebar", {
|
||||||
dock: ev.detail?.open
|
dock: ev.detail?.open
|
||||||
@@ -180,7 +176,6 @@ export class HomeAssistantMain extends LitElement {
|
|||||||
/* remove the grey tap highlights in iOS on the fullscreen touch targets */
|
/* remove the grey tap highlights in iOS on the fullscreen touch targets */
|
||||||
-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
|
-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
|
||||||
--mdc-drawer-width: 56px;
|
--mdc-drawer-width: 56px;
|
||||||
--mdc-top-app-bar-width: calc(100% - var(--mdc-drawer-width));
|
|
||||||
}
|
}
|
||||||
:host([expanded]) {
|
:host([expanded]) {
|
||||||
--mdc-drawer-width: calc(256px + env(safe-area-inset-left));
|
--mdc-drawer-width: calc(256px + env(safe-area-inset-left));
|
||||||
|
@@ -23,11 +23,11 @@ class OnboardingAnalytics extends LitElement {
|
|||||||
|
|
||||||
protected render(): TemplateResult {
|
protected render(): TemplateResult {
|
||||||
return html`
|
return html`
|
||||||
<p>${this.hass.localize("ui.panel.page-onboarding.analytics.intro")}</p>
|
<p>${this.localize("ui.panel.page-onboarding.analytics.intro")}</p>
|
||||||
<ha-analytics
|
<ha-analytics
|
||||||
translation_key_panel="page-onboarding"
|
translation_key_panel="page-onboarding"
|
||||||
@analytics-preferences-changed=${this._preferencesChanged}
|
@analytics-preferences-changed=${this._preferencesChanged}
|
||||||
.hass=${this.hass}
|
.localize=${this.localize}
|
||||||
.analytics=${this._analyticsDetails}
|
.analytics=${this._analyticsDetails}
|
||||||
>
|
>
|
||||||
</ha-analytics>
|
</ha-analytics>
|
||||||
@@ -41,7 +41,7 @@ class OnboardingAnalytics extends LitElement {
|
|||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noreferrer"
|
rel="noreferrer"
|
||||||
>
|
>
|
||||||
${this.hass.localize("ui.panel.page-onboarding.analytics.learn_more")}
|
${this.localize("ui.panel.page-onboarding.analytics.learn_more")}
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
|
@@ -182,8 +182,6 @@ export default class HaAutomationActionRow extends LitElement {
|
|||||||
: html`
|
: html`
|
||||||
<ha-button-menu
|
<ha-button-menu
|
||||||
slot="icons"
|
slot="icons"
|
||||||
fixed
|
|
||||||
corner="BOTTOM_START"
|
|
||||||
@action=${this._handleAction}
|
@action=${this._handleAction}
|
||||||
@click=${preventDefault}
|
@click=${preventDefault}
|
||||||
>
|
>
|
||||||
|
@@ -128,11 +128,7 @@ export default class HaAutomationAction extends LitElement {
|
|||||||
`
|
`
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<ha-button-menu
|
<ha-button-menu @action=${this._addAction} .disabled=${this.disabled}>
|
||||||
fixed
|
|
||||||
@action=${this._addAction}
|
|
||||||
.disabled=${this.disabled}
|
|
||||||
>
|
|
||||||
<ha-button
|
<ha-button
|
||||||
slot="trigger"
|
slot="trigger"
|
||||||
outlined
|
outlined
|
||||||
|
@@ -116,8 +116,6 @@ export default class HaAutomationConditionRow extends LitElement {
|
|||||||
: html`
|
: html`
|
||||||
<ha-button-menu
|
<ha-button-menu
|
||||||
slot="icons"
|
slot="icons"
|
||||||
fixed
|
|
||||||
corner="BOTTOM_START"
|
|
||||||
@action=${this._handleAction}
|
@action=${this._handleAction}
|
||||||
@click=${preventDefault}
|
@click=${preventDefault}
|
||||||
>
|
>
|
||||||
|
@@ -180,11 +180,7 @@ export default class HaAutomationCondition extends LitElement {
|
|||||||
`
|
`
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<ha-button-menu
|
<ha-button-menu @action=${this._addCondition} .disabled=${this.disabled}>
|
||||||
fixed
|
|
||||||
@action=${this._addCondition}
|
|
||||||
.disabled=${this.disabled}
|
|
||||||
>
|
|
||||||
<ha-button
|
<ha-button
|
||||||
slot="trigger"
|
slot="trigger"
|
||||||
outlined
|
outlined
|
||||||
|
@@ -141,7 +141,7 @@ export class HaAutomationEditor extends KeyboardShortcutMixin(LitElement) {
|
|||||||
</mwc-button>
|
</mwc-button>
|
||||||
`
|
`
|
||||||
: ""}
|
: ""}
|
||||||
<ha-button-menu corner="BOTTOM_START" slot="toolbar-icon">
|
<ha-button-menu slot="toolbar-icon">
|
||||||
<ha-icon-button
|
<ha-icon-button
|
||||||
slot="trigger"
|
slot="trigger"
|
||||||
.label=${this.hass.localize("ui.common.menu")}
|
.label=${this.hass.localize("ui.common.menu")}
|
||||||
|
@@ -287,7 +287,6 @@ class HaAutomationPicker extends LitElement {
|
|||||||
></ha-icon-button>
|
></ha-icon-button>
|
||||||
<ha-button-related-filter-menu
|
<ha-button-related-filter-menu
|
||||||
slot="filter-menu"
|
slot="filter-menu"
|
||||||
corner="BOTTOM_START"
|
|
||||||
.narrow=${this.narrow}
|
.narrow=${this.narrow}
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
.value=${this._filterValue}
|
.value=${this._filterValue}
|
||||||
|
@@ -113,7 +113,7 @@ export class HaAutomationTrace extends LitElement {
|
|||||||
</a>
|
</a>
|
||||||
`
|
`
|
||||||
: ""}
|
: ""}
|
||||||
<ha-button-menu corner="BOTTOM_START" slot="toolbar-icon">
|
<ha-button-menu slot="toolbar-icon">
|
||||||
<ha-icon-button
|
<ha-icon-button
|
||||||
slot="trigger"
|
slot="trigger"
|
||||||
.label=${this.hass.localize("ui.common.menu")}
|
.label=${this.hass.localize("ui.common.menu")}
|
||||||
|
@@ -140,8 +140,6 @@ export default class HaAutomationTriggerRow extends LitElement {
|
|||||||
: html`
|
: html`
|
||||||
<ha-button-menu
|
<ha-button-menu
|
||||||
slot="icons"
|
slot="icons"
|
||||||
fixed
|
|
||||||
corner="BOTTOM_START"
|
|
||||||
@action=${this._handleAction}
|
@action=${this._handleAction}
|
||||||
@click=${preventDefault}
|
@click=${preventDefault}
|
||||||
>
|
>
|
||||||
|
@@ -168,7 +168,6 @@ class CloudAlexa extends LitElement {
|
|||||||
${!emptyFilter
|
${!emptyFilter
|
||||||
? html`${iconButton}`
|
? html`${iconButton}`
|
||||||
: html`<ha-button-menu
|
: html`<ha-button-menu
|
||||||
corner="BOTTOM_START"
|
|
||||||
.entityId=${stateObj.entity_id}
|
.entityId=${stateObj.entity_id}
|
||||||
@action=${this._exposeChanged}
|
@action=${this._exposeChanged}
|
||||||
>
|
>
|
||||||
@@ -225,7 +224,7 @@ class CloudAlexa extends LitElement {
|
|||||||
.narrow=${this.narrow}
|
.narrow=${this.narrow}
|
||||||
.header=${this.hass!.localize("ui.panel.config.cloud.alexa.title")}
|
.header=${this.hass!.localize("ui.panel.config.cloud.alexa.title")}
|
||||||
>
|
>
|
||||||
<ha-button-menu corner="BOTTOM_START" slot="toolbar-icon">
|
<ha-button-menu slot="toolbar-icon">
|
||||||
<ha-icon-button
|
<ha-icon-button
|
||||||
slot="trigger"
|
slot="trigger"
|
||||||
.label=${this.hass.localize("ui.common.menu")}
|
.label=${this.hass.localize("ui.common.menu")}
|
||||||
|
@@ -228,7 +228,6 @@ class CloudGoogleAssistant extends LitElement {
|
|||||||
${!emptyFilter
|
${!emptyFilter
|
||||||
? html`${iconButton}`
|
? html`${iconButton}`
|
||||||
: html`<ha-button-menu
|
: html`<ha-button-menu
|
||||||
corner="BOTTOM_START"
|
|
||||||
.entityId=${entity.entity_id}
|
.entityId=${entity.entity_id}
|
||||||
@action=${this._exposeChanged}
|
@action=${this._exposeChanged}
|
||||||
>
|
>
|
||||||
@@ -302,7 +301,7 @@ class CloudGoogleAssistant extends LitElement {
|
|||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
.header=${this.hass!.localize("ui.panel.config.cloud.google.title")}
|
.header=${this.hass!.localize("ui.panel.config.cloud.google.title")}
|
||||||
.narrow=${this.narrow}>
|
.narrow=${this.narrow}>
|
||||||
<ha-button-menu corner="BOTTOM_START" slot="toolbar-icon">
|
<ha-button-menu slot="toolbar-icon">
|
||||||
<ha-icon-button
|
<ha-icon-button
|
||||||
slot="trigger"
|
slot="trigger"
|
||||||
.label=${this.hass.localize("ui.common.menu")}
|
.label=${this.hass.localize("ui.common.menu")}
|
||||||
|
@@ -45,7 +45,7 @@ class ConfigAnalytics extends LitElement {
|
|||||||
<ha-analytics
|
<ha-analytics
|
||||||
translation_key_panel="config"
|
translation_key_panel="config"
|
||||||
@analytics-preferences-changed=${this._preferencesChanged}
|
@analytics-preferences-changed=${this._preferencesChanged}
|
||||||
.hass=${this.hass}
|
.localize=${this.hass.localize}
|
||||||
.analytics=${this._analyticsDetails}
|
.analytics=${this._analyticsDetails}
|
||||||
></ha-analytics>
|
></ha-analytics>
|
||||||
</div>
|
</div>
|
||||||
|
@@ -69,7 +69,7 @@ class HaConfigSectionUpdates extends LitElement {
|
|||||||
.path=${mdiUpdate}
|
.path=${mdiUpdate}
|
||||||
@click=${this._checkUpdates}
|
@click=${this._checkUpdates}
|
||||||
></ha-icon-button>
|
></ha-icon-button>
|
||||||
<ha-button-menu corner="BOTTOM_START" multi>
|
<ha-button-menu multi>
|
||||||
<ha-icon-button
|
<ha-icon-button
|
||||||
slot="trigger"
|
slot="trigger"
|
||||||
.label=${this.hass.localize("ui.common.menu")}
|
.label=${this.hass.localize("ui.common.menu")}
|
||||||
|
@@ -194,11 +194,7 @@ class HaConfigDashboard extends SubscribeMixin(LitElement) {
|
|||||||
.path=${mdiMagnify}
|
.path=${mdiMagnify}
|
||||||
@click=${this._showQuickBar}
|
@click=${this._showQuickBar}
|
||||||
></ha-icon-button>
|
></ha-icon-button>
|
||||||
<ha-button-menu
|
<ha-button-menu slot="actionItems" @action=${this._handleMenuAction}>
|
||||||
slot="actionItems"
|
|
||||||
corner="BOTTOM_START"
|
|
||||||
@action=${this._handleMenuAction}
|
|
||||||
>
|
|
||||||
<ha-icon-button
|
<ha-icon-button
|
||||||
slot="trigger"
|
slot="trigger"
|
||||||
.label=${this.hass.localize("ui.common.menu")}
|
.label=${this.hass.localize("ui.common.menu")}
|
||||||
|
@@ -748,7 +748,7 @@ export class HaConfigDevicePage extends LitElement {
|
|||||||
|
|
||||||
${actions.length
|
${actions.length
|
||||||
? html`
|
? html`
|
||||||
<ha-button-menu corner="BOTTOM_START">
|
<ha-button-menu>
|
||||||
<ha-icon-button
|
<ha-icon-button
|
||||||
slot="trigger"
|
slot="trigger"
|
||||||
.label=${this.hass.localize(
|
.label=${this.hass.localize(
|
||||||
|
@@ -448,7 +448,7 @@ export class HaConfigDeviceDashboard extends LitElement {
|
|||||||
>
|
>
|
||||||
<ha-svg-icon slot="icon" .path=${mdiPlus}></ha-svg-icon>
|
<ha-svg-icon slot="icon" .path=${mdiPlus}></ha-svg-icon>
|
||||||
</ha-fab>
|
</ha-fab>
|
||||||
<ha-button-menu slot="filter-menu" corner="BOTTOM_START" multi>
|
<ha-button-menu slot="filter-menu" multi>
|
||||||
<ha-icon-button
|
<ha-icon-button
|
||||||
slot="trigger"
|
slot="trigger"
|
||||||
.label=${this.hass!.localize(
|
.label=${this.hass!.localize(
|
||||||
|
@@ -663,7 +663,7 @@ export class EntityRegistrySettings extends SubscribeMixin(LitElement) {
|
|||||||
)}
|
)}
|
||||||
</ha-select>`
|
</ha-select>`
|
||||||
: ""}
|
: ""}
|
||||||
${this._helperConfigEntry
|
${this._helperConfigEntry && this._helperConfigEntry.supports_options
|
||||||
? html`
|
? html`
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<mwc-button
|
<mwc-button
|
||||||
|
@@ -620,7 +620,7 @@ export class HaConfigEntities extends SubscribeMixin(LitElement) {
|
|||||||
</div>
|
</div>
|
||||||
`
|
`
|
||||||
: html`
|
: html`
|
||||||
<ha-button-menu slot="filter-menu" corner="BOTTOM_START" multi>
|
<ha-button-menu slot="filter-menu" multi>
|
||||||
<ha-icon-button
|
<ha-icon-button
|
||||||
slot="trigger"
|
slot="trigger"
|
||||||
.label=${this.hass!.localize(
|
.label=${this.hass!.localize(
|
||||||
|
@@ -353,12 +353,9 @@ class HaPanelConfig extends HassRouterPage {
|
|||||||
tag: "ha-config-areas",
|
tag: "ha-config-areas",
|
||||||
load: () => import("./areas/ha-config-areas"),
|
load: () => import("./areas/ha-config-areas"),
|
||||||
},
|
},
|
||||||
voice_assistant: {
|
"voice-assistants": {
|
||||||
tag: "assist-pipeline-debug",
|
tag: "ha-config-voice-assistants",
|
||||||
load: () =>
|
load: () => import("./voice-assistants/ha-config-voice-assistants"),
|
||||||
import(
|
|
||||||
"./integrations/integration-panels/voice_assistant/assist/assist-pipeline-debug"
|
|
||||||
),
|
|
||||||
},
|
},
|
||||||
automation: {
|
automation: {
|
||||||
tag: "ha-config-automation",
|
tag: "ha-config-automation",
|
||||||
|
@@ -60,7 +60,7 @@ export class HaConfigFlowCard extends LitElement {
|
|||||||
}`
|
}`
|
||||||
)}
|
)}
|
||||||
></mwc-button>
|
></mwc-button>
|
||||||
<ha-button-menu corner="BOTTOM_START">
|
<ha-button-menu>
|
||||||
<ha-icon-button
|
<ha-icon-button
|
||||||
slot="trigger"
|
slot="trigger"
|
||||||
.label=${this.hass.localize("ui.common.menu")}
|
.label=${this.hass.localize("ui.common.menu")}
|
||||||
|
@@ -382,7 +382,6 @@ class HaConfigIntegrations extends SubscribeMixin(LitElement) {
|
|||||||
? html`<span class="badge">${disabledCount}</span>`
|
? html`<span class="badge">${disabledCount}</span>`
|
||||||
: ""}
|
: ""}
|
||||||
<ha-button-menu
|
<ha-button-menu
|
||||||
corner="BOTTOM_START"
|
|
||||||
multi
|
multi
|
||||||
@action=${this._handleMenuAction}
|
@action=${this._handleMenuAction}
|
||||||
@click=${this._preventDefault}
|
@click=${this._preventDefault}
|
||||||
@@ -454,9 +453,9 @@ class HaConfigIntegrations extends SubscribeMixin(LitElement) {
|
|||||||
"ui.panel.config.integrations.search"
|
"ui.panel.config.integrations.search"
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
${!this._showDisabled && disabledCount
|
<div class="filters" slot="suffix">
|
||||||
? html`<div class="filters" slot="suffix">
|
${!this._showDisabled && disabledCount
|
||||||
<div
|
? html`<div
|
||||||
class="active-filters"
|
class="active-filters"
|
||||||
@click=${this._preventDefault}
|
@click=${this._preventDefault}
|
||||||
>
|
>
|
||||||
@@ -470,10 +469,10 @@ class HaConfigIntegrations extends SubscribeMixin(LitElement) {
|
|||||||
"ui.panel.config.integrations.disable.show"
|
"ui.panel.config.integrations.disable.show"
|
||||||
)}
|
)}
|
||||||
></mwc-button>
|
></mwc-button>
|
||||||
</div>
|
</div>`
|
||||||
${filterMenu}
|
: ""}
|
||||||
</div>`
|
${filterMenu}
|
||||||
: ""}
|
</div>
|
||||||
</search-input>
|
</search-input>
|
||||||
</div>
|
</div>
|
||||||
`}
|
`}
|
||||||
|
@@ -370,7 +370,7 @@ export class HaIntegrationCard extends LitElement {
|
|||||||
`
|
`
|
||||||
: ""}
|
: ""}
|
||||||
</div>
|
</div>
|
||||||
<ha-button-menu corner="BOTTOM_START">
|
<ha-button-menu>
|
||||||
<ha-icon-button
|
<ha-icon-button
|
||||||
slot="trigger"
|
slot="trigger"
|
||||||
.label=${this.hass.localize("ui.common.menu")}
|
.label=${this.hass.localize("ui.common.menu")}
|
||||||
|
@@ -12,7 +12,7 @@ export class HaIntegrationOverflowMenu extends LitElement {
|
|||||||
|
|
||||||
protected render() {
|
protected render() {
|
||||||
return html`
|
return html`
|
||||||
<ha-button-menu activatable corner="BOTTOM_START">
|
<ha-button-menu activatable>
|
||||||
<ha-icon-button
|
<ha-icon-button
|
||||||
slot="trigger"
|
slot="trigger"
|
||||||
.label=${this.hass.localize("ui.common.menu")}
|
.label=${this.hass.localize("ui.common.menu")}
|
||||||
|
@@ -69,7 +69,7 @@ export class ThreadConfigPanel extends SubscribeMixin(LitElement) {
|
|||||||
|
|
||||||
return html`
|
return html`
|
||||||
<hass-subpage .narrow=${this.narrow} .hass=${this.hass} header="Thread">
|
<hass-subpage .narrow=${this.narrow} .hass=${this.hass} header="Thread">
|
||||||
<ha-button-menu slot="toolbar-icon" corner="BOTTOM_START">
|
<ha-button-menu slot="toolbar-icon">
|
||||||
<ha-icon-button
|
<ha-icon-button
|
||||||
.path=${mdiDotsVertical}
|
.path=${mdiDotsVertical}
|
||||||
slot="trigger"
|
slot="trigger"
|
||||||
@@ -186,7 +186,6 @@ export class ThreadConfigPanel extends SubscribeMixin(LitElement) {
|
|||||||
<span slot="secondary">${router.server}</span>
|
<span slot="secondary">${router.server}</span>
|
||||||
${router.extended_address === this._otbrInfo?.extended_address
|
${router.extended_address === this._otbrInfo?.extended_address
|
||||||
? html`<ha-button-menu
|
? html`<ha-button-menu
|
||||||
corner="BOTTOM_START"
|
|
||||||
slot="meta"
|
slot="meta"
|
||||||
@action=${this._handleRouterAction}
|
@action=${this._handleRouterAction}
|
||||||
>
|
>
|
||||||
@@ -251,6 +250,7 @@ export class ThreadConfigPanel extends SubscribeMixin(LitElement) {
|
|||||||
showAlertDialog(this, {
|
showAlertDialog(this, {
|
||||||
title: dataset.network_name,
|
title: dataset.network_name,
|
||||||
text: html`Network name: ${dataset.network_name}<br />
|
text: html`Network name: ${dataset.network_name}<br />
|
||||||
|
Channel: ${dataset.channel}<br />
|
||||||
Dataset id: ${dataset.dataset_id}<br />
|
Dataset id: ${dataset.dataset_id}<br />
|
||||||
Pan id: ${dataset.pan_id}<br />
|
Pan id: ${dataset.pan_id}<br />
|
||||||
Extended Pan id: ${dataset.extended_pan_id}<br />
|
Extended Pan id: ${dataset.extended_pan_id}<br />
|
||||||
@@ -263,6 +263,7 @@ export class ThreadConfigPanel extends SubscribeMixin(LitElement) {
|
|||||||
showAlertDialog(this, {
|
showAlertDialog(this, {
|
||||||
title: dataset.network_name,
|
title: dataset.network_name,
|
||||||
text: html`Network name: ${dataset.network_name}<br />
|
text: html`Network name: ${dataset.network_name}<br />
|
||||||
|
Channel: ${dataset.channel}<br />
|
||||||
Dataset id: ${dataset.dataset_id}<br />
|
Dataset id: ${dataset.dataset_id}<br />
|
||||||
Pan id: ${dataset.pan_id}<br />
|
Pan id: ${dataset.pan_id}<br />
|
||||||
Extended Pan id: ${dataset.extended_pan_id}`,
|
Extended Pan id: ${dataset.extended_pan_id}`,
|
||||||
|
@@ -1,20 +1,25 @@
|
|||||||
import { css, html, LitElement, PropertyValues, TemplateResult } from "lit";
|
import { css, html, LitElement, TemplateResult } from "lit";
|
||||||
import { customElement, property, query, state } from "lit/decorators";
|
import { customElement, property, query, state } from "lit/decorators";
|
||||||
import "../../../../../../components/ha-button";
|
import "../../../../../../components/ha-button";
|
||||||
import {
|
import {
|
||||||
PipelineRun,
|
PipelineRun,
|
||||||
runPipelineFromText,
|
PipelineRunOptions,
|
||||||
|
runVoiceAssistantPipeline,
|
||||||
} from "../../../../../../data/voice_assistant";
|
} from "../../../../../../data/voice_assistant";
|
||||||
import "../../../../../../layouts/hass-subpage";
|
import "../../../../../../layouts/hass-subpage";
|
||||||
import "../../../../../../components/ha-formfield";
|
import "../../../../../../components/ha-formfield";
|
||||||
import "../../../../../../components/ha-checkbox";
|
import "../../../../../../components/ha-checkbox";
|
||||||
import { haStyle } from "../../../../../../resources/styles";
|
import { haStyle } from "../../../../../../resources/styles";
|
||||||
import type { HomeAssistant } from "../../../../../../types";
|
import type { HomeAssistant } from "../../../../../../types";
|
||||||
import { showPromptDialog } from "../../../../../../dialogs/generic/show-dialog-box";
|
import {
|
||||||
|
showAlertDialog,
|
||||||
|
showPromptDialog,
|
||||||
|
} from "../../../../../../dialogs/generic/show-dialog-box";
|
||||||
import "./assist-render-pipeline-run";
|
import "./assist-render-pipeline-run";
|
||||||
import type { HaCheckbox } from "../../../../../../components/ha-checkbox";
|
import type { HaCheckbox } from "../../../../../../components/ha-checkbox";
|
||||||
import type { HaTextField } from "../../../../../../components/ha-textfield";
|
import type { HaTextField } from "../../../../../../components/ha-textfield";
|
||||||
import "../../../../../../components/ha-textfield";
|
import "../../../../../../components/ha-textfield";
|
||||||
|
import { fileDownload } from "../../../../../../util/file_download";
|
||||||
|
|
||||||
@customElement("assist-pipeline-debug")
|
@customElement("assist-pipeline-debug")
|
||||||
export class AssistPipelineDebug extends LitElement {
|
export class AssistPipelineDebug extends LitElement {
|
||||||
@@ -24,8 +29,6 @@ export class AssistPipelineDebug extends LitElement {
|
|||||||
|
|
||||||
@state() private _pipelineRuns: PipelineRun[] = [];
|
@state() private _pipelineRuns: PipelineRun[] = [];
|
||||||
|
|
||||||
@state() private _stopRecording?: () => void;
|
|
||||||
|
|
||||||
@query("#continue-conversation")
|
@query("#continue-conversation")
|
||||||
private _continueConversationCheckbox!: HaCheckbox;
|
private _continueConversationCheckbox!: HaCheckbox;
|
||||||
|
|
||||||
@@ -36,6 +39,8 @@ export class AssistPipelineDebug extends LitElement {
|
|||||||
|
|
||||||
@state() private _finished = false;
|
@state() private _finished = false;
|
||||||
|
|
||||||
|
@state() private _languageOverride?: string;
|
||||||
|
|
||||||
protected render(): TemplateResult {
|
protected render(): TemplateResult {
|
||||||
return html`
|
return html`
|
||||||
<hass-subpage
|
<hass-subpage
|
||||||
@@ -52,8 +57,18 @@ export class AssistPipelineDebug extends LitElement {
|
|||||||
>
|
>
|
||||||
Clear
|
Clear
|
||||||
</ha-button>
|
</ha-button>
|
||||||
|
<ha-button
|
||||||
|
slot="toolbar-icon"
|
||||||
|
@click=${this._downloadConversation}
|
||||||
|
>
|
||||||
|
Download
|
||||||
|
</ha-button>
|
||||||
`
|
`
|
||||||
: ""}
|
: html`
|
||||||
|
<ha-button slot="toolbar-icon" @click=${this._setLanguage}>
|
||||||
|
Set Language
|
||||||
|
</ha-button>
|
||||||
|
`}
|
||||||
|
|
||||||
<div class="content">
|
<div class="content">
|
||||||
<div class="start-row">
|
<div class="start-row">
|
||||||
@@ -81,6 +96,12 @@ export class AssistPipelineDebug extends LitElement {
|
|||||||
Send
|
Send
|
||||||
</ha-button>
|
</ha-button>
|
||||||
`
|
`
|
||||||
|
: this._finished
|
||||||
|
? html`
|
||||||
|
<ha-button @click=${this._runAudioPipeline}>
|
||||||
|
Continue talking
|
||||||
|
</ha-button>
|
||||||
|
`
|
||||||
: html`
|
: html`
|
||||||
<ha-formfield label="Continue conversation">
|
<ha-formfield label="Continue conversation">
|
||||||
<ha-checkbox
|
<ha-checkbox
|
||||||
@@ -106,53 +127,6 @@ export class AssistPipelineDebug extends LitElement {
|
|||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected willUpdate(changedProperties: PropertyValues): void {
|
|
||||||
super.willUpdate(changedProperties);
|
|
||||||
|
|
||||||
if (
|
|
||||||
!changedProperties.has("_pipelineRuns") ||
|
|
||||||
this._pipelineRuns.length === 0
|
|
||||||
) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const currentRun = this._pipelineRuns[0];
|
|
||||||
|
|
||||||
if (currentRun.init_options.start_stage !== "stt") {
|
|
||||||
if (["error", "done"].includes(currentRun.stage)) {
|
|
||||||
this._finished = true;
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (currentRun.stage === "stt" && this._audioBuffer) {
|
|
||||||
// Send the buffer over the WS to the STT engine.
|
|
||||||
for (const buffer of this._audioBuffer) {
|
|
||||||
this._sendAudioChunk(buffer);
|
|
||||||
}
|
|
||||||
this._audioBuffer = undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (currentRun.stage !== "stt" && this._stopRecording) {
|
|
||||||
this._stopRecording();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (currentRun.stage === "done") {
|
|
||||||
const url = currentRun.tts!.tts_output!.url;
|
|
||||||
const audio = new Audio(url);
|
|
||||||
audio.addEventListener("ended", () => {
|
|
||||||
if (this._continueConversationCheckbox.checked) {
|
|
||||||
this._runAudioPipeline();
|
|
||||||
} else {
|
|
||||||
this._finished = true;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
audio.play();
|
|
||||||
} else if (currentRun.stage === "error") {
|
|
||||||
this._finished = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private get conversationId(): string | null {
|
private get conversationId(): string | null {
|
||||||
return this._pipelineRuns.length === 0
|
return this._pipelineRuns.length === 0
|
||||||
? null
|
? null
|
||||||
@@ -177,26 +151,19 @@ export class AssistPipelineDebug extends LitElement {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let added = false;
|
await this._doRunPipeline(
|
||||||
runPipelineFromText(
|
|
||||||
this.hass,
|
|
||||||
(run) => {
|
(run) => {
|
||||||
if (textfield && ["done", "error"].includes(run.stage)) {
|
if (["done", "error"].includes(run.stage)) {
|
||||||
textfield.value = "";
|
this._finished = true;
|
||||||
}
|
if (textfield) {
|
||||||
|
textfield.value = "";
|
||||||
if (added) {
|
}
|
||||||
this._pipelineRuns = [run, ...this._pipelineRuns.slice(1)];
|
|
||||||
} else {
|
|
||||||
this._pipelineRuns = [run, ...this._pipelineRuns];
|
|
||||||
added = true;
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
start_stage: "intent",
|
start_stage: "intent",
|
||||||
end_stage: "intent",
|
end_stage: "intent",
|
||||||
input: { text },
|
input: { text },
|
||||||
conversation_id: this.conversationId,
|
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -204,7 +171,13 @@ export class AssistPipelineDebug extends LitElement {
|
|||||||
private async _runAudioPipeline() {
|
private async _runAudioPipeline() {
|
||||||
// @ts-ignore-next-line
|
// @ts-ignore-next-line
|
||||||
const context = new (window.AudioContext || window.webkitAudioContext)();
|
const context = new (window.AudioContext || window.webkitAudioContext)();
|
||||||
const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
|
let stream: MediaStream;
|
||||||
|
try {
|
||||||
|
stream = await navigator.mediaDevices.getUserMedia({ audio: true });
|
||||||
|
} catch (err) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
await context.audioWorklet.addModule(
|
await context.audioWorklet.addModule(
|
||||||
new URL("./recorder.worklet.js", import.meta.url)
|
new URL("./recorder.worklet.js", import.meta.url)
|
||||||
);
|
);
|
||||||
@@ -213,47 +186,111 @@ export class AssistPipelineDebug extends LitElement {
|
|||||||
const recorder = new AudioWorkletNode(context, "recorder.worklet");
|
const recorder = new AudioWorkletNode(context, "recorder.worklet");
|
||||||
|
|
||||||
this.hass.connection.socket!.binaryType = "arraybuffer";
|
this.hass.connection.socket!.binaryType = "arraybuffer";
|
||||||
this._stopRecording = () => {
|
|
||||||
|
let run: PipelineRun | undefined;
|
||||||
|
|
||||||
|
let stopRecording: (() => void) | undefined = () => {
|
||||||
|
stopRecording = undefined;
|
||||||
|
// We're currently STTing, so finish audio
|
||||||
|
if (run?.stage === "stt" && run.stt!.done === false) {
|
||||||
|
if (this._audioBuffer) {
|
||||||
|
for (const chunk of this._audioBuffer) {
|
||||||
|
this._sendAudioChunk(chunk);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Send empty message to indicate we're done streaming.
|
||||||
|
this._sendAudioChunk(new Int16Array());
|
||||||
|
}
|
||||||
|
this._audioBuffer = undefined;
|
||||||
stream.getTracks()[0].stop();
|
stream.getTracks()[0].stop();
|
||||||
context.close();
|
context.close();
|
||||||
this._stopRecording = undefined;
|
|
||||||
this._audioBuffer = undefined;
|
|
||||||
// Send empty message to indicate we're done streaming.
|
|
||||||
this._sendAudioChunk(new Int16Array());
|
|
||||||
};
|
};
|
||||||
this._audioBuffer = [];
|
this._audioBuffer = [];
|
||||||
source.connect(recorder).connect(context.destination);
|
source.connect(recorder).connect(context.destination);
|
||||||
recorder.port.onmessage = (e) => {
|
recorder.port.onmessage = (e) => {
|
||||||
|
if (!stopRecording) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (this._audioBuffer) {
|
if (this._audioBuffer) {
|
||||||
this._audioBuffer.push(e.data);
|
this._audioBuffer.push(e.data);
|
||||||
return;
|
} else {
|
||||||
|
this._sendAudioChunk(e.data);
|
||||||
}
|
}
|
||||||
if (this._pipelineRuns[0].stage !== "stt") {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this._sendAudioChunk(e.data);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
this._finished = false;
|
await this._doRunPipeline(
|
||||||
let added = false;
|
(updatedRun) => {
|
||||||
runPipelineFromText(
|
run = updatedRun;
|
||||||
this.hass,
|
|
||||||
(run) => {
|
// When we start STT stage, the WS has a binary handler
|
||||||
if (added) {
|
if (updatedRun.stage === "stt" && this._audioBuffer) {
|
||||||
this._pipelineRuns = [run, ...this._pipelineRuns.slice(1)];
|
// Send the buffer over the WS to the STT engine.
|
||||||
} else {
|
for (const buffer of this._audioBuffer) {
|
||||||
this._pipelineRuns = [run, ...this._pipelineRuns];
|
this._sendAudioChunk(buffer);
|
||||||
added = true;
|
}
|
||||||
|
this._audioBuffer = undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stop recording if the server is done with STT stage
|
||||||
|
if (!["ready", "stt"].includes(updatedRun.stage) && stopRecording) {
|
||||||
|
stopRecording();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Play audio when we're done.
|
||||||
|
if (updatedRun.stage === "done") {
|
||||||
|
const url = updatedRun.tts!.tts_output!.url;
|
||||||
|
const audio = new Audio(url);
|
||||||
|
audio.addEventListener("ended", () => {
|
||||||
|
if (this._continueConversationCheckbox.checked) {
|
||||||
|
this._runAudioPipeline();
|
||||||
|
} else {
|
||||||
|
this._finished = true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
audio.play();
|
||||||
|
} else if (updatedRun.stage === "error") {
|
||||||
|
this._finished = true;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
start_stage: "stt",
|
start_stage: "stt",
|
||||||
end_stage: "tts",
|
end_stage: "tts",
|
||||||
conversation_id: this.conversationId,
|
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async _doRunPipeline(
|
||||||
|
callback: (event: PipelineRun) => void,
|
||||||
|
options: PipelineRunOptions
|
||||||
|
) {
|
||||||
|
this._finished = false;
|
||||||
|
let added = false;
|
||||||
|
try {
|
||||||
|
await runVoiceAssistantPipeline(
|
||||||
|
this.hass,
|
||||||
|
(updatedRun) => {
|
||||||
|
if (added) {
|
||||||
|
this._pipelineRuns = [updatedRun, ...this._pipelineRuns.slice(1)];
|
||||||
|
} else {
|
||||||
|
this._pipelineRuns = [updatedRun, ...this._pipelineRuns];
|
||||||
|
added = true;
|
||||||
|
}
|
||||||
|
callback(updatedRun);
|
||||||
|
},
|
||||||
|
{
|
||||||
|
...options,
|
||||||
|
language: this._languageOverride,
|
||||||
|
conversation_id: this.conversationId,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
} catch (err: any) {
|
||||||
|
await showAlertDialog(this, {
|
||||||
|
title: "Error starting pipeline",
|
||||||
|
text: err.message || err,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private _sendAudioChunk(chunk: Int16Array) {
|
private _sendAudioChunk(chunk: Int16Array) {
|
||||||
// Turn into 8 bit so we can prefix our handler ID.
|
// Turn into 8 bit so we can prefix our handler ID.
|
||||||
const data = new Uint8Array(1 + chunk.length * 2);
|
const data = new Uint8Array(1 + chunk.length * 2);
|
||||||
@@ -273,6 +310,27 @@ export class AssistPipelineDebug extends LitElement {
|
|||||||
this._pipelineRuns = [];
|
this._pipelineRuns = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private _downloadConversation() {
|
||||||
|
fileDownload(
|
||||||
|
`data:text/plain;charset=utf-8,${encodeURIComponent(
|
||||||
|
JSON.stringify(this._pipelineRuns, null, 2)
|
||||||
|
)}`,
|
||||||
|
`conversation.json`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async _setLanguage() {
|
||||||
|
const language = await showPromptDialog(this, {
|
||||||
|
title: "Language override",
|
||||||
|
inputLabel: "Language",
|
||||||
|
inputType: "text",
|
||||||
|
confirmText: "Set",
|
||||||
|
});
|
||||||
|
if (language) {
|
||||||
|
this._languageOverride = language;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static styles = [
|
static styles = [
|
||||||
haStyle,
|
haStyle,
|
||||||
css`
|
css`
|
||||||
|
@@ -50,9 +50,11 @@ const maybeRenderError = (
|
|||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
return html`<ha-alert alert-type="error">
|
return html`
|
||||||
${run.error!.message} (${run.error!.code})
|
<ha-alert alert-type="error">
|
||||||
</ha-alert>`;
|
${run.error!.message} (${run.error!.code})
|
||||||
|
</ha-alert>
|
||||||
|
`;
|
||||||
};
|
};
|
||||||
|
|
||||||
const renderProgress = (
|
const renderProgress = (
|
||||||
@@ -76,10 +78,9 @@ const renderProgress = (
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!finishEvent) {
|
if (!finishEvent) {
|
||||||
return html`<ha-circular-progress
|
return html`
|
||||||
size="tiny"
|
<ha-circular-progress size="tiny" active></ha-circular-progress>
|
||||||
active
|
`;
|
||||||
></ha-circular-progress>`;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const duration =
|
const duration =
|
||||||
@@ -109,7 +110,7 @@ const dataMinusKeysRender = (
|
|||||||
const result = {};
|
const result = {};
|
||||||
let render = false;
|
let render = false;
|
||||||
for (const key in data) {
|
for (const key in data) {
|
||||||
if (key in keys) {
|
if (key in keys || key === "done") {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
render = true;
|
render = true;
|
||||||
|
@@ -115,7 +115,7 @@ export class HaConfigLogs extends LitElement {
|
|||||||
${isComponentLoaded(this.hass, "hassio") &&
|
${isComponentLoaded(this.hass, "hassio") &&
|
||||||
this.hass.userData?.showAdvanced
|
this.hass.userData?.showAdvanced
|
||||||
? html`
|
? html`
|
||||||
<ha-button-menu corner="BOTTOM_START" slot="toolbar-icon">
|
<ha-button-menu slot="toolbar-icon">
|
||||||
<ha-button
|
<ha-button
|
||||||
slot="trigger"
|
slot="trigger"
|
||||||
.label=${this._logProviders.find(
|
.label=${this._logProviders.find(
|
||||||
|
@@ -267,11 +267,7 @@ export class HaConfigLovelaceDashboards extends LitElement {
|
|||||||
>
|
>
|
||||||
${this.hass.userData?.showAdvanced
|
${this.hass.userData?.showAdvanced
|
||||||
? html`
|
? html`
|
||||||
<ha-button-menu
|
<ha-button-menu slot="toolbar-icon" activatable>
|
||||||
corner="BOTTOM_START"
|
|
||||||
slot="toolbar-icon"
|
|
||||||
activatable
|
|
||||||
>
|
|
||||||
<ha-icon-button
|
<ha-icon-button
|
||||||
slot="trigger"
|
slot="trigger"
|
||||||
.label=${this.hass.localize("ui.common.menu")}
|
.label=${this.hass.localize("ui.common.menu")}
|
||||||
|
@@ -248,7 +248,7 @@ export class HassioNetwork extends LitElement {
|
|||||||
</ha-circular-progress>`
|
</ha-circular-progress>`
|
||||||
: this.hass.localize("ui.common.save")}
|
: this.hass.localize("ui.common.save")}
|
||||||
</mwc-button>
|
</mwc-button>
|
||||||
<ha-button-menu corner="BOTTOM_START" @action=${this._handleAction}>
|
<ha-button-menu @action=${this._handleAction}>
|
||||||
<ha-icon-button
|
<ha-icon-button
|
||||||
slot="trigger"
|
slot="trigger"
|
||||||
.label=${"ui.common.menu"}
|
.label=${"ui.common.menu"}
|
||||||
|
@@ -79,7 +79,7 @@ class HaConfigRepairsDashboard extends SubscribeMixin(LitElement) {
|
|||||||
.header=${this.hass.localize("ui.panel.config.repairs.caption")}
|
.header=${this.hass.localize("ui.panel.config.repairs.caption")}
|
||||||
>
|
>
|
||||||
<div slot="toolbar-icon">
|
<div slot="toolbar-icon">
|
||||||
<ha-button-menu corner="BOTTOM_START" multi>
|
<ha-button-menu multi>
|
||||||
<ha-icon-button
|
<ha-icon-button
|
||||||
slot="trigger"
|
slot="trigger"
|
||||||
.label=${this.hass.localize("ui.common.menu")}
|
.label=${this.hass.localize("ui.common.menu")}
|
||||||
|
@@ -225,7 +225,6 @@ class HaSceneDashboard extends LitElement {
|
|||||||
></ha-icon-button>
|
></ha-icon-button>
|
||||||
<ha-button-related-filter-menu
|
<ha-button-related-filter-menu
|
||||||
slot="filter-menu"
|
slot="filter-menu"
|
||||||
corner="BOTTOM_START"
|
|
||||||
.narrow=${this.narrow}
|
.narrow=${this.narrow}
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
.value=${this._filterValue}
|
.value=${this._filterValue}
|
||||||
|
@@ -228,7 +228,6 @@ export class HaSceneEditor extends SubscribeMixin(
|
|||||||
: this.hass.localize("ui.panel.config.scene.editor.default_name")}
|
: this.hass.localize("ui.panel.config.scene.editor.default_name")}
|
||||||
>
|
>
|
||||||
<ha-button-menu
|
<ha-button-menu
|
||||||
corner="BOTTOM_START"
|
|
||||||
slot="toolbar-icon"
|
slot="toolbar-icon"
|
||||||
@action=${this._handleMenuAction}
|
@action=${this._handleMenuAction}
|
||||||
activatable
|
activatable
|
||||||
|
@@ -191,7 +191,7 @@ export class HaScriptEditor extends KeyboardShortcutMixin(LitElement) {
|
|||||||
</mwc-button>
|
</mwc-button>
|
||||||
`
|
`
|
||||||
: ""}
|
: ""}
|
||||||
<ha-button-menu corner="BOTTOM_START" slot="toolbar-icon">
|
<ha-button-menu slot="toolbar-icon">
|
||||||
<ha-icon-button
|
<ha-icon-button
|
||||||
slot="trigger"
|
slot="trigger"
|
||||||
.label=${this.hass.localize("ui.common.menu")}
|
.label=${this.hass.localize("ui.common.menu")}
|
||||||
|
@@ -227,7 +227,6 @@ class HaScriptPicker extends LitElement {
|
|||||||
></ha-icon-button>
|
></ha-icon-button>
|
||||||
<ha-button-related-filter-menu
|
<ha-button-related-filter-menu
|
||||||
slot="filter-menu"
|
slot="filter-menu"
|
||||||
corner="BOTTOM_START"
|
|
||||||
.narrow=${this.narrow}
|
.narrow=${this.narrow}
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
.value=${this._filterValue}
|
.value=${this._filterValue}
|
||||||
|
@@ -116,7 +116,7 @@ export class HaScriptTrace extends LitElement {
|
|||||||
`
|
`
|
||||||
: ""}
|
: ""}
|
||||||
|
|
||||||
<ha-button-menu corner="BOTTOM_START" slot="toolbar-icon">
|
<ha-button-menu slot="toolbar-icon">
|
||||||
<ha-icon-button
|
<ha-icon-button
|
||||||
slot="trigger"
|
slot="trigger"
|
||||||
.label=${this.hass.localize("ui.common.menu")}
|
.label=${this.hass.localize("ui.common.menu")}
|
||||||
|
@@ -22,6 +22,7 @@ import {
|
|||||||
import { showAlertDialog } from "../../../dialogs/generic/show-dialog-box";
|
import { showAlertDialog } from "../../../dialogs/generic/show-dialog-box";
|
||||||
import { haStyle, haStyleDialog } from "../../../resources/styles";
|
import { haStyle, haStyleDialog } from "../../../resources/styles";
|
||||||
import { HomeAssistant } from "../../../types";
|
import { HomeAssistant } from "../../../types";
|
||||||
|
import { bytesToString } from "../../../util/bytes-to-string";
|
||||||
import { MoveDatadiskDialogParams } from "./show-dialog-move-datadisk";
|
import { MoveDatadiskDialogParams } from "./show-dialog-move-datadisk";
|
||||||
|
|
||||||
const calculateMoveTime = memoizeOne((hostInfo: HassioHostInfo): number => {
|
const calculateMoveTime = memoizeOne((hostInfo: HassioHostInfo): number => {
|
||||||
@@ -39,7 +40,7 @@ class MoveDatadiskDialog extends LitElement {
|
|||||||
|
|
||||||
@state() private _selectedDevice?: string;
|
@state() private _selectedDevice?: string;
|
||||||
|
|
||||||
@state() private _devices?: DatadiskList["devices"];
|
@state() private _disks?: DatadiskList["disks"];
|
||||||
|
|
||||||
@state() private _osInfo?: HassioHassOSInfo;
|
@state() private _osInfo?: HassioHassOSInfo;
|
||||||
|
|
||||||
@@ -55,7 +56,7 @@ class MoveDatadiskDialog extends LitElement {
|
|||||||
|
|
||||||
const data = await listDatadisks(this.hass);
|
const data = await listDatadisks(this.hass);
|
||||||
if (data.devices.length > 0) {
|
if (data.devices.length > 0) {
|
||||||
this._devices = data.devices;
|
this._disks = data.disks;
|
||||||
} else {
|
} else {
|
||||||
this.closeDialog();
|
this.closeDialog();
|
||||||
await showAlertDialog(this, {
|
await showAlertDialog(this, {
|
||||||
@@ -80,7 +81,7 @@ class MoveDatadiskDialog extends LitElement {
|
|||||||
|
|
||||||
public closeDialog(): void {
|
public closeDialog(): void {
|
||||||
this._selectedDevice = undefined;
|
this._selectedDevice = undefined;
|
||||||
this._devices = undefined;
|
this._disks = undefined;
|
||||||
this._moving = false;
|
this._moving = false;
|
||||||
this._hostInfo = undefined;
|
this._hostInfo = undefined;
|
||||||
this._osInfo = undefined;
|
this._osInfo = undefined;
|
||||||
@@ -88,7 +89,7 @@ class MoveDatadiskDialog extends LitElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected render() {
|
protected render() {
|
||||||
if (!this._hostInfo || !this._osInfo || !this._devices) {
|
if (!this._hostInfo || !this._osInfo || !this._disks) {
|
||||||
return nothing;
|
return nothing;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -132,10 +133,19 @@ class MoveDatadiskDialog extends LitElement {
|
|||||||
dialogInitialFocus
|
dialogInitialFocus
|
||||||
fixedMenuPosition
|
fixedMenuPosition
|
||||||
>
|
>
|
||||||
${this._devices.map(
|
${this._disks.map(
|
||||||
(device) =>
|
(disk) =>
|
||||||
html`<mwc-list-item .value=${device}>
|
html`<mwc-list-item twoline .value=${disk.id}>
|
||||||
${device}
|
<span>${disk.vendor} ${disk.model}</span>
|
||||||
|
<span slot="secondary">
|
||||||
|
${this.hass.localize(
|
||||||
|
"ui.panel.config.storage.datadisk.extra_information",
|
||||||
|
{
|
||||||
|
size: bytesToString(disk.size),
|
||||||
|
serial: disk.serial,
|
||||||
|
}
|
||||||
|
)}
|
||||||
|
</span>
|
||||||
</mwc-list-item>`
|
</mwc-list-item>`
|
||||||
)}
|
)}
|
||||||
</ha-select>
|
</ha-select>
|
||||||
|
@@ -44,7 +44,7 @@ class HaConfigSectionStorage extends LitElement {
|
|||||||
>
|
>
|
||||||
${this._hostInfo
|
${this._hostInfo
|
||||||
? html`
|
? html`
|
||||||
<ha-button-menu corner="BOTTOM_START" slot="toolbar-icon">
|
<ha-button-menu slot="toolbar-icon">
|
||||||
<ha-icon-button
|
<ha-icon-button
|
||||||
slot="trigger"
|
slot="trigger"
|
||||||
.label=${this.hass.localize("ui.common.menu")}
|
.label=${this.hass.localize("ui.common.menu")}
|
||||||
|
@@ -0,0 +1,41 @@
|
|||||||
|
import { customElement, property } from "lit/decorators";
|
||||||
|
import {
|
||||||
|
HassRouterPage,
|
||||||
|
RouterOptions,
|
||||||
|
} from "../../../layouts/hass-router-page";
|
||||||
|
import { HomeAssistant } from "../../../types";
|
||||||
|
|
||||||
|
@customElement("ha-config-voice-assistants")
|
||||||
|
class HaConfigVoiceAssistants extends HassRouterPage {
|
||||||
|
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||||
|
|
||||||
|
@property() public narrow!: boolean;
|
||||||
|
|
||||||
|
@property() public isWide!: boolean;
|
||||||
|
|
||||||
|
protected routerOptions: RouterOptions = {
|
||||||
|
defaultPage: "debug",
|
||||||
|
routes: {
|
||||||
|
debug: {
|
||||||
|
tag: "assist-pipeline-debug",
|
||||||
|
load: () =>
|
||||||
|
import(
|
||||||
|
"../integrations/integration-panels/voice_assistant/assist/assist-pipeline-debug"
|
||||||
|
),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
protected updatePageEl(pageEl) {
|
||||||
|
pageEl.hass = this.hass;
|
||||||
|
pageEl.narrow = this.narrow;
|
||||||
|
pageEl.isWide = this.isWide;
|
||||||
|
pageEl.route = this.routeTail;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface HTMLElementTagNameMap {
|
||||||
|
"ha-config-voice-assistants": HaConfigVoiceAssistants;
|
||||||
|
}
|
||||||
|
}
|
@@ -515,22 +515,7 @@ class HaPanelHistory extends SubscribeMixin(LitElement) {
|
|||||||
css`
|
css`
|
||||||
.content {
|
.content {
|
||||||
padding: 0 16px 16px;
|
padding: 0 16px 16px;
|
||||||
}
|
padding-bottom: max(env(safe-area-inset-bottom), 16px);
|
||||||
|
|
||||||
state-history-charts {
|
|
||||||
height: calc(100vh - 136px);
|
|
||||||
}
|
|
||||||
|
|
||||||
:host([narrow]) state-history-charts {
|
|
||||||
height: calc(100vh - 198px);
|
|
||||||
}
|
|
||||||
|
|
||||||
.progress-wrapper {
|
|
||||||
height: calc(100vh - 136px);
|
|
||||||
}
|
|
||||||
|
|
||||||
:host([narrow]) .progress-wrapper {
|
|
||||||
height: calc(100vh - 198px);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
:host([virtualize]) {
|
:host([virtualize]) {
|
||||||
@@ -539,6 +524,10 @@ class HaPanelHistory extends SubscribeMixin(LitElement) {
|
|||||||
|
|
||||||
.progress-wrapper {
|
.progress-wrapper {
|
||||||
position: relative;
|
position: relative;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
flex-direction: column;
|
||||||
|
padding: 16px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.filters {
|
.filters {
|
||||||
@@ -566,13 +555,6 @@ class HaPanelHistory extends SubscribeMixin(LitElement) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ha-circular-progress {
|
|
||||||
position: absolute;
|
|
||||||
left: 50%;
|
|
||||||
top: 50%;
|
|
||||||
transform: translate(-50%, -50%);
|
|
||||||
}
|
|
||||||
|
|
||||||
.start-search {
|
.start-search {
|
||||||
padding-top: 16px;
|
padding-top: 16px;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
|
@@ -214,7 +214,7 @@ export class HuiTileCard extends LitElement implements LovelaceCard {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (domain === "fan" && stateActive(stateObj)) {
|
if (domain === "fan") {
|
||||||
const speedStateDisplay = computeFanSpeedStateDisplay(
|
const speedStateDisplay = computeFanSpeedStateDisplay(
|
||||||
stateObj as FanEntity,
|
stateObj as FanEntity,
|
||||||
this.hass!.locale
|
this.hass!.locale
|
||||||
@@ -231,12 +231,11 @@ export class HuiTileCard extends LitElement implements LovelaceCard {
|
|||||||
this.hass!.entities
|
this.hass!.entities
|
||||||
);
|
);
|
||||||
|
|
||||||
if (domain === "cover" && stateActive(stateObj)) {
|
if (domain === "cover") {
|
||||||
const positionStateDisplay = computeCoverPositionStateDisplay(
|
const positionStateDisplay = computeCoverPositionStateDisplay(
|
||||||
stateObj as CoverEntity,
|
stateObj as CoverEntity,
|
||||||
this.hass!.locale
|
this.hass!.locale
|
||||||
);
|
);
|
||||||
|
|
||||||
if (positionStateDisplay) {
|
if (positionStateDisplay) {
|
||||||
return `${stateDisplay} ⸱ ${positionStateDisplay}`;
|
return `${stateDisplay} ⸱ ${positionStateDisplay}`;
|
||||||
}
|
}
|
||||||
|
@@ -80,7 +80,7 @@ export class HuiCardOptions extends LitElement {
|
|||||||
@click=${this._cardUp}
|
@click=${this._cardUp}
|
||||||
?disabled=${this.path![1] === 0}
|
?disabled=${this.path![1] === 0}
|
||||||
></ha-icon-button>
|
></ha-icon-button>
|
||||||
<ha-button-menu corner="BOTTOM_START" @action=${this._handleAction}>
|
<ha-button-menu @action=${this._handleAction}>
|
||||||
<ha-icon-button
|
<ha-icon-button
|
||||||
slot="trigger"
|
slot="trigger"
|
||||||
.label=${this.hass!.localize(
|
.label=${this.hass!.localize(
|
||||||
|
@@ -3,7 +3,6 @@ import { customElement, property, state } from "lit/decorators";
|
|||||||
import memoizeOne from "memoize-one";
|
import memoizeOne from "memoize-one";
|
||||||
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 { entityId } from "../../../../common/structs/is-entity-id";
|
|
||||||
import type { LocalizeFunc } from "../../../../common/translations/localize";
|
import type { LocalizeFunc } from "../../../../common/translations/localize";
|
||||||
import "../../../../components/ha-form/ha-form";
|
import "../../../../components/ha-form/ha-form";
|
||||||
import type { SchemaUnion } from "../../../../components/ha-form/types";
|
import type { SchemaUnion } from "../../../../components/ha-form/types";
|
||||||
@@ -15,7 +14,7 @@ import { baseLovelaceCardConfig } from "../structs/base-card-struct";
|
|||||||
const cardConfigStruct = assign(
|
const cardConfigStruct = assign(
|
||||||
baseLovelaceCardConfig,
|
baseLovelaceCardConfig,
|
||||||
object({
|
object({
|
||||||
entity: optional(entityId()),
|
entity: optional(string()),
|
||||||
name: optional(string()),
|
name: optional(string()),
|
||||||
states: optional(array()),
|
states: optional(array()),
|
||||||
theme: optional(string()),
|
theme: optional(string()),
|
||||||
|
@@ -2,7 +2,6 @@ import { CSSResultGroup, html, LitElement, nothing } from "lit";
|
|||||||
import { customElement, property, state } from "lit/decorators";
|
import { customElement, property, state } from "lit/decorators";
|
||||||
import { assert, assign, boolean, object, optional, string } 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 { entityId } from "../../../../common/structs/is-entity-id";
|
|
||||||
import "../../../../components/ha-form/ha-form";
|
import "../../../../components/ha-form/ha-form";
|
||||||
import type { SchemaUnion } from "../../../../components/ha-form/types";
|
import type { SchemaUnion } from "../../../../components/ha-form/types";
|
||||||
import type { HomeAssistant } from "../../../../types";
|
import type { HomeAssistant } from "../../../../types";
|
||||||
@@ -15,7 +14,7 @@ import { configElementStyle } from "./config-elements-style";
|
|||||||
const cardConfigStruct = assign(
|
const cardConfigStruct = assign(
|
||||||
baseLovelaceCardConfig,
|
baseLovelaceCardConfig,
|
||||||
object({
|
object({
|
||||||
entity: optional(entityId()),
|
entity: optional(string()),
|
||||||
name: optional(string()),
|
name: optional(string()),
|
||||||
show_name: optional(boolean()),
|
show_name: optional(boolean()),
|
||||||
icon: optional(string()),
|
icon: optional(string()),
|
||||||
|
@@ -18,7 +18,6 @@ import {
|
|||||||
} from "superstruct";
|
} from "superstruct";
|
||||||
import { fireEvent, HASSDomEvent } from "../../../../common/dom/fire_event";
|
import { fireEvent, HASSDomEvent } from "../../../../common/dom/fire_event";
|
||||||
import { customType } from "../../../../common/structs/is-custom-type";
|
import { customType } from "../../../../common/structs/is-custom-type";
|
||||||
import { entityId } from "../../../../common/structs/is-entity-id";
|
|
||||||
import { computeRTLDirection } from "../../../../common/util/compute_rtl";
|
import { computeRTLDirection } from "../../../../common/util/compute_rtl";
|
||||||
import "../../../../components/entity/state-badge";
|
import "../../../../components/entity/state-badge";
|
||||||
import "../../../../components/ha-card";
|
import "../../../../components/ha-card";
|
||||||
@@ -184,7 +183,7 @@ const cardConfigStruct = assign(
|
|||||||
baseLovelaceCardConfig,
|
baseLovelaceCardConfig,
|
||||||
object({
|
object({
|
||||||
title: optional(union([string(), boolean()])),
|
title: optional(union([string(), boolean()])),
|
||||||
entity: optional(entityId()),
|
entity: optional(string()),
|
||||||
theme: optional(string()),
|
theme: optional(string()),
|
||||||
icon: optional(string()),
|
icon: optional(string()),
|
||||||
show_header_toggle: optional(boolean()),
|
show_header_toggle: optional(boolean()),
|
||||||
|
@@ -2,7 +2,6 @@ import { html, LitElement, nothing } from "lit";
|
|||||||
import { customElement, property, state } from "lit/decorators";
|
import { customElement, property, state } from "lit/decorators";
|
||||||
import { assert, assign, boolean, object, optional, string } 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 { entityId } from "../../../../common/structs/is-entity-id";
|
|
||||||
import "../../../../components/ha-form/ha-form";
|
import "../../../../components/ha-form/ha-form";
|
||||||
import type { SchemaUnion } from "../../../../components/ha-form/types";
|
import type { SchemaUnion } from "../../../../components/ha-form/types";
|
||||||
import type { HomeAssistant } from "../../../../types";
|
import type { HomeAssistant } from "../../../../types";
|
||||||
@@ -14,7 +13,7 @@ import { baseLovelaceCardConfig } from "../structs/base-card-struct";
|
|||||||
const cardConfigStruct = assign(
|
const cardConfigStruct = assign(
|
||||||
baseLovelaceCardConfig,
|
baseLovelaceCardConfig,
|
||||||
object({
|
object({
|
||||||
entity: optional(entityId()),
|
entity: optional(string()),
|
||||||
name: optional(string()),
|
name: optional(string()),
|
||||||
icon: optional(string()),
|
icon: optional(string()),
|
||||||
attribute: optional(string()),
|
attribute: optional(string()),
|
||||||
|
@@ -12,7 +12,6 @@ import {
|
|||||||
string,
|
string,
|
||||||
} from "superstruct";
|
} from "superstruct";
|
||||||
import { fireEvent } from "../../../../common/dom/fire_event";
|
import { fireEvent } from "../../../../common/dom/fire_event";
|
||||||
import { entityId } from "../../../../common/structs/is-entity-id";
|
|
||||||
import "../../../../components/ha-form/ha-form";
|
import "../../../../components/ha-form/ha-form";
|
||||||
import type { SchemaUnion } from "../../../../components/ha-form/types";
|
import type { SchemaUnion } from "../../../../components/ha-form/types";
|
||||||
import type { HomeAssistant } from "../../../../types";
|
import type { HomeAssistant } from "../../../../types";
|
||||||
@@ -31,7 +30,7 @@ const cardConfigStruct = assign(
|
|||||||
baseLovelaceCardConfig,
|
baseLovelaceCardConfig,
|
||||||
object({
|
object({
|
||||||
name: optional(string()),
|
name: optional(string()),
|
||||||
entity: optional(entityId()),
|
entity: optional(string()),
|
||||||
unit: optional(string()),
|
unit: optional(string()),
|
||||||
min: optional(number()),
|
min: optional(number()),
|
||||||
max: optional(number()),
|
max: optional(number()),
|
||||||
|
@@ -2,7 +2,6 @@ import { html, LitElement, nothing } from "lit";
|
|||||||
import { customElement, property, state } from "lit/decorators";
|
import { customElement, property, state } from "lit/decorators";
|
||||||
import { assert, assign, object, optional, string } from "superstruct";
|
import { assert, assign, object, optional, string } from "superstruct";
|
||||||
import { fireEvent } from "../../../../common/dom/fire_event";
|
import { fireEvent } from "../../../../common/dom/fire_event";
|
||||||
import { entityId } from "../../../../common/structs/is-entity-id";
|
|
||||||
import "../../../../components/ha-form/ha-form";
|
import "../../../../components/ha-form/ha-form";
|
||||||
import type { SchemaUnion } from "../../../../components/ha-form/types";
|
import type { SchemaUnion } from "../../../../components/ha-form/types";
|
||||||
import type { HomeAssistant } from "../../../../types";
|
import type { HomeAssistant } from "../../../../types";
|
||||||
@@ -13,7 +12,7 @@ import { baseLovelaceCardConfig } from "../structs/base-card-struct";
|
|||||||
const cardConfigStruct = assign(
|
const cardConfigStruct = assign(
|
||||||
baseLovelaceCardConfig,
|
baseLovelaceCardConfig,
|
||||||
object({
|
object({
|
||||||
entity: optional(entityId()),
|
entity: optional(string()),
|
||||||
name: optional(string()),
|
name: optional(string()),
|
||||||
theme: optional(string()),
|
theme: optional(string()),
|
||||||
})
|
})
|
||||||
|
@@ -2,7 +2,6 @@ import { CSSResultGroup, html, LitElement, nothing } from "lit";
|
|||||||
import { customElement, property, state } from "lit/decorators";
|
import { customElement, property, state } from "lit/decorators";
|
||||||
import { assert, assign, object, optional, string } from "superstruct";
|
import { assert, assign, object, optional, string } from "superstruct";
|
||||||
import { fireEvent } from "../../../../common/dom/fire_event";
|
import { fireEvent } from "../../../../common/dom/fire_event";
|
||||||
import { entityId } from "../../../../common/structs/is-entity-id";
|
|
||||||
import "../../../../components/ha-form/ha-form";
|
import "../../../../components/ha-form/ha-form";
|
||||||
import type { SchemaUnion } from "../../../../components/ha-form/types";
|
import type { SchemaUnion } from "../../../../components/ha-form/types";
|
||||||
import type { HomeAssistant } from "../../../../types";
|
import type { HomeAssistant } from "../../../../types";
|
||||||
@@ -16,7 +15,7 @@ const cardConfigStruct = assign(
|
|||||||
baseLovelaceCardConfig,
|
baseLovelaceCardConfig,
|
||||||
object({
|
object({
|
||||||
name: optional(string()),
|
name: optional(string()),
|
||||||
entity: optional(entityId()),
|
entity: optional(string()),
|
||||||
theme: optional(string()),
|
theme: optional(string()),
|
||||||
icon: optional(string()),
|
icon: optional(string()),
|
||||||
hold_action: optional(actionConfigStruct),
|
hold_action: optional(actionConfigStruct),
|
||||||
|
@@ -2,7 +2,6 @@ import { html, LitElement, nothing } from "lit";
|
|||||||
import { customElement, property, state } from "lit/decorators";
|
import { customElement, property, state } from "lit/decorators";
|
||||||
import { assert, assign, object, optional, string } from "superstruct";
|
import { assert, assign, object, optional, string } from "superstruct";
|
||||||
import { fireEvent } from "../../../../common/dom/fire_event";
|
import { fireEvent } from "../../../../common/dom/fire_event";
|
||||||
import { entityId } from "../../../../common/structs/is-entity-id";
|
|
||||||
import "../../../../components/entity/ha-entity-picker";
|
import "../../../../components/entity/ha-entity-picker";
|
||||||
import "../../../../components/ha-theme-picker";
|
import "../../../../components/ha-theme-picker";
|
||||||
import { HomeAssistant } from "../../../../types";
|
import { HomeAssistant } from "../../../../types";
|
||||||
@@ -14,7 +13,7 @@ import { EditorTarget, EntitiesEditorEvent } from "../types";
|
|||||||
const cardConfigStruct = assign(
|
const cardConfigStruct = assign(
|
||||||
baseLovelaceCardConfig,
|
baseLovelaceCardConfig,
|
||||||
object({
|
object({
|
||||||
entity: optional(entityId()),
|
entity: optional(string()),
|
||||||
theme: optional(string()),
|
theme: optional(string()),
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
@@ -2,7 +2,6 @@ import { CSSResultGroup, html, LitElement, nothing } from "lit";
|
|||||||
import { customElement, property, state } from "lit/decorators";
|
import { customElement, property, state } from "lit/decorators";
|
||||||
import { assert, assign, boolean, object, optional, string } 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 { entityId } from "../../../../common/structs/is-entity-id";
|
|
||||||
import "../../../../components/ha-form/ha-form";
|
import "../../../../components/ha-form/ha-form";
|
||||||
import type { SchemaUnion } from "../../../../components/ha-form/types";
|
import type { SchemaUnion } from "../../../../components/ha-form/types";
|
||||||
import type { HomeAssistant } from "../../../../types";
|
import type { HomeAssistant } from "../../../../types";
|
||||||
@@ -15,7 +14,7 @@ import { configElementStyle } from "./config-elements-style";
|
|||||||
const cardConfigStruct = assign(
|
const cardConfigStruct = assign(
|
||||||
baseLovelaceCardConfig,
|
baseLovelaceCardConfig,
|
||||||
object({
|
object({
|
||||||
entity: optional(entityId()),
|
entity: optional(string()),
|
||||||
image: optional(string()),
|
image: optional(string()),
|
||||||
name: optional(string()),
|
name: optional(string()),
|
||||||
camera_image: optional(string()),
|
camera_image: optional(string()),
|
||||||
|
@@ -2,7 +2,6 @@ import { html, LitElement, nothing } from "lit";
|
|||||||
import { customElement, property, state } from "lit/decorators";
|
import { customElement, property, state } from "lit/decorators";
|
||||||
import { assert, assign, object, optional, string } from "superstruct";
|
import { assert, assign, object, optional, string } from "superstruct";
|
||||||
import { fireEvent } from "../../../../common/dom/fire_event";
|
import { fireEvent } from "../../../../common/dom/fire_event";
|
||||||
import { entityId } from "../../../../common/structs/is-entity-id";
|
|
||||||
import "../../../../components/ha-form/ha-form";
|
import "../../../../components/ha-form/ha-form";
|
||||||
import type { SchemaUnion } from "../../../../components/ha-form/types";
|
import type { SchemaUnion } from "../../../../components/ha-form/types";
|
||||||
import type { HomeAssistant } from "../../../../types";
|
import type { HomeAssistant } from "../../../../types";
|
||||||
@@ -13,7 +12,7 @@ import { baseLovelaceCardConfig } from "../structs/base-card-struct";
|
|||||||
const cardConfigStruct = assign(
|
const cardConfigStruct = assign(
|
||||||
baseLovelaceCardConfig,
|
baseLovelaceCardConfig,
|
||||||
object({
|
object({
|
||||||
entity: optional(entityId()),
|
entity: optional(string()),
|
||||||
name: optional(string()),
|
name: optional(string()),
|
||||||
theme: optional(string()),
|
theme: optional(string()),
|
||||||
})
|
})
|
||||||
|
@@ -11,7 +11,6 @@ import {
|
|||||||
union,
|
union,
|
||||||
} from "superstruct";
|
} from "superstruct";
|
||||||
import { fireEvent } from "../../../../common/dom/fire_event";
|
import { fireEvent } from "../../../../common/dom/fire_event";
|
||||||
import { entityId } from "../../../../common/structs/is-entity-id";
|
|
||||||
import "../../../../components/ha-form/ha-form";
|
import "../../../../components/ha-form/ha-form";
|
||||||
import type { SchemaUnion } from "../../../../components/ha-form/types";
|
import type { SchemaUnion } from "../../../../components/ha-form/types";
|
||||||
import type { HomeAssistant } from "../../../../types";
|
import type { HomeAssistant } from "../../../../types";
|
||||||
@@ -24,7 +23,7 @@ import { DEFAULT_HOURS_TO_SHOW } from "../../cards/hui-sensor-card";
|
|||||||
const cardConfigStruct = assign(
|
const cardConfigStruct = assign(
|
||||||
baseLovelaceCardConfig,
|
baseLovelaceCardConfig,
|
||||||
object({
|
object({
|
||||||
entity: optional(entityId()),
|
entity: optional(string()),
|
||||||
name: optional(string()),
|
name: optional(string()),
|
||||||
icon: optional(string()),
|
icon: optional(string()),
|
||||||
graph: optional(union([literal("line"), literal("none")])),
|
graph: optional(union([literal("line"), literal("none")])),
|
||||||
|
@@ -2,7 +2,6 @@ import { html, LitElement, nothing } from "lit";
|
|||||||
import { customElement, property, state } from "lit/decorators";
|
import { customElement, property, state } from "lit/decorators";
|
||||||
import { assert, assign, object, optional, string } from "superstruct";
|
import { assert, assign, object, optional, string } from "superstruct";
|
||||||
import { fireEvent } from "../../../../common/dom/fire_event";
|
import { fireEvent } from "../../../../common/dom/fire_event";
|
||||||
import { entityId } from "../../../../common/structs/is-entity-id";
|
|
||||||
import "../../../../components/ha-form/ha-form";
|
import "../../../../components/ha-form/ha-form";
|
||||||
import type { SchemaUnion } from "../../../../components/ha-form/types";
|
import type { SchemaUnion } from "../../../../components/ha-form/types";
|
||||||
import type { HomeAssistant } from "../../../../types";
|
import type { HomeAssistant } from "../../../../types";
|
||||||
@@ -13,7 +12,7 @@ import { baseLovelaceCardConfig } from "../structs/base-card-struct";
|
|||||||
const cardConfigStruct = assign(
|
const cardConfigStruct = assign(
|
||||||
baseLovelaceCardConfig,
|
baseLovelaceCardConfig,
|
||||||
object({
|
object({
|
||||||
entity: optional(entityId()),
|
entity: optional(string()),
|
||||||
name: optional(string()),
|
name: optional(string()),
|
||||||
theme: optional(string()),
|
theme: optional(string()),
|
||||||
})
|
})
|
||||||
|
@@ -14,7 +14,6 @@ import {
|
|||||||
string,
|
string,
|
||||||
} from "superstruct";
|
} from "superstruct";
|
||||||
import { fireEvent, HASSDomEvent } from "../../../../common/dom/fire_event";
|
import { fireEvent, HASSDomEvent } from "../../../../common/dom/fire_event";
|
||||||
import { entityId } from "../../../../common/structs/is-entity-id";
|
|
||||||
import { LocalizeFunc } from "../../../../common/translations/localize";
|
import { LocalizeFunc } from "../../../../common/translations/localize";
|
||||||
import "../../../../components/ha-form/ha-form";
|
import "../../../../components/ha-form/ha-form";
|
||||||
import type { SchemaUnion } from "../../../../components/ha-form/types";
|
import type { SchemaUnion } from "../../../../components/ha-form/types";
|
||||||
@@ -35,7 +34,7 @@ import "./hui-tile-card-features-editor";
|
|||||||
const cardConfigStruct = assign(
|
const cardConfigStruct = assign(
|
||||||
baseLovelaceCardConfig,
|
baseLovelaceCardConfig,
|
||||||
object({
|
object({
|
||||||
entity: optional(entityId()),
|
entity: optional(string()),
|
||||||
name: optional(string()),
|
name: optional(string()),
|
||||||
icon: optional(string()),
|
icon: optional(string()),
|
||||||
color: optional(string()),
|
color: optional(string()),
|
||||||
|
@@ -3,7 +3,6 @@ import { customElement, property, state } from "lit/decorators";
|
|||||||
import memoizeOne from "memoize-one";
|
import memoizeOne from "memoize-one";
|
||||||
import { assert, assign, boolean, object, optional, string } 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 { entityId } from "../../../../common/structs/is-entity-id";
|
|
||||||
import type { LocalizeFunc } from "../../../../common/translations/localize";
|
import type { LocalizeFunc } from "../../../../common/translations/localize";
|
||||||
import "../../../../components/ha-form/ha-form";
|
import "../../../../components/ha-form/ha-form";
|
||||||
import type { SchemaUnion } from "../../../../components/ha-form/types";
|
import type { SchemaUnion } from "../../../../components/ha-form/types";
|
||||||
@@ -18,7 +17,7 @@ import { baseLovelaceCardConfig } from "../structs/base-card-struct";
|
|||||||
const cardConfigStruct = assign(
|
const cardConfigStruct = assign(
|
||||||
baseLovelaceCardConfig,
|
baseLovelaceCardConfig,
|
||||||
object({
|
object({
|
||||||
entity: optional(entityId()),
|
entity: optional(string()),
|
||||||
name: optional(string()),
|
name: optional(string()),
|
||||||
theme: optional(string()),
|
theme: optional(string()),
|
||||||
show_current: optional(boolean()),
|
show_current: optional(boolean()),
|
||||||
|
@@ -93,7 +93,6 @@ export class HuiViewEditor extends LitElement {
|
|||||||
const schema = this._schema(this.hass.localize);
|
const schema = this._schema(this.hass.localize);
|
||||||
|
|
||||||
const data = {
|
const data = {
|
||||||
theme: "Backend-selected",
|
|
||||||
...this._config,
|
...this._config,
|
||||||
type: this._type,
|
type: this._type,
|
||||||
};
|
};
|
||||||
|
@@ -165,7 +165,7 @@ class HUIRoot extends LitElement {
|
|||||||
.path=${mdiHelpCircle}
|
.path=${mdiHelpCircle}
|
||||||
></ha-icon-button>
|
></ha-icon-button>
|
||||||
</a>
|
</a>
|
||||||
<ha-button-menu corner="BOTTOM_START">
|
<ha-button-menu>
|
||||||
<ha-icon-button
|
<ha-icon-button
|
||||||
slot="trigger"
|
slot="trigger"
|
||||||
.label=${this.hass!.localize(
|
.label=${this.hass!.localize(
|
||||||
@@ -319,10 +319,7 @@ class HUIRoot extends LitElement {
|
|||||||
: ""}
|
: ""}
|
||||||
${this._showButtonMenu
|
${this._showButtonMenu
|
||||||
? html`
|
? html`
|
||||||
<ha-button-menu
|
<ha-button-menu slot="actionItems">
|
||||||
corner="BOTTOM_START"
|
|
||||||
slot="actionItems"
|
|
||||||
>
|
|
||||||
<ha-icon-button
|
<ha-icon-button
|
||||||
slot="trigger"
|
slot="trigger"
|
||||||
.label=${this.hass!.localize(
|
.label=${this.hass!.localize(
|
||||||
@@ -961,11 +958,17 @@ class HUIRoot extends LitElement {
|
|||||||
position: fixed;
|
position: fixed;
|
||||||
top: 0;
|
top: 0;
|
||||||
width: var(--mdc-top-app-bar-width, 100%);
|
width: var(--mdc-top-app-bar-width, 100%);
|
||||||
z-index: 2;
|
padding-top: env(safe-area-inset-top);
|
||||||
transition: box-shadow 0.3s ease-out;
|
z-index: 4;
|
||||||
|
transition: box-shadow 200ms linear;
|
||||||
}
|
}
|
||||||
:host([scrolled]) .header {
|
:host([scrolled]) .header {
|
||||||
box-shadow: 0px 0px 8px 0px rgba(0, 0, 0, 0.75);
|
box-shadow: var(
|
||||||
|
--mdc-top-app-bar-fixed-box-shadow,
|
||||||
|
0px 2px 4px -1px rgba(0, 0, 0, 0.2),
|
||||||
|
0px 4px 5px 0px rgba(0, 0, 0, 0.14),
|
||||||
|
0px 1px 10px 0px rgba(0, 0, 0, 0.12)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
.edit-mode .header {
|
.edit-mode .header {
|
||||||
background-color: var(--app-header-edit-background-color, #455a64);
|
background-color: var(--app-header-edit-background-color, #455a64);
|
||||||
@@ -1044,29 +1047,30 @@ class HUIRoot extends LitElement {
|
|||||||
color: var(--error-color);
|
color: var(--error-color);
|
||||||
}
|
}
|
||||||
#view {
|
#view {
|
||||||
margin-top: var(--header-height);
|
margin-top: calc(var(--header-height) + env(safe-area-inset-top));
|
||||||
height: calc(
|
height: calc(100vh - var(--header-height) - env(safe-area-inset-top));
|
||||||
100vh - var(--header-height) - env(safe-area-inset-top) -
|
padding-left: env(safe-area-inset-left);
|
||||||
env(safe-area-inset-bottom)
|
padding-right: env(safe-area-inset-right);
|
||||||
);
|
|
||||||
background: var(
|
background: var(
|
||||||
--lovelace-background,
|
--lovelace-background,
|
||||||
var(--primary-background-color)
|
var(--primary-background-color)
|
||||||
);
|
);
|
||||||
display: flex;
|
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
|
transform: translateZ(0);
|
||||||
|
display: flex;
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* In edit mode we have the tab bar on a new line *
|
* In edit mode we have the tab bar on a new line *
|
||||||
*/
|
*/
|
||||||
.edit-mode #view {
|
.edit-mode #view {
|
||||||
height: calc(
|
height: calc(
|
||||||
100vh - var(--header-height) - 48px - env(safe-area-inset-top) -
|
100vh - var(--header-height) - 48px - env(safe-area-inset-top)
|
||||||
env(safe-area-inset-bottom)
|
);
|
||||||
|
margin-top: calc(
|
||||||
|
var(--header-height) + 48px + env(safe-area-inset-top)
|
||||||
);
|
);
|
||||||
margin-top: calc(var(--header-height) + 48px);
|
|
||||||
}
|
}
|
||||||
#view > * {
|
hui-view {
|
||||||
/**
|
/**
|
||||||
* The view could get larger than the window in Firefox
|
* The view could get larger than the window in Firefox
|
||||||
* to prevent that we set the max-width to 100%
|
* to prevent that we set the max-width to 100%
|
||||||
@@ -1076,7 +1080,9 @@ class HUIRoot extends LitElement {
|
|||||||
* https://github.com/home-assistant/home-assistant-polymer/pull/3806
|
* https://github.com/home-assistant/home-assistant-polymer/pull/3806
|
||||||
*/
|
*/
|
||||||
flex: 1 1 100%;
|
flex: 1 1 100%;
|
||||||
|
height: 100%;
|
||||||
max-width: 100%;
|
max-width: 100%;
|
||||||
|
display: block;
|
||||||
}
|
}
|
||||||
.hide-tab {
|
.hide-tab {
|
||||||
display: none;
|
display: none;
|
||||||
|
@@ -95,14 +95,14 @@ class HuiAlarmModeTileFeature
|
|||||||
|
|
||||||
private _getCurrentMode(stateObj: AlarmControlPanelEntity) {
|
private _getCurrentMode(stateObj: AlarmControlPanelEntity) {
|
||||||
return this._modes(stateObj, this._config?.modes).find(
|
return this._modes(stateObj, this._config?.modes).find(
|
||||||
(mode) => ALARM_MODES[mode].state === stateObj.state
|
(mode) => mode === stateObj.state
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async _valueChanged(ev: CustomEvent) {
|
private async _valueChanged(ev: CustomEvent) {
|
||||||
const mode = (ev.detail as any).value as AlarmMode;
|
const mode = (ev.detail as any).value as AlarmMode;
|
||||||
|
|
||||||
if (ALARM_MODES[mode].state === this.stateObj!.state) return;
|
if (mode === this.stateObj!.state) return;
|
||||||
|
|
||||||
const oldMode = this._getCurrentMode(this.stateObj!);
|
const oldMode = this._getCurrentMode(this.stateObj!);
|
||||||
this._currentMode = mode;
|
this._currentMode = mode;
|
||||||
|
@@ -4,6 +4,7 @@ import { customElement, property, state } from "lit/decorators";
|
|||||||
import { computeAttributeNameDisplay } from "../../../common/entity/compute_attribute_display";
|
import { computeAttributeNameDisplay } from "../../../common/entity/compute_attribute_display";
|
||||||
import { computeDomain } from "../../../common/entity/compute_domain";
|
import { computeDomain } from "../../../common/entity/compute_domain";
|
||||||
import { computeStateDisplay } from "../../../common/entity/compute_state_display";
|
import { computeStateDisplay } from "../../../common/entity/compute_state_display";
|
||||||
|
import { stateActive } from "../../../common/entity/state_active";
|
||||||
import { supportsFeature } from "../../../common/entity/supports-feature";
|
import { supportsFeature } from "../../../common/entity/supports-feature";
|
||||||
import "../../../components/ha-control-select";
|
import "../../../components/ha-control-select";
|
||||||
import type { ControlSelectOption } from "../../../components/ha-control-select";
|
import type { ControlSelectOption } from "../../../components/ha-control-select";
|
||||||
@@ -12,6 +13,7 @@ import { UNAVAILABLE } from "../../../data/entity";
|
|||||||
import {
|
import {
|
||||||
computeFanSpeedCount,
|
computeFanSpeedCount,
|
||||||
computeFanSpeedIcon,
|
computeFanSpeedIcon,
|
||||||
|
FanEntity,
|
||||||
FanEntityFeature,
|
FanEntityFeature,
|
||||||
fanPercentageToSpeed,
|
fanPercentageToSpeed,
|
||||||
FanSpeed,
|
FanSpeed,
|
||||||
@@ -34,7 +36,7 @@ export const supportsFanSpeedTileFeature = (stateObj: HassEntity) => {
|
|||||||
class HuiFanSpeedTileFeature extends LitElement implements LovelaceTileFeature {
|
class HuiFanSpeedTileFeature extends LitElement implements LovelaceTileFeature {
|
||||||
@property({ attribute: false }) public hass?: HomeAssistant;
|
@property({ attribute: false }) public hass?: HomeAssistant;
|
||||||
|
|
||||||
@property({ attribute: false }) public stateObj?: HassEntity;
|
@property({ attribute: false }) public stateObj?: FanEntity;
|
||||||
|
|
||||||
@state() private _config?: FanSpeedTileFeatureConfig;
|
@state() private _config?: FanSpeedTileFeatureConfig;
|
||||||
|
|
||||||
@@ -79,6 +81,10 @@ class HuiFanSpeedTileFeature extends LitElement implements LovelaceTileFeature {
|
|||||||
|
|
||||||
const speedCount = computeFanSpeedCount(this.stateObj);
|
const speedCount = computeFanSpeedCount(this.stateObj);
|
||||||
|
|
||||||
|
const percentage = stateActive(this.stateObj)
|
||||||
|
? this.stateObj.attributes.percentage ?? 0
|
||||||
|
: 0;
|
||||||
|
|
||||||
if (speedCount <= FAN_SPEED_COUNT_MAX_FOR_BUTTONS) {
|
if (speedCount <= FAN_SPEED_COUNT_MAX_FOR_BUTTONS) {
|
||||||
const options = FAN_SPEEDS[speedCount]!.map<ControlSelectOption>(
|
const options = FAN_SPEEDS[speedCount]!.map<ControlSelectOption>(
|
||||||
(speed) => ({
|
(speed) => ({
|
||||||
@@ -88,10 +94,7 @@ class HuiFanSpeedTileFeature extends LitElement implements LovelaceTileFeature {
|
|||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
const speed = fanPercentageToSpeed(
|
const speed = fanPercentageToSpeed(this.stateObj, percentage);
|
||||||
this.stateObj,
|
|
||||||
this.stateObj.attributes.percentage ?? 0
|
|
||||||
);
|
|
||||||
|
|
||||||
return html`
|
return html`
|
||||||
<div class="container">
|
<div class="container">
|
||||||
@@ -113,15 +116,12 @@ class HuiFanSpeedTileFeature extends LitElement implements LovelaceTileFeature {
|
|||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
const percentage =
|
const value = Math.max(Math.round(percentage), 0);
|
||||||
this.stateObj.attributes.percentage != null
|
|
||||||
? Math.max(Math.round(this.stateObj.attributes.percentage), 0)
|
|
||||||
: undefined;
|
|
||||||
|
|
||||||
return html`
|
return html`
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<ha-control-slider
|
<ha-control-slider
|
||||||
.value=${percentage}
|
.value=${value}
|
||||||
min="0"
|
min="0"
|
||||||
max="100"
|
max="100"
|
||||||
.step=${this.stateObj.attributes.percentage_step ?? 1}
|
.step=${this.stateObj.attributes.percentage_step ?? 1}
|
||||||
|
@@ -298,8 +298,7 @@ export class MasonryView extends LitElement implements LovelaceViewElement {
|
|||||||
:host {
|
:host {
|
||||||
display: block;
|
display: block;
|
||||||
padding-top: 4px;
|
padding-top: 4px;
|
||||||
height: 100%;
|
padding-bottom: env(safe-area-inset-bottom);
|
||||||
box-sizing: border-box;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.badges {
|
.badges {
|
||||||
|
@@ -196,8 +196,7 @@ export class SideBarView extends LitElement implements LovelaceViewElement {
|
|||||||
:host {
|
:host {
|
||||||
display: block;
|
display: block;
|
||||||
padding-top: 4px;
|
padding-top: 4px;
|
||||||
height: 100%;
|
padding-bottom: env(safe-area-inset-bottom);
|
||||||
box-sizing: border-box;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.container {
|
.container {
|
||||||
|
@@ -104,6 +104,41 @@ export class HUIView extends ReactiveElement {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public connectedCallback() {
|
||||||
|
super.connectedCallback();
|
||||||
|
this._applyBackgroundTheme();
|
||||||
|
}
|
||||||
|
|
||||||
|
private _applyBackgroundTheme() {
|
||||||
|
if (this._viewConfigTheme) {
|
||||||
|
const theme = this.hass.themes?.themes[this._viewConfigTheme];
|
||||||
|
if (!theme) {
|
||||||
|
this.parentElement?.style.removeProperty("--lovelace-background");
|
||||||
|
this.parentElement?.style.removeProperty("--primary-background-color");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (theme["lovelace-background"]) {
|
||||||
|
this.parentElement?.style.setProperty(
|
||||||
|
"--lovelace-background",
|
||||||
|
theme["lovelace-background"]
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
this.parentElement?.style.removeProperty("--lovelace-background");
|
||||||
|
}
|
||||||
|
if (theme["primary-background-color"]) {
|
||||||
|
this.parentElement?.style.setProperty(
|
||||||
|
"--primary-background-color",
|
||||||
|
theme["primary-background-color"]
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
this.parentElement?.style.removeProperty("--primary-background-color");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.parentElement?.style.removeProperty("--lovelace-background");
|
||||||
|
this.parentElement?.style.removeProperty("--primary-background-color");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public willUpdate(changedProperties: PropertyValues): void {
|
public willUpdate(changedProperties: PropertyValues): void {
|
||||||
super.willUpdate(changedProperties);
|
super.willUpdate(changedProperties);
|
||||||
|
|
||||||
@@ -166,6 +201,7 @@ export class HUIView extends ReactiveElement {
|
|||||||
this.hass.selectedTheme !== oldHass.selectedTheme
|
this.hass.selectedTheme !== oldHass.selectedTheme
|
||||||
) {
|
) {
|
||||||
applyThemesOnElement(this, this.hass.themes, this._viewConfigTheme);
|
applyThemesOnElement(this, this.hass.themes, this._viewConfigTheme);
|
||||||
|
this._applyBackgroundTheme();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (changedProperties.has("narrow")) {
|
if (changedProperties.has("narrow")) {
|
||||||
@@ -225,6 +261,8 @@ export class HUIView extends ReactiveElement {
|
|||||||
applyThemesOnElement(this, this.hass.themes, viewConfig.theme);
|
applyThemesOnElement(this, this.hass.themes, viewConfig.theme);
|
||||||
this._viewConfigTheme = viewConfig.theme;
|
this._viewConfigTheme = viewConfig.theme;
|
||||||
|
|
||||||
|
this._applyBackgroundTheme();
|
||||||
|
|
||||||
if (addLayoutElement) {
|
if (addLayoutElement) {
|
||||||
while (this.lastChild) {
|
while (this.lastChild) {
|
||||||
this.removeChild(this.lastChild);
|
this.removeChild(this.lastChild);
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user