Merge branch 'streaming_history_hui-graph-header-footer_maps' into remove_legacy_history_calls

This commit is contained in:
J. Nick Koston 2023-01-21 17:46:08 -10:00
commit a77589922c
42 changed files with 1507 additions and 1178 deletions

4
.gitignore vendored
View File

@ -8,7 +8,7 @@ dist/
/translations/ /translations/
# yarn # yarn
.yarn/** .yarn/*
!.yarn/patches !.yarn/patches
!.yarn/releases !.yarn/releases
!.yarn/plugins !.yarn/plugins
@ -31,7 +31,7 @@ pip-selfcheck.json
.venv .venv
# vscode # vscode
.vscode/** .vscode/*
!.vscode/extensions.json !.vscode/extensions.json
!.vscode/launch.json !.vscode/launch.json
!.vscode/tasks.json !.vscode/tasks.json

View File

@ -1,12 +0,0 @@
diff --git a/mwc-icon-button-base.js b/mwc-icon-button-base.js
index 45cdaab93ccc0a6daaaaabc01266dcdc32e46bfd..b3ea5b541597308d85f86ce6c23fd00785fda835 100644
--- a/mwc-icon-button-base.js
+++ b/mwc-icon-button-base.js
@@ -63,7 +63,6 @@ export class IconButtonBase extends LitElement {
@touchend="${this.handleRippleDeactivate}"
@touchcancel="${this.handleRippleDeactivate}"
>${this.renderRipple()}
- <i class="material-icons">${this.icon}</i>
<span
><slot></slot
></span>

View File

@ -1,36 +1,40 @@
const del = require("del"); const del = import("del");
const gulp = require("gulp"); const gulp = require("gulp");
const paths = require("../paths"); const paths = require("../paths");
require("./translations"); require("./translations");
gulp.task( gulp.task(
"clean", "clean",
gulp.parallel("clean-translations", () => gulp.parallel("clean-translations", async () =>
del([paths.app_output_root, paths.build_dir]) (await del).deleteSync([paths.app_output_root, paths.build_dir])
) )
); );
gulp.task( gulp.task(
"clean-demo", "clean-demo",
gulp.parallel("clean-translations", () => gulp.parallel("clean-translations", async () =>
del([paths.demo_output_root, paths.build_dir]) (await del).deleteSync([paths.demo_output_root, paths.build_dir])
) )
); );
gulp.task( gulp.task(
"clean-cast", "clean-cast",
gulp.parallel("clean-translations", () => gulp.parallel("clean-translations", async () =>
del([paths.cast_output_root, paths.build_dir]) (await del).deleteSync([paths.cast_output_root, paths.build_dir])
) )
); );
gulp.task("clean-hassio", () => gulp.task("clean-hassio", async () =>
del([paths.hassio_output_root, paths.build_dir]) (await del).deleteSync([paths.hassio_output_root, paths.build_dir])
); );
gulp.task( gulp.task(
"clean-gallery", "clean-gallery",
gulp.parallel("clean-translations", () => gulp.parallel("clean-translations", async () =>
del([paths.gallery_output_root, paths.gallery_build, paths.build_dir]) (await del).deleteSync([
paths.gallery_output_root,
paths.gallery_build,
paths.build_dir,
])
) )
); );

View File

@ -1,9 +1,9 @@
// Task to download the latest Lokalise translations from the nightly workflow artifacts // Task to download the latest Lokalise translations from the nightly workflow artifacts
const del = import("del");
const fs = require("fs/promises"); const fs = require("fs/promises");
const path = require("path"); const path = require("path");
const process = require("process"); const process = require("process");
const del = require("del");
const gulp = require("gulp"); const gulp = require("gulp");
const jszip = require("jszip"); const jszip = require("jszip");
const tar = require("tar"); const tar = require("tar");
@ -17,8 +17,8 @@ const WORKFLOW_NAME = "nightly.yaml";
const ARTIFACT_NAME = "translations"; const ARTIFACT_NAME = "translations";
const CLIENT_ID = "Iv1.3914e28cb27834d1"; const CLIENT_ID = "Iv1.3914e28cb27834d1";
const EXTRACT_DIR = "translations"; const EXTRACT_DIR = "translations";
const TOKEN_FILE = path.join(EXTRACT_DIR, "token.json"); const TOKEN_FILE = path.posix.join(EXTRACT_DIR, "token.json");
const ARTIFACT_FILE = path.join(EXTRACT_DIR, "artifact.json"); const ARTIFACT_FILE = path.posix.join(EXTRACT_DIR, "artifact.json");
let allowTokenSetup = false; let allowTokenSetup = false;
gulp.task("allow-setup-fetch-nightly-translations", (done) => { gulp.task("allow-setup-fetch-nightly-translations", (done) => {
@ -137,7 +137,11 @@ gulp.task("fetch-nightly-translations", async function () {
// Remove the current translations // Remove the current translations
const deleteCurrent = Promise.all(writings).then( const deleteCurrent = Promise.all(writings).then(
del([`${EXTRACT_DIR}/*`, `!${ARTIFACT_FILE}`, `!${TOKEN_FILE}`]) (await del).deleteAsync([
`${EXTRACT_DIR}/*`,
`!${ARTIFACT_FILE}`,
`!${TOKEN_FILE}`,
])
); );
// Get the download URL and follow the redirect to download (stored as ArrayBuffer) // Get the download URL and follow the redirect to download (stored as ArrayBuffer)

View File

@ -1,4 +1,4 @@
const del = require("del"); const del = import("del");
const path = require("path"); const path = require("path");
const gulp = require("gulp"); const gulp = require("gulp");
const fs = require("fs"); const fs = require("fs");
@ -6,7 +6,7 @@ const paths = require("../paths");
const outDir = "build/locale-data"; const outDir = "build/locale-data";
gulp.task("clean-locale-data", () => del([outDir])); gulp.task("clean-locale-data", async () => (await del).deleteSync([outDir]));
gulp.task("ensure-locale-data-build-dir", (done) => { gulp.task("ensure-locale-data-build-dir", (done) => {
if (!fs.existsSync(outDir)) { if (!fs.existsSync(outDir)) {

View File

@ -1,5 +1,5 @@
const del = import("del");
const crypto = require("crypto"); const crypto = require("crypto");
const del = require("del");
const path = require("path"); const path = require("path");
const source = require("vinyl-source-stream"); const source = require("vinyl-source-stream");
const vinylBuffer = require("vinyl-buffer"); const vinylBuffer = require("vinyl-buffer");
@ -13,7 +13,7 @@ const { mapFiles } = require("../util");
const env = require("../env"); const env = require("../env");
const paths = require("../paths"); const paths = require("../paths");
require("./fetch-nightly_translations"); require("./fetch-nightly-translations");
const inFrontendDir = "translations/frontend"; const inFrontendDir = "translations/frontend";
const inBackendDir = "translations/backend"; const inBackendDir = "translations/backend";
@ -120,7 +120,7 @@ function lokaliseTransform(data, original, file) {
return output; return output;
} }
gulp.task("clean-translations", () => del([workDir])); gulp.task("clean-translations", async () => (await del).deleteSync([workDir]));
gulp.task("ensure-translations-build-dir", (done) => { gulp.task("ensure-translations-build-dir", (done) => {
if (!fs.existsSync(workDir)) { if (!fs.existsSync(workDir)) {

View File

@ -47,30 +47,30 @@
"@lezer/highlight": "^1.1.3", "@lezer/highlight": "^1.1.3",
"@lit-labs/motion": "^1.0.2", "@lit-labs/motion": "^1.0.2",
"@lit-labs/virtualizer": "patch:@lit-labs/virtualizer@0.7.0-pre.2#./.yarn/patches/@lit-labs/virtualizer/event-target-shim.patch", "@lit-labs/virtualizer": "patch:@lit-labs/virtualizer@0.7.0-pre.2#./.yarn/patches/@lit-labs/virtualizer/event-target-shim.patch",
"@material/chips": "14.0.0-canary.261f2db59.0", "@material/chips": "=14.0.0-canary.53b3cad2f.0",
"@material/data-table": "14.0.0-canary.261f2db59.0", "@material/data-table": "=14.0.0-canary.53b3cad2f.0",
"@material/mwc-button": "0.25.3", "@material/mwc-button": "^0.27.0",
"@material/mwc-checkbox": "0.25.3", "@material/mwc-checkbox": "^0.27.0",
"@material/mwc-circular-progress": "0.25.3", "@material/mwc-circular-progress": "^0.27.0",
"@material/mwc-dialog": "0.25.3", "@material/mwc-dialog": "^0.27.0",
"@material/mwc-drawer": "^0.25.3", "@material/mwc-drawer": "^0.27.0",
"@material/mwc-fab": "0.25.3", "@material/mwc-fab": "^0.27.0",
"@material/mwc-formfield": "0.25.3", "@material/mwc-formfield": "^0.27.0",
"@material/mwc-icon-button": "patch:@material/mwc-icon-button@0.25.3#./.yarn/patches/@material/mwc-icon-button/remove-icon.patch", "@material/mwc-icon-button": "^0.27.0",
"@material/mwc-linear-progress": "0.25.3", "@material/mwc-linear-progress": "^0.27.0",
"@material/mwc-list": "^0.25.3", "@material/mwc-list": "^0.27.0",
"@material/mwc-menu": "0.25.3", "@material/mwc-menu": "^0.27.0",
"@material/mwc-radio": "0.25.3", "@material/mwc-radio": "^0.27.0",
"@material/mwc-ripple": "0.25.3", "@material/mwc-ripple": "^0.27.0",
"@material/mwc-select": "0.25.3", "@material/mwc-select": "^0.27.0",
"@material/mwc-slider": "0.25.3", "@material/mwc-slider": "^0.27.0",
"@material/mwc-switch": "0.25.3", "@material/mwc-switch": "^0.27.0",
"@material/mwc-tab": "0.25.3", "@material/mwc-tab": "^0.27.0",
"@material/mwc-tab-bar": "0.25.3", "@material/mwc-tab-bar": "^0.27.0",
"@material/mwc-textarea": "^0.25.3", "@material/mwc-textarea": "^0.27.0",
"@material/mwc-textfield": "0.25.3", "@material/mwc-textfield": "^0.27.0",
"@material/mwc-top-app-bar-fixed": "^0.25.3", "@material/mwc-top-app-bar-fixed": "^0.27.0",
"@material/top-app-bar": "14.0.0-canary.261f2db59.0", "@material/top-app-bar": "=14.0.0-canary.53b3cad2f.0",
"@mdi/js": "7.1.96", "@mdi/js": "7.1.96",
"@mdi/svg": "7.1.96", "@mdi/svg": "7.1.96",
"@polymer/app-layout": "^3.1.0", "@polymer/app-layout": "^3.1.0",
@ -108,7 +108,7 @@
"fuse.js": "^6.0.0", "fuse.js": "^6.0.0",
"google-timezones-json": "^1.0.2", "google-timezones-json": "^1.0.2",
"hammerjs": "^2.0.8", "hammerjs": "^2.0.8",
"hls.js": "^1.2.5", "hls.js": "^1.3.1",
"home-assistant-js-websocket": "^8.0.1", "home-assistant-js-websocket": "^8.0.1",
"idb-keyval": "^5.1.3", "idb-keyval": "^5.1.3",
"intl-messageformat": "^9.9.1", "intl-messageformat": "^9.9.1",
@ -117,7 +117,7 @@
"leaflet-draw": "^1.0.4", "leaflet-draw": "^1.0.4",
"lit": "^2.1.2", "lit": "^2.1.2",
"marked": "^4.0.12", "marked": "^4.0.12",
"memoize-one": "^5.2.1", "memoize-one": "^6.0.0",
"node-vibrant": "3.2.1-alpha.1", "node-vibrant": "3.2.1-alpha.1",
"proxy-polyfill": "^0.3.2", "proxy-polyfill": "^0.3.2",
"punycode": "^2.1.1", "punycode": "^2.1.1",
@ -143,13 +143,13 @@
"workbox-precaching": "^6.5.4", "workbox-precaching": "^6.5.4",
"workbox-routing": "^6.5.4", "workbox-routing": "^6.5.4",
"workbox-strategies": "^6.5.4", "workbox-strategies": "^6.5.4",
"xss": "^1.0.9" "xss": "^1.0.14"
}, },
"devDependencies": { "devDependencies": {
"@babel/core": "^7.20.2", "@babel/core": "^7.20.2",
"@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.20.2", "@babel/plugin-proposal-decorators": "^7.20.7",
"@babel/plugin-proposal-nullish-coalescing-operator": "^7.18.6", "@babel/plugin-proposal-nullish-coalescing-operator": "^7.18.6",
"@babel/plugin-proposal-object-rest-spread": "^7.20.2", "@babel/plugin-proposal-object-rest-spread": "^7.20.2",
"@babel/plugin-proposal-optional-chaining": "^7.18.9", "@babel/plugin-proposal-optional-chaining": "^7.18.9",
@ -169,7 +169,7 @@
"@rollup/plugin-replace": "^2.3.2", "@rollup/plugin-replace": "^2.3.2",
"@types/chromecast-caf-receiver": "5.0.12", "@types/chromecast-caf-receiver": "5.0.12",
"@types/chromecast-caf-sender": "^1.0.3", "@types/chromecast-caf-sender": "^1.0.3",
"@types/glob": "^7", "@types/glob": "^8",
"@types/hammerjs": "^2.0.41", "@types/hammerjs": "^2.0.41",
"@types/js-yaml": "^4", "@types/js-yaml": "^4",
"@types/leaflet": "^1", "@types/leaflet": "^1",
@ -186,7 +186,7 @@
"@web/dev-server-rollup": "^0.2.11", "@web/dev-server-rollup": "^0.2.11",
"babel-loader": "^9.1.0", "babel-loader": "^9.1.0",
"chai": "^4.3.4", "chai": "^4.3.4",
"del": "^4.0.0", "del": "^7.0.0",
"eslint": "^7.32.0", "eslint": "^7.32.0",
"eslint-config-airbnb-base": "^14.2.1", "eslint-config-airbnb-base": "^14.2.1",
"eslint-config-airbnb-typescript": "^14.0.0", "eslint-config-airbnb-typescript": "^14.0.0",
@ -196,10 +196,10 @@
"eslint-plugin-import": "^2.24.2", "eslint-plugin-import": "^2.24.2",
"eslint-plugin-lit": "^1.6.1", "eslint-plugin-lit": "^1.6.1",
"eslint-plugin-unused-imports": "^1.1.5", "eslint-plugin-unused-imports": "^1.1.5",
"eslint-plugin-wc": "^1.3.2", "eslint-plugin-wc": "^1.4.0",
"fancy-log": "^2.0.0", "fancy-log": "^2.0.0",
"fs-extra": "^11.1.0", "fs-extra": "^11.1.0",
"glob": "^7.2.0", "glob": "^8.1.0",
"gulp": "^4.0.2", "gulp": "^4.0.2",
"gulp-flatmap": "^1.0.2", "gulp-flatmap": "^1.0.2",
"gulp-json-transform": "^0.4.6", "gulp-json-transform": "^0.4.6",
@ -217,17 +217,17 @@
"map-stream": "^0.0.7", "map-stream": "^0.0.7",
"merge-stream": "^1.0.1", "merge-stream": "^1.0.1",
"mocha": "^8.4.0", "mocha": "^8.4.0",
"object-hash": "^2.0.3", "object-hash": "^3.0.0",
"open": "^8.4.0", "open": "^8.4.0",
"pinst": "^3.0.0", "pinst": "^3.0.0",
"prettier": "^2.8.1", "prettier": "^2.8.3",
"require-dir": "^1.2.0", "require-dir": "^1.2.0",
"rollup": "^2.8.2", "rollup": "^2.8.2",
"rollup-plugin-string": "^3.0.0", "rollup-plugin-string": "^3.0.0",
"rollup-plugin-terser": "^5.3.0", "rollup-plugin-terser": "^5.3.0",
"rollup-plugin-visualizer": "^5.9.0", "rollup-plugin-visualizer": "^5.9.0",
"serve": "^11.3.2", "serve": "^11.3.2",
"sinon": "^11.0.0", "sinon": "^15.0.1",
"source-map-url": "^0.4.0", "source-map-url": "^0.4.0",
"systemjs": "^6.3.2", "systemjs": "^6.3.2",
"tar": "^6.1.11", "tar": "^6.1.11",
@ -238,7 +238,7 @@
"vinyl-source-stream": "^2.0.0", "vinyl-source-stream": "^2.0.0",
"webpack": "^5.55.1", "webpack": "^5.55.1",
"webpack-cli": "^4.8.0", "webpack-cli": "^4.8.0",
"webpack-dev-server": "^4.3.0", "webpack-dev-server": "^4.11.1",
"webpack-manifest-plugin": "^4.0.2", "webpack-manifest-plugin": "^4.0.2",
"webpackbar": "^5.0.0-3", "webpackbar": "^5.0.0-3",
"workbox-build": "^6.5.4" "workbox-build": "^6.5.4"

View File

@ -39,5 +39,5 @@ export default function scrollToTarget(element, target) {
); );
requestAnimationFrame(updateFrame.bind(element)); requestAnimationFrame(updateFrame.bind(element));
} }
}.call(element)); }).call(element);
} }

View File

@ -1,6 +1,7 @@
import type { HassEntity } from "home-assistant-js-websocket"; import type { HassEntity } from "home-assistant-js-websocket";
import { css, html, LitElement, TemplateResult } from "lit"; import { css, html, LitElement, TemplateResult } from "lit";
import { customElement, property } from "lit/decorators"; import { customElement, property } from "lit/decorators";
import memoizeOne from "memoize-one";
import { fireEvent } from "../../common/dom/fire_event"; import { fireEvent } from "../../common/dom/fire_event";
import { isValidEntityId } from "../../common/entity/valid_entity_id"; import { isValidEntityId } from "../../common/entity/valid_entity_id";
import type { PolymerChangedEvent } from "../../polymer-types"; import type { PolymerChangedEvent } from "../../polymer-types";
@ -95,7 +96,10 @@ class HaEntitiesPickerLight extends LitElement {
.excludeEntities=${this.excludeEntities} .excludeEntities=${this.excludeEntities}
.includeDeviceClasses=${this.includeDeviceClasses} .includeDeviceClasses=${this.includeDeviceClasses}
.includeUnitOfMeasurement=${this.includeUnitOfMeasurement} .includeUnitOfMeasurement=${this.includeUnitOfMeasurement}
.entityFilter=${this._entityFilter} .entityFilter=${this._getEntityFilter(
this.value,
this.entityFilter
)}
.value=${entityId} .value=${entityId}
.label=${this.pickedEntityLabel} .label=${this.pickedEntityLabel}
.disabled=${this.disabled} .disabled=${this.disabled}
@ -114,7 +118,7 @@ class HaEntitiesPickerLight extends LitElement {
.excludeEntities=${this.excludeEntities} .excludeEntities=${this.excludeEntities}
.includeDeviceClasses=${this.includeDeviceClasses} .includeDeviceClasses=${this.includeDeviceClasses}
.includeUnitOfMeasurement=${this.includeUnitOfMeasurement} .includeUnitOfMeasurement=${this.includeUnitOfMeasurement}
.entityFilter=${this._entityFilter} .entityFilter=${this._getEntityFilter(this.value, this.entityFilter)}
.label=${this.pickEntityLabel} .label=${this.pickEntityLabel}
.helper=${this.helper} .helper=${this.helper}
.disabled=${this.disabled} .disabled=${this.disabled}
@ -125,11 +129,15 @@ class HaEntitiesPickerLight extends LitElement {
`; `;
} }
private _entityFilter: HaEntityPickerEntityFilterFunc = ( private _getEntityFilter = memoizeOne(
stateObj: HassEntity (
) => value: string[] | undefined,
(!this.value || !this.value.includes(stateObj.entity_id)) && entityFilter: HaEntityPickerEntityFilterFunc | undefined
(!this.entityFilter || this.entityFilter(stateObj)); ): HaEntityPickerEntityFilterFunc =>
(stateObj: HassEntity) =>
(!value || !value.includes(stateObj.entity_id)) &&
(!entityFilter || entityFilter(stateObj))
);
private get _currentEntities() { private get _currentEntities() {
return this.value || []; return this.value || [];

View File

@ -35,9 +35,9 @@ const TRUNCATED_DOMAINS = [
"person", "person",
] as const satisfies ReadonlyArray<keyof typeof FIXED_DOMAIN_STATES>; ] as const satisfies ReadonlyArray<keyof typeof FIXED_DOMAIN_STATES>;
type TruncatedDomain = typeof TRUNCATED_DOMAINS[number]; type TruncatedDomain = (typeof TRUNCATED_DOMAINS)[number];
type TruncatedKey = { type TruncatedKey = {
[T in TruncatedDomain]: `${T}.${typeof FIXED_DOMAIN_STATES[T][number]}`; [T in TruncatedDomain]: `${T}.${(typeof FIXED_DOMAIN_STATES)[T][number]}`;
}[TruncatedDomain]; }[TruncatedDomain];
const getTruncatedKey = (domainKey: string, stateKey: string) => { const getTruncatedKey = (domainKey: string, stateKey: string) => {

View File

@ -278,6 +278,11 @@ export class HaBarSlider extends LitElement {
--slider-bar-border-radius: 10px; --slider-bar-border-radius: 10px;
height: var(--slider-bar-thickness); height: var(--slider-bar-thickness);
width: 100%; width: 100%;
border-radius: var(--slider-bar-border-radius);
outline: none;
}
:host(:focus-visible) {
box-shadow: 0 0 0 2px var(--slider-bar-color);
} }
:host([vertical]) { :host([vertical]) {
width: var(--slider-bar-thickness); width: var(--slider-bar-thickness);

View File

@ -104,6 +104,14 @@ export class HaBarSwitch extends LitElement {
box-sizing: border-box; box-sizing: border-box;
user-select: none; user-select: none;
cursor: pointer; cursor: pointer;
border-radius: var(--switch-bar-border-radius);
outline: none;
}
:host(:focus-visible) {
box-shadow: 0 0 0 2px var(--switch-bar-off-color);
}
:host([checked]:focus-visible) {
box-shadow: 0 0 0 2px var(--switch-bar-on-color);
} }
.switch { .switch {
box-sizing: border-box; box-sizing: border-box;

View File

@ -164,10 +164,11 @@ export class HaSelectSelector extends LitElement {
<ha-select <ha-select
fixedMenuPosition fixedMenuPosition
naturalMenuWidth naturalMenuWidth
.label=${this.label} .label=${this.label ?? ""}
.value=${this.value} .value=${this.value ?? ""}
.helper=${this.helper} .helper=${this.helper ?? ""}
.disabled=${this.disabled} .disabled=${this.disabled}
.required=${this.required}
@closed=${stopPropagation} @closed=${stopPropagation}
@selected=${this._valueChanged} @selected=${this._valueChanged}
> >

View File

@ -8,7 +8,7 @@ import { BlueprintInput } from "./blueprint";
import { DeviceCondition, DeviceTrigger } from "./device_automation"; import { DeviceCondition, DeviceTrigger } from "./device_automation";
import { Action, MODES } from "./script"; import { Action, MODES } from "./script";
export const AUTOMATION_DEFAULT_MODE: typeof MODES[number] = "single"; export const AUTOMATION_DEFAULT_MODE: (typeof MODES)[number] = "single";
export const AUTOMATION_DEFAULT_MAX = 10; export const AUTOMATION_DEFAULT_MAX = 10;
export interface AutomationEntity extends HassEntityBase { export interface AutomationEntity extends HassEntityBase {
@ -29,7 +29,7 @@ export interface ManualAutomationConfig {
trigger: Trigger | Trigger[]; trigger: Trigger | Trigger[];
condition?: Condition | Condition[]; condition?: Condition | Condition[];
action: Action | Action[]; action: Action | Action[];
mode?: typeof MODES[number]; mode?: (typeof MODES)[number];
max?: number; max?: number;
max_exceeded?: max_exceeded?:
| "silent" | "silent"

View File

@ -84,3 +84,12 @@ export const setConversationOnboarding = (
type: "conversation/onboarding/set", type: "conversation/onboarding/set",
shown: value, shown: value,
}); });
export const prepareConversation = (
hass: HomeAssistant,
language?: string
): Promise<void> =>
hass.callWS({
type: "conversation/prepare",
language,
});

View File

@ -671,7 +671,7 @@ export const getEnergySolarForecasts = (hass: HomeAssistant) =>
}); });
const energyGasUnitClass = ["volume", "energy"] as const; const energyGasUnitClass = ["volume", "energy"] as const;
export type EnergyGasUnitClass = typeof energyGasUnitClass[number]; export type EnergyGasUnitClass = (typeof energyGasUnitClass)[number];
export const getEnergyGasUnitClass = ( export const getEnergyGasUnitClass = (
prefs: EnergyPreferences, prefs: EnergyPreferences,

View File

@ -7,8 +7,8 @@ import { TranslationDict } from "../types";
import { UNAVAILABLE_STATES } from "./entity"; import { UNAVAILABLE_STATES } from "./entity";
type HumidifierState = type HumidifierState =
| typeof FIXED_DOMAIN_STATES.humidifier[number] | (typeof FIXED_DOMAIN_STATES.humidifier)[number]
| typeof UNAVAILABLE_STATES[number]; | (typeof UNAVAILABLE_STATES)[number];
type HumidifierMode = type HumidifierMode =
keyof TranslationDict["state_attributes"]["humidifier"]["mode"]; keyof TranslationDict["state_attributes"]["humidifier"]["mode"];

View File

@ -98,7 +98,7 @@ const statisticTypes = [
"state", "state",
"sum", "sum",
] as const; ] as const;
export type StatisticsTypes = typeof statisticTypes[number][]; export type StatisticsTypes = (typeof statisticTypes)[number][];
export interface StatisticsValidationResults { export interface StatisticsValidationResults {
[statisticId: string]: StatisticsValidationResult[]; [statisticId: string]: StatisticsValidationResult[];

View File

@ -15,7 +15,7 @@ export interface ScheduleDay {
to: string; to: string;
} }
type ScheduleDays = { [K in typeof weekdays[number]]?: ScheduleDay[] }; type ScheduleDays = { [K in (typeof weekdays)[number]]?: ScheduleDay[] };
export interface Schedule extends ScheduleDays { export interface Schedule extends ScheduleDays {
id: string; id: string;

View File

@ -77,7 +77,7 @@ const activateSceneActionStruct: Describe<ServiceSceneAction> = assign(
export interface ScriptEntity extends HassEntityBase { export interface ScriptEntity extends HassEntityBase {
attributes: HassEntityAttributeBase & { attributes: HassEntityAttributeBase & {
last_triggered: string; last_triggered: string;
mode: typeof MODES[number]; mode: (typeof MODES)[number];
current?: number; current?: number;
max?: number; max?: number;
}; };
@ -89,7 +89,7 @@ export interface ManualScriptConfig {
alias: string; alias: string;
sequence: Action | Action[]; sequence: Action | Action[];
icon?: string; icon?: string;
mode?: typeof MODES[number]; mode?: (typeof MODES)[number];
max?: number; max?: number;
} }

11
src/data/thread.ts Normal file
View File

@ -0,0 +1,11 @@
import { HomeAssistant } from "../types";
export interface ThreadInfo {
url: string;
active_dataset_tlvs: string;
}
export const threadGetInfo = (hass: HomeAssistant): Promise<ThreadInfo> =>
hass.callWS({
type: "otbr/info",
});

View File

@ -22,6 +22,7 @@ import type { HaTextField } from "../../components/ha-textfield";
import { import {
AgentInfo, AgentInfo,
getAgentInfo, getAgentInfo,
prepareConversation,
processConversationInput, processConversationInput,
setConversationOnboarding, setConversationOnboarding,
} from "../../data/conversation"; } from "../../data/conversation";
@ -220,6 +221,7 @@ export class HaVoiceCommandDialog extends LitElement {
text: this.hass.localize("ui.dialogs.voice_command.how_can_i_help"), text: this.hass.localize("ui.dialogs.voice_command.how_can_i_help"),
}, },
]; ];
prepareConversation(this.hass, this.hass.language);
} }
protected updated(changedProps: PropertyValues) { protected updated(changedProps: PropertyValues) {

View File

@ -26,7 +26,7 @@ class DialogAutomationMode extends LitElement implements HassDialog {
private _params!: AutomationModeDialog; private _params!: AutomationModeDialog;
@state() private _newMode: typeof MODES[number] = AUTOMATION_DEFAULT_MODE; @state() private _newMode: (typeof MODES)[number] = AUTOMATION_DEFAULT_MODE;
@state() private _newMax?: number; @state() private _newMax?: number;

View File

@ -485,6 +485,13 @@ class HaPanelConfig extends HassRouterPage {
"./integrations/integration-panels/matter/matter-config-panel" "./integrations/integration-panels/matter/matter-config-panel"
), ),
}, },
thread: {
tag: "thread-config-panel",
load: () =>
import(
"./integrations/integration-panels/thread/thread-config-panel"
),
},
application_credentials: { application_credentials: {
tag: "ha-config-application-credentials", tag: "ha-config-application-credentials",
load: () => load: () =>

View File

@ -39,6 +39,7 @@ import {
rebootHost, rebootHost,
shutdownHost, shutdownHost,
} from "../../../data/hassio/host"; } from "../../../data/hassio/host";
import { scanUSBDevices } from "../../../data/usb";
import { showOptionsFlowDialog } from "../../../dialogs/config-flow/show-dialog-options-flow"; import { showOptionsFlowDialog } from "../../../dialogs/config-flow/show-dialog-options-flow";
import { import {
showAlertDialog, showAlertDialog,
@ -219,6 +220,10 @@ class HaConfigHardware extends SubscribeMixin(LitElement) {
} }
protected render(): TemplateResult { protected render(): TemplateResult {
if (!this._configEntries) {
return html``;
}
let boardId: string | undefined; let boardId: string | undefined;
let boardName: string | undefined; let boardName: string | undefined;
let imageURL: string | undefined; let imageURL: string | undefined;
@ -230,13 +235,22 @@ class HaConfigHardware extends SubscribeMixin(LitElement) {
); );
const dongles = this._hardwareInfo?.hardware.filter( const dongles = this._hardwareInfo?.hardware.filter(
(hw) => hw.dongle !== null (hw) =>
hw.dongle !== null &&
(!hw.config_entries.length ||
hw.config_entries.some(
(entryId) =>
this._configEntries![entryId] &&
!this._configEntries![entryId].disabled_by
))
); );
if (boardData) { if (boardData) {
boardConfigEntries = boardData.config_entries boardConfigEntries = boardData.config_entries
.map((id) => this._configEntries?.[id]) .map((id) => this._configEntries![id])
.filter((entry) => entry?.supports_options) as ConfigEntry[]; .filter(
(entry) => entry?.supports_options && !entry.disabled_by
) as ConfigEntry[];
boardId = boardData.board!.hassio_board_id; boardId = boardData.board!.hassio_board_id;
boardName = boardData.name; boardName = boardData.name;
documentationURL = boardData.url; documentationURL = boardData.url;
@ -362,8 +376,10 @@ class HaConfigHardware extends SubscribeMixin(LitElement) {
? html`<ha-card> ? html`<ha-card>
${dongles.map((dongle) => { ${dongles.map((dongle) => {
const configEntry = dongle.config_entries const configEntry = dongle.config_entries
.map((id) => this._configEntries?.[id]) .map((id) => this._configEntries![id])
.filter((entry) => entry?.supports_options)[0]; .filter(
(entry) => entry?.supports_options && !entry.disabled_by
)[0];
return html`<div class="row"> return html`<div class="row">
${dongle.name}${configEntry ${dongle.name}${configEntry
? html`<mwc-button ? html`<mwc-button
@ -444,6 +460,10 @@ class HaConfigHardware extends SubscribeMixin(LitElement) {
} }
private async _load() { private async _load() {
if (isComponentLoaded(this.hass, "usb")) {
await scanUSBDevices(this.hass);
}
const isHassioLoaded = isComponentLoaded(this.hass, "hassio"); const isHassioLoaded = isComponentLoaded(this.hass, "hassio");
try { try {
if (isComponentLoaded(this.hass, "hardware")) { if (isComponentLoaded(this.hass, "hardware")) {

View File

@ -21,7 +21,7 @@ export const HELPER_DOMAINS = [
"schedule", "schedule",
] as const; ] as const;
export type HelperDomain = typeof HELPER_DOMAINS[number]; export type HelperDomain = (typeof HELPER_DOMAINS)[number];
export const isHelperDomain = arrayLiteralIncludes(HELPER_DOMAINS); export const isHelperDomain = arrayLiteralIncludes(HELPER_DOMAINS);
export type Helper = export type Helper =

View File

@ -79,6 +79,7 @@ const integrationsWithPanel = {
zha: "/config/zha/dashboard", zha: "/config/zha/dashboard",
zwave_js: "/config/zwave_js/dashboard", zwave_js: "/config/zwave_js/dashboard",
matter: "/config/matter", matter: "/config/matter",
otbr: "/config/thread",
}; };
@customElement("ha-integration-card") @customElement("ha-integration-card")

View File

@ -14,6 +14,7 @@ import { HomeAssistant } from "../../../../../types";
import "../../../../../components/ha-alert"; import "../../../../../components/ha-alert";
import { showPromptDialog } from "../../../../../dialogs/generic/show-dialog-box"; import { showPromptDialog } from "../../../../../dialogs/generic/show-dialog-box";
import { navigate } from "../../../../../common/navigate"; import { navigate } from "../../../../../common/navigate";
import { isComponentLoaded } from "../../../../../common/config/is_component_loaded";
@customElement("matter-config-panel") @customElement("matter-config-panel")
export class MatterConfigPanel extends LitElement { export class MatterConfigPanel extends LitElement {
@ -32,18 +33,24 @@ export class MatterConfigPanel extends LitElement {
protected render(): TemplateResult { protected render(): TemplateResult {
return html` return html`
<hass-subpage .narrow=${this.narrow} .hass=${this.hass} header="Matter"> <hass-subpage .narrow=${this.narrow} .hass=${this.hass} header="Matter">
${isComponentLoaded(this.hass, "otbr")
? html`
<a href="/config/thread" slot="toolbar-icon">
<mwc-button>Visit Thread Panel</mwc-button>
</a>
`
: ""}
<div class="content"> <div class="content">
<ha-card header="Matter"> <ha-card header="Matter">
<ha-alert alert-type="warning"
>Matter is still in the early phase of development, it is not
meant to be used in production. This panel is for development
only.</ha-alert
>
<div class="card-content"> <div class="card-content">
${this._error ${this._error
? html`<ha-alert alert-type="error">${this._error}</ha-alert>` ? html`<ha-alert alert-type="error">${this._error}</ha-alert>`
: ""} : ""}
<ha-alert alert-type="warning"
>Matter is still in the early phase of development, it is not
meant to be used in production. This panel is for development
only.</ha-alert
>
You can add Matter devices by commissing them if they are not You can add Matter devices by commissing them if they are not
setup yet, or share them from another controller and enter the setup yet, or share them from another controller and enter the
share code. share code.
@ -199,6 +206,10 @@ export class MatterConfigPanel extends LitElement {
static styles = [ static styles = [
haStyle, haStyle,
css` css`
ha-alert[alert-type="warning"] {
position: relative;
top: -16px;
}
.content { .content {
padding: 24px 0 32px; padding: 24px 0 32px;
max-width: 600px; max-width: 600px;
@ -208,6 +219,9 @@ export class MatterConfigPanel extends LitElement {
ha-card:first-child { ha-card:first-child {
margin-bottom: 16px; margin-bottom: 16px;
} }
a[slot="toolbar-icon"] {
text-decoration: none;
}
`, `,
]; ];
} }

View File

@ -0,0 +1,73 @@
import "@material/mwc-button";
import { css, html, LitElement, PropertyValues, TemplateResult } from "lit";
import { customElement, property, state } from "lit/decorators";
import "../../../../../components/ha-card";
import "../../../../../layouts/hass-subpage";
import { haStyle } from "../../../../../resources/styles";
import { HomeAssistant } from "../../../../../types";
import { threadGetInfo, ThreadInfo } from "../../../../../data/thread";
@customElement("thread-config-panel")
export class ThreadConfigPanel extends LitElement {
@property({ attribute: false }) public hass!: HomeAssistant;
@property({ type: Boolean }) public narrow!: boolean;
@state() private _info?: ThreadInfo;
protected render(): TemplateResult {
return html`
<hass-subpage .narrow=${this.narrow} .hass=${this.hass} header="Thread">
<div class="content">
<ha-card header="Thread Border Router">
<div class="card-content">
${!this._info
? html`<ha-circular-progress active></ha-circular-progress>`
: html`
<table>
<tr>
<td>URL</td>
<td>${this._info.url}</td>
</tr>
<tr>
<td>Active Dataset TLVs</td>
<td>${this._info.active_dataset_tlvs || "-"}</td>
</tr>
</table>
`}
</div>
</ha-card>
</div>
</hass-subpage>
`;
}
protected override firstUpdated(changedProps: PropertyValues) {
super.firstUpdated(changedProps);
threadGetInfo(this.hass).then((info) => {
this._info = info;
});
}
static styles = [
haStyle,
css`
.content {
padding: 24px 0 32px;
max-width: 600px;
margin: 0 auto;
direction: ltr;
}
ha-card:first-child {
margin-bottom: 16px;
}
`,
];
}
declare global {
interface HTMLElementTagNameMap {
"thread-config-panel": ThreadConfigPanel;
}
}

View File

@ -50,7 +50,7 @@ export class ZHAManageClusters extends LitElement {
@state() private _selectedCluster?: Cluster; @state() private _selectedCluster?: Cluster;
@state() private _currTab: typeof tabs[number] = "attributes"; @state() private _currTab: (typeof tabs)[number] = "attributes";
@state() private _clustersLoaded = false; @state() private _clustersLoaded = false;

View File

@ -95,7 +95,7 @@ export class HaScriptEditor extends KeyboardShortcutMixin(LitElement) {
( (
hasID: boolean, hasID: boolean,
useBluePrint?: boolean, useBluePrint?: boolean,
currentMode?: typeof MODES[number] currentMode?: (typeof MODES)[number]
) => ) =>
[ [
{ {
@ -528,7 +528,7 @@ export class HaScriptEditor extends KeyboardShortcutMixin(LitElement) {
// Mode must be one of max modes per schema definition above // Mode must be one of max modes per schema definition above
return this.hass.localize( return this.hass.localize(
`ui.panel.config.script.editor.max.${ `ui.panel.config.script.editor.max.${
data.mode as typeof MODES_MAX[number] data.mode as (typeof MODES_MAX)[number]
}` }`
); );
default: default:

View File

@ -1,8 +1,16 @@
import { memoize } from "@fullcalendar/common"; import { memoize } from "@fullcalendar/common";
import { Ripple } from "@material/mwc-ripple";
import { RippleHandlers } from "@material/mwc-ripple/ripple-handlers";
import { mdiExclamationThick, mdiHelp } from "@mdi/js"; import { mdiExclamationThick, mdiHelp } from "@mdi/js";
import { HassEntity } from "home-assistant-js-websocket"; import { HassEntity } from "home-assistant-js-websocket";
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit"; import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
import { customElement, property, state } from "lit/decorators"; import {
customElement,
eventOptions,
property,
queryAsync,
state,
} from "lit/decorators";
import { styleMap } from "lit/directives/style-map"; import { styleMap } from "lit/directives/style-map";
import { computeCssColor } from "../../../common/color/compute-color"; import { computeCssColor } from "../../../common/color/compute-color";
import { hsv2rgb, rgb2hsv } from "../../../common/color/convert-color"; import { hsv2rgb, rgb2hsv } from "../../../common/color/convert-color";
@ -105,7 +113,8 @@ export class HuiTileCard extends LitElement implements LovelaceCard {
handleAction(this, this.hass!, this._config!, ev.detail.action!); handleAction(this, this.hass!, this._config!, ev.detail.action!);
} }
private _handleIconAction() { private _handleIconAction(ev: CustomEvent) {
ev.stopPropagation();
const config = { const config = {
entity: this._config!.entity, entity: this._config!.entity,
tap_action: this._config!.icon_tap_action, tap_action: this._config!.icon_tap_action,
@ -219,6 +228,32 @@ export class HuiTileCard extends LitElement implements LovelaceCard {
return stateDisplay; return stateDisplay;
} }
@queryAsync("mwc-ripple") private _ripple!: Promise<Ripple | null>;
@state() private _shouldRenderRipple = false;
private _rippleHandlers: RippleHandlers = new RippleHandlers(() => {
this._shouldRenderRipple = true;
return this._ripple;
});
@eventOptions({ passive: true })
private handleRippleActivate(evt?: Event) {
this._rippleHandlers.startPress(evt);
}
private handleRippleDeactivate() {
this._rippleHandlers.endPress();
}
private handleRippleMouseEnter() {
this._rippleHandlers.startHover();
}
private handleRippleMouseLeave() {
this._rippleHandlers.endHover();
}
protected render(): TemplateResult { protected render(): TemplateResult {
if (!this._config || !this.hass) { if (!this._config || !this.hass) {
return html``; return html``;
@ -274,6 +309,7 @@ export class HuiTileCard extends LitElement implements LovelaceCard {
return html` return html`
<ha-card style=${styleMap(style)}> <ha-card style=${styleMap(style)}>
${this._shouldRenderRipple ? html`<mwc-ripple></mwc-ripple>` : null}
<div class="tile"> <div class="tile">
<div <div
class="icon-container" class="icon-container"
@ -313,10 +349,17 @@ export class HuiTileCard extends LitElement implements LovelaceCard {
class="info" class="info"
.primary=${name} .primary=${name}
.secondary=${stateDisplay} .secondary=${stateDisplay}
role="button"
tabindex="0"
@action=${this._handleAction} @action=${this._handleAction}
.actionHandler=${actionHandler()} .actionHandler=${actionHandler()}
role="button"
tabindex="0"
@mousedown=${this.handleRippleActivate}
@mouseup=${this.handleRippleDeactivate}
@mouseenter=${this.handleRippleMouseEnter}
@mouseleave=${this.handleRippleMouseLeave}
@touchstart=${this.handleRippleActivate}
@touchend=${this.handleRippleDeactivate}
@touchcancel=${this.handleRippleDeactivate}
></ha-tile-info> ></ha-tile-info>
</div> </div>
${supportedFeatures?.length ${supportedFeatures?.length
@ -365,11 +408,18 @@ export class HuiTileCard extends LitElement implements LovelaceCard {
return css` return css`
:host { :host {
--tile-color: rgb(var(--rgb-state-inactive-color)); --tile-color: rgb(var(--rgb-state-inactive-color));
--tile-tap-padding: 6px;
-webkit-tap-highlight-color: transparent; -webkit-tap-highlight-color: transparent;
} }
ha-card:has(ha-tile-info:focus-visible) {
border-color: var(--tile-color);
box-shadow: 0 0 0 1px var(--tile-color);
}
ha-card { ha-card {
--mdc-ripple-color: var(--tile-color);
height: 100%; height: 100%;
overflow: hidden;
// For safari overflow hidden
z-index: 0;
} }
ha-card.disabled { ha-card.disabled {
--tile-color: rgb(var(--rgb-disabled-color)); --tile-color: rgb(var(--rgb-disabled-color));
@ -381,18 +431,16 @@ export class HuiTileCard extends LitElement implements LovelaceCard {
outline: none; outline: none;
} }
.tile { .tile {
padding: calc(12px - var(--tile-tap-padding));
display: flex; display: flex;
flex-direction: row; flex-direction: row;
align-items: center; align-items: center;
} }
.icon-container { .icon-container {
position: relative; position: relative;
padding: var(--tile-tap-padding);
flex: none; flex: none;
margin-right: calc(12px - 2 * var(--tile-tap-padding)); margin-right: 12px;
margin-inline-end: calc(12px - 2 * var(--tile-tap-padding)); margin-inline-start: 12px;
margin-inline-start: initial; margin-inline-end: initial;
direction: var(--direction); direction: var(--direction);
transition: transform 180ms ease-in-out; transition: transform 180ms ease-in-out;
} }
@ -401,8 +449,8 @@ export class HuiTileCard extends LitElement implements LovelaceCard {
} }
.icon-container .badge { .icon-container .badge {
position: absolute; position: absolute;
top: calc(-3px + var(--tile-tap-padding)); top: -3px;
right: calc(-3px + var(--tile-tap-padding)); right: -3px;
} }
.icon-container[role="button"]:focus-visible, .icon-container[role="button"]:focus-visible,
.icon-container[role="button"]:active { .icon-container[role="button"]:active {
@ -410,27 +458,12 @@ export class HuiTileCard extends LitElement implements LovelaceCard {
} }
.info { .info {
position: relative; position: relative;
padding: var(--tile-tap-padding); padding: 12px;
flex: 1; flex: 1;
min-width: 0; min-width: 0;
min-height: 40px; min-height: 40px;
transition: background-color 180ms ease-in-out; transition: background-color 180ms ease-in-out;
} }
.info::before {
content: "";
position: absolute;
top: 0;
left: 0;
height: 100%;
width: 100%;
border-radius: calc(var(--ha-card-border-radius, 10px) - 2px);
background-color: transparent;
opacity: 0.1;
transition: background-color ease-in-out 180ms;
}
.info:focus-visible::before {
background-color: var(--tile-color);
}
`; `;
} }
} }

View File

@ -161,7 +161,7 @@ export const computeCards = (
renderFooterEntities && renderFooterEntities &&
(domain === "scene" || domain === "script") (domain === "scene" || domain === "script")
) { ) {
const conf: typeof footerEntities[0] = { const conf: (typeof footerEntities)[0] = {
entity: entityId, entity: entityId,
show_icon: true, show_icon: true,
show_name: true, show_name: true,

View File

@ -16,4 +16,4 @@ export const TIMESTAMP_RENDERING_FORMATS = [
] as const; ] as const;
export type TimestampRenderingFormat = export type TimestampRenderingFormat =
typeof TIMESTAMP_RENDERING_FORMATS[number]; (typeof TIMESTAMP_RENDERING_FORMATS)[number];

View File

@ -38,7 +38,7 @@ const cardConfigStruct = assign(
const stat_types = ["mean", "min", "max", "change"] as const; const stat_types = ["mean", "min", "max", "change"] as const;
const statTypeMap: Record<typeof stat_types[number], StatisticType> = { const statTypeMap: Record<(typeof stat_types)[number], StatisticType> = {
mean: "mean", mean: "mean",
min: "min", min: "min",
max: "max", max: "max",

View File

@ -102,6 +102,7 @@ class HuiSelectEntityRow extends LitElement implements LovelaceRow {
} }
ha-select { ha-select {
width: 100%; width: 100%;
--ha-select-min-width: 0;
} }
`; `;
} }

View File

@ -186,10 +186,10 @@ export class HuiGraphHeaderFooter
} }
private _unsubscribeHistoryTimeWindow() { private _unsubscribeHistoryTimeWindow() {
clearInterval(this._interval);
if (!this._subscribed) { if (!this._subscribed) {
return; return;
} }
clearInterval(this._interval);
this._subscribed.then((unsubscribe) => { this._subscribed.then((unsubscribe) => {
if (unsubscribe) { if (unsubscribe) {
unsubscribe(); unsubscribe();

View File

@ -18,7 +18,7 @@ export const VACUUM_COMMANDS = [
"return_home", "return_home",
] as const; ] as const;
export type VacuumCommand = typeof VACUUM_COMMANDS[number]; export type VacuumCommand = (typeof VACUUM_COMMANDS)[number];
export interface VacuumCommandsTileFeatureConfig { export interface VacuumCommandsTileFeatureConfig {
type: "vacuum-commands"; type: "vacuum-commands";

View File

@ -2,14 +2,10 @@
import { expose } from "comlink"; import { expose } from "comlink";
import { marked } from "marked"; import { marked } from "marked";
import "proxy-polyfill"; import "proxy-polyfill";
import { filterXSS, getDefaultWhiteList } from "xss"; import { filterXSS, getDefaultWhiteList, IWhiteList } from "xss";
interface WhiteList { let whiteListNormal: IWhiteList | undefined;
[tag: string]: string[]; let whiteListSvg: IWhiteList | undefined;
}
let whiteListNormal: WhiteList | undefined;
let whiteListSvg: WhiteList | undefined;
// Override the default `onTagAttr` behavior to only render // Override the default `onTagAttr` behavior to only render
// our markdown checkboxes. // our markdown checkboxes.
@ -43,7 +39,7 @@ const renderMarkdown = (
): string => { ): string => {
if (!whiteListNormal) { if (!whiteListNormal) {
whiteListNormal = { whiteListNormal = {
...(getDefaultWhiteList() as WhiteList), ...getDefaultWhiteList(),
input: ["type", "disabled", "checked"], input: ["type", "disabled", "checked"],
"ha-icon": ["icon"], "ha-icon": ["icon"],
"ha-svg-icon": ["path"], "ha-svg-icon": ["path"],
@ -51,7 +47,7 @@ const renderMarkdown = (
}; };
} }
let whiteList: WhiteList | undefined; let whiteList: IWhiteList | undefined;
if (hassOptions.allowSvg) { if (hassOptions.allowSvg) {
if (!whiteListSvg) { if (!whiteListSvg) {

View File

@ -2,6 +2,7 @@ import type { PropertyValues } from "lit";
import tinykeys from "tinykeys"; import tinykeys from "tinykeys";
import { isComponentLoaded } from "../common/config/is_component_loaded"; import { isComponentLoaded } from "../common/config/is_component_loaded";
import { mainWindow } from "../common/dom/get_main_window"; import { mainWindow } from "../common/dom/get_main_window";
import { HaSelect } from "../components/ha-select";
import { import {
QuickBarParams, QuickBarParams,
showQuickBar, showQuickBar,
@ -133,17 +134,17 @@ export default <T extends Constructor<HassElement>>(superClass: T) =>
} }
private _canOverrideAlphanumericInput(e: KeyboardEvent) { private _canOverrideAlphanumericInput(e: KeyboardEvent) {
const el = e.composedPath()[0] as any; const el = e.composedPath()[0];
if (el.tagName === "TEXTAREA") { if (el instanceof HTMLTextAreaElement) {
return false; return false;
} }
if (el.parentElement.tagName === "HA-SELECT") { if (el instanceof Element && el.parentElement instanceof HaSelect) {
return false; return false;
} }
if (el.tagName !== "INPUT") { if (!(el instanceof HTMLInputElement)) {
return true; return true;
} }

View File

@ -808,7 +808,7 @@
"did_not_understand": "Didn't quite get that", "did_not_understand": "Didn't quite get that",
"found": "I found the following for you:", "found": "I found the following for you:",
"error": "Oops, an error has occurred", "error": "Oops, an error has occurred",
"how_can_i_help": "How can I help?", "how_can_i_help": "How can I assist?",
"input_label": "Enter a request", "input_label": "Enter a request",
"send_text": "Send text", "send_text": "Send text",
"start_listening": "Start listening" "start_listening": "Start listening"

2189
yarn.lock

File diff suppressed because it is too large Load Diff