mirror of
https://github.com/home-assistant/frontend.git
synced 2025-04-30 16:27:21 +00:00
20220203.0 (#11533)
* Only upload wheels to PyPI (#11514) * Make sure we load data in update card (#11516) * Guard load diagnostics (#11518) * Design home - Fix GitHub Links (#11519) * Add filtering to system log card and error log card (#11166) Co-authored-by: Bram Kragten <mail@bramkragten.nl> * Handle unknown toggle state (#11522) * Fix dialog heading aria label (#11524) Co-authored-by: Zack Barett <arnett.zackary@gmail.com> * Use css to hide hint in quickbar (#11527) * Revert "Mobile click accessibility" (#11526) * Clear old src when disconnected so we can't fetch it with the wrong t… (#11528) * Add name of integration to diagnostics when more than 1 (#11523) * Bumped version to 20220203.0 Co-authored-by: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Co-authored-by: Joakim Sørensen <joasoe@gmail.com> Co-authored-by: Paulus Schoutsen <balloob@gmail.com> Co-authored-by: fpro1212 <75439345+fpro1212@users.noreply.github.com> Co-authored-by: Kuba Wolanin <hi@kubawolanin.com> Co-authored-by: Zack Barett <arnett.zackary@gmail.com>
This commit is contained in:
commit
51938fb51f
@ -17,7 +17,7 @@ We want to make it as easy for designers to contribute as it is for developers.
|
|||||||
|
|
||||||
- Meet us at <a href="https://discord.gg/BPBc8rZ9" rel="noopener noreferrer" target="_blank">devs_ux Discord</a>. Feel free to share your designs, user test or strategic ideas.
|
- Meet us at <a href="https://discord.gg/BPBc8rZ9" rel="noopener noreferrer" target="_blank">devs_ux Discord</a>. Feel free to share your designs, user test or strategic ideas.
|
||||||
- Start designing with our <a href="https://www.figma.com/community/file/967153512097289521/Home-Assistant-DesignKit" rel="noopener noreferrer" target="_blank">Figma DesignKit</a>.
|
- Start designing with our <a href="https://www.figma.com/community/file/967153512097289521/Home-Assistant-DesignKit" rel="noopener noreferrer" target="_blank">Figma DesignKit</a>.
|
||||||
- Find the lates UX <a href="https://github.com/home-assistant/frontend/labels/ux" rel="noopener noreferrer" target="_blank">discussions</a> and <a href="https://github.com/home-assistant/frontend/discussions?discussions_q=label%3Aux" rel="noopener noreferrer" target="_blank">issues</a> on GitHub. Everyone can start a new issue or discussion!
|
- Find the lates UX <a href="https://github.com/home-assistant/frontend/discussions?discussions_q=label%3Aux" rel="noopener noreferrer" target="_blank">discussions</a> and <a href="https://github.com/home-assistant/frontend/labels/ux" rel="noopener noreferrer" target="_blank">issues</a> on GitHub. Everyone can start a new issue or discussion!
|
||||||
|
|
||||||
|
|
||||||
## Developers
|
## Developers
|
||||||
|
@ -47,7 +47,9 @@ export class DialogHassioBackupUpload
|
|||||||
scrimClickAction
|
scrimClickAction
|
||||||
escapeKeyAction
|
escapeKeyAction
|
||||||
hideActions
|
hideActions
|
||||||
.heading=${true}
|
.heading=${this.hass?.localize(
|
||||||
|
"ui.panel.page-onboarding.restore.upload_backup"
|
||||||
|
) || "Upload backup"}
|
||||||
@closed=${this.closeDialog}
|
@closed=${this.closeDialog}
|
||||||
>
|
>
|
||||||
<div slot="heading">
|
<div slot="heading">
|
||||||
|
@ -71,7 +71,7 @@ class HassioBackupDialog
|
|||||||
open
|
open
|
||||||
scrimClickAction
|
scrimClickAction
|
||||||
@closed=${this.closeDialog}
|
@closed=${this.closeDialog}
|
||||||
.heading=${true}
|
.heading=${this._backup.name}
|
||||||
>
|
>
|
||||||
<div slot="heading">
|
<div slot="heading">
|
||||||
<ha-header-bar>
|
<ha-header-bar>
|
||||||
|
@ -65,7 +65,9 @@ class HassioHardwareDialog extends LitElement {
|
|||||||
scrimClickAction
|
scrimClickAction
|
||||||
hideActions
|
hideActions
|
||||||
@closed=${this.closeDialog}
|
@closed=${this.closeDialog}
|
||||||
.heading=${true}
|
.heading=${this._dialogParams.supervisor.localize(
|
||||||
|
"dialog.hardware.title"
|
||||||
|
)}
|
||||||
>
|
>
|
||||||
<div class="header" slot="heading">
|
<div class="header" slot="heading">
|
||||||
<h2>
|
<h2>
|
||||||
|
@ -94,7 +94,7 @@ export class DialogHassioNetwork
|
|||||||
open
|
open
|
||||||
scrimClickAction
|
scrimClickAction
|
||||||
escapeKeyAction
|
escapeKeyAction
|
||||||
.heading=${true}
|
.heading=${this.supervisor.localize("dialog.network.title")}
|
||||||
hideActions
|
hideActions
|
||||||
@closed=${this.closeDialog}
|
@closed=${this.closeDialog}
|
||||||
>
|
>
|
||||||
|
@ -33,8 +33,12 @@ import {
|
|||||||
extractApiErrorMessage,
|
extractApiErrorMessage,
|
||||||
ignoreSupervisorError,
|
ignoreSupervisorError,
|
||||||
} from "../../../src/data/hassio/common";
|
} from "../../../src/data/hassio/common";
|
||||||
import { updateOS } from "../../../src/data/hassio/host";
|
import { fetchHassioHassOsInfo, updateOS } from "../../../src/data/hassio/host";
|
||||||
import { updateSupervisor } from "../../../src/data/hassio/supervisor";
|
import {
|
||||||
|
fetchHassioHomeAssistantInfo,
|
||||||
|
fetchHassioSupervisorInfo,
|
||||||
|
updateSupervisor,
|
||||||
|
} from "../../../src/data/hassio/supervisor";
|
||||||
import { updateCore } from "../../../src/data/supervisor/core";
|
import { updateCore } from "../../../src/data/supervisor/core";
|
||||||
import { StoreAddon } from "../../../src/data/supervisor/store";
|
import { StoreAddon } from "../../../src/data/supervisor/store";
|
||||||
import { Supervisor } from "../../../src/data/supervisor/supervisor";
|
import { Supervisor } from "../../../src/data/supervisor/supervisor";
|
||||||
@ -212,11 +216,22 @@ class UpdateAvailableCard extends LitElement {
|
|||||||
: "addon";
|
: "addon";
|
||||||
this._updateType = updateType as updateType;
|
this._updateType = updateType as updateType;
|
||||||
|
|
||||||
if (updateType === "addon") {
|
switch (updateType) {
|
||||||
|
case "addon":
|
||||||
if (!this.addonSlug) {
|
if (!this.addonSlug) {
|
||||||
this.addonSlug = pathPart;
|
this.addonSlug = pathPart;
|
||||||
}
|
}
|
||||||
this._loadAddonData();
|
this._loadAddonData();
|
||||||
|
break;
|
||||||
|
case "core":
|
||||||
|
this._loadCoreData();
|
||||||
|
break;
|
||||||
|
case "supervisor":
|
||||||
|
this._loadSupervisorData();
|
||||||
|
break;
|
||||||
|
case "os":
|
||||||
|
this._loadOsData();
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -308,6 +323,42 @@ class UpdateAvailableCard extends LitElement {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async _loadSupervisorData() {
|
||||||
|
try {
|
||||||
|
const supervisor = await fetchHassioSupervisorInfo(this.hass);
|
||||||
|
fireEvent(this, "supervisor-update", { supervisor });
|
||||||
|
} catch (err) {
|
||||||
|
showAlertDialog(this, {
|
||||||
|
title: this._updateType,
|
||||||
|
text: extractApiErrorMessage(err),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async _loadCoreData() {
|
||||||
|
try {
|
||||||
|
const core = await fetchHassioHomeAssistantInfo(this.hass);
|
||||||
|
fireEvent(this, "supervisor-update", { core });
|
||||||
|
} catch (err) {
|
||||||
|
showAlertDialog(this, {
|
||||||
|
title: this._updateType,
|
||||||
|
text: extractApiErrorMessage(err),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async _loadOsData() {
|
||||||
|
try {
|
||||||
|
const os = await fetchHassioHassOsInfo(this.hass);
|
||||||
|
fireEvent(this, "supervisor-update", { os });
|
||||||
|
} catch (err) {
|
||||||
|
showAlertDialog(this, {
|
||||||
|
title: this._updateType,
|
||||||
|
text: extractApiErrorMessage(err),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private async _update() {
|
private async _update() {
|
||||||
this._error = undefined;
|
this._error = undefined;
|
||||||
this._updating = true;
|
this._updating = true;
|
||||||
|
@ -13,4 +13,4 @@ script/build_frontend
|
|||||||
|
|
||||||
rm -rf dist home_assistant_frontend.egg-info
|
rm -rf dist home_assistant_frontend.egg-info
|
||||||
python3 -m build
|
python3 -m build
|
||||||
python3 -m twine upload dist/* --skip-existing
|
python3 -m twine upload dist/*.whl --skip-existing
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
[metadata]
|
[metadata]
|
||||||
name = home-assistant-frontend
|
name = home-assistant-frontend
|
||||||
version = 20220202.0
|
version = 20220203.0
|
||||||
author = The Home Assistant Authors
|
author = The Home Assistant Authors
|
||||||
author_email = hello@home-assistant.io
|
author_email = hello@home-assistant.io
|
||||||
license = Apache-2.0
|
license = Apache-2.0
|
||||||
|
@ -12,7 +12,7 @@ import { property, state } from "lit/decorators";
|
|||||||
import { STATES_OFF } from "../../common/const";
|
import { STATES_OFF } from "../../common/const";
|
||||||
import { computeStateDomain } from "../../common/entity/compute_state_domain";
|
import { computeStateDomain } from "../../common/entity/compute_state_domain";
|
||||||
import { computeStateName } from "../../common/entity/compute_state_name";
|
import { computeStateName } from "../../common/entity/compute_state_name";
|
||||||
import { UNAVAILABLE, UNAVAILABLE_STATES } from "../../data/entity";
|
import { UNAVAILABLE, UNAVAILABLE_STATES, UNKNOWN } from "../../data/entity";
|
||||||
import { forwardHaptic } from "../../data/haptics";
|
import { forwardHaptic } from "../../data/haptics";
|
||||||
import { HomeAssistant } from "../../types";
|
import { HomeAssistant } from "../../types";
|
||||||
import "../ha-formfield";
|
import "../ha-formfield";
|
||||||
@ -39,21 +39,26 @@ export class HaEntityToggle extends LitElement {
|
|||||||
return html` <ha-switch disabled></ha-switch> `;
|
return html` <ha-switch disabled></ha-switch> `;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.stateObj.attributes.assumed_state) {
|
if (
|
||||||
|
this.stateObj.attributes.assumed_state ||
|
||||||
|
this.stateObj.state === UNKNOWN
|
||||||
|
) {
|
||||||
return html`
|
return html`
|
||||||
<ha-icon-button
|
<ha-icon-button
|
||||||
.label=${`Turn ${computeStateName(this.stateObj)} off`}
|
.label=${`Turn ${computeStateName(this.stateObj)} off`}
|
||||||
.path=${mdiFlashOff}
|
.path=${mdiFlashOff}
|
||||||
.disabled=${this.stateObj.state === UNAVAILABLE}
|
.disabled=${this.stateObj.state === UNAVAILABLE}
|
||||||
@click=${this._turnOff}
|
@click=${this._turnOff}
|
||||||
?state-active=${!this._isOn}
|
class=${!this._isOn && this.stateObj.state !== UNKNOWN
|
||||||
|
? "state-active"
|
||||||
|
: ""}
|
||||||
></ha-icon-button>
|
></ha-icon-button>
|
||||||
<ha-icon-button
|
<ha-icon-button
|
||||||
.label=${`Turn ${computeStateName(this.stateObj)} on`}
|
.label=${`Turn ${computeStateName(this.stateObj)} on`}
|
||||||
.path=${mdiFlash}
|
.path=${mdiFlash}
|
||||||
.disabled=${this.stateObj.state === UNAVAILABLE}
|
.disabled=${this.stateObj.state === UNAVAILABLE}
|
||||||
@click=${this._turnOn}
|
@click=${this._turnOn}
|
||||||
?state-active=${this._isOn}
|
class=${this._isOn ? "state-active" : ""}
|
||||||
></ha-icon-button>
|
></ha-icon-button>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
@ -63,7 +68,7 @@ export class HaEntityToggle extends LitElement {
|
|||||||
this._isOn ? "off" : "on"
|
this._isOn ? "off" : "on"
|
||||||
}`}
|
}`}
|
||||||
.checked=${this._isOn}
|
.checked=${this._isOn}
|
||||||
.disabled=${UNAVAILABLE_STATES.includes(this.stateObj.state)}
|
.disabled=${this.stateObj.state === UNAVAILABLE}
|
||||||
@change=${this._toggleChanged}
|
@change=${this._toggleChanged}
|
||||||
></ha-switch>`;
|
></ha-switch>`;
|
||||||
|
|
||||||
@ -156,10 +161,11 @@ export class HaEntityToggle extends LitElement {
|
|||||||
min-width: 38px;
|
min-width: 38px;
|
||||||
}
|
}
|
||||||
ha-icon-button {
|
ha-icon-button {
|
||||||
|
--mdc-icon-button-size: 40px;
|
||||||
color: var(--ha-icon-button-inactive-color, var(--primary-text-color));
|
color: var(--ha-icon-button-inactive-color, var(--primary-text-color));
|
||||||
transition: color 0.5s;
|
transition: color 0.5s;
|
||||||
}
|
}
|
||||||
ha-icon-button[state-active] {
|
ha-icon-button.state-active {
|
||||||
color: var(--ha-icon-button-active-color, var(--primary-color));
|
color: var(--ha-icon-button-active-color, var(--primary-color));
|
||||||
}
|
}
|
||||||
ha-switch {
|
ha-switch {
|
||||||
|
@ -55,7 +55,11 @@ class DialogMediaPlayerBrowse extends LitElement {
|
|||||||
escapeKeyAction
|
escapeKeyAction
|
||||||
hideActions
|
hideActions
|
||||||
flexContent
|
flexContent
|
||||||
.heading=${true}
|
.heading=${!this._currentItem
|
||||||
|
? this.hass.localize(
|
||||||
|
"ui.components.media-browser.media-player-browser"
|
||||||
|
)
|
||||||
|
: this._currentItem.title}
|
||||||
@closed=${this.closeDialog}
|
@closed=${this.closeDialog}
|
||||||
>
|
>
|
||||||
<ha-header-bar slot="heading">
|
<ha-header-bar slot="heading">
|
||||||
|
@ -82,7 +82,7 @@ async function processEvent(
|
|||||||
event: SupervisorEvent,
|
event: SupervisorEvent,
|
||||||
key: string
|
key: string
|
||||||
) {
|
) {
|
||||||
if (event.event !== "supervisor-update" || event.update_key !== key) {
|
if (event.event !== "supervisor_update" || event.update_key !== key) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -105,7 +105,7 @@ export class MoreInfoDialog extends LitElement {
|
|||||||
<ha-dialog
|
<ha-dialog
|
||||||
open
|
open
|
||||||
@closed=${this.closeDialog}
|
@closed=${this.closeDialog}
|
||||||
.heading=${true}
|
.heading=${name}
|
||||||
hideActions
|
hideActions
|
||||||
data-domain=${domain}
|
data-domain=${domain}
|
||||||
>
|
>
|
||||||
|
@ -139,7 +139,7 @@ export class QuickBar extends LitElement {
|
|||||||
|
|
||||||
return html`
|
return html`
|
||||||
<ha-dialog
|
<ha-dialog
|
||||||
.heading=${true}
|
.heading=${this.hass.localize("ui.dialogs.quick-bar.title")}
|
||||||
open
|
open
|
||||||
@opened=${this._handleOpened}
|
@opened=${this._handleOpened}
|
||||||
@closed=${this.closeDialog}
|
@closed=${this.closeDialog}
|
||||||
@ -231,9 +231,7 @@ export class QuickBar extends LitElement {
|
|||||||
})}
|
})}
|
||||||
</mwc-list>
|
</mwc-list>
|
||||||
`}
|
`}
|
||||||
${!this._narrow && this._hint
|
${this._hint ? html`<div class="hint">${this._hint}</div>` : ""}
|
||||||
? html`<div class="hint">${this._hint}</div>`
|
|
||||||
: ""}
|
|
||||||
</ha-dialog>
|
</ha-dialog>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
@ -718,6 +716,12 @@ export class QuickBar extends LitElement {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@media all and (max-width: 450px), all and (max-height: 690px) {
|
||||||
|
.hint {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ha-icon.entity,
|
ha-icon.entity,
|
||||||
ha-svg-icon.entity {
|
ha-svg-icon.entity {
|
||||||
margin-left: 20px;
|
margin-left: 20px;
|
||||||
|
@ -1,11 +1,7 @@
|
|||||||
import {
|
import { setPassiveTouchGestures } from "@polymer/polymer/lib/utils/settings";
|
||||||
setPassiveTouchGestures,
|
|
||||||
setCancelSyntheticClickEvents,
|
|
||||||
} from "@polymer/polymer/lib/utils/settings";
|
|
||||||
import "../layouts/home-assistant";
|
import "../layouts/home-assistant";
|
||||||
import "../resources/ha-style";
|
import "../resources/ha-style";
|
||||||
import "../resources/roboto";
|
import "../resources/roboto";
|
||||||
import "../util/legacy-support";
|
import "../util/legacy-support";
|
||||||
|
|
||||||
setPassiveTouchGestures(true);
|
setPassiveTouchGestures(true);
|
||||||
setCancelSyntheticClickEvents(false);
|
|
||||||
|
@ -1,14 +1,11 @@
|
|||||||
// Compat needs to be first import
|
// Compat needs to be first import
|
||||||
import "../resources/compatibility";
|
import "../resources/compatibility";
|
||||||
import { setCancelSyntheticClickEvents } from "@polymer/polymer/lib/utils/settings";
|
|
||||||
import "../auth/ha-authorize";
|
import "../auth/ha-authorize";
|
||||||
import "../resources/ha-style";
|
import "../resources/ha-style";
|
||||||
import "../resources/roboto";
|
import "../resources/roboto";
|
||||||
import "../resources/safari-14-attachshadow-patch";
|
import "../resources/safari-14-attachshadow-patch";
|
||||||
import "../resources/array.flat.polyfill";
|
import "../resources/array.flat.polyfill";
|
||||||
|
|
||||||
setCancelSyntheticClickEvents(false);
|
|
||||||
|
|
||||||
/* polyfill for paper-dropdown */
|
/* polyfill for paper-dropdown */
|
||||||
setTimeout(
|
setTimeout(
|
||||||
() => import("web-animations-js/web-animations-next-lite.min"),
|
() => import("web-animations-js/web-animations-next-lite.min"),
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
// Compat needs to be first import
|
// Compat needs to be first import
|
||||||
import "../resources/compatibility";
|
import "../resources/compatibility";
|
||||||
import { setCancelSyntheticClickEvents } from "@polymer/polymer/lib/utils/settings";
|
|
||||||
import "../resources/safari-14-attachshadow-patch";
|
import "../resources/safari-14-attachshadow-patch";
|
||||||
|
|
||||||
import { PolymerElement } from "@polymer/polymer";
|
import { PolymerElement } from "@polymer/polymer";
|
||||||
@ -16,8 +15,6 @@ import { createCustomPanelElement } from "../util/custom-panel/create-custom-pan
|
|||||||
import { loadCustomPanel } from "../util/custom-panel/load-custom-panel";
|
import { loadCustomPanel } from "../util/custom-panel/load-custom-panel";
|
||||||
import { setCustomPanelProperties } from "../util/custom-panel/set-custom-panel-properties";
|
import { setCustomPanelProperties } from "../util/custom-panel/set-custom-panel-properties";
|
||||||
|
|
||||||
setCancelSyntheticClickEvents(false);
|
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
interface Window {
|
interface Window {
|
||||||
loadES5Adapter: () => Promise<unknown>;
|
loadES5Adapter: () => Promise<unknown>;
|
||||||
|
@ -1,14 +1,11 @@
|
|||||||
// Compat needs to be first import
|
// Compat needs to be first import
|
||||||
import "../resources/compatibility";
|
import "../resources/compatibility";
|
||||||
import { setCancelSyntheticClickEvents } from "@polymer/polymer/lib/utils/settings";
|
|
||||||
import "../onboarding/ha-onboarding";
|
import "../onboarding/ha-onboarding";
|
||||||
import "../resources/ha-style";
|
import "../resources/ha-style";
|
||||||
import "../resources/roboto";
|
import "../resources/roboto";
|
||||||
import "../resources/safari-14-attachshadow-patch";
|
import "../resources/safari-14-attachshadow-patch";
|
||||||
import "../resources/array.flat.polyfill";
|
import "../resources/array.flat.polyfill";
|
||||||
|
|
||||||
setCancelSyntheticClickEvents(false);
|
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
interface Window {
|
interface Window {
|
||||||
stepsPromise: Promise<Response>;
|
stepsPromise: Promise<Response>;
|
||||||
|
@ -39,6 +39,7 @@ import {
|
|||||||
findBatteryEntity,
|
findBatteryEntity,
|
||||||
updateEntityRegistryEntry,
|
updateEntityRegistryEntry,
|
||||||
} from "../../../data/entity_registry";
|
} from "../../../data/entity_registry";
|
||||||
|
import { domainToName } from "../../../data/integration";
|
||||||
import { SceneEntities, showSceneEditor } from "../../../data/scene";
|
import { SceneEntities, showSceneEditor } from "../../../data/scene";
|
||||||
import { findRelated, RelatedResult } from "../../../data/search";
|
import { findRelated, RelatedResult } from "../../../data/search";
|
||||||
import {
|
import {
|
||||||
@ -201,6 +202,10 @@ export class HaConfigDevicePage extends LitElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private async _renderDiagnosticButtons(requestId: number): Promise<void> {
|
private async _renderDiagnosticButtons(requestId: number): Promise<void> {
|
||||||
|
if (!isComponentLoaded(this.hass, "diagnostics")) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const device = this._device(this.deviceId, this.devices);
|
const device = this._device(this.deviceId, this.devices);
|
||||||
|
|
||||||
if (!device) {
|
if (!device) {
|
||||||
@ -208,34 +213,53 @@ export class HaConfigDevicePage extends LitElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let links = await Promise.all(
|
let links = await Promise.all(
|
||||||
this._integrations(device, this.entries)
|
this._integrations(device, this.entries).map(async (entry) => {
|
||||||
.filter((entry) => entry.state === "loaded")
|
if (entry.state !== "loaded") {
|
||||||
.map(async (entry) => {
|
return false;
|
||||||
|
}
|
||||||
const info = await fetchDiagnosticHandler(this.hass, entry.domain);
|
const info = await fetchDiagnosticHandler(this.hass, entry.domain);
|
||||||
|
|
||||||
if (!info.handlers.device && !info.handlers.config_entry) {
|
if (!info.handlers.device && !info.handlers.config_entry) {
|
||||||
return "";
|
return false;
|
||||||
}
|
}
|
||||||
const link = info.handlers.device
|
return {
|
||||||
|
link: info.handlers.device
|
||||||
? getDeviceDiagnosticsDownloadUrl(entry.entry_id, this.deviceId)
|
? getDeviceDiagnosticsDownloadUrl(entry.entry_id, this.deviceId)
|
||||||
: getConfigEntryDiagnosticsDownloadUrl(entry.entry_id);
|
: getConfigEntryDiagnosticsDownloadUrl(entry.entry_id),
|
||||||
return html`
|
domain: entry.domain,
|
||||||
<a href=${link} @click=${this._signUrl}>
|
};
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
links = links.filter(Boolean);
|
||||||
|
|
||||||
|
if (this._diagnosticDownloadLinks !== requestId) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (links.length > 0) {
|
||||||
|
this._diagnosticDownloadLinks = (
|
||||||
|
links as { link: string; domain: string }[]
|
||||||
|
).map(
|
||||||
|
(link) => html`
|
||||||
|
<a href=${link.link} @click=${this._signUrl}>
|
||||||
<mwc-button>
|
<mwc-button>
|
||||||
${this.hass.localize(
|
${links.length > 1
|
||||||
|
? this.hass.localize(
|
||||||
|
`ui.panel.config.devices.download_diagnostics_integration`,
|
||||||
|
{
|
||||||
|
integration: domainToName(
|
||||||
|
this.hass.localize,
|
||||||
|
link.domain
|
||||||
|
),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
: this.hass.localize(
|
||||||
`ui.panel.config.devices.download_diagnostics`
|
`ui.panel.config.devices.download_diagnostics`
|
||||||
)}
|
)}
|
||||||
</mwc-button>
|
</mwc-button>
|
||||||
</a>
|
</a>
|
||||||
`;
|
`
|
||||||
})
|
|
||||||
);
|
);
|
||||||
if (this._diagnosticDownloadLinks !== requestId) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
links = links.filter(Boolean);
|
|
||||||
if (links.length > 0) {
|
|
||||||
this._diagnosticDownloadLinks = links;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -74,7 +74,9 @@ export class DialogEntityEditor extends LitElement {
|
|||||||
return html`
|
return html`
|
||||||
<ha-dialog
|
<ha-dialog
|
||||||
open
|
open
|
||||||
.heading=${true}
|
.heading=${stateObj
|
||||||
|
? computeStateName(stateObj)
|
||||||
|
: entry?.name || entityId}
|
||||||
hideActions
|
hideActions
|
||||||
@closed=${this.closeDialog}
|
@closed=${this.closeDialog}
|
||||||
@close-dialog=${this.closeDialog}
|
@close-dialog=${this.closeDialog}
|
||||||
|
@ -69,17 +69,7 @@ class DialogSystemLogDetail extends LitElement {
|
|||||||
// Custom components with our official docs should not link to our docs
|
// Custom components with our official docs should not link to our docs
|
||||||
!this._manifest.documentation.includes("://www.home-assistant.io"));
|
!this._manifest.documentation.includes("://www.home-assistant.io"));
|
||||||
|
|
||||||
return html`
|
const title = this.hass.localize(
|
||||||
<ha-dialog open @closed=${this.closeDialog} hideActions .heading=${true}>
|
|
||||||
<ha-header-bar slot="heading">
|
|
||||||
<ha-icon-button
|
|
||||||
slot="navigationIcon"
|
|
||||||
dialogAction="cancel"
|
|
||||||
.label=${this.hass.localize("ui.common.close")}
|
|
||||||
.path=${mdiClose}
|
|
||||||
></ha-icon-button>
|
|
||||||
<span slot="title">
|
|
||||||
${this.hass.localize(
|
|
||||||
"ui.panel.config.logs.details",
|
"ui.panel.config.logs.details",
|
||||||
"level",
|
"level",
|
||||||
html`<span class=${item.level.toLowerCase()}
|
html`<span class=${item.level.toLowerCase()}
|
||||||
@ -87,8 +77,18 @@ class DialogSystemLogDetail extends LitElement {
|
|||||||
"ui.panel.config.logs.level." + item.level.toLowerCase()
|
"ui.panel.config.logs.level." + item.level.toLowerCase()
|
||||||
)}</span
|
)}</span
|
||||||
>`
|
>`
|
||||||
)}
|
);
|
||||||
</span>
|
|
||||||
|
return html`
|
||||||
|
<ha-dialog open @closed=${this.closeDialog} hideActions .heading=${title}>
|
||||||
|
<ha-header-bar slot="heading">
|
||||||
|
<ha-icon-button
|
||||||
|
slot="navigationIcon"
|
||||||
|
dialogAction="cancel"
|
||||||
|
.label=${this.hass.localize("ui.common.close")}
|
||||||
|
.path=${mdiClose}
|
||||||
|
></ha-icon-button>
|
||||||
|
<span slot="title"> ${title} </span>
|
||||||
<ha-icon-button
|
<ha-icon-button
|
||||||
id="copy"
|
id="copy"
|
||||||
@click=${this._copyLog}
|
@click=${this._copyLog}
|
||||||
|
@ -9,6 +9,10 @@ import { HomeAssistant } from "../../../types";
|
|||||||
class ErrorLogCard extends LitElement {
|
class ErrorLogCard extends LitElement {
|
||||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||||
|
|
||||||
|
@property() public filter = "";
|
||||||
|
|
||||||
|
@state() private _isLogLoaded = false;
|
||||||
|
|
||||||
@state() private _errorHTML!: TemplateResult[] | string;
|
@state() private _errorHTML!: TemplateResult[] | string;
|
||||||
|
|
||||||
protected render(): TemplateResult {
|
protected render(): TemplateResult {
|
||||||
@ -43,6 +47,14 @@ class ErrorLogCard extends LitElement {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected updated(changedProps) {
|
||||||
|
super.updated(changedProps);
|
||||||
|
|
||||||
|
if (changedProps.has("filter") && this._isLogLoaded) {
|
||||||
|
this._refreshErrorLog();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static get styles(): CSSResultGroup {
|
static get styles(): CSSResultGroup {
|
||||||
return css`
|
return css`
|
||||||
.error-log-intro {
|
.error-log-intro {
|
||||||
@ -55,12 +67,16 @@ class ErrorLogCard extends LitElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.error-log {
|
.error-log {
|
||||||
@apply --paper-font-code)
|
font-family: var(--code-font-family, monospace);
|
||||||
clear: both;
|
clear: both;
|
||||||
text-align: left;
|
text-align: left;
|
||||||
padding-top: 12px;
|
padding-top: 12px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.error-log > div:hover {
|
||||||
|
background-color: var(--secondary-background-color);
|
||||||
|
}
|
||||||
|
|
||||||
.error {
|
.error {
|
||||||
color: var(--error-color);
|
color: var(--error-color);
|
||||||
}
|
}
|
||||||
@ -74,9 +90,18 @@ class ErrorLogCard extends LitElement {
|
|||||||
private async _refreshErrorLog(): Promise<void> {
|
private async _refreshErrorLog(): Promise<void> {
|
||||||
this._errorHTML = this.hass.localize("ui.panel.config.logs.loading_log");
|
this._errorHTML = this.hass.localize("ui.panel.config.logs.loading_log");
|
||||||
const log = await fetchErrorLog(this.hass!);
|
const log = await fetchErrorLog(this.hass!);
|
||||||
|
this._isLogLoaded = true;
|
||||||
|
|
||||||
this._errorHTML = log
|
this._errorHTML = log
|
||||||
? log.split("\n").map((entry) => {
|
? log
|
||||||
|
.split("\n")
|
||||||
|
.filter((entry) => {
|
||||||
|
if (this.filter) {
|
||||||
|
return entry.toLowerCase().includes(this.filter.toLowerCase());
|
||||||
|
}
|
||||||
|
return entry;
|
||||||
|
})
|
||||||
|
.map((entry) => {
|
||||||
if (entry.includes("INFO"))
|
if (entry.includes("INFO"))
|
||||||
return html`<div class="info">${entry}</div>`;
|
return html`<div class="info">${entry}</div>`;
|
||||||
|
|
||||||
|
@ -1,9 +1,11 @@
|
|||||||
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
||||||
import { customElement, property, query } from "lit/decorators";
|
import { customElement, property, query, state } from "lit/decorators";
|
||||||
import "../../../layouts/hass-tabs-subpage";
|
import "../../../layouts/hass-tabs-subpage";
|
||||||
import { haStyle } from "../../../resources/styles";
|
import { haStyle } from "../../../resources/styles";
|
||||||
import { HomeAssistant, Route } from "../../../types";
|
import { HomeAssistant, Route } from "../../../types";
|
||||||
import { configSections } from "../ha-panel-config";
|
import { configSections } from "../ha-panel-config";
|
||||||
|
import "../../../common/search/search-input";
|
||||||
|
import { extractSearchParam } from "../../../common/url/search-params";
|
||||||
import "./error-log-card";
|
import "./error-log-card";
|
||||||
import "./system-log-card";
|
import "./system-log-card";
|
||||||
import type { SystemLogCard } from "./system-log-card";
|
import type { SystemLogCard } from "./system-log-card";
|
||||||
@ -20,6 +22,8 @@ export class HaConfigLogs extends LitElement {
|
|||||||
|
|
||||||
@property() public route!: Route;
|
@property() public route!: Route;
|
||||||
|
|
||||||
|
@state() private _filter = extractSearchParam("filter") ?? "";
|
||||||
|
|
||||||
@query("system-log-card", true) private systemLog?: SystemLogCard;
|
@query("system-log-card", true) private systemLog?: SystemLogCard;
|
||||||
|
|
||||||
public connectedCallback() {
|
public connectedCallback() {
|
||||||
@ -29,7 +33,39 @@ export class HaConfigLogs extends LitElement {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async _filterChanged(ev) {
|
||||||
|
this._filter = ev.detail.value;
|
||||||
|
}
|
||||||
|
|
||||||
protected render(): TemplateResult {
|
protected render(): TemplateResult {
|
||||||
|
const search = this.narrow
|
||||||
|
? html`
|
||||||
|
<div slot="header">
|
||||||
|
<search-input
|
||||||
|
class="header"
|
||||||
|
no-label-float
|
||||||
|
no-underline
|
||||||
|
@value-changed=${this._filterChanged}
|
||||||
|
.hass=${this.hass}
|
||||||
|
.filter=${this._filter}
|
||||||
|
.label=${this.hass.localize("ui.panel.config.logs.search")}
|
||||||
|
></search-input>
|
||||||
|
</div>
|
||||||
|
`
|
||||||
|
: html`
|
||||||
|
<div class="search">
|
||||||
|
<search-input
|
||||||
|
autofocus
|
||||||
|
no-label-float
|
||||||
|
no-underline
|
||||||
|
@value-changed=${this._filterChanged}
|
||||||
|
.hass=${this.hass}
|
||||||
|
.filter=${this._filter}
|
||||||
|
.label=${this.hass.localize("ui.panel.config.logs.search")}
|
||||||
|
></search-input>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
|
||||||
return html`
|
return html`
|
||||||
<hass-tabs-subpage
|
<hass-tabs-subpage
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
@ -38,9 +74,16 @@ export class HaConfigLogs extends LitElement {
|
|||||||
.route=${this.route}
|
.route=${this.route}
|
||||||
.tabs=${configSections.general}
|
.tabs=${configSections.general}
|
||||||
>
|
>
|
||||||
|
${search}
|
||||||
<div class="content">
|
<div class="content">
|
||||||
<system-log-card .hass=${this.hass}></system-log-card>
|
<system-log-card
|
||||||
<error-log-card .hass=${this.hass}></error-log-card>
|
.hass=${this.hass}
|
||||||
|
.filter=${this._filter}
|
||||||
|
></system-log-card>
|
||||||
|
<error-log-card
|
||||||
|
.hass=${this.hass}
|
||||||
|
.filter=${this._filter}
|
||||||
|
></error-log-card>
|
||||||
</div>
|
</div>
|
||||||
</hass-tabs-subpage>
|
</hass-tabs-subpage>
|
||||||
`;
|
`;
|
||||||
@ -56,6 +99,17 @@ export class HaConfigLogs extends LitElement {
|
|||||||
-moz-user-select: initial;
|
-moz-user-select: initial;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.search {
|
||||||
|
padding: 0 16px;
|
||||||
|
background: var(--sidebar-background-color);
|
||||||
|
border-bottom: 1px solid var(--divider-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.search search-input {
|
||||||
|
position: relative;
|
||||||
|
top: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
.content {
|
.content {
|
||||||
direction: ltr;
|
direction: ltr;
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,7 @@ import "@polymer/paper-item/paper-item";
|
|||||||
import "@polymer/paper-item/paper-item-body";
|
import "@polymer/paper-item/paper-item-body";
|
||||||
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
||||||
import { customElement, property, state } from "lit/decorators";
|
import { customElement, property, state } from "lit/decorators";
|
||||||
|
import memoizeOne from "memoize-one";
|
||||||
import "../../../components/buttons/ha-call-service-button";
|
import "../../../components/buttons/ha-call-service-button";
|
||||||
import "../../../components/buttons/ha-progress-button";
|
import "../../../components/buttons/ha-progress-button";
|
||||||
import "../../../components/ha-card";
|
import "../../../components/ha-card";
|
||||||
@ -22,6 +23,8 @@ import { formatSystemLogTime } from "./util";
|
|||||||
export class SystemLogCard extends LitElement {
|
export class SystemLogCard extends LitElement {
|
||||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||||
|
|
||||||
|
@property() public filter = "";
|
||||||
|
|
||||||
public loaded = false;
|
public loaded = false;
|
||||||
|
|
||||||
@state() private _items?: LoggedError[];
|
@state() private _items?: LoggedError[];
|
||||||
@ -31,9 +34,44 @@ export class SystemLogCard extends LitElement {
|
|||||||
this._items = await fetchSystemLog(this.hass!);
|
this._items = await fetchSystemLog(this.hass!);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private _timestamp(item: LoggedError): string {
|
||||||
|
return formatSystemLogTime(item.timestamp, this.hass!.locale);
|
||||||
|
}
|
||||||
|
|
||||||
|
private _multipleMessages(item: LoggedError): string {
|
||||||
|
return this.hass.localize(
|
||||||
|
"ui.panel.config.logs.multiple_messages",
|
||||||
|
"time",
|
||||||
|
formatSystemLogTime(item.first_occurred, this.hass!.locale),
|
||||||
|
"counter",
|
||||||
|
item.count
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private _getFilteredItems = memoizeOne(
|
||||||
|
(items: LoggedError[], filter: string) =>
|
||||||
|
items.filter((item: LoggedError) => {
|
||||||
|
if (filter) {
|
||||||
|
return (
|
||||||
|
item.message.some((message: string) =>
|
||||||
|
message.toLowerCase().includes(filter)
|
||||||
|
) ||
|
||||||
|
item.source[0].toLowerCase().includes(filter) ||
|
||||||
|
item.name.toLowerCase().includes(filter) ||
|
||||||
|
this._timestamp(item).toLowerCase().includes(filter) ||
|
||||||
|
this._multipleMessages(item).toLowerCase().includes(filter)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return item;
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
protected render(): TemplateResult {
|
protected render(): TemplateResult {
|
||||||
const integrations = this._items
|
const filteredItems = this._items
|
||||||
? this._items.map((item) => getLoggedErrorIntegration(item))
|
? this._getFilteredItems(this._items, this.filter.toLowerCase())
|
||||||
|
: [];
|
||||||
|
const integrations = filteredItems.length
|
||||||
|
? filteredItems.map((item) => getLoggedErrorIntegration(item))
|
||||||
: [];
|
: [];
|
||||||
return html`
|
return html`
|
||||||
<div class="system-log-intro">
|
<div class="system-log-intro">
|
||||||
@ -51,17 +89,21 @@ export class SystemLogCard extends LitElement {
|
|||||||
${this.hass.localize("ui.panel.config.logs.no_issues")}
|
${this.hass.localize("ui.panel.config.logs.no_issues")}
|
||||||
</div>
|
</div>
|
||||||
`
|
`
|
||||||
: this._items.map(
|
: filteredItems.length === 0 && this.filter
|
||||||
|
? html`<div class="card-content">
|
||||||
|
${this.hass.localize(
|
||||||
|
"ui.panel.config.logs.no_issues_search",
|
||||||
|
"term",
|
||||||
|
this.filter
|
||||||
|
)}
|
||||||
|
</div>`
|
||||||
|
: filteredItems.map(
|
||||||
(item, idx) => html`
|
(item, idx) => html`
|
||||||
<paper-item @click=${this._openLog} .logItem=${item}>
|
<paper-item @click=${this._openLog} .logItem=${item}>
|
||||||
<paper-item-body two-line>
|
<paper-item-body two-line>
|
||||||
<div class="row">${item.message[0]}</div>
|
<div class="row">${item.message[0]}</div>
|
||||||
<div secondary>
|
<div secondary>
|
||||||
${formatSystemLogTime(
|
${this._timestamp(item)} –
|
||||||
item.timestamp,
|
|
||||||
this.hass!.locale
|
|
||||||
)}
|
|
||||||
–
|
|
||||||
${html`(<span class=${item.level.toLowerCase()}
|
${html`(<span class=${item.level.toLowerCase()}
|
||||||
>${this.hass.localize(
|
>${this.hass.localize(
|
||||||
"ui.panel.config.logs.level." +
|
"ui.panel.config.logs.level." +
|
||||||
@ -81,19 +123,7 @@ export class SystemLogCard extends LitElement {
|
|||||||
}`
|
}`
|
||||||
: item.source[0]}
|
: item.source[0]}
|
||||||
${item.count > 1
|
${item.count > 1
|
||||||
? html`
|
? html` - ${this._multipleMessages(item)} `
|
||||||
-
|
|
||||||
${this.hass.localize(
|
|
||||||
"ui.panel.config.logs.multiple_messages",
|
|
||||||
"time",
|
|
||||||
formatSystemLogTime(
|
|
||||||
item.first_occurred,
|
|
||||||
this.hass!.locale
|
|
||||||
),
|
|
||||||
"counter",
|
|
||||||
item.count
|
|
||||||
)}
|
|
||||||
`
|
|
||||||
: html``}
|
: html``}
|
||||||
</div>
|
</div>
|
||||||
</paper-item-body>
|
</paper-item-body>
|
||||||
|
@ -106,6 +106,9 @@ export class HuiImage extends LitElement {
|
|||||||
} else if (!this.hass!.connected) {
|
} else if (!this.hass!.connected) {
|
||||||
this._stopUpdateCameraInterval();
|
this._stopUpdateCameraInterval();
|
||||||
this._stopIntersectionObserver();
|
this._stopIntersectionObserver();
|
||||||
|
this._loadState = LoadState.Loading;
|
||||||
|
this._cameraImageSrc = undefined;
|
||||||
|
this._loadedImageSrc = undefined;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (changedProps.has("_imageVisible")) {
|
if (changedProps.has("_imageVisible")) {
|
||||||
|
@ -65,28 +65,26 @@ export class HuiCreateDialogCard
|
|||||||
return html``;
|
return html``;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const title = this._viewConfig.title
|
||||||
|
? this.hass!.localize(
|
||||||
|
"ui.panel.lovelace.editor.edit_card.pick_card_view_title",
|
||||||
|
"name",
|
||||||
|
`"${this._viewConfig.title}"`
|
||||||
|
)
|
||||||
|
: this.hass!.localize("ui.panel.lovelace.editor.edit_card.pick_card");
|
||||||
|
|
||||||
return html`
|
return html`
|
||||||
<ha-dialog
|
<ha-dialog
|
||||||
open
|
open
|
||||||
scrimClickAction
|
scrimClickAction
|
||||||
@keydown=${this._ignoreKeydown}
|
@keydown=${this._ignoreKeydown}
|
||||||
@closed=${this._cancel}
|
@closed=${this._cancel}
|
||||||
.heading=${true}
|
.heading=${title}
|
||||||
class=${classMap({ table: this._currTabIndex === 1 })}
|
class=${classMap({ table: this._currTabIndex === 1 })}
|
||||||
>
|
>
|
||||||
<div slot="heading">
|
<div slot="heading">
|
||||||
<ha-header-bar>
|
<ha-header-bar>
|
||||||
<span slot="title">
|
<span slot="title"> ${title} </span>
|
||||||
${this._viewConfig.title
|
|
||||||
? this.hass!.localize(
|
|
||||||
"ui.panel.lovelace.editor.edit_card.pick_card_view_title",
|
|
||||||
"name",
|
|
||||||
`"${this._viewConfig.title}"`
|
|
||||||
)
|
|
||||||
: this.hass!.localize(
|
|
||||||
"ui.panel.lovelace.editor.edit_card.pick_card"
|
|
||||||
)}
|
|
||||||
</span>
|
|
||||||
</ha-header-bar>
|
</ha-header-bar>
|
||||||
<mwc-tab-bar
|
<mwc-tab-bar
|
||||||
.activeIndex=${this._currTabIndex}
|
.activeIndex=${this._currTabIndex}
|
||||||
|
@ -176,7 +176,7 @@ export class HuiDialogEditCard
|
|||||||
@keydown=${this._ignoreKeydown}
|
@keydown=${this._ignoreKeydown}
|
||||||
@closed=${this._cancel}
|
@closed=${this._cancel}
|
||||||
@opened=${this._opened}
|
@opened=${this._opened}
|
||||||
.heading=${true}
|
.heading=${heading}
|
||||||
>
|
>
|
||||||
<div slot="heading">
|
<div slot="heading">
|
||||||
<ha-header-bar>
|
<ha-header-bar>
|
||||||
|
@ -173,7 +173,7 @@ export class HuiDialogEditView extends LitElement {
|
|||||||
scrimClickAction
|
scrimClickAction
|
||||||
escapeKeyAction
|
escapeKeyAction
|
||||||
@closed=${this.closeDialog}
|
@closed=${this.closeDialog}
|
||||||
.heading=${true}
|
.heading=${this._viewConfigTitle}
|
||||||
>
|
>
|
||||||
<div slot="heading">
|
<div slot="heading">
|
||||||
<h2>${this._viewConfigTitle}</h2>
|
<h2>${this._viewConfigTitle}</h2>
|
||||||
|
@ -1344,6 +1344,8 @@
|
|||||||
"caption": "Logs",
|
"caption": "Logs",
|
||||||
"description": "View the Home Assistant logs",
|
"description": "View the Home Assistant logs",
|
||||||
"details": "Log Details ({level})",
|
"details": "Log Details ({level})",
|
||||||
|
"search": "Search logs",
|
||||||
|
"no_issues_search": "No issues found for search term ''{term}''",
|
||||||
"load_full_log": "Load Full Home Assistant Log",
|
"load_full_log": "Load Full Home Assistant Log",
|
||||||
"loading_log": "Loading error log…",
|
"loading_log": "Loading error log…",
|
||||||
"no_errors": "No errors have been reported",
|
"no_errors": "No errors have been reported",
|
||||||
@ -2229,6 +2231,7 @@
|
|||||||
"open_configuration_url_device": "Visit device",
|
"open_configuration_url_device": "Visit device",
|
||||||
"open_configuration_url_service": "Visit service",
|
"open_configuration_url_service": "Visit service",
|
||||||
"download_diagnostics": "Download diagnostics",
|
"download_diagnostics": "Download diagnostics",
|
||||||
|
"download_diagnostics_integration": "Download {integration} diagnostics",
|
||||||
"type": {
|
"type": {
|
||||||
"device_heading": "Device",
|
"device_heading": "Device",
|
||||||
"device": "device",
|
"device": "device",
|
||||||
|
Loading…
x
Reference in New Issue
Block a user