Compare commits

..

1 Commits

Author SHA1 Message Date
Zack
be6fef1824 Align entity registry buttons 2022-08-31 10:30:38 -05:00
136 changed files with 26555 additions and 49403 deletions

View File

@@ -27,7 +27,9 @@ jobs:
- name: Build Demo
run: ./node_modules/.bin/gulp build-demo
- name: Deploy to Netlify
run: npx netlify-cli deploy --dir=demo/dist --prod
uses: netlify/actions/cli@master
env:
NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_AUTH_TOKEN }}
NETLIFY_SITE_ID: ${{ secrets.NETLIFY_DEMO_DEV_SITE_ID }}
with:
args: deploy --dir=demo/dist --prod

View File

@@ -61,7 +61,6 @@ class HaDemo extends HomeAssistantAppEl {
area_id: null,
disabled_by: null,
entity_id: "sensor.co2_intensity",
id: "sensor.co2_intensity",
name: null,
icon: null,
platform: "co2signal",
@@ -75,7 +74,6 @@ class HaDemo extends HomeAssistantAppEl {
area_id: null,
disabled_by: null,
entity_id: "sensor.grid_fossil_fuel_percentage",
id: "sensor.co2_intensity",
name: null,
icon: null,
platform: "co2signal",

View File

@@ -1,56 +0,0 @@
---
title: When to use remove, delete, add and create
subtitle: The difference between remove/delete and add/create.
---
# Remove vs Delete
Remove and Delete are quite similar, but can be frustrating if used inconsistently.
## Remove
Take away and set aside, but kept in existence.
For example:
* Removing a user's permission
* Removing a user from a group
* Removing links between items
* Removing a widget
* Removing a link
* Removing an item from a cart
## Delete
Erase, rendered nonexistent or nonrecoverable.
For example:
* Deleting a field
* Deleting a value in a field
* Deleting a task
* Deleting a group
* Deleting a permission
* Deleting a calendar event
# Add vs Create
In most cases, Create can be paired with Delete, and Add can be paired with Remove.
## Add
An already-exisiting item.
For example:
* Adding a permission to a user
* Adding a user to a group
* Adding links between items
* Adding a widget
* Adding a link
* Adding an item to a cart
## Create
Something made from scratch.
For example:
* Creating a new field
* Creating a new value in a field
* Creating a new task
* Creating a new group
* Creating a new permission
* Creating a new calendar event
Based on this is [UX magazine article](https://uxmag.com/articles/ui-copy-remove-vs-delete2-banner).

View File

@@ -1,9 +1,9 @@
---
title: Dialogs
title: Dialgos
subtitle: Dialogs provide important prompts in a user flow.
---
# Material Design 3
# Material Desing 3
Our dialogs are based on the latest version of Material Design. Specs and guidelines can be found on it's [website](https://m3.material.io/components/dialogs/overview).

View File

@@ -191,7 +191,6 @@ const createEntityRegistryEntries = (
hidden_by: null,
entity_category: null,
entity_id: "binary_sensor.updater",
id: "binary_sensor.updater",
name: null,
icon: null,
platform: "updater",

View File

@@ -93,8 +93,8 @@
"@polymer/paper-tooltip": "^3.0.1",
"@polymer/polymer": "3.4.1",
"@thomasloven/round-slider": "0.5.4",
"@vaadin/combo-box": "^23.2.0",
"@vaadin/vaadin-themable-mixin": "^23.2.0",
"@vaadin/combo-box": "^23.1.5",
"@vaadin/vaadin-themable-mixin": "^23.1.5",
"@vibrant/color": "^3.2.1-alpha.1",
"@vibrant/core": "^3.2.1-alpha.1",
"@vibrant/quantizer-mmcq": "^3.2.1-alpha.1",

View File

@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
[project]
name = "home-assistant-frontend"
version = "20220907.2"
version = "20220816.0"
license = {text = "Apache-2.0"}
description = "The Home Assistant frontend"
readme = "README.md"

View File

@@ -126,7 +126,6 @@ export const FIXED_DEVICE_CLASS_ICONS = {
gas: mdiGasCylinder,
humidity: mdiWaterPercent,
illuminance: mdiBrightness5,
moisture: mdiWaterPercent,
monetary: mdiCash,
nitrogen_dioxide: mdiMolecule,
nitrogen_monoxide: mdiMolecule,

View File

@@ -1,28 +0,0 @@
import { HaDurationData } from "../../components/ha-duration-input";
const leftPad = (num: number) => (num < 10 ? `0${num}` : num);
export const formatDuration = (duration: HaDurationData) => {
const d = duration.days || 0;
const h = duration.hours || 0;
const m = duration.minutes || 0;
const s = duration.seconds || 0;
const ms = duration.milliseconds || 0;
if (d > 0) {
return `${d} days ${h}:${leftPad(m)}:${leftPad(s)}`;
}
if (h > 0) {
return `${h}:${leftPad(m)}:${leftPad(s)}`;
}
if (m > 0) {
return `${m}:${leftPad(s)}`;
}
if (s > 0) {
return `${s} seconds`;
}
if (ms > 0) {
return `${ms} milliseconds`;
}
return null;
};

View File

@@ -2,18 +2,17 @@ import { HassEntity } from "home-assistant-js-websocket";
import { UNAVAILABLE, UNKNOWN } from "../../data/entity";
import { FrontendLocaleData } from "../../data/translation";
import {
updateIsInstallingFromAttributes,
UPDATE_SUPPORT_PROGRESS,
updateIsInstallingFromAttributes,
} from "../../data/update";
import { formatDuration, UNIT_TO_SECOND_CONVERT } from "../datetime/duration";
import { formatDate } from "../datetime/format_date";
import { formatDateTime } from "../datetime/format_date_time";
import { formatTime } from "../datetime/format_time";
import { formatNumber, isNumericFromAttributes } from "../number/format_number";
import { blankBeforePercent } from "../translations/blank_before_percent";
import { LocalizeFunc } from "../translations/localize";
import { computeDomain } from "./compute_domain";
import { supportsFeatureFromAttributes } from "./supports-feature";
import { formatDuration, UNIT_TO_SECOND_CONVERT } from "../datetime/duration";
import { computeDomain } from "./compute_domain";
export const computeStateDisplay = (
localize: LocalizeFunc,
@@ -68,7 +67,7 @@ export const computeStateDisplayFromEntityAttributes = (
const unit = !attributes.unit_of_measurement
? ""
: attributes.unit_of_measurement === "%"
? blankBeforePercent(locale) + "%"
? "%"
: ` ${attributes.unit_of_measurement}`;
return `${formatNumber(state, locale)}${unit}`;
}

View File

@@ -239,13 +239,10 @@ export const getStates = (
}
break;
case "light":
if (attribute === "effect" && state.attributes.effect_list) {
if (attribute === "effect") {
result.push(...state.attributes.effect_list);
} else if (
attribute === "color_mode" &&
state.attributes.supported_color_modes
) {
result.push(...state.attributes.supported_color_modes);
} else if (attribute === "color_mode") {
result.push(...state.attributes.color_modes);
}
break;
case "media_player":

View File

@@ -1,18 +0,0 @@
import { FrontendLocaleData } from "../../data/translation";
// Logic based on https://en.wikipedia.org/wiki/Percent_sign#Form_and_spacing
export const blankBeforePercent = (
localeOptions: FrontendLocaleData
): string => {
switch (localeOptions.language) {
case "cz":
case "de":
case "fi":
case "fr":
case "sk":
case "sv":
return " ";
default:
return "";
}
};

View File

@@ -7,8 +7,6 @@ import { getStates } from "../../common/entity/get_states";
import { HomeAssistant } from "../../types";
import "../ha-combo-box";
import type { HaComboBox } from "../ha-combo-box";
import { formatAttributeValue } from "../../data/entity_attributes";
import { fireEvent } from "../../common/dom/fire_event";
export type HaEntityPickerEntityFilterFunc = (entityId: HassEntity) => boolean;
@@ -57,7 +55,7 @@ class HaEntityStatePicker extends LitElement {
this.hass.locale,
key
)
: formatAttributeValue(this.hass, key),
: key,
}))
: [];
}
@@ -71,7 +69,16 @@ class HaEntityStatePicker extends LitElement {
return html`
<ha-combo-box
.hass=${this.hass}
.value=${this._value}
.value=${this.value
? this.entityId && this.hass.states[this.entityId]
? computeStateDisplay(
this.hass.localize,
this.hass.states[this.entityId],
this.hass.locale,
this.value
)
: this.value
: ""}
.autofocus=${this.autofocus}
.label=${this.label ??
this.hass.localize("ui.components.entity.entity-state-picker.state")}
@@ -88,28 +95,12 @@ class HaEntityStatePicker extends LitElement {
`;
}
private get _value() {
return this.value || "";
}
private _openedChanged(ev: PolymerChangedEvent<boolean>) {
this._opened = ev.detail.value;
}
private _valueChanged(ev: PolymerChangedEvent<string>) {
ev.stopPropagation();
const newValue = ev.detail.value;
if (newValue !== this._value) {
this._setValue(newValue);
}
}
private _setValue(value: string) {
this.value = value;
setTimeout(() => {
fireEvent(this, "value-changed", { value });
fireEvent(this, "change");
}, 0);
this.value = ev.detail.value;
}
}

View File

@@ -2,7 +2,6 @@ import { css, LitElement, PropertyValues, svg, TemplateResult } from "lit";
import { customElement, property, state } from "lit/decorators";
import { styleMap } from "lit/directives/style-map";
import { formatNumber } from "../common/number/format_number";
import { blankBeforePercent } from "../common/translations/blank_before_percent";
import { afterNextRender } from "../common/util/render-status";
import { FrontendLocaleData } from "../data/translation";
import { getValueInPercentage, normalize } from "../util/calculate";
@@ -134,11 +133,7 @@ export class Gauge extends LitElement {
? this._segment_label
: this.valueText || formatNumber(this.value, this.locale)
}${
this._segment_label
? ""
: this.label === "%"
? blankBeforePercent(this.locale) + "%"
: ` ${this.label}`
this._segment_label ? "" : this.label === "%" ? "%" : ` ${this.label}`
}
</text>
</svg>`;

View File

@@ -3,8 +3,6 @@ import { mdiDotsVertical } from "@mdi/js";
import "@polymer/paper-tooltip/paper-tooltip";
import { css, html, LitElement, TemplateResult } from "lit";
import { customElement, property } from "lit/decorators";
import { classMap } from "lit/directives/class-map";
import { haStyle } from "../resources/styles";
import { HomeAssistant } from "../types";
import "./ha-button-menu";
import "./ha-icon-button";
@@ -17,9 +15,7 @@ export interface IconOverflowMenuItem {
narrowOnly?: boolean;
disabled?: boolean;
tooltip?: string;
action: () => any;
warning?: boolean;
divider?: boolean;
onClick: CallableFunction;
}
@customElement("ha-icon-overflow-menu")
@@ -47,23 +43,19 @@ export class HaIconOverflowMenu extends LitElement {
slot="trigger"
></ha-icon-button>
${this.items.map((item) =>
item.divider
? html`<li divider role="separator"></li>`
: html`<mwc-list-item
graphic="icon"
?disabled=${item.disabled}
@click=${item.action}
class=${classMap({ warning: Boolean(item.warning) })}
>
<div slot="graphic">
<ha-svg-icon
class=${classMap({ warning: Boolean(item.warning) })}
.path=${item.path}
></ha-svg-icon>
</div>
${item.label}
</mwc-list-item> `
${this.items.map(
(item) => html`
<mwc-list-item
graphic="icon"
.disabled=${item.disabled}
@click=${item.action}
>
<div slot="graphic">
<ha-svg-icon .path=${item.path}></ha-svg-icon>
</div>
${item.label}
</mwc-list-item>
`
)}
</ha-button-menu>`
: html`
@@ -71,8 +63,6 @@ export class HaIconOverflowMenu extends LitElement {
${this.items.map((item) =>
item.narrowOnly
? ""
: item.divider
? html`<div role="separator"></div>`
: html`<div>
${item.tooltip
? html`<paper-tooltip animation-delay="0" position="left">
@@ -83,7 +73,7 @@ export class HaIconOverflowMenu extends LitElement {
@click=${item.action}
.label=${item.label}
.path=${item.path}
?disabled=${item.disabled}
.disabled=${item.disabled}
></ha-icon-button>
</div> `
)}
@@ -91,8 +81,7 @@ export class HaIconOverflowMenu extends LitElement {
`;
}
protected _handleIconOverflowMenuOpened(e) {
e.stopPropagation();
protected _handleIconOverflowMenuOpened() {
// If this component is used inside a data table, the z-index of the row
// needs to be increased. Otherwise the ha-button-menu would be displayed
// underneath the next row in the table.
@@ -110,22 +99,12 @@ export class HaIconOverflowMenu extends LitElement {
}
static get styles() {
return [
haStyle,
css`
:host {
display: flex;
justify-content: flex-end;
}
li[role="separator"] {
border-bottom-color: var(--divider-color);
}
div[role="separator"] {
border-right: 1px solid var(--divider-color);
width: 1px;
}
`,
];
return css`
:host {
display: flex;
justify-content: flex-end;
}
`;
}
}

View File

@@ -1,4 +1,4 @@
import { css, html, LitElement, PropertyValues, TemplateResult } from "lit";
import { css, html, LitElement, TemplateResult } from "lit";
import { ComboBoxLitRenderer } from "@vaadin/combo-box/lit";
import { customElement, property, query, state } from "lit/decorators";
import { fireEvent } from "../common/dom/fire_event";
@@ -13,8 +13,7 @@ type IconItem = {
icon: string;
keywords: string[];
};
let iconItems: IconItem[] = [{ icon: "", keywords: [] }];
let iconLoaded = false;
let iconItems: IconItem[] = [];
// eslint-disable-next-line lit/prefer-static-styles
const rowRenderer: ComboBoxLitRenderer<IconItem> = (item) => html`<mwc-list-item
@@ -89,16 +88,15 @@ export class HaIconPicker extends LitElement {
private async _openedChanged(ev: PolymerChangedEvent<boolean>) {
this._opened = ev.detail.value;
if (this._opened && !iconLoaded) {
if (this._opened && !iconItems.length) {
const iconList = await import("../../build/mdi/iconList.json");
iconItems = iconList.default.map((icon) => ({
icon: `mdi:${icon.name}`,
keywords: icon.keywords,
}));
iconLoaded = true;
this.comboBox.filteredItems = iconItems;
(this.comboBox as any).filteredItems = iconItems;
Object.keys(customIcons).forEach((iconSet) => {
this._loadCustomIconItems(iconSet);
@@ -118,17 +116,13 @@ export class HaIconPicker extends LitElement {
keywords: icon.keywords ?? [],
}));
iconItems.push(...customIconItems);
this.comboBox.filteredItems = iconItems;
(this.comboBox as any).filteredItems = iconItems;
} catch (e) {
// eslint-disable-next-line
console.warn(`Unable to load icon list for ${iconsetPrefix} iconset`);
}
}
protected shouldUpdate(changedProps: PropertyValues) {
return !this._opened || changedProps.has("_opened");
}
private _valueChanged(ev: PolymerChangedEvent<string>) {
ev.stopPropagation();
this._setValue(ev.detail.value);
@@ -167,12 +161,14 @@ export class HaIconPicker extends LitElement {
filteredItems.push(...filteredItemsByKeywords);
if (filteredItems.length > 0) {
this.comboBox.filteredItems = filteredItems;
(this.comboBox as any).filteredItems = filteredItems;
} else {
this.comboBox.filteredItems = [{ icon: filterString, keywords: [] }];
(this.comboBox as any).filteredItems = [
{ icon: filterString, keywords: [] },
];
}
} else {
this.comboBox.filteredItems = iconItems;
(this.comboBox as any).filteredItems = iconItems;
}
}

View File

@@ -51,9 +51,8 @@ export class HaNumberSelector extends LitElement {
`
: ""}
<ha-textfield
.inputMode=${(this.selector.number.step || 1) % 1 !== 0
? "decimal"
: "numeric"}
inputMode="numeric"
pattern="[0-9]+([\\.][0-9]+)?"
.label=${this.selector.number.mode !== "box" ? undefined : this.label}
.placeholder=${this.placeholder}
class=${classMap({ single: this.selector.number.mode === "box" })}

View File

@@ -221,15 +221,13 @@ class HaSidebar extends SubscribeMixin(LitElement) {
private _sortable?: SortableInstance;
public hassSubscribe(): UnsubscribeFunc[] {
return this.hass.user?.is_admin
? [
subscribeRepairsIssueRegistry(this.hass.connection!, (repairs) => {
this._issuesCount = repairs.issues.filter(
(issue) => !issue.ignored
).length;
}),
]
: [];
return [
subscribeRepairsIssueRegistry(this.hass.connection!, (repairs) => {
this._issuesCount = repairs.issues.filter(
(issue) => !issue.ignored
).length;
}),
];
}
protected render() {

View File

@@ -9,7 +9,6 @@ import { DeviceCondition, DeviceTrigger } from "./device_automation";
import { Action, MODES } from "./script";
export const AUTOMATION_DEFAULT_MODE: typeof MODES[number] = "single";
export const AUTOMATION_DEFAULT_MAX = 10;
export interface AutomationEntity extends HassEntityBase {
attributes: HassEntityAttributeBase & {
@@ -314,25 +313,11 @@ let inititialAutomationEditorData: Partial<AutomationConfig> | undefined;
export const getAutomationConfig = (hass: HomeAssistant, id: string) =>
hass.callApi<AutomationConfig>("GET", `config/automation/config/${id}`);
export const saveAutomationConfig = (
hass: HomeAssistant,
id: string,
config: AutomationConfig
) => hass.callApi<void>("POST", `config/automation/config/${id}`, config);
export const showAutomationEditor = (data?: Partial<AutomationConfig>) => {
inititialAutomationEditorData = data;
navigate("/config/automation/edit/new");
};
export const duplicateAutomation = (config: AutomationConfig) => {
showAutomationEditor({
...config,
id: undefined,
alias: undefined,
});
};
export const getAutomationEditorInitData = () => {
const data = inititialAutomationEditorData;
inititialAutomationEditorData = undefined;

View File

@@ -1,15 +1,7 @@
import { formatDuration } from "../common/datetime/format_duration";
import secondsToDuration from "../common/datetime/seconds_to_duration";
import { ensureArray } from "../common/ensure-array";
import { computeStateName } from "../common/entity/compute_state_name";
import type { HomeAssistant } from "../types";
import { Condition, Trigger } from "./automation";
import {
DeviceCondition,
DeviceTrigger,
localizeDeviceAutomationCondition,
localizeDeviceAutomationTrigger,
} from "./device_automation";
import { formatAttributeName } from "./entity_attributes";
export const describeTrigger = (
@@ -76,7 +68,7 @@ export const describeTrigger = (
}
// State Trigger
if (trigger.platform === "state") {
if (trigger.platform === "state" && trigger.entity_id) {
let base = "When";
let entities = "";
@@ -97,17 +89,12 @@ export const describeTrigger = (
} ${computeStateName(states[entity]) || entity}`;
}
}
} else if (trigger.entity_id) {
} else {
entities = states[trigger.entity_id]
? computeStateName(states[trigger.entity_id])
: trigger.entity_id;
}
if (!entities) {
// no entity_id or empty array
entities = "something";
}
base += ` ${entities} changes`;
if (trigger.from) {
@@ -141,19 +128,17 @@ export const describeTrigger = (
base += ` to ${to}`;
}
if (trigger.for) {
let duration: string | null;
if ("for" in trigger) {
let duration: string;
if (typeof trigger.for === "number") {
duration = secondsToDuration(trigger.for);
duration = `for ${secondsToDuration(trigger.for)!}`;
} else if (typeof trigger.for === "string") {
duration = trigger.for;
duration = `for ${trigger.for}`;
} else {
duration = formatDuration(trigger.for);
duration = `for ${JSON.stringify(trigger.for)}`;
}
if (duration) {
base += ` for ${duration}`;
}
base += ` for ${duration}`;
}
return base;
@@ -295,7 +280,7 @@ export const describeTrigger = (
}
// MQTT Trigger
if (trigger.platform === "mqtt") {
return "When an MQTT message has been received";
return "When a MQTT payload has been received";
}
// Template Trigger
@@ -307,25 +292,7 @@ export const describeTrigger = (
if (trigger.platform === "webhook") {
return "When a Webhook payload has been received";
}
if (trigger.platform === "device") {
if (!trigger.device_id) {
return "Device trigger";
}
const config = trigger as DeviceTrigger;
const localized = localizeDeviceAutomationTrigger(hass, config);
if (localized) {
return localized;
}
const stateObj = hass.states[config.entity_id as string];
return `${stateObj ? computeStateName(stateObj) : config.entity_id} ${
config.type
}`;
}
return `${
trigger.platform ? trigger.platform.replace(/_/g, " ") : "Unknown"
} trigger`;
return `${trigger.platform || "Unknown"} trigger`;
};
export const describeCondition = (
@@ -337,64 +304,15 @@ export const describeCondition = (
return condition.alias;
}
if (!condition.condition) {
const shorthands: Array<"and" | "or" | "not"> = ["and", "or", "not"];
for (const key of shorthands) {
if (!(key in condition)) {
continue;
}
if (ensureArray(condition[key])) {
condition = {
condition: key,
conditions: condition[key],
};
}
}
}
if (condition.condition === "or") {
const conditions = ensureArray(condition.conditions);
let count = "condition";
if (conditions && conditions.length > 0) {
count = `of ${conditions.length} conditions`;
}
return `Test if any ${count} matches`;
}
if (condition.condition === "and") {
const conditions = ensureArray(condition.conditions);
const count =
conditions && conditions.length > 0
? `${conditions.length} `
: "multiple";
return `Test if ${count} conditions match`;
}
if (condition.condition === "not") {
const conditions = ensureArray(condition.conditions);
const what =
conditions && conditions.length > 0
? `none of ${conditions.length} conditions match`
: "no condition matches";
return `Test if ${what}`;
if (["or", "and", "not"].includes(condition.condition)) {
return `multiple conditions using "${condition.condition}"`;
}
// State Condition
if (condition.condition === "state") {
if (condition.condition === "state" && condition.entity_id) {
let base = "Confirm";
const stateObj = hass.states[condition.entity_id];
const entity = stateObj
? computeStateName(stateObj)
: condition.entity_id
? condition.entity_id
: "an entity";
const entity = stateObj ? computeStateName(stateObj) : condition.entity_id;
if ("attribute" in condition) {
base += ` ${condition.attribute} from`;
@@ -410,14 +328,10 @@ export const describeCondition = (
: ""
} ${state}`;
}
} else if (condition.state) {
} else {
states = condition.state.toString();
}
if (!states) {
states = "a state";
}
base += ` ${entity} is ${states}`;
if ("for" in condition) {
@@ -553,22 +467,5 @@ export const describeCondition = (
}`;
}
if (condition.condition === "device") {
if (!condition.device_id) {
return "Device condition";
}
const config = condition as DeviceCondition;
const localized = localizeDeviceAutomationCondition(hass, config);
if (localized) {
return localized;
}
const stateObj = hass.states[config.entity_id as string];
return `${stateObj ? computeStateName(stateObj) : config.entity_id} ${
config.type
}`;
}
return `${
condition.condition ? condition.condition.replace(/_/g, " ") : "Unknown"
} condition`;
return `${condition.condition} condition`;
};

View File

@@ -1,13 +1,11 @@
import { Connection, createCollection } from "home-assistant-js-websocket";
import { Store } from "home-assistant-js-websocket/dist/store";
import memoizeOne from "memoize-one";
import { computeStateName } from "../common/entity/compute_state_name";
import { caseInsensitiveStringCompare } from "../common/string/compare";
import { debounce } from "../common/util/debounce";
import { HomeAssistant } from "../types";
export interface EntityRegistryEntry {
id: string;
entity_id: string;
name: string | null;
icon: string | null;
@@ -93,10 +91,7 @@ export const computeEntityRegistryName = (
return entry.name;
}
const state = hass.states[entry.entity_id];
if (state) {
return computeStateName(state);
}
return entry.original_name ? entry.original_name : entry.entity_id;
return state ? computeStateName(state) : entry.entity_id;
};
export const getExtendedEntityRegistryEntry = (
@@ -166,16 +161,6 @@ export const sortEntityRegistryByName = (entries: EntityRegistryEntry[]) =>
caseInsensitiveStringCompare(entry1.name || "", entry2.name || "")
);
export const entityRegistryById = memoizeOne(
(entries: HomeAssistant["entities"]) => {
const entities: HomeAssistant["entities"] = {};
for (const entity of Object.values(entries)) {
entities[entity.id] = entity;
}
return entities;
}
);
export const getEntityPlatformLookup = (
entities: EntityRegistryEntry[]
): Record<string, string> => {

View File

@@ -51,7 +51,6 @@ interface MediaPlayerEntityAttributes extends HassEntityAttributeBase {
media_duration?: number;
media_position?: number;
media_title?: string;
media_channel?: string;
icon?: string;
entity_picture_local?: string;
is_volume_muted?: boolean;
@@ -236,9 +235,6 @@ export const computeMediaDescription = (
}
}
break;
case "channel":
secondaryTitle = stateObj.attributes.media_channel!;
break;
default:
secondaryTitle = stateObj.attributes.app_name || "";
}

View File

@@ -301,9 +301,6 @@ export const deleteScript = (hass: HomeAssistant, objectId: string) =>
let inititialScriptEditorData: Partial<ScriptConfig> | undefined;
export const getScriptConfig = (hass: HomeAssistant, objectId: string) =>
hass.callApi<ScriptConfig>("GET", `config/script/config/${objectId}`);
export const showScriptEditor = (data?: Partial<ScriptConfig>) => {
inititialScriptEditorData = data;
navigate("/config/script/edit/new");

View File

@@ -1,4 +1,3 @@
import { formatDuration } from "../common/datetime/format_duration";
import secondsToDuration from "../common/datetime/seconds_to_duration";
import { ensureArray } from "../common/ensure-array";
import { computeStateName } from "../common/entity/compute_state_name";
@@ -6,13 +5,6 @@ import { isTemplate } from "../common/string/has-template";
import { HomeAssistant } from "../types";
import { Condition } from "./automation";
import { describeCondition, describeTrigger } from "./automation_i18n";
import { localizeDeviceAutomationAction } from "./device_automation";
import { computeDeviceName } from "./device_registry";
import {
computeEntityRegistryName,
entityRegistryById,
} from "./entity_registry";
import { domainToName } from "./integration";
import {
ActionType,
ActionTypes,
@@ -55,13 +47,9 @@ export const describeAction = <T extends ActionType>(
) {
base = "Call a service based on a template";
} else if (config.service) {
const [domain, serviceName] = config.service.split(".", 2);
const service = hass.services[domain][serviceName];
base = service
? `${domainToName(hass.localize, domain)}: ${service.name}`
: `Call service: ${config.service}`;
base = `Call service ${config.service}`;
} else {
return "Call a service";
return actionType;
}
if (config.target) {
const targets: string[] = [];
@@ -78,49 +66,26 @@ export const describeAction = <T extends ActionType>(
? 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 if (key === "entity_id") {
if (targetThing.includes(".")) {
const state = hass.states[targetThing];
if (state) {
targets.push(computeStateName(state));
} else {
targets.push(targetThing);
}
} else {
const entityReg = entityRegistryById(hass.entities)[targetThing];
if (entityReg) {
targets.push(
computeEntityRegistryName(hass, entityReg) || targetThing
);
} else {
targets.push("unknown entity");
}
}
} else if (key === "device_id") {
const device = hass.devices[targetThing];
if (device) {
targets.push(computeDeviceName(device, hass));
} else {
targets.push("unknown device");
}
} else if (key === "area_id") {
const area = hass.areas[targetThing];
if (area?.name) {
targets.push(area.name);
} else {
targets.push("unknown area");
}
} else {
targets.push(targetThing);
values.push(targetThing);
}
}
if (renderValues) {
targets.push(`${label} ${values.join(", ")}`);
}
}
if (targets.length > 0) {
base += ` ${targets.join(", ")}`;
base += ` on ${targets.join(", ")}`;
}
}
@@ -137,11 +102,9 @@ export const describeAction = <T extends ActionType>(
} else if (typeof config.delay === "string") {
duration = isTemplate(config.delay)
? "based on a template"
: `for ${config.delay || "a duration"}`;
} else if (config.delay) {
duration = `for ${formatDuration(config.delay)}`;
: `for ${config.delay}`;
} else {
duration = "for a duration";
duration = `for ${JSON.stringify(config.delay)}`;
}
return `Delay ${duration}`;
@@ -155,12 +118,13 @@ export const describeAction = <T extends ActionType>(
} else {
entityId = config.target?.entity_id || config.entity_id;
}
if (!entityId) {
return "Activate a scene";
}
const sceneStateObj = entityId ? hass.states[entityId] : undefined;
return `Active scene ${
sceneStateObj ? computeStateName(sceneStateObj) : entityId
return `Scene ${
sceneStateObj
? computeStateName(sceneStateObj)
: "scene" in config
? config.scene
: config.target?.entity_id || config.entity_id || ""
}`;
}
@@ -168,22 +132,16 @@ export const describeAction = <T extends ActionType>(
const config = action as PlayMediaAction;
const entityId = config.target?.entity_id || config.entity_id;
const mediaStateObj = entityId ? hass.states[entityId] : undefined;
return `Play ${
config.metadata.title || config.data.media_content_id || "media"
} on ${
return `Play ${config.metadata.title || config.data.media_content_id} on ${
mediaStateObj
? computeStateName(mediaStateObj)
: entityId || "a media player"
: config.target?.entity_id || config.entity_id
}`;
}
if (actionType === "wait_for_trigger") {
const config = action as WaitForTriggerAction;
const triggers = ensureArray(config.wait_for_trigger);
if (!triggers || triggers.length === 0) {
return "Wait for a trigger";
}
return `Wait for ${triggers
return `Wait for ${ensureArray(config.wait_for_trigger)
.map((trigger) => describeTrigger(trigger, hass))
.join(", ")}`;
}
@@ -206,26 +164,22 @@ export const describeAction = <T extends ActionType>(
}
if (actionType === "check_condition") {
return describeCondition(action as Condition, hass);
return `Test ${describeCondition(action as Condition, hass)}`;
}
if (actionType === "stop") {
const config = action as StopAction;
return `Stop${config.stop ? ` because: ${config.stop}` : ""}`;
return `Stopped${config.stop ? ` because: ${config.stop}` : ""}`;
}
if (actionType === "if") {
const config = action as IfAction;
return `Perform an action if: ${
!config.if
? ""
: typeof config.if === "string"
typeof config.if === "string"
? config.if
: ensureArray(config.if).length > 1
? `${ensureArray(config.if).length} conditions`
: ensureArray(config.if).length
? describeCondition(ensureArray(config.if)[0], hass)
: ""
: describeCondition(ensureArray(config.if)[0], hass)
}${config.else ? " (or else!)" : ""}`;
}
@@ -265,13 +219,6 @@ export const describeAction = <T extends ActionType>(
if (actionType === "device_action") {
const config = action as DeviceAction;
if (!config.device_id) {
return "Device action";
}
const localized = localizeDeviceAutomationAction(hass, config);
if (localized) {
return localized;
}
const stateObj = hass.states[config.entity_id as string];
return `${config.type || "Perform action with"} ${
stateObj ? computeStateName(stateObj) : config.entity_id

View File

@@ -85,13 +85,6 @@ enum Protocols {
ZWaveLongRange = 1,
}
enum NodeType {
Controller,
/** @deprecated Use `NodeType["End Node"]` instead */
"Routing End Node",
"End Node" = 1,
}
export enum FirmwareUpdateStatus {
Error_Timeout = -1,
Error_Checksum = 0,
@@ -149,12 +142,12 @@ export interface ZWaveJSController {
sdk_version: string;
type: number;
own_node_id: number;
is_primary: boolean;
is_secondary: boolean;
is_using_home_id_from_other_network: boolean;
is_sis_present: boolean;
was_real_primary: boolean;
is_suc: boolean;
node_type: NodeType;
is_static_update_controller: boolean;
is_slave: boolean;
firmware_version: string;
manufacturer_id: number;
product_id: number;

View File

@@ -167,8 +167,7 @@ class MoreInfoMediaPlayer extends LitElement {
</div>
`
: ""}
${![UNAVAILABLE, UNKNOWN, "off"].includes(stateObj.state) &&
supportsFeature(stateObj, SUPPORT_SELECT_SOUND_MODE) &&
${supportsFeature(stateObj, SUPPORT_SELECT_SOUND_MODE) &&
stateObj.attributes.sound_mode_list?.length
? html`
<div class="sound-input">

View File

@@ -1,15 +1,6 @@
import {
css,
CSSResultGroup,
html,
LitElement,
PropertyValues,
TemplateResult,
} from "lit";
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
import { customElement, eventOptions, property } from "lit/decorators";
import { restoreScroll } from "../common/decorators/restore-scroll";
import { toggleAttribute } from "../common/dom/toggle_attribute";
import { computeRTL } from "../common/util/compute_rtl";
import "../components/ha-icon-button-arrow-prev";
import "../components/ha-menu-button";
import { HomeAssistant } from "../types";
@@ -24,8 +15,6 @@ class HassSubpage extends LitElement {
@property({ type: String, attribute: "back-path" }) public backPath?: string;
@property() public backCallback?: () => void;
@property({ type: Boolean, reflect: true }) public narrow = false;
@property({ type: Boolean }) public supervisor = false;
@@ -33,17 +22,6 @@ class HassSubpage extends LitElement {
// @ts-ignore
@restoreScroll(".content") private _savedScrollPos?: number;
protected willUpdate(changedProps: PropertyValues): void {
super.willUpdate(changedProps);
if (!changedProps.has("hass")) {
return;
}
const oldHass = changedProps.get("hass") as HomeAssistant | undefined;
if (!oldHass || oldHass.locale !== this.hass.locale) {
toggleAttribute(this, "rtl", computeRTL(this.hass));
}
}
protected render(): TemplateResult {
return html`
<div class="toolbar">
@@ -74,9 +52,6 @@ class HassSubpage extends LitElement {
<slot name="toolbar-icon"></slot>
</div>
<div class="content" @scroll=${this._saveScrollPos}><slot></slot></div>
<div id="fab">
<slot name="fab"></slot>
</div>
`;
}
@@ -86,10 +61,6 @@ class HassSubpage extends LitElement {
}
private _backTapped(): void {
if (this.backCallback) {
this.backCallback();
return;
}
history.back();
}
@@ -145,29 +116,6 @@ class HassSubpage extends LitElement {
overflow: auto;
-webkit-overflow-scrolling: touch;
}
#fab {
position: fixed;
right: calc(16px + env(safe-area-inset-right));
bottom: calc(16px + env(safe-area-inset-bottom));
z-index: 1;
}
:host([narrow]) #fab.tabs {
bottom: calc(84px + env(safe-area-inset-bottom));
}
#fab[is-wide] {
bottom: 24px;
right: 24px;
}
:host([rtl]) #fab {
right: auto;
left: calc(16px + env(safe-area-inset-left));
}
:host([rtl][is-wide]) #fab {
bottom: 24px;
left: 24px;
right: auto;
}
`;
}
}

View File

@@ -375,9 +375,3 @@ export class HaTabsSubpageDataTable extends LitElement {
`;
}
}
declare global {
interface HTMLElementTagNameMap {
"hass-tabs-subpage-data-table": HaTabsSubpageDataTable;
}
}

View File

@@ -45,14 +45,13 @@ import { showConfirmationDialog } from "../../../dialogs/generic/show-dialog-box
import { showMoreInfoDialog } from "../../../dialogs/more-info/show-ha-more-info-dialog";
import { SubscribeMixin } from "../../../mixins/subscribe-mixin";
import { haStyle } from "../../../resources/styles";
import { HomeAssistant } from "../../../types";
import { HomeAssistant, Route } from "../../../types";
import "../../logbook/ha-logbook";
import { configSections } from "../ha-panel-config";
import {
loadAreaRegistryDetailDialog,
showAreaRegistryDetailDialog,
} from "./show-dialog-area-registry-detail";
import "../../../layouts/hass-error-screen";
import "../../../layouts/hass-subpage";
declare type NameAndEntity<EntityType extends HassEntity> = {
name: string;
@@ -67,9 +66,11 @@ class HaConfigAreaPage extends SubscribeMixin(LitElement) {
@property({ type: Boolean, reflect: true }) public narrow!: boolean;
@property({ type: Boolean }) public isWide!: boolean;
@property() public isWide!: boolean;
@property({ type: Boolean }) public showAdvanced!: boolean;
@property() public showAdvanced!: boolean;
@property() public route!: Route;
@state() public _areas!: AreaRegistryEntry[];
@@ -241,20 +242,43 @@ class HaConfigAreaPage extends SubscribeMixin(LitElement) {
}
return html`
<hass-subpage
<hass-tabs-subpage
.hass=${this.hass}
.narrow=${this.narrow}
.header=${area.name}
.tabs=${configSections.areas}
.route=${this.route}
>
<ha-icon-button
.path=${mdiPencil}
.entry=${area}
@click=${this._showSettings}
slot="toolbar-icon"
.label=${this.hass.localize("ui.panel.config.areas.edit_settings")}
></ha-icon-button>
${this.narrow
? html`<span slot="header"> ${area.name} </span>
<ha-icon-button
.path=${mdiPencil}
.entry=${area}
@click=${this._showSettings}
slot="toolbar-icon"
.label=${this.hass.localize(
"ui.panel.config.areas.edit_settings"
)}
></ha-icon-button>`
: ""}
<div class="container">
${!this.narrow
? html`
<div class="fullwidth">
<h1>
${area.name}
<ha-icon-button
.path=${mdiPencil}
.entry=${area}
@click=${this._showSettings}
.label=${this.hass.localize(
"ui.panel.config.areas.edit_settings"
)}
></ha-icon-button>
</h1>
</div>
`
: ""}
<div class="column">
${area.picture
? html`<div class="img-container">
@@ -480,7 +504,7 @@ class HaConfigAreaPage extends SubscribeMixin(LitElement) {
: ""}
</div>
</div>
</hass-subpage>
</hass-tabs-subpage>
`;
}

View File

@@ -1,6 +1,8 @@
import { ActionDetail } from "@material/mwc-list/mwc-list-foundation";
import "@material/mwc-list/mwc-list-item";
import {
mdiArrowDown,
mdiArrowUp,
mdiCheck,
mdiContentDuplicate,
mdiDelete,
@@ -15,15 +17,13 @@ import { customElement, property, query, state } from "lit/decorators";
import { classMap } from "lit/directives/class-map";
import { dynamicElement } from "../../../../common/dom/dynamic-element-directive";
import { fireEvent } from "../../../../common/dom/fire_event";
import { capitalizeFirstLetter } from "../../../../common/string/capitalize-first-letter";
import { handleStructError } from "../../../../common/structs/handle-errors";
import "../../../../components/ha-alert";
import "../../../../components/ha-button-menu";
import "../../../../components/ha-card";
import "../../../../components/ha-expansion-panel";
import "../../../../components/ha-icon-button";
import "../../../../components/ha-expansion-panel";
import type { HaYamlEditor } from "../../../../components/ha-yaml-editor";
import { ACTION_TYPES } from "../../../../data/action";
import { validateConfig } from "../../../../data/config";
import { Action, getActionType } from "../../../../data/script";
import { describeAction } from "../../../../data/script_i18n";
@@ -50,6 +50,8 @@ import "./types/ha-automation-action-service";
import "./types/ha-automation-action-stop";
import "./types/ha-automation-action-wait_for_trigger";
import "./types/ha-automation-action-wait_template";
import { ACTION_TYPES } from "../../../../data/action";
import { capitalizeFirstLetter } from "../../../../common/string/capitalize-first-letter";
const getType = (action: Action | undefined) => {
if (!action) {
@@ -64,6 +66,13 @@ const getType = (action: Action | undefined) => {
return Object.keys(ACTION_TYPES).find((option) => option in action);
};
declare global {
// for fire event
interface HASSDomEvents {
"move-action": { direction: "up" | "down" };
}
}
export interface ActionElement extends LitElement {
action: Action;
}
@@ -98,12 +107,12 @@ export default class HaAutomationActionRow extends LitElement {
@property() public action!: Action;
@property() public index!: number;
@property() public totalActions!: number;
@property({ type: Boolean }) public narrow = false;
@property({ type: Boolean }) public hideMenu = false;
@property({ type: Boolean }) public reOrderMode = false;
@state() private _warnings?: string[];
@state() private _uiModeAvailable = true;
@@ -148,120 +157,125 @@ export default class HaAutomationActionRow extends LitElement {
</div>`
: ""}
<ha-expansion-panel leftChevron>
<h3 slot="header">
<div slot="header">
<ha-svg-icon
class="action-icon"
.path=${ACTION_TYPES[type!]}
></ha-svg-icon>
${capitalizeFirstLetter(describeAction(this.hass, this.action))}
</h3>
</div>
<slot name="icons" slot="icons"></slot>
${this.hideMenu
? ""
: html`
<ha-button-menu
${this.index !== 0
? html`
<ha-icon-button
slot="icons"
fixed
corner="BOTTOM_START"
@action=${this._handleAction}
@click=${preventDefault}
>
<ha-icon-button
slot="trigger"
.label=${this.hass.localize("ui.common.menu")}
.path=${mdiDotsVertical}
></ha-icon-button>
<mwc-list-item graphic="icon">
${this.hass.localize(
"ui.panel.config.automation.editor.actions.run"
)}
<ha-svg-icon slot="graphic" .path=${mdiPlay}></ha-svg-icon>
</mwc-list-item>
.label=${this.hass.localize(
"ui.panel.config.automation.editor.move_up"
)}
.path=${mdiArrowUp}
@click=${this._moveUp}
></ha-icon-button>
`
: ""}
${this.index !== this.totalActions - 1
? html`
<ha-icon-button
slot="icons"
.label=${this.hass.localize(
"ui.panel.config.automation.editor.move_down"
)}
.path=${mdiArrowDown}
@click=${this._moveDown}
></ha-icon-button>
`
: ""}
<ha-button-menu
slot="icons"
fixed
corner="BOTTOM_START"
@action=${this._handleAction}
@click=${preventDefault}
>
<ha-icon-button
slot="trigger"
.label=${this.hass.localize("ui.common.menu")}
.path=${mdiDotsVertical}
></ha-icon-button>
<mwc-list-item graphic="icon">
${this.hass.localize(
"ui.panel.config.automation.editor.actions.run"
)}
<ha-svg-icon slot="graphic" .path=${mdiPlay}></ha-svg-icon>
</mwc-list-item>
<mwc-list-item graphic="icon">
${this.hass.localize(
"ui.panel.config.automation.editor.actions.rename"
)}
<ha-svg-icon
slot="graphic"
.path=${mdiRenameBox}
></ha-svg-icon>
</mwc-list-item>
<mwc-list-item graphic="icon">
${this.hass.localize(
"ui.panel.config.automation.editor.actions.duplicate"
)}
<ha-svg-icon
slot="graphic"
.path=${mdiContentDuplicate}
></ha-svg-icon>
</mwc-list-item>
<mwc-list-item graphic="icon">
${this.hass.localize(
"ui.panel.config.automation.editor.actions.rename"
)}
<ha-svg-icon slot="graphic" .path=${mdiRenameBox}></ha-svg-icon>
</mwc-list-item>
<mwc-list-item graphic="icon">
${this.hass.localize(
"ui.panel.config.automation.editor.actions.duplicate"
)}
<ha-svg-icon
slot="graphic"
.path=${mdiContentDuplicate}
></ha-svg-icon>
</mwc-list-item>
<li divider role="separator"></li>
<li divider role="separator"></li>
<mwc-list-item
.disabled=${!this._uiModeAvailable}
graphic="icon"
>
${this.hass.localize(
"ui.panel.config.automation.editor.edit_ui"
)}
${!yamlMode
? html`<ha-svg-icon
class="selected_menu_item"
slot="graphic"
.path=${mdiCheck}
></ha-svg-icon>`
: ``}
</mwc-list-item>
<mwc-list-item .disabled=${!this._uiModeAvailable} graphic="icon">
${this.hass.localize("ui.panel.config.automation.editor.edit_ui")}
${!yamlMode
? html`<ha-svg-icon
slot="graphic"
.path=${mdiCheck}
></ha-svg-icon>`
: ``}
</mwc-list-item>
<mwc-list-item
.disabled=${!this._uiModeAvailable}
graphic="icon"
>
${this.hass.localize(
"ui.panel.config.automation.editor.edit_yaml"
)}
${yamlMode
? html`<ha-svg-icon
class="selected_menu_item"
slot="graphic"
.path=${mdiCheck}
></ha-svg-icon>`
: ``}
</mwc-list-item>
<mwc-list-item .disabled=${!this._uiModeAvailable} graphic="icon">
${this.hass.localize(
"ui.panel.config.automation.editor.edit_yaml"
)}
${yamlMode
? html`<ha-svg-icon
slot="graphic"
.path=${mdiCheck}
></ha-svg-icon>`
: ``}
</mwc-list-item>
<li divider role="separator"></li>
<mwc-list-item graphic="icon">
${this.action.enabled === false
? this.hass.localize(
"ui.panel.config.automation.editor.actions.enable"
)
: this.hass.localize(
"ui.panel.config.automation.editor.actions.disable"
)}
<ha-svg-icon
slot="graphic"
.path=${this.action.enabled === false
? mdiPlayCircleOutline
: mdiStopCircleOutline}
></ha-svg-icon>
</mwc-list-item>
<mwc-list-item class="warning" graphic="icon">
${this.hass.localize(
"ui.panel.config.automation.editor.actions.delete"
)}
<ha-svg-icon
class="warning"
slot="graphic"
.path=${mdiDelete}
></ha-svg-icon>
</mwc-list-item>
</ha-button-menu>
`}
<li divider role="separator"></li>
<mwc-list-item graphic="icon">
${this.action.enabled === false
? this.hass.localize(
"ui.panel.config.automation.editor.actions.enable"
)
: this.hass.localize(
"ui.panel.config.automation.editor.actions.disable"
)}
<ha-svg-icon
slot="graphic"
.path=${this.action.enabled === false
? mdiPlayCircleOutline
: mdiStopCircleOutline}
></ha-svg-icon>
</mwc-list-item>
<mwc-list-item class="warning" graphic="icon">
${this.hass.localize(
"ui.panel.config.automation.editor.actions.delete"
)}
<ha-svg-icon
class="warning"
slot="graphic"
.path=${mdiDelete}
></ha-svg-icon>
</mwc-list-item>
</ha-button-menu>
<div
class=${classMap({
"card-content": true,
@@ -311,7 +325,6 @@ export default class HaAutomationActionRow extends LitElement {
hass: this.hass,
action: this.action,
narrow: this.narrow,
reOrderMode: this.reOrderMode,
})}
</div>
`}
@@ -331,6 +344,16 @@ export default class HaAutomationActionRow extends LitElement {
}
}
private _moveUp(ev) {
ev.preventDefault();
fireEvent(this, "move-action", { direction: "up" });
}
private _moveDown(ev) {
ev.preventDefault();
fireEvent(this, "move-action", { direction: "down" });
}
private async _handleAction(ev: CustomEvent<ActionDetail>) {
switch (ev.detail.index) {
case 0:
@@ -484,18 +507,13 @@ export default class HaAutomationActionRow extends LitElement {
--expansion-panel-summary-padding: 0 0 0 8px;
--expansion-panel-content-padding: 0;
}
h3 {
margin: 0;
font-size: inherit;
font-weight: inherit;
}
.action-icon {
display: none;
}
@media (min-width: 870px) {
.action-icon {
display: inline-block;
color: var(--secondary-text-color);
color: var(--primary-color);
opacity: 0.9;
margin-right: 8px;
}
@@ -516,12 +534,6 @@ export default class HaAutomationActionRow extends LitElement {
.warning ul {
margin: 4px 0;
}
.selected_menu_item {
color: var(--primary-color);
}
li[role="separator"] {
border-bottom-color: var(--divider-color);
}
`,
];
}

View File

@@ -1,25 +1,15 @@
import { repeat } from "lit/directives/repeat";
import { mdiPlus } from "@mdi/js";
import deepClone from "deep-clone-simple";
import "@material/mwc-button";
import type { ActionDetail } from "@material/mwc-list";
import { mdiArrowDown, mdiArrowUp, mdiDrag, mdiPlus } from "@mdi/js";
import deepClone from "deep-clone-simple";
import memoizeOne from "memoize-one";
import { css, CSSResultGroup, html, LitElement, PropertyValues } from "lit";
import { customElement, property } from "lit/decorators";
import { repeat } from "lit/directives/repeat";
import memoizeOne from "memoize-one";
import type { SortableEvent } from "sortablejs";
import { fireEvent } from "../../../../common/dom/fire_event";
import { stringCompare } from "../../../../common/string/compare";
import { LocalizeFunc } from "../../../../common/translations/localize";
import "../../../../components/ha-button-menu";
import type { HaSelect } from "../../../../components/ha-select";
import "../../../../components/ha-svg-icon";
import { ACTION_TYPES } from "../../../../data/action";
import "../../../../components/ha-button-menu";
import { Action } from "../../../../data/script";
import { sortableStyles } from "../../../../resources/ha-sortable-style";
import {
loadSortable,
SortableInstance,
} from "../../../../resources/sortable.ondemand";
import { HomeAssistant } from "../../../../types";
import "./ha-automation-action-row";
import type HaAutomationActionRow from "./ha-automation-action-row";
@@ -37,6 +27,10 @@ import "./types/ha-automation-action-service";
import "./types/ha-automation-action-stop";
import "./types/ha-automation-action-wait_for_trigger";
import "./types/ha-automation-action-wait_template";
import { ACTION_TYPES } from "../../../../data/action";
import { stringCompare } from "../../../../common/string/compare";
import { LocalizeFunc } from "../../../../common/translations/localize";
import type { HaSelect } from "../../../../components/ha-select";
@customElement("ha-automation-action")
export default class HaAutomationAction extends LitElement {
@@ -46,62 +40,28 @@ export default class HaAutomationAction extends LitElement {
@property() public actions!: Action[];
@property({ type: Boolean }) public reOrderMode = false;
private _focusLastActionOnChange = false;
private _actionKeys = new WeakMap<Action, string>();
private _sortable?: SortableInstance;
protected render() {
return html`
<div class="actions">
${repeat(
this.actions,
(action) => this._getKey(action),
(action, idx) => html`
<ha-automation-action-row
.index=${idx}
.action=${action}
.narrow=${this.narrow}
.hideMenu=${this.reOrderMode}
.reOrderMode=${this.reOrderMode}
@duplicate=${this._duplicateAction}
@value-changed=${this._actionChanged}
.hass=${this.hass}
>
${this.reOrderMode
? html`
<ha-icon-button
.index=${idx}
slot="icons"
.label=${this.hass.localize(
"ui.panel.config.automation.editor.move_up"
)}
.path=${mdiArrowUp}
@click=${this._moveUp}
.disabled=${idx === 0}
></ha-icon-button>
<ha-icon-button
.index=${idx}
slot="icons"
.label=${this.hass.localize(
"ui.panel.config.automation.editor.move_down"
)}
.path=${mdiArrowDown}
@click=${this._moveDown}
.disabled=${idx === this.actions.length - 1}
></ha-icon-button>
<div class="handle" slot="icons">
<ha-svg-icon .path=${mdiDrag}></ha-svg-icon>
</div>
`
: ""}
</ha-automation-action-row>
`
)}
</div>
${repeat(
this.actions,
(action) => this._getKey(action),
(action, idx) => html`
<ha-automation-action-row
.index=${idx}
.totalActions=${this.actions.length}
.action=${action}
.narrow=${this.narrow}
@duplicate=${this._duplicateAction}
@move-action=${this._move}
@value-changed=${this._actionChanged}
.hass=${this.hass}
></ha-automation-action-row>
`
)}
<ha-button-menu fixed @action=${this._addAction}>
<mwc-button
slot="trigger"
@@ -126,13 +86,6 @@ export default class HaAutomationAction extends LitElement {
protected updated(changedProps: PropertyValues) {
super.updated(changedProps);
if (changedProps.has("reOrderMode")) {
if (this.reOrderMode) {
this._createSortable();
} else {
this._destroySortable();
}
}
if (changedProps.has("actions") && this._focusLastActionOnChange) {
this._focusLastActionOnChange = false;
@@ -147,33 +100,6 @@ export default class HaAutomationAction extends LitElement {
}
}
private async _createSortable() {
const Sortable = await loadSortable();
this._sortable = new Sortable(this.shadowRoot!.querySelector(".actions")!, {
animation: 150,
fallbackClass: "sortable-fallback",
handle: ".handle",
onChoose: (evt: SortableEvent) => {
(evt.item as any).placeholder =
document.createComment("sort-placeholder");
evt.item.after((evt.item as any).placeholder);
},
onEnd: (evt: SortableEvent) => {
// put back in original location
if ((evt.item as any).placeholder) {
(evt.item as any).placeholder.replaceWith(evt.item);
delete (evt.item as any).placeholder;
}
this._dragged(evt);
},
});
}
private _destroySortable() {
this._sortable?.destroy();
this._sortable = undefined;
}
private _getKey(action: Action) {
if (!this._actionKeys.has(action)) {
this._actionKeys.set(action, Math.random().toString());
@@ -195,24 +121,12 @@ export default class HaAutomationAction extends LitElement {
fireEvent(this, "value-changed", { value: actions });
}
private _moveUp(ev) {
private _move(ev: CustomEvent) {
// Prevent possible parent action-row from also moving
ev.stopPropagation();
const index = (ev.target as any).index;
const newIndex = index - 1;
this._move(index, newIndex);
}
private _moveDown(ev) {
const index = (ev.target as any).index;
const newIndex = index + 1;
this._move(index, newIndex);
}
private _dragged(ev: SortableEvent): void {
if (ev.oldIndex === ev.newIndex) return;
this._move(ev.oldIndex!, ev.newIndex!);
}
private _move(index: number, newIndex: number) {
const newIndex = ev.detail.direction === "up" ? index - 1 : index + 1;
const actions = this.actions.concat();
const action = actions.splice(index, 1)[0];
actions.splice(newIndex, 0, action);
@@ -263,27 +177,16 @@ export default class HaAutomationAction extends LitElement {
);
static get styles(): CSSResultGroup {
return [
sortableStyles,
css`
ha-automation-action-row {
display: block;
margin-bottom: 16px;
scroll-margin-top: 48px;
}
ha-svg-icon {
height: 20px;
}
.handle {
cursor: move;
padding: 12px;
}
.handle ha-svg-icon {
pointer-events: none;
height: 24px;
}
`,
];
return css`
ha-automation-action-row {
display: block;
margin-bottom: 16px;
scroll-margin-top: 48px;
}
ha-svg-icon {
height: 20px;
}
`;
}
}

View File

@@ -9,6 +9,7 @@ import { Action, ChooseAction } from "../../../../../data/script";
import { haStyle } from "../../../../../resources/styles";
import { HomeAssistant } from "../../../../../types";
import { ActionElement } from "../ha-automation-action-row";
import "../../../../../components/ha-form/ha-form";
@customElement("ha-automation-action-choose")
export class HaChooseAction extends LitElement implements ActionElement {
@@ -16,8 +17,6 @@ export class HaChooseAction extends LitElement implements ActionElement {
@property() public action!: ChooseAction;
@property({ type: Boolean }) public reOrderMode = false;
@state() private _showDefault = false;
public static get defaultConfig() {
@@ -53,7 +52,6 @@ export class HaChooseAction extends LitElement implements ActionElement {
</h3>
<ha-automation-condition
.conditions=${option.conditions}
.reOrderMode=${this.reOrderMode}
.hass=${this.hass}
.idx=${idx}
@value-changed=${this._conditionChanged}
@@ -63,13 +61,13 @@ export class HaChooseAction extends LitElement implements ActionElement {
"ui.panel.config.automation.editor.actions.type.choose.sequence"
)}:
</h3>
<ha-automation-action
.actions=${option.sequence || []}
.reOrderMode=${this.reOrderMode}
<ha-form
.hass=${this.hass}
.schema=${[{ name: "sequence", selector: { action: {} } }]}
.data=${option}
.idx=${idx}
@value-changed=${this._actionChanged}
></ha-automation-action>
></ha-form>
</div>
</ha-card>`
)}
@@ -91,7 +89,6 @@ export class HaChooseAction extends LitElement implements ActionElement {
</h2>
<ha-automation-action
.actions=${action.default || []}
.reOrderMode=${this.reOrderMode}
@value-changed=${this._defaultChanged}
.hass=${this.hass}
></ha-automation-action>
@@ -125,7 +122,7 @@ export class HaChooseAction extends LitElement implements ActionElement {
private _actionChanged(ev: CustomEvent) {
ev.stopPropagation();
const value = ev.detail.value as Action[];
const value = ev.detail.value.sequence as Action[];
const index = (ev.target as any).idx;
const choose = this.action.choose
? [...ensureArray(this.action.choose)]
@@ -184,6 +181,9 @@ export class HaChooseAction extends LitElement implements ActionElement {
right: 0;
padding: 4px;
}
ha-form::part(root) {
overflow: visible;
}
ha-svg-icon {
height: 20px;
}

View File

@@ -15,8 +15,6 @@ export class HaIfAction extends LitElement implements ActionElement {
@property({ attribute: false }) public action!: IfAction;
@property({ type: Boolean }) public reOrderMode = false;
@state() private _showElse = false;
public static get defaultConfig() {
@@ -37,9 +35,8 @@ export class HaIfAction extends LitElement implements ActionElement {
</h3>
<ha-automation-condition
.conditions=${action.if}
.reOrderMode=${this.reOrderMode}
@value-changed=${this._ifChanged}
.hass=${this.hass}
@value-changed=${this._ifChanged}
></ha-automation-condition>
<h3>
@@ -49,7 +46,6 @@ export class HaIfAction extends LitElement implements ActionElement {
</h3>
<ha-automation-action
.actions=${action.then}
.reOrderMode=${this.reOrderMode}
@value-changed=${this._thenChanged}
.hass=${this.hass}
></ha-automation-action>
@@ -62,7 +58,6 @@ export class HaIfAction extends LitElement implements ActionElement {
</h3>
<ha-automation-action
.actions=${action.else || []}
.reOrderMode=${this.reOrderMode}
@value-changed=${this._elseChanged}
.hass=${this.hass}
></ha-automation-action>

View File

@@ -14,8 +14,6 @@ export class HaParallelAction extends LitElement implements ActionElement {
@property({ attribute: false }) public action!: ParallelAction;
@property({ type: Boolean }) public reOrderMode = false;
public static get defaultConfig() {
return {
parallel: [],
@@ -28,7 +26,6 @@ export class HaParallelAction extends LitElement implements ActionElement {
return html`
<ha-automation-action
.actions=${action.parallel}
.reOrderMode=${this.reOrderMode}
@value-changed=${this._actionsChanged}
.hass=${this.hass}
></ha-automation-action>

View File

@@ -25,8 +25,6 @@ export class HaRepeatAction extends LitElement implements ActionElement {
@property({ attribute: false }) public action!: RepeatAction;
@property({ type: Boolean }) public reOrderMode = false;
public static get defaultConfig() {
return { repeat: { count: 2, sequence: [] } };
}
@@ -97,7 +95,6 @@ export class HaRepeatAction extends LitElement implements ActionElement {
</h3>
<ha-automation-action
.actions=${action.sequence}
.reOrderMode=${this.reOrderMode}
@value-changed=${this._actionChanged}
.hass=${this.hass}
></ha-automation-action>
@@ -115,7 +112,6 @@ export class HaRepeatAction extends LitElement implements ActionElement {
fireEvent(this, "value-changed", {
value: {
...this.action,
repeat: { [type]: value, sequence: this.action.repeat.sequence },
},
});
@@ -126,7 +122,6 @@ export class HaRepeatAction extends LitElement implements ActionElement {
const value = ev.detail.value as Condition[];
fireEvent(this, "value-changed", {
value: {
...this.action,
repeat: {
...this.action.repeat,
[getType(this.action.repeat)!]: value,
@@ -140,7 +135,6 @@ export class HaRepeatAction extends LitElement implements ActionElement {
const value = ev.detail.value as Action[];
fireEvent(this, "value-changed", {
value: {
...this.action,
repeat: {
...this.action.repeat,
sequence: value,
@@ -156,7 +150,6 @@ export class HaRepeatAction extends LitElement implements ActionElement {
}
fireEvent(this, "value-changed", {
value: {
...this.action,
repeat: {
...this.action.repeat,
count: newVal,

View File

@@ -1,167 +0,0 @@
import "@material/mwc-button";
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
import { customElement, property, state } from "lit/decorators";
import { fireEvent } from "../../../../common/dom/fire_event";
import { createCloseHeading } from "../../../../components/ha-dialog";
import "../../../../components/ha-textfield";
import "../../../../components/ha-select";
import { HassDialog } from "../../../../dialogs/make-dialog-manager";
import { haStyle, haStyleDialog } from "../../../../resources/styles";
import type { HomeAssistant } from "../../../../types";
import type { AutomationModeDialog } from "./show-dialog-automation-mode";
import {
AUTOMATION_DEFAULT_MAX,
AUTOMATION_DEFAULT_MODE,
} from "../../../../data/automation";
import { documentationUrl } from "../../../../util/documentation-url";
import { isMaxMode, MODES } from "../../../../data/script";
import "@material/mwc-list/mwc-list-item";
import { stopPropagation } from "../../../../common/dom/stop_propagation";
@customElement("ha-dialog-automation-mode")
class DialogAutomationMode extends LitElement implements HassDialog {
@property({ attribute: false }) public hass!: HomeAssistant;
@state() private _opened = false;
private _params!: AutomationModeDialog;
@state() private _newMode: typeof MODES[number] = AUTOMATION_DEFAULT_MODE;
@state() private _newMax?: number;
public showDialog(params: AutomationModeDialog): void {
this._opened = true;
this._params = params;
this._newMode = params.config.mode || AUTOMATION_DEFAULT_MODE;
this._newMax = isMaxMode(this._newMode)
? params.config.max || AUTOMATION_DEFAULT_MAX
: undefined;
}
public closeDialog(): void {
this._params.onClose();
if (this._opened) {
fireEvent(this, "dialog-closed", { dialog: this.localName });
}
this._opened = false;
}
protected render(): TemplateResult {
if (!this._opened) {
return html``;
}
return html`
<ha-dialog
open
scrimClickAction
@closed=${this.closeDialog}
.heading=${createCloseHeading(
this.hass,
this.hass.localize("ui.panel.config.automation.editor.change_mode")
)}
>
<ha-select
.label=${this.hass.localize(
"ui.panel.config.automation.editor.modes.label"
)}
.value=${this._newMode}
@selected=${this._modeChanged}
@closed=${stopPropagation}
fixedMenuPosition
.helper=${html`
<a
style="color: var(--secondary-text-color)"
href=${documentationUrl(this.hass, "/docs/automation/modes/")}
target="_blank"
rel="noreferrer"
>${this.hass.localize(
"ui.panel.config.automation.editor.modes.learn_more"
)}</a
>
`}
>
${MODES.map(
(mode) => html`
<mwc-list-item .value=${mode}>
${this.hass.localize(
`ui.panel.config.automation.editor.modes.${mode}`
) || mode}
</mwc-list-item>
`
)}
</ha-select>
${isMaxMode(this._newMode)
? html`
<br /><ha-textfield
.label=${this.hass.localize(
`ui.panel.config.automation.editor.max.${this._newMode}`
)}
type="number"
name="max"
.value=${this._newMax?.toString() ?? ""}
@change=${this._valueChanged}
class="max"
>
</ha-textfield>
`
: html``}
<mwc-button @click=${this.closeDialog} slot="secondaryAction">
${this.hass.localize("ui.dialogs.generic.cancel")}
</mwc-button>
<mwc-button @click=${this._save} slot="primaryAction">
${this.hass.localize("ui.panel.config.automation.editor.change_mode")}
</mwc-button>
</ha-dialog>
`;
}
private _modeChanged(ev) {
const mode = ev.target.value;
this._newMode = mode;
if (!isMaxMode(mode)) {
this._newMax = undefined;
} else if (!this._newMax) {
this._newMax = AUTOMATION_DEFAULT_MAX;
}
}
private _valueChanged(ev: CustomEvent) {
ev.stopPropagation();
const target = ev.target as any;
if (target.name === "max") {
this._newMax = Number(target.value);
}
}
private _save(): void {
this._params.updateAutomation({
...this._params.config,
mode: this._newMode,
max: this._newMax,
});
this.closeDialog();
}
static get styles(): CSSResultGroup {
return [
haStyle,
haStyleDialog,
css`
ha-select,
ha-textfield {
display: block;
}
`,
];
}
}
declare global {
interface HTMLElementTagNameMap {
"ha-dialog-automation-mode": DialogAutomationMode;
}
}

View File

@@ -1,22 +0,0 @@
import { fireEvent } from "../../../../common/dom/fire_event";
import type { AutomationConfig } from "../../../../data/automation";
export const loadAutomationModeDialog = () =>
import("./dialog-automation-mode");
export interface AutomationModeDialog {
config: AutomationConfig;
updateAutomation: (config: AutomationConfig) => void;
onClose: () => void;
}
export const showAutomationModeDialog = (
element: HTMLElement,
dialogParams: AutomationModeDialog
): void => {
fireEvent(element, "show-dialog", {
dialogTag: "ha-dialog-automation-mode",
dialogImport: loadAutomationModeDialog,
dialogParams,
});
};

View File

@@ -1,157 +0,0 @@
import "@material/mwc-button";
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
import { customElement, property, state } from "lit/decorators";
import { fireEvent } from "../../../../common/dom/fire_event";
import { createCloseHeading } from "../../../../components/ha-dialog";
import { HassDialog } from "../../../../dialogs/make-dialog-manager";
import { haStyle, haStyleDialog } from "../../../../resources/styles";
import type { HomeAssistant } from "../../../../types";
import type { AutomationRenameDialog } from "./show-dialog-automation-rename";
import "../../../../components/ha-textarea";
import "../../../../components/ha-alert";
import "../../../../components/ha-textfield";
@customElement("ha-dialog-automation-rename")
class DialogAutomationRename extends LitElement implements HassDialog {
@property({ attribute: false }) public hass!: HomeAssistant;
@state() private _opened = false;
@state() private _error?: string;
private _params!: AutomationRenameDialog;
private _newName?: string;
private _newDescription?: string;
public showDialog(params: AutomationRenameDialog): void {
this._opened = true;
this._params = params;
this._newName =
params.config.alias ||
this.hass.localize("ui.panel.config.automation.editor.default_name");
this._newDescription = params.config.description || "";
}
public closeDialog(): void {
this._params.onClose();
if (this._opened) {
fireEvent(this, "dialog-closed", { dialog: this.localName });
}
this._opened = false;
}
protected render(): TemplateResult {
if (!this._opened) {
return html``;
}
return html`
<ha-dialog
open
scrimClickAction
@closed=${this.closeDialog}
.heading=${createCloseHeading(
this.hass,
this.hass.localize(
this._params.config.alias
? "ui.panel.config.automation.editor.rename"
: "ui.panel.config.automation.editor.save"
)
)}
>
${this._error
? html`<ha-alert alert-type="error"
>${this.hass.localize(
"ui.panel.config.automation.editor.missing_name"
)}</ha-alert
>`
: ""}
<ha-textfield
dialogInitialFocus
.value=${this._newName}
.placeholder=${this.hass.localize(
"ui.panel.config.automation.editor.default_name"
)}
.label=${this.hass.localize(
"ui.panel.config.automation.editor.alias"
)}
required
type="string"
@input=${this._valueChanged}
></ha-textfield>
<ha-textarea
.label=${this.hass.localize(
"ui.panel.config.automation.editor.description.label"
)}
.placeholder=${this.hass.localize(
"ui.panel.config.automation.editor.description.placeholder"
)}
name="description"
autogrow
.value=${this._newDescription}
@input=${this._valueChanged}
></ha-textarea>
<mwc-button @click=${this.closeDialog} slot="secondaryAction">
${this.hass.localize("ui.dialogs.generic.cancel")}
</mwc-button>
<mwc-button @click=${this._save} slot="primaryAction">
${this.hass.localize(
this._params.config.alias
? "ui.panel.config.automation.editor.rename"
: "ui.panel.config.automation.editor.save"
)}
</mwc-button>
</ha-dialog>
`;
}
private _valueChanged(ev: CustomEvent) {
ev.stopPropagation();
const target = ev.target as any;
if (target.name === "description") {
this._newDescription = target.value;
} else {
this._newName = target.value;
}
}
private _save(): void {
if (!this._newName) {
this._error = "Name is required";
return;
}
this._params.updateAutomation({
...this._params.config,
alias: this._newName,
description: this._newDescription,
});
this.closeDialog();
}
static get styles(): CSSResultGroup {
return [
haStyle,
haStyleDialog,
css`
ha-textfield,
ha-textarea {
display: block;
}
ha-alert {
display: block;
margin-bottom: 16px;
}
`,
];
}
}
declare global {
interface HTMLElementTagNameMap {
"ha-dialog-automation-rename": DialogAutomationRename;
}
}

View File

@@ -1,22 +0,0 @@
import { fireEvent } from "../../../../common/dom/fire_event";
import type { AutomationConfig } from "../../../../data/automation";
export const loadAutomationRenameDialog = () =>
import("./dialog-automation-rename");
export interface AutomationRenameDialog {
config: AutomationConfig;
updateAutomation: (config: AutomationConfig) => void;
onClose: () => void;
}
export const showAutomationRenameDialog = (
element: HTMLElement,
dialogParams: AutomationRenameDialog
): void => {
fireEvent(element, "show-dialog", {
dialogTag: "ha-dialog-automation-rename",
dialogImport: loadAutomationRenameDialog,
dialogParams,
});
};

View File

@@ -1,8 +1,9 @@
import "@material/mwc-button/mwc-button";
import { HassEntity } from "home-assistant-js-websocket";
import { css, CSSResultGroup, html, LitElement } from "lit";
import { css, CSSResultGroup, html, LitElement, PropertyValues } from "lit";
import { customElement, property, state } from "lit/decorators";
import { fireEvent } from "../../../common/dom/fire_event";
import "../../../components/entity/ha-entity-toggle";
import "../../../components/ha-blueprint-picker";
import "../../../components/ha-card";
import "../../../components/ha-circular-progress";
@@ -10,8 +11,10 @@ import "../../../components/ha-markdown";
import "../../../components/ha-selector/ha-selector";
import "../../../components/ha-settings-row";
import "../../../components/ha-textfield";
import "../../../components/ha-alert";
import { BlueprintAutomationConfig } from "../../../data/automation";
import {
BlueprintAutomationConfig,
triggerAutomationActions,
} from "../../../data/automation";
import {
BlueprintOrError,
Blueprints,
@@ -35,6 +38,8 @@ export class HaBlueprintAutomationEditor extends LitElement {
@state() private _blueprints?: Blueprints;
@state() private _showDescription = false;
protected firstUpdated(changedProps) {
super.firstUpdated(changedProps);
this._getBlueprints();
@@ -47,26 +52,89 @@ export class HaBlueprintAutomationEditor extends LitElement {
return this._blueprints[this.config.use_blueprint.path];
}
protected willUpdate(changedProps: PropertyValues): void {
super.willUpdate(changedProps);
if (
!this._showDescription &&
changedProps.has("config") &&
this.config.description
) {
this._showDescription = true;
}
}
protected render() {
const blueprint = this._blueprint;
return html`
${this.stateObj?.state === "off"
? html`
<ha-alert alert-type="info">
${this.hass.localize(
"ui.panel.config.automation.editor.disabled"
)}
<mwc-button slot="action" @click=${this._enable}>
${this.hass.localize(
"ui.panel.config.automation.editor.enable"
)}
</mwc-button>
</ha-alert>
`
: ""}
${this.config.description
? html`<p class="description">${this.config.description}</p>`
: ""}
<ha-config-section vertical .isWide=${this.isWide}>
${!this.narrow
? html` <span slot="header">${this.config.alias}</span> `
: ""}
<span slot="introduction">
${this.hass.localize(
"ui.panel.config.automation.editor.introduction"
)}
</span>
<ha-card outlined>
<div class="card-content">
${this._showDescription
? html`
<ha-textarea
.label=${this.hass.localize(
"ui.panel.config.automation.editor.description.label"
)}
.placeholder=${this.hass.localize(
"ui.panel.config.automation.editor.description.placeholder"
)}
name="description"
autogrow
.value=${this.config.description || ""}
@change=${this._valueChanged}
></ha-textarea>
`
: html`
<div class="link-button-row">
<button class="link" @click=${this._addDescription}>
${this.hass.localize(
"ui.panel.config.automation.editor.description.add"
)}
</button>
</div>
`}
</div>
${this.stateObj
? html`
<div class="card-actions layout horizontal justified center">
<div class="layout horizontal center">
<ha-entity-toggle
.hass=${this.hass}
.stateObj=${this.stateObj!}
></ha-entity-toggle>
${this.hass.localize(
"ui.panel.config.automation.editor.enable_disable"
)}
</div>
<div>
<a href="/config/automation/trace/${this.config.id}">
<mwc-button>
${this.hass.localize(
"ui.panel.config.automation.editor.show_trace"
)}
</mwc-button>
</a>
<mwc-button
@click=${this._runActions}
.stateObj=${this.stateObj}
>
${this.hass.localize("ui.card.automation.trigger")}
</mwc-button>
</div>
</div>
`
: ""}
</ha-card>
</ha-config-section>
<ha-card
outlined
class="blueprint"
@@ -152,6 +220,10 @@ export class HaBlueprintAutomationEditor extends LitElement {
this._blueprints = await fetchBlueprints(this.hass, "automation");
}
private _runActions(ev: Event) {
triggerAutomationActions(this.hass, (ev.target as any).stateObj.entity_id);
}
private _blueprintChanged(ev) {
ev.stopPropagation();
if (this.config.use_blueprint.path === ev.detail.value) {
@@ -196,24 +268,33 @@ export class HaBlueprintAutomationEditor extends LitElement {
});
}
private async _enable(): Promise<void> {
if (!this.hass || !this.stateObj) {
private _valueChanged(ev: CustomEvent) {
ev.stopPropagation();
const target = ev.target as any;
const name = target.name;
if (!name) {
return;
}
await this.hass.callService("automation", "turn_on", {
entity_id: this.stateObj.entity_id,
const newVal = target.value;
if ((this.config![name] || "") === newVal) {
return;
}
fireEvent(this, "value-changed", {
value: { ...this.config!, [name]: newVal },
});
}
private _addDescription() {
this._showDescription = true;
}
static get styles(): CSSResultGroup {
return [
haStyle,
css`
:host {
display: block;
}
ha-card.blueprint {
margin: 0 auto;
max-width: 1040px;
margin: 24px auto;
}
.padding {
padding: 16px;
@@ -224,6 +305,7 @@ export class HaBlueprintAutomationEditor extends LitElement {
.blueprint-picker-container {
padding: 0 16px 16px;
}
ha-textarea,
ha-textfield,
ha-blueprint-picker {
display: block;
@@ -231,18 +313,14 @@ export class HaBlueprintAutomationEditor extends LitElement {
h3 {
margin: 16px;
}
.introduction {
margin-top: 0;
margin-bottom: 12px;
}
.introduction a {
span[slot="introduction"] a {
color: var(--primary-color);
}
p {
margin-bottom: 0;
}
.description {
margin-bottom: 16px;
ha-entity-toggle {
margin-right: 8px;
}
ha-settings-row {
--paper-time-input-justify-content: flex-end;
@@ -250,10 +328,6 @@ export class HaBlueprintAutomationEditor extends LitElement {
--settings-row-prefix-display: contents;
border-top: 1px solid var(--divider-color);
}
ha-alert {
margin-bottom: 16px;
display: block;
}
`,
];
}

View File

@@ -28,8 +28,6 @@ export default class HaAutomationConditionEditor extends LitElement {
@property({ type: Boolean }) public yamlMode = false;
@property({ type: Boolean }) public reOrderMode = false;
private _processedCondition = memoizeOne((condition) =>
expandConditionWithShorthand(condition)
);
@@ -62,11 +60,7 @@ export default class HaAutomationConditionEditor extends LitElement {
<div>
${dynamicElement(
`ha-automation-condition-${condition.condition}`,
{
hass: this.hass,
condition: condition,
reOrderMode: this.reOrderMode,
}
{ hass: this.hass, condition: condition }
)}
</div>
`}

View File

@@ -70,10 +70,6 @@ export default class HaAutomationConditionRow extends LitElement {
@property() public condition!: Condition;
@property({ type: Boolean }) public hideMenu = false;
@property({ type: Boolean }) public reOrderMode = false;
@state() private _yamlMode = false;
@state() private _warnings?: string[];
@@ -97,7 +93,7 @@ export default class HaAutomationConditionRow extends LitElement {
: ""}
<ha-expansion-panel leftChevron>
<h3 slot="header">
<div slot="header">
<ha-svg-icon
class="condition-icon"
.path=${CONDITION_TYPES[this.condition.condition]}
@@ -105,108 +101,96 @@ export default class HaAutomationConditionRow extends LitElement {
${capitalizeFirstLetter(
describeCondition(this.condition, this.hass)
)}
</h3>
</div>
<slot name="icons" slot="icons"></slot>
${this.hideMenu
? ""
: html`
<ha-button-menu
slot="icons"
fixed
corner="BOTTOM_START"
@action=${this._handleAction}
@click=${preventDefault}
>
<ha-icon-button
slot="trigger"
.label=${this.hass.localize("ui.common.menu")}
.path=${mdiDotsVertical}
>
</ha-icon-button>
<ha-button-menu
slot="icons"
fixed
corner="BOTTOM_START"
@action=${this._handleAction}
@click=${preventDefault}
>
<ha-icon-button
slot="trigger"
.label=${this.hass.localize("ui.common.menu")}
.path=${mdiDotsVertical}
>
</ha-icon-button>
<mwc-list-item graphic="icon">
${this.hass.localize(
"ui.panel.config.automation.editor.conditions.test"
)}
<ha-svg-icon slot="graphic" .path=${mdiFlask}></ha-svg-icon>
</mwc-list-item>
<mwc-list-item graphic="icon">
${this.hass.localize(
"ui.panel.config.automation.editor.conditions.rename"
)}
<ha-svg-icon
slot="graphic"
.path=${mdiRenameBox}
></ha-svg-icon>
</mwc-list-item>
<mwc-list-item graphic="icon">
${this.hass.localize(
"ui.panel.config.automation.editor.actions.duplicate"
)}
<ha-svg-icon
slot="graphic"
.path=${mdiContentDuplicate}
></ha-svg-icon>
</mwc-list-item>
<mwc-list-item graphic="icon">
${this.hass.localize(
"ui.panel.config.automation.editor.conditions.test"
)}
<ha-svg-icon slot="graphic" .path=${mdiFlask}></ha-svg-icon>
</mwc-list-item>
<mwc-list-item graphic="icon">
${this.hass.localize(
"ui.panel.config.automation.editor.conditions.rename"
)}
<ha-svg-icon slot="graphic" .path=${mdiRenameBox}></ha-svg-icon>
</mwc-list-item>
<mwc-list-item graphic="icon">
${this.hass.localize(
"ui.panel.config.automation.editor.actions.duplicate"
)}
<ha-svg-icon
slot="graphic"
.path=${mdiContentDuplicate}
></ha-svg-icon>
</mwc-list-item>
<li divider role="separator"></li>
<li divider role="separator"></li>
<mwc-list-item graphic="icon">
${this.hass.localize(
"ui.panel.config.automation.editor.edit_ui"
)}
${!this._yamlMode
? html`<ha-svg-icon
class="selected_menu_item"
slot="graphic"
.path=${mdiCheck}
></ha-svg-icon>`
: ``}
</mwc-list-item>
<mwc-list-item graphic="icon">
${this.hass.localize("ui.panel.config.automation.editor.edit_ui")}
${!this._yamlMode
? html`<ha-svg-icon
slot="graphic"
.path=${mdiCheck}
></ha-svg-icon>`
: ``}
</mwc-list-item>
<mwc-list-item graphic="icon">
${this.hass.localize(
"ui.panel.config.automation.editor.edit_yaml"
)}
${this._yamlMode
? html`<ha-svg-icon
class="selected_menu_item"
slot="graphic"
.path=${mdiCheck}
></ha-svg-icon>`
: ``}
</mwc-list-item>
<mwc-list-item graphic="icon">
${this.hass.localize(
"ui.panel.config.automation.editor.edit_yaml"
)}
${this._yamlMode
? html`<ha-svg-icon
slot="graphic"
.path=${mdiCheck}
></ha-svg-icon>`
: ``}
</mwc-list-item>
<li divider role="separator"></li>
<li divider role="separator"></li>
<mwc-list-item graphic="icon">
${this.condition.enabled === false
? this.hass.localize(
"ui.panel.config.automation.editor.actions.enable"
)
: this.hass.localize(
"ui.panel.config.automation.editor.actions.disable"
)}
<ha-svg-icon
slot="graphic"
.path=${this.condition.enabled === false
? mdiPlayCircleOutline
: mdiStopCircleOutline}
></ha-svg-icon>
</mwc-list-item>
<mwc-list-item class="warning" graphic="icon">
${this.hass.localize(
"ui.panel.config.automation.editor.actions.delete"
)}
<ha-svg-icon
class="warning"
slot="graphic"
.path=${mdiDelete}
></ha-svg-icon>
</mwc-list-item>
</ha-button-menu>
`}
<mwc-list-item graphic="icon">
${this.condition.enabled === false
? this.hass.localize(
"ui.panel.config.automation.editor.actions.enable"
)
: this.hass.localize(
"ui.panel.config.automation.editor.actions.disable"
)}
<ha-svg-icon
slot="graphic"
.path=${this.condition.enabled === false
? mdiPlayCircleOutline
: mdiStopCircleOutline}
></ha-svg-icon>
</mwc-list-item>
<mwc-list-item class="warning" graphic="icon">
${this.hass.localize(
"ui.panel.config.automation.editor.actions.delete"
)}
<ha-svg-icon
class="warning"
slot="graphic"
.path=${mdiDelete}
></ha-svg-icon>
</mwc-list-item>
</ha-button-menu>
<div
class=${classMap({
@@ -240,7 +224,6 @@ export default class HaAutomationConditionRow extends LitElement {
.yamlMode=${this._yamlMode}
.hass=${this.hass}
.condition=${this.condition}
.reOrderMode=${this.reOrderMode}
></ha-automation-condition-editor>
</div>
</ha-expansion-panel>
@@ -440,18 +423,13 @@ export default class HaAutomationConditionRow extends LitElement {
--expansion-panel-summary-padding: 0 0 0 8px;
--expansion-panel-content-padding: 0;
}
h3 {
margin: 0;
font-size: inherit;
font-weight: inherit;
}
.condition-icon {
display: none;
}
@media (min-width: 870px) {
.condition-icon {
display: inline-block;
color: var(--secondary-text-color);
color: var(--primary-color);
opacity: 0.9;
margin-right: 8px;
}
@@ -494,12 +472,6 @@ export default class HaAutomationConditionRow extends LitElement {
.testing.pass {
background-color: var(--success-color);
}
.selected_menu_item {
color: var(--primary-color);
}
li[role="separator"] {
border-bottom-color: var(--divider-color);
}
`,
];
}

View File

@@ -1,15 +1,14 @@
import "@material/mwc-button";
import type { ActionDetail } from "@material/mwc-list";
import { mdiArrowDown, mdiArrowUp, mdiDrag, mdiPlus } from "@mdi/js";
import { mdiPlus } from "@mdi/js";
import { repeat } from "lit/directives/repeat";
import deepClone from "deep-clone-simple";
import "@material/mwc-button";
import { css, CSSResultGroup, html, LitElement, PropertyValues } from "lit";
import { customElement, property } from "lit/decorators";
import { repeat } from "lit/directives/repeat";
import memoizeOne from "memoize-one";
import type { SortableEvent } from "sortablejs";
import type { ActionDetail } from "@material/mwc-list";
import { fireEvent } from "../../../../common/dom/fire_event";
import "../../../../components/ha-button-menu";
import "../../../../components/ha-svg-icon";
import "../../../../components/ha-button-menu";
import type { Condition } from "../../../../data/automation";
import type { HomeAssistant } from "../../../../types";
import "./ha-automation-condition-row";
@@ -17,14 +16,6 @@ import type HaAutomationConditionRow from "./ha-automation-condition-row";
// Uncommenting these and this element doesn't load
// import "./types/ha-automation-condition-not";
// import "./types/ha-automation-condition-or";
import { stringCompare } from "../../../../common/string/compare";
import type { LocalizeFunc } from "../../../../common/translations/localize";
import type { HaSelect } from "../../../../components/ha-select";
import { CONDITION_TYPES } from "../../../../data/condition";
import {
loadSortable,
SortableInstance,
} from "../../../../resources/sortable.ondemand";
import "./types/ha-automation-condition-and";
import "./types/ha-automation-condition-device";
import "./types/ha-automation-condition-numeric_state";
@@ -34,7 +25,10 @@ import "./types/ha-automation-condition-template";
import "./types/ha-automation-condition-time";
import "./types/ha-automation-condition-trigger";
import "./types/ha-automation-condition-zone";
import { sortableStyles } from "../../../../resources/ha-sortable-style";
import { CONDITION_TYPES } from "../../../../data/condition";
import { stringCompare } from "../../../../common/string/compare";
import type { LocalizeFunc } from "../../../../common/translations/localize";
import type { HaSelect } from "../../../../components/ha-select";
@customElement("ha-automation-condition")
export default class HaAutomationCondition extends LitElement {
@@ -42,23 +36,11 @@ export default class HaAutomationCondition extends LitElement {
@property() public conditions!: Condition[];
@property({ type: Boolean }) public reOrderMode = false;
private _focusLastConditionOnChange = false;
private _conditionKeys = new WeakMap<Condition, string>();
private _sortable?: SortableInstance;
protected updated(changedProperties: PropertyValues) {
if (changedProperties.has("reOrderMode")) {
if (this.reOrderMode) {
this._createSortable();
} else {
this._destroySortable();
}
}
if (!changedProperties.has("conditions")) {
return;
}
@@ -100,53 +82,19 @@ export default class HaAutomationCondition extends LitElement {
return html``;
}
return html`
<div class="conditions">
${repeat(
this.conditions,
(condition) => this._getKey(condition),
(cond, idx) => html`
<ha-automation-condition-row
.index=${idx}
.totalConditions=${this.conditions.length}
.condition=${cond}
.hideMenu=${this.reOrderMode}
.reOrderMode=${this.reOrderMode}
@duplicate=${this._duplicateCondition}
@move-condition=${this._move}
@value-changed=${this._conditionChanged}
.hass=${this.hass}
>
${this.reOrderMode
? html`
<ha-icon-button
.index=${idx}
slot="icons"
.label=${this.hass.localize(
"ui.panel.config.automation.editor.move_up"
)}
.path=${mdiArrowUp}
@click=${this._moveUp}
.disabled=${idx === 0}
></ha-icon-button>
<ha-icon-button
.index=${idx}
slot="icons"
.label=${this.hass.localize(
"ui.panel.config.automation.editor.move_down"
)}
.path=${mdiArrowDown}
@click=${this._moveDown}
.disabled=${idx === this.conditions.length - 1}
></ha-icon-button>
<div class="handle" slot="icons">
<ha-svg-icon .path=${mdiDrag}></ha-svg-icon>
</div>
`
: ""}
</ha-automation-condition-row>
`
)}
</div>
${repeat(
this.conditions,
(condition) => this._getKey(condition),
(cond, idx) => html`
<ha-automation-condition-row
.index=${idx}
.condition=${cond}
@duplicate=${this._duplicateCondition}
@value-changed=${this._conditionChanged}
.hass=${this.hass}
></ha-automation-condition-row>
`
)}
<ha-button-menu fixed @action=${this._addCondition}>
<mwc-button
slot="trigger"
@@ -168,36 +116,6 @@ export default class HaAutomationCondition extends LitElement {
`;
}
private async _createSortable() {
const Sortable = await loadSortable();
this._sortable = new Sortable(
this.shadowRoot!.querySelector(".conditions")!,
{
animation: 150,
fallbackClass: "sortable-fallback",
handle: ".handle",
onChoose: (evt: SortableEvent) => {
(evt.item as any).placeholder =
document.createComment("sort-placeholder");
evt.item.after((evt.item as any).placeholder);
},
onEnd: (evt: SortableEvent) => {
// put back in original location
if ((evt.item as any).placeholder) {
(evt.item as any).placeholder.replaceWith(evt.item);
delete (evt.item as any).placeholder;
}
this._dragged(evt);
},
}
);
}
private _destroySortable() {
this._sortable?.destroy();
this._sortable = undefined;
}
private _getKey(condition: Condition) {
if (!this._conditionKeys.has(condition)) {
this._conditionKeys.set(condition, Math.random().toString());
@@ -224,30 +142,6 @@ export default class HaAutomationCondition extends LitElement {
fireEvent(this, "value-changed", { value: conditions });
}
private _moveUp(ev) {
const index = (ev.target as any).index;
const newIndex = index - 1;
this._move(index, newIndex);
}
private _moveDown(ev) {
const index = (ev.target as any).index;
const newIndex = index + 1;
this._move(index, newIndex);
}
private _dragged(ev: SortableEvent): void {
if (ev.oldIndex === ev.newIndex) return;
this._move(ev.oldIndex!, ev.newIndex!);
}
private _move(index: number, newIndex: number) {
const conditions = this.conditions.concat();
const condition = conditions.splice(index, 1)[0];
conditions.splice(newIndex, 0, condition);
fireEvent(this, "value-changed", { value: conditions });
}
private _conditionChanged(ev: CustomEvent) {
ev.stopPropagation();
const conditions = [...this.conditions];
@@ -292,27 +186,16 @@ export default class HaAutomationCondition extends LitElement {
);
static get styles(): CSSResultGroup {
return [
sortableStyles,
css`
ha-automation-condition-row {
display: block;
margin-bottom: 16px;
scroll-margin-top: 48px;
}
ha-svg-icon {
height: 20px;
}
.handle {
cursor: move;
padding: 12px;
}
.handle ha-svg-icon {
pointer-events: none;
height: 24px;
}
`,
];
return css`
ha-automation-condition-row {
display: block;
margin-bottom: 16px;
scroll-margin-top: 48px;
}
ha-svg-icon {
height: 20px;
}
`;
}
}

View File

@@ -12,8 +12,6 @@ export class HaLogicalCondition extends LitElement implements ConditionElement {
@property({ attribute: false }) public condition!: LogicalCondition;
@property({ type: Boolean }) public reOrderMode = false;
public static get defaultConfig() {
return {
conditions: [],
@@ -26,7 +24,6 @@ export class HaLogicalCondition extends LitElement implements ConditionElement {
.conditions=${this.condition.conditions || []}
@value-changed=${this._valueChanged}
.hass=${this.hass}
.reOrderMode=${this.reOrderMode}
></ha-automation-condition>
`;
}

View File

@@ -1,17 +1,14 @@
import "@material/mwc-button";
import "@material/mwc-list/mwc-list-item";
import {
mdiCheck,
mdiContentDuplicate,
mdiContentSave,
mdiDebugStepOver,
mdiDelete,
mdiDotsVertical,
mdiInformationOutline,
mdiPencil,
mdiPlay,
mdiPlayCircleOutline,
mdiRenameBox,
mdiSort,
mdiStopCircleOutline,
mdiTransitConnection,
} from "@mdi/js";
@@ -26,9 +23,8 @@ import {
PropertyValues,
TemplateResult,
} from "lit";
import { property, query, state } from "lit/decorators";
import { property, state, query } from "lit/decorators";
import { classMap } from "lit/directives/class-map";
import { fireEvent } from "../../../common/dom/fire_event";
import { navigate } from "../../../common/navigate";
import { copyToClipboard } from "../../../common/util/copy-clipboard";
import "../../../components/ha-button-menu";
@@ -44,26 +40,24 @@ import {
deleteAutomation,
getAutomationConfig,
getAutomationEditorInitData,
saveAutomationConfig,
showAutomationEditor,
triggerAutomationActions,
} from "../../../data/automation";
import {
showAlertDialog,
showConfirmationDialog,
showPromptDialog,
} from "../../../dialogs/generic/show-dialog-box";
import "../../../layouts/ha-app-layout";
import "../../../layouts/hass-subpage";
import "../../../layouts/hass-tabs-subpage";
import { KeyboardShortcutMixin } from "../../../mixins/keyboard-shortcut-mixin";
import { haStyle } from "../../../resources/styles";
import { HomeAssistant, Route } from "../../../types";
import { showToast } from "../../../util/toast";
import "../ha-config-section";
import { showAutomationModeDialog } from "./automation-mode-dialog/show-dialog-automation-mode";
import { showAutomationRenameDialog } from "./automation-rename-dialog/show-dialog-automation-rename";
import { configSections } from "../ha-panel-config";
import "./blueprint-automation-editor";
import "./manual-automation-editor";
import type { HaManualAutomationEditor } from "./manual-automation-editor";
declare global {
interface HTMLElementTagNameMap {
@@ -103,10 +97,7 @@ export class HaAutomationEditor extends KeyboardShortcutMixin(LitElement) {
@state() private _mode: "gui" | "yaml" = "gui";
@query("ha-yaml-editor", true) private _yamlEditor?: HaYamlEditor;
@query("manual-automation-editor")
private _manualEditor?: HaManualAutomationEditor;
@query("ha-yaml-editor", true) private _editor?: HaYamlEditor;
private _configSubscriptions: Record<
string,
@@ -120,27 +111,13 @@ export class HaAutomationEditor extends KeyboardShortcutMixin(LitElement) {
? this.hass.states[this._entityId]
: undefined;
return html`
<hass-subpage
<hass-tabs-subpage
.hass=${this.hass}
.narrow=${this.narrow}
.route=${this.route}
.backCallback=${this._backTapped}
.header=${!this._config
? ""
: this._config.alias ||
this.hass.localize(
"ui.panel.config.automation.editor.default_name"
)}
.tabs=${configSections.automations}
>
${this._config?.id && !this.narrow
? html`
<mwc-button @click=${this._showTrace} slot="toolbar-icon">
${this.hass.localize(
"ui.panel.config.automation.editor.show_trace"
)}
</mwc-button>
`
: ""}
<ha-button-menu corner="BOTTOM_START" slot="toolbar-icon">
<ha-icon-button
slot="trigger"
@@ -148,18 +125,6 @@ export class HaAutomationEditor extends KeyboardShortcutMixin(LitElement) {
.path=${mdiDotsVertical}
></ha-icon-button>
<mwc-list-item
graphic="icon"
.disabled=${!stateObj}
@click=${this._showInfo}
>
${this.hass.localize("ui.panel.config.automation.editor.show_info")}
<ha-svg-icon
slot="graphic"
.path=${mdiInformationOutline}
></ha-svg-icon>
</mwc-list-item>
<mwc-list-item
graphic="icon"
.disabled=${!stateObj}
@@ -169,9 +134,15 @@ export class HaAutomationEditor extends KeyboardShortcutMixin(LitElement) {
<ha-svg-icon slot="graphic" .path=${mdiPlay}></ha-svg-icon>
</mwc-list-item>
${stateObj && this._config && this.narrow
? html`<a href="/config/automation/trace/${this._config.id}">
<mwc-list-item graphic="icon">
${stateObj
? html`<a
href="/config/automation/trace/${this._config
? this._config.id
: ""}"
target="_blank"
.disabled=${!stateObj}
>
<mwc-list-item graphic="icon" .disabled=${!stateObj}>
${this.hass.localize(
"ui.panel.config.automation.editor.show_trace"
)}
@@ -183,45 +154,11 @@ export class HaAutomationEditor extends KeyboardShortcutMixin(LitElement) {
</a>`
: ""}
<mwc-list-item
graphic="icon"
@click=${this._promptAutomationAlias}
.disabled=${!this.automationId || this._mode === "yaml"}
>
<mwc-list-item graphic="icon" @click=${this._promptAutomationAlias}>
${this.hass.localize("ui.panel.config.automation.editor.rename")}
<ha-svg-icon slot="graphic" .path=${mdiRenameBox}></ha-svg-icon>
</mwc-list-item>
${this._config && !("use_blueprint" in this._config)
? html`
<mwc-list-item
graphic="icon"
@click=${this._promptAutomationMode}
.disabled=${this._mode === "yaml"}
>
${this.hass.localize(
"ui.panel.config.automation.editor.change_mode"
)}
<ha-svg-icon
slot="graphic"
.path=${mdiDebugStepOver}
></ha-svg-icon>
</mwc-list-item>
`
: ""}
${this._config && !("use_blueprint" in this._config)
? html`<mwc-list-item
graphic="icon"
@click=${this._toggleReOrderMode}
.disabled=${this._mode === "yaml"}
>
${this.hass.localize(
"ui.panel.config.automation.editor.re_order"
)}
<ha-svg-icon slot="graphic" .path=${mdiSort}></ha-svg-icon>
</mwc-list-item>`
: ""}
<mwc-list-item
.disabled=${!this.automationId}
graphic="icon"
@@ -264,12 +201,12 @@ export class HaAutomationEditor extends KeyboardShortcutMixin(LitElement) {
.disabled=${!stateObj}
@click=${this._toggle}
>
${stateObj?.state === "off"
${!stateObj || stateObj.state === "off"
? this.hass.localize("ui.panel.config.automation.editor.enable")
: this.hass.localize("ui.panel.config.automation.editor.disable")}
<ha-svg-icon
slot="graphic"
.path=${stateObj?.state === "off"
.path=${!stateObj || stateObj.state === "off"
? mdiPlayCircleOutline
: mdiStopCircleOutline}
></ha-svg-icon>
@@ -300,48 +237,70 @@ export class HaAutomationEditor extends KeyboardShortcutMixin(LitElement) {
@subscribe-automation-config=${this._subscribeAutomationConfig}
>
${this._errors
? html`<ha-alert alert-type="error">
${this._errors}
</ha-alert>`
? html`<div class="errors">${this._errors}</div>`
: ""}
${this._mode === "gui"
? "use_blueprint" in this._config
? html`
<blueprint-automation-editor
.hass=${this.hass}
.narrow=${this.narrow}
.isWide=${this.isWide}
.stateObj=${stateObj}
.config=${this._config}
@value-changed=${this._valueChanged}
></blueprint-automation-editor>
`
: html`
<manual-automation-editor
.hass=${this.hass}
.narrow=${this.narrow}
.isWide=${this.isWide}
.stateObj=${stateObj}
.config=${this._config}
@value-changed=${this._valueChanged}
></manual-automation-editor>
`
? html`
${this.narrow
? html`<span slot="header"
>${this._config!.alias ||
this.hass.localize(
"ui.panel.config.automation.editor.default_name"
)}</span
>`
: html`
<div class="header-name">
<h1>
${this._config!.alias ||
this.hass.localize(
"ui.panel.config.automation.editor.default_name"
)}
</h1>
<ha-icon-button
.path=${mdiPencil}
@click=${this._promptAutomationAlias}
.label=${this.hass.localize(
"ui.panel.config.automation.editor.rename"
)}
></ha-icon-button>
</div>
`}
${"use_blueprint" in this._config
? html`
<blueprint-automation-editor
.hass=${this.hass}
.narrow=${this.narrow}
.isWide=${this.isWide}
.stateObj=${stateObj}
.config=${this._config}
@value-changed=${this._valueChanged}
></blueprint-automation-editor>
`
: html`
<manual-automation-editor
.hass=${this.hass}
.narrow=${this.narrow}
.isWide=${this.isWide}
.stateObj=${stateObj}
.config=${this._config}
@value-changed=${this._valueChanged}
></manual-automation-editor>
`}
`
: this._mode === "yaml"
? html`
${stateObj?.state === "off"
${!this.narrow
? html`
<ha-alert alert-type="info">
${this.hass.localize(
"ui.panel.config.automation.editor.disabled"
)}
<mwc-button slot="action" @click=${this._toggle}>
${this.hass.localize(
"ui.panel.config.automation.editor.enable"
<ha-card outlined>
<div class="card-header">
${this._config.alias ||
this.hass.localize(
"ui.panel.config.automation.editor.default_name"
)}
</mwc-button>
</ha-alert>
</div>
</ha-card>
`
: ""}
: ``}
<ha-yaml-editor
.hass=${this.hass}
.defaultValue=${this._preprocessYaml()}
@@ -370,7 +329,7 @@ export class HaAutomationEditor extends KeyboardShortcutMixin(LitElement) {
>
<ha-svg-icon slot="icon" .path=${mdiContentSave}></ha-svg-icon>
</ha-fab>
</hass-subpage>
</hass-tabs-subpage>
`;
}
@@ -449,7 +408,7 @@ export class HaAutomationEditor extends KeyboardShortcutMixin(LitElement) {
this._dirty = false;
this._config = config;
} catch (err: any) {
await showAlertDialog(this, {
showAlertDialog(this, {
text:
err.status_code === 404
? this.hass.localize(
@@ -460,8 +419,7 @@ export class HaAutomationEditor extends KeyboardShortcutMixin(LitElement) {
"err_no",
err.status_code
),
});
history.back();
}).then(() => history.back());
}
}
@@ -472,22 +430,6 @@ export class HaAutomationEditor extends KeyboardShortcutMixin(LitElement) {
this._errors = undefined;
}
private _showInfo() {
if (!this.hass || !this._entityId) {
return;
}
fireEvent(this, "hass-more-info", { entityId: this._entityId });
}
private async _showTrace() {
if (this._config?.id) {
const result = await this.confirmUnsavedChanged();
if (result) {
navigate(`/config/automation/trace/${this._config.id}`);
}
}
}
private _runActions() {
if (!this.hass || !this._entityId) {
return;
@@ -510,17 +452,19 @@ export class HaAutomationEditor extends KeyboardShortcutMixin(LitElement) {
}
private _preprocessYaml() {
if (!this._config) {
const cleanConfig = this._config;
if (!cleanConfig) {
return {};
}
const cleanConfig: AutomationConfig = { ...this._config };
delete cleanConfig.id;
return cleanConfig;
}
private async _copyYaml(): Promise<void> {
if (this._yamlEditor?.yaml) {
await copyToClipboard(this._yamlEditor.yaml);
if (this._editor?.yaml) {
await copyToClipboard(this._editor.yaml);
showToast(this, {
message: this.hass.localize("ui.common.copied_clipboard"),
});
@@ -532,40 +476,49 @@ export class HaAutomationEditor extends KeyboardShortcutMixin(LitElement) {
if (!ev.detail.isValid) {
return;
}
this._config = { id: this._config?.id, ...ev.detail.value };
this._config = ev.detail.value;
this._errors = undefined;
this._dirty = true;
}
private async confirmUnsavedChanged(): Promise<boolean> {
private _backTapped = (): void => {
if (this._dirty) {
return showConfirmationDialog(this, {
showConfirmationDialog(this, {
text: this.hass!.localize(
"ui.panel.config.automation.editor.unsaved_confirm"
),
confirmText: this.hass!.localize("ui.common.leave"),
dismissText: this.hass!.localize("ui.common.stay"),
confirm: () => {
setTimeout(() => history.back());
},
});
}
return true;
}
private _backTapped = async () => {
const result = await this.confirmUnsavedChanged();
if (result) {
} else {
history.back();
}
};
private async _duplicate() {
const result = await this.confirmUnsavedChanged();
if (result) {
showAutomationEditor({
...this._config,
id: undefined,
alias: undefined,
});
if (this._dirty) {
if (
!(await showConfirmationDialog(this, {
text: this.hass!.localize(
"ui.panel.config.automation.editor.unsaved_confirm"
),
confirmText: this.hass!.localize("ui.common.leave"),
dismissText: this.hass!.localize("ui.common.stay"),
}))
) {
return;
}
// Wait for dialog to complete closing
await new Promise((resolve) => setTimeout(resolve, 0));
}
showAutomationEditor({
...this._config,
id: undefined,
alias: undefined,
});
}
private async _deleteConfirm() {
@@ -580,10 +533,8 @@ export class HaAutomationEditor extends KeyboardShortcutMixin(LitElement) {
}
private async _delete() {
if (this.automationId) {
await deleteAutomation(this.hass, this.automationId);
history.back();
}
await deleteAutomation(this.hass, this.automationId as string);
history.back();
}
private _switchUiMode() {
@@ -594,63 +545,62 @@ export class HaAutomationEditor extends KeyboardShortcutMixin(LitElement) {
this._mode = "yaml";
}
private _toggleReOrderMode() {
if (this._manualEditor) {
this._manualEditor.reOrderMode = !this._manualEditor.reOrderMode;
private async _promptAutomationAlias(): Promise<string | null> {
const result = await showPromptDialog(this, {
title: this.hass.localize(
"ui.panel.config.automation.editor.automation_alias"
),
inputLabel: this.hass.localize("ui.panel.config.automation.editor.alias"),
inputType: "string",
placeholder: this.hass.localize(
"ui.panel.config.automation.editor.default_name"
),
defaultValue: this._config!.alias,
confirmText: this.hass.localize("ui.common.submit"),
});
if (result) {
this._config!.alias = result;
this._dirty = true;
this.requestUpdate();
}
}
private async _promptAutomationAlias(): Promise<void> {
return new Promise((resolve) => {
showAutomationRenameDialog(this, {
config: this._config!,
updateAutomation: (config) => {
this._config = config;
this._dirty = true;
this.requestUpdate();
resolve();
},
onClose: () => resolve(),
});
});
}
private async _promptAutomationMode(): Promise<void> {
return new Promise((resolve) => {
showAutomationModeDialog(this, {
config: this._config!,
updateAutomation: (config) => {
this._config = config;
this._dirty = true;
this.requestUpdate();
resolve();
},
onClose: () => resolve(),
});
});
return result;
}
private async _saveAutomation(): Promise<void> {
const id = this.automationId || String(Date.now());
if (!this.automationId) {
await this._promptAutomationAlias();
if (!this._config!.alias) {
const alias = await this._promptAutomationAlias();
if (!alias) {
showAlertDialog(this, {
text: this.hass.localize(
"ui.panel.config.automation.editor.missing_name"
),
});
return;
}
this._config!.alias = alias;
}
try {
await saveAutomationConfig(this.hass, id, this._config!);
} catch (errors: any) {
this._errors = errors.body.message || errors.error || errors.body;
showToast(this, {
message: errors.body.message || errors.error || errors.body,
});
throw errors;
}
this.hass!.callApi(
"POST",
"config/automation/config/" + id,
this._config
).then(
() => {
this._dirty = false;
this._dirty = false;
if (!this.automationId) {
navigate(`/config/automation/edit/${id}`, { replace: true });
}
if (!this.automationId) {
navigate(`/config/automation/edit/${id}`, { replace: true });
}
},
(errors) => {
this._errors = errors.body.message || errors.error || errors.body;
showToast(this, {
message: errors.body.message || errors.error || errors.body,
});
throw errors;
}
);
}
private _subscribeAutomationConfig(ev) {
@@ -673,6 +623,11 @@ export class HaAutomationEditor extends KeyboardShortcutMixin(LitElement) {
ha-card {
overflow: hidden;
}
.errors {
padding: 20px;
font-weight: bold;
color: var(--error-color);
}
.content {
padding-bottom: 20px;
}
@@ -682,8 +637,7 @@ export class HaAutomationEditor extends KeyboardShortcutMixin(LitElement) {
flex-direction: column;
padding-bottom: 0;
}
manual-automation-editor,
blueprint-automation-editor {
manual-automation-editor {
margin: 0 auto;
max-width: 1040px;
padding: 28px 20px 0;
@@ -724,6 +678,15 @@ export class HaAutomationEditor extends KeyboardShortcutMixin(LitElement) {
}
h1 {
margin: 0;
font-family: var(--paper-font-headline_-_font-family);
-webkit-font-smoothing: var(
--paper-font-headline_-_-webkit-font-smoothing
);
font-size: var(--paper-font-headline_-_font-size);
font-weight: var(--paper-font-headline_-_font-weight);
letter-spacing: var(--paper-font-headline_-_letter-spacing);
line-height: var(--paper-font-headline_-_line-height);
opacity: var(--dark-primary-opacity);
}
.header-name {
display: flex;

View File

@@ -1,16 +1,4 @@
import {
mdiCancel,
mdiContentDuplicate,
mdiDelete,
mdiHelpCircle,
mdiInformationOutline,
mdiPlay,
mdiPlayCircleOutline,
mdiPlus,
mdiStopCircleOutline,
mdiTransitConnection,
} from "@mdi/js";
import "@polymer/paper-tooltip/paper-tooltip";
import { mdiHelpCircle, mdiInformationOutline, mdiPlus } from "@mdi/js";
import { CSSResultGroup, html, LitElement, TemplateResult } from "lit";
import { customElement, property, state } from "lit/decorators";
import memoizeOne from "memoize-one";
@@ -25,22 +13,11 @@ import type {
RowClickedEvent,
} from "../../../components/data-table/ha-data-table";
import "../../../components/ha-button-related-filter-menu";
import "../../../components/ha-chip";
import "../../../components/ha-fab";
import "../../../components/ha-icon-button";
import "../../../components/ha-icon-overflow-menu";
import "../../../components/ha-svg-icon";
import {
AutomationEntity,
deleteAutomation,
duplicateAutomation,
getAutomationConfig,
triggerAutomationActions,
} from "../../../data/automation";
import {
showAlertDialog,
showConfirmationDialog,
} from "../../../dialogs/generic/show-dialog-box";
import type { AutomationEntity } from "../../../data/automation";
import { showAlertDialog } from "../../../dialogs/generic/show-dialog-box";
import "../../../layouts/hass-tabs-subpage-data-table";
import { haStyle } from "../../../resources/styles";
import { HomeAssistant, Route } from "../../../types";
@@ -86,7 +63,6 @@ class HaAutomationPicker extends LitElement {
...automation,
name: computeStateName(automation),
last_triggered: automation.attributes.last_triggered || undefined,
disabled: automation.state === "off",
}));
}
);
@@ -147,108 +123,22 @@ class HaAutomationPicker extends LitElement {
},
};
}
columns.disabled = this.narrow
? {
title: "",
template: (disabled: boolean) =>
disabled
? html`
<paper-tooltip animation-delay="0" position="left">
${this.hass.localize(
"ui.panel.config.automation.picker.disabled"
)}
</paper-tooltip>
<ha-svg-icon
.path=${mdiCancel}
style="color: var(--secondary-text-color)"
></ha-svg-icon>
`
: "",
}
: {
width: "20%",
title: "",
template: (disabled: boolean) =>
disabled
? html`
<ha-chip>
${this.hass.localize(
"ui.panel.config.automation.picker.disabled"
)}
</ha-chip>
`
: "",
};
columns.actions = {
title: "",
width: this.narrow ? undefined : "10%",
type: "overflow-menu",
template: (_: string, automation: any) =>
html`
<ha-icon-overflow-menu
.hass=${this.hass}
narrow
.items=${[
{
path: mdiInformationOutline,
label: this.hass.localize(
"ui.panel.config.automation.editor.show_info"
),
action: () => this._showInfo(automation),
},
{
path: mdiPlay,
label: this.hass.localize(
"ui.panel.config.automation.editor.run"
),
action: () => this._runActions(automation),
},
{
path: mdiTransitConnection,
label: this.hass.localize(
"ui.panel.config.automation.editor.show_trace"
),
action: () => this._showTrace(automation),
},
{
divider: true,
},
{
path: mdiContentDuplicate,
label: this.hass.localize(
"ui.panel.config.automation.picker.duplicate"
),
action: () => this.duplicate(automation),
},
{
path:
automation.state === "off"
? mdiPlayCircleOutline
: mdiStopCircleOutline,
label:
automation.state === "off"
? this.hass.localize(
"ui.panel.config.automation.editor.enable"
)
: this.hass.localize(
"ui.panel.config.automation.editor.disable"
),
action: () => this._toggle(automation),
},
{
label: this.hass.localize(
"ui.panel.config.automation.picker.delete"
),
path: mdiDelete,
action: () => this._deleteConfirm(automation),
warning: true,
},
]}
>
</ha-icon-overflow-menu>
`,
label: this.hass.localize(
"ui.panel.config.automation.picker.headers.actions"
),
type: "icon-button",
template: (_info, automation: any) => html`
<ha-icon-button
.automation=${automation}
.label=${this.hass.localize(
"ui.panel.config.automation.picker.headers.actions"
)}
.path=${mdiInformationOutline}
@click=${this._showInfo}
></ha-icon-button>
`,
};
return columns;
}
@@ -320,78 +210,12 @@ class HaAutomationPicker extends LitElement {
this._filterValue = undefined;
}
private _showInfo(automation: any) {
private _showInfo(ev) {
ev.stopPropagation();
const automation = ev.currentTarget.automation;
fireEvent(this, "hass-more-info", { entityId: automation.entity_id });
}
private _runActions(automation: any) {
triggerAutomationActions(this.hass, automation.entity_id);
}
private _showTrace(automation: any) {
navigate(`/config/automation/trace/${automation.attributes.id}`);
}
private async _toggle(automation): Promise<void> {
const service = automation.state === "off" ? "turn_on" : "turn_off";
await this.hass.callService("automation", service, {
entity_id: automation.entity_id,
});
}
private async _deleteConfirm(automation) {
showConfirmationDialog(this, {
text: this.hass.localize(
"ui.panel.config.automation.picker.delete_confirm"
),
confirmText: this.hass!.localize("ui.common.delete"),
dismissText: this.hass!.localize("ui.common.cancel"),
confirm: () => this._delete(automation),
});
}
private async _delete(automation) {
try {
await deleteAutomation(this.hass, automation.attributes.id);
} catch (err: any) {
await showAlertDialog(this, {
text:
err.status_code === 400
? this.hass.localize(
"ui.panel.config.automation.editor.load_error_not_deletable"
)
: this.hass.localize(
"ui.panel.config.automation.editor.load_error_unknown",
"err_no",
err.status_code
),
});
}
}
private async duplicate(automation) {
try {
const config = await getAutomationConfig(
this.hass,
automation.attributes.id
);
duplicateAutomation(config);
} catch (err: any) {
await showAlertDialog(this, {
text:
err.status_code === 404
? this.hass.localize(
"ui.panel.config.automation.editor.load_error_not_duplicable"
)
: this.hass.localize(
"ui.panel.config.automation.editor.load_error_unknown",
"err_no",
err.status_code
),
});
}
}
private _showHelp() {
showAlertDialog(this, {
title: this.hass.localize("ui.panel.config.automation.caption"),
@@ -418,7 +242,7 @@ class HaAutomationPicker extends LitElement {
);
if (automation?.attributes.id) {
navigate(`/config/automation/edit/${automation.attributes.id}`);
navigate(`/config/automation/edit/${automation?.attributes.id}`);
}
}

View File

@@ -1,7 +1,5 @@
import {
mdiDotsVertical,
mdiDownload,
mdiInformationOutline,
mdiPencil,
mdiRayEndArrow,
mdiRayStartArrow,
@@ -13,8 +11,6 @@ import { classMap } from "lit/directives/class-map";
import { repeat } from "lit/directives/repeat";
import { isComponentLoaded } from "../../../common/config/is_component_loaded";
import { formatDateTimeWithSeconds } from "../../../common/datetime/format_date_time";
import { fireEvent } from "../../../common/dom/fire_event";
import "../../../components/ha-button-menu";
import "../../../components/ha-icon-button";
import "../../../components/trace/ha-trace-blueprint-config";
import "../../../components/trace/ha-trace-config";
@@ -36,9 +32,9 @@ import {
loadTraces,
} from "../../../data/trace";
import { showAlertDialog } from "../../../dialogs/generic/show-dialog-box";
import "../../../layouts/hass-subpage";
import { haStyle } from "../../../resources/styles";
import { HomeAssistant, Route } from "../../../types";
import { configSections } from "../ha-panel-config";
@customElement("ha-automation-trace")
export class HaAutomationTrace extends LitElement {
@@ -94,116 +90,89 @@ export class HaAutomationTrace extends LitElement {
</div>`;
}
const actionButtons = html`
<ha-icon-button
.label=${this.hass.localize("ui.panel.config.automation.trace.refresh")}
.path=${mdiRefresh}
@click=${this._refreshTraces}
></ha-icon-button>
<ha-icon-button
.label=${this.hass.localize(
"ui.panel.config.automation.trace.download_trace"
)}
.path=${mdiDownload}
.disabled=${!this._trace}
@click=${this._downloadTrace}
></ha-icon-button>
`;
return html`
${devButtons}
<hass-subpage .hass=${this.hass} .narrow=${this.narrow} .header=${title}>
${!this.narrow && stateObj?.attributes.id
? html`
<a
class="trace-link"
href="/config/automation/edit/${stateObj.attributes.id}"
slot="toolbar-icon"
>
<mwc-button>
${this.hass.localize(
"ui.panel.config.automation.trace.edit_automation"
)}
</mwc-button>
</a>
`
<hass-tabs-subpage
.hass=${this.hass}
.narrow=${this.narrow}
.route=${this.route}
.tabs=${configSections.automations}
>
${this.narrow
? html`<span slot="header">${title}</span>
<div slot="toolbar-icon">${actionButtons}</div>`
: ""}
<ha-button-menu corner="BOTTOM_START" slot="toolbar-icon">
<ha-icon-button
slot="trigger"
.label=${this.hass.localize("ui.common.menu")}
.path=${mdiDotsVertical}
></ha-icon-button>
<mwc-list-item
graphic="icon"
.disabled=${!stateObj}
@click=${this._showInfo}
>
${this.hass.localize("ui.panel.config.automation.editor.show_info")}
<ha-svg-icon
slot="graphic"
.path=${mdiInformationOutline}
></ha-svg-icon>
</mwc-list-item>
${stateObj?.attributes.id && this.narrow
? html`
<div class="toolbar">
${!this.narrow
? html`<div>
${title}
<a
class="trace-link"
href="/config/automation/edit/${stateObj.attributes.id}"
class="linkButton"
href="/config/automation/edit/${this.automationId}"
>
<mwc-list-item graphic="icon">
${this.hass.localize(
<ha-icon-button
.label=${this.hass!.localize(
"ui.panel.config.automation.trace.edit_automation"
)}
<ha-svg-icon
slot="graphic"
.path=${mdiPencil}
></ha-svg-icon>
</mwc-list-item>
.path=${mdiPencil}
tabindex="-1"
></ha-icon-button>
</a>
`
</div>`
: ""}
<li divider role="separator"></li>
<mwc-list-item graphic="icon" @click=${this._refreshTraces}>
${this.hass.localize("ui.panel.config.automation.trace.refresh")}
<ha-svg-icon slot="graphic" .path=${mdiRefresh}></ha-svg-icon>
</mwc-list-item>
<mwc-list-item
graphic="icon"
.disabled=${!this._trace}
@click=${this._downloadTrace}
>
${this.hass.localize(
"ui.panel.config.automation.trace.download_trace"
)}
<ha-svg-icon slot="graphic" .path=${mdiDownload}></ha-svg-icon>
</mwc-list-item>
</ha-button-menu>
<div class="toolbar">
${this._traces && this._traces.length > 0
? html`
<ha-icon-button
.label=${this.hass!.localize(
"ui.panel.config.automation.trace.older_trace"
)}
.path=${mdiRayEndArrow}
.disabled=${this._traces[this._traces.length - 1].run_id ===
this._runId}
@click=${this._pickOlderTrace}
></ha-icon-button>
<select .value=${this._runId} @change=${this._pickTrace}>
${repeat(
this._traces,
(trace) => trace.run_id,
(trace) =>
html`<option value=${trace.run_id}>
${formatDateTimeWithSeconds(
new Date(trace.timestamp.start),
this.hass.locale
)}
</option>`
)}
</select>
<ha-icon-button
.label=${this.hass!.localize(
"ui.panel.config.automation.trace.newer_trace"
)}
.path=${mdiRayStartArrow}
.disabled=${this._traces[0].run_id === this._runId}
@click=${this._pickNewerTrace}
></ha-icon-button>
<div>
<ha-icon-button
.label=${this.hass!.localize(
"ui.panel.config.automation.trace.older_trace"
)}
.path=${mdiRayEndArrow}
.disabled=${this._traces[this._traces.length - 1].run_id ===
this._runId}
@click=${this._pickOlderTrace}
></ha-icon-button>
<select .value=${this._runId} @change=${this._pickTrace}>
${repeat(
this._traces,
(trace) => trace.run_id,
(trace) =>
html`<option value=${trace.run_id}>
${formatDateTimeWithSeconds(
new Date(trace.timestamp.start),
this.hass.locale
)}
</option>`
)}
</select>
<ha-icon-button
.label=${this.hass!.localize(
"ui.panel.config.automation.trace.newer_trace"
)}
.path=${mdiRayStartArrow}
.disabled=${this._traces[0].run_id === this._runId}
@click=${this._pickNewerTrace}
></ha-icon-button>
</div>
`
: ""}
${!this.narrow ? html`<div>${actionButtons}</div>` : ""}
</div>
${this._traces === undefined
@@ -307,7 +276,7 @@ export class HaAutomationTrace extends LitElement {
</div>
</div>
`}
</hass-subpage>
</hass-tabs-subpage>
`;
}
@@ -488,13 +457,6 @@ export class HaAutomationTrace extends LitElement {
}
}
private _showInfo() {
if (!this.hass || !this._entityId) {
return;
}
fireEvent(this, "hass-more-info", { entityId: this._entityId });
}
static get styles(): CSSResultGroup {
return [
haStyle,
@@ -503,7 +465,7 @@ export class HaAutomationTrace extends LitElement {
.toolbar {
display: flex;
align-items: center;
justify-content: center;
justify-content: space-between;
font-size: 20px;
height: var(--header-height);
padding: 0 16px;
@@ -514,6 +476,15 @@ export class HaAutomationTrace extends LitElement {
box-sizing: border-box;
}
.toolbar > * {
display: flex;
align-items: center;
}
:host([narrow]) .toolbar > * {
display: contents;
}
.main {
height: calc(100% - 56px);
display: flex;
@@ -549,9 +520,6 @@ export class HaAutomationTrace extends LitElement {
.linkButton {
color: var(--primary-text-color);
}
.trace-link {
text-decoration: none;
}
`,
];
}

View File

@@ -1,18 +1,21 @@
import "@material/mwc-button/mwc-button";
import { mdiHelpCircle } from "@mdi/js";
import { mdiHelpCircle, mdiRobot } from "@mdi/js";
import { HassEntity } from "home-assistant-js-websocket";
import { css, CSSResultGroup, html, LitElement } from "lit";
import { customElement, property } from "lit/decorators";
import { fireEvent } from "../../../common/dom/fire_event";
import "../../../components/entity/ha-entity-toggle";
import "../../../components/ha-card";
import "../../../components/ha-textarea";
import "../../../components/ha-textfield";
import "../../../components/ha-icon-button";
import "../../../components/ha-alert";
import {
AUTOMATION_DEFAULT_MODE,
Condition,
ManualAutomationConfig,
Trigger,
} from "../../../data/automation";
import { Action } from "../../../data/script";
import { Action, isMaxMode, MODES } from "../../../data/script";
import { haStyle } from "../../../resources/styles";
import type { HomeAssistant } from "../../../types";
import { documentationUrl } from "../../../util/documentation-url";
@@ -32,53 +35,91 @@ export class HaManualAutomationEditor extends LitElement {
@property({ attribute: false }) public stateObj?: HassEntity;
@property({ type: Boolean, reflect: true, attribute: "re-order-mode" })
public reOrderMode = false;
protected render() {
return html`
${this.stateObj?.state === "off"
? html`
<ha-alert alert-type="info">
<ha-card outlined>
${this.stateObj && this.stateObj.state === "off"
? html`<div class="disabled-bar">
${this.hass.localize(
"ui.panel.config.automation.editor.disabled"
)}
<mwc-button slot="action" @click=${this._enable}>
${this.hass.localize(
"ui.panel.config.automation.editor.enable"
)}
</mwc-button>
</ha-alert>
`
: ""}
${this.reOrderMode
? html`
<ha-alert
alert-type="info"
.title=${this.hass.localize(
"ui.panel.config.automation.editor.re_order_mode.title"
</div>`
: ""}
<ha-expansion-panel leftChevron>
<div slot="header">
<ha-svg-icon class="settings-icon" .path=${mdiRobot}></ha-svg-icon>
${this.hass.localize(
"ui.panel.config.automation.editor.automation_settings"
)}
</div>
<div class="card-content">
<ha-textarea
.label=${this.hass.localize(
"ui.panel.config.automation.editor.description.label"
)}
.placeholder=${this.hass.localize(
"ui.panel.config.automation.editor.description.placeholder"
)}
name="description"
autogrow
.value=${this.config.description || ""}
@change=${this._valueChanged}
></ha-textarea>
<ha-select
.label=${this.hass.localize(
"ui.panel.config.automation.editor.modes.label"
)}
.value=${this.config.mode || AUTOMATION_DEFAULT_MODE}
@selected=${this._modeChanged}
fixedMenuPosition
.helper=${html`
<a
style="color: var(--secondary-text-color)"
href=${documentationUrl(this.hass, "/docs/automation/modes/")}
target="_blank"
rel="noreferrer"
>${this.hass.localize(
"ui.panel.config.automation.editor.modes.learn_more"
)}</a
>
`}
>
${this.hass.localize(
"ui.panel.config.automation.editor.re_order_mode.description"
${MODES.map(
(mode) => html`
<mwc-list-item .value=${mode}>
${this.hass.localize(
`ui.panel.config.automation.editor.modes.${mode}`
) || mode}
</mwc-list-item>
`
)}
<mwc-button slot="action" @click=${this._exitReOrderMode}>
${this.hass.localize(
"ui.panel.config.automation.editor.re_order_mode.exit"
)}
</mwc-button>
</ha-alert>
`
: ""}
${this.config.description
? html`<p class="description">${this.config.description}</p>`
: ""}
</ha-select>
${this.config.mode && isMaxMode(this.config.mode)
? html`
<br /><ha-textfield
.label=${this.hass.localize(
`ui.panel.config.automation.editor.max.${this.config.mode}`
)}
type="number"
name="max"
.value=${this.config.max || "10"}
@change=${this._valueChanged}
class="max"
>
</ha-textfield>
`
: html``}
</div>
</ha-expansion-panel>
</ha-card>
<div class="header">
<h2 id="triggers-heading" class="name">
<div class="name">
${this.hass.localize(
"ui.panel.config.automation.editor.triggers.header"
)}
</h2>
</div>
<a
href=${documentationUrl(this.hass, "/docs/automation/trigger/")}
target="_blank"
@@ -94,20 +135,17 @@ export class HaManualAutomationEditor extends LitElement {
</div>
<ha-automation-trigger
role="region"
aria-labelledby="triggers-heading"
.triggers=${this.config.trigger}
@value-changed=${this._triggerChanged}
.hass=${this.hass}
.reOrderMode=${this.reOrderMode}
></ha-automation-trigger>
<div class="header">
<h2 id="conditions-heading" class="name">
<div class="name">
${this.hass.localize(
"ui.panel.config.automation.editor.conditions.header"
)}
</h2>
</div>
<a
href=${documentationUrl(this.hass, "/docs/automation/condition/")}
target="_blank"
@@ -123,50 +161,80 @@ export class HaManualAutomationEditor extends LitElement {
</div>
<ha-automation-condition
role="region"
aria-labelledby="conditions-heading"
.conditions=${this.config.condition || []}
@value-changed=${this._conditionChanged}
.hass=${this.hass}
.reOrderMode=${this.reOrderMode}
></ha-automation-condition>
<div class="header">
<h2 id="actions-heading" class="name">
<div class="name">
${this.hass.localize(
"ui.panel.config.automation.editor.actions.header"
)}
</h2>
<div>
<a
href=${documentationUrl(this.hass, "/docs/automation/action/")}
target="_blank"
rel="noreferrer"
>
<ha-icon-button
.path=${mdiHelpCircle}
.label=${this.hass.localize(
"ui.panel.config.automation.editor.actions.learn_more"
)}
></ha-icon-button>
</a>
</div>
<a
href=${documentationUrl(this.hass, "/docs/automation/action/")}
target="_blank"
rel="noreferrer"
>
<ha-icon-button
.path=${mdiHelpCircle}
.label=${this.hass.localize(
"ui.panel.config.automation.editor.actions.learn_more"
)}
></ha-icon-button>
</a>
</div>
<ha-automation-action
role="region"
aria-labelledby="actions-heading"
.actions=${this.config.action}
@value-changed=${this._actionChanged}
.hass=${this.hass}
.narrow=${this.narrow}
.reOrderMode=${this.reOrderMode}
></ha-automation-action>
`;
}
private _exitReOrderMode() {
this.reOrderMode = !this.reOrderMode;
private _valueChanged(ev: CustomEvent) {
ev.stopPropagation();
const target = ev.target as any;
const name = target.name;
if (!name) {
return;
}
let newVal = target.value;
if (target.type === "number") {
newVal = Number(newVal);
}
if ((this.config![name] || "") === newVal) {
return;
}
fireEvent(this, "value-changed", {
value: { ...this.config!, [name]: newVal },
});
}
private _modeChanged(ev) {
const mode = ev.target.value;
if (
mode === this.config!.mode ||
(!this.config!.mode && mode === MODES[0])
) {
return;
}
const value = {
...this.config!,
mode,
};
if (!isMaxMode(mode)) {
delete value.max;
}
fireEvent(this, "value-changed", {
value,
});
}
private _triggerChanged(ev: CustomEvent): void {
@@ -193,15 +261,6 @@ export class HaManualAutomationEditor extends LitElement {
});
}
private async _enable(): Promise<void> {
if (!this.hass || !this.stateObj) {
return;
}
await this.hass.callService("automation", "turn_on", {
entity_id: this.stateObj.entity_id,
});
}
static get styles(): CSSResultGroup {
return [
haStyle,
@@ -212,19 +271,30 @@ export class HaManualAutomationEditor extends LitElement {
ha-card {
overflow: hidden;
}
.description {
margin: 0;
.link-button-row {
padding: 14px;
}
ha-textarea,
ha-textfield {
display: block;
}
p {
margin-bottom: 0;
}
ha-entity-toggle {
margin-right: 8px;
}
ha-select,
.max {
margin-top: 16px;
width: 200px;
}
.header {
display: flex;
margin: 16px 0;
align-items: center;
}
.header:first-child {
margin-top: -16px;
}
.header .name {
font-size: 20px;
font-weight: 400;
@@ -233,9 +303,32 @@ export class HaManualAutomationEditor extends LitElement {
.header a {
color: var(--secondary-text-color);
}
ha-alert {
display: block;
margin-bottom: 16px;
ha-expansion-panel {
--expansion-panel-summary-padding: 0 0 0 8px;
--expansion-panel-content-padding: 0;
}
.card-content {
padding: 16px;
}
.card-content ha-textarea:first-child {
margin-top: -16px;
}
.settings-icon {
display: none;
}
@media (min-width: 870px) {
.settings-icon {
display: inline-block;
color: var(--primary-color);
opacity: 0.9;
margin-right: 8px;
}
}
.disabled-bar {
background: var(--divider-color, #e0e0e0);
text-align: center;
border-top-right-radius: var(--ha-card-border-radius);
border-top-left-radius: var(--ha-card-border-radius);
}
`,
];

View File

@@ -87,8 +87,6 @@ export default class HaAutomationTriggerRow extends LitElement {
@property({ attribute: false }) public trigger!: Trigger;
@property({ type: Boolean }) public hideMenu = false;
@state() private _warnings?: string[];
@state() private _yamlMode = false;
@@ -123,117 +121,102 @@ export default class HaAutomationTriggerRow extends LitElement {
: ""}
<ha-expansion-panel leftChevron>
<h3 slot="header">
<div slot="header">
<ha-svg-icon
class="trigger-icon"
.path=${TRIGGER_TYPES[this.trigger.platform]}
></ha-svg-icon>
${capitalizeFirstLetter(describeTrigger(this.trigger, this.hass))}
</h3>
</div>
<ha-button-menu
slot="icons"
fixed
corner="BOTTOM_START"
@action=${this._handleAction}
@click=${preventDefault}
>
<ha-icon-button
slot="trigger"
.label=${this.hass.localize("ui.common.menu")}
.path=${mdiDotsVertical}
></ha-icon-button>
<slot name="icons" slot="icons"></slot>
${this.hideMenu
? ""
: html`
<ha-button-menu
slot="icons"
fixed
corner="BOTTOM_START"
@action=${this._handleAction}
@click=${preventDefault}
>
<ha-icon-button
slot="trigger"
.label=${this.hass.localize("ui.common.menu")}
.path=${mdiDotsVertical}
></ha-icon-button>
<mwc-list-item graphic="icon">
${this.hass.localize(
"ui.panel.config.automation.editor.triggers.rename"
)}
<ha-svg-icon slot="graphic" .path=${mdiRenameBox}></ha-svg-icon>
</mwc-list-item>
<mwc-list-item graphic="icon">
${this.hass.localize(
"ui.panel.config.automation.editor.actions.duplicate"
)}
<ha-svg-icon
slot="graphic"
.path=${mdiContentDuplicate}
></ha-svg-icon>
</mwc-list-item>
<mwc-list-item graphic="icon">
${this.hass.localize(
"ui.panel.config.automation.editor.triggers.rename"
)}
<ha-svg-icon
slot="graphic"
.path=${mdiRenameBox}
></ha-svg-icon>
</mwc-list-item>
<mwc-list-item graphic="icon">
${this.hass.localize(
"ui.panel.config.automation.editor.actions.duplicate"
)}
<ha-svg-icon
slot="graphic"
.path=${mdiContentDuplicate}
></ha-svg-icon>
</mwc-list-item>
<mwc-list-item graphic="icon">
${this.hass.localize(
"ui.panel.config.automation.editor.triggers.edit_id"
)}
<ha-svg-icon slot="graphic" .path=${mdiIdentifier}></ha-svg-icon>
</mwc-list-item>
<mwc-list-item graphic="icon">
${this.hass.localize(
"ui.panel.config.automation.editor.triggers.edit_id"
)}
<ha-svg-icon
slot="graphic"
.path=${mdiIdentifier}
></ha-svg-icon>
</mwc-list-item>
<li divider role="separator"></li>
<li divider role="separator"></li>
<mwc-list-item .disabled=${!supported} graphic="icon">
${this.hass.localize("ui.panel.config.automation.editor.edit_ui")}
${!yamlMode
? html`<ha-svg-icon
slot="graphic"
.path=${mdiCheck}
></ha-svg-icon>`
: ``}
</mwc-list-item>
<mwc-list-item .disabled=${!supported} graphic="icon">
${this.hass.localize(
"ui.panel.config.automation.editor.edit_ui"
)}
${!yamlMode
? html`<ha-svg-icon
class="selected_menu_item"
slot="graphic"
.path=${mdiCheck}
></ha-svg-icon>`
: ``}
</mwc-list-item>
<mwc-list-item .disabled=${!supported} graphic="icon">
${this.hass.localize(
"ui.panel.config.automation.editor.edit_yaml"
)}
${yamlMode
? html`<ha-svg-icon
slot="graphic"
.path=${mdiCheck}
></ha-svg-icon>`
: ``}
</mwc-list-item>
<mwc-list-item .disabled=${!supported} graphic="icon">
${this.hass.localize(
"ui.panel.config.automation.editor.edit_yaml"
)}
${yamlMode
? html`<ha-svg-icon
class="selected_menu_item"
slot="graphic"
.path=${mdiCheck}
></ha-svg-icon>`
: ``}
</mwc-list-item>
<li divider role="separator"></li>
<li divider role="separator"></li>
<mwc-list-item graphic="icon">
${this.trigger.enabled === false
? this.hass.localize(
"ui.panel.config.automation.editor.actions.enable"
)
: this.hass.localize(
"ui.panel.config.automation.editor.actions.disable"
)}
<ha-svg-icon
slot="graphic"
.path=${this.trigger.enabled === false
? mdiPlayCircleOutline
: mdiStopCircleOutline}
></ha-svg-icon>
</mwc-list-item>
<mwc-list-item class="warning" graphic="icon">
${this.hass.localize(
"ui.panel.config.automation.editor.actions.delete"
)}
<ha-svg-icon
class="warning"
slot="graphic"
.path=${mdiDelete}
></ha-svg-icon>
</mwc-list-item>
</ha-button-menu>
<mwc-list-item graphic="icon">
${this.trigger.enabled === false
? this.hass.localize(
"ui.panel.config.automation.editor.actions.enable"
)
: this.hass.localize(
"ui.panel.config.automation.editor.actions.disable"
)}
<ha-svg-icon
slot="graphic"
.path=${this.trigger.enabled === false
? mdiPlayCircleOutline
: mdiStopCircleOutline}
></ha-svg-icon>
</mwc-list-item>
<mwc-list-item class="warning" graphic="icon">
${this.hass.localize(
"ui.panel.config.automation.editor.actions.delete"
)}
<ha-svg-icon
class="warning"
slot="graphic"
.path=${mdiDelete}
></ha-svg-icon>
</mwc-list-item>
</ha-button-menu>
`}
<div
class=${classMap({
"card-content": true,
@@ -549,18 +532,13 @@ export default class HaAutomationTriggerRow extends LitElement {
--expansion-panel-summary-padding: 0 0 0 8px;
--expansion-panel-content-padding: 0;
}
h3 {
margin: 0;
font-size: inherit;
font-weight: inherit;
}
.trigger-icon {
display: none;
}
@media (min-width: 870px) {
.trigger-icon {
display: inline-block;
color: var(--secondary-text-color);
color: var(--primary-color);
opacity: 0.9;
margin-right: 8px;
}
@@ -609,12 +587,6 @@ export default class HaAutomationTriggerRow extends LitElement {
display: block;
margin-bottom: 24px;
}
.selected_menu_item {
color: var(--primary-color);
}
li[role="separator"] {
border-bottom-color: var(--divider-color);
}
`,
];
}

View File

@@ -1,26 +1,22 @@
import "@material/mwc-button";
import type { ActionDetail } from "@material/mwc-list";
import { mdiArrowDown, mdiArrowUp, mdiDrag, mdiPlus } from "@mdi/js";
import { repeat } from "lit/directives/repeat";
import { mdiPlus } from "@mdi/js";
import deepClone from "deep-clone-simple";
import memoizeOne from "memoize-one";
import "@material/mwc-button";
import { css, CSSResultGroup, html, LitElement, PropertyValues } from "lit";
import { customElement, property } from "lit/decorators";
import { repeat } from "lit/directives/repeat";
import memoizeOne from "memoize-one";
import type { SortableEvent } from "sortablejs";
import type { ActionDetail } from "@material/mwc-list";
import { fireEvent } from "../../../../common/dom/fire_event";
import { stringCompare } from "../../../../common/string/compare";
import type { LocalizeFunc } from "../../../../common/translations/localize";
import "../../../../components/ha-button-menu";
import type { HaSelect } from "../../../../components/ha-select";
import "../../../../components/ha-svg-icon";
import "../../../../components/ha-button-menu";
import { Trigger } from "../../../../data/automation";
import { TRIGGER_TYPES } from "../../../../data/trigger";
import { sortableStyles } from "../../../../resources/ha-sortable-style";
import { SortableInstance } from "../../../../resources/sortable";
import { loadSortable } from "../../../../resources/sortable.ondemand";
import { HomeAssistant } from "../../../../types";
import "./ha-automation-trigger-row";
import type HaAutomationTriggerRow from "./ha-automation-trigger-row";
import type { LocalizeFunc } from "../../../../common/translations/localize";
import { stringCompare } from "../../../../common/string/compare";
import type { HaSelect } from "../../../../components/ha-select";
import "./types/ha-automation-trigger-calendar";
import "./types/ha-automation-trigger-device";
import "./types/ha-automation-trigger-event";
@@ -43,93 +39,49 @@ export default class HaAutomationTrigger extends LitElement {
@property() public triggers!: Trigger[];
@property({ type: Boolean }) public reOrderMode = false;
private _focusLastTriggerOnChange = false;
private _triggerKeys = new WeakMap<Trigger, string>();
private _sortable?: SortableInstance;
protected render() {
return html`
<div class="triggers">
${repeat(
this.triggers,
(trigger) => this._getKey(trigger),
(trg, idx) => html`
<ha-automation-trigger-row
.index=${idx}
.trigger=${trg}
.hideMenu=${this.reOrderMode}
@duplicate=${this._duplicateTrigger}
@value-changed=${this._triggerChanged}
.hass=${this.hass}
>
${this.reOrderMode
? html`
<ha-icon-button
.index=${idx}
slot="icons"
.label=${this.hass.localize(
"ui.panel.config.automation.editor.move_up"
)}
.path=${mdiArrowUp}
@click=${this._moveUp}
.disabled=${idx === 0}
></ha-icon-button>
<ha-icon-button
.index=${idx}
slot="icons"
.label=${this.hass.localize(
"ui.panel.config.automation.editor.move_down"
)}
.path=${mdiArrowDown}
@click=${this._moveDown}
.disabled=${idx === this.triggers.length - 1}
></ha-icon-button>
<div class="handle" slot="icons">
<ha-svg-icon .path=${mdiDrag}></ha-svg-icon>
</div>
`
: ""}
</ha-automation-trigger-row>
${repeat(
this.triggers,
(trigger) => this._getKey(trigger),
(trg, idx) => html`
<ha-automation-trigger-row
.index=${idx}
.trigger=${trg}
@duplicate=${this._duplicateTrigger}
@value-changed=${this._triggerChanged}
.hass=${this.hass}
></ha-automation-trigger-row>
`
)}
<ha-button-menu @action=${this._addTrigger}>
<mwc-button
slot="trigger"
outlined
.label=${this.hass.localize(
"ui.panel.config.automation.editor.triggers.add"
)}
>
<ha-svg-icon .path=${mdiPlus} slot="icon"></ha-svg-icon>
</mwc-button>
${this._processedTypes(this.hass.localize).map(
([opt, label, icon]) => html`
<mwc-list-item .value=${opt} aria-label=${label} graphic="icon">
${label}<ha-svg-icon slot="graphic" .path=${icon}></ha-svg-icon
></mwc-list-item>
`
)}
</div>
<ha-button-menu @action=${this._addTrigger}>
<mwc-button
slot="trigger"
outlined
.label=${this.hass.localize(
"ui.panel.config.automation.editor.triggers.add"
)}
>
<ha-svg-icon .path=${mdiPlus} slot="icon"></ha-svg-icon>
</mwc-button>
${this._processedTypes(this.hass.localize).map(
([opt, label, icon]) => html`
<mwc-list-item .value=${opt} aria-label=${label} graphic="icon">
${label}<ha-svg-icon slot="graphic" .path=${icon}></ha-svg-icon
></mwc-list-item>
`
)}
</ha-button-menu>
</div>
</ha-button-menu>
`;
}
protected updated(changedProps: PropertyValues) {
super.updated(changedProps);
if (changedProps.has("reOrderMode")) {
if (this.reOrderMode) {
this._createSortable();
} else {
this._destroySortable();
}
}
if (changedProps.has("triggers") && this._focusLastTriggerOnChange) {
this._focusLastTriggerOnChange = false;
@@ -144,36 +96,6 @@ export default class HaAutomationTrigger extends LitElement {
}
}
private async _createSortable() {
const Sortable = await loadSortable();
this._sortable = new Sortable(
this.shadowRoot!.querySelector(".triggers")!,
{
animation: 150,
fallbackClass: "sortable-fallback",
handle: ".handle",
onChoose: (evt: SortableEvent) => {
(evt.item as any).placeholder =
document.createComment("sort-placeholder");
evt.item.after((evt.item as any).placeholder);
},
onEnd: (evt: SortableEvent) => {
// put back in original location
if ((evt.item as any).placeholder) {
(evt.item as any).placeholder.replaceWith(evt.item);
delete (evt.item as any).placeholder;
}
this._dragged(evt);
},
}
);
}
private _destroySortable() {
this._sortable?.destroy();
this._sortable = undefined;
}
private _getKey(action: Trigger) {
if (!this._triggerKeys.has(action)) {
this._triggerKeys.set(action, Math.random().toString());
@@ -200,30 +122,6 @@ export default class HaAutomationTrigger extends LitElement {
fireEvent(this, "value-changed", { value: triggers });
}
private _moveUp(ev) {
const index = (ev.target as any).index;
const newIndex = index - 1;
this._move(index, newIndex);
}
private _moveDown(ev) {
const index = (ev.target as any).index;
const newIndex = index + 1;
this._move(index, newIndex);
}
private _dragged(ev: SortableEvent): void {
if (ev.oldIndex === ev.newIndex) return;
this._move(ev.oldIndex!, ev.newIndex!);
}
private _move(index: number, newIndex: number) {
const triggers = this.triggers.concat();
const trigger = triggers.splice(index, 1)[0];
triggers.splice(newIndex, 0, trigger);
fireEvent(this, "value-changed", { value: triggers });
}
private _triggerChanged(ev: CustomEvent) {
ev.stopPropagation();
const triggers = [...this.triggers];
@@ -268,27 +166,16 @@ export default class HaAutomationTrigger extends LitElement {
);
static get styles(): CSSResultGroup {
return [
sortableStyles,
css`
ha-automation-trigger-row {
display: block;
margin-bottom: 16px;
scroll-margin-top: 48px;
}
ha-svg-icon {
height: 20px;
}
.handle {
cursor: move;
padding: 12px;
}
.handle ha-svg-icon {
pointer-events: none;
height: 24px;
}
`,
];
return css`
ha-automation-trigger-row {
display: block;
margin-bottom: 16px;
scroll-margin-top: 48px;
}
ha-svg-icon {
height: 20px;
}
`;
}
}

View File

@@ -15,7 +15,7 @@ export class HaTagTrigger extends LitElement implements TriggerElement {
@property() public trigger!: TagTrigger;
@state() private _tags?: Tag[];
@state() private _tags: Tag[] = [];
public static get defaultConfig() {
return { tag_id: "" };
@@ -27,16 +27,14 @@ export class HaTagTrigger extends LitElement implements TriggerElement {
}
protected render() {
if (!this._tags) {
return html``;
}
const { tag_id } = this.trigger;
return html`
<ha-select
.label=${this.hass.localize(
"ui.panel.config.automation.editor.triggers.type.tag.label"
)}
.disabled=${this._tags.length === 0}
.value=${this.trigger.tag_id}
.value=${tag_id}
@selected=${this._tagChanged}
>
${this._tags.map(
@@ -51,19 +49,13 @@ export class HaTagTrigger extends LitElement implements TriggerElement {
}
private async _fetchTags() {
this._tags = (await fetchTags(this.hass)).sort((a, b) =>
this._tags = await fetchTags(this.hass);
this._tags.sort((a, b) =>
caseInsensitiveStringCompare(a.name || a.id, b.name || b.id)
);
}
private _tagChanged(ev) {
if (
!ev.target.value ||
!this._tags ||
this.trigger.tag_id === ev.target.value
) {
return;
}
fireEvent(this, "value-changed", {
value: {
...this.trigger,

View File

@@ -116,7 +116,6 @@ class HaConfigSystemNavigation extends LitElement {
return html`
<hass-subpage
.hass=${this.hass}
back-path="/config"
.header=${this.hass.localize("ui.panel.config.dashboard.system.main")}
>

View File

@@ -60,11 +60,12 @@ import {
import "../../../layouts/hass-error-screen";
import "../../../layouts/hass-tabs-subpage";
import { haStyle } from "../../../resources/styles";
import type { HomeAssistant } from "../../../types";
import type { HomeAssistant, Route } from "../../../types";
import { brandsUrl } from "../../../util/brands-url";
import { fileDownload } from "../../../util/file_download";
import "../../logbook/ha-logbook";
import "../ha-config-section";
import { configSections } from "../ha-panel-config";
import "./device-detail/ha-device-entities-card";
import "./device-detail/ha-device-info-card";
import { showDeviceAutomationDialog } from "./device-detail/show-dialog-device-automation";
@@ -72,7 +73,6 @@ import {
loadDeviceRegistryDetailDialog,
showDeviceRegistryDetailDialog,
} from "./device-registry-detail/show-dialog-device-registry-detail";
import "../../../layouts/hass-subpage";
export interface EntityRegistryStateEntry extends EntityRegistryEntry {
stateName?: string | null;
@@ -80,7 +80,6 @@ export interface EntityRegistryStateEntry extends EntityRegistryEntry {
export interface DeviceAction {
href?: string;
target?: string;
action?: (ev: any) => void;
label: string;
icon?: string;
@@ -97,21 +96,23 @@ export interface DeviceAlert {
export class HaConfigDevicePage extends LitElement {
@property({ attribute: false }) public hass!: HomeAssistant;
@property({ attribute: false }) public devices!: DeviceRegistryEntry[];
@property() public devices!: DeviceRegistryEntry[];
@property({ attribute: false }) public entries!: ConfigEntry[];
@property() public entries!: ConfigEntry[];
@property({ attribute: false }) public entities!: EntityRegistryEntry[];
@property() public entities!: EntityRegistryEntry[];
@property({ attribute: false }) public areas!: AreaRegistryEntry[];
@property() public areas!: AreaRegistryEntry[];
@property() public deviceId!: string;
@property({ type: Boolean, reflect: true }) public narrow!: boolean;
@property({ type: Boolean }) public isWide!: boolean;
@property() public isWide!: boolean;
@property({ type: Boolean }) public showAdvanced!: boolean;
@property() public showAdvanced!: boolean;
@property() public route!: Route;
@state() private _related?: RelatedResult;
@@ -608,12 +609,16 @@ export class HaConfigDevicePage extends LitElement {
: "";
return html`
<hass-subpage
<hass-tabs-subpage
.hass=${this.hass}
.narrow=${this.narrow}
.header=${deviceName}
.tabs=${configSections.devices}
.route=${this.route}
>
${
this.narrow
? html`
<span slot="header">${deviceName}</span>
<ha-icon-button
slot="toolbar-icon"
.path=${mdiPencil}
@@ -622,20 +627,39 @@ export class HaConfigDevicePage extends LitElement {
"ui.panel.config.devices.edit_settings"
)}
></ha-icon-button>
`
: ""
}
<div class="container">
<div class="header fullwidth">
${
area
? html`<div class="header-name">
<a href="/config/areas/area/${area.area_id}"
>${this.hass.localize(
"ui.panel.config.integrations.config_entry.area",
"area",
area.name || "Unnamed Area"
)}</a
>
</div>`
: ""
this.narrow
? ""
: html`
<div class="header-name">
<div>
<h1>${deviceName}</h1>
${area
? html`
<a href="/config/areas/area/${area.area_id}"
>${this.hass.localize(
"ui.panel.config.integrations.config_entry.area",
"area",
area.name || "Unnamed Area"
)}</a
>
`
: ""}
</div>
<ha-icon-button
.path=${mdiPencil}
@click=${this._showSettings}
.label=${this.hass.localize(
"ui.panel.config.devices.edit_settings"
)}
></ha-icon-button>
</div>
`
}
<div class="header-right">
${
@@ -700,15 +724,7 @@ export class HaConfigDevicePage extends LitElement {
? html`
<div class="card-actions" slot="actions">
<div>
<a
href=${ifDefined(firstDeviceAction!.href)}
rel=${ifDefined(
firstDeviceAction!.target
? "noreferrer"
: undefined
)}
target=${ifDefined(firstDeviceAction!.target)}
>
<a href=${ifDefined(firstDeviceAction!.href)}>
<mwc-button
class=${ifDefined(firstDeviceAction!.classes)}
.action=${firstDeviceAction!.action}
@@ -751,15 +767,7 @@ export class HaConfigDevicePage extends LitElement {
></ha-icon-button>
${actions.map(
(deviceAction) => html`
<a
href=${ifDefined(deviceAction.href)}
target=${ifDefined(deviceAction.target)}
rel=${ifDefined(
deviceAction.target
? "noreferrer"
: undefined
)}
>
<a href=${ifDefined(deviceAction.href)}>
<mwc-list-item
class=${ifDefined(
deviceAction.classes
@@ -851,7 +859,7 @@ export class HaConfigDevicePage extends LitElement {
</div>
</div>
</ha-config-section>
</hass-subpage> `;
</hass-tabs-subpage> `;
}
private async _getDiagnosticButtons(requestId: number): Promise<void> {
@@ -936,18 +944,7 @@ export class HaConfigDevicePage extends LitElement {
buttons.push({
action: async () => {
const confirmed = await showConfirmationDialog(this, {
text:
this._integrations(device, this.entries).length > 1
? this.hass.localize(
`ui.panel.config.devices.confirm_delete_integration`,
{
integration: domainToName(
this.hass.localize,
entry.domain
),
}
)
: this.hass.localize(`ui.panel.config.devices.confirm_delete`),
text: this.hass.localize("ui.panel.config.devices.confirm_delete"),
});
if (!confirmed) {
@@ -963,7 +960,7 @@ export class HaConfigDevicePage extends LitElement {
classes: "warning",
icon: mdiDelete,
label:
this._integrations(device, this.entries).length > 1
buttons.length > 1
? this.hass.localize(
`ui.panel.config.devices.delete_device_integration`,
{
@@ -998,7 +995,6 @@ export class HaConfigDevicePage extends LitElement {
if (configurationUrl) {
deviceActions.push({
href: configurationUrl,
target: configurationUrlIsHomeAssistant ? undefined : "_blank",
icon: mdiCog,
label: this.hass.localize(
"ui.panel.config.devices.open_configuration_url"

View File

@@ -872,17 +872,10 @@ export class EntityRegistrySettings extends SubscribeMixin(LitElement) {
name: this._name.trim() || null,
icon: this._icon.trim() || null,
area_id: this._areaId || null,
device_class: this._deviceClass || null,
new_entity_id: this._entityId.trim(),
};
// Only update device class if changed by user
if (
this._deviceClass !==
(this.entry.device_class || this.entry.original_device_class)
) {
params.device_class = this._deviceClass;
}
const stateObj: HassEntity | undefined =
this.hass.states[this.entry.entity_id];
const domain = computeDomain(this.entry.entity_id);
@@ -900,7 +893,7 @@ export class EntityRegistrySettings extends SubscribeMixin(LitElement) {
params.hidden_by = this._hiddenBy;
}
if (
(domain === "number" || domain === "sensor") &&
(domain === "number" || domain === "number") &&
stateObj?.attributes?.unit_of_measurement !== this._unit_of_measurement
) {
params.options_domain = domain;
@@ -1058,10 +1051,9 @@ export class EntityRegistrySettings extends SubscribeMixin(LitElement) {
.buttons {
box-sizing: border-box;
display: flex;
padding: 24px;
padding-top: 16px;
padding: 8px 24px 24px 24px;
justify-content: space-between;
padding-bottom: max(env(safe-area-inset-bottom), 24px);
padding-bottom: max(env(safe-area-inset-bottom), 16px);
background-color: var(--mdc-theme-surface, #fff);
border-top: 1px solid var(--divider-color);
position: sticky;

View File

@@ -68,10 +68,9 @@ import type { HomeAssistant, Route } from "../../../types";
import { configSections } from "../ha-panel-config";
import "../integrations/ha-integration-overflow-menu";
export interface StateEntity extends Omit<EntityRegistryEntry, "id"> {
export interface StateEntity extends EntityRegistryEntry {
readonly?: boolean;
selectable?: boolean;
id?: string;
}
export interface EntityRow extends StateEntity {
@@ -303,7 +302,7 @@ export class HaConfigEntities extends SubscribeMixin(LitElement) {
private _filteredEntitiesAndDomains = memoize(
(
entities: StateEntity[],
entities: EntityRegistryEntry[],
devices: DeviceRegistryEntry[] | undefined,
areas: AreaRegistryEntry[] | undefined,
stateEntities: StateEntity[],
@@ -393,10 +392,7 @@ export class HaConfigEntities extends SubscribeMixin(LitElement) {
result.push({
...entry,
entity,
name: computeEntityRegistryName(
this.hass!,
entry as EntityRegistryEntry
),
name: computeEntityRegistryName(this.hass!, entry),
unavailable,
restored,
area: area ? area.name : "—",

View File

@@ -126,9 +126,3 @@ export class HaConfigSection extends LitElement {
`;
}
}
declare global {
interface HTMLElementTagNameMap {
"ha-config-section": HaConfigSection;
}
}

View File

@@ -319,7 +319,7 @@ export const configSections: { [name: string]: PageNavigation[] } = {
translationKey: "hardware",
iconPath: mdiMemory,
iconColor: "#301A8E",
components: ["hassio", "hardware"],
component: "hassio",
},
],
about: [

View File

@@ -80,31 +80,29 @@ class HaConfigHardware extends SubscribeMixin(LitElement) {
private _cpuEntries: { x: number; y: number | null }[] = [];
public hassSubscribe() {
return isComponentLoaded(this.hass, "hardware")
? [
this.hass.connection.subscribeMessage<SystemStatusStreamMessage>(
(message) => {
// Only store the last 60 entries
this._memoryEntries.shift();
this._cpuEntries.shift();
return [
this.hass.connection.subscribeMessage<SystemStatusStreamMessage>(
(message) => {
// Only store the last 60 entries
this._memoryEntries.shift();
this._cpuEntries.shift();
this._memoryEntries.push({
x: new Date(message.timestamp).getTime(),
y: message.memory_used_percent,
});
this._cpuEntries.push({
x: new Date(message.timestamp).getTime(),
y: message.cpu_percent,
});
this._memoryEntries.push({
x: new Date(message.timestamp).getTime(),
y: message.memory_used_percent,
});
this._cpuEntries.push({
x: new Date(message.timestamp).getTime(),
y: message.cpu_percent,
});
this._systemStatusData = message;
},
{
type: "hardware/subscribe_system_status",
}
),
]
: [];
this._systemStatusData = message;
},
{
type: "hardware/subscribe_system_status",
}
),
];
}
protected willUpdate(): void {
@@ -177,7 +175,7 @@ class HaConfigHardware extends SubscribeMixin(LitElement) {
let documentationURL: string | undefined;
if (this._hardwareInfo?.hardware.length) {
const boardData = this._hardwareInfo.hardware[0];
const boardData = this._hardwareInfo!.hardware[0];
boardId = boardData.board.hassio_board_id;
boardName = boardData.name;
@@ -200,34 +198,32 @@ class HaConfigHardware extends SubscribeMixin(LitElement) {
.narrow=${this.narrow}
.header=${this.hass.localize("ui.panel.config.hardware.caption")}
>
${isComponentLoaded(this.hass, "hassio")
? html`<ha-button-menu corner="BOTTOM_START" slot="toolbar-icon">
<ha-icon-button
.label=${this.hass.localize("ui.common.menu")}
.path=${mdiDotsVertical}
slot="trigger"
></ha-icon-button>
<mwc-list-item @click=${this._openHardware}
>${this.hass.localize(
"ui.panel.config.hardware.available_hardware.title"
)}</mwc-list-item
>
${this._hostData
? html`
<mwc-list-item class="warning" @click=${this._hostReboot}
>${this.hass.localize(
"ui.panel.config.hardware.reboot_host"
)}</mwc-list-item
>
<mwc-list-item class="warning" @click=${this._hostShutdown}
>${this.hass.localize(
"ui.panel.config.hardware.shutdown_host"
)}</mwc-list-item
>
`
: ""}
</ha-button-menu>`
: ""}
<ha-button-menu corner="BOTTOM_START" slot="toolbar-icon">
<ha-icon-button
.label=${this.hass.localize("ui.common.menu")}
.path=${mdiDotsVertical}
slot="trigger"
></ha-icon-button>
<mwc-list-item @click=${this._openHardware}
>${this.hass.localize(
"ui.panel.config.hardware.available_hardware.title"
)}</mwc-list-item
>
${this._hostData
? html`
<mwc-list-item class="warning" @click=${this._hostReboot}
>${this.hass.localize(
"ui.panel.config.hardware.reboot_host"
)}</mwc-list-item
>
<mwc-list-item class="warning" @click=${this._hostShutdown}
>${this.hass.localize(
"ui.panel.config.hardware.shutdown_host"
)}</mwc-list-item
>
`
: ""}
</ha-button-menu>
${this._error
? html`
<ha-alert alert-type="error"
@@ -288,38 +284,38 @@ class HaConfigHardware extends SubscribeMixin(LitElement) {
</ha-card>
`
: ""}
${this._systemStatusData
? html`<ha-card outlined>
<div class="header">
<div class="title">
${this.hass.localize(
"ui.panel.config.hardware.processor"
)}
</div>
<div class="value">
${this._systemStatusData.cpu_percent || "-"}%
</div>
</div>
<div class="card-content">
<ha-chart-base
.data=${{
datasets: [
{
...DATA_SET_CONFIG,
data: this._cpuEntries,
},
],
}}
.options=${this._chartOptions}
></ha-chart-base>
</div>
</ha-card>
<ha-card outlined>
<div class="header">
<div class="title">
${this.hass.localize("ui.panel.config.hardware.memory")}
</div>
<div class="value">
<ha-card outlined>
<div class="header">
<div class="title">
${this.hass.localize("ui.panel.config.hardware.processor")}
</div>
<div class="value">
${this._systemStatusData?.cpu_percent || "-"}%
</div>
</div>
<div class="card-content">
<ha-chart-base
.data=${{
datasets: [
{
...DATA_SET_CONFIG,
data: this._cpuEntries,
},
],
}}
.options=${this._chartOptions}
></ha-chart-base>
</div>
</ha-card>
<ha-card outlined>
<div class="header">
<div class="title">
${this.hass.localize("ui.panel.config.hardware.memory")}
</div>
<div class="value">
${this._systemStatusData
? html`
${round(this._systemStatusData.memory_used_mb / 1024, 1)}
GB /
${round(
@@ -329,23 +325,24 @@ class HaConfigHardware extends SubscribeMixin(LitElement) {
0
)}
GB
</div>
</div>
<div class="card-content">
<ha-chart-base
.data=${{
datasets: [
{
...DATA_SET_CONFIG,
data: this._memoryEntries,
},
],
}}
.options=${this._chartOptions}
></ha-chart-base>
</div>
</ha-card>`
: ""}
`
: "- GB / - GB"}
</div>
</div>
<div class="card-content">
<ha-chart-base
.data=${{
datasets: [
{
...DATA_SET_CONFIG,
data: this._memoryEntries,
},
],
}}
.options=${this._chartOptions}
></ha-chart-base>
</div>
</ha-card>
</div>
</hass-subpage>
`;
@@ -356,9 +353,7 @@ class HaConfigHardware extends SubscribeMixin(LitElement) {
try {
if (isComponentLoaded(this.hass, "hardware")) {
this._hardwareInfo = await this.hass.callWS({ type: "hardware/info" });
}
if (isHassioLoaded && !this._hardwareInfo?.hardware.length) {
} else if (isHassioLoaded) {
this._OSData = await fetchHassioHassOsInfo(this.hass);
}

View File

@@ -26,6 +26,8 @@ class HaInputNumberForm extends LitElement {
@state() private _min?: number;
@state() private _initial?: number;
@state() private _mode?: string;
@state() private _step?: number;
@@ -42,6 +44,7 @@ class HaInputNumberForm extends LitElement {
this._min = item.min ?? 0;
this._mode = item.mode || "slider";
this._step = item.step ?? 1;
this._initial = item.initial ?? 0;
this._unit_of_measurement = item.unit_of_measurement;
} else {
this._item = {
@@ -54,6 +57,7 @@ class HaInputNumberForm extends LitElement {
this._min = 0;
this._mode = "slider";
this._step = 1;
this._initial = 0;
}
}
@@ -113,6 +117,15 @@ class HaInputNumberForm extends LitElement {
"ui.dialogs.helper_settings.input_number.max"
)}
></ha-textfield>
<ha-textfield
.value=${this._initial}
.configValue=${"initial"}
type="number"
@input=${this._valueChanged}
.label=${this.hass!.localize(
"ui.dialogs.helper_settings.input_number.initial"
)}
></ha-textfield>
${this.hass.userData?.showAdvanced
? html`
<div class="layout horizontal center justified">

View File

@@ -36,6 +36,7 @@ const defaultFullCalendarConfig: CalendarOptions = {
selectOverlap: false,
eventOverlap: false,
allDaySlot: false,
slotMinTime: "00:00:59",
height: "parent",
locales: allLocales,
firstDay: 1,
@@ -177,7 +178,7 @@ class HaScheduleForm extends LitElement {
},
eventTimeFormat: {
hour: useAmPm(this.hass.locale) ? "numeric" : "2-digit",
minute: useAmPm(this.hass.locale) ? "numeric" : "2-digit",
minute: undefined,
hour12: useAmPm(this.hass.locale),
meridiem: useAmPm(this.hass.locale) ? "narrow" : false,
},
@@ -213,8 +214,7 @@ class HaScheduleForm extends LitElement {
}
this[`_${day}`].forEach((item: ScheduleDay, index: number) => {
// Add 7 to 0 because we start the calendar on Monday
const distance = i - currentDay + (i === 0 ? 7 : 0);
const distance = i - currentDay;
const start = new Date();
start.setDate(start.getDate() + distance);
@@ -227,9 +227,7 @@ class HaScheduleForm extends LitElement {
end.setDate(end.getDate() + distance);
end.setHours(
parseInt(item.to.slice(0, 2)),
parseInt(item.to.slice(-2)),
0,
0
parseInt(item.to.slice(-2))
);
events.push({
@@ -383,9 +381,6 @@ class HaScheduleForm extends LitElement {
margin: 8px 0;
height: 450px;
width: 100%;
-webkit-user-select: none;
-ms-user-select: none;
user-select: none;
}
.fc-scroller {
overflow-x: visible !important;

View File

@@ -15,6 +15,7 @@ import {
} from "../../../components/data-table/ha-data-table";
import "../../../components/ha-fab";
import "../../../components/ha-icon";
import "../../../components/ha-icon-overflow-menu";
import "../../../components/ha-svg-icon";
import { ConfigEntry, getConfigEntries } from "../../../data/config_entries";
import { getConfigFlowHandlers } from "../../../data/config_flow";

View File

@@ -5,7 +5,7 @@ import {
mdiHandsPray,
mdiHelp,
mdiHomeAssistant,
mdiNewspaperVariant,
mdiPower,
mdiTshirtCrew,
} from "@mdi/js";
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
@@ -36,7 +36,7 @@ const PAGES: Array<{
{
name: "change_log",
path: "/latest-release-notes/",
iconPath: mdiNewspaperVariant,
iconPath: mdiPower,
iconColor: "#4A5963",
},
{

View File

@@ -689,22 +689,22 @@ class HaConfigIntegrations extends SubscribeMixin(LitElement) {
if (handlers.includes(domain)) {
const localize = await localizePromise;
if (
await showConfirmationDialog(this, {
!(await showConfirmationDialog(this, {
title: localize("ui.panel.config.integrations.confirm_new", {
integration: domainToName(localize, domain),
}),
})
}))
) {
showConfigFlowDialog(this, {
dialogClosedCallback: () => {
this._handleFlowUpdated();
},
startFlowHandler: domain,
manifest: this._manifests[domain],
showAdvanced: this.hass.userData?.showAdvanced,
});
return;
}
return;
showConfigFlowDialog(this, {
dialogClosedCallback: () => {
this._handleFlowUpdated();
},
startFlowHandler: domain,
manifest: this._manifests[domain],
showAdvanced: this.hass.userData?.showAdvanced,
});
}
const supportedBrands = await getSupportedBrands(this.hass);

View File

@@ -60,7 +60,7 @@ class ZHAConfigDashboardRouter extends HassRouterPage {
} else if (this._currentPage === "device") {
el.ieee = this.routeTail.path.substr(1);
} else if (this._currentPage === "visualization") {
el.zoomedDeviceIdFromURL = this.routeTail.path.substr(1);
el.zoomedDeviceId = this.routeTail.path.substr(1);
}
const searchParams = new URLSearchParams(window.location.search);

View File

@@ -37,10 +37,7 @@ export class ZHANetworkVisualizationPage extends LitElement {
@property({ type: Boolean }) public isWide!: boolean;
@property()
public zoomedDeviceIdFromURL?: string;
@state()
private zoomedDeviceId?: string;
public zoomedDeviceId?: string;
@query("#visualization", true)
private _visualization?: HTMLElement;
@@ -67,11 +64,6 @@ export class ZHANetworkVisualizationPage extends LitElement {
protected firstUpdated(changedProperties: PropertyValues): void {
super.firstUpdated(changedProperties);
// prevent zoomedDeviceIdFromURL from being restored to zoomedDeviceId after the user clears it
if (this.zoomedDeviceIdFromURL) {
this.zoomedDeviceId = this.zoomedDeviceIdFromURL;
}
if (this.hass) {
this._fetchData();
}

View File

@@ -1,8 +1,7 @@
import {
mdiContentDuplicate,
mdiDelete,
mdiHelpCircle,
mdiInformationOutline,
mdiPencil,
mdiPencilOff,
mdiPlay,
mdiPlus,
@@ -10,40 +9,25 @@ import {
import "@polymer/paper-tooltip/paper-tooltip";
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
import { customElement, property, state } from "lit/decorators";
import { ifDefined } from "lit/directives/if-defined";
import memoizeOne from "memoize-one";
import { fireEvent, HASSDomEvent } from "../../../common/dom/fire_event";
import { fireEvent } from "../../../common/dom/fire_event";
import { computeStateName } from "../../../common/entity/compute_state_name";
import { navigate } from "../../../common/navigate";
import {
DataTableColumnContainer,
RowClickedEvent,
} from "../../../components/data-table/ha-data-table";
import { DataTableColumnContainer } from "../../../components/data-table/ha-data-table";
import "../../../components/ha-button-related-filter-menu";
import "../../../components/ha-fab";
import "../../../components/ha-icon-button";
import "../../../components/ha-state-icon";
import "../../../components/ha-svg-icon";
import "../../../components/ha-icon-overflow-menu";
import { forwardHaptic } from "../../../data/haptics";
import {
activateScene,
deleteScene,
getSceneConfig,
SceneEntity,
showSceneEditor,
} from "../../../data/scene";
import {
showAlertDialog,
showConfirmationDialog,
} from "../../../dialogs/generic/show-dialog-box";
import { activateScene, SceneEntity } from "../../../data/scene";
import { showAlertDialog } from "../../../dialogs/generic/show-dialog-box";
import "../../../layouts/hass-tabs-subpage-data-table";
import { haStyle } from "../../../resources/styles";
import { HomeAssistant, Route } from "../../../types";
import { documentationUrl } from "../../../util/documentation-url";
import { showToast } from "../../../util/toast";
import { configSections } from "../ha-panel-config";
import { formatDateTime } from "../../../common/datetime/format_date_time";
import { UNAVAILABLE_STATES } from "../../../data/entity";
@customElement("ha-scene-dashboard")
class HaSceneDashboard extends LitElement {
@@ -80,111 +64,88 @@ class HaSceneDashboard extends LitElement {
);
private _columns = memoizeOne(
(_language, narrow): DataTableColumnContainer => {
const columns: DataTableColumnContainer = {
icon: {
title: "",
label: this.hass.localize(
"ui.panel.config.scene.picker.headers.state"
),
type: "icon",
template: (_, scene) =>
html` <ha-state-icon .state=${scene}></ha-state-icon> `,
},
name: {
title: this.hass.localize(
"ui.panel.config.scene.picker.headers.name"
),
sortable: true,
filterable: true,
direction: "asc",
grows: true,
},
};
if (!narrow) {
columns.state = {
title: this.hass.localize(
"ui.panel.config.scene.picker.headers.last_activated"
),
sortable: true,
width: "30%",
template: (last_activated) => html`
${last_activated && !UNAVAILABLE_STATES.includes(last_activated)
? formatDateTime(new Date(last_activated), this.hass.locale)
: this.hass.localize("ui.components.relative_time.never")}
`,
};
}
columns.only_editable = {
(_language): DataTableColumnContainer => ({
activate: {
title: "",
width: "56px",
template: (_info, scene: any) =>
!scene.attributes.id
label: this.hass.localize(
"ui.panel.config.scene.picker.headers.activate"
),
type: "icon-button",
template: (_toggle, scene) =>
html`
<ha-icon-button
.scene=${scene}
.label=${this.hass.localize(
"ui.panel.config.scene.picker.activate_scene"
)}
.path=${mdiPlay}
@click=${this._activateScene}
></ha-icon-button>
`,
},
icon: {
title: "",
label: this.hass.localize("ui.panel.config.scene.picker.headers.state"),
type: "icon",
template: (_, scene) =>
html` <ha-state-icon .state=${scene}></ha-state-icon> `,
},
name: {
title: this.hass.localize("ui.panel.config.scene.picker.headers.name"),
sortable: true,
filterable: true,
direction: "asc",
grows: true,
},
info: {
title: "",
label: this.hass.localize(
"ui.panel.config.scene.picker.headers.show_info"
),
type: "icon-button",
template: (_info, scene) => html`
<ha-icon-button
.scene=${scene}
@click=${this._showInfo}
.label=${this.hass.localize(
"ui.panel.config.scene.picker.show_info_scene"
)}
.path=${mdiInformationOutline}
></ha-icon-button>
`,
},
edit: {
title: "",
label: this.hass.localize("ui.panel.config.scene.picker.headers.edit"),
type: "icon-button",
template: (_info, scene: any) => html`
<a
href=${ifDefined(
scene.attributes.id
? `/config/scene/edit/${scene.attributes.id}`
: undefined
)}
>
<ha-icon-button
.disabled=${!scene.attributes.id}
.label=${this.hass.localize(
"ui.panel.config.scene.picker.edit_scene"
)}
.path=${scene.attributes.id ? mdiPencil : mdiPencilOff}
></ha-icon-button>
</a>
${!scene.attributes.id
? html`
<paper-tooltip animation-delay="0" position="left">
${this.hass.localize(
"ui.panel.config.scene.picker.only_editable"
)}
</paper-tooltip>
<ha-svg-icon
.path=${mdiPencilOff}
style="color: var(--secondary-text-color)"
></ha-svg-icon>
`
: "",
};
columns.actions = {
title: "",
width: "72px",
type: "overflow-menu",
template: (_: string, scene: any) =>
html`
<ha-icon-overflow-menu
.hass=${this.hass}
narrow
.items=${[
{
path: mdiInformationOutline,
label: this.hass.localize(
"ui.panel.config.scene.picker.show_info"
),
action: () => this._showInfo(scene),
},
{
path: mdiPlay,
label: this.hass.localize(
"ui.panel.config.scene.picker.activate"
),
action: () => this._activateScene(scene),
},
{
divider: true,
},
{
path: mdiContentDuplicate,
label: this.hass.localize(
"ui.panel.config.scene.picker.duplicate"
),
action: () => this._duplicate(scene),
disabled: !scene.attributes.id,
},
{
label: this.hass.localize(
"ui.panel.config.scene.picker.delete"
),
path: mdiDelete,
action: () => this._deleteConfirm(scene),
warning: scene.attributes.id,
disabled: !scene.attributes.id,
},
]}
>
</ha-icon-overflow-menu>
`,
};
return columns;
}
: ""}
`,
},
})
);
protected render(): TemplateResult {
@@ -195,7 +156,7 @@ class HaSceneDashboard extends LitElement {
back-path="/config"
.route=${this.route}
.tabs=${configSections.automations}
.columns=${this._columns(this.hass.locale, this.narrow)}
.columns=${this._columns(this.hass.language)}
id="entity_id"
.data=${this._scenes(this.scenes, this._filteredScenes)}
.activeFilters=${this._activeFilters}
@@ -204,8 +165,6 @@ class HaSceneDashboard extends LitElement {
)}
@clear-filter=${this._clearFilter}
hasFab
clickable
@row-click=${this._handleRowClicked}
>
<ha-icon-button
slot="toolbar-icon"
@@ -237,14 +196,6 @@ class HaSceneDashboard extends LitElement {
`;
}
private _handleRowClicked(ev: HASSDomEvent<RowClickedEvent>) {
const scene = this.scenes.find((a) => a.entity_id === ev.detail.id);
if (scene?.attributes.id) {
navigate(`/config/scene/edit/${scene?.attributes.id}`);
}
}
private _relatedFilterChanged(ev: CustomEvent) {
this._filterValue = ev.detail.value;
if (!this._filterValue) {
@@ -261,11 +212,15 @@ class HaSceneDashboard extends LitElement {
this._filterValue = undefined;
}
private _showInfo(scene: SceneEntity) {
fireEvent(this, "hass-more-info", { entityId: scene.entity_id });
private _showInfo(ev) {
ev.stopPropagation();
const entityId = ev.currentTarget.scene.entity_id;
fireEvent(this, "hass-more-info", { entityId });
}
private _activateScene = async (scene: SceneEntity) => {
private _activateScene = async (ev) => {
ev.stopPropagation();
const scene = ev.currentTarget.scene as SceneEntity;
await activateScene(this.hass, scene.entity_id);
showToast(this, {
message: this.hass.localize(
@@ -277,34 +232,6 @@ class HaSceneDashboard extends LitElement {
forwardHaptic("light");
};
private _deleteConfirm(scene: SceneEntity): void {
showConfirmationDialog(this, {
text: this.hass!.localize("ui.panel.config.scene.picker.delete_confirm"),
confirmText: this.hass!.localize("ui.common.delete"),
dismissText: this.hass!.localize("ui.common.cancel"),
confirm: () => this._delete(scene),
});
}
private async _delete(scene: SceneEntity): Promise<void> {
if (scene.attributes.id) {
await deleteScene(this.hass, scene.attributes.id);
}
}
private async _duplicate(scene) {
if (scene.attributes.id) {
const config = await getSceneConfig(this.hass, scene.attributes.id);
showSceneEditor({
...config,
id: undefined,
name: `${config?.name} (${this.hass.localize(
"ui.panel.config.scene.picker.duplicate"
)})`,
});
}
}
private _showHelp() {
showAlertDialog(this, {
title: this.hass.localize("ui.panel.config.scene.picker.header"),

View File

@@ -29,7 +29,6 @@ import { computeRTL } from "../../../common/util/compute_rtl";
import "../../../components/device/ha-device-picker";
import "../../../components/entity/ha-entities-picker";
import "../../../components/ha-area-picker";
import "../../../components/ha-button-menu";
import "../../../components/ha-card";
import "../../../components/ha-fab";
import "../../../components/ha-icon-button";
@@ -64,13 +63,13 @@ import {
showAlertDialog,
showConfirmationDialog,
} from "../../../dialogs/generic/show-dialog-box";
import "../../../layouts/hass-subpage";
import { KeyboardShortcutMixin } from "../../../mixins/keyboard-shortcut-mixin";
import { SubscribeMixin } from "../../../mixins/subscribe-mixin";
import { haStyle } from "../../../resources/styles";
import { HomeAssistant, Route } from "../../../types";
import { showToast } from "../../../util/toast";
import "../ha-config-section";
import { configSections } from "../ha-panel-config";
interface DeviceEntities {
id: string;
@@ -215,16 +214,17 @@ export class HaSceneEditor extends SubscribeMixin(
this._deviceEntityLookup,
this._deviceRegistryEntries
);
const name = this._scene
? computeStateName(this._scene)
: this.hass.localize("ui.panel.config.scene.editor.default_name");
return html`
<hass-subpage
<hass-tabs-subpage
.hass=${this.hass}
.narrow=${this.narrow}
.route=${this.route}
.backCallback=${this._backTapped}
.header=${this._scene
? computeStateName(this._scene)
: this.hass.localize("ui.panel.config.scene.editor.default_name")}
.tabs=${configSections.automations}
>
<ha-button-menu
corner="BOTTOM_START"
@@ -272,6 +272,7 @@ export class HaSceneEditor extends SubscribeMixin(
</mwc-list-item>
</ha-button-menu>
${this._errors ? html` <div class="errors">${this._errors}</div> ` : ""}
${this.narrow ? html` <span slot="header">${name}</span> ` : ""}
<div
id="root"
class=${classMap({
@@ -280,12 +281,15 @@ export class HaSceneEditor extends SubscribeMixin(
>
${this._config
? html`
<div
class=${classMap({
container: true,
narrow: !this.isWide,
})}
>
<ha-config-section vertical .isWide=${this.isWide}>
${!this.narrow
? html` <span slot="header">${name}</span> `
: ""}
<div slot="introduction">
${this.hass.localize(
"ui.panel.config.scene.editor.introduction"
)}
</div>
<ha-card outlined>
<div class="card-content">
<ha-textfield
@@ -318,7 +322,7 @@ export class HaSceneEditor extends SubscribeMixin(
</ha-area-picker>
</div>
</ha-card>
</div>
</ha-config-section>
<ha-config-section vertical .isWide=${this.isWide}>
<div slot="header">
@@ -482,7 +486,7 @@ export class HaSceneEditor extends SubscribeMixin(
>
<ha-svg-icon slot="icon" .path=${mdiContentSave}></ha-svg-icon>
</ha-fab>
</hass-subpage>
</hass-tabs-subpage>
`;
}
@@ -959,14 +963,6 @@ export class HaSceneEditor extends SubscribeMixin(
ha-card {
overflow: hidden;
}
.container {
padding: 28px 20px 0;
max-width: 1040px;
margin: 0 auto;
}
.narrow.container {
max-width: 640px;
}
.errors {
padding: 20px;
font-weight: bold;

View File

@@ -45,14 +45,13 @@ export class HaBlueprintScriptEditor extends LitElement {
protected render() {
const blueprint = this._blueprint;
return html`
<ha-card
outlined
class="blueprint"
.header=${this.hass.localize(
return html` <ha-config-section vertical .isWide=${this.isWide}>
<span slot="header"
>${this.hass.localize(
"ui.panel.config.automation.editor.blueprint.header"
)}
)}</span
>
<ha-card outlined>
<div class="blueprint-picker-container">
${this._blueprints
? Object.keys(this._blueprints).length
@@ -119,7 +118,7 @@ export class HaBlueprintScriptEditor extends LitElement {
</p>`}`
: ""}
</ha-card>
`;
</ha-config-section>`;
}
private async _getBlueprints() {
@@ -174,50 +173,22 @@ export class HaBlueprintScriptEditor extends LitElement {
return [
haStyle,
css`
:host {
display: block;
}
ha-card.blueprint {
margin: 0 auto;
}
.padding {
padding: 16px;
}
.link-button-row {
padding: 14px;
}
.blueprint-picker-container {
padding: 0 16px 16px;
}
ha-textfield,
ha-blueprint-picker {
display: block;
}
h3 {
margin: 16px;
}
.introduction {
margin-top: 0;
margin-bottom: 12px;
}
.introduction a {
color: var(--primary-color);
padding: 16px;
}
p {
margin-bottom: 0;
}
.description {
margin-bottom: 16px;
}
ha-settings-row {
--paper-time-input-justify-content: flex-end;
--settings-row-content-width: 100%;
--settings-row-prefix-display: contents;
border-top: 1px solid var(--divider-color);
}
ha-alert {
margin-bottom: 16px;
display: block;
:host(:not([narrow])) ha-settings-row ha-textfield,
:host(:not([narrow])) ha-settings-row ha-selector {
width: 60%;
}
`,
];

View File

@@ -1,3 +1,4 @@
import type { ActionDetail } from "@material/mwc-list/mwc-list-foundation";
import "@material/mwc-list/mwc-list-item";
import {
mdiCheck,
@@ -5,10 +6,7 @@ import {
mdiContentSave,
mdiDelete,
mdiDotsVertical,
mdiInformationOutline,
mdiPlay,
mdiSort,
mdiTransitConnection,
mdiHelpCircle,
} from "@mdi/js";
import "@polymer/app-layout/app-header/app-header";
import "@polymer/app-layout/app-toolbar/app-toolbar";
@@ -20,10 +18,9 @@ import {
PropertyValues,
TemplateResult,
} from "lit";
import { property, query, state } from "lit/decorators";
import { property, state, query } from "lit/decorators";
import { classMap } from "lit/directives/class-map";
import memoizeOne from "memoize-one";
import { fireEvent } from "../../../common/dom/fire_event";
import { computeObjectId } from "../../../common/entity/compute_object_id";
import { navigate } from "../../../common/navigate";
import { slugify } from "../../../common/string/slugify";
@@ -41,10 +38,11 @@ import "../../../components/ha-svg-icon";
import "../../../components/ha-yaml-editor";
import type { HaYamlEditor } from "../../../components/ha-yaml-editor";
import {
Action,
deleteScript,
getScriptConfig,
getScriptEditorInitData,
isMaxMode,
ManualScriptConfig,
MODES,
MODES_MAX,
ScriptConfig,
@@ -53,16 +51,14 @@ import {
} from "../../../data/script";
import { showConfirmationDialog } from "../../../dialogs/generic/show-dialog-box";
import "../../../layouts/ha-app-layout";
import "../../../layouts/hass-subpage";
import { KeyboardShortcutMixin } from "../../../mixins/keyboard-shortcut-mixin";
import { haStyle } from "../../../resources/styles";
import type { HomeAssistant, Route } from "../../../types";
import { documentationUrl } from "../../../util/documentation-url";
import { showToast } from "../../../util/toast";
import { HaDeviceAction } from "../automation/action/types/ha-automation-action-device_id";
import { configSections } from "../ha-panel-config";
import "./blueprint-script-editor";
import "./manual-script-editor";
import type { HaManualScriptEditor } from "./manual-script-editor";
export class HaScriptEditor extends KeyboardShortcutMixin(LitElement) {
@property({ attribute: false }) public hass!: HomeAssistant;
@@ -87,10 +83,7 @@ export class HaScriptEditor extends KeyboardShortcutMixin(LitElement) {
@state() private _mode: "gui" | "yaml" = "gui";
@query("ha-yaml-editor", true) private _yamlEditor?: HaYamlEditor;
@query("manual-script-editor")
private _manualEditor?: HaManualScriptEditor;
@query("ha-yaml-editor", true) private _editor?: HaYamlEditor;
private _schema = memoizeOne(
(
@@ -175,91 +168,31 @@ export class HaScriptEditor extends KeyboardShortcutMixin(LitElement) {
};
return html`
<hass-subpage
<hass-tabs-subpage
.hass=${this.hass}
.narrow=${this.narrow}
.route=${this.route}
.backCallback=${this._backTapped}
.header=${!this._config?.alias ? "" : this._config.alias}
.tabs=${configSections.automations}
>
${this.scriptEntityId && !this.narrow
? html`
<mwc-button @click=${this._showTrace} slot="toolbar-icon">
${this.hass.localize(
"ui.panel.config.script.editor.show_trace"
)}
</mwc-button>
`
: ""}
<ha-button-menu corner="BOTTOM_START" slot="toolbar-icon">
<ha-button-menu
corner="BOTTOM_START"
slot="toolbar-icon"
@action=${this._handleMenuAction}
activatable
>
<ha-icon-button
slot="trigger"
.label=${this.hass.localize("ui.common.menu")}
.path=${mdiDotsVertical}
></ha-icon-button>
<mwc-list-item
graphic="icon"
.disabled=${!this.scriptEntityId}
@click=${this._showInfo}
>
${this.hass.localize("ui.panel.config.script.editor.show_info")}
<ha-svg-icon
slot="graphic"
.path=${mdiInformationOutline}
></ha-svg-icon>
</mwc-list-item>
<mwc-list-item
graphic="icon"
.disabled=${!this.scriptEntityId}
@click=${this._runScript}
>
${this.hass.localize("ui.panel.config.script.picker.run_script")}
<ha-svg-icon slot="graphic" .path=${mdiPlay}></ha-svg-icon>
</mwc-list-item>
${this.scriptEntityId && this.narrow
? html`
<a href="/config/script/trace/${this.scriptEntityId}">
<mwc-list-item graphic="icon">
${this.hass.localize(
"ui.panel.config.script.editor.show_trace"
)}
<ha-svg-icon
slot="graphic"
.path=${mdiTransitConnection}
></ha-svg-icon>
</mwc-list-item>
</a>
`
: ""}
${this._config && !("use_blueprint" in this._config)
? html`
<mwc-list-item
aria-label=${this.hass.localize(
"ui.panel.config.automation.editor.re_order"
)}
graphic="icon"
.disabled=${this._mode !== "gui"}
@click=${this._toggleReOrderMode}
>
${this.hass.localize(
"ui.panel.config.automation.editor.re_order"
)}
<ha-svg-icon slot="graphic" .path=${mdiSort}></ha-svg-icon>
</mwc-list-item>
`
: ""}
<li divider role="separator"></li>
<mwc-list-item
aria-label=${this.hass.localize(
"ui.panel.config.automation.editor.edit_ui"
)}
graphic="icon"
@click=${this._switchUiMode}
?activated=${this._mode === "gui"}
>
${this.hass.localize("ui.panel.config.automation.editor.edit_ui")}
${this._mode === "gui"
@@ -277,7 +210,7 @@ export class HaScriptEditor extends KeyboardShortcutMixin(LitElement) {
"ui.panel.config.automation.editor.edit_yaml"
)}
graphic="icon"
@click=${this._switchYamlMode}
?activated=${this._mode === "yaml"}
>
${this.hass.localize("ui.panel.config.automation.editor.edit_yaml")}
${this._mode === "yaml"
@@ -296,12 +229,13 @@ export class HaScriptEditor extends KeyboardShortcutMixin(LitElement) {
<mwc-list-item
.disabled=${!this.scriptEntityId}
.label=${this.hass.localize(
"ui.panel.config.script.picker.duplicate"
"ui.panel.config.script.picker.duplicate_script"
)}
graphic="icon"
@click=${this._duplicate}
>
${this.hass.localize("ui.panel.config.script.picker.duplicate")}
${this.hass.localize(
"ui.panel.config.script.picker.duplicate_script"
)}
<ha-svg-icon
slot="graphic"
.path=${mdiContentDuplicate}
@@ -311,13 +245,12 @@ export class HaScriptEditor extends KeyboardShortcutMixin(LitElement) {
<mwc-list-item
.disabled=${!this.scriptEntityId}
aria-label=${this.hass.localize(
"ui.panel.config.script.picker.delete"
"ui.panel.config.script.editor.delete_script"
)}
class=${classMap({ warning: Boolean(this.scriptEntityId) })}
graphic="icon"
@click=${this._deleteConfirm}
>
${this.hass.localize("ui.panel.config.script.picker.delete")}
${this.hass.localize("ui.panel.config.script.editor.delete_script")}
<ha-svg-icon
class=${classMap({ warning: Boolean(this.scriptEntityId) })}
slot="graphic"
@@ -326,6 +259,9 @@ export class HaScriptEditor extends KeyboardShortcutMixin(LitElement) {
</ha-svg-icon>
</mwc-list-item>
</ha-button-menu>
${this.narrow
? html`<span slot="header">${this._config?.alias}</span>`
: ""}
<div
class="content ${classMap({
"yaml-mode": this._mode === "yaml",
@@ -341,20 +277,47 @@ export class HaScriptEditor extends KeyboardShortcutMixin(LitElement) {
>
${this._config
? html`
<div class="config-container">
<ha-card outlined>
<div class="card-content">
<ha-form
.schema=${schema}
.data=${data}
.hass=${this.hass}
.computeLabel=${this._computeLabelCallback}
.computeHelper=${this._computeHelperCallback}
@value-changed=${this._valueChanged}
></ha-form>
</div>
</ha-card>
</div>
<ha-card outlined>
<div class="card-content">
<ha-form
.schema=${schema}
.data=${data}
.hass=${this.hass}
.computeLabel=${this._computeLabelCallback}
.computeHelper=${this._computeHelperCallback}
@value-changed=${this._valueChanged}
></ha-form>
</div>
${this.scriptEntityId
? html`
<div
class="card-actions layout horizontal justified center"
>
<a
href="/config/script/trace/${this
.scriptEntityId}"
>
<mwc-button>
${this.hass.localize(
"ui.panel.config.script.editor.show_trace"
)}
</mwc-button>
</a>
<mwc-button
@click=${this._runScript}
title=${this.hass.localize(
"ui.panel.config.script.picker.run_script"
)}
?disabled=${this._dirty}
>
${this.hass.localize(
"ui.panel.config.script.picker.run_script"
)}
</mwc-button>
</div>
`
: ``}
</ha-card>
${"use_blueprint" in this._config
? html`
@@ -367,13 +330,34 @@ export class HaScriptEditor extends KeyboardShortcutMixin(LitElement) {
></blueprint-script-editor>
`
: html`
<manual-script-editor
<div class="header">
<div class="name">
${this.hass.localize(
"ui.panel.config.script.editor.sequence"
)}
</div>
<a
href=${documentationUrl(
this.hass,
"/docs/scripts/"
)}
target="_blank"
rel="noreferrer"
>
<ha-icon-button
.path=${mdiHelpCircle}
.label=${this.hass.localize(
"ui.panel.config.script.editor.link_available_actions"
)}
></ha-icon-button>
</a>
</div>
<ha-automation-action
.actions=${this._config.sequence}
@value-changed=${this._sequenceChanged}
.hass=${this.hass}
.narrow=${this.narrow}
.isWide=${this.isWide}
.config=${this._config}
@value-changed=${this._configChanged}
></manual-script-editor>
></ha-automation-action>
`}
`
: ""}
@@ -381,6 +365,28 @@ export class HaScriptEditor extends KeyboardShortcutMixin(LitElement) {
`
: this._mode === "yaml"
? html`
${!this.narrow
? html`
<ha-card outlined>
<div class="card-header">${this._config?.alias}</div>
<div
class="card-actions layout horizontal justified center"
>
<mwc-button
@click=${this._runScript}
title=${this.hass.localize(
"ui.panel.config.script.picker.run_script"
)}
?disabled=${this._dirty}
>
${this.hass.localize(
"ui.panel.config.script.picker.run_script"
)}
</mwc-button>
</div>
</ha-card>
`
: ``}
<ha-yaml-editor
.hass=${this.hass}
.defaultValue=${this._preprocessYaml()}
@@ -411,7 +417,7 @@ export class HaScriptEditor extends KeyboardShortcutMixin(LitElement) {
>
<ha-svg-icon slot="icon" .path=${mdiContentSave}></ha-svg-icon>
</ha-fab>
</hass-subpage>
</hass-tabs-subpage>
`;
}
@@ -426,32 +432,37 @@ export class HaScriptEditor extends KeyboardShortcutMixin(LitElement) {
// Only refresh config if we picked a new script. If same ID, don't fetch it.
(!oldScript || oldScript !== this.scriptEntityId)
) {
getScriptConfig(this.hass, computeObjectId(this.scriptEntityId)).then(
(config) => {
// Normalize data: ensure sequence is a list
// Happens when people copy paste their scripts into the config
const value = config.sequence;
if (value && !Array.isArray(value)) {
config.sequence = [value];
this.hass
.callApi<ManualScriptConfig>(
"GET",
`config/script/config/${computeObjectId(this.scriptEntityId)}`
)
.then(
(config) => {
// Normalize data: ensure sequence is a list
// Happens when people copy paste their scripts into the config
const value = config.sequence;
if (value && !Array.isArray(value)) {
config.sequence = [value];
}
this._dirty = false;
this._config = config;
},
(resp) => {
alert(
resp.status_code === 404
? this.hass.localize(
"ui.panel.config.script.editor.load_error_not_editable"
)
: this.hass.localize(
"ui.panel.config.script.editor.load_error_unknown",
"err_no",
resp.status_code
)
);
history.back();
}
this._dirty = false;
this._config = config;
},
(resp) => {
alert(
resp.status_code === 404
? this.hass.localize(
"ui.panel.config.script.editor.load_error_not_editable"
)
: this.hass.localize(
"ui.panel.config.script.editor.load_error_unknown",
"err_no",
resp.status_code
)
);
history.back();
}
);
);
}
if (
@@ -517,22 +528,6 @@ export class HaScriptEditor extends KeyboardShortcutMixin(LitElement) {
return undefined;
};
private async _showInfo() {
if (!this.scriptEntityId) {
return;
}
fireEvent(this, "hass-more-info", { entityId: this.scriptEntityId });
}
private async _showTrace() {
if (this.scriptEntityId) {
const result = await this.confirmUnsavedChanged();
if (result) {
navigate(`/config/script/trace/${this.scriptEntityId}`);
}
}
}
private async _runScript(ev: CustomEvent) {
ev.stopPropagation();
await triggerScript(this.hass, this.scriptEntityId as string);
@@ -546,12 +541,6 @@ export class HaScriptEditor extends KeyboardShortcutMixin(LitElement) {
}
private _modeChanged(mode) {
const curMode = this._config!.mode || MODES[0];
if (mode === curMode) {
return;
}
this._config = { ...this._config!, mode };
if (!isMaxMode(mode)) {
delete this._config.max;
@@ -591,7 +580,6 @@ export class HaScriptEditor extends KeyboardShortcutMixin(LitElement) {
ev.stopPropagation();
const values = ev.detail.value as any;
const currentId = this._entityId;
let changed = false;
for (const key of Object.keys(values)) {
if (key === "sequence") {
@@ -607,32 +595,27 @@ export class HaScriptEditor extends KeyboardShortcutMixin(LitElement) {
continue;
}
changed = true;
switch (key) {
case "id":
this._idChanged(value);
break;
return;
case "alias":
this._aliasChanged(value);
break;
case "mode":
this._modeChanged(value);
break;
return;
}
if (values[key] === undefined) {
const newConfig = { ...this._config! };
delete newConfig![key];
this._config = newConfig;
delete this._config![key];
this._config = { ...this._config! };
} else {
this._config = { ...this._config!, [key]: value };
}
}
if (changed) {
this._dirty = true;
}
this._dirty = true;
}
private _configChanged(ev) {
@@ -640,13 +623,22 @@ export class HaScriptEditor extends KeyboardShortcutMixin(LitElement) {
this._dirty = true;
}
private _sequenceChanged(ev: CustomEvent): void {
this._config = {
...this._config!,
sequence: ev.detail.value as Action[],
};
this._errors = undefined;
this._dirty = true;
}
private _preprocessYaml() {
return this._config;
}
private async _copyYaml(): Promise<void> {
if (this._yamlEditor?.yaml) {
await copyToClipboard(this._yamlEditor.yaml);
if (this._editor?.yaml) {
await copyToClipboard(this._editor.yaml);
showToast(this, {
message: this.hass.localize("ui.common.copied_clipboard"),
});
@@ -663,36 +655,45 @@ export class HaScriptEditor extends KeyboardShortcutMixin(LitElement) {
this._dirty = true;
}
private async confirmUnsavedChanged(): Promise<boolean> {
private _backTapped = (): void => {
if (this._dirty) {
return showConfirmationDialog(this, {
showConfirmationDialog(this, {
text: this.hass!.localize(
"ui.panel.config.automation.editor.unsaved_confirm"
"ui.panel.config.common.editor.confirm_unsaved"
),
confirmText: this.hass!.localize("ui.common.leave"),
dismissText: this.hass!.localize("ui.common.stay"),
confirm: () => {
setTimeout(() => history.back());
},
});
}
return true;
}
private _backTapped = async () => {
const result = await this.confirmUnsavedChanged();
if (result) {
} else {
history.back();
}
};
private async _duplicate() {
const result = await this.confirmUnsavedChanged();
if (result) {
showScriptEditor({
...this._config,
alias: `${this._config?.alias} (${this.hass.localize(
"ui.panel.config.script.picker.duplicate"
)})`,
});
if (this._dirty) {
if (
!(await showConfirmationDialog(this, {
text: this.hass!.localize(
"ui.panel.config.common.editor.confirm_unsaved"
),
confirmText: this.hass!.localize("ui.common.yes"),
dismissText: this.hass!.localize("ui.common.no"),
}))
) {
return;
}
// Wait for dialog to complete closing
await new Promise((resolve) => setTimeout(resolve, 0));
}
showScriptEditor({
...this._config,
alias: `${this._config?.alias} (${this.hass.localize(
"ui.panel.config.script.picker.duplicate"
)})`,
});
}
private async _deleteConfirm() {
@@ -712,17 +713,20 @@ export class HaScriptEditor extends KeyboardShortcutMixin(LitElement) {
history.back();
}
private _switchUiMode() {
this._mode = "gui";
}
private _switchYamlMode() {
this._mode = "yaml";
}
private _toggleReOrderMode() {
if (this._manualEditor) {
this._manualEditor.reOrderMode = !this._manualEditor.reOrderMode;
private async _handleMenuAction(ev: CustomEvent<ActionDetail>) {
switch (ev.detail.index) {
case 0:
this._mode = "gui";
break;
case 1:
this._mode = "yaml";
break;
case 2:
this._duplicate();
break;
case 3:
this._deleteConfirm();
break;
}
}
@@ -781,19 +785,15 @@ export class HaScriptEditor extends KeyboardShortcutMixin(LitElement) {
font-weight: bold;
color: var(--error-color);
}
.content {
padding: 16px 16px 20px;
}
.yaml-mode {
height: 100%;
display: flex;
flex-direction: column;
padding-bottom: 0;
}
.config-container,
manual-script-editor,
blueprint-script-editor {
margin: 0 auto;
max-width: 1040px;
padding: 28px 20px 0;
}
ha-yaml-editor {
flex-grow: 1;
--code-mirror-height: 100%;
@@ -831,13 +831,6 @@ export class HaScriptEditor extends KeyboardShortcutMixin(LitElement) {
font-weight: 400;
flex: 1;
}
.header a {
color: var(--secondary-text-color);
}
ha-button-menu a {
text-decoration: none;
color: var(--primary-color);
}
`,
];
}

View File

@@ -1,41 +1,26 @@
import {
mdiContentDuplicate,
mdiDelete,
mdiHelpCircle,
mdiHistory,
mdiInformationOutline,
mdiPencil,
mdiPlay,
mdiPlus,
mdiTransitConnection,
} from "@mdi/js";
import { HassEntity } from "home-assistant-js-websocket";
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
import { customElement, property, state } from "lit/decorators";
import memoizeOne from "memoize-one";
import { formatDateTime } from "../../../common/datetime/format_date_time";
import { fireEvent, HASSDomEvent } from "../../../common/dom/fire_event";
import { computeObjectId } from "../../../common/entity/compute_object_id";
import { fireEvent } from "../../../common/dom/fire_event";
import { computeStateName } from "../../../common/entity/compute_state_name";
import { navigate } from "../../../common/navigate";
import { computeRTL } from "../../../common/util/compute_rtl";
import {
DataTableColumnContainer,
RowClickedEvent,
} from "../../../components/data-table/ha-data-table";
import { DataTableColumnContainer } from "../../../components/data-table/ha-data-table";
import "../../../components/ha-button-related-filter-menu";
import "../../../components/ha-fab";
import "../../../components/ha-icon-button";
import "../../../components/ha-icon-overflow-menu";
import "../../../components/ha-svg-icon";
import {
deleteScript,
getScriptConfig,
showScriptEditor,
triggerScript,
} from "../../../data/script";
import {
showAlertDialog,
showConfirmationDialog,
} from "../../../dialogs/generic/show-dialog-box";
import { triggerScript } from "../../../data/script";
import { showAlertDialog } from "../../../dialogs/generic/show-dialog-box";
import "../../../layouts/hass-tabs-subpage-data-table";
import { haStyle } from "../../../resources/styles";
import { HomeAssistant, Route } from "../../../types";
@@ -82,6 +67,22 @@ class HaScriptPicker extends LitElement {
private _columns = memoizeOne((narrow, _locale): DataTableColumnContainer => {
const columns: DataTableColumnContainer = {
activate: {
title: "",
label: this.hass.localize("ui.panel.config.script.picker.run_script"),
type: "icon-button",
template: (_toggle, script) =>
html`
<ha-icon-button
.script=${script}
.label=${this.hass.localize(
"ui.panel.config.script.picker.run_script"
)}
@click=${this._runScript}
.path=${mdiPlay}
></ha-icon-button>
`,
},
icon: {
title: "",
label: this.hass.localize(
@@ -116,7 +117,7 @@ class HaScriptPicker extends LitElement {
if (!narrow) {
columns.last_triggered = {
sortable: true,
width: "40%",
width: "20%",
title: this.hass.localize("ui.card.automation.last_triggered"),
template: (last_triggered) => html`
${last_triggered
@@ -125,60 +126,51 @@ class HaScriptPicker extends LitElement {
`,
};
}
columns.actions = {
columns.info = {
title: "",
width: this.narrow ? undefined : "10%",
type: "overflow-menu",
template: (_: string, script: any) =>
html`
<ha-icon-overflow-menu
.hass=${this.hass}
narrow
.items=${[
{
path: mdiInformationOutline,
label: this.hass.localize(
"ui.panel.config.script.picker.show_info"
),
action: () => this._showInfo(script),
},
{
path: mdiPlay,
label: this.hass.localize("ui.panel.config.script.picker.run"),
action: () => this._runScript(script),
},
{
path: mdiTransitConnection,
label: this.hass.localize(
"ui.panel.config.script.picker.show_trace"
),
action: () => this._showTrace(script),
},
{
divider: true,
},
{
path: mdiContentDuplicate,
label: this.hass.localize(
"ui.panel.config.script.picker.duplicate"
),
action: () => this._duplicate(script),
},
{
label: this.hass.localize(
"ui.panel.config.script.picker.delete"
),
path: mdiDelete,
action: () => this._deleteConfirm(script),
warning: true,
},
]}
>
</ha-icon-overflow-menu>
`,
label: this.hass.localize("ui.panel.config.script.picker.show_info"),
type: "icon-button",
template: (_info, script) => html`
<ha-icon-button
.script=${script}
@click=${this._showInfo}
.label=${this.hass.localize(
"ui.panel.config.script.picker.show_info"
)}
.path=${mdiInformationOutline}
></ha-icon-button>
`,
};
columns.trace = {
title: "",
label: this.hass.localize("ui.panel.config.script.picker.dev_script"),
type: "icon-button",
template: (_info, script: any) => html`
<a href="/config/script/trace/${script.entity_id}">
<ha-icon-button
.label=${this.hass.localize(
"ui.panel.config.script.picker.dev_script"
)}
.path=${mdiHistory}
></ha-icon-button>
</a>
`,
};
columns.edit = {
title: "",
label: this.hass.localize("ui.panel.config.script.picker.edit_script"),
type: "icon-button",
template: (_info, script: any) => html`
<a href="/config/script/edit/${script.entity_id}">
<ha-icon-button
.label=${this.hass.localize(
"ui.panel.config.script.picker.edit_script"
)}
.path=${mdiPencil}
></ha-icon-button>
</a>
`,
};
return columns;
});
@@ -199,8 +191,6 @@ class HaScriptPicker extends LitElement {
)}
@clear-filter=${this._clearFilter}
hasFab
clickable
@row-click=${this._handleRowClicked}
>
<ha-icon-button
slot="toolbar-icon"
@@ -251,11 +241,9 @@ class HaScriptPicker extends LitElement {
this._filterValue = undefined;
}
private _handleRowClicked(ev: HASSDomEvent<RowClickedEvent>) {
navigate(`/config/script/edit/${ev.detail.id}`);
}
private _runScript = async (script: any) => {
private _runScript = async (ev) => {
ev.stopPropagation();
const script = ev.currentTarget.script as HassEntity;
await triggerScript(this.hass, script.entity_id);
showToast(this, {
message: this.hass.localize(
@@ -266,12 +254,10 @@ class HaScriptPicker extends LitElement {
});
};
private _showInfo(script: any) {
fireEvent(this, "hass-more-info", { entityId: script.entity_id });
}
private _showTrace(script: any) {
navigate(`/config/script/trace/${script.entity_id}`);
private _showInfo(ev) {
ev.stopPropagation();
const entityId = ev.currentTarget.script.entity_id;
fireEvent(this, "hass-more-info", { entityId });
}
private _showHelp() {
@@ -292,62 +278,6 @@ class HaScriptPicker extends LitElement {
});
}
private async _duplicate(script: any) {
try {
const config = await getScriptConfig(
this.hass,
computeObjectId(script.entity_id)
);
showScriptEditor({
...config,
alias: `${config?.alias} (${this.hass.localize(
"ui.panel.config.script.picker.duplicate"
)})`,
});
} catch (err: any) {
await showAlertDialog(this, {
text:
err.status_code === 404
? this.hass.localize(
"ui.panel.config.script.editor.load_error_not_duplicable"
)
: this.hass.localize(
"ui.panel.config.script.editor.load_error_unknown",
"err_no",
err.status_code
),
});
}
}
private async _deleteConfirm(script: any) {
showConfirmationDialog(this, {
text: this.hass.localize("ui.panel.config.script.editor.delete_confirm"),
confirmText: this.hass!.localize("ui.common.delete"),
dismissText: this.hass!.localize("ui.common.cancel"),
confirm: () => this._delete(script),
});
}
private async _delete(script: any) {
try {
await deleteScript(this.hass, computeObjectId(script.entity_id));
} catch (err: any) {
await showAlertDialog(this, {
text:
err.status_code === 400
? this.hass.localize(
"ui.panel.config.script.editor.load_error_not_deletable"
)
: this.hass.localize(
"ui.panel.config.script.editor.load_error_unknown",
"err_no",
err.status_code
),
});
}
}
static get styles(): CSSResultGroup {
return [
haStyle,

View File

@@ -1,7 +1,5 @@
import {
mdiDotsVertical,
mdiDownload,
mdiInformationOutline,
mdiPencil,
mdiRayEndArrow,
mdiRayStartArrow,
@@ -36,9 +34,7 @@ import {
import { showAlertDialog } from "../../../dialogs/generic/show-dialog-box";
import { haStyle } from "../../../resources/styles";
import { HomeAssistant, Route } from "../../../types";
import "../../../layouts/hass-subpage";
import "../../../components/ha-button-menu";
import { fireEvent } from "../../../common/dom/fire_event";
import { configSections } from "../ha-panel-config";
@customElement("ha-script-trace")
export class HaScriptTrace extends LitElement {
@@ -92,113 +88,81 @@ export class HaScriptTrace extends LitElement {
</div>`;
}
const actionButtons = html`
<ha-icon-button
label="Refresh"
@click=${this._refreshTraces}
.path=${mdiRefresh}
></ha-icon-button>
<ha-icon-button
.disabled=${!this._trace}
label="Download Trace"
@click=${this._downloadTrace}
.path=${mdiDownload}
></ha-icon-button>
`;
return html`
${devButtons}
<hass-subpage .hass=${this.hass} .narrow=${this.narrow} .header=${title}>
${!this.narrow && this.scriptEntityId
? html`
<a
class="trace-link"
href="/config/script/edit/${this.scriptEntityId}"
slot="toolbar-icon"
>
<mwc-button>
${this.hass.localize(
"ui.panel.config.script.trace.edit_script"
)}
</mwc-button>
</a>
`
<hass-tabs-subpage
.hass=${this.hass}
.narrow=${this.narrow}
.route=${this.route}
.tabs=${configSections.automations}
>
${this.narrow
? html`<span slot="header"> ${title} </span>
<div slot="toolbar-icon">${actionButtons}</div>`
: ""}
<ha-button-menu corner="BOTTOM_START" slot="toolbar-icon">
<ha-icon-button
slot="trigger"
.label=${this.hass.localize("ui.common.menu")}
.path=${mdiDotsVertical}
></ha-icon-button>
<mwc-list-item
graphic="icon"
.disabled=${!this.scriptEntityId}
@click=${this._showInfo}
>
${this.hass.localize("ui.panel.config.script.editor.show_info")}
<ha-svg-icon
slot="graphic"
.path=${mdiInformationOutline}
></ha-svg-icon>
</mwc-list-item>
${this.narrow && this.scriptEntityId
? html`
<div class="toolbar">
${!this.narrow
? html`<div>
${title}
<a
class="trace-link"
class="linkButton"
href="/config/script/edit/${this.scriptEntityId}"
>
<mwc-list-item graphic="icon">
${this.hass.localize(
"ui.panel.config.script.trace.edit_script"
)}
<ha-svg-icon
slot="graphic"
.path=${mdiPencil}
></ha-svg-icon>
</mwc-list-item>
<ha-icon-button
label="Edit Script"
tabindex="-1"
.path=${mdiPencil}
></ha-icon-button>
</a>
`
</div>`
: ""}
<li divider role="separator"></li>
<mwc-list-item graphic="icon" @click=${this._refreshTraces}>
${this.hass.localize("ui.panel.config.automation.trace.refresh")}
<ha-svg-icon slot="graphic" .path=${mdiRefresh}></ha-svg-icon>
</mwc-list-item>
<mwc-list-item
graphic="icon"
.disabled=${!this._trace}
@click=${this._downloadTrace}
>
${this.hass.localize(
"ui.panel.config.automation.trace.download_trace"
)}
<ha-svg-icon slot="graphic" .path=${mdiDownload}></ha-svg-icon>
</mwc-list-item>
</ha-button-menu>
<div class="toolbar">
${this._traces && this._traces.length > 0
? html`
<ha-icon-button
.disabled=${this._traces[this._traces.length - 1].run_id ===
this._runId}
label="Older trace"
@click=${this._pickOlderTrace}
.path=${mdiRayEndArrow}
></ha-icon-button>
<select .value=${this._runId} @change=${this._pickTrace}>
${repeat(
this._traces,
(trace) => trace.run_id,
(trace) =>
html`<option value=${trace.run_id}>
${formatDateTimeWithSeconds(
new Date(trace.timestamp.start),
this.hass.locale
)}
</option>`
)}
</select>
<ha-icon-button
.disabled=${this._traces[0].run_id === this._runId}
label="Newer trace"
@click=${this._pickNewerTrace}
.path=${mdiRayStartArrow}
></ha-icon-button>
<div>
<ha-icon-button
.disabled=${this._traces[this._traces.length - 1].run_id ===
this._runId}
label="Older trace"
@click=${this._pickOlderTrace}
.path=${mdiRayEndArrow}
></ha-icon-button>
<select .value=${this._runId} @change=${this._pickTrace}>
${repeat(
this._traces,
(trace) => trace.run_id,
(trace) =>
html`<option value=${trace.run_id}>
${formatDateTimeWithSeconds(
new Date(trace.timestamp.start),
this.hass.locale
)}
</option>`
)}
</select>
<ha-icon-button
.disabled=${this._traces[0].run_id === this._runId}
label="Newer trace"
@click=${this._pickNewerTrace}
.path=${mdiRayStartArrow}
></ha-icon-button>
</div>
`
: ""}
${!this.narrow ? html`<div>${actionButtons}</div>` : ""}
</div>
${this._traces === undefined
@@ -302,7 +266,7 @@ export class HaScriptTrace extends LitElement {
</div>
</div>
`}
</hass-subpage>
</hass-tabs-subpage>
`;
}
@@ -475,13 +439,6 @@ export class HaScriptTrace extends LitElement {
}
}
private async _showInfo() {
if (!this.scriptEntityId) {
return;
}
fireEvent(this, "hass-more-info", { entityId: this.scriptEntityId });
}
static get styles(): CSSResultGroup {
return [
haStyle,
@@ -490,14 +447,26 @@ export class HaScriptTrace extends LitElement {
.toolbar {
display: flex;
align-items: center;
justify-content: center;
justify-content: space-between;
font-size: 20px;
height: var(--header-height);
padding: 0 16px;
background-color: var(--primary-background-color);
font-weight: 400;
color: var(--app-header-text-color, white);
border-bottom: var(--app-header-border-bottom, none);
box-sizing: border-box;
}
.toolbar > * {
display: flex;
align-items: center;
}
:host([narrow]) .toolbar > * {
display: contents;
}
.main {
height: calc(100% - 56px);
display: flex;
@@ -530,9 +499,6 @@ export class HaScriptTrace extends LitElement {
.linkButton {
color: var(--primary-text-color);
}
.trace-link {
text-decoration: none;
}
`,
];
}

View File

@@ -1,135 +0,0 @@
import "@material/mwc-button/mwc-button";
import { mdiHelpCircle } from "@mdi/js";
import { css, CSSResultGroup, html, LitElement } from "lit";
import { customElement, property } from "lit/decorators";
import { fireEvent } from "../../../common/dom/fire_event";
import "../../../components/ha-alert";
import "../../../components/ha-card";
import "../../../components/ha-icon-button";
import { Action, ScriptConfig } from "../../../data/script";
import { haStyle } from "../../../resources/styles";
import type { HomeAssistant } from "../../../types";
import { documentationUrl } from "../../../util/documentation-url";
import "../automation/action/ha-automation-action";
@customElement("manual-script-editor")
export class HaManualScriptEditor extends LitElement {
@property({ attribute: false }) public hass!: HomeAssistant;
@property({ type: Boolean }) public isWide!: boolean;
@property({ type: Boolean }) public narrow!: boolean;
@property({ attribute: false }) public config!: ScriptConfig;
@property({ type: Boolean, reflect: true, attribute: "re-order-mode" })
public reOrderMode = false;
protected render() {
return html`
${this.reOrderMode
? html`
<ha-alert
alert-type="info"
.title=${this.hass.localize(
"ui.panel.config.automation.editor.re_order_mode.title"
)}
>
${this.hass.localize(
"ui.panel.config.automation.editor.re_order_mode.description"
)}
<mwc-button slot="action" @click=${this._exitReOrderMode}>
${this.hass.localize(
"ui.panel.config.automation.editor.re_order_mode.exit"
)}
</mwc-button>
</ha-alert>
`
: ""}
<div class="header">
<h2 id="sequence-heading" class="name">
${this.hass.localize("ui.panel.config.script.editor.sequence")}
</h2>
<a
href=${documentationUrl(this.hass, "/docs/scripts/")}
target="_blank"
rel="noreferrer"
>
<ha-icon-button
.path=${mdiHelpCircle}
.label=${this.hass.localize(
"ui.panel.config.script.editor.link_available_actions"
)}
></ha-icon-button>
</a>
</div>
<ha-automation-action
role="region"
aria-labelledby="sequence-heading"
.actions=${this.config.sequence}
@value-changed=${this._sequenceChanged}
.hass=${this.hass}
.narrow=${this.narrow}
.reOrderMode=${this.reOrderMode}
></ha-automation-action>
`;
}
private _sequenceChanged(ev: CustomEvent): void {
ev.stopPropagation();
fireEvent(this, "value-changed", {
value: { ...this.config!, sequence: ev.detail.value as Action[] },
});
}
private _exitReOrderMode() {
this.reOrderMode = !this.reOrderMode;
}
static get styles(): CSSResultGroup {
return [
haStyle,
css`
:host {
display: block;
}
ha-card {
overflow: hidden;
}
.description {
margin: 0;
}
p {
margin-bottom: 0;
}
.header {
display: flex;
align-items: center;
}
.header:first-child {
margin-top: -16px;
}
.header .name {
font-size: 20px;
font-weight: 400;
flex: 1;
}
.header a {
color: var(--secondary-text-color);
}
ha-alert {
display: block;
margin-bottom: 16px;
}
`,
];
}
}
declare global {
interface HTMLElementTagNameMap {
"manual-script-editor": HaManualScriptEditor;
}
}

View File

@@ -34,8 +34,6 @@ import {
} from "../../resources/styles";
import { HomeAssistant } from "../../types";
import { brandsUrl } from "../../util/brands-url";
import "../../components/ha-icon-next";
import { navigate } from "../../common/navigate";
declare global {
interface HASSDomEvents {
@@ -158,26 +156,8 @@ class HaLogbookRenderer extends LitElement {
})
: undefined;
const traceContext =
triggerDomains.includes(item.domain!) &&
item.context_id! in this.traceContexts
? this.traceContexts[item.context_id!]
: undefined;
const hasTrace = traceContext !== undefined;
return html`
<div
class="entry-container ${classMap({ clickable: hasTrace })}"
.traceLink=${traceContext
? `/config/${traceContext.domain}/trace/${
traceContext.domain === "script"
? `script.${traceContext.item_id}`
: traceContext.item_id
}?run_id=${traceContext.run_id}`
: undefined}
@click=${this._handleClick}
>
<div class="entry-container">
${index === 0 ||
(item?.when &&
previous?.when &&
@@ -206,16 +186,15 @@ class HaLogbookRenderer extends LitElement {
<div class="message-relative_time">
<div class="message">
${!this.noName // Used for more-info panel (single entity case)
? this._renderEntity(item.entity_id, item.name, hasTrace)
? this._renderEntity(item.entity_id, item.name)
: ""}
${this._renderMessage(
item,
seenEntityIds,
domain,
historicStateObj,
hasTrace
historicStateObj
)}
${this._renderContextMessage(item, seenEntityIds, hasTrace)}
${this._renderContextMessage(item, seenEntityIds)}
</div>
<div class="secondary">
<span
@@ -231,15 +210,33 @@ class HaLogbookRenderer extends LitElement {
capitalize
></ha-relative-time>
${item.context_user_id ? html`${this._renderUser(item)}` : ""}
${hasTrace
? `- ${this.hass.localize(
"ui.components.logbook.show_trace"
)}`
${triggerDomains.includes(item.domain!) &&
item.context_id! in this.traceContexts
? html`
-
<a
href=${`/config/${
this.traceContexts[item.context_id!].domain
}/trace/${
this.traceContexts[item.context_id!].domain ===
"script"
? `script.${
this.traceContexts[item.context_id!].item_id
}`
: this.traceContexts[item.context_id!].item_id
}?run_id=${
this.traceContexts[item.context_id!].run_id
}`}
@click=${this._close}
>${this.hass.localize(
"ui.components.logbook.show_trace"
)}</a
>
`
: ""}
</div>
</div>
</div>
${hasTrace ? html`<ha-icon-next></ha-icon-next>` : ""}
</div>
</div>
`;
@@ -261,8 +258,7 @@ class HaLogbookRenderer extends LitElement {
item: LogbookEntry,
seenEntityIds: string[],
domain?: string,
historicStateObj?: HassEntity,
noLink?: boolean
historicStateObj?: HassEntity
) {
if (item.entity_id) {
if (item.state) {
@@ -295,8 +291,7 @@ class HaLogbookRenderer extends LitElement {
? stripEntityId(message, item.context_entity_id)
: message,
seenEntityIds,
undefined,
noLink
undefined
)
: "";
}
@@ -312,8 +307,7 @@ class HaLogbookRenderer extends LitElement {
private _renderUnseenContextSourceEntity(
item: LogbookEntry,
seenEntityIds: string[],
noLink: boolean
seenEntityIds: string[]
) {
if (
!item.context_entity_id ||
@@ -326,16 +320,11 @@ class HaLogbookRenderer extends LitElement {
// described event.
return html` (${this._renderEntity(
item.context_entity_id,
item.context_entity_id_name,
noLink
item.context_entity_id_name
)})`;
}
private _renderContextMessage(
item: LogbookEntry,
seenEntityIds: string[],
noLink: boolean
) {
private _renderContextMessage(item: LogbookEntry, seenEntityIds: string[]) {
// State change
if (item.context_state) {
const historicStateObj =
@@ -348,11 +337,7 @@ class HaLogbookRenderer extends LitElement {
return html`${this.hass.localize(
"ui.components.logbook.triggered_by_state_of"
)}
${this._renderEntity(
item.context_entity_id,
item.context_entity_id_name,
noLink
)}
${this._renderEntity(item.context_entity_id, item.context_entity_id_name)}
${historicStateObj
? localizeStateMessage(
this.hass,
@@ -394,17 +379,11 @@ class HaLogbookRenderer extends LitElement {
? "ui.components.logbook.triggered_by_automation"
: "ui.components.logbook.triggered_by_script"
)}
${this._renderEntity(
item.context_entity_id,
item.context_entity_id_name,
noLink
)}
${this._renderEntity(item.context_entity_id, item.context_entity_id_name)}
${item.context_message
? this._formatMessageWithPossibleEntity(
contextTriggerSource,
seenEntityIds,
undefined,
noLink
seenEntityIds
)
: ""}`;
}
@@ -415,16 +394,14 @@ class HaLogbookRenderer extends LitElement {
${this._formatMessageWithPossibleEntity(
item.context_message,
seenEntityIds,
item.context_entity_id,
noLink
item.context_entity_id
)}
${this._renderUnseenContextSourceEntity(item, seenEntityIds, noLink)}`;
${this._renderUnseenContextSourceEntity(item, seenEntityIds)}`;
}
private _renderEntity(
entityId: string | undefined,
entityName: string | undefined,
noLink?: boolean
entityName: string | undefined
) {
const hasState = entityId && entityId in this.hass.states;
const displayName =
@@ -435,22 +412,19 @@ class HaLogbookRenderer extends LitElement {
if (!hasState) {
return displayName;
}
return noLink
? displayName
: html`<button
class="link"
@click=${this._entityClicked}
.entityId=${entityId}
>
${displayName}
</button>`;
return html`<button
class="link"
@click=${this._entityClicked}
.entityId=${entityId}
>
${displayName}
</button>`;
}
private _formatMessageWithPossibleEntity(
message: string,
seenEntities: string[],
possibleEntity?: string,
noLink?: boolean
possibleEntity?: string
) {
//
// As we are looking at a log(book), we are doing entity_id
@@ -475,8 +449,7 @@ class HaLogbookRenderer extends LitElement {
return html`${messageParts.join(" ")}
${this._renderEntity(
entityId,
this.hass.states[entityId].attributes.friendly_name,
noLink
this.hass.states[entityId].attributes.friendly_name
)}
${messageEnd.join(" ")}`;
}
@@ -502,7 +475,7 @@ class HaLogbookRenderer extends LitElement {
message.length - possibleEntityName.length
);
return html`${message}
${this._renderEntity(possibleEntity, possibleEntityName, noLink)}`;
${this._renderEntity(possibleEntity, possibleEntityName)}`;
}
}
return message;
@@ -521,12 +494,8 @@ class HaLogbookRenderer extends LitElement {
});
}
_handleClick(ev) {
if (!ev.currentTarget.traceLink) {
return;
}
navigate(ev.currentTarget.traceLink);
fireEvent(this, "closed");
private _close(): void {
setTimeout(() => fireEvent(this, "closed"), 500);
}
static get styles(): CSSResultGroup {
@@ -551,20 +520,10 @@ class HaLogbookRenderer extends LitElement {
padding: 8px 16px;
box-sizing: border-box;
border-top: 1px solid var(--divider-color);
justify-content: space-between;
align-items: center;
}
ha-icon-next {
color: var(--secondary-text-color);
}
.clickable {
cursor: pointer;
}
:not(.clickable) .entry.no-entity,
:not(.clickable) .no-name .entry {
.entry.no-entity,
.no-name .entry {
cursor: default;
}

View File

@@ -377,32 +377,16 @@ export class HaLogbook extends LitElement {
return;
}
const nonExpiredRecords = this._nonExpiredRecords(purgeBeforePythonTime);
// Entries are sorted in descending order with newest first.
if (!nonExpiredRecords.length) {
// We have no records left, so we can just replace the list
this._logbookEntries = newEntries;
} else if (
newEntries[newEntries.length - 1].when > // oldest new entry
nonExpiredRecords[0].when // newest old entry
) {
// The new records are newer than the old records
// append the old records to the end of the new records
this._logbookEntries = newEntries.concat(nonExpiredRecords);
} else if (
nonExpiredRecords[nonExpiredRecords.length - 1].when > // oldest old entry
newEntries[0].when // newest new entry
) {
// The new records are older than the old records
// append the new records to the end of the old records
this._logbookEntries = nonExpiredRecords.concat(newEntries);
} else {
// The new records are in the middle of the old records
// so we need to re-sort them
this._logbookEntries = nonExpiredRecords
.concat(newEntries)
.sort((a, b) => b.when - a.when);
}
this._logbookEntries = !nonExpiredRecords.length
? // All existing entries expired
newEntries
: newEntries[0].when >= nonExpiredRecords[0].when
? // The new records are newer than the old records
// append the old records to the end of the new records
newEntries.concat(nonExpiredRecords)
: // The new records are older than the old records
// append the new records to the end of the old records
nonExpiredRecords.concat(newEntries);
};
private _updateTraceContexts = throttle(async () => {

View File

@@ -301,7 +301,6 @@
"refresh": "Refresh",
"cancel": "Cancel",
"delete": "Delete",
"duplicate": "Duplicate",
"remove": "Remove",
"enable": "Enable",
"disable": "Disable",
@@ -924,6 +923,7 @@
"pattern": "Regex pattern for client-side validation"
},
"input_number": {
"initial": "Initial value",
"min": "Minimum value",
"max": "Maximum value",
"mode": "Display mode",
@@ -1790,10 +1790,9 @@
"edit_automation": "Edit automation",
"dev_automation": "Debug automation",
"show_info_automation": "Show info about automation",
"delete": "[%key:ui::common::delete%]",
"delete": "Delete",
"delete_confirm": "Are you sure you want to delete this automation?",
"duplicate": "[%key:ui::common::duplicate%]",
"disabled": "Disabled",
"duplicate": "Duplicate",
"headers": {
"toggle": "Enable/disable",
"name": "Name",
@@ -1823,12 +1822,10 @@
"run": "[%key:ui::panel::config::automation::editor::actions::run%]",
"rename": "[%key:ui::panel::config::automation::editor::triggers::rename%]",
"show_trace": "Traces",
"show_info": "Information",
"introduction": "Use automations to bring your home to life.",
"default_name": "New Automation",
"missing_name": "Cannot save automation without a name",
"load_error_not_editable": "Only automations in automations.yaml are editable.",
"load_error_not_duplicable": "Only automations in automations.yaml can be duplicated.",
"load_error_not_deletable": "Only automations in automations.yaml can be deleted.",
"load_error_unknown": "Error loading automation ({err_no}).",
"save": "Save",
"unsaved_confirm": "You have unsaved changes. Are you sure you want to leave?",
@@ -1837,12 +1834,6 @@
"automation_settings": "Automation settings",
"move_up": "Move up",
"move_down": "Move down",
"re_order": "Re-order",
"re_order_mode": {
"title": "Re-order mode",
"description": "You are in re-order mode, you can re-order your triggers, conditions and actions.",
"exit": "Exit"
},
"description": {
"label": "Description",
"placeholder": "Optional description",
@@ -1854,7 +1845,6 @@
"no_blueprints": "You don't have any blueprints",
"no_inputs": "This blueprint doesn't have any inputs."
},
"change_mode": "Change mode",
"modes": {
"label": "Mode",
"learn_more": "Learn about modes",
@@ -1877,11 +1867,11 @@
"add": "Add trigger",
"id": "Trigger ID",
"edit_id": "Edit ID",
"duplicate": "[%key:ui::common::duplicate%]",
"duplicate": "Duplicate",
"rename": "Rename",
"change_alias": "Rename trigger",
"alias": "Trigger name",
"delete": "[%key:ui::common::delete%]",
"delete": "[%key:ui::panel::mailbox::delete_button%]",
"delete_confirm": "Are you sure you want to delete this?",
"unsupported_platform": "No visual editor support for platform: {platform}",
"type_select": "Trigger type",
@@ -1997,11 +1987,11 @@
"testing_pass": "Condition passes",
"invalid_condition": "Invalid condition configuration",
"test_failed": "Error occurred while testing condition",
"duplicate": "[%key:ui::common::duplicate%]",
"duplicate": "[%key:ui::panel::config::automation::editor::triggers::duplicate%]",
"rename": "[%key:ui::panel::config::automation::editor::triggers::rename%]",
"change_alias": "Rename condition",
"alias": "Condition name",
"delete": "[%key:ui::common::delete%]",
"delete": "[%key:ui::panel::mailbox::delete_button%]",
"delete_confirm": "[%key:ui::panel::config::automation::editor::triggers::delete_confirm%]",
"unsupported_condition": "No visual editor support for condition: {condition}",
"type_select": "Condition type",
@@ -2088,14 +2078,14 @@
"run": "Run",
"run_action_error": "Error running action",
"run_action_success": "Action run successfully",
"duplicate": "[%key:ui::common::duplicate%]",
"duplicate": "[%key:ui::panel::config::automation::editor::triggers::duplicate%]",
"rename": "[%key:ui::panel::config::automation::editor::triggers::rename%]",
"change_alias": "Rename action",
"alias": "Action name",
"enable": "Enable",
"disable": "Disable",
"disabled": "Disabled",
"delete": "[%key:ui::common::delete%]",
"delete": "[%key:ui::panel::mailbox::delete_button%]",
"delete_confirm": "[%key:ui::panel::config::automation::editor::triggers::delete_confirm%]",
"unsupported_action": "No visual editor support for action: {action}",
"type_select": "Action type",
@@ -2268,18 +2258,18 @@
"header": "Script Editor",
"introduction": "The script editor allows you to create and edit scripts. Please follow the link below to read the instructions to make sure that you have configured Home Assistant correctly.",
"learn_more": "Learn more about scripts",
"no_scripts": "We couldn't find any scripts",
"no_scripts": "We couldnt find any scripts",
"add_script": "Add script",
"show_info": "Show info about script",
"run_script": "Run script",
"run": "[%key:ui::panel::config::automation::editor::actions::run%]",
"show_trace": "[%key:ui::panel::config::automation::editor::show_trace%]",
"show_info": "[%key:ui::panel::config::automation::editor::show_info%]",
"edit_script": "Edit script",
"dev_script": "Debug script",
"headers": {
"name": "Name",
"state": "State"
},
"delete": "[%key:ui::common::delete%]",
"duplicate": "[%key:ui::common::duplicate%]"
"duplicate_script": "Duplicate script",
"duplicate": "[%key:ui::panel::config::automation::picker::duplicate%]"
},
"editor": {
"alias": "Name",
@@ -2289,7 +2279,6 @@
"id_already_exists": "This ID already exists",
"introduction": "Use scripts to run a sequence of actions.",
"show_trace": "[%key:ui::panel::config::automation::editor::show_trace%]",
"show_info": "[%key:ui::panel::config::automation::editor::show_info%]",
"header": "Script: {name}",
"default_name": "New Script",
"modes": {
@@ -2305,16 +2294,14 @@
"parallel": "Max number of parallel runs"
},
"load_error_not_editable": "Only scripts inside scripts.yaml are editable.",
"load_error_not_duplicable": "Only scripts in scripts.yaml can be duplicated.",
"load_error_not_deletable": "Only scripts in scripts.yaml can be deleted.",
"load_error_unknown": "Error loading script ({err_no}).",
"delete_confirm": "Are you sure you want to delete this script?",
"delete_script": "Delete script",
"save_script": "Save script",
"sequence": "Sequence",
"sequence_sentence": "The sequence of actions of this script.",
"link_available_actions": "Learn more about available actions."
},
"trace": { "edit_script": "Edit script" }
}
},
"scene": {
"caption": "Scenes",
@@ -2325,23 +2312,25 @@
"introduction": "The scene editor allows you to create and edit scenes. Please follow the link below to read the instructions to make sure that you have configured Home Assistant correctly.",
"learn_more": "Learn more about scenes",
"pick_scene": "Pick scene to edit",
"no_scenes": "We couldn't find any scenes",
"no_scenes": "We couldnt find any scenes",
"add_scene": "Add scene",
"only_editable": "Only scenes defined in scenes.yaml are editable.",
"edit_scene": "Edit scene",
"show_info": "[%key:ui::panel::config::automation::editor::show_info%]",
"activate": "Activate",
"show_info_scene": "Show info about scene",
"delete_scene": "Delete scene",
"delete": "[%key:ui::common::delete%]",
"delete_confirm": "Are you sure you want to delete this scene?",
"duplicate_scene": "Duplicate scene",
"duplicate": "[%key:ui::common::duplicate%]",
"duplicate": "Duplicate",
"headers": {
"activate": "Activate",
"state": "State",
"name": "Name",
"last_activated": "Last activated"
"show_info": "Show information",
"edit": "Edit"
}
},
"editor": {
"introduction": "Use scenes to bring your home to life.",
"default_name": "New Scene",
"load_error_not_editable": "Only scenes in scenes.yaml are editable.",
"load_error_unknown": "Error loading scene ({err_no}).",
@@ -2587,7 +2576,7 @@
"download_diagnostics": "Download diagnostics",
"download_diagnostics_integration": "Download {integration} diagnostics",
"delete_device": "Delete",
"delete_device_integration": "Remove device from {integration}",
"delete_device_integration": "Remove {integration} from device",
"type": {
"device_heading": "Device",
"device": "device",
@@ -2664,7 +2653,6 @@
},
"delete": "Delete",
"confirm_delete": "Are you sure you want to delete this device?",
"confirm_delete_integration": "Are you sure you want to remove this device from {integration}?",
"picker": {
"search": "Search devices",
"filter": {
@@ -3864,7 +3852,7 @@
},
"entity": {
"name": "Entity",
"description": "The Entity card gives you a quick overview of your entity's state."
"description": "The Entity card gives you a quick overview of your entitys state."
},
"button": {
"name": "Button",

View File

@@ -49,11 +49,6 @@
"none": "Geen",
"sleep": "Slaap"
}
},
"humidifier": {
"mode": {
"normal": "Normāls"
}
}
},
"state_badge": {
@@ -98,6 +93,11 @@
}
},
"ui": {
"auth_store": {
"ask": "Wil u hierdie aanmelding stoor?",
"confirm": "Stoor aanmelding",
"decline": "Nee"
},
"card": {
"alarm_control_panel": {
"arm_away": "Bewapen weg",
@@ -271,6 +271,13 @@
"script": "Deel van die volgende skrifte"
},
"relative_time": {
"duration": {
"day": "{count} {count, plural,\n one {dag}\n other {dae}\n}",
"hour": "{count} {count, plural,\n one {uur}\n other {ure}\n}",
"minute": "{count} {count, plural,\n one {minuut}\n other {minute}\n}",
"second": "{count} {count, plural,\n one {sekonde}\n other {sekondes}\n}",
"week": "{count} {count, plural,\n one {week}\n other {weke}\n}"
},
"never": "Nooit"
},
"service-picker": {
@@ -380,17 +387,6 @@
"stop": "Stop"
}
},
"quick-bar": {
"commands": {
"navigation": {
"server_control": "Bedienerkontroles"
},
"reload": {
"person": "Herlaai persone",
"zone": "Herlaai sones"
}
}
},
"voice_command": {
"how_can_i_help": "Hoe kan ek help?",
"label": "Voer 'n vraag in en druk 'Enter'",
@@ -426,15 +422,9 @@
},
"panel": {
"config": {
"application_credentials": {
"editor": {
"name": "Naam"
},
"picker": {
"headers": {
"name": "Naam"
}
}
"advanced_mode": {
"hint_enable": "Ontbreekopsies ontbreek? Aktiveer gevorderde modus aan",
"link_profile_page": "jou profiel bladsy"
},
"areas": {
"caption": "Areas",
@@ -443,7 +433,6 @@
"create": "SKEP",
"default_name": "Nuwe Gebied",
"delete": "SKRAP",
"name": "Naam",
"update": "Opdateer"
},
"picker": {
@@ -478,7 +467,8 @@
},
"event": {
"event": "Gebeurtenis:",
"label": "Vuur geval"
"label": "Vuur geval",
"service_data": "Diens data"
},
"service": {
"label": "Roep diens"
@@ -646,9 +636,6 @@
"delete_confirm": "Is u seker dat u hierdie outomatisering wil uitvee?",
"edit_automation": "Wysig outomatisering",
"header": "Outomatiseringsredakteur",
"headers": {
"name": "Naam"
},
"introduction": "Die outomatiseringsredakteur stel u in staat om outomatisasies te skep en te wysig. Volg die onderstaande skakel om die instruksies te lees om seker te maak dat u Home Assistant korrek opgestel het.",
"learn_more": "Kom meer te wete oor outomatisasies",
"no_automations": "Ons kon nie redigeerbare outomatisasies vind nie",
@@ -657,23 +644,21 @@
"show_info_automation": "Wys inligting oor outomatisering"
}
},
"backup": {
"created": "Geskep"
},
"blueprint": {
"overview": {
"headers": {
"name": "Naam"
}
}
},
"cloud": {
"account": {
"google": {
"sync_entities_404_message": "Kon nie u entiteite met Google sinkroniseer nie, vra Google 'Hallo Google, sinkroniseer my toestelle' om u entiteite te sinkroniseer."
},
"remote": {
"not_connected": "Nie gekoppel nie"
"not_connected": "Nie gekoppel nie",
"remote_enabled": {
"caption": "Verbind outomaties",
"description": "Skakel hierdie opsie in om seker te maak dat u Home Assistent-instansie altyd op afstand toeganklik is."
}
}
},
"description_features": "Beheer terwyl weg van die huis af is, integreer met Alexa en Google Assistant.",
"description_login": "Aangemeld as {email}",
"description_not_login": "Nie aangemeld nie"
},
"core": {
@@ -701,6 +686,25 @@
}
}
},
"customize": {
"attributes_customize": "Die volgende kenmerke is reeds opgestel in customize.yaml",
"attributes_not_set": "Die volgende eienskappe is nie gestel nie. Stel dit in as u wil.",
"attributes_override": "Jy kan hulle oorheers as jy wil.",
"attributes_set": "Die volgende eienskappe van die entiteit is programmaties gestel",
"caption": "Pasgemaakte Instellings",
"description": "Pas u entiteite aan",
"different_include": "Moontlik via 'n domein, 'n glob of 'n ander insluit.",
"pick_attribute": "Kies 'n kenmerk om te ignoreer",
"picker": {
"header": "Pasgemaakte Instellings",
"introduction": "Verfyn per-entiteit eienskappe. Bygevoegde / gewysigde aanpassings sal onmiddellik in werking tree. Verwyderde aanpassings sal in werking tree wanneer die entiteit opgedateer word."
},
"warning": {
"include_link": "sluit customize.yaml in",
"include_sentence": "Dit lyk asof u konfigurasie.yaml nie behoorlik is nie",
"not_applied": "Wysigings wat hier aangebring is, word daarin geskryf, maar sal nie toegepas word na die herlaai van die konfigurasie nie, tensy die insluit in plek is."
}
},
"devices": {
"automation": {
"automations": "Outomatisering",
@@ -730,11 +734,13 @@
"no_scenes": "Geen tonele nie",
"scenes": "Tonele"
},
"scenes": "Tonele",
"script": {
"create": "Skep skrip met toestel",
"no_scripts": "Geen skrifte nie",
"scripts": "Skripte"
},
"scripts": "Skripte",
"unknown_error": "Onbekende fout",
"unnamed_device": "Naamlose toestel",
"update": "Opdateer"
@@ -761,7 +767,6 @@
},
"header": "Entiteit Register",
"headers": {
"name": "Naam",
"status": "Status"
},
"introduction": "Home Assistant hou 'n register van al die vorige entiteite wat uniek geïdentifiseer kan word. Elk van hierdie entiteite sal 'n entiteit-ID hê wat vir hierdie entiteit gereserveer sal word.",
@@ -785,9 +790,12 @@
"caption": "Helpers",
"description": "Elemente wat kan help met die opbou van outomatisering.",
"dialog": {
"add_helper": "Voeg helper by",
"add_platform": "Voeg {platform}",
"create": "Skep"
},
"picker": {
"add_helper": "Voeg helper by",
"headers": {
"editable": "Redigeerbaar",
"name": "Naam",
@@ -808,8 +816,11 @@
"config_entry": {
"delete": "Verwyder",
"delete_confirm": "Is u seker u wil hierdie integrasie skrap?",
"device_unavailable": "toestel nie beskikbaar nie",
"documentation": "Dokumentasie",
"entity_unavailable": "entiteit nie beskikbaar nie",
"firmware": "Fermware: {version}",
"hub": "Gekonnekteer via",
"manuf": "deur {manufacturer}",
"no_area": "Geen Gebied",
"rename": "Hernoem",
@@ -855,6 +866,7 @@
"rename_dialog": "Redigeer die naam van hierdie config-inskrywing",
"rename_input_label": "Inskrywingsnaam"
},
"introduction": "Hier is dit moontlik om u komponente en Home Assistant op te stel. Tans kan alles in verband met die gebruikerskoppelvlak nog nie hier opgestel word nie, maar ons werk daaraan.",
"lovelace": {
"caption": "Lovelace Kontroleskerms",
"dashboards": {
@@ -864,6 +876,7 @@
"storage": "UI beheerde",
"yaml": "YAML-lêer"
},
"confirm_delete": "Is u seker dat u hierdie dashboard wil uitvee?",
"detail": {
"create": "Skep",
"delete": "Verwyder",
@@ -926,6 +939,11 @@
"mqtt": {
"title": "MQTT"
},
"ozw": {
"network_status": {
"unknown": "Onbekend"
}
},
"person": {
"caption": "Persone",
"description": "Bestuur die persone wat Home Assistant op spoor.",
@@ -971,9 +989,6 @@
"duplicate_scene": "Duplikaat toneel",
"edit_scene": "Wysig toneel",
"header": "Toneel-Redakteur",
"headers": {
"name": "Naam"
},
"introduction": "Die toneel redakteur kan jy om te skep en wysig skerms. Volg asseblief die skakel hieronder om die instruksies te lees om seker te maak dat jy het ingestel Huis Assistent korrek.",
"learn_more": "Kom meer te wete oor tonele",
"no_scenes": "Ons kon geen bewerkbare tonele vind nie",
@@ -995,18 +1010,16 @@
},
"picker": {
"duplicate": "Dupliseer",
"edit_script": "Redigeer skrip",
"headers": {
"name": "Naam"
}
"edit_script": "Redigeer skrip"
}
},
"tag": {
"detail": {
"name": "Naam"
},
"headers": {
"name": "Naam"
"server_control": {
"caption": "Bedienerkontroles",
"section": {
"reloading": {
"person": "Herlaai persone",
"zone": "Herlaai sones"
}
}
},
"users": {
@@ -1097,6 +1110,50 @@
"introduction": "Met sones kan u sekere streke op aarde spesifiseer. As 'n persoon binne 'n sone is, neem die toestand die sonenaam aan. Sones kan ook as 'n sneller of toestand gebruik word in outomatiseringsinstellings.",
"no_zones_created_yet": "Lyk asof jy nog geen sones geskep het nie."
},
"zwave": {
"common": {
"index": "Indeks",
"instance": "Aktiwiteit",
"value": "Waarde"
},
"description": "Bestuur u Z-Wave netwerk",
"network_management": {
"header": "Bestuur Z-Wave Netwerk",
"introduction": "Begin opdragte wat die Z-Wave-netwerk beïnvloed. U sal nie terugvoer kry of meeste opdragte slaag nie, maar u kan die OZW-logboek nagaan om dit uit te vind."
},
"network_status": {
"network_started": "Z-Wave Netwerk is Aangeskakel",
"network_started_note_all_queried": "Alle knooppunte is nagevra",
"network_started_note_some_queried": "Aktiewe Knooppunte is nagevra. Slapende Knooppunte sal nagevra word sodra hulle ontwaak.",
"network_starting": "Skakel Tans Z-Wave Netwerk Aan…",
"network_starting_note": "Afhangend van die grootte van u netwerk, kan dit 'n rukkie duur.",
"network_stopped": "Z-Wave Netwerk het Gestop"
},
"node_config": {
"config_value": "Konfigurasiewaarde",
"header": "Knooppuntkonfigurasieopsies",
"set_config_parameter": "Stel Config-parameter in"
},
"node_management": {
"add_to_group": "Voeg by Groep",
"remove_from_group": "Verwyder van Groep"
},
"services": {
"add_node": "Voeg Knooppunt By",
"add_node_secure": "Voeg Veilige Knooppunt By",
"cancel_command": "Kanselleer Opdrag",
"heal_network": "Herstel Netwerk",
"remove_node": "Verwyder Knooppunt",
"save_config": "Stoor Konfigurasie",
"soft_reset": "Sagte Herstel",
"start_network": "Skakel Netwerk Aan",
"stop_network": "Stop Netwerk",
"test_network": "Toets Netwerk"
},
"values": {
"header": "Knooppuntwaardes"
}
},
"zwave_js": {
"add_node": {
"interview_failed": "Die toestelonderhoud het misluk. Bykomende inligting kan moontlik in die logboeke beskikbaar wees.",
@@ -1129,22 +1186,9 @@
},
"templates": {
"title": "Template"
},
"yaml": {
"caption": "Bedienerkontroles",
"section": {
"reloading": {
"person": "Herlaai persone",
"zone": "Herlaai sones"
}
}
}
}
},
"history": {
"add_all": "Voeg alle entiteite by",
"remove_all": "Herverifieer integrasie"
},
"lovelace": {
"add_entities": {
"generated_unsupported": "Jy kan hierdie funksie net gebruik wanneer jy beheer van die Lovelace UI geneem het.",
@@ -1196,7 +1240,6 @@
"name": "Knoppie"
},
"generic": {
"name": "Naam",
"url": "URL"
},
"humidifier": {
@@ -1460,7 +1503,8 @@
"username": "Gebruikersnaam"
},
"error": {
"password_not_match": "Wagwoorde stem nie ooreen nie"
"password_not_match": "Wagwoorde stem nie ooreen nie",
"required_fields": "Vul al die vereiste velde in"
},
"intro": "Kom ons begin deur 'n gebruikers rekening te skep.",
"required_field": "Vereis"
@@ -1501,7 +1545,6 @@
"empty_state": "U het nog geen langlewende-toegangs-tekseenhede nie.",
"header": "Langlewende-toegangs-tekseenhede",
"learn_auth_requests": "Leer hoe om geverifieerde versoeke te maak.",
"name": "Naam",
"prompt_copy_token": "Kopieer u toegangs-tekseenheid. Dit sal nie weer gewys word nie.",
"prompt_name": "Naam?"
},

View File

@@ -166,14 +166,16 @@
"auth": "المصادقة",
"docker": "docker",
"hardware": "عتاد",
"hass": "hass",
"hassio": "hassio",
"host": "مضيف",
"host_pid": "pid المضيف",
"ingress": "مدخل",
"rating": "تقييم"
"rating": "تقييم",
"stage": "مرحلة"
},
"rating": {
"description": "يوفر Home Assistant تصنيفًا أمنيًا لكل من الوظائف الإضافية ، مما يشير إلى المخاطر التي تنطوي عليها عند استخدام هذه الوظيفة الإضافية. كلما زاد الوصول الذي تتطلبه الوظيفة الإضافية على نظامك ، انخفضت النتيجة ، وبالتالي تزيد من مخاطر الأمان المحتملة. \n\n تكون الدرجة على مقياس من 1 إلى 8. حيث 1 هي أدنى درجة (تعتبر الأكثر انعدامًا للأمان والأكثر خطورة) والنتيجة 8 هي أعلى درجة (تعتبر الأكثر أمانًا والأقل خطورة)."
"description": "يوفر Home Assistant تصنيفًا أمنيًا لكل من الوظائف الإضافية ، مما يشير إلى المخاطر التي تنطوي عليها عند استخدام هذه الوظيفة الإضافية. كلما زاد الوصول الذي تتطلبه الوظيفة الإضافية على نظامك ، انخفضت النتيجة ، وبالتالي تزيد من مخاطر الأمان المحتملة. \n\n تكون الدرجة على مقياس من 1 إلى 6. حيث 1 هي أدنى درجة (تعتبر الأكثر انعدامًا للأمان والأكثر خطورة) والنتيجة 6 هي أعلى درجة (تعتبر الأكثر أمانًا والأقل خطورة)."
},
"role": {
"admin": "المشرف",
@@ -294,6 +296,10 @@
"restart": {
"text": "هل أنت متأكد أنك تريد إعادة تشغيل {name} ؟",
"title": "أعد تشغيل {name}"
},
"update": {
"text": "هل تريد بالتأكيد تحديث {name} إلى الإصدار {version} ؟",
"title": "تحديث {name}"
}
},
"dashboard": {
@@ -350,6 +356,12 @@
"restart_addon": {
"confirm_text": "أعد تشغيل الإضافة",
"text": "هل تريد إعادة تشغيل الإضافة بالتغييرات التي قمت بها؟"
},
"update": {
"backup": "نسخة احتياطية",
"create_backup": "قم بإنشاء نسخة احتياطية من {name} قبل التحديث",
"creating_backup": "إنشاء نسخة احتياطية من {name}",
"updating": "تحديث {name} إلى الإصدار {version}"
}
},
"my": {
@@ -432,6 +444,7 @@
"unsupported_description": "يوجد أدناه قائمة بالمشكلات التي تم العثور عليها في التثبيت الخاص بك ، انقر فوق الارتباطات لمعرفة كيفية حل المشكلات.",
"unsupported_reason": {
"apparmor": "لم يتم تمكين AppArmor على المضيف",
"content-trust": "تم تعطيل التحقق من صحة المحتوى",
"dbus": "DBUS",
"docker_version": "نسخة Docker",
"lxc": "LXC",
@@ -447,6 +460,11 @@
}
},
"ui": {
"auth_store": {
"ask": "هل تريد البقاء مسجلا الدخول؟",
"confirm": "نعم",
"decline": "لا"
},
"card": {
"alarm_control_panel": {
"arm_away": "تفعيل خارج المنزل",
@@ -786,7 +804,26 @@
"integration": "تكامل"
},
"relative_time": {
"never": "Nooit"
"duration": {
"day": "{count} {count, plural,\none {يوم}\nother {أيام}\n}",
"hour": "{count} {count, plural,\none {ساعة}\nother {ساعات}\n}",
"minute": "{count} {count, plural,\n one {دقيقة}\n other {دقائق}\n}",
"second": "{count} {count, plural,\none {ثانية}\nother {ثواني}\n}",
"week": "{count} {count, plural,\none {أسبوع}\nother {أسابيع}\n}"
},
"future_duration": {
"day": "خلال {count, plural,\n one {يوم}\n two {يومين}\n other {{count} يوم}\n}",
"minute": "خلال {count} {count, plural,\n one {دقيقة}\n other {دقيقة}\n}"
},
"just_now": "في هذة اللحظة",
"never": "Nooit",
"past_duration": {
"day": "قبل {count} {count, plural,\n one {يوم}\n other {ايام}\n}",
"hour": "قبل {count} {count, plural,\n one {ساعة}\n other {ساعات}\n}",
"minute": "قبل {count} {count, plural,\n one {دقيقة}\n other {دقيقة}\n}",
"second": "قبل {count} {count, plural,\n one {ثانية}\n other {ثواني}\n}",
"week": "قبل {count} {count, plural,\n one {اسبوع}\n other {اسابيع}\n}"
}
},
"service-control": {
"integration_doc": "وثائق التكامل",
@@ -829,6 +866,7 @@
},
"entity_registry": {
"control": "تحكم",
"customize_link": "تخصيصات العنصر",
"dismiss": "إخفاء",
"editor": {
"advanced": "إعدادات متقدمة",
@@ -851,6 +889,7 @@
"unavailable": "هذا الكيان غير متوفر حاليا.",
"update": "تحديث"
},
"info_customize": "يمكنك الكتابة فوق بعض السمات في قسم {customize_link} .",
"no_unique_id": "لا يحتوي هذا الكيان (\"{entity_id}\") على معرف فريد، ولذلك لا يمكن إدارة إعداداته من واجهة المستخدم. راجع {faq_link} لمزيد من التفاصيل.",
"related": "ذات صلة",
"settings": "إعدادات"
@@ -915,6 +954,7 @@
"more_info_control": {
"cover": {
"close_cover": "إغلاق الغطاء",
"close_tile_cover": "إغلاق إمالة الغطاء",
"open_cover": "غطاء مفتوح",
"open_tilt_cover": "فتح إمالة الغطاء",
"stop_cover": "إيقاف الغطاء عن الحركة"
@@ -990,6 +1030,8 @@
"areas": "المناطق",
"automation": "الأتمتة",
"blueprint": "المخططات",
"core": "عام",
"customize": "التخصيصات",
"devices": "الأجهزة",
"entities": "الكيانات",
"helpers": "المساعدين",
@@ -1001,18 +1043,16 @@
"scene": "مشاهد",
"script": "السكريبتات",
"server_control": "عناصر التحكم بالخادم",
"tag": "العلامات",
"users": "المستخدمين",
"zone": "المناطق"
},
"reload": {
"automation": "الأتمتة",
"command_line": "كيانات سطر الأوامر",
"core": "الموقع والتخصيصات",
"filesize": "كيانات حجم الملف",
"filter": "تصفية الكيانات",
"generic": "كيانات كاميرا IP عامة",
"generic_thermostat": "كيانات ترموستات عامة",
"group": "خدمات المجموعات وكيانات المجموعة والاشعارات",
"history_stats": "كيانات احصائيات التاريخ",
"homekit": "HomeKit",
"input_boolean": "قيم الإدخال المنطقية",
@@ -1023,11 +1063,9 @@
"min_max": "الحد الأدنى/الأقصى للكيانات",
"mqtt": "كيانات MQTT المكونة يدويًا",
"person": "الناس",
"reload": "{domain}",
"rest": "اعادة ضبط الكيانات وإخطار الخدمات",
"rpi_gpio": "كيانات Raspberry Pi GPIO",
"scene": "مشاهد",
"script": "السكريبتات",
"smtp": "خدمات إعلام SMTP",
"statistics": "الكيانات الإحصائية",
"telegram": "خدمات اشعارات Telegram",
@@ -1036,9 +1074,7 @@
"zone": "المناطق"
},
"server_control": {
"perform_action": "{action} الخادم",
"restart": "إعادة التشغيل",
"stop": "ايقاف التشغيل"
"perform_action": "{action} الخادم"
},
"types": {
"navigation": "التنقل",
@@ -1151,15 +1187,9 @@
},
"panel": {
"config": {
"application_credentials": {
"editor": {
"name": "الاسم"
},
"picker": {
"headers": {
"name": "الاسم"
}
}
"advanced_mode": {
"hint_enable": "خيارات التكوين مفقودة؟ مكن الوضع المتقدم على",
"link_profile_page": "صفحة ملفك الشخصي"
},
"areas": {
"caption": "المناطق",
@@ -1256,6 +1286,9 @@
}
}
},
"scene": {
"label": "تنشيط مشهد"
},
"service": {
"label": "طلب خدمة"
},
@@ -1533,7 +1566,7 @@
"import_btn": "معاينة مخطط",
"import_header": "Blueprint \"{name}\"",
"import_introduction_link": "يمكنك استيراد مخططات المستخدمين الآخرين من Github و {community_link} . أدخل عنوان URL للمخطط أدناه.",
"importing": "تحميل المخطط ",
"importing": "تحميل المخطط ...",
"raw_blueprint": "محتوى المخطط",
"save_btn": "استورد Blueprint…",
"saving": "جاري استيراد Blueprint …",
@@ -1550,13 +1583,15 @@
"discover_more": "اكتشف المزيد من المخططات",
"header": "محرر المخطط",
"headers": {
"domain": "المجال",
"file_name": "اسم الملف",
"name": "الاسم"
},
"introduction": "يسمح لك تكوين المخطط باستيراد المخططات الخاصة بك وإدارتها.",
"learn_more": "تعرف على المزيد حول استخدام المخططات",
"share_blueprint": "شارك المخطط",
"share_blueprint_no_url": "تعذرت مشاركة المخطط: لا يوجد عنوان url للمصدر"
"share_blueprint_no_url": "تعذرت مشاركة المخطط: لا يوجد عنوان url للمصدر",
"use_blueprint": "إنشاء أتمتة"
}
},
"cloud": {
@@ -1594,6 +1629,7 @@
"not_configured_title": "لم يتم تنشيط Google Assistant",
"security_devices": "أجهزة الأمان",
"sync_entities": "مزامنة الكيانات مع Google",
"sync_entities_404_message": "فشل مزامنة الكيانات الخاصة بك مع Google ، اطلب من Google \"Hey Google ، مزامنة أجهزتي\" لمزامنة الكيانات الخاصة بك.",
"title": "Google Assistant"
},
"integrations": "تكاملات",
@@ -1613,6 +1649,10 @@
"link_learn_how_it_works": "تعرف على كيفية عملها",
"not_connected": "غير متصل",
"reconnecting": "جاري إعادة الاتصال…",
"remote_enabled": {
"caption": "الاتصال تلقائيا",
"description": "قم بتفعيل هذا الخيار للتأكد من أن Home Assistant الخاص بك يمكن الوصول إليه دائمًا عن بُعد."
},
"title": "التحكم عن بعد"
},
"sign_out": "تسجيل الخروج",
@@ -1659,6 +1699,7 @@
"title": "Alexa"
},
"description_features": "تحكم في المنزل عندما تكون بعيدًا وتكامل مع Alexa و Google Assistant",
"description_login": "تم تسجيل الدخول كـ {email}",
"description_not_login": "لم يتم تسجيل الدخول",
"dialog_certificate": {
"certificate_expiration_date": "تاريخ انتهاء صلاحية الشهادة:",
@@ -1760,7 +1801,9 @@
"edit_requires_storage": "تم تعطيل المحرر بسبب تخزين التكوين في configuration.yaml.",
"elevation": "ارتفاع",
"elevation_meters": "أمتار",
"external_url": "العنوان الخارجي",
"imperial_example": "فهرنهايت، رطل",
"internal_url": "العنوان المحلي",
"latitude": "خط العرض",
"location_name": "اسم تثبيت Home Assistant الخاص بك",
"longitude": "خط الطول",
@@ -1776,6 +1819,18 @@
}
}
},
"customize": {
"attributes_override": "يمكنك تجاوزها إذا أردت.",
"caption": "التخصيصات",
"description": "تخصيص الكيانات الخاصة بك",
"picker": {
"header": "التخصيصات",
"introduction": "تعديل السمات لكل كيان. سيتم تفعيل التخصيصات المضافة / المعدلة على الفور. ستسري التخصيصات التي تمت إزالتها عندما يتم تحديث الكيان."
},
"warning": {
"include_link": "تشمل customize.yaml"
}
},
"devices": {
"add_prompt": "لم تتم إضافة {name} باستخدام هذا الجهاز حتى الآن. يمكنك إضافة واحدة عن طريق النقر فوق الزر + أعلاه.",
"automation": {
@@ -1795,7 +1850,7 @@
"no_automations": "لا أتمتة",
"no_device_automations": "لا توجد أتمتة متاحة لهذا الجهاز.",
"triggers": {
"caption": "افعل شيئًا عندما ",
"caption": "افعل شيئًا عندما ...",
"no_triggers": "لا مستهدفات",
"unknown_trigger": "مشغل غير معروف"
},
@@ -1831,7 +1886,9 @@
"enabled_label": "تمكين الجهاز",
"entities": {
"add_entities_lovelace": "أضف إلى Lovelace",
"disabled_entities": "{count} {count, plural,\n one {كيان}\n other {كيانات}\n}",
"entities": "الكيانات",
"hide_disabled": "إخفاء معطل",
"none": "هذا الجهاز ليس له كيانات"
},
"name": "الاسم",
@@ -1850,12 +1907,14 @@
"no_scenes": "لايوجد مشاهد",
"scenes": "مشاهد"
},
"scenes": "مشاهد",
"script": {
"create": "إنشاء سكريبت مع جهاز",
"create_disable": "لا يمكن إنشاء سكريبت بجهاز معطل",
"no_scripts": "لايوجد سكريبتات",
"scripts": "السكريبتات"
},
"scripts": "السكريبتات",
"unknown_error": "خطأ غير معروف",
"unnamed_device": "جهاز غير مسمى",
"update": "تحديث",
@@ -1978,9 +2037,12 @@
"caption": "المساعدين",
"description": "العناصر التي تساعد في بناء الأتمتة",
"dialog": {
"add_helper": "إضافة مساعد",
"add_platform": "إضافة {platform}",
"create": "إضافة"
},
"picker": {
"add_helper": "إضافة مساعد",
"headers": {
"editable": "قابل للتحرير",
"entity_id": "معرف الكيان",
@@ -2010,6 +2072,7 @@
"frontend_version": "إصدار الواجهة الأمامية: {version} - {type}",
"home_assistant_logo": "شعار Home Assistant",
"icons_by": "الايقونات بواسطة",
"integrations": "تكاملات",
"issues": "الاعطال",
"license": "نشرت تحت رخصة Apache 2.0",
"path_configuration": "المسار إلى configuration.yaml: {path}",
@@ -2033,6 +2096,7 @@
"delete": "حذف",
"delete_confirm": "هل تريد حقا حذف هذا التكامل؟",
"depends_on_cloud": "يعتمد على السحابة",
"device_unavailable": "الجهاز غير متوفر",
"devices": "{count} {count, plural,\n one {جهاز}\n other {أجهزة}\n}",
"disable": {
"disable_confirm": "هل أنت متأكد أنك تريد تعطيل إدخال التكوين هذا؟ سيتم تعطيل أجهزتها وكياناتها.",
@@ -2050,7 +2114,9 @@
"documentation": "الوثائق",
"enable_restart_confirm": "أعد تشغيل Home Assistant لإنهاء تمكين هذا التكامل",
"entities": "{count} {count, plural,\n one {كيان}\n other {كيانات}\n}",
"entity_unavailable": "الكيان غير متوفر",
"firmware": "نظام التشغيل {version}",
"hub": "متصل عبر",
"manuf": "بواسطة {manufacturer}",
"no_area": "لا توجد منطقة",
"not_loaded": "لم يُحمل",
@@ -2128,6 +2194,7 @@
"rename_input_label": "الاسم",
"search": "إبحث عن التكاملات"
},
"introduction": "يمكنك هنا برمجة المكونات الخاصة بك و إعداد نظام Home Assistant. ليس كل شيء متاح للبرمجة من خلال واجهة المستخدم حتى الآن، ولكننا نعمل على ذلك.",
"logs": {
"caption": "السجلات",
"clear": "مسح",
@@ -2141,7 +2208,8 @@
"info": "معلومات",
"warning": "تحذير"
},
"loading_log": "جارٍ تحميل سجل الأخطاء …",
"load_full_log": "تحميل سجل Home Assistant الكامل",
"loading_log": "جارٍ تحميل سجل الأخطاء ...",
"multiple_messages": "ظهرت الرسالة لأول مرة في {time} وتظهر {counter} مرات",
"no_errors": "لم يتم الإبلاغ عن أي أخطاء",
"no_issues": "لا توجد مشاكل جديدة!",
@@ -2153,6 +2221,7 @@
"cant_edit_default": "لا يمكن تعديل لوحة تحكم لوفليس القياسية من واجهة المستخدم. يمكنك إخفائه عن طريق تعيين لوحة تحكم أخرى كإعداد افتراضي.",
"cant_edit_yaml": "لا يمكن تحرير لوحات المعلومات المعرفة في YAML من واجهة المستخدم. تغييرها في configuration.yaml.",
"caption": "لوحات التحكم",
"confirm_delete": "هل أنت متأكد أنك تريد حذف لوحة التحكم ؟",
"default_dashboard": "هذه هي لوحة المعلومات الافتراضية",
"detail": {
"create": "إضافة",
@@ -2222,6 +2291,96 @@
"message_received": "الرسالة {id} التي تم تلقيها على {topic} في {time}:",
"title": "MQTT"
},
"ozw": {
"common": {
"controller": "وحده تحكم",
"instance": "مثيل",
"network": "الشبكة"
},
"device_info": {
"node_failed": "العقدة فشلت",
"stage": "مرحلة",
"zwave_info": "معلومات Z-Wave"
},
"navigation": {
"network": "الشبكة",
"node": {
"dashboard": "لوحة المعلومات"
},
"nodes": "العقد",
"select_instance": "حدد مثيل"
},
"network": {
"header": "إدارة الشبكة",
"introduction": "إدارة وظائف الشبكة.",
"node_count": "{count} عقدة"
},
"network_status": {
"details": {
"driverallnodesqueried": "تم الاستعلام عن جميع العقد",
"driverallnodesqueriedsomedead": "تم الاستعلام عن جميع العقد. تم العثور على بعض العقد ميتة",
"driverawakenodesqueries": "تم الاستعلام عن جميع العقد المستيقظة",
"driverfailed": "فشل الاتصال بوحدة تحكم Z-Wave",
"driverready": "تهيئة وحدة تحكم Z-Wave",
"driverremoved": "تمت إزالة برنامج التشغيل",
"driverreset": "تم إعادة تعيين برنامج التشغيل",
"offline": "OZWDaemon غير متصل",
"ready": "جاهز للاتصال",
"started": "متصل بـ MQTT",
"starting": "الاتصال بـ MQTT",
"stopped": "توقف OpenZWave"
},
"offline": "غير متصل",
"online": "متصل",
"starting": "يبدء",
"unknown": "مجهول"
},
"node": {
"button": "تفاصيل العقدة",
"not_found": "لم يتم العثور على العقدة"
},
"node_config": {
"wakeup_help": "يجب أن تكون العقد التي تعمل بالبطارية مستيقظة لتغيير تكوينها. إذا لم تكن العقدة مستيقظة، سيحاول OpenZWave تحديث تكوين العقدة في المرة التالية التي تستيقظ فيها، والتي قد تكون عدة ساعات (أو أيام) لاحقا. اتبع الخطوات التالية لتنبيه جهازك:"
},
"node_query_stages": {
"complete": "اكتملت عملية المقابلة",
"configuration": "الحصول على قيم التكوين من العقدة",
"dynamic": "الحصول على القيم المتغيرة بشكل متكرر من العقدة",
"instances": "الحصول على تفاصيل حول المثيلات أو القنوات التي يدعمها الجهاز",
"probe": "التحقق مما إذا كانت العقدة مستيقظة / حية",
"static": "الحصول على قيم ثابتة من الجهاز",
"wakeup": "إعداد الدعم لقوائم انتظار التنبيه والرسائل"
},
"nodes_table": {
"failed": "فشل",
"id": "معرف",
"manufacturer": "الشركه المصنعه",
"model": "الموديل",
"query_stage": "مرحلة الاستعلام",
"zwave_plus": "Z-Wave Plus"
},
"refresh_node": {
"battery_note": "إذا كانت العقدة تعمل بالبطارية ، فتأكد من تنشيطها قبل المتابعة",
"button": "تحديث العقدة",
"complete": "اكتمل تحديث العقدة",
"description": "سيؤدي هذا إلى إخبار OpenZWave بإعادة إجراء مقابلة مع عقدة وتحديث فئات أوامر العقدة وقدراتها وقيمها.",
"node_status": "حالة العقدة",
"refreshing_description": "جاري تحديث معلومات العقدة…",
"start_refresh_button": "ابدأ التحديث",
"step": "خطوة",
"title": "تحديث معلومات العقدة",
"wakeup_header": "تعليمات الاستيقاظ لـ",
"wakeup_instructions_source": "يتم الحصول على تعليمات التنبيه من قاعدة بيانات جهاز مجتمع OpenZWave."
},
"select_instance": {
"header": "حدد مثيل OpenZWave",
"introduction": "لديك أكثر من مثيل OpenZWave قيد التشغيل. ما هو المثال الذي تريد إدارته؟"
},
"services": {
"add_node": "إضافة عقدة",
"remove_node": "إزالة عقدة"
}
},
"person": {
"add_person": "إضافة شخص",
"caption": "الأشخاص",
@@ -2332,6 +2491,60 @@
"show_info": "عرض معلومات حول السكربت"
}
},
"server_control": {
"caption": "عناصر التحكم بالخادم",
"description": "أعد تشغيل خادم Home Assistant وأوقفه",
"section": {
"reloading": {
"automation": "الأتمتة",
"command_line": "كيانات سطر الأوامر",
"core": "الموقع والتخصيصات",
"filesize": "كيانات حجم الملف",
"filter": "تصفية الكيانات",
"generic": "كيانات كاميرا IP عامة",
"generic_thermostat": "كيانات ترموستات عامة",
"group": "خدمات المجموعات وكيانات المجموعة والاشعارات",
"heading": "إعادة تحميل تكوين YAML",
"history_stats": "كيانات احصائيات التاريخ",
"homekit": "HomeKit",
"input_boolean": "قيم الإدخال المنطقية",
"input_datetime": "مدخل الوقت والتاريخ",
"input_number": "أرقام الإدخال",
"input_select": "تحديد المدخلات",
"input_text": "نصوص الإدخال",
"introduction": "يمكن إعادة تحميل بعض أجزاء Home Assistant دون الحاجة إلى إعادة التشغيل. سيؤدي النقر فوق أحد الخيارات أدناه إلى إلغاء تحميل تهيئة YAML الحالية وتحميل التكوين الجديد.",
"min_max": "الحد الأدنى/الأقصى للكيانات",
"mqtt": "كيانات MQTT المكونة يدويًا",
"person": "الناس",
"reload": "{domain}",
"rest": "اعادة ضبط الكيانات وإخطار الخدمات",
"rpi_gpio": "كيانات Raspberry Pi GPIO",
"scene": "مشاهد",
"script": "السكريبتات",
"smtp": "خدمات إعلام SMTP",
"statistics": "الكيانات الإحصائية",
"telegram": "خدمات اشعارات Telegram",
"template": "كيانات القالب",
"universal": "كيانات مشغل الوسائط العالمي",
"zone": "المناطق"
},
"server_management": {
"confirm_restart": "هل تريد بالتأكيد اعادة تشغيل Home Assistant؟",
"confirm_stop": "هل تريد بالتأكيد إيقاف Home Assistant؟",
"heading": "إدارة الخادم",
"introduction": "تحكم في خادم Home Assistant ... من Home Assistant.",
"restart": "إعادة التشغيل",
"stop": "ايقاف التشغيل"
},
"validation": {
"check_config": "تحقق من التكوين",
"heading": "التحقق من صحة التكوين",
"introduction": "تحقق من صحة التكوين الخاص بك إذا قمت مؤخرًا بإجراء بعض التغييرات على التكوين الخاص بك وتريد التأكد من أنها كلها صالحة.",
"invalid": "التكوين غير صالح",
"valid": "التكوين صالح!"
}
}
},
"tag": {
"add_tag": "إضافة علامة",
"automation_title": "تم فحص العلامة {name}",
@@ -2398,6 +2611,7 @@
"headers": {
"group": "مجموعة",
"is_active": "نشط",
"is_owner": "المالك",
"name": "اسم العرض",
"system": "منشئ من قبل النظام",
"username": "اسم المستخدم"
@@ -2498,16 +2712,108 @@
"edit_home_zone": "لا يمكن تعديل نصف قطر المنطقة الرئيسية من Frontend بعد. اسحب العلامة على الخريطة لتحريك المنطقة الرئيسية.",
"introduction": "تسمح لك المناطق بتحديد مناطق معينة على وجه الأرض. عندما يكون الشخص داخل منطقة ما ، ستأخذ الحالة الاسم من المنطقة. يمكن أيضًا استخدام المناطق كمشغل أو حالة داخل إعدادات الأتمتة."
},
"zwave": {
"common": {
"index": "فهرس",
"instance": "مثيل",
"unknown": "غير معروف",
"value": "قيمة",
"wakeup_interval": "فترة الاستيقاظ"
},
"description": "إدارة شبكة Z-Wave",
"migration": {
"ozw": {
"header": "الانتقال الى OpenZWave",
"introduction": "سيساعدك هذا المعالج على الانتقال تكامل Z-Wave القديم إلى تكامل OpenZWave الموجود حاليا في الإصدار التجريبي."
}
},
"network_management": {
"header": "إدارة شبكة Z-Wave",
"introduction": "قم بتشغيل الأوامر التي تؤثر على شبكة Z-Wave. لن تحصل على تعليقات حول ما إذا كانت معظم الأوامر قد نجحت ، ولكن يمكنك التحقق من سجل OZW لمحاولة اكتشاف ذلك."
},
"network_status": {
"network_started": "بدء تشغيل شبكة Z-Wave",
"network_started_note_all_queried": "تم الاستعلام عن جميع العقد.",
"network_started_note_some_queried": "تم الاستعلام عن العقد المستيقظة. سيتم الاستعلام عن العقد النائمة عند التنبيه.",
"network_starting": "جارٍ بدء تشغيل شبكة Z-Wave …",
"network_starting_note": "قد يستغرق هذا بعض الوقت اعتمادا على حجم الشبكة.",
"network_stopped": "توقفت شبكة Z-Wave"
},
"node_config": {
"config_parameter": "معلمة التكوين",
"config_value": "قيمة التكوين",
"false": "خطأ",
"header": "خيارات تكوين العقدة",
"seconds": "ثواني",
"set_config_parameter": "تعيين معلمة التكوين",
"set_wakeup": "تعيين فترة الاستيقاظ",
"true": "صح"
},
"node_management": {
"add_to_group": "إضافة إلى المجموعة",
"entities": "كيانات هذه العقدة",
"entity_info": "معلومات الكيان",
"exclude_entity": "استبعد هذا الكيان من Home Assistant",
"group": "مجموعة",
"header": "إدارة عقد Z-Wave",
"introduction": "قم بتشغيل أوامر Z-Wave التي تؤثر على عقدة واحدة. اختر عقدة لترى قائمة بالأوامر المتاحة.",
"max_associations": "اعلى رقم للجمعيات:",
"node_group_associations": "ارتباطات مجموعة العقدة",
"node_protection": "حماية العقدة",
"node_to_control": "تحكم للعقدة",
"nodes": "العقد",
"nodes_hint": "تحديد عقدة لعرض خيارات كل عقدة",
"nodes_in_group": "العقد الأخرى في هذه المجموعة:",
"pooling_intensity": "كثافة الاقتراع",
"protection": "الحماية",
"remove_broadcast": "إزالة البث",
"remove_from_group": "إزالة من المجموعة",
"set_protection": "تعيين الحماية"
},
"ozw_log": {
"introduction": "اعرض السجل. 0 هو الحد الأدنى (تحميل السجل بالكامل) و 1000 هو الحد الأقصى. سيعرض التحميل سجلاً ثابتًا وسيتم تحديث الذيل تلقائيًا بآخر عدد محدد من الأسطر من السجل.",
"last_log_lines": "عدد أسطر السجل الأخيرة",
"load": "حمل",
"tail": "الذيل"
},
"services": {
"add_node": "إضافة عقدة",
"add_node_secure": "إضافة عقدة آمنة",
"cancel_command": "إلغاء الأمر",
"heal_network": "عالج الشبكة",
"heal_node": "اختبار عقدة",
"node_info": "معلومات العقدة",
"print_node": "طباعة عقدة",
"refresh_entity": "تحديث الكيان",
"refresh_node": "تحديث العقدة",
"remove_failed_node": "إزالة العقدة الفاشلة",
"remove_node": "إزالة عقدة",
"replace_failed_node": "استبدال العقدة الفاشلة",
"save_config": "حفظ التكوين",
"soft_reset": "إعادة تشغيل بسيطه",
"start_network": "بدء تشغيل الشبكة",
"stop_network": "إيقاف الشبكة",
"test_network": "اختبار الشبكة",
"test_node": "اختبار عقدة"
},
"values": {
"header": "قيم العقدة"
}
},
"zwave_js": {
"add_node": {
"cancel_inclusion": "إلغاء التضمين",
"controller_in_inclusion_mode": "أصبحت وحدة التحكم Z-Wave الآن في وضع التضمين.",
"follow_device_instructions": "اتبع الإرشادات المرفقة مع جهازك لبدء الاقتران على الجهاز.",
"inclusion_finished": "تم إضافة العقدة.",
"interview_failed": "فشلت مقابلة الجهاز. قد تتوفر معلومات إضافية في السجلات.",
"interview_started": "الجهاز قيد المقابلة. هذا قد يستغرق بعض الوقت.",
"secure_inclusion_warning": "تتطلب الأجهزة الآمنة نطاقًا تردديًا إضافيًا ؛ يمكن أن يؤدي وجود عدد كبير جدًا من الأجهزة الآمنة إلى إبطاء شبكة Z-Wave. نوصي باستخدام التضمين الآمن فقط للأجهزة التي تتطلب ذلك ، مثل الأقفال أو فتاحات أبواب المرآب.",
"view_device": "عرض الجهاز"
},
"common": {
"add_node": "إضافة عقدة",
"close": "إغلاق",
"heal_network": "عالج الشبكة",
"home_id": "معرف المنزل",
"network": "الشبكة",
@@ -2518,9 +2824,16 @@
},
"dashboard": {
"driver_version": "إصدار برنامج التشغيل",
"dump_dead_nodes_text": "بعض العقد الخاصة بك لم تستجب ويفترض أنها ميتة. لن يتم تصديرها بالكامل.",
"dump_dead_nodes_title": "بعض العقد الخاصة بك ميتة",
"dump_debug": "قم بتنزيل ملف تفريغ لشبكتك للمساعدة في تشخيص المشكلات",
"dump_not_ready_confirm": "تحميل",
"dump_not_ready_text": "إذا أنشأت تصديرًا بينما لم تكن جميع العقد جاهزة ، فقد تفوتك البيانات المطلوبة. امنح شبكتك بعض الوقت للاستعلام عن جميع العقد. هل تريد الاستمرار في التفريغ؟",
"dump_not_ready_title": "ليست كل العقد جاهزة بعد",
"header": "إدارة شبكة Z-Wave",
"home_id": "معرف المنزل",
"introduction": "إدارة شبكة Z-Wave وعقد Z-Wave",
"nodes_ready": "العقد جاهزة",
"server_version": "إصدار الخادم"
},
"device_info": {
@@ -2632,60 +2945,6 @@
"reset": "إعادة التعيين إلى القالب التجريبي",
"time": "يتم تحديث هذا القالب في بداية كل دقيقة.",
"title": "نماذج"
},
"yaml": {
"caption": "عناصر التحكم بالخادم",
"description": "أعد تشغيل خادم Home Assistant وأوقفه",
"section": {
"reloading": {
"automation": "الأتمتة",
"command_line": "كيانات سطر الأوامر",
"core": "الموقع والتخصيصات",
"filesize": "كيانات حجم الملف",
"filter": "تصفية الكيانات",
"generic": "كيانات كاميرا IP عامة",
"generic_thermostat": "كيانات ترموستات عامة",
"group": "خدمات المجموعات وكيانات المجموعة والاشعارات",
"heading": "إعادة تحميل تكوين YAML",
"history_stats": "كيانات احصائيات التاريخ",
"homekit": "HomeKit",
"input_boolean": "قيم الإدخال المنطقية",
"input_datetime": "مدخل الوقت والتاريخ",
"input_number": "أرقام الإدخال",
"input_select": "تحديد المدخلات",
"input_text": "نصوص الإدخال",
"introduction": "يمكن إعادة تحميل بعض أجزاء Home Assistant دون الحاجة إلى إعادة التشغيل. سيؤدي النقر فوق أحد الخيارات أدناه إلى إلغاء تحميل تهيئة YAML الحالية وتحميل التكوين الجديد.",
"min_max": "الحد الأدنى/الأقصى للكيانات",
"mqtt": "كيانات MQTT المكونة يدويًا",
"person": "الناس",
"reload": "{domain}",
"rest": "اعادة ضبط الكيانات وإخطار الخدمات",
"rpi_gpio": "كيانات Raspberry Pi GPIO",
"scene": "مشاهد",
"script": "السكريبتات",
"smtp": "خدمات إعلام SMTP",
"statistics": "الكيانات الإحصائية",
"telegram": "خدمات اشعارات Telegram",
"template": "كيانات القالب",
"universal": "كيانات مشغل الوسائط العالمي",
"zone": "المناطق"
},
"server_management": {
"confirm_restart": "هل تريد بالتأكيد اعادة تشغيل Home Assistant؟",
"confirm_stop": "هل تريد بالتأكيد إيقاف Home Assistant؟",
"heading": "إدارة الخادم",
"introduction": "تحكم في خادم Home Assistant … من Home Assistant.",
"restart": "إعادة التشغيل",
"stop": "ايقاف التشغيل"
},
"validation": {
"check_config": "تحقق من التكوين",
"heading": "التحقق من صحة التكوين",
"introduction": "تحقق من صحة التكوين الخاص بك إذا قمت مؤخرًا بإجراء بعض التغييرات على التكوين الخاص بك وتريد التأكد من أنها كلها صالحة.",
"invalid": "التكوين غير صالح",
"valid": "التكوين صالح!"
}
}
}
}
},
@@ -2834,6 +3093,7 @@
"maximum": "الحد الاقصي",
"minimum": "الحد الأدنى",
"name": "الاسم",
"no_theme": "لا توجد تصاميم متاحة",
"refresh_interval": "مدة التحديث",
"search": "بحث",
"secondary_info_attribute": "سمة المعلومات الثانوية",
@@ -2941,7 +3201,8 @@
},
"weather-forecast": {
"description": "تعرض بطاقة توقعات الطقس حالة الطقس. من المفيد جدًا تضمينه في الواجهات التي يعرضها الأشخاص على الحائط.",
"name": "النشرة الجوية"
"name": "النشرة الجوية",
"show_forecast": "التوقعات"
}
},
"cardpicker": {
@@ -2955,6 +3216,7 @@
"edit": "تعديل"
},
"edit_badges": {
"panel_mode": "لن يتم عرض هذه الشارات لأن هذا العرض في \"وضع اللوحة\".",
"view_no_badges": "لا يدعم نوع العرض الحالي الشارات."
},
"edit_card": {
@@ -3200,6 +3462,7 @@
"working": "الرجاء الانتظار"
},
"initializing": "جار التهيئة",
"logging_in_to_with": "تسجيل الدخول إلى ** {locationName} ** باستخدام ** {authProviderName} **.",
"logging_in_with": "تسجيل الدخول باستخدام **{authProviderName}**.",
"pick_auth_provider": "أو سجل الدخول باستخدام"
},
@@ -3288,7 +3551,8 @@
"username": "المستخدم"
},
"error": {
"password_not_match": "كلمات المرور غير متطابقة"
"password_not_match": "كلمات المرور غير متطابقة",
"required_fields": "املأ جميع الحقول المطلوبة"
},
"intro": "لنبدأ بإنشاء حساب مستخدم.",
"required_field": "مطلوب"

File diff suppressed because it is too large Load Diff

View File

@@ -160,7 +160,7 @@
"import_btn": "পূর্বরূপ ব্লুপ্রিন্ট",
"import_header": "ব্লুপ্রিন্ট \"{name}\"",
"import_introduction_link": "আপনি গিটহাব এবং {community_link} থেকে অন্যান্য ব্যবহারকারীদের ব্লুপ্রিন্টস আমদানি করতে পারেন। নিচের ব্লুপ্রিন্টের URL লিখুন।",
"importing": "ব্লুপ্রিন্ট লোড হচ্ছে ",
"importing": "ব্লুপ্রিন্ট লোড হচ্ছে ...",
"raw_blueprint": "ব্লুপ্রিন্ট সামগ্রী",
"save_btn": "ব্লুপ্রিন্ট আমদানি করুন",
"saving": "ব্লুপ্রিন্ট আমদানি করা হচ্ছে …",
@@ -177,11 +177,13 @@
"discover_more": "আরও ব্লুপ্রিন্টস আবিষ্কার করুন",
"header": "ব্লুপ্রিন্ট সম্পাদক",
"headers": {
"domain": "ডোমেইন",
"file_name": "ফাইলের নাম",
"name": "নাম"
},
"introduction": "ব্লুপ্রিন্ট কনফিগারেশন আপনাকে আপনার ব্লুপ্রিন্টগুলি আমদানি ও পরিচালনা করতে দেয়।",
"learn_more": "ব্লুপ্রিন্ট ব্যবহার সম্পর্কে আরও জানুন"
"learn_more": "ব্লুপ্রিন্ট ব্যবহার সম্পর্কে আরও জানুন",
"use_blueprint": "অটোমেশন তৈরি করুন"
}
},
"cloud": {
@@ -203,6 +205,16 @@
"feature_remote_control": "বাড়ি থেকে দূরে Home Assistant এর নিয়ন্ত্রণ"
}
},
"core": {
"section": {
"core": {
"core_config": {
"external_url": "বহিঃস্থ URL",
"internal_url": "অভ্যন্তরীণ URL"
}
}
}
},
"devices": {
"automation": {
"create_disable": "নিষ্ক্রিয় ডিভাইস দিয়ে অটোমেশন তৈরি করা যাচ্ছে না"
@@ -259,6 +271,7 @@
"restart_confirm": "এই ইন্টিগ্রেশন অপসারণ শেষ করতে Home Assistant পুনর্সূচনা করুন"
}
},
"introduction": "এই দৃশ্যে আপনার উপাদানগুলি এবং Home Assistant কনফিগার করা সম্ভব। UI থেকে সব কিছু এখনও কনফিগার করা সম্ভব নয়, তবে আমরা এটিতে কাজ করছি।",
"logs": {
"description": "Home Assistant এর লগগুলি দেখুন"
},
@@ -281,6 +294,11 @@
}
}
},
"ozw": {
"select_instance": {
"none_found": "আমরা কোন OpenZWave দৃষ্টান্ত খুঁজে পাইনি। আপনি যদি বিশ্বাস করেন যে এটি ভুল, আপনার OpenZWave এবং MQTT সেটআপ পরীক্ষা করুন এবং Home Assistant আপনার MQTT ব্রোকারের সাথে যোগাযোগ করতে পারেন তা নিশ্চিত করুন।"
}
},
"person": {
"description": "Home Assistant এর দ্বারা ট্র্যাক করে এমন লোকদের পরিচালনা করুন"
},
@@ -298,6 +316,7 @@
"picker": {
"headers": {
"is_active": "সক্রিয়",
"is_owner": "মালিক",
"username": "ব্যবহারকারীর নাম"
}
}
@@ -322,6 +341,11 @@
"header": "নেটওয়ার্ক ভিজ্যুয়ালাইজেশন",
"zoom_label": "ডিভাইসে জুম করুন"
}
},
"zwave": {
"node_management": {
"exclude_entity": "Home Assistant থেকে এই সত্তাটি বাদ দিন"
}
}
},
"custom": {

View File

@@ -82,6 +82,16 @@
},
"mqtt": {
"title": "MQTT"
},
"ozw": {
"network_status": {
"unknown": "Nepoznato"
}
},
"zwave": {
"node_config": {
"set_config_parameter": "Podesite parametar Config"
}
}
},
"developer-tools": {

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -133,11 +133,6 @@
"undo": "Dad-wneud"
},
"components": {
"area-picker": {
"add_dialog": {
"name": "Enw"
}
},
"device-picker": {
"device": "Dyfais"
},
@@ -165,6 +160,12 @@
"was_unsafe": "yn anniogel"
}
},
"relative_time": {
"future_duration": {
"day": "Mewn {count} {count, plural,\n one {day}\n other {days}\n}",
"week": "Mewn {count} {count, plural,\n one {week}\n other {weeks}\n}"
}
},
"service-picker": {
"service": "Gwasanaeth"
},
@@ -193,9 +194,6 @@
"restore": "Adfer y gwerth hysbys diwethaf pan fydd Home Assistant yn cychwyn",
"step": "Maint cam"
},
"generic": {
"name": "Enw"
},
"input_datetime": {
"date": "Dyddiad",
"datetime": "Dyddiad ac amser",
@@ -233,25 +231,22 @@
},
"quick-bar": {
"commands": {
"navigation": {
"server_control": "Rheoli gweinydd"
},
"reload": {
"automation": "Ail-lwytho awtomeiddiadau",
"command_line": "Ail-lwytho endidau llinell orchymyn",
"core": "Ail-lwytho craidd",
"filter": "Ail-lwytho endidau hidlo",
"group": "Ail-lwytho grwpiau",
"mqtt": "Ail-lwytho endidau mqtt wedi'u ffurfweddu",
"rest": "Ail-lwytho endidau gorffwys",
"script": "Ail-lwytho sgriptiau",
"statistics": "Ail-lwytho endidau ystadegau",
"universal": "Ail-lwytho endidau chwaraewyr cyfryngau cyffredinol"
"filesize": "Ailgychwyn endidau maint ffeil",
"history_stats": "Ail-lwytho endidau ystadegau hanes",
"homekit": "Ail-lwytho HomeKit",
"min_max": "AIl-lwytho endidau lleiaf/uchaf",
"mqtt": "Ailgychwyn endidau mqtt wedi'u ffurfweddu",
"ping": "Ailgychwyn endidau sensor ping binary",
"rpi_gpio": "Ailgychwyn endidau GPIO Raspberry Pi",
"smtp": "Ailgychwyn gwasanaethau hysbysu smtp",
"telegram": "Ailgychwyn gwasanaethau hysbysu telegram",
"trend": "Ail-lwytho endidau tuedd"
},
"server_control": {
"perform_action": "{action} Gweinydd",
"restart": "Ailgychwyn",
"stop": "Stopio"
"stop": "Stop"
}
},
"filter_placeholder": "Hidlo Endid"
@@ -278,15 +273,9 @@
},
"panel": {
"config": {
"application_credentials": {
"editor": {
"name": "Enw"
},
"picker": {
"headers": {
"name": "Enw"
}
}
"advanced_mode": {
"hint_enable": "Opsiynau ffurfweddu ar goll? Galluogi modd uwch ymlaen",
"link_profile_page": "eich tudalen proffil"
},
"areas": {
"caption": "Gofrestrfa ardal",
@@ -302,7 +291,6 @@
"editor": {
"create": "Creu",
"delete": "Dileu",
"name": "Enw",
"update": "Diweddaru"
},
"picker": {
@@ -336,7 +324,8 @@
},
"event": {
"event": "Digwyddiad",
"label": "Digwyddiad tân"
"label": "Digwyddiad tân",
"service_data": "Data gwasanaeth"
},
"service": {
"label": "Gwasanaeth galw"
@@ -508,15 +497,9 @@
"pick_automation": "Dewiswch awtomeiddiad i olygu"
}
},
"blueprint": {
"overview": {
"headers": {
"name": "Enw"
}
}
},
"cloud": {
"description_features": "Rheolaeth oddi cartref, integreiddio gyda Alexa a Google Assistant.",
"description_login": "Wedi mewngofnodi fel {email}",
"description_not_login": "Heb fewngofnodi"
},
"core": {
@@ -544,6 +527,15 @@
}
}
},
"customize": {
"caption": "Addasu",
"description": "Addasu eich endidau",
"picker": {
"documentation": "Dogfennaeth addasu",
"header": "Addasu",
"introduction": "Addasu nodweddion yr endid. Bydd ychwanegu/golygu osodiadau bersonol i rym ar unwaith. Bydd addasiadau sydd wedi tynnu yn dod i rym pan fydd yr endid yn cael ei ddiweddaru."
}
},
"devices": {
"automation": {
"actions": {
@@ -574,7 +566,6 @@
},
"delete": "Dileu",
"device_not_found": "Heb ganfod y ddyfais.",
"name": "Enw",
"no_devices": "Dim dyfeisiau",
"unknown_error": "Gwall anhysbys",
"unnamed_device": "Dyfais ddienw"
@@ -588,20 +579,12 @@
"show_all": "Dangos popeth"
},
"header": "Gofrestrfa Endid",
"headers": {
"name": "Enw"
},
"introduction": "Mae Home Assistant yn cadw gofrestrfa pob endid a welodd erioed ble gellir eu nodi yn unigryw. Bydd pob un endid wedi neilltuo efo endid adnabod sy'n cael eu cadw ar gyfer yr endid hwnnw.",
"introduction2": "Defnyddiwch y gofrestrfa endid i ddiystyru yr enw, newid ID endid neu gael gwared y cofnod Home Assistant. Noder, ni fydd dileu'r cofnod cofrestrfa endid yn dileu yr endid. I wneud hynny, dilynwch y ddolen isod a thynnu oddi ar y dudalen integreiddio."
}
},
"header": "Ffurfweddu Home Assistant",
"helpers": {
"picker": {
"headers": {
"name": "Enw"
}
},
"types": {
"counter": "Cownter",
"timer": "Amserydd"
@@ -611,7 +594,10 @@
"caption": "Integreiddiadau",
"config_entry": {
"delete_confirm": "A ydych yn siŵr bod chi eisiau dileu'r integreiddiad yma?",
"device_unavailable": "Dyfais ddim ar gael",
"entity_unavailable": "endid ar gael",
"firmware": "Cadarnwedd: {version}",
"hub": "Cysylltiad drwy",
"manuf": "gan {manufacturer}",
"no_area": "Dim Ardal",
"reload": "Ail-lwytho",
@@ -644,6 +630,7 @@
"new": "Sefydlu integreiddiad newydd",
"none": "Dim byd wedi'i ffurfweddu eto."
},
"introduction": "Yma mae'n bosib ffurfweddu'ch cydrannau ac Home Assistant. Nid yw popeth yn bosib i'w ffurfweddu o'r UI eto, ond rydym yn gweithio arno.",
"lovelace": {
"dashboards": {
"cant_edit_default": "Ni ellir golygu dangosfwrdd safonol Lovelace o'r UI. Gallwch ei guddio trwy osod dangosfwrdd arall yn ddiofyn.",
@@ -665,6 +652,68 @@
"mqtt": {
"title": "MQTT"
},
"ozw": {
"common": {
"controller": "Rheolwr",
"instance": "Enghraifft",
"network": "Rhwydwaith",
"query_stage": "Cyfnod Ymholiad",
"wakeup_instructions": "Cyfarwyddiadau Deffro"
},
"navigation": {
"network": "Rhwydwaith",
"node": {
"config": "Ffurfweddu",
"dashboard": "Dashfwrdd"
},
"nodes": "Nodau",
"select_instance": "Dewis Enghraifft"
},
"network": {
"header": "Rheolaeth Rhwydwaith",
"introduction": "Rheoli swyddogaethau rhwydwaith-lydan",
"node_count": "nodau {count}"
},
"network_status": {
"details": {
"driverallnodesqueried": "Holwyd pob nod",
"driverallnodesqueriedsomedead": "Holwyd pob nod. Cafwyd hyd i rai nodau yn farw",
"driverawakenodesqueries": "Holwyd pob nod effro",
"driverfailed": "Wedi methu cysylltu â rheolydd Z-Wave",
"driverready": "Cychwyn y rheolydd Z-Wave",
"driverremoved": "Mae'r gyrrwr wedi'i diddymu",
"driverreset": "Mae'r gyrrwr wedi cael ei ailosod",
"offline": "OZWDaemon all-lein",
"ready": "Barod i gysylltu",
"started": "Cysylltu â MQTT",
"starting": "Cysylltu â MQTT",
"stopped": "Stopiodd OpenZWave"
},
"offline": "All-lein",
"online": "Ar-lein",
"starting": "Cychwyn",
"unknown": "Anhysbys"
},
"node_config": {
"header": "Ffurfweddu nod",
"help_source": "Darperir disgrifiadau paramedr a thestun cymorth gan brosiect OpenZWave.",
"introduction": "Rheoli'r gwahanol baramedrau ffurfweddu ar gyfer nod Z-Wave.",
"wakeup_help": "Rhaid i nodau wedi'u pweru gan fatri fod yn effro i newid eu cyfluniad. Os nad yw'r nod yn effro, bydd OpenZWave yn ceisio diweddaru cyfluniad y nod tro nesaf y bydd yn deffro, a allai fod yn oriau lluosog (neu ddyddiau) yn ddiweddarach. Dilynwch y camau hyn i ddeffro'ch dyfais:"
},
"node_metadata": {
"product_manual": "Llawlyfr Cynnyrch"
},
"select_instance": {
"header": "Dewiswch Enghraifft OpenZWave",
"introduction": "Mae gennych fwy nag un enghraifft OpenZWave yn rhedeg. Pa enghraifft hoffech chi ei rheoli?",
"none_found": "Ni allem ddod o hyd i enghraifft OpenZWave. Os ydych chi'n credu bod hyn yn anghywir, gwiriwch eich setiau OpenZWave a MQTT a sicrhau y gall Home Assistant gyfathrebu â'ch brocer MQTT."
},
"services": {
"add_node": "Ychwanegu Nod",
"cancel_command": "Canslo Gorchymyn",
"remove_node": "Tynnu Nod"
}
},
"person": {
"caption": "Pobl",
"description": "Rheoli'r pobl mae Home Assistant yn tracio.",
@@ -713,7 +762,6 @@
"caption": "Sgript",
"description": "Creu a golygu sgriptiau",
"editor": {
"alias": "Enw",
"save_script": "Arbed sgript"
},
"picker": {
@@ -725,15 +773,42 @@
"show_info": "Dangos gwybodaeth am y sgript"
}
},
"server_control": {
"caption": "Rheoli gweinydd",
"section": {
"reloading": {
"automation": "Ail-lwytho awtomeiddiadau",
"command_line": "Ail-lwytho endidau llinell orchymyn",
"core": "Ail-lwytho craidd",
"filter": "Ail-lwytho endidau hidlo",
"group": "Ail-lwytho grwpiau",
"heading": "Ffurfweddiad yn ail-lwytho",
"introduction": "Gall rhai rhannau o Home Assistant ail-lwytho heb orfod ailgychwyn. Bydd taro ail-lwytho yn dadlwytho eu cyfluniad cyfredol a llwytho'r un newydd.",
"mqtt": "Ail-lwytho endidau mqtt wedi'u ffurfweddu",
"rest": "Ail-lwytho endidau gorffwys",
"script": "Ail-lwytho sgriptiau",
"statistics": "Ail-lwytho endidau ystadegau",
"universal": "Ail-lwytho endidau chwaraewyr cyfryngau cyffredinol"
},
"server_management": {
"heading": "Rheoli gweinydd",
"introduction": "Rheoli eich gweinydd Home Assistant.. o Home Assistant.",
"restart": "Ailgychwyn",
"stop": "Stopio"
},
"validation": {
"check_config": "Gwirio config",
"introduction": "Dilyswch eich ffurfweddiad os gwnaethoch rai newidiadau ffurfweddu yn ddiweddar ac rydych eisiau gwneud yn siŵr fod o'n ddilys",
"invalid": "Ffurfweddiad annilys",
"valid": "Ffurfweddiad dilys!"
}
}
},
"tag": {
"detail": {
"companion_apps": "apiau cydymaith",
"name": "Enw",
"usage": "Gall tag sbarduno awtomeiddiad wrth ei sganio, gallwch ddefnyddio tagiau NFC, codau QR neu unrhyw fath arall o dag. Defnyddiwch ein {companion_link} i ysgrifennu'r tag hwn i dag NFC rhaglenadwy neu i greu cod QR isod."
},
"headers": {
"name": "Enw"
},
"learn_more": "Dysgu mwy am dagiau"
},
"users": {
@@ -782,9 +857,15 @@
"help_cluster_dropdown": "Dewiswch clwstwr i weld priodoleddau a gorchmynion."
}
},
"zone": {
"detail": {
"name": "Enw"
"zwave": {
"description": "Rheoli eich rhwydwaith Z-Wave",
"learn_more": "Dysgu mwy am Z-Wave",
"node_config": {
"set_config_parameter": "Gosod Paramedr Ffurfweddu"
},
"ozw_log": {
"header": "OZW Log",
"introduction": "Gweld y log. 0 yn y lleiaf (llwyth cyfan log) a 1000 yn yr uchafswm. Llwyth yn dangos sefydlog o log ac yn y gynffon, bydd yn diweddariad automatig gyda nifer penodol diwethaf o linellau o log."
}
}
},
@@ -812,37 +893,6 @@
"result_type": "Math canlyniad",
"time": "Mae'r templed yma yn diweddaru ar ddechrau pob munud.",
"title": "Templedi"
},
"yaml": {
"caption": "Rheoli gweinydd",
"section": {
"reloading": {
"automation": "Ail-lwytho awtomeiddiadau",
"command_line": "Ail-lwytho endidau llinell orchymyn",
"core": "Ail-lwytho craidd",
"filter": "Ail-lwytho endidau hidlo",
"group": "Ail-lwytho grwpiau",
"heading": "Ffurfweddiad yn ail-lwytho",
"introduction": "Gall rhai rhannau o Home Assistant ail-lwytho heb orfod ailgychwyn. Bydd taro ail-lwytho yn dadlwytho eu cyfluniad cyfredol a llwytho'r un newydd.",
"mqtt": "Ail-lwytho endidau mqtt wedi'u ffurfweddu",
"rest": "Ail-lwytho endidau gorffwys",
"script": "Ail-lwytho sgriptiau",
"statistics": "Ail-lwytho endidau ystadegau",
"universal": "Ail-lwytho endidau chwaraewyr cyfryngau cyffredinol"
},
"server_management": {
"heading": "Rheoli gweinydd",
"introduction": "Rheoli eich gweinydd Home Assistant.. o Home Assistant.",
"restart": "Ailgychwyn",
"stop": "Stopio"
},
"validation": {
"check_config": "Gwirio config",
"introduction": "Dilyswch eich ffurfweddiad os gwnaethoch rai newidiadau ffurfweddu yn ddiweddar ac rydych eisiau gwneud yn siŵr fod o'n ddilys",
"invalid": "Ffurfweddiad annilys",
"valid": "Ffurfweddiad dilys!"
}
}
}
}
},
@@ -929,7 +979,7 @@
"double_tap_action": "Gweithred tab dwbl",
"manual": "Llawlyfr",
"manual_description": "Angen ychwanegu cerdyn 'custom' neu ddim ond eisiau ysgrifennu'r yaml?",
"name": "Enw",
"no_theme": "Dim thema",
"state": "Stad"
},
"glance": {
@@ -1010,6 +1060,9 @@
"custom_card": "Custom",
"no_description": "Dim disgrifiad ar gael."
},
"edit_badges": {
"panel_mode": "Ni fydd y bathodynnau hyn yn cael eu harddangos oherwydd bod y farn hon yn \"Modd Panel\"."
},
"edit_card": {
"add": "Ychwanegu Cerdyn",
"delete": "Dileu",
@@ -1195,7 +1248,6 @@
},
"user": {
"data": {
"name": "Enw",
"password_confirm": "Cadarnhewch y Cyfrinair"
},
"error": {
@@ -1217,9 +1269,6 @@
"description": "Galluogi neu analluogi llwybrau byr bysellfwrdd ar gyfer perfformio gweithredoedd amrywiol yn yr UI.",
"header": "Llwybrau byr bysellfwrdd"
},
"long_lived_access_tokens": {
"name": "Enw"
},
"push_notifications": {
"add_device_prompt": {
"input_label": "Enw dyfais",

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1,11 +1,4 @@
{
"state_attributes": {
"climate": {
"fan_mode": {
"on": "On"
}
}
},
"supervisor": {
"addon": {
"dashboard": {
@@ -25,6 +18,9 @@
}
},
"dialogs": {
"entity_registry": {
"customize_link": "entity customisations"
},
"options_flow": {
"loading": {
"loading_flow": "Please wait while the options for {integration} are being initialised"
@@ -32,8 +28,8 @@
},
"quick-bar": {
"commands": {
"reload": {
"core": "Location & customisations"
"navigation": {
"customize": "Customisations"
}
}
}
@@ -58,9 +54,13 @@
}
}
},
"cloud": {
"google": {
"sync_to_google": "Synchronising changes to Google."
"customize": {
"attributes_outside": "The following attributes are customised from outside of customize.yaml",
"caption": "Customisations",
"description": "Customise your entities",
"picker": {
"documentation": "Customisation documentation",
"header": "Customisations"
}
},
"integrations": {
@@ -75,25 +75,9 @@
"lovelace": {
"description": "Create customised sets of cards to control your home"
},
"zha": {
"device_pairing_card": {
"CONFIGURED_status_text": "Initialising",
"INITIALIZED": "Initialisation complete"
},
"visualization": {
"caption": "Visualisation",
"header": "Network visualisation"
}
}
},
"developer-tools": {
"tabs": {
"yaml": {
"section": {
"reloading": {
"core": "Location & customisations"
}
}
"ozw": {
"network_status": {
"unknown": "Unknown"
}
}
},

File diff suppressed because it is too large Load Diff

View File

@@ -1,11 +1,4 @@
{
"state_attributes": {
"climate": {
"fan_mode": {
"on": "On"
}
}
},
"ui": {
"card": {
"climate": {
@@ -64,6 +57,10 @@
},
"panel": {
"config": {
"advanced_mode": {
"hint_enable": "Mankas agordaj opcioj? Ebligu altnivelan reĝimon",
"link_profile_page": "via profilpaĝo"
},
"automation": {
"editor": {
"actions": {
@@ -100,6 +97,20 @@
"show_info_automation": "Montri informojn pri aŭtomatigon"
}
},
"customize": {
"attributes_customize": "La sekvaj atributoj estas jam agorditaj en customize.yaml",
"attributes_not_set": "La sekvaj atributoj ne estis agorditaj. Agordu ilin, se vi volas",
"attributes_outside": "La sekvaj atributoj estas agorditaj de ekstere de customize.yaml",
"attributes_override": "Vi povas superregi ilin, se vi volas.",
"attributes_set": "La sekvaj atributoj de la ento estas agorditaj laŭprogramaj.",
"different_include": "Eble per domajno, globo aŭ malsama inkluzivo.",
"pick_attribute": "Elektu atributon por superregi",
"warning": {
"include_link": "inkluzivi customize.yaml",
"include_sentence": "Ŝajnas, ke via configuration.yaml ne taŭgas",
"not_applied": "Ŝanĝoj faritaj ĉi tie estas skribita en ĝi, sed ne estos aplikita post agordo reŝarĝo krom se la inkluzivo estas en loko."
}
},
"devices": {
"confirm_rename_entity_ids": "Ĉu vi ankaŭ volas alinomi la identigilon de viaj entoj?",
"data_table": {
@@ -157,7 +168,8 @@
"logs": {
"clear": "Klarigi",
"details": "Logaj Detaloj ({level})",
"loading_log": "Ŝarĝante eraran protokolon …",
"load_full_log": "Ŝargi Plena Home Assistant Protokolo",
"loading_log": "Ŝarĝante eraran protokolon ...",
"multiple_messages": "mesaĝo unuafoje okazis je {time} kaj aperas {counter} fojojn",
"no_errors": "Neniuj eraroj estis raportitaj.",
"no_issues": "Ne estas novaj aferoj!",
@@ -175,6 +187,11 @@
"subscribe_to": "Temo por aboni",
"topic": "temo"
},
"ozw": {
"network_status": {
"unknown": "Nekonata"
}
},
"scene": {
"activated": "Aktivigita sceno {name}.",
"caption": "Scenoj",
@@ -270,6 +287,13 @@
"manufacturer_code_override": "Fabrikkodo Nuligo",
"value": "Valoro"
}
},
"zwave": {
"learn_more": "Lerni pli pri Z-Wave",
"ozw_log": {
"header": "OZW Protokolo",
"introduction": "Rigardu la protokolon. 0 estas la minimumo (ŝarĝas tutan protokolo) kaj 1000 estas la maksimumo. Ŝarĝo montros statikan protokolon kaj vosto aŭtomate ĝisdatigos kun la lasta specifita nombro da linioj de la protokolo."
}
}
},
"custom": {
@@ -283,6 +307,7 @@
"tabs": {
"events": {
"alert_event_type": "Eventa tipo estas deviga kampo",
"available_events": "Haveblaj Eventoj",
"count_listeners": " ({count} aŭskultantoj)",
"data": "Eventaj datumoj (YAML, nedeviga)",
"description": "Ekbruligu eventon sur la eventa buso.",

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1,9 +1,4 @@
{
"config_entry": {
"disabled_by": {
"integration": "Integrazioa"
}
},
"groups": {
"system-admin": "Administratzaileak",
"system-read-only": "Soilik irakurtzeko erabiltzaileak",
@@ -33,14 +28,6 @@
"auto": "Automatikoa",
"off": "Itzalita",
"on": "Piztuta"
},
"hvac_action": {
"idle": "Geldirik"
},
"preset_mode": {
"activity": "Jarduera",
"comfort": "Erosotasuna",
"sleep": "Lo egin"
}
}
},
@@ -59,6 +46,11 @@
}
},
"ui": {
"auth_store": {
"ask": "Saio hau gorde nahi duzu?",
"confirm": "Erabiltzailea gorde",
"decline": "Ez, eskerrik asko"
},
"card": {
"alarm_control_panel": {
"clear_code": "Garbitu",
@@ -86,9 +78,7 @@
"effect": "Efektua"
},
"lock": {
"code": "Kodea",
"lock": "Blokeatu",
"unlock": "Desblokeatu"
"code": "Kodea"
},
"media_player": {
"sound_mode": "Soinu modua",
@@ -98,9 +88,6 @@
"scene": {
"activate": "Aktibatu"
},
"script": {
"cancel": "Ezeztatu"
},
"vacuum": {
"actions": {
"resume_cleaning": "Garbitzen jarraitu",
@@ -113,14 +100,12 @@
"water_heater": {
"currently": "Orain",
"on_off": "Piztuta / itzalita",
"operation": "Operazioa",
"target_temperature": "Helburu-tenperatura"
"operation": "Operazioa"
},
"weather": {
"attributes": {
"air_pressure": "Aire presioa",
"humidity": "Hezetasuna",
"precipitation": "Prezipitazioa",
"temperature": "Tenperatura",
"visibility": "Ikusgarritasuna",
"wind_speed": "Haizearen abiadura"
@@ -143,47 +128,25 @@
"wnw": "MIM",
"wsw": "MHM"
},
"day": "Eguna",
"forecast": "Iragarpena",
"night": "Gaua"
"forecast": "Iragarpena"
}
},
"common": {
"edit": "Editatu",
"loading": "Kargatzen",
"no": "Ez",
"save": "Gorde",
"yes": "Bai"
"save": "Gorde"
},
"components": {
"area-picker": {
"add_dialog": {
"failed_create_area": "Ezin izan da gunea sortu.",
"name": "Izena"
},
"area": "Gunea",
"clear": "Garbitu",
"show_areas": "Guneak erakutsi"
},
"data-table": {
"no-data": "Daturik ez",
"search": "Bilatu"
},
"device-picker": {
"clear": "Garbitu",
"device": "Gailua"
},
"media-browser": {
"class": {
"game": "Jokoa",
"image": "Irudia"
}
},
"related-items": {
"area": "Gunea",
"integration": "Integrazioa"
"no-data": "Daturik ez"
},
"relative_time": {
"duration": {
"day": "{count} {count, plural,\n one {egun}\n other {egun}\n}",
"hour": "{count} {count, plural,\n one {ordu}\n other {ordu}\n}",
"minute": "{count} {count, plural,\n one {minutu}\n other {minutu}\n}",
"second": "{count} {count, plural,\n one {segundo}\n other {segundo}\n}",
"week": "{count} {count, plural,\n one {aste}\n other {aste}\n}"
},
"never": "Inoiz"
},
"service-picker": {
@@ -191,27 +154,6 @@
}
},
"dialogs": {
"entity_registry": {
"editor": {
"name": "Izena",
"update": "Eguneratu"
},
"settings": "Ezarpenak"
},
"generic": {
"cancel": "Ezeztatu"
},
"helper_settings": {
"generic": {
"name": "Izena"
}
},
"join_beta_channel": {
"backup": "Ezaugarri hau aktibatu aurretik, ziurtatu zure datuen babeskopiak dituzula.",
"confirm": "Beta kanalean sartu nahi duzu?",
"title": "Beta kanalean sartu",
"warning": "Beta bertsioak probatzaileentzat eta lehen erabiltzaileentzat dira eta kode aldaketa ezegonkorrak izan ditzakete"
},
"more_info_control": {
"script": {
"last_action": "Azken ekintza"
@@ -223,44 +165,6 @@
"updater": {
"title": "Argibideak Eguneratu"
}
},
"mqtt_device_debug_info": {
"entities": "Entitateak",
"no_entities": "Entitaterik ez"
},
"unhealthy": {
"description": "Osasuntsua ez den instalazio bat exekutatzeak arazoak sortuko ditu. Jarraian, zure instalazioarekin aurkitutako arazoen zerrenda dago, egin klik esteketan arazoak nola konpon ditzakezun jakiteko.",
"reasons": {
"docker": "Docker ingurunea ez du behar bezala funtzionatzen",
"privileged": "Supervisorrak ez dauzka pribilegiorik",
"setup": "Supervisorraren konfigurazioak huts egin du",
"supervisor": "Ezin izan da Supervisorra eguneratu",
"untrusted": "Fidagarria ez den edukia detektatu da"
},
"title": "Zure instalazioa ez da osasuntsua"
},
"unsupported": {
"reasons": {
"source_mods": "Iturburu aldaketak",
"systemd": "Systemd",
"systemd_resolved": "Systemd-Resolved"
}
},
"voice_command": {
"error": "Errore bat gertatu da",
"how_can_i_help": "Nola lagundu dezaket?"
},
"zha_device_info": {
"buttons": {
"zigbee_information": "Zigbee gailuaren sinadura"
},
"device_signature": "Zigbee gailuaren sinadura",
"services": {
"updateDeviceName": "Gailu honentzako izen pertsonalizatua ezarri gailuen erregistroan."
},
"zha_device_card": {
"device_name_placeholder": "Gailuaren izena aldatu"
}
}
},
"duration": {
@@ -280,33 +184,21 @@
"title": "Jakinarazpenak"
},
"notification_toast": {
"connection_lost": "Konexioa galdu da. Berriro konektatzen…",
"no_matching_link_found": "Ez da {path}-erako bat datorren Nire estekarik aurkitu"
"connection_lost": "Konexioa galdu da. Berriro konektatzen…"
},
"panel": {
"config": {
"application_credentials": {
"editor": {
"name": "Izena"
},
"picker": {
"headers": {
"name": "Izena"
}
}
},
"areas": {
"caption": "Guneak",
"caption": "Gune Erregistroa",
"description": "Zure etxeko gune guztien ikuspegi orokorra.",
"editor": {
"create": "SORTU",
"default_name": "Gune berria",
"delete": "EZABATU",
"name": "Izena",
"update": "EGUNERATU"
},
"picker": {
"header": "Gunek",
"header": "Gune Erregistroa",
"integrations_page": "Integrazioen orria"
}
},
@@ -315,10 +207,7 @@
"actions": {
"add": "Ekintza gehitu",
"delete": "Ezabatu",
"disable": "Desgaitu",
"disabled": "Desgaituta",
"duplicate": "Bikoiztu",
"enable": "Gaitu",
"header": "Ekintzak",
"learn_more": "Ekintzei buruz gehiago ikasi",
"type": {
@@ -329,15 +218,6 @@
"delay": "Atzerapena",
"label": "Atzerapena"
},
"device_id": {
"extra_fields": {
"code": "Kodea"
},
"label": "Gailua"
},
"parallel": {
"label": "Paraleloan exekutatu"
},
"service": {
"label": "Zerbitzua deitu"
},
@@ -357,18 +237,6 @@
"header": "Baldintzak",
"learn_more": "Baldintzei buruz gehiago ikasi",
"type": {
"and": {
"label": "Eta"
},
"device": {
"extra_fields": {
"for": "Iraupena"
},
"label": "Gailua"
},
"or": {
"label": "Edo"
},
"state": {
"label": "Egoera",
"state": "Egoera"
@@ -392,13 +260,8 @@
"type_select": "Baldintza mota"
},
"default_name": "Automatizazio berria",
"description": {
"placeholder": "Aukerako deskribapena"
},
"load_error_not_editable": "Soilik automations.yaml fitxategian dauden automatizazioak dira editagarriak.",
"load_error_unknown": "Errorea automatizazioa kargatzean ({err_no}).",
"move_down": "Beherantz mugitu",
"move_up": "Gorantz mugitu",
"save": "Gorde",
"triggers": {
"add": "Abiarazlea gehitu",
@@ -407,9 +270,6 @@
"header": "Abiarazleak",
"learn_more": "Abiarazleei buruz gehiago ikasi",
"type": {
"device": {
"label": "Gailua"
},
"event": {
"event_type": "Gertaera mota",
"label": "Gertaera"
@@ -464,7 +324,6 @@
},
"zone": {
"enter": "Sartu",
"entity": "Kokapena duen entitatea",
"leave": "Utzi"
}
},
@@ -473,48 +332,18 @@
},
"picker": {
"add_automation": "Automatizazioa gehitu",
"headers": {
"name": "Izena"
},
"learn_more": "Automatizazioei buruz gehiago ikasi"
}
},
"backup": {
"picker": {
"search": "Babeskopiak bilatu"
}
},
"blueprint": {
"overview": {
"headers": {
"name": "Izena"
}
}
},
"cloud": {
"alexa": {
"exposed_entities": "Ageriko entitateak"
},
"description_not_login": "Ez da saioa hasi",
"forgot_password": {
"title": "Pasahitza ahaztu dut"
},
"login": {
"password": "Pasahitza",
"trial_info": "Ez da ordainketa-informaziorik behar"
},
"register": {
"link_terms_conditions": "Baldintzak",
"password": "Pasahitza"
}
"description_login": "{email} bezala hasi duzu saioa",
"description_not_login": "Ez da saioa hasi"
},
"core": {
"caption": "Orokorra",
"section": {
"core": {
"core_config": {
"edit_location": "Kokapena editatu",
"edit_location_description": "Kokapena tokien konfigurazioan alda daiteke",
"elevation_meters": "metro",
"imperial_example": "Fahrenheit, librak",
"latitude": "Latitudea",
@@ -526,111 +355,41 @@
"unit_system": "Unitate Sistema",
"unit_system_imperial": "Inperiala",
"unit_system_metric": "Metrikoa"
},
"header": "Konfigurazio Orokorra"
}
}
}
},
"dashboard": {
"areas": {
"main": "Guneak eta Tokiak"
}
},
"devices": {
"data_table": {
"battery": "Bateria",
"device": "Gailua",
"integration": "Integrazioa",
"manufacturer": "Fabrikatzailea",
"no_devices": "Gailurik ez"
},
"name": "Izena",
"no_devices": "Gailurik ez"
},
"entities": {
"caption": "Entitate Erregistroa",
"picker": {
"header": "Entitate Erregistroa",
"headers": {
"integration": "Integrazioa",
"name": "Izena"
},
"status": {
"ok": "Ados"
}
}
},
"helpers": {
"dialog": {
"create": "Sortu"
},
"picker": {
"headers": {
"editable": "Editagarria",
"name": "Izena"
}
},
"types": {
"input_number": "Zenbakia"
"header": "Entitate Erregistroa"
}
},
"integrations": {
"caption": "Integrazioak",
"config_entry": {
"firmware": "Firmware: {version}",
"no_area": "Ez dago gunerik",
"system_options": "Sistemaren aukerak"
"no_area": "Ez dago gunerik"
},
"config_flow": {
"external_step": {
"description": "Urrats hau betetzeko kanpoko webgune bat bisitatu beharko duzu.",
"open_site": "Webgunea ireki"
},
"not_all_required_fields": "Ez dira nahitaezko eremu guztiak bete."
}
},
"configure": "Konfiguratu",
"configured": "Konfiguratuta",
"details": "Integrazioaren xehetasunak",
"new": "Integrazio berri bat konfiguratu",
"none": "Ez dago ezer konfiguratuta",
"rename_input_label": "Sarreraren izena"
},
"logs": {
"clear": "Garbitu",
"full_logs": "Erregistro guztiak",
"load_logs": "Erregistro Guztiak Kargatu",
"refresh": "Freskatu"
},
"lovelace": {
"dashboards": {
"detail": {
"show_sidebar": "Alboko barran erakutsi"
},
"picker": {
"headers": {
"filename": "Fitxategi izena",
"sidebar": "Alboko barran erakutsi"
}
}
},
"resources": {
"picker": {
"headers": {
"type": "Mota"
},
"no_resources": "Baliabiderik ez"
},
"types": {
"html": "HTML (zaharkitua)",
"js": "JavaScript fitxategia (zaharkitua)",
"module": "JavaScript Modulua"
}
}
"none": "Ez dago ezer konfiguratuta"
},
"mqtt": {
"description_publish": "Pakete bat argitaratu",
"title": "MQTT"
},
"ozw": {
"network_status": {
"unknown": "Ezezaguna"
}
},
"person": {
"caption": "Pertsonak",
"description": "Kudeatu Home Assistantek jarraituko dituen pertsonak.",
@@ -641,59 +400,9 @@
"name": "Izena"
}
},
"scene": {
"editor": {
"devices": {
"header": "Gailuak"
},
"entities": {
"add": "Entitate bat gehitu",
"header": "Entitateak",
"introduction": "Gailu batekoak ez diren entitateak hemen ezar daitezke.",
"without_device": "Gailurik gabeko entitateak"
},
"name": "Izena"
},
"picker": {
"headers": {
"name": "Izena"
}
}
},
"script": {
"caption": "Script",
"description": "Scriptak sortu eta editatu",
"editor": {
"alias": "Izena",
"sequence": "Sekuentzia"
},
"picker": {
"headers": {
"name": "Izena"
},
"show_info": "Script-ari buruzko informazioa erakutsi"
}
},
"system_dashboard": {
"confirm_restart": "Ziur al zaude Home Assistant berrabiarazi nahi duzula?",
"confirm_restart_text": "Home Assistant berrabiarazten baduzu, zure aginte-panel, automatizazio eta script aktibo guztiak geldituko dira.",
"confirm_restart_title": "Home Assistant berrabiarazi?",
"restart_error": "Ezin izan da Home Assistant berrabiarazi",
"restart_homeassistant": "Home Assistant berrabiarazi",
"restart_homeassistant_short": "Berrabiarazi"
},
"tag": {
"detail": {
"name": "Izena"
},
"headers": {
"last_scanned": "Azken eskaneoa",
"name": "Izena"
}
},
"updates": {
"join_beta": "Beta kanalean sartu",
"leave_beta": "Beta kanala utzi"
"description": "Scriptak sortu eta editatu"
},
"users": {
"add_user": {
@@ -714,30 +423,12 @@
"zha": {
"add_device_page": {
"spinner": "ZHA Zigbee gailuak bilatzen…"
},
"cluster_attributes": {
"attributes_of_cluster": "Hautatutako klusterren atributuak"
},
"cluster_commands": {
"commands_of_cluster": "Hautatutako klusterraren komandoak"
},
"group_binding": {
"bind_button_label": "Lotu Taldea",
"group_picker_label": "Talde lotugarriak"
},
"groups": {
"caption": "Taldeak"
}
},
"zone": {
"detail": {
"delete": "Ezabatu",
"name": "Izena",
"passive": "Pasiboa",
"radius": "Erradioa",
"required_error_msg": "Eremu hau derrigorrezkoa da"
},
"edit_home": "Etxea editatu"
"zwave": {
"node_config": {
"set_config_parameter": "Ezarri konfigurazio-parametroa"
}
}
},
"developer-tools": {
@@ -746,29 +437,13 @@
"title": "Gertaerak"
},
"services": {
"column_parameter": "Parametroa",
"title": "Zerbitzuak"
},
"states": {
"filter_attributes": "Atributuak iragazi",
"filter_entities": "Entitateak iragazi",
"filter_states": "Egoerak iragazi",
"no_entities": "Entitaterik ez",
"title": "Egoerak"
},
"templates": {
"title": "Txantiloiak"
},
"yaml": {
"section": {
"server_management": {
"confirm_restart_text": "Home Assistant berrabiarazten baduzu, zure aginte-panel, automatizazio eta script aktibo guztiak geldituko dira.",
"confirm_restart_title": "Home Assistant berrabiarazi?",
"restart_error": "Ezin izan da Home Assistant berrabiarazi",
"restart_home_assistant": "Home Assistant berrabiarazi"
}
},
"title": "YAML Konfigurazioa"
}
}
},
@@ -791,27 +466,6 @@
}
},
"editor": {
"card": {
"conditional": {
"change_type": "Mota aldatu",
"conditions": "Baldintzak"
},
"entity": {
"name": "Entitatea"
},
"generic": {
"name": "Izena",
"search": "Bilatu",
"state": "Egoera",
"unit": "Unitatea"
},
"glance": {
"columns": "Zutabeak"
},
"humidifier": {
"name": "Hezegailua"
}
},
"edit_card": {
"add": "Txartela gehitu",
"delete": "Ezabatu",
@@ -846,10 +500,6 @@
"configure_ui": "Erabiltzaile interfazea konfiguratu",
"help": "Laguntza"
},
"unused_entities": {
"domain": "Domeinua",
"title": "Erabili gabeko entitateak"
},
"warning": {
"entity_non_numeric": "Entitatea ez da zenbakizkoa: {entity}",
"entity_not_found": "Entitatea ez dago eskuragarri: {entity}"
@@ -918,33 +568,6 @@
},
"initializing": "Hasieratzen"
},
"page-demo": {
"cards": {
"demo": {
"demo_by": "Egilea: {name}"
}
},
"config": {
"arsaboo": {
"labels": {
"activity": "Jarduera",
"entertainment": "Entretenimendua",
"information": "Informazioa"
},
"names": {
"family_room": "Familia Gela",
"hallway": "Pasilloa",
"kitchen": "Sukaldea",
"left": "Ezker",
"mirror": "Ispilua",
"right": "Eskuin"
},
"unit": {
"minutes_abbr": "min"
}
}
}
},
"page-onboarding": {
"core-config": {
"button_detect": "Detektatu",
@@ -966,7 +589,8 @@
"username": "Erabiltzaile izena"
},
"error": {
"password_not_match": "Pasahitzak ez datoz bat"
"password_not_match": "Pasahitzak ez datoz bat",
"required_fields": "Beharrezkoak diren eremu guztiak bete"
},
"intro": "Erabiltzaile kontu bat sortuz has gaitezen.",
"required_field": "Beharrezkoa"
@@ -982,9 +606,6 @@
"submit": "Bidali"
},
"current_user": "{fullName} moduan hasi duzu saioa.",
"force_narrow": {
"header": "Alboko barra beti ezkutatu"
},
"is_owner": "Jabea zara",
"language": {
"dropdown_label": "Hizkuntza",
@@ -996,7 +617,6 @@
"create_failed": "Ezin izan da sarbide token sortu.",
"delete_failed": "Errorea sortu da sarbide tokena ezabatzerakoan.",
"header": "Iraupen luzeko sarbide tokenak",
"name": "Izena",
"prompt_copy_token": "Zure sarbide tokena kopiatu. Ez da berriro erakutsiko.",
"prompt_name": "Izena?"
},
@@ -1007,11 +627,9 @@
"mfa_setup": {
"close": "Itxi",
"submit": "Bidali",
"title_aborted": "Bertan behera utzi",
"title_success": "Arrakasta!"
},
"push_notifications": {
"description": "Bidali jakinarazpenak gailu honetara.",
"error_load_platform": "notify.html5 konfiguratu.",
"header": "Push jakinarazpenak",
"link_promo": "Gehiago ikasi",
@@ -1024,24 +642,15 @@
"not_used": "Ez da inoiz erabili"
},
"themes": {
"dark_mode": {
"auto": "Automatikoa"
},
"dropdown_label": "Gaia",
"error_no_theme": "Ez dago gairik eskuragarri",
"header": "Gaia",
"link_promo": "Gaiei buruz gehiago ikasi"
},
"vibrate": {
"header": "Bibratu"
}
}
},
"sidebar": {
"external_app_configuration": "Aplikazioaren Konfigurazioa"
},
"tips": {
"key_m_hint": "Sakatu 'm' edozein orrialdetan Nire Home Assistant esteka lortzeko"
}
}
}

View File

@@ -112,7 +112,8 @@
},
"network": {
"disabled": "غیر فعال",
"header": "شبکه"
"header": "شبکه",
"host": "میزبان"
},
"options": {
"edit_in_ui": "ویرایش در رابط کاربری",
@@ -133,6 +134,11 @@
}
},
"ui": {
"auth_store": {
"ask": "ميخواي وارد سیستم بموني؟",
"confirm": "ذخیره ورود به سیستم",
"decline": "نه"
},
"card": {
"alarm_control_panel": {
"arm_away": "حفاظت بیرون",
@@ -359,6 +365,13 @@
"integration": "Integrare"
},
"relative_time": {
"duration": {
"day": "{count} {count, plural,\n one {روز}\n other {روزها}\n}",
"hour": "{count} {count, plural,\n one {ساعت}\n other {ساعت ها}\n}",
"minute": "{count} {count, plural,\n one {دقیقه}\n other {دقیقه ها}\n}",
"second": "{count} {count, plural,\n one {ثانیه}\n other {ثانیه ها}\n}",
"week": "{count} {count, plural,\n one {هفته}\n other {هفته ها}\n}"
},
"never": "هرگز"
},
"service-picker": {
@@ -384,7 +397,6 @@
"editor": {
"delete": "حذف",
"device_disabled": "دستگاه این نهاد غیر فعال است.",
"name": "نام",
"open_device_settings": "باز کردن تنظیمات دستگاه",
"update": "به روز رسانی"
},
@@ -394,9 +406,6 @@
"close": "بستن"
},
"helper_settings": {
"generic": {
"name": "نام"
},
"input_datetime": {
"date": "تاریخ",
"datetime": "تاریخ و زمان",
@@ -481,26 +490,17 @@
"areas": "ناحیه ها",
"automation": "اتوماسیون",
"blueprint": "Blueprint ",
"core": "عمومی",
"customize": "سفارشی سازی ها",
"devices": "دستگاه ها",
"entities": "نهادها",
"info": "اطلاعات",
"person": "افراد",
"script": "اسکریپت ها",
"server_control": "Controale Server",
"server_control": "کنترل های سرور",
"users": "کاربران",
"zone": "مناطق"
},
"reload": {
"automation": "بارگیری مجدد اتوماسیون",
"core": "بارگذاری مجدد هسته",
"group": "بارگیری مجدد گروه‌ها",
"scene": "بارگذاری سناریوها",
"script": "بارگزاری مجدد اسکریپت"
},
"server_control": {
"restart": "راه‌اندازی مجدد",
"stop": "بایست"
},
"types": {
"server_control": "سرور"
}
@@ -553,16 +553,6 @@
},
"panel": {
"config": {
"application_credentials": {
"editor": {
"name": "نام"
},
"picker": {
"headers": {
"name": "نام"
}
}
},
"areas": {
"caption": "ناحیه ها",
"data_table": {
@@ -578,7 +568,6 @@
"create": "ايجاد كردن",
"default_name": "ناحیه جدید",
"delete": "حذف",
"name": "نام",
"update": "به روز رسانی"
},
"picker": {
@@ -629,7 +618,11 @@
},
"event": {
"event": "اتفاق",
"label": "اجرا رویداد"
"label": "اجرا رویداد",
"service_data": "اطلاعات خدمات"
},
"scene": {
"label": "Activeaza scena"
},
"service": {
"label": "خدمات فراخوانی"
@@ -849,9 +842,11 @@
"delete_blueprint": "حذف Blueprint",
"discover_more": "blueprint های بیشتری را پیدا کنید",
"headers": {
"domain": "دامنه",
"file_name": "نام فایل",
"name": "نام"
}
},
"use_blueprint": "ایجاد اتوماسیون"
}
},
"cloud": {
@@ -887,6 +882,7 @@
"title": "Alexa"
},
"description_features": "کنترل کردن از خانه، یکپارچه با الکسا و Google Assistant.",
"description_login": "وارد شده به عنوان {email}",
"description_not_login": "وارد نشده اید",
"dialog_certificate": {
"certificate_expiration_date": "Data expirare certificat",
@@ -962,8 +958,10 @@
"edit_requires_storage": "ویرایشگر غیرفعال شده است چون پیکربندی ذخیره شده در configuration.yaml.",
"elevation": "ارتفاع",
"elevation_meters": "متر",
"external_url": "URL خارجی",
"find_currency_value": "ارزش خود را بیابید",
"imperial_example": "فارنهایت، پوند",
"internal_url": "URL داخلی",
"latitude": "طول و عرض جغرافیایی",
"location_name": "نام برای نصب صفحه اصلی دستیار شما",
"longitude": "جغرافیایی",
@@ -979,6 +977,19 @@
}
}
},
"customize": {
"attributes_not_set": "Urmatoarele atribute nu au fost fixate. Fixeaza-le daca vrei",
"caption": "سفارشی سازی ها",
"description": "نهادهای خود را سفارشی کنید",
"pick_attribute": "Alege un atribut pentru a trece peste acesta",
"picker": {
"header": "سفارشی سازی ها",
"introduction": "نویسه ویژگی های هر سازمانی. سفارشی سازی اضافه شده / ویرایش شده فورا اثر می کند. سفارشی های حذف شده هنگامی که موجودیت به روز می شود اثر می کند."
},
"warning": {
"include_link": "Include customize.yaml"
}
},
"devices": {
"automation": {
"actions": {
@@ -993,7 +1004,7 @@
"no_automations": "Nu exista automatizari",
"no_device_automations": "Nu exista automatizari disponibile pentru acest dispozitiv",
"triggers": {
"caption": "يه کاري بکن وقتي"
"caption": "يه کاري بکن وقتي..."
}
},
"cant_edit": "Puteti edita doar elemente create in Interfata Utilizator",
@@ -1044,12 +1055,14 @@
"no_scenes": "Nu exista scene",
"scenes": "Scene"
},
"scenes": "Scene",
"script": {
"create": "Creaza script cu acest dispozitiv",
"create_disable": "نمی توان اسکریپت را با دستگاه غیر فعال ایجاد کرد",
"no_scripts": "Nu exista scripturi",
"scripts": "Scripturi"
},
"scripts": "Scripturi",
"unknown_error": "Eroare necunoscuta",
"unnamed_device": "Dispozitiv nedenumit",
"update": "Actualizeaza"
@@ -1116,9 +1129,12 @@
"caption": "Ajutoare",
"description": "Elemente ce pot ajuta la construirea de automatizari",
"dialog": {
"add_helper": "اضافه کردن کمکی",
"add_platform": "افزودن {platform}",
"create": "ایجاد کردن"
},
"picker": {
"add_helper": "اضافه کردن کمکی",
"headers": {
"editable": "قابل ویرایش",
"entity_id": "شناسه نهاد",
@@ -1138,6 +1154,7 @@
"documentation": "مستندات",
"home_assistant_logo": "Logo Home Assistant",
"icons_by": "آیکون توسط",
"integrations": "یکپارچگی",
"issues": "موضوعات",
"server": "سرور",
"source": "Sursa:"
@@ -1148,7 +1165,10 @@
"config_entry": {
"delete": "حذف",
"delete_confirm": "آیا مطمئن هستید که می خواهید این یکپارچگی را حذف کنید؟",
"device_unavailable": "دستگاه در دسترس نیست",
"entity_unavailable": "نهاد در دسترس نیست",
"firmware": "سیستم عامل: {version}",
"hub": "اتصال از طریق",
"manuf": "توسط {manufacturer}",
"no_area": "بدون منطقه",
"rename": "تغییر نام",
@@ -1188,6 +1208,7 @@
"rename_dialog": "ویرایش نام این پیکربندی ورود",
"rename_input_label": "ورود نام"
},
"introduction": "در اینجا می توانید اجزای خود و صفحه اصلی دستیار را پیکربندی کنید.فعلا ویرایش همه چیز از طریق ویرایش بصری امکان پذیر نیست ، اما ما داریم روی اون کار می کنیم.",
"logs": {
"level": {
"critical": "بحرانی",
@@ -1267,6 +1288,17 @@
"subscribe_to": "Subiect pentru abonare",
"title": "MQTT"
},
"ozw": {
"common": {
"controller": "کنترل کننده",
"network": "شبکه"
},
"network_status": {
"offline": "آفلاین",
"online": "آنلاین",
"unknown": "نامشخص"
}
},
"person": {
"add_person": "Adauga Persoana",
"caption": "افراد",
@@ -1299,8 +1331,7 @@
"add": "Adauga entitate",
"delete": "Elimina entitatea",
"device_entities": "Daca adaugi o entitate care apartine unui dispozitiv, acel dispozitiv va fi adaugat"
},
"name": "نام"
}
},
"picker": {
"header": "Editor de scene",
@@ -1316,7 +1347,6 @@
"caption": "اسکریپت ها",
"description": "دنباله ای از اقدامات را اجرا کنید",
"editor": {
"alias": "نام",
"default_name": "Script nou",
"delete_confirm": "Esti sigur ca vrei sa elimini acest script?",
"delete_script": "Elimina script",
@@ -1339,6 +1369,33 @@
"show_info": "نمایش اطلاعات در مورد اسکریپت"
}
},
"server_control": {
"caption": "Controale Server",
"description": "سرور Home Assistant را مجدداً راه اندازی کرده و متوقف کنید",
"section": {
"reloading": {
"automation": "بارگیری مجدد اتوماسیون",
"core": "بارگذاری مجدد هسته",
"group": "بارگیری مجدد گروه‌ها",
"heading": "بارگیری مجدد پیکربندی YAML",
"introduction": "برخی از قسمت‌های Home Assistant بدون نیاز به راه‌اندازی مجدد می‌توانند بارگیری شوند. با بارگیری مجدد، تنظیمات فعلی آنها حذف می‌شوند و نسخه جدید را بارگیری خواهد شد.",
"scene": "بارگذاری سناریوها",
"script": "بارگزاری مجدد اسکریپت"
},
"server_management": {
"heading": "مدیریت سرور",
"introduction": "سرور Home Assistant خود را کنترل کنید ... از Home Assistant.",
"restart": "راه‌اندازی مجدد",
"stop": "بایست"
},
"validation": {
"check_config": "بررسی پیکربندی",
"heading": "تایید پیکربندی",
"invalid": "پیکربندی نامعتبر است",
"valid": "پیکربندی معتبر است!"
}
}
},
"tag": {
"detail": {
"delete": "حذف",
@@ -1379,6 +1436,7 @@
"headers": {
"group": "گروه",
"is_active": "فعال",
"is_owner": "مالک",
"name": "نام",
"system": "سیستم",
"username": "نام کاربری"
@@ -1438,7 +1496,61 @@
"edit_home_zone": "شعاع منطقه خانه را هنوزنمی توان از پیش زمینه ویرایش کرد. نشانگر را بر روی نقشه حرکت دهید",
"no_zones_created_yet": "Se pare ca inca nu ai creat nicio zona"
},
"zwave": {
"common": {
"index": "Index",
"instance": "نمونه، مثال",
"unknown": "نامشخص",
"value": "Valoare",
"wakeup_interval": "فاصله Wakeup"
},
"description": "شبکه Z-Wave خود را مدیریت کنید",
"network_management": {
"header": "مدیریت شبکه Z-Wave"
},
"network_status": {
"network_started": "شبکه Z-Wave شروع شد",
"network_starting": "شروع شبکه Z-Wave …",
"network_starting_note": "این کار بسته به اندازه شبکه شما ممکن است مدتی طول بکشد.",
"network_stopped": "شبکه Z-Wave متوقف شد"
},
"node_config": {
"config_parameter": "پارامتر پیکربندی",
"config_value": "مقدار پیکربندی",
"false": "نادرست",
"header": "گزینه‌های پیکربندی گره",
"seconds": "ثانیه‌ها",
"set_config_parameter": "تنظیم پارامتر پیکربندی",
"set_wakeup": "تنظیم فاصله Wakeup",
"true": "صحیح"
},
"node_management": {
"group": "گروه",
"nodes": "گره ها"
},
"services": {
"add_node": "Adauga Nod",
"cancel_command": "Anuleaza Comanda",
"heal_network": "ترمیم شبکه",
"remove_node": "Elimina Nodul",
"save_config": "Salveaza Configuratie",
"soft_reset": "تنظیم مجدد نرم",
"start_network": "شروع شبکه",
"stop_network": "توقف شبکه",
"test_network": "Testare Retea"
}
},
"zwave_js": {
"add_node": {
"security_classes": {
"S2_AccessControl": {
"description": "مثال: قفل درها و درهای گاراژ"
},
"S2_Unauthenticated": {
"title": "S2 غیر مجاز"
}
}
},
"heal_node": {
"start_heal": "تعمیر دستگاه",
"title": " یک دستگاه Z-Wave را تعمیر کنید"
@@ -1494,33 +1606,6 @@
},
"templates": {
"title": "قالب"
},
"yaml": {
"caption": "Controale Server",
"description": "سرور Home Assistant را مجدداً راه اندازی کرده و متوقف کنید",
"section": {
"reloading": {
"automation": "بارگیری مجدد اتوماسیون",
"core": "بارگذاری مجدد هسته",
"group": "بارگیری مجدد گروه‌ها",
"heading": "بارگیری مجدد پیکربندی YAML",
"introduction": "برخی از قسمت‌های Home Assistant بدون نیاز به راه‌اندازی مجدد می‌توانند بارگیری شوند. با بارگیری مجدد، تنظیمات فعلی آنها حذف می‌شوند و نسخه جدید را بارگیری خواهد شد.",
"scene": "بارگذاری سناریوها",
"script": "بارگزاری مجدد اسکریپت"
},
"server_management": {
"heading": "مدیریت سرور",
"introduction": "سرور Home Assistant خود را کنترل کنید … از Home Assistant.",
"restart": "راه‌اندازی مجدد",
"stop": "بایست"
},
"validation": {
"check_config": "بررسی پیکربندی",
"heading": "تایید پیکربندی",
"invalid": "پیکربندی نامعتبر است",
"valid": "پیکربندی معتبر است!"
}
}
}
}
},
@@ -1615,6 +1700,7 @@
"maximum": "بیشترین",
"minimum": "کمترین",
"name": "نام",
"no_theme": "بدون موضوع",
"refresh_interval": "فاصله رفرش",
"search": "جستجو",
"secondary_info_attribute": "ویژگی اطلاعات ثانویه",
@@ -1678,7 +1764,8 @@
},
"weather-forecast": {
"description": "کارت پیش بینی آب و هوا را نشان می دهد. اضافه کردن آن به سایر رابط ها بسیار مفید است .",
"name": "پیش بینی آب و هوا"
"name": "پیش بینی آب و هوا",
"show_forecast": "نمایش پیش بینی"
}
},
"cardpicker": {
@@ -1965,7 +2052,8 @@
"username": "نام کاربری"
},
"error": {
"password_not_match": "رمزهای عبور مطابقت ندارند"
"password_not_match": "رمزهای عبور مطابقت ندارند",
"required_fields": "تمام فیلدهای لازم را پر کنید"
},
"intro": "بیایید با ایجاد یک حساب کاربری شروع کنیم.",
"required_field": "ضروری"

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -2,9 +2,6 @@
"config_entry": {
"disabled_by": {
"user": "Brûker"
},
"hidden_by": {
"user": "Brûker"
}
},
"groups": {
@@ -29,19 +26,8 @@
"state_attributes": {
"climate": {
"fan_mode": {
"auto": "Auto",
"off": "Út",
"on": "Oan"
},
"preset_mode": {
"away": "Fuort",
"home": "Thús"
}
},
"humidifier": {
"mode": {
"home": "Thús",
"normal": "Normaal"
}
}
},
@@ -65,12 +51,8 @@
"capability": {
"label": {
"docker": "docker"
},
"role": {
"manager": "behearder"
}
},
"hostname": "Hostnamme",
"install": "ynstallearje",
"start": "start",
"stop": "stop"
@@ -84,31 +66,21 @@
"created": "Makke",
"delete_backup_confirm": "ferwiderje",
"failed_to_delete": "Ferwiderje is mislearre",
"selected": "{number} selekteare",
"size": "Grutte"
"selected": "{number} selekteare"
},
"common": {
"add": "Tafoegje",
"cancel": "Annulearje",
"close": "Slute",
"learn_more": "Lear mear",
"newest_version": "Nijste Ferzje",
"no": "Nee",
"refresh": "Ferfarskje",
"restart": "Opnij starte",
"restart_name": "{Name} opnij starte",
"version": "Ferzje",
"yes": "Ja"
},
"dashboard": {
"addons": "Ynstallearre add-ons"
},
"dialog": {
"network": {
"open": "Iepenje"
},
"registries": {
"password": "Wachtwurd",
"username": "Brûkersnamme"
},
"repositories": {
@@ -117,25 +89,20 @@
"restart_addon": {
"confirm_text": "Start add-on opnij op",
"text": "Wolle jo de add-on opnij starte mei jo feroaringen?"
},
"update": {
"updating": "{name} bywurkje nei ferzje {version}"
}
},
"my": {
"error_addon_not_found": "Add-on net fûn"
},
"panel": {
"system": "Systeem"
},
"system": {
"host": {
"change": "Feroarje",
"operating_system": "Bestjoeringssysteem"
"change": "Feroarje"
},
"supervisor": {
"search": "Sykje",
"share_diagonstics_title": "Help Home Assistant ferbetterje",
"unsupported_reason": {
"os": "Bestjoeringssysteem"
},
"warning": "Warskoging"
}
}
@@ -144,21 +111,7 @@
"card": {
"climate": {
"on_off": "Oan / út",
"operation": "Operaasje",
"target_temperature": "Doeltemperatuer"
},
"fan": {
"direction": "Rjochting",
"forward": "Foarút"
},
"humidifier": {
"on_entity": "{name} oan"
},
"light": {
"brightness": "Helderheid"
},
"media_player": {
"media_volume_down": "Volume del"
"operation": "Operaasje"
},
"script": {
"cancel": "Annulearje",
@@ -193,23 +146,16 @@
},
"common": {
"cancel": "Annulearje",
"delete": "Ferwiderje",
"loading": "Oan it laden",
"next": "Folgjende",
"no": "Nee",
"rename": "Omneame",
"yes": "Ja"
},
"components": {
"addon-picker": {
"addon": "Add-on"
},
"area-picker": {
"add_dialog": {
"add": "Tafoegje",
"name": "Namme"
},
"add_new": "Nij gebiet tafoegje..."
}
},
"blueprint-picker": {
"add_user": "Brûker tafoegje"
@@ -218,9 +164,7 @@
"today": "Hjoed"
},
"data-table": {
"clear": "Opklearje",
"filtering_by": "Filterje troch",
"search": "Sykje"
"clear": "Opklearje"
},
"date-range-picker": {
"ranges": {
@@ -234,78 +178,20 @@
"device": "Apparaat",
"toggle": "Skeakelje"
},
"logbook": {
"messages": {
"was_at_home": "is thús"
}
},
"media-browser": {
"class": {
"url": "URL"
},
"media-player-browser": "Media",
"tts": {
"message": "Berjocht"
}
},
"related-items": {
"integration": "Yntegraasje"
},
"user-picker": {
"add_user": "Brûker tafoegje",
"no_user": "Gjin brûker"
}
},
"dialogs": {
"entity_registry": {
"editor": {
"device_classes": {
"cover": {
"curtain": "Gerdyn",
"window": "Finster"
}
},
"hidden_cause": "Ferburgen troch {cause}.",
"name": "Namme"
}
},
"generic": {
"cancel": "Annulearje"
},
"helper_settings": {
"generic": {
"name": "Namme"
},
"input_datetime": {
"time": "Tiid"
},
"input_select": {
"add": "Tafoegje"
},
"input_text": {
"min": "Minimum lingte",
"password": "Wachtwurd"
}
},
"more_info_control": {
"update": {
"skip": "Oerslaan"
}
},
"mqtt_device_debug_info": {
"show_as_yaml": "Lit sjen as YAML"
},
"quick-bar": {
"commands": {
"navigation": {
"info": "Oer",
"integrations": "Yntegraasjes",
"system": "Systeem",
"users": "Brûkers"
},
"reload": {
"script": "Skripten"
},
"types": {
"navigation": "Navigearje",
"server_control": "Server"
@@ -313,7 +199,6 @@
}
},
"zha_device_info": {
"unknown": "Ûnbekend",
"zha_device_card": {
"device_name_placeholder": "Feroarje apparaatnamme"
}
@@ -324,75 +209,34 @@
"second": "{count} {count, plural,\none {sekonde}\nother {sekonden}\n}",
"week": "{count} {count, plural,\none {wike}\nother {wiken}\n}"
},
"login-form": {
"password": "Wachtwurd"
},
"notification_drawer": {
"close": "Slute"
},
"panel": {
"config": {
"application_credentials": {
"editor": {
"domain": "Yntegraasje",
"name": "Namme"
},
"picker": {
"headers": {
"application": "Yntegraasje",
"name": "Namme"
}
}
},
"areas": {
"editor": {
"name": "Namme"
},
"picker": {
"header": "Gebieten"
}
},
"automation": {
"editor": {
"actions": {
"header": "Aksjes",
"type": {
"choose": {
"add_option": "Opsje tafoegje",
"label": "Kieze",
"option": "Opsje {number}"
"add_option": "Opsje tafoegje"
},
"device_id": {
"extra_fields": {
"brightness_pct": "Helderheid",
"message": "Berjocht",
"mode": "Wize",
"title": "Titel",
"value": "Wearde"
}
},
"if": {
"then": "Dan"
}
}
},
"alias": "Namme",
"conditions": {
"type": {
"device": {
"extra_fields": {
"above": "Boppe",
"below": "Ûnder"
}
},
"sun": {
"label": "Sinne"
},
"time": {
"after": "Efter",
"before": "Foar",
"label": "Tiid",
"type_value": "Fêste tiid",
"weekdays": {
"fri": "Freed",
"mon": "Moandei",
@@ -409,17 +253,6 @@
"add": "Trigger tafoegje",
"header": "",
"type": {
"calendar": {
"before": "Foar"
},
"device": {
"extra_fields": {
"below": "Ûnder"
}
},
"event": {
"context_user_pick": "Selektearje brûker"
},
"homeassistant": {
"label": "",
"shutdown": "Ofslúte",
@@ -433,16 +266,8 @@
"from": "Fan",
"to": "Nei"
},
"sun": {
"sunset": "Sinneûndergong"
},
"time": {
"at": "Om",
"label": "Tiid",
"type_value": "Fêste tiid"
},
"time_pattern": {
"seconds": "Sekonden"
"at": "Om"
}
}
}
@@ -454,10 +279,8 @@
}
},
"blueprint": {
"caption": "Blueprints",
"overview": {
"headers": {
"file_name": "Bestânsnamme",
"name": "Namme"
},
"share_blueprint": "Diel blaudruk"
@@ -465,11 +288,7 @@
},
"cloud": {
"account": {
"alexa": {
"disable": "útskeakelje"
},
"connecting": "Ferbine …",
"integrations": "Yntegraasjes",
"tts": {
"try": "bisykje"
}
@@ -477,131 +296,40 @@
"alexa": {
"title": "Alexa"
},
"forgot_password": {
"email_error_msg": "Ferkearde email"
},
"google": {
"title": "Google Assistant"
},
"login": {
"forgot_password": "Wachtwurd ferjitten?",
"password": "Wachtwurd",
"sign_in": "Ynlogge"
},
"register": {
"email_error_msg": "ûnjildich e-mailadres",
"password": "Wachtwurd"
}
},
"core": {
"section": {
"core": {
"core_config": {
"edit_location": "Lokaasje bewurkje",
"location_name": "Namme"
}
}
}
},
"dashboard": {
"about": {
"description": "Ferzje, systeemstatus en keppelings nei dokumintaasje",
"main": "Oer",
"secondary": "Ferzje, laden yntegraasjes, credits en keppelings nei dokumintaasje",
"title": "Oer"
},
"energy": {
"main": "Enerzjy"
},
"supervisor": {
"main": "Add-ons"
},
"system": {
"main": "Systeem",
"title": "Systeem"
}
},
"devices": {
"data_table": {
"device": "Apparaat",
"integration": "Yntegraasje",
"no_integration": "Gjin yntegraasje"
"device": "Apparaat"
},
"device_not_found": "Apparaat net fûn.",
"disabled_by": {
"integration": "Yntegraasje",
"user": "Gebrûker"
},
"name": "Namme",
"picker": {
"filter": {
"filter": "Filter"
}
},
"script": {
"scripts_heading": "Skripten"
}
},
"energy": {
"grid": {
"learn_more": "Mear ynformaasje oer hoe te begjinnen."
},
"solar": {
"title": "Sinnepanielen"
}
},
"entities": {
"picker": {
"filter": {
"show_all": "Lit alles sjen"
},
"headers": {
"integration": "Yntegraasje",
"name": "Namme"
}
}
},
"helpers": {
"dialog": {
"create_platform": "Meitsje {platform}"
},
"picker": {
"headers": {
"name": "Namme"
}
},
"types": {
"input_boolean": "Skeakelje",
"input_number": "Nûmer",
"input_text": "Tekst"
"input_number": "Nûmer"
}
},
"info": {
"caption": "Oer",
"description": "Ynformaasje oer dyn Home Assistant ynstallaasje"
},
"integrations": {
"caption": "Yntegraasjes",
"config_entry": {
"configure": "Ynstelle",
"disable": {
"disabled": "Útsetten",
"disabled_by": {
"device": "apparaat",
"integration": "yntegraasje",
"user": "Gebrûker"
}
},
"version": "Ferzje: {version}"
},
"config_flow": {
"next": "Folgjende",
"success": "Sukses"
}
},
"disable": {
"show": "Toan"
},
"integration": "yntegraasje"
}
},
"logs": {
"level": {
@@ -611,52 +339,21 @@
}
},
"lovelace": {
"dashboards": {
"detail": {
"create": "Meitsje",
"delete": "Ferwiderje",
"dismiss": "Slute",
"url": "URL"
},
"picker": {
"headers": {
"filename": "Bestânsnamme"
}
}
},
"description": "Konfigurearje dyn Lovelace-dashboards",
"resources": {
"detail": {
"dismiss": "Slute",
"url": "URL",
"warning_header": "Wês foarsichtich!"
},
"picker": {
"headers": {
"url": "URL"
}
}
}
},
"mqtt": {
"title": "MQTT"
},
"network": {
"supervisor": {
"static": "Statysk"
}
},
"person": {
"detail": {
"name": "Namme"
}
},
"scene": {
"editor": {
"devices": {
"add": "Foegje in apparaat ta",
"delete": "Apparaat ferwiderje",
"header": "Apparaten"
"delete": "Apparaat ferwiderje"
},
"name": "Namme"
},
@@ -669,58 +366,25 @@
}
},
"script": {
"caption": "Skripten",
"description": "Meitsje en bewurkje scripts",
"editor": {
"alias": "Namme",
"default_name": "Nij Skript",
"header": "Skript: {name}"
},
"picker": {
"headers": {
"name": "Namme"
}
}
},
"system_dashboard": {
"confirm_restart_title": "Home Assistant opnij starte?"
},
"tag": {
"detail": {
"name": "Namme"
},
"headers": {
"name": "Namme",
"write": "Skriuwe"
},
"write": "Skriuwe"
},
"updates": {
"version_available": "Ferzje {version_available} is beskikber"
},
"url": {
"caption": "Home Assistant URL",
"external_url_label": "Ynternet",
"internal_url_label": "Lokaal netwurk"
},
"users": {
"add_user": {
"caption": "Brûker tafoegje",
"password": "Wachtwurd"
"caption": "Brûker tafoegje"
},
"caption": "Brûkers",
"editor": {
"new_password": "Nij Wachtwurd",
"owner": "Eigner",
"password_changed": "It wachtwurd is feroare!",
"system_generated": "Systeem brûker",
"username": "Brûkersnamme"
},
"is_system": "Systeem brûker",
"picker": {
"headers": {
"is_owner": "Eigner",
"name": "Namme",
"system": "Systeem",
"username": "Brûkersnamme"
}
}
@@ -728,85 +392,28 @@
"zha": {
"clusters": {
"introduction": "Clusters binne de boustiennen foar Zigbee-funksjonaliteit. Se skiede funksjonaliteit yn logyske ienheden. D'r binne client- en serversoarten en disse besteane út attributen en kommando's."
}
},
"zwave": {
"node_config": {
"config_value": "Konfiguraasje wearde"
},
"node_management": {
"add_to_group": "Tafoegje oan groep",
"remove_from_group": "Ferwiderje út groep"
},
"groups": {
"create_group": "Meitsje groep"
}
},
"zone": {
"detail": {
"create": "Tafoegje",
"name": "Namme"
}
},
"zwave_js": {
"device_info": {
"unknown": "Ûnbekend",
"zwave_plus_version": "Ferzje {version}"
}
}
},
"developer-tools": {
"tabs": {
"services": {
"yaml_mode": "Gean nei YAML modus"
},
"yaml": {
"section": {
"reloading": {
"script": "Skripten"
},
"server_management": {
"confirm_restart_title": "Home Assistant opnij starte?",
"heading": "Home Assistant"
}
},
"title": "YAML"
"cancel_command": "Opdracht annulearje"
}
}
},
"energy": {
"setup": {
"next": "Folgjende"
}
},
"lovelace": {
"cards": {
"empty_state": {
"title": "Wolkom thús"
},
"energy": {
"energy_distribution": {
"home": "Thús"
},
"energy_solar_graph": {
"production": "Produksje {name}"
},
"energy_sources_table": {
"energy": "Enerzjy"
},
"loading": "Laden…"
},
"picture-elements": {
"toggle": "Skeakelje {name}"
},
"starting": {
"description": "Home Assistant komt op gong, eefkes geduld…"
}
},
"components": {
"energy_period_selector": {
"month": "Moanne",
"next": "Folgjende"
}
},
"editor": {
"action-editor": {
"actions": {
"more-info": "Mear Ynformaasje",
"url": "URL"
}
},
"card": {
"calendar": {
"views": {
@@ -814,66 +421,25 @@
"dayGridMonth": "Moanne"
}
},
"config": {
"required": "fereaske"
},
"entities": {
"edit_special_row": "Rige bewurkje mei de code-editor",
"entity_row": {
"cast": "Cast"
},
"secondary_info_values": {
"last-changed": "Lêst feroare"
},
"special_row": "Spesjale rige"
},
"generic": {
"maximum": "Maksimum",
"name": "Namme",
"unit": "Ienheid",
"url": "URL"
},
"markdown": {
"name": "Markdown"
},
"statistics-graph": {
"periods": {
"hour": "Oere"
}
}
},
"common": {
"add": "Tafoegje"
},
"edit_card": {
"clear": "Opklearje"
},
"raw_editor": {
"confirm_remove_config_title": "Binne jo wis dat jo jo Lovelace UI-konfiguraasje ferwiderje wolle?"
},
"select_view": {
"dashboard_label": "Dashboard"
},
"suggest_card": {
"add": "Taheakje oan dashboard"
}
},
"menu": {
"close": "Slute",
"configure_ui": "Konfigurearje UI"
}
},
"mailbox": {
"playback_title": "Berjocht ôfspylje"
},
"media-browser": {
"tts": {
"message": "Berjocht"
}
},
"my": {
"documentation": "dokumintaasje"
},
"page-authorize": {
"form": {
"providers": {
@@ -886,45 +452,6 @@
}
}
}
},
"homeassistant": {
"error": {
"invalid_auth": "Unjildige brûkersnamme of wachtwurd"
},
"step": {
"init": {
"data": {
"password": "Wachtwurd",
"username": "Brûkersnamme"
}
}
}
},
"trusted_networks": {
"step": {
"init": {
"data": {
"user": "Brûker"
}
}
}
}
},
"start_over": "Oernij begjinne"
}
},
"page-demo": {
"cards": {
"demo": {
"demo_by": "troch {name}",
"next_demo": "Folgjende demo"
}
},
"config": {
"arsaboo": {
"labels": {
"air": "Loft",
"commute_home": "Reizgje nei hûs"
}
}
}
@@ -933,63 +460,31 @@
"analytics": {
"finish": "Folgjende"
},
"core-config": {
"finish": "Folgjende",
"location_name_default": "Hûs"
},
"finish": "Ein",
"integration": {
"more_integrations": "Mear"
},
"next": "Folgjende",
"user": {
"data": {
"name": "Namme",
"password": "Wachtwurd",
"username": "Brûkersnamme"
}
}
"next": "Folgjende"
},
"profile": {
"advanced_mode": {
"link_promo": "Lear mear"
},
"change_password": {
"new_password": "Nij Wachtwurd"
},
"dashboard": {
"dropdown_label": "Dashboard",
"header": "Dashboard"
},
"logout_title": "Útlogge?",
"long_lived_access_tokens": {
"name": "Namme"
},
"mfa_setup": {
"close": "Slute",
"title_success": "Sukses!"
"close": "Slute"
},
"number_format": {
"formats": {
"comma_decimal": "1.234.567.89",
"none": "Gjin"
}
},
"push_notifications": {
"link_promo": "Lear mear"
},
"themes": {
"dark_mode": {
"auto": "Auto",
"dark": "Tsjuster",
"light": "Ljocht"
},
"primary_color": "Primêre kleur"
},
"time_format": {
"formats": {
"24": "24 oeren"
}
}
}
}

View File

@@ -19,7 +19,6 @@
},
"state_badge": {
"default": {
"error": "Erro",
"unknown": "Descoñecido"
},
"person": {
@@ -38,7 +37,8 @@
"network": {
"container": "Contedor",
"disabled": "Deshabilitada",
"header": "Rede"
"header": "Rede",
"host": "Host"
},
"no_configuration": "Este complemento non expón a configuración para que poidas xogar con …",
"options": {
@@ -103,14 +103,16 @@
"auth": "auth",
"docker": "docker",
"hardware": "hardware",
"hass": "hass",
"hassio": "hassio",
"host": "host",
"host_pid": "pid do host",
"ingress": "entrada",
"rating": "clasificación"
"rating": "clasificación",
"stage": "etapa"
},
"rating": {
"description": "Home Assistant ofrece unha clasificación de seguridade para cada un dos complementos, indicando os riscos que implica o uso do complemento. Canto máis acceso precisa un complemento no seu sistema, menor será a puntuación, aumentando os riscos potenciais de seguridade. \n\nA puntuación está nunha escala de 1 a 8. Onde 1 é a puntuación máis baixa (considerada o máis inseguro e maior risco) e unha puntuación de 8 é a puntuación máis alta (considerada o máis seguro e menor risco).",
"description": "Home Assistant ofrece unha clasificación de seguridade para cada un dos complementos, indicando os riscos que implica o uso do complemento. Canto máis acceso precisa un complemento no seu sistema, menor será a puntuación, aumentando os riscos potenciais de seguridade. \n\nA puntuación está nunha escala de 1 a 6. Onde 1 é a puntuación máis baixa (considerada o máis inseguro e maior risco) e unha puntuación de 6 é a puntuación máis alta (considerada o máis seguro e menor risco).",
"title": "Clasificación de seguridade do complemento"
},
"role": {
@@ -237,6 +239,10 @@
"restart": {
"text": "Estás seguro de que queres reiniciar {name} ?",
"title": "Reiniciar {name}"
},
"update": {
"text": "Seguro que queres actualizar {name} á versión {version}?",
"title": "Actualizar {name}"
}
},
"dashboard": {
@@ -286,6 +292,9 @@
"restart_addon": {
"confirm_text": "Reiniciar complemento",
"text": "¿Queres reiniciar o complemento cos teus cambios?"
},
"update": {
"updating": "Actualizando {name} á versión {version}"
}
},
"my": {
@@ -368,6 +377,8 @@
"unhealthy_title": "A túa instalación non é saudable",
"unsupported_description": "Abaixo amósase unha lista de problemas atopados coa súa instalación, faga clic nas ligazóns para saber como pode resolver os problemas.",
"unsupported_reason": {
"container": "Contedores coñecidos por causar problemas",
"content-trust": "A validación de confianza de contido está desactivada",
"dbus": "DBUS",
"docker_configuration": "Configuración de Docker",
"docker_version": "Versión Docker",
@@ -384,15 +395,11 @@
}
},
"update_available": {
"description": "Tes instalada a {version}. Fai clic en actualizar para actualizar á versión {newest_version}",
"no_update": "Non hai ningunha actualización dispoñible para {name}"
}
},
"ui": {
"card": {
"area": {
"area_not_found": "Área non atopada"
},
"fan": {
"direction": "Enderezo",
"preset_mode": "Modo predefinido"
@@ -479,16 +486,10 @@
"show_entities": "Amosar entidades"
}
},
"history_charts": {
"no_history_found": "Non se atopou historial de estado."
},
"logbook": {
"messages": {
"became_unavailable": "deixou de estar dispoñible",
"detected_device_class": "detectouse {device_class}",
"is_closing": "estase pechando",
"is_opening": "estase abrindo",
"was_normal": "era normal"
"is_opening": "estase abrindo"
},
"show_trace": "Amosar rastro"
},
@@ -513,9 +514,6 @@
"filtered_by_device": "dispositivo: {device_name}",
"filtered_by_entity": "entidade: {entity_name}"
},
"related-items": {
"entity": "Entidades relacionadas"
},
"service-control": {
"required": "Este campo é necesario",
"service_data": "Datos do servizo",
@@ -533,23 +531,17 @@
"door": "Porta"
}
}
},
"related": "Relacionado"
},
"generic": {
"ok": "OK"
}
},
"more_info_control": {
"cover": {
"close_tilt_cover": "Pechar a inclinación da persiana"
},
"dismiss": "Ocultar a fiestra de diálogo",
"remote": {
"activity": "Actividade actual"
}
},
"mqtt_device_debug_info": {
"entities": "Entidades",
"payload_display": "Amosar carga útil",
"show_as_yaml": "Amosar como YAML"
},
@@ -557,17 +549,10 @@
"commands": {
"navigation": {
"energy": "Enerxía",
"users": "Usuarios"
"tag": "Etiquetas"
},
"reload": {
"filesize": "Entidades de tamaño de ficheiro",
"mqtt": "Entidades MQTT configuradas manualmente",
"ping": "Ping a entidades binarias do sensor",
"reload": "{domain}",
"rpi_gpio": "Entidades GPIO de Raspberry Pi",
"smtp": "Servizos de notificación SMTP",
"themes": "Temas",
"trend": "Entidades de tendencia"
"themes": "Temas"
},
"types": {
"navigation": "Navegar",
@@ -579,8 +564,7 @@
"zha_device_info": {
"buttons": {
"device_children": "Ver fillos",
"view_in_visualization": "Ver en Visualización",
"view_network": "Ver rede"
"view_in_visualization": "Ver en Visualización"
},
"device_children": "Dispositivos Zigbee fillos"
},
@@ -601,15 +585,9 @@
"login-form": {
"password": "Contrasinal"
},
"notification_drawer": {
"close": "Cerrar"
},
"panel": {
"config": {
"areas": {
"editor": {
"create": "Crear"
},
"picker": {
"introduction2": "Para colocar os dispositivos nunha área, use a ligazón seguinte para navegar ata a páxina de integracións e, a continuación, faga clic nunha integración configurada para acceder ás tarxetas do dispositivo."
}
@@ -617,8 +595,6 @@
"automation": {
"editor": {
"actions": {
"alias": "Nome da acción",
"change_alias": "Ronomear acción",
"type": {
"device_id": {
"extra_fields": {
@@ -631,10 +607,7 @@
}
}
},
"automation_alias": "Nome da automatización",
"conditions": {
"testing_error": "Non cumpre coa condición",
"testing_pass": "Cumpre coa condición",
"type": {
"device": {
"extra_fields": {
@@ -649,11 +622,6 @@
}
}
},
"disable": "Desactivar",
"disabled": "Automatización desactivada",
"enable": "Activar",
"missing_name": "Non se pode grabar a automatización se non ten nome",
"rename": "Renomear",
"show_trace": "Mostrar rastro",
"triggers": {
"edit_id": "Editar ID do disparador",
@@ -670,7 +638,6 @@
}
},
"picker": {
"delete": "Borrar",
"dev_automation": "Depurar automatización",
"dev_only_editable": "Só se poden depurar os automatismos definidos en automations.yaml",
"show_info_automation": "Amosar información sobre a automatización"
@@ -688,7 +655,6 @@
"cloud": {
"account": {
"google": {
"devices_pin": "PIN de dispositivo de seguridade",
"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"
},
@@ -701,28 +667,60 @@
"target_browser": "Navegador"
},
"try": "Probar"
},
"webhooks": {
"no_hooks_yet": "Semella que aínda non tes webhooks. Comeza configurando un"
}
},
"dialog_cloudhook": {
"copied_to_clipboard": "copiado ao portapapeis"
},
"forgot_password": {
"instructions": "Introduce o teu enderezo de correo electrónico e enviarémosche unha ligazón para restablecer o teu contrasinal."
},
"google": {
"exposed": "{selected} exposto"
},
"register": {
"email_address": "Enderezo electrónico",
"title": "Rexistrar conta"
"email_address": "Enderezo electrónico"
}
},
"common": {
"learn_more": "Saber máis"
},
"dashboard": {
"automations": {
"description": "Xestiona automatizacións, escenas, guións e axudantes.",
"title": "Automatizacións e escenas"
},
"blueprints": {
"description": "Scripts e automatizacións preconstruídas pola comunidade",
"title": "Planos"
},
"companion": {
"description": "Localización e notificacións",
"title": "Aplicación complementaria"
},
"dashboards": {
"description": "Crea conxuntos de tarxetas personalizados para controlar o teu fogar",
"title": "Paneis de control"
},
"devices": {
"description": "Integracións, dispositivos, entidades e áreas",
"title": "Dispositivos e servizos"
},
"energy": {
"description": "Monitoriza a túa produción e consumo de enerxía",
"title": "Enerxía"
},
"people": {
"description": "Xestiona as persoas e as áreas que rastrexa o Home Assistant",
"title": "Persoas e Zonas"
},
"settings": {
"description": "Configuración básica, controis do servidor, rexistros e información",
"title": "Configuración"
},
"supervisor": {
"description": "Crea copias de seguridade, revisa os rexistros ou reinicia o teu sistema",
"title": "Complementos, copias de seguridade e Supervisor"
},
"tags": {
"description": "Activar automatizacións cando se escanea unha etiqueta NFC, un código QR, etc.",
"title": "Etiquetas"
}
},
"devices": {
"confirm_disable_config_entry": "Non hai máis dispositivos para a entrada de configuración {entry_name} . Queres deshabilitar a entrada de configuración?",
"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.",
@@ -787,17 +785,9 @@
},
"headers": {
"disabled_by": "Deshabilitado por"
},
"status": {
"disabled": "Desactivado"
}
}
},
"helpers": {
"types": {
"input_text": "Texto"
}
},
"integrations": {
"config_entry": {
"disable": {
@@ -813,10 +803,7 @@
"disable_restart_confirm": "Reinicie o Home Assistant para rematar de desactivar esta integración",
"enable_restart_confirm": "Reinicie o Home Assistant para rematar de habilitar esta integración",
"hardware": "Hardware: {version}",
"not_loaded": "Non cargado, comproba o {logs_link}",
"state": {
"setup_in_progress": "Inicializando"
}
"not_loaded": "Non cargado, comproba o {logs_link}"
},
"config_flow": {
"could_not_load": "Non se puido cargar o fluxo de configuración",
@@ -825,8 +812,7 @@
"pick_flow_step": {
"new_flow": "Non, configura outra instancia de {integration}",
"title": "Descubrimos estes, queres montalos?"
},
"success": "Éxito"
}
},
"confirm_new": "¿Queres configurar {integration} ?",
"disable": {
@@ -839,6 +825,7 @@
"show_ignored": "Amosar integracións ignoradas"
}
},
"introduction": "Aquí podes configurar os teus compoñentes e Home Assistant. Non todo se pode configurar graficamente aínda, pero estamos traballando niso.",
"logs": {
"level": {
"critical": "CRÍTICO",
@@ -851,9 +838,7 @@
"lovelace": {
"dashboards": {
"detail": {
"delete": "Borrar",
"show_sidebar": "Amosar na barra lateral",
"title_required": "O título é necesario"
"show_sidebar": "Amosar na barra lateral"
},
"picker": {
"headers": {
@@ -863,32 +848,40 @@
},
"description": "Crea conxuntos de tarxetas personalizados para controlar a túa casa"
},
"person": {
"detail": {
"local_only": "Somentes se pode iniciar sesión dende a rede local"
"ozw": {
"network_status": {
"unknown": "Descoñecido"
}
},
"scene": {
"editor": {
"entities": {
"add": "Engadir unha entidade"
}
},
"picker": {
"show_info_scene": "Amosar información sobre a escena"
}
},
"script": {
"picker": {
"learn_more": "Aprender máis sobre scripts",
"show_info": "Amosar información sobre o script"
}
},
"server_control": {
"section": {
"reloading": {
"filesize": "Entidades de tamaño de ficheiro",
"mqtt": "Entidades MQTT configuradas manualmente",
"ping": "Ping a entidades binarias do sensor",
"reload": "{domain}",
"rpi_gpio": "Entidades GPIO de Raspberry Pi",
"smtp": "Servizos de notificación SMTP",
"themes": "Temas",
"timer": "Temporizadores",
"trend": "Entidades de tendencia"
}
}
},
"users": {
"editor": {
"name": "Nome de visualización",
"password_changed": "O contrasinal cambiouse correctamente",
"username": "Usuario"
"password_changed": "O contrasinal cambiouse correctamente"
},
"picker": {
"headers": {
@@ -902,6 +895,17 @@
"refresh_topology": "Actualizar topoloxía"
}
},
"zwave": {
"migration": {
"ozw": {
"header": "Migrar a OpenZWave",
"introduction": "Este asistente axudarache a migrar da integración herdada de Z-Wave á integración OpenZWave que está actualmente en versión beta."
}
},
"ozw_log": {
"introduction": "Ver o rexistro. 0 é o mínimo (carga todo o rexistro) e 1000 é o máximo. Load amosará un rexistro estático e a cola actualizarase automaticamente co último número de liñas especificado no rexistro."
}
},
"zwave_js": {
"common": {
"back": "Volver"
@@ -912,12 +916,6 @@
"logs": {
"download_logs": "Descargar rexistros"
},
"navigation": {
"network": "Rede"
},
"network_status": {
"unknown": "Descoñecido"
},
"node_config": {
"attribution": "{device_database} proporciona os parámetros e as descricións de configuración do dispositivo",
"battery_device_notice": "Os dispositivos a batería deben estar espertos para actualizarla súa configuración. Consulte o manual do dispositivo para obter instrucións sobre como activalo.",
@@ -931,9 +929,6 @@
"confirm_unprovision_text": "Se desaprovisiona o dispositivo, non se engadirá ao Home Assistant cando estea prendido. Se xa está engadido ao Home Assistant, ao eliminar o dispositivo aprovisionado non se eliminará do Home Assistant.",
"included": "Incluído",
"not_included": "Non incluído"
},
"reinterview_node": {
"interview_complete": "Entrevista co dispositivo completa."
}
}
},
@@ -942,39 +937,17 @@
"services": {
"accepts_target": "Este servizo acepta un destino, por exemplo: `entity_id: light.bed_light`",
"all_parameters": "Todos os parámetros dispoñibles",
"call_service": "Chamar servizo",
"no_template_ui_support": "A interface de usuario non admite modelos, máis podes usar o editor YAML.",
"ui_mode": "Ir ao modo de interface de usuario",
"yaml_mode": "Ir ao modo YAML",
"yaml_parameters": "Os parámetros só están dispoñibles no modo YAML"
},
"states": {
"alert_entity_field": "Entidade é un campo obrigatorio",
"copy_id": "Copia a identificación ao portapapeis"
},
"statistics": {
"issues": {
"no_state": "Non hai estádo dispoñible para esta entidade"
}
},
"templates": {
"no_listeners": "Este modelo non escoita ningún evento e non se actualizará automaticamente.",
"unknown_error_template": "Erro descoñecido ao amosar o modelo"
},
"yaml": {
"section": {
"reloading": {
"filesize": "Entidades de tamaño de ficheiro",
"mqtt": "Entidades MQTT configuradas manualmente",
"ping": "Ping a entidades binarias do sensor",
"reload": "{domain}",
"rpi_gpio": "Entidades GPIO de Raspberry Pi",
"smtp": "Servizos de notificación SMTP",
"themes": "Temas",
"timer": "Temporizadores",
"trend": "Entidades de tendencia"
}
}
}
}
},
@@ -982,8 +955,7 @@
"cards": {
"confirm_delete": "Seguro que queres eliminar esta tarxeta?",
"picture-elements": {
"more_info": "Amosar máis información: {name}",
"url": "Abrir fiestra a {url_path}"
"more_info": "Amosar máis información: {name}"
}
},
"editor": {
@@ -1010,9 +982,6 @@
"condition_explanation": "A tarxeta amosarase cando se cumpran TODAS as seguintes condicións.",
"description": "A tarxeta Condicional mostra outra tarxeta en función dos estados da entidade."
},
"config": {
"optional": "Opcional"
},
"entities": {
"description": "A tarxeta de entidades é o tipo de tarxeta máis común. Agrupa elementos en listas.",
"show_header_toggle": "Queres amosar o interruptor na cabeceira?"
@@ -1029,7 +998,6 @@
"generic": {
"hours_to_show": "Horas para amosar",
"manual_description": "Necesitas engadir unha tarxeta personalizada ou só queres escribir manualmente o YAML?",
"maximum": "Máximo",
"show_icon": "Amosar icona?",
"show_name": "Amosar nome?",
"show_state": "Amosar estado?"
@@ -1066,8 +1034,7 @@
"hours_to_show": "Horas para amosar"
},
"markdown": {
"description": "A tarxeta Markdown úsase para representar Markdown.",
"name": "Markdown"
"description": "A tarxeta Markdown úsase para representar Markdown."
},
"media-control": {
"description": "A tarxeta Media Control úsase para amosar as entidades do reprodutor multimedia nunha interface con controis fáciles de usar."
@@ -1103,12 +1070,16 @@
},
"weather-forecast": {
"description": "A tarxeta Predición metereolóxica mostra o tempo. Moi útil para incluír nas interfaces que a xente amosa na parede.",
"name": "Predición metereolóxica"
"name": "Predición metereolóxica",
"show_forecast": "Amosar predición"
}
},
"cardpicker": {
"by_card": "Por tarxeta"
},
"edit_badges": {
"panel_mode": "Estas insignias non se amosarán porque esta vista está en \"modo de panel\"."
},
"edit_card": {
"add": "Engadir tarxeta",
"delete": "Eliminar tarxeta",
@@ -1123,13 +1094,6 @@
"show_visual_editor": "Amosar editor visual",
"typed_header": "Configuración da tarxeta {type}"
},
"header-footer": {
"types": {
"graph": {
"name": "Gráfico"
}
}
},
"menu": {
"manage_dashboards": "Xestionar paneis",
"manage_resources": "Xestionar recursos"
@@ -1188,13 +1152,6 @@
}
},
"profile": {
"change_password": {
"success": "A contrasinal cambiouse correctamente"
},
"enable_shortcuts": {
"header": "Atallos de teclado"
},
"logout_text": "¿Estás seguro de querer cerrar sesión?",
"long_lived_access_tokens": {
"prompt_copy_token": "Copia o teu token de acceso. Non aparecerá de novo."
},

View File

@@ -51,13 +51,6 @@
"home": "Dahei"
}
},
"supervisor": {
"addon": {
"panel": {
"documentation": "Beschribig"
}
}
},
"ui": {
"card": {
"alarm_control_panel": {
@@ -141,9 +134,7 @@
"wnw": "WNW",
"wsw": "WSW"
},
"forecast": "Vorhärsag",
"high": "höch",
"low": "wenig"
"forecast": "Vorhärsag"
}
},
"common": {
@@ -153,11 +144,6 @@
"refresh": "Neu laden"
},
"components": {
"area-picker": {
"add_dialog": {
"name": "Name"
}
},
"date-range-picker": {
"ranges": {
"last_week": "Letschti Wuche",
@@ -188,18 +174,6 @@
"statistic": "Statistik"
}
},
"dialogs": {
"entity_registry": {
"editor": {
"name": "Name"
}
},
"helper_settings": {
"generic": {
"name": "Name"
}
}
},
"login-form": {
"password": "Passwort"
},
@@ -208,21 +182,6 @@
},
"panel": {
"config": {
"application_credentials": {
"editor": {
"name": "Name"
},
"picker": {
"headers": {
"name": "Name"
}
}
},
"areas": {
"editor": {
"name": "Name"
}
},
"automation": {
"caption": "Automation",
"editor": {
@@ -311,69 +270,33 @@
}
}
}
},
"picker": {
"headers": {
"name": "Name"
}
}
},
"blueprint": {
"overview": {
"headers": {
"name": "Name"
}
}
},
"cloud": {
"account": {
"tts": {
"dialog": {
"play": "Abspilä"
}
}
}
},
"core": {
"section": {
"core": {
"core_config": {
"currency": "Währig",
"location_name": "Name"
"currency": "Währig"
}
}
}
},
"devices": {
"device_info": "Geräteinformationen",
"name": "Name"
"scripts": "Scripts"
},
"energy": {
"device_consumption": {
"title": "Einzelne Devices"
}
},
"entities": {
"picker": {
"headers": {
"name": "Name"
}
}
},
"helpers": {
"picker": {
"headers": {
"name": "Name"
}
}
},
"integrations": {
"caption": "Integratione",
"config_entry": {
"check_the_logs": "Lug Logs a",
"device_unavailable": "Grät verfüägbar",
"entity_unavailable": "Entitiy verfüägbar",
"firmware": "Firmware: {version}",
"manuf": "vo {manufacturer}",
"rename": "Umschribä"
"manuf": "vo {manufacturer}"
},
"configure": "Konfiguriärä",
"configured": "Konfiguriärt",
@@ -384,75 +307,16 @@
"mqtt": {
"title": "MQTT"
},
"person": {
"detail": {
"name": "Name"
}
},
"scene": {
"editor": {
"name": "Name"
},
"picker": {
"headers": {
"name": "Name"
}
}
},
"script": {
"editor": {
"alias": "Name"
},
"picker": {
"headers": {
"name": "Name"
}
}
},
"tag": {
"detail": {
"name": "Name"
},
"headers": {
"name": "Name"
}
},
"users": {
"caption": "Benutzer",
"editor": {
"delete_user": "Benutzer lösche"
}
},
"zone": {
"detail": {
"name": "Name"
}
}
},
"lovelace": {
"editor": {
"card": {
"generic": {
"name": "Name"
}
}
}
},
"mailbox": {
"delete_button": "Lösche"
},
"page-authorize": {
"form": {
"working": "Bitte Wartä"
}
},
"page-onboarding": {
"user": {
"data": {
"name": "Name"
}
}
},
"profile": {
"change_password": {
"confirm_new_password": "Nöis Passwort bestätige",
@@ -463,12 +327,8 @@
"submit": "Abschicke"
},
"is_owner": "Du bisch dr Bsitzer",
"language": {
"header": "Sprach"
},
"long_lived_access_tokens": {
"create": "Token erzüge",
"name": "Name",
"prompt_name": "Name?"
},
"mfa": {

File diff suppressed because it is too large Load Diff

Some files were not shown because too many files have changed in this diff Show More