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/
# yarn
.yarn/**
.yarn/*
!.yarn/patches
!.yarn/releases
!.yarn/plugins
@ -31,7 +31,7 @@ pip-selfcheck.json
.venv
# vscode
.vscode/**
.vscode/*
!.vscode/extensions.json
!.vscode/launch.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 paths = require("../paths");
require("./translations");
gulp.task(
"clean",
gulp.parallel("clean-translations", () =>
del([paths.app_output_root, paths.build_dir])
gulp.parallel("clean-translations", async () =>
(await del).deleteSync([paths.app_output_root, paths.build_dir])
)
);
gulp.task(
"clean-demo",
gulp.parallel("clean-translations", () =>
del([paths.demo_output_root, paths.build_dir])
gulp.parallel("clean-translations", async () =>
(await del).deleteSync([paths.demo_output_root, paths.build_dir])
)
);
gulp.task(
"clean-cast",
gulp.parallel("clean-translations", () =>
del([paths.cast_output_root, paths.build_dir])
gulp.parallel("clean-translations", async () =>
(await del).deleteSync([paths.cast_output_root, paths.build_dir])
)
);
gulp.task("clean-hassio", () =>
del([paths.hassio_output_root, paths.build_dir])
gulp.task("clean-hassio", async () =>
(await del).deleteSync([paths.hassio_output_root, paths.build_dir])
);
gulp.task(
"clean-gallery",
gulp.parallel("clean-translations", () =>
del([paths.gallery_output_root, paths.gallery_build, paths.build_dir])
gulp.parallel("clean-translations", async () =>
(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
const del = import("del");
const fs = require("fs/promises");
const path = require("path");
const process = require("process");
const del = require("del");
const gulp = require("gulp");
const jszip = require("jszip");
const tar = require("tar");
@ -17,8 +17,8 @@ const WORKFLOW_NAME = "nightly.yaml";
const ARTIFACT_NAME = "translations";
const CLIENT_ID = "Iv1.3914e28cb27834d1";
const EXTRACT_DIR = "translations";
const TOKEN_FILE = path.join(EXTRACT_DIR, "token.json");
const ARTIFACT_FILE = path.join(EXTRACT_DIR, "artifact.json");
const TOKEN_FILE = path.posix.join(EXTRACT_DIR, "token.json");
const ARTIFACT_FILE = path.posix.join(EXTRACT_DIR, "artifact.json");
let allowTokenSetup = false;
gulp.task("allow-setup-fetch-nightly-translations", (done) => {
@ -137,7 +137,11 @@ gulp.task("fetch-nightly-translations", async function () {
// Remove the current translations
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)

View File

@ -1,4 +1,4 @@
const del = require("del");
const del = import("del");
const path = require("path");
const gulp = require("gulp");
const fs = require("fs");
@ -6,7 +6,7 @@ const paths = require("../paths");
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) => {
if (!fs.existsSync(outDir)) {

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -278,6 +278,11 @@ export class HaBarSlider extends LitElement {
--slider-bar-border-radius: 10px;
height: var(--slider-bar-thickness);
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]) {
width: var(--slider-bar-thickness);

View File

@ -104,6 +104,14 @@ export class HaBarSwitch extends LitElement {
box-sizing: border-box;
user-select: none;
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 {
box-sizing: border-box;

View File

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

View File

@ -8,7 +8,7 @@ import { BlueprintInput } from "./blueprint";
import { DeviceCondition, DeviceTrigger } from "./device_automation";
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 interface AutomationEntity extends HassEntityBase {
@ -29,7 +29,7 @@ export interface ManualAutomationConfig {
trigger: Trigger | Trigger[];
condition?: Condition | Condition[];
action: Action | Action[];
mode?: typeof MODES[number];
mode?: (typeof MODES)[number];
max?: number;
max_exceeded?:
| "silent"

View File

@ -84,3 +84,12 @@ export const setConversationOnboarding = (
type: "conversation/onboarding/set",
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;
export type EnergyGasUnitClass = typeof energyGasUnitClass[number];
export type EnergyGasUnitClass = (typeof energyGasUnitClass)[number];
export const getEnergyGasUnitClass = (
prefs: EnergyPreferences,

View File

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

View File

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

View File

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

View File

@ -77,7 +77,7 @@ const activateSceneActionStruct: Describe<ServiceSceneAction> = assign(
export interface ScriptEntity extends HassEntityBase {
attributes: HassEntityAttributeBase & {
last_triggered: string;
mode: typeof MODES[number];
mode: (typeof MODES)[number];
current?: number;
max?: number;
};
@ -89,7 +89,7 @@ export interface ManualScriptConfig {
alias: string;
sequence: Action | Action[];
icon?: string;
mode?: typeof MODES[number];
mode?: (typeof MODES)[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 {
AgentInfo,
getAgentInfo,
prepareConversation,
processConversationInput,
setConversationOnboarding,
} from "../../data/conversation";
@ -220,6 +221,7 @@ export class HaVoiceCommandDialog extends LitElement {
text: this.hass.localize("ui.dialogs.voice_command.how_can_i_help"),
},
];
prepareConversation(this.hass, this.hass.language);
}
protected updated(changedProps: PropertyValues) {

View File

@ -26,7 +26,7 @@ class DialogAutomationMode extends LitElement implements HassDialog {
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;

View File

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

View File

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

View File

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

View File

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

View File

@ -14,6 +14,7 @@ import { HomeAssistant } from "../../../../../types";
import "../../../../../components/ha-alert";
import { showPromptDialog } from "../../../../../dialogs/generic/show-dialog-box";
import { navigate } from "../../../../../common/navigate";
import { isComponentLoaded } from "../../../../../common/config/is_component_loaded";
@customElement("matter-config-panel")
export class MatterConfigPanel extends LitElement {
@ -32,18 +33,24 @@ export class MatterConfigPanel extends LitElement {
protected render(): TemplateResult {
return html`
<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">
<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">
${this._error
? 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
setup yet, or share them from another controller and enter the
share code.
@ -199,6 +206,10 @@ export class MatterConfigPanel extends LitElement {
static styles = [
haStyle,
css`
ha-alert[alert-type="warning"] {
position: relative;
top: -16px;
}
.content {
padding: 24px 0 32px;
max-width: 600px;
@ -208,6 +219,9 @@ export class MatterConfigPanel extends LitElement {
ha-card:first-child {
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 _currTab: typeof tabs[number] = "attributes";
@state() private _currTab: (typeof tabs)[number] = "attributes";
@state() private _clustersLoaded = false;

View File

@ -95,7 +95,7 @@ export class HaScriptEditor extends KeyboardShortcutMixin(LitElement) {
(
hasID: 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
return this.hass.localize(
`ui.panel.config.script.editor.max.${
data.mode as typeof MODES_MAX[number]
data.mode as (typeof MODES_MAX)[number]
}`
);
default:

View File

@ -1,8 +1,16 @@
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 { HassEntity } from "home-assistant-js-websocket";
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 { computeCssColor } from "../../../common/color/compute-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!);
}
private _handleIconAction() {
private _handleIconAction(ev: CustomEvent) {
ev.stopPropagation();
const config = {
entity: this._config!.entity,
tap_action: this._config!.icon_tap_action,
@ -219,6 +228,32 @@ export class HuiTileCard extends LitElement implements LovelaceCard {
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 {
if (!this._config || !this.hass) {
return html``;
@ -274,6 +309,7 @@ export class HuiTileCard extends LitElement implements LovelaceCard {
return html`
<ha-card style=${styleMap(style)}>
${this._shouldRenderRipple ? html`<mwc-ripple></mwc-ripple>` : null}
<div class="tile">
<div
class="icon-container"
@ -313,10 +349,17 @@ export class HuiTileCard extends LitElement implements LovelaceCard {
class="info"
.primary=${name}
.secondary=${stateDisplay}
role="button"
tabindex="0"
@action=${this._handleAction}
.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>
</div>
${supportedFeatures?.length
@ -365,11 +408,18 @@ export class HuiTileCard extends LitElement implements LovelaceCard {
return css`
:host {
--tile-color: rgb(var(--rgb-state-inactive-color));
--tile-tap-padding: 6px;
-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 {
--mdc-ripple-color: var(--tile-color);
height: 100%;
overflow: hidden;
// For safari overflow hidden
z-index: 0;
}
ha-card.disabled {
--tile-color: rgb(var(--rgb-disabled-color));
@ -381,18 +431,16 @@ export class HuiTileCard extends LitElement implements LovelaceCard {
outline: none;
}
.tile {
padding: calc(12px - var(--tile-tap-padding));
display: flex;
flex-direction: row;
align-items: center;
}
.icon-container {
position: relative;
padding: var(--tile-tap-padding);
flex: none;
margin-right: calc(12px - 2 * var(--tile-tap-padding));
margin-inline-end: calc(12px - 2 * var(--tile-tap-padding));
margin-inline-start: initial;
margin-right: 12px;
margin-inline-start: 12px;
margin-inline-end: initial;
direction: var(--direction);
transition: transform 180ms ease-in-out;
}
@ -401,8 +449,8 @@ export class HuiTileCard extends LitElement implements LovelaceCard {
}
.icon-container .badge {
position: absolute;
top: calc(-3px + var(--tile-tap-padding));
right: calc(-3px + var(--tile-tap-padding));
top: -3px;
right: -3px;
}
.icon-container[role="button"]:focus-visible,
.icon-container[role="button"]:active {
@ -410,27 +458,12 @@ export class HuiTileCard extends LitElement implements LovelaceCard {
}
.info {
position: relative;
padding: var(--tile-tap-padding);
padding: 12px;
flex: 1;
min-width: 0;
min-height: 40px;
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 &&
(domain === "scene" || domain === "script")
) {
const conf: typeof footerEntities[0] = {
const conf: (typeof footerEntities)[0] = {
entity: entityId,
show_icon: true,
show_name: true,

View File

@ -16,4 +16,4 @@ export const TIMESTAMP_RENDERING_FORMATS = [
] as const;
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 statTypeMap: Record<typeof stat_types[number], StatisticType> = {
const statTypeMap: Record<(typeof stat_types)[number], StatisticType> = {
mean: "mean",
min: "min",
max: "max",

View File

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

View File

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

View File

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

View File

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

View File

@ -2,6 +2,7 @@ import type { PropertyValues } from "lit";
import tinykeys from "tinykeys";
import { isComponentLoaded } from "../common/config/is_component_loaded";
import { mainWindow } from "../common/dom/get_main_window";
import { HaSelect } from "../components/ha-select";
import {
QuickBarParams,
showQuickBar,
@ -133,17 +134,17 @@ export default <T extends Constructor<HassElement>>(superClass: T) =>
}
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;
}
if (el.parentElement.tagName === "HA-SELECT") {
if (el instanceof Element && el.parentElement instanceof HaSelect) {
return false;
}
if (el.tagName !== "INPUT") {
if (!(el instanceof HTMLInputElement)) {
return true;
}

View File

@ -808,7 +808,7 @@
"did_not_understand": "Didn't quite get that",
"found": "I found the following for you:",
"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",
"send_text": "Send text",
"start_listening": "Start listening"

2189
yarn.lock

File diff suppressed because it is too large Load Diff