mirror of
https://github.com/home-assistant/frontend.git
synced 2025-07-25 18:26:35 +00:00
Merge pull request #8795 from home-assistant/dev
This commit is contained in:
commit
c810e541ea
@ -2,8 +2,7 @@ import { DemoTrace } from "./types";
|
|||||||
|
|
||||||
export const basicTrace: DemoTrace = {
|
export const basicTrace: DemoTrace = {
|
||||||
trace: {
|
trace: {
|
||||||
last_action: "action/2",
|
last_step: "action/2",
|
||||||
last_condition: "condition/0",
|
|
||||||
run_id: "0",
|
run_id: "0",
|
||||||
state: "stopped",
|
state: "stopped",
|
||||||
timestamp: {
|
timestamp: {
|
||||||
@ -14,6 +13,12 @@ export const basicTrace: DemoTrace = {
|
|||||||
domain: "automation",
|
domain: "automation",
|
||||||
item_id: "1615419646544",
|
item_id: "1615419646544",
|
||||||
trace: {
|
trace: {
|
||||||
|
"trigger/0": [
|
||||||
|
{
|
||||||
|
path: "trigger/0",
|
||||||
|
timestamp: "2021-03-25T04:36:51.223693+00:00",
|
||||||
|
},
|
||||||
|
],
|
||||||
"condition/0": [
|
"condition/0": [
|
||||||
{
|
{
|
||||||
path: "condition/0",
|
path: "condition/0",
|
||||||
@ -284,45 +289,7 @@ export const basicTrace: DemoTrace = {
|
|||||||
parent_id: "664d6d261450a9ecea6738e97269a149",
|
parent_id: "664d6d261450a9ecea6738e97269a149",
|
||||||
user_id: null,
|
user_id: null,
|
||||||
},
|
},
|
||||||
variables: {
|
script_execution: "finished",
|
||||||
trigger: {
|
|
||||||
platform: "state",
|
|
||||||
entity_id: "input_boolean.toggle_1",
|
|
||||||
from_state: {
|
|
||||||
entity_id: "input_boolean.toggle_1",
|
|
||||||
state: "on",
|
|
||||||
attributes: {
|
|
||||||
editable: true,
|
|
||||||
friendly_name: "Toggle 1",
|
|
||||||
},
|
|
||||||
last_changed: "2021-03-24T19:03:59.141440+00:00",
|
|
||||||
last_updated: "2021-03-24T19:03:59.141440+00:00",
|
|
||||||
context: {
|
|
||||||
id: "5d0918eb379214d07554bdab6a08bcff",
|
|
||||||
parent_id: null,
|
|
||||||
user_id: null,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
to_state: {
|
|
||||||
entity_id: "input_boolean.toggle_1",
|
|
||||||
state: "off",
|
|
||||||
attributes: {
|
|
||||||
editable: true,
|
|
||||||
friendly_name: "Toggle 1",
|
|
||||||
},
|
|
||||||
last_changed: "2021-03-25T04:36:51.220696+00:00",
|
|
||||||
last_updated: "2021-03-25T04:36:51.220696+00:00",
|
|
||||||
context: {
|
|
||||||
id: "664d6d261450a9ecea6738e97269a149",
|
|
||||||
parent_id: null,
|
|
||||||
user_id: "d1b4e89da01445fa8bc98e39fac477ca",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
for: null,
|
|
||||||
attribute: null,
|
|
||||||
description: "state of input_boolean.toggle_1",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
logbookEntries: [
|
logbookEntries: [
|
||||||
{
|
{
|
||||||
|
44
gallery/src/data/traces/mock-demo-trace.ts
Normal file
44
gallery/src/data/traces/mock-demo-trace.ts
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
import { LogbookEntry } from "../../../../src/data/logbook";
|
||||||
|
import { AutomationTraceExtended } from "../../../../src/data/trace";
|
||||||
|
import { DemoTrace } from "./types";
|
||||||
|
|
||||||
|
export const mockDemoTrace = (
|
||||||
|
tracePartial: Partial<AutomationTraceExtended>,
|
||||||
|
logbookEntries?: LogbookEntry[]
|
||||||
|
): DemoTrace => ({
|
||||||
|
trace: {
|
||||||
|
last_step: "",
|
||||||
|
run_id: "0",
|
||||||
|
state: "stopped",
|
||||||
|
timestamp: {
|
||||||
|
start: "2021-03-25T04:36:51.223693+00:00",
|
||||||
|
finish: "2021-03-25T04:36:51.266132+00:00",
|
||||||
|
},
|
||||||
|
trigger: "mocked trigger",
|
||||||
|
domain: "automation",
|
||||||
|
item_id: "1615419646544",
|
||||||
|
trace: {
|
||||||
|
"trigger/0": [
|
||||||
|
{
|
||||||
|
path: "trigger/0",
|
||||||
|
changed_variables: {
|
||||||
|
trigger: {
|
||||||
|
description: "mocked trigger",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
timestamp: "2021-03-25T04:36:51.223693+00:00",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
config: {
|
||||||
|
trigger: [],
|
||||||
|
action: [],
|
||||||
|
},
|
||||||
|
context: {
|
||||||
|
id: "abcd",
|
||||||
|
},
|
||||||
|
script_execution: "finished",
|
||||||
|
...tracePartial,
|
||||||
|
},
|
||||||
|
logbookEntries: logbookEntries || [],
|
||||||
|
});
|
@ -2,8 +2,7 @@ import { DemoTrace } from "./types";
|
|||||||
|
|
||||||
export const motionLightTrace: DemoTrace = {
|
export const motionLightTrace: DemoTrace = {
|
||||||
trace: {
|
trace: {
|
||||||
last_action: "action/3",
|
last_step: "action/3",
|
||||||
last_condition: null,
|
|
||||||
run_id: "1",
|
run_id: "1",
|
||||||
state: "stopped",
|
state: "stopped",
|
||||||
timestamp: {
|
timestamp: {
|
||||||
@ -14,6 +13,12 @@ export const motionLightTrace: DemoTrace = {
|
|||||||
domain: "automation",
|
domain: "automation",
|
||||||
item_id: "1614732497392",
|
item_id: "1614732497392",
|
||||||
trace: {
|
trace: {
|
||||||
|
"trigger/0": [
|
||||||
|
{
|
||||||
|
path: "trigger/0",
|
||||||
|
timestamp: "2021-03-25T04:36:51.223693+00:00",
|
||||||
|
},
|
||||||
|
],
|
||||||
"action/0": [
|
"action/0": [
|
||||||
{
|
{
|
||||||
path: "action/0",
|
path: "action/0",
|
||||||
@ -171,45 +176,7 @@ export const motionLightTrace: DemoTrace = {
|
|||||||
parent_id: "e22ddfd5f11dc4aad9a52fc10dab613b",
|
parent_id: "e22ddfd5f11dc4aad9a52fc10dab613b",
|
||||||
user_id: null,
|
user_id: null,
|
||||||
},
|
},
|
||||||
variables: {
|
script_execution: "finished",
|
||||||
trigger: {
|
|
||||||
platform: "state",
|
|
||||||
entity_id: "binary_sensor.pauluss_macbook_pro_camera_in_use",
|
|
||||||
from_state: {
|
|
||||||
entity_id: "binary_sensor.pauluss_macbook_pro_camera_in_use",
|
|
||||||
state: "off",
|
|
||||||
attributes: {
|
|
||||||
friendly_name: "Paulus’s MacBook Pro Camera In Use",
|
|
||||||
icon: "mdi:camera-off",
|
|
||||||
},
|
|
||||||
last_changed: "2021-03-14T06:06:29.235325+00:00",
|
|
||||||
last_updated: "2021-03-14T06:06:29.235325+00:00",
|
|
||||||
context: {
|
|
||||||
id: "ad4864c5ce957c38a07b50378eeb245d",
|
|
||||||
parent_id: null,
|
|
||||||
user_id: null,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
to_state: {
|
|
||||||
entity_id: "binary_sensor.pauluss_macbook_pro_camera_in_use",
|
|
||||||
state: "on",
|
|
||||||
attributes: {
|
|
||||||
friendly_name: "Paulus’s MacBook Pro Camera In Use",
|
|
||||||
icon: "mdi:camera",
|
|
||||||
},
|
|
||||||
last_changed: "2021-03-14T06:07:01.762009+00:00",
|
|
||||||
last_updated: "2021-03-14T06:07:01.762009+00:00",
|
|
||||||
context: {
|
|
||||||
id: "e22ddfd5f11dc4aad9a52fc10dab613b",
|
|
||||||
parent_id: null,
|
|
||||||
user_id: null,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
for: null,
|
|
||||||
attribute: null,
|
|
||||||
description: "state of binary_sensor.pauluss_macbook_pro_camera_in_use",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
logbookEntries: [
|
logbookEntries: [
|
||||||
{
|
{
|
||||||
|
102
gallery/src/demos/demo-automation-describe-action.ts
Normal file
102
gallery/src/demos/demo-automation-describe-action.ts
Normal file
@ -0,0 +1,102 @@
|
|||||||
|
import { safeDump } from "js-yaml";
|
||||||
|
import {
|
||||||
|
customElement,
|
||||||
|
html,
|
||||||
|
css,
|
||||||
|
LitElement,
|
||||||
|
TemplateResult,
|
||||||
|
property,
|
||||||
|
} from "lit-element";
|
||||||
|
import "../../../src/components/ha-card";
|
||||||
|
import { describeAction } from "../../../src/data/script_i18n";
|
||||||
|
import { provideHass } from "../../../src/fake_data/provide_hass";
|
||||||
|
import { HomeAssistant } from "../../../src/types";
|
||||||
|
|
||||||
|
const actions = [
|
||||||
|
{ wait_template: "{{ true }}", alias: "Something with an alias" },
|
||||||
|
{ delay: "0:05" },
|
||||||
|
{ wait_template: "{{ true }}" },
|
||||||
|
{
|
||||||
|
condition: "template",
|
||||||
|
value_template: "{{ true }}",
|
||||||
|
},
|
||||||
|
{ event: "happy_event" },
|
||||||
|
{
|
||||||
|
device_id: "abcdefgh",
|
||||||
|
domain: "plex",
|
||||||
|
entity_id: "media_player.kitchen",
|
||||||
|
},
|
||||||
|
{ scene: "scene.kitchen_morning" },
|
||||||
|
{
|
||||||
|
wait_for_trigger: [
|
||||||
|
{
|
||||||
|
platform: "state",
|
||||||
|
entity_id: "input_boolean.toggle_1",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
variables: {
|
||||||
|
hello: "world",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
service: "input_boolean.toggle",
|
||||||
|
target: {
|
||||||
|
entity_id: "input_boolean.toggle_4",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
@customElement("demo-automation-describe-action")
|
||||||
|
export class DemoAutomationDescribeAction extends LitElement {
|
||||||
|
@property({ attribute: false }) hass!: HomeAssistant;
|
||||||
|
|
||||||
|
protected render(): TemplateResult {
|
||||||
|
if (!this.hass) {
|
||||||
|
return html``;
|
||||||
|
}
|
||||||
|
return html`
|
||||||
|
<ha-card header="Actions">
|
||||||
|
${actions.map(
|
||||||
|
(conf) => html`
|
||||||
|
<div class="action">
|
||||||
|
<span>${describeAction(this.hass, conf as any)}</span>
|
||||||
|
<pre>${safeDump(conf)}</pre>
|
||||||
|
</div>
|
||||||
|
`
|
||||||
|
)}
|
||||||
|
</ha-card>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected firstUpdated(changedProps) {
|
||||||
|
super.firstUpdated(changedProps);
|
||||||
|
const hass = provideHass(this);
|
||||||
|
hass.updateTranslations(null, "en");
|
||||||
|
}
|
||||||
|
|
||||||
|
static get styles() {
|
||||||
|
return css`
|
||||||
|
ha-card {
|
||||||
|
max-width: 600px;
|
||||||
|
margin: 24px auto;
|
||||||
|
}
|
||||||
|
.action {
|
||||||
|
padding: 16px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
span {
|
||||||
|
margin-right: 16px;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface HTMLElementTagNameMap {
|
||||||
|
"demo-automation-describe-action": DemoAutomationDescribeAction;
|
||||||
|
}
|
||||||
|
}
|
65
gallery/src/demos/demo-automation-describe-condition.ts
Normal file
65
gallery/src/demos/demo-automation-describe-condition.ts
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
import { safeDump } from "js-yaml";
|
||||||
|
import {
|
||||||
|
customElement,
|
||||||
|
html,
|
||||||
|
css,
|
||||||
|
LitElement,
|
||||||
|
TemplateResult,
|
||||||
|
} from "lit-element";
|
||||||
|
import "../../../src/components/ha-card";
|
||||||
|
import { describeCondition } from "../../../src/data/automation_i18n";
|
||||||
|
|
||||||
|
const conditions = [
|
||||||
|
{ condition: "and" },
|
||||||
|
{ condition: "not" },
|
||||||
|
{ condition: "or" },
|
||||||
|
{ condition: "state" },
|
||||||
|
{ condition: "numeric_state" },
|
||||||
|
{ condition: "sun", after: "sunset" },
|
||||||
|
{ condition: "sun", after: "sunrise" },
|
||||||
|
{ condition: "zone" },
|
||||||
|
{ condition: "time" },
|
||||||
|
{ condition: "template" },
|
||||||
|
];
|
||||||
|
|
||||||
|
@customElement("demo-automation-describe-condition")
|
||||||
|
export class DemoAutomationDescribeCondition extends LitElement {
|
||||||
|
protected render(): TemplateResult {
|
||||||
|
return html`
|
||||||
|
<ha-card header="Conditions">
|
||||||
|
${conditions.map(
|
||||||
|
(conf) => html`
|
||||||
|
<div class="condition">
|
||||||
|
<span>${describeCondition(conf as any)}</span>
|
||||||
|
<pre>${safeDump(conf)}</pre>
|
||||||
|
</div>
|
||||||
|
`
|
||||||
|
)}
|
||||||
|
</ha-card>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
static get styles() {
|
||||||
|
return css`
|
||||||
|
ha-card {
|
||||||
|
max-width: 600px;
|
||||||
|
margin: 24px auto;
|
||||||
|
}
|
||||||
|
.condition {
|
||||||
|
padding: 16px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
span {
|
||||||
|
margin-right: 16px;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface HTMLElementTagNameMap {
|
||||||
|
"demo-automation-describe-condition": DemoAutomationDescribeCondition;
|
||||||
|
}
|
||||||
|
}
|
68
gallery/src/demos/demo-automation-describe-trigger.ts
Normal file
68
gallery/src/demos/demo-automation-describe-trigger.ts
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
import { safeDump } from "js-yaml";
|
||||||
|
import {
|
||||||
|
customElement,
|
||||||
|
html,
|
||||||
|
css,
|
||||||
|
LitElement,
|
||||||
|
TemplateResult,
|
||||||
|
} from "lit-element";
|
||||||
|
import "../../../src/components/ha-card";
|
||||||
|
import { describeTrigger } from "../../../src/data/automation_i18n";
|
||||||
|
|
||||||
|
const triggers = [
|
||||||
|
{ platform: "state" },
|
||||||
|
{ platform: "mqtt" },
|
||||||
|
{ platform: "geo_location" },
|
||||||
|
{ platform: "homeassistant" },
|
||||||
|
{ platform: "numeric_state" },
|
||||||
|
{ platform: "sun" },
|
||||||
|
{ platform: "time_pattern" },
|
||||||
|
{ platform: "webhook" },
|
||||||
|
{ platform: "zone" },
|
||||||
|
{ platform: "tag" },
|
||||||
|
{ platform: "time" },
|
||||||
|
{ platform: "template" },
|
||||||
|
{ platform: "event" },
|
||||||
|
];
|
||||||
|
|
||||||
|
@customElement("demo-automation-describe-trigger")
|
||||||
|
export class DemoAutomationDescribeTrigger extends LitElement {
|
||||||
|
protected render(): TemplateResult {
|
||||||
|
return html`
|
||||||
|
<ha-card header="Triggers">
|
||||||
|
${triggers.map(
|
||||||
|
(conf) => html`
|
||||||
|
<div class="trigger">
|
||||||
|
<span>${describeTrigger(conf as any)}</span>
|
||||||
|
<pre>${safeDump(conf)}</pre>
|
||||||
|
</div>
|
||||||
|
`
|
||||||
|
)}
|
||||||
|
</ha-card>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
static get styles() {
|
||||||
|
return css`
|
||||||
|
ha-card {
|
||||||
|
max-width: 600px;
|
||||||
|
margin: 24px auto;
|
||||||
|
}
|
||||||
|
.trigger {
|
||||||
|
padding: 16px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
span {
|
||||||
|
margin-right: 16px;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface HTMLElementTagNameMap {
|
||||||
|
"demo-automation-describe-trigger": DemoAutomationDescribeTrigger;
|
||||||
|
}
|
||||||
|
}
|
87
gallery/src/demos/demo-automation-trace-timeline.ts
Normal file
87
gallery/src/demos/demo-automation-trace-timeline.ts
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
import {
|
||||||
|
customElement,
|
||||||
|
html,
|
||||||
|
css,
|
||||||
|
LitElement,
|
||||||
|
TemplateResult,
|
||||||
|
property,
|
||||||
|
} from "lit-element";
|
||||||
|
import "../../../src/components/ha-card";
|
||||||
|
import "../../../src/components/trace/hat-script-graph";
|
||||||
|
import "../../../src/components/trace/hat-trace-timeline";
|
||||||
|
import { provideHass } from "../../../src/fake_data/provide_hass";
|
||||||
|
import { HomeAssistant } from "../../../src/types";
|
||||||
|
import { mockDemoTrace } from "../data/traces/mock-demo-trace";
|
||||||
|
import { DemoTrace } from "../data/traces/types";
|
||||||
|
|
||||||
|
const traces: DemoTrace[] = [
|
||||||
|
mockDemoTrace({ state: "running" }),
|
||||||
|
mockDemoTrace({ state: "debugged" }),
|
||||||
|
mockDemoTrace({ state: "stopped", script_execution: "failed_condition" }),
|
||||||
|
mockDemoTrace({ state: "stopped", script_execution: "failed_single" }),
|
||||||
|
mockDemoTrace({ state: "stopped", script_execution: "failed_max_runs" }),
|
||||||
|
mockDemoTrace({ state: "stopped", script_execution: "finished" }),
|
||||||
|
mockDemoTrace({ state: "stopped", script_execution: "aborted" }),
|
||||||
|
mockDemoTrace({
|
||||||
|
state: "stopped",
|
||||||
|
script_execution: "error",
|
||||||
|
error: 'Variable "beer" cannot be None',
|
||||||
|
}),
|
||||||
|
mockDemoTrace({ state: "stopped", script_execution: "cancelled" }),
|
||||||
|
];
|
||||||
|
|
||||||
|
@customElement("demo-automation-trace-timeline")
|
||||||
|
export class DemoAutomationTraceTimeline extends LitElement {
|
||||||
|
@property({ attribute: false }) hass?: HomeAssistant;
|
||||||
|
|
||||||
|
protected render(): TemplateResult {
|
||||||
|
if (!this.hass) {
|
||||||
|
return html``;
|
||||||
|
}
|
||||||
|
return html`
|
||||||
|
${traces.map(
|
||||||
|
(trace) => html`
|
||||||
|
<ha-card .header=${trace.trace.config.alias}>
|
||||||
|
<div class="card-content">
|
||||||
|
<hat-trace-timeline
|
||||||
|
.hass=${this.hass}
|
||||||
|
.trace=${trace.trace}
|
||||||
|
.logbookEntries=${trace.logbookEntries}
|
||||||
|
></hat-trace-timeline>
|
||||||
|
<button @click=${() => console.log(trace)}>Log trace</button>
|
||||||
|
</div>
|
||||||
|
</ha-card>
|
||||||
|
`
|
||||||
|
)}
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected firstUpdated(changedProps) {
|
||||||
|
super.firstUpdated(changedProps);
|
||||||
|
const hass = provideHass(this);
|
||||||
|
hass.updateTranslations(null, "en");
|
||||||
|
}
|
||||||
|
|
||||||
|
static get styles() {
|
||||||
|
return css`
|
||||||
|
ha-card {
|
||||||
|
max-width: 600px;
|
||||||
|
margin: 24px;
|
||||||
|
}
|
||||||
|
.card-content {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
button {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
right: 0;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface HTMLElementTagNameMap {
|
||||||
|
"demo-automation-trace-timeline": DemoAutomationTraceTimeline;
|
||||||
|
}
|
||||||
|
}
|
@ -4,9 +4,11 @@ import {
|
|||||||
css,
|
css,
|
||||||
LitElement,
|
LitElement,
|
||||||
TemplateResult,
|
TemplateResult,
|
||||||
|
internalProperty,
|
||||||
property,
|
property,
|
||||||
} from "lit-element";
|
} from "lit-element";
|
||||||
import "../../../src/components/ha-card";
|
import "../../../src/components/ha-card";
|
||||||
|
import "../../../src/components/trace/hat-script-graph";
|
||||||
import "../../../src/components/trace/hat-trace-timeline";
|
import "../../../src/components/trace/hat-trace-timeline";
|
||||||
import { provideHass } from "../../../src/fake_data/provide_hass";
|
import { provideHass } from "../../../src/fake_data/provide_hass";
|
||||||
import { HomeAssistant } from "../../../src/types";
|
import { HomeAssistant } from "../../../src/types";
|
||||||
@ -20,20 +22,38 @@ const traces: DemoTrace[] = [basicTrace, motionLightTrace];
|
|||||||
export class DemoAutomationTrace extends LitElement {
|
export class DemoAutomationTrace extends LitElement {
|
||||||
@property({ attribute: false }) hass?: HomeAssistant;
|
@property({ attribute: false }) hass?: HomeAssistant;
|
||||||
|
|
||||||
|
@internalProperty() private _selected = {};
|
||||||
|
|
||||||
protected render(): TemplateResult {
|
protected render(): TemplateResult {
|
||||||
if (!this.hass) {
|
if (!this.hass) {
|
||||||
return html``;
|
return html``;
|
||||||
}
|
}
|
||||||
return html`
|
return html`
|
||||||
${traces.map(
|
${traces.map(
|
||||||
(trace) => html`
|
(trace, idx) => html`
|
||||||
<ha-card .heading=${trace.trace.config.alias}>
|
<ha-card .header=${trace.trace.config.alias}>
|
||||||
<div class="card-content">
|
<div class="card-content">
|
||||||
|
<hat-script-graph
|
||||||
|
.trace=${trace.trace}
|
||||||
|
.selected=${this._selected[idx]}
|
||||||
|
@graph-node-selected=${(ev) => {
|
||||||
|
this._selected = { ...this._selected, [idx]: ev.detail.path };
|
||||||
|
}}
|
||||||
|
></hat-script-graph>
|
||||||
<hat-trace-timeline
|
<hat-trace-timeline
|
||||||
|
allowPick
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
.trace=${trace.trace}
|
.trace=${trace.trace}
|
||||||
.logbookEntries=${trace.logbookEntries}
|
.logbookEntries=${trace.logbookEntries}
|
||||||
|
.selectedPath=${this._selected[idx]}
|
||||||
|
@value-changed=${(ev) => {
|
||||||
|
this._selected = {
|
||||||
|
...this._selected,
|
||||||
|
[idx]: ev.detail.value,
|
||||||
|
};
|
||||||
|
}}
|
||||||
></hat-trace-timeline>
|
></hat-trace-timeline>
|
||||||
|
<button @click=${() => console.log(trace)}>Log trace</button>
|
||||||
</div>
|
</div>
|
||||||
</ha-card>
|
</ha-card>
|
||||||
`
|
`
|
||||||
@ -53,6 +73,20 @@ export class DemoAutomationTrace extends LitElement {
|
|||||||
max-width: 600px;
|
max-width: 600px;
|
||||||
margin: 24px;
|
margin: 24px;
|
||||||
}
|
}
|
||||||
|
.card-content {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
.card-content > * {
|
||||||
|
margin-right: 16px;
|
||||||
|
}
|
||||||
|
.card-content > *:last-child {
|
||||||
|
margin-right: 0;
|
||||||
|
}
|
||||||
|
button {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
right: 0;
|
||||||
|
}
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -10,10 +10,10 @@ function patch(version) {
|
|||||||
|
|
||||||
function today() {
|
function today() {
|
||||||
const now = new Date();
|
const now = new Date();
|
||||||
return `${now.getFullYear()}${String(now.getMonth() + 1).padStart(
|
return `${now.getUTCFullYear()}${String(now.getUTCMonth() + 1).padStart(
|
||||||
2,
|
2,
|
||||||
"0"
|
"0"
|
||||||
)}${String(now.getDate()).padStart(2, "0")}.0`;
|
)}${String(now.getUTCDate()).padStart(2, "0")}.0`;
|
||||||
}
|
}
|
||||||
|
|
||||||
function auto(version) {
|
function auto(version) {
|
||||||
|
2
setup.py
2
setup.py
@ -2,7 +2,7 @@ from setuptools import setup, find_packages
|
|||||||
|
|
||||||
setup(
|
setup(
|
||||||
name="home-assistant-frontend",
|
name="home-assistant-frontend",
|
||||||
version="20210331.0",
|
version="20210402.0",
|
||||||
description="The Home Assistant frontend",
|
description="The Home Assistant frontend",
|
||||||
url="https://github.com/home-assistant/home-assistant-polymer",
|
url="https://github.com/home-assistant/home-assistant-polymer",
|
||||||
author="The Home Assistant Authors",
|
author="The Home Assistant Authors",
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
export const ensureArray = (value?: any) => {
|
export function ensureArray(value: undefined): undefined;
|
||||||
if (!value || Array.isArray(value)) {
|
export function ensureArray<T>(value: T | T[]): T[];
|
||||||
|
export function ensureArray(value) {
|
||||||
|
if (value === undefined || Array.isArray(value)) {
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
return [value];
|
return [value];
|
||||||
};
|
}
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
const isTemplateRegex = new RegExp("{%|{{|{#");
|
const isTemplateRegex = new RegExp("{%|{{");
|
||||||
|
|
||||||
export const isTemplate = (value: string): boolean =>
|
export const isTemplate = (value: string): boolean =>
|
||||||
isTemplateRegex.test(value);
|
isTemplateRegex.test(value);
|
||||||
|
|
||||||
@ -11,7 +12,7 @@ export const hasTemplate = (value: unknown): boolean => {
|
|||||||
}
|
}
|
||||||
if (typeof value === "object") {
|
if (typeof value === "object") {
|
||||||
const values = Array.isArray(value) ? value : Object.values(value!);
|
const values = Array.isArray(value) ? value : Object.values(value!);
|
||||||
return values.some((val) => hasTemplate(val));
|
return values.some((val) => val && hasTemplate(val));
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
|
@ -63,7 +63,7 @@ export interface DataTableSortColumnData {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface DataTableColumnData extends DataTableSortColumnData {
|
export interface DataTableColumnData extends DataTableSortColumnData {
|
||||||
title: string;
|
title: TemplateResult | string;
|
||||||
type?: "numeric" | "icon" | "icon-button";
|
type?: "numeric" | "icon" | "icon-button";
|
||||||
template?: <T>(data: any, row: T) => TemplateResult | string;
|
template?: <T>(data: any, row: T) => TemplateResult | string;
|
||||||
width?: string;
|
width?: string;
|
||||||
@ -74,7 +74,7 @@ export interface DataTableColumnData extends DataTableSortColumnData {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type ClonedDataTableColumnData = Omit<DataTableColumnData, "title"> & {
|
type ClonedDataTableColumnData = Omit<DataTableColumnData, "title"> & {
|
||||||
title?: string;
|
title?: TemplateResult | string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export interface DataTableRowData {
|
export interface DataTableRowData {
|
||||||
|
@ -125,35 +125,41 @@ export class HaTargetPicker extends SubscribeMixin(LitElement) {
|
|||||||
return html``;
|
return html``;
|
||||||
}
|
}
|
||||||
return html`<div class="mdc-chip-set items">
|
return html`<div class="mdc-chip-set items">
|
||||||
${ensureArray(this.value?.area_id)?.map((area_id) => {
|
${this.value?.area_id
|
||||||
const area = this._areas![area_id];
|
? ensureArray(this.value.area_id).map((area_id) => {
|
||||||
return this._renderChip(
|
const area = this._areas![area_id];
|
||||||
"area_id",
|
return this._renderChip(
|
||||||
area_id,
|
"area_id",
|
||||||
area?.name || area_id,
|
area_id,
|
||||||
undefined,
|
area?.name || area_id,
|
||||||
mdiSofa
|
undefined,
|
||||||
);
|
mdiSofa
|
||||||
})}
|
);
|
||||||
${ensureArray(this.value?.device_id)?.map((device_id) => {
|
})
|
||||||
const device = this._devices![device_id];
|
: ""}
|
||||||
return this._renderChip(
|
${this.value?.device_id
|
||||||
"device_id",
|
? ensureArray(this.value.device_id).map((device_id) => {
|
||||||
device_id,
|
const device = this._devices![device_id];
|
||||||
device ? computeDeviceName(device, this.hass) : device_id,
|
return this._renderChip(
|
||||||
undefined,
|
"device_id",
|
||||||
mdiDevices
|
device_id,
|
||||||
);
|
device ? computeDeviceName(device, this.hass) : device_id,
|
||||||
})}
|
undefined,
|
||||||
${ensureArray(this.value?.entity_id)?.map((entity_id) => {
|
mdiDevices
|
||||||
const entity = this.hass.states[entity_id];
|
);
|
||||||
return this._renderChip(
|
})
|
||||||
"entity_id",
|
: ""}
|
||||||
entity_id,
|
${this.value?.entity_id
|
||||||
entity ? computeStateName(entity) : entity_id,
|
? ensureArray(this.value.entity_id).map((entity_id) => {
|
||||||
entity ? stateIcon(entity) : undefined
|
const entity = this.hass.states[entity_id];
|
||||||
);
|
return this._renderChip(
|
||||||
})}
|
"entity_id",
|
||||||
|
entity_id,
|
||||||
|
entity ? computeStateName(entity) : entity_id,
|
||||||
|
entity ? stateIcon(entity) : undefined
|
||||||
|
);
|
||||||
|
})
|
||||||
|
: ""}
|
||||||
</div>
|
</div>
|
||||||
${this._renderPicker()}
|
${this._renderPicker()}
|
||||||
<div class="mdc-chip-set">
|
<div class="mdc-chip-set">
|
||||||
|
@ -48,6 +48,7 @@ import {
|
|||||||
WaitAction,
|
WaitAction,
|
||||||
WaitForTriggerAction,
|
WaitForTriggerAction,
|
||||||
} from "../../data/script";
|
} from "../../data/script";
|
||||||
|
import { ensureArray } from "../../common/ensure-array";
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
interface HASSDomEvents {
|
interface HASSDomEvents {
|
||||||
@ -93,7 +94,7 @@ class HatScriptGraph extends LitElement {
|
|||||||
const path = `condition/${i}`;
|
const path = `condition/${i}`;
|
||||||
const trace = this.trace.trace[path] as ConditionTraceStep[] | undefined;
|
const trace = this.trace.trace[path] as ConditionTraceStep[] | undefined;
|
||||||
const track_path =
|
const track_path =
|
||||||
trace === undefined ? 0 : trace![0].result.result ? 1 : 2;
|
trace?.[0].result === undefined ? 0 : trace[0].result.result ? 1 : 2;
|
||||||
if (trace) {
|
if (trace) {
|
||||||
this.trackedNodes[path] = { config, path };
|
this.trackedNodes[path] = { config, path };
|
||||||
}
|
}
|
||||||
@ -139,7 +140,7 @@ class HatScriptGraph extends LitElement {
|
|||||||
|
|
||||||
private render_choose_node(config: ChooseAction, path: string) {
|
private render_choose_node(config: ChooseAction, path: string) {
|
||||||
const trace = this.trace.trace[path] as ChooseActionTraceStep[] | undefined;
|
const trace = this.trace.trace[path] as ChooseActionTraceStep[] | undefined;
|
||||||
const trace_path = trace
|
const trace_path = trace?.[0].result
|
||||||
? trace[0].result.choice === "default"
|
? trace[0].result.choice === "default"
|
||||||
? [config.choose.length]
|
? [config.choose.length]
|
||||||
: [trace[0].result.choice]
|
: [trace[0].result.choice]
|
||||||
@ -173,7 +174,7 @@ class HatScriptGraph extends LitElement {
|
|||||||
.iconPath=${mdiCheckBoxOutline}
|
.iconPath=${mdiCheckBoxOutline}
|
||||||
nofocus
|
nofocus
|
||||||
class=${classMap({
|
class=${classMap({
|
||||||
track: trace !== undefined && trace[0].result.choice === i,
|
track: trace !== undefined && trace[0].result?.choice === i,
|
||||||
})}
|
})}
|
||||||
></hat-graph-node>
|
></hat-graph-node>
|
||||||
${branch.sequence.map((action, j) =>
|
${branch.sequence.map((action, j) =>
|
||||||
@ -188,7 +189,7 @@ class HatScriptGraph extends LitElement {
|
|||||||
nofocus
|
nofocus
|
||||||
class=${classMap({
|
class=${classMap({
|
||||||
track:
|
track:
|
||||||
trace !== undefined && trace[0].result.choice === "default",
|
trace !== undefined && trace[0].result?.choice === "default",
|
||||||
})}
|
})}
|
||||||
></hat-graph-node>
|
></hat-graph-node>
|
||||||
${config.default?.map((action, i) =>
|
${config.default?.map((action, i) =>
|
||||||
@ -200,8 +201,9 @@ class HatScriptGraph extends LitElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private render_condition_node(node: Condition, path: string) {
|
private render_condition_node(node: Condition, path: string) {
|
||||||
const trace: any = this.trace.trace[path];
|
const trace = (this.trace.trace[path] as ConditionTraceStep[]) || undefined;
|
||||||
const track_path = trace === undefined ? 0 : trace[0].result.result ? 1 : 2;
|
const track_path =
|
||||||
|
trace?.[0].result === undefined ? 0 : trace[0].result.result ? 1 : 2;
|
||||||
return html`
|
return html`
|
||||||
<hat-graph
|
<hat-graph
|
||||||
branching
|
branching
|
||||||
@ -218,7 +220,7 @@ class HatScriptGraph extends LitElement {
|
|||||||
<hat-graph-node
|
<hat-graph-node
|
||||||
slot="head"
|
slot="head"
|
||||||
class=${classMap({
|
class=${classMap({
|
||||||
track: trace,
|
track: Boolean(trace),
|
||||||
})}
|
})}
|
||||||
.iconPath=${mdiAbTesting}
|
.iconPath=${mdiAbTesting}
|
||||||
nofocus
|
nofocus
|
||||||
@ -411,16 +413,14 @@ class HatScriptGraph extends LitElement {
|
|||||||
|
|
||||||
const manual_triggered = this.trace && "trigger" in this.trace.trace;
|
const manual_triggered = this.trace && "trigger" in this.trace.trace;
|
||||||
let track_path = manual_triggered ? undefined : [0];
|
let track_path = manual_triggered ? undefined : [0];
|
||||||
const trigger_nodes = (Array.isArray(this.trace.config.trigger)
|
const trigger_nodes = ensureArray(this.trace.config.trigger).map(
|
||||||
? this.trace.config.trigger
|
(trigger, i) => {
|
||||||
: [this.trace.config.trigger]
|
if (this.trace && `trigger/${i}` in this.trace.trace) {
|
||||||
).map((trigger, i) => {
|
track_path = [i];
|
||||||
if (this.trace && `trigger/${i}` in this.trace.trace) {
|
}
|
||||||
track_path = [i];
|
return this.render_trigger(trigger, i);
|
||||||
}
|
}
|
||||||
return this.render_trigger(trigger, i);
|
);
|
||||||
});
|
|
||||||
|
|
||||||
return html`
|
return html`
|
||||||
<hat-graph class="parent">
|
<hat-graph class="parent">
|
||||||
<div></div>
|
<div></div>
|
||||||
@ -434,16 +434,13 @@ class HatScriptGraph extends LitElement {
|
|||||||
${trigger_nodes}
|
${trigger_nodes}
|
||||||
</hat-graph>
|
</hat-graph>
|
||||||
<hat-graph id="condition">
|
<hat-graph id="condition">
|
||||||
${(!this.trace.config.condition ||
|
${ensureArray(this.trace.config.condition)?.map((condition, i) =>
|
||||||
Array.isArray(this.trace.config.condition)
|
this.render_condition(condition!, i)
|
||||||
? this.trace.config.condition
|
)}
|
||||||
: [this.trace.config.condition]
|
|
||||||
)?.map((condition, i) => this.render_condition(condition, i))}
|
|
||||||
</hat-graph>
|
</hat-graph>
|
||||||
${(Array.isArray(this.trace.config.action)
|
${ensureArray(this.trace.config.action).map((action, i) =>
|
||||||
? this.trace.config.action
|
this.render_node(action, `action/${i}`)
|
||||||
: [this.trace.config.action]
|
)}
|
||||||
).map((action, i) => this.render_node(action, `action/${i}`))}
|
|
||||||
</hat-graph>
|
</hat-graph>
|
||||||
<div class="actions">
|
<div class="actions">
|
||||||
<mwc-icon-button
|
<mwc-icon-button
|
||||||
|
@ -20,9 +20,11 @@ import { HomeAssistant } from "../../types";
|
|||||||
import "./ha-timeline";
|
import "./ha-timeline";
|
||||||
import type { HaTimeline } from "./ha-timeline";
|
import type { HaTimeline } from "./ha-timeline";
|
||||||
import {
|
import {
|
||||||
|
mdiAlertCircle,
|
||||||
mdiCircle,
|
mdiCircle,
|
||||||
mdiCircleOutline,
|
mdiCircleOutline,
|
||||||
mdiPauseCircleOutline,
|
mdiProgressClock,
|
||||||
|
mdiProgressWrench,
|
||||||
mdiRecordCircleOutline,
|
mdiRecordCircleOutline,
|
||||||
} from "@mdi/js";
|
} from "@mdi/js";
|
||||||
import { LogbookEntry } from "../../data/logbook";
|
import { LogbookEntry } from "../../data/logbook";
|
||||||
@ -33,6 +35,8 @@ import {
|
|||||||
} 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";
|
import { fireEvent } from "../../common/dom/fire_event";
|
||||||
|
import { describeAction } from "../../data/script_i18n";
|
||||||
|
import { ifDefined } from "lit-html/directives/if-defined";
|
||||||
|
|
||||||
const LOGBOOK_ENTRIES_BEFORE_FOLD = 2;
|
const LOGBOOK_ENTRIES_BEFORE_FOLD = 2;
|
||||||
|
|
||||||
@ -262,7 +266,7 @@ class ActionRenderer {
|
|||||||
return this._handleChoose(index);
|
return this._handleChoose(index);
|
||||||
}
|
}
|
||||||
|
|
||||||
this._renderEntry(path, data.alias || actionType);
|
this._renderEntry(path, describeAction(this.hass, data, actionType));
|
||||||
return index + 1;
|
return index + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -272,7 +276,7 @@ class ActionRenderer {
|
|||||||
`Triggered ${
|
`Triggered ${
|
||||||
triggerStep.path === "trigger"
|
triggerStep.path === "trigger"
|
||||||
? "manually"
|
? "manually"
|
||||||
: `by the ${triggerStep.changed_variables.trigger.description}`
|
: `by the ${this.trace.trigger}`
|
||||||
} at
|
} at
|
||||||
${formatDateTimeWithSeconds(
|
${formatDateTimeWithSeconds(
|
||||||
new Date(triggerStep.timestamp),
|
new Date(triggerStep.timestamp),
|
||||||
@ -302,7 +306,7 @@ class ActionRenderer {
|
|||||||
const startLevel = choosePath.split("/").length - 1;
|
const startLevel = choosePath.split("/").length - 1;
|
||||||
|
|
||||||
const chooseTrace = this._getItem(index)[0] as ChooseActionTraceStep;
|
const chooseTrace = this._getItem(index)[0] as ChooseActionTraceStep;
|
||||||
const defaultExecuted = chooseTrace.result.choice === "default";
|
const defaultExecuted = chooseTrace.result?.choice === "default";
|
||||||
const chooseConfig = this._getDataFromPath(
|
const chooseConfig = this._getDataFromPath(
|
||||||
this.keys[index]
|
this.keys[index]
|
||||||
) as ChooseAction;
|
) as ChooseAction;
|
||||||
@ -312,11 +316,14 @@ class ActionRenderer {
|
|||||||
this._renderEntry(choosePath, `${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 | undefined;
|
||||||
const choiceName =
|
const choiceName = choiceConfig
|
||||||
choiceConfig.alias || `Choice ${chooseTrace.result.choice}`;
|
? `${
|
||||||
this._renderEntry(choosePath, `${name}: ${choiceName} executed`);
|
choiceConfig.alias || `Choice ${chooseTrace.result?.choice}`
|
||||||
|
} executed`
|
||||||
|
: `Error: ${chooseTrace.error}`;
|
||||||
|
this._renderEntry(choosePath, `${name}: ${choiceName}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
let i;
|
let i;
|
||||||
@ -331,7 +338,10 @@ class ActionRenderer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// We're going to skip all conditions
|
// We're going to skip all conditions
|
||||||
if (parts[startLevel + 3] === "sequence") {
|
if (
|
||||||
|
(defaultExecuted && parts[startLevel + 1] === "default") ||
|
||||||
|
(!defaultExecuted && parts[startLevel + 3] === "sequence")
|
||||||
|
) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -414,29 +424,92 @@ export class HaAutomationTracer extends LitElement {
|
|||||||
|
|
||||||
logbookRenderer.flush();
|
logbookRenderer.flush();
|
||||||
|
|
||||||
|
// Render footer
|
||||||
|
const renderFinishedAt = () =>
|
||||||
|
formatDateTimeWithSeconds(
|
||||||
|
new Date(this.trace!.timestamp.finish!),
|
||||||
|
this.hass.locale
|
||||||
|
);
|
||||||
|
const renderRuntime = () => `(runtime:
|
||||||
|
${(
|
||||||
|
(new Date(this.trace!.timestamp.finish!).getTime() -
|
||||||
|
new Date(this.trace!.timestamp.start).getTime()) /
|
||||||
|
1000
|
||||||
|
).toFixed(2)}
|
||||||
|
seconds)`;
|
||||||
|
|
||||||
|
let entry: {
|
||||||
|
description: TemplateResult | string;
|
||||||
|
icon: string;
|
||||||
|
className?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
if (this.trace.state === "running") {
|
||||||
|
entry = {
|
||||||
|
description: "Still running",
|
||||||
|
icon: mdiProgressClock,
|
||||||
|
};
|
||||||
|
} else if (this.trace.state === "debugged") {
|
||||||
|
entry = {
|
||||||
|
description: "Debugged",
|
||||||
|
icon: mdiProgressWrench,
|
||||||
|
};
|
||||||
|
} else if (this.trace.script_execution === "finished") {
|
||||||
|
entry = {
|
||||||
|
description: `Finished at ${renderFinishedAt()} ${renderRuntime()}`,
|
||||||
|
icon: mdiCircle,
|
||||||
|
};
|
||||||
|
} else if (this.trace.script_execution === "aborted") {
|
||||||
|
entry = {
|
||||||
|
description: `Aborted at ${renderFinishedAt()} ${renderRuntime()}`,
|
||||||
|
icon: mdiAlertCircle,
|
||||||
|
};
|
||||||
|
} else if (this.trace.script_execution === "cancelled") {
|
||||||
|
entry = {
|
||||||
|
description: `Cancelled at ${renderFinishedAt()} ${renderRuntime()}`,
|
||||||
|
icon: mdiAlertCircle,
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
let reason: string;
|
||||||
|
let isError = false;
|
||||||
|
let extra: TemplateResult | undefined;
|
||||||
|
|
||||||
|
switch (this.trace.script_execution) {
|
||||||
|
case "failed_condition":
|
||||||
|
reason = "a condition failed";
|
||||||
|
break;
|
||||||
|
case "failed_single":
|
||||||
|
reason = "only a single execution is allowed";
|
||||||
|
break;
|
||||||
|
case "failed_max_runs":
|
||||||
|
reason = "maximum number of parallel runs reached";
|
||||||
|
break;
|
||||||
|
case "error":
|
||||||
|
reason = "an error was encountered";
|
||||||
|
isError = true;
|
||||||
|
extra = html`<br /><br />${this.trace.error!}`;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
reason = `of unknown reason "${this.trace.script_execution}"`;
|
||||||
|
isError = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
entry = {
|
||||||
|
description: html`Stopped because ${reason} at ${renderFinishedAt()}
|
||||||
|
${renderRuntime()}${extra || ""}`,
|
||||||
|
icon: mdiAlertCircle,
|
||||||
|
className: isError ? "error" : undefined,
|
||||||
|
};
|
||||||
|
}
|
||||||
// null means it was stopped by a condition
|
// null means it was stopped by a condition
|
||||||
if (this.trace.last_action !== null) {
|
if (entry) {
|
||||||
entries.push(html`
|
entries.push(html`
|
||||||
<ha-timeline
|
<ha-timeline
|
||||||
lastItem
|
lastItem
|
||||||
.icon=${this.trace.timestamp.finish
|
.icon=${entry.icon}
|
||||||
? mdiCircle
|
class=${ifDefined(entry.className)}
|
||||||
: mdiPauseCircleOutline}
|
|
||||||
>
|
>
|
||||||
${this.trace.timestamp.finish
|
${entry.description}
|
||||||
? html`Finished at
|
|
||||||
${formatDateTimeWithSeconds(
|
|
||||||
new Date(this.trace.timestamp.finish),
|
|
||||||
this.hass.locale
|
|
||||||
)}
|
|
||||||
(runtime:
|
|
||||||
${(
|
|
||||||
(new Date(this.trace.timestamp.finish!).getTime() -
|
|
||||||
new Date(this.trace.timestamp.start).getTime()) /
|
|
||||||
1000
|
|
||||||
).toFixed(2)}
|
|
||||||
seconds)`
|
|
||||||
: "Still running"}
|
|
||||||
</ha-timeline>
|
</ha-timeline>
|
||||||
`);
|
`);
|
||||||
}
|
}
|
||||||
@ -468,17 +541,20 @@ export class HaAutomationTracer extends LitElement {
|
|||||||
this.shadowRoot!.querySelectorAll<HaTimeline>(
|
this.shadowRoot!.querySelectorAll<HaTimeline>(
|
||||||
"ha-timeline[data-path]"
|
"ha-timeline[data-path]"
|
||||||
).forEach((el) => {
|
).forEach((el) => {
|
||||||
el.style.setProperty(
|
el.toggleAttribute("selected", this.selectedPath === el.dataset.path);
|
||||||
"--timeline-ball-color",
|
if (!this.allowPick || el.tabIndex === 0) {
|
||||||
this.selectedPath === el.dataset.path ? "var(--primary-color)" : null
|
|
||||||
);
|
|
||||||
if (!this.allowPick || el.dataset.upgraded) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
el.dataset.upgraded = "1";
|
el.tabIndex = 0;
|
||||||
el.addEventListener("click", () => {
|
const selectEl = () => {
|
||||||
this.selectedPath = el.dataset.path;
|
this.selectedPath = el.dataset.path;
|
||||||
fireEvent(this, "value-changed", { value: el.dataset.path });
|
fireEvent(this, "value-changed", { value: el.dataset.path });
|
||||||
|
};
|
||||||
|
el.addEventListener("click", selectEl);
|
||||||
|
el.addEventListener("keydown", (ev: KeyboardEvent) => {
|
||||||
|
if (ev.key === "Enter" || ev.key === " ") {
|
||||||
|
selectEl();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
el.addEventListener("mouseover", () => {
|
el.addEventListener("mouseover", () => {
|
||||||
el.raised = true;
|
el.raised = true;
|
||||||
@ -499,6 +575,17 @@ export class HaAutomationTracer extends LitElement {
|
|||||||
ha-timeline[data-path] {
|
ha-timeline[data-path] {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
ha-timeline[selected] {
|
||||||
|
--timeline-ball-color: var(--primary-color);
|
||||||
|
}
|
||||||
|
ha-timeline:focus {
|
||||||
|
outline: none;
|
||||||
|
--timeline-ball-color: var(--accent-color);
|
||||||
|
}
|
||||||
|
.error {
|
||||||
|
--timeline-ball-color: var(--error-color);
|
||||||
|
color: var(--error-color);
|
||||||
|
}
|
||||||
`,
|
`,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
@ -238,6 +238,9 @@ export const deleteAutomation = (hass: HomeAssistant, id: string) =>
|
|||||||
|
|
||||||
let inititialAutomationEditorData: Partial<AutomationConfig> | undefined;
|
let inititialAutomationEditorData: Partial<AutomationConfig> | undefined;
|
||||||
|
|
||||||
|
export const getAutomationConfig = (hass: HomeAssistant, id: string) =>
|
||||||
|
hass.callApi<AutomationConfig>("GET", `config/automation/config/${id}`);
|
||||||
|
|
||||||
export const showAutomationEditor = (
|
export const showAutomationEditor = (
|
||||||
el: HTMLElement,
|
el: HTMLElement,
|
||||||
data?: Partial<AutomationConfig>
|
data?: Partial<AutomationConfig>
|
||||||
|
15
src/data/automation_i18n.ts
Normal file
15
src/data/automation_i18n.ts
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
import { Trigger, Condition } from "./automation";
|
||||||
|
|
||||||
|
export const describeTrigger = (trigger: Trigger) => {
|
||||||
|
return `${trigger.platform} trigger`;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const describeCondition = (condition: Condition) => {
|
||||||
|
if (condition.alias) {
|
||||||
|
return condition.alias;
|
||||||
|
}
|
||||||
|
if (condition.condition === "template") {
|
||||||
|
return "Test a template";
|
||||||
|
}
|
||||||
|
return `${condition.condition} condition`;
|
||||||
|
};
|
@ -45,7 +45,7 @@ export interface DataEntryFlowStepCreateEntry {
|
|||||||
flow_id: string;
|
flow_id: string;
|
||||||
handler: string;
|
handler: string;
|
||||||
title: string;
|
title: string;
|
||||||
result: ConfigEntry;
|
result?: ConfigEntry;
|
||||||
description: string;
|
description: string;
|
||||||
description_placeholders: Record<string, string>;
|
description_placeholders: Record<string, string>;
|
||||||
}
|
}
|
||||||
|
@ -37,7 +37,8 @@ export interface EventAction {
|
|||||||
|
|
||||||
export interface ServiceAction {
|
export interface ServiceAction {
|
||||||
alias?: string;
|
alias?: string;
|
||||||
service: string;
|
service?: string;
|
||||||
|
service_template?: string;
|
||||||
entity_id?: string;
|
entity_id?: string;
|
||||||
target?: HassServiceTarget;
|
target?: HassServiceTarget;
|
||||||
data?: Record<string, any>;
|
data?: Record<string, any>;
|
||||||
@ -76,7 +77,7 @@ export interface WaitAction {
|
|||||||
|
|
||||||
export interface WaitForTriggerAction {
|
export interface WaitForTriggerAction {
|
||||||
alias?: string;
|
alias?: string;
|
||||||
wait_for_trigger: Trigger[];
|
wait_for_trigger: Trigger | Trigger[];
|
||||||
timeout?: number;
|
timeout?: number;
|
||||||
continue_on_timeout?: boolean;
|
continue_on_timeout?: boolean;
|
||||||
}
|
}
|
||||||
@ -115,6 +116,16 @@ export interface ChooseAction {
|
|||||||
default?: Action[];
|
default?: Action[];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface VariablesAction {
|
||||||
|
alias?: string;
|
||||||
|
variables: Record<string, unknown>;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface UnknownAction {
|
||||||
|
alias?: string;
|
||||||
|
[key: string]: unknown;
|
||||||
|
}
|
||||||
|
|
||||||
export type Action =
|
export type Action =
|
||||||
| EventAction
|
| EventAction
|
||||||
| DeviceAction
|
| DeviceAction
|
||||||
@ -125,7 +136,26 @@ export type Action =
|
|||||||
| WaitAction
|
| WaitAction
|
||||||
| WaitForTriggerAction
|
| WaitForTriggerAction
|
||||||
| RepeatAction
|
| RepeatAction
|
||||||
| ChooseAction;
|
| ChooseAction
|
||||||
|
| VariablesAction
|
||||||
|
| UnknownAction;
|
||||||
|
|
||||||
|
export interface ActionTypes {
|
||||||
|
delay: DelayAction;
|
||||||
|
wait_template: WaitAction;
|
||||||
|
check_condition: Condition;
|
||||||
|
fire_event: EventAction;
|
||||||
|
device_action: DeviceAction;
|
||||||
|
activate_scene: SceneAction;
|
||||||
|
repeat: RepeatAction;
|
||||||
|
choose: ChooseAction;
|
||||||
|
wait_for_trigger: WaitForTriggerAction;
|
||||||
|
variables: VariablesAction;
|
||||||
|
service: ServiceAction;
|
||||||
|
unknown: UnknownAction;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type ActionType = keyof ActionTypes;
|
||||||
|
|
||||||
export const triggerScript = (
|
export const triggerScript = (
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
@ -166,7 +196,7 @@ export const getScriptEditorInitData = () => {
|
|||||||
return data;
|
return data;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getActionType = (action: Action) => {
|
export const getActionType = (action: Action): ActionType => {
|
||||||
// Check based on config_validation.py#determine_script_action
|
// Check based on config_validation.py#determine_script_action
|
||||||
if ("delay" in action) {
|
if ("delay" in action) {
|
||||||
return "delay";
|
return "delay";
|
||||||
|
142
src/data/script_i18n.ts
Normal file
142
src/data/script_i18n.ts
Normal file
@ -0,0 +1,142 @@
|
|||||||
|
import secondsToDuration from "../common/datetime/seconds_to_duration";
|
||||||
|
import { ensureArray } from "../common/ensure-array";
|
||||||
|
import { computeStateName } from "../common/entity/compute_state_name";
|
||||||
|
import { isTemplate } from "../common/string/has-template";
|
||||||
|
import { HomeAssistant } from "../types";
|
||||||
|
import { Condition } from "./automation";
|
||||||
|
import { describeCondition, describeTrigger } from "./automation_i18n";
|
||||||
|
import {
|
||||||
|
ActionType,
|
||||||
|
getActionType,
|
||||||
|
DelayAction,
|
||||||
|
SceneAction,
|
||||||
|
WaitForTriggerAction,
|
||||||
|
ActionTypes,
|
||||||
|
VariablesAction,
|
||||||
|
EventAction,
|
||||||
|
} from "./script";
|
||||||
|
|
||||||
|
export const describeAction = <T extends ActionType>(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
action: ActionTypes[T],
|
||||||
|
actionType?: T
|
||||||
|
): string => {
|
||||||
|
if (action.alias) {
|
||||||
|
return action.alias;
|
||||||
|
}
|
||||||
|
if (!actionType) {
|
||||||
|
actionType = getActionType(action) as T;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (actionType === "service") {
|
||||||
|
const config = action as ActionTypes["service"];
|
||||||
|
|
||||||
|
let base: string | undefined;
|
||||||
|
|
||||||
|
if (
|
||||||
|
config.service_template ||
|
||||||
|
(config.service && isTemplate(config.service))
|
||||||
|
) {
|
||||||
|
base = "Call a service based on a template";
|
||||||
|
} else if (config.service) {
|
||||||
|
base = `Call service ${config.service}`;
|
||||||
|
} else {
|
||||||
|
return actionType;
|
||||||
|
}
|
||||||
|
if (config.target) {
|
||||||
|
const targets: string[] = [];
|
||||||
|
|
||||||
|
for (const [key, label] of Object.entries({
|
||||||
|
area_id: "areas",
|
||||||
|
device_id: "devices",
|
||||||
|
entity_id: "entities",
|
||||||
|
})) {
|
||||||
|
if (!(key in config.target)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const keyConf: string[] = Array.isArray(config.target[key])
|
||||||
|
? config.target[key]
|
||||||
|
: [config.target[key]];
|
||||||
|
|
||||||
|
const values: string[] = [];
|
||||||
|
|
||||||
|
let renderValues = true;
|
||||||
|
|
||||||
|
for (const targetThing of keyConf) {
|
||||||
|
if (isTemplate(targetThing)) {
|
||||||
|
targets.push(`templated ${label}`);
|
||||||
|
renderValues = false;
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
values.push(targetThing);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (renderValues) {
|
||||||
|
targets.push(`${label} ${values.join(", ")}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (targets.length > 0) {
|
||||||
|
base += ` on ${targets.join(", ")}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return base;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (actionType === "delay") {
|
||||||
|
const config = action as DelayAction;
|
||||||
|
|
||||||
|
let duration: string;
|
||||||
|
|
||||||
|
if (typeof config.delay === "number") {
|
||||||
|
duration = `for ${secondsToDuration(config.delay)!}`;
|
||||||
|
} else if (typeof config.delay === "string") {
|
||||||
|
duration = isTemplate(config.delay)
|
||||||
|
? "based on a template"
|
||||||
|
: `for ${config.delay}`;
|
||||||
|
} else {
|
||||||
|
duration = `for ${JSON.stringify(config.delay)}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return `Delay ${duration}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (actionType === "activate_scene") {
|
||||||
|
const config = action as SceneAction;
|
||||||
|
const sceneStateObj = hass.states[config.scene];
|
||||||
|
return `Activate scene ${
|
||||||
|
sceneStateObj ? computeStateName(sceneStateObj) : config.scene
|
||||||
|
}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (actionType === "wait_for_trigger") {
|
||||||
|
const config = action as WaitForTriggerAction;
|
||||||
|
return `Wait for ${ensureArray(config.wait_for_trigger)
|
||||||
|
.map((trigger) => describeTrigger(trigger))
|
||||||
|
.join(", ")}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (actionType === "variables") {
|
||||||
|
const config = action as VariablesAction;
|
||||||
|
return `Define variables ${Object.keys(config.variables).join(", ")}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (actionType === "fire_event") {
|
||||||
|
const config = action as EventAction;
|
||||||
|
if (isTemplate(config.event)) {
|
||||||
|
return "Fire event based on a template";
|
||||||
|
}
|
||||||
|
return `Fire event ${config.event}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (actionType === "wait_template") {
|
||||||
|
return "Wait for a template to render true";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (actionType === "check_condition") {
|
||||||
|
return `Test ${describeCondition(action as Condition)}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return actionType;
|
||||||
|
};
|
@ -1,10 +1,14 @@
|
|||||||
import { strStartsWith } from "../common/string/starts-with";
|
import { strStartsWith } from "../common/string/starts-with";
|
||||||
import { HomeAssistant, Context } from "../types";
|
import { HomeAssistant, Context } from "../types";
|
||||||
import { AutomationConfig } from "./automation";
|
import {
|
||||||
|
BlueprintAutomationConfig,
|
||||||
|
ManualAutomationConfig,
|
||||||
|
} from "./automation";
|
||||||
|
|
||||||
interface BaseTraceStep {
|
interface BaseTraceStep {
|
||||||
path: string;
|
path: string;
|
||||||
timestamp: string;
|
timestamp: string;
|
||||||
|
error?: string;
|
||||||
changed_variables?: Record<string, unknown>;
|
changed_variables?: Record<string, unknown>;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -19,11 +23,11 @@ export interface TriggerTraceStep extends BaseTraceStep {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface ConditionTraceStep extends BaseTraceStep {
|
export interface ConditionTraceStep extends BaseTraceStep {
|
||||||
result: { result: boolean };
|
result?: { result: boolean };
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface CallServiceActionTraceStep extends BaseTraceStep {
|
export interface CallServiceActionTraceStep extends BaseTraceStep {
|
||||||
result: {
|
result?: {
|
||||||
limit: number;
|
limit: number;
|
||||||
running_script: boolean;
|
running_script: boolean;
|
||||||
params: Record<string, unknown>;
|
params: Record<string, unknown>;
|
||||||
@ -36,11 +40,11 @@ export interface CallServiceActionTraceStep extends BaseTraceStep {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface ChooseActionTraceStep extends BaseTraceStep {
|
export interface ChooseActionTraceStep extends BaseTraceStep {
|
||||||
result: { choice: number | "default" };
|
result?: { choice: number | "default" };
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ChooseChoiceActionTraceStep extends BaseTraceStep {
|
export interface ChooseChoiceActionTraceStep extends BaseTraceStep {
|
||||||
result: { result: boolean };
|
result?: { result: boolean };
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ActionTraceStep =
|
export type ActionTraceStep =
|
||||||
@ -53,22 +57,41 @@ export type ActionTraceStep =
|
|||||||
export interface AutomationTrace {
|
export interface AutomationTrace {
|
||||||
domain: string;
|
domain: string;
|
||||||
item_id: string;
|
item_id: string;
|
||||||
last_action: string | null;
|
last_step: string | null;
|
||||||
last_condition: string | null;
|
|
||||||
run_id: string;
|
run_id: string;
|
||||||
state: "running" | "stopped" | "debugged";
|
state: "running" | "stopped" | "debugged";
|
||||||
timestamp: {
|
timestamp: {
|
||||||
start: string;
|
start: string;
|
||||||
finish: string | null;
|
finish: string | null;
|
||||||
};
|
};
|
||||||
trigger: unknown;
|
script_execution:
|
||||||
|
| // The script was not executed because the automation's condition failed
|
||||||
|
"failed_condition"
|
||||||
|
// The script was not executed because the run mode is single
|
||||||
|
| "failed_single"
|
||||||
|
// The script was not executed because max parallel runs would be exceeded
|
||||||
|
| "failed_max_runs"
|
||||||
|
// All script steps finished:
|
||||||
|
| "finished"
|
||||||
|
// Script execution stopped by the script itself because a condition fails, wait_for_trigger timeouts etc:
|
||||||
|
| "aborted"
|
||||||
|
// Details about failing condition, timeout etc. is in the last element of the trace
|
||||||
|
// Script execution stops because of an unexpected exception:
|
||||||
|
| "error"
|
||||||
|
// The exception is in the trace itself or in the last element of the trace
|
||||||
|
// Script execution stopped by async_stop called on the script run because home assistant is shutting down, script mode is SCRIPT_MODE_RESTART etc:
|
||||||
|
| "cancelled"
|
||||||
|
| string;
|
||||||
|
// Automation only, should become it's own type when we support script in frontend
|
||||||
|
trigger: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface AutomationTraceExtended extends AutomationTrace {
|
export interface AutomationTraceExtended extends AutomationTrace {
|
||||||
trace: Record<string, ActionTraceStep[]>;
|
trace: Record<string, ActionTraceStep[]>;
|
||||||
context: Context;
|
context: Context;
|
||||||
variables: Record<string, unknown>;
|
config: ManualAutomationConfig;
|
||||||
config: AutomationConfig;
|
blueprint_inputs?: BlueprintAutomationConfig;
|
||||||
|
error?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface TraceTypes {
|
interface TraceTypes {
|
||||||
@ -119,7 +142,7 @@ export const loadTraceContexts = (
|
|||||||
});
|
});
|
||||||
|
|
||||||
export const getDataFromPath = (
|
export const getDataFromPath = (
|
||||||
config: AutomationConfig,
|
config: ManualAutomationConfig,
|
||||||
path: string
|
path: string
|
||||||
): any => {
|
): any => {
|
||||||
const parts = path.split("/").reverse();
|
const parts = path.split("/").reverse();
|
||||||
|
@ -314,7 +314,7 @@ class DataEntryFlowDialog extends LitElement {
|
|||||||
this._step &&
|
this._step &&
|
||||||
this._step.type === "create_entry"
|
this._step.type === "create_entry"
|
||||||
) {
|
) {
|
||||||
if (this._params!.flowConfig.loadDevicesAndAreas) {
|
if (this._step.result && this._params!.flowConfig.loadDevicesAndAreas) {
|
||||||
this._fetchDevices(this._step.result.entry_id);
|
this._fetchDevices(this._step.result.entry_id);
|
||||||
this._fetchAreas();
|
this._fetchAreas();
|
||||||
} else {
|
} else {
|
||||||
|
@ -43,7 +43,7 @@ class StepFlowCreateEntry extends LitElement {
|
|||||||
<h2>Success!</h2>
|
<h2>Success!</h2>
|
||||||
<div class="content">
|
<div class="content">
|
||||||
${this.flowConfig.renderCreateEntryDescription(this.hass, this.step)}
|
${this.flowConfig.renderCreateEntryDescription(this.hass, this.step)}
|
||||||
${this.step.result.state === "not_loaded"
|
${this.step.result?.state === "not_loaded"
|
||||||
? html`<span class="error"
|
? html`<span class="error"
|
||||||
>${localize(
|
>${localize(
|
||||||
"ui.panel.config.integrations.config_flow.not_loaded"
|
"ui.panel.config.integrations.config_flow.not_loaded"
|
||||||
|
@ -36,6 +36,7 @@ import {
|
|||||||
AutomationConfig,
|
AutomationConfig,
|
||||||
AutomationEntity,
|
AutomationEntity,
|
||||||
deleteAutomation,
|
deleteAutomation,
|
||||||
|
getAutomationConfig,
|
||||||
getAutomationEditorInitData,
|
getAutomationEditorInitData,
|
||||||
showAutomationEditor,
|
showAutomationEditor,
|
||||||
triggerAutomationActions,
|
triggerAutomationActions,
|
||||||
@ -303,39 +304,7 @@ export class HaAutomationEditor extends KeyboardShortcutMixin(LitElement) {
|
|||||||
oldAutomationId !== this.automationId
|
oldAutomationId !== this.automationId
|
||||||
) {
|
) {
|
||||||
this._setEntityId();
|
this._setEntityId();
|
||||||
this.hass
|
this._loadConfig();
|
||||||
.callApi<AutomationConfig>(
|
|
||||||
"GET",
|
|
||||||
`config/automation/config/${this.automationId}`
|
|
||||||
)
|
|
||||||
.then(
|
|
||||||
(config) => {
|
|
||||||
// Normalize data: ensure trigger, action and condition are lists
|
|
||||||
// Happens when people copy paste their automations into the config
|
|
||||||
for (const key of ["trigger", "condition", "action"]) {
|
|
||||||
const value = config[key];
|
|
||||||
if (value && !Array.isArray(value)) {
|
|
||||||
config[key] = [value];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
this._dirty = false;
|
|
||||||
this._config = config;
|
|
||||||
},
|
|
||||||
(resp) => {
|
|
||||||
showAlertDialog(this, {
|
|
||||||
text:
|
|
||||||
resp.status_code === 404
|
|
||||||
? this.hass.localize(
|
|
||||||
"ui.panel.config.automation.editor.load_error_not_editable"
|
|
||||||
)
|
|
||||||
: this.hass.localize(
|
|
||||||
"ui.panel.config.automation.editor.load_error_unknown",
|
|
||||||
"err_no",
|
|
||||||
resp.status_code
|
|
||||||
),
|
|
||||||
}).then(() => history.back());
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (changedProps.has("automationId") && !this.automationId && this.hass) {
|
if (changedProps.has("automationId") && !this.automationId && this.hass) {
|
||||||
@ -378,6 +347,36 @@ export class HaAutomationEditor extends KeyboardShortcutMixin(LitElement) {
|
|||||||
this._entityId = automation?.entity_id;
|
this._entityId = automation?.entity_id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async _loadConfig() {
|
||||||
|
try {
|
||||||
|
const config = await getAutomationConfig(this.hass, this.automationId);
|
||||||
|
|
||||||
|
// Normalize data: ensure trigger, action and condition are lists
|
||||||
|
// Happens when people copy paste their automations into the config
|
||||||
|
for (const key of ["trigger", "condition", "action"]) {
|
||||||
|
const value = config[key];
|
||||||
|
if (value && !Array.isArray(value)) {
|
||||||
|
config[key] = [value];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this._dirty = false;
|
||||||
|
this._config = config;
|
||||||
|
} catch (err) {
|
||||||
|
showAlertDialog(this, {
|
||||||
|
text:
|
||||||
|
err.status_code === 404
|
||||||
|
? this.hass.localize(
|
||||||
|
"ui.panel.config.automation.editor.load_error_not_editable"
|
||||||
|
)
|
||||||
|
: this.hass.localize(
|
||||||
|
"ui.panel.config.automation.editor.load_error_unknown",
|
||||||
|
"err_no",
|
||||||
|
err.status_code
|
||||||
|
),
|
||||||
|
}).then(() => history.back());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private _valueChanged(ev: CustomEvent<{ value: AutomationConfig }>) {
|
private _valueChanged(ev: CustomEvent<{ value: AutomationConfig }>) {
|
||||||
ev.stopPropagation();
|
ev.stopPropagation();
|
||||||
this._config = ev.detail.value;
|
this._config = ev.detail.value;
|
||||||
|
@ -1,5 +1,12 @@
|
|||||||
import "@material/mwc-icon-button";
|
import "@material/mwc-icon-button";
|
||||||
import { mdiHelpCircle, mdiPlus } from "@mdi/js";
|
import {
|
||||||
|
mdiHelpCircle,
|
||||||
|
mdiHistory,
|
||||||
|
mdiInformationOutline,
|
||||||
|
mdiPencil,
|
||||||
|
mdiPencilOff,
|
||||||
|
mdiPlus,
|
||||||
|
} from "@mdi/js";
|
||||||
import "@polymer/paper-tooltip/paper-tooltip";
|
import "@polymer/paper-tooltip/paper-tooltip";
|
||||||
import {
|
import {
|
||||||
CSSResult,
|
CSSResult,
|
||||||
@ -70,6 +77,7 @@ class HaAutomationPicker extends LitElement {
|
|||||||
return {
|
return {
|
||||||
...automation,
|
...automation,
|
||||||
name: computeStateName(automation),
|
name: computeStateName(automation),
|
||||||
|
last_triggered: automation.attributes.last_triggered || undefined,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -97,23 +105,41 @@ class HaAutomationPicker extends LitElement {
|
|||||||
filterable: true,
|
filterable: true,
|
||||||
direction: "asc",
|
direction: "asc",
|
||||||
grows: true,
|
grows: true,
|
||||||
template: (name, automation: any) => html`
|
template: narrow
|
||||||
${name}
|
? (name, automation: any) =>
|
||||||
<div class="secondary">
|
html`
|
||||||
${this.hass.localize("ui.card.automation.last_triggered")}:
|
${name}
|
||||||
${automation.attributes.last_triggered
|
<div class="secondary">
|
||||||
? formatDateTime(
|
${this.hass.localize("ui.card.automation.last_triggered")}:
|
||||||
new Date(automation.attributes.last_triggered),
|
${automation.attributes.last_triggered
|
||||||
this.hass.locale
|
? formatDateTime(
|
||||||
)
|
new Date(automation.attributes.last_triggered),
|
||||||
: this.hass.localize("ui.components.relative_time.never")}
|
this.hass.locale
|
||||||
</div>
|
)
|
||||||
`,
|
: this.hass.localize("ui.components.relative_time.never")}
|
||||||
|
</div>
|
||||||
|
`
|
||||||
|
: undefined,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
if (!narrow) {
|
if (!narrow) {
|
||||||
|
columns.last_triggered = {
|
||||||
|
sortable: true,
|
||||||
|
width: "20%",
|
||||||
|
title: this.hass.localize("ui.card.automation.last_triggered"),
|
||||||
|
template: (last_triggered) => html`
|
||||||
|
${last_triggered
|
||||||
|
? formatDateTime(new Date(last_triggered), this.hass.locale)
|
||||||
|
: this.hass.localize("ui.components.relative_time.never")}
|
||||||
|
`,
|
||||||
|
};
|
||||||
columns.trigger = {
|
columns.trigger = {
|
||||||
title: "",
|
title: html`
|
||||||
|
<mwc-button style="visibility: hidden">
|
||||||
|
${this.hass.localize("ui.card.automation.trigger")}
|
||||||
|
</mwc-button>
|
||||||
|
`,
|
||||||
|
width: "20%",
|
||||||
template: (_info, automation: any) => html`
|
template: (_info, automation: any) => html`
|
||||||
<mwc-button
|
<mwc-button
|
||||||
.automation=${automation}
|
.automation=${automation}
|
||||||
@ -129,14 +155,15 @@ class HaAutomationPicker extends LitElement {
|
|||||||
title: "",
|
title: "",
|
||||||
type: "icon-button",
|
type: "icon-button",
|
||||||
template: (_info, automation) => html`
|
template: (_info, automation) => html`
|
||||||
<ha-icon-button
|
<mwc-icon-button
|
||||||
.automation=${automation}
|
.automation=${automation}
|
||||||
@click=${this._showInfo}
|
@click=${this._showInfo}
|
||||||
icon="hass:information-outline"
|
.label="${this.hass.localize(
|
||||||
title="${this.hass.localize(
|
|
||||||
"ui.panel.config.automation.picker.show_info_automation"
|
"ui.panel.config.automation.picker.show_info_automation"
|
||||||
)}"
|
)}"
|
||||||
></ha-icon-button>
|
>
|
||||||
|
<ha-svg-icon .path=${mdiInformationOutline}></ha-svg-icon>
|
||||||
|
</mwc-icon-button>
|
||||||
`,
|
`,
|
||||||
};
|
};
|
||||||
columns.trace = {
|
columns.trace = {
|
||||||
@ -150,13 +177,14 @@ class HaAutomationPicker extends LitElement {
|
|||||||
: undefined
|
: undefined
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<ha-icon-button
|
<mwc-icon-button
|
||||||
icon="hass:graph-outline"
|
.label=${this.hass.localize(
|
||||||
.disabled=${!automation.attributes.id}
|
|
||||||
title="${this.hass.localize(
|
|
||||||
"ui.panel.config.automation.picker.dev_automation"
|
"ui.panel.config.automation.picker.dev_automation"
|
||||||
)}"
|
)}
|
||||||
></ha-icon-button>
|
.disabled=${!automation.attributes.id}
|
||||||
|
>
|
||||||
|
<ha-svg-icon .path=${mdiHistory}></ha-svg-icon>
|
||||||
|
</mwc-icon-button>
|
||||||
</a>
|
</a>
|
||||||
${!automation.attributes.id
|
${!automation.attributes.id
|
||||||
? html`
|
? html`
|
||||||
@ -180,25 +208,26 @@ class HaAutomationPicker extends LitElement {
|
|||||||
: undefined
|
: undefined
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<ha-icon-button
|
<mwc-icon-button
|
||||||
.icon=${automation.attributes.id
|
|
||||||
? "hass:pencil"
|
|
||||||
: "hass:pencil-off"}
|
|
||||||
.disabled=${!automation.attributes.id}
|
.disabled=${!automation.attributes.id}
|
||||||
title="${this.hass.localize(
|
.label="${this.hass.localize(
|
||||||
"ui.panel.config.automation.picker.edit_automation"
|
"ui.panel.config.automation.picker.edit_automation"
|
||||||
)}"
|
)}"
|
||||||
></ha-icon-button>
|
><ha-svg-icon .path=${
|
||||||
|
automation.attributes.id ? mdiPencil : mdiPencilOff
|
||||||
|
}></ha-svg-icon>
|
||||||
</a>
|
</a>
|
||||||
${!automation.attributes.id
|
${
|
||||||
? html`
|
!automation.attributes.id
|
||||||
<paper-tooltip animation-delay="0" position="left">
|
? html`
|
||||||
${this.hass.localize(
|
<paper-tooltip animation-delay="0" position="left">
|
||||||
"ui.panel.config.automation.picker.only_editable"
|
${this.hass.localize(
|
||||||
)}
|
"ui.panel.config.automation.picker.only_editable"
|
||||||
</paper-tooltip>
|
)}
|
||||||
`
|
</paper-tooltip>
|
||||||
: ""}
|
`
|
||||||
|
: ""
|
||||||
|
}
|
||||||
`,
|
`,
|
||||||
};
|
};
|
||||||
return columns;
|
return columns;
|
||||||
|
@ -0,0 +1,34 @@
|
|||||||
|
import { safeDump } from "js-yaml";
|
||||||
|
import {
|
||||||
|
customElement,
|
||||||
|
html,
|
||||||
|
LitElement,
|
||||||
|
property,
|
||||||
|
TemplateResult,
|
||||||
|
} from "lit-element";
|
||||||
|
import "../../../../components/ha-icon-button";
|
||||||
|
import "../../../../components/ha-code-editor";
|
||||||
|
import { HomeAssistant } from "../../../../types";
|
||||||
|
import { AutomationTraceExtended } from "../../../../data/trace";
|
||||||
|
|
||||||
|
@customElement("ha-automation-trace-blueprint-config")
|
||||||
|
export class HaAutomationTraceBlueprintConfig extends LitElement {
|
||||||
|
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||||
|
|
||||||
|
@property() public trace!: AutomationTraceExtended;
|
||||||
|
|
||||||
|
protected render(): TemplateResult {
|
||||||
|
return html`
|
||||||
|
<ha-code-editor
|
||||||
|
.value=${safeDump(this.trace.blueprint_inputs || "").trimRight()}
|
||||||
|
readOnly
|
||||||
|
></ha-code-editor>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface HTMLElementTagNameMap {
|
||||||
|
"ha-automation-trace-blueprint-config": HaAutomationTraceBlueprintConfig;
|
||||||
|
}
|
||||||
|
}
|
@ -57,13 +57,13 @@ export class HaAutomationTracePathDetails extends LitElement {
|
|||||||
["logbook", "Related logbook entries"],
|
["logbook", "Related logbook entries"],
|
||||||
].map(
|
].map(
|
||||||
([view, label]) => html`
|
([view, label]) => html`
|
||||||
<div
|
<button
|
||||||
.view=${view}
|
.view=${view}
|
||||||
class=${classMap({ active: this._view === view })}
|
class=${classMap({ active: this._view === view })}
|
||||||
@click=${this._showTab}
|
@click=${this._showTab}
|
||||||
>
|
>
|
||||||
${label}
|
${label}
|
||||||
</div>
|
</button>
|
||||||
`
|
`
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
@ -39,6 +39,7 @@ import {
|
|||||||
mdiRefresh,
|
mdiRefresh,
|
||||||
mdiDownload,
|
mdiDownload,
|
||||||
} from "@mdi/js";
|
} from "@mdi/js";
|
||||||
|
import "./ha-automation-trace-blueprint-config";
|
||||||
|
|
||||||
@customElement("ha-automation-trace")
|
@customElement("ha-automation-trace")
|
||||||
export class HaAutomationTrace extends LitElement {
|
export class HaAutomationTrace extends LitElement {
|
||||||
@ -66,8 +67,12 @@ export class HaAutomationTrace extends LitElement {
|
|||||||
|
|
||||||
@internalProperty() private _logbookEntries?: LogbookEntry[];
|
@internalProperty() private _logbookEntries?: LogbookEntry[];
|
||||||
|
|
||||||
@internalProperty() private _view: "details" | "config" | "timeline" =
|
@internalProperty() private _view:
|
||||||
"details";
|
| "details"
|
||||||
|
| "config"
|
||||||
|
| "timeline"
|
||||||
|
| "logbook"
|
||||||
|
| "blueprint" = "details";
|
||||||
|
|
||||||
protected render(): TemplateResult {
|
protected render(): TemplateResult {
|
||||||
const stateObj = this._entityId
|
const stateObj = this._entityId
|
||||||
@ -117,7 +122,7 @@ export class HaAutomationTrace extends LitElement {
|
|||||||
class="linkButton"
|
class="linkButton"
|
||||||
href="/config/automation/edit/${this.automationId}"
|
href="/config/automation/edit/${this.automationId}"
|
||||||
>
|
>
|
||||||
<mwc-icon-button label="Edit Automation">
|
<mwc-icon-button label="Edit Automation" tabindex="-1">
|
||||||
<ha-svg-icon .path=${mdiPencil}></ha-svg-icon>
|
<ha-svg-icon .path=${mdiPencil}></ha-svg-icon>
|
||||||
</mwc-icon-button>
|
</mwc-icon-button>
|
||||||
</a>
|
</a>
|
||||||
@ -181,18 +186,34 @@ export class HaAutomationTrace extends LitElement {
|
|||||||
${[
|
${[
|
||||||
["details", "Step Details"],
|
["details", "Step Details"],
|
||||||
["timeline", "Trace Timeline"],
|
["timeline", "Trace Timeline"],
|
||||||
|
["logbook", "Related logbook entries"],
|
||||||
["config", "Automation Config"],
|
["config", "Automation Config"],
|
||||||
].map(
|
].map(
|
||||||
([view, label]) => html`
|
([view, label]) => html`
|
||||||
<div
|
<button
|
||||||
|
tabindex="0"
|
||||||
.view=${view}
|
.view=${view}
|
||||||
class=${classMap({ active: this._view === view })}
|
class=${classMap({ active: this._view === view })}
|
||||||
@click=${this._showTab}
|
@click=${this._showTab}
|
||||||
>
|
>
|
||||||
${label}
|
${label}
|
||||||
</div>
|
</button>
|
||||||
`
|
`
|
||||||
)}
|
)}
|
||||||
|
${this._trace.blueprint_inputs
|
||||||
|
? html`
|
||||||
|
<button
|
||||||
|
tabindex="0"
|
||||||
|
.view=${"blueprint"}
|
||||||
|
class=${classMap({
|
||||||
|
active: this._view === "blueprint",
|
||||||
|
})}
|
||||||
|
@click=${this._showTab}
|
||||||
|
>
|
||||||
|
Blueprint Config
|
||||||
|
</div>
|
||||||
|
`
|
||||||
|
: ""}
|
||||||
</div>
|
</div>
|
||||||
${this._selected === undefined ||
|
${this._selected === undefined ||
|
||||||
this._logbookEntries === undefined ||
|
this._logbookEntries === undefined ||
|
||||||
@ -216,6 +237,20 @@ export class HaAutomationTrace extends LitElement {
|
|||||||
.trace=${this._trace}
|
.trace=${this._trace}
|
||||||
></ha-automation-trace-config>
|
></ha-automation-trace-config>
|
||||||
`
|
`
|
||||||
|
: this._view === "logbook"
|
||||||
|
? html`
|
||||||
|
<ha-logbook
|
||||||
|
.hass=${this.hass}
|
||||||
|
.entries=${this._logbookEntries}
|
||||||
|
></ha-logbook>
|
||||||
|
`
|
||||||
|
: this._view === "blueprint"
|
||||||
|
? html`
|
||||||
|
<ha-automation-trace-blueprint-config
|
||||||
|
.hass=${this.hass}
|
||||||
|
.trace=${this._trace}
|
||||||
|
></ha-automation-trace-blueprint-config>
|
||||||
|
`
|
||||||
: html`
|
: html`
|
||||||
<ha-automation-trace-timeline
|
<ha-automation-trace-timeline
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
|
@ -18,8 +18,10 @@ export const traceTabStyles = css`
|
|||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
position: relative;
|
position: relative;
|
||||||
bottom: -1px;
|
bottom: -1px;
|
||||||
|
border: none;
|
||||||
border-bottom: 2px solid transparent;
|
border-bottom: 2px solid transparent;
|
||||||
user-select: none;
|
user-select: none;
|
||||||
|
background: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tabs > *.active {
|
.tabs > *.active {
|
||||||
|
@ -66,14 +66,15 @@ class HaScriptPicker extends LitElement {
|
|||||||
...script,
|
...script,
|
||||||
name: computeStateName(script),
|
name: computeStateName(script),
|
||||||
icon: stateIcon(script),
|
icon: stateIcon(script),
|
||||||
|
last_triggered: script.attributes.last_triggered || undefined,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
private _columns = memoizeOne(
|
private _columns = memoizeOne(
|
||||||
(_language): DataTableColumnContainer => {
|
(narrow, _locale): DataTableColumnContainer => {
|
||||||
return {
|
const columns: DataTableColumnContainer = {
|
||||||
activate: {
|
activate: {
|
||||||
title: "",
|
title: "",
|
||||||
type: "icon-button",
|
type: "icon-button",
|
||||||
@ -103,50 +104,65 @@ class HaScriptPicker extends LitElement {
|
|||||||
filterable: true,
|
filterable: true,
|
||||||
direction: "asc",
|
direction: "asc",
|
||||||
grows: true,
|
grows: true,
|
||||||
template: (name, script: any) => html`
|
template: narrow
|
||||||
${name}
|
? (name, script: any) => html`
|
||||||
<div class="secondary">
|
${name}
|
||||||
${this.hass.localize("ui.card.automation.last_triggered")}:
|
<div class="secondary">
|
||||||
${script.attributes.last_triggered
|
${this.hass.localize("ui.card.automation.last_triggered")}:
|
||||||
? formatDateTime(
|
${script.attributes.last_triggered
|
||||||
new Date(script.attributes.last_triggered),
|
? formatDateTime(
|
||||||
this.hass.locale
|
new Date(script.attributes.last_triggered),
|
||||||
)
|
this.hass.locale
|
||||||
: this.hass.localize("ui.components.relative_time.never")}
|
)
|
||||||
</div>
|
: this.hass.localize("ui.components.relative_time.never")}
|
||||||
`,
|
</div>
|
||||||
},
|
`
|
||||||
info: {
|
: undefined,
|
||||||
title: "",
|
|
||||||
type: "icon-button",
|
|
||||||
template: (_info, script) => html`
|
|
||||||
<mwc-icon-button
|
|
||||||
.script=${script}
|
|
||||||
@click=${this._showInfo}
|
|
||||||
title="${this.hass.localize(
|
|
||||||
"ui.panel.config.script.picker.show_info"
|
|
||||||
)}"
|
|
||||||
>
|
|
||||||
<ha-svg-icon .path=${mdiInformationOutline}></ha-svg-icon>
|
|
||||||
</mwc-icon-button>
|
|
||||||
`,
|
|
||||||
},
|
|
||||||
edit: {
|
|
||||||
title: "",
|
|
||||||
type: "icon-button",
|
|
||||||
template: (_info, script: any) => html`
|
|
||||||
<a href="/config/script/edit/${script.entity_id}">
|
|
||||||
<mwc-icon-button
|
|
||||||
title="${this.hass.localize(
|
|
||||||
"ui.panel.config.script.picker.edit_script"
|
|
||||||
)}"
|
|
||||||
>
|
|
||||||
<ha-svg-icon .path=${mdiPencil}></ha-svg-icon>
|
|
||||||
</mwc-icon-button>
|
|
||||||
</a>
|
|
||||||
`,
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
if (!narrow) {
|
||||||
|
columns.last_triggered = {
|
||||||
|
sortable: true,
|
||||||
|
width: "20%",
|
||||||
|
title: this.hass.localize("ui.card.automation.last_triggered"),
|
||||||
|
template: (last_triggered) => html`
|
||||||
|
${last_triggered
|
||||||
|
? formatDateTime(new Date(last_triggered), this.hass.locale)
|
||||||
|
: this.hass.localize("ui.components.relative_time.never")}
|
||||||
|
`,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
columns.info = {
|
||||||
|
title: "",
|
||||||
|
type: "icon-button",
|
||||||
|
template: (_info, script) => html`
|
||||||
|
<mwc-icon-button
|
||||||
|
.script=${script}
|
||||||
|
@click=${this._showInfo}
|
||||||
|
title="${this.hass.localize(
|
||||||
|
"ui.panel.config.script.picker.show_info"
|
||||||
|
)}"
|
||||||
|
>
|
||||||
|
<ha-svg-icon .path=${mdiInformationOutline}></ha-svg-icon>
|
||||||
|
</mwc-icon-button>
|
||||||
|
`,
|
||||||
|
};
|
||||||
|
columns.edit = {
|
||||||
|
title: "",
|
||||||
|
type: "icon-button",
|
||||||
|
template: (_info, script: any) => html`
|
||||||
|
<a href="/config/script/edit/${script.entity_id}">
|
||||||
|
<mwc-icon-button
|
||||||
|
title="${this.hass.localize(
|
||||||
|
"ui.panel.config.script.picker.edit_script"
|
||||||
|
)}"
|
||||||
|
>
|
||||||
|
<ha-svg-icon .path=${mdiPencil}></ha-svg-icon>
|
||||||
|
</mwc-icon-button>
|
||||||
|
</a>
|
||||||
|
`,
|
||||||
|
};
|
||||||
|
return columns;
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -158,7 +174,7 @@ class HaScriptPicker extends LitElement {
|
|||||||
back-path="/config"
|
back-path="/config"
|
||||||
.route=${this.route}
|
.route=${this.route}
|
||||||
.tabs=${configSections.automation}
|
.tabs=${configSections.automation}
|
||||||
.columns=${this._columns(this.hass.language)}
|
.columns=${this._columns(this.narrow, this.hass.locale)}
|
||||||
.data=${this._scripts(this.scripts, this._filteredScripts)}
|
.data=${this._scripts(this.scripts, this._filteredScripts)}
|
||||||
.activeFilters=${this._activeFilters}
|
.activeFilters=${this._activeFilters}
|
||||||
id="entity_id"
|
id="entity_id"
|
||||||
|
@ -286,7 +286,7 @@ class HaPanelDevService extends LitElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private _checkUiSupported() {
|
private _checkUiSupported() {
|
||||||
if (hasTemplate(this._serviceData)) {
|
if (this._serviceData && hasTemplate(this._serviceData)) {
|
||||||
this._yamlMode = true;
|
this._yamlMode = true;
|
||||||
this._uiAvailable = false;
|
this._uiAvailable = false;
|
||||||
} else {
|
} else {
|
||||||
|
@ -259,7 +259,7 @@ class HaLogbook extends LitElement {
|
|||||||
haStyle,
|
haStyle,
|
||||||
haStyleScrollbar,
|
haStyleScrollbar,
|
||||||
css`
|
css`
|
||||||
:host {
|
:host([virtualize]) {
|
||||||
display: block;
|
display: block;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
@ -1188,6 +1188,9 @@
|
|||||||
"zha_device_card": {
|
"zha_device_card": {
|
||||||
"device_name_placeholder": "Změnit název zařízení"
|
"device_name_placeholder": "Změnit název zařízení"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"zha_reconfigure_device": {
|
||||||
|
"heading": "Znovunastavení zařízení"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"duration": {
|
"duration": {
|
||||||
@ -1691,6 +1694,7 @@
|
|||||||
"info": "Díky integraci Google Assistant pro Home Assistant Cloud budete moci ovládat všechna zařízení v Home Assistant pomocí jakéhokoli zařízení podporujícího Google Assistant.",
|
"info": "Díky integraci Google Assistant pro Home Assistant Cloud budete moci ovládat všechna zařízení v Home Assistant pomocí jakéhokoli zařízení podporujícího Google Assistant.",
|
||||||
"info_state_reporting": "Pokud povolíte hlášení stavu, Home Assistant bude posílat veškeré změny stavů všech exponovaných entit do Google. Toto vám umožní sledovat vždy aktuální stavy entit v aplikaci Google.",
|
"info_state_reporting": "Pokud povolíte hlášení stavu, Home Assistant bude posílat veškeré změny stavů všech exponovaných entit do Google. Toto vám umožní sledovat vždy aktuální stavy entit v aplikaci Google.",
|
||||||
"manage_entities": "Správa entit",
|
"manage_entities": "Správa entit",
|
||||||
|
"not_configured_text": "Než budete moci používat Google Assistant, musíte v aplikaci Google Home aktivovat dovednost Home Assistant Cloud pro Google Assistant.",
|
||||||
"not_configured_title": "Google Asistent není aktivován",
|
"not_configured_title": "Google Asistent není aktivován",
|
||||||
"security_devices": "Zabezpečovací zařízení",
|
"security_devices": "Zabezpečovací zařízení",
|
||||||
"sync_entities": "Synchronizovat entity s Google",
|
"sync_entities": "Synchronizovat entity s Google",
|
||||||
@ -1859,6 +1863,7 @@
|
|||||||
"header": "Analytika",
|
"header": "Analytika",
|
||||||
"instance_id": "ID instance: {huuid}",
|
"instance_id": "ID instance: {huuid}",
|
||||||
"introduction": "Sdílejte analytiku ze své instance. Tato data budou veřejně dostupná na {link}",
|
"introduction": "Sdílejte analytiku ze své instance. Tato data budou veřejně dostupná na {link}",
|
||||||
|
"learn_more": "Zjistěte více, jak budou vaše údaje zpracovány.",
|
||||||
"needs_base": "Aby byla tato možnost k dispozici, musíte povolit základní analytiku",
|
"needs_base": "Aby byla tato možnost k dispozici, musíte povolit základní analytiku",
|
||||||
"preference": {
|
"preference": {
|
||||||
"base": {
|
"base": {
|
||||||
@ -2070,6 +2075,9 @@
|
|||||||
"filtering_by": "Filtrování podle",
|
"filtering_by": "Filtrování podle",
|
||||||
"show": "Zobrazit"
|
"show": "Zobrazit"
|
||||||
},
|
},
|
||||||
|
"hassio": {
|
||||||
|
"button": "Nastavit"
|
||||||
|
},
|
||||||
"header": "Nastavení Home Assistant",
|
"header": "Nastavení Home Assistant",
|
||||||
"helpers": {
|
"helpers": {
|
||||||
"caption": "Pomocníci",
|
"caption": "Pomocníci",
|
||||||
@ -2236,10 +2244,17 @@
|
|||||||
"clear": "Zrušit",
|
"clear": "Zrušit",
|
||||||
"description": "Zobrazení logů Home Assistant",
|
"description": "Zobrazení logů Home Assistant",
|
||||||
"details": "Detaily protokolu ({level})",
|
"details": "Detaily protokolu ({level})",
|
||||||
|
"level": {
|
||||||
|
"critical": "KRITICKÉ",
|
||||||
|
"debug": "LADĚNÍ",
|
||||||
|
"error": "CHYBA",
|
||||||
|
"info": "INFO",
|
||||||
|
"warning": "VAROVÁNÍ"
|
||||||
|
},
|
||||||
"load_full_log": "Načíst úplný protokol Home Assistanta",
|
"load_full_log": "Načíst úplný protokol Home Assistanta",
|
||||||
"loading_log": "Načítání protokolu chyb...",
|
"loading_log": "Načítání protokolu chyb...",
|
||||||
"multiple_messages": "zpráva se poprvé objevila v {time} a zobrazuje se {counter} krát",
|
"multiple_messages": "zpráva se poprvé objevila v {time} a zobrazuje se {counter} krát",
|
||||||
"no_errors": "Nebyly hlášeny žádné chyby.",
|
"no_errors": "Nebyly hlášeny žádné chyby",
|
||||||
"no_issues": "Nejsou žádné nové problémy!",
|
"no_issues": "Nejsou žádné nové problémy!",
|
||||||
"refresh": "Obnovit"
|
"refresh": "Obnovit"
|
||||||
},
|
},
|
||||||
|
@ -1188,6 +1188,9 @@
|
|||||||
"zha_device_card": {
|
"zha_device_card": {
|
||||||
"device_name_placeholder": "Change device name"
|
"device_name_placeholder": "Change device name"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"zha_reconfigure_device": {
|
||||||
|
"heading": "Reconfiguring device"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"duration": {
|
"duration": {
|
||||||
@ -1859,7 +1862,7 @@
|
|||||||
"documentation": "Before you enable this make sure you visit the analytics documentation page {link} to understand what you are sending and how it's stored.",
|
"documentation": "Before you enable this make sure you visit the analytics documentation page {link} to understand what you are sending and how it's stored.",
|
||||||
"header": "Analytics",
|
"header": "Analytics",
|
||||||
"instance_id": "Instance ID: {huuid}",
|
"instance_id": "Instance ID: {huuid}",
|
||||||
"introduction": "Share analytics from your instance. This data will be publiclly available at {link}",
|
"introduction": "Share analytics from your instance. This data will be publicly available at {link}",
|
||||||
"learn_more": "Learn more about how your data will be processed.",
|
"learn_more": "Learn more about how your data will be processed.",
|
||||||
"needs_base": "You need to enable base analytics for this option to be available",
|
"needs_base": "You need to enable base analytics for this option to be available",
|
||||||
"preference": {
|
"preference": {
|
||||||
@ -3746,7 +3749,7 @@
|
|||||||
"page-onboarding": {
|
"page-onboarding": {
|
||||||
"analytics": {
|
"analytics": {
|
||||||
"finish": "Next",
|
"finish": "Next",
|
||||||
"intro": "Share analytics from your instance. This data will be publiclly available at {link}"
|
"intro": "Share analytics from your instance. This data will be publicly available at {link}"
|
||||||
},
|
},
|
||||||
"core-config": {
|
"core-config": {
|
||||||
"button_detect": "Detect",
|
"button_detect": "Detect",
|
||||||
|
@ -1187,6 +1187,9 @@
|
|||||||
"zha_device_card": {
|
"zha_device_card": {
|
||||||
"device_name_placeholder": "Cambiar el nombre del dispositivo"
|
"device_name_placeholder": "Cambiar el nombre del dispositivo"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"zha_reconfigure_device": {
|
||||||
|
"heading": "Reconfigurando el dispositivo"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"duration": {
|
"duration": {
|
||||||
@ -1859,6 +1862,7 @@
|
|||||||
"header": "Analítica",
|
"header": "Analítica",
|
||||||
"instance_id": "ID de instancia: {huuid}",
|
"instance_id": "ID de instancia: {huuid}",
|
||||||
"introduction": "Comparte análisis de tu instancia. Estos datos estarán disponibles públicamente en {link}",
|
"introduction": "Comparte análisis de tu instancia. Estos datos estarán disponibles públicamente en {link}",
|
||||||
|
"learn_more": "Aprende más sobre cómo se procesarán tus datos.",
|
||||||
"needs_base": "Debes habilitar el análisis base para que esta opción esté disponible",
|
"needs_base": "Debes habilitar el análisis base para que esta opción esté disponible",
|
||||||
"preference": {
|
"preference": {
|
||||||
"base": {
|
"base": {
|
||||||
@ -2070,6 +2074,9 @@
|
|||||||
"filtering_by": "Filtrando por",
|
"filtering_by": "Filtrando por",
|
||||||
"show": "Mostrar"
|
"show": "Mostrar"
|
||||||
},
|
},
|
||||||
|
"hassio": {
|
||||||
|
"button": "Configurar"
|
||||||
|
},
|
||||||
"header": "Configurar Home Assistant",
|
"header": "Configurar Home Assistant",
|
||||||
"helpers": {
|
"helpers": {
|
||||||
"caption": "Ayudantes",
|
"caption": "Ayudantes",
|
||||||
@ -2236,10 +2243,17 @@
|
|||||||
"clear": "Limpiar",
|
"clear": "Limpiar",
|
||||||
"description": "Ve los registros de Home Assistant",
|
"description": "Ve los registros de Home Assistant",
|
||||||
"details": "Detalles de registro ({level})",
|
"details": "Detalles de registro ({level})",
|
||||||
|
"level": {
|
||||||
|
"critical": "CRÍTICO",
|
||||||
|
"debug": "DEPURACIÓN",
|
||||||
|
"error": "ERROR",
|
||||||
|
"info": "INFO",
|
||||||
|
"warning": "ADVERTENCIA"
|
||||||
|
},
|
||||||
"load_full_log": "Cargar registro completo de Home Assistant",
|
"load_full_log": "Cargar registro completo de Home Assistant",
|
||||||
"loading_log": "Cargando registro de errores...",
|
"loading_log": "Cargando registro de errores...",
|
||||||
"multiple_messages": "el mensaje se produjo por primera vez a las {time} y aparece {counter} veces",
|
"multiple_messages": "el mensaje se produjo por primera vez a las {time} y aparece {counter} veces",
|
||||||
"no_errors": "No se han reportado errores.",
|
"no_errors": "No se han reportado errores",
|
||||||
"no_issues": "¡No hay nuevos problemas!",
|
"no_issues": "¡No hay nuevos problemas!",
|
||||||
"refresh": "Actualizar"
|
"refresh": "Actualizar"
|
||||||
},
|
},
|
||||||
|
@ -1188,6 +1188,9 @@
|
|||||||
"zha_device_card": {
|
"zha_device_card": {
|
||||||
"device_name_placeholder": "Muuda seadme nime"
|
"device_name_placeholder": "Muuda seadme nime"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"zha_reconfigure_device": {
|
||||||
|
"heading": "Seadme sätete muutmine"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"duration": {
|
"duration": {
|
||||||
@ -2072,6 +2075,9 @@
|
|||||||
"filtering_by": "Filtreeri",
|
"filtering_by": "Filtreeri",
|
||||||
"show": "Kuva"
|
"show": "Kuva"
|
||||||
},
|
},
|
||||||
|
"hassio": {
|
||||||
|
"button": "Seadista"
|
||||||
|
},
|
||||||
"header": "Home Assistant'i seadistamine",
|
"header": "Home Assistant'i seadistamine",
|
||||||
"helpers": {
|
"helpers": {
|
||||||
"caption": "Abimehed",
|
"caption": "Abimehed",
|
||||||
|
@ -440,6 +440,9 @@
|
|||||||
"area-picker": {
|
"area-picker": {
|
||||||
"show_areas": "Amosar áreas"
|
"show_areas": "Amosar áreas"
|
||||||
},
|
},
|
||||||
|
"data-table": {
|
||||||
|
"clear": "Elim"
|
||||||
|
},
|
||||||
"device-picker": {
|
"device-picker": {
|
||||||
"show_devices": "Amosar dispositivos"
|
"show_devices": "Amosar dispositivos"
|
||||||
},
|
},
|
||||||
@ -457,6 +460,12 @@
|
|||||||
"is_opening": "estase abrindo"
|
"is_opening": "estase abrindo"
|
||||||
},
|
},
|
||||||
"show_trace": "Amosar rastro"
|
"show_trace": "Amosar rastro"
|
||||||
|
},
|
||||||
|
"related-filter-menu": {
|
||||||
|
"filter_by_area": "Filtrar por área",
|
||||||
|
"filter_by_device": "Filtrar por dispositivo",
|
||||||
|
"filtered_by_area": "área: {area_name}",
|
||||||
|
"filtered_by_device": "dispositivo: {device_name}"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"dialogs": {
|
"dialogs": {
|
||||||
@ -531,6 +540,7 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"show_trace": "Mostrar rastro",
|
||||||
"triggers": {
|
"triggers": {
|
||||||
"type": {
|
"type": {
|
||||||
"mqtt": {
|
"mqtt": {
|
||||||
@ -555,6 +565,12 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"cloud": {
|
"cloud": {
|
||||||
|
"account": {
|
||||||
|
"google": {
|
||||||
|
"not_configured_text": "Antes de usar o Asistente de Google, debes activar a skill Home Assistant Cloud para Google Assistant na aplicación Google Home.",
|
||||||
|
"not_configured_title": "Google Assistant non está activado"
|
||||||
|
}
|
||||||
|
},
|
||||||
"forgot_password": {
|
"forgot_password": {
|
||||||
"instructions": "Introduce o teu enderezo de correo electrónico e enviarémosche unha ligazón para restablecer o teu contrasinal."
|
"instructions": "Introduce o teu enderezo de correo electrónico e enviarémosche unha ligazón para restablecer o teu contrasinal."
|
||||||
},
|
},
|
||||||
@ -562,6 +578,41 @@
|
|||||||
"email_address": "Enderezo electrónico"
|
"email_address": "Enderezo electrónico"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"core": {
|
||||||
|
"section": {
|
||||||
|
"core": {
|
||||||
|
"analytics": {
|
||||||
|
"documentation": "Antes de habilitalo, asegúrese de visitar a páxina de documentación de análise {link} para comprender o que está a enviar e como se almacena.",
|
||||||
|
"header": "Analítica",
|
||||||
|
"instance_id": "ID de instancia: {huuid}",
|
||||||
|
"introduction": "Comparte analítica desde a túa instancia. Estes datos estarán dispoñibles publicamente en {link}",
|
||||||
|
"learn_more": "Máis información sobre como se procesarán os teus datos.",
|
||||||
|
"needs_base": "Debe habilitar a analítica base para que esta opción estea dispoñible",
|
||||||
|
"preference": {
|
||||||
|
"base": {
|
||||||
|
"description": "Isto inclúe o ID de instancia, a versión e o tipo de instalación",
|
||||||
|
"title": "Analítica básica"
|
||||||
|
},
|
||||||
|
"diagnostics": {
|
||||||
|
"description": "Comparte informes de fallos e información de diagnóstico",
|
||||||
|
"title": "Diagnóstico"
|
||||||
|
},
|
||||||
|
"statistics": {
|
||||||
|
"description": "Isto inclúe un reconto de elementos na súa instalación, para ver unha lista completa consulta a documentación",
|
||||||
|
"title": "Estatísticas de uso"
|
||||||
|
},
|
||||||
|
"usage_supervisor": {
|
||||||
|
"title": "Integracións e complementos usados"
|
||||||
|
},
|
||||||
|
"usage": {
|
||||||
|
"description": "Isto inclúe os nomes das túas integracións",
|
||||||
|
"title": "Integracións usadas"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"devices": {
|
"devices": {
|
||||||
"enabled_description": "Os dispositivos desactivados non se amosarán e as entidades que pertencen ao dispositivo desactivaranse e non se engadirán ao Asistente doméstico.",
|
"enabled_description": "Os dispositivos desactivados non se amosarán e as entidades que pertencen ao dispositivo desactivaranse e non se engadirán ao Asistente doméstico.",
|
||||||
"picker": {
|
"picker": {
|
||||||
@ -585,7 +636,15 @@
|
|||||||
"show": "Amosar"
|
"show": "Amosar"
|
||||||
},
|
},
|
||||||
"integrations": {
|
"integrations": {
|
||||||
|
"config_entry": {
|
||||||
|
"logs": "rexistros",
|
||||||
|
"not_loaded": "Non cargado, comproba o {logs_link}"
|
||||||
|
},
|
||||||
|
"config_flow": {
|
||||||
|
"not_loaded": "Non se puido cargar a integración. Tenta reiniciar Home Assistant."
|
||||||
|
},
|
||||||
"disable": {
|
"disable": {
|
||||||
|
"show": "Amosar",
|
||||||
"show_disabled": "Amosar as integracións desactivadas"
|
"show_disabled": "Amosar as integracións desactivadas"
|
||||||
},
|
},
|
||||||
"ignore": {
|
"ignore": {
|
||||||
@ -593,6 +652,15 @@
|
|||||||
"show_ignored": "Amosar integracións ignoradas"
|
"show_ignored": "Amosar integracións ignoradas"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"logs": {
|
||||||
|
"level": {
|
||||||
|
"critical": "CRÍTICO",
|
||||||
|
"debug": "DEPURAR",
|
||||||
|
"error": "ERRO",
|
||||||
|
"info": "INFORMACIÓN",
|
||||||
|
"warning": "AVISO"
|
||||||
|
}
|
||||||
|
},
|
||||||
"lovelace": {
|
"lovelace": {
|
||||||
"dashboards": {
|
"dashboards": {
|
||||||
"detail": {
|
"detail": {
|
||||||
@ -853,9 +921,14 @@
|
|||||||
"not_supported": "Esta redirección non é compatible coa túa instancia de Home Assistant. Comprobe a {link} para coñecer as redireccións compatibles e a versión na que se introduciron."
|
"not_supported": "Esta redirección non é compatible coa túa instancia de Home Assistant. Comprobe a {link} para coñecer as redireccións compatibles e a versión na que se introduciron."
|
||||||
},
|
},
|
||||||
"page-onboarding": {
|
"page-onboarding": {
|
||||||
|
"analytics": {
|
||||||
|
"finish": "Seguinte"
|
||||||
|
},
|
||||||
"core-config": {
|
"core-config": {
|
||||||
"intro_location": "Gustaríanos saber onde vives. Esta información axudará a amosar información e configurar automatismos baseados no sol. Estes datos nunca se comparten fóra da túa rede."
|
"intro_location": "Gustaríanos saber onde vives. Esta información axudará a amosar información e configurar automatismos baseados no sol. Estes datos nunca se comparten fóra da túa rede."
|
||||||
},
|
},
|
||||||
|
"finish": "Rematar",
|
||||||
|
"next": "Seguinte",
|
||||||
"restore": {
|
"restore": {
|
||||||
"show_log": "Amosar rexistro completo"
|
"show_log": "Amosar rexistro completo"
|
||||||
}
|
}
|
||||||
@ -863,6 +936,19 @@
|
|||||||
"profile": {
|
"profile": {
|
||||||
"long_lived_access_tokens": {
|
"long_lived_access_tokens": {
|
||||||
"prompt_copy_token": "Copia o teu token de acceso. Non aparecerá de novo."
|
"prompt_copy_token": "Copia o teu token de acceso. Non aparecerá de novo."
|
||||||
|
},
|
||||||
|
"number_format": {
|
||||||
|
"description": "Escolle como se formatean os números.",
|
||||||
|
"dropdown_label": "Formato de número",
|
||||||
|
"formats": {
|
||||||
|
"comma_decimal": "1,234,567.89",
|
||||||
|
"decimal_comma": "1.234.567,89",
|
||||||
|
"language": "Automático (usar a configuración do idioma)",
|
||||||
|
"none": "Ningunha",
|
||||||
|
"space_comma": "1 234 567,89",
|
||||||
|
"system": "Usa a configuración rexional do sistema"
|
||||||
|
},
|
||||||
|
"header": "Formato de número"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1860,6 +1860,7 @@
|
|||||||
"header": "분석",
|
"header": "분석",
|
||||||
"instance_id": "인스턴스 ID: {huuid}",
|
"instance_id": "인스턴스 ID: {huuid}",
|
||||||
"introduction": "인스턴스의 분석 내용을 공유합니다. 이 데이터는 {link}에서 공개적으로 사용할 수 있습니다",
|
"introduction": "인스턴스의 분석 내용을 공유합니다. 이 데이터는 {link}에서 공개적으로 사용할 수 있습니다",
|
||||||
|
"learn_more": "통계자료가 어떻게 처리되는지 알아보기.",
|
||||||
"needs_base": "이 옵션을 사용하려면 기본 분석을 활성화해야 합니다",
|
"needs_base": "이 옵션을 사용하려면 기본 분석을 활성화해야 합니다",
|
||||||
"preference": {
|
"preference": {
|
||||||
"base": {
|
"base": {
|
||||||
@ -2237,6 +2238,13 @@
|
|||||||
"clear": "지우기",
|
"clear": "지우기",
|
||||||
"description": "Home Assistant 로그 내역을 봅니다",
|
"description": "Home Assistant 로그 내역을 봅니다",
|
||||||
"details": "로그 상세정보 ({level})",
|
"details": "로그 상세정보 ({level})",
|
||||||
|
"level": {
|
||||||
|
"critical": "치명적오류",
|
||||||
|
"debug": "디버그",
|
||||||
|
"error": "오류",
|
||||||
|
"info": "정보",
|
||||||
|
"warning": "경고"
|
||||||
|
},
|
||||||
"load_full_log": "Home Assistant 로그 전부 불러오기",
|
"load_full_log": "Home Assistant 로그 전부 불러오기",
|
||||||
"loading_log": "오류 로그를 읽는 중...",
|
"loading_log": "오류 로그를 읽는 중...",
|
||||||
"multiple_messages": "{time}에 처음 발생했으며, {counter}번 발생했습니다.",
|
"multiple_messages": "{time}에 처음 발생했으며, {counter}번 발생했습니다.",
|
||||||
|
@ -1860,6 +1860,7 @@
|
|||||||
"header": "Analytics",
|
"header": "Analytics",
|
||||||
"instance_id": "Forekomst-ID: {huuid}",
|
"instance_id": "Forekomst-ID: {huuid}",
|
||||||
"introduction": "Del analyse fra forekomsten din. Disse dataene vil være offentlig tilgjengelige på {link}",
|
"introduction": "Del analyse fra forekomsten din. Disse dataene vil være offentlig tilgjengelige på {link}",
|
||||||
|
"learn_more": "Lær mer om hvordan dataene dine blir behandlet.",
|
||||||
"needs_base": "Du må aktivere basisanalyse for at dette alternativet skal være tilgjengelig",
|
"needs_base": "Du må aktivere basisanalyse for at dette alternativet skal være tilgjengelig",
|
||||||
"preference": {
|
"preference": {
|
||||||
"base": {
|
"base": {
|
||||||
@ -2071,6 +2072,9 @@
|
|||||||
"filtering_by": "Filtrering etter",
|
"filtering_by": "Filtrering etter",
|
||||||
"show": "Vis"
|
"show": "Vis"
|
||||||
},
|
},
|
||||||
|
"hassio": {
|
||||||
|
"button": "Konfigurer"
|
||||||
|
},
|
||||||
"header": "Konfigurer Home Assistant",
|
"header": "Konfigurer Home Assistant",
|
||||||
"helpers": {
|
"helpers": {
|
||||||
"caption": "Hjelpere",
|
"caption": "Hjelpere",
|
||||||
@ -2237,10 +2241,17 @@
|
|||||||
"clear": "Tøm",
|
"clear": "Tøm",
|
||||||
"description": "Vis Home Assistant loggene",
|
"description": "Vis Home Assistant loggene",
|
||||||
"details": "Loggdetaljer ({level})",
|
"details": "Loggdetaljer ({level})",
|
||||||
|
"level": {
|
||||||
|
"critical": "KRITISK",
|
||||||
|
"debug": "DEBUG",
|
||||||
|
"error": "FEIL",
|
||||||
|
"info": "INFO",
|
||||||
|
"warning": "ADVARSEL"
|
||||||
|
},
|
||||||
"load_full_log": "Last inn fullstendig Home Assistant logg",
|
"load_full_log": "Last inn fullstendig Home Assistant logg",
|
||||||
"loading_log": "Laster inn feillogg ...",
|
"loading_log": "Laster inn feillogg ...",
|
||||||
"multiple_messages": "meldingen oppstod først ved {time} og vist {counter} ganger",
|
"multiple_messages": "meldingen oppstod først ved {time} og vist {counter} ganger",
|
||||||
"no_errors": "Ingen feil er rapportert.",
|
"no_errors": "Ingen feil er rapportert",
|
||||||
"no_issues": "Det er ingen nye problemer!",
|
"no_issues": "Det er ingen nye problemer!",
|
||||||
"refresh": "Oppdater"
|
"refresh": "Oppdater"
|
||||||
},
|
},
|
||||||
|
@ -1188,6 +1188,9 @@
|
|||||||
"zha_device_card": {
|
"zha_device_card": {
|
||||||
"device_name_placeholder": "Wijzig apparaatnaam"
|
"device_name_placeholder": "Wijzig apparaatnaam"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"zha_reconfigure_device": {
|
||||||
|
"heading": "Apparaat opnieuw configureren"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"duration": {
|
"duration": {
|
||||||
@ -1656,8 +1659,8 @@
|
|||||||
},
|
},
|
||||||
"introduction": "Met de Blueprinteditor kunt je Blueprints maken en bewerken.",
|
"introduction": "Met de Blueprinteditor kunt je Blueprints maken en bewerken.",
|
||||||
"learn_more": "Meer informatie over Blueprints",
|
"learn_more": "Meer informatie over Blueprints",
|
||||||
"share_blueprint": "Deel blauwdruk",
|
"share_blueprint": "Deel Blueprint",
|
||||||
"share_blueprint_no_url": "Kan blauwdruk niet delen: geen bron-URL",
|
"share_blueprint_no_url": "Kan Blueprint niet delen: geen bron-URL",
|
||||||
"use_blueprint": "Automatisering maken"
|
"use_blueprint": "Automatisering maken"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -1860,6 +1863,7 @@
|
|||||||
"header": "Analytics",
|
"header": "Analytics",
|
||||||
"instance_id": "Instantie-ID: {huuid}",
|
"instance_id": "Instantie-ID: {huuid}",
|
||||||
"introduction": "Deel analyses vanuit uw instantie. Deze gegevens zijn openbaar beschikbaar op {link}",
|
"introduction": "Deel analyses vanuit uw instantie. Deze gegevens zijn openbaar beschikbaar op {link}",
|
||||||
|
"learn_more": "Lees meer over hoe uw gegevens worden verwerkt.",
|
||||||
"needs_base": "U moet basisanalyses inschakelen om deze optie beschikbaar te maken",
|
"needs_base": "U moet basisanalyses inschakelen om deze optie beschikbaar te maken",
|
||||||
"preference": {
|
"preference": {
|
||||||
"base": {
|
"base": {
|
||||||
@ -2071,6 +2075,9 @@
|
|||||||
"filtering_by": "Filteren op",
|
"filtering_by": "Filteren op",
|
||||||
"show": "Toon"
|
"show": "Toon"
|
||||||
},
|
},
|
||||||
|
"hassio": {
|
||||||
|
"button": "Configureer"
|
||||||
|
},
|
||||||
"header": "Configureer Home Assistant",
|
"header": "Configureer Home Assistant",
|
||||||
"helpers": {
|
"helpers": {
|
||||||
"caption": "Helpers",
|
"caption": "Helpers",
|
||||||
@ -2237,6 +2244,13 @@
|
|||||||
"clear": "Wis",
|
"clear": "Wis",
|
||||||
"description": "Home Assistant logboek bekijken",
|
"description": "Home Assistant logboek bekijken",
|
||||||
"details": "Logboekdetails ({level})",
|
"details": "Logboekdetails ({level})",
|
||||||
|
"level": {
|
||||||
|
"critical": "KRITISCH",
|
||||||
|
"debug": "DEBUG",
|
||||||
|
"error": "FOUT",
|
||||||
|
"info": "INFO",
|
||||||
|
"warning": "WAARSCHUWING"
|
||||||
|
},
|
||||||
"load_full_log": "Laad volledige Home Assistant logboek",
|
"load_full_log": "Laad volledige Home Assistant logboek",
|
||||||
"loading_log": "Foutenlogboek laden ...",
|
"loading_log": "Foutenlogboek laden ...",
|
||||||
"multiple_messages": "bericht kwam voor het eerst om {time} en verschijnt {counter} keer",
|
"multiple_messages": "bericht kwam voor het eerst om {time} en verschijnt {counter} keer",
|
||||||
|
@ -1188,6 +1188,9 @@
|
|||||||
"zha_device_card": {
|
"zha_device_card": {
|
||||||
"device_name_placeholder": "Название"
|
"device_name_placeholder": "Название"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"zha_reconfigure_device": {
|
||||||
|
"heading": "Перенастройка устройства"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"duration": {
|
"duration": {
|
||||||
@ -1859,7 +1862,8 @@
|
|||||||
"documentation": "Прежде всего, посетите страницу документации по аналитике {link}, чтобы понять, что Вы будете отправлять и как это будет храниться.",
|
"documentation": "Прежде всего, посетите страницу документации по аналитике {link}, чтобы понять, что Вы будете отправлять и как это будет храниться.",
|
||||||
"header": "Аналитика",
|
"header": "Аналитика",
|
||||||
"instance_id": "Идентификатор экземпляра Home Assistant: {huuid}",
|
"instance_id": "Идентификатор экземпляра Home Assistant: {huuid}",
|
||||||
"introduction": "Поделитесь аналитикой из Вашего Home Assistant. Эти данные будут доступны для всех по адресу {ссылка}.",
|
"introduction": "Поделитесь аналитикой из Вашего Home Assistant. Эти данные будут доступны для всех по адресу {link}.",
|
||||||
|
"learn_more": "Узнайте больше о том, как будут обрабатываться Ваши данные.",
|
||||||
"needs_base": "Включите базовую аналитику, чтобы эта опция была доступна",
|
"needs_base": "Включите базовую аналитику, чтобы эта опция была доступна",
|
||||||
"preference": {
|
"preference": {
|
||||||
"base": {
|
"base": {
|
||||||
@ -2071,6 +2075,9 @@
|
|||||||
"filtering_by": "Отфильтровано по принадлежности к",
|
"filtering_by": "Отфильтровано по принадлежности к",
|
||||||
"show": "Показать"
|
"show": "Показать"
|
||||||
},
|
},
|
||||||
|
"hassio": {
|
||||||
|
"button": "Настроить"
|
||||||
|
},
|
||||||
"header": "Настройка Home Assistant",
|
"header": "Настройка Home Assistant",
|
||||||
"helpers": {
|
"helpers": {
|
||||||
"caption": "Вспомогательное",
|
"caption": "Вспомогательное",
|
||||||
@ -2237,10 +2244,17 @@
|
|||||||
"clear": "Очистить",
|
"clear": "Очистить",
|
||||||
"description": "Журналы работы сервера",
|
"description": "Журналы работы сервера",
|
||||||
"details": "Уровень: {level}",
|
"details": "Уровень: {level}",
|
||||||
|
"level": {
|
||||||
|
"critical": "КРИТИЧЕСКАЯ НЕИСПРАВНОСТЬ",
|
||||||
|
"debug": "ОТЛАДКА",
|
||||||
|
"error": "ОШИБКА",
|
||||||
|
"info": "ИНФОРМАЦИЯ",
|
||||||
|
"warning": "ПРЕДУПРЕЖДЕНИЕ"
|
||||||
|
},
|
||||||
"load_full_log": "Показать весь журнал",
|
"load_full_log": "Показать весь журнал",
|
||||||
"loading_log": "Загрузка журнала…",
|
"loading_log": "Загрузка журнала…",
|
||||||
"multiple_messages": "первое сообщение получено {time} и повторялось {counter} раз",
|
"multiple_messages": "первое сообщение получено {time} и повторялось {counter} раз",
|
||||||
"no_errors": "Нет сообщений об ошибках.",
|
"no_errors": "Нет сообщений об ошибках",
|
||||||
"no_issues": "Нет сообщений о проблемах.",
|
"no_issues": "Нет сообщений о проблемах.",
|
||||||
"refresh": "Обновить"
|
"refresh": "Обновить"
|
||||||
},
|
},
|
||||||
@ -3735,7 +3749,7 @@
|
|||||||
"page-onboarding": {
|
"page-onboarding": {
|
||||||
"analytics": {
|
"analytics": {
|
||||||
"finish": "Далее",
|
"finish": "Далее",
|
||||||
"intro": "Поделитесь аналитикой из Вашего Home Assistant. Эти данные будут доступны для всех по адресу {ссылка}."
|
"intro": "Поделитесь аналитикой из Вашего Home Assistant. Эти данные будут доступны для всех по адресу {link}."
|
||||||
},
|
},
|
||||||
"core-config": {
|
"core-config": {
|
||||||
"button_detect": "Заполнить",
|
"button_detect": "Заполнить",
|
||||||
|
@ -1188,6 +1188,9 @@
|
|||||||
"zha_device_card": {
|
"zha_device_card": {
|
||||||
"device_name_placeholder": "變更裝置名稱"
|
"device_name_placeholder": "變更裝置名稱"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"zha_reconfigure_device": {
|
||||||
|
"heading": "重新設定裝置"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"duration": {
|
"duration": {
|
||||||
@ -1860,6 +1863,7 @@
|
|||||||
"header": "分析資料",
|
"header": "分析資料",
|
||||||
"instance_id": "實例 ID:{huuid}",
|
"instance_id": "實例 ID:{huuid}",
|
||||||
"introduction": "分享實例分析資料,資料將可透過 {link} 連結公開取得",
|
"introduction": "分享實例分析資料,資料將可透過 {link} 連結公開取得",
|
||||||
|
"learn_more": "詳細了解資料會如何處理。",
|
||||||
"needs_base": "需要開啟基本分析、方能使用此選項",
|
"needs_base": "需要開啟基本分析、方能使用此選項",
|
||||||
"preference": {
|
"preference": {
|
||||||
"base": {
|
"base": {
|
||||||
@ -2071,6 +2075,9 @@
|
|||||||
"filtering_by": "篩選",
|
"filtering_by": "篩選",
|
||||||
"show": "顯示"
|
"show": "顯示"
|
||||||
},
|
},
|
||||||
|
"hassio": {
|
||||||
|
"button": "設定"
|
||||||
|
},
|
||||||
"header": "設定 Home Assistant",
|
"header": "設定 Home Assistant",
|
||||||
"helpers": {
|
"helpers": {
|
||||||
"caption": "助手",
|
"caption": "助手",
|
||||||
@ -2237,10 +2244,17 @@
|
|||||||
"clear": "清除",
|
"clear": "清除",
|
||||||
"description": "檢視 Home Assistant 日誌",
|
"description": "檢視 Home Assistant 日誌",
|
||||||
"details": "記錄詳細資料({level})",
|
"details": "記錄詳細資料({level})",
|
||||||
|
"level": {
|
||||||
|
"critical": "緊急",
|
||||||
|
"debug": "除錯",
|
||||||
|
"error": "錯誤",
|
||||||
|
"info": "資訊",
|
||||||
|
"warning": "警告"
|
||||||
|
},
|
||||||
"load_full_log": "載入完整 Home Assistant 記錄",
|
"load_full_log": "載入完整 Home Assistant 記錄",
|
||||||
"loading_log": "載入錯誤記錄中...",
|
"loading_log": "載入錯誤記錄中...",
|
||||||
"multiple_messages": "訊息首次出現於 {time}、共顯示 {counter} 次",
|
"multiple_messages": "訊息首次出現於 {time}、共顯示 {counter} 次",
|
||||||
"no_errors": "未回報任何錯誤。",
|
"no_errors": "未回報任何錯誤",
|
||||||
"no_issues": "沒有新問題!",
|
"no_issues": "沒有新問題!",
|
||||||
"refresh": "更新"
|
"refresh": "更新"
|
||||||
},
|
},
|
||||||
|
@ -1926,7 +1926,7 @@
|
|||||||
"@codemirror/state" "^0.18.0"
|
"@codemirror/state" "^0.18.0"
|
||||||
"@codemirror/view" "^0.18.0"
|
"@codemirror/view" "^0.18.0"
|
||||||
|
|
||||||
"@codemirror/highlight@^0.18.0":
|
"@codemirror/highlight@^0.18.0", "@codemirror/highlight@^0.18.1":
|
||||||
version "0.18.3"
|
version "0.18.3"
|
||||||
resolved "https://registry.yarnpkg.com/@codemirror/highlight/-/highlight-0.18.3.tgz#50e268630f113c322a2dc97c9f68d71934fffcb0"
|
resolved "https://registry.yarnpkg.com/@codemirror/highlight/-/highlight-0.18.3.tgz#50e268630f113c322a2dc97c9f68d71934fffcb0"
|
||||||
integrity sha512-NmRmkmWl8ht6Y6Y39ghov84AMPCqhUPIH9fmILs2NaWxZFZf4jGCTzrULnmREGsTie+26+LbKUncIU+PBu1Qng==
|
integrity sha512-NmRmkmWl8ht6Y6Y39ghov84AMPCqhUPIH9fmILs2NaWxZFZf4jGCTzrULnmREGsTie+26+LbKUncIU+PBu1Qng==
|
||||||
|
Loading…
x
Reference in New Issue
Block a user