mirror of
https://github.com/home-assistant/frontend.git
synced 2026-02-20 08:27:59 +00:00
Compare commits
8 Commits
dialog-nex
...
dev
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2c136e00f5 | ||
|
|
6f82478598 | ||
|
|
1093bd890f | ||
|
|
456c638750 | ||
|
|
60ca50deb4 | ||
|
|
2064ab4141 | ||
|
|
d34c42e587 | ||
|
|
5da7bf6fba |
@@ -210,7 +210,7 @@
|
||||
"rspack-manifest-plugin": "5.2.1",
|
||||
"serve": "14.2.5",
|
||||
"sinon": "21.0.1",
|
||||
"tar": "7.5.7",
|
||||
"tar": "7.5.8",
|
||||
"terser-webpack-plugin": "5.3.16",
|
||||
"ts-lit-plugin": "2.0.2",
|
||||
"typescript": "5.9.3",
|
||||
|
||||
@@ -220,6 +220,7 @@ export class HaAdaptiveDialog extends LitElement {
|
||||
return [
|
||||
css`
|
||||
ha-bottom-sheet {
|
||||
--ha-bottom-sheet-border-radius: var(--ha-border-radius-2xl);
|
||||
--ha-bottom-sheet-surface-background: var(
|
||||
--ha-dialog-surface-background,
|
||||
var(--card-background-color, var(--ha-color-surface-default))
|
||||
|
||||
@@ -197,6 +197,9 @@ export class HaBottomSheet extends ScrollableFadeMixin(LitElement) {
|
||||
without-header
|
||||
@touchstart=${this._handleTouchStart}
|
||||
>
|
||||
<div class="handle-wrapper" aria-hidden="true">
|
||||
<div class="handle"></div>
|
||||
</div>
|
||||
<slot name="header"></slot>
|
||||
<div class="content-wrapper">
|
||||
<div id="body" class="body ha-scrollbar">
|
||||
@@ -372,6 +375,7 @@ export class HaBottomSheet extends ScrollableFadeMixin(LitElement) {
|
||||
wa-drawer::part(body) {
|
||||
max-width: var(--ha-bottom-sheet-max-width);
|
||||
width: 100%;
|
||||
position: relative;
|
||||
border-top-left-radius: var(
|
||||
--ha-bottom-sheet-border-radius,
|
||||
var(--ha-dialog-border-radius, var(--ha-border-radius-2xl))
|
||||
@@ -394,6 +398,35 @@ export class HaBottomSheet extends ScrollableFadeMixin(LitElement) {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
:host([prevent-scrim-close]) .handle-wrapper {
|
||||
display: none;
|
||||
}
|
||||
.handle-wrapper {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
inset-inline-start: 0;
|
||||
width: 100%;
|
||||
padding-bottom: 2px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
pointer-events: none;
|
||||
z-index: 1;
|
||||
}
|
||||
.handle-wrapper .handle {
|
||||
height: 16px;
|
||||
width: 200px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
.handle-wrapper .handle::after {
|
||||
content: "";
|
||||
border-radius: var(--ha-border-radius-md);
|
||||
height: 4px;
|
||||
background: var(--ha-bottom-sheet-handle-color, var(--divider-color));
|
||||
width: 40px;
|
||||
}
|
||||
.content-wrapper {
|
||||
position: relative;
|
||||
flex: 1;
|
||||
|
||||
@@ -194,7 +194,6 @@ export class HaGenericPicker extends PickerMixin(LitElement) {
|
||||
.image=${this.image}
|
||||
.label=${label}
|
||||
.placeholder=${this.placeholder}
|
||||
.helper=${this.helper}
|
||||
.value=${this.value}
|
||||
.valueRenderer=${this.valueRenderer}
|
||||
.required=${this.required}
|
||||
|
||||
@@ -796,7 +796,7 @@ export class HaPickerComboBox extends ScrollableFadeMixin(LitElement) {
|
||||
:host {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding-top: var(--ha-space-3);
|
||||
padding-top: var(--ha-space-4);
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
|
||||
@@ -126,6 +126,7 @@ export class HaPickerField extends PickerMixin(LitElement) {
|
||||
);
|
||||
}
|
||||
ha-combo-box-item {
|
||||
position: relative;
|
||||
background-color: var(--mdc-text-field-fill-color, whitesmoke);
|
||||
border-radius: var(--ha-border-radius-sm);
|
||||
border-end-end-radius: 0;
|
||||
|
||||
@@ -5,6 +5,7 @@ import memoizeOne from "memoize-one";
|
||||
import { fireEvent } from "../common/dom/fire_event";
|
||||
import "./ha-dropdown";
|
||||
import "./ha-dropdown-item";
|
||||
import "./ha-input-helper-text";
|
||||
import "./ha-picker-field";
|
||||
import type { HaPickerField } from "./ha-picker-field";
|
||||
import "./ha-svg-icon";
|
||||
@@ -75,7 +76,7 @@ export class HaSelect extends LitElement {
|
||||
|
||||
protected override render() {
|
||||
if (this.disabled) {
|
||||
return this._renderField();
|
||||
return html`${this._renderField()}${this._renderHelper()}`;
|
||||
}
|
||||
|
||||
return html`
|
||||
@@ -116,6 +117,7 @@ export class HaSelect extends LitElement {
|
||||
)
|
||||
: html`<slot></slot>`}
|
||||
</ha-dropdown>
|
||||
${this._renderHelper()}
|
||||
`;
|
||||
}
|
||||
|
||||
@@ -131,7 +133,6 @@ export class HaSelect extends LitElement {
|
||||
aria-label=${ifDefined(this.label)}
|
||||
@clear=${this._clearValue}
|
||||
.label=${this.label}
|
||||
.helper=${this.helper}
|
||||
.value=${valueLabel}
|
||||
.required=${this.required}
|
||||
.disabled=${this.disabled}
|
||||
@@ -144,6 +145,14 @@ export class HaSelect extends LitElement {
|
||||
`;
|
||||
}
|
||||
|
||||
private _renderHelper() {
|
||||
return this.helper
|
||||
? html`<ha-input-helper-text .disabled=${this.disabled}
|
||||
>${this.helper}</ha-input-helper-text
|
||||
>`
|
||||
: nothing;
|
||||
}
|
||||
|
||||
private _handleSelect(ev: CustomEvent<{ item: { value: string } }>) {
|
||||
ev.stopPropagation();
|
||||
const value = ev.detail.item.value;
|
||||
@@ -194,6 +203,11 @@ export class HaSelect extends LitElement {
|
||||
ha-dropdown::part(menu) {
|
||||
min-width: var(--select-menu-width);
|
||||
}
|
||||
|
||||
ha-input-helper-text {
|
||||
display: block;
|
||||
margin: var(--ha-space-2) 0 0;
|
||||
}
|
||||
`;
|
||||
}
|
||||
declare global {
|
||||
|
||||
@@ -9,10 +9,16 @@ import { css, html, LitElement } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import memoizeOne from "memoize-one";
|
||||
import { fireEvent } from "../../../common/dom/fire_event";
|
||||
import { navigate } from "../../../common/navigate";
|
||||
import { extractSearchParam } from "../../../common/url/search-params";
|
||||
import type { HassioAddonDetails } from "../../../data/hassio/addon";
|
||||
import { fetchHassioAddonInfo } from "../../../data/hassio/addon";
|
||||
import { extractApiErrorMessage } from "../../../data/hassio/common";
|
||||
import {
|
||||
addStoreRepository,
|
||||
fetchSupervisorStore,
|
||||
} from "../../../data/supervisor/store";
|
||||
import { showConfirmationDialog } from "../../../dialogs/generic/show-dialog-box";
|
||||
import "../../../layouts/hass-error-screen";
|
||||
import "../../../layouts/hass-loading-screen";
|
||||
import "../../../layouts/hass-tabs-subpage";
|
||||
@@ -39,6 +45,8 @@ class HaConfigAppDashboard extends LitElement {
|
||||
|
||||
@state() private _fromStore = false;
|
||||
|
||||
@state() private _loading = true;
|
||||
|
||||
private _computeTail = memoizeOne((route: Route) => {
|
||||
const pathParts = route.path.split("/").filter(Boolean);
|
||||
// Path is like /<slug>/info or /<slug>/config
|
||||
@@ -53,8 +61,15 @@ class HaConfigAppDashboard extends LitElement {
|
||||
|
||||
protected async firstUpdated(): Promise<void> {
|
||||
this._fromStore = extractSearchParam("store") === "true";
|
||||
await this._loadAddon();
|
||||
const repositoryUrl = extractSearchParam("repository_url");
|
||||
if (repositoryUrl) {
|
||||
navigate(`/config/app/${this.route.path.split("/")[1]}`, {
|
||||
replace: true,
|
||||
});
|
||||
}
|
||||
await this._loadAddon(repositoryUrl);
|
||||
this.addEventListener("hass-api-called", (ev) => this._apiCalled(ev));
|
||||
this._loading = false;
|
||||
}
|
||||
|
||||
protected updated(changedProperties: PropertyValues) {
|
||||
@@ -63,7 +78,7 @@ class HaConfigAppDashboard extends LitElement {
|
||||
const oldSlug = oldRoute?.path.split("/")[1];
|
||||
const newSlug = this.route.path.split("/")[1];
|
||||
|
||||
if (oldSlug !== newSlug && newSlug) {
|
||||
if (oldSlug !== newSlug && newSlug && !this._loading) {
|
||||
this._loadAddon();
|
||||
}
|
||||
}
|
||||
@@ -138,7 +153,7 @@ class HaConfigAppDashboard extends LitElement {
|
||||
`;
|
||||
}
|
||||
|
||||
private async _loadAddon(): Promise<void> {
|
||||
private async _loadAddon(repositoryUrl?: string | null): Promise<void> {
|
||||
const slug = this.route.path.split("/")[1];
|
||||
if (!slug) {
|
||||
this._error = "No addon specified";
|
||||
@@ -148,10 +163,56 @@ class HaConfigAppDashboard extends LitElement {
|
||||
try {
|
||||
this._addon = await fetchHassioAddonInfo(this.hass, slug);
|
||||
} catch (err: any) {
|
||||
this._error = `Error loading addon: ${extractApiErrorMessage(err)}`;
|
||||
if (repositoryUrl) {
|
||||
try {
|
||||
await this._handleMissingRepository(slug, repositoryUrl);
|
||||
if (this._addon) {
|
||||
// Clear error if we successfully added the repository and loaded the addon
|
||||
this._error = undefined;
|
||||
return;
|
||||
}
|
||||
} catch (addRepoErr: any) {
|
||||
this._error = extractApiErrorMessage(addRepoErr);
|
||||
return;
|
||||
}
|
||||
}
|
||||
this._error = `Error loading app: ${extractApiErrorMessage(err)}`;
|
||||
}
|
||||
}
|
||||
|
||||
private async _handleMissingRepository(
|
||||
slug: string,
|
||||
repositoryUrl: string
|
||||
): Promise<void> {
|
||||
const storeInfo = await fetchSupervisorStore(this.hass);
|
||||
if (storeInfo.repositories.some((repo) => repo.source === repositoryUrl)) {
|
||||
// Repository is already installed, addon just doesn't exist
|
||||
return;
|
||||
}
|
||||
|
||||
if (
|
||||
!(await showConfirmationDialog(this, {
|
||||
title: this.hass.localize(
|
||||
"ui.panel.config.apps.my.add_repository_title"
|
||||
),
|
||||
text: this.hass.localize(
|
||||
"ui.panel.config.apps.my.add_repository_description",
|
||||
{ repository: repositoryUrl }
|
||||
),
|
||||
confirmText: this.hass.localize("ui.common.add"),
|
||||
dismissText: this.hass.localize("ui.common.cancel"),
|
||||
}))
|
||||
) {
|
||||
this._error = this.hass.localize(
|
||||
"ui.panel.config.apps.my.error_repository_not_found"
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
await addStoreRepository(this.hass, repositoryUrl);
|
||||
this._addon = await fetchHassioAddonInfo(this.hass, slug);
|
||||
}
|
||||
|
||||
private async _apiCalled(ev): Promise<void> {
|
||||
if (!ev.detail.success) {
|
||||
return;
|
||||
|
||||
@@ -2,17 +2,26 @@ import type { CSSResultGroup, PropertyValues, TemplateResult } from "lit";
|
||||
import { css, html, LitElement, nothing } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import { isComponentLoaded } from "../../../common/config/is_component_loaded";
|
||||
import { extractSearchParam } from "../../../common/url/search-params";
|
||||
import "../../../components/ha-analytics";
|
||||
import "../../../components/ha-card";
|
||||
import "../../../components/ha-settings-row";
|
||||
import "../../../components/ha-md-list";
|
||||
import "../../../components/ha-md-list-item";
|
||||
import "../../../components/ha-spinner";
|
||||
import "../../../components/ha-switch";
|
||||
import type { HaSwitch } from "../../../components/ha-switch";
|
||||
import type { Analytics } from "../../../data/analytics";
|
||||
import {
|
||||
getAnalyticsDetails,
|
||||
setAnalyticsPreferences,
|
||||
} from "../../../data/analytics";
|
||||
import { getConfigEntries } from "../../../data/config_entries";
|
||||
import type { LabPreviewFeature } from "../../../data/labs";
|
||||
import { subscribeLabFeature } from "../../../data/labs";
|
||||
import {
|
||||
fetchZwaveDataCollectionStatus,
|
||||
setZwaveDataCollectionPreference,
|
||||
} from "../../../data/zwave_js";
|
||||
import { SubscribeMixin } from "../../../mixins/subscribe-mixin";
|
||||
import { haStyle } from "../../../resources/styles";
|
||||
import type { HomeAssistant } from "../../../types";
|
||||
@@ -28,6 +37,12 @@ class ConfigAnalytics extends SubscribeMixin(LitElement) {
|
||||
|
||||
@state() private _snapshotsLabEnabled = false;
|
||||
|
||||
@state() private _zwaveEntryId?: string;
|
||||
|
||||
@state() private _zwaveDataCollectionOptIn?: boolean;
|
||||
|
||||
@state() private _highlightedSection?: string;
|
||||
|
||||
protected render(): TemplateResult {
|
||||
const error = this._error
|
||||
? this._error
|
||||
@@ -83,25 +98,77 @@ class ConfigAnalytics extends SubscribeMixin(LitElement) {
|
||||
}
|
||||
)}
|
||||
</p>
|
||||
<ha-settings-row>
|
||||
<span slot="heading" data-for="snapshots">
|
||||
${this.hass.localize(
|
||||
`ui.panel.config.analytics.preferences.snapshots.title`
|
||||
)}
|
||||
</span>
|
||||
<span slot="description" data-for="snapshots">
|
||||
${this.hass.localize(
|
||||
`ui.panel.config.analytics.preferences.snapshots.description`
|
||||
)}
|
||||
</span>
|
||||
<ha-switch
|
||||
@change=${this._handleDeviceRowClick}
|
||||
.checked=${!!this._analyticsDetails?.preferences.snapshots}
|
||||
.disabled=${this._analyticsDetails === undefined}
|
||||
name="snapshots"
|
||||
>
|
||||
</ha-switch>
|
||||
</ha-settings-row>
|
||||
<ha-md-list>
|
||||
<ha-md-list-item>
|
||||
<span slot="headline">
|
||||
${this.hass.localize(
|
||||
`ui.panel.config.analytics.preferences.snapshots.title`
|
||||
)}
|
||||
</span>
|
||||
<span slot="supporting-text">
|
||||
${this.hass.localize(
|
||||
`ui.panel.config.analytics.preferences.snapshots.description`
|
||||
)}
|
||||
</span>
|
||||
<ha-switch
|
||||
slot="end"
|
||||
@change=${this._handleDeviceRowClick}
|
||||
.checked=${!!this._analyticsDetails?.preferences.snapshots}
|
||||
.disabled=${this._analyticsDetails === undefined}
|
||||
></ha-switch>
|
||||
</ha-md-list-item>
|
||||
</ha-md-list>
|
||||
</div>
|
||||
</ha-card>`
|
||||
: nothing}
|
||||
${this._zwaveEntryId !== undefined
|
||||
? html`<ha-card
|
||||
outlined
|
||||
data-section="zwave"
|
||||
class=${this._highlightedSection === "zwave" ? "highlighted" : ""}
|
||||
.header=${this.hass.localize(
|
||||
"ui.panel.config.zwave_js.dashboard.data_collection.title"
|
||||
)}
|
||||
>
|
||||
<div class="card-content">
|
||||
<p>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.zwave_js.dashboard.data_collection.info",
|
||||
{
|
||||
documentation_link: html`<a
|
||||
target="_blank"
|
||||
href="https://zwave-js.github.io/node-zwave-js/#/data-collection/data-collection"
|
||||
rel="noreferrer"
|
||||
>${this.hass.localize(
|
||||
"ui.panel.config.zwave_js.dashboard.data_collection.documentation_link"
|
||||
)}</a
|
||||
>`,
|
||||
}
|
||||
)}
|
||||
</p>
|
||||
<ha-md-list>
|
||||
<ha-md-list-item>
|
||||
<span slot="headline">
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.zwave_js.dashboard.data_collection.toggle_title"
|
||||
)}
|
||||
</span>
|
||||
<span slot="supporting-text">
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.zwave_js.dashboard.data_collection.toggle_description"
|
||||
)}
|
||||
</span>
|
||||
${this._zwaveDataCollectionOptIn !== undefined
|
||||
? html`
|
||||
<ha-switch
|
||||
slot="end"
|
||||
@change=${this._zwaveDataCollectionToggled}
|
||||
.checked=${this._zwaveDataCollectionOptIn === true}
|
||||
></ha-switch>
|
||||
`
|
||||
: html`<ha-spinner slot="end" size="small"></ha-spinner>`}
|
||||
</ha-md-list-item>
|
||||
</ha-md-list>
|
||||
</div>
|
||||
</ha-card>`
|
||||
: nothing}
|
||||
@@ -123,6 +190,10 @@ class ConfigAnalytics extends SubscribeMixin(LitElement) {
|
||||
|
||||
protected firstUpdated(changedProps: PropertyValues) {
|
||||
super.firstUpdated(changedProps);
|
||||
const section = extractSearchParam("section");
|
||||
if (section) {
|
||||
this._highlightedSection = section;
|
||||
}
|
||||
if (isComponentLoaded(this.hass, "analytics")) {
|
||||
this._load();
|
||||
}
|
||||
@@ -135,6 +206,47 @@ class ConfigAnalytics extends SubscribeMixin(LitElement) {
|
||||
} catch (err: any) {
|
||||
this._error = err.message || err;
|
||||
}
|
||||
this._loadZwaveDataCollection();
|
||||
}
|
||||
|
||||
private async _loadZwaveDataCollection() {
|
||||
if (!isComponentLoaded(this.hass, "zwave_js")) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
const entries = await getConfigEntries(this.hass, {
|
||||
domain: "zwave_js",
|
||||
});
|
||||
const entry = entries.find((e) => !e.disabled_by);
|
||||
if (entry) {
|
||||
this._zwaveEntryId = entry.entry_id;
|
||||
const status = await fetchZwaveDataCollectionStatus(
|
||||
this.hass,
|
||||
entry.entry_id
|
||||
);
|
||||
this._zwaveDataCollectionOptIn =
|
||||
status.opted_in === true || status.enabled === true;
|
||||
if (this._highlightedSection === "zwave") {
|
||||
this.updateComplete.then(() => {
|
||||
this._scrollToSection("zwave");
|
||||
});
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
// Z-Wave data collection status is optional
|
||||
}
|
||||
}
|
||||
|
||||
private _scrollToSection(section: string): void {
|
||||
const card = this.shadowRoot?.querySelector(
|
||||
`[data-section="${section}"]`
|
||||
) as HTMLElement;
|
||||
if (card) {
|
||||
card.scrollIntoView({ behavior: "smooth", block: "center" });
|
||||
setTimeout(() => {
|
||||
this._highlightedSection = undefined;
|
||||
}, 3000);
|
||||
}
|
||||
}
|
||||
|
||||
private async _save() {
|
||||
@@ -162,6 +274,14 @@ class ConfigAnalytics extends SubscribeMixin(LitElement) {
|
||||
this._save();
|
||||
}
|
||||
|
||||
private _zwaveDataCollectionToggled(ev: Event) {
|
||||
setZwaveDataCollectionPreference(
|
||||
this.hass,
|
||||
this._zwaveEntryId!,
|
||||
(ev.target as HTMLInputElement).checked
|
||||
);
|
||||
}
|
||||
|
||||
private _preferencesChanged(event: CustomEvent): void {
|
||||
this._analyticsDetails = {
|
||||
...this._analyticsDetails!,
|
||||
@@ -178,15 +298,38 @@ class ConfigAnalytics extends SubscribeMixin(LitElement) {
|
||||
color: var(--error-color);
|
||||
}
|
||||
|
||||
ha-settings-row {
|
||||
padding: 0;
|
||||
}
|
||||
p {
|
||||
margin-top: 0;
|
||||
}
|
||||
ha-card:not(:first-of-type) {
|
||||
margin-top: 24px;
|
||||
}
|
||||
ha-md-list {
|
||||
background: none;
|
||||
--md-list-item-leading-space: 0;
|
||||
--md-list-item-trailing-space: 0;
|
||||
}
|
||||
ha-md-list-item {
|
||||
--md-item-overflow: visible;
|
||||
}
|
||||
ha-card {
|
||||
transition: box-shadow 0.3s ease;
|
||||
}
|
||||
ha-card.highlighted {
|
||||
animation: highlight-fade 2.5s ease-out forwards;
|
||||
}
|
||||
@keyframes highlight-fade {
|
||||
0% {
|
||||
box-shadow:
|
||||
0 0 0 var(--ha-border-width-md) var(--primary-color),
|
||||
0 0 var(--ha-shadow-blur-lg) rgba(var(--rgb-primary-color), 0.4);
|
||||
}
|
||||
100% {
|
||||
box-shadow:
|
||||
0 0 0 var(--ha-border-width-md) transparent,
|
||||
0 0 0 transparent;
|
||||
}
|
||||
}
|
||||
`,
|
||||
];
|
||||
}
|
||||
|
||||
@@ -36,7 +36,7 @@ import { showQuickBar } from "../../../dialogs/quick-bar/show-dialog-quick-bar";
|
||||
import { showRestartDialog } from "../../../dialogs/restart/show-dialog-restart";
|
||||
import { showShortcutsDialog } from "../../../dialogs/shortcuts/show-shortcuts-dialog";
|
||||
import { SubscribeMixin } from "../../../mixins/subscribe-mixin";
|
||||
import { haStyle } from "../../../resources/styles";
|
||||
import { haStyle, haStyleScrollbar } from "../../../resources/styles";
|
||||
import type { HomeAssistant } from "../../../types";
|
||||
import { documentationUrl } from "../../../util/documentation-url";
|
||||
import { isMac } from "../../../util/is_mac";
|
||||
@@ -255,88 +255,90 @@ class HaConfigDashboard extends SubscribeMixin(LitElement) {
|
||||
</ha-dropdown-item>
|
||||
</ha-dropdown>
|
||||
|
||||
<ha-config-section
|
||||
.narrow=${this.narrow}
|
||||
.isWide=${this.isWide}
|
||||
full-width
|
||||
>
|
||||
${repairsIssues.length || canInstallUpdates.length
|
||||
? html`<ha-card outlined>
|
||||
${repairsIssues.length
|
||||
? html`
|
||||
<ha-config-repairs
|
||||
<div class="content ha-scrollbar">
|
||||
<ha-config-section
|
||||
.narrow=${this.narrow}
|
||||
.isWide=${this.isWide}
|
||||
full-width
|
||||
>
|
||||
${repairsIssues.length || canInstallUpdates.length
|
||||
? html`<ha-card outlined>
|
||||
${repairsIssues.length
|
||||
? html`
|
||||
<ha-config-repairs
|
||||
.hass=${this.hass}
|
||||
.narrow=${this.narrow}
|
||||
.total=${totalRepairIssues}
|
||||
.repairsIssues=${repairsIssues}
|
||||
></ha-config-repairs>
|
||||
${totalRepairIssues > repairsIssues.length
|
||||
? html`
|
||||
<ha-assist-chip
|
||||
href="/config/repairs"
|
||||
.label=${this.hass.localize(
|
||||
"ui.panel.config.repairs.more_repairs",
|
||||
{
|
||||
count:
|
||||
totalRepairIssues - repairsIssues.length,
|
||||
}
|
||||
)}
|
||||
>
|
||||
</ha-assist-chip>
|
||||
`
|
||||
: ""}
|
||||
`
|
||||
: ""}
|
||||
${repairsIssues.length && canInstallUpdates.length
|
||||
? html`<hr />`
|
||||
: ""}
|
||||
${canInstallUpdates.length
|
||||
? html`
|
||||
<ha-config-updates
|
||||
.hass=${this.hass}
|
||||
.narrow=${this.narrow}
|
||||
.total=${totalUpdates}
|
||||
.updateEntities=${canInstallUpdates}
|
||||
.isInstallable=${true}
|
||||
></ha-config-updates>
|
||||
${totalUpdates > canInstallUpdates.length
|
||||
? html`
|
||||
<ha-assist-chip
|
||||
href="/config/updates"
|
||||
label=${this.hass.localize(
|
||||
"ui.panel.config.updates.more_updates",
|
||||
{
|
||||
count:
|
||||
totalUpdates - canInstallUpdates.length,
|
||||
}
|
||||
)}
|
||||
>
|
||||
</ha-assist-chip>
|
||||
`
|
||||
: ""}
|
||||
`
|
||||
: ""}
|
||||
</ha-card>`
|
||||
: ""}
|
||||
${this._pages(
|
||||
this.cloudStatus,
|
||||
isComponentLoaded(this.hass, "cloud"),
|
||||
this.hass.auth.external?.config.hasSettingsScreen
|
||||
).map((categoryPages) =>
|
||||
categoryPages.length === 0
|
||||
? nothing
|
||||
: html`
|
||||
<ha-card outlined>
|
||||
<ha-config-navigation
|
||||
.hass=${this.hass}
|
||||
.narrow=${this.narrow}
|
||||
.total=${totalRepairIssues}
|
||||
.repairsIssues=${repairsIssues}
|
||||
></ha-config-repairs>
|
||||
${totalRepairIssues > repairsIssues.length
|
||||
? html`
|
||||
<ha-assist-chip
|
||||
href="/config/repairs"
|
||||
.label=${this.hass.localize(
|
||||
"ui.panel.config.repairs.more_repairs",
|
||||
{
|
||||
count:
|
||||
totalRepairIssues - repairsIssues.length,
|
||||
}
|
||||
)}
|
||||
>
|
||||
</ha-assist-chip>
|
||||
`
|
||||
: ""}
|
||||
`
|
||||
: ""}
|
||||
${repairsIssues.length && canInstallUpdates.length
|
||||
? html`<hr />`
|
||||
: ""}
|
||||
${canInstallUpdates.length
|
||||
? html`
|
||||
<ha-config-updates
|
||||
.hass=${this.hass}
|
||||
.narrow=${this.narrow}
|
||||
.total=${totalUpdates}
|
||||
.updateEntities=${canInstallUpdates}
|
||||
.isInstallable=${true}
|
||||
></ha-config-updates>
|
||||
${totalUpdates > canInstallUpdates.length
|
||||
? html`
|
||||
<ha-assist-chip
|
||||
href="/config/updates"
|
||||
label=${this.hass.localize(
|
||||
"ui.panel.config.updates.more_updates",
|
||||
{
|
||||
count:
|
||||
totalUpdates - canInstallUpdates.length,
|
||||
}
|
||||
)}
|
||||
>
|
||||
</ha-assist-chip>
|
||||
`
|
||||
: ""}
|
||||
`
|
||||
: ""}
|
||||
</ha-card>`
|
||||
: ""}
|
||||
${this._pages(
|
||||
this.cloudStatus,
|
||||
isComponentLoaded(this.hass, "cloud"),
|
||||
this.hass.auth.external?.config.hasSettingsScreen
|
||||
).map((categoryPages) =>
|
||||
categoryPages.length === 0
|
||||
? nothing
|
||||
: html`
|
||||
<ha-card outlined>
|
||||
<ha-config-navigation
|
||||
.hass=${this.hass}
|
||||
.narrow=${this.narrow}
|
||||
.pages=${categoryPages}
|
||||
></ha-config-navigation>
|
||||
</ha-card>
|
||||
`
|
||||
)}
|
||||
<ha-tip .hass=${this.hass}>${this._tip}</ha-tip>
|
||||
</ha-config-section>
|
||||
.pages=${categoryPages}
|
||||
></ha-config-navigation>
|
||||
</ha-card>
|
||||
`
|
||||
)}
|
||||
<ha-tip .hass=${this.hass}>${this._tip}</ha-tip>
|
||||
</ha-config-section>
|
||||
</div>
|
||||
</ha-top-app-bar-fixed>
|
||||
`;
|
||||
}
|
||||
@@ -392,7 +394,36 @@ class HaConfigDashboard extends SubscribeMixin(LitElement) {
|
||||
static get styles(): CSSResultGroup {
|
||||
return [
|
||||
haStyle,
|
||||
haStyleScrollbar,
|
||||
css`
|
||||
:host {
|
||||
display: block;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
ha-top-app-bar-fixed {
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.content {
|
||||
height: calc(
|
||||
100vh - var(--header-height, 0px) - var(
|
||||
--safe-area-inset-top,
|
||||
0px
|
||||
) - var(--safe-area-inset-bottom, 0px)
|
||||
);
|
||||
height: calc(
|
||||
100dvh - var(--header-height, 0px) - var(
|
||||
--safe-area-inset-top,
|
||||
0px
|
||||
) - var(--safe-area-inset-bottom, 0px)
|
||||
);
|
||||
padding-bottom: var(--ha-space-5);
|
||||
box-sizing: border-box;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
:host(:not([narrow])) ha-card:last-child {
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,27 +1,7 @@
|
||||
import { mdiNetwork, mdiServerNetwork, mdiTextBoxOutline } from "@mdi/js";
|
||||
import { customElement, property } from "lit/decorators";
|
||||
import type { RouterOptions } from "../../../../../layouts/hass-router-page";
|
||||
import { HassRouterPage } from "../../../../../layouts/hass-router-page";
|
||||
import type { HomeAssistant } from "../../../../../types";
|
||||
import type { PageNavigation } from "../../../../../layouts/hass-tabs-subpage";
|
||||
|
||||
export const configTabs: PageNavigation[] = [
|
||||
{
|
||||
translationKey: "ui.panel.config.zwave_js.navigation.network",
|
||||
path: `/config/zwave_js/dashboard`,
|
||||
iconPath: mdiServerNetwork,
|
||||
},
|
||||
{
|
||||
translationKey: "ui.panel.config.zwave_js.navigation.logs",
|
||||
path: `/config/zwave_js/logs`,
|
||||
iconPath: mdiTextBoxOutline,
|
||||
},
|
||||
{
|
||||
translationKey: "ui.panel.config.zwave_js.navigation.visualization",
|
||||
path: `/config/zwave_js/visualization`,
|
||||
iconPath: mdiNetwork,
|
||||
},
|
||||
];
|
||||
|
||||
@customElement("zwave_js-config-router")
|
||||
class ZWaveJSConfigRouter extends HassRouterPage {
|
||||
@@ -77,6 +57,10 @@ class ZWaveJSConfigRouter extends HassRouterPage {
|
||||
tag: "zwave_js-node-installer",
|
||||
load: () => import("./zwave_js-node-installer"),
|
||||
},
|
||||
statistics: {
|
||||
tag: "zwave_js-controller-statistics",
|
||||
load: () => import("./zwave_js-controller-statistics"),
|
||||
},
|
||||
logs: {
|
||||
tag: "zwave_js-logs",
|
||||
load: () => import("./zwave_js-logs"),
|
||||
@@ -85,6 +69,14 @@ class ZWaveJSConfigRouter extends HassRouterPage {
|
||||
tag: "zwave_js-provisioned",
|
||||
load: () => import("./zwave_js-provisioned"),
|
||||
},
|
||||
"network-info": {
|
||||
tag: "zwave_js-network-info-page",
|
||||
load: () => import("./zwave_js-network-info-page"),
|
||||
},
|
||||
options: {
|
||||
tag: "zwave_js-options-page",
|
||||
load: () => import("./zwave_js-options-page"),
|
||||
},
|
||||
visualization: {
|
||||
tag: "zwave_js-network-visualization",
|
||||
load: () => import("./zwave_js-network-visualization"),
|
||||
|
||||
@@ -0,0 +1,132 @@
|
||||
import type { UnsubscribeFunc } from "home-assistant-js-websocket";
|
||||
import type { CSSResultGroup } from "lit";
|
||||
import { css, html, LitElement, nothing } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import "../../../../../components/ha-card";
|
||||
import "../../../../../components/ha-md-list";
|
||||
import "../../../../../components/ha-md-list-item";
|
||||
import type { ZWaveJSControllerStatisticsUpdatedMessage } from "../../../../../data/zwave_js";
|
||||
import { subscribeZwaveControllerStatistics } from "../../../../../data/zwave_js";
|
||||
import "../../../../../layouts/hass-subpage";
|
||||
import { SubscribeMixin } from "../../../../../mixins/subscribe-mixin";
|
||||
import { haStyle } from "../../../../../resources/styles";
|
||||
import type { HomeAssistant, Route } from "../../../../../types";
|
||||
|
||||
@customElement("zwave_js-controller-statistics")
|
||||
class ZWaveJSControllerStatistics extends SubscribeMixin(LitElement) {
|
||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||
|
||||
@property({ attribute: false }) public route!: Route;
|
||||
|
||||
@property({ type: Boolean }) public narrow = false;
|
||||
|
||||
@property({ attribute: "is-wide", type: Boolean }) public isWide = false;
|
||||
|
||||
@property({ attribute: false }) public configEntryId!: string;
|
||||
|
||||
@state()
|
||||
private _statistics?: ZWaveJSControllerStatisticsUpdatedMessage;
|
||||
|
||||
public hassSubscribe(): (UnsubscribeFunc | Promise<UnsubscribeFunc>)[] {
|
||||
return [
|
||||
subscribeZwaveControllerStatistics(
|
||||
this.hass,
|
||||
this.configEntryId,
|
||||
(message) => {
|
||||
if (!this.hasUpdated) {
|
||||
return;
|
||||
}
|
||||
this._statistics = message;
|
||||
}
|
||||
),
|
||||
];
|
||||
}
|
||||
|
||||
protected render() {
|
||||
if (!this._statistics) {
|
||||
return nothing;
|
||||
}
|
||||
|
||||
return html`
|
||||
<hass-subpage
|
||||
.hass=${this.hass}
|
||||
.narrow=${this.narrow}
|
||||
.header=${this.hass.localize(
|
||||
"ui.panel.config.zwave_js.navigation.statistics"
|
||||
)}
|
||||
back-path="/config/zwave_js/dashboard?config_entry=${this
|
||||
.configEntryId}"
|
||||
>
|
||||
<div class="container">
|
||||
<ha-card>
|
||||
<ha-md-list>
|
||||
${this._renderStat("messages_tx")}
|
||||
${this._renderStat("messages_rx")}
|
||||
${this._renderStat("messages_dropped_tx")}
|
||||
${this._renderStat("messages_dropped_rx")}
|
||||
${this._renderStat("nak")} ${this._renderStat("can")}
|
||||
${this._renderStat("timeout_ack")}
|
||||
${this._renderStat("timeout_response")}
|
||||
${this._renderStat("timeout_callback")}
|
||||
</ha-md-list>
|
||||
</ha-card>
|
||||
</div>
|
||||
</hass-subpage>
|
||||
`;
|
||||
}
|
||||
|
||||
private _renderStat(key: string) {
|
||||
return html`
|
||||
<ha-md-list-item>
|
||||
<span slot="headline">
|
||||
${this.hass.localize(
|
||||
`ui.panel.config.zwave_js.dashboard.statistics.${key}.label`
|
||||
)}
|
||||
</span>
|
||||
<span slot="supporting-text">
|
||||
${this.hass.localize(
|
||||
`ui.panel.config.zwave_js.dashboard.statistics.${key}.tooltip`
|
||||
)}
|
||||
</span>
|
||||
<span slot="end">${this._statistics?.[key] ?? 0}</span>
|
||||
</ha-md-list-item>
|
||||
`;
|
||||
}
|
||||
|
||||
static get styles(): CSSResultGroup {
|
||||
return [
|
||||
haStyle,
|
||||
css`
|
||||
ha-card {
|
||||
margin: auto;
|
||||
margin-top: var(--ha-space-4);
|
||||
max-width: 600px;
|
||||
}
|
||||
|
||||
ha-md-list {
|
||||
background: none;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
ha-md-list-item {
|
||||
--md-item-overflow: visible;
|
||||
}
|
||||
|
||||
span[slot="end"] {
|
||||
font-size: 0.95em;
|
||||
color: var(--primary-text-color);
|
||||
}
|
||||
|
||||
.container {
|
||||
padding: var(--ha-space-2) var(--ha-space-4) var(--ha-space-4);
|
||||
}
|
||||
`,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"zwave_js-controller-statistics": ZWaveJSControllerStatistics;
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,7 @@ import type { CSSResultArray } from "lit";
|
||||
import { css, html, LitElement } from "lit";
|
||||
import { customElement, property, query, state } from "lit/decorators";
|
||||
import { capitalizeFirstLetter } from "../../../../../common/string/capitalize-first-letter";
|
||||
import "../../../../../components/ha-card";
|
||||
import "../../../../../components/ha-icon-button";
|
||||
import "../../../../../components/ha-select";
|
||||
import type { HaSelectSelectEvent } from "../../../../../components/ha-select";
|
||||
@@ -13,12 +14,11 @@ import {
|
||||
setZWaveJSLogLevel,
|
||||
subscribeZWaveJSLogs,
|
||||
} from "../../../../../data/zwave_js";
|
||||
import "../../../../../layouts/hass-tabs-subpage";
|
||||
import "../../../../../layouts/hass-subpage";
|
||||
import { SubscribeMixin } from "../../../../../mixins/subscribe-mixin";
|
||||
import { haStyle } from "../../../../../resources/styles";
|
||||
import type { HomeAssistant, Route } from "../../../../../types";
|
||||
import { fileDownload } from "../../../../../util/file_download";
|
||||
import { configTabs } from "./zwave_js-config-router";
|
||||
|
||||
@customElement("zwave_js-logs")
|
||||
class ZWaveJSLogs extends SubscribeMixin(LitElement) {
|
||||
@@ -62,11 +62,12 @@ class ZWaveJSLogs extends SubscribeMixin(LitElement) {
|
||||
|
||||
protected render() {
|
||||
return html`
|
||||
<hass-tabs-subpage
|
||||
<hass-subpage
|
||||
.hass=${this.hass}
|
||||
.narrow=${this.narrow}
|
||||
.route=${this.route}
|
||||
.tabs=${configTabs}
|
||||
.header=${this.hass.localize("ui.panel.config.zwave_js.logs.caption")}
|
||||
back-path="/config/zwave_js/dashboard?config_entry=${this
|
||||
.configEntryId}"
|
||||
>
|
||||
<div class="container">
|
||||
<ha-card>
|
||||
@@ -110,7 +111,7 @@ class ZWaveJSLogs extends SubscribeMixin(LitElement) {
|
||||
</ha-card>
|
||||
<textarea readonly></textarea>
|
||||
</div>
|
||||
</hass-tabs-subpage>
|
||||
</hass-subpage>
|
||||
`;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,133 @@
|
||||
import type { CSSResultGroup, TemplateResult } from "lit";
|
||||
import { css, html, LitElement, nothing } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import "../../../../../components/ha-card";
|
||||
import "../../../../../components/ha-md-list";
|
||||
import "../../../../../components/ha-md-list-item";
|
||||
import type { ZWaveJSNetwork } from "../../../../../data/zwave_js";
|
||||
import { fetchZwaveNetworkStatus } from "../../../../../data/zwave_js";
|
||||
import "../../../../../layouts/hass-subpage";
|
||||
import { haStyle } from "../../../../../resources/styles";
|
||||
import type { HomeAssistant, Route } from "../../../../../types";
|
||||
import { formatHomeIdAsHex } from "./functions";
|
||||
|
||||
@customElement("zwave_js-network-info-page")
|
||||
class ZWaveJSNetworkInfoPage extends LitElement {
|
||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||
|
||||
@property({ attribute: false }) public route!: Route;
|
||||
|
||||
@property({ type: Boolean }) public narrow = false;
|
||||
|
||||
@property({ attribute: "is-wide", type: Boolean }) public isWide = false;
|
||||
|
||||
@property({ attribute: false }) public configEntryId!: string;
|
||||
|
||||
@state() private _network?: ZWaveJSNetwork;
|
||||
|
||||
protected async firstUpdated() {
|
||||
if (this.hass && this.configEntryId) {
|
||||
this._network = await fetchZwaveNetworkStatus(this.hass, {
|
||||
entry_id: this.configEntryId,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
protected render(): TemplateResult {
|
||||
return html`
|
||||
<hass-subpage
|
||||
.hass=${this.hass}
|
||||
.narrow=${this.narrow}
|
||||
.header=${this.hass.localize(
|
||||
"ui.panel.config.zwave_js.dashboard.network_info_title"
|
||||
)}
|
||||
back-path="/config/zwave_js/dashboard?config_entry=${this
|
||||
.configEntryId}"
|
||||
>
|
||||
<div class="container">
|
||||
<ha-card>
|
||||
${this._network
|
||||
? html`<ha-md-list>
|
||||
<ha-md-list-item>
|
||||
<span slot="headline">
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.zwave_js.dashboard.home_id"
|
||||
)}
|
||||
</span>
|
||||
<span slot="supporting-text">
|
||||
${formatHomeIdAsHex(this._network.controller.home_id)}
|
||||
</span>
|
||||
</ha-md-list-item>
|
||||
<ha-md-list-item>
|
||||
<span slot="headline">
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.zwave_js.dashboard.driver_version"
|
||||
)}
|
||||
</span>
|
||||
<span slot="supporting-text">
|
||||
${this._network.client.driver_version}
|
||||
</span>
|
||||
</ha-md-list-item>
|
||||
<ha-md-list-item>
|
||||
<span slot="headline">
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.zwave_js.dashboard.server_version"
|
||||
)}
|
||||
</span>
|
||||
<span slot="supporting-text">
|
||||
${this._network.client.server_version}
|
||||
</span>
|
||||
</ha-md-list-item>
|
||||
<ha-md-list-item>
|
||||
<span slot="headline">
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.zwave_js.dashboard.server_url"
|
||||
)}
|
||||
</span>
|
||||
<span slot="supporting-text">
|
||||
${this._network.client.ws_server_url}
|
||||
</span>
|
||||
</ha-md-list-item>
|
||||
</ha-md-list>`
|
||||
: nothing}
|
||||
</ha-card>
|
||||
</div>
|
||||
</hass-subpage>
|
||||
`;
|
||||
}
|
||||
|
||||
static get styles(): CSSResultGroup {
|
||||
return [
|
||||
haStyle,
|
||||
css`
|
||||
.container {
|
||||
padding: var(--ha-space-2) var(--ha-space-4) var(--ha-space-4);
|
||||
}
|
||||
|
||||
ha-card {
|
||||
max-width: 600px;
|
||||
margin: auto;
|
||||
}
|
||||
|
||||
ha-md-list {
|
||||
background: none;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
ha-md-list-item {
|
||||
--md-item-overflow: visible;
|
||||
--md-list-item-supporting-text-size: var(
|
||||
--md-list-item-label-text-size,
|
||||
var(--md-sys-typescale-body-large-size, 1rem)
|
||||
);
|
||||
}
|
||||
`,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"zwave_js-network-info-page": ZWaveJSNetworkInfoPage;
|
||||
}
|
||||
}
|
||||
@@ -24,10 +24,9 @@ import {
|
||||
NodeStatus,
|
||||
subscribeZwaveNodeStatistics,
|
||||
} from "../../../../../data/zwave_js";
|
||||
import "../../../../../layouts/hass-tabs-subpage";
|
||||
import "../../../../../layouts/hass-subpage";
|
||||
import { SubscribeMixin } from "../../../../../mixins/subscribe-mixin";
|
||||
import type { HomeAssistant, Route } from "../../../../../types";
|
||||
import { configTabs } from "./zwave_js-config-router";
|
||||
|
||||
@customElement("zwave_js-network-visualization")
|
||||
export class ZWaveJSNetworkVisualization extends SubscribeMixin(LitElement) {
|
||||
@@ -72,11 +71,14 @@ export class ZWaveJSNetworkVisualization extends SubscribeMixin(LitElement) {
|
||||
|
||||
protected render() {
|
||||
return html`
|
||||
<hass-tabs-subpage
|
||||
<hass-subpage
|
||||
.hass=${this.hass}
|
||||
.narrow=${this.narrow}
|
||||
.route=${this.route}
|
||||
.tabs=${configTabs}
|
||||
.header=${this.hass.localize(
|
||||
"ui.panel.config.zwave_js.navigation.visualization"
|
||||
)}
|
||||
back-path="/config/zwave_js/dashboard?config_entry=${this
|
||||
.configEntryId}"
|
||||
>
|
||||
<ha-network-graph
|
||||
.hass=${this.hass}
|
||||
@@ -86,8 +88,8 @@ export class ZWaveJSNetworkVisualization extends SubscribeMixin(LitElement) {
|
||||
)}
|
||||
.tooltipFormatter=${this._tooltipFormatter}
|
||||
@chart-click=${this._handleChartClick}
|
||||
></ha-network-graph
|
||||
></hass-tabs-subpage>
|
||||
></ha-network-graph>
|
||||
</hass-subpage>
|
||||
`;
|
||||
}
|
||||
|
||||
|
||||
@@ -41,7 +41,7 @@ import {
|
||||
import { showConfirmationDialog } from "../../../../../dialogs/generic/show-dialog-box";
|
||||
import "../../../../../layouts/hass-error-screen";
|
||||
import "../../../../../layouts/hass-loading-screen";
|
||||
import "../../../../../layouts/hass-tabs-subpage";
|
||||
import "../../../../../layouts/hass-subpage";
|
||||
import { haStyle } from "../../../../../resources/styles";
|
||||
import type {
|
||||
HomeAssistant,
|
||||
@@ -49,7 +49,6 @@ import type {
|
||||
ValueChangedEvent,
|
||||
} from "../../../../../types";
|
||||
import "../../../ha-config-section";
|
||||
import { configTabs } from "./zwave_js-config-router";
|
||||
import "./zwave_js-custom-param";
|
||||
|
||||
const icons = {
|
||||
@@ -116,11 +115,14 @@ class ZWaveJSNodeConfig extends LitElement {
|
||||
: "";
|
||||
|
||||
return html`
|
||||
<hass-tabs-subpage
|
||||
<hass-subpage
|
||||
.hass=${this.hass}
|
||||
.narrow=${this.narrow}
|
||||
.route=${this.route}
|
||||
.tabs=${configTabs}
|
||||
.header=${this.hass.localize(
|
||||
"ui.panel.config.zwave_js.node_config.header"
|
||||
)}
|
||||
back-path="/config/zwave_js/dashboard?config_entry=${this
|
||||
.configEntryId}"
|
||||
>
|
||||
<ha-config-section
|
||||
.narrow=${this.narrow}
|
||||
@@ -226,7 +228,7 @@ class ZWaveJSNodeConfig extends LitElement {
|
||||
></zwave_js-custom-param>
|
||||
</ha-card>
|
||||
</ha-config-section>
|
||||
</hass-tabs-subpage>
|
||||
</hass-subpage>
|
||||
`;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,165 @@
|
||||
import type { CSSResultGroup } from "lit";
|
||||
import { css, html, LitElement, nothing } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import "../../../../../components/ha-button";
|
||||
import "../../../../../components/ha-card";
|
||||
import "../../../../../components/ha-md-list";
|
||||
import "../../../../../components/ha-md-list-item";
|
||||
import type {
|
||||
ZWaveJSClient,
|
||||
ZWaveJSNetwork,
|
||||
} from "../../../../../data/zwave_js";
|
||||
import {
|
||||
fetchZwaveNetworkStatus,
|
||||
InclusionState,
|
||||
} from "../../../../../data/zwave_js";
|
||||
import "../../../../../layouts/hass-subpage";
|
||||
import { haStyle } from "../../../../../resources/styles";
|
||||
import type { HomeAssistant, Route } from "../../../../../types";
|
||||
import { showZWaveJSRebuildNetworkRoutesDialog } from "./show-dialog-zwave_js-rebuild-network-routes";
|
||||
import { showZWaveJSRemoveNodeDialog } from "./show-dialog-zwave_js-remove-node";
|
||||
|
||||
@customElement("zwave_js-options-page")
|
||||
class ZWaveJSOptionsPage extends LitElement {
|
||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||
|
||||
@property({ attribute: false }) public route!: Route;
|
||||
|
||||
@property({ type: Boolean }) public narrow = false;
|
||||
|
||||
@property({ attribute: "is-wide", type: Boolean }) public isWide = false;
|
||||
|
||||
@property({ attribute: false }) public configEntryId!: string;
|
||||
|
||||
@state() private _network?: ZWaveJSNetwork;
|
||||
|
||||
@state() private _status?: ZWaveJSClient["state"];
|
||||
|
||||
protected async firstUpdated() {
|
||||
if (this.hass && this.configEntryId) {
|
||||
const network = await fetchZwaveNetworkStatus(this.hass, {
|
||||
entry_id: this.configEntryId,
|
||||
});
|
||||
this._network = network;
|
||||
this._status = network.client.state;
|
||||
}
|
||||
}
|
||||
|
||||
protected render() {
|
||||
return html`
|
||||
<hass-subpage
|
||||
.hass=${this.hass}
|
||||
.narrow=${this.narrow}
|
||||
.header=${this.hass.localize(
|
||||
"ui.panel.config.zwave_js.dashboard.options_title"
|
||||
)}
|
||||
back-path="/config/zwave_js/dashboard?config_entry=${this
|
||||
.configEntryId}"
|
||||
>
|
||||
<div class="container">
|
||||
<ha-card>
|
||||
${this._network
|
||||
? html`<ha-md-list>
|
||||
<ha-md-list-item>
|
||||
<span slot="headline">
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.zwave_js.common.rebuild_network_routes"
|
||||
)}
|
||||
</span>
|
||||
<span slot="supporting-text">
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.zwave_js.dashboard.rebuild_routes_description"
|
||||
)}
|
||||
</span>
|
||||
<ha-button
|
||||
appearance="plain"
|
||||
slot="end"
|
||||
size="small"
|
||||
@click=${this._rebuildNetworkRoutesClicked}
|
||||
.disabled=${this._status === "disconnected"}
|
||||
>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.zwave_js.dashboard.rebuild_routes_action"
|
||||
)}
|
||||
</ha-button>
|
||||
</ha-md-list-item>
|
||||
<ha-md-list-item>
|
||||
<span slot="headline">
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.zwave_js.common.remove_node"
|
||||
)}
|
||||
</span>
|
||||
<span slot="supporting-text">
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.zwave_js.dashboard.remove_node_description"
|
||||
)}
|
||||
</span>
|
||||
<ha-button
|
||||
appearance="plain"
|
||||
slot="end"
|
||||
size="small"
|
||||
@click=${this._removeNodeClicked}
|
||||
.disabled=${this._status !== "connected" ||
|
||||
(this._network?.controller.inclusion_state !==
|
||||
InclusionState.Idle &&
|
||||
this._network?.controller.inclusion_state !==
|
||||
InclusionState.SmartStart)}
|
||||
>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.zwave_js.dashboard.remove_node_action"
|
||||
)}
|
||||
</ha-button>
|
||||
</ha-md-list-item>
|
||||
</ha-md-list>`
|
||||
: nothing}
|
||||
</ha-card>
|
||||
</div>
|
||||
</hass-subpage>
|
||||
`;
|
||||
}
|
||||
|
||||
private _rebuildNetworkRoutesClicked() {
|
||||
showZWaveJSRebuildNetworkRoutesDialog(this, {
|
||||
entry_id: this.configEntryId,
|
||||
});
|
||||
}
|
||||
|
||||
private _removeNodeClicked() {
|
||||
showZWaveJSRemoveNodeDialog(this, {
|
||||
entryId: this.configEntryId,
|
||||
skipConfirmation:
|
||||
this._network?.controller.inclusion_state === InclusionState.Excluding,
|
||||
});
|
||||
}
|
||||
|
||||
static get styles(): CSSResultGroup {
|
||||
return [
|
||||
haStyle,
|
||||
css`
|
||||
.container {
|
||||
padding: var(--ha-space-2) var(--ha-space-4) var(--ha-space-4);
|
||||
}
|
||||
|
||||
ha-card {
|
||||
max-width: 600px;
|
||||
margin: auto;
|
||||
}
|
||||
|
||||
ha-md-list {
|
||||
background: none;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
ha-md-list-item {
|
||||
--md-item-overflow: visible;
|
||||
}
|
||||
`,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"zwave_js-options-page": ZWaveJSOptionsPage;
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,10 @@
|
||||
import { mdiCheckCircle, mdiCloseCircleOutline, mdiDelete } from "@mdi/js";
|
||||
import { mdiCheck, mdiDelete } from "@mdi/js";
|
||||
import { html, LitElement } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import memoizeOne from "memoize-one";
|
||||
import { computeDeviceName } from "../../../../../common/entity/compute_device_name";
|
||||
import type { DataTableColumnContainer } from "../../../../../components/data-table/ha-data-table";
|
||||
import type { DeviceRegistryEntry } from "../../../../../data/device/device_registry";
|
||||
import type { ZwaveJSProvisioningEntry } from "../../../../../data/zwave_js";
|
||||
import {
|
||||
fetchZwaveProvisioningEntries,
|
||||
@@ -14,7 +16,6 @@ import type { LocalizeFunc } from "../../../../../common/translations/localize";
|
||||
import { showConfirmationDialog } from "../../../../../dialogs/generic/show-dialog-box";
|
||||
import "../../../../../layouts/hass-tabs-subpage-data-table";
|
||||
import type { HomeAssistant, Route } from "../../../../../types";
|
||||
import { configTabs } from "./zwave_js-config-router";
|
||||
|
||||
@customElement("zwave_js-provisioned")
|
||||
class ZWaveJSProvisioned extends LitElement {
|
||||
@@ -28,15 +29,26 @@ class ZWaveJSProvisioned extends LitElement {
|
||||
|
||||
@state() private _provisioningEntries: ZwaveJSProvisioningEntry[] = [];
|
||||
|
||||
@state() private _nodeIdToDevice: Record<number, DeviceRegistryEntry> = {};
|
||||
|
||||
protected render() {
|
||||
return html`
|
||||
<hass-tabs-subpage-data-table
|
||||
.hass=${this.hass}
|
||||
.narrow=${this.narrow}
|
||||
.route=${this.route}
|
||||
.tabs=${configTabs}
|
||||
.tabs=${[
|
||||
{
|
||||
path: `/config/zwave_js/provisioned?config_entry=${this.configEntryId}`,
|
||||
name: this.hass.localize(
|
||||
"ui.panel.config.zwave_js.provisioned.caption"
|
||||
),
|
||||
},
|
||||
]}
|
||||
back-path="/config/zwave_js/dashboard?config_entry=${this
|
||||
.configEntryId}"
|
||||
.columns=${this._columns(this.hass.localize)}
|
||||
.data=${this._provisioningEntries}
|
||||
.data=${this._getData(this._provisioningEntries, this._nodeIdToDevice)}
|
||||
>
|
||||
</hass-tabs-subpage-data-table>
|
||||
`;
|
||||
@@ -46,39 +58,13 @@ class ZWaveJSProvisioned extends LitElement {
|
||||
(
|
||||
localize: LocalizeFunc
|
||||
): DataTableColumnContainer<ZwaveJSProvisioningEntry> => ({
|
||||
included: {
|
||||
showNarrow: true,
|
||||
title: localize("ui.panel.config.zwave_js.provisioned.included"),
|
||||
type: "icon",
|
||||
template: (entry) =>
|
||||
entry.nodeId
|
||||
? html`
|
||||
<ha-svg-icon
|
||||
.label=${this.hass.localize(
|
||||
"ui.panel.config.zwave_js.provisioned.included"
|
||||
)}
|
||||
.path=${mdiCheckCircle}
|
||||
></ha-svg-icon>
|
||||
`
|
||||
: html`
|
||||
<ha-svg-icon
|
||||
.label=${this.hass.localize(
|
||||
"ui.panel.config.zwave_js.provisioned.not_included"
|
||||
)}
|
||||
.path=${mdiCloseCircleOutline}
|
||||
></ha-svg-icon>
|
||||
`,
|
||||
},
|
||||
active: {
|
||||
title: localize("ui.panel.config.zwave_js.provisioned.active"),
|
||||
type: "icon",
|
||||
template: (entry) =>
|
||||
entry.status === ProvisioningEntryStatus.Active
|
||||
? html`<ha-svg-icon .path=${mdiCheckCircle}></ha-svg-icon>`
|
||||
: html`<ha-svg-icon .path=${mdiCloseCircleOutline}></ha-svg-icon>`,
|
||||
name: {
|
||||
title: localize("ui.panel.config.zwave_js.provisioned.name"),
|
||||
main: true,
|
||||
sortable: true,
|
||||
filterable: true,
|
||||
},
|
||||
dsk: {
|
||||
main: true,
|
||||
title: localize("ui.panel.config.zwave_js.provisioned.dsk"),
|
||||
sortable: true,
|
||||
filterable: true,
|
||||
@@ -101,10 +87,35 @@ class ZWaveJSProvisioned extends LitElement {
|
||||
.join(", ");
|
||||
},
|
||||
},
|
||||
included: {
|
||||
title: localize("ui.panel.config.zwave_js.provisioned.included"),
|
||||
sortable: true,
|
||||
type: "icon",
|
||||
minWidth: "120px",
|
||||
maxWidth: "120px",
|
||||
template: (entry) =>
|
||||
entry.nodeId
|
||||
? html`<ha-svg-icon .path=${mdiCheck}></ha-svg-icon>`
|
||||
: html`—`,
|
||||
},
|
||||
active: {
|
||||
title: localize("ui.panel.config.zwave_js.provisioned.active"),
|
||||
sortable: true,
|
||||
type: "icon",
|
||||
minWidth: "120px",
|
||||
maxWidth: "120px",
|
||||
template: (entry) =>
|
||||
entry.status === ProvisioningEntryStatus.Active
|
||||
? html`<ha-svg-icon .path=${mdiCheck}></ha-svg-icon>`
|
||||
: html`—`,
|
||||
},
|
||||
unprovision: {
|
||||
showNarrow: true,
|
||||
title: localize("ui.panel.config.zwave_js.provisioned.unprovision"),
|
||||
title: "",
|
||||
label: localize("ui.panel.config.zwave_js.provisioned.unprovision"),
|
||||
type: "icon-button",
|
||||
showNarrow: true,
|
||||
moveable: false,
|
||||
hideable: false,
|
||||
template: (entry) => html`
|
||||
<ha-icon-button
|
||||
.label=${this.hass.localize(
|
||||
@@ -119,12 +130,50 @@ class ZWaveJSProvisioned extends LitElement {
|
||||
})
|
||||
);
|
||||
|
||||
private _getData = memoizeOne(
|
||||
(
|
||||
entries: ZwaveJSProvisioningEntry[],
|
||||
nodeIdToDevice: Record<number, DeviceRegistryEntry>
|
||||
) =>
|
||||
entries.map((entry) => {
|
||||
const device = entry.nodeId ? nodeIdToDevice[entry.nodeId] : undefined;
|
||||
return {
|
||||
...entry,
|
||||
name: device ? computeDeviceName(device) || "—" : "—",
|
||||
};
|
||||
})
|
||||
);
|
||||
|
||||
protected firstUpdated(changedProps) {
|
||||
super.firstUpdated(changedProps);
|
||||
this._fetchData();
|
||||
}
|
||||
|
||||
private async _fetchData() {
|
||||
private _fetchData() {
|
||||
this._buildNodeIdToDeviceMap();
|
||||
this._fetchProvisioningEntries();
|
||||
}
|
||||
|
||||
private _buildNodeIdToDeviceMap() {
|
||||
const map: Record<number, DeviceRegistryEntry> = {};
|
||||
for (const device of Object.values(this.hass.devices)) {
|
||||
if (!device.config_entries.includes(this.configEntryId)) {
|
||||
continue;
|
||||
}
|
||||
for (const [domain, id] of device.identifiers) {
|
||||
if (domain === "zwave_js") {
|
||||
const nodeId = parseInt(id.split("-")[1]);
|
||||
if (!isNaN(nodeId)) {
|
||||
map[nodeId] = device;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
this._nodeIdToDevice = map;
|
||||
}
|
||||
|
||||
private async _fetchProvisioningEntries() {
|
||||
this._provisioningEntries = await fetchZwaveProvisioningEntries(
|
||||
this.hass!,
|
||||
this.configEntryId
|
||||
@@ -154,7 +203,7 @@ class ZWaveJSProvisioned extends LitElement {
|
||||
}
|
||||
|
||||
await unprovisionZwaveSmartStartNode(this.hass, this.configEntryId, dsk);
|
||||
this._fetchData();
|
||||
this._fetchProvisioningEntries();
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -17,15 +17,25 @@ import type { LovelaceGenericElementEditor } from "../../types";
|
||||
import "../conditions/ha-card-conditions-editor";
|
||||
import { configElementStyle } from "../config-elements/config-elements-style";
|
||||
import { actionConfigStruct } from "../structs/action-struct";
|
||||
import type { UiAction } from "../../components/hui-action-editor";
|
||||
import { supportedActions } from "../../components/hui-action-editor";
|
||||
|
||||
const ACTIONS: UiAction[] = [
|
||||
"navigate",
|
||||
"url",
|
||||
"perform-action",
|
||||
"assist",
|
||||
"none",
|
||||
];
|
||||
|
||||
const buttonConfigStruct = object({
|
||||
type: optional(string()),
|
||||
text: optional(string()),
|
||||
icon: optional(string()),
|
||||
color: optional(string()),
|
||||
tap_action: optional(actionConfigStruct),
|
||||
hold_action: optional(actionConfigStruct),
|
||||
double_tap_action: optional(actionConfigStruct),
|
||||
tap_action: optional(supportedActions(actionConfigStruct, ACTIONS)),
|
||||
hold_action: optional(supportedActions(actionConfigStruct, ACTIONS)),
|
||||
double_tap_action: optional(supportedActions(actionConfigStruct, ACTIONS)),
|
||||
visibility: optional(array(any())),
|
||||
});
|
||||
|
||||
@@ -78,6 +88,7 @@ export class HuiButtonHeadingBadgeEditor
|
||||
name: "tap_action",
|
||||
selector: {
|
||||
ui_action: {
|
||||
actions: ACTIONS,
|
||||
default_action: "none",
|
||||
},
|
||||
},
|
||||
@@ -91,6 +102,7 @@ export class HuiButtonHeadingBadgeEditor
|
||||
name: action,
|
||||
selector: {
|
||||
ui_action: {
|
||||
actions: ACTIONS,
|
||||
default_action: "none" as const,
|
||||
},
|
||||
},
|
||||
|
||||
@@ -1419,6 +1419,7 @@ class HUIRoot extends LitElement {
|
||||
}
|
||||
ha-tab-group-tab {
|
||||
--ha-tab-group-tab-height: var(--header-height, 56px);
|
||||
height: var(--ha-tab-group-tab-height);
|
||||
}
|
||||
.tab-bar ha-tab-group-tab {
|
||||
--ha-tab-group-tab-height: var(--tab-bar-height, 56px);
|
||||
|
||||
@@ -2614,6 +2614,11 @@
|
||||
"password": "Password"
|
||||
}
|
||||
},
|
||||
"my": {
|
||||
"add_repository_title": "Add app repository?",
|
||||
"add_repository_description": "This app requires a repository that is currently not known. Do you want to add the repository {repository}?",
|
||||
"error_repository_not_found": "The repository for this app was not found"
|
||||
},
|
||||
"panel": {
|
||||
"info": "Info",
|
||||
"documentation": "Documentation",
|
||||
@@ -6937,9 +6942,10 @@
|
||||
},
|
||||
"zwave_js": {
|
||||
"navigation": {
|
||||
"network": "Network",
|
||||
"general": "Z-Wave",
|
||||
"statistics": "Statistics",
|
||||
"logs": "Logs",
|
||||
"visualization": "Visualization"
|
||||
"visualization": "Z-Wave visualization"
|
||||
},
|
||||
"common": {
|
||||
"network": "Network",
|
||||
@@ -6948,36 +6954,64 @@
|
||||
"source": "Source",
|
||||
"back": "Back",
|
||||
"add_node": "Add device",
|
||||
"remove_node": "Remove device",
|
||||
"remove_node": "Remove foreign device",
|
||||
"remove_a_node": "Remove a device",
|
||||
"rebuild_network_routes": "Rebuild network routes",
|
||||
"rebuild_network_routes": "Discover and assign new routes",
|
||||
"in_progress_inclusion_exclusion": "Z-Wave JS is searching for devices",
|
||||
"cancel_inclusion_exclusion": "Stop searching"
|
||||
},
|
||||
"dashboard": {
|
||||
"network_card_title": "My network",
|
||||
"show_map": "Show map",
|
||||
"options_title": "Options",
|
||||
"options_description": "Manage network routes and remove devices",
|
||||
"network_info_title": "Network information",
|
||||
"network_info_description": "View driver, server, and home ID details",
|
||||
"visualization_description": "Visualize the network topology and device connections",
|
||||
"statistics_description": "View adapter and network communication statistics",
|
||||
"logs_description": "View and download Z-Wave logs",
|
||||
"analytics_title": "Analytics",
|
||||
"analytics_description": "Share anonymized telemetry and statistics with Z-Wave JS",
|
||||
"analytics_on": "On",
|
||||
"analytics_off": "Off",
|
||||
"driver_version": "Driver version",
|
||||
"server_version": "Server version",
|
||||
"home_id": "Home ID",
|
||||
"server_url": "Server URL",
|
||||
"devices": "{count} {count, plural,\n one {device}\n other {devices}\n}",
|
||||
"device_count": "{count} {count, plural,\n one {device}\n other {devices}\n}",
|
||||
"entity_count": "{count} {count, plural,\n one {entity}\n other {entities}\n}",
|
||||
"provisioned_devices": "Provisioned devices",
|
||||
"not_ready": "{count} not ready",
|
||||
"provisioned_count": "{count} {count, plural,\n one {provisioned device}\n other {provisioned devices}\n}",
|
||||
"not_included": "{count} not included",
|
||||
"devices_offline": "{count} offline",
|
||||
"rebuild_routes_description": "Rebuilding routes creates heavy traffic and may degrade performance for minutes to hours",
|
||||
"rebuild_routes_action": "Rebuild",
|
||||
"remove_node_description": "Useful when a device is stuck in another network and must be excluded before it can be included again",
|
||||
"remove_node_action": "Remove",
|
||||
"nvm_backup": {
|
||||
"title": "Backup and restore",
|
||||
"description": "Back up or restore your Z-Wave adapter's non-volatile memory (NVM). The NVM contains your network information including paired devices. It's recommended to create a backup before making any major changes to your Z-Wave network.",
|
||||
"download_backup": "Download backup",
|
||||
"download_backup_description": "Create and download a backup of your Z-Wave adapter",
|
||||
"download_action": "Download",
|
||||
"restore_backup": "Restore from backup",
|
||||
"restore_backup_description": "Restore a previously downloaded backup to your Z-Wave adapter",
|
||||
"restore_action": "Restore",
|
||||
"backup_failed": "Failed to download backup",
|
||||
"restore_complete": "Backup restored",
|
||||
"restore_failed": "Failed to restore backup",
|
||||
"creating": "Creating backup",
|
||||
"restoring": "Restoring backup",
|
||||
"migrate": "Migrate adapter"
|
||||
"migrate": "Migrate adapter",
|
||||
"migrate_description": "Move your Z-Wave network to a different adapter",
|
||||
"migrate_action": "Migrate"
|
||||
},
|
||||
"data_collection": {
|
||||
"title": "Third-party data reporting",
|
||||
"description": "Enable the reporting of anonymized telemetry and statistics to the Z-Wave JS organization. This data will be used to focus development efforts and improve the user experience. Information about the data that is collected and how it is used, including an example of the data collected, can be found in the {documentation_link}.",
|
||||
"documentation_link": "Z-Wave JS data collection documentation"
|
||||
"title": "Z-Wave JS analytics",
|
||||
"info": "Enable the reporting of anonymized telemetry and statistics to the Z-Wave JS organization. This data will be used to focus development efforts and improve the user experience. Information about the data that is collected and how it is used, including an example of the data collected, can be found in the {documentation_link}.",
|
||||
"documentation_link": "Z-Wave JS data collection documentation",
|
||||
"toggle_title": "Data reporting",
|
||||
"toggle_description": "Share anonymized telemetry and statistics with Z-Wave JS"
|
||||
},
|
||||
"statistics": {
|
||||
"title": "Adapter statistics",
|
||||
@@ -7174,9 +7208,10 @@
|
||||
"default": "Default"
|
||||
},
|
||||
"network_status": {
|
||||
"connected": "status: connected",
|
||||
"connecting": "status: connecting",
|
||||
"unknown": "status: unknown"
|
||||
"online": "Online",
|
||||
"offline": "Offline",
|
||||
"online_named": "{name} online",
|
||||
"offline_named": "{name} offline"
|
||||
},
|
||||
"add_node": {
|
||||
"title": "Add a Z-Wave device",
|
||||
@@ -7267,13 +7302,16 @@
|
||||
}
|
||||
},
|
||||
"provisioned": {
|
||||
"caption": "Provisioned devices",
|
||||
"name": "Name",
|
||||
"dsk": "DSK",
|
||||
"security_classes": "Security classes",
|
||||
"unprovision": "Unprovision",
|
||||
"included": "Included",
|
||||
"not_included": "Not included",
|
||||
"confirm_unprovision_title": "Remove device?",
|
||||
"confirm_unprovision_text": "{name} will be permanently removed from Home Assistant and your Z-Wave network.",
|
||||
"confirm_unprovision_text": "This device will be permanently removed from your Z-Wave network.",
|
||||
"confirm_unprovision_text_included": "This device will be permanently removed from Home Assistant and your Z-Wave network.",
|
||||
"active": "Active"
|
||||
},
|
||||
"security_classes": {
|
||||
@@ -7391,7 +7429,8 @@
|
||||
}
|
||||
},
|
||||
"logs": {
|
||||
"title": "Z-Wave JS logs",
|
||||
"caption": "Logs",
|
||||
"title": "Z-Wave logs",
|
||||
"log_level": "Log level",
|
||||
"subscribed_to_logs": "Subscribed to Z-Wave JS log messages…",
|
||||
"log_level_changed": "Log level changed to: {level}",
|
||||
@@ -7636,12 +7675,12 @@
|
||||
},
|
||||
"analytics": {
|
||||
"caption": "Analytics",
|
||||
"header": "Home Assistant analytics",
|
||||
"header": "Home Assistant",
|
||||
"description": "Learn how to share data to improve Home Assistant",
|
||||
"preferences": {
|
||||
"base": {
|
||||
"title": "Basic analytics",
|
||||
"description": "This includes information about your system."
|
||||
"description": "This includes information about your system"
|
||||
},
|
||||
"usage": {
|
||||
"title": "Usage",
|
||||
@@ -7649,16 +7688,16 @@
|
||||
},
|
||||
"statistics": {
|
||||
"title": "Statistical data",
|
||||
"description": "Counts containing total number of datapoints."
|
||||
"description": "Counts containing total number of datapoints"
|
||||
},
|
||||
"diagnostics": {
|
||||
"title": "Diagnostics",
|
||||
"description": "Share crash reports when unexpected errors occur."
|
||||
"description": "Share crash reports when unexpected errors occur"
|
||||
},
|
||||
"snapshots": {
|
||||
"title": "Devices",
|
||||
"description": "Generic information about your devices.",
|
||||
"header": "Device analytics",
|
||||
"description": "Generic information about your devices",
|
||||
"header": "Device database",
|
||||
"info": "Anonymously share data about your devices to help build the Open Home Foundation's device database. This free, open source resource helps users find useful information about smart home devices. Only device-specific details (like model or manufacturer) are shared — never personally identifying information (like the names you assign). Learn more about the device database and how we process your data in our {data_use_statement}, which you accept by opting in.",
|
||||
"data_use_statement": "Data Use Statement",
|
||||
"alert": {
|
||||
|
||||
10
yarn.lock
10
yarn.lock
@@ -9302,7 +9302,7 @@ __metadata:
|
||||
sortablejs: "patch:sortablejs@npm%3A1.15.6#~/.yarn/patches/sortablejs-npm-1.15.6-3235a8f83b.patch"
|
||||
stacktrace-js: "npm:2.0.2"
|
||||
superstruct: "npm:2.0.2"
|
||||
tar: "npm:7.5.7"
|
||||
tar: "npm:7.5.8"
|
||||
terser-webpack-plugin: "npm:5.3.16"
|
||||
tinykeys: "npm:3.0.0"
|
||||
ts-lit-plugin: "npm:2.0.2"
|
||||
@@ -13722,16 +13722,16 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"tar@npm:7.5.7, tar@npm:^7.5.2":
|
||||
version: 7.5.7
|
||||
resolution: "tar@npm:7.5.7"
|
||||
"tar@npm:7.5.8, tar@npm:^7.5.2":
|
||||
version: 7.5.8
|
||||
resolution: "tar@npm:7.5.8"
|
||||
dependencies:
|
||||
"@isaacs/fs-minipass": "npm:^4.0.0"
|
||||
chownr: "npm:^3.0.0"
|
||||
minipass: "npm:^7.1.2"
|
||||
minizlib: "npm:^3.1.0"
|
||||
yallist: "npm:^5.0.0"
|
||||
checksum: 10/0d6938dd32fe5c0f17c8098d92bd9889ee0ed9d11f12381b8146b6e8c87bb5aa49feec7abc42463f0597503d8e89e4c4c0b42bff1a5a38444e918b4878b7fd21
|
||||
checksum: 10/5fddc22e0fd03e73d5e9e922e71d8681f85443dee4f21403059a757e186ae4004abc9a709cdc7f4143d7d75758a2935f7306b3cc193123d46b6f786dd2b99c2a
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
|
||||
Reference in New Issue
Block a user