mirror of
https://github.com/home-assistant/frontend.git
synced 2025-07-24 09:46:36 +00:00
20230227.0 (#15625)
This commit is contained in:
commit
a8a85a2af6
@ -59,7 +59,6 @@
|
||||
"prefer-destructuring": "off",
|
||||
"no-restricted-globals": [2, "event"],
|
||||
"prefer-promise-reject-errors": "off",
|
||||
"no-unsafe-optional-chaining": "warn",
|
||||
"import/prefer-default-export": "off",
|
||||
"import/no-default-export": "off",
|
||||
"import/no-unresolved": "off",
|
||||
|
@ -134,10 +134,10 @@
|
||||
"tsparticles-preset-links": "^2.9.3",
|
||||
"unfetch": "^5.0.0",
|
||||
"vis-data": "^7.1.4",
|
||||
"vis-network": "^9.1.2",
|
||||
"vis-network": "^9.1.4",
|
||||
"vue": "^2.7.14",
|
||||
"vue2-daterange-picker": "^0.6.8",
|
||||
"weekstart": "^1.1.0",
|
||||
"weekstart": "^2.0.0",
|
||||
"workbox-cacheable-response": "^6.5.4",
|
||||
"workbox-core": "^6.5.4",
|
||||
"workbox-expiration": "^6.5.4",
|
||||
@ -188,7 +188,7 @@
|
||||
"babel-loader": "^9.1.2",
|
||||
"chai": "^4.3.7",
|
||||
"del": "^7.0.0",
|
||||
"eslint": "^8.34.0",
|
||||
"eslint": "^8.35.0",
|
||||
"eslint-config-airbnb-base": "^15.0.0",
|
||||
"eslint-config-airbnb-typescript": "^17.0.0",
|
||||
"eslint-config-prettier": "^8.6.0",
|
||||
@ -232,7 +232,7 @@
|
||||
"serve": "^11.3.2",
|
||||
"sinon": "^15.0.1",
|
||||
"source-map-url": "^0.4.1",
|
||||
"systemjs": "^6.13.0",
|
||||
"systemjs": "^6.14.0",
|
||||
"tar": "^6.1.13",
|
||||
"terser-webpack-plugin": "^5.3.6",
|
||||
"ts-lit-plugin": "^1.2.1",
|
||||
|
@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
||||
|
||||
[project]
|
||||
name = "home-assistant-frontend"
|
||||
version = "20230224.0"
|
||||
version = "20230227.0"
|
||||
license = {text = "Apache-2.0"}
|
||||
description = "The Home Assistant frontend"
|
||||
readme = "README.md"
|
||||
|
@ -1,6 +1,6 @@
|
||||
/* eslint-disable lit/prefer-static-styles */
|
||||
import { html, LitElement, TemplateResult } from "lit";
|
||||
import { css, html, LitElement, nothing } from "lit";
|
||||
import { customElement, property } from "lit/decorators";
|
||||
import { styleMap } from "lit/directives/style-map";
|
||||
import { fireEvent } from "../common/dom/fire_event";
|
||||
import type { HaFormSchema } from "../components/ha-form/types";
|
||||
import { autocompleteLoginFields } from "../data/auth";
|
||||
@ -29,35 +29,43 @@ export class HaPasswordManagerPolyfill extends LitElement {
|
||||
|
||||
@property({ attribute: false }) public boundingRect?: DOMRect;
|
||||
|
||||
private _styleElement?: HTMLStyleElement;
|
||||
|
||||
public connectedCallback() {
|
||||
super.connectedCallback();
|
||||
this._styleElement = document.createElement("style");
|
||||
this._styleElement.textContent = css`
|
||||
.password-manager-polyfill {
|
||||
position: absolute;
|
||||
opacity: 0;
|
||||
z-index: -1;
|
||||
}
|
||||
.password-manager-polyfill input {
|
||||
width: 100%;
|
||||
height: 62px;
|
||||
padding: 0;
|
||||
border: 0;
|
||||
}
|
||||
.password-manager-polyfill input[type="submit"] {
|
||||
width: 0;
|
||||
height: 0;
|
||||
}
|
||||
`.toString();
|
||||
document.head.append(this._styleElement);
|
||||
}
|
||||
|
||||
public disconnectedCallback() {
|
||||
super.disconnectedCallback();
|
||||
this._styleElement?.remove();
|
||||
delete this._styleElement;
|
||||
}
|
||||
|
||||
protected createRenderRoot() {
|
||||
// Add under document body so the element isn't placed inside any shadow roots
|
||||
return document.body;
|
||||
}
|
||||
|
||||
private get styles() {
|
||||
return `
|
||||
.password-manager-polyfill {
|
||||
position: absolute;
|
||||
top: ${this.boundingRect?.y || 148}px;
|
||||
left: calc(50% - ${(this.boundingRect?.width || 360) / 2}px);
|
||||
width: ${this.boundingRect?.width || 360}px;
|
||||
opacity: 0;
|
||||
z-index: -1;
|
||||
}
|
||||
.password-manager-polyfill input {
|
||||
width: 100%;
|
||||
height: 62px;
|
||||
padding: 0;
|
||||
border: 0;
|
||||
}
|
||||
.password-manager-polyfill input[type="submit"] {
|
||||
width: 0;
|
||||
height: 0;
|
||||
}
|
||||
`;
|
||||
}
|
||||
|
||||
protected render(): TemplateResult {
|
||||
protected render() {
|
||||
if (
|
||||
this.step &&
|
||||
this.step.type === "form" &&
|
||||
@ -67,6 +75,11 @@ export class HaPasswordManagerPolyfill extends LitElement {
|
||||
return html`
|
||||
<form
|
||||
class="password-manager-polyfill"
|
||||
style=${styleMap({
|
||||
top: `${this.boundingRect?.y || 148}px`,
|
||||
left: `calc(50% - ${(this.boundingRect?.width || 360) / 2}px)`,
|
||||
width: `${this.boundingRect?.width || 360}px`,
|
||||
})}
|
||||
aria-hidden="true"
|
||||
@submit=${this._handleSubmit}
|
||||
>
|
||||
@ -74,16 +87,13 @@ export class HaPasswordManagerPolyfill extends LitElement {
|
||||
this.render_input(input)
|
||||
)}
|
||||
<input type="submit" />
|
||||
<style>
|
||||
${this.styles}
|
||||
</style>
|
||||
</form>
|
||||
`;
|
||||
}
|
||||
return html``;
|
||||
return nothing;
|
||||
}
|
||||
|
||||
private render_input(schema: HaFormSchema): TemplateResult | string {
|
||||
private render_input(schema: HaFormSchema) {
|
||||
const inputType = schema.name.includes("password") ? "password" : "text";
|
||||
if (schema.type !== "string") {
|
||||
return "";
|
||||
|
@ -24,7 +24,6 @@ import {
|
||||
mdiDatabase,
|
||||
mdiEarHearing,
|
||||
mdiEye,
|
||||
mdiFan,
|
||||
mdiFlash,
|
||||
mdiFlower,
|
||||
mdiFormatListBulleted,
|
||||
@ -91,7 +90,6 @@ export const FIXED_DOMAIN_ICONS = {
|
||||
conversation: mdiMicrophoneMessage,
|
||||
counter: mdiCounter,
|
||||
demo: mdiHomeAssistant,
|
||||
fan: mdiFan,
|
||||
google_assistant: mdiGoogleAssistant,
|
||||
group: mdiGoogleCirclesCommunities,
|
||||
homeassistant: mdiHomeAssistant,
|
||||
|
@ -15,6 +15,8 @@ import {
|
||||
mdiCheckCircleOutline,
|
||||
mdiClock,
|
||||
mdiCloseCircleOutline,
|
||||
mdiFan,
|
||||
mdiFanOff,
|
||||
mdiGestureTapButton,
|
||||
mdiLanConnect,
|
||||
mdiLanDisconnect,
|
||||
@ -108,6 +110,9 @@ export const domainIconWithoutDefault = (
|
||||
}
|
||||
return compareState === "not_home" ? mdiAccountArrowRight : mdiAccount;
|
||||
|
||||
case "fan":
|
||||
return compareState === "off" ? mdiFanOff : mdiFan;
|
||||
|
||||
case "humidifier":
|
||||
return compareState === "off" ? mdiAirHumidifierOff : mdiAirHumidifier;
|
||||
|
||||
|
@ -332,7 +332,10 @@ class StatisticsChart extends LitElement {
|
||||
prevEndTime = end;
|
||||
};
|
||||
|
||||
const color = getGraphColorByIndex(colorIndex, this._computedStyle!);
|
||||
const color = getGraphColorByIndex(
|
||||
colorIndex,
|
||||
this._computedStyle || getComputedStyle(this)
|
||||
);
|
||||
colorIndex++;
|
||||
|
||||
const statTypes: this["statTypes"] = [];
|
||||
|
@ -44,7 +44,6 @@ export class HaBar extends LitElement {
|
||||
}
|
||||
rect:last-child {
|
||||
fill: var(--ha-bar-primary-color, var(--primary-color));
|
||||
rx: var(--ha-bar-border-radius, 4px);
|
||||
}
|
||||
svg {
|
||||
border-radius: var(--ha-bar-border-radius, 4px);
|
||||
|
@ -62,7 +62,7 @@ class HaLabelBadge extends LitElement {
|
||||
height: var(--ha-label-badge-size, 2.5em);
|
||||
line-height: var(--ha-label-badge-size, 2.5em);
|
||||
font-size: var(--ha-label-badge-font-size, 1.5em);
|
||||
border-radius: 50%;
|
||||
border-radius: var(--ha-label-badge-border-radius, 50%);
|
||||
border: 0.1em solid var(--ha-label-badge-color, var(--primary-color));
|
||||
color: var(--label-badge-text-color, rgb(76, 76, 76));
|
||||
|
||||
|
@ -30,6 +30,30 @@ export class HaListItem extends ListItemBase {
|
||||
margin-inline-end: 0px !important;
|
||||
direction: var(--direction);
|
||||
}
|
||||
:host([multiline-secondary]) {
|
||||
height: auto;
|
||||
}
|
||||
:host([multiline-secondary]) .mdc-deprecated-list-item__text {
|
||||
padding: 8px 0;
|
||||
}
|
||||
:host([multiline-secondary]) .mdc-deprecated-list-item__secondary-text {
|
||||
text-overflow: initial;
|
||||
white-space: normal;
|
||||
overflow: auto;
|
||||
display: inline-block;
|
||||
margin-top: 10px;
|
||||
}
|
||||
:host([multiline-secondary]) .mdc-deprecated-list-item__primary-text {
|
||||
margin-top: 10px;
|
||||
}
|
||||
:host([multiline-secondary])
|
||||
.mdc-deprecated-list-item__secondary-text::before {
|
||||
display: none;
|
||||
}
|
||||
:host([multiline-secondary])
|
||||
.mdc-deprecated-list-item__primary-text::before {
|
||||
display: none;
|
||||
}
|
||||
`,
|
||||
];
|
||||
}
|
||||
|
@ -10,13 +10,13 @@ export interface ThreadRouter {
|
||||
}
|
||||
|
||||
export interface ThreadDataSet {
|
||||
created;
|
||||
dataset_id;
|
||||
extended_pan_id;
|
||||
network_name: string;
|
||||
pan_id;
|
||||
created: string;
|
||||
dataset_id: string;
|
||||
preferred: boolean;
|
||||
source;
|
||||
source: string;
|
||||
network_name: string;
|
||||
extended_pan_id?: string;
|
||||
pan_id?: string;
|
||||
}
|
||||
|
||||
export interface ThreadRouterDiscoveryEvent {
|
||||
@ -61,3 +61,29 @@ export const listThreadDataSets = (
|
||||
hass.callWS({
|
||||
type: "thread/list_datasets",
|
||||
});
|
||||
|
||||
export const getThreadDataSetTLV = (
|
||||
hass: HomeAssistant,
|
||||
dataset_id: string
|
||||
): Promise<{ tlv: string }> =>
|
||||
hass.callWS({ type: "thread/get_dataset_tlv", dataset_id });
|
||||
|
||||
export const addThreadDataSet = (
|
||||
hass: HomeAssistant,
|
||||
source: string,
|
||||
tlv: string
|
||||
): Promise<void> =>
|
||||
hass.callWS({
|
||||
type: "thread/add_dataset_tlv",
|
||||
source,
|
||||
tlv,
|
||||
});
|
||||
|
||||
export const removeThreadDataSet = (
|
||||
hass: HomeAssistant,
|
||||
dataset_id: string
|
||||
): Promise<void> =>
|
||||
hass.callWS({
|
||||
type: "thread/delete_dataset",
|
||||
dataset_id,
|
||||
});
|
||||
|
@ -14,6 +14,7 @@ import {
|
||||
getStatisticMetadata,
|
||||
Statistics,
|
||||
StatisticsTypes,
|
||||
StatisticsMetaData,
|
||||
} from "../../data/recorder";
|
||||
import { HomeAssistant } from "../../types";
|
||||
import "../../components/chart/statistics-chart";
|
||||
@ -47,6 +48,8 @@ export class MoreInfoHistory extends LitElement {
|
||||
|
||||
private _error?: string;
|
||||
|
||||
private _metadata?: Record<string, StatisticsMetaData>;
|
||||
|
||||
protected render(): TemplateResult {
|
||||
if (!this.entityId) {
|
||||
return html``;
|
||||
@ -70,6 +73,7 @@ export class MoreInfoHistory extends LitElement {
|
||||
.hass=${this.hass}
|
||||
.isLoadingData=${!this._statistics}
|
||||
.statisticsData=${this._statistics}
|
||||
.metadata=${this._metadata}
|
||||
.statTypes=${statTypes}
|
||||
.names=${this._statNames}
|
||||
hideLegend
|
||||
@ -136,15 +140,33 @@ export class MoreInfoHistory extends LitElement {
|
||||
this._interval = window.setInterval(() => this._redrawGraph(), 1000 * 60);
|
||||
}
|
||||
|
||||
private async _getStatisticsMetaData(statisticIds: string[] | undefined) {
|
||||
const statsMetadataArray = await getStatisticMetadata(
|
||||
this.hass,
|
||||
statisticIds
|
||||
);
|
||||
const statisticsMetaData = {};
|
||||
statsMetadataArray.forEach((x) => {
|
||||
statisticsMetaData[x.statistic_id] = x;
|
||||
});
|
||||
return statisticsMetaData;
|
||||
}
|
||||
|
||||
private async _getStateHistory(): Promise<void> {
|
||||
if (
|
||||
isComponentLoaded(this.hass, "recorder") &&
|
||||
computeDomain(this.entityId) === "sensor"
|
||||
) {
|
||||
const metadata = await getStatisticMetadata(this.hass, [this.entityId]);
|
||||
this._statNames = { [this.entityId]: "" };
|
||||
if (metadata.length) {
|
||||
this._statistics = await fetchStatistics(
|
||||
const stateObj = this.hass.states[this.entityId];
|
||||
// If there is no state class, the integration providing the entity
|
||||
// has not opted into statistics so there is no need to check as it
|
||||
// requires another round-trip to the server.
|
||||
if (stateObj && stateObj.attributes.state_class) {
|
||||
// Fire off the metadata and fetch at the same time
|
||||
// to avoid waiting in sequence so the UI responds
|
||||
// faster.
|
||||
const _metadata = this._getStatisticsMetaData([this.entityId]);
|
||||
const _statistics = fetchStatistics(
|
||||
this.hass!,
|
||||
subHours(new Date(), 24),
|
||||
undefined,
|
||||
@ -153,7 +175,16 @@ export class MoreInfoHistory extends LitElement {
|
||||
undefined,
|
||||
statTypes
|
||||
);
|
||||
return;
|
||||
const [metadata, statistics] = await Promise.all([
|
||||
_metadata,
|
||||
_statistics,
|
||||
]);
|
||||
if (metadata && Object.keys(metadata).length) {
|
||||
this._metadata = metadata;
|
||||
this._statistics = statistics;
|
||||
this._statNames = { [this.entityId]: "" };
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!isComponentLoaded(this.hass, "history") || this._subscribed) {
|
||||
|
@ -7,6 +7,7 @@ import { fireEvent } from "../../common/dom/fire_event";
|
||||
import { shouldHandleRequestSelectedEvent } from "../../common/mwc/handle-request-selected-event";
|
||||
import "../../components/ha-circular-progress";
|
||||
import { createCloseHeading } from "../../components/ha-dialog";
|
||||
import "../../components/ha-expansion-panel";
|
||||
import "../../components/ha-list-item";
|
||||
import {
|
||||
extractApiErrorMessage,
|
||||
@ -106,6 +107,7 @@ class DialogRestart extends LitElement {
|
||||
<ha-list-item
|
||||
graphic="avatar"
|
||||
twoline
|
||||
multiline-secondary
|
||||
hasMeta
|
||||
@request-selected=${this._reload}
|
||||
>
|
||||
@ -128,6 +130,7 @@ class DialogRestart extends LitElement {
|
||||
<ha-list-item
|
||||
graphic="avatar"
|
||||
twoline
|
||||
multiline-secondary
|
||||
hasMeta
|
||||
@request-selected=${this._restart}
|
||||
>
|
||||
@ -143,57 +146,62 @@ class DialogRestart extends LitElement {
|
||||
)}
|
||||
</span>
|
||||
</ha-list-item>
|
||||
${showRebootShutdown
|
||||
? html`
|
||||
<div class="divider"></div>
|
||||
<p class="section">
|
||||
${this.hass.localize(
|
||||
"ui.dialogs.restart.advanced_options"
|
||||
)}
|
||||
</p>
|
||||
<ha-list-item
|
||||
graphic="avatar"
|
||||
twoline
|
||||
hasMeta
|
||||
@request-selected=${this._hostReboot}
|
||||
>
|
||||
<div slot="graphic" class="icon-background reboot">
|
||||
<ha-svg-icon .path=${mdiPowerCycle}></ha-svg-icon>
|
||||
</div>
|
||||
<span>
|
||||
${this.hass.localize(
|
||||
"ui.dialogs.restart.reboot.title"
|
||||
)}
|
||||
</span>
|
||||
<span slot="secondary">
|
||||
${this.hass.localize(
|
||||
"ui.dialogs.restart.reboot.description"
|
||||
)}
|
||||
</span>
|
||||
</ha-list-item>
|
||||
<ha-list-item
|
||||
graphic="avatar"
|
||||
twoline
|
||||
hasMeta
|
||||
@request-selected=${this._hostShutdown}
|
||||
>
|
||||
<div slot="graphic" class="icon-background shutdown">
|
||||
<ha-svg-icon .path=${mdiPower}></ha-svg-icon>
|
||||
</div>
|
||||
<span>
|
||||
${this.hass.localize(
|
||||
"ui.dialogs.restart.shutdown.title"
|
||||
)}
|
||||
</span>
|
||||
<span slot="secondary">
|
||||
${this.hass.localize(
|
||||
"ui.dialogs.restart.shutdown.description"
|
||||
)}
|
||||
</span>
|
||||
</ha-list-item>
|
||||
`
|
||||
: null}
|
||||
</mwc-list>
|
||||
|
||||
${showRebootShutdown
|
||||
? html`
|
||||
<ha-expansion-panel
|
||||
.header=${this.hass.localize(
|
||||
"ui.dialogs.restart.advanced_options"
|
||||
)}
|
||||
>
|
||||
<mwc-list>
|
||||
<ha-list-item
|
||||
graphic="avatar"
|
||||
twoline
|
||||
multiline-secondary
|
||||
hasMeta
|
||||
@request-selected=${this._hostReboot}
|
||||
>
|
||||
<div slot="graphic" class="icon-background reboot">
|
||||
<ha-svg-icon .path=${mdiPowerCycle}></ha-svg-icon>
|
||||
</div>
|
||||
<span>
|
||||
${this.hass.localize(
|
||||
"ui.dialogs.restart.reboot.title"
|
||||
)}
|
||||
</span>
|
||||
<span slot="secondary">
|
||||
${this.hass.localize(
|
||||
"ui.dialogs.restart.reboot.description"
|
||||
)}
|
||||
</span>
|
||||
</ha-list-item>
|
||||
<ha-list-item
|
||||
graphic="avatar"
|
||||
twoline
|
||||
multiline-secondary
|
||||
hasMeta
|
||||
@request-selected=${this._hostShutdown}
|
||||
>
|
||||
<div slot="graphic" class="icon-background shutdown">
|
||||
<ha-svg-icon .path=${mdiPower}></ha-svg-icon>
|
||||
</div>
|
||||
<span>
|
||||
${this.hass.localize(
|
||||
"ui.dialogs.restart.shutdown.title"
|
||||
)}
|
||||
</span>
|
||||
<span slot="secondary">
|
||||
${this.hass.localize(
|
||||
"ui.dialogs.restart.shutdown.description"
|
||||
)}
|
||||
</span>
|
||||
</ha-list-item>
|
||||
</mwc-list>
|
||||
</ha-expansion-panel>
|
||||
`
|
||||
: null}
|
||||
`}
|
||||
</ha-dialog>
|
||||
`;
|
||||
@ -340,6 +348,23 @@ class DialogRestart extends LitElement {
|
||||
ha-dialog {
|
||||
--dialog-content-padding: 0;
|
||||
}
|
||||
@media all and (min-width: 550px) {
|
||||
ha-dialog {
|
||||
--mdc-dialog-min-width: 500px;
|
||||
--mdc-dialog-max-width: 500px;
|
||||
}
|
||||
}
|
||||
|
||||
ha-expansion-panel {
|
||||
border-top: 1px solid var(--divider-color);
|
||||
margin-bottom: 10px;
|
||||
box-shadow: none;
|
||||
--expansion-panel-content-padding: 0;
|
||||
--expansion-panel-summary-padding: 0
|
||||
var(--mdc-list-side-padding, 20px);
|
||||
--ha-card-border-radius: 0;
|
||||
}
|
||||
|
||||
.icon-background {
|
||||
border-radius: 50%;
|
||||
color: #fff;
|
||||
|
@ -288,7 +288,8 @@ export class EntityRegistrySettings extends SubscribeMixin(LitElement) {
|
||||
}
|
||||
|
||||
private precisionLabel(precision?: number, stateValue?: string) {
|
||||
const value = stateValue ?? 0;
|
||||
const stateValueNumber = Number(stateValue);
|
||||
const value = !isNaN(stateValueNumber) ? stateValueNumber : 0;
|
||||
return formatNumber(value, this.hass.locale, {
|
||||
minimumFractionDigits: precision,
|
||||
maximumFractionDigits: precision,
|
||||
|
@ -278,7 +278,7 @@ class HaConfigHardware extends SubscribeMixin(LitElement) {
|
||||
`
|
||||
: ""}
|
||||
<div class="content">
|
||||
${boardName
|
||||
${boardName || isComponentLoaded(this.hass, "hassio")
|
||||
? html`
|
||||
<ha-card outlined>
|
||||
<div class="card-content">
|
||||
@ -293,7 +293,9 @@ class HaConfigHardware extends SubscribeMixin(LitElement) {
|
||||
: ""}
|
||||
<span class="primary-text">
|
||||
${boardName ||
|
||||
this.hass.localize("ui.panel.config.hardware.board")}
|
||||
this.hass.localize(
|
||||
"ui.panel.config.hardware.generic_hardware"
|
||||
)}
|
||||
</span>
|
||||
${boardId
|
||||
? html`
|
||||
|
@ -1,5 +1,10 @@
|
||||
import "@material/mwc-button";
|
||||
import { mdiDevices, mdiDotsVertical, mdiInformationOutline } from "@mdi/js";
|
||||
import {
|
||||
mdiDeleteOutline,
|
||||
mdiDevices,
|
||||
mdiDotsVertical,
|
||||
mdiInformationOutline,
|
||||
} from "@mdi/js";
|
||||
import { css, html, LitElement, PropertyValues, TemplateResult } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import memoizeOne from "memoize-one";
|
||||
@ -11,13 +16,19 @@ import { getSignedPath } from "../../../../../data/auth";
|
||||
import { getConfigEntryDiagnosticsDownloadUrl } from "../../../../../data/diagnostics";
|
||||
import { getOTBRInfo } from "../../../../../data/otbr";
|
||||
import {
|
||||
addThreadDataSet,
|
||||
listThreadDataSets,
|
||||
removeThreadDataSet,
|
||||
subscribeDiscoverThreadRouters,
|
||||
ThreadDataSet,
|
||||
ThreadRouter,
|
||||
} from "../../../../../data/thread";
|
||||
import { showConfigFlowDialog } from "../../../../../dialogs/config-flow/show-dialog-config-flow";
|
||||
import { showAlertDialog } from "../../../../../dialogs/generic/show-dialog-box";
|
||||
import {
|
||||
showAlertDialog,
|
||||
showConfirmationDialog,
|
||||
showPromptDialog,
|
||||
} from "../../../../../dialogs/generic/show-dialog-box";
|
||||
import "../../../../../layouts/hass-subpage";
|
||||
import { SubscribeMixin } from "../../../../../mixins/subscribe-mixin";
|
||||
import { haStyle } from "../../../../../resources/styles";
|
||||
@ -66,6 +77,11 @@ export class ThreadConfigPanel extends SubscribeMixin(LitElement) {
|
||||
)}
|
||||
</mwc-list-item>
|
||||
</a>
|
||||
<mwc-list-item @click=${this._addTLV}
|
||||
>${this.hass.localize(
|
||||
"ui.panel.config.thread.add_dataset_from_tlv"
|
||||
)}</mwc-list-item
|
||||
>
|
||||
<mwc-list-item @click=${this._addOTBR}
|
||||
>${this.hass.localize(
|
||||
"ui.panel.config.thread.add_open_thread_border_router"
|
||||
@ -108,11 +124,20 @@ export class ThreadConfigPanel extends SubscribeMixin(LitElement) {
|
||||
return html`<ha-card>
|
||||
<div class="card-header">
|
||||
${network.name}${network.dataset
|
||||
? html`<ha-icon-button
|
||||
.networkDataset=${network.dataset}
|
||||
.path=${mdiInformationOutline}
|
||||
@click=${this._showDatasetInfo}
|
||||
></ha-icon-button>`
|
||||
? html`<div>
|
||||
<ha-icon-button
|
||||
.networkDataset=${network.dataset}
|
||||
.path=${mdiInformationOutline}
|
||||
@click=${this._showDatasetInfo}
|
||||
></ha-icon-button
|
||||
>${!network.dataset.preferred && !network.routers?.length
|
||||
? html`<ha-icon-button
|
||||
.networkDataset=${network.dataset}
|
||||
.path=${mdiDeleteOutline}
|
||||
@click=${this._removeDataset}
|
||||
></ha-icon-button>`
|
||||
: ""}
|
||||
</div>`
|
||||
: ""}
|
||||
</div>
|
||||
${network.routers?.length
|
||||
@ -154,7 +179,10 @@ export class ThreadConfigPanel extends SubscribeMixin(LitElement) {
|
||||
const dataset = (ev.currentTarget as any).networkDataset as ThreadDataSet;
|
||||
if (isComponentLoaded(this.hass, "otbr")) {
|
||||
const otbrInfo = await getOTBRInfo(this.hass);
|
||||
if (otbrInfo.active_dataset_tlvs.includes(dataset.extended_pan_id)) {
|
||||
if (
|
||||
dataset.extended_pan_id &&
|
||||
otbrInfo.active_dataset_tlvs?.includes(dataset.extended_pan_id)
|
||||
) {
|
||||
showAlertDialog(this, {
|
||||
title: dataset.network_name,
|
||||
text: html`Network name: ${dataset.network_name}<br />
|
||||
@ -267,6 +295,57 @@ export class ThreadConfigPanel extends SubscribeMixin(LitElement) {
|
||||
});
|
||||
}
|
||||
|
||||
private async _addTLV() {
|
||||
const tlv = await showPromptDialog(this, {
|
||||
title: this.hass.localize("ui.panel.config.thread.add_dataset"),
|
||||
inputLabel: this.hass.localize(
|
||||
"ui.panel.config.thread.add_dataset_label"
|
||||
),
|
||||
confirmText: this.hass.localize(
|
||||
"ui.panel.config.thread.add_dataset_button"
|
||||
),
|
||||
});
|
||||
if (!tlv) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
await addThreadDataSet(this.hass, "manual", tlv);
|
||||
} catch (err: any) {
|
||||
showAlertDialog(this, {
|
||||
title: "Error",
|
||||
text: err.message || err,
|
||||
});
|
||||
}
|
||||
this._refresh();
|
||||
}
|
||||
|
||||
private async _removeDataset(ev: Event) {
|
||||
const dataset = (ev.currentTarget as any).networkDataset as ThreadDataSet;
|
||||
const confirm = await showConfirmationDialog(this, {
|
||||
title: this.hass.localize(
|
||||
"ui.panel.config.thread.confirm_delete_dataset",
|
||||
{ name: dataset.network_name }
|
||||
),
|
||||
text: this.hass.localize(
|
||||
"ui.panel.config.thread.confirm_delete_dataset_text"
|
||||
),
|
||||
destructive: true,
|
||||
confirmText: this.hass.localize("ui.common.delete"),
|
||||
});
|
||||
if (!confirm) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
await removeThreadDataSet(this.hass, dataset.dataset_id);
|
||||
} catch (err: any) {
|
||||
showAlertDialog(this, {
|
||||
title: "Error",
|
||||
text: err.message || err,
|
||||
});
|
||||
}
|
||||
this._refresh();
|
||||
}
|
||||
|
||||
static styles = [
|
||||
haStyle,
|
||||
css`
|
||||
|
@ -72,7 +72,7 @@ class DialogZHAReconfigureDevice extends LitElement {
|
||||
this.hass,
|
||||
this.hass.localize(`ui.dialogs.zha_reconfigure_device.heading`) +
|
||||
": " +
|
||||
(this._params?.device.user_given_name || this._params?.device.name)
|
||||
(this._params.device.user_given_name || this._params.device.name)
|
||||
)}
|
||||
>
|
||||
${!this._status
|
||||
|
@ -183,7 +183,7 @@ class HuiGaugeCard extends LitElement implements LovelaceCard {
|
||||
// new format
|
||||
let segments = this._config!.segments;
|
||||
if (segments) {
|
||||
segments = [...segments].sort((a, b) => a?.from - b?.from);
|
||||
segments = [...segments].sort((a, b) => a.from - b.from);
|
||||
|
||||
for (let i = 0; i < segments.length; i++) {
|
||||
const segment = segments[i];
|
||||
|
@ -1038,9 +1038,9 @@
|
||||
"restart": {
|
||||
"title": "Restart Home Assistant",
|
||||
"description": "Interrupts all running automations and scripts.",
|
||||
"confirm_title": "Restart?",
|
||||
"confirm_title": "Restart Home Assistant?",
|
||||
"confirm_description": "This will interrupt all running automations and scripts.",
|
||||
"confirm_action": "Restart Home Assistant",
|
||||
"confirm_action": "Restart",
|
||||
"failed": "Failed to restart Home Assistant"
|
||||
},
|
||||
"reboot": {
|
||||
@ -1801,7 +1801,7 @@
|
||||
"reboot_moved_link": "Go to system page.",
|
||||
"processor": "Processor",
|
||||
"memory": "Memory",
|
||||
"board": "Board",
|
||||
"generic_hardware": "Generic Hardware",
|
||||
"documentation": "Documentation",
|
||||
"configure": "Configure",
|
||||
"documentation_description": "Find extra information about your device",
|
||||
@ -3288,6 +3288,12 @@
|
||||
"my_network": "My network",
|
||||
"no_preferred_network": "You don't have a preferred network yet.",
|
||||
"add_open_thread_border_router": "Add an OpenThread border router",
|
||||
"add_dataset_from_tlv": "Add dataset from TLV",
|
||||
"add_dataset": "Add Thread dataset",
|
||||
"add_dataset_label": "Operational dataset TLV",
|
||||
"add_dataset_button": "Add dataset",
|
||||
"confirm_delete_dataset": "Delete {name} dataset?",
|
||||
"confirm_delete_dataset_text": "This network will be removed from Home Assistant.",
|
||||
"no_border_routers": "No border routers found",
|
||||
"border_routers": "{count} border {count, plural,\n one {router}\n other {routers}\n}",
|
||||
"managed_by_home_assistant": "Managed by Home Assistant",
|
||||
|
70
yarn.lock
70
yarn.lock
@ -1457,9 +1457,9 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@eslint/eslintrc@npm:^1.4.1":
|
||||
version: 1.4.1
|
||||
resolution: "@eslint/eslintrc@npm:1.4.1"
|
||||
"@eslint/eslintrc@npm:^2.0.0":
|
||||
version: 2.0.0
|
||||
resolution: "@eslint/eslintrc@npm:2.0.0"
|
||||
dependencies:
|
||||
ajv: ^6.12.4
|
||||
debug: ^4.3.2
|
||||
@ -1470,7 +1470,14 @@ __metadata:
|
||||
js-yaml: ^4.1.0
|
||||
minimatch: ^3.1.2
|
||||
strip-json-comments: ^3.1.1
|
||||
checksum: cd3e5a8683db604739938b1c1c8b77927dc04fce3e28e0c88e7f2cd4900b89466baf83dfbad76b2b9e4d2746abdd00dd3f9da544d3e311633d8693f327d04cd7
|
||||
checksum: 31119c8ca06723d80384f18f5c78e0530d8e6306ad36379868650131a8b10dd7cffd7aff79a5deb3a2e9933660823052623d268532bae9538ded53d5b19a69a6
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@eslint/js@npm:8.35.0":
|
||||
version: 8.35.0
|
||||
resolution: "@eslint/js@npm:8.35.0"
|
||||
checksum: 6687ceff659a6d617e37823f809dc9c4b096535961a81acead27d26b1a51a4cf608a5e59d831ddd57f24f6f8bb99340a4a0e19f9c99b390fbb4b275f51ed5f5e
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@ -8073,11 +8080,12 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"eslint@npm:^8.34.0":
|
||||
version: 8.34.0
|
||||
resolution: "eslint@npm:8.34.0"
|
||||
"eslint@npm:^8.35.0":
|
||||
version: 8.35.0
|
||||
resolution: "eslint@npm:8.35.0"
|
||||
dependencies:
|
||||
"@eslint/eslintrc": ^1.4.1
|
||||
"@eslint/eslintrc": ^2.0.0
|
||||
"@eslint/js": 8.35.0
|
||||
"@humanwhocodes/config-array": ^0.11.8
|
||||
"@humanwhocodes/module-importer": ^1.0.1
|
||||
"@nodelib/fs.walk": ^1.2.8
|
||||
@ -8091,7 +8099,7 @@ __metadata:
|
||||
eslint-utils: ^3.0.0
|
||||
eslint-visitor-keys: ^3.3.0
|
||||
espree: ^9.4.0
|
||||
esquery: ^1.4.0
|
||||
esquery: ^1.4.2
|
||||
esutils: ^2.0.2
|
||||
fast-deep-equal: ^3.1.3
|
||||
file-entry-cache: ^6.0.1
|
||||
@ -8118,7 +8126,7 @@ __metadata:
|
||||
text-table: ^0.2.0
|
||||
bin:
|
||||
eslint: bin/eslint.js
|
||||
checksum: 4e13e9eb05ac2248efbb6acae0b2325091235d5c47ba91a4775c7d6760778cbcd358a773ebd42f4629d2ad89e27c02f5d66eb1f737d75d9f5fc411454f83b2e5
|
||||
checksum: 6212173691d90b1bc94dd3d640e1f210374b30c3905fc0a15e501cf71c6ca52aa3d80ea7a9a245adaaed26d6019169e01fb6881b3f2885b188d37069c749308c
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@ -8143,12 +8151,12 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"esquery@npm:^1.4.0":
|
||||
version: 1.4.0
|
||||
resolution: "esquery@npm:1.4.0"
|
||||
"esquery@npm:^1.4.2":
|
||||
version: 1.4.2
|
||||
resolution: "esquery@npm:1.4.2"
|
||||
dependencies:
|
||||
estraverse: ^5.1.0
|
||||
checksum: a0807e17abd7fbe5fbd4fab673038d6d8a50675cdae6b04fbaa520c34581be0c5fa24582990e8acd8854f671dd291c78bb2efb9e0ed5b62f33bac4f9cf820210
|
||||
checksum: 2f4ad89c5aafaca61cc2c15e256190f0d6deb4791cae6552d3cb4b1eb8867958cdf27a56aaa3272ff17435e3eaa19ee0d4129fac336ca6373d7354d7b5da7966
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@ -9625,7 +9633,7 @@ fsevents@~2.3.2:
|
||||
deep-clone-simple: ^1.1.1
|
||||
deep-freeze: ^0.0.1
|
||||
del: ^7.0.0
|
||||
eslint: ^8.34.0
|
||||
eslint: ^8.35.0
|
||||
eslint-config-airbnb-base: ^15.0.0
|
||||
eslint-config-airbnb-typescript: ^17.0.0
|
||||
eslint-config-prettier: ^8.6.0
|
||||
@ -9692,7 +9700,7 @@ fsevents@~2.3.2:
|
||||
sortablejs: ^1.15.0
|
||||
source-map-url: ^0.4.1
|
||||
superstruct: ^1.0.3
|
||||
systemjs: ^6.13.0
|
||||
systemjs: ^6.14.0
|
||||
tar: ^6.1.13
|
||||
terser-webpack-plugin: ^5.3.6
|
||||
tinykeys: ^1.4.0
|
||||
@ -9704,7 +9712,7 @@ fsevents@~2.3.2:
|
||||
vinyl-buffer: ^1.0.1
|
||||
vinyl-source-stream: ^2.0.0
|
||||
vis-data: ^7.1.4
|
||||
vis-network: ^9.1.2
|
||||
vis-network: ^9.1.4
|
||||
vue: ^2.7.14
|
||||
vue2-daterange-picker: ^0.6.8
|
||||
webpack: =5.72.1
|
||||
@ -9712,7 +9720,7 @@ fsevents@~2.3.2:
|
||||
webpack-dev-server: ^4.11.1
|
||||
webpack-manifest-plugin: ^5.0.0
|
||||
webpackbar: ^5.0.2
|
||||
weekstart: ^1.1.0
|
||||
weekstart: ^2.0.0
|
||||
workbox-build: ^6.5.4
|
||||
workbox-cacheable-response: ^6.5.4
|
||||
workbox-core: ^6.5.4
|
||||
@ -15048,10 +15056,10 @@ fsevents@~2.3.2:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"systemjs@npm:^6.13.0":
|
||||
version: 6.13.0
|
||||
resolution: "systemjs@npm:6.13.0"
|
||||
checksum: df8d7374249778291f3a85278fdb3e1b9d81ac07767b0a7f9edeca0ee45d847c19bceb01522c817605e2908d32a4fcfed6bacd707bfb7bd577774ab900d3707d
|
||||
"systemjs@npm:^6.14.0":
|
||||
version: 6.14.0
|
||||
resolution: "systemjs@npm:6.14.0"
|
||||
checksum: df82c38a5f23012dfee693e97dacfc48eef787a80cc41bc3bd594567ba9441c6392978841c6555d88df7451a2e22c8df9fcbf9c88c78f9f9b9027393ac38d84b
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@ -16065,18 +16073,18 @@ typescript@^3.8.3:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"vis-network@npm:^9.1.2":
|
||||
version: 9.1.2
|
||||
resolution: "vis-network@npm:9.1.2"
|
||||
"vis-network@npm:^9.1.4":
|
||||
version: 9.1.4
|
||||
resolution: "vis-network@npm:9.1.4"
|
||||
peerDependencies:
|
||||
"@egjs/hammerjs": ^2.0.0
|
||||
component-emitter: ^1.3.0
|
||||
keycharm: ^0.2.0 || ^0.3.0 || ^0.4.0
|
||||
timsort: ^0.3.0
|
||||
uuid: ^3.4.0 || ^7.0.0 || ^8.0.0
|
||||
uuid: ^3.4.0 || ^7.0.0 || ^8.0.0 || ^9.0.0
|
||||
vis-data: ^7.0.0
|
||||
vis-util: ^5.0.1
|
||||
checksum: 763bee104c9d69f6b229d10675f712f824568f28dd13540d35d0f1f1fc58fcb9dc27cc12da80f6e874076a7cb75793d168b49c3c7783f8cb06f57931d9edfe0e
|
||||
checksum: 1c3ce02d251bf04ce3284301d597ee2630ad2a76de6c168c78df4dccf8ec4f4d058d67a1d78d507b763ff909d04d0d1afadcb3c3daf664e5090720b25a00aa2f
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@ -16419,10 +16427,10 @@ typescript@^3.8.3:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"weekstart@npm:^1.1.0":
|
||||
version: 1.1.0
|
||||
resolution: "weekstart@npm:1.1.0"
|
||||
checksum: afce96e0b95809a30f00fa02b13a0927324d9f76b9c10ce6b3de9bbd5926615156f8a0526c63e2bd1cabdc8ec3da68b8df8d6608b6364ded11b5da300a8cfcb4
|
||||
"weekstart@npm:^2.0.0":
|
||||
version: 2.0.0
|
||||
resolution: "weekstart@npm:2.0.0"
|
||||
checksum: 9a27d7fe30d847997d50006c814e68ad9b105858a43029b2312bf5757dd49839865a64ad28345d2dcb1b9f25b72f0de81e23d8db50362890b253486defff69e6
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user