mirror of
https://github.com/home-assistant/frontend.git
synced 2025-07-29 12:16:39 +00:00
Compare commits
10 Commits
c73a9fccb8
...
9a4469588c
Author | SHA1 | Date | |
---|---|---|---|
![]() |
9a4469588c | ||
![]() |
f9eadf08fd | ||
![]() |
c630176fcf | ||
![]() |
0389fbba52 | ||
![]() |
d56c7c41e2 | ||
![]() |
e74cac697e | ||
![]() |
77216e8e76 | ||
![]() |
02a8924f63 | ||
![]() |
9fc28e5abb | ||
![]() |
933fb1327a |
File diff suppressed because one or more lines are too long
@ -6,4 +6,4 @@ enableGlobalCache: false
|
||||
|
||||
nodeLinker: node-modules
|
||||
|
||||
yarnPath: .yarn/releases/yarn-4.9.0.cjs
|
||||
yarnPath: .yarn/releases/yarn-4.9.1.cjs
|
||||
|
@ -89,8 +89,8 @@
|
||||
"@thomasloven/round-slider": "0.6.0",
|
||||
"@tsparticles/engine": "3.8.1",
|
||||
"@tsparticles/preset-links": "3.2.0",
|
||||
"@vaadin/combo-box": "24.7.2",
|
||||
"@vaadin/vaadin-themable-mixin": "24.7.2",
|
||||
"@vaadin/combo-box": "24.7.3",
|
||||
"@vaadin/vaadin-themable-mixin": "24.7.3",
|
||||
"@vibrant/color": "4.0.0",
|
||||
"@vue/web-component-wrapper": "1.3.0",
|
||||
"@webcomponents/scoped-custom-element-registry": "0.0.10",
|
||||
@ -221,7 +221,7 @@
|
||||
"terser-webpack-plugin": "5.3.14",
|
||||
"ts-lit-plugin": "2.0.2",
|
||||
"typescript": "5.8.3",
|
||||
"typescript-eslint": "8.29.1",
|
||||
"typescript-eslint": "8.30.0",
|
||||
"vite-tsconfig-paths": "5.1.4",
|
||||
"vitest": "3.1.1",
|
||||
"webpack-stats-plugin": "1.1.3",
|
||||
@ -238,5 +238,5 @@
|
||||
"globals": "16.0.0",
|
||||
"tslib": "2.8.1"
|
||||
},
|
||||
"packageManager": "yarn@4.9.0"
|
||||
"packageManager": "yarn@4.9.1"
|
||||
}
|
||||
|
15
public/static/images/z-wave-add-node/long-range.svg
Normal file
15
public/static/images/z-wave-add-node/long-range.svg
Normal file
@ -0,0 +1,15 @@
|
||||
<svg width="94" height="72" viewBox="0 0 94 72" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M76.9105 39.4999C77.739 39.4999 78.4105 38.8283 78.4105 37.9999C78.4105 37.1715 77.739 36.4999 76.9105 36.4999V39.4999ZM37.5 39.4999L76.9105 39.4999V36.4999L37.5 36.4999L37.5 39.4999Z" fill="#00AFFF" fill-opacity="0.3"/>
|
||||
<path d="M30.8239 22.3365L38.8239 38.8365L30.3239 50.3365" stroke="black" stroke-opacity="0.12" stroke-width="3" stroke-linecap="round"/>
|
||||
<mask id="mask0_1110_23734" style="mask-type:alpha" maskUnits="userSpaceOnUse" x="30" y="27" width="18" height="18">
|
||||
<path d="M45.75 42.075C45.75 42.4462 45.4462 42.75 45.075 42.75H32.925C32.5538 42.75 32.25 42.4462 32.25 42.075V36.675C32.25 36.3037 32.4649 35.7851 32.7276 35.5224L38.5224 29.7275C38.7851 29.4649 39.2143 29.4649 39.477 29.7275L45.2724 35.523C45.5351 35.7857 45.75 36.3043 45.75 36.6755V42.075Z" fill="black"/>
|
||||
</mask>
|
||||
<g mask="url(#mask0_1110_23734)">
|
||||
<rect x="30" y="27" width="18" height="18" fill="#212121"/>
|
||||
</g>
|
||||
<path d="M82 37.9999C82 36.343 83.3431 34.9999 85 34.9999C86.6569 34.9999 88 36.343 88 37.9999C88 39.6567 86.6569 40.9999 85 40.9999C83.3431 40.9999 82 39.6567 82 37.9999Z" stroke="#00AFFF" stroke-width="2"/>
|
||||
<rect x="23" y="11" width="8" height="8" rx="4" fill="black" fill-opacity="0.32"/>
|
||||
<rect x="22" y="52" width="8" height="8" rx="4" fill="black" fill-opacity="0.32"/>
|
||||
<path d="M21.5715 19.5C17.4983 23.801 15 29.6087 15 36C15 41.9085 17.1351 47.3183 20.6759 51.5" stroke="black" stroke-opacity="0.12" stroke-width="3" stroke-linecap="round"/>
|
||||
<circle cx="39" cy="36" r="33.5" stroke="black" stroke-opacity="0.12"/>
|
||||
</svg>
|
After Width: | Height: | Size: 1.6 KiB |
15
public/static/images/z-wave-add-node/long-range_dark.svg
Normal file
15
public/static/images/z-wave-add-node/long-range_dark.svg
Normal file
@ -0,0 +1,15 @@
|
||||
<svg width="94" height="72" viewBox="0 0 94 72" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M30.824 22.3365L38.824 38.8365L30.324 50.3365" stroke="white" stroke-opacity="0.24" stroke-width="3" stroke-linecap="round"/>
|
||||
<mask id="mask0_1180_4955" style="mask-type:alpha" maskUnits="userSpaceOnUse" x="30" y="27" width="18" height="18">
|
||||
<path d="M45.75 42.075C45.75 42.4462 45.4462 42.75 45.075 42.75H32.925C32.5538 42.75 32.25 42.4462 32.25 42.075V36.675C32.25 36.3037 32.4649 35.7851 32.7276 35.5224L38.5224 29.7275C38.7851 29.4649 39.2143 29.4649 39.477 29.7275L45.2724 35.523C45.5351 35.7857 45.75 36.3043 45.75 36.6755V42.075Z" fill="black"/>
|
||||
</mask>
|
||||
<g mask="url(#mask0_1180_4955)">
|
||||
<rect x="30" y="27" width="18" height="18" fill="#00AFFF"/>
|
||||
</g>
|
||||
<path d="M76.9105 39.4999C77.739 39.4999 78.4105 38.8283 78.4105 37.9999C78.4105 37.1715 77.739 36.4999 76.9105 36.4999V39.4999ZM37.5 39.4999L76.9105 39.4999V36.4999L37.5 36.4999L37.5 39.4999Z" fill="#00AFFF" fill-opacity="0.3"/>
|
||||
<path d="M82 37.9999C82 36.343 83.3431 34.9999 85 34.9999C86.6569 34.9999 88 36.343 88 37.9999C88 39.6567 86.6569 40.9999 85 40.9999C83.3431 40.9999 82 39.6567 82 37.9999Z" stroke="#00AFFF" stroke-width="2"/>
|
||||
<rect x="23" y="11" width="8" height="8" rx="4" fill="white" fill-opacity="0.48"/>
|
||||
<rect x="22" y="52" width="8" height="8" rx="4" fill="white" fill-opacity="0.48"/>
|
||||
<path d="M21.5715 19.5C17.4983 23.801 15 29.6087 15 36C15 41.9085 17.1351 47.3183 20.6759 51.5" stroke="white" stroke-opacity="0.24" stroke-width="3" stroke-linecap="round"/>
|
||||
<circle cx="39" cy="36" r="33.5" stroke="white" stroke-opacity="0.24"/>
|
||||
</svg>
|
After Width: | Height: | Size: 1.6 KiB |
19
public/static/images/z-wave-add-node/mesh.svg
Normal file
19
public/static/images/z-wave-add-node/mesh.svg
Normal file
@ -0,0 +1,19 @@
|
||||
<svg width="94" height="72" viewBox="0 0 94 72" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M63.1358 38.5084C63.9608 38.4334 64.5688 37.7037 64.4938 36.8787C64.4188 36.0537 63.6892 35.4457 62.8642 35.5207L63.1358 38.5084ZM46.6358 40.0084L63.1358 38.5084L62.8642 35.5207L46.3642 37.0207L46.6358 40.0084Z" fill="#00AFFF" fill-opacity="0.3"/>
|
||||
<path d="M38.5 22L45.9722 37.4115C46.2967 38.0807 46.223 38.8747 45.781 39.4728L38 50" stroke="#00AFFF" stroke-opacity="0.3" stroke-width="3" stroke-linecap="round" stroke-linejoin="bevel"/>
|
||||
<circle cx="47" cy="36" r="34" fill="white"/>
|
||||
<circle cx="47" cy="36" r="33.5" stroke="black" stroke-opacity="0.12"/>
|
||||
<path d="M41.8777 12.5216C43.4905 12.1798 45.1631 12 46.8777 12C58.2401 12 67.7582 19.8959 70.2445 30.5M40 59C42.1788 59.6506 44.4874 60 46.8777 60C56.9498 60 65.5728 53.7955 69.1332 45" stroke="#00AFFF" stroke-opacity="0.3" stroke-width="3" stroke-linecap="round" stroke-linejoin="bevel"/>
|
||||
<path d="M38.5 22L45.9722 37.4115C46.2967 38.0807 46.223 38.8747 45.781 39.4728L38 50" stroke="#00AFFF" stroke-opacity="0.3" stroke-width="3" stroke-linecap="round" stroke-linejoin="bevel"/>
|
||||
<mask id="mask0_1110_23775" style="mask-type:alpha" maskUnits="userSpaceOnUse" x="38" y="27" width="18" height="18">
|
||||
<path d="M53.75 42.075C53.75 42.4462 53.4462 42.75 53.075 42.75H40.925C40.5538 42.75 40.25 42.4462 40.25 42.075V36.675C40.25 36.3037 40.4649 35.7851 40.7276 35.5224L46.5224 29.7275C46.7851 29.4649 47.2143 29.4649 47.477 29.7275L53.2724 35.523C53.5351 35.7857 53.75 36.3043 53.75 36.6755V42.075Z" fill="black"/>
|
||||
</mask>
|
||||
<g mask="url(#mask0_1110_23775)">
|
||||
<rect x="38" y="27" width="18" height="18" fill="#212121"/>
|
||||
</g>
|
||||
<path d="M63.5 39.4999C64.3284 39.4999 65 38.8283 65 37.9999C65 37.1715 64.3284 36.4999 63.5 36.4999L63.5 39.4999ZM49.5 39.4999L63.5 39.4999L63.5 36.4999L49.5 36.4999L49.5 39.4999Z" fill="#00AFFF" fill-opacity="0.3"/>
|
||||
<rect x="31" y="11" width="8" height="8" rx="4" fill="#00AFFF" fill-opacity="0.6"/>
|
||||
<rect x="30" y="52" width="8" height="8" rx="4" fill="#00AFFF" fill-opacity="0.6"/>
|
||||
<path d="M29.5715 19.5C25.4983 23.801 23 29.6087 23 36C23 41.9085 25.1351 47.3183 28.6759 51.5" stroke="#00AFFF" stroke-opacity="0.3" stroke-width="3" stroke-linecap="round" stroke-linejoin="bevel"/>
|
||||
<path d="M68 37.9999C68 36.343 69.3431 34.9999 71 34.9999C72.6569 34.9999 74 36.343 74 37.9999C74 39.6567 72.6569 40.9999 71 40.9999C69.3431 40.9999 68 39.6567 68 37.9999Z" stroke="#00AFFF" stroke-width="2"/>
|
||||
</svg>
|
After Width: | Height: | Size: 2.4 KiB |
19
public/static/images/z-wave-add-node/mesh_dark.svg
Normal file
19
public/static/images/z-wave-add-node/mesh_dark.svg
Normal file
@ -0,0 +1,19 @@
|
||||
<svg width="94" height="72" viewBox="0 0 94 72" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M63.1358 38.5084C63.9608 38.4334 64.5688 37.7037 64.4938 36.8787C64.4188 36.0537 63.6892 35.4457 62.8642 35.5207L63.1358 38.5084ZM46.6358 40.0084L63.1358 38.5084L62.8642 35.5207L46.3642 37.0207L46.6358 40.0084Z" fill="#00AFFF" fill-opacity="0.3"/>
|
||||
<path d="M38.5 22L45.9722 37.4115C46.2967 38.0807 46.223 38.8747 45.781 39.4728L38 50" stroke="#00AFFF" stroke-opacity="0.3" stroke-width="3" stroke-linecap="round" stroke-linejoin="bevel"/>
|
||||
<circle cx="47" cy="36" r="34" fill="#1C1C1C"/>
|
||||
<circle cx="47" cy="36" r="33.5" stroke="white" stroke-opacity="0.24"/>
|
||||
<path d="M41.8777 12.5216C43.4905 12.1798 45.1631 12 46.8777 12C58.2401 12 67.7582 19.8959 70.2445 30.5M40 59C42.1788 59.6506 44.4874 60 46.8777 60C56.9498 60 65.5728 53.7955 69.1332 45" stroke="#00AFFF" stroke-opacity="0.3" stroke-width="3" stroke-linecap="round" stroke-linejoin="bevel"/>
|
||||
<path d="M38.5 22L45.9722 37.4115C46.2967 38.0807 46.223 38.8747 45.781 39.4728L38 50" stroke="#00AFFF" stroke-opacity="0.3" stroke-width="3" stroke-linecap="round" stroke-linejoin="bevel"/>
|
||||
<mask id="mask0_1180_4965" style="mask-type:alpha" maskUnits="userSpaceOnUse" x="38" y="27" width="18" height="18">
|
||||
<path d="M53.75 42.075C53.75 42.4462 53.4462 42.75 53.075 42.75H40.925C40.5538 42.75 40.25 42.4462 40.25 42.075V36.675C40.25 36.3037 40.4649 35.7851 40.7276 35.5224L46.5224 29.7275C46.7851 29.4649 47.2143 29.4649 47.477 29.7275L53.2724 35.523C53.5351 35.7857 53.75 36.3043 53.75 36.6755V42.075Z" fill="black"/>
|
||||
</mask>
|
||||
<g mask="url(#mask0_1180_4965)">
|
||||
<rect x="38" y="27" width="18" height="18" fill="#00AFFF"/>
|
||||
</g>
|
||||
<path d="M63.5 39.4999C64.3284 39.4999 65 38.8283 65 37.9999C65 37.1715 64.3284 36.4999 63.5 36.4999L63.5 39.4999ZM49.5 39.4999L63.5 39.4999L63.5 36.4999L49.5 36.4999L49.5 39.4999Z" fill="#00AFFF" fill-opacity="0.3"/>
|
||||
<rect x="31" y="11" width="8" height="8" rx="4" fill="#00AFFF" fill-opacity="0.6"/>
|
||||
<rect x="30" y="52" width="8" height="8" rx="4" fill="#00AFFF" fill-opacity="0.6"/>
|
||||
<path d="M29.5715 19.5C25.4983 23.801 23 29.6087 23 36C23 41.9085 25.1351 47.3183 28.6759 51.5" stroke="#00AFFF" stroke-opacity="0.3" stroke-width="3" stroke-linecap="round" stroke-linejoin="bevel"/>
|
||||
<path d="M68 37.9999C68 36.343 69.3431 34.9999 71 34.9999C72.6569 34.9999 74 36.343 74 37.9999C74 39.6567 72.6569 40.9999 71 40.9999C69.3431 40.9999 68 39.6567 68 37.9999Z" stroke="#00AFFF" stroke-width="2"/>
|
||||
</svg>
|
After Width: | Height: | Size: 2.4 KiB |
@ -5,7 +5,7 @@ import { getIntegrationDescriptions } from "../../data/integrations";
|
||||
import { showConfigFlowDialog } from "../../dialogs/config-flow/show-dialog-config-flow";
|
||||
import { showConfirmationDialog } from "../../dialogs/generic/show-dialog-box";
|
||||
import { showMatterAddDeviceDialog } from "../../panels/config/integrations/integration-panels/matter/show-dialog-add-matter-device";
|
||||
import { showZWaveJSAddNodeDialog } from "../../panels/config/integrations/integration-panels/zwave_js/show-dialog-zwave_js-add-node";
|
||||
import { showZWaveJSAddNodeDialog } from "../../panels/config/integrations/integration-panels/zwave_js/add-node/show-dialog-zwave_js-add-node";
|
||||
import type { HomeAssistant } from "../../types";
|
||||
import { documentationUrl } from "../../util/documentation-url";
|
||||
import { isComponentLoaded } from "../config/is_component_loaded";
|
||||
|
@ -1,7 +1,7 @@
|
||||
import type { ComboBoxLitRenderer } from "@vaadin/combo-box/lit";
|
||||
import type { HassEntity } from "home-assistant-js-websocket";
|
||||
import type { PropertyValues, TemplateResult } from "lit";
|
||||
import { LitElement, html } from "lit";
|
||||
import { LitElement, html, nothing } from "lit";
|
||||
import { customElement, property, query, state } from "lit/decorators";
|
||||
import memoizeOne from "memoize-one";
|
||||
import { fireEvent } from "../../common/dom/fire_event";
|
||||
@ -19,7 +19,7 @@ import type { EntityRegistryDisplayEntry } from "../../data/entity_registry";
|
||||
import type { HomeAssistant, ValueChangedEvent } from "../../types";
|
||||
import "../ha-combo-box";
|
||||
import type { HaComboBox } from "../ha-combo-box";
|
||||
import "../ha-list-item";
|
||||
import "../ha-combo-box-item";
|
||||
|
||||
interface Device {
|
||||
name: string;
|
||||
@ -35,11 +35,14 @@ export type HaDevicePickerDeviceFilterFunc = (
|
||||
|
||||
export type HaDevicePickerEntityFilterFunc = (entity: HassEntity) => boolean;
|
||||
|
||||
const rowRenderer: ComboBoxLitRenderer<Device> = (item) =>
|
||||
html`<ha-list-item .twoline=${!!item.area}>
|
||||
<span>${item.name}</span>
|
||||
<span slot="secondary">${item.area}</span>
|
||||
</ha-list-item>`;
|
||||
const rowRenderer: ComboBoxLitRenderer<Device> = (item) => html`
|
||||
<ha-combo-box-item type="button">
|
||||
<span slot="headline">${item.name}</span>
|
||||
${item.area
|
||||
? html`<span slot="supporting-text">${item.area}</span>`
|
||||
: nothing}
|
||||
</ha-combo-box-item>
|
||||
`;
|
||||
|
||||
@customElement("ha-device-picker")
|
||||
export class HaDevicePicker extends LitElement {
|
||||
|
@ -1,35 +1,78 @@
|
||||
import "../ha-list-item";
|
||||
import { mdiMagnify, mdiPlus } from "@mdi/js";
|
||||
import type { ComboBoxLitRenderer } from "@vaadin/combo-box/lit";
|
||||
import type { IFuseOptions } from "fuse.js";
|
||||
import Fuse from "fuse.js";
|
||||
import type { HassEntity } from "home-assistant-js-websocket";
|
||||
import type { PropertyValues, TemplateResult } from "lit";
|
||||
import { html, LitElement } from "lit";
|
||||
import type { ComboBoxLitRenderer } from "@vaadin/combo-box/lit";
|
||||
import { html, LitElement, nothing } from "lit";
|
||||
import { customElement, property, query, state } from "lit/decorators";
|
||||
import { styleMap } from "lit/directives/style-map";
|
||||
import memoizeOne from "memoize-one";
|
||||
import { fireEvent } from "../../common/dom/fire_event";
|
||||
import { computeAreaName } from "../../common/entity/compute_area_name";
|
||||
import { computeDeviceName } from "../../common/entity/compute_device_name";
|
||||
import { computeDomain } from "../../common/entity/compute_domain";
|
||||
import { computeEntityName } from "../../common/entity/compute_entity_name";
|
||||
import { computeStateName } from "../../common/entity/compute_state_name";
|
||||
import type { ScorableTextItem } from "../../common/string/filter/sequence-matching";
|
||||
import { fuzzyFilterSort } from "../../common/string/filter/sequence-matching";
|
||||
import type { ValueChangedEvent, HomeAssistant } from "../../types";
|
||||
import "../ha-combo-box";
|
||||
import type { HaComboBox } from "../ha-combo-box";
|
||||
import "../ha-icon-button";
|
||||
import "../ha-svg-icon";
|
||||
import "./state-badge";
|
||||
import { getEntityContext } from "../../common/entity/get_entity_context";
|
||||
import { caseInsensitiveStringCompare } from "../../common/string/compare";
|
||||
import { showHelperDetailDialog } from "../../panels/config/helpers/show-dialog-helper-detail";
|
||||
import { computeRTL } from "../../common/util/compute_rtl";
|
||||
import { domainToName } from "../../data/integration";
|
||||
import type { HelperDomain } from "../../panels/config/helpers/const";
|
||||
import { isHelperDomain } from "../../panels/config/helpers/const";
|
||||
import { showHelperDetailDialog } from "../../panels/config/helpers/show-dialog-helper-detail";
|
||||
import type { HomeAssistant, ValueChangedEvent } from "../../types";
|
||||
import "../ha-combo-box";
|
||||
import type { HaComboBox } from "../ha-combo-box";
|
||||
import "../ha-combo-box-item";
|
||||
import "../ha-icon-button";
|
||||
import "../ha-list-item";
|
||||
import "../ha-svg-icon";
|
||||
import "./state-badge";
|
||||
|
||||
interface HassEntityWithCachedName extends HassEntity, ScorableTextItem {
|
||||
friendly_name: string;
|
||||
const FAKE_ENTITY: HassEntity = {
|
||||
entity_id: "",
|
||||
state: "",
|
||||
last_changed: "",
|
||||
last_updated: "",
|
||||
context: { id: "", user_id: null, parent_id: null },
|
||||
attributes: {},
|
||||
};
|
||||
|
||||
interface EntityPickerItem extends HassEntity {
|
||||
label: string;
|
||||
primary: string;
|
||||
secondary?: string;
|
||||
translated_domain?: string;
|
||||
show_entity_id?: boolean;
|
||||
entity_name?: string;
|
||||
area_name?: string;
|
||||
device_name?: string;
|
||||
friendly_name?: string;
|
||||
sorting_label?: string;
|
||||
icon_path?: string;
|
||||
}
|
||||
|
||||
export type HaEntityPickerEntityFilterFunc = (entity: HassEntity) => boolean;
|
||||
|
||||
const CREATE_ID = "___create-new-entity___";
|
||||
|
||||
const DOMAIN_STYLE = styleMap({
|
||||
fontSize: "12px",
|
||||
fontWeight: "400",
|
||||
lineHeight: "18px",
|
||||
alignSelf: "flex-end",
|
||||
maxWidth: "30%",
|
||||
textOverflow: "ellipsis",
|
||||
overflow: "hidden",
|
||||
whiteSpace: "nowrap",
|
||||
});
|
||||
|
||||
const ENTITY_ID_STYLE = styleMap({
|
||||
fontFamily: "var(--code-font-family, monospace)",
|
||||
fontSize: "11px",
|
||||
});
|
||||
|
||||
@customElement("ha-entity-picker")
|
||||
export class HaEntityPicker extends LitElement {
|
||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||
@ -106,8 +149,7 @@ export class HaEntityPicker extends LitElement {
|
||||
@property({ attribute: "hide-clear-icon", type: Boolean })
|
||||
public hideClearIcon = false;
|
||||
|
||||
@property({ attribute: "item-label-path" }) public itemLabelPath =
|
||||
"friendly_name";
|
||||
@property({ attribute: "item-label-path" }) public itemLabelPath = "label";
|
||||
|
||||
@state() private _opened = false;
|
||||
|
||||
@ -123,30 +165,48 @@ export class HaEntityPicker extends LitElement {
|
||||
await this.comboBox?.focus();
|
||||
}
|
||||
|
||||
private _initedStates = false;
|
||||
private _initialItems = false;
|
||||
|
||||
private _states: HassEntityWithCachedName[] = [];
|
||||
private _items: EntityPickerItem[] = [];
|
||||
|
||||
private _rowRenderer: ComboBoxLitRenderer<HassEntityWithCachedName> = (
|
||||
item
|
||||
) =>
|
||||
html`<ha-list-item graphic="avatar" .twoline=${!!item.entity_id}>
|
||||
${item.state
|
||||
? html`<state-badge
|
||||
slot="graphic"
|
||||
.stateObj=${item}
|
||||
.hass=${this.hass}
|
||||
></state-badge>`
|
||||
: ""}
|
||||
<span>${item.friendly_name}</span>
|
||||
<span slot="secondary"
|
||||
>${item.entity_id.startsWith(CREATE_ID)
|
||||
? this.hass.localize("ui.components.entity.entity-picker.new_entity")
|
||||
: item.entity_id}</span
|
||||
>
|
||||
</ha-list-item>`;
|
||||
protected firstUpdated(changedProperties: PropertyValues): void {
|
||||
super.firstUpdated(changedProperties);
|
||||
this.hass.loadBackendTranslation("title");
|
||||
}
|
||||
|
||||
private _getStates = memoizeOne(
|
||||
private _rowRenderer: ComboBoxLitRenderer<EntityPickerItem> = (
|
||||
item,
|
||||
{ index }
|
||||
) => html`
|
||||
<ha-combo-box-item type="button" compact .borderTop=${index !== 0}>
|
||||
${item.icon_path
|
||||
? html`<ha-svg-icon slot="start" .path=${item.icon_path}></ha-svg-icon>`
|
||||
: html`
|
||||
<state-badge
|
||||
slot="start"
|
||||
.stateObj=${item}
|
||||
.hass=${this.hass}
|
||||
></state-badge>
|
||||
`}
|
||||
|
||||
<span slot="headline">${item.primary} </span>
|
||||
${item.secondary
|
||||
? html`<span slot="supporting-text">${item.secondary}</span>`
|
||||
: nothing}
|
||||
${item.entity_id && item.show_entity_id
|
||||
? html`<span slot="supporting-text" style=${ENTITY_ID_STYLE}
|
||||
>${item.entity_id}</span
|
||||
>`
|
||||
: nothing}
|
||||
${item.translated_domain && !item.show_entity_id
|
||||
? html`<div slot="trailing-supporting-text" style=${DOMAIN_STYLE}>
|
||||
${item.translated_domain}
|
||||
</div>`
|
||||
: nothing}
|
||||
</ha-combo-box-item>
|
||||
`;
|
||||
|
||||
private _getItems = memoizeOne(
|
||||
(
|
||||
_opened: boolean,
|
||||
hass: this["hass"],
|
||||
@ -158,8 +218,8 @@ export class HaEntityPicker extends LitElement {
|
||||
includeEntities: this["includeEntities"],
|
||||
excludeEntities: this["excludeEntities"],
|
||||
createDomains: this["createDomains"]
|
||||
): HassEntityWithCachedName[] => {
|
||||
let states: HassEntityWithCachedName[] = [];
|
||||
): EntityPickerItem[] => {
|
||||
let states: EntityPickerItem[] = [];
|
||||
|
||||
if (!hass) {
|
||||
return [];
|
||||
@ -168,7 +228,7 @@ export class HaEntityPicker extends LitElement {
|
||||
|
||||
const createItems = createDomains?.length
|
||||
? createDomains.map((domain) => {
|
||||
const newFriendlyName = hass.localize(
|
||||
const primary = hass.localize(
|
||||
"ui.components.entity.entity-picker.create_helper",
|
||||
{
|
||||
domain: isHelperDomain(domain)
|
||||
@ -180,16 +240,14 @@ export class HaEntityPicker extends LitElement {
|
||||
);
|
||||
|
||||
return {
|
||||
...FAKE_ENTITY,
|
||||
entity_id: CREATE_ID + domain,
|
||||
state: "on",
|
||||
last_changed: "",
|
||||
last_updated: "",
|
||||
context: { id: "", user_id: null, parent_id: null },
|
||||
friendly_name: newFriendlyName,
|
||||
attributes: {
|
||||
icon: "mdi:plus",
|
||||
},
|
||||
strings: [domain, newFriendlyName],
|
||||
primary: primary,
|
||||
label: primary,
|
||||
secondary: this.hass.localize(
|
||||
"ui.components.entity.entity-picker.new_entity"
|
||||
),
|
||||
icon_path: mdiPlus,
|
||||
};
|
||||
})
|
||||
: [];
|
||||
@ -197,21 +255,14 @@ export class HaEntityPicker extends LitElement {
|
||||
if (!entityIds.length) {
|
||||
return [
|
||||
{
|
||||
entity_id: "",
|
||||
state: "",
|
||||
last_changed: "",
|
||||
last_updated: "",
|
||||
context: { id: "", user_id: null, parent_id: null },
|
||||
friendly_name: this.hass!.localize(
|
||||
...FAKE_ENTITY,
|
||||
primary: this.hass!.localize(
|
||||
"ui.components.entity.entity-picker.no_entities"
|
||||
),
|
||||
attributes: {
|
||||
friendly_name: this.hass!.localize(
|
||||
"ui.components.entity.entity-picker.no_entities"
|
||||
),
|
||||
icon: "mdi:magnify",
|
||||
},
|
||||
strings: [],
|
||||
label: this.hass!.localize(
|
||||
"ui.components.entity.entity-picker.no_entities"
|
||||
),
|
||||
icon_path: mdiMagnify,
|
||||
},
|
||||
...createItems,
|
||||
];
|
||||
@ -241,19 +292,49 @@ export class HaEntityPicker extends LitElement {
|
||||
);
|
||||
}
|
||||
|
||||
const isRTL = computeRTL(this.hass);
|
||||
|
||||
states = entityIds
|
||||
.map((key) => {
|
||||
const friendly_name = computeStateName(hass!.states[key]) || key;
|
||||
.map<EntityPickerItem>((entityId) => {
|
||||
const stateObj = hass!.states[entityId];
|
||||
|
||||
const { area, device } = getEntityContext(stateObj, hass);
|
||||
|
||||
const friendlyName = computeStateName(stateObj); // Keep this for search
|
||||
const entityName = computeEntityName(stateObj, hass);
|
||||
const deviceName = device ? computeDeviceName(device) : undefined;
|
||||
const areaName = area ? computeAreaName(area) : undefined;
|
||||
|
||||
const primary = entityName || deviceName || entityId;
|
||||
const secondary = [areaName, entityName ? deviceName : undefined]
|
||||
.filter(Boolean)
|
||||
.join(isRTL ? " ◂ " : " ▸ ");
|
||||
|
||||
const translatedDomain = domainToName(
|
||||
this.hass.localize,
|
||||
computeDomain(entityId)
|
||||
);
|
||||
|
||||
return {
|
||||
...hass!.states[key],
|
||||
friendly_name,
|
||||
strings: [key, friendly_name],
|
||||
...hass!.states[entityId],
|
||||
primary: primary,
|
||||
secondary:
|
||||
secondary ||
|
||||
this.hass.localize("ui.components.device-picker.no_area"),
|
||||
label: friendlyName,
|
||||
translated_domain: translatedDomain,
|
||||
sorting_label: [deviceName, entityName].filter(Boolean).join("-"),
|
||||
entity_name: entityName || deviceName,
|
||||
area_name: areaName,
|
||||
device_name: deviceName,
|
||||
friendly_name: friendlyName,
|
||||
show_entity_id: hass.userData?.showEntityIdPicker,
|
||||
};
|
||||
})
|
||||
.sort((entityA, entityB) =>
|
||||
caseInsensitiveStringCompare(
|
||||
entityA.friendly_name,
|
||||
entityB.friendly_name,
|
||||
entityA.sorting_label!,
|
||||
entityB.sorting_label!,
|
||||
this.hass.locale.language
|
||||
)
|
||||
);
|
||||
@ -291,21 +372,14 @@ export class HaEntityPicker extends LitElement {
|
||||
if (!states.length) {
|
||||
return [
|
||||
{
|
||||
entity_id: "",
|
||||
state: "",
|
||||
last_changed: "",
|
||||
last_updated: "",
|
||||
context: { id: "", user_id: null, parent_id: null },
|
||||
friendly_name: this.hass!.localize(
|
||||
...FAKE_ENTITY,
|
||||
primary: this.hass!.localize(
|
||||
"ui.components.entity.entity-picker.no_match"
|
||||
),
|
||||
attributes: {
|
||||
friendly_name: this.hass!.localize(
|
||||
"ui.components.entity.entity-picker.no_match"
|
||||
),
|
||||
icon: "mdi:magnify",
|
||||
},
|
||||
strings: [],
|
||||
label: this.hass!.localize(
|
||||
"ui.components.entity.entity-picker.no_match"
|
||||
),
|
||||
icon_path: mdiMagnify,
|
||||
},
|
||||
...createItems,
|
||||
];
|
||||
@ -331,8 +405,8 @@ export class HaEntityPicker extends LitElement {
|
||||
}
|
||||
|
||||
public willUpdate(changedProps: PropertyValues) {
|
||||
if (!this._initedStates || (changedProps.has("_opened") && this._opened)) {
|
||||
this._states = this._getStates(
|
||||
if (!this._initialItems || (changedProps.has("_opened") && this._opened)) {
|
||||
this._items = this._getItems(
|
||||
this._opened,
|
||||
this.hass,
|
||||
this.includeDomains,
|
||||
@ -344,10 +418,10 @@ export class HaEntityPicker extends LitElement {
|
||||
this.excludeEntities,
|
||||
this.createDomains
|
||||
);
|
||||
if (this._initedStates) {
|
||||
this.comboBox.filteredItems = this._states;
|
||||
if (this._initialItems) {
|
||||
this.comboBox.filteredItems = this._items;
|
||||
}
|
||||
this._initedStates = true;
|
||||
this._initialItems = true;
|
||||
}
|
||||
|
||||
if (changedProps.has("createDomains") && this.createDomains?.length) {
|
||||
@ -367,7 +441,7 @@ export class HaEntityPicker extends LitElement {
|
||||
: this.label}
|
||||
.helper=${this.helper}
|
||||
.allowCustomValue=${this.allowCustomEntity}
|
||||
.filteredItems=${this._states}
|
||||
.filteredItems=${this._items}
|
||||
.renderer=${this._rowRenderer}
|
||||
.required=${this.required}
|
||||
.disabled=${this.disabled}
|
||||
@ -408,12 +482,49 @@ export class HaEntityPicker extends LitElement {
|
||||
}
|
||||
}
|
||||
|
||||
private _fuseKeys = [
|
||||
"entity_name",
|
||||
"device_name",
|
||||
"area_name",
|
||||
"translated_domain",
|
||||
"friendly_name", // for backwards compatibility
|
||||
"entity_id", // for technical search
|
||||
];
|
||||
|
||||
private _fuseIndex = memoizeOne((states: EntityPickerItem[]) =>
|
||||
Fuse.createIndex(this._fuseKeys, states)
|
||||
);
|
||||
|
||||
private _filterChanged(ev: CustomEvent): void {
|
||||
const target = ev.target as HaComboBox;
|
||||
const filterString = ev.detail.value.trim().toLowerCase();
|
||||
target.filteredItems = filterString.length
|
||||
? fuzzyFilterSort<HassEntityWithCachedName>(filterString, this._states)
|
||||
: this._states;
|
||||
const filterString = ev.detail.value.trim().toLowerCase() as string;
|
||||
|
||||
const minLength = 2;
|
||||
|
||||
const searchTerms = (filterString.split(" ") ?? []).filter(
|
||||
(term) => term.length >= minLength
|
||||
);
|
||||
|
||||
if (searchTerms.length > 0) {
|
||||
const index = this._fuseIndex(this._items);
|
||||
|
||||
const options: IFuseOptions<EntityPickerItem> = {
|
||||
isCaseSensitive: false,
|
||||
threshold: 0.3,
|
||||
ignoreDiacritics: true,
|
||||
minMatchCharLength: minLength,
|
||||
};
|
||||
|
||||
const fuse = new Fuse(this._items, options, index);
|
||||
const results = fuse.search({
|
||||
$and: searchTerms.map((term) => ({
|
||||
$or: this._fuseKeys.map((key) => ({ [key]: term })),
|
||||
})),
|
||||
});
|
||||
target.filteredItems = results.map((result) => result.item);
|
||||
} else {
|
||||
target.filteredItems = this._items;
|
||||
}
|
||||
}
|
||||
|
||||
private _setValue(value: string | undefined) {
|
||||
|
@ -1,23 +1,23 @@
|
||||
import "@material/mwc-list/mwc-list-item";
|
||||
import type { ComboBoxLitRenderer } from "@vaadin/combo-box/lit";
|
||||
import type { HassEntity } from "home-assistant-js-websocket";
|
||||
import type { PropertyValues, TemplateResult } from "lit";
|
||||
import { html, LitElement, nothing } from "lit";
|
||||
import type { ComboBoxLitRenderer } from "@vaadin/combo-box/lit";
|
||||
import { customElement, property, query, state } from "lit/decorators";
|
||||
import memoizeOne from "memoize-one";
|
||||
import { ensureArray } from "../../common/array/ensure-array";
|
||||
import { fireEvent } from "../../common/dom/fire_event";
|
||||
import { stringCompare } from "../../common/string/compare";
|
||||
import type { ScorableTextItem } from "../../common/string/filter/sequence-matching";
|
||||
import { fuzzyFilterSort } from "../../common/string/filter/sequence-matching";
|
||||
import type { StatisticsMetaData } from "../../data/recorder";
|
||||
import { getStatisticIds, getStatisticLabel } from "../../data/recorder";
|
||||
import type { ValueChangedEvent, HomeAssistant } from "../../types";
|
||||
import type { HomeAssistant, ValueChangedEvent } from "../../types";
|
||||
import { documentationUrl } from "../../util/documentation-url";
|
||||
import "../ha-combo-box";
|
||||
import type { HaComboBox } from "../ha-combo-box";
|
||||
import "../ha-combo-box-item";
|
||||
import "../ha-svg-icon";
|
||||
import "./state-badge";
|
||||
import type { ScorableTextItem } from "../../common/string/filter/sequence-matching";
|
||||
import { fuzzyFilterSort } from "../../common/string/filter/sequence-matching";
|
||||
|
||||
interface StatisticItem extends ScorableTextItem {
|
||||
id: string;
|
||||
@ -99,16 +99,18 @@ export class HaStatisticPicker extends LitElement {
|
||||
@state() private _filteredItems?: StatisticItem[] = undefined;
|
||||
|
||||
private _rowRenderer: ComboBoxLitRenderer<StatisticItem> = (item) =>
|
||||
html`<mwc-list-item graphic="avatar" twoline>
|
||||
html`<ha-combo-box-item type="button">
|
||||
${item.state
|
||||
? html`<state-badge
|
||||
slot="graphic"
|
||||
.stateObj=${item.state}
|
||||
.hass=${this.hass}
|
||||
></state-badge>`
|
||||
: ""}
|
||||
<span>${item.name}</span>
|
||||
<span slot="secondary"
|
||||
? html`
|
||||
<state-badge
|
||||
slot="start"
|
||||
.stateObj=${item.state}
|
||||
.hass=${this.hass}
|
||||
></state-badge>
|
||||
`
|
||||
: html`<span slot="start" style="width: 32px"></span>`}
|
||||
<span slot="headline">${item.name}</span>
|
||||
<span slot="supporting-text"
|
||||
>${item.id === "" || item.id === "__missing"
|
||||
? html`<a
|
||||
target="_blank"
|
||||
@ -120,7 +122,7 @@ export class HaStatisticPicker extends LitElement {
|
||||
>`
|
||||
: item.id}</span
|
||||
>
|
||||
</mwc-list-item>`;
|
||||
</ha-combo-box-item>`;
|
||||
|
||||
private _getStatistics = memoizeOne(
|
||||
(
|
||||
|
@ -10,20 +10,23 @@ import type { HomeAssistant, ValueChangedEvent } from "../types";
|
||||
import "./ha-alert";
|
||||
import "./ha-combo-box";
|
||||
import type { HaComboBox } from "./ha-combo-box";
|
||||
import "./ha-list-item";
|
||||
import "./ha-combo-box-item";
|
||||
|
||||
const rowRenderer: ComboBoxLitRenderer<HassioAddonInfo> = (item) =>
|
||||
html`<ha-list-item twoline graphic="icon">
|
||||
<span>${item.name}</span>
|
||||
<span slot="secondary">${item.slug}</span>
|
||||
const rowRenderer: ComboBoxLitRenderer<HassioAddonInfo> = (item) => html`
|
||||
<ha-combo-box-item type="button">
|
||||
<span slot="headline">${item.name}</span>
|
||||
<span slot="supporting-text">${item.slug}</span>
|
||||
${item.icon
|
||||
? html`<img
|
||||
alt=""
|
||||
slot="graphic"
|
||||
.src="/api/hassio/addons/${item.slug}/icon"
|
||||
/>`
|
||||
: ""}
|
||||
</ha-list-item>`;
|
||||
? html`
|
||||
<img
|
||||
alt=""
|
||||
slot="start"
|
||||
.src="/api/hassio/addons/${item.slug}/icon"
|
||||
/>
|
||||
`
|
||||
: nothing}
|
||||
</ha-combo-box-item>
|
||||
`;
|
||||
|
||||
@customElement("ha-addon-picker")
|
||||
class HaAddonPicker extends LitElement {
|
||||
|
@ -25,6 +25,7 @@ import type { HomeAssistant, ValueChangedEvent } from "../types";
|
||||
import type { HaDevicePickerDeviceFilterFunc } from "./device/ha-device-picker";
|
||||
import "./ha-combo-box";
|
||||
import type { HaComboBox } from "./ha-combo-box";
|
||||
import "./ha-combo-box-item";
|
||||
import "./ha-floor-icon";
|
||||
import "./ha-icon-button";
|
||||
import "./ha-list-item";
|
||||
@ -125,38 +126,38 @@ export class HaAreaFloorPicker extends LitElement {
|
||||
private _rowRenderer: ComboBoxLitRenderer<FloorAreaEntry> = (item) => {
|
||||
const rtl = computeRTL(this.hass);
|
||||
return html`
|
||||
<ha-list-item
|
||||
graphic="icon"
|
||||
<ha-combo-box-item
|
||||
type="button"
|
||||
style=${item.type === "area" && item.hasFloor
|
||||
? rtl
|
||||
? "--mdc-list-side-padding-right: 48px;"
|
||||
: "--mdc-list-side-padding-left: 48px;"
|
||||
? "--md-list-item-leading-space: 48px;"
|
||||
: ""}
|
||||
>
|
||||
${item.type === "area" && item.hasFloor
|
||||
? html`<ha-tree-indicator
|
||||
style=${styleMap({
|
||||
width: "48px",
|
||||
position: "absolute",
|
||||
top: "0px",
|
||||
left: rtl ? undefined : "8px",
|
||||
right: rtl ? "8px" : undefined,
|
||||
transform: rtl ? "scaleX(-1)" : "",
|
||||
})}
|
||||
.end=${item.lastArea}
|
||||
slot="graphic"
|
||||
></ha-tree-indicator>`
|
||||
? html`
|
||||
<ha-tree-indicator
|
||||
style=${styleMap({
|
||||
width: "48px",
|
||||
position: "absolute",
|
||||
top: "0px",
|
||||
left: rtl ? undefined : "4px",
|
||||
right: rtl ? "4px" : undefined,
|
||||
transform: rtl ? "scaleX(-1)" : "",
|
||||
})}
|
||||
.end=${item.lastArea}
|
||||
slot="start"
|
||||
></ha-tree-indicator>
|
||||
`
|
||||
: nothing}
|
||||
${item.type === "floor"
|
||||
? html`<ha-floor-icon slot="graphic" .floor=${item}></ha-floor-icon>`
|
||||
? html`<ha-floor-icon slot="start" .floor=${item}></ha-floor-icon>`
|
||||
: item.icon
|
||||
? html`<ha-icon slot="graphic" .icon=${item.icon}></ha-icon>`
|
||||
? html`<ha-icon slot="start" .icon=${item.icon}></ha-icon>`
|
||||
: html`<ha-svg-icon
|
||||
slot="graphic"
|
||||
slot="start"
|
||||
.path=${mdiTextureBox}
|
||||
></ha-svg-icon>`}
|
||||
${item.name}
|
||||
</ha-list-item>
|
||||
</ha-combo-box-item>
|
||||
`;
|
||||
};
|
||||
|
||||
|
@ -4,7 +4,6 @@ import type { HassEntity } from "home-assistant-js-websocket";
|
||||
import type { PropertyValues, TemplateResult } from "lit";
|
||||
import { LitElement, html } from "lit";
|
||||
import { customElement, property, query, state } from "lit/decorators";
|
||||
import { classMap } from "lit/directives/class-map";
|
||||
import memoizeOne from "memoize-one";
|
||||
import { fireEvent } from "../common/dom/fire_event";
|
||||
import { computeDomain } from "../common/entity/compute_domain";
|
||||
@ -24,22 +23,21 @@ import type { HomeAssistant, ValueChangedEvent } from "../types";
|
||||
import type { HaDevicePickerDeviceFilterFunc } from "./device/ha-device-picker";
|
||||
import "./ha-combo-box";
|
||||
import type { HaComboBox } from "./ha-combo-box";
|
||||
import "./ha-combo-box-item";
|
||||
import "./ha-icon-button";
|
||||
import "./ha-list-item";
|
||||
import "./ha-svg-icon";
|
||||
|
||||
type ScorableAreaRegistryEntry = ScorableTextItem & AreaRegistryEntry;
|
||||
|
||||
const rowRenderer: ComboBoxLitRenderer<AreaRegistryEntry> = (item) =>
|
||||
html`<ha-list-item
|
||||
graphic="icon"
|
||||
class=${classMap({ "add-new": item.area_id === ADD_NEW_ID })}
|
||||
>
|
||||
const rowRenderer: ComboBoxLitRenderer<AreaRegistryEntry> = (item) => html`
|
||||
<ha-combo-box-item type="button">
|
||||
${item.icon
|
||||
? html`<ha-icon slot="graphic" .icon=${item.icon}></ha-icon>`
|
||||
: html`<ha-svg-icon slot="graphic" .path=${mdiTextureBox}></ha-svg-icon>`}
|
||||
? html`<ha-icon slot="start" .icon=${item.icon}></ha-icon>`
|
||||
: html`<ha-svg-icon slot="start" .path=${mdiTextureBox}></ha-svg-icon>`}
|
||||
${item.name}
|
||||
</ha-list-item>`;
|
||||
</ha-combo-box-item>
|
||||
`;
|
||||
|
||||
const ADD_NEW_ID = "___ADD_NEW___";
|
||||
const NO_ITEMS_ID = "___NO_ITEMS___";
|
||||
|
@ -1,6 +1,7 @@
|
||||
import { css, html, LitElement, nothing, type PropertyValues } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import { repeat } from "lit/directives/repeat";
|
||||
import { styleMap } from "lit/directives/style-map";
|
||||
import memoizeOne from "memoize-one";
|
||||
import { computeStateName } from "../common/entity/compute_state_name";
|
||||
import { supportsFeature } from "../common/entity/supports-feature";
|
||||
@ -32,6 +33,10 @@ export class HaCameraStream extends LitElement {
|
||||
|
||||
@property({ attribute: false }) public stateObj?: CameraEntity;
|
||||
|
||||
@property({ attribute: false }) public aspectRatio?: number;
|
||||
|
||||
@property({ attribute: false }) public fitMode?: "cover" | "contain" | "fill";
|
||||
|
||||
@property({ type: Boolean, attribute: "controls" })
|
||||
public controls = false;
|
||||
|
||||
@ -101,6 +106,10 @@ export class HaCameraStream extends LitElement {
|
||||
: this._connected
|
||||
? computeMJPEGStreamUrl(this.stateObj)
|
||||
: this._posterUrl || ""}
|
||||
style=${styleMap({
|
||||
aspectRatio: this.aspectRatio,
|
||||
objectFit: this.fitMode,
|
||||
})}
|
||||
alt=${`Preview of the ${computeStateName(this.stateObj)} camera.`}
|
||||
/>`;
|
||||
}
|
||||
@ -117,6 +126,8 @@ export class HaCameraStream extends LitElement {
|
||||
.posterUrl=${this._posterUrl}
|
||||
@streams=${this._handleHlsStreams}
|
||||
class=${stream.visible ? "" : "hidden"}
|
||||
.aspectRatio=${this.aspectRatio}
|
||||
.fitMode=${this.fitMode}
|
||||
></ha-hls-player>`;
|
||||
}
|
||||
|
||||
@ -131,6 +142,8 @@ export class HaCameraStream extends LitElement {
|
||||
.posterUrl=${this._posterUrl}
|
||||
@streams=${this._handleWebRtcStreams}
|
||||
class=${stream.visible ? "" : "hidden"}
|
||||
.aspectRatio=${this.aspectRatio}
|
||||
.fitMode=${this.fitMode}
|
||||
></ha-web-rtc-player>`;
|
||||
}
|
||||
|
||||
@ -259,6 +272,16 @@ export class HaCameraStream extends LitElement {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
ha-web-rtc-player {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
ha-hls-player {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.hidden {
|
||||
display: none;
|
||||
}
|
||||
|
46
src/components/ha-combo-box-item.ts
Normal file
46
src/components/ha-combo-box-item.ts
Normal file
@ -0,0 +1,46 @@
|
||||
import { css } from "lit";
|
||||
import { customElement, property } from "lit/decorators";
|
||||
import { HaMdListItem } from "./ha-md-list-item";
|
||||
|
||||
@customElement("ha-combo-box-item")
|
||||
export class HaComboBoxItem extends HaMdListItem {
|
||||
@property({ type: Boolean, reflect: true, attribute: "border-top" })
|
||||
public borderTop = false;
|
||||
|
||||
static override styles = [
|
||||
...super.styles,
|
||||
css`
|
||||
:host {
|
||||
--md-list-item-one-line-container-height: 48px;
|
||||
--md-list-item-two-line-container-height: 64px;
|
||||
}
|
||||
:host([border-top]) md-item {
|
||||
border-top: 1px solid var(--divider-color);
|
||||
}
|
||||
[slot="start"] {
|
||||
--paper-item-icon-color: var(--secondary-text-color);
|
||||
}
|
||||
[slot="headline"] {
|
||||
line-height: 22px;
|
||||
font-size: 14px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
[slot="supporting-text"] {
|
||||
line-height: 18px;
|
||||
font-size: 12px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
::slotted(state-badge),
|
||||
::slotted(img) {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
}
|
||||
`,
|
||||
];
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"ha-combo-box-item": HaComboBoxItem;
|
||||
}
|
||||
}
|
@ -16,8 +16,8 @@ import { customElement, property, query } from "lit/decorators";
|
||||
import { ifDefined } from "lit/directives/if-defined";
|
||||
import { fireEvent } from "../common/dom/fire_event";
|
||||
import type { HomeAssistant } from "../types";
|
||||
import "./ha-combo-box-item";
|
||||
import "./ha-icon-button";
|
||||
import "./ha-list-item";
|
||||
import "./ha-textfield";
|
||||
import type { HaTextField } from "./ha-textfield";
|
||||
|
||||
@ -216,10 +216,11 @@ export class HaComboBox extends LitElement {
|
||||
|
||||
private _defaultRowRenderer: ComboBoxLitRenderer<
|
||||
string | Record<string, any>
|
||||
> = (item) =>
|
||||
html`<ha-list-item>
|
||||
> = (item) => html`
|
||||
<ha-combo-box-item type="button">
|
||||
${this.itemLabelPath ? item[this.itemLabelPath] : item}
|
||||
</ha-list-item>`;
|
||||
</ha-combo-box-item>
|
||||
`;
|
||||
|
||||
private _clearValue(ev: Event) {
|
||||
ev.stopPropagation();
|
||||
|
@ -1,4 +1,3 @@
|
||||
import "@material/mwc-list/mwc-list-item";
|
||||
import type { ComboBoxLitRenderer } from "@vaadin/combo-box/lit";
|
||||
import { html, LitElement, nothing } from "lit";
|
||||
import { customElement, property, query, state } from "lit/decorators";
|
||||
@ -11,6 +10,7 @@ import type { ValueChangedEvent, HomeAssistant } from "../types";
|
||||
import { brandsUrl } from "../util/brands-url";
|
||||
import "./ha-combo-box";
|
||||
import type { HaComboBox } from "./ha-combo-box";
|
||||
import "./ha-combo-box-item";
|
||||
|
||||
export interface ConfigEntryExtended extends ConfigEntry {
|
||||
localized_domain_name?: string;
|
||||
@ -48,18 +48,20 @@ class HaConfigEntryPicker extends LitElement {
|
||||
this._getConfigEntries();
|
||||
}
|
||||
|
||||
private _rowRenderer: ComboBoxLitRenderer<ConfigEntryExtended> = (item) =>
|
||||
html`<mwc-list-item twoline graphic="icon">
|
||||
<span
|
||||
>${item.title ||
|
||||
private _rowRenderer: ComboBoxLitRenderer<ConfigEntryExtended> = (
|
||||
item
|
||||
) => html`
|
||||
<ha-combo-box-item type="button">
|
||||
<span slot="headline">
|
||||
${item.title ||
|
||||
this.hass.localize(
|
||||
"ui.panel.config.integrations.config_entry.unnamed_entry"
|
||||
)}</span
|
||||
>
|
||||
<span slot="secondary">${item.localized_domain_name}</span>
|
||||
)}
|
||||
</span>
|
||||
<span slot="supporting-text">${item.localized_domain_name}</span>
|
||||
<img
|
||||
alt=""
|
||||
slot="graphic"
|
||||
slot="start"
|
||||
src=${brandsUrl({
|
||||
domain: item.domain,
|
||||
type: "icon",
|
||||
@ -70,7 +72,8 @@ class HaConfigEntryPicker extends LitElement {
|
||||
@error=${this._onImageError}
|
||||
@load=${this._onImageLoad}
|
||||
/>
|
||||
</mwc-list-item>`;
|
||||
</ha-combo-box-item>
|
||||
`;
|
||||
|
||||
protected render() {
|
||||
if (!this._configEntries) {
|
||||
|
@ -3,7 +3,6 @@ import type { HassEntity } from "home-assistant-js-websocket";
|
||||
import type { PropertyValues, TemplateResult } from "lit";
|
||||
import { LitElement, html } from "lit";
|
||||
import { customElement, property, query, state } from "lit/decorators";
|
||||
import { classMap } from "lit/directives/class-map";
|
||||
import memoizeOne from "memoize-one";
|
||||
import { fireEvent } from "../common/dom/fire_event";
|
||||
import { computeDomain } from "../common/entity/compute_domain";
|
||||
@ -28,9 +27,9 @@ import type { HomeAssistant, ValueChangedEvent } from "../types";
|
||||
import type { HaDevicePickerDeviceFilterFunc } from "./device/ha-device-picker";
|
||||
import "./ha-combo-box";
|
||||
import type { HaComboBox } from "./ha-combo-box";
|
||||
import "./ha-combo-box-item";
|
||||
import "./ha-floor-icon";
|
||||
import "./ha-icon-button";
|
||||
import "./ha-list-item";
|
||||
|
||||
type ScorableFloorRegistryEntry = ScorableTextItem & FloorRegistryEntry;
|
||||
|
||||
@ -38,14 +37,12 @@ const ADD_NEW_ID = "___ADD_NEW___";
|
||||
const NO_FLOORS_ID = "___NO_FLOORS___";
|
||||
const ADD_NEW_SUGGESTION_ID = "___ADD_NEW_SUGGESTION___";
|
||||
|
||||
const rowRenderer: ComboBoxLitRenderer<FloorRegistryEntry> = (item) =>
|
||||
html`<ha-list-item
|
||||
graphic="icon"
|
||||
class=${classMap({ "add-new": item.floor_id === ADD_NEW_ID })}
|
||||
>
|
||||
<ha-floor-icon slot="graphic" .floor=${item}></ha-floor-icon>
|
||||
const rowRenderer: ComboBoxLitRenderer<FloorRegistryEntry> = (item) => html`
|
||||
<ha-combo-box-item type="button">
|
||||
<ha-floor-icon slot="start" .floor=${item}></ha-floor-icon>
|
||||
${item.name}
|
||||
</ha-list-item>`;
|
||||
</ha-combo-box-item>
|
||||
`;
|
||||
|
||||
@customElement("ha-floor-picker")
|
||||
export class HaFloorPicker extends LitElement {
|
||||
|
@ -2,12 +2,13 @@ import type HlsType from "hls.js";
|
||||
import type { PropertyValues, TemplateResult } from "lit";
|
||||
import { css, html, LitElement } from "lit";
|
||||
import { customElement, property, query, state } from "lit/decorators";
|
||||
import { styleMap } from "lit/directives/style-map";
|
||||
import { isComponentLoaded } from "../common/config/is_component_loaded";
|
||||
import { fireEvent } from "../common/dom/fire_event";
|
||||
import { nextRender } from "../common/util/render-status";
|
||||
import { fetchStreamUrl } from "../data/camera";
|
||||
import type { HomeAssistant } from "../types";
|
||||
import "./ha-alert";
|
||||
import { fetchStreamUrl } from "../data/camera";
|
||||
import { isComponentLoaded } from "../common/config/is_component_loaded";
|
||||
|
||||
type HlsLite = Omit<
|
||||
HlsType,
|
||||
@ -24,6 +25,10 @@ class HaHLSPlayer extends LitElement {
|
||||
|
||||
@property({ attribute: "poster-url" }) public posterUrl?: string;
|
||||
|
||||
@property({ attribute: false }) public aspectRatio?: number;
|
||||
|
||||
@property({ attribute: false }) public fitMode?: "cover" | "contain" | "fill";
|
||||
|
||||
@property({ type: Boolean, attribute: "controls" })
|
||||
public controls = false;
|
||||
|
||||
@ -87,6 +92,11 @@ class HaHLSPlayer extends LitElement {
|
||||
?playsinline=${this.playsInline}
|
||||
?controls=${this.controls}
|
||||
@loadeddata=${this._loadedData}
|
||||
style=${styleMap({
|
||||
height: this.aspectRatio == null ? "100%" : "auto",
|
||||
aspectRatio: this.aspectRatio,
|
||||
objectFit: this.fitMode,
|
||||
})}
|
||||
></video>`
|
||||
: ""}
|
||||
`;
|
||||
|
@ -11,8 +11,8 @@ import { fireEvent } from "../common/dom/fire_event";
|
||||
import { customIcons } from "../data/custom_icons";
|
||||
import type { HomeAssistant, ValueChangedEvent } from "../types";
|
||||
import "./ha-combo-box";
|
||||
import "./ha-list-item";
|
||||
import "./ha-icon";
|
||||
import "./ha-combo-box-item";
|
||||
|
||||
interface IconItem {
|
||||
icon: string;
|
||||
@ -67,11 +67,12 @@ const loadCustomIconItems = async (iconsetPrefix: string) => {
|
||||
}
|
||||
};
|
||||
|
||||
const rowRenderer: ComboBoxLitRenderer<IconItem | RankedIcon> = (item) =>
|
||||
html`<ha-list-item graphic="avatar">
|
||||
<ha-icon .icon=${item.icon} slot="graphic"></ha-icon>
|
||||
const rowRenderer: ComboBoxLitRenderer<IconItem | RankedIcon> = (item) => html`
|
||||
<ha-combo-box-item type="button">
|
||||
<ha-icon .icon=${item.icon} slot="start"></ha-icon>
|
||||
${item.icon}
|
||||
</ha-list-item>`;
|
||||
</ha-combo-box-item>
|
||||
`;
|
||||
|
||||
@customElement("ha-icon-picker")
|
||||
export class HaIconPicker extends LitElement {
|
||||
|
@ -3,7 +3,6 @@ import type { HassEntity, UnsubscribeFunc } from "home-assistant-js-websocket";
|
||||
import type { PropertyValues, TemplateResult } from "lit";
|
||||
import { LitElement, html, nothing } from "lit";
|
||||
import { customElement, property, query, state } from "lit/decorators";
|
||||
import { classMap } from "lit/directives/class-map";
|
||||
import memoizeOne from "memoize-one";
|
||||
import { fireEvent } from "../common/dom/fire_event";
|
||||
import { computeDomain } from "../common/entity/compute_domain";
|
||||
@ -26,8 +25,8 @@ import type { HomeAssistant, ValueChangedEvent } from "../types";
|
||||
import type { HaDevicePickerDeviceFilterFunc } from "./device/ha-device-picker";
|
||||
import "./ha-combo-box";
|
||||
import type { HaComboBox } from "./ha-combo-box";
|
||||
import "./ha-combo-box-item";
|
||||
import "./ha-icon-button";
|
||||
import "./ha-list-item";
|
||||
import "./ha-svg-icon";
|
||||
|
||||
type ScorableLabelItem = ScorableTextItem & LabelRegistryEntry;
|
||||
@ -36,16 +35,14 @@ const ADD_NEW_ID = "___ADD_NEW___";
|
||||
const NO_LABELS_ID = "___NO_LABELS___";
|
||||
const ADD_NEW_SUGGESTION_ID = "___ADD_NEW_SUGGESTION___";
|
||||
|
||||
const rowRenderer: ComboBoxLitRenderer<LabelRegistryEntry> = (item) =>
|
||||
html`<ha-list-item
|
||||
graphic="icon"
|
||||
class=${classMap({ "add-new": item.label_id === ADD_NEW_ID })}
|
||||
>
|
||||
const rowRenderer: ComboBoxLitRenderer<LabelRegistryEntry> = (item) => html`
|
||||
<ha-combo-box-item type="button">
|
||||
${item.icon
|
||||
? html`<ha-icon slot="graphic" .icon=${item.icon}></ha-icon>`
|
||||
? html`<ha-icon slot="start" .icon=${item.icon}></ha-icon>`
|
||||
: nothing}
|
||||
${item.name}
|
||||
</ha-list-item>`;
|
||||
</ha-combo-box-item>
|
||||
`;
|
||||
|
||||
@customElement("ha-label-picker")
|
||||
export class HaLabelPicker extends SubscribeMixin(LitElement) {
|
||||
|
@ -1,7 +1,6 @@
|
||||
import "@material/mwc-list/mwc-list-item";
|
||||
import type { ComboBoxLitRenderer } from "@vaadin/combo-box/lit";
|
||||
import type { PropertyValues, TemplateResult } from "lit";
|
||||
import { css, html, LitElement } from "lit";
|
||||
import { css, html, LitElement, nothing } from "lit";
|
||||
import { customElement, property, query, state } from "lit/decorators";
|
||||
import { fireEvent } from "../common/dom/fire_event";
|
||||
import { titleCase } from "../common/string/title-case";
|
||||
@ -10,6 +9,7 @@ import type { LovelaceViewRawConfig } from "../data/lovelace/config/view";
|
||||
import type { HomeAssistant, PanelInfo, ValueChangedEvent } from "../types";
|
||||
import "./ha-combo-box";
|
||||
import type { HaComboBox } from "./ha-combo-box";
|
||||
import "./ha-combo-box-item";
|
||||
import "./ha-icon";
|
||||
|
||||
interface NavigationItem {
|
||||
@ -21,11 +21,13 @@ interface NavigationItem {
|
||||
const DEFAULT_ITEMS: NavigationItem[] = [];
|
||||
|
||||
const rowRenderer: ComboBoxLitRenderer<NavigationItem> = (item) => html`
|
||||
<mwc-list-item graphic="icon" .twoline=${!!item.title}>
|
||||
<ha-icon .icon=${item.icon} slot="graphic"></ha-icon>
|
||||
<span>${item.title || item.path}</span>
|
||||
<span slot="secondary">${item.path}</span>
|
||||
</mwc-list-item>
|
||||
<ha-combo-box-item type="button">
|
||||
<ha-icon .icon=${item.icon} slot="start"></ha-icon>
|
||||
<span slot="headline">${item.title || item.path}</span>
|
||||
${item.title
|
||||
? html`<span slot="supporting-text">${item.path}</span>`
|
||||
: nothing}
|
||||
</ha-combo-box-item>
|
||||
`;
|
||||
|
||||
const createViewNavigationItem = (
|
||||
|
@ -1,7 +1,5 @@
|
||||
import "@material/mwc-button/mwc-button";
|
||||
import { mdiCamera } from "@mdi/js";
|
||||
import type { UnsubscribeFunc } from "home-assistant-js-websocket";
|
||||
import type { PropertyValues } from "lit";
|
||||
import { css, html, LitElement, nothing } from "lit";
|
||||
import { customElement, property, query, state } from "lit/decorators";
|
||||
// The BarcodeDetector Web API is not yet supported in all browsers,
|
||||
@ -12,12 +10,13 @@ import { prepareZXingModule } from "barcode-detector";
|
||||
import type QrScanner from "qr-scanner";
|
||||
import { fireEvent } from "../common/dom/fire_event";
|
||||
import { stopPropagation } from "../common/dom/stop_propagation";
|
||||
import type { LocalizeFunc } from "../common/translations/localize";
|
||||
import { addExternalBarCodeListener } from "../external_app/external_app_entrypoint";
|
||||
import type { HomeAssistant } from "../types";
|
||||
import "./ha-alert";
|
||||
import "./ha-button";
|
||||
import "./ha-button-menu";
|
||||
import "./ha-list-item";
|
||||
import "./ha-spinner";
|
||||
import "./ha-textfield";
|
||||
import type { HaTextField } from "./ha-textfield";
|
||||
|
||||
@ -36,18 +35,22 @@ prepareZXingModule({
|
||||
class HaQrScanner extends LitElement {
|
||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||
|
||||
@property({ attribute: false }) public localize!: LocalizeFunc;
|
||||
|
||||
@property() public description?: string;
|
||||
|
||||
@property({ attribute: "alternative_option_label" })
|
||||
public alternativeOptionLabel?: string;
|
||||
|
||||
@property() public error?: string;
|
||||
@property({ attribute: false }) public validate?: (
|
||||
value: string
|
||||
) => string | undefined;
|
||||
|
||||
@state() private _cameras?: QrScanner.Camera[];
|
||||
|
||||
@state() private _manual = false;
|
||||
@state() private _loading = true;
|
||||
|
||||
@state() private _error?: string;
|
||||
|
||||
@state() private _warning?: string;
|
||||
|
||||
private _qrScanner?: QrScanner;
|
||||
|
||||
@ -88,29 +91,40 @@ class HaQrScanner extends LitElement {
|
||||
this._loadQrScanner();
|
||||
}
|
||||
|
||||
protected updated(changedProps: PropertyValues) {
|
||||
if (changedProps.has("error") && this.error) {
|
||||
alert(`error: ${this.error}`);
|
||||
this._notifyExternalScanner(this.error);
|
||||
}
|
||||
}
|
||||
|
||||
protected render() {
|
||||
if (this._nativeBarcodeScanner && !this._manual) {
|
||||
if (this._nativeBarcodeScanner) {
|
||||
return nothing;
|
||||
}
|
||||
|
||||
return html`${this.error
|
||||
? html`<ha-alert alert-type="error">${this.error}</ha-alert>`
|
||||
: ""}
|
||||
${navigator.mediaDevices && !this._manual
|
||||
return html`${this._error || this._warning
|
||||
? html`<ha-alert
|
||||
.alertType=${this._error ? "error" : "warning"}
|
||||
class=${this._error ? "" : "warning"}
|
||||
>
|
||||
${this._error || this._warning}
|
||||
${this._error
|
||||
? html` <ha-button @click=${this._retry} slot="action">
|
||||
${this.hass.localize("ui.components.qr-scanner.retry")}
|
||||
</ha-button>`
|
||||
: nothing}
|
||||
</ha-alert>`
|
||||
: nothing}
|
||||
${navigator.mediaDevices
|
||||
? html`<video></video>
|
||||
<div id="canvas-container">
|
||||
${this._cameras && this._cameras.length > 1
|
||||
${this._loading
|
||||
? html`<div class="loading">
|
||||
<ha-spinner active></ha-spinner>
|
||||
</div>`
|
||||
: nothing}
|
||||
${!this._loading &&
|
||||
!this._error &&
|
||||
this._cameras &&
|
||||
this._cameras.length > 1
|
||||
? html`<ha-button-menu fixed @closed=${stopPropagation}>
|
||||
<ha-icon-button
|
||||
slot="trigger"
|
||||
.label=${this.localize(
|
||||
.label=${this.hass.localize(
|
||||
"ui.components.qr-scanner.select_camera"
|
||||
)}
|
||||
.path=${mdiCamera}
|
||||
@ -128,25 +142,25 @@ class HaQrScanner extends LitElement {
|
||||
</ha-button-menu>`
|
||||
: nothing}
|
||||
</div>`
|
||||
: html`${this._manual
|
||||
? nothing
|
||||
: html`<ha-alert alert-type="warning">
|
||||
${!window.isSecureContext
|
||||
? this.localize(
|
||||
"ui.components.qr-scanner.only_https_supported"
|
||||
)
|
||||
: this.localize("ui.components.qr-scanner.not_supported")}
|
||||
</ha-alert>`}
|
||||
<p>${this.localize("ui.components.qr-scanner.manual_input")}</p>
|
||||
: html`<ha-alert alert-type="warning">
|
||||
${!window.isSecureContext
|
||||
? this.hass.localize(
|
||||
"ui.components.qr-scanner.only_https_supported"
|
||||
)
|
||||
: this.hass.localize("ui.components.qr-scanner.not_supported")}
|
||||
</ha-alert>
|
||||
<p>${this.hass.localize("ui.components.qr-scanner.manual_input")}</p>
|
||||
<div class="row">
|
||||
<ha-textfield
|
||||
.label=${this.localize("ui.components.qr-scanner.enter_qr_code")}
|
||||
.label=${this.hass.localize(
|
||||
"ui.components.qr-scanner.enter_qr_code"
|
||||
)}
|
||||
@keyup=${this._manualKeyup}
|
||||
@paste=${this._manualPaste}
|
||||
></ha-textfield>
|
||||
<mwc-button @click=${this._manualSubmit}>
|
||||
${this.localize("ui.common.submit")}
|
||||
</mwc-button>
|
||||
<ha-button @click=${this._manualSubmit}>
|
||||
${this.hass.localize("ui.common.submit")}
|
||||
</ha-button>
|
||||
</div>`}`;
|
||||
}
|
||||
|
||||
@ -165,7 +179,9 @@ class HaQrScanner extends LitElement {
|
||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||
const QrScanner = (await import("qr-scanner")).default;
|
||||
if (!(await QrScanner.hasCamera())) {
|
||||
this._reportError("No camera found");
|
||||
this._reportError(
|
||||
this.hass.localize("ui.components.qr-scanner.no_camera_found")
|
||||
);
|
||||
return;
|
||||
}
|
||||
QrScanner.WORKER_PATH = "/static/js/qr-scanner-worker.min.js";
|
||||
@ -181,6 +197,7 @@ class HaQrScanner extends LitElement {
|
||||
canvas.style.display = "block";
|
||||
try {
|
||||
await this._qrScanner.start();
|
||||
this._loading = false;
|
||||
} catch (err: any) {
|
||||
this._reportError(err);
|
||||
}
|
||||
@ -193,8 +210,8 @@ class HaQrScanner extends LitElement {
|
||||
private _qrCodeError = (err: any) => {
|
||||
if (err.endsWith("No QR code found")) {
|
||||
this._qrNotFoundCount++;
|
||||
if (this._qrNotFoundCount === 250) {
|
||||
this._reportError(err);
|
||||
if (this._qrNotFoundCount >= 250) {
|
||||
this._reportWarning(err);
|
||||
}
|
||||
return;
|
||||
}
|
||||
@ -204,7 +221,17 @@ class HaQrScanner extends LitElement {
|
||||
};
|
||||
|
||||
private _qrCodeScanned = (qrCodeString: string): void => {
|
||||
this._warning = undefined;
|
||||
this._qrNotFoundCount = 0;
|
||||
if (this.validate) {
|
||||
const validationMessage = this.validate(qrCodeString);
|
||||
|
||||
if (validationMessage) {
|
||||
this._reportWarning(validationMessage);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
fireEvent(this, "qr-code-scanned", { value: qrCodeString });
|
||||
};
|
||||
|
||||
@ -234,7 +261,10 @@ class HaQrScanner extends LitElement {
|
||||
if (msg.command === "bar_code/scan_result") {
|
||||
if (msg.payload.format !== "qr_code") {
|
||||
this._notifyExternalScanner(
|
||||
`Wrong barcode scanned! ${msg.payload.format}: ${msg.payload.rawValue}, we need a QR code.`
|
||||
this.hass.localize("ui.components.qr-scanner.wrong_code", {
|
||||
format: msg.payload.format,
|
||||
rawValue: msg.payload.rawValue,
|
||||
})
|
||||
);
|
||||
} else {
|
||||
this._qrCodeScanned(msg.payload.rawValue);
|
||||
@ -244,7 +274,7 @@ class HaQrScanner extends LitElement {
|
||||
if (msg.payload.reason === "canceled") {
|
||||
fireEvent(this, "qr-code-closed");
|
||||
} else {
|
||||
this._manual = true;
|
||||
fireEvent(this, "qr-code-more-options");
|
||||
}
|
||||
}
|
||||
return true;
|
||||
@ -252,10 +282,17 @@ class HaQrScanner extends LitElement {
|
||||
this.hass.auth.external!.fireMessage({
|
||||
type: "bar_code/scan",
|
||||
payload: {
|
||||
title: this.title || "Scan QR code",
|
||||
description: this.description || "Scan a barcode.",
|
||||
title:
|
||||
this.title ||
|
||||
this.hass.localize("ui.components.qr-scanner.app.title"),
|
||||
description:
|
||||
this.description ||
|
||||
this.hass.localize("ui.components.qr-scanner.app.description"),
|
||||
alternative_option_label:
|
||||
this.alternativeOptionLabel || "Click to manually enter the barcode",
|
||||
this.alternativeOptionLabel ||
|
||||
this.hass.localize(
|
||||
"ui.components.qr-scanner.app.alternativeOptionLabel"
|
||||
),
|
||||
},
|
||||
});
|
||||
}
|
||||
@ -269,25 +306,55 @@ class HaQrScanner extends LitElement {
|
||||
}
|
||||
|
||||
private _notifyExternalScanner(message: string) {
|
||||
if (!this.hass.auth.external) {
|
||||
if (!this._nativeBarcodeScanner) {
|
||||
return;
|
||||
}
|
||||
this.hass.auth.external.fireMessage({
|
||||
this.hass.auth.external!.fireMessage({
|
||||
type: "bar_code/notify",
|
||||
payload: {
|
||||
message,
|
||||
},
|
||||
});
|
||||
this.error = undefined;
|
||||
this._warning = undefined;
|
||||
this._error = undefined;
|
||||
}
|
||||
|
||||
private _reportError(message: string) {
|
||||
fireEvent(this, "qr-code-error", { message });
|
||||
const canvas = this._qrScanner?.$canvas;
|
||||
if (canvas) {
|
||||
canvas.style.display = "none";
|
||||
}
|
||||
this._error = message;
|
||||
}
|
||||
|
||||
private _reportWarning(message: string) {
|
||||
if (this._nativeBarcodeScanner) {
|
||||
this._notifyExternalScanner(message);
|
||||
} else {
|
||||
this._warning = message;
|
||||
}
|
||||
}
|
||||
|
||||
private async _retry() {
|
||||
if (this._qrScanner) {
|
||||
this._loading = true;
|
||||
this._error = undefined;
|
||||
this._warning = undefined;
|
||||
const canvas = this._qrScanner.$canvas;
|
||||
canvas.style.display = "block";
|
||||
this._qrNotFoundCount = 0;
|
||||
await this._qrScanner.start();
|
||||
this._loading = false;
|
||||
}
|
||||
}
|
||||
|
||||
static styles = css`
|
||||
:root {
|
||||
position: relative;
|
||||
}
|
||||
canvas {
|
||||
width: 100%;
|
||||
border-radius: 16px;
|
||||
}
|
||||
#canvas-container {
|
||||
position: relative;
|
||||
@ -312,6 +379,24 @@ class HaQrScanner extends LitElement {
|
||||
margin-inline-end: 8px;
|
||||
margin-inline-start: initial;
|
||||
}
|
||||
.loading {
|
||||
display: flex;
|
||||
position: absolute;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
ha-alert {
|
||||
display: block;
|
||||
}
|
||||
ha-alert.warning {
|
||||
position: absolute;
|
||||
z-index: 1;
|
||||
background-color: var(--primary-background-color);
|
||||
top: 0;
|
||||
width: calc(100% - 48px);
|
||||
}
|
||||
`;
|
||||
}
|
||||
|
||||
@ -319,8 +404,8 @@ declare global {
|
||||
// for fire event
|
||||
interface HASSDomEvents {
|
||||
"qr-code-scanned": { value: string };
|
||||
"qr-code-error": { message: string };
|
||||
"qr-code-closed": undefined;
|
||||
"qr-code-more-options": undefined;
|
||||
}
|
||||
|
||||
interface HTMLElementTagNameMap {
|
||||
|
@ -7,7 +7,7 @@ import type { LocalizeFunc } from "../common/translations/localize";
|
||||
import { domainToName } from "../data/integration";
|
||||
import type { HomeAssistant } from "../types";
|
||||
import "./ha-combo-box";
|
||||
import "./ha-list-item";
|
||||
import "./ha-combo-box-item";
|
||||
import "./ha-service-icon";
|
||||
import { getServiceIcons } from "../data/icons";
|
||||
|
||||
@ -29,18 +29,19 @@ class HaServicePicker extends LitElement {
|
||||
}
|
||||
|
||||
private _rowRenderer: ComboBoxLitRenderer<{ service: string; name: string }> =
|
||||
(item) =>
|
||||
html`<ha-list-item twoline graphic="icon">
|
||||
(item) => html`
|
||||
<ha-combo-box-item type="button">
|
||||
<ha-service-icon
|
||||
slot="graphic"
|
||||
slot="start"
|
||||
.hass=${this.hass}
|
||||
.service=${item.service}
|
||||
></ha-service-icon>
|
||||
<span>${item.name}</span>
|
||||
<span slot="secondary"
|
||||
<span slot="headline">${item.name}</span>
|
||||
<span slot="supporting-text"
|
||||
>${item.name === item.service ? "" : item.service}</span
|
||||
>
|
||||
</ha-list-item>`;
|
||||
</ha-combo-box-item>
|
||||
`;
|
||||
|
||||
protected render() {
|
||||
return html`
|
||||
|
@ -1,8 +1,9 @@
|
||||
import type { PropertyValues, TemplateResult } from "lit";
|
||||
import type { UnsubscribeFunc } from "home-assistant-js-websocket";
|
||||
import type { PropertyValues, TemplateResult } from "lit";
|
||||
import { css, html, LitElement } from "lit";
|
||||
import { customElement, property, query, state } from "lit/decorators";
|
||||
import { ifDefined } from "lit/directives/if-defined";
|
||||
import { styleMap } from "lit/directives/style-map";
|
||||
import { fireEvent } from "../common/dom/fire_event";
|
||||
import {
|
||||
addWebRtcCandidate,
|
||||
@ -26,6 +27,10 @@ class HaWebRtcPlayer extends LitElement {
|
||||
|
||||
@property() public entityid?: string;
|
||||
|
||||
@property({ attribute: false }) public aspectRatio?: number;
|
||||
|
||||
@property({ attribute: false }) public fitMode?: "cover" | "contain" | "fill";
|
||||
|
||||
@property({ type: Boolean, attribute: "controls" })
|
||||
public controls = false;
|
||||
|
||||
@ -69,6 +74,11 @@ class HaWebRtcPlayer extends LitElement {
|
||||
?controls=${this.controls}
|
||||
poster=${ifDefined(this.posterUrl)}
|
||||
@loadeddata=${this._loadedData}
|
||||
style=${styleMap({
|
||||
height: this.aspectRatio == null ? "100%" : "auto",
|
||||
aspectRatio: this.aspectRatio,
|
||||
objectFit: this.fitMode,
|
||||
})}
|
||||
></video>
|
||||
`;
|
||||
}
|
||||
|
@ -3,6 +3,7 @@ import { getOptimisticCollection } from "./collection";
|
||||
|
||||
export interface CoreFrontendUserData {
|
||||
showAdvanced?: boolean;
|
||||
showEntityIdPicker?: boolean;
|
||||
}
|
||||
|
||||
declare global {
|
||||
|
@ -80,7 +80,7 @@ enum QRCodeVersion {
|
||||
SmartStart = 1,
|
||||
}
|
||||
|
||||
enum Protocols {
|
||||
export enum Protocols {
|
||||
ZWave = 0,
|
||||
ZWaveLongRange = 1,
|
||||
}
|
||||
@ -151,12 +151,35 @@ export interface QRProvisioningInformation {
|
||||
maxInclusionRequestInterval?: number | undefined;
|
||||
uuid?: string | undefined;
|
||||
supportedProtocols?: Protocols[] | undefined;
|
||||
status?: ProvisioningEntryStatus;
|
||||
}
|
||||
|
||||
export interface PlannedProvisioningEntry {
|
||||
/** The device specific key (DSK) in the form aaaaa-bbbbb-ccccc-ddddd-eeeee-fffff-11111-22222 */
|
||||
dsk: string;
|
||||
securityClasses: SecurityClass[];
|
||||
status?: ProvisioningEntryStatus;
|
||||
}
|
||||
|
||||
export enum ProvisioningEntryStatus {
|
||||
Active = 0,
|
||||
Inactive = 1,
|
||||
}
|
||||
|
||||
export interface DeviceConfig {
|
||||
filename: string;
|
||||
manufacturer: string;
|
||||
manufacturerId: number;
|
||||
label: string;
|
||||
description: string;
|
||||
devices: {
|
||||
productType: number;
|
||||
productId: number;
|
||||
}[];
|
||||
firmwareVersion: {
|
||||
min: string;
|
||||
max: string;
|
||||
};
|
||||
}
|
||||
|
||||
export const MINIMUM_QR_STRING_LENGTH = 52;
|
||||
@ -195,6 +218,7 @@ export interface ZWaveJSController {
|
||||
is_rebuilding_routes: boolean;
|
||||
inclusion_state: InclusionState;
|
||||
nodes: ZWaveJSNodeStatus[];
|
||||
supports_long_range: boolean;
|
||||
}
|
||||
|
||||
export interface ZWaveJSNodeStatus {
|
||||
@ -555,7 +579,7 @@ export const zwaveTryParseDskFromQrCode = (
|
||||
export const zwaveValidateDskAndEnterPin = (
|
||||
hass: HomeAssistant,
|
||||
entry_id: string,
|
||||
pin: string
|
||||
pin: string | false
|
||||
) =>
|
||||
hass.callWS({
|
||||
type: "zwave_js/validate_dsk_and_enter_pin",
|
||||
@ -585,19 +609,38 @@ export const zwaveParseQrCode = (
|
||||
qr_code_string,
|
||||
});
|
||||
|
||||
export const lookupZwaveDevice = (
|
||||
hass: HomeAssistant,
|
||||
entry_id: string,
|
||||
manufacturerId: number,
|
||||
productType: number,
|
||||
productId: number,
|
||||
applicationVersion?: string
|
||||
): Promise<DeviceConfig> =>
|
||||
hass.callWS({
|
||||
type: "zwave_js/lookup_device",
|
||||
entry_id,
|
||||
manufacturerId,
|
||||
productType,
|
||||
productId,
|
||||
applicationVersion,
|
||||
});
|
||||
|
||||
export const provisionZwaveSmartStartNode = (
|
||||
hass: HomeAssistant,
|
||||
entry_id: string,
|
||||
qr_provisioning_information?: QRProvisioningInformation,
|
||||
qr_code_string?: string,
|
||||
planned_provisioning_entry?: PlannedProvisioningEntry
|
||||
): Promise<QRProvisioningInformation> =>
|
||||
protocol?: Protocols,
|
||||
device_name?: string,
|
||||
area_id?: string
|
||||
): Promise<string> =>
|
||||
hass.callWS({
|
||||
type: "zwave_js/provision_smart_start_node",
|
||||
entry_id,
|
||||
qr_code_string,
|
||||
qr_provisioning_information,
|
||||
planned_provisioning_entry,
|
||||
protocol,
|
||||
device_name,
|
||||
area_id,
|
||||
});
|
||||
|
||||
export const unprovisionZwaveSmartStartNode = (
|
||||
@ -613,6 +656,16 @@ export const unprovisionZwaveSmartStartNode = (
|
||||
node_id,
|
||||
});
|
||||
|
||||
export const subscribeNewDevices = (
|
||||
hass: HomeAssistant,
|
||||
entry_id: string,
|
||||
callbackFunction: (message: any) => void
|
||||
): Promise<UnsubscribeFunc> =>
|
||||
hass.connection.subscribeMessage((message) => callbackFunction(message), {
|
||||
type: "zwave_js/subscribe_new_devices",
|
||||
entry_id: entry_id,
|
||||
});
|
||||
|
||||
export const fetchZwaveNodeStatus = (
|
||||
hass: HomeAssistant,
|
||||
device_id: string
|
||||
|
@ -133,8 +133,8 @@ export class MoreInfoInfo extends LitElement {
|
||||
|
||||
[data-domain="camera"] .content {
|
||||
padding: 0;
|
||||
/* max height of the video is full screen, minus the height of the header of the dialog and the padding of the dialog (mdc-dialog-max-height: calc(100% - 72px)) */
|
||||
--video-max-height: calc(100vh - 65px - 72px);
|
||||
/* max height of the video is full screen, minus the height of the header of the dialog (79px) and the max height of the dialog (mdc-dialog-max-height: calc(100% - 72px)) and the actions bar 60px */
|
||||
--video-max-height: calc(100vh - 72px - 79px - 60px);
|
||||
}
|
||||
|
||||
more-info-content {
|
||||
|
@ -1,17 +1,15 @@
|
||||
import "@material/mwc-list/mwc-list-item";
|
||||
import { mdiOpenInNew } from "@mdi/js";
|
||||
import type { ComboBoxLitRenderer } from "@vaadin/combo-box/lit";
|
||||
import type { CSSResultGroup } from "lit";
|
||||
import { css, html, LitElement, nothing } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import { fireEvent } from "../../../common/dom/fire_event";
|
||||
import "../../../components/ha-alert";
|
||||
import "../../../components/ha-button";
|
||||
import "../../../components/ha-spinner";
|
||||
import "../../../components/ha-combo-box";
|
||||
import { createCloseHeading } from "../../../components/ha-dialog";
|
||||
import "../../../components/ha-markdown";
|
||||
import "../../../components/ha-password-field";
|
||||
import "../../../components/ha-spinner";
|
||||
import "../../../components/ha-textfield";
|
||||
import type {
|
||||
ApplicationCredential,
|
||||
@ -33,11 +31,6 @@ interface Domain {
|
||||
name: string;
|
||||
}
|
||||
|
||||
const rowRenderer: ComboBoxLitRenderer<Domain> = (item) =>
|
||||
html`<mwc-list-item>
|
||||
<span>${item.name}</span>
|
||||
</mwc-list-item>`;
|
||||
|
||||
@customElement("dialog-add-application-credential")
|
||||
export class DialogAddApplicationCredential extends LitElement {
|
||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||
@ -171,7 +164,6 @@ export class DialogAddApplicationCredential extends LitElement {
|
||||
"ui.panel.config.application_credentials.editor.domain"
|
||||
)}
|
||||
.value=${this._domain}
|
||||
.renderer=${rowRenderer}
|
||||
.items=${this._domains}
|
||||
item-id-path="id"
|
||||
item-value-path="id"
|
||||
|
@ -4,15 +4,14 @@ import type { UnsubscribeFunc } from "home-assistant-js-websocket";
|
||||
import type { PropertyValues } from "lit";
|
||||
import { html, LitElement, nothing } from "lit";
|
||||
import { customElement, property, query, state } from "lit/decorators";
|
||||
import { classMap } from "lit/directives/class-map";
|
||||
import memoizeOne from "memoize-one";
|
||||
import { fireEvent } from "../../../common/dom/fire_event";
|
||||
import type { ScorableTextItem } from "../../../common/string/filter/sequence-matching";
|
||||
import { fuzzyFilterSort } from "../../../common/string/filter/sequence-matching";
|
||||
import "../../../components/ha-combo-box";
|
||||
import type { HaComboBox } from "../../../components/ha-combo-box";
|
||||
import "../../../components/ha-combo-box-item";
|
||||
import "../../../components/ha-icon-button";
|
||||
import "../../../components/ha-list-item";
|
||||
import "../../../components/ha-svg-icon";
|
||||
import type { CategoryRegistryEntry } from "../../../data/category_registry";
|
||||
import {
|
||||
@ -29,16 +28,14 @@ const ADD_NEW_ID = "___ADD_NEW___";
|
||||
const NO_CATEGORIES_ID = "___NO_CATEGORIES___";
|
||||
const ADD_NEW_SUGGESTION_ID = "___ADD_NEW_SUGGESTION___";
|
||||
|
||||
const rowRenderer: ComboBoxLitRenderer<CategoryRegistryEntry> = (item) =>
|
||||
html`<ha-list-item
|
||||
graphic="icon"
|
||||
class=${classMap({ "add-new": item.category_id === ADD_NEW_ID })}
|
||||
>
|
||||
const rowRenderer: ComboBoxLitRenderer<CategoryRegistryEntry> = (item) => html`
|
||||
<ha-combo-box-item type="button">
|
||||
${item.icon
|
||||
? html`<ha-icon slot="graphic" .icon=${item.icon}></ha-icon>`
|
||||
: html`<ha-svg-icon .path=${mdiTag} slot="graphic"></ha-svg-icon>`}
|
||||
? html`<ha-icon slot="start" .icon=${item.icon}></ha-icon>`
|
||||
: html`<ha-svg-icon .path=${mdiTag} slot="start"></ha-svg-icon>`}
|
||||
${item.name}
|
||||
</ha-list-item>`;
|
||||
</ha-combo-box-item>
|
||||
`;
|
||||
|
||||
@customElement("ha-category-picker")
|
||||
export class HaCategoryPicker extends SubscribeMixin(LitElement) {
|
||||
|
@ -0,0 +1,54 @@
|
||||
import type { QRProvisioningInformation } from "../../../../../../data/zwave_js";
|
||||
|
||||
export const backButtonStages: Partial<ZWaveJSAddNodeStage>[] = [
|
||||
"qr_scan",
|
||||
"select_other_method",
|
||||
"qr_code_input",
|
||||
"choose_security_strategy",
|
||||
"configure_device",
|
||||
];
|
||||
|
||||
export const closeButtonStages: Partial<ZWaveJSAddNodeStage>[] = [
|
||||
"select_method",
|
||||
"search_devices",
|
||||
"search_smart_start_device",
|
||||
"search_s2_device",
|
||||
"failed",
|
||||
"interviewing",
|
||||
"validate_dsk_enter_pin",
|
||||
"added_insecure",
|
||||
"grant_security_classes",
|
||||
"rename_device",
|
||||
];
|
||||
|
||||
export type ZWaveJSAddNodeStage =
|
||||
| "loading"
|
||||
| "qr_scan"
|
||||
| "select_method"
|
||||
| "select_other_method"
|
||||
| "qr_code_input"
|
||||
| "search_devices"
|
||||
| "search_smart_start_device"
|
||||
| "search_s2_device"
|
||||
| "choose_security_strategy"
|
||||
| "configure_device"
|
||||
| "interviewing"
|
||||
| "failed"
|
||||
| "timed_out"
|
||||
| "added_insecure"
|
||||
| "validate_dsk_enter_pin"
|
||||
| "grant_security_classes"
|
||||
| "waiting_for_device"
|
||||
| "rename_device";
|
||||
|
||||
export interface ZWaveJSAddNodeSmartStartOptions {
|
||||
name: string;
|
||||
area?: string;
|
||||
network_type?: string;
|
||||
}
|
||||
|
||||
export interface ZWaveJSAddNodeDevice {
|
||||
id?: string;
|
||||
name: string;
|
||||
provisioningInfo?: QRProvisioningInformation;
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -1,7 +1,9 @@
|
||||
import { fireEvent } from "../../../../../common/dom/fire_event";
|
||||
import { fireEvent } from "../../../../../../common/dom/fire_event";
|
||||
|
||||
export interface ZWaveJSAddNodeDialogParams {
|
||||
entry_id: string;
|
||||
longRangeSupported?: boolean;
|
||||
inclusionOngoing?: boolean;
|
||||
dsk?: string;
|
||||
onStop?: () => void;
|
||||
}
|
@ -0,0 +1,65 @@
|
||||
import { mdiCheckCircleOutline } from "@mdi/js";
|
||||
import { customElement, property } from "lit/decorators";
|
||||
import "@shoelace-style/shoelace/dist/components/animation/animation";
|
||||
import { css, html, LitElement } from "lit";
|
||||
import type { HomeAssistant } from "../../../../../../types";
|
||||
|
||||
import "../../../../../../components/ha-svg-icon";
|
||||
import "../../../../../../components/ha-alert";
|
||||
|
||||
@customElement("zwave-js-add-node-added-insecure")
|
||||
export class ZWaveJsAddNodeFinished extends LitElement {
|
||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||
|
||||
@property({ attribute: "device-name" }) public deviceName?: string;
|
||||
|
||||
@property() public reason?;
|
||||
|
||||
render() {
|
||||
return html`
|
||||
<sl-animation name="zoomIn" .iterations=${1} play>
|
||||
<ha-svg-icon .path=${mdiCheckCircleOutline}></ha-svg-icon>
|
||||
</sl-animation>
|
||||
<ha-alert alert-type="warning">
|
||||
${this.reason
|
||||
? this.hass.localize(
|
||||
`ui.panel.config.zwave_js.add_node.added_insecure.low_security_reason.${this.reason}`
|
||||
)
|
||||
: ""}
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.zwave_js.add_node.added_insecure.added_insecurely_text",
|
||||
{
|
||||
deviceName: html`<b>${this.deviceName}</b>`,
|
||||
}
|
||||
)}
|
||||
<p>
|
||||
${this.hass.localize(
|
||||
`ui.panel.config.zwave_js.add_node.added_insecure.try_again_text`
|
||||
)}
|
||||
</p>
|
||||
</ha-alert>
|
||||
`;
|
||||
}
|
||||
|
||||
static styles = css`
|
||||
:host {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
ha-svg-icon {
|
||||
--mdc-icon-size: 96px;
|
||||
color: var(--warning-color);
|
||||
}
|
||||
ha-alert {
|
||||
margin-top: 16px;
|
||||
}
|
||||
`;
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"zwave-js-add-node-added-insecure": ZWaveJsAddNodeFinished;
|
||||
}
|
||||
}
|
@ -0,0 +1,99 @@
|
||||
import { customElement, property } from "lit/decorators";
|
||||
import { css, html, LitElement, nothing } from "lit";
|
||||
|
||||
import { fireEvent } from "../../../../../../common/dom/fire_event";
|
||||
import type { HaTextField } from "../../../../../../components/ha-textfield";
|
||||
|
||||
import "../../../../../../components/ha-textfield";
|
||||
import "../../../../../../components/ha-alert";
|
||||
|
||||
@customElement("zwave-js-add-node-code-input")
|
||||
export class ZWaveJsAddNodeCodeInput extends LitElement {
|
||||
@property() public value = "";
|
||||
|
||||
@property() public description = "";
|
||||
|
||||
@property() public placeholder = "";
|
||||
|
||||
@property({ attribute: "reference-key" }) public referenceKey = "";
|
||||
|
||||
@property() public error?: string;
|
||||
|
||||
@property({ type: Boolean }) public numeric = false;
|
||||
|
||||
render() {
|
||||
return html`
|
||||
<p>${this.description}</p>
|
||||
${this.error
|
||||
? html`<ha-alert alert-type="error">${this.error}</ha-alert>`
|
||||
: nothing}
|
||||
<ha-textfield
|
||||
.placeholder=${this.placeholder}
|
||||
.value=${this.value}
|
||||
@input=${this._handleChange}
|
||||
@keyup=${this._handleKeyup}
|
||||
required
|
||||
autofocus
|
||||
></ha-textfield>
|
||||
${this.referenceKey
|
||||
? html`<div>
|
||||
<span>${this.value.padEnd(5, "·")}</span>${this.referenceKey}
|
||||
</div> `
|
||||
: nothing}
|
||||
`;
|
||||
}
|
||||
|
||||
private _handleKeyup(ev: KeyboardEvent): void {
|
||||
if (ev.key === "Enter" && this.value) {
|
||||
fireEvent(this, "z-wave-submit");
|
||||
}
|
||||
}
|
||||
|
||||
private _handleChange(ev: InputEvent): void {
|
||||
const inputElement = ev.target as HaTextField;
|
||||
if (
|
||||
this.numeric &&
|
||||
(isNaN(Number(inputElement.value)) || inputElement.value.length > 5)
|
||||
) {
|
||||
inputElement.value = this.value;
|
||||
return;
|
||||
}
|
||||
|
||||
this.value = (ev.target as HaTextField).value;
|
||||
|
||||
fireEvent(this, "value-changed", {
|
||||
value: (ev.target as HaTextField).value,
|
||||
});
|
||||
}
|
||||
|
||||
static styles = css`
|
||||
ha-textfield {
|
||||
width: 100%;
|
||||
}
|
||||
ha-alert {
|
||||
display: block;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
p {
|
||||
color: var(--secondary-text-color);
|
||||
margin-top: 0;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
div {
|
||||
font-family: "Roboto Mono", "Consolas", "Menlo", monospace;
|
||||
margin-top: 16px;
|
||||
}
|
||||
div span {
|
||||
color: var(--primary-color);
|
||||
}
|
||||
`;
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"zwave-js-add-node-code-input": ZWaveJsAddNodeCodeInput;
|
||||
}
|
||||
interface HASSDomEvents {
|
||||
"z-wave-submit";
|
||||
}
|
||||
}
|
@ -0,0 +1,152 @@
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import { html, LitElement, type PropertyValues } from "lit";
|
||||
import memoizeOne from "memoize-one";
|
||||
|
||||
import type { HomeAssistant } from "../../../../../../types";
|
||||
import type { LocalizeFunc } from "../../../../../../common/translations/localize";
|
||||
import type { HaFormSchema } from "../../../../../../components/ha-form/types";
|
||||
import { fireEvent } from "../../../../../../common/dom/fire_event";
|
||||
import { Protocols } from "../../../../../../data/zwave_js";
|
||||
import type { ZWaveJSAddNodeSmartStartOptions } from "./data";
|
||||
|
||||
import "../../../../../../components/ha-form/ha-form";
|
||||
|
||||
@customElement("zwave-js-add-node-configure-device")
|
||||
export class ZWaveJsAddNodeConfigureDevice extends LitElement {
|
||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||
|
||||
@property({ attribute: "device-name" }) public deviceName = "";
|
||||
|
||||
@property({ type: Boolean, attribute: "lr-supported" })
|
||||
public longRangeSupported = false;
|
||||
|
||||
@state() private _options?: ZWaveJSAddNodeSmartStartOptions;
|
||||
|
||||
protected willUpdate(changedProps: PropertyValues): void {
|
||||
super.willUpdate(changedProps);
|
||||
|
||||
if (!this.hasUpdated) {
|
||||
this._options = {
|
||||
name: this.deviceName,
|
||||
};
|
||||
|
||||
if (this.longRangeSupported) {
|
||||
this._options.network_type = Protocols.ZWaveLongRange.toString();
|
||||
}
|
||||
|
||||
fireEvent(this, "value-changed", { value: this._options });
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
return html`
|
||||
<ha-form
|
||||
.hass=${this.hass}
|
||||
.schema=${this._getSchema(this.hass.localize, this.longRangeSupported)}
|
||||
.data=${this._options!}
|
||||
@value-changed=${this._setOptions}
|
||||
.computeLabel=${this._computeLabel}
|
||||
>
|
||||
</ha-form>
|
||||
`;
|
||||
}
|
||||
|
||||
private _getSchema = memoizeOne(
|
||||
(localize: LocalizeFunc, longRangeSupported: boolean): HaFormSchema[] => {
|
||||
const schema: HaFormSchema[] = [
|
||||
{
|
||||
name: "name",
|
||||
required: true,
|
||||
default: this.deviceName,
|
||||
type: "string",
|
||||
autofocus: true,
|
||||
},
|
||||
{
|
||||
name: "area",
|
||||
selector: {
|
||||
area: {},
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
if (longRangeSupported) {
|
||||
schema.push({
|
||||
name: "network_type",
|
||||
required: true,
|
||||
selector: {
|
||||
select: {
|
||||
box_max_columns: 1,
|
||||
mode: "box",
|
||||
options: [
|
||||
{
|
||||
value: Protocols.ZWaveLongRange.toString(),
|
||||
label: localize(
|
||||
"ui.panel.config.zwave_js.add_node.configure_device.long_range_label"
|
||||
),
|
||||
description: localize(
|
||||
"ui.panel.config.zwave_js.add_node.configure_device.long_range_description"
|
||||
),
|
||||
image: {
|
||||
src: "/static/images/z-wave-add-node/long-range.svg",
|
||||
src_dark:
|
||||
"/static/images/z-wave-add-node/long-range_dark.svg",
|
||||
flip_rtl: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
value: Protocols.ZWave.toString(),
|
||||
label: localize(
|
||||
"ui.panel.config.zwave_js.add_node.configure_device.mesh_label"
|
||||
),
|
||||
description: localize(
|
||||
"ui.panel.config.zwave_js.add_node.configure_device.mesh_description"
|
||||
),
|
||||
image: {
|
||||
src: "/static/images/z-wave-add-node/mesh.svg",
|
||||
src_dark: "/static/images/z-wave-add-node/mesh_dark.svg",
|
||||
flip_rtl: true,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
return schema;
|
||||
}
|
||||
);
|
||||
|
||||
private _computeLabel = (schema: HaFormSchema): string | undefined => {
|
||||
if (schema.name === "network_type") {
|
||||
return this.hass.localize(
|
||||
"ui.panel.config.zwave_js.add_node.configure_device.choose_network_type"
|
||||
);
|
||||
}
|
||||
if (schema.name === "name") {
|
||||
return this.hass.localize(
|
||||
"ui.panel.config.zwave_js.add_node.configure_device.device_name"
|
||||
);
|
||||
}
|
||||
if (schema.name === "area") {
|
||||
return this.hass.localize(
|
||||
"ui.panel.config.zwave_js.add_node.configure_device.device_area"
|
||||
);
|
||||
}
|
||||
return undefined;
|
||||
};
|
||||
|
||||
private _setOptions(event: any) {
|
||||
this._options = {
|
||||
...this._options!,
|
||||
...event.detail.value,
|
||||
};
|
||||
|
||||
fireEvent(this, "value-changed", { value: this._options });
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"zwave-js-add-node-configure-device": ZWaveJsAddNodeConfigureDevice;
|
||||
}
|
||||
}
|
@ -0,0 +1,68 @@
|
||||
import { customElement, property } from "lit/decorators";
|
||||
import { css, html, LitElement, nothing } from "lit";
|
||||
import type { HomeAssistant } from "../../../../../../types";
|
||||
import type { ZWaveJSAddNodeDevice } from "./data";
|
||||
|
||||
import "../../../../../../components/ha-alert";
|
||||
import "../../../../../../components/ha-button";
|
||||
|
||||
@customElement("zwave-js-add-node-failed")
|
||||
export class ZWaveJsAddNodeFailed extends LitElement {
|
||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||
|
||||
@property() public error?: string;
|
||||
|
||||
@property({ attribute: false }) public device?: ZWaveJSAddNodeDevice;
|
||||
|
||||
render() {
|
||||
return html`
|
||||
<ha-alert
|
||||
alert-type="error"
|
||||
.title=${this.hass.localize(
|
||||
"ui.panel.config.zwave_js.add_node.inclusion_failed"
|
||||
)}
|
||||
>
|
||||
${this.error ||
|
||||
this.hass.localize("ui.panel.config.zwave_js.add_node.check_logs")}
|
||||
</ha-alert>
|
||||
${this.error
|
||||
? html`<div class="note">
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.zwave_js.add_node.check_logs"
|
||||
)}
|
||||
</div>`
|
||||
: nothing}
|
||||
${this.device?.id
|
||||
? html`<a href=${`/config/devices/device/${this.device.id}`}>
|
||||
<ha-button>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.zwave_js.add_node.view_device"
|
||||
)}
|
||||
</ha-button>
|
||||
</a>`
|
||||
: nothing}
|
||||
`;
|
||||
}
|
||||
|
||||
static styles = css`
|
||||
:host {
|
||||
display: block;
|
||||
padding: 16px;
|
||||
}
|
||||
div.note {
|
||||
text-align: center;
|
||||
margin-top: 16px;
|
||||
font-size: 12px;
|
||||
color: var(--secondary-text-color);
|
||||
}
|
||||
ha-button {
|
||||
margin-top: 32px;
|
||||
}
|
||||
`;
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"zwave-js-add-node-failed": ZWaveJsAddNodeFailed;
|
||||
}
|
||||
}
|
@ -0,0 +1,108 @@
|
||||
import { customElement, property } from "lit/decorators";
|
||||
import "@shoelace-style/shoelace/dist/components/animation/animation";
|
||||
import { css, html, LitElement, nothing } from "lit";
|
||||
import type { HomeAssistant } from "../../../../../../types";
|
||||
import { SecurityClass } from "../../../../../../data/zwave_js";
|
||||
import type { HaCheckbox } from "../../../../../../components/ha-checkbox";
|
||||
import { fireEvent } from "../../../../../../common/dom/fire_event";
|
||||
|
||||
import "../../../../../../components/ha-alert";
|
||||
import "../../../../../../components/ha-formfield";
|
||||
import "../../../../../../components/ha-checkbox";
|
||||
|
||||
@customElement("zwave-js-add-node-grant-security-classes")
|
||||
export class ZWaveJsAddNodeGrantSecurityClasses extends LitElement {
|
||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||
|
||||
@property() public error?: string;
|
||||
|
||||
@property({ attribute: false }) public securityClassOptions!: SecurityClass[];
|
||||
|
||||
@property({ attribute: false })
|
||||
public selectedSecurityClasses: SecurityClass[] = [];
|
||||
|
||||
render() {
|
||||
return html`
|
||||
${this.error
|
||||
? html`<ha-alert alert-type="error"> ${this.error} </ha-alert>`
|
||||
: nothing}
|
||||
<p>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.zwave_js.add_node.grant_security_classes.description"
|
||||
)}
|
||||
</p>
|
||||
<div class="flex-column">
|
||||
${this.securityClassOptions
|
||||
.sort((a, b) => {
|
||||
// Put highest security classes at the top, S0 at the bottom
|
||||
if (a === SecurityClass.S0_Legacy) return 1;
|
||||
if (b === SecurityClass.S0_Legacy) return -1;
|
||||
return b - a;
|
||||
})
|
||||
.map(
|
||||
(securityClass) =>
|
||||
html`<ha-formfield
|
||||
.label=${html`<b
|
||||
>${this.hass.localize(
|
||||
`ui.panel.config.zwave_js.security_classes.${SecurityClass[securityClass]}.title`
|
||||
)}</b
|
||||
>
|
||||
<div class="secondary">
|
||||
${this.hass.localize(
|
||||
`ui.panel.config.zwave_js.security_classes.${SecurityClass[securityClass]}.description`
|
||||
)}
|
||||
</div>`}
|
||||
>
|
||||
<ha-checkbox
|
||||
@change=${this._handleSecurityClassChange}
|
||||
.value=${securityClass.toString()}
|
||||
.checked=${this.selectedSecurityClasses.includes(
|
||||
securityClass
|
||||
)}
|
||||
>
|
||||
</ha-checkbox>
|
||||
</ha-formfield>`
|
||||
)}
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
private _handleSecurityClassChange(ev: CustomEvent) {
|
||||
const checkbox = ev.currentTarget as HaCheckbox;
|
||||
const securityClass = Number(checkbox.value);
|
||||
if (
|
||||
checkbox.checked &&
|
||||
!this.selectedSecurityClasses.includes(securityClass)
|
||||
) {
|
||||
fireEvent(this, "value-changed", {
|
||||
value: [...this.selectedSecurityClasses, securityClass],
|
||||
});
|
||||
} else if (!checkbox.checked) {
|
||||
fireEvent(this, "value-changed", {
|
||||
value: this.selectedSecurityClasses.filter(
|
||||
(val) => val !== securityClass
|
||||
),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
static styles = css`
|
||||
ha-alert {
|
||||
display: block;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
.flex-column {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
.secondary {
|
||||
color: var(--secondary-text-color);
|
||||
}
|
||||
`;
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"zwave-js-add-node-grant-security-classes": ZWaveJsAddNodeGrantSecurityClasses;
|
||||
}
|
||||
}
|
@ -0,0 +1,46 @@
|
||||
import { customElement, property } from "lit/decorators";
|
||||
import { css, html, LitElement, nothing } from "lit";
|
||||
|
||||
import "../../../../../../components/ha-fade-in";
|
||||
import "../../../../../../components/ha-spinner";
|
||||
|
||||
@customElement("zwave-js-add-node-loading")
|
||||
export class ZWaveJsAddNodeLoading extends LitElement {
|
||||
@property() public description?: string;
|
||||
|
||||
@property({ type: Number }) public delay = 0;
|
||||
|
||||
render() {
|
||||
return html`
|
||||
<ha-fade-in .delay=${this.delay}>
|
||||
<div class="loading">
|
||||
<ha-spinner size="large"></ha-spinner>
|
||||
</div>
|
||||
${this.description ? html`<p>${this.description}</p>` : nothing}
|
||||
</ha-fade-in>
|
||||
`;
|
||||
}
|
||||
|
||||
static styles = css`
|
||||
ha-fade-in {
|
||||
display: block;
|
||||
}
|
||||
.loading {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
min-height: 200px;
|
||||
}
|
||||
p {
|
||||
margin-top: 16px;
|
||||
color: var(--secondary-text-color);
|
||||
text-align: center;
|
||||
}
|
||||
`;
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"zwave-js-add-node-loading": ZWaveJsAddNodeLoading;
|
||||
}
|
||||
}
|
@ -0,0 +1,164 @@
|
||||
import "@shoelace-style/shoelace/dist/components/animation/animation";
|
||||
import { mdiRestart } from "@mdi/js";
|
||||
|
||||
import { customElement, property } from "lit/decorators";
|
||||
import { css, html, LitElement, nothing } from "lit";
|
||||
import type { HomeAssistant } from "../../../../../../types";
|
||||
import { fireEvent } from "../../../../../../common/dom/fire_event";
|
||||
import { InclusionStrategy } from "../../../../../../data/zwave_js";
|
||||
|
||||
import "../../../../../../components/ha-spinner";
|
||||
import "../../../../../../components/ha-button";
|
||||
import "../../../../../../components/ha-alert";
|
||||
|
||||
@customElement("zwave-js-add-node-searching-devices")
|
||||
export class ZWaveJsAddNodeSearchingDevices extends LitElement {
|
||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||
|
||||
@property({ type: Boolean, attribute: "smart-start" })
|
||||
public smartStart = false;
|
||||
|
||||
@property({ type: Boolean, attribute: "show-security-options" })
|
||||
public showSecurityOptions = false;
|
||||
|
||||
@property({ type: Boolean, attribute: "show-add-another-device" })
|
||||
public showAddAnotherDevice = false;
|
||||
|
||||
@property({ attribute: false }) public inclusionStrategy?: InclusionStrategy;
|
||||
|
||||
render() {
|
||||
let inclusionStrategyTranslationKey = "";
|
||||
if (this.inclusionStrategy !== undefined) {
|
||||
switch (this.inclusionStrategy) {
|
||||
case InclusionStrategy.Security_S0:
|
||||
inclusionStrategyTranslationKey = "s0";
|
||||
break;
|
||||
case InclusionStrategy.Insecure:
|
||||
inclusionStrategyTranslationKey = "insecure";
|
||||
break;
|
||||
default:
|
||||
inclusionStrategyTranslationKey = "default";
|
||||
}
|
||||
}
|
||||
|
||||
return html`
|
||||
<div class="searching-devices">
|
||||
<div class="searching-spinner">
|
||||
<div class="spinner">
|
||||
<ha-spinner></ha-spinner>
|
||||
</div>
|
||||
<sl-animation name="pulse" easing="linear" .duration=${2000} play>
|
||||
<div class="circle"></div>
|
||||
</sl-animation>
|
||||
</div>
|
||||
${this.smartStart
|
||||
? html`<ha-alert
|
||||
.title=${this.hass.localize(
|
||||
"ui.panel.config.zwave_js.add_node.specific_device.turn_on_device"
|
||||
)}
|
||||
>
|
||||
<ha-svg-icon slot="icon" .path=${mdiRestart}></ha-svg-icon>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.zwave_js.add_node.specific_device.turn_on_device_description"
|
||||
)}
|
||||
</ha-alert>
|
||||
<p class="note">
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.zwave_js.add_node.specific_device.close_description"
|
||||
)}
|
||||
</p>`
|
||||
: html`
|
||||
<p>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.zwave_js.add_node.follow_device_instructions"
|
||||
)}
|
||||
</p>
|
||||
`}
|
||||
${this.showSecurityOptions && !inclusionStrategyTranslationKey
|
||||
? html`<ha-button @click=${this._handleSecurityOptions}>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.zwave_js.add_node.security_options"
|
||||
)}
|
||||
</ha-button>`
|
||||
: inclusionStrategyTranslationKey
|
||||
? html`<span class="note">
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.zwave_js.add_node.select_strategy.inclusion_strategy",
|
||||
{
|
||||
strategy: this.hass.localize(
|
||||
`ui.panel.config.zwave_js.add_node.select_strategy.${inclusionStrategyTranslationKey}_label`
|
||||
),
|
||||
}
|
||||
)}
|
||||
</span>`
|
||||
: nothing}
|
||||
${this.showAddAnotherDevice
|
||||
? html`<ha-button @click=${this._handleAddAnotherDevice}>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.zwave_js.add_node.specific_device.add_another_z_wave_device"
|
||||
)}
|
||||
</ha-button>`
|
||||
: nothing}
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
private _handleSecurityOptions() {
|
||||
fireEvent(this, "show-z-wave-security-options");
|
||||
}
|
||||
|
||||
private _handleAddAnotherDevice() {
|
||||
fireEvent(this, "add-another-z-wave-device");
|
||||
}
|
||||
|
||||
static styles = css`
|
||||
:host {
|
||||
text-align: center;
|
||||
display: block;
|
||||
}
|
||||
ha-alert {
|
||||
margin-top: 32px;
|
||||
display: block;
|
||||
}
|
||||
.note {
|
||||
font-size: 12px;
|
||||
color: var(--secondary-text-color);
|
||||
}
|
||||
.searching-spinner {
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
position: relative;
|
||||
width: 128px;
|
||||
height: 128px;
|
||||
}
|
||||
.searching-spinner .circle {
|
||||
border-radius: 50%;
|
||||
background-color: var(--light-primary-color);
|
||||
position: absolute;
|
||||
width: calc(100% - 32px);
|
||||
height: calc(100% - 32px);
|
||||
margin: 16px;
|
||||
}
|
||||
.searching-spinner .spinner {
|
||||
z-index: 1;
|
||||
position: absolute;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
--ha-spinner-divider-color: var(--light-primary-color);
|
||||
}
|
||||
`;
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"zwave-js-add-node-searching-devices": ZWaveJsAddNodeSearchingDevices;
|
||||
}
|
||||
|
||||
interface HASSDomEvents {
|
||||
"show-z-wave-security-options": undefined;
|
||||
"add-another-z-wave-device": undefined;
|
||||
}
|
||||
}
|
@ -0,0 +1,112 @@
|
||||
import { customElement, property } from "lit/decorators";
|
||||
import { css, html, LitElement, nothing } from "lit";
|
||||
import { fireEvent } from "../../../../../../common/dom/fire_event";
|
||||
import type { HomeAssistant } from "../../../../../../types";
|
||||
|
||||
import "../../../../../../components/ha-md-list";
|
||||
import "../../../../../../components/ha-md-list-item";
|
||||
import "../../../../../../components/ha-alert";
|
||||
import "../../../../../../components/ha-icon-next";
|
||||
|
||||
@customElement("zwave-js-add-node-select-method")
|
||||
export class ZWaveJsAddNodeSelectMethod extends LitElement {
|
||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||
|
||||
@property({ type: Boolean, attribute: "hide-qr-webcam" })
|
||||
public hideQrWebcam = false;
|
||||
|
||||
render() {
|
||||
return html`
|
||||
${!this.hideQrWebcam && !window.isSecureContext
|
||||
? html`<ha-alert alert-type="warning">
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.zwave_js.add_node.select_method.webcam_unsupported"
|
||||
)}</ha-alert
|
||||
>`
|
||||
: nothing}
|
||||
<ha-md-list>
|
||||
${!this.hideQrWebcam
|
||||
? html`<ha-md-list-item
|
||||
interactive
|
||||
type="button"
|
||||
@click=${this._selectMethod}
|
||||
.value=${"qr_code_webcam"}
|
||||
.disabled=${!window.isSecureContext}
|
||||
>
|
||||
<div slot="headline">
|
||||
${this.hass.localize(
|
||||
`ui.panel.config.zwave_js.add_node.select_method.qr_code_webcam`
|
||||
)}
|
||||
</div>
|
||||
<div slot="supporting-text">
|
||||
${this.hass.localize(
|
||||
`ui.panel.config.zwave_js.add_node.select_method.qr_code_webcam_description`
|
||||
)}
|
||||
</div>
|
||||
<ha-icon-next slot="end"></ha-icon-next>
|
||||
</ha-md-list-item>`
|
||||
: nothing}
|
||||
<ha-md-list-item
|
||||
interactive
|
||||
type="button"
|
||||
@click=${this._selectMethod}
|
||||
.value=${"qr_code_manual"}
|
||||
>
|
||||
<div slot="headline">
|
||||
${this.hass.localize(
|
||||
`ui.panel.config.zwave_js.add_node.select_method.qr_code_manual`
|
||||
)}
|
||||
</div>
|
||||
<div slot="supporting-text">
|
||||
${this.hass.localize(
|
||||
`ui.panel.config.zwave_js.add_node.select_method.qr_code_manual_description`
|
||||
)}
|
||||
</div>
|
||||
<ha-icon-next slot="end"></ha-icon-next>
|
||||
</ha-md-list-item>
|
||||
<ha-md-list-item
|
||||
interactive
|
||||
type="button"
|
||||
@click=${this._selectMethod}
|
||||
.value=${"search_device"}
|
||||
>
|
||||
<div slot="headline">
|
||||
${this.hass.localize(
|
||||
`ui.panel.config.zwave_js.add_node.select_method.search_device`
|
||||
)}
|
||||
</div>
|
||||
<div slot="supporting-text">
|
||||
${this.hass.localize(
|
||||
`ui.panel.config.zwave_js.add_node.select_method.search_device_description`
|
||||
)}
|
||||
</div>
|
||||
<ha-icon-next slot="end"></ha-icon-next>
|
||||
</ha-md-list-item>
|
||||
</ha-md-list>
|
||||
`;
|
||||
}
|
||||
|
||||
private _selectMethod(event: any) {
|
||||
const method = event.currentTarget.value;
|
||||
if (method !== "qr_code_webcam" || window.isSecureContext) {
|
||||
fireEvent(this, "z-wave-method-selected", { method });
|
||||
}
|
||||
}
|
||||
|
||||
static styles = css`
|
||||
ha-md-list {
|
||||
padding: 0;
|
||||
}
|
||||
`;
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"zwave-js-add-node-select-method": ZWaveJsAddNodeSelectMethod;
|
||||
}
|
||||
interface HASSDomEvents {
|
||||
"z-wave-method-selected": {
|
||||
method: "qr_code_webcam" | "qr_code_manual" | "search_device";
|
||||
};
|
||||
}
|
||||
}
|
@ -0,0 +1,107 @@
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import { css, html, LitElement } from "lit";
|
||||
import memoizeOne from "memoize-one";
|
||||
|
||||
import { fireEvent } from "../../../../../../common/dom/fire_event";
|
||||
import type { HomeAssistant } from "../../../../../../types";
|
||||
import { InclusionStrategy } from "../../../../../../data/zwave_js";
|
||||
import type { LocalizeFunc } from "../../../../../../common/translations/localize";
|
||||
import type { HaFormSchema } from "../../../../../../components/ha-form/types";
|
||||
|
||||
import "../../../../../../components/ha-form/ha-form";
|
||||
|
||||
@customElement("zwave-js-add-node-select-security-strategy")
|
||||
export class ZWaveJsAddNodeSelectMethod extends LitElement {
|
||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||
|
||||
@state() public _inclusionStrategy?: InclusionStrategy;
|
||||
|
||||
private _getSchema = memoizeOne((localize: LocalizeFunc): HaFormSchema[] => [
|
||||
{
|
||||
name: "strategy",
|
||||
required: true,
|
||||
selector: {
|
||||
select: {
|
||||
box_max_columns: 1,
|
||||
mode: "box",
|
||||
options: [
|
||||
{
|
||||
value: InclusionStrategy.Default.toString(),
|
||||
label: localize(
|
||||
"ui.panel.config.zwave_js.add_node.select_strategy.default_label"
|
||||
),
|
||||
description: localize(
|
||||
"ui.panel.config.zwave_js.add_node.select_strategy.default_description"
|
||||
),
|
||||
},
|
||||
{
|
||||
value: InclusionStrategy.Security_S0.toString(),
|
||||
label: localize(
|
||||
"ui.panel.config.zwave_js.add_node.select_strategy.s0_label"
|
||||
),
|
||||
description: localize(
|
||||
"ui.panel.config.zwave_js.add_node.select_strategy.s0_description"
|
||||
),
|
||||
},
|
||||
{
|
||||
value: InclusionStrategy.Insecure.toString(),
|
||||
label: localize(
|
||||
"ui.panel.config.zwave_js.add_node.select_strategy.insecure_label"
|
||||
),
|
||||
description: localize(
|
||||
"ui.panel.config.zwave_js.add_node.select_strategy.insecure_description"
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
]);
|
||||
|
||||
render() {
|
||||
return html`
|
||||
<ha-form
|
||||
.schema=${this._getSchema(this.hass.localize)}
|
||||
.data=${{ strategy: this._inclusionStrategy?.toString() }}
|
||||
@value-changed=${this._selectStrategy}
|
||||
.computeLabel=${this._computeLabel}
|
||||
>
|
||||
</ha-form>
|
||||
`;
|
||||
}
|
||||
|
||||
private _computeLabel = () =>
|
||||
this.hass.localize(
|
||||
"ui.panel.config.zwave_js.add_node.select_strategy.title"
|
||||
);
|
||||
|
||||
private _selectStrategy(event: any) {
|
||||
const selectedStrategy = Number(
|
||||
event.detail.value.strategy
|
||||
) as InclusionStrategy;
|
||||
fireEvent(this, "z-wave-strategy-selected", {
|
||||
strategy: selectedStrategy,
|
||||
});
|
||||
}
|
||||
|
||||
static styles = css`
|
||||
:host {
|
||||
display: block;
|
||||
padding: 16px;
|
||||
}
|
||||
ha-md-list {
|
||||
padding: 0;
|
||||
}
|
||||
`;
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"zwave-js-add-node-select-security-strategy": ZWaveJsAddNodeSelectMethod;
|
||||
}
|
||||
interface HASSDomEvents {
|
||||
"z-wave-strategy-selected": {
|
||||
strategy: InclusionStrategy;
|
||||
};
|
||||
}
|
||||
}
|
@ -1,7 +1,7 @@
|
||||
/* eslint-disable lit/lifecycle-super */
|
||||
import { customElement } from "lit/decorators";
|
||||
import { navigate } from "../../../../../common/navigate";
|
||||
import type { HomeAssistant } from "../../../../../types";
|
||||
import { navigate } from "../../../../../../common/navigate";
|
||||
import type { HomeAssistant } from "../../../../../../types";
|
||||
import { showZWaveJSAddNodeDialog } from "./show-dialog-zwave_js-add-node";
|
||||
|
||||
@customElement("zwave_js-add-node")
|
||||
@ -21,6 +21,7 @@ export class DialogZWaveJSAddNode extends HTMLElement {
|
||||
replace: true,
|
||||
}
|
||||
);
|
||||
|
||||
showZWaveJSAddNodeDialog(this, {
|
||||
entry_id: this.configEntryId,
|
||||
});
|
File diff suppressed because it is too large
Load Diff
@ -49,7 +49,7 @@ import "../../../../../layouts/hass-tabs-subpage";
|
||||
import { SubscribeMixin } from "../../../../../mixins/subscribe-mixin";
|
||||
import { haStyle } from "../../../../../resources/styles";
|
||||
import type { HomeAssistant, Route } from "../../../../../types";
|
||||
import { showZWaveJSAddNodeDialog } from "./show-dialog-zwave_js-add-node";
|
||||
import { showZWaveJSAddNodeDialog } from "./add-node/show-dialog-zwave_js-add-node";
|
||||
import { showZWaveJSRebuildNetworkRoutesDialog } from "./show-dialog-zwave_js-rebuild-network-routes";
|
||||
import { showZWaveJSRemoveNodeDialog } from "./show-dialog-zwave_js-remove-node";
|
||||
import { configTabs } from "./zwave_js-config-router";
|
||||
@ -101,7 +101,7 @@ class ZWaveJSConfigDashboard extends SubscribeMixin(LitElement) {
|
||||
const inclusion_state = this._network?.controller.inclusion_state;
|
||||
// show dialog if inclusion/exclusion is already in progress
|
||||
if (inclusion_state === InclusionState.Including) {
|
||||
this._addNodeClicked();
|
||||
this._openInclusionDialog(undefined, true);
|
||||
} else if (inclusion_state === InclusionState.Excluding) {
|
||||
this._removeNodeClicked();
|
||||
}
|
||||
@ -419,11 +419,6 @@ class ZWaveJSConfigDashboard extends SubscribeMixin(LitElement) {
|
||||
"ui.panel.config.zwave_js.common.rebuild_network_routes"
|
||||
)}
|
||||
</ha-button>
|
||||
<ha-button @click=${this._openOptionFlow}>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.zwave_js.common.reconfigure_server"
|
||||
)}
|
||||
</ha-button>
|
||||
</div>
|
||||
</ha-card>
|
||||
<ha-card>
|
||||
@ -508,7 +503,15 @@ class ZWaveJSConfigDashboard extends SubscribeMixin(LitElement) {
|
||||
@change=${this._handleRestoreFileSelected}
|
||||
style="display: none"
|
||||
/>
|
||||
</div>`}
|
||||
</div>
|
||||
<ha-button
|
||||
@click=${this._openOptionFlow}
|
||||
class="warning migrate-button"
|
||||
>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.zwave_js.dashboard.nvm_backup.migrate"
|
||||
)}
|
||||
</ha-button>`}
|
||||
</div>
|
||||
</ha-card>
|
||||
`
|
||||
@ -743,7 +746,7 @@ class ZWaveJSConfigDashboard extends SubscribeMixin(LitElement) {
|
||||
input.value = "";
|
||||
}
|
||||
|
||||
private _openInclusionDialog(dsk?: string) {
|
||||
private _openInclusionDialog(dsk?: string, inclusionOngoing = false) {
|
||||
if (!this._dialogOpen) {
|
||||
// Unsubscribe from S2 inclusion before opening dialog
|
||||
if (this._s2InclusionUnsubscribe) {
|
||||
@ -755,6 +758,8 @@ class ZWaveJSConfigDashboard extends SubscribeMixin(LitElement) {
|
||||
entry_id: this.configEntryId!,
|
||||
dsk,
|
||||
onStop: this._handleInclusionDialogClosed,
|
||||
longRangeSupported: !!this._network?.controller?.supports_long_range,
|
||||
inclusionOngoing,
|
||||
});
|
||||
this._dialogOpen = true;
|
||||
}
|
||||
@ -962,6 +967,10 @@ class ZWaveJSConfigDashboard extends SubscribeMixin(LitElement) {
|
||||
.button-content {
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.migrate-button {
|
||||
margin-left: auto;
|
||||
}
|
||||
`,
|
||||
];
|
||||
}
|
||||
|
@ -42,7 +42,7 @@ class ZWaveJSConfigRouter extends HassRouterPage {
|
||||
},
|
||||
add: {
|
||||
tag: "zwave_js-add-node",
|
||||
load: () => import("./zwave_js-add-node"),
|
||||
load: () => import("./add-node/zwave_js-add-node"),
|
||||
},
|
||||
node_config: {
|
||||
tag: "zwave_js-node-config",
|
||||
|
@ -93,8 +93,7 @@ export class HuiAreaCard
|
||||
|
||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||
|
||||
@property({ attribute: false })
|
||||
public layout?: string;
|
||||
@property({ attribute: false }) public layout?: string;
|
||||
|
||||
@state() private _config?: AreaCardConfig;
|
||||
|
||||
|
@ -55,6 +55,8 @@ class HuiPictureEntityCard extends LitElement implements LovelaceCard {
|
||||
|
||||
@property({ attribute: false }) public hass?: HomeAssistant;
|
||||
|
||||
@property({ attribute: false }) public layout?: string;
|
||||
|
||||
@state() private _config?: PictureEntityCardConfig;
|
||||
|
||||
public getCardSize(): number {
|
||||
@ -155,6 +157,10 @@ class HuiPictureEntityCard extends LitElement implements LovelaceCard {
|
||||
}
|
||||
}
|
||||
|
||||
const ignoreAspectRatio =
|
||||
this.layout === "grid" &&
|
||||
typeof this._config.grid_options?.rows === "number";
|
||||
|
||||
return html`
|
||||
<ha-card>
|
||||
<hui-image
|
||||
@ -167,7 +173,9 @@ class HuiPictureEntityCard extends LitElement implements LovelaceCard {
|
||||
: this._config.camera_image}
|
||||
.cameraView=${this._config.camera_view}
|
||||
.entity=${this._config.entity}
|
||||
.aspectRatio=${this._config.aspect_ratio}
|
||||
.aspectRatio=${ignoreAspectRatio
|
||||
? undefined
|
||||
: this._config.aspect_ratio}
|
||||
.fitMode=${this._config.fit_mode}
|
||||
@action=${this._handleAction}
|
||||
.actionHandler=${actionHandler({
|
||||
|
@ -63,6 +63,8 @@ class HuiPictureGlanceCard extends LitElement implements LovelaceCard {
|
||||
|
||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||
|
||||
@property({ attribute: false }) public layout?: string;
|
||||
|
||||
@state() private _config?: PictureGlanceCardConfig;
|
||||
|
||||
private _entitiesDialog?: PictureGlanceEntityConfig[];
|
||||
@ -199,6 +201,10 @@ class HuiPictureGlanceCard extends LitElement implements LovelaceCard {
|
||||
}
|
||||
}
|
||||
|
||||
const ignoreAspectRatio =
|
||||
this.layout === "grid" &&
|
||||
typeof this._config.grid_options?.rows === "number";
|
||||
|
||||
return html`
|
||||
<ha-card>
|
||||
<hui-image
|
||||
@ -226,7 +232,10 @@ class HuiPictureGlanceCard extends LitElement implements LovelaceCard {
|
||||
.cameraImage=${this._config.camera_image}
|
||||
.cameraView=${this._config.camera_view}
|
||||
.entity=${this._config.entity}
|
||||
.aspectRatio=${this._config.aspect_ratio}
|
||||
.fitMode=${this._config.fit_mode}
|
||||
.aspectRatio=${ignoreAspectRatio
|
||||
? undefined
|
||||
: this._config.aspect_ratio}
|
||||
></hui-image>
|
||||
<div class="box">
|
||||
${this._config.title
|
||||
@ -324,6 +333,9 @@ class HuiPictureGlanceCard extends LitElement implements LovelaceCard {
|
||||
height: 100%;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
hui-image {
|
||||
height: 100%;
|
||||
}
|
||||
hui-image.clickable {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
@ -440,6 +440,7 @@ export interface PictureEntityCardConfig extends LovelaceCardConfig {
|
||||
state_image?: Record<string, unknown>;
|
||||
state_filter?: string[];
|
||||
aspect_ratio?: string;
|
||||
fit_mode?: "cover" | "contain" | "fill";
|
||||
tap_action?: ActionConfig;
|
||||
hold_action?: ActionConfig;
|
||||
double_tap_action?: ActionConfig;
|
||||
@ -458,6 +459,7 @@ export interface PictureGlanceCardConfig extends LovelaceCardConfig {
|
||||
state_image?: Record<string, unknown>;
|
||||
state_filter?: string[];
|
||||
aspect_ratio?: string;
|
||||
fit_mode?: "cover" | "contain" | "fill";
|
||||
entity?: string;
|
||||
tap_action?: ActionConfig;
|
||||
hold_action?: ActionConfig;
|
||||
|
@ -217,6 +217,10 @@ export class HuiImage extends LitElement {
|
||||
muted
|
||||
.hass=${this.hass}
|
||||
.stateObj=${cameraObj}
|
||||
.fitMode=${this.fitMode}
|
||||
.aspectRatio=${this._ratio
|
||||
? this._ratio.w / this._ratio.h
|
||||
: undefined}
|
||||
@load=${this._onVideoLoad}
|
||||
></ha-camera-stream>
|
||||
`
|
||||
@ -400,6 +404,12 @@ export class HuiImage extends LitElement {
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
ha-camera-stream {
|
||||
display: block;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.progress-container {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
|
@ -2,11 +2,24 @@ import { mdiGestureTap } from "@mdi/js";
|
||||
import type { CSSResultGroup } from "lit";
|
||||
import { html, LitElement, nothing } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import { assert, assign, boolean, object, optional, string } from "superstruct";
|
||||
import memoizeOne from "memoize-one";
|
||||
import {
|
||||
assert,
|
||||
assign,
|
||||
boolean,
|
||||
enums,
|
||||
object,
|
||||
optional,
|
||||
string,
|
||||
} from "superstruct";
|
||||
import { fireEvent } from "../../../../common/dom/fire_event";
|
||||
import { computeDomain } from "../../../../common/entity/compute_domain";
|
||||
import type { LocalizeFunc } from "../../../../common/translations/localize";
|
||||
import "../../../../components/ha-form/ha-form";
|
||||
import type { SchemaUnion } from "../../../../components/ha-form/types";
|
||||
import type {
|
||||
HaFormSchema,
|
||||
SchemaUnion,
|
||||
} from "../../../../components/ha-form/types";
|
||||
import type { HomeAssistant } from "../../../../types";
|
||||
import { STUB_IMAGE } from "../../cards/hui-picture-entity-card";
|
||||
import type { PictureEntityCardConfig } from "../../cards/types";
|
||||
@ -22,7 +35,7 @@ const cardConfigStruct = assign(
|
||||
image: optional(string()),
|
||||
name: optional(string()),
|
||||
camera_image: optional(string()),
|
||||
camera_view: optional(string()),
|
||||
camera_view: optional(enums(["auto", "live"])),
|
||||
aspect_ratio: optional(string()),
|
||||
tap_action: optional(actionConfigStruct),
|
||||
hold_action: optional(actionConfigStruct),
|
||||
@ -30,74 +43,10 @@ const cardConfigStruct = assign(
|
||||
show_name: optional(boolean()),
|
||||
show_state: optional(boolean()),
|
||||
theme: optional(string()),
|
||||
fit_mode: optional(string()),
|
||||
fit_mode: optional(enums(["cover", "contain", "fill"])),
|
||||
})
|
||||
);
|
||||
|
||||
const SCHEMA = [
|
||||
{ name: "entity", required: true, selector: { entity: {} } },
|
||||
{ name: "name", selector: { text: {} } },
|
||||
{ name: "image", selector: { image: {} } },
|
||||
{ name: "camera_image", selector: { entity: { domain: "camera" } } },
|
||||
{
|
||||
name: "",
|
||||
type: "grid",
|
||||
schema: [
|
||||
{
|
||||
name: "camera_view",
|
||||
selector: { select: { options: ["auto", "live"] } },
|
||||
},
|
||||
{ name: "aspect_ratio", selector: { text: {} } },
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "",
|
||||
type: "grid",
|
||||
schema: [
|
||||
{
|
||||
name: "show_name",
|
||||
selector: { boolean: {} },
|
||||
},
|
||||
{
|
||||
name: "show_state",
|
||||
selector: { boolean: {} },
|
||||
},
|
||||
],
|
||||
},
|
||||
{ name: "theme", selector: { theme: {} } },
|
||||
{
|
||||
name: "interactions",
|
||||
type: "expandable",
|
||||
flatten: true,
|
||||
iconPath: mdiGestureTap,
|
||||
schema: [
|
||||
{
|
||||
name: "tap_action",
|
||||
selector: {
|
||||
ui_action: {
|
||||
default_action: "more-info",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "",
|
||||
type: "optional_actions",
|
||||
flatten: true,
|
||||
schema: (["hold_action", "double_tap_action"] as const).map(
|
||||
(action) => ({
|
||||
name: action,
|
||||
selector: {
|
||||
ui_action: {
|
||||
default_action: "none" as const,
|
||||
},
|
||||
},
|
||||
})
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
] as const;
|
||||
|
||||
@customElement("hui-picture-entity-card-editor")
|
||||
export class HuiPictureEntityCardEditor
|
||||
extends LitElement
|
||||
@ -112,6 +61,99 @@ export class HuiPictureEntityCardEditor
|
||||
this._config = config;
|
||||
}
|
||||
|
||||
private _schema = memoizeOne(
|
||||
(localize: LocalizeFunc) =>
|
||||
[
|
||||
{ name: "entity", required: true, selector: { entity: {} } },
|
||||
{ name: "name", selector: { text: {} } },
|
||||
{ name: "image", selector: { image: {} } },
|
||||
{ name: "camera_image", selector: { entity: { domain: "camera" } } },
|
||||
{
|
||||
name: "",
|
||||
type: "grid",
|
||||
schema: [
|
||||
{
|
||||
name: "camera_view",
|
||||
required: true,
|
||||
selector: {
|
||||
select: {
|
||||
options: ["auto", "live"].map((value) => ({
|
||||
value,
|
||||
label: localize(
|
||||
`ui.panel.lovelace.editor.card.generic.camera_view_options.${value}`
|
||||
),
|
||||
})),
|
||||
mode: "dropdown",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "fit_mode",
|
||||
required: true,
|
||||
selector: {
|
||||
select: {
|
||||
options: ["cover", "contain", "fill"].map((value) => ({
|
||||
value,
|
||||
label: localize(
|
||||
`ui.panel.lovelace.editor.card.generic.fit_mode_options.${value}`
|
||||
),
|
||||
})),
|
||||
mode: "dropdown",
|
||||
},
|
||||
},
|
||||
},
|
||||
{ name: "aspect_ratio", selector: { text: {} } },
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "",
|
||||
type: "grid",
|
||||
schema: [
|
||||
{
|
||||
name: "show_name",
|
||||
selector: { boolean: {} },
|
||||
},
|
||||
{
|
||||
name: "show_state",
|
||||
selector: { boolean: {} },
|
||||
},
|
||||
],
|
||||
},
|
||||
{ name: "theme", selector: { theme: {} } },
|
||||
{
|
||||
name: "interactions",
|
||||
type: "expandable",
|
||||
flatten: true,
|
||||
iconPath: mdiGestureTap,
|
||||
schema: [
|
||||
{
|
||||
name: "tap_action",
|
||||
selector: {
|
||||
ui_action: {
|
||||
default_action: "more-info",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "",
|
||||
type: "optional_actions",
|
||||
flatten: true,
|
||||
schema: (["hold_action", "double_tap_action"] as const).map(
|
||||
(action) => ({
|
||||
name: action,
|
||||
selector: {
|
||||
ui_action: {
|
||||
default_action: "none" as const,
|
||||
},
|
||||
},
|
||||
})
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
] as const satisfies HaFormSchema[]
|
||||
);
|
||||
|
||||
protected render() {
|
||||
if (!this.hass || !this._config) {
|
||||
return nothing;
|
||||
@ -121,15 +163,19 @@ export class HuiPictureEntityCardEditor
|
||||
show_state: true,
|
||||
show_name: true,
|
||||
camera_view: "auto",
|
||||
fit_mode: "cover",
|
||||
...this._config,
|
||||
};
|
||||
|
||||
const schema = this._schema(this.hass.localize);
|
||||
|
||||
return html`
|
||||
<ha-form
|
||||
.hass=${this.hass}
|
||||
.data=${data}
|
||||
.schema=${SCHEMA}
|
||||
.schema=${schema}
|
||||
.computeLabel=${this._computeLabelCallback}
|
||||
.computeHelper=${this._computeHelperCallback}
|
||||
@value-changed=${this._valueChanged}
|
||||
></ha-form>
|
||||
</div>
|
||||
@ -152,7 +198,9 @@ export class HuiPictureEntityCardEditor
|
||||
fireEvent(this, "config-changed", { config });
|
||||
}
|
||||
|
||||
private _computeLabelCallback = (schema: SchemaUnion<typeof SCHEMA>) => {
|
||||
private _computeLabelCallback = (
|
||||
schema: SchemaUnion<ReturnType<typeof this._schema>>
|
||||
) => {
|
||||
switch (schema.name) {
|
||||
case "theme":
|
||||
case "tap_action":
|
||||
@ -170,6 +218,21 @@ export class HuiPictureEntityCardEditor
|
||||
}
|
||||
};
|
||||
|
||||
private _computeHelperCallback = (
|
||||
schema: SchemaUnion<ReturnType<typeof this._schema>>
|
||||
) => {
|
||||
switch (schema.name) {
|
||||
case "aspect_ratio":
|
||||
return typeof this._config?.grid_options?.rows === "number"
|
||||
? this.hass!.localize(
|
||||
`ui.panel.lovelace.editor.card.generic.aspect_ratio_ignored`
|
||||
)
|
||||
: "";
|
||||
default:
|
||||
return "";
|
||||
}
|
||||
};
|
||||
|
||||
static styles: CSSResultGroup = configElementStyle;
|
||||
}
|
||||
|
||||
|
@ -1,11 +1,24 @@
|
||||
import { mdiGestureTap } from "@mdi/js";
|
||||
import type { CSSResultGroup } from "lit";
|
||||
import { html, LitElement, nothing } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import { array, assert, assign, object, optional, string } from "superstruct";
|
||||
import { mdiGestureTap } from "@mdi/js";
|
||||
import memoizeOne from "memoize-one";
|
||||
import {
|
||||
array,
|
||||
assert,
|
||||
assign,
|
||||
enums,
|
||||
object,
|
||||
optional,
|
||||
string,
|
||||
} from "superstruct";
|
||||
import { fireEvent } from "../../../../common/dom/fire_event";
|
||||
import type { LocalizeFunc } from "../../../../common/translations/localize";
|
||||
import "../../../../components/ha-form/ha-form";
|
||||
import type { SchemaUnion } from "../../../../components/ha-form/types";
|
||||
import type {
|
||||
HaFormSchema,
|
||||
SchemaUnion,
|
||||
} from "../../../../components/ha-form/types";
|
||||
import type { ActionConfig } from "../../../../data/lovelace/config/action";
|
||||
import type { HomeAssistant } from "../../../../types";
|
||||
import type { PictureGlanceCardConfig } from "../../cards/types";
|
||||
@ -26,78 +39,17 @@ const cardConfigStruct = assign(
|
||||
image: optional(string()),
|
||||
image_entity: optional(string()),
|
||||
camera_image: optional(string()),
|
||||
camera_view: optional(string()),
|
||||
camera_view: optional(enums(["auto", "live"])),
|
||||
aspect_ratio: optional(string()),
|
||||
tap_action: optional(actionConfigStruct),
|
||||
hold_action: optional(actionConfigStruct),
|
||||
double_tap_action: optional(actionConfigStruct),
|
||||
entities: array(entitiesConfigStruct),
|
||||
theme: optional(string()),
|
||||
fit_mode: optional(enums(["cover", "contain", "fill"])),
|
||||
})
|
||||
);
|
||||
|
||||
const SCHEMA = [
|
||||
{ name: "title", selector: { text: {} } },
|
||||
{ name: "image", selector: { image: {} } },
|
||||
{
|
||||
name: "image_entity",
|
||||
selector: { entity: { domain: ["image", "person"] } },
|
||||
},
|
||||
{ name: "camera_image", selector: { entity: { domain: "camera" } } },
|
||||
{
|
||||
name: "",
|
||||
type: "grid",
|
||||
schema: [
|
||||
{
|
||||
name: "camera_view",
|
||||
selector: { select: { options: ["auto", "live"] } },
|
||||
},
|
||||
{ name: "aspect_ratio", selector: { text: {} } },
|
||||
],
|
||||
},
|
||||
{ name: "entity", selector: { entity: {} } },
|
||||
{ name: "theme", selector: { theme: {} } },
|
||||
{
|
||||
name: "interactions",
|
||||
type: "expandable",
|
||||
flatten: true,
|
||||
iconPath: mdiGestureTap,
|
||||
schema: [
|
||||
{
|
||||
name: "tap_action",
|
||||
selector: {
|
||||
ui_action: {
|
||||
default_action: "more-info",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "hold_action",
|
||||
selector: {
|
||||
ui_action: {
|
||||
default_action: "more-info",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "",
|
||||
type: "optional_actions",
|
||||
flatten: true,
|
||||
schema: [
|
||||
{
|
||||
name: "double_tap_action",
|
||||
selector: {
|
||||
ui_action: {
|
||||
default_action: "none",
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
] as const;
|
||||
|
||||
@customElement("hui-picture-glance-card-editor")
|
||||
export class HuiPictureGlanceCardEditor
|
||||
extends LitElement
|
||||
@ -109,6 +61,97 @@ export class HuiPictureGlanceCardEditor
|
||||
|
||||
@state() private _configEntities?: EntityConfig[];
|
||||
|
||||
private _schema = memoizeOne(
|
||||
(localize: LocalizeFunc) =>
|
||||
[
|
||||
{ name: "title", selector: { text: {} } },
|
||||
{ name: "image", selector: { image: {} } },
|
||||
{
|
||||
name: "image_entity",
|
||||
selector: { entity: { domain: ["image", "person"] } },
|
||||
},
|
||||
{ name: "camera_image", selector: { entity: { domain: "camera" } } },
|
||||
{
|
||||
name: "",
|
||||
type: "grid",
|
||||
schema: [
|
||||
{
|
||||
name: "camera_view",
|
||||
required: true,
|
||||
selector: {
|
||||
select: {
|
||||
options: ["auto", "live"].map((value) => ({
|
||||
value,
|
||||
label: localize(
|
||||
`ui.panel.lovelace.editor.card.generic.camera_view_options.${value}`
|
||||
),
|
||||
})),
|
||||
mode: "dropdown",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "fit_mode",
|
||||
required: true,
|
||||
selector: {
|
||||
select: {
|
||||
options: ["cover", "contain", "fill"].map((value) => ({
|
||||
value,
|
||||
label: localize(
|
||||
`ui.panel.lovelace.editor.card.generic.fit_mode_options.${value}`
|
||||
),
|
||||
})),
|
||||
mode: "dropdown",
|
||||
},
|
||||
},
|
||||
},
|
||||
{ name: "aspect_ratio", selector: { text: {} } },
|
||||
],
|
||||
},
|
||||
{ name: "entity", selector: { entity: {} } },
|
||||
{ name: "theme", selector: { theme: {} } },
|
||||
{
|
||||
name: "interactions",
|
||||
type: "expandable",
|
||||
flatten: true,
|
||||
iconPath: mdiGestureTap,
|
||||
schema: [
|
||||
{
|
||||
name: "tap_action",
|
||||
selector: {
|
||||
ui_action: {
|
||||
default_action: "more-info",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "hold_action",
|
||||
selector: {
|
||||
ui_action: {
|
||||
default_action: "more-info",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "",
|
||||
type: "optional_actions",
|
||||
flatten: true,
|
||||
schema: [
|
||||
{
|
||||
name: "double_tap_action",
|
||||
selector: {
|
||||
ui_action: {
|
||||
default_action: "none",
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
] as const satisfies HaFormSchema[]
|
||||
);
|
||||
|
||||
public setConfig(config: PictureGlanceCardConfig): void {
|
||||
assert(config, cardConfigStruct);
|
||||
this._config = config;
|
||||
@ -128,14 +171,17 @@ export class HuiPictureGlanceCardEditor
|
||||
return nothing;
|
||||
}
|
||||
|
||||
const data = { camera_view: "auto", ...this._config };
|
||||
const data = { camera_view: "auto", fit_mode: "cover", ...this._config };
|
||||
|
||||
const schema = this._schema(this.hass.localize);
|
||||
|
||||
return html`
|
||||
<ha-form
|
||||
.hass=${this.hass}
|
||||
.data=${data}
|
||||
.schema=${SCHEMA}
|
||||
.schema=${schema}
|
||||
.computeLabel=${this._computeLabelCallback}
|
||||
.computeHelper=${this._computeHelperCallback}
|
||||
@value-changed=${this._valueChanged}
|
||||
></ha-form>
|
||||
<div class="card-config">
|
||||
@ -164,7 +210,9 @@ export class HuiPictureGlanceCardEditor
|
||||
fireEvent(this, "config-changed", { config: this._config });
|
||||
}
|
||||
|
||||
private _computeLabelCallback = (schema: SchemaUnion<typeof SCHEMA>) => {
|
||||
private _computeLabelCallback = (
|
||||
schema: SchemaUnion<ReturnType<typeof this._schema>>
|
||||
) => {
|
||||
switch (schema.name) {
|
||||
case "theme":
|
||||
case "tap_action":
|
||||
@ -186,6 +234,21 @@ export class HuiPictureGlanceCardEditor
|
||||
}
|
||||
};
|
||||
|
||||
private _computeHelperCallback = (
|
||||
schema: SchemaUnion<ReturnType<typeof this._schema>>
|
||||
) => {
|
||||
switch (schema.name) {
|
||||
case "aspect_ratio":
|
||||
return typeof this._config?.grid_options?.rows === "number"
|
||||
? this.hass!.localize(
|
||||
`ui.panel.lovelace.editor.card.generic.aspect_ratio_ignored`
|
||||
)
|
||||
: "";
|
||||
default:
|
||||
return "";
|
||||
}
|
||||
};
|
||||
|
||||
static styles: CSSResultGroup = configElementStyle;
|
||||
}
|
||||
|
||||
|
55
src/panels/profile/ha-entity-id-picker-row.ts
Normal file
55
src/panels/profile/ha-entity-id-picker-row.ts
Normal file
@ -0,0 +1,55 @@
|
||||
import type { TemplateResult } from "lit";
|
||||
import { css, html, LitElement } from "lit";
|
||||
import { customElement, property } from "lit/decorators";
|
||||
import "../../components/ha-card";
|
||||
import "../../components/ha-settings-row";
|
||||
import "../../components/ha-switch";
|
||||
import type { CoreFrontendUserData } from "../../data/frontend";
|
||||
import { getOptimisticFrontendUserDataCollection } from "../../data/frontend";
|
||||
import type { HomeAssistant } from "../../types";
|
||||
|
||||
@customElement("ha-entity-id-picker-row")
|
||||
class EntityIdPickerRow extends LitElement {
|
||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||
|
||||
@property({ type: Boolean }) public narrow = false;
|
||||
|
||||
@property({ attribute: false }) public coreUserData?: CoreFrontendUserData;
|
||||
|
||||
protected render(): TemplateResult {
|
||||
return html`
|
||||
<ha-settings-row .narrow=${this.narrow}>
|
||||
<span slot="heading">
|
||||
${this.hass.localize("ui.panel.profile.entity_id_picker.title")}</span
|
||||
>
|
||||
<span slot="description">
|
||||
${this.hass.localize("ui.panel.profile.entity_id_picker.description")}
|
||||
</span>
|
||||
<ha-switch
|
||||
.checked=${this.coreUserData && this.coreUserData.showEntityIdPicker}
|
||||
.disabled=${this.coreUserData === undefined}
|
||||
@change=${this._toggled}
|
||||
></ha-switch>
|
||||
</ha-settings-row>
|
||||
`;
|
||||
}
|
||||
|
||||
private async _toggled(ev) {
|
||||
getOptimisticFrontendUserDataCollection(this.hass.connection, "core").save({
|
||||
...this.coreUserData,
|
||||
showEntityIdPicker: ev.currentTarget.checked,
|
||||
});
|
||||
}
|
||||
|
||||
static styles = css`
|
||||
a {
|
||||
color: var(--primary-color);
|
||||
}
|
||||
`;
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"ha-entity-id-picker-row": EntityIdPickerRow;
|
||||
}
|
||||
}
|
@ -16,6 +16,7 @@ import { haStyle } from "../../resources/styles";
|
||||
import type { HomeAssistant, Route } from "../../types";
|
||||
import "./ha-advanced-mode-row";
|
||||
import "./ha-enable-shortcuts-row";
|
||||
import "./ha-entity-id-picker-row";
|
||||
import "./ha-force-narrow-row";
|
||||
import "./ha-pick-dashboard-row";
|
||||
import "./ha-pick-first-weekday-row";
|
||||
@ -156,6 +157,15 @@ class HaProfileSectionGeneral extends LitElement {
|
||||
></ha-advanced-mode-row>
|
||||
`
|
||||
: ""}
|
||||
${this.hass.user!.is_admin
|
||||
? html`
|
||||
<ha-entity-id-picker-row
|
||||
.hass=${this.hass}
|
||||
.narrow=${this.narrow}
|
||||
.coreUserData=${this._coreUserData}
|
||||
></ha-entity-id-picker-row>
|
||||
`
|
||||
: ""}
|
||||
</ha-card>
|
||||
<ha-card
|
||||
.header=${this.hass.localize(
|
||||
|
@ -1106,7 +1106,15 @@
|
||||
"only_https_supported": "You can only use your camera to scan a QR code when using HTTPS.",
|
||||
"not_supported": "Your browser doesn't support QR scanning.",
|
||||
"manual_input": "You can scan the QR code with another QR scanner and paste the code in the input below",
|
||||
"enter_qr_code": "Enter QR code value"
|
||||
"enter_qr_code": "Enter QR code value",
|
||||
"retry": "Retry",
|
||||
"wrong_code": "Wrong barcode scanned! {format}: {rawValue}, we need a QR code.",
|
||||
"no_camera_found": "No camera found",
|
||||
"app": {
|
||||
"title": "Scan QR code",
|
||||
"description": "Find the code on your device",
|
||||
"alternativeOptionLabel": "More options"
|
||||
}
|
||||
},
|
||||
"climate-control": {
|
||||
"temperature_up": "Increase temperature",
|
||||
@ -5652,7 +5660,6 @@
|
||||
"back": "Back",
|
||||
"add_node": "Add device",
|
||||
"remove_node": "Remove device",
|
||||
"reconfigure_server": "Re-configure server",
|
||||
"rebuild_network_routes": "Rebuild network routes",
|
||||
"in_progress_inclusion_exclusion": "Z-Wave JS is searching for devices",
|
||||
"cancel_inclusion_exclusion": "Stop searching"
|
||||
@ -5673,7 +5680,9 @@
|
||||
"backup_failed": "Failed to download backup",
|
||||
"restore_complete": "Backup restored",
|
||||
"restore_failed": "Failed to restore backup",
|
||||
"downloading": "Downloading backup"
|
||||
"downloading": "Downloading backup",
|
||||
"restoring": "Restoring backup",
|
||||
"migrate": "Migrate controller"
|
||||
},
|
||||
"statistics": {
|
||||
"title": "Controller statistics",
|
||||
@ -5876,33 +5885,91 @@
|
||||
},
|
||||
"add_node": {
|
||||
"title": "Add a Z-Wave device",
|
||||
"searching_device": "Searching for devices…",
|
||||
"follow_device_instructions": "Follow the directions that came with your device to trigger pairing on the device.",
|
||||
"searching_devices": "Searching for devices",
|
||||
"follow_device_instructions": "Follow the directions provided with your device to put it into pairing mode (sometimes called \"inclusion mode\")",
|
||||
"security_options": "Advanced security options",
|
||||
"choose_inclusion_strategy": "How do you want to add your device",
|
||||
"qr_code": "QR Code",
|
||||
"qr_code_paragraph": "If your device supports SmartStart you can scan the QR code for easy pairing.",
|
||||
"scan_qr_code": "Scan QR code",
|
||||
"add_device_failed": "Add device failed",
|
||||
"inclusion_failed": "The device could not be added.",
|
||||
"getting_device_information": "Getting device information",
|
||||
"saving_device": "Saving device",
|
||||
"check_logs": "Please check the logs for more information.",
|
||||
"inclusion_finished": "The device has been added.",
|
||||
"provisioning_finished": "The device has been added. Once you power it on, it will become available.",
|
||||
"view_device": "View Device",
|
||||
"interview_started": "The device is being interviewed. This may take some time.",
|
||||
"interview_failed": "The device interview failed. Additional information may be available in the logs.",
|
||||
"waiting_for_device": "Communicating with the device. Please wait.",
|
||||
"adding_insecurely": "The device is being added insecurely",
|
||||
"added_insecurely": "The device was added insecurely",
|
||||
"added_insecurely_text": "There was an error during secure inclusion. You can try again by excluding the device and adding it again.",
|
||||
"low_security_reason": {
|
||||
"0": "Security bootstrapping was canceled by the user.",
|
||||
"1": "The required security keys were not configured in the driver.",
|
||||
"2": "No Security S2 user callbacks (or provisioning info) were provided to grant security classes and/or validate the DSK.",
|
||||
"3": "An expected message was not received within the corresponding timeout.",
|
||||
"4": "There was no possible match in encryption parameters between the controller and the node.",
|
||||
"5": "Security bootstrapping was canceled by the included node.",
|
||||
"6": "The PIN was incorrect, so the included node could not decode the key exchange commands.",
|
||||
"7": "There was a mismatch in security keys between the controller and the node.",
|
||||
"8": "Unknown error occurred."
|
||||
"timeout_error": "No device found after {minutes} minutes. Please check the device and try again.",
|
||||
"select_method": {
|
||||
"webcam_unsupported": "You can only use your camera to scan a QR code when using a secure connection with the app or over HTTPS.",
|
||||
"qr_code_webcam": "Scan QR code using webcam",
|
||||
"qr_code_webcam_description": "Find and scan the code on your device.",
|
||||
"qr_code_manual": "Enter QR code manually",
|
||||
"qr_code_manual_description": "You can scan the QR code with another QR scanner and paste the code.",
|
||||
"search_device": "Search for device",
|
||||
"search_device_description": "Use this option to manually include a device that does not have a SmartStart QR code."
|
||||
},
|
||||
"qr": {
|
||||
"manual": {
|
||||
"title": "Enter QR code manually",
|
||||
"text": "You can scan the QR code with another QR scanner and paste the code here.",
|
||||
"placeholder": "Code"
|
||||
},
|
||||
"scan_code": "Scan QR code",
|
||||
"other_add_options": "Other add options",
|
||||
"invalid_code": "Invalid QR code ({code})",
|
||||
"unsupported_code": "This QR code is not supported \"{code}\""
|
||||
},
|
||||
"specific_device": {
|
||||
"title": "Searching for device",
|
||||
"turn_on_device": "Turn on the device",
|
||||
"turn_on_device_description": "If your device is already turned on, you might need to turn it off and on again. Consult your device manual if you are unsure.",
|
||||
"add_another_z_wave_device": "Add another Z-Wave device",
|
||||
"close_description": "Feel free to close this screen, as this process will continue in the background."
|
||||
},
|
||||
"select_strategy": {
|
||||
"title": "Choose security strategy",
|
||||
"default_label": "Secure if possible",
|
||||
"default_description": "Requires user interaction during inclusion. Fast and secure with S2 when supported. Allows manually selecting which security keys to grant. Fallback to legacy S0 or no encryption when necessary.",
|
||||
"s0_label": "Legacy Secure",
|
||||
"s0_description": "Uses the older S0 security that is secure, but slow due to a lot of overhead. Allows securely including S2 capable devices which fail to be included with S2.",
|
||||
"insecure_label": "Insecure",
|
||||
"insecure_description": "Do not use encryption.",
|
||||
"inclusion_strategy": "Inclusion strategy: {strategy}"
|
||||
},
|
||||
"configure_device": {
|
||||
"title": "Add device",
|
||||
"device_name": "Name",
|
||||
"device_area": "Area",
|
||||
"choose_network_type": "Choose network type",
|
||||
"long_range_label": "Direct long range",
|
||||
"long_range_description": "Direct connection to Home Assistant for extended coverage, without a mesh network.",
|
||||
"mesh_label": "Mesh network",
|
||||
"mesh_description": "Devices relay signals to each other, enhancing coverage and reliability.",
|
||||
"add_device": "Add device",
|
||||
"save_device_failed": "Saving new device info failed"
|
||||
},
|
||||
"validate_dsk_pin": {
|
||||
"title": "Verify key",
|
||||
"text": "Find the key on your device or manual and enter the 5-digit PIN.",
|
||||
"placeholder": "PIN"
|
||||
},
|
||||
"added_insecure": {
|
||||
"title": "Device added insecurely",
|
||||
"text": "The device {name} has been added to your Z-Wave network.",
|
||||
"added_insecurely_text": "{deviceName} has been added without secure inclusion. You can still use your device.",
|
||||
"try_again_text": "You can try again by removing the device and adding it again.",
|
||||
"view_device": "View device",
|
||||
"low_security_reason": {
|
||||
"0": "Security bootstrapping was canceled by the user.",
|
||||
"1": "The required security keys were not configured in the driver.",
|
||||
"2": "No Security S2 user callbacks (or provisioning info) were provided to grant security classes and/or validate the DSK.",
|
||||
"3": "An expected message was not received within the corresponding timeout.",
|
||||
"4": "There was no possible match in encryption parameters between the controller and the node.",
|
||||
"5": "Security bootstrapping was canceled by the included node.",
|
||||
"6": "The PIN was incorrect, so the included node could not decode the key exchange commands.",
|
||||
"7": "There was a mismatch in security keys between the controller and the node.",
|
||||
"8": "Unknown error occurred."
|
||||
}
|
||||
},
|
||||
"grant_security_classes": {
|
||||
"title": "Security classes",
|
||||
"description": "The device has requested the following security classes"
|
||||
}
|
||||
},
|
||||
"provisioned": {
|
||||
@ -7127,13 +7194,24 @@
|
||||
"generic": {
|
||||
"alt_text": "Alternative text",
|
||||
"aspect_ratio": "Aspect ratio",
|
||||
"aspect_ratio_ignored": "Will be ignored because the card is resized.",
|
||||
"attribute": "Attribute",
|
||||
"camera_image": "Camera entity",
|
||||
"image_entity": "Image entity",
|
||||
"camera_view": "Camera view",
|
||||
"camera_view_options": {
|
||||
"auto": "Auto",
|
||||
"live": "Live"
|
||||
},
|
||||
"double_tap_action": "Double tap behavior",
|
||||
"entities": "Entities",
|
||||
"entity": "Entity",
|
||||
"fit_mode": "Fit mode",
|
||||
"fit_mode_options": {
|
||||
"contain": "Contain",
|
||||
"cover": "Cover",
|
||||
"fill": "Fill"
|
||||
},
|
||||
"hold_action": "Hold behavior",
|
||||
"hours_to_show": "Hours to show",
|
||||
"days_to_show": "Days to show",
|
||||
@ -7848,6 +7926,10 @@
|
||||
"description": "Unlocks advanced features.",
|
||||
"link_promo": "Learn more"
|
||||
},
|
||||
"entity_id_picker": {
|
||||
"title": "Display entity IDs in picker",
|
||||
"description": "Shows the full entity IDs when selecting entities with a picker."
|
||||
},
|
||||
"refresh_tokens": {
|
||||
"header": "Refresh tokens",
|
||||
"description": "Each refresh token represents a login session. Refresh tokens will be automatically deleted when you log out. Unused refresh tokens will be automatically deleted after 90 days. The following refresh tokens are currently active for your account.",
|
||||
|
292
yarn.lock
292
yarn.lock
@ -5001,15 +5001,15 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@typescript-eslint/eslint-plugin@npm:8.29.1":
|
||||
version: 8.29.1
|
||||
resolution: "@typescript-eslint/eslint-plugin@npm:8.29.1"
|
||||
"@typescript-eslint/eslint-plugin@npm:8.30.0":
|
||||
version: 8.30.0
|
||||
resolution: "@typescript-eslint/eslint-plugin@npm:8.30.0"
|
||||
dependencies:
|
||||
"@eslint-community/regexpp": "npm:^4.10.0"
|
||||
"@typescript-eslint/scope-manager": "npm:8.29.1"
|
||||
"@typescript-eslint/type-utils": "npm:8.29.1"
|
||||
"@typescript-eslint/utils": "npm:8.29.1"
|
||||
"@typescript-eslint/visitor-keys": "npm:8.29.1"
|
||||
"@typescript-eslint/scope-manager": "npm:8.30.0"
|
||||
"@typescript-eslint/type-utils": "npm:8.30.0"
|
||||
"@typescript-eslint/utils": "npm:8.30.0"
|
||||
"@typescript-eslint/visitor-keys": "npm:8.30.0"
|
||||
graphemer: "npm:^1.4.0"
|
||||
ignore: "npm:^5.3.1"
|
||||
natural-compare: "npm:^1.4.0"
|
||||
@ -5018,64 +5018,64 @@ __metadata:
|
||||
"@typescript-eslint/parser": ^8.0.0 || ^8.0.0-alpha.0
|
||||
eslint: ^8.57.0 || ^9.0.0
|
||||
typescript: ">=4.8.4 <5.9.0"
|
||||
checksum: 10/0568894f0ea50e67622605eb347d4e26a41571ad06234478ac695a097e9b3ff6252d6d60034853d7a6b8ec644b5c1d354be21075d691173dd7f2fbecb1102674
|
||||
checksum: 10/55c7823bae8874e7aa5ff5a82f794e78c47f5ad6e17b021e8ca0bc25a8abf262bb824d0d16da760a24411e53abba0b5bbd159ab430d373dda4c84672edf954a9
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@typescript-eslint/parser@npm:8.29.1":
|
||||
version: 8.29.1
|
||||
resolution: "@typescript-eslint/parser@npm:8.29.1"
|
||||
"@typescript-eslint/parser@npm:8.30.0":
|
||||
version: 8.30.0
|
||||
resolution: "@typescript-eslint/parser@npm:8.30.0"
|
||||
dependencies:
|
||||
"@typescript-eslint/scope-manager": "npm:8.29.1"
|
||||
"@typescript-eslint/types": "npm:8.29.1"
|
||||
"@typescript-eslint/typescript-estree": "npm:8.29.1"
|
||||
"@typescript-eslint/visitor-keys": "npm:8.29.1"
|
||||
"@typescript-eslint/scope-manager": "npm:8.30.0"
|
||||
"@typescript-eslint/types": "npm:8.30.0"
|
||||
"@typescript-eslint/typescript-estree": "npm:8.30.0"
|
||||
"@typescript-eslint/visitor-keys": "npm:8.30.0"
|
||||
debug: "npm:^4.3.4"
|
||||
peerDependencies:
|
||||
eslint: ^8.57.0 || ^9.0.0
|
||||
typescript: ">=4.8.4 <5.9.0"
|
||||
checksum: 10/effb4cc24e375e4229e711b3ea8611a205bf81964cb487f518dd4a7d6cecde7324201ce4e2c3cd420e0ce7649899294307da2cd77f145af4efef3a0292189f20
|
||||
checksum: 10/88cc1492f6ed7723ec5e3561229a7f7082da38b77e30f628a4215a453a204d2c6aa68361f2e1f56058479840d130d5141f0e30057e836fc2b619b773ef4d5e8c
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@typescript-eslint/scope-manager@npm:8.29.1":
|
||||
version: 8.29.1
|
||||
resolution: "@typescript-eslint/scope-manager@npm:8.29.1"
|
||||
"@typescript-eslint/scope-manager@npm:8.30.0":
|
||||
version: 8.30.0
|
||||
resolution: "@typescript-eslint/scope-manager@npm:8.30.0"
|
||||
dependencies:
|
||||
"@typescript-eslint/types": "npm:8.29.1"
|
||||
"@typescript-eslint/visitor-keys": "npm:8.29.1"
|
||||
checksum: 10/33a02f490b53436729f5ca2e6e0c5b8db72adb455274e5de43bdaada21033e7941aed1d92653321991e186af77f7794dc0ac35d2fce891cdf65a6d3fb192249e
|
||||
"@typescript-eslint/types": "npm:8.30.0"
|
||||
"@typescript-eslint/visitor-keys": "npm:8.30.0"
|
||||
checksum: 10/511aec42d71cb0e38ff8fef12569d332304af308b9e57ddf9d11f9d6886094813170ff9d29dd79575c25d7b2a0f09b1e74a20f4548a243c38259944f26188ff8
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@typescript-eslint/type-utils@npm:8.29.1":
|
||||
version: 8.29.1
|
||||
resolution: "@typescript-eslint/type-utils@npm:8.29.1"
|
||||
"@typescript-eslint/type-utils@npm:8.30.0":
|
||||
version: 8.30.0
|
||||
resolution: "@typescript-eslint/type-utils@npm:8.30.0"
|
||||
dependencies:
|
||||
"@typescript-eslint/typescript-estree": "npm:8.29.1"
|
||||
"@typescript-eslint/utils": "npm:8.29.1"
|
||||
"@typescript-eslint/typescript-estree": "npm:8.30.0"
|
||||
"@typescript-eslint/utils": "npm:8.30.0"
|
||||
debug: "npm:^4.3.4"
|
||||
ts-api-utils: "npm:^2.0.1"
|
||||
peerDependencies:
|
||||
eslint: ^8.57.0 || ^9.0.0
|
||||
typescript: ">=4.8.4 <5.9.0"
|
||||
checksum: 10/3774d6fb32c058b3fa607480e5603fdd2919e07d8babbc00aa703f31c6fd6f7fbc25c25ff246e7e6ac6b77ad0e0b66838a7136aebc31503a58fbe509f68ab2f4
|
||||
checksum: 10/dee013705224abdd4f0f84f8de00bb0f70f3e435d116e6f7dc6fbdce92eaec160e9c61faf25ce51945137f0b75e00e4b5304adaf8289049620cfec3d4c966a1d
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@typescript-eslint/types@npm:8.29.1":
|
||||
version: 8.29.1
|
||||
resolution: "@typescript-eslint/types@npm:8.29.1"
|
||||
checksum: 10/99ff59e7af3728858af9b7fdc0165955bcf5cd2eefeaafabfbdd7951fbc8ad869cbb7bed7bcbed6c3c50a8661333b33364dc1de0a0c8f3c64d5882339a5d30dd
|
||||
"@typescript-eslint/types@npm:8.30.0":
|
||||
version: 8.30.0
|
||||
resolution: "@typescript-eslint/types@npm:8.30.0"
|
||||
checksum: 10/c7b01c3d78db145cffcb3703e432fc65758cc140ae442422f9fbd9aea0e2d2d86d210c656bf3767904af8c541c73eb36a745727473493e691195245eee19b03c
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@typescript-eslint/typescript-estree@npm:8.29.1":
|
||||
version: 8.29.1
|
||||
resolution: "@typescript-eslint/typescript-estree@npm:8.29.1"
|
||||
"@typescript-eslint/typescript-estree@npm:8.30.0":
|
||||
version: 8.30.0
|
||||
resolution: "@typescript-eslint/typescript-estree@npm:8.30.0"
|
||||
dependencies:
|
||||
"@typescript-eslint/types": "npm:8.29.1"
|
||||
"@typescript-eslint/visitor-keys": "npm:8.29.1"
|
||||
"@typescript-eslint/types": "npm:8.30.0"
|
||||
"@typescript-eslint/visitor-keys": "npm:8.30.0"
|
||||
debug: "npm:^4.3.4"
|
||||
fast-glob: "npm:^3.3.2"
|
||||
is-glob: "npm:^4.0.3"
|
||||
@ -5084,160 +5084,160 @@ __metadata:
|
||||
ts-api-utils: "npm:^2.0.1"
|
||||
peerDependencies:
|
||||
typescript: ">=4.8.4 <5.9.0"
|
||||
checksum: 10/dded2ebe4c3287443000e3b825e673d0eddb7c48eb2d373c5b7059ea7dbbeba488d7f1de2e42ed7a9299ccff926a65821f2b5594022b49564026ba01c0cc07ab
|
||||
checksum: 10/1cb95ba8001ed8e90c9bdddc1c852c8e7244a4cd4f332171636f62dd98fe170b1652cd8debb90569c21b0f7dfffd2550ad1ad19c3e8d8fe095b7f09dfd4e6971
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@typescript-eslint/utils@npm:8.29.1":
|
||||
version: 8.29.1
|
||||
resolution: "@typescript-eslint/utils@npm:8.29.1"
|
||||
"@typescript-eslint/utils@npm:8.30.0":
|
||||
version: 8.30.0
|
||||
resolution: "@typescript-eslint/utils@npm:8.30.0"
|
||||
dependencies:
|
||||
"@eslint-community/eslint-utils": "npm:^4.4.0"
|
||||
"@typescript-eslint/scope-manager": "npm:8.29.1"
|
||||
"@typescript-eslint/types": "npm:8.29.1"
|
||||
"@typescript-eslint/typescript-estree": "npm:8.29.1"
|
||||
"@typescript-eslint/scope-manager": "npm:8.30.0"
|
||||
"@typescript-eslint/types": "npm:8.30.0"
|
||||
"@typescript-eslint/typescript-estree": "npm:8.30.0"
|
||||
peerDependencies:
|
||||
eslint: ^8.57.0 || ^9.0.0
|
||||
typescript: ">=4.8.4 <5.9.0"
|
||||
checksum: 10/1d2c85c97a39e063fe490c0cdb6513716e4735bda0bc937475b44d9224074a5070f888dfed1e75f4a91c8f4d825b44fce90b685e07bda391dfeb2553b9b39a5a
|
||||
checksum: 10/20a6134584e035f7047248d6e16c543863375a21337a3e0fc6518fa42978da9fe8f49f2c2cbb6940a3d298d2b7cb1dcd38e69cc62869728c634acd9722c14b8b
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@typescript-eslint/visitor-keys@npm:8.29.1":
|
||||
version: 8.29.1
|
||||
resolution: "@typescript-eslint/visitor-keys@npm:8.29.1"
|
||||
"@typescript-eslint/visitor-keys@npm:8.30.0":
|
||||
version: 8.30.0
|
||||
resolution: "@typescript-eslint/visitor-keys@npm:8.30.0"
|
||||
dependencies:
|
||||
"@typescript-eslint/types": "npm:8.29.1"
|
||||
"@typescript-eslint/types": "npm:8.30.0"
|
||||
eslint-visitor-keys: "npm:^4.2.0"
|
||||
checksum: 10/788290c369c13403692d857e0a464b5c2223e1fef28c717b7dbc61140d33697ce43c7d1a7cb7ac585f2012fc702ce2ec489363d34bf566303d3c48fa494803c5
|
||||
checksum: 10/b8c843bb0b07bb09a49b53574627ac81b5044cdaa5c58cdcf502528384613493132f05ae7ab82a7477fbb75a6520b4b4b2b638e45c37dadb39f51c9356036d85
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@vaadin/a11y-base@npm:~24.7.2":
|
||||
version: 24.7.2
|
||||
resolution: "@vaadin/a11y-base@npm:24.7.2"
|
||||
"@vaadin/a11y-base@npm:~24.7.3":
|
||||
version: 24.7.3
|
||||
resolution: "@vaadin/a11y-base@npm:24.7.3"
|
||||
dependencies:
|
||||
"@open-wc/dedupe-mixin": "npm:^1.3.0"
|
||||
"@polymer/polymer": "npm:^3.0.0"
|
||||
"@vaadin/component-base": "npm:~24.7.2"
|
||||
"@vaadin/component-base": "npm:~24.7.3"
|
||||
lit: "npm:^3.0.0"
|
||||
checksum: 10/f208a92ae3cdb6584b1491a995e01c5da87c6c533a49d7599f1ea451a18ad094f0d14f3b45b4070d298d435b46c42077cb474bfc08f38a45ce4b7d57ca860479
|
||||
checksum: 10/a3d2e68ea3ee48e1974c2e6691ae8d09dae0fb0f19e6180e398e3cecb5e89d8d5fb57187f30a835a3c2b98ae4dffdffab1de00ffa9228c4389de4499cbebb4a5
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@vaadin/combo-box@npm:24.7.2":
|
||||
version: 24.7.2
|
||||
resolution: "@vaadin/combo-box@npm:24.7.2"
|
||||
"@vaadin/combo-box@npm:24.7.3":
|
||||
version: 24.7.3
|
||||
resolution: "@vaadin/combo-box@npm:24.7.3"
|
||||
dependencies:
|
||||
"@open-wc/dedupe-mixin": "npm:^1.3.0"
|
||||
"@polymer/polymer": "npm:^3.0.0"
|
||||
"@vaadin/a11y-base": "npm:~24.7.2"
|
||||
"@vaadin/component-base": "npm:~24.7.2"
|
||||
"@vaadin/field-base": "npm:~24.7.2"
|
||||
"@vaadin/input-container": "npm:~24.7.2"
|
||||
"@vaadin/item": "npm:~24.7.2"
|
||||
"@vaadin/lit-renderer": "npm:~24.7.2"
|
||||
"@vaadin/overlay": "npm:~24.7.2"
|
||||
"@vaadin/vaadin-lumo-styles": "npm:~24.7.2"
|
||||
"@vaadin/vaadin-material-styles": "npm:~24.7.2"
|
||||
"@vaadin/vaadin-themable-mixin": "npm:~24.7.2"
|
||||
"@vaadin/a11y-base": "npm:~24.7.3"
|
||||
"@vaadin/component-base": "npm:~24.7.3"
|
||||
"@vaadin/field-base": "npm:~24.7.3"
|
||||
"@vaadin/input-container": "npm:~24.7.3"
|
||||
"@vaadin/item": "npm:~24.7.3"
|
||||
"@vaadin/lit-renderer": "npm:~24.7.3"
|
||||
"@vaadin/overlay": "npm:~24.7.3"
|
||||
"@vaadin/vaadin-lumo-styles": "npm:~24.7.3"
|
||||
"@vaadin/vaadin-material-styles": "npm:~24.7.3"
|
||||
"@vaadin/vaadin-themable-mixin": "npm:~24.7.3"
|
||||
lit: "npm:^3.0.0"
|
||||
checksum: 10/a5218abc97d9974e2481e7735bd8de08fe36d461a19e298dceaf66b5b2016d7c8cc9c1aa37391bf0be5c7e6e7d59797483d776acd8a87f21c1364cf1ac2e528f
|
||||
checksum: 10/fe559fac266a317b49b4a33031e9193c6e8d396592148e04ca36363bf138ed45323716d0bce0d0efe7b1a47feb008c16e0bf24a2fe4e61abd9f7d1fe6144b767
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@vaadin/component-base@npm:~24.7.2":
|
||||
version: 24.7.2
|
||||
resolution: "@vaadin/component-base@npm:24.7.2"
|
||||
"@vaadin/component-base@npm:~24.7.3":
|
||||
version: 24.7.3
|
||||
resolution: "@vaadin/component-base@npm:24.7.3"
|
||||
dependencies:
|
||||
"@open-wc/dedupe-mixin": "npm:^1.3.0"
|
||||
"@polymer/polymer": "npm:^3.0.0"
|
||||
"@vaadin/vaadin-development-mode-detector": "npm:^2.0.0"
|
||||
"@vaadin/vaadin-usage-statistics": "npm:^2.1.0"
|
||||
lit: "npm:^3.0.0"
|
||||
checksum: 10/9bdb2109e0b795fbe44b0b4848a102e064d902aeae383d25e0fc1c1ec334f9973985cb5697d9e6f1695a94de9729fedf20e058b6cbc63afe36b85cb26a7e209d
|
||||
checksum: 10/11540081b0b966a970784edaace6ac16fcc3c651439f37ec19e79440850df381c94f8118b0e447650b8ca8ba56445c30a1f4aefb8d5d009936c1b6ca707fd46c
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@vaadin/field-base@npm:~24.7.2":
|
||||
version: 24.7.2
|
||||
resolution: "@vaadin/field-base@npm:24.7.2"
|
||||
"@vaadin/field-base@npm:~24.7.3":
|
||||
version: 24.7.3
|
||||
resolution: "@vaadin/field-base@npm:24.7.3"
|
||||
dependencies:
|
||||
"@open-wc/dedupe-mixin": "npm:^1.3.0"
|
||||
"@polymer/polymer": "npm:^3.0.0"
|
||||
"@vaadin/a11y-base": "npm:~24.7.2"
|
||||
"@vaadin/component-base": "npm:~24.7.2"
|
||||
"@vaadin/a11y-base": "npm:~24.7.3"
|
||||
"@vaadin/component-base": "npm:~24.7.3"
|
||||
lit: "npm:^3.0.0"
|
||||
checksum: 10/18d72417e8cef2c345fb6098d17f8b59c7fd9e99140ee05927d793d84c0e76786041ebed321d14fe0b5861d496b9a687e60fdf3a80f77963640ea69944501970
|
||||
checksum: 10/97ef6c9eea49962b2c4526eba4dedc24de67ba2df57d469ff82e449488918165e42b5e98fa212dba82e0fe1e9623889b6a22e0db3442e045caec6cee371c12d4
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@vaadin/icon@npm:~24.7.2":
|
||||
version: 24.7.2
|
||||
resolution: "@vaadin/icon@npm:24.7.2"
|
||||
"@vaadin/icon@npm:~24.7.3":
|
||||
version: 24.7.3
|
||||
resolution: "@vaadin/icon@npm:24.7.3"
|
||||
dependencies:
|
||||
"@open-wc/dedupe-mixin": "npm:^1.3.0"
|
||||
"@polymer/polymer": "npm:^3.0.0"
|
||||
"@vaadin/component-base": "npm:~24.7.2"
|
||||
"@vaadin/vaadin-lumo-styles": "npm:~24.7.2"
|
||||
"@vaadin/vaadin-themable-mixin": "npm:~24.7.2"
|
||||
"@vaadin/component-base": "npm:~24.7.3"
|
||||
"@vaadin/vaadin-lumo-styles": "npm:~24.7.3"
|
||||
"@vaadin/vaadin-themable-mixin": "npm:~24.7.3"
|
||||
lit: "npm:^3.0.0"
|
||||
checksum: 10/7e86cfe73e4ffd24c339f7c24616885b58593a501d8b6e908123dc89623558a676512dd49546091ac511250cf24c54d5abfe1fc5e2e67fdb4d388d4a2941df29
|
||||
checksum: 10/a3da83bc9effd9eabd7382e4d4fdea1bb412c8fe09bcfc2c17db37184d856c6058e36f5f1bfab25b1c726ed5328a8af338405fc6b5b11b9aff5c6824e282173b
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@vaadin/input-container@npm:~24.7.2":
|
||||
version: 24.7.2
|
||||
resolution: "@vaadin/input-container@npm:24.7.2"
|
||||
"@vaadin/input-container@npm:~24.7.3":
|
||||
version: 24.7.3
|
||||
resolution: "@vaadin/input-container@npm:24.7.3"
|
||||
dependencies:
|
||||
"@polymer/polymer": "npm:^3.0.0"
|
||||
"@vaadin/component-base": "npm:~24.7.2"
|
||||
"@vaadin/vaadin-lumo-styles": "npm:~24.7.2"
|
||||
"@vaadin/vaadin-material-styles": "npm:~24.7.2"
|
||||
"@vaadin/vaadin-themable-mixin": "npm:~24.7.2"
|
||||
"@vaadin/component-base": "npm:~24.7.3"
|
||||
"@vaadin/vaadin-lumo-styles": "npm:~24.7.3"
|
||||
"@vaadin/vaadin-material-styles": "npm:~24.7.3"
|
||||
"@vaadin/vaadin-themable-mixin": "npm:~24.7.3"
|
||||
lit: "npm:^3.0.0"
|
||||
checksum: 10/f5f1d396ad14798c84d5db4915e3bda99c80067255e462fec8bad3a2163ec7420f7571cfca5121b6c5625c3bfe01c1ca3f17d2552689cd7dc383e04a7e93d745
|
||||
checksum: 10/61f2293927a22396480bd88f84565651146f485a5d9b73ab8a7607931220d5eccf9f87ce208feb6156e716462b4aeb3d03877f13d4229d62a3c60468686973e7
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@vaadin/item@npm:~24.7.2":
|
||||
version: 24.7.2
|
||||
resolution: "@vaadin/item@npm:24.7.2"
|
||||
"@vaadin/item@npm:~24.7.3":
|
||||
version: 24.7.3
|
||||
resolution: "@vaadin/item@npm:24.7.3"
|
||||
dependencies:
|
||||
"@open-wc/dedupe-mixin": "npm:^1.3.0"
|
||||
"@polymer/polymer": "npm:^3.0.0"
|
||||
"@vaadin/a11y-base": "npm:~24.7.2"
|
||||
"@vaadin/component-base": "npm:~24.7.2"
|
||||
"@vaadin/vaadin-lumo-styles": "npm:~24.7.2"
|
||||
"@vaadin/vaadin-material-styles": "npm:~24.7.2"
|
||||
"@vaadin/vaadin-themable-mixin": "npm:~24.7.2"
|
||||
"@vaadin/a11y-base": "npm:~24.7.3"
|
||||
"@vaadin/component-base": "npm:~24.7.3"
|
||||
"@vaadin/vaadin-lumo-styles": "npm:~24.7.3"
|
||||
"@vaadin/vaadin-material-styles": "npm:~24.7.3"
|
||||
"@vaadin/vaadin-themable-mixin": "npm:~24.7.3"
|
||||
lit: "npm:^3.0.0"
|
||||
checksum: 10/5ca44c455382cb56cd81757b51ac1bbc7c53c97807ca5868f7f1b9e9100ea274772590de55d910b16a1d6771c09d11a4b5fad5cd64b44dd0a73265f286ef16ac
|
||||
checksum: 10/bb6d7a9576d8204b20b44d6f672f650c227893869a88dc59fb27f5f5b40b21e172523990d6ff90bbdd2324cca4e62a1d711eb27a0971ea91377d94aa89cc72a6
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@vaadin/lit-renderer@npm:~24.7.2":
|
||||
version: 24.7.2
|
||||
resolution: "@vaadin/lit-renderer@npm:24.7.2"
|
||||
"@vaadin/lit-renderer@npm:~24.7.3":
|
||||
version: 24.7.3
|
||||
resolution: "@vaadin/lit-renderer@npm:24.7.3"
|
||||
dependencies:
|
||||
lit: "npm:^3.0.0"
|
||||
checksum: 10/aba4cab64a8249e2386902c5574fcec5f0205f818e7f9120494826b68f4d13dee78f916a6987bfc6ce969d37641ccec251d744dcbe6a79412c87e7e7fc6faf7f
|
||||
checksum: 10/323fb6c79c6857019db94634e218bf9cc9c7e454ea28ae83c9be0ca70b34cc6cf57e0b931167e05c6387cc76fcd69a1e2b6fce04181020c919dcfdd072d4baae
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@vaadin/overlay@npm:~24.7.2":
|
||||
version: 24.7.2
|
||||
resolution: "@vaadin/overlay@npm:24.7.2"
|
||||
"@vaadin/overlay@npm:~24.7.3":
|
||||
version: 24.7.3
|
||||
resolution: "@vaadin/overlay@npm:24.7.3"
|
||||
dependencies:
|
||||
"@open-wc/dedupe-mixin": "npm:^1.3.0"
|
||||
"@polymer/polymer": "npm:^3.0.0"
|
||||
"@vaadin/a11y-base": "npm:~24.7.2"
|
||||
"@vaadin/component-base": "npm:~24.7.2"
|
||||
"@vaadin/vaadin-lumo-styles": "npm:~24.7.2"
|
||||
"@vaadin/vaadin-material-styles": "npm:~24.7.2"
|
||||
"@vaadin/vaadin-themable-mixin": "npm:~24.7.2"
|
||||
"@vaadin/a11y-base": "npm:~24.7.3"
|
||||
"@vaadin/component-base": "npm:~24.7.3"
|
||||
"@vaadin/vaadin-lumo-styles": "npm:~24.7.3"
|
||||
"@vaadin/vaadin-material-styles": "npm:~24.7.3"
|
||||
"@vaadin/vaadin-themable-mixin": "npm:~24.7.3"
|
||||
lit: "npm:^3.0.0"
|
||||
checksum: 10/79a49e4db54a757a5d714ecb9761b912aff93abde7fd97c50af16f8e63eedbcefac5d00bc23b8e2431656befcc3258ee827191cd3ec6c179e6444a5b028c70ff
|
||||
checksum: 10/1a14d94e77ffe68c6d62d5c576b4a6049c2c180b74c9b9114eee5c77f49aacded28ef5a1452938ee7f89151f7db292978156b523ac1c727edf3a464f2eda5b30
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@ -5248,36 +5248,36 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@vaadin/vaadin-lumo-styles@npm:~24.7.2":
|
||||
version: 24.7.2
|
||||
resolution: "@vaadin/vaadin-lumo-styles@npm:24.7.2"
|
||||
"@vaadin/vaadin-lumo-styles@npm:~24.7.3":
|
||||
version: 24.7.3
|
||||
resolution: "@vaadin/vaadin-lumo-styles@npm:24.7.3"
|
||||
dependencies:
|
||||
"@polymer/polymer": "npm:^3.0.0"
|
||||
"@vaadin/component-base": "npm:~24.7.2"
|
||||
"@vaadin/icon": "npm:~24.7.2"
|
||||
"@vaadin/vaadin-themable-mixin": "npm:~24.7.2"
|
||||
checksum: 10/3c89b55c34f9b360bb52f2ed1d7716bc4caf69ca53f8643b4cf0195000b05a945e3b37476716afe9dc59e41676297a2d31380f8cfb3ea6df2f973740f06cb701
|
||||
"@vaadin/component-base": "npm:~24.7.3"
|
||||
"@vaadin/icon": "npm:~24.7.3"
|
||||
"@vaadin/vaadin-themable-mixin": "npm:~24.7.3"
|
||||
checksum: 10/8a4d14ac3ffe6109c517462e16cb72aebdd7b277c88a96eda6f85925bb9a9d8584753a33e06d64486e2c0d427c339bebaa12cd3b4fe3f9cbb8c19cd716c6c96a
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@vaadin/vaadin-material-styles@npm:~24.7.2":
|
||||
version: 24.7.2
|
||||
resolution: "@vaadin/vaadin-material-styles@npm:24.7.2"
|
||||
"@vaadin/vaadin-material-styles@npm:~24.7.3":
|
||||
version: 24.7.3
|
||||
resolution: "@vaadin/vaadin-material-styles@npm:24.7.3"
|
||||
dependencies:
|
||||
"@polymer/polymer": "npm:^3.0.0"
|
||||
"@vaadin/component-base": "npm:~24.7.2"
|
||||
"@vaadin/vaadin-themable-mixin": "npm:~24.7.2"
|
||||
checksum: 10/da94da8155b61cd60acbc8cba81e8ca83e07a5443867c1e2a8177fc938998e0d91aa689a94d104bf8f43523f64a7780c8a5f1f606447a8fca8771bc7c2beca75
|
||||
"@vaadin/component-base": "npm:~24.7.3"
|
||||
"@vaadin/vaadin-themable-mixin": "npm:~24.7.3"
|
||||
checksum: 10/554652d87b41a0090b91c337a495d88e3bca4cdc1daeb2357a413fcb581a761fcba98db00344a5c0df851cb98993758ec03f22f008e38442def4d2378562cf84
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@vaadin/vaadin-themable-mixin@npm:24.7.2, @vaadin/vaadin-themable-mixin@npm:~24.7.2":
|
||||
version: 24.7.2
|
||||
resolution: "@vaadin/vaadin-themable-mixin@npm:24.7.2"
|
||||
"@vaadin/vaadin-themable-mixin@npm:24.7.3, @vaadin/vaadin-themable-mixin@npm:~24.7.3":
|
||||
version: 24.7.3
|
||||
resolution: "@vaadin/vaadin-themable-mixin@npm:24.7.3"
|
||||
dependencies:
|
||||
"@open-wc/dedupe-mixin": "npm:^1.3.0"
|
||||
lit: "npm:^3.0.0"
|
||||
checksum: 10/239a1ac07f9b08d527391e89d31fb2a368cabf3bca0cbccde06eed86c050b732b2de6182740328f63769744cdcfcceb0cb15156770811967fb19170d5e9bf9b6
|
||||
checksum: 10/e176e162fdb374ab4aa5f948f0a4b23f7fa22f617e2fa7b6c73b15df84b57e910d16f2c54add434be30a337313d9a5874945a97c3a86d4989b308a157a8fe4ce
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@ -9455,8 +9455,8 @@ __metadata:
|
||||
"@types/tar": "npm:6.1.13"
|
||||
"@types/ua-parser-js": "npm:0.7.39"
|
||||
"@types/webspeechapi": "npm:0.0.29"
|
||||
"@vaadin/combo-box": "npm:24.7.2"
|
||||
"@vaadin/vaadin-themable-mixin": "npm:24.7.2"
|
||||
"@vaadin/combo-box": "npm:24.7.3"
|
||||
"@vaadin/vaadin-themable-mixin": "npm:24.7.3"
|
||||
"@vibrant/color": "npm:4.0.0"
|
||||
"@vitest/coverage-v8": "npm:3.1.1"
|
||||
"@vue/web-component-wrapper": "npm:1.3.0"
|
||||
@ -9540,7 +9540,7 @@ __metadata:
|
||||
tinykeys: "npm:3.0.0"
|
||||
ts-lit-plugin: "npm:2.0.2"
|
||||
typescript: "npm:5.8.3"
|
||||
typescript-eslint: "npm:8.29.1"
|
||||
typescript-eslint: "npm:8.30.0"
|
||||
ua-parser-js: "npm:2.0.3"
|
||||
vis-data: "npm:7.1.9"
|
||||
vis-network: "npm:9.1.9"
|
||||
@ -9709,8 +9709,8 @@ __metadata:
|
||||
linkType: hard
|
||||
|
||||
"http-proxy-middleware@npm:^2.0.7":
|
||||
version: 2.0.7
|
||||
resolution: "http-proxy-middleware@npm:2.0.7"
|
||||
version: 2.0.9
|
||||
resolution: "http-proxy-middleware@npm:2.0.9"
|
||||
dependencies:
|
||||
"@types/http-proxy": "npm:^1.17.8"
|
||||
http-proxy: "npm:^1.18.1"
|
||||
@ -9722,7 +9722,7 @@ __metadata:
|
||||
peerDependenciesMeta:
|
||||
"@types/express":
|
||||
optional: true
|
||||
checksum: 10/4a51bf612b752ad945701995c1c029e9501c97e7224c0cf3f8bf6d48d172d6a8f2b57c20fec469534fdcac3aa8a6f332224a33c6b0d7f387aa2cfff9b67216fd
|
||||
checksum: 10/4ece416a91d52e96f8136c5f4abfbf7ac2f39becbad21fa8b158a12d7e7d8f808287ff1ae342b903fd1f15f2249dee87fabc09e1f0e73106b83331c496d67660
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@ -14619,17 +14619,17 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"typescript-eslint@npm:8.29.1":
|
||||
version: 8.29.1
|
||||
resolution: "typescript-eslint@npm:8.29.1"
|
||||
"typescript-eslint@npm:8.30.0":
|
||||
version: 8.30.0
|
||||
resolution: "typescript-eslint@npm:8.30.0"
|
||||
dependencies:
|
||||
"@typescript-eslint/eslint-plugin": "npm:8.29.1"
|
||||
"@typescript-eslint/parser": "npm:8.29.1"
|
||||
"@typescript-eslint/utils": "npm:8.29.1"
|
||||
"@typescript-eslint/eslint-plugin": "npm:8.30.0"
|
||||
"@typescript-eslint/parser": "npm:8.30.0"
|
||||
"@typescript-eslint/utils": "npm:8.30.0"
|
||||
peerDependencies:
|
||||
eslint: ^8.57.0 || ^9.0.0
|
||||
typescript: ">=4.8.4 <5.9.0"
|
||||
checksum: 10/0073f809d04586ca09a482ac1becfdc16bc76c8a1828954de1886e0a9f3a75350fd7d65cc5f482eb1524f721dd71457496fee1c59c07de1f7f29e135db005252
|
||||
checksum: 10/e44afc64e14751684127999e149d30582f68c9bd5a09945260e7b5bacaa4c025e932c82eb23dc51a219a4d1c3715ec6b42d4363e6069c0d921c64e1c0f1472a1
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user