mirror of
https://github.com/home-assistant/frontend.git
synced 2025-07-28 11:46:42 +00:00
Add trace details foundation (#8716)
Co-authored-by: Bram Kragten <mail@bramkragten.nl>
This commit is contained in:
parent
ee38c419de
commit
40cf4c8d32
@ -12,9 +12,11 @@ import { buttonLinkStyle } from "../../resources/styles";
|
|||||||
import "../ha-svg-icon";
|
import "../ha-svg-icon";
|
||||||
|
|
||||||
@customElement("ha-timeline")
|
@customElement("ha-timeline")
|
||||||
class HaTimeline extends LitElement {
|
export class HaTimeline extends LitElement {
|
||||||
@property({ type: Boolean, reflect: true }) public label = false;
|
@property({ type: Boolean, reflect: true }) public label = false;
|
||||||
|
|
||||||
|
@property({ type: Boolean, reflect: true }) public raised = false;
|
||||||
|
|
||||||
@property({ type: Boolean }) public lastItem = false;
|
@property({ type: Boolean }) public lastItem = false;
|
||||||
|
|
||||||
@property({ type: String }) public icon?: string;
|
@property({ type: String }) public icon?: string;
|
||||||
@ -86,6 +88,10 @@ class HaTimeline extends LitElement {
|
|||||||
--timeline-ball-color,
|
--timeline-ball-color,
|
||||||
var(--timeline-color, var(--secondary-text-color))
|
var(--timeline-color, var(--secondary-text-color))
|
||||||
);
|
);
|
||||||
|
border-radius: 50%;
|
||||||
|
}
|
||||||
|
:host([raised]) ha-svg-icon {
|
||||||
|
transform: scale(1.3);
|
||||||
}
|
}
|
||||||
.line {
|
.line {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
|
@ -3,6 +3,7 @@ import {
|
|||||||
CSSResult,
|
CSSResult,
|
||||||
customElement,
|
customElement,
|
||||||
html,
|
html,
|
||||||
|
internalProperty,
|
||||||
LitElement,
|
LitElement,
|
||||||
property,
|
property,
|
||||||
TemplateResult,
|
TemplateResult,
|
||||||
@ -15,6 +16,7 @@ import {
|
|||||||
} from "../../data/trace";
|
} from "../../data/trace";
|
||||||
import { HomeAssistant } from "../../types";
|
import { HomeAssistant } from "../../types";
|
||||||
import "./ha-timeline";
|
import "./ha-timeline";
|
||||||
|
import type { HaTimeline } from "./ha-timeline";
|
||||||
import {
|
import {
|
||||||
mdiCheckCircleOutline,
|
mdiCheckCircleOutline,
|
||||||
mdiCircle,
|
mdiCircle,
|
||||||
@ -30,6 +32,7 @@ import {
|
|||||||
getActionType,
|
getActionType,
|
||||||
} from "../../data/script";
|
} from "../../data/script";
|
||||||
import relativeTime from "../../common/datetime/relative_time";
|
import relativeTime from "../../common/datetime/relative_time";
|
||||||
|
import { fireEvent } from "../../common/dom/fire_event";
|
||||||
|
|
||||||
const LOGBOOK_ENTRIES_BEFORE_FOLD = 2;
|
const LOGBOOK_ENTRIES_BEFORE_FOLD = 2;
|
||||||
|
|
||||||
@ -242,7 +245,7 @@ class ActionRenderer {
|
|||||||
const isTopLevel = path.split("/").length === 2;
|
const isTopLevel = path.split("/").length === 2;
|
||||||
|
|
||||||
if (!isTopLevel && !actionType) {
|
if (!isTopLevel && !actionType) {
|
||||||
this._renderEntry(path.replace(/\//g, " "));
|
this._renderEntry(path, path.replace(/\//g, " "));
|
||||||
return index + 1;
|
return index + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -254,7 +257,7 @@ class ActionRenderer {
|
|||||||
return this._handleChoose(index);
|
return this._handleChoose(index);
|
||||||
}
|
}
|
||||||
|
|
||||||
this._renderEntry(data.alias || actionType);
|
this._renderEntry(path, data.alias || actionType);
|
||||||
return index + 1;
|
return index + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -273,7 +276,8 @@ class ActionRenderer {
|
|||||||
// +3: 'sequence'
|
// +3: 'sequence'
|
||||||
// +4: executed sequence
|
// +4: executed sequence
|
||||||
|
|
||||||
const startLevel = this.keys[index].split("/").length - 1;
|
const choosePath = this.keys[index];
|
||||||
|
const startLevel = choosePath.split("/").length - 1;
|
||||||
|
|
||||||
const chooseTrace = this._getItem(index)[0] as ChooseActionTrace;
|
const chooseTrace = this._getItem(index)[0] as ChooseActionTrace;
|
||||||
const defaultExecuted = chooseTrace.result.choice === "default";
|
const defaultExecuted = chooseTrace.result.choice === "default";
|
||||||
@ -283,14 +287,14 @@ class ActionRenderer {
|
|||||||
const name = chooseConfig.alias || "Choose";
|
const name = chooseConfig.alias || "Choose";
|
||||||
|
|
||||||
if (defaultExecuted) {
|
if (defaultExecuted) {
|
||||||
this._renderEntry(`${name}: Default action executed`);
|
this._renderEntry(choosePath, `${name}: Default action executed`);
|
||||||
} else {
|
} else {
|
||||||
const choiceConfig = this._getDataFromPath(
|
const choiceConfig = this._getDataFromPath(
|
||||||
`${this.keys[index]}/choose/${chooseTrace.result.choice}`
|
`${this.keys[index]}/choose/${chooseTrace.result.choice}`
|
||||||
) as ChooseActionChoice;
|
) as ChooseActionChoice;
|
||||||
const choiceName =
|
const choiceName =
|
||||||
choiceConfig.alias || `Choice ${chooseTrace.result.choice}`;
|
choiceConfig.alias || `Choice ${chooseTrace.result.choice}`;
|
||||||
this._renderEntry(`${name}: ${choiceName} executed`);
|
this._renderEntry(choosePath, `${name}: ${choiceName} executed`);
|
||||||
}
|
}
|
||||||
|
|
||||||
let i;
|
let i;
|
||||||
@ -328,9 +332,9 @@ class ActionRenderer {
|
|||||||
return i;
|
return i;
|
||||||
}
|
}
|
||||||
|
|
||||||
private _renderEntry(description: string) {
|
private _renderEntry(path: string, description: string) {
|
||||||
this.entries.push(html`
|
this.entries.push(html`
|
||||||
<ha-timeline .icon=${mdiRecordCircleOutline}>
|
<ha-timeline .icon=${mdiRecordCircleOutline} data-path=${path}>
|
||||||
${description}
|
${description}
|
||||||
</ha-timeline>
|
</ha-timeline>
|
||||||
`);
|
`);
|
||||||
@ -345,9 +349,11 @@ class ActionRenderer {
|
|||||||
export class HaAutomationTracer extends LitElement {
|
export class HaAutomationTracer extends LitElement {
|
||||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||||
|
|
||||||
@property({ attribute: false }) private trace?: AutomationTraceExtended;
|
@property({ attribute: false }) public trace?: AutomationTraceExtended;
|
||||||
|
|
||||||
@property({ attribute: false }) private logbookEntries?: LogbookEntry[];
|
@property({ attribute: false }) public logbookEntries?: LogbookEntry[];
|
||||||
|
|
||||||
|
@internalProperty() private _selectedPath?: string;
|
||||||
|
|
||||||
protected render(): TemplateResult {
|
protected render(): TemplateResult {
|
||||||
if (!this.trace) {
|
if (!this.trace) {
|
||||||
@ -374,6 +380,7 @@ export class HaAutomationTracer extends LitElement {
|
|||||||
.icon=${value[0].result.result
|
.icon=${value[0].result.result
|
||||||
? mdiCheckCircleOutline
|
? mdiCheckCircleOutline
|
||||||
: mdiStopCircleOutline}
|
: mdiStopCircleOutline}
|
||||||
|
data-path=${path}
|
||||||
>
|
>
|
||||||
${getDataFromPath(this.trace!.config, path).alias ||
|
${getDataFromPath(this.trace!.config, path).alias ||
|
||||||
pathToName(path)}
|
pathToName(path)}
|
||||||
@ -442,12 +449,53 @@ export class HaAutomationTracer extends LitElement {
|
|||||||
return html`${entries}`;
|
return html`${entries}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected updated(props) {
|
||||||
|
super.updated(props);
|
||||||
|
|
||||||
|
// Pick first path when we load a new trace.
|
||||||
|
if (props.has("trace")) {
|
||||||
|
const element = this.shadowRoot!.querySelector<HaTimeline>(
|
||||||
|
"ha-timeline[data-path]"
|
||||||
|
);
|
||||||
|
if (element) {
|
||||||
|
fireEvent(this, "value-changed", { value: element.dataset.path });
|
||||||
|
this._selectedPath = element.dataset.path;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.shadowRoot!.querySelectorAll<HaTimeline>(
|
||||||
|
"ha-timeline[data-path]"
|
||||||
|
).forEach((el) => {
|
||||||
|
el.style.setProperty(
|
||||||
|
"--timeline-ball-color",
|
||||||
|
this._selectedPath === el.dataset.path ? "var(--primary-color)" : null
|
||||||
|
);
|
||||||
|
if (el.dataset.upgraded) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
el.dataset.upgraded = "1";
|
||||||
|
el.addEventListener("click", () => {
|
||||||
|
this._selectedPath = el.dataset.path;
|
||||||
|
fireEvent(this, "value-changed", { value: el.dataset.path });
|
||||||
|
});
|
||||||
|
el.addEventListener("mouseover", () => {
|
||||||
|
el.raised = true;
|
||||||
|
});
|
||||||
|
el.addEventListener("mouseout", () => {
|
||||||
|
el.raised = false;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
static get styles(): CSSResult[] {
|
static get styles(): CSSResult[] {
|
||||||
return [
|
return [
|
||||||
css`
|
css`
|
||||||
ha-timeline[lastItem].condition {
|
ha-timeline[lastItem].condition {
|
||||||
--timeline-ball-color: var(--error-color);
|
--timeline-ball-color: var(--error-color);
|
||||||
}
|
}
|
||||||
|
ha-timeline[data-path] {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
`,
|
`,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import { safeDump } from "js-yaml";
|
||||||
import {
|
import {
|
||||||
css,
|
css,
|
||||||
CSSResult,
|
CSSResult,
|
||||||
@ -12,6 +13,7 @@ import { AutomationEntity } from "../../../../data/automation";
|
|||||||
import {
|
import {
|
||||||
AutomationTrace,
|
AutomationTrace,
|
||||||
AutomationTraceExtended,
|
AutomationTraceExtended,
|
||||||
|
getDataFromPath,
|
||||||
loadTrace,
|
loadTrace,
|
||||||
loadTraces,
|
loadTraces,
|
||||||
} from "../../../../data/trace";
|
} from "../../../../data/trace";
|
||||||
@ -48,6 +50,8 @@ export class HaAutomationTrace extends LitElement {
|
|||||||
|
|
||||||
@internalProperty() private _runId?: string;
|
@internalProperty() private _runId?: string;
|
||||||
|
|
||||||
|
@internalProperty() private _path?: string;
|
||||||
|
|
||||||
@internalProperty() private _trace?: AutomationTraceExtended;
|
@internalProperty() private _trace?: AutomationTraceExtended;
|
||||||
|
|
||||||
@internalProperty() private _logbookEntries?: LogbookEntry[];
|
@internalProperty() private _logbookEntries?: LogbookEntry[];
|
||||||
@ -107,10 +111,31 @@ export class HaAutomationTrace extends LitElement {
|
|||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
.trace=${this._trace}
|
.trace=${this._trace}
|
||||||
.logbookEntries=${this._logbookEntries}
|
.logbookEntries=${this._logbookEntries}
|
||||||
|
@value-changed=${this._pickPath}
|
||||||
></hat-trace>
|
></hat-trace>
|
||||||
`}
|
`}
|
||||||
</div>
|
</div>
|
||||||
</ha-card>
|
</ha-card>
|
||||||
|
${!this._path || !this._trace
|
||||||
|
? ""
|
||||||
|
: html`
|
||||||
|
<div class="details">
|
||||||
|
<ha-card header="Config">
|
||||||
|
<pre class="config card-content">
|
||||||
|
${safeDump(getDataFromPath(this._trace.config, this._path))}</pre
|
||||||
|
>
|
||||||
|
</ha-card>
|
||||||
|
<ha-card header="Trace">
|
||||||
|
<pre class="trace card-content">
|
||||||
|
${safeDump(
|
||||||
|
(this._path.split("/")[0] === "condition"
|
||||||
|
? this._trace.condition_trace
|
||||||
|
: this._trace.action_trace)[this._path]
|
||||||
|
)}</pre
|
||||||
|
>
|
||||||
|
</ha-card>
|
||||||
|
</div>
|
||||||
|
`}
|
||||||
</hass-tabs-subpage>
|
</hass-tabs-subpage>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
@ -162,6 +187,11 @@ export class HaAutomationTrace extends LitElement {
|
|||||||
|
|
||||||
private _pickTrace(ev) {
|
private _pickTrace(ev) {
|
||||||
this._runId = ev.target.value;
|
this._runId = ev.target.value;
|
||||||
|
this._path = undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _pickPath(ev) {
|
||||||
|
this._path = ev.detail.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async _loadTraces(runId?: string) {
|
private async _loadTraces(runId?: string) {
|
||||||
@ -179,6 +209,7 @@ export class HaAutomationTrace extends LitElement {
|
|||||||
!this._traces.some((trace) => trace.run_id === this._runId)
|
!this._traces.some((trace) => trace.run_id === this._runId)
|
||||||
) {
|
) {
|
||||||
this._runId = undefined;
|
this._runId = undefined;
|
||||||
|
this._path = undefined;
|
||||||
|
|
||||||
// If we came here from a trace passed into the url, clear it.
|
// If we came here from a trace passed into the url, clear it.
|
||||||
if (runId) {
|
if (runId) {
|
||||||
@ -254,6 +285,17 @@ export class HaAutomationTrace extends LitElement {
|
|||||||
top: 8px;
|
top: 8px;
|
||||||
right: 8px;
|
right: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.details {
|
||||||
|
display: flex;
|
||||||
|
margin: 0 16px;
|
||||||
|
}
|
||||||
|
.details > * {
|
||||||
|
flex: 1 1 0px;
|
||||||
|
}
|
||||||
|
.details > *:first-child {
|
||||||
|
margin-right: 16px;
|
||||||
|
}
|
||||||
`,
|
`,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user