mirror of
https://github.com/home-assistant/frontend.git
synced 2026-09-16 23:20:38 +00:00
Compare commits
49
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f69bce534a | ||
|
|
575f58bd88 | ||
|
|
35535628fc | ||
|
|
8e018c9cfe | ||
|
|
5ae268b792 | ||
|
|
329732ac30 | ||
|
|
7f88bab552 | ||
|
|
9f3bb7f4d6 | ||
|
|
73bb346c00 | ||
|
|
33703a3b53 | ||
|
|
b7a4f97eca | ||
|
|
dd4efe0f51 | ||
|
|
7e0522c3b3 | ||
|
|
e682abfb75 | ||
|
|
24e202a3d7 | ||
|
|
ac9a881ab5 | ||
|
|
4d287a1f83 | ||
|
|
b8d6b1ebdd | ||
|
|
8ca1b9320d | ||
|
|
cba3992d2b | ||
|
|
96d6e337be | ||
|
|
959f7ae046 | ||
|
|
9572a58764 | ||
|
|
393ae9e5dc | ||
|
|
63e10314bd | ||
|
|
b599417a37 | ||
|
|
899eab4e5c | ||
|
|
3f21c87a3d | ||
|
|
c296a60bab | ||
|
|
5f78f18cb4 | ||
|
|
0b8d356865 | ||
|
|
e8d1318a5b | ||
|
|
07ce07c4a5 | ||
|
|
a07220f383 | ||
|
|
f21ed24a49 | ||
|
|
e3c38b93f4 | ||
|
|
b398727413 | ||
|
|
9bc2ab29a1 | ||
|
|
51f1ff26f1 | ||
|
|
97d5e6512d | ||
|
|
b76c67fc9b | ||
|
|
b96a70cd55 | ||
|
|
982ab93cdb | ||
|
|
c7f4e1152d | ||
|
|
519988326b | ||
|
|
b518f4b03c | ||
|
|
5493fdfcb7 | ||
|
|
179767e9f8 | ||
|
|
25b3bb1285 |
@@ -1,8 +1,6 @@
|
||||
name: Report a bug with the UI, Frontend or Lovelace
|
||||
about: Report an issue related to the Home Assistant frontend.
|
||||
description: Report an issue related to the Home Assistant frontend.
|
||||
labels: bug
|
||||
title: ""
|
||||
issue_body: true
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
@@ -97,11 +95,7 @@ body:
|
||||
If your issue is about how an entity is shown in the UI, please add the
|
||||
state and attributes for all situations. You can find this information
|
||||
at Developer Tools -> States.
|
||||
value: |
|
||||
```yaml
|
||||
# Paste your state here.
|
||||
|
||||
```
|
||||
render: txt
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Problem-relevant frontend configuration
|
||||
@@ -110,29 +104,18 @@ body:
|
||||
configuration of the used cards. Fill this out even if it seems
|
||||
unimportant to you. Please be sure to remove personal information like
|
||||
passwords, private URLs and other credentials.
|
||||
value: |
|
||||
```yaml
|
||||
# Paste your YAML here.
|
||||
|
||||
```
|
||||
render: yaml
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Javascript errors shown in your browser console/inspector
|
||||
description: >
|
||||
If you come across any Javascript or other error logs, e.g., in your
|
||||
browser console/inspector please provide them.
|
||||
value: |
|
||||
```txt
|
||||
# Paste your logs here.
|
||||
|
||||
```
|
||||
- type: markdown
|
||||
render: txt
|
||||
- type: textarea
|
||||
attributes:
|
||||
value: |
|
||||
## Additional information
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
label: Additional information
|
||||
description: >
|
||||
If you have any additional information for us, use the field below.
|
||||
Please note, you can attach screenshots or screen recordings here,
|
||||
by dragging and dropping files in the field below.
|
||||
Please note, you can attach screenshots or screen recordings here, by
|
||||
dragging and dropping files in the field below.
|
||||
|
||||
@@ -35,6 +35,7 @@ class HcLovelace extends LitElement {
|
||||
}
|
||||
const lovelace: Lovelace = {
|
||||
config: this.lovelaceConfig,
|
||||
rawConfig: this.lovelaceConfig,
|
||||
editMode: false,
|
||||
urlPath: this.urlPath!,
|
||||
enableFullEditMode: () => undefined,
|
||||
|
||||
@@ -221,11 +221,17 @@ export class HcMain extends HassElement {
|
||||
}
|
||||
|
||||
private async _generateLovelaceConfig() {
|
||||
const { generateLovelaceConfigFromHass } = await import(
|
||||
"../../../../src/panels/lovelace/common/generate-lovelace-config"
|
||||
const { generateLovelaceDashboardStrategy } = await import(
|
||||
"../../../../src/panels/lovelace/strategies/get-strategy"
|
||||
);
|
||||
this._handleNewLovelaceConfig(
|
||||
await generateLovelaceConfigFromHass(this.hass!)
|
||||
await generateLovelaceDashboardStrategy(
|
||||
{
|
||||
hass: this.hass!,
|
||||
narrow: false,
|
||||
},
|
||||
"original-states"
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -39,14 +39,16 @@ const createConfigEntry = (
|
||||
supports_options: false,
|
||||
supports_unload: true,
|
||||
disabled_by: null,
|
||||
reason: null,
|
||||
...override,
|
||||
});
|
||||
|
||||
const createManifest = (
|
||||
isCustom: boolean,
|
||||
isCloud: boolean
|
||||
isCloud: boolean,
|
||||
name = "ESPHome"
|
||||
): IntegrationManifest => ({
|
||||
name: "ESPHome",
|
||||
name,
|
||||
domain: "esphome",
|
||||
is_built_in: !isCustom,
|
||||
config_flow: false,
|
||||
@@ -75,6 +77,14 @@ const migrationErrorEntry = createConfigEntry("Migration Error", {
|
||||
const setupRetryEntry = createConfigEntry("Setup Retry", {
|
||||
state: "setup_retry",
|
||||
});
|
||||
const setupRetryReasonEntry = createConfigEntry("Setup Retry", {
|
||||
state: "setup_retry",
|
||||
reason: "connection_error",
|
||||
});
|
||||
const setupRetryReasonMissingKeyEntry = createConfigEntry("Setup Retry", {
|
||||
state: "setup_retry",
|
||||
reason: "resolve_error",
|
||||
});
|
||||
const failedUnloadEntry = createConfigEntry("Failed Unload", {
|
||||
state: "failed_unload",
|
||||
});
|
||||
@@ -103,7 +113,7 @@ const configFlows: DataEntryFlowProgressExtended[] = [
|
||||
},
|
||||
},
|
||||
step_id: "discovery_confirm",
|
||||
localized_title: "Roku: Living room Roku",
|
||||
localized_title: "Living room Roku",
|
||||
},
|
||||
{
|
||||
flow_id: "adbb401329d8439ebb78ef29837826a8",
|
||||
@@ -134,14 +144,16 @@ const configEntries: Array<{
|
||||
{ items: [setupErrorEntry] },
|
||||
{ items: [migrationErrorEntry] },
|
||||
{ items: [setupRetryEntry] },
|
||||
{ items: [setupRetryReasonEntry] },
|
||||
{ items: [setupRetryReasonMissingKeyEntry] },
|
||||
{ items: [failedUnloadEntry] },
|
||||
{ items: [notLoadedEntry] },
|
||||
{
|
||||
items: [
|
||||
loadedEntry,
|
||||
longNameEntry,
|
||||
setupErrorEntry,
|
||||
migrationErrorEntry,
|
||||
longNameEntry,
|
||||
setupRetryEntry,
|
||||
failedUnloadEntry,
|
||||
notLoadedEntry,
|
||||
@@ -211,47 +223,78 @@ export class DemoIntegrationCard extends LitElement {
|
||||
return html``;
|
||||
}
|
||||
return html`
|
||||
<div class="filters">
|
||||
<ha-formfield label="Custom Integration">
|
||||
<ha-switch @change=${this._toggleCustomIntegration}></ha-switch>
|
||||
</ha-formfield>
|
||||
<ha-formfield label="Relies on cloud">
|
||||
<ha-switch @change=${this._toggleCloud}></ha-switch>
|
||||
</ha-formfield>
|
||||
<div class="container">
|
||||
<div class="filters">
|
||||
<ha-formfield label="Custom Integration">
|
||||
<ha-switch @change=${this._toggleCustomIntegration}></ha-switch>
|
||||
</ha-formfield>
|
||||
<ha-formfield label="Relies on cloud">
|
||||
<ha-switch @change=${this._toggleCloud}></ha-switch>
|
||||
</ha-formfield>
|
||||
</div>
|
||||
|
||||
<ha-ignored-config-entry-card
|
||||
.hass=${this.hass}
|
||||
.entry=${createConfigEntry("Ignored Entry")}
|
||||
.manifest=${createManifest(this.isCustomIntegration, this.isCloud)}
|
||||
></ha-ignored-config-entry-card>
|
||||
|
||||
${configFlows.map(
|
||||
(flow) => html`
|
||||
<ha-config-flow-card
|
||||
.hass=${this.hass}
|
||||
.flow=${flow}
|
||||
.manifest=${createManifest(
|
||||
this.isCustomIntegration,
|
||||
this.isCloud,
|
||||
flow.handler === "roku" ? "Roku" : "Philips Hue"
|
||||
)}
|
||||
></ha-config-flow-card>
|
||||
`
|
||||
)}
|
||||
${configEntries.map(
|
||||
(info) => html`
|
||||
<ha-integration-card
|
||||
class=${classMap({
|
||||
highlight: info.highlight !== undefined,
|
||||
})}
|
||||
.hass=${this.hass}
|
||||
domain="esphome"
|
||||
.items=${info.items}
|
||||
.manifest=${createManifest(
|
||||
this.isCustomIntegration,
|
||||
this.isCloud
|
||||
)}
|
||||
.entityRegistryEntries=${createEntityRegistryEntries(
|
||||
info.items[0]
|
||||
)}
|
||||
.deviceRegistryEntries=${createDeviceRegistryEntries(
|
||||
info.items[0]
|
||||
)}
|
||||
?disabled=${info.disabled}
|
||||
.selectedConfigEntryId=${info.highlight}
|
||||
></ha-integration-card>
|
||||
`
|
||||
)}
|
||||
</div>
|
||||
<div class="container">
|
||||
<!-- One that is standalone to see how it increases height if height
|
||||
not defined by other cards. -->
|
||||
<ha-integration-card
|
||||
.hass=${this.hass}
|
||||
domain="esphome"
|
||||
.items=${[
|
||||
loadedEntry,
|
||||
setupErrorEntry,
|
||||
migrationErrorEntry,
|
||||
setupRetryEntry,
|
||||
failedUnloadEntry,
|
||||
]}
|
||||
.manifest=${createManifest(this.isCustomIntegration, this.isCloud)}
|
||||
.entityRegistryEntries=${createEntityRegistryEntries(loadedEntry)}
|
||||
.deviceRegistryEntries=${createDeviceRegistryEntries(loadedEntry)}
|
||||
></ha-integration-card>
|
||||
</div>
|
||||
|
||||
<ha-ignored-config-entry-card
|
||||
.hass=${this.hass}
|
||||
.entry=${createConfigEntry("Ignored Entry")}
|
||||
.manifest=${createManifest(this.isCustomIntegration, this.isCloud)}
|
||||
></ha-ignored-config-entry-card>
|
||||
|
||||
${configFlows.map(
|
||||
(flow) => html`
|
||||
<ha-config-flow-card
|
||||
.hass=${this.hass}
|
||||
.flow=${flow}
|
||||
.manifest=${createManifest(this.isCustomIntegration, this.isCloud)}
|
||||
></ha-config-flow-card>
|
||||
`
|
||||
)}
|
||||
${configEntries.map(
|
||||
(info) => html`
|
||||
<ha-integration-card
|
||||
class=${classMap({
|
||||
highlight: info.highlight !== undefined,
|
||||
})}
|
||||
.hass=${this.hass}
|
||||
domain="esphome"
|
||||
.items=${info.items}
|
||||
.manifest=${createManifest(this.isCustomIntegration, this.isCloud)}
|
||||
.entityRegistryEntries=${createEntityRegistryEntries(info.items[0])}
|
||||
.deviceRegistryEntries=${createDeviceRegistryEntries(info.items[0])}
|
||||
?disabled=${info.disabled}
|
||||
.selectedConfigEntryId=${info.highlight}
|
||||
></ha-integration-card>
|
||||
`
|
||||
)}
|
||||
`;
|
||||
}
|
||||
|
||||
@@ -260,6 +303,14 @@ export class DemoIntegrationCard extends LitElement {
|
||||
const hass = provideHass(this);
|
||||
hass.updateTranslations(null, "en");
|
||||
hass.updateTranslations("config", "en");
|
||||
// Normally this string is loaded from backend
|
||||
hass.addTranslations(
|
||||
{
|
||||
"component.esphome.config.error.connection_error":
|
||||
"Can't connect to ESP. Please make sure your YAML file contains an 'api:' line.",
|
||||
},
|
||||
"en"
|
||||
);
|
||||
}
|
||||
|
||||
private _toggleCustomIntegration() {
|
||||
@@ -272,7 +323,7 @@ export class DemoIntegrationCard extends LitElement {
|
||||
|
||||
static get styles() {
|
||||
return css`
|
||||
:host {
|
||||
.container {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
|
||||
grid-gap: 16px 16px;
|
||||
@@ -280,7 +331,7 @@ export class DemoIntegrationCard extends LitElement {
|
||||
margin-bottom: 64px;
|
||||
}
|
||||
|
||||
:host > * {
|
||||
.container > * {
|
||||
max-width: 500px;
|
||||
}
|
||||
|
||||
|
||||
@@ -177,8 +177,9 @@ class HassioAddonDashboard extends LitElement {
|
||||
const requestedAddon = extractSearchParam("addon");
|
||||
if (requestedAddon) {
|
||||
const addonsInfo = await fetchHassioAddonsInfo(this.hass);
|
||||
const validAddon = addonsInfo.addons
|
||||
.some((addon) => addon.slug === requestedAddon);
|
||||
const validAddon = addonsInfo.addons.some(
|
||||
(addon) => addon.slug === requestedAddon
|
||||
);
|
||||
if (!validAddon) {
|
||||
this._error = this.supervisor.localize("my.error_addon_not_found");
|
||||
} else {
|
||||
|
||||
@@ -242,14 +242,18 @@ class HassioAddonInfo extends LitElement {
|
||||
? html`
|
||||
Current version: ${this.addon.version}
|
||||
<div class="changelog" @click=${this._openChangelog}>
|
||||
(<span class="changelog-link">${
|
||||
this.supervisor.localize("addon.dashboard.changelog")}</span
|
||||
(<span class="changelog-link"
|
||||
>${this.supervisor.localize(
|
||||
"addon.dashboard.changelog"
|
||||
)}</span
|
||||
>)
|
||||
</div>
|
||||
`
|
||||
: html`<span class="changelog-link" @click=${this._openChangelog}>${
|
||||
this.supervisor.localize("addon.dashboard.changelog")
|
||||
}</span>`}
|
||||
: html`<span class="changelog-link" @click=${this._openChangelog}
|
||||
>${this.supervisor.localize(
|
||||
"addon.dashboard.changelog"
|
||||
)}</span
|
||||
>`}
|
||||
</div>
|
||||
|
||||
<div class="description light-color">
|
||||
|
||||
@@ -44,7 +44,10 @@ export class HassioMain extends SupervisorBaseElement {
|
||||
// We changed the navigate event to fire directly on the window, as that's
|
||||
// where we are listening for it. However, the older panel_custom will
|
||||
// listen on this element for navigation events, so we need to forward them.
|
||||
window.addEventListener("location-changed", (ev) =>
|
||||
|
||||
// Joakim - April 26, 2021
|
||||
// Due to changes in behavior in Google Chrome, we changed navigate to fire on the top element
|
||||
top.addEventListener("location-changed", (ev) =>
|
||||
// @ts-ignore
|
||||
fireEvent(this, ev.type, ev.detail, {
|
||||
bubbles: false,
|
||||
|
||||
+1
-2
@@ -108,7 +108,7 @@
|
||||
"fecha": "^4.2.0",
|
||||
"fuse.js": "^6.0.0",
|
||||
"google-timezones-json": "^1.0.2",
|
||||
"hls.js": "^0.13.2",
|
||||
"hls.js": "^1.0.1",
|
||||
"home-assistant-js-websocket": "^5.9.0",
|
||||
"idb-keyval": "^3.2.0",
|
||||
"intl-messageformat": "^8.3.9",
|
||||
@@ -167,7 +167,6 @@
|
||||
"@types/chromecast-caf-receiver": "^5.0.11",
|
||||
"@types/chromecast-caf-sender": "^1.0.3",
|
||||
"@types/codemirror": "^0.0.97",
|
||||
"@types/hls.js": "^0.12.3",
|
||||
"@types/js-yaml": "^3.12.1",
|
||||
"@types/leaflet": "^1.4.3",
|
||||
"@types/leaflet-draw": "^1.0.1",
|
||||
|
||||
@@ -2,7 +2,7 @@ from setuptools import setup, find_packages
|
||||
|
||||
setup(
|
||||
name="home-assistant-frontend",
|
||||
version="20210407.1",
|
||||
version="20210423.0",
|
||||
description="The Home Assistant frontend",
|
||||
url="https://github.com/home-assistant/home-assistant-polymer",
|
||||
author="The Home Assistant Authors",
|
||||
|
||||
@@ -8,6 +8,7 @@ import {
|
||||
PropertyValues,
|
||||
} from "lit-element";
|
||||
import punycode from "punycode";
|
||||
import { applyThemesOnElement } from "../common/dom/apply_themes_on_element";
|
||||
import { extractSearchParamsObject } from "../common/url/search-params";
|
||||
import {
|
||||
AuthProvider,
|
||||
@@ -116,6 +117,20 @@ class HaAuthorize extends litLocalizeLiteMixin(LitElement) {
|
||||
this._fetchAuthProviders();
|
||||
this._fetchDiscoveryInfo();
|
||||
|
||||
if (matchMedia("(prefers-color-scheme: dark)").matches) {
|
||||
applyThemesOnElement(
|
||||
document.documentElement,
|
||||
{
|
||||
default_theme: "default",
|
||||
default_dark_theme: null,
|
||||
themes: {},
|
||||
darkMode: false,
|
||||
},
|
||||
"default",
|
||||
{ dark: true }
|
||||
);
|
||||
}
|
||||
|
||||
if (!this.redirectUri) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -70,13 +70,18 @@ export const applyThemesOnElement = (
|
||||
themeRules["text-accent-color"] =
|
||||
rgbContrast(rgbAccentColor, [33, 33, 33]) < 6 ? "#fff" : "#212121";
|
||||
}
|
||||
|
||||
// Nothing was changed
|
||||
if (element._themes?.cacheKey === cacheKey) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (selectedTheme && themes.themes[selectedTheme]) {
|
||||
themeRules = themes.themes[selectedTheme];
|
||||
}
|
||||
|
||||
if (!element._themes && !Object.keys(themeRules).length) {
|
||||
if (!element._themes?.keys && !Object.keys(themeRules).length) {
|
||||
// No styles to reset, and no styles to set
|
||||
return;
|
||||
}
|
||||
@@ -87,8 +92,8 @@ export const applyThemesOnElement = (
|
||||
: undefined;
|
||||
|
||||
// Add previous set keys to reset them, and new theme
|
||||
const styles = { ...element._themes, ...newTheme?.styles };
|
||||
element._themes = newTheme?.keys;
|
||||
const styles = { ...element._themes?.keys, ...newTheme?.styles };
|
||||
element._themes = { cacheKey, keys: newTheme?.keys };
|
||||
|
||||
// Set and/or reset styles
|
||||
if (element.updateStyles) {
|
||||
|
||||
+11
-7
@@ -12,20 +12,24 @@ declare global {
|
||||
export const navigate = (_node: any, path: string, replace = false) => {
|
||||
if (__DEMO__) {
|
||||
if (replace) {
|
||||
history.replaceState(
|
||||
history.state?.root ? { root: true } : null,
|
||||
top.history.replaceState(
|
||||
top.history.state?.root ? { root: true } : null,
|
||||
"",
|
||||
`${location.pathname}#${path}`
|
||||
`${top.location.pathname}#${path}`
|
||||
);
|
||||
} else {
|
||||
window.location.hash = path;
|
||||
top.location.hash = path;
|
||||
}
|
||||
} else if (replace) {
|
||||
history.replaceState(history.state?.root ? { root: true } : null, "", path);
|
||||
top.history.replaceState(
|
||||
top.history.state?.root ? { root: true } : null,
|
||||
"",
|
||||
path
|
||||
);
|
||||
} else {
|
||||
history.pushState(null, "", path);
|
||||
top.history.pushState(null, "", path);
|
||||
}
|
||||
fireEvent(window, "location-changed", {
|
||||
fireEvent(top, "location-changed", {
|
||||
replace,
|
||||
});
|
||||
};
|
||||
|
||||
@@ -58,7 +58,7 @@ export const formatNumber = (
|
||||
).format(Number(num));
|
||||
}
|
||||
}
|
||||
return num ? num.toString() : "";
|
||||
return num.toString();
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import type HlsType from "hls.js";
|
||||
import {
|
||||
css,
|
||||
CSSResult,
|
||||
@@ -15,8 +16,6 @@ import { nextRender } from "../common/util/render-status";
|
||||
import { getExternalConfig } from "../external_app/external_config";
|
||||
import type { HomeAssistant } from "../types";
|
||||
|
||||
type HLSModule = typeof import("hls.js");
|
||||
|
||||
@customElement("ha-hls-player")
|
||||
class HaHLSPlayer extends LitElement {
|
||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||
@@ -43,7 +42,7 @@ class HaHLSPlayer extends LitElement {
|
||||
|
||||
@internalProperty() private _attached = false;
|
||||
|
||||
private _hlsPolyfillInstance?: Hls;
|
||||
private _hlsPolyfillInstance?: HlsType;
|
||||
|
||||
private _useExoPlayer = false;
|
||||
|
||||
@@ -107,8 +106,8 @@ class HaHLSPlayer extends LitElement {
|
||||
const useExoPlayerPromise = this._getUseExoPlayer();
|
||||
const masterPlaylistPromise = fetch(this.url);
|
||||
|
||||
const hls = ((await import("hls.js")) as any).default as HLSModule;
|
||||
let hlsSupported = hls.isSupported();
|
||||
const Hls = (await import("hls.js")).default;
|
||||
let hlsSupported = Hls.isSupported();
|
||||
|
||||
if (!hlsSupported) {
|
||||
hlsSupported =
|
||||
@@ -144,8 +143,8 @@ class HaHLSPlayer extends LitElement {
|
||||
// If codec is HEVC and ExoPlayer is supported, use ExoPlayer.
|
||||
if (this._useExoPlayer && match !== null && match[1] !== undefined) {
|
||||
this._renderHLSExoPlayer(playlist_url);
|
||||
} else if (hls.isSupported()) {
|
||||
this._renderHLSPolyfill(videoEl, hls, playlist_url);
|
||||
} else if (Hls.isSupported()) {
|
||||
this._renderHLSPolyfill(videoEl, Hls, playlist_url);
|
||||
} else {
|
||||
this._renderHLSNative(videoEl, playlist_url);
|
||||
}
|
||||
@@ -182,7 +181,7 @@ class HaHLSPlayer extends LitElement {
|
||||
|
||||
private async _renderHLSPolyfill(
|
||||
videoEl: HTMLVideoElement,
|
||||
Hls: HLSModule,
|
||||
Hls: typeof HlsType,
|
||||
url: string
|
||||
) {
|
||||
const hls = new Hls({
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { mdiHelpCircle } from "@mdi/js";
|
||||
import { HassService, HassServiceTarget } from "home-assistant-js-websocket";
|
||||
import {
|
||||
css,
|
||||
@@ -18,11 +19,12 @@ import { ENTITY_COMPONENT_DOMAINS } from "../data/entity";
|
||||
import { Selector } from "../data/selector";
|
||||
import { PolymerChangedEvent } from "../polymer-types";
|
||||
import { HomeAssistant } from "../types";
|
||||
import { documentationUrl } from "../util/documentation-url";
|
||||
import "./ha-checkbox";
|
||||
import "./ha-selector/ha-selector";
|
||||
import "./ha-service-picker";
|
||||
import "./ha-settings-row";
|
||||
import "./ha-yaml-editor";
|
||||
import "./ha-checkbox";
|
||||
import type { HaYamlEditor } from "./ha-yaml-editor";
|
||||
|
||||
interface ExtHassService extends Omit<HassService, "fields"> {
|
||||
@@ -49,6 +51,8 @@ export class HaServiceControl extends LitElement {
|
||||
data?: Record<string, any>;
|
||||
};
|
||||
|
||||
@internalProperty() private _value!: this["value"];
|
||||
|
||||
@property({ reflect: true, type: Boolean }) public narrow!: boolean;
|
||||
|
||||
@property({ type: Boolean }) public showAdvanced?: boolean;
|
||||
@@ -57,7 +61,7 @@ export class HaServiceControl extends LitElement {
|
||||
|
||||
@query("ha-yaml-editor") private _yamlEditor?: HaYamlEditor;
|
||||
|
||||
protected updated(changedProperties: PropertyValues) {
|
||||
protected updated(changedProperties: PropertyValues<this>) {
|
||||
if (!changedProperties.has("value")) {
|
||||
return;
|
||||
}
|
||||
@@ -92,21 +96,23 @@ export class HaServiceControl extends LitElement {
|
||||
target.device_id = this.value.data.device_id;
|
||||
}
|
||||
|
||||
this.value = {
|
||||
this._value = {
|
||||
...this.value,
|
||||
target,
|
||||
data: { ...this.value.data },
|
||||
};
|
||||
|
||||
delete this.value.data!.entity_id;
|
||||
delete this.value.data!.device_id;
|
||||
delete this.value.data!.area_id;
|
||||
delete this._value.data!.entity_id;
|
||||
delete this._value.data!.device_id;
|
||||
delete this._value.data!.area_id;
|
||||
} else {
|
||||
this._value = this.value;
|
||||
}
|
||||
|
||||
if (this.value?.data) {
|
||||
if (this._value?.data) {
|
||||
const yamlEditor = this._yamlEditor;
|
||||
if (yamlEditor && yamlEditor.value !== this.value.data) {
|
||||
yamlEditor.setValue(this.value.data);
|
||||
if (yamlEditor && yamlEditor.value !== this._value.data) {
|
||||
yamlEditor.setValue(this._value.data);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -151,12 +157,12 @@ export class HaServiceControl extends LitElement {
|
||||
});
|
||||
|
||||
protected render() {
|
||||
const serviceData = this._getServiceInfo(this.value?.service);
|
||||
const serviceData = this._getServiceInfo(this._value?.service);
|
||||
|
||||
const shouldRenderServiceDataYaml =
|
||||
(serviceData?.fields.length && !serviceData.hasSelector.length) ||
|
||||
(serviceData &&
|
||||
Object.keys(this.value?.data || {}).some(
|
||||
Object.keys(this._value?.data || {}).some(
|
||||
(key) => !serviceData!.hasSelector.includes(key)
|
||||
));
|
||||
|
||||
@@ -171,10 +177,32 @@ export class HaServiceControl extends LitElement {
|
||||
|
||||
return html`<ha-service-picker
|
||||
.hass=${this.hass}
|
||||
.value=${this.value?.service}
|
||||
.value=${this._value?.service}
|
||||
@value-changed=${this._serviceChanged}
|
||||
></ha-service-picker>
|
||||
<p>${serviceData?.description}</p>
|
||||
<div class="description">
|
||||
<p>${serviceData?.description}</p>
|
||||
${this.value?.service
|
||||
? html` <a
|
||||
href="${documentationUrl(
|
||||
this.hass,
|
||||
"/integrations/" + computeDomain(this.value?.service)
|
||||
)}"
|
||||
title="${this.hass.localize(
|
||||
"ui.components.service-control.integration_doc"
|
||||
)}"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>
|
||||
<mwc-icon-button>
|
||||
<ha-svg-icon
|
||||
path=${mdiHelpCircle}
|
||||
class="help-icon"
|
||||
></ha-svg-icon>
|
||||
</mwc-icon-button>
|
||||
</a>`
|
||||
: ""}
|
||||
</div>
|
||||
${serviceData && "target" in serviceData
|
||||
? html`<ha-settings-row .narrow=${this.narrow}>
|
||||
${hasOptional
|
||||
@@ -195,19 +223,19 @@ export class HaServiceControl extends LitElement {
|
||||
? { target: serviceData.target }
|
||||
: {
|
||||
target: {
|
||||
entity: { domain: computeDomain(this.value!.service) },
|
||||
entity: { domain: computeDomain(this._value!.service) },
|
||||
},
|
||||
}}
|
||||
@value-changed=${this._targetChanged}
|
||||
.value=${this.value?.target}
|
||||
.value=${this._value?.target}
|
||||
></ha-selector
|
||||
></ha-settings-row>`
|
||||
: entityId
|
||||
? html`<ha-entity-picker
|
||||
.hass=${this.hass}
|
||||
.value=${this.value?.data?.entity_id}
|
||||
.value=${this._value?.data?.entity_id}
|
||||
.label=${entityId.description}
|
||||
.includeDomains=${this._domainFilter(this.value!.service)}
|
||||
.includeDomains=${this._domainFilter(this._value!.service)}
|
||||
@value-changed=${this._entityPicked}
|
||||
allow-custom-entity
|
||||
></ha-entity-picker>`
|
||||
@@ -218,15 +246,15 @@ export class HaServiceControl extends LitElement {
|
||||
"ui.components.service-control.service_data"
|
||||
)}
|
||||
.name=${"data"}
|
||||
.defaultValue=${this.value?.data}
|
||||
.defaultValue=${this._value?.data}
|
||||
@value-changed=${this._dataChanged}
|
||||
></ha-yaml-editor>`
|
||||
: serviceData?.fields.map((dataField) =>
|
||||
dataField.selector &&
|
||||
(!dataField.advanced ||
|
||||
this.showAdvanced ||
|
||||
(this.value?.data &&
|
||||
this.value.data[dataField.key] !== undefined))
|
||||
(this._value?.data &&
|
||||
this._value.data[dataField.key] !== undefined))
|
||||
? html`<ha-settings-row .narrow=${this.narrow}>
|
||||
${dataField.required
|
||||
? hasOptional
|
||||
@@ -235,8 +263,8 @@ export class HaServiceControl extends LitElement {
|
||||
: html`<ha-checkbox
|
||||
.key=${dataField.key}
|
||||
.checked=${this._checkedKeys.has(dataField.key) ||
|
||||
(this.value?.data &&
|
||||
this.value.data[dataField.key] !== undefined)}
|
||||
(this._value?.data &&
|
||||
this._value.data[dataField.key] !== undefined)}
|
||||
@change=${this._checkboxChanged}
|
||||
slot="prefix"
|
||||
></ha-checkbox>`}
|
||||
@@ -245,15 +273,15 @@ export class HaServiceControl extends LitElement {
|
||||
><ha-selector
|
||||
.disabled=${!dataField.required &&
|
||||
!this._checkedKeys.has(dataField.key) &&
|
||||
(!this.value?.data ||
|
||||
this.value.data[dataField.key] === undefined)}
|
||||
(!this._value?.data ||
|
||||
this._value.data[dataField.key] === undefined)}
|
||||
.hass=${this.hass}
|
||||
.selector=${dataField.selector}
|
||||
.key=${dataField.key}
|
||||
@value-changed=${this._serviceDataChanged}
|
||||
.value=${this.value?.data &&
|
||||
this.value.data[dataField.key] !== undefined
|
||||
? this.value.data[dataField.key]
|
||||
.value=${this._value?.data &&
|
||||
this._value.data[dataField.key] !== undefined
|
||||
? this._value.data[dataField.key]
|
||||
: dataField.default}
|
||||
></ha-selector
|
||||
></ha-settings-row>`
|
||||
@@ -268,13 +296,13 @@ export class HaServiceControl extends LitElement {
|
||||
this._checkedKeys.add(key);
|
||||
} else {
|
||||
this._checkedKeys.delete(key);
|
||||
const data = { ...this.value?.data };
|
||||
const data = { ...this._value?.data };
|
||||
|
||||
delete data[key];
|
||||
|
||||
fireEvent(this, "value-changed", {
|
||||
value: {
|
||||
...this.value,
|
||||
...this._value,
|
||||
data,
|
||||
},
|
||||
});
|
||||
@@ -284,7 +312,7 @@ export class HaServiceControl extends LitElement {
|
||||
|
||||
private _serviceChanged(ev: PolymerChangedEvent<string>) {
|
||||
ev.stopPropagation();
|
||||
if (ev.detail.value === this.value?.service) {
|
||||
if (ev.detail.value === this._value?.service) {
|
||||
return;
|
||||
}
|
||||
fireEvent(this, "value-changed", {
|
||||
@@ -295,17 +323,17 @@ export class HaServiceControl extends LitElement {
|
||||
private _entityPicked(ev: CustomEvent) {
|
||||
ev.stopPropagation();
|
||||
const newValue = ev.detail.value;
|
||||
if (this.value?.data?.entity_id === newValue) {
|
||||
if (this._value?.data?.entity_id === newValue) {
|
||||
return;
|
||||
}
|
||||
let value;
|
||||
if (!newValue && this.value?.data) {
|
||||
value = { ...this.value };
|
||||
if (!newValue && this._value?.data) {
|
||||
value = { ...this._value };
|
||||
delete value.data.entity_id;
|
||||
} else {
|
||||
value = {
|
||||
...this.value,
|
||||
data: { ...this.value?.data, entity_id: ev.detail.value },
|
||||
...this._value,
|
||||
data: { ...this._value?.data, entity_id: ev.detail.value },
|
||||
};
|
||||
}
|
||||
fireEvent(this, "value-changed", {
|
||||
@@ -316,15 +344,15 @@ export class HaServiceControl extends LitElement {
|
||||
private _targetChanged(ev: CustomEvent) {
|
||||
ev.stopPropagation();
|
||||
const newValue = ev.detail.value;
|
||||
if (this.value?.target === newValue) {
|
||||
if (this._value?.target === newValue) {
|
||||
return;
|
||||
}
|
||||
let value;
|
||||
if (!newValue) {
|
||||
value = { ...this.value };
|
||||
value = { ...this._value };
|
||||
delete value.target;
|
||||
} else {
|
||||
value = { ...this.value, target: ev.detail.value };
|
||||
value = { ...this._value, target: ev.detail.value };
|
||||
}
|
||||
fireEvent(this, "value-changed", {
|
||||
value,
|
||||
@@ -336,13 +364,13 @@ export class HaServiceControl extends LitElement {
|
||||
const key = (ev.currentTarget as any).key;
|
||||
const value = ev.detail.value;
|
||||
if (
|
||||
this.value?.data?.[key] === value ||
|
||||
(!this.value?.data?.[key] && (value === "" || value === undefined))
|
||||
this._value?.data?.[key] === value ||
|
||||
(!this._value?.data?.[key] && (value === "" || value === undefined))
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
const data = { ...this.value?.data, [key]: value };
|
||||
const data = { ...this._value?.data, [key]: value };
|
||||
|
||||
if (value === "" || value === undefined) {
|
||||
delete data[key];
|
||||
@@ -350,7 +378,7 @@ export class HaServiceControl extends LitElement {
|
||||
|
||||
fireEvent(this, "value-changed", {
|
||||
value: {
|
||||
...this.value,
|
||||
...this._value,
|
||||
data,
|
||||
},
|
||||
});
|
||||
@@ -363,7 +391,7 @@ export class HaServiceControl extends LitElement {
|
||||
}
|
||||
fireEvent(this, "value-changed", {
|
||||
value: {
|
||||
...this.value,
|
||||
...this._value,
|
||||
data: ev.detail.value,
|
||||
},
|
||||
});
|
||||
@@ -406,6 +434,15 @@ export class HaServiceControl extends LitElement {
|
||||
ha-checkbox {
|
||||
margin-left: -16px;
|
||||
}
|
||||
.help-icon {
|
||||
color: var(--secondary-text-color);
|
||||
}
|
||||
.description {
|
||||
justify-content: space-between;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding-right: 2px;
|
||||
}
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -314,16 +314,18 @@ class ActionRenderer {
|
||||
|
||||
if (defaultExecuted) {
|
||||
this._renderEntry(choosePath, `${name}: Default action executed`);
|
||||
} else {
|
||||
} else if (chooseTrace.result) {
|
||||
const choiceConfig = this._getDataFromPath(
|
||||
`${this.keys[index]}/choose/${chooseTrace.result?.choice}`
|
||||
`${this.keys[index]}/choose/${chooseTrace.result.choice}`
|
||||
) as ChooseActionChoice | undefined;
|
||||
const choiceName = choiceConfig
|
||||
? `${
|
||||
choiceConfig.alias || `Choice ${chooseTrace.result?.choice}`
|
||||
choiceConfig.alias || `Choice ${chooseTrace.result.choice}`
|
||||
} executed`
|
||||
: `Error: ${chooseTrace.error}`;
|
||||
this._renderEntry(choosePath, `${name}: ${choiceName}`);
|
||||
} else {
|
||||
this._renderEntry(choosePath, `${name}: No action taken`);
|
||||
}
|
||||
|
||||
let i;
|
||||
|
||||
@@ -9,6 +9,7 @@ export interface AnalyticsPreferences {
|
||||
|
||||
export interface Analytics {
|
||||
preferences: AnalyticsPreferences;
|
||||
onboarded: boolean;
|
||||
}
|
||||
|
||||
export const getAnalyticsDetails = (hass: HomeAssistant) =>
|
||||
|
||||
@@ -16,6 +16,7 @@ export interface ConfigEntry {
|
||||
supports_options: boolean;
|
||||
supports_unload: boolean;
|
||||
disabled_by: "user" | null;
|
||||
reason: string | null;
|
||||
}
|
||||
|
||||
export interface ConfigEntryMutableParams {
|
||||
|
||||
@@ -28,6 +28,7 @@ export interface DataEntryFlowStepForm {
|
||||
data_schema: HaFormSchema[];
|
||||
errors: Record<string, string>;
|
||||
description_placeholders: Record<string, string>;
|
||||
last_step: boolean | null;
|
||||
}
|
||||
|
||||
export interface DataEntryFlowStepExternal {
|
||||
|
||||
@@ -19,6 +19,10 @@ export interface LovelacePanelConfig {
|
||||
|
||||
export interface LovelaceConfig {
|
||||
title?: string;
|
||||
strategy?: {
|
||||
name: string;
|
||||
options?: Record<string, unknown>;
|
||||
};
|
||||
views: LovelaceViewConfig[];
|
||||
background?: string;
|
||||
}
|
||||
@@ -77,6 +81,10 @@ export interface LovelaceViewConfig {
|
||||
index?: number;
|
||||
title?: string;
|
||||
type?: string;
|
||||
strategy?: {
|
||||
name: string;
|
||||
options?: Record<string, unknown>;
|
||||
};
|
||||
badges?: Array<string | LovelaceBadgeConfig>;
|
||||
cards?: LovelaceCardConfig[];
|
||||
path?: string;
|
||||
@@ -94,6 +102,7 @@ export interface LovelaceViewElement extends HTMLElement {
|
||||
index?: number;
|
||||
cards?: Array<LovelaceCard | HuiErrorCard>;
|
||||
badges?: LovelaceBadge[];
|
||||
isStrategy: boolean;
|
||||
setConfig(config: LovelaceViewConfig): void;
|
||||
}
|
||||
|
||||
|
||||
@@ -6,3 +6,6 @@ export const callExecuteScript = (hass: HomeAssistant, sequence: Action[]) =>
|
||||
type: "execute_script",
|
||||
sequence,
|
||||
});
|
||||
|
||||
export const serviceCallWillDisconnect = (domain: string, service: string) =>
|
||||
domain === "homeassistant" && ["restart", "stop"].includes(service);
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { HassEntity } from "home-assistant-js-websocket";
|
||||
import { HaFormSchema } from "../components/ha-form/ha-form";
|
||||
import { HomeAssistant } from "../types";
|
||||
|
||||
export interface ZHAEntityReference extends HassEntity {
|
||||
@@ -75,6 +76,11 @@ export interface ZHAGroup {
|
||||
members: ZHADeviceEndpoint[];
|
||||
}
|
||||
|
||||
export interface ZHAConfiguration {
|
||||
data: Record<string, Record<string, unknown>>;
|
||||
schemas: Record<string, HaFormSchema[]>;
|
||||
}
|
||||
|
||||
export interface ZHAGroupMember {
|
||||
ieee: string;
|
||||
endpoint_id: string;
|
||||
@@ -282,6 +288,22 @@ export const addGroup = (
|
||||
members: membersToAdd,
|
||||
});
|
||||
|
||||
export const fetchZHAConfiguration = (
|
||||
hass: HomeAssistant
|
||||
): Promise<ZHAConfiguration> =>
|
||||
hass.callWS({
|
||||
type: "zha/configuration",
|
||||
});
|
||||
|
||||
export const updateZHAConfiguration = (
|
||||
hass: HomeAssistant,
|
||||
data: any
|
||||
): Promise<any> =>
|
||||
hass.callWS({
|
||||
type: "zha/configuration/update",
|
||||
data: data,
|
||||
});
|
||||
|
||||
export const INITIALIZED = "INITIALIZED";
|
||||
export const INTERVIEW_COMPLETE = "INTERVIEW_COMPLETE";
|
||||
export const CONFIGURED = "CONFIGURED";
|
||||
|
||||
+37
-2
@@ -29,6 +29,10 @@ export interface ZWaveJSNode {
|
||||
}
|
||||
|
||||
export interface ZWaveJSNodeConfigParams {
|
||||
[key: string]: ZWaveJSNodeConfigParam;
|
||||
}
|
||||
|
||||
export interface ZWaveJSNodeConfigParam {
|
||||
property: number;
|
||||
value: any;
|
||||
configuration_value_type: string;
|
||||
@@ -56,6 +60,17 @@ export interface ZWaveJSSetConfigParamData {
|
||||
value: string | number;
|
||||
}
|
||||
|
||||
export interface ZWaveJSSetConfigParamResult {
|
||||
value_id?: string;
|
||||
status?: string;
|
||||
error?: string;
|
||||
}
|
||||
|
||||
export interface ZWaveJSDataCollectionStatus {
|
||||
enabled: boolean;
|
||||
opted_in: boolean;
|
||||
}
|
||||
|
||||
export enum NodeStatus {
|
||||
Unknown,
|
||||
Asleep,
|
||||
@@ -75,6 +90,26 @@ export const fetchNetworkStatus = (
|
||||
entry_id,
|
||||
});
|
||||
|
||||
export const fetchDataCollectionStatus = (
|
||||
hass: HomeAssistant,
|
||||
entry_id: string
|
||||
): Promise<ZWaveJSDataCollectionStatus> =>
|
||||
hass.callWS({
|
||||
type: "zwave_js/data_collection_status",
|
||||
entry_id,
|
||||
});
|
||||
|
||||
export const setDataCollectionPreference = (
|
||||
hass: HomeAssistant,
|
||||
entry_id: string,
|
||||
opted_in: boolean
|
||||
): Promise<any> =>
|
||||
hass.callWS({
|
||||
type: "zwave_js/update_data_collection_preference",
|
||||
entry_id,
|
||||
opted_in,
|
||||
});
|
||||
|
||||
export const fetchNodeStatus = (
|
||||
hass: HomeAssistant,
|
||||
entry_id: string,
|
||||
@@ -90,7 +125,7 @@ export const fetchNodeConfigParameters = (
|
||||
hass: HomeAssistant,
|
||||
entry_id: string,
|
||||
node_id: number
|
||||
): Promise<ZWaveJSNodeConfigParams[]> =>
|
||||
): Promise<ZWaveJSNodeConfigParams> =>
|
||||
hass.callWS({
|
||||
type: "zwave_js/get_config_parameters",
|
||||
entry_id,
|
||||
@@ -104,7 +139,7 @@ export const setNodeConfigParameter = (
|
||||
property: number,
|
||||
value: number,
|
||||
property_key?: number
|
||||
): Promise<unknown> => {
|
||||
): Promise<ZWaveJSSetConfigParamResult> => {
|
||||
const data: ZWaveJSSetConfigParamData = {
|
||||
type: "zwave_js/set_config_parameter",
|
||||
entry_id,
|
||||
|
||||
@@ -0,0 +1,165 @@
|
||||
import "../../components/ha-analytics";
|
||||
import "@material/mwc-button/mwc-button";
|
||||
import {
|
||||
css,
|
||||
CSSResult,
|
||||
customElement,
|
||||
html,
|
||||
internalProperty,
|
||||
LitElement,
|
||||
property,
|
||||
TemplateResult,
|
||||
} from "lit-element";
|
||||
import { fireEvent } from "../../common/dom/fire_event";
|
||||
import "../../components/ha-dialog";
|
||||
import { Analytics, setAnalyticsPreferences } from "../../data/analytics";
|
||||
import { haStyleDialog } from "../../resources/styles";
|
||||
import type { HomeAssistant } from "../../types";
|
||||
import { DialogAnalyticsOptInParams } from "./show-dialog-analytics-optin";
|
||||
import { analyticsLearnMore } from "../../components/ha-analytics-learn-more";
|
||||
|
||||
@customElement("dialog-analytics-optin")
|
||||
class DialogAnalyticsOptIn extends LitElement {
|
||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||
|
||||
@internalProperty() private _error?: string;
|
||||
|
||||
@internalProperty() private _submitting = false;
|
||||
|
||||
@internalProperty() private _showPreferences = false;
|
||||
|
||||
@internalProperty() private _analyticsDetails?: Analytics;
|
||||
|
||||
public showDialog(params: DialogAnalyticsOptInParams): void {
|
||||
this._error = undefined;
|
||||
this._submitting = false;
|
||||
this._analyticsDetails = params.analytics;
|
||||
}
|
||||
|
||||
public closeDialog(): void {
|
||||
this._error = undefined;
|
||||
this._submitting = false;
|
||||
this._showPreferences = false;
|
||||
this._analyticsDetails = undefined;
|
||||
fireEvent(this, "dialog-closed", { dialog: this.localName });
|
||||
}
|
||||
|
||||
protected render(): TemplateResult {
|
||||
if (!this._analyticsDetails) {
|
||||
return html``;
|
||||
}
|
||||
return html`
|
||||
<ha-dialog
|
||||
open
|
||||
heading="Analytics"
|
||||
scrimClickAction
|
||||
escapeKeyAction
|
||||
hideActions
|
||||
>
|
||||
<div class="content">
|
||||
${this._error ? html` <div class="error">${this._error}</div> ` : ""}
|
||||
${this._showPreferences
|
||||
? html`<ha-analytics
|
||||
@analytics-preferences-changed=${this._preferencesChanged}
|
||||
.hass=${this.hass}
|
||||
.analytics=${this._analyticsDetails!}
|
||||
></ha-analytics>`
|
||||
: html` <div class="introduction">
|
||||
To help us better understand how you use Home Assistant, and to
|
||||
ensure our priorities align with yours, we ask that you share
|
||||
anonymized information from your installation. This will help make Home
|
||||
Assistant better and help us convince manufacturers to add local
|
||||
control and privacy-focused features.
|
||||
<p>
|
||||
If you want to change what you share, you can find this in
|
||||
under "General" here in the configuration panel
|
||||
</p>
|
||||
</div>`}
|
||||
${analyticsLearnMore(this.hass)}
|
||||
</div>
|
||||
<div class="dialog-actions">
|
||||
<mwc-button @click=${this._ignore} .disabled=${this._submitting}>
|
||||
Ignore
|
||||
</mwc-button>
|
||||
<mwc-button
|
||||
@click=${this._customize}
|
||||
.disabled=${this._submitting || this._showPreferences}
|
||||
>
|
||||
Customize
|
||||
</mwc-button>
|
||||
<mwc-button @click=${this._submit} .disabled=${this._submitting}>
|
||||
${this._showPreferences ? "Submit" : "Enable analytics"}
|
||||
</mwc-button>
|
||||
</div>
|
||||
</ha-dialog>
|
||||
`;
|
||||
}
|
||||
|
||||
private _preferencesChanged(event: CustomEvent): void {
|
||||
this._analyticsDetails = {
|
||||
...this._analyticsDetails!,
|
||||
preferences: event.detail.preferences,
|
||||
};
|
||||
}
|
||||
|
||||
private async _ignore() {
|
||||
this._submitting = true;
|
||||
try {
|
||||
await setAnalyticsPreferences(this.hass, {});
|
||||
} catch (err) {
|
||||
this._error = err.message;
|
||||
this._submitting = false;
|
||||
return;
|
||||
}
|
||||
this.closeDialog();
|
||||
}
|
||||
|
||||
private async _customize() {
|
||||
this._showPreferences = true;
|
||||
}
|
||||
|
||||
private async _submit() {
|
||||
this._submitting = true;
|
||||
try {
|
||||
await setAnalyticsPreferences(
|
||||
this.hass,
|
||||
this._showPreferences
|
||||
? this._analyticsDetails!.preferences
|
||||
: { base: true, usage: true, statistics: true }
|
||||
);
|
||||
} catch (err) {
|
||||
this._error = err.message;
|
||||
this._submitting = false;
|
||||
return;
|
||||
}
|
||||
|
||||
this.closeDialog();
|
||||
}
|
||||
|
||||
static get styles(): CSSResult[] {
|
||||
return [
|
||||
haStyleDialog,
|
||||
css`
|
||||
.error {
|
||||
color: var(--error-color);
|
||||
}
|
||||
.content {
|
||||
padding-bottom: 54px;
|
||||
}
|
||||
.dialog-actions {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
bottom: 16px;
|
||||
position: absolute;
|
||||
width: calc(100% - 48px);
|
||||
}
|
||||
`,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"dialog-analytics-optin": DialogAnalyticsOptIn;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
import { fireEvent } from "../../common/dom/fire_event";
|
||||
import { Analytics } from "../../data/analytics";
|
||||
|
||||
export interface DialogAnalyticsOptInParams {
|
||||
analytics: Analytics;
|
||||
}
|
||||
|
||||
export const loadConfigEntrySystemOptionsDialog = () =>
|
||||
import("./dialog-analytics-optin");
|
||||
|
||||
export const showDialogAnalyticsOptIn = (
|
||||
element: HTMLElement,
|
||||
dialogParams: DialogAnalyticsOptInParams
|
||||
): void => {
|
||||
fireEvent(element, "show-dialog", {
|
||||
dialogTag: "dialog-analytics-optin",
|
||||
dialogImport: loadConfigEntrySystemOptionsDialog,
|
||||
dialogParams,
|
||||
});
|
||||
};
|
||||
@@ -61,25 +61,25 @@ export const showDialog = async (
|
||||
}
|
||||
|
||||
if (addHistory) {
|
||||
history.replaceState(
|
||||
top.history.replaceState(
|
||||
{
|
||||
dialog: dialogTag,
|
||||
open: false,
|
||||
oldState:
|
||||
history.state?.open && history.state?.dialog !== dialogTag
|
||||
? history.state
|
||||
top.history.state?.open && top.history.state?.dialog !== dialogTag
|
||||
? top.history.state
|
||||
: null,
|
||||
},
|
||||
""
|
||||
);
|
||||
try {
|
||||
history.pushState(
|
||||
top.history.pushState(
|
||||
{ dialog: dialogTag, dialogParams: dialogParams, open: true },
|
||||
""
|
||||
);
|
||||
} catch (err) {
|
||||
// dialogParams could not be cloned, probably contains callback
|
||||
history.pushState(
|
||||
top.history.pushState(
|
||||
{ dialog: dialogTag, dialogParams: null, open: true },
|
||||
""
|
||||
);
|
||||
@@ -90,7 +90,7 @@ export const showDialog = async (
|
||||
};
|
||||
|
||||
export const replaceDialog = () => {
|
||||
history.replaceState({ ...history.state, replaced: true }, "");
|
||||
top.history.replaceState({ ...top.history.state, replaced: true }, "");
|
||||
};
|
||||
|
||||
export const closeDialog = async (dialogTag: string): Promise<boolean> => {
|
||||
|
||||
@@ -30,6 +30,7 @@ export interface MockHomeAssistant extends HomeAssistant {
|
||||
updateStates(newStates: HassEntities);
|
||||
addEntities(entites: Entity | Entity[], replace?: boolean);
|
||||
updateTranslations(fragment: null | string, language?: string);
|
||||
addTranslations(translations: Record<string, string>, language?: string);
|
||||
mockWS(
|
||||
type: string,
|
||||
callback: (msg: any, onChange?: (response: any) => void) => any
|
||||
@@ -60,15 +61,25 @@ export const provideHass = (
|
||||
) {
|
||||
const lang = language || getLocalLanguage();
|
||||
const translation = await getTranslation(fragment, lang);
|
||||
await addTranslations(translation.data, lang);
|
||||
}
|
||||
|
||||
async function addTranslations(
|
||||
translations: Record<string, string>,
|
||||
language?: string
|
||||
) {
|
||||
const lang = language || getLocalLanguage();
|
||||
const resources = {
|
||||
[lang]: {
|
||||
...(hass().resources && hass().resources[lang]),
|
||||
...translation.data,
|
||||
...translations,
|
||||
},
|
||||
};
|
||||
hass().updateHass({
|
||||
resources,
|
||||
localize: await computeLocalize(elements[0], lang, resources),
|
||||
});
|
||||
hass().updateHass({
|
||||
localize: await computeLocalize(elements[0], lang, hass().resources),
|
||||
});
|
||||
}
|
||||
|
||||
@@ -209,6 +220,9 @@ export const provideHass = (
|
||||
localize: () => "",
|
||||
|
||||
translationMetadata: translationMetadata as any,
|
||||
async loadBackendTranslation() {
|
||||
return hass().localize;
|
||||
},
|
||||
dockedSidebar: "auto",
|
||||
vibrate: true,
|
||||
suspendWhenHidden: false,
|
||||
@@ -250,6 +264,7 @@ export const provideHass = (
|
||||
},
|
||||
updateStates,
|
||||
updateTranslations,
|
||||
addTranslations,
|
||||
addEntities,
|
||||
mockWS(type, callback) {
|
||||
wsCommands[type] = callback;
|
||||
|
||||
@@ -23,11 +23,9 @@
|
||||
margin-right: 16px;
|
||||
}
|
||||
@media (prefers-color-scheme: dark) {
|
||||
body {
|
||||
html {
|
||||
background-color: #111111;
|
||||
color: #e1e1e1;
|
||||
--primary-text-color: #e1e1e1;
|
||||
--secondary-text-color: #9b9b9b;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -51,6 +51,7 @@
|
||||
@media (prefers-color-scheme: dark) {
|
||||
html {
|
||||
background-color: #111111;
|
||||
color: #e1e1e1;
|
||||
}
|
||||
#ha-init-skeleton::before {
|
||||
background-color: #1c1c1c;
|
||||
|
||||
@@ -34,17 +34,8 @@
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
html {
|
||||
color: #e1e1e1;
|
||||
}
|
||||
ha-onboarding {
|
||||
--primary-text-color: #e1e1e1;
|
||||
--secondary-text-color: #9b9b9b;
|
||||
--disabled-text-color: #6f6f6f;
|
||||
--mdc-theme-surface: #1e1e1e;
|
||||
--ha-card-background: #1e1e1e;
|
||||
}
|
||||
.content {
|
||||
background-color: #111111;
|
||||
color: #e1e1e1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -32,6 +32,7 @@ import { registerServiceWorker } from "../util/register-service-worker";
|
||||
import "./onboarding-create-user";
|
||||
import "./onboarding-loading";
|
||||
import "./onboarding-analytics";
|
||||
import { applyThemesOnElement } from "../common/dom/apply_themes_on_element";
|
||||
|
||||
type OnboardingEvent =
|
||||
| {
|
||||
@@ -137,6 +138,19 @@ class HaOnboarding extends litLocalizeLiteMixin(HassElement) {
|
||||
if (window.innerWidth > 450) {
|
||||
import("./particles");
|
||||
}
|
||||
if (matchMedia("(prefers-color-scheme: dark)").matches) {
|
||||
applyThemesOnElement(
|
||||
document.documentElement,
|
||||
{
|
||||
default_theme: "default",
|
||||
default_dark_theme: null,
|
||||
themes: {},
|
||||
darkMode: false,
|
||||
},
|
||||
"default",
|
||||
{ dark: true }
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
protected updated(changedProps: PropertyValues) {
|
||||
|
||||
@@ -27,6 +27,7 @@ class OnboardingAnalytics extends LitElement {
|
||||
|
||||
@internalProperty() private _analyticsDetails: Analytics = {
|
||||
preferences: {},
|
||||
onboarded: false,
|
||||
};
|
||||
|
||||
protected render(): TemplateResult {
|
||||
|
||||
@@ -15,7 +15,9 @@ import "../../../components/ha-card";
|
||||
import "../../../components/ha-icon-next";
|
||||
import "../../../components/ha-menu-button";
|
||||
import "../../../components/ha-svg-icon";
|
||||
import { getAnalyticsDetails } from "../../../data/analytics";
|
||||
import { CloudStatus } from "../../../data/cloud";
|
||||
import { showDialogAnalyticsOptIn } from "../../../dialogs/analytics/show-dialog-analytics-optin";
|
||||
import "../../../layouts/ha-app-layout";
|
||||
import { haStyle } from "../../../resources/styles";
|
||||
import { HomeAssistant } from "../../../types";
|
||||
@@ -36,6 +38,15 @@ class HaConfigDashboard extends LitElement {
|
||||
|
||||
@property() public showAdvanced!: boolean;
|
||||
|
||||
protected firstUpdated(changedProperties) {
|
||||
super.firstUpdated(changedProperties);
|
||||
getAnalyticsDetails(this.hass).then((analytics) => {
|
||||
if (!analytics.onboarded) {
|
||||
showDialogAnalyticsOptIn(this, { analytics });
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
protected render(): TemplateResult {
|
||||
const content = html` <ha-config-section
|
||||
.narrow=${this.narrow}
|
||||
|
||||
@@ -30,8 +30,9 @@ class IntegrationsCard extends LitElement {
|
||||
private _sortedIntegrations = memoizeOne((components: string[]) => {
|
||||
return Array.from(
|
||||
new Set(
|
||||
components
|
||||
.map((comp) => (comp.includes(".") ? comp.split(".")[1] : comp))
|
||||
components.map((comp) =>
|
||||
comp.includes(".") ? comp.split(".")[1] : comp
|
||||
)
|
||||
)
|
||||
).sort();
|
||||
});
|
||||
|
||||
@@ -1,75 +0,0 @@
|
||||
import { mdiPackageVariant, mdiCloud } from "@mdi/js";
|
||||
import "@polymer/paper-tooltip/paper-tooltip";
|
||||
import { css, html } from "lit-element";
|
||||
import { IntegrationManifest } from "../../../data/integration";
|
||||
import { HomeAssistant } from "../../../types";
|
||||
|
||||
export const haConfigIntegrationsStyles = css`
|
||||
.banner {
|
||||
background-color: var(--state-color);
|
||||
color: var(--text-on-state-color);
|
||||
text-align: center;
|
||||
padding: 8px;
|
||||
}
|
||||
.icons {
|
||||
position: absolute;
|
||||
top: 0px;
|
||||
right: 16px;
|
||||
color: var(--text-on-state-color, var(--secondary-text-color));
|
||||
background-color: var(--state-color, #e0e0e0);
|
||||
border-bottom-left-radius: 4px;
|
||||
border-bottom-right-radius: 4px;
|
||||
padding: 1px 4px 2px;
|
||||
}
|
||||
.icons ha-svg-icon {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
}
|
||||
paper-tooltip {
|
||||
white-space: nowrap;
|
||||
}
|
||||
`;
|
||||
|
||||
export const haConfigIntegrationRenderIcons = (
|
||||
hass: HomeAssistant,
|
||||
manifest?: IntegrationManifest
|
||||
) => {
|
||||
const icons: [string, string][] = [];
|
||||
|
||||
if (manifest) {
|
||||
if (!manifest.is_built_in) {
|
||||
icons.push([
|
||||
mdiPackageVariant,
|
||||
hass.localize(
|
||||
"ui.panel.config.integrations.config_entry.provided_by_custom_integration"
|
||||
),
|
||||
]);
|
||||
}
|
||||
|
||||
if (manifest.iot_class && manifest.iot_class.startsWith("cloud_")) {
|
||||
icons.push([
|
||||
mdiCloud,
|
||||
hass.localize(
|
||||
"ui.panel.config.integrations.config_entry.depends_on_cloud"
|
||||
),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
return icons.length === 0
|
||||
? ""
|
||||
: html`
|
||||
<div class="icons">
|
||||
${icons.map(
|
||||
([icon, description]) => html`
|
||||
<span>
|
||||
<ha-svg-icon .path=${icon}></ha-svg-icon>
|
||||
<paper-tooltip animation-delay="0"
|
||||
>${description}</paper-tooltip
|
||||
>
|
||||
</span>
|
||||
`
|
||||
)}
|
||||
</div>
|
||||
`;
|
||||
};
|
||||
@@ -38,6 +38,7 @@ import {
|
||||
} from "../../../data/entity_registry";
|
||||
import {
|
||||
domainToName,
|
||||
fetchIntegrationManifest,
|
||||
fetchIntegrationManifests,
|
||||
IntegrationManifest,
|
||||
} from "../../../data/integration";
|
||||
@@ -127,6 +128,8 @@ class HaConfigIntegrations extends SubscribeMixin(LitElement) {
|
||||
@internalProperty()
|
||||
private _manifests: Record<string, IntegrationManifest> = {};
|
||||
|
||||
private _extraFetchedManifests?: Set<string>;
|
||||
|
||||
@internalProperty() private _showIgnored = false;
|
||||
|
||||
@internalProperty() private _showDisabled = false;
|
||||
@@ -154,15 +157,14 @@ class HaConfigIntegrations extends SubscribeMixin(LitElement) {
|
||||
this.hass.loadBackendTranslation("config", flow.handler)
|
||||
);
|
||||
}
|
||||
this._fetchManifest(flow.handler);
|
||||
});
|
||||
await Promise.all(translationsPromisses);
|
||||
await nextRender();
|
||||
this._configEntriesInProgress = flowsInProgress.map((flow) => {
|
||||
return {
|
||||
...flow,
|
||||
localized_title: localizeConfigFlowTitle(this.hass.localize, flow),
|
||||
};
|
||||
});
|
||||
this._configEntriesInProgress = flowsInProgress.map((flow) => ({
|
||||
...flow,
|
||||
localized_title: localizeConfigFlowTitle(this.hass.localize, flow),
|
||||
}));
|
||||
}),
|
||||
];
|
||||
}
|
||||
@@ -381,6 +383,7 @@ class HaConfigIntegrations extends SubscribeMixin(LitElement) {
|
||||
(flow: DataEntryFlowProgressExtended) => html`
|
||||
<ha-config-flow-card
|
||||
.hass=${this.hass}
|
||||
.manifest=${this._manifests[flow.handler]}
|
||||
.flow=${flow}
|
||||
@change=${this._handleFlowUpdated}
|
||||
></ha-config-flow-card>
|
||||
@@ -495,12 +498,33 @@ class HaConfigIntegrations extends SubscribeMixin(LitElement) {
|
||||
}
|
||||
|
||||
private async _fetchManifests() {
|
||||
const manifests = {};
|
||||
const fetched = await fetchIntegrationManifests(this.hass);
|
||||
// Make a copy so we can keep track of previously loaded manifests
|
||||
// for discovered flows (which are not part of these results)
|
||||
const manifests = { ...this._manifests };
|
||||
for (const manifest of fetched) manifests[manifest.domain] = manifest;
|
||||
this._manifests = manifests;
|
||||
}
|
||||
|
||||
private async _fetchManifest(domain: string) {
|
||||
if (domain in this._manifests) {
|
||||
return;
|
||||
}
|
||||
if (this._extraFetchedManifests) {
|
||||
if (this._extraFetchedManifests.has(domain)) {
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
this._extraFetchedManifests = new Set();
|
||||
}
|
||||
this._extraFetchedManifests.add(domain);
|
||||
const manifest = await fetchIntegrationManifest(this.hass, domain);
|
||||
this._manifests = {
|
||||
...this._manifests,
|
||||
[domain]: manifest,
|
||||
};
|
||||
}
|
||||
|
||||
private _handleEntryRemoved(ev: HASSDomEvent<ConfigEntryRemovedEvent>) {
|
||||
this._configEntries = this._configEntries!.filter(
|
||||
(entry) => entry.entry_id !== ev.detail.entryId
|
||||
|
||||
@@ -31,6 +31,7 @@ export class HaIgnoredConfigEntryCard extends LitElement {
|
||||
"ui.panel.config.integrations.ignore.ignored"
|
||||
)}
|
||||
.domain=${this.entry.domain}
|
||||
.localizedDomainName=${this.entry.localized_domain_name}
|
||||
.label=${this.entry.title === "Ignored"
|
||||
? // In 2020.2 we added support for entry.title. All ignored entries before
|
||||
// that have title "Ignored" so we fallback to localized domain name.
|
||||
@@ -38,7 +39,6 @@ export class HaIgnoredConfigEntryCard extends LitElement {
|
||||
: this.entry.title}
|
||||
>
|
||||
<mwc-button
|
||||
unelevated
|
||||
@click=${this._removeIgnoredIntegration}
|
||||
.label=${this.hass.localize(
|
||||
"ui.panel.config.integrations.ignore.stop_ignore"
|
||||
|
||||
@@ -1,18 +1,14 @@
|
||||
import {
|
||||
TemplateResult,
|
||||
html,
|
||||
customElement,
|
||||
LitElement,
|
||||
property,
|
||||
CSSResult,
|
||||
css,
|
||||
} from "lit-element";
|
||||
import { TemplateResult, html } from "lit-html";
|
||||
import { IntegrationManifest } from "../../../data/integration";
|
||||
import { HomeAssistant } from "../../../types";
|
||||
import { brandsUrl } from "../../../util/brands-url";
|
||||
import {
|
||||
haConfigIntegrationRenderIcons,
|
||||
haConfigIntegrationsStyles,
|
||||
} from "./ha-config-integrations-common";
|
||||
import type { IntegrationManifest } from "../../../data/integration";
|
||||
import type { HomeAssistant } from "../../../types";
|
||||
import "./ha-integration-header";
|
||||
|
||||
@customElement("ha-integration-action-card")
|
||||
export class HaIntegrationActionCard extends LitElement {
|
||||
@@ -20,6 +16,8 @@ export class HaIntegrationActionCard extends LitElement {
|
||||
|
||||
@property() public banner!: string;
|
||||
|
||||
@property() public localizedDomainName?: string;
|
||||
|
||||
@property() public domain!: string;
|
||||
|
||||
@property() public label!: string;
|
||||
@@ -29,82 +27,47 @@ export class HaIntegrationActionCard extends LitElement {
|
||||
protected render(): TemplateResult {
|
||||
return html`
|
||||
<ha-card outlined>
|
||||
<div class="banner">
|
||||
${this.banner}
|
||||
</div>
|
||||
<div class="content">
|
||||
${haConfigIntegrationRenderIcons(this.hass, this.manifest)}
|
||||
<div class="image">
|
||||
<img
|
||||
src=${brandsUrl(this.domain, "logo")}
|
||||
referrerpolicy="no-referrer"
|
||||
@error=${this._onImageError}
|
||||
@load=${this._onImageLoad}
|
||||
/>
|
||||
</div>
|
||||
<h2>${this.label}</h2>
|
||||
</div>
|
||||
<ha-integration-header
|
||||
.hass=${this.hass}
|
||||
.banner=${this.banner}
|
||||
.domain=${this.domain}
|
||||
.label=${this.label}
|
||||
.localizedDomainName=${this.localizedDomainName}
|
||||
.manifest=${this.manifest}
|
||||
></ha-integration-header>
|
||||
<div class="filler"></div>
|
||||
<div class="actions"><slot></slot></div>
|
||||
</ha-card>
|
||||
`;
|
||||
}
|
||||
|
||||
private _onImageLoad(ev) {
|
||||
ev.target.style.visibility = "initial";
|
||||
}
|
||||
|
||||
private _onImageError(ev) {
|
||||
ev.target.style.visibility = "hidden";
|
||||
}
|
||||
|
||||
static get styles(): CSSResult[] {
|
||||
return [
|
||||
haConfigIntegrationsStyles,
|
||||
css`
|
||||
ha-card {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
--ha-card-border-color: var(--state-color);
|
||||
--mdc-theme-primary: var(--state-color);
|
||||
}
|
||||
.content {
|
||||
position: relative;
|
||||
flex: 1;
|
||||
}
|
||||
.image {
|
||||
height: 60px;
|
||||
margin-top: 16px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-around;
|
||||
}
|
||||
img {
|
||||
max-width: 90%;
|
||||
max-height: 100%;
|
||||
}
|
||||
h2 {
|
||||
text-align: center;
|
||||
margin: 16px 8px 0;
|
||||
}
|
||||
.attention {
|
||||
--state-color: var(--error-color);
|
||||
--text-on-state-color: var(--text-primary-color);
|
||||
}
|
||||
.discovered {
|
||||
--state-color: var(--primary-color);
|
||||
--text-on-state-color: var(--text-primary-color);
|
||||
}
|
||||
.actions {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 8px 6px 0;
|
||||
height: 48px;
|
||||
}
|
||||
`,
|
||||
];
|
||||
}
|
||||
static styles = css`
|
||||
ha-card {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
--ha-card-border-color: var(--state-color);
|
||||
--mdc-theme-primary: var(--state-color);
|
||||
}
|
||||
.filler {
|
||||
flex: 1;
|
||||
}
|
||||
.attention {
|
||||
--state-color: var(--error-color);
|
||||
--text-on-state-color: var(--text-primary-color);
|
||||
}
|
||||
.discovered {
|
||||
--state-color: var(--primary-color);
|
||||
--text-on-state-color: var(--text-primary-color);
|
||||
}
|
||||
.actions {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 8px 6px 0;
|
||||
height: 48px;
|
||||
}
|
||||
`;
|
||||
}
|
||||
|
||||
declare global {
|
||||
|
||||
@@ -31,7 +31,7 @@ import {
|
||||
} from "../../../data/config_entries";
|
||||
import type { DeviceRegistryEntry } from "../../../data/device_registry";
|
||||
import type { EntityRegistryEntry } from "../../../data/entity_registry";
|
||||
import { domainToName, IntegrationManifest } from "../../../data/integration";
|
||||
import type { IntegrationManifest } from "../../../data/integration";
|
||||
import { showConfigEntrySystemOptionsDialog } from "../../../dialogs/config-entry-system-options/show-dialog-config-entry-system-options";
|
||||
import { showOptionsFlowDialog } from "../../../dialogs/config-flow/show-dialog-options-flow";
|
||||
import {
|
||||
@@ -40,16 +40,11 @@ import {
|
||||
showPromptDialog,
|
||||
} from "../../../dialogs/generic/show-dialog-box";
|
||||
import { haStyle } from "../../../resources/styles";
|
||||
import { HomeAssistant } from "../../../types";
|
||||
import { brandsUrl } from "../../../util/brands-url";
|
||||
import { ConfigEntryExtended } from "./ha-config-integrations";
|
||||
import {
|
||||
haConfigIntegrationRenderIcons,
|
||||
haConfigIntegrationsStyles,
|
||||
} from "./ha-config-integrations-common";
|
||||
import type { HomeAssistant } from "../../../types";
|
||||
import type { ConfigEntryExtended } from "./ha-config-integrations";
|
||||
import "./ha-integration-header";
|
||||
|
||||
const ERROR_STATES: ConfigEntry["state"][] = [
|
||||
"failed_unload",
|
||||
"migration_error",
|
||||
"setup_error",
|
||||
"setup_retry",
|
||||
@@ -93,18 +88,6 @@ export class HaIntegrationCard extends LitElement {
|
||||
);
|
||||
}
|
||||
|
||||
let primary: string;
|
||||
let secondary: string | undefined;
|
||||
|
||||
if (item) {
|
||||
primary = item.title || item.localized_domain_name || this.domain;
|
||||
if (primary !== item.localized_domain_name) {
|
||||
secondary = item.localized_domain_name;
|
||||
}
|
||||
} else {
|
||||
primary = domainToName(this.hass.localize, this.domain, this.manifest);
|
||||
}
|
||||
|
||||
const hasItem = item !== undefined;
|
||||
|
||||
return html`
|
||||
@@ -116,42 +99,37 @@ export class HaIntegrationCard extends LitElement {
|
||||
hasMultiple: this.items.length > 1,
|
||||
disabled: this.disabled,
|
||||
"state-not-loaded": hasItem && item!.state === "not_loaded",
|
||||
"state-failed-unload": hasItem && item!.state === "failed_unload",
|
||||
"state-error": hasItem && ERROR_STATES.includes(item!.state),
|
||||
})}"
|
||||
.configEntry=${item}
|
||||
>
|
||||
${this.disabled
|
||||
? html`
|
||||
<div class="banner">
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.integrations.config_entry.disable.disabled"
|
||||
)}
|
||||
</div>
|
||||
`
|
||||
: ""}
|
||||
${this.items.length > 1
|
||||
? html`
|
||||
<div class="back-btn">
|
||||
<ha-icon-button
|
||||
icon="hass:chevron-left"
|
||||
@click=${this._back}
|
||||
></ha-icon-button>
|
||||
</div>
|
||||
`
|
||||
: ""}
|
||||
<div class="header">
|
||||
<img
|
||||
src=${brandsUrl(this.domain, "icon")}
|
||||
referrerpolicy="no-referrer"
|
||||
@error=${this._onImageError}
|
||||
@load=${this._onImageLoad}
|
||||
/>
|
||||
<div class="info">
|
||||
<div class="primary">${primary}</div>
|
||||
${secondary ? html`<div class="secondary">${secondary}</div>` : ""}
|
||||
</div>
|
||||
${haConfigIntegrationRenderIcons(this.hass, this.manifest)}
|
||||
</div>
|
||||
<ha-integration-header
|
||||
.hass=${this.hass}
|
||||
.banner=${this.disabled
|
||||
? this.hass.localize(
|
||||
"ui.panel.config.integrations.config_entry.disable.disabled"
|
||||
)
|
||||
: undefined}
|
||||
.domain=${this.domain}
|
||||
.label=${item
|
||||
? item.title || item.localized_domain_name || this.domain
|
||||
: undefined}
|
||||
.localizedDomainName=${item ? item.localized_domain_name : undefined}
|
||||
.manifest=${this.manifest}
|
||||
>
|
||||
${this.items.length > 1
|
||||
? html`
|
||||
<div class="back-btn" slot="above-header">
|
||||
<ha-icon-button
|
||||
icon="hass:chevron-left"
|
||||
@click=${this._back}
|
||||
></ha-icon-button>
|
||||
</div>
|
||||
`
|
||||
: ""}
|
||||
</ha-integration-header>
|
||||
|
||||
${item
|
||||
? this._renderSingleEntry(item)
|
||||
: this._renderGroupedIntegration()}
|
||||
@@ -221,25 +199,36 @@ export class HaIntegrationCard extends LitElement {
|
||||
stateText = [
|
||||
`ui.panel.config.integrations.config_entry.state.${item.state}`,
|
||||
];
|
||||
stateTextExtra = html`
|
||||
<br />
|
||||
<a href="/config/logs"
|
||||
>${this.hass.localize(
|
||||
"ui.panel.config.integrations.config_entry.check_the_logs"
|
||||
)}</a
|
||||
>
|
||||
`;
|
||||
if (item.reason) {
|
||||
this.hass.loadBackendTranslation("config", item.domain);
|
||||
stateTextExtra = html`:
|
||||
${this.hass.localize(
|
||||
`component.${item.domain}.config.error.${item.reason}`
|
||||
) || item.reason}`;
|
||||
} else {
|
||||
stateTextExtra = html`
|
||||
<br />
|
||||
<a href="/config/logs"
|
||||
>${this.hass.localize(
|
||||
"ui.panel.config.integrations.config_entry.check_the_logs"
|
||||
)}</a
|
||||
>
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
||||
return html`
|
||||
<div class="content">
|
||||
${stateText
|
||||
? html`
|
||||
<div class="message">
|
||||
${stateText
|
||||
? html`
|
||||
<div class="message">
|
||||
<ha-svg-icon .path=${mdiAlertCircle}></ha-svg-icon>
|
||||
<div>
|
||||
${this.hass.localize(...stateText)}${stateTextExtra}
|
||||
</div>
|
||||
`
|
||||
: ""}
|
||||
</div>
|
||||
`
|
||||
: ""}
|
||||
<div class="content">
|
||||
${devices.length || services.length || entities.length
|
||||
? html`
|
||||
<div>
|
||||
@@ -294,9 +283,9 @@ export class HaIntegrationCard extends LitElement {
|
||||
</mwc-button>`
|
||||
: item.domain in integrationsWithPanel
|
||||
? html`<a
|
||||
href=${`${
|
||||
integrationsWithPanel[item.domain].path
|
||||
}?config_entry=${item.entry_id}`}
|
||||
href=${`${integrationsWithPanel[item.domain]}?config_entry=${
|
||||
item.entry_id
|
||||
}`}
|
||||
><mwc-button>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.integrations.config_entry.configure"
|
||||
@@ -441,14 +430,6 @@ export class HaIntegrationCard extends LitElement {
|
||||
);
|
||||
}
|
||||
|
||||
private _onImageLoad(ev) {
|
||||
ev.target.style.visibility = "initial";
|
||||
}
|
||||
|
||||
private _onImageError(ev) {
|
||||
ev.target.style.visibility = "hidden";
|
||||
}
|
||||
|
||||
private _showOptions(ev) {
|
||||
showOptionsFlowDialog(this, ev.target.closest("ha-card").configEntry);
|
||||
}
|
||||
@@ -605,7 +586,6 @@ export class HaIntegrationCard extends LitElement {
|
||||
static get styles(): CSSResult[] {
|
||||
return [
|
||||
haStyle,
|
||||
haConfigIntegrationsStyles,
|
||||
css`
|
||||
ha-card {
|
||||
display: flex;
|
||||
@@ -619,16 +599,17 @@ export class HaIntegrationCard extends LitElement {
|
||||
--state-color: var(--error-color);
|
||||
--text-on-state-color: var(--text-primary-color);
|
||||
}
|
||||
.state-failed-unload {
|
||||
--state-color: var(--warning-color);
|
||||
--text-on-state-color: var(--primary-text-color);
|
||||
}
|
||||
.state-not-loaded {
|
||||
--state-message-color: var(--primary-text-color);
|
||||
}
|
||||
:host(.highlight) ha-card {
|
||||
--state-color: var(--accent-color);
|
||||
--state-color: var(--primary-color);
|
||||
--text-on-state-color: var(--text-primary-color);
|
||||
}
|
||||
ha-card.group {
|
||||
max-height: 200px;
|
||||
}
|
||||
|
||||
.back-btn {
|
||||
background-color: var(--state-color);
|
||||
@@ -638,50 +619,28 @@ export class HaIntegrationCard extends LitElement {
|
||||
overflow: hidden;
|
||||
}
|
||||
.hasMultiple.single .back-btn {
|
||||
height: 32px;
|
||||
height: 24px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
.hasMultiple.group .back-btn {
|
||||
height: 0px;
|
||||
}
|
||||
|
||||
.header {
|
||||
display: flex;
|
||||
position: relative;
|
||||
align-items: center;
|
||||
padding: 16px 8px 8px 16px;
|
||||
}
|
||||
.group.disabled .header {
|
||||
padding-top: 8px;
|
||||
}
|
||||
.header img {
|
||||
margin-right: 16px;
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
}
|
||||
.header .info div,
|
||||
paper-item-body {
|
||||
word-wrap: break-word;
|
||||
display: -webkit-box;
|
||||
-webkit-box-orient: vertical;
|
||||
-webkit-line-clamp: 2;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
.primary {
|
||||
font-size: 16px;
|
||||
font-weight: 400;
|
||||
color: var(--primary-text-color);
|
||||
}
|
||||
.secondary {
|
||||
font-size: 14px;
|
||||
color: var(--secondary-text-color);
|
||||
}
|
||||
|
||||
.message {
|
||||
font-weight: bold;
|
||||
padding-bottom: 16px;
|
||||
display: flex;
|
||||
margin-left: 40px;
|
||||
}
|
||||
.message ha-svg-icon {
|
||||
color: var(--state-message-color);
|
||||
}
|
||||
.message div {
|
||||
flex: 1;
|
||||
margin-left: 8px;
|
||||
padding-top: 2px;
|
||||
}
|
||||
|
||||
.content {
|
||||
flex: 1;
|
||||
@@ -706,15 +665,34 @@ export class HaIntegrationCard extends LitElement {
|
||||
--mdc-menu-min-width: 200px;
|
||||
}
|
||||
@media (min-width: 563px) {
|
||||
ha-card.group {
|
||||
position: relative;
|
||||
min-height: 164px;
|
||||
}
|
||||
paper-listbox {
|
||||
flex: 1;
|
||||
position: absolute;
|
||||
top: 64px;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
overflow: auto;
|
||||
}
|
||||
.disabled paper-listbox {
|
||||
top: 88px;
|
||||
}
|
||||
}
|
||||
paper-item {
|
||||
cursor: pointer;
|
||||
min-height: 35px;
|
||||
}
|
||||
paper-item-body {
|
||||
word-wrap: break-word;
|
||||
display: -webkit-box;
|
||||
-webkit-box-orient: vertical;
|
||||
-webkit-line-clamp: 2;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
mwc-list-item ha-svg-icon {
|
||||
color: var(--secondary-text-color);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,176 @@
|
||||
import { mdiPackageVariant, mdiCloud } from "@mdi/js";
|
||||
import "@polymer/paper-tooltip/paper-tooltip";
|
||||
import {
|
||||
css,
|
||||
html,
|
||||
customElement,
|
||||
property,
|
||||
LitElement,
|
||||
TemplateResult,
|
||||
} from "lit-element";
|
||||
import { domainToName, IntegrationManifest } from "../../../data/integration";
|
||||
import { HomeAssistant } from "../../../types";
|
||||
import { brandsUrl } from "../../../util/brands-url";
|
||||
|
||||
@customElement("ha-integration-header")
|
||||
export class HaIntegrationHeader extends LitElement {
|
||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||
|
||||
@property() public banner!: string;
|
||||
|
||||
@property() public localizedDomainName?: string;
|
||||
|
||||
@property() public domain!: string;
|
||||
|
||||
@property() public label!: string;
|
||||
|
||||
@property() public manifest?: IntegrationManifest;
|
||||
|
||||
protected render(): TemplateResult {
|
||||
let primary: string;
|
||||
let secondary: string | undefined;
|
||||
|
||||
const domainName =
|
||||
this.localizedDomainName ||
|
||||
domainToName(this.hass.localize, this.domain, this.manifest);
|
||||
|
||||
if (this.label) {
|
||||
primary = this.label;
|
||||
secondary = primary === domainName ? undefined : domainName;
|
||||
} else {
|
||||
primary = domainName;
|
||||
}
|
||||
|
||||
const icons: [string, string][] = [];
|
||||
|
||||
if (this.manifest) {
|
||||
if (!this.manifest.is_built_in) {
|
||||
icons.push([
|
||||
mdiPackageVariant,
|
||||
this.hass.localize(
|
||||
"ui.panel.config.integrations.config_entry.provided_by_custom_integration"
|
||||
),
|
||||
]);
|
||||
}
|
||||
|
||||
if (
|
||||
this.manifest.iot_class &&
|
||||
this.manifest.iot_class.startsWith("cloud_")
|
||||
) {
|
||||
icons.push([
|
||||
mdiCloud,
|
||||
this.hass.localize(
|
||||
"ui.panel.config.integrations.config_entry.depends_on_cloud"
|
||||
),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
return html`
|
||||
${!this.banner
|
||||
? ""
|
||||
: html`<div class="banner">
|
||||
${this.banner}
|
||||
</div>`}
|
||||
<slot name="above-header"></slot>
|
||||
<div class="header">
|
||||
<img
|
||||
src=${brandsUrl(this.domain, "icon")}
|
||||
referrerpolicy="no-referrer"
|
||||
@error=${this._onImageError}
|
||||
@load=${this._onImageLoad}
|
||||
/>
|
||||
<div class="info">
|
||||
<div class="primary">${primary}</div>
|
||||
${secondary ? html`<div class="secondary">${secondary}</div>` : ""}
|
||||
</div>
|
||||
${icons.length === 0
|
||||
? ""
|
||||
: html`
|
||||
<div class="icons">
|
||||
${icons.map(
|
||||
([icon, description]) => html`
|
||||
<span>
|
||||
<ha-svg-icon .path=${icon}></ha-svg-icon>
|
||||
<paper-tooltip animation-delay="0"
|
||||
>${description}</paper-tooltip
|
||||
>
|
||||
</span>
|
||||
`
|
||||
)}
|
||||
</div>
|
||||
`}
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
private _onImageLoad(ev) {
|
||||
ev.target.style.visibility = "initial";
|
||||
}
|
||||
|
||||
private _onImageError(ev) {
|
||||
ev.target.style.visibility = "hidden";
|
||||
}
|
||||
|
||||
static styles = css`
|
||||
.banner {
|
||||
background-color: var(--state-color);
|
||||
color: var(--text-on-state-color);
|
||||
text-align: center;
|
||||
padding: 2px;
|
||||
}
|
||||
.header {
|
||||
display: flex;
|
||||
position: relative;
|
||||
padding: 16px 8px 8px 16px;
|
||||
}
|
||||
.header img {
|
||||
margin-right: 16px;
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
}
|
||||
.header .info {
|
||||
align-self: center;
|
||||
}
|
||||
.header .info div {
|
||||
word-wrap: break-word;
|
||||
display: -webkit-box;
|
||||
-webkit-box-orient: vertical;
|
||||
-webkit-line-clamp: 2;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
.primary {
|
||||
font-size: 16px;
|
||||
font-weight: 400;
|
||||
color: var(--primary-text-color);
|
||||
}
|
||||
.secondary {
|
||||
font-size: 14px;
|
||||
color: var(--secondary-text-color);
|
||||
}
|
||||
.icons {
|
||||
position: absolute;
|
||||
top: 0px;
|
||||
right: 16px;
|
||||
color: var(--text-on-state-color, var(--secondary-text-color));
|
||||
background-color: var(--state-color, #e0e0e0);
|
||||
border-bottom-left-radius: 4px;
|
||||
border-bottom-right-radius: 4px;
|
||||
padding: 1px 4px 2px;
|
||||
}
|
||||
.icons ha-svg-icon {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
}
|
||||
paper-tooltip {
|
||||
white-space: nowrap;
|
||||
}
|
||||
`;
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"ha-integration-header": HaIntegrationHeader;
|
||||
}
|
||||
}
|
||||
@@ -76,9 +76,7 @@ class DialogZHADeviceChildren extends LitElement {
|
||||
},
|
||||
};
|
||||
|
||||
public showDialog(
|
||||
params: ZHADeviceChildrenDialogParams
|
||||
): void {
|
||||
public showDialog(params: ZHADeviceChildrenDialogParams): void {
|
||||
this._device = params.device;
|
||||
this._fetchData();
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ import {
|
||||
html,
|
||||
LitElement,
|
||||
property,
|
||||
PropertyValues,
|
||||
TemplateResult,
|
||||
} from "lit-element";
|
||||
import { computeRTL } from "../../../../../common/util/compute_rtl";
|
||||
@@ -20,6 +21,12 @@ import type { PageNavigation } from "../../../../../layouts/hass-tabs-subpage";
|
||||
import { haStyle } from "../../../../../resources/styles";
|
||||
import type { HomeAssistant, Route } from "../../../../../types";
|
||||
import "../../../ha-config-section";
|
||||
import "../../../../../components/ha-form/ha-form";
|
||||
import {
|
||||
fetchZHAConfiguration,
|
||||
updateZHAConfiguration,
|
||||
ZHAConfiguration,
|
||||
} from "../../../../../data/zha";
|
||||
|
||||
export const zhaTabs: PageNavigation[] = [
|
||||
{
|
||||
@@ -51,6 +58,15 @@ class ZHAConfigDashboard extends LitElement {
|
||||
|
||||
@property() public configEntryId?: string;
|
||||
|
||||
@property() private _configuration?: ZHAConfiguration;
|
||||
|
||||
protected firstUpdated(changedProperties: PropertyValues): void {
|
||||
super.firstUpdated(changedProperties);
|
||||
if (this.hass) {
|
||||
this._fetchConfiguration();
|
||||
}
|
||||
}
|
||||
|
||||
protected render(): TemplateResult {
|
||||
return html`
|
||||
<hass-tabs-subpage
|
||||
@@ -60,10 +76,11 @@ class ZHAConfigDashboard extends LitElement {
|
||||
.tabs=${zhaTabs}
|
||||
back-path="/config/integrations"
|
||||
>
|
||||
<ha-card header="Zigbee Network">
|
||||
<div class="card-content">
|
||||
In the future you can change network settings for ZHA here.
|
||||
</div>
|
||||
<ha-card
|
||||
header=${this.hass.localize(
|
||||
"ui.panel.config.zha.configuration_page.shortcuts_title"
|
||||
)}
|
||||
>
|
||||
${this.configEntryId
|
||||
? html`<div class="card-actions">
|
||||
<a
|
||||
@@ -87,6 +104,38 @@ class ZHAConfigDashboard extends LitElement {
|
||||
</div>`
|
||||
: ""}
|
||||
</ha-card>
|
||||
${this._configuration
|
||||
? Object.entries(this._configuration.schemas).map(
|
||||
([section, schema]) => html` <ha-card
|
||||
header=${this.hass.localize(
|
||||
`ui.panel.config.zha.configuration_page.${section}.title`
|
||||
)}
|
||||
>
|
||||
<div class="card-content">
|
||||
<ha-form
|
||||
.schema=${schema}
|
||||
.data=${this._configuration!.data[section]}
|
||||
@value-changed=${this._dataChanged}
|
||||
.section=${section}
|
||||
.computeLabel=${this._computeLabelCallback(
|
||||
this.hass.localize,
|
||||
section
|
||||
)}
|
||||
></ha-form>
|
||||
</div>
|
||||
</ha-card>`
|
||||
)
|
||||
: ""}
|
||||
<ha-card>
|
||||
<div class="card-actions">
|
||||
<mwc-button @click=${this._updateConfiguration}>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.zha.configuration_page.update_button"
|
||||
)}
|
||||
</mwc-button>
|
||||
</div>
|
||||
</ha-card>
|
||||
|
||||
<a href="/config/zha/add" slot="fab">
|
||||
<ha-fab
|
||||
.label=${this.hass.localize("ui.panel.config.zha.add_device")}
|
||||
@@ -100,6 +149,26 @@ class ZHAConfigDashboard extends LitElement {
|
||||
`;
|
||||
}
|
||||
|
||||
private async _fetchConfiguration(): Promise<void> {
|
||||
this._configuration = await fetchZHAConfiguration(this.hass!);
|
||||
}
|
||||
|
||||
private _dataChanged(ev) {
|
||||
this._configuration!.data[ev.currentTarget!.section] = ev.detail.value;
|
||||
}
|
||||
|
||||
private async _updateConfiguration(): Promise<any> {
|
||||
await updateZHAConfiguration(this.hass!, this._configuration!.data);
|
||||
}
|
||||
|
||||
private _computeLabelCallback(localize, section: string) {
|
||||
// Returns a callback for ha-form to calculate labels per schema object
|
||||
return (schema) =>
|
||||
localize(
|
||||
`ui.panel.config.zha.configuration_page.${section}.${schema.name}`
|
||||
) || schema.name;
|
||||
}
|
||||
|
||||
static get styles(): CSSResultArray {
|
||||
return [
|
||||
haStyle,
|
||||
|
||||
+14
-6
@@ -17,8 +17,8 @@ import {
|
||||
refreshTopology,
|
||||
ZHADevice,
|
||||
} from "../../../../../data/zha";
|
||||
import "../../../../../layouts/hass-subpage";
|
||||
import type { HomeAssistant } from "../../../../../types";
|
||||
import "../../../../../layouts/hass-tabs-subpage";
|
||||
import type { HomeAssistant, Route } from "../../../../../types";
|
||||
import { Network, Edge, Node, EdgeOptions } from "vis-network";
|
||||
import "../../../../../common/search/search-input";
|
||||
import "../../../../../components/device/ha-device-picker";
|
||||
@@ -29,12 +29,17 @@ import { formatAsPaddedHex } from "./functions";
|
||||
import { DeviceRegistryEntry } from "../../../../../data/device_registry";
|
||||
import "../../../../../components/ha-checkbox";
|
||||
import type { HaCheckbox } from "../../../../../components/ha-checkbox";
|
||||
import { zhaTabs } from "./zha-config-dashboard";
|
||||
|
||||
@customElement("zha-network-visualization-page")
|
||||
export class ZHANetworkVisualizationPage extends LitElement {
|
||||
@property({ type: Object }) public hass!: HomeAssistant;
|
||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||
|
||||
@property({ type: Boolean, reflect: true }) public narrow = false;
|
||||
@property({ attribute: false }) public route!: Route;
|
||||
|
||||
@property({ type: Boolean }) public narrow!: boolean;
|
||||
|
||||
@property({ type: Boolean }) public isWide!: boolean;
|
||||
|
||||
@property()
|
||||
public zoomedDeviceId?: string;
|
||||
@@ -133,9 +138,12 @@ export class ZHANetworkVisualizationPage extends LitElement {
|
||||
|
||||
protected render() {
|
||||
return html`
|
||||
<hass-subpage
|
||||
<hass-tabs-subpage
|
||||
.tabs=${zhaTabs}
|
||||
.hass=${this.hass}
|
||||
.narrow=${this.narrow}
|
||||
.isWide=${this.isWide}
|
||||
.route=${this.route}
|
||||
.header=${this.hass.localize(
|
||||
"ui.panel.config.zha.visualization.header"
|
||||
)}
|
||||
@@ -172,7 +180,7 @@ export class ZHANetworkVisualizationPage extends LitElement {
|
||||
>
|
||||
</div>
|
||||
<div id="visualization"></div>
|
||||
</hass-subpage>
|
||||
</hass-tabs-subpage>
|
||||
`;
|
||||
}
|
||||
|
||||
|
||||
+69
-2
@@ -17,9 +17,11 @@ import "../../../../../components/ha-svg-icon";
|
||||
import "../../../../../components/ha-icon-next";
|
||||
import { getSignedPath } from "../../../../../data/auth";
|
||||
import {
|
||||
fetchDataCollectionStatus,
|
||||
fetchNetworkStatus,
|
||||
fetchNodeStatus,
|
||||
NodeStatus,
|
||||
setDataCollectionPreference,
|
||||
ZWaveJSNetwork,
|
||||
ZWaveJSNode,
|
||||
} from "../../../../../data/zwave_js";
|
||||
@@ -55,6 +57,8 @@ class ZWaveJSConfigDashboard extends LitElement {
|
||||
|
||||
@internalProperty() private _icon = mdiCircle;
|
||||
|
||||
@internalProperty() private _dataCollectionOptIn?: boolean;
|
||||
|
||||
protected firstUpdated() {
|
||||
if (this.hass) {
|
||||
this._fetchData();
|
||||
@@ -167,6 +171,39 @@ class ZWaveJSConfigDashboard extends LitElement {
|
||||
</mwc-button>
|
||||
</div>
|
||||
</ha-card>
|
||||
<ha-card>
|
||||
<div class="card-header">
|
||||
<h1>Third-Party Data Reporting</h1>
|
||||
${this._dataCollectionOptIn !== undefined
|
||||
? html`
|
||||
<ha-switch
|
||||
.checked=${this._dataCollectionOptIn === true}
|
||||
@change=${this._dataCollectionToggled}
|
||||
></ha-switch>
|
||||
`
|
||||
: html`
|
||||
<ha-circular-progress
|
||||
size="small"
|
||||
active
|
||||
></ha-circular-progress>
|
||||
`}
|
||||
</div>
|
||||
<div class="card-content">
|
||||
<p>
|
||||
Enable the reporting of anonymized telemetry and
|
||||
statistics to the <em>Z-Wave JS organization</em>. This
|
||||
data will be used to focus development efforts and improve
|
||||
the user experience. Information about the data that is
|
||||
collected and how it is used, including an example of the
|
||||
data collected, can be found in the
|
||||
<a
|
||||
target="_blank"
|
||||
href="https://zwave-js.github.io/node-zwave-js/#/data-collection/data-collection?id=usage-statistics"
|
||||
>Z-Wave JS data collection documentation</a
|
||||
>.
|
||||
</p>
|
||||
</div>
|
||||
</ha-card>
|
||||
`
|
||||
: ``}
|
||||
<button class="link dump" @click=${this._dumpDebugClicked}>
|
||||
@@ -183,11 +220,22 @@ class ZWaveJSConfigDashboard extends LitElement {
|
||||
if (!this.configEntryId) {
|
||||
return;
|
||||
}
|
||||
this._network = await fetchNetworkStatus(this.hass!, this.configEntryId);
|
||||
const [network, dataCollectionStatus] = await Promise.all([
|
||||
fetchNetworkStatus(this.hass!, this.configEntryId),
|
||||
fetchDataCollectionStatus(this.hass!, this.configEntryId),
|
||||
]);
|
||||
|
||||
this._network = network;
|
||||
|
||||
this._status = this._network.client.state;
|
||||
if (this._status === "connected") {
|
||||
this._icon = mdiCheckCircle;
|
||||
}
|
||||
|
||||
this._dataCollectionOptIn =
|
||||
dataCollectionStatus.opted_in === true ||
|
||||
dataCollectionStatus.enabled === true;
|
||||
|
||||
this._fetchNodeStatus();
|
||||
}
|
||||
|
||||
@@ -213,6 +261,14 @@ class ZWaveJSConfigDashboard extends LitElement {
|
||||
});
|
||||
}
|
||||
|
||||
private _dataCollectionToggled(ev) {
|
||||
setDataCollectionPreference(
|
||||
this.hass!,
|
||||
this.configEntryId!,
|
||||
ev.target.checked
|
||||
);
|
||||
}
|
||||
|
||||
private async _dumpDebugClicked() {
|
||||
await this._fetchNodeStatus();
|
||||
|
||||
@@ -321,8 +377,19 @@ class ZWaveJSConfigDashboard extends LitElement {
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.card-header {
|
||||
display: flex;
|
||||
}
|
||||
.card-header h1 {
|
||||
flex: 1;
|
||||
}
|
||||
.card-header ha-switch {
|
||||
width: 48px;
|
||||
margin-top: 16px;
|
||||
}
|
||||
|
||||
ha-card {
|
||||
margin: 0 auto;
|
||||
margin: 0px auto 24px;
|
||||
max-width: 600px;
|
||||
}
|
||||
|
||||
|
||||
+86
-11
@@ -1,3 +1,9 @@
|
||||
import {
|
||||
mdiCheckCircle,
|
||||
mdiCircle,
|
||||
mdiProgressClock,
|
||||
mdiCloseCircle,
|
||||
} from "@mdi/js";
|
||||
import "../../../../../components/ha-settings-row";
|
||||
import "@polymer/paper-item/paper-item";
|
||||
import "@polymer/paper-listbox/paper-listbox";
|
||||
@@ -24,6 +30,7 @@ import {
|
||||
fetchNodeConfigParameters,
|
||||
setNodeConfigParameter,
|
||||
ZWaveJSNodeConfigParams,
|
||||
ZWaveJSSetConfigParamResult,
|
||||
} from "../../../../../data/zwave_js";
|
||||
import "../../../../../layouts/hass-tabs-subpage";
|
||||
import { haStyle } from "../../../../../resources/styles";
|
||||
@@ -38,6 +45,13 @@ import {
|
||||
import { SubscribeMixin } from "../../../../../mixins/subscribe-mixin";
|
||||
import { UnsubscribeFunc } from "home-assistant-js-websocket";
|
||||
import memoizeOne from "memoize-one";
|
||||
import { classMap } from "lit-html/directives/class-map";
|
||||
|
||||
const icons = {
|
||||
accepted: mdiCheckCircle,
|
||||
queued: mdiProgressClock,
|
||||
error: mdiCloseCircle,
|
||||
};
|
||||
|
||||
const getDevice = memoizeOne(
|
||||
(
|
||||
@@ -77,7 +91,12 @@ class ZWaveJSNodeConfig extends SubscribeMixin(LitElement) {
|
||||
@property({ type: Array })
|
||||
private _deviceRegistryEntries?: DeviceRegistryEntry[];
|
||||
|
||||
@internalProperty() private _config?: ZWaveJSNodeConfigParams[];
|
||||
@internalProperty() private _config?: ZWaveJSNodeConfigParams;
|
||||
|
||||
@internalProperty() private _results: Record<
|
||||
string,
|
||||
ZWaveJSSetConfigParamResult
|
||||
> = {};
|
||||
|
||||
@internalProperty() private _error?: string;
|
||||
|
||||
@@ -178,6 +197,7 @@ class ZWaveJSNodeConfig extends SubscribeMixin(LitElement) {
|
||||
}
|
||||
|
||||
private _generateConfigBox(id, item): TemplateResult {
|
||||
const result = this._results[id];
|
||||
const labelAndDescription = html`
|
||||
<span slot="heading">${item.metadata.label}</span>
|
||||
<span slot="description">
|
||||
@@ -192,6 +212,26 @@ class ZWaveJSNodeConfig extends SubscribeMixin(LitElement) {
|
||||
)}
|
||||
</em>`
|
||||
: ""}
|
||||
${result?.status
|
||||
? html` <p
|
||||
class="result ${classMap({
|
||||
[result.status]: true,
|
||||
})}"
|
||||
>
|
||||
<ha-svg-icon
|
||||
.path=${icons[result.status] ? icons[result.status] : mdiCircle}
|
||||
class="result-icon"
|
||||
slot="item-icon"
|
||||
></ha-svg-icon>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.zwave_js.node_config.set_param_" +
|
||||
result.status
|
||||
)}
|
||||
${result.status === "error" && result.error
|
||||
? html` <br /><em>${result.error}</em> `
|
||||
: ""}
|
||||
</p>`
|
||||
: ""}
|
||||
</span>
|
||||
`;
|
||||
|
||||
@@ -293,6 +333,7 @@ class ZWaveJSNodeConfig extends SubscribeMixin(LitElement) {
|
||||
}
|
||||
|
||||
private _switchToggled(ev) {
|
||||
this.setResult(ev.target.key, undefined);
|
||||
this._updateConfigParameter(ev.target, ev.target.checked ? 1 : 0);
|
||||
}
|
||||
|
||||
@@ -303,6 +344,7 @@ class ZWaveJSNodeConfig extends SubscribeMixin(LitElement) {
|
||||
if (this._config![ev.target.key].value === ev.target.selected) {
|
||||
return;
|
||||
}
|
||||
this.setResult(ev.target.key, undefined);
|
||||
|
||||
this._updateConfigParameter(ev.target, Number(ev.target.selected));
|
||||
}
|
||||
@@ -321,20 +363,41 @@ class ZWaveJSNodeConfig extends SubscribeMixin(LitElement) {
|
||||
if (Number(this._config![ev.target.key].value) === value) {
|
||||
return;
|
||||
}
|
||||
this.setResult(ev.target.key, undefined);
|
||||
this.debouncedUpdate(ev.target, value);
|
||||
}
|
||||
|
||||
private _updateConfigParameter(target, value) {
|
||||
private async _updateConfigParameter(target, value) {
|
||||
const nodeId = getNodeId(this._device!);
|
||||
setNodeConfigParameter(
|
||||
this.hass,
|
||||
this.configEntryId!,
|
||||
nodeId!,
|
||||
target.property,
|
||||
value,
|
||||
target.propertyKey ? target.propertyKey : undefined
|
||||
);
|
||||
this._config![target.key].value = value;
|
||||
try {
|
||||
const result = await setNodeConfigParameter(
|
||||
this.hass,
|
||||
this.configEntryId!,
|
||||
nodeId!,
|
||||
target.property,
|
||||
value,
|
||||
target.propertyKey ? target.propertyKey : undefined
|
||||
);
|
||||
this._config![target.key].value = value;
|
||||
|
||||
this.setResult(target.key, result.status);
|
||||
} catch (error) {
|
||||
this.setError(target.key, error.message);
|
||||
}
|
||||
}
|
||||
|
||||
private setResult(key: string, value: string | undefined) {
|
||||
if (value === undefined) {
|
||||
delete this._results[key];
|
||||
this.requestUpdate();
|
||||
} else {
|
||||
this._results = { ...this._results, [key]: { status: value } };
|
||||
}
|
||||
}
|
||||
|
||||
private setError(key: string, message: string) {
|
||||
const errorParam = { status: "error", error: message };
|
||||
this._results = { ...this._results, [key]: errorParam };
|
||||
}
|
||||
|
||||
private get _device(): DeviceRegistryEntry | undefined {
|
||||
@@ -369,6 +432,18 @@ class ZWaveJSNodeConfig extends SubscribeMixin(LitElement) {
|
||||
return [
|
||||
haStyle,
|
||||
css`
|
||||
.accepted {
|
||||
color: var(--success-color);
|
||||
}
|
||||
|
||||
.queued {
|
||||
color: var(--warning-color);
|
||||
}
|
||||
|
||||
.error {
|
||||
color: var(--error-color);
|
||||
}
|
||||
|
||||
.secondary {
|
||||
color: var(--secondary-text-color);
|
||||
}
|
||||
|
||||
@@ -24,17 +24,21 @@ class HaPanelDevEvent extends EventsMixin(LocalizeMixin(PolymerElement)) {
|
||||
return html`
|
||||
<style include="ha-style iron-flex iron-positioning"></style>
|
||||
<style>
|
||||
.content {
|
||||
padding: 16px;
|
||||
max-width: 1200px;
|
||||
margin: auto;
|
||||
}
|
||||
|
||||
:host {
|
||||
-ms-user-select: initial;
|
||||
-webkit-user-select: initial;
|
||||
-moz-user-select: initial;
|
||||
@apply --paper-font-body1;
|
||||
padding: 16px;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.ha-form {
|
||||
margin-right: 16px;
|
||||
.inputs {
|
||||
max-width: 400px;
|
||||
}
|
||||
|
||||
@@ -42,14 +46,17 @@ class HaPanelDevEvent extends EventsMixin(LocalizeMixin(PolymerElement)) {
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
||||
.code-editor {
|
||||
margin-right: 16px;
|
||||
}
|
||||
|
||||
.header {
|
||||
@apply --paper-font-title;
|
||||
}
|
||||
|
||||
event-subscribe-card {
|
||||
display: block;
|
||||
max-width: 800px;
|
||||
margin: 16px auto;
|
||||
margin: 16px 16px 0 0;
|
||||
}
|
||||
|
||||
a {
|
||||
@@ -70,7 +77,7 @@ class HaPanelDevEvent extends EventsMixin(LocalizeMixin(PolymerElement)) {
|
||||
)]]
|
||||
</a>
|
||||
</p>
|
||||
<div class="ha-form">
|
||||
<div class="inputs">
|
||||
<paper-input
|
||||
label="[[localize(
|
||||
'ui.panel.developer-tools.tabs.events.type'
|
||||
@@ -82,17 +89,20 @@ class HaPanelDevEvent extends EventsMixin(LocalizeMixin(PolymerElement)) {
|
||||
<p>
|
||||
[[localize( 'ui.panel.developer-tools.tabs.events.data' )]]
|
||||
</p>
|
||||
</div>
|
||||
<div class="code-editor">
|
||||
<ha-code-editor
|
||||
mode="yaml"
|
||||
value="[[eventData]]"
|
||||
error="[[!validJSON]]"
|
||||
on-value-changed="_yamlChanged"
|
||||
></ha-code-editor>
|
||||
<mwc-button on-click="fireEvent" raised disabled="[[!validJSON]]"
|
||||
>[[localize( 'ui.panel.developer-tools.tabs.events.fire_event'
|
||||
)]]</mwc-button
|
||||
>
|
||||
</div>
|
||||
<mwc-button on-click="fireEvent" raised disabled="[[!validJSON]]"
|
||||
>[[localize( 'ui.panel.developer-tools.tabs.events.fire_event'
|
||||
)]]</mwc-button
|
||||
>
|
||||
<event-subscribe-card hass="[[hass]]"></event-subscribe-card>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
@@ -106,7 +116,6 @@ class HaPanelDevEvent extends EventsMixin(LocalizeMixin(PolymerElement)) {
|
||||
></events-list>
|
||||
</div>
|
||||
</div>
|
||||
<event-subscribe-card hass="[[hass]]"></event-subscribe-card>
|
||||
`;
|
||||
}
|
||||
|
||||
@@ -185,7 +194,7 @@ class HaPanelDevEvent extends EventsMixin(LocalizeMixin(PolymerElement)) {
|
||||
}
|
||||
|
||||
computeFormClasses(narrow) {
|
||||
return narrow ? "" : "layout horizontal";
|
||||
return narrow ? "content" : "content layout horizontal";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -122,19 +122,23 @@ class EventSubscribeCard extends LitElement {
|
||||
return css`
|
||||
form {
|
||||
display: block;
|
||||
padding: 16px;
|
||||
padding: 0 0 16px 16px;
|
||||
}
|
||||
paper-input {
|
||||
display: inline-block;
|
||||
width: 200px;
|
||||
}
|
||||
mwc-button {
|
||||
vertical-align: middle;
|
||||
}
|
||||
.events {
|
||||
margin: -16px 0;
|
||||
padding: 0 16px;
|
||||
}
|
||||
.event {
|
||||
border-bottom: 1px solid var(--divider-color);
|
||||
padding-bottom: 16px;
|
||||
border-top: 1px solid var(--divider-color);
|
||||
padding-top: 8px;
|
||||
padding-bottom: 8px;
|
||||
margin: 16px 0;
|
||||
}
|
||||
.event:last-child {
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import { mdiHelpCircle } from "@mdi/js";
|
||||
import { ERR_CONNECTION_LOST } from "home-assistant-js-websocket";
|
||||
import { safeLoad } from "js-yaml";
|
||||
import {
|
||||
css,
|
||||
@@ -22,12 +24,17 @@ import "../../../components/ha-service-control";
|
||||
import "../../../components/ha-service-picker";
|
||||
import "../../../components/ha-yaml-editor";
|
||||
import type { HaYamlEditor } from "../../../components/ha-yaml-editor";
|
||||
import { forwardHaptic } from "../../../data/haptics";
|
||||
import { ServiceAction } from "../../../data/script";
|
||||
import { callExecuteScript } from "../../../data/service";
|
||||
import {
|
||||
callExecuteScript,
|
||||
serviceCallWillDisconnect,
|
||||
} from "../../../data/service";
|
||||
import { haStyle } from "../../../resources/styles";
|
||||
import "../../../styles/polymer-ha-style";
|
||||
import { HomeAssistant } from "../../../types";
|
||||
import "../../../util/app-localstorage-document";
|
||||
import { documentationUrl } from "../../../util/documentation-url";
|
||||
import { showToast } from "../../../util/toast";
|
||||
|
||||
class HaPanelDevService extends LitElement {
|
||||
@@ -157,12 +164,39 @@ class HaPanelDevService extends LitElement {
|
||||
outlined
|
||||
.expanded=${this._yamlMode}
|
||||
>
|
||||
${this._yamlMode && target
|
||||
? html`<h3>
|
||||
${this.hass.localize(
|
||||
"ui.panel.developer-tools.tabs.services.accepts_target"
|
||||
)}
|
||||
</h3>`
|
||||
${this._yamlMode
|
||||
? html` <div class="description">
|
||||
<h3>
|
||||
${target
|
||||
? html`
|
||||
${this.hass.localize(
|
||||
"ui.panel.developer-tools.tabs.services.accepts_target"
|
||||
)}
|
||||
`
|
||||
: ""}
|
||||
</h3>
|
||||
${this._serviceData?.service
|
||||
? html` <a
|
||||
href="${documentationUrl(
|
||||
this.hass,
|
||||
"/integrations/" +
|
||||
computeDomain(this._serviceData?.service)
|
||||
)}"
|
||||
title="${this.hass.localize(
|
||||
"ui.components.service-control.integration_doc"
|
||||
)}"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>
|
||||
<mwc-icon-button>
|
||||
<ha-svg-icon
|
||||
path=${mdiHelpCircle}
|
||||
class="help-icon"
|
||||
></ha-svg-icon>
|
||||
</mwc-icon-button>
|
||||
</a>`
|
||||
: ""}
|
||||
</div>`
|
||||
: ""}
|
||||
<table class="attributes">
|
||||
<tr>
|
||||
@@ -275,6 +309,14 @@ class HaPanelDevService extends LitElement {
|
||||
try {
|
||||
await callExecuteScript(this.hass, [this._serviceData]);
|
||||
} catch (err) {
|
||||
const [domain, service] = this._serviceData.service.split(".", 2);
|
||||
if (
|
||||
err.error?.code === ERR_CONNECTION_LOST &&
|
||||
serviceCallWillDisconnect(domain, service)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
forwardHaptic("failure");
|
||||
showToast(this, {
|
||||
message:
|
||||
this.hass.localize(
|
||||
@@ -406,6 +448,15 @@ class HaPanelDevService extends LitElement {
|
||||
padding: 4px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.help-icon {
|
||||
color: var(--secondary-text-color);
|
||||
}
|
||||
.description {
|
||||
justify-content: space-between;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
`,
|
||||
];
|
||||
}
|
||||
|
||||
@@ -31,11 +31,18 @@ export class HuiErrorCard extends LitElement implements LovelaceCard {
|
||||
return html``;
|
||||
}
|
||||
|
||||
let dumped: string | undefined;
|
||||
|
||||
if (this._config.origConfig) {
|
||||
try {
|
||||
dumped = safeDump(this._config.origConfig);
|
||||
} catch (err) {
|
||||
dumped = `[Error dumping ${this._config.origConfig}]`;
|
||||
}
|
||||
}
|
||||
|
||||
return html`
|
||||
${this._config.error}
|
||||
${this._config.origConfig
|
||||
? html`<pre>${safeDump(this._config.origConfig)}</pre>`
|
||||
: ""}
|
||||
${this._config.error}${dumped ? html`<pre>${dumped}</pre>` : ""}
|
||||
`;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,41 +1,16 @@
|
||||
import {
|
||||
HassEntities,
|
||||
HassEntity,
|
||||
STATE_NOT_RUNNING,
|
||||
} from "home-assistant-js-websocket";
|
||||
import { isComponentLoaded } from "../../../common/config/is_component_loaded";
|
||||
import { DEFAULT_VIEW_ENTITY_ID } from "../../../common/const";
|
||||
import { HassEntities, HassEntity } from "home-assistant-js-websocket";
|
||||
import { computeDomain } from "../../../common/entity/compute_domain";
|
||||
import { computeObjectId } from "../../../common/entity/compute_object_id";
|
||||
import { computeStateDomain } from "../../../common/entity/compute_state_domain";
|
||||
import { computeStateName } from "../../../common/entity/compute_state_name";
|
||||
import { extractViews } from "../../../common/entity/extract_views";
|
||||
import { getViewEntities } from "../../../common/entity/get_view_entities";
|
||||
import { splitByGroups } from "../../../common/entity/split_by_groups";
|
||||
import { compare } from "../../../common/string/compare";
|
||||
import { LocalizeFunc } from "../../../common/translations/localize";
|
||||
import { subscribeOne } from "../../../common/util/subscribe-one";
|
||||
import {
|
||||
AreaRegistryEntry,
|
||||
subscribeAreaRegistry,
|
||||
} from "../../../data/area_registry";
|
||||
import {
|
||||
DeviceRegistryEntry,
|
||||
subscribeDeviceRegistry,
|
||||
} from "../../../data/device_registry";
|
||||
import {
|
||||
EntityRegistryEntry,
|
||||
subscribeEntityRegistry,
|
||||
} from "../../../data/entity_registry";
|
||||
import { GroupEntity } from "../../../data/group";
|
||||
import type { AreaRegistryEntry } from "../../../data/area_registry";
|
||||
import type { DeviceRegistryEntry } from "../../../data/device_registry";
|
||||
import type { EntityRegistryEntry } from "../../../data/entity_registry";
|
||||
import { domainToName } from "../../../data/integration";
|
||||
import {
|
||||
LovelaceCardConfig,
|
||||
LovelaceConfig,
|
||||
LovelaceViewConfig,
|
||||
} from "../../../data/lovelace";
|
||||
import { LovelaceCardConfig, LovelaceViewConfig } from "../../../data/lovelace";
|
||||
import { SENSOR_DEVICE_CLASS_BATTERY } from "../../../data/sensor";
|
||||
import { HomeAssistant } from "../../../types";
|
||||
import {
|
||||
AlarmPanelCardConfig,
|
||||
EntitiesCardConfig,
|
||||
@@ -57,8 +32,6 @@ const HIDE_DOMAIN = new Set([
|
||||
|
||||
const HIDE_PLATFORM = new Set(["mobile_app"]);
|
||||
|
||||
let subscribedRegistries = false;
|
||||
|
||||
interface SplittedByAreas {
|
||||
areasWithEntities: Array<[AreaRegistryEntry, HassEntity[]]>;
|
||||
otherEntities: HassEntities;
|
||||
@@ -239,7 +212,7 @@ const computeDefaultViewStates = (
|
||||
return states;
|
||||
};
|
||||
|
||||
const generateViewConfig = (
|
||||
export const generateViewConfig = (
|
||||
localize: LocalizeFunc,
|
||||
path: string,
|
||||
title: string | undefined,
|
||||
@@ -373,141 +346,3 @@ export const generateDefaultViewConfig = (
|
||||
|
||||
return config;
|
||||
};
|
||||
|
||||
export const generateLovelaceConfigFromData = async (
|
||||
hass: HomeAssistant,
|
||||
areaEntries: AreaRegistryEntry[],
|
||||
deviceEntries: DeviceRegistryEntry[],
|
||||
entityEntries: EntityRegistryEntry[],
|
||||
entities: HassEntities,
|
||||
localize: LocalizeFunc
|
||||
): Promise<LovelaceConfig> => {
|
||||
if (hass.config.safe_mode) {
|
||||
return {
|
||||
title: hass.config.location_name,
|
||||
views: [
|
||||
{
|
||||
cards: [{ type: "safe-mode" }],
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
const viewEntities = extractViews(entities);
|
||||
|
||||
const views = viewEntities.map((viewEntity: GroupEntity) => {
|
||||
const states = getViewEntities(entities, viewEntity);
|
||||
|
||||
// In the case of a normal view, we use group order as specified in view
|
||||
const groupOrders = {};
|
||||
Object.keys(states).forEach((entityId, idx) => {
|
||||
groupOrders[entityId] = idx;
|
||||
});
|
||||
|
||||
return generateViewConfig(
|
||||
localize,
|
||||
computeObjectId(viewEntity.entity_id),
|
||||
computeStateName(viewEntity),
|
||||
viewEntity.attributes.icon,
|
||||
states,
|
||||
groupOrders
|
||||
);
|
||||
});
|
||||
|
||||
let title = hass.config.location_name;
|
||||
|
||||
// User can override default view. If they didn't, we will add one
|
||||
// that contains all entities.
|
||||
if (
|
||||
viewEntities.length === 0 ||
|
||||
viewEntities[0].entity_id !== DEFAULT_VIEW_ENTITY_ID
|
||||
) {
|
||||
views.unshift(
|
||||
generateDefaultViewConfig(
|
||||
areaEntries,
|
||||
deviceEntries,
|
||||
entityEntries,
|
||||
entities,
|
||||
localize
|
||||
)
|
||||
);
|
||||
|
||||
// Add map of geo locations to default view if loaded
|
||||
if (isComponentLoaded(hass, "geo_location")) {
|
||||
if (views[0] && views[0].cards) {
|
||||
views[0].cards.push({
|
||||
type: "map",
|
||||
geo_location_sources: ["all"],
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Make sure we don't have Home as title and first tab.
|
||||
if (views.length > 1 && title === "Home") {
|
||||
title = "Home Assistant";
|
||||
}
|
||||
}
|
||||
|
||||
// User has no entities
|
||||
if (views.length === 1 && views[0].cards!.length === 0) {
|
||||
views[0].cards!.push({
|
||||
type: "empty-state",
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
title,
|
||||
views,
|
||||
};
|
||||
};
|
||||
|
||||
export const generateLovelaceConfigFromHass = async (
|
||||
hass: HomeAssistant,
|
||||
localize?: LocalizeFunc
|
||||
): Promise<LovelaceConfig> => {
|
||||
if (hass.config.state === STATE_NOT_RUNNING) {
|
||||
return {
|
||||
title: hass.config.location_name,
|
||||
views: [
|
||||
{
|
||||
cards: [{ type: "starting" }],
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
if (hass.config.safe_mode) {
|
||||
return {
|
||||
title: hass.config.location_name,
|
||||
views: [
|
||||
{
|
||||
cards: [{ type: "safe-mode" }],
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
// We want to keep the registry subscriptions alive after generating the UI
|
||||
// so that we don't serve up stale data after changing areas.
|
||||
if (!subscribedRegistries) {
|
||||
subscribedRegistries = true;
|
||||
subscribeAreaRegistry(hass.connection, () => undefined);
|
||||
subscribeDeviceRegistry(hass.connection, () => undefined);
|
||||
subscribeEntityRegistry(hass.connection, () => undefined);
|
||||
}
|
||||
|
||||
const [areaEntries, deviceEntries, entityEntries] = await Promise.all([
|
||||
subscribeOne(hass.connection, subscribeAreaRegistry),
|
||||
subscribeOne(hass.connection, subscribeDeviceRegistry),
|
||||
subscribeOne(hass.connection, subscribeEntityRegistry),
|
||||
]);
|
||||
|
||||
return generateLovelaceConfigFromData(
|
||||
hass,
|
||||
areaEntries,
|
||||
deviceEntries,
|
||||
entityEntries,
|
||||
hass.states,
|
||||
localize || hass.localize
|
||||
);
|
||||
};
|
||||
|
||||
@@ -129,7 +129,8 @@ class HuiGenericEntityRow extends LitElement {
|
||||
stateObj.attributes.brightness
|
||||
? html`${Math.round(
|
||||
(stateObj.attributes.brightness / 255) * 100
|
||||
)} %`
|
||||
)}
|
||||
%`
|
||||
: "")}
|
||||
</div>
|
||||
`
|
||||
|
||||
@@ -2,6 +2,7 @@ import {
|
||||
LovelaceViewConfig,
|
||||
LovelaceViewElement,
|
||||
} from "../../../data/lovelace";
|
||||
import { HuiErrorCard } from "../cards/hui-error-card";
|
||||
import "../views/hui-masonry-view";
|
||||
import { createLovelaceElement } from "./create-element-base";
|
||||
|
||||
@@ -13,7 +14,7 @@ const LAZY_LOAD_LAYOUTS = {
|
||||
|
||||
export const createViewElement = (
|
||||
config: LovelaceViewConfig
|
||||
): LovelaceViewElement => {
|
||||
): LovelaceViewElement | HuiErrorCard => {
|
||||
return createLovelaceElement(
|
||||
"view",
|
||||
config,
|
||||
|
||||
@@ -19,13 +19,15 @@ import "../../../components/ha-formfield";
|
||||
import "../../../components/ha-svg-icon";
|
||||
import "../../../components/ha-switch";
|
||||
import "../../../components/ha-yaml-editor";
|
||||
import type { LovelaceConfig } from "../../../data/lovelace";
|
||||
import type { HassDialog } from "../../../dialogs/make-dialog-manager";
|
||||
import { haStyleDialog } from "../../../resources/styles";
|
||||
import type { HomeAssistant } from "../../../types";
|
||||
import { documentationUrl } from "../../../util/documentation-url";
|
||||
import { expandLovelaceConfigStrategies } from "../strategies/get-strategy";
|
||||
import type { SaveDialogParams } from "./show-save-config-dialog";
|
||||
|
||||
const EMPTY_CONFIG = { views: [] };
|
||||
const EMPTY_CONFIG: LovelaceConfig = { views: [{ title: "Home" }] };
|
||||
|
||||
@customElement("hui-dialog-save-config")
|
||||
export class HuiSaveConfig extends LitElement implements HassDialog {
|
||||
@@ -125,14 +127,17 @@ export class HuiSaveConfig extends LitElement implements HassDialog {
|
||||
</div>
|
||||
${this._params.mode === "storage"
|
||||
? html`
|
||||
<mwc-button slot="primaryAction" @click=${this.closeDialog}
|
||||
>${this.hass!.localize(
|
||||
"ui.common.cancel"
|
||||
)}
|
||||
</mwc-button>
|
||||
<mwc-button
|
||||
slot="primaryAction"
|
||||
.label=${this.hass!.localize("ui.common.cancel")}
|
||||
@click=${this.closeDialog}
|
||||
></mwc-button>
|
||||
<mwc-button
|
||||
slot="primaryAction"
|
||||
?disabled=${this._saving}
|
||||
aria-label=${this.hass!.localize(
|
||||
"ui.panel.lovelace.editor.save_config.save"
|
||||
)}
|
||||
@click=${this._saveConfig}
|
||||
>
|
||||
${this._saving
|
||||
@@ -148,11 +153,13 @@ export class HuiSaveConfig extends LitElement implements HassDialog {
|
||||
</mwc-button>
|
||||
`
|
||||
: html`
|
||||
<mwc-button slot="primaryAction" @click=${this.closeDialog}
|
||||
>${this.hass!.localize(
|
||||
<mwc-button
|
||||
slot="primaryAction"
|
||||
.label=${this.hass!.localize(
|
||||
"ui.panel.lovelace.editor.save_config.close"
|
||||
)}
|
||||
</mwc-button>
|
||||
@click=${this.closeDialog}
|
||||
></mwc-button>
|
||||
`}
|
||||
</ha-dialog>
|
||||
`;
|
||||
@@ -177,7 +184,13 @@ export class HuiSaveConfig extends LitElement implements HassDialog {
|
||||
try {
|
||||
const lovelace = this._params!.lovelace;
|
||||
await lovelace.saveConfig(
|
||||
this._emptyConfig ? EMPTY_CONFIG : lovelace.config
|
||||
this._emptyConfig
|
||||
? EMPTY_CONFIG
|
||||
: await expandLovelaceConfigStrategies({
|
||||
config: lovelace.config,
|
||||
hass: this.hass!,
|
||||
narrow: this._params!.narrow,
|
||||
})
|
||||
);
|
||||
lovelace.setEditMode(true);
|
||||
this._saving = false;
|
||||
|
||||
@@ -14,6 +14,7 @@ const dialogTag = "hui-dialog-save-config";
|
||||
export interface SaveDialogParams {
|
||||
lovelace: Lovelace;
|
||||
mode: "yaml" | "storage";
|
||||
narrow: boolean;
|
||||
}
|
||||
|
||||
let registeredDialog = false;
|
||||
|
||||
@@ -57,7 +57,7 @@ class HuiStateLabelElement extends LitElement implements LovelaceElement {
|
||||
|
||||
if (
|
||||
this._config.attribute &&
|
||||
!stateObj.attributes[this._config.attribute]
|
||||
!(this._config.attribute in stateObj.attributes)
|
||||
) {
|
||||
return html`
|
||||
<hui-warning-element
|
||||
|
||||
@@ -7,6 +7,11 @@ import {
|
||||
property,
|
||||
TemplateResult,
|
||||
} from "lit-element";
|
||||
import { constructUrlCurrentPath } from "../../common/url/construct-url";
|
||||
import {
|
||||
addSearchParam,
|
||||
removeSearchParam,
|
||||
} from "../../common/url/search-params";
|
||||
import { domainToName } from "../../data/integration";
|
||||
import {
|
||||
deleteConfig,
|
||||
@@ -21,14 +26,16 @@ import "../../layouts/hass-error-screen";
|
||||
import "../../layouts/hass-loading-screen";
|
||||
import { HomeAssistant, PanelInfo, Route } from "../../types";
|
||||
import { showToast } from "../../util/toast";
|
||||
import { generateLovelaceConfigFromHass } from "./common/generate-lovelace-config";
|
||||
import { loadLovelaceResources } from "./common/load-resources";
|
||||
import { showSaveDialog } from "./editor/show-save-config-dialog";
|
||||
import "./hui-root";
|
||||
import { generateLovelaceDashboardStrategy } from "./strategies/get-strategy";
|
||||
import { Lovelace } from "./types";
|
||||
|
||||
(window as any).loadCardHelpers = () => import("./custom-card-helpers");
|
||||
|
||||
const DEFAULT_STRATEGY = "original-states";
|
||||
|
||||
interface LovelacePanelConfig {
|
||||
mode: "yaml" | "storage";
|
||||
}
|
||||
@@ -71,7 +78,11 @@ class LovelacePanel extends LitElement {
|
||||
this.lovelace.locale !== this.hass.locale
|
||||
) {
|
||||
// language has been changed, rebuild UI
|
||||
this._setLovelaceConfig(this.lovelace.config, this.lovelace.mode);
|
||||
this._setLovelaceConfig(
|
||||
this.lovelace.config,
|
||||
this.lovelace.rawConfig,
|
||||
this.lovelace.mode
|
||||
);
|
||||
} else if (this.lovelace && this.lovelace.mode === "generated") {
|
||||
// When lovelace is generated, we re-generate each time a user goes
|
||||
// to the states panel to make sure new entities are shown.
|
||||
@@ -139,7 +150,9 @@ class LovelacePanel extends LitElement {
|
||||
`;
|
||||
}
|
||||
|
||||
protected firstUpdated() {
|
||||
protected firstUpdated(changedProps) {
|
||||
super.firstUpdated(changedProps);
|
||||
|
||||
this._fetchConfig(false);
|
||||
if (!this._unsubUpdates) {
|
||||
this._subscribeUpdates();
|
||||
@@ -153,8 +166,14 @@ class LovelacePanel extends LitElement {
|
||||
}
|
||||
|
||||
private async _regenerateConfig() {
|
||||
const conf = await generateLovelaceConfigFromHass(this.hass!);
|
||||
this._setLovelaceConfig(conf, "generated");
|
||||
const conf = await generateLovelaceDashboardStrategy(
|
||||
{
|
||||
hass: this.hass!,
|
||||
narrow: this.narrow,
|
||||
},
|
||||
DEFAULT_STRATEGY
|
||||
);
|
||||
this._setLovelaceConfig(conf, undefined, "generated");
|
||||
this._state = "loaded";
|
||||
}
|
||||
|
||||
@@ -202,6 +221,7 @@ class LovelacePanel extends LitElement {
|
||||
|
||||
private async _fetchConfig(forceDiskRefresh: boolean) {
|
||||
let conf: LovelaceConfig;
|
||||
let rawConf: LovelaceConfig | undefined;
|
||||
let confMode: Lovelace["mode"] = this.panel!.config.mode;
|
||||
let confProm: Promise<LovelaceConfig> | undefined;
|
||||
const llWindow = window as WindowWithLovelaceProm;
|
||||
@@ -236,7 +256,18 @@ class LovelacePanel extends LitElement {
|
||||
}
|
||||
|
||||
try {
|
||||
conf = await confProm!;
|
||||
rawConf = await confProm!;
|
||||
|
||||
// If strategy defined, apply it here.
|
||||
if (rawConf.strategy) {
|
||||
conf = await generateLovelaceDashboardStrategy({
|
||||
config: rawConf,
|
||||
hass: this.hass!,
|
||||
narrow: this.narrow,
|
||||
});
|
||||
} else {
|
||||
conf = rawConf;
|
||||
}
|
||||
} catch (err) {
|
||||
if (err.code !== "config_not_found") {
|
||||
// eslint-disable-next-line
|
||||
@@ -245,8 +276,13 @@ class LovelacePanel extends LitElement {
|
||||
this._errorMsg = err.message;
|
||||
return;
|
||||
}
|
||||
const localize = await this.hass!.loadBackendTranslation("title");
|
||||
conf = await generateLovelaceConfigFromHass(this.hass!, localize);
|
||||
conf = await generateLovelaceDashboardStrategy(
|
||||
{
|
||||
hass: this.hass!,
|
||||
narrow: this.narrow,
|
||||
},
|
||||
DEFAULT_STRATEGY
|
||||
);
|
||||
confMode = "generated";
|
||||
} finally {
|
||||
// Ignore updates for another 2 seconds.
|
||||
@@ -258,7 +294,7 @@ class LovelacePanel extends LitElement {
|
||||
}
|
||||
|
||||
this._state = this._state === "yaml-editor" ? this._state : "loaded";
|
||||
this._setLovelaceConfig(conf, confMode);
|
||||
this._setLovelaceConfig(conf, rawConf, confMode);
|
||||
}
|
||||
|
||||
private _checkLovelaceConfig(config: LovelaceConfig) {
|
||||
@@ -277,11 +313,16 @@ class LovelacePanel extends LitElement {
|
||||
return checkedConfig ? deepFreeze(checkedConfig) : config;
|
||||
}
|
||||
|
||||
private _setLovelaceConfig(config: LovelaceConfig, mode: Lovelace["mode"]) {
|
||||
private _setLovelaceConfig(
|
||||
config: LovelaceConfig,
|
||||
rawConfig: LovelaceConfig | undefined,
|
||||
mode: Lovelace["mode"]
|
||||
) {
|
||||
config = this._checkLovelaceConfig(config);
|
||||
const urlPath = this.urlPath;
|
||||
this.lovelace = {
|
||||
config,
|
||||
rawConfig,
|
||||
mode,
|
||||
urlPath: this.urlPath,
|
||||
editMode: this.lovelace ? this.lovelace.editMode : false,
|
||||
@@ -294,22 +335,39 @@ class LovelacePanel extends LitElement {
|
||||
this._state = "yaml-editor";
|
||||
},
|
||||
setEditMode: (editMode: boolean) => {
|
||||
// If we use a strategy for dashboard, we cannot show the edit UI
|
||||
// So go straight to the YAML editor
|
||||
if (
|
||||
this.lovelace!.rawConfig &&
|
||||
this.lovelace!.rawConfig !== this.lovelace!.config
|
||||
) {
|
||||
this.lovelace!.enableFullEditMode();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!editMode || this.lovelace!.mode !== "generated") {
|
||||
this._updateLovelace({ editMode });
|
||||
return;
|
||||
}
|
||||
|
||||
showSaveDialog(this, {
|
||||
lovelace: this.lovelace!,
|
||||
mode: this.panel!.config.mode,
|
||||
narrow: this.narrow!,
|
||||
});
|
||||
},
|
||||
saveConfig: async (newConfig: LovelaceConfig): Promise<void> => {
|
||||
const { config: previousConfig, mode: previousMode } = this.lovelace!;
|
||||
const {
|
||||
config: previousConfig,
|
||||
rawConfig: previousRawConfig,
|
||||
mode: previousMode,
|
||||
} = this.lovelace!;
|
||||
newConfig = this._checkLovelaceConfig(newConfig);
|
||||
try {
|
||||
// Optimistic update
|
||||
this._updateLovelace({
|
||||
config: newConfig,
|
||||
rawConfig: undefined,
|
||||
mode: "storage",
|
||||
});
|
||||
this._ignoreNextUpdateEvent = true;
|
||||
@@ -320,18 +378,30 @@ class LovelacePanel extends LitElement {
|
||||
// Rollback the optimistic update
|
||||
this._updateLovelace({
|
||||
config: previousConfig,
|
||||
rawConfig: previousRawConfig,
|
||||
mode: previousMode,
|
||||
});
|
||||
throw err;
|
||||
}
|
||||
},
|
||||
deleteConfig: async (): Promise<void> => {
|
||||
const { config: previousConfig, mode: previousMode } = this.lovelace!;
|
||||
const {
|
||||
config: previousConfig,
|
||||
rawConfig: previousRawConfig,
|
||||
mode: previousMode,
|
||||
} = this.lovelace!;
|
||||
try {
|
||||
// Optimistic update
|
||||
const localize = await this.hass!.loadBackendTranslation("title");
|
||||
const generatedConf = await generateLovelaceDashboardStrategy(
|
||||
{
|
||||
hass: this.hass!,
|
||||
narrow: this.narrow,
|
||||
},
|
||||
DEFAULT_STRATEGY
|
||||
);
|
||||
this._updateLovelace({
|
||||
config: await generateLovelaceConfigFromHass(this.hass!, localize),
|
||||
config: generatedConf,
|
||||
rawConfig: undefined,
|
||||
mode: "generated",
|
||||
editMode: false,
|
||||
});
|
||||
@@ -343,6 +413,7 @@ class LovelacePanel extends LitElement {
|
||||
// Rollback the optimistic update
|
||||
this._updateLovelace({
|
||||
config: previousConfig,
|
||||
rawConfig: previousRawConfig,
|
||||
mode: previousMode,
|
||||
});
|
||||
throw err;
|
||||
@@ -356,6 +427,18 @@ class LovelacePanel extends LitElement {
|
||||
...this.lovelace!,
|
||||
...props,
|
||||
};
|
||||
|
||||
if ("editMode" in props) {
|
||||
window.history.replaceState(
|
||||
null,
|
||||
"",
|
||||
constructUrlCurrentPath(
|
||||
props.editMode
|
||||
? addSearchParam({ edit: "1" })
|
||||
: removeSearchParam("edit")
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -106,7 +106,7 @@ class LovelaceFullConfigEditor extends LitElement {
|
||||
|
||||
protected firstUpdated(changedProps: PropertyValues) {
|
||||
super.firstUpdated(changedProps);
|
||||
this.yamlEditor.value = safeDump(this.lovelace!.config);
|
||||
this.yamlEditor.value = safeDump(this.lovelace!.rawConfig);
|
||||
}
|
||||
|
||||
protected updated(changedProps: PropertyValues) {
|
||||
|
||||
@@ -43,9 +43,7 @@ import { navigate } from "../../common/navigate";
|
||||
import {
|
||||
addSearchParam,
|
||||
extractSearchParam,
|
||||
removeSearchParam,
|
||||
} from "../../common/url/search-params";
|
||||
import { constructUrlCurrentPath } from "../../common/url/construct-url";
|
||||
import { computeRTLDirection } from "../../common/util/compute_rtl";
|
||||
import { debounce } from "../../common/util/debounce";
|
||||
import { afterNextRender } from "../../common/util/render-status";
|
||||
@@ -539,7 +537,7 @@ class HUIRoot extends LitElement {
|
||||
protected firstUpdated() {
|
||||
// Check for requested edit mode
|
||||
if (extractSearchParam("edit") === "1") {
|
||||
this._enableEditMode();
|
||||
this.lovelace!.setEditMode(true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -613,7 +611,7 @@ class HUIRoot extends LitElement {
|
||||
}
|
||||
|
||||
if (!force && huiView) {
|
||||
huiView.lovelace = this.lovelace;
|
||||
huiView.lovelace = this.lovelace!;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -715,25 +713,11 @@ class HUIRoot extends LitElement {
|
||||
});
|
||||
return;
|
||||
}
|
||||
this._enableEditMode();
|
||||
}
|
||||
|
||||
private _enableEditMode(): void {
|
||||
this.lovelace!.setEditMode(true);
|
||||
window.history.replaceState(
|
||||
null,
|
||||
"",
|
||||
constructUrlCurrentPath(addSearchParam({ edit: "1" }))
|
||||
);
|
||||
}
|
||||
|
||||
private _editModeDisable(): void {
|
||||
this.lovelace!.setEditMode(false);
|
||||
window.history.replaceState(
|
||||
null,
|
||||
"",
|
||||
constructUrlCurrentPath(removeSearchParam("edit"))
|
||||
);
|
||||
}
|
||||
|
||||
private _editLovelace() {
|
||||
@@ -837,7 +821,7 @@ class HUIRoot extends LitElement {
|
||||
const viewConfig = this.config.views[viewIndex];
|
||||
|
||||
if (!viewConfig) {
|
||||
this._enableEditMode();
|
||||
this.lovelace!.setEditMode(true);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,158 @@
|
||||
import { LovelaceConfig, LovelaceViewConfig } from "../../../data/lovelace";
|
||||
import { AsyncReturnType, HomeAssistant } from "../../../types";
|
||||
import { OriginalStatesStrategy } from "./original-states-strategy";
|
||||
|
||||
const MAX_WAIT_STRATEGY_LOAD = 5000;
|
||||
const CUSTOM_PREFIX = "custom:";
|
||||
|
||||
export interface LovelaceDashboardStrategy {
|
||||
generateDashboard(info: {
|
||||
config?: LovelaceConfig;
|
||||
hass: HomeAssistant;
|
||||
narrow: boolean | undefined;
|
||||
}): Promise<LovelaceConfig>;
|
||||
}
|
||||
|
||||
export interface LovelaceViewStrategy {
|
||||
generateView(info: {
|
||||
view: LovelaceViewConfig;
|
||||
config: LovelaceConfig;
|
||||
hass: HomeAssistant;
|
||||
narrow: boolean | undefined;
|
||||
}): Promise<LovelaceViewConfig>;
|
||||
}
|
||||
|
||||
const strategies: Record<
|
||||
string,
|
||||
LovelaceDashboardStrategy & LovelaceViewStrategy
|
||||
> = {
|
||||
"original-states": OriginalStatesStrategy,
|
||||
};
|
||||
|
||||
const getLovelaceStrategy = async <
|
||||
T extends LovelaceDashboardStrategy | LovelaceViewStrategy
|
||||
>(
|
||||
name: string
|
||||
): Promise<T> => {
|
||||
if (name in strategies) {
|
||||
return strategies[name] as T;
|
||||
}
|
||||
|
||||
if (!name.startsWith(CUSTOM_PREFIX)) {
|
||||
throw new Error("Unknown strategy");
|
||||
}
|
||||
|
||||
const tag = `ll-strategy-${name.substr(CUSTOM_PREFIX.length)}`;
|
||||
|
||||
if (
|
||||
(await Promise.race([
|
||||
customElements.whenDefined(tag),
|
||||
new Promise((resolve) =>
|
||||
setTimeout(() => resolve(true), MAX_WAIT_STRATEGY_LOAD)
|
||||
),
|
||||
])) === true
|
||||
) {
|
||||
throw new Error(
|
||||
`Timeout waiting for strategy element ${tag} to be registered`
|
||||
);
|
||||
}
|
||||
|
||||
return customElements.get(tag);
|
||||
};
|
||||
|
||||
interface GenerateMethods {
|
||||
generateDashboard: LovelaceDashboardStrategy["generateDashboard"];
|
||||
generateView: LovelaceViewStrategy["generateView"];
|
||||
}
|
||||
|
||||
const generateStrategy = async <T extends keyof GenerateMethods>(
|
||||
generateMethod: T,
|
||||
renderError: (err: string | Error) => AsyncReturnType<GenerateMethods[T]>,
|
||||
info: Parameters<GenerateMethods[T]>[0],
|
||||
name: string | undefined
|
||||
): Promise<ReturnType<GenerateMethods[T]>> => {
|
||||
if (!name) {
|
||||
return renderError("No strategy name found");
|
||||
}
|
||||
|
||||
try {
|
||||
const strategy = (await getLovelaceStrategy(name)) as any;
|
||||
return await strategy[generateMethod](info);
|
||||
} catch (err) {
|
||||
if (err.message !== "timeout") {
|
||||
// eslint-disable-next-line
|
||||
console.error(err);
|
||||
}
|
||||
|
||||
return renderError(err);
|
||||
}
|
||||
};
|
||||
|
||||
export const generateLovelaceDashboardStrategy = async (
|
||||
info: Parameters<LovelaceDashboardStrategy["generateDashboard"]>[0],
|
||||
name?: string
|
||||
): ReturnType<LovelaceDashboardStrategy["generateDashboard"]> =>
|
||||
generateStrategy(
|
||||
"generateDashboard",
|
||||
(err) => ({
|
||||
views: [
|
||||
{
|
||||
title: "Error",
|
||||
cards: [
|
||||
{
|
||||
type: "markdown",
|
||||
content: `Error loading the dashboard strategy:\n> ${err}`,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
}),
|
||||
info,
|
||||
name || info.config?.strategy?.name
|
||||
);
|
||||
|
||||
export const generateLovelaceViewStrategy = async (
|
||||
info: Parameters<LovelaceViewStrategy["generateView"]>[0],
|
||||
name?: string
|
||||
): ReturnType<LovelaceViewStrategy["generateView"]> =>
|
||||
generateStrategy(
|
||||
"generateView",
|
||||
(err) => ({
|
||||
cards: [
|
||||
{
|
||||
type: "markdown",
|
||||
content: `Error loading the view strategy:\n> ${err}`,
|
||||
},
|
||||
],
|
||||
}),
|
||||
info,
|
||||
name || info.view?.strategy?.name
|
||||
);
|
||||
|
||||
/**
|
||||
* Find all references to strategies and replaces them with the generated output
|
||||
*/
|
||||
export const expandLovelaceConfigStrategies = async (
|
||||
info: Parameters<LovelaceDashboardStrategy["generateDashboard"]>[0] & {
|
||||
config: LovelaceConfig;
|
||||
}
|
||||
): Promise<LovelaceConfig> => {
|
||||
const config = info.config.strategy
|
||||
? await generateLovelaceDashboardStrategy(info)
|
||||
: { ...info.config };
|
||||
|
||||
config.views = await Promise.all(
|
||||
config.views.map((view) =>
|
||||
view.strategy
|
||||
? generateLovelaceViewStrategy({
|
||||
hass: info.hass,
|
||||
narrow: info.narrow,
|
||||
config,
|
||||
view,
|
||||
})
|
||||
: view
|
||||
)
|
||||
);
|
||||
|
||||
return config;
|
||||
};
|
||||
@@ -0,0 +1,94 @@
|
||||
import { STATE_NOT_RUNNING } from "home-assistant-js-websocket";
|
||||
import { subscribeOne } from "../../../common/util/subscribe-one";
|
||||
import { subscribeAreaRegistry } from "../../../data/area_registry";
|
||||
import { subscribeDeviceRegistry } from "../../../data/device_registry";
|
||||
import { subscribeEntityRegistry } from "../../../data/entity_registry";
|
||||
import { generateDefaultViewConfig } from "../common/generate-lovelace-config";
|
||||
import {
|
||||
LovelaceViewStrategy,
|
||||
LovelaceDashboardStrategy,
|
||||
} from "./get-strategy";
|
||||
|
||||
let subscribedRegistries = false;
|
||||
|
||||
export class OriginalStatesStrategy {
|
||||
static async generateView(
|
||||
info: Parameters<LovelaceViewStrategy["generateView"]>[0]
|
||||
): ReturnType<LovelaceViewStrategy["generateView"]> {
|
||||
const hass = info.hass;
|
||||
|
||||
if (hass.config.state === STATE_NOT_RUNNING) {
|
||||
return {
|
||||
cards: [{ type: "starting" }],
|
||||
};
|
||||
}
|
||||
|
||||
if (hass.config.safe_mode) {
|
||||
return {
|
||||
cards: [{ type: "safe-mode" }],
|
||||
};
|
||||
}
|
||||
|
||||
// We leave this here so we always have the freshest data.
|
||||
if (!subscribedRegistries) {
|
||||
subscribedRegistries = true;
|
||||
subscribeAreaRegistry(hass.connection, () => undefined);
|
||||
subscribeDeviceRegistry(hass.connection, () => undefined);
|
||||
subscribeEntityRegistry(hass.connection, () => undefined);
|
||||
}
|
||||
|
||||
const [
|
||||
areaEntries,
|
||||
deviceEntries,
|
||||
entityEntries,
|
||||
localize,
|
||||
] = await Promise.all([
|
||||
subscribeOne(hass.connection, subscribeAreaRegistry),
|
||||
subscribeOne(hass.connection, subscribeDeviceRegistry),
|
||||
subscribeOne(hass.connection, subscribeEntityRegistry),
|
||||
hass.loadBackendTranslation("title"),
|
||||
]);
|
||||
|
||||
// User can override default view. If they didn't, we will add one
|
||||
// that contains all entities.
|
||||
const view = generateDefaultViewConfig(
|
||||
areaEntries,
|
||||
deviceEntries,
|
||||
entityEntries,
|
||||
hass.states,
|
||||
localize
|
||||
);
|
||||
|
||||
// Add map of geo locations to default view if loaded
|
||||
if (hass.config.components.includes("geo_location")) {
|
||||
if (view && view.cards) {
|
||||
view.cards.push({
|
||||
type: "map",
|
||||
geo_location_sources: ["all"],
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// User has no entities
|
||||
if (view.cards!.length === 0) {
|
||||
view.cards!.push({
|
||||
type: "empty-state",
|
||||
});
|
||||
}
|
||||
|
||||
return view;
|
||||
}
|
||||
|
||||
static async generateDashboard(
|
||||
info: Parameters<LovelaceDashboardStrategy["generateDashboard"]>[0]
|
||||
): ReturnType<LovelaceDashboardStrategy["generateDashboard"]> {
|
||||
return {
|
||||
views: [
|
||||
{
|
||||
strategy: { name: "original-states" },
|
||||
title: info.hass.config.location_name,
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -18,6 +18,8 @@ declare global {
|
||||
|
||||
export interface Lovelace {
|
||||
config: LovelaceConfig;
|
||||
// If not set, a strategy was used to generate everything
|
||||
rawConfig: LovelaceConfig | undefined;
|
||||
editMode: boolean;
|
||||
urlPath: string | null;
|
||||
mode: "generated" | "yaml" | "storage";
|
||||
|
||||
@@ -53,6 +53,8 @@ export class MasonryView extends LitElement implements LovelaceViewElement {
|
||||
|
||||
@property({ type: Number }) public index?: number;
|
||||
|
||||
@property({ type: Boolean }) public isStrategy = false;
|
||||
|
||||
@property({ attribute: false }) public cards: Array<
|
||||
LovelaceCard | HuiErrorCard
|
||||
> = [];
|
||||
@@ -228,7 +230,7 @@ export class MasonryView extends LitElement implements LovelaceViewElement {
|
||||
|
||||
private _addCardToColumn(columnEl, index, editMode) {
|
||||
const card: LovelaceCard = this.cards[index];
|
||||
if (!editMode) {
|
||||
if (!editMode || this.isStrategy) {
|
||||
card.editMode = false;
|
||||
columnEl.appendChild(card);
|
||||
} else {
|
||||
|
||||
@@ -31,6 +31,8 @@ export class PanelView extends LitElement implements LovelaceViewElement {
|
||||
|
||||
@property({ type: Number }) public index?: number;
|
||||
|
||||
@property({ type: Boolean }) public isStrategy = false;
|
||||
|
||||
@property({ attribute: false }) public cards: Array<
|
||||
LovelaceCard | HuiErrorCard
|
||||
> = [];
|
||||
@@ -101,10 +103,15 @@ export class PanelView extends LitElement implements LovelaceViewElement {
|
||||
}
|
||||
|
||||
private _createCard(): void {
|
||||
if (this.cards.length === 0) {
|
||||
this._card = undefined;
|
||||
return;
|
||||
}
|
||||
|
||||
const card: LovelaceCard = this.cards[0];
|
||||
card.isPanel = true;
|
||||
|
||||
if (!this.lovelace?.editMode) {
|
||||
if (this.isStrategy || !this.lovelace?.editMode) {
|
||||
card.editMode = false;
|
||||
this._card = card;
|
||||
return;
|
||||
|
||||
@@ -23,6 +23,7 @@ import { createViewElement } from "../create-element/create-view-element";
|
||||
import { showCreateCardDialog } from "../editor/card-editor/show-create-card-dialog";
|
||||
import { showEditCardDialog } from "../editor/card-editor/show-edit-card-dialog";
|
||||
import { confDeleteCard } from "../editor/delete-card";
|
||||
import { generateLovelaceViewStrategy } from "../strategies/get-strategy";
|
||||
import type { Lovelace, LovelaceBadge, LovelaceCard } from "../types";
|
||||
|
||||
const DEFAULT_VIEW_LAYOUT = "masonry";
|
||||
@@ -39,13 +40,13 @@ declare global {
|
||||
|
||||
@customElement("hui-view")
|
||||
export class HUIView extends UpdatingElement {
|
||||
@property({ attribute: false }) public hass?: HomeAssistant;
|
||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||
|
||||
@property({ attribute: false }) public lovelace?: Lovelace;
|
||||
@property({ attribute: false }) public lovelace!: Lovelace;
|
||||
|
||||
@property({ type: Boolean }) public narrow!: boolean;
|
||||
|
||||
@property({ type: Number }) public index?: number;
|
||||
@property({ type: Number }) public index!: number;
|
||||
|
||||
@internalProperty() private _cards: Array<LovelaceCard | HuiErrorCard> = [];
|
||||
|
||||
@@ -55,6 +56,8 @@ export class HUIView extends UpdatingElement {
|
||||
|
||||
private _layoutElement?: LovelaceViewElement;
|
||||
|
||||
private _viewConfigTheme?: string;
|
||||
|
||||
// Public to make demo happy
|
||||
public createCardElement(cardConfig: LovelaceCardConfig) {
|
||||
const element = createCardElement(cardConfig) as LovelaceCard;
|
||||
@@ -89,129 +92,142 @@ export class HUIView extends UpdatingElement {
|
||||
protected updated(changedProperties: PropertyValues): void {
|
||||
super.updated(changedProperties);
|
||||
|
||||
const hass = this.hass!;
|
||||
const lovelace = this.lovelace!;
|
||||
/*
|
||||
We need to handle the following use cases:
|
||||
- initialization: create layout element, populate
|
||||
- config changed to view with same layout element
|
||||
- config changed to view with different layout element
|
||||
- forwarded properties hass/narrow/lovelace/cards/badges change
|
||||
- cards/badges change if one is rebuild when it was loaded later
|
||||
- lovelace changes if edit mode is enabled or config has changed
|
||||
*/
|
||||
|
||||
const hassChanged = changedProperties.has("hass");
|
||||
const oldLovelace = changedProperties.get("lovelace") as Lovelace;
|
||||
|
||||
let editModeChanged = false;
|
||||
let configChanged = false;
|
||||
|
||||
if (changedProperties.has("index")) {
|
||||
configChanged = true;
|
||||
} else if (changedProperties.has("lovelace")) {
|
||||
editModeChanged =
|
||||
oldLovelace && lovelace.editMode !== oldLovelace.editMode;
|
||||
configChanged = !oldLovelace || lovelace.config !== oldLovelace.config;
|
||||
}
|
||||
|
||||
let viewConfig: LovelaceViewConfig | undefined;
|
||||
|
||||
if (configChanged) {
|
||||
viewConfig = lovelace.config.views[this.index!];
|
||||
viewConfig = {
|
||||
...viewConfig,
|
||||
type: viewConfig.panel
|
||||
? PANEL_VIEW_LAYOUT
|
||||
: viewConfig.type || DEFAULT_VIEW_LAYOUT,
|
||||
};
|
||||
}
|
||||
|
||||
let replace = false;
|
||||
const oldLovelace = changedProperties.get("lovelace") as this["lovelace"];
|
||||
|
||||
// If config has changed, create element if necessary and set all values.
|
||||
if (
|
||||
configChanged &&
|
||||
(!this._layoutElement || this._layoutElementType !== viewConfig!.type)
|
||||
changedProperties.has("index") ||
|
||||
(changedProperties.has("lovelace") &&
|
||||
(!oldLovelace ||
|
||||
this.lovelace.config.views[this.index] !==
|
||||
oldLovelace.config.views[this.index]))
|
||||
) {
|
||||
replace = true;
|
||||
this._layoutElement = createViewElement(viewConfig!);
|
||||
this._layoutElementType = viewConfig!.type;
|
||||
this._layoutElement.addEventListener("ll-create-card", () => {
|
||||
showCreateCardDialog(this, {
|
||||
lovelaceConfig: this.lovelace!.config,
|
||||
saveConfig: this.lovelace!.saveConfig,
|
||||
path: [this.index!],
|
||||
this._initializeConfig();
|
||||
return;
|
||||
}
|
||||
|
||||
// If no layout element, we're still creating one
|
||||
if (this._layoutElement) {
|
||||
// Config has not changed. Just props
|
||||
if (changedProperties.has("hass")) {
|
||||
this._badges.forEach((badge) => {
|
||||
badge.hass = this.hass;
|
||||
});
|
||||
});
|
||||
this._layoutElement.addEventListener("ll-edit-card", (ev) => {
|
||||
showEditCardDialog(this, {
|
||||
lovelaceConfig: this.lovelace!.config,
|
||||
saveConfig: this.lovelace!.saveConfig,
|
||||
path: ev.detail.path,
|
||||
|
||||
this._cards.forEach((element) => {
|
||||
element.hass = this.hass;
|
||||
});
|
||||
});
|
||||
this._layoutElement.addEventListener("ll-delete-card", (ev) => {
|
||||
confDeleteCard(this, this.hass!, this.lovelace!, ev.detail.path);
|
||||
});
|
||||
}
|
||||
|
||||
if (configChanged) {
|
||||
this._createBadges(viewConfig!);
|
||||
this._createCards(viewConfig!);
|
||||
|
||||
this._layoutElement!.hass = this.hass;
|
||||
this._layoutElement!.narrow = this.narrow;
|
||||
this._layoutElement!.lovelace = lovelace;
|
||||
this._layoutElement!.index = this.index;
|
||||
}
|
||||
|
||||
if (hassChanged) {
|
||||
this._badges.forEach((badge) => {
|
||||
badge.hass = hass;
|
||||
});
|
||||
|
||||
this._cards.forEach((element) => {
|
||||
element.hass = hass;
|
||||
});
|
||||
|
||||
this._layoutElement!.hass = this.hass;
|
||||
}
|
||||
|
||||
if (changedProperties.has("narrow")) {
|
||||
this._layoutElement!.narrow = this.narrow;
|
||||
}
|
||||
|
||||
if (editModeChanged) {
|
||||
this._layoutElement!.lovelace = lovelace;
|
||||
}
|
||||
|
||||
if (
|
||||
configChanged ||
|
||||
hassChanged ||
|
||||
editModeChanged ||
|
||||
changedProperties.has("_cards") ||
|
||||
changedProperties.has("_badges")
|
||||
) {
|
||||
this._layoutElement!.cards = this._cards;
|
||||
this._layoutElement!.badges = this._badges;
|
||||
this._layoutElement.hass = this.hass;
|
||||
}
|
||||
if (changedProperties.has("narrow")) {
|
||||
this._layoutElement.narrow = this.narrow;
|
||||
}
|
||||
if (changedProperties.has("lovelace")) {
|
||||
this._layoutElement.lovelace = this.lovelace;
|
||||
}
|
||||
if (changedProperties.has("_cards")) {
|
||||
this._layoutElement.cards = this._cards;
|
||||
}
|
||||
if (changedProperties.has("_badges")) {
|
||||
this._layoutElement.badges = this._badges;
|
||||
}
|
||||
}
|
||||
|
||||
const oldHass = changedProperties.get("hass") as this["hass"] | undefined;
|
||||
|
||||
if (
|
||||
configChanged ||
|
||||
editModeChanged ||
|
||||
(hassChanged &&
|
||||
oldHass &&
|
||||
(hass.themes !== oldHass.themes ||
|
||||
hass.selectedTheme !== oldHass.selectedTheme))
|
||||
changedProperties.has("hass") &&
|
||||
(!oldHass ||
|
||||
this.hass.themes !== oldHass.themes ||
|
||||
this.hass.selectedTheme !== oldHass.selectedTheme)
|
||||
) {
|
||||
applyThemesOnElement(
|
||||
this,
|
||||
hass.themes,
|
||||
lovelace.config.views[this.index!].theme
|
||||
);
|
||||
applyThemesOnElement(this, this.hass.themes, this._viewConfigTheme);
|
||||
}
|
||||
}
|
||||
|
||||
private async _initializeConfig() {
|
||||
let viewConfig = this.lovelace.config.views[this.index];
|
||||
let isStrategy = false;
|
||||
|
||||
if (viewConfig.strategy) {
|
||||
isStrategy = true;
|
||||
viewConfig = await generateLovelaceViewStrategy({
|
||||
hass: this.hass,
|
||||
config: this.lovelace.config,
|
||||
narrow: this.narrow,
|
||||
view: viewConfig,
|
||||
});
|
||||
}
|
||||
|
||||
if (this._layoutElement && replace) {
|
||||
viewConfig = {
|
||||
...viewConfig,
|
||||
type: viewConfig.panel
|
||||
? PANEL_VIEW_LAYOUT
|
||||
: viewConfig.type || DEFAULT_VIEW_LAYOUT,
|
||||
};
|
||||
|
||||
// Create a new layout element if necessary.
|
||||
let addLayoutElement = false;
|
||||
|
||||
if (!this._layoutElement || this._layoutElementType !== viewConfig.type) {
|
||||
addLayoutElement = true;
|
||||
this._createLayoutElement(viewConfig);
|
||||
}
|
||||
|
||||
this._createBadges(viewConfig);
|
||||
this._createCards(viewConfig);
|
||||
this._layoutElement!.isStrategy = isStrategy;
|
||||
this._layoutElement!.hass = this.hass;
|
||||
this._layoutElement!.narrow = this.narrow;
|
||||
this._layoutElement!.lovelace = this.lovelace;
|
||||
this._layoutElement!.index = this.index;
|
||||
this._layoutElement!.cards = this._cards;
|
||||
this._layoutElement!.badges = this._badges;
|
||||
|
||||
applyThemesOnElement(this, this.hass.themes, viewConfig.theme);
|
||||
this._viewConfigTheme = viewConfig.theme;
|
||||
|
||||
if (addLayoutElement) {
|
||||
while (this.lastChild) {
|
||||
this.removeChild(this.lastChild);
|
||||
}
|
||||
this.appendChild(this._layoutElement);
|
||||
this.appendChild(this._layoutElement!);
|
||||
}
|
||||
}
|
||||
|
||||
private _createLayoutElement(config: LovelaceViewConfig): void {
|
||||
this._layoutElement = createViewElement(config) as LovelaceViewElement;
|
||||
this._layoutElementType = config.type;
|
||||
this._layoutElement.addEventListener("ll-create-card", () => {
|
||||
showCreateCardDialog(this, {
|
||||
lovelaceConfig: this.lovelace.config,
|
||||
saveConfig: this.lovelace.saveConfig,
|
||||
path: [this.index],
|
||||
});
|
||||
});
|
||||
this._layoutElement.addEventListener("ll-edit-card", (ev) => {
|
||||
showEditCardDialog(this, {
|
||||
lovelaceConfig: this.lovelace.config,
|
||||
saveConfig: this.lovelace.saveConfig,
|
||||
path: ev.detail.path,
|
||||
});
|
||||
});
|
||||
this._layoutElement.addEventListener("ll-delete-card", (ev) => {
|
||||
confDeleteCard(this, this.hass!, this.lovelace!, ev.detail.path);
|
||||
});
|
||||
}
|
||||
|
||||
private _createBadges(config: LovelaceViewConfig): void {
|
||||
if (!config || !config.badges || !Array.isArray(config.badges)) {
|
||||
this._badges = [];
|
||||
|
||||
@@ -3,6 +3,7 @@ import {
|
||||
callService,
|
||||
Connection,
|
||||
ERR_INVALID_AUTH,
|
||||
ERR_CONNECTION_LOST,
|
||||
HassConfig,
|
||||
subscribeConfig,
|
||||
subscribeEntities,
|
||||
@@ -13,6 +14,7 @@ import { broadcastConnectionStatus } from "../data/connection-status";
|
||||
import { subscribeFrontendUserData } from "../data/frontend";
|
||||
import { forwardHaptic } from "../data/haptics";
|
||||
import { DEFAULT_PANEL } from "../data/panel";
|
||||
import { serviceCallWillDisconnect } from "../data/service";
|
||||
import { NumberFormat } from "../data/translation";
|
||||
import { subscribePanels } from "../data/ws-panels";
|
||||
import { translationMetadata } from "../resources/translations-metadata";
|
||||
@@ -78,6 +80,12 @@ export const connectionMixin = <T extends Constructor<HassBaseEl>>(
|
||||
target
|
||||
)) as Promise<ServiceCallResponse>;
|
||||
} catch (err) {
|
||||
if (
|
||||
err.error?.code === ERR_CONNECTION_LOST &&
|
||||
serviceCallWillDisconnect(domain, service)
|
||||
) {
|
||||
throw err;
|
||||
}
|
||||
if (__DEV__) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.error(
|
||||
|
||||
@@ -32,6 +32,19 @@ export default <T extends Constructor<HassBaseEl>>(superClass: T) =>
|
||||
storeState(this.hass!);
|
||||
});
|
||||
mql.addListener((ev) => this._applyTheme(ev.matches));
|
||||
if (mql.matches) {
|
||||
applyThemesOnElement(
|
||||
document.documentElement,
|
||||
{
|
||||
default_theme: "default",
|
||||
default_dark_theme: null,
|
||||
themes: {},
|
||||
darkMode: false,
|
||||
},
|
||||
"default",
|
||||
{ dark: true }
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
protected hassConnected() {
|
||||
|
||||
@@ -28,13 +28,13 @@ export const urlSyncMixin = <
|
||||
if (history.length === 1) {
|
||||
history.replaceState({ ...history.state, root: true }, "");
|
||||
}
|
||||
window.addEventListener("popstate", this._popstateChangeListener);
|
||||
top.addEventListener("popstate", this._popstateChangeListener);
|
||||
this.addEventListener("dialog-closed", this._dialogClosedListener);
|
||||
}
|
||||
|
||||
public disconnectedCallback(): void {
|
||||
super.disconnectedCallback();
|
||||
window.removeEventListener("popstate", this._popstateChangeListener);
|
||||
top.removeEventListener("popstate", this._popstateChangeListener);
|
||||
this.removeEventListener("dialog-closed", this._dialogClosedListener);
|
||||
}
|
||||
|
||||
@@ -45,21 +45,21 @@ export const urlSyncMixin = <
|
||||
console.log("dialog closed", ev.detail.dialog);
|
||||
console.log(
|
||||
"open",
|
||||
history.state?.open,
|
||||
top.history.state?.open,
|
||||
"dialog",
|
||||
history.state?.dialog
|
||||
top.history.state?.dialog
|
||||
);
|
||||
}
|
||||
// If not closed by navigating back, and not a new dialog is open, remove the open state from history
|
||||
if (
|
||||
history.state?.open &&
|
||||
history.state?.dialog === ev.detail.dialog
|
||||
top.history.state?.open &&
|
||||
top.history.state?.dialog === ev.detail.dialog
|
||||
) {
|
||||
if (DEBUG) {
|
||||
console.log("remove state", ev.detail.dialog);
|
||||
}
|
||||
this._ignoreNextPopState = true;
|
||||
history.back();
|
||||
top.history.back();
|
||||
}
|
||||
};
|
||||
|
||||
@@ -73,7 +73,7 @@ export const urlSyncMixin = <
|
||||
if (DEBUG) {
|
||||
console.log("remove old state", ev.state.oldState);
|
||||
}
|
||||
history.back();
|
||||
top.history.back();
|
||||
return;
|
||||
}
|
||||
this._ignoreNextPopState = false;
|
||||
@@ -98,7 +98,7 @@ export const urlSyncMixin = <
|
||||
console.log("dialog could not be closed");
|
||||
}
|
||||
// dialog could not be closed, push state again
|
||||
history.pushState(
|
||||
top.history.pushState(
|
||||
{
|
||||
dialog: state.dialog,
|
||||
open: true,
|
||||
|
||||
+27
-14
@@ -374,7 +374,7 @@
|
||||
"clear": "Clear",
|
||||
"show_areas": "Show areas",
|
||||
"area": "Area",
|
||||
"add_new": "Add new area…",
|
||||
"add_new": "Add new area...",
|
||||
"no_areas": "You don't have any areas",
|
||||
"no_match": "No matching areas found",
|
||||
"add_dialog": {
|
||||
@@ -452,7 +452,8 @@
|
||||
"required": "This field is required",
|
||||
"target": "Targets",
|
||||
"target_description": "What should this service use as targeted areas, devices or entities.",
|
||||
"service_data": "Service data"
|
||||
"service_data": "Service data",
|
||||
"integration_doc": "Integration documentation"
|
||||
},
|
||||
"related-items": {
|
||||
"no_related_found": "No related items found.",
|
||||
@@ -847,7 +848,7 @@
|
||||
},
|
||||
"notification_toast": {
|
||||
"service_call_failed": "Failed to call service {service}.",
|
||||
"connection_lost": "Connection lost. Reconnecting…",
|
||||
"connection_lost": "Connection lost. Reconnecting...",
|
||||
"started": "Home Assistant has started!",
|
||||
"starting": "Home Assistant is starting, not everything will be available until it is finished.",
|
||||
"wrapping_up_startup": "Wrapping up startup, not everything will be available until it is finished.",
|
||||
@@ -1055,7 +1056,7 @@
|
||||
"description": "View the Home Assistant logs",
|
||||
"details": "Log Details ({level})",
|
||||
"load_full_log": "Load Full Home Assistant Log",
|
||||
"loading_log": "Loading error log…",
|
||||
"loading_log": "Loading error log...",
|
||||
"no_errors": "No errors have been reported",
|
||||
"no_issues": "There are no new issues!",
|
||||
"clear": "Clear",
|
||||
@@ -1196,7 +1197,7 @@
|
||||
},
|
||||
"server_management": {
|
||||
"heading": "Server management",
|
||||
"introduction": "Control your Home Assistant server… from Home Assistant.",
|
||||
"introduction": "Control your Home Assistant server... from Home Assistant.",
|
||||
"restart": "Restart",
|
||||
"confirm_restart": "Are you sure you want to restart Home Assistant?",
|
||||
"stop": "Stop",
|
||||
@@ -1781,7 +1782,7 @@
|
||||
"integrations_link_all_features": " all available features",
|
||||
"connected": "Connected",
|
||||
"not_connected": "Not Connected",
|
||||
"fetching_subscription": "Fetching subscription…",
|
||||
"fetching_subscription": "Fetching subscription...",
|
||||
"tts": {
|
||||
"title": "Text to Speech",
|
||||
"info": "Bring personality to your home by having it speak to you by using our Text-to-Speech services. You can use this in automations and scripts by using the {service} service.",
|
||||
@@ -1846,7 +1847,7 @@
|
||||
"no_hooks_yet2": " or by creating a ",
|
||||
"no_hooks_yet_link_automation": "webhook automation",
|
||||
"link_learn_more": "Learn more about creating webhook-powered automations.",
|
||||
"loading": "Loading ...",
|
||||
"loading": "Loading...",
|
||||
"manage": "Manage",
|
||||
"disable_hook_error_msg": "Failed to disable webhook:"
|
||||
}
|
||||
@@ -2179,7 +2180,7 @@
|
||||
"loaded": "Loaded",
|
||||
"setup_error": "Failed to set up",
|
||||
"migration_error": "Migration error",
|
||||
"setup_retry": "Retrying to set up",
|
||||
"setup_retry": "Retrying setup",
|
||||
"not_loaded": "Not loaded",
|
||||
"failed_unload": "Failed to unload"
|
||||
}
|
||||
@@ -2386,6 +2387,15 @@
|
||||
"manufacturer_code_override": "Manufacturer Code Override",
|
||||
"value": "Value"
|
||||
},
|
||||
"configuration_page": {
|
||||
"shortcuts_title": "Shortcuts",
|
||||
"update_button": "Update Configuration",
|
||||
"zha_options": {
|
||||
"title": "Global Options",
|
||||
"enable_identify_on_join": "Enable identify effect when devices join the network",
|
||||
"default_light_transition": "Default light transition time (seconds)"
|
||||
}
|
||||
},
|
||||
"add_device_page": {
|
||||
"spinner": "Searching for ZHA Zigbee devices...",
|
||||
"pairing_mode": "Make sure your devices are in pairing mode. Check the instructions of your device on how to do this.",
|
||||
@@ -2596,7 +2606,10 @@
|
||||
"zwave_js_device_database": "Z-Wave JS Device Database",
|
||||
"battery_device_notice": "Battery devices must be awake to update their config. Please refer to your device manual for instructions on how to wake the device.",
|
||||
"parameter_is_read_only": "This parameter is read-only.",
|
||||
"error_device_not_found": "Device not found"
|
||||
"error_device_not_found": "Device not found",
|
||||
"set_param_accepted": "The parameter has been updated.",
|
||||
"set_param_queued": "The parameter change has been queued, and will be updated when the device wakes up.",
|
||||
"set_param_error": "An error occurred."
|
||||
},
|
||||
"node_status": {
|
||||
"unknown": "Unknown",
|
||||
@@ -2799,7 +2812,7 @@
|
||||
"clear": "Clear",
|
||||
"delete": "Delete card",
|
||||
"duplicate": "Duplicate card",
|
||||
"move": "Move to View",
|
||||
"move": "Move to view",
|
||||
"move_before": "Move card before",
|
||||
"move_after": "Move card after",
|
||||
"options": "More options",
|
||||
@@ -3410,7 +3423,7 @@
|
||||
"events": {
|
||||
"title": "Events",
|
||||
"description": "Fire an event on the event bus.",
|
||||
"documentation": "Events Documentation.",
|
||||
"documentation": "Events documentation",
|
||||
"type": "Event Type",
|
||||
"data": "Event Data (YAML, optional)",
|
||||
"fire_event": "Fire Event",
|
||||
@@ -3423,7 +3436,7 @@
|
||||
"start_listening": "Start listening",
|
||||
"stop_listening": "Stop listening",
|
||||
"alert_event_type": "Event type is a mandatory field",
|
||||
"notification_event_fired": "Event {type} successful fired!"
|
||||
"notification_event_fired": "Event {type} successfully fired!"
|
||||
},
|
||||
"services": {
|
||||
"title": "Services",
|
||||
@@ -3442,8 +3455,8 @@
|
||||
},
|
||||
"states": {
|
||||
"title": "States",
|
||||
"description1": "Set the representation of a device within Home Assistant.",
|
||||
"description2": "This will not communicate with the actual device.",
|
||||
"description1": "Set the current state representation of an entity within Home Assistant.",
|
||||
"description2": "If the entity belongs to a device, there will be no actual communication with that device.",
|
||||
"entity": "Entity",
|
||||
"state": "State",
|
||||
"attributes": "Attributes",
|
||||
|
||||
@@ -256,3 +256,12 @@ export interface LocalizeMixin {
|
||||
hass?: HomeAssistant;
|
||||
localize: LocalizeFunc;
|
||||
}
|
||||
|
||||
// https://www.jpwilliams.dev/how-to-unpack-the-return-type-of-a-promise-in-typescript
|
||||
export type AsyncReturnType<T extends (...args: any) => any> = T extends (
|
||||
...args: any
|
||||
) => Promise<infer U>
|
||||
? U
|
||||
: T extends (...args: any) => infer U
|
||||
? U
|
||||
: never;
|
||||
|
||||
@@ -15,6 +15,56 @@ describe("formatNumber", () => {
|
||||
);
|
||||
});
|
||||
|
||||
it("Test format 'none' (keep dot despite language 'de')", () => {
|
||||
assert.strictEqual(
|
||||
formatNumber(1.23, {
|
||||
language: "de",
|
||||
number_format: NumberFormat.none,
|
||||
}),
|
||||
"1.23"
|
||||
);
|
||||
});
|
||||
|
||||
it("Ensure zero is kept for format 'language'", () => {
|
||||
assert.strictEqual(
|
||||
formatNumber(0, {
|
||||
language: "en",
|
||||
number_format: NumberFormat.language,
|
||||
}),
|
||||
"0"
|
||||
);
|
||||
});
|
||||
|
||||
it("Ensure zero is kept for format 'none'", () => {
|
||||
assert.strictEqual(
|
||||
formatNumber(0, {
|
||||
language: "en",
|
||||
number_format: NumberFormat.none,
|
||||
}),
|
||||
"0"
|
||||
);
|
||||
});
|
||||
|
||||
it("Test empty string input for format 'none'", () => {
|
||||
assert.strictEqual(
|
||||
formatNumber("", {
|
||||
language: "en",
|
||||
number_format: NumberFormat.none,
|
||||
}),
|
||||
""
|
||||
);
|
||||
});
|
||||
|
||||
it("Test empty string input for format 'language'", () => {
|
||||
assert.strictEqual(
|
||||
formatNumber("", {
|
||||
language: "en",
|
||||
number_format: NumberFormat.language,
|
||||
}),
|
||||
"0"
|
||||
);
|
||||
});
|
||||
|
||||
it("Formats number with options", () => {
|
||||
assert.strictEqual(
|
||||
formatNumber(
|
||||
|
||||
+108
-28
@@ -180,6 +180,7 @@
|
||||
"new_update_available": "{name} {version} е налично",
|
||||
"not_available_arch": "Тази добавка не е съвместима с процесора на вашето устройство или операционната система, която сте инсталирали на вашето устройство.",
|
||||
"not_available_version": "Изпълнявате Home Assistant {core_version_installed}, за да актуализирате до тази версия на добавката ви е необходима поне версия {core_version_needed} на Home Assistant",
|
||||
"open_web_ui": "Отваряне на потребителския WEB интерфейс",
|
||||
"option": {
|
||||
"auto_update": {
|
||||
"description": "Автоматично актуализиране на добавката, когато има налична нова версия",
|
||||
@@ -324,9 +325,9 @@
|
||||
"text": "Искате ли да рестартирате добавката с вашите промени?"
|
||||
},
|
||||
"update": {
|
||||
"create_snapshot": "Създаване на моментна снимка на {name} преди актуализиране",
|
||||
"snapshot": "Моментна снимка",
|
||||
"snapshotting": "Създава се моментна снимка на {name}",
|
||||
"create_snapshot": "Създаване на снапшот на {name} преди актуализиране",
|
||||
"snapshot": "Снапшот",
|
||||
"snapshotting": "Създава се снапшот на {name}",
|
||||
"updating": "Актуализиране на {name} до версия {version}"
|
||||
}
|
||||
},
|
||||
@@ -338,18 +339,18 @@
|
||||
},
|
||||
"panel": {
|
||||
"dashboard": "Табло",
|
||||
"snapshots": "Моментни снимки",
|
||||
"snapshots": "Снапшоти",
|
||||
"store": "Хранилище за добавки",
|
||||
"system": "Система"
|
||||
},
|
||||
"snapshot": {
|
||||
"addons": "Добавки",
|
||||
"available_snapshots": "Налични системни снимки",
|
||||
"could_not_create": "Не можа да се създаде моментна снимка",
|
||||
"available_snapshots": "Налични снапшоти",
|
||||
"could_not_create": "Не можа да се създаде снапшот",
|
||||
"create": "Създаване",
|
||||
"create_blocked_not_running": "Създаването на моментна снимка в момента не е възможно, тъй като системата е в състояние {state}.",
|
||||
"create_snapshot": "Създаване на моментна снимка",
|
||||
"description": "Моментните снимки Ви позволяват лесно да архивирате и възстановявате всички данни от вашия екземпляр на Home Assistant.",
|
||||
"create_blocked_not_running": "Създаването на снапшот в момента не е възможно, тъй като системата е в състояние {state}.",
|
||||
"create_snapshot": "Създаване на снапшот",
|
||||
"description": "Снапшотите ви позволяват лесно да архивирате и възстановявате всички данни от вашия екземпляр на Home Assistant.",
|
||||
"enter_password": "Моля, въведете парола.",
|
||||
"folder": {
|
||||
"addons/local": "Локални добавки",
|
||||
@@ -359,16 +360,16 @@
|
||||
"ssl": "SSL"
|
||||
},
|
||||
"folders": "Папки",
|
||||
"full_snapshot": "Пълена моментна снимка",
|
||||
"full_snapshot": "Пълен снапшот",
|
||||
"name": "Име",
|
||||
"no_snapshots": "Все още нямате моментни снимки",
|
||||
"partial_snapshot": "Частичена моментна снимка",
|
||||
"no_snapshots": "Все още нямате снапшоти",
|
||||
"partial_snapshot": "Частичен снапшот",
|
||||
"password": "Парола",
|
||||
"password_protected": "защитен с парола",
|
||||
"password_protection": "Защита с парола",
|
||||
"security": "Сигурност",
|
||||
"type": "Тип",
|
||||
"upload_snapshot": "Качване на моментна снимка"
|
||||
"upload_snapshot": "Качване на снапшот"
|
||||
},
|
||||
"store": {
|
||||
"missing_addons": "Липсват добавки? Активирайте разширения режим в страницата на потребителския си профил",
|
||||
@@ -452,6 +453,7 @@
|
||||
"privileged": "Supervisor не е привилегирован",
|
||||
"systemd": "Systemd"
|
||||
},
|
||||
"unsupported_title": "Използвате неподдържана инсталация",
|
||||
"update_supervisor": "Актуализиране на Supervisor",
|
||||
"warning": "ВНИМАНИЕ"
|
||||
}
|
||||
@@ -652,7 +654,13 @@
|
||||
},
|
||||
"components": {
|
||||
"addon-picker": {
|
||||
"addon": "Добавка"
|
||||
"addon": "Добавка",
|
||||
"error": {
|
||||
"no_supervisor": {
|
||||
"description": "Не е намерен Supervisor, така че добавките не могат да бъдат заредени.",
|
||||
"title": "Няма Supervisor"
|
||||
}
|
||||
}
|
||||
},
|
||||
"area-picker": {
|
||||
"add_dialog": {
|
||||
@@ -1109,9 +1117,11 @@
|
||||
"errors": {
|
||||
"config": {
|
||||
"edit_in_yaml_supported": "Все още можете да редактирате вашата конфигурация в YAML.",
|
||||
"editor_not_supported": "Визуалния редактор не се поддържа за тази конфигурация",
|
||||
"error_detected": "Открити са грешки в конфигурацията",
|
||||
"key_missing": "Липсва задължителният ключ \"{key}\".",
|
||||
"no_template_editor_support": "Шаблоните не се поддържат във визуалния редактор"
|
||||
"no_template_editor_support": "Шаблоните не се поддържат във визуалния редактор",
|
||||
"no_type_provided": "Не е указан тип."
|
||||
},
|
||||
"supervisor": {
|
||||
"ask": "Помолете за помощ",
|
||||
@@ -1509,6 +1519,7 @@
|
||||
"thingtalk": {
|
||||
"create": "Създайте автоматизация",
|
||||
"task_selection": {
|
||||
"error_empty": "Въведете команда или натиснете Пропускане",
|
||||
"for_example": "Например:",
|
||||
"header": "Създайте нова автоматизация",
|
||||
"introduction": "Въведете по-долу какво трябва да направи тази автоматизация и ние ще се опитаме да я преобразуваме в автоматизация на Home Assistant.",
|
||||
@@ -1575,9 +1586,12 @@
|
||||
"config_documentation": "Документация за конфигурацията",
|
||||
"devices_pin": "ПИН код за Устройства за защита",
|
||||
"enter_pin_hint": "Въведете ПИН за използване на устройства за защита",
|
||||
"manage_entities": "Управление на обекти",
|
||||
"not_configured_text": "Преди да можете да използвате Google Assistant, трябва да активирате умението Home Assistant Cloud за Google Assistant в приложението Google Home.",
|
||||
"not_configured_title": "Google Assistant не е активиран",
|
||||
"security_devices": "Устройства за защита",
|
||||
"sync_entities": "Синхронизиране на обекти с Google",
|
||||
"sync_entities_404_message": "Неуспешно синхронизиране на вашите обекти с Google, помолете Google „Hey Google, sync my devices“, за да синхронизира вашите обекти.",
|
||||
"title": "Google Assistant"
|
||||
},
|
||||
"integrations": "Интеграции",
|
||||
@@ -1624,6 +1638,7 @@
|
||||
"expose": "Откриване към Alexa",
|
||||
"expose_entity": "Изложи обекта",
|
||||
"exposed": "{selected} изложен",
|
||||
"exposed_entities": "Изложени обекти",
|
||||
"follow_domain": "Следван домейн",
|
||||
"manage_domains": "Управление на домейни",
|
||||
"not_exposed": "{selected} неизложен",
|
||||
@@ -1637,7 +1652,7 @@
|
||||
"certificate_expiration_date": "Дата на изтичане на сертификата:",
|
||||
"certificate_information": "Информация за сертификата",
|
||||
"close": "Затвори",
|
||||
"will_be_auto_renewed": "ще бъде автоматично подновено"
|
||||
"will_be_auto_renewed": "ще бъде автоматично подновен"
|
||||
},
|
||||
"dialog_cloudhook": {
|
||||
"close": "Затвори",
|
||||
@@ -1659,9 +1674,12 @@
|
||||
"expose": "Откриване към Google Assistant",
|
||||
"expose_entity": "Изложи обекта",
|
||||
"exposed": "{selected} изложен",
|
||||
"exposed_entities": "Изложени обекти",
|
||||
"follow_domain": "следван домейн",
|
||||
"manage_domains": "Управление на домейни",
|
||||
"not_exposed": "{selected} неизложен",
|
||||
"not_exposed_entities": "Не изложени обекти",
|
||||
"sync_to_google": "Синхронизиране на промените с Google.",
|
||||
"title": "Google Assistant"
|
||||
},
|
||||
"login": {
|
||||
@@ -1835,7 +1853,8 @@
|
||||
"add_entities_lovelace": "Добавете към Lovelace",
|
||||
"disabled_entities": "+{count} {count, plural,\n one {деактивиран обект}\n other {деактивирани обекта}\n}",
|
||||
"entities": "Обекти",
|
||||
"hide_disabled": "Скриване на деактивираните"
|
||||
"hide_disabled": "Скриване на деактивираните",
|
||||
"none": "Това устройство няма обекти"
|
||||
},
|
||||
"name": "Име",
|
||||
"no_devices": "Няма устройства",
|
||||
@@ -1871,10 +1890,13 @@
|
||||
"description": "Управление на известните обекти",
|
||||
"picker": {
|
||||
"disable_selected": {
|
||||
"button": "Деактивиране на избраните",
|
||||
"confirm_text": "Деактивираните обекти няма да бъдат добавени в Home Assistant.",
|
||||
"confirm_title": "Искате ли да забраните {number} {number, plural,\n one {обект}\n other {обекта}\n}?"
|
||||
},
|
||||
"enable_selected": {
|
||||
"button": "Активиране на избраните",
|
||||
"confirm_text": "Това ще ги направи отново достъпни в Home Assistant, ако сега са деактивирани.",
|
||||
"confirm_title": "Искате ли да разрешите {number} {number, plural,\n one {обект}\n other {обекта}\n}?"
|
||||
},
|
||||
"filter": {
|
||||
@@ -1896,14 +1918,17 @@
|
||||
"introduction": "Home Assistant поддържа регистър на всички обекти, които някога е виждал, които могат да бъдат идентифицирани уникално. Всеки от тези обекти ще има идентификатор на обект, който ще бъде резервиран само за този обект.",
|
||||
"introduction2": "Използвайте регистъра на обектите, за да промените името, идентификатора на обекта или да премахнете записа от Home Assistant. Моля имайте на предвид, че премахването на записа от регистъра на обектите няма да премахне обекта. За да направите това, следвайте препратката по-долу и я премахнете от страницата за интеграции.",
|
||||
"remove_selected": {
|
||||
"button": "Премахване на избраните",
|
||||
"confirm_partly_title": "Само {number} {number, plural,\n one {избран обект}\n other {избрани обекта}\n} могат да бъдат премахнати.",
|
||||
"confirm_title": "Искате ли да премахнете {number} {number, plural,\n one {обект}\n other {обекта}\n}?"
|
||||
},
|
||||
"search": "Търсене на обекти",
|
||||
"selected": "{number} избрани",
|
||||
"status": {
|
||||
"disabled": "Деактивиран",
|
||||
"ok": "ДА",
|
||||
"readonly": "Само за четене",
|
||||
"restored": "Възстановен",
|
||||
"unavailable": "Недостъпен"
|
||||
}
|
||||
}
|
||||
@@ -1943,6 +1968,8 @@
|
||||
"copy_github": "За GitHub",
|
||||
"description": "Версия, състояние на системата и връзки към документация",
|
||||
"documentation": "Документация",
|
||||
"frontend": "Интерфейс",
|
||||
"frontend_version": "Версия на интерфейса: {version} - {type}",
|
||||
"home_assistant_logo": "Лого на Home Assistant",
|
||||
"icons_by": "Икони от",
|
||||
"integrations": "Интеграции",
|
||||
@@ -1993,6 +2020,11 @@
|
||||
"rename": "Преименуване",
|
||||
"restart_confirm": "Рестартирайте Home Assistant за да завършите премахването на интеграцията",
|
||||
"services": "{count} {count, plural,\n one {услуга}\n other {услуги}\n}",
|
||||
"state": {
|
||||
"loaded": "Зареден",
|
||||
"migration_error": "Грешка при миграцията",
|
||||
"not_loaded": "Не е зареден"
|
||||
},
|
||||
"system_options": "Системни настройки",
|
||||
"unnamed_entry": "Запис без име"
|
||||
},
|
||||
@@ -2041,6 +2073,8 @@
|
||||
"logs": {
|
||||
"caption": "Журнали",
|
||||
"clear": "Изчистване",
|
||||
"custom_integration": "персонализирана интеграция",
|
||||
"error_from_custom_integration": "Тази грешка произтича от персонализирана интеграция.",
|
||||
"level": {
|
||||
"critical": "КРИТИЧНО",
|
||||
"debug": "ДЕБЪГ",
|
||||
@@ -2051,6 +2085,7 @@
|
||||
"load_full_log": "Зареждане на пълния журнал на Home Assistant",
|
||||
"loading_log": "Зарежда се журнала за грешки...",
|
||||
"multiple_messages": "съобщението е възникнало за първи път в {time} и се показва {counter} пъти",
|
||||
"no_issues": "Няма нови проблеми!",
|
||||
"refresh": "Опресняване"
|
||||
},
|
||||
"lovelace": {
|
||||
@@ -2134,7 +2169,8 @@
|
||||
"network": "Мрежа",
|
||||
"node": {
|
||||
"dashboard": "Табло"
|
||||
}
|
||||
},
|
||||
"nodes": "Възли"
|
||||
},
|
||||
"network_status": {
|
||||
"details": {
|
||||
@@ -2147,6 +2183,9 @@
|
||||
"starting": "Стартиране",
|
||||
"unknown": "Неизвестно"
|
||||
},
|
||||
"network": {
|
||||
"node_count": "{count} възли"
|
||||
},
|
||||
"node_metadata": {
|
||||
"product_manual": "Ръководство за продукта"
|
||||
},
|
||||
@@ -2155,7 +2194,7 @@
|
||||
},
|
||||
"node": {
|
||||
"button": "Детайли за възела",
|
||||
"not_found": "Възел не е намерен"
|
||||
"not_found": "Не е намерен възел"
|
||||
},
|
||||
"nodes_table": {
|
||||
"failed": "Неуспешно",
|
||||
@@ -2167,13 +2206,17 @@
|
||||
},
|
||||
"refresh_node": {
|
||||
"button": "Обнови възела",
|
||||
"node_status": "Състояние на възела",
|
||||
"refreshing_description": "Опресняване на информацията за възела...",
|
||||
"step": "Стъпка"
|
||||
},
|
||||
"select_instance": {
|
||||
"none_found": "Не можахме да намерим OpenZWave инстанция. Ако смятате че не е вярно, проверете Вашите OpenZWare и MQTT и се уверете. че Home Assistant може да комуникира със MQTT брокера."
|
||||
},
|
||||
"services": {
|
||||
"cancel_command": "Отмени командата"
|
||||
"add_node": "Добавяне на възел",
|
||||
"cancel_command": "Отмени командата",
|
||||
"remove_node": "Премахване на възел"
|
||||
}
|
||||
},
|
||||
"person": {
|
||||
@@ -2404,11 +2447,15 @@
|
||||
"add_device": "Добавяне на устройство",
|
||||
"add_device_page": {
|
||||
"discovered_text": "Устройствата ще се показват тук, след като бъдат открити.",
|
||||
"no_devices_found": "Не бяха намерени устройства, уверете се, че са в режим на сдвояване и ги дръжте будни, докато откриването работи.",
|
||||
"no_devices_found": "Не бяха намерени устройства, уверете се, че те са в режим на сдвояване и ги дръжте будни, докато откриването работи.",
|
||||
"pairing_mode": "Уверете се, че вашите устройства са в режим на сдвояване. Проверете инструкциите на вашето устройство за да разберете как да направите това.",
|
||||
"search_again": "Потърси отново",
|
||||
"spinner": "Търсене на ZHA Zigbee устройства..."
|
||||
},
|
||||
"button": "Конфигуриране",
|
||||
"cluster_attributes": {
|
||||
"attributes_of_cluster": "Атрибути на избрания клъстер"
|
||||
},
|
||||
"clusters": {
|
||||
"header": "Клъстери",
|
||||
"introduction": "Клъстерите са градивните елементи за функционалността на Zigbee. Те разделят функционалността на логически единици. Има типове клиенти и сървъри, състоящи се от атрибути и команди."
|
||||
@@ -2433,6 +2480,7 @@
|
||||
"create_group": "Zigbee Home Automation - Създаване на група",
|
||||
"create_group_details": "Въведете необходимите данни, за да създадете нова zigbee група",
|
||||
"creating_group": "Създаване на група",
|
||||
"group_details": "Тук са всички подробности за избраната Zigbee група.",
|
||||
"group_info": "Информация за групата",
|
||||
"group_name_placeholder": "Име на групата",
|
||||
"group_not_found": "Не е намерена група!",
|
||||
@@ -2476,34 +2524,50 @@
|
||||
"no_zones_created_yet": "Изглежда, че все още не сте създали никакви зони."
|
||||
},
|
||||
"zwave_js": {
|
||||
"add_node": {
|
||||
"title": "Добавяне на Z-Wave възел"
|
||||
},
|
||||
"button": "Конфигуриране",
|
||||
"common": {
|
||||
"add_node": "Добавяне на възел",
|
||||
"close": "Затвори",
|
||||
"network": "Мрежа"
|
||||
"network": "Мрежа",
|
||||
"node_id": "ID на възел",
|
||||
"remove_node": "Премахване на възел"
|
||||
},
|
||||
"dashboard": {
|
||||
"driver_version": "Версия на драйвера",
|
||||
"dump_dead_nodes_title": "Някои от вашите възли са мъртви",
|
||||
"dump_not_ready_confirm": "Изтегляне",
|
||||
"introduction": "Управлявайте вашата Z-Wave мрежа и Z-Wave възли",
|
||||
"server_version": "Версия на сървъра"
|
||||
},
|
||||
"device_info": {
|
||||
"device_config": "Конфигуриране на устройство"
|
||||
"device_config": "Конфигуриране на устройство",
|
||||
"node_status": "Състояние на възела"
|
||||
},
|
||||
"navigation": {
|
||||
"network": "Мрежа"
|
||||
},
|
||||
"network_status": {
|
||||
"connected": "Свързан",
|
||||
"connecting": "Свързване"
|
||||
"connecting": "Свързване",
|
||||
"unknown": "Неизвестен"
|
||||
},
|
||||
"node_config": {
|
||||
"attribution": "Параметрите и описанията на конфигурацията на устройството се предоставят от {device_database}",
|
||||
"battery_device_notice": "Устройствата на батерии трябва да са будни, за да актуализират конфигурацията си. Моля, вижте ръководството на устройството за инструкции как да го събудите.",
|
||||
"error_device_not_found": "Не е намерено устройство",
|
||||
"header": "Z-Wave конфигурация на устройството",
|
||||
"introduction": "Управление и настройване на специфични конфигурационни параметри на подбраноto устройството (възел, node)",
|
||||
"introduction": "Управление и настройване на специфични конфигурационни параметри на подбраното устройството (възел, node)",
|
||||
"parameter_is_read_only": "Този параметър е само за четене.",
|
||||
"zwave_js_device_database": "Z-Wave JS база данни с устройства"
|
||||
},
|
||||
"node_status": {
|
||||
"unknown": "Неизвестен"
|
||||
},
|
||||
"remove_node": {
|
||||
"title": "Премахване на Z-Wave възел"
|
||||
}
|
||||
},
|
||||
"zwave": {
|
||||
@@ -2548,6 +2612,10 @@
|
||||
"node_management": {
|
||||
"add_to_group": "Добавяне към група",
|
||||
"group": "Група",
|
||||
"node_protection": "Защита на възела",
|
||||
"nodes": "Възли",
|
||||
"nodes_in_group": "Други възли в тази група:",
|
||||
"protection": "Защита",
|
||||
"remove_from_group": "Премахване от групата"
|
||||
},
|
||||
"services": {
|
||||
@@ -2555,6 +2623,7 @@
|
||||
"add_node_secure": "Добавяне на криптирано устройство",
|
||||
"cancel_command": "Отмени командата",
|
||||
"heal_network": "Оздравяване на мрежата",
|
||||
"node_info": "Информация за възела",
|
||||
"remove_node": "Премахване на устройство",
|
||||
"save_config": "Запазване на конфигурацията",
|
||||
"soft_reset": "Soft нулиране",
|
||||
@@ -2581,6 +2650,8 @@
|
||||
"column_parameter": "Параметър",
|
||||
"no_template_ui_support": "Потребителският интерфейс не поддържа шаблони, все още можете да използвате YAML редактора.",
|
||||
"title": "Услуги",
|
||||
"ui_mode": "Отидете в режим Потребителски интерфейс",
|
||||
"yaml_mode": "Отидете в YAML режим",
|
||||
"yaml_parameters": "Параметрите са налични само в YAML режим"
|
||||
},
|
||||
"states": {
|
||||
@@ -2588,11 +2659,14 @@
|
||||
"current_entities": "Настоящи обекти",
|
||||
"description1": "Задайте представянето на устройство или обект в Home Assistant.",
|
||||
"description2": "Промяната е фиктивна, до следваща актуализация на устройството или обекта.",
|
||||
"entity": "Обект",
|
||||
"filter_attributes": "Филтриране на атрибути",
|
||||
"last_changed": "Последна промяна",
|
||||
"last_updated": "Последна актуализация",
|
||||
"more_info": "Повече информация",
|
||||
"no_entities": "Няма обекти",
|
||||
"set_state": "Задаване на състояние",
|
||||
"state": "Състояние",
|
||||
"state_attributes": "Атрибути на състоянието (YAML, по избор)",
|
||||
"title": "Състояния"
|
||||
},
|
||||
@@ -2663,6 +2737,9 @@
|
||||
"toggle": "Превключване на {name}",
|
||||
"url": "Отваряне на прозорец към {url_path}"
|
||||
},
|
||||
"safe-mode": {
|
||||
"header": "Активиран е безопасен режим"
|
||||
},
|
||||
"shopping-list": {
|
||||
"add_item": "Добави артикул",
|
||||
"checked_items": "Отметнати артикули",
|
||||
@@ -2791,6 +2868,7 @@
|
||||
"show_icon": "Показване на иконата?",
|
||||
"show_name": "Показване на името?",
|
||||
"show_state": "Показване на състоянието?",
|
||||
"state": "Състояние",
|
||||
"state_color": "Да се оцветят ли иконите спрямо състоянието?",
|
||||
"tap_action": "Действие Докосване",
|
||||
"theme": "Тема",
|
||||
@@ -3004,6 +3082,8 @@
|
||||
},
|
||||
"unused_entities": {
|
||||
"available_entities": "Това са обектите, които имате на разположение, но все още не са във вашия потребителски интерфейс на Lovelace.",
|
||||
"domain": "Домейн",
|
||||
"entity": "Обект",
|
||||
"entity_id": "ID на обекта",
|
||||
"last_changed": "Последна промяна",
|
||||
"no_data": "Не са намерени неизползвани обекти",
|
||||
@@ -3198,7 +3278,7 @@
|
||||
"intro": "Готови ли сте да събудите дома си, да отвоювате независимостта си и да се присъедините към световна общност от хора автоматизиращи домовете си?",
|
||||
"next": "Следващ",
|
||||
"restore": {
|
||||
"description": "Като алтернатива можете да възстановите от предишна моментна снимка.",
|
||||
"description": "Като алтернатива можете да възстановите от предишен снапшот.",
|
||||
"hide_log": "Скриване на пълния дневник",
|
||||
"in_progress": "Възстановяването е в ход",
|
||||
"show_log": "Показване на пълния дневник"
|
||||
@@ -3297,10 +3377,10 @@
|
||||
"formats": {
|
||||
"comma_decimal": "1,234,567.89",
|
||||
"decimal_comma": "1.234.567,89",
|
||||
"language": "Авто (изп. езикова настройка)",
|
||||
"language": "Авто (езикова настройка)",
|
||||
"none": "Без",
|
||||
"space_comma": "1 234 567,89",
|
||||
"system": "Изп. системната настройка"
|
||||
"system": "Системна настройка"
|
||||
},
|
||||
"header": "Формат на числата"
|
||||
},
|
||||
|
||||
@@ -707,7 +707,7 @@
|
||||
"text": "Introdueix el nom de la nova àrea.",
|
||||
"title": "Afegeix àrea nova"
|
||||
},
|
||||
"add_new": "Afegir àrea nova...",
|
||||
"add_new": "Afegeix nova àrea...",
|
||||
"area": "Àrea",
|
||||
"clear": "Esborra",
|
||||
"no_areas": "No tens cap àrea",
|
||||
@@ -886,6 +886,7 @@
|
||||
}
|
||||
},
|
||||
"service-control": {
|
||||
"integration_doc": "Documentació de la integració",
|
||||
"required": "Aquest camp és obligatori",
|
||||
"service_data": "Dades del servei",
|
||||
"target": "Objectius",
|
||||
@@ -1695,7 +1696,7 @@
|
||||
},
|
||||
"connected": "Connectat",
|
||||
"connection_status": "Estat de connexió amb Cloud",
|
||||
"fetching_subscription": "Obtenint la subscripció...",
|
||||
"fetching_subscription": "Obtenint subscripció...",
|
||||
"google": {
|
||||
"config_documentation": "Documentació de configuració",
|
||||
"devices_pin": "PIN dels dispositius de seguretat",
|
||||
@@ -1751,7 +1752,7 @@
|
||||
"disable_hook_error_msg": "No s'ha pogut desactivar el webhook:",
|
||||
"info": "Qualsevol cosa que estigui configurada per disparar-se a través d'un webhook pot disposar d'un URL accessible públicament que permet retornar-li dades a Home Assistant des de qualsevol lloc i sense exposar la teva instància a Internet.",
|
||||
"link_learn_more": "Més informació sobre la creació d'automatizacions basats en webhook.",
|
||||
"loading": "Carregant ...",
|
||||
"loading": "Carregant...",
|
||||
"manage": "Gestiona",
|
||||
"no_hooks_yet": "Sembla que encara no tens cap webhook. Comença configurant una ",
|
||||
"no_hooks_yet_link_automation": "automatització de webhook",
|
||||
@@ -2156,8 +2157,11 @@
|
||||
"caption": "Integracions",
|
||||
"config_entry": {
|
||||
"area": "A {area}",
|
||||
"check_the_logs": "Comprova els registres",
|
||||
"configure": "Configura",
|
||||
"delete": "Elimina",
|
||||
"delete_confirm": "Estàs segur que vols eliminar aquesta integració?",
|
||||
"depends_on_cloud": "Depèn del núvol",
|
||||
"device_unavailable": "Dispositiu no disponible",
|
||||
"devices": "{count} {count, plural,\n one {dispositiu}\n other {dispositius}\n}",
|
||||
"disable_restart_confirm": "Reinicia Home Assistant per acabar de desactivar aquesta integració",
|
||||
@@ -2180,14 +2184,23 @@
|
||||
"logs": "registres",
|
||||
"manuf": "de {manufacturer}",
|
||||
"no_area": "Sense àrea",
|
||||
"not_loaded": "No carregat, comprova {logs_link}",
|
||||
"not_loaded": "No carregada",
|
||||
"options": "Opcions",
|
||||
"provided_by_custom_integration": "Proporcionada per una integració personalitzada",
|
||||
"reload": "Torna a carregar",
|
||||
"reload_confirm": "La integració s'ha tornat a carregar",
|
||||
"reload_restart_confirm": "Reinicia Home Assistant per acabar de carregar aquesta integració",
|
||||
"rename": "Canvia el nom",
|
||||
"restart_confirm": "Reinicia Home Assistant per acabar d'eliminar aquesta integració",
|
||||
"services": "{count} {count, plural,\n one {servei}\n other {serveis}\n}",
|
||||
"state": {
|
||||
"failed_unload": "No s'ha pogut desactivar",
|
||||
"loaded": "Carregada",
|
||||
"migration_error": "Error de migració",
|
||||
"not_loaded": "No carregada",
|
||||
"setup_error": "No s'ha pogut configurar",
|
||||
"setup_retry": "S'està tornant a provar de configurar"
|
||||
},
|
||||
"system_options": "Opcions de sistema",
|
||||
"unnamed_entry": "Entrada sense nom"
|
||||
},
|
||||
@@ -2267,7 +2280,7 @@
|
||||
"warning": "ALERTA"
|
||||
},
|
||||
"load_full_log": "Carrega el registre complet de Home Assistant",
|
||||
"loading_log": "Carregant el registre d'errors...",
|
||||
"loading_log": "Carregant registre d'errors...",
|
||||
"multiple_messages": "missatge produit per primera vegada a les {time}, apareix {counter} vegades",
|
||||
"no_errors": "No s'ha informat de cap error",
|
||||
"no_issues": "No hi ha registres nous!",
|
||||
@@ -2648,7 +2661,7 @@
|
||||
"confirm_restart": "Segur que vols reiniciar Home Assistant?",
|
||||
"confirm_stop": "Segur que vols aturar Home Assistant?",
|
||||
"heading": "Gestió del servidor",
|
||||
"introduction": "Controla el servidor de Home Assistant... des de Home Assistant.",
|
||||
"introduction": "Controla el teu servidor de Home Assistant... des de Home Assistant.",
|
||||
"restart": "Reinicia",
|
||||
"stop": "Atura"
|
||||
},
|
||||
@@ -2775,6 +2788,15 @@
|
||||
"manufacturer_code_override": "Substitució del codi de fabricant",
|
||||
"value": "Valor"
|
||||
},
|
||||
"configuration_page": {
|
||||
"shortcuts_title": "Dreceres",
|
||||
"update_button": "Actualitza la configuració",
|
||||
"zha_options": {
|
||||
"default_light_transition": "Temps de transició predeterminat (segons)",
|
||||
"enable_identify_on_join": "Activa l'efecte d'identificació quan els dispositius s'uneixin a la xarxa",
|
||||
"title": "Opcions globals"
|
||||
}
|
||||
},
|
||||
"device_pairing_card": {
|
||||
"CONFIGURED": "Configuració completada",
|
||||
"CONFIGURED_status_text": "Inicialitzant",
|
||||
@@ -2868,7 +2890,7 @@
|
||||
"start_secure_inclusion": "Inicia la inclusió segura",
|
||||
"title": "Afegeix un node Z-Wave",
|
||||
"use_secure_inclusion": "Utilitza la inclusió segura",
|
||||
"view_device": "Veure el dispositiu"
|
||||
"view_device": "Mostra el dispositiu"
|
||||
},
|
||||
"button": "Configura",
|
||||
"common": {
|
||||
@@ -2914,6 +2936,9 @@
|
||||
"header": "Configuració de dispositiu Z-Wave",
|
||||
"introduction": "Gestiona i ajusta els paràmetres de configuració específics del dispositiu (node) seleccionat",
|
||||
"parameter_is_read_only": "Aquest paràmetre de només lectura.",
|
||||
"set_param_accepted": "El paràmetre s'ha actualitzat.",
|
||||
"set_param_error": "S'ha produït un error.",
|
||||
"set_param_queued": "El canvi de paràmetre s'ha posat en espera i s'actualitzarà quan es desperti el dispositiu.",
|
||||
"zwave_js_device_database": "Base de dades de dispositiu Z-Wave JS"
|
||||
},
|
||||
"node_status": {
|
||||
@@ -3041,12 +3066,12 @@
|
||||
"count_listeners": " ({count} oient/s)",
|
||||
"data": "Dades de l'esdeveniment (en YAML, opcionals)",
|
||||
"description": "Crida un esdeveniment al bus d'esdeveniments.",
|
||||
"documentation": "Documentació d'esdeveniments.",
|
||||
"documentation": "Documentació d'esdeveniments",
|
||||
"event_fired": "Esdeveniment {name} cridat",
|
||||
"fire_event": "Crida esdeveniment",
|
||||
"listen_to_events": "Escolta esdeveniments",
|
||||
"listening_to": "Escoltant a",
|
||||
"notification_event_fired": "L'esdeveniment {type} s'ha cridat correctament",
|
||||
"notification_event_fired": "Esdeveniment {type} disparat correctament!",
|
||||
"start_listening": "Comença a escoltar",
|
||||
"stop_listening": "Deixa d'escoltar",
|
||||
"subscribe_to": "Esdeveniment al qual subscriure's",
|
||||
@@ -3073,8 +3098,8 @@
|
||||
"attributes": "Atributs",
|
||||
"copy_id": "Copia l'identificador al porta-retalls",
|
||||
"current_entities": "Entitats actuals",
|
||||
"description1": "Defineix la representació d'un dispositiu a Home Assistant.",
|
||||
"description2": "No hi haurà cap comunicació amb el dispositiu real.",
|
||||
"description1": "Defineix la visualització de l'estat actual d'una entitat a Home Assistant.",
|
||||
"description2": "Si l'entitat pertany a un dispositiu, no hi haurà cap comunicació amb el dispositiu.",
|
||||
"entity": "Entitat",
|
||||
"filter_attributes": "Filtra atributs",
|
||||
"filter_entities": "Filtra entitats",
|
||||
|
||||
@@ -1243,7 +1243,8 @@
|
||||
"service_call_failed": "Službu {service} se nepodařilo zavolat.",
|
||||
"started": "Home Assistant je spuštěn!",
|
||||
"starting": "Home Assistant se spouští, ne všechno bude k dispozici, dokud nebude spuštění dokončeno.",
|
||||
"triggered": "Spuštěno {name}"
|
||||
"triggered": "Spuštěno {name}",
|
||||
"wrapping_up_startup": "Home Assistant se spouští, vše ještě nemusí být dostupné"
|
||||
},
|
||||
"panel": {
|
||||
"config": {
|
||||
@@ -2155,8 +2156,11 @@
|
||||
"caption": "Integrace",
|
||||
"config_entry": {
|
||||
"area": "V {area}",
|
||||
"check_the_logs": "Zkontrolujte logy",
|
||||
"configure": "Nastavit",
|
||||
"delete": "Smazat",
|
||||
"delete_confirm": "Opravdu chcete odstranit tuto integraci?",
|
||||
"depends_on_cloud": "Závisí na cloudu",
|
||||
"device_unavailable": "Zařízení není dostupné",
|
||||
"devices": "{count} {count, plural,\n one {zařízení}\n other {zařízení}\n}",
|
||||
"disable_restart_confirm": "Restartujte Home Assistant pro dokončení odstranění této integrace",
|
||||
@@ -2179,14 +2183,23 @@
|
||||
"logs": "logy",
|
||||
"manuf": "od {manufacturer}",
|
||||
"no_area": "Žádná oblast",
|
||||
"not_loaded": "Není načtena, zkontrolujte {logs_link}",
|
||||
"not_loaded": "Nenačteno",
|
||||
"options": "Možnosti",
|
||||
"provided_by_custom_integration": "Poskytováno vlastní integrací",
|
||||
"reload": "Nově načíst",
|
||||
"reload_confirm": "Integrace byla nově načtena",
|
||||
"reload_restart_confirm": "Restartujte Home Assistant pro nové načtení této integrace",
|
||||
"rename": "Přejmenovat",
|
||||
"restart_confirm": "Restartujte Home Assistant pro odstranění této integrace",
|
||||
"services": "{count} {count, plural,\n one {služba}\n few {služby}\n other {služeb}\n}",
|
||||
"state": {
|
||||
"failed_unload": "Zrušení načtení se nezdařilo",
|
||||
"loaded": "Načteno",
|
||||
"migration_error": "Chyba migrace",
|
||||
"not_loaded": "Nenačteno",
|
||||
"setup_error": "Nastavení se nezdařilo",
|
||||
"setup_retry": "Opakovaný pokus o nastavení"
|
||||
},
|
||||
"system_options": "Více možností",
|
||||
"unnamed_entry": "Nepojmenovaný záznam"
|
||||
},
|
||||
@@ -2215,7 +2228,7 @@
|
||||
"configure": "Nastavit",
|
||||
"configured": "Nastaveno",
|
||||
"confirm_new": "Chcete nastavit {integration}?",
|
||||
"description": "Správa integrací a jejich služeb, zařízení, ...",
|
||||
"description": "Správa integrací a jejich služeb či zařízení",
|
||||
"details": "Podrobnosti o integraci",
|
||||
"disable": {
|
||||
"disabled_integrations": "{number} zakázáno",
|
||||
@@ -2254,8 +2267,10 @@
|
||||
"logs": {
|
||||
"caption": "Logy",
|
||||
"clear": "Zrušit",
|
||||
"custom_integration": "vlastní integrace",
|
||||
"description": "Zobrazení logů Home Assistant",
|
||||
"details": "Detaily protokolu ({level})",
|
||||
"error_from_custom_integration": "Tato chyba pochází z vlastní integrace.",
|
||||
"level": {
|
||||
"critical": "KRITICKÉ",
|
||||
"debug": "LADĚNÍ",
|
||||
|
||||
@@ -2216,7 +2216,7 @@
|
||||
"configure": "Konfigurieren",
|
||||
"configured": "Konfiguriert",
|
||||
"confirm_new": "Möchtest du {integration} einrichten?",
|
||||
"description": "Integrationen zu Diensten, Geräten, usw. verwalten",
|
||||
"description": "Verwalte verbundene Geräte und Dienste",
|
||||
"details": "Details zur Integration",
|
||||
"disable": {
|
||||
"disabled_integrations": "{number} deaktiviert",
|
||||
@@ -2255,8 +2255,10 @@
|
||||
"logs": {
|
||||
"caption": "Logs",
|
||||
"clear": "Löschen",
|
||||
"custom_integration": "benutzerdefiniert integration",
|
||||
"description": "Home Assistant Logs einsehen",
|
||||
"details": "Protokolldetails ({level})",
|
||||
"error_from_custom_integration": "benutzerdefinierte",
|
||||
"level": {
|
||||
"critical": "KRITISCH",
|
||||
"debug": "DEBUG",
|
||||
|
||||
@@ -707,7 +707,7 @@
|
||||
"text": "Enter the name of the new area.",
|
||||
"title": "Add new area"
|
||||
},
|
||||
"add_new": "Add new area…",
|
||||
"add_new": "Add new area...",
|
||||
"area": "Area",
|
||||
"clear": "Clear",
|
||||
"no_areas": "You don't have any areas",
|
||||
@@ -886,6 +886,7 @@
|
||||
}
|
||||
},
|
||||
"service-control": {
|
||||
"integration_doc": "Integration documentation",
|
||||
"required": "This field is required",
|
||||
"service_data": "Service data",
|
||||
"target": "Targets",
|
||||
@@ -1237,7 +1238,7 @@
|
||||
"title": "Notifications"
|
||||
},
|
||||
"notification_toast": {
|
||||
"connection_lost": "Connection lost. Reconnecting…",
|
||||
"connection_lost": "Connection lost. Reconnecting...",
|
||||
"dismiss": "Dismiss",
|
||||
"intergration_starting": "Starting {integration}, not everything will be available until it is finished.",
|
||||
"service_call_failed": "Failed to call service {service}.",
|
||||
@@ -1695,7 +1696,7 @@
|
||||
},
|
||||
"connected": "Connected",
|
||||
"connection_status": "Cloud connection status",
|
||||
"fetching_subscription": "Fetching subscription…",
|
||||
"fetching_subscription": "Fetching subscription...",
|
||||
"google": {
|
||||
"config_documentation": "Configuration documentation",
|
||||
"devices_pin": "Security Devices PIN",
|
||||
@@ -1751,7 +1752,7 @@
|
||||
"disable_hook_error_msg": "Failed to disable webhook:",
|
||||
"info": "Anything that is configured to be triggered by a webhook can be given a publicly accessible URL to allow you to send data back to Home Assistant from anywhere, without exposing your instance to the internet.",
|
||||
"link_learn_more": "Learn more about creating webhook-powered automations.",
|
||||
"loading": "Loading ...",
|
||||
"loading": "Loading...",
|
||||
"manage": "Manage",
|
||||
"no_hooks_yet": "Looks like you have no webhooks yet. Get started by configuring a ",
|
||||
"no_hooks_yet_link_automation": "webhook automation",
|
||||
@@ -2156,8 +2157,11 @@
|
||||
"caption": "Integrations",
|
||||
"config_entry": {
|
||||
"area": "In {area}",
|
||||
"check_the_logs": "Check the logs",
|
||||
"configure": "Configure",
|
||||
"delete": "Delete",
|
||||
"delete_confirm": "Are you sure you want to delete this integration?",
|
||||
"depends_on_cloud": "Depends on the cloud",
|
||||
"device_unavailable": "Device unavailable",
|
||||
"devices": "{count} {count, plural,\n one {device}\n other {devices}\n}",
|
||||
"disable_restart_confirm": "Restart Home Assistant to finish disabling this integration",
|
||||
@@ -2180,14 +2184,23 @@
|
||||
"logs": "logs",
|
||||
"manuf": "by {manufacturer}",
|
||||
"no_area": "No Area",
|
||||
"not_loaded": "Not loaded, check the {logs_link}",
|
||||
"not_loaded": "Not loaded",
|
||||
"options": "Options",
|
||||
"provided_by_custom_integration": "Provided by a custom integration",
|
||||
"reload": "Reload",
|
||||
"reload_confirm": "The integration was reloaded",
|
||||
"reload_restart_confirm": "Restart Home Assistant to finish reloading this integration",
|
||||
"rename": "Rename",
|
||||
"restart_confirm": "Restart Home Assistant to finish removing this integration",
|
||||
"services": "{count} {count, plural,\n one {service}\n other {services}\n}",
|
||||
"state": {
|
||||
"failed_unload": "Failed to unload",
|
||||
"loaded": "Loaded",
|
||||
"migration_error": "Migration error",
|
||||
"not_loaded": "Not loaded",
|
||||
"setup_error": "Failed to set up",
|
||||
"setup_retry": "Retrying setup"
|
||||
},
|
||||
"system_options": "System options",
|
||||
"unnamed_entry": "Unnamed entry"
|
||||
},
|
||||
@@ -2267,7 +2280,7 @@
|
||||
"warning": "WARNING"
|
||||
},
|
||||
"load_full_log": "Load Full Home Assistant Log",
|
||||
"loading_log": "Loading error log…",
|
||||
"loading_log": "Loading error log...",
|
||||
"multiple_messages": "message first occurred at {time} and shows up {counter} times",
|
||||
"no_errors": "No errors have been reported",
|
||||
"no_issues": "There are no new issues!",
|
||||
@@ -2648,7 +2661,7 @@
|
||||
"confirm_restart": "Are you sure you want to restart Home Assistant?",
|
||||
"confirm_stop": "Are you sure you want to stop Home Assistant?",
|
||||
"heading": "Server management",
|
||||
"introduction": "Control your Home Assistant server… from Home Assistant.",
|
||||
"introduction": "Control your Home Assistant server... from Home Assistant.",
|
||||
"restart": "Restart",
|
||||
"stop": "Stop"
|
||||
},
|
||||
@@ -2775,6 +2788,15 @@
|
||||
"manufacturer_code_override": "Manufacturer Code Override",
|
||||
"value": "Value"
|
||||
},
|
||||
"configuration_page": {
|
||||
"shortcuts_title": "Shortcuts",
|
||||
"update_button": "Update Configuration",
|
||||
"zha_options": {
|
||||
"default_light_transition": "Default light transition time (seconds)",
|
||||
"enable_identify_on_join": "Enable identify effect when devices join the network",
|
||||
"title": "Global Options"
|
||||
}
|
||||
},
|
||||
"device_pairing_card": {
|
||||
"CONFIGURED": "Configuration Complete",
|
||||
"CONFIGURED_status_text": "Initializing",
|
||||
@@ -2914,6 +2936,9 @@
|
||||
"header": "Z-Wave Device Configuration",
|
||||
"introduction": "Manage and adjust device (node) specific configuration parameters for the selected device",
|
||||
"parameter_is_read_only": "This parameter is read-only.",
|
||||
"set_param_accepted": "The parameter has been updated.",
|
||||
"set_param_error": "An error occurred.",
|
||||
"set_param_queued": "The parameter change has been queued, and will be updated when the device wakes up.",
|
||||
"zwave_js_device_database": "Z-Wave JS Device Database"
|
||||
},
|
||||
"node_status": {
|
||||
@@ -3041,12 +3066,12 @@
|
||||
"count_listeners": " ({count} listeners)",
|
||||
"data": "Event Data (YAML, optional)",
|
||||
"description": "Fire an event on the event bus.",
|
||||
"documentation": "Events Documentation.",
|
||||
"documentation": "Events documentation",
|
||||
"event_fired": "Event {name} fired",
|
||||
"fire_event": "Fire Event",
|
||||
"listen_to_events": "Listen to events",
|
||||
"listening_to": "Listening to",
|
||||
"notification_event_fired": "Event {type} successful fired!",
|
||||
"notification_event_fired": "Event {type} successfully fired!",
|
||||
"start_listening": "Start listening",
|
||||
"stop_listening": "Stop listening",
|
||||
"subscribe_to": "Event to subscribe to",
|
||||
@@ -3073,8 +3098,8 @@
|
||||
"attributes": "Attributes",
|
||||
"copy_id": "Copy ID to clipboard",
|
||||
"current_entities": "Current entities",
|
||||
"description1": "Set the representation of a device within Home Assistant.",
|
||||
"description2": "This will not communicate with the actual device.",
|
||||
"description1": "Set the current state representation of an entity within Home Assistant.",
|
||||
"description2": "If the entity belongs to a device, there will be no actual communication with that device.",
|
||||
"entity": "Entity",
|
||||
"filter_attributes": "Filter attributes",
|
||||
"filter_entities": "Filter entities",
|
||||
@@ -3446,7 +3471,7 @@
|
||||
"duplicate": "Duplicate card",
|
||||
"edit": "Edit",
|
||||
"header": "Card Configuration",
|
||||
"move": "Move to View",
|
||||
"move": "Move to view",
|
||||
"move_after": "Move card after",
|
||||
"move_before": "Move card before",
|
||||
"options": "More options",
|
||||
|
||||
@@ -2156,8 +2156,11 @@
|
||||
"caption": "Integraciones",
|
||||
"config_entry": {
|
||||
"area": "En {area}",
|
||||
"check_the_logs": "Compruebe los registros",
|
||||
"configure": "Configurar",
|
||||
"delete": "Eliminar",
|
||||
"delete_confirm": "¿Estás seguro de que quieres eliminar esta integración?",
|
||||
"depends_on_cloud": "Depende de la nube",
|
||||
"device_unavailable": "Dispositivo no disponible",
|
||||
"devices": "{count} {count, plural,\n one {dispositivo}\n other {dispositivos}\n}",
|
||||
"disable_restart_confirm": "Reinicie Home Assistant para terminar de deshabilitar esta integración",
|
||||
@@ -2180,14 +2183,23 @@
|
||||
"logs": "Registros",
|
||||
"manuf": "por {manufacturer}",
|
||||
"no_area": "Ninguna área",
|
||||
"not_loaded": "No se ha cargado, consulte en {logs_link}",
|
||||
"not_loaded": "No cargado",
|
||||
"options": "Opciones",
|
||||
"provided_by_custom_integration": "Proporcionado por una integración personalizada",
|
||||
"reload": "Recargar",
|
||||
"reload_confirm": "La integración se recargó",
|
||||
"reload_restart_confirm": "Reinicie Home Assistant para terminar de recargar esta integración",
|
||||
"rename": "Renombrar",
|
||||
"restart_confirm": "Reinicie Home Assistant para terminar de eliminar esta integración.",
|
||||
"services": "{count} {count, plural,\n one {service}\n other {services}\n}",
|
||||
"state": {
|
||||
"failed_unload": "No se pudo descargar",
|
||||
"loaded": "Cargado",
|
||||
"migration_error": "Error de migración",
|
||||
"not_loaded": "No cargado",
|
||||
"setup_error": "No se pudo configurar",
|
||||
"setup_retry": "Reintentando configurar"
|
||||
},
|
||||
"system_options": "Opciones de Sistema",
|
||||
"unnamed_entry": "Entrada sin nombre"
|
||||
},
|
||||
@@ -2255,8 +2267,10 @@
|
||||
"logs": {
|
||||
"caption": "Registros",
|
||||
"clear": "Limpiar",
|
||||
"custom_integration": "integración personalizada",
|
||||
"description": "Ver los registros de Home Assistant",
|
||||
"details": "Detalles del registro ({level})",
|
||||
"error_from_custom_integration": "Este error se originó en una integración personalizada.",
|
||||
"level": {
|
||||
"critical": "CRÍTICO",
|
||||
"debug": "DEPURAR",
|
||||
@@ -2636,7 +2650,7 @@
|
||||
"script": "Recargar scripts",
|
||||
"smtp": "Recargar servicios de notificación SMTP",
|
||||
"statistics": "Recargar entidades de estadísticas",
|
||||
"telegram": "Recargar servicios de notificación de telegram",
|
||||
"telegram": "Recargar servicios de notificación de Telegram",
|
||||
"template": "Recargar las entidades de plantilla",
|
||||
"trend": "Recargar entidades de tendencia",
|
||||
"universal": "Recargar entidades de reproductor multimedia universal",
|
||||
|
||||
@@ -287,7 +287,7 @@
|
||||
"reset_defaults": "Restablecer los valores predeterminados",
|
||||
"reset_options": "Restablecer opciones",
|
||||
"restart": "Reiniciar",
|
||||
"restart_name": "Reiniciar {name}",
|
||||
"restart_name": "Reiniciar el {name}",
|
||||
"running_version": "Actualmente estás ejecutando la versión {version}",
|
||||
"save": "Guardar",
|
||||
"show_more": "Mostrar más información sobre esto",
|
||||
@@ -303,7 +303,7 @@
|
||||
},
|
||||
"restart": {
|
||||
"text": "¿Estás seguro de que deseas reiniciar {name}?",
|
||||
"title": "Reiniciar {name}"
|
||||
"title": "Reiniciar el {name}"
|
||||
},
|
||||
"update": {
|
||||
"text": "¿Estás seguro de que deseas actualizar {name} a la versión {version}?",
|
||||
@@ -412,8 +412,8 @@
|
||||
},
|
||||
"system": {
|
||||
"core": {
|
||||
"cpu_usage": "Uso de CPU principal",
|
||||
"ram_usage": "Uso de RAM principal"
|
||||
"cpu_usage": "Uso de CPU del core",
|
||||
"ram_usage": "Uso de RAM del core"
|
||||
},
|
||||
"host": {
|
||||
"change": "Cambiar",
|
||||
@@ -731,7 +731,7 @@
|
||||
"search": "Buscar"
|
||||
},
|
||||
"date-range-picker": {
|
||||
"end_date": "Fecha de finalización",
|
||||
"end_date": "Fecha de fin",
|
||||
"select": "Seleccionar",
|
||||
"start_date": "Fecha de inicio"
|
||||
},
|
||||
@@ -1695,7 +1695,7 @@
|
||||
},
|
||||
"connected": "Conectado",
|
||||
"connection_status": "Estado de conexión a la nube",
|
||||
"fetching_subscription": "Obteniendo suscripción ...",
|
||||
"fetching_subscription": "Obteniendo suscripción...",
|
||||
"google": {
|
||||
"config_documentation": "Documentación de configuración",
|
||||
"devices_pin": "PIN de dispositivos de seguridad",
|
||||
@@ -1751,7 +1751,7 @@
|
||||
"disable_hook_error_msg": "No se pudo deshabilitar el webhook:",
|
||||
"info": "Cualquier cosa que esté configurada para ser activada por un webhook puede recibir una URL de acceso público para permitirte enviar datos a Home Assistant desde cualquier lugar, sin exponer tu instancia a Internet.",
|
||||
"link_learn_more": "Saber más sobre la creación de automatizaciones basadas en webhook.",
|
||||
"loading": "Cargando ...",
|
||||
"loading": "Cargando...",
|
||||
"manage": "Administrar",
|
||||
"no_hooks_yet": "Parece que aún no tienes webhooks. Comienza configurando un",
|
||||
"no_hooks_yet_link_automation": "automatización de webhook",
|
||||
@@ -2156,8 +2156,11 @@
|
||||
"caption": "Integraciones",
|
||||
"config_entry": {
|
||||
"area": "En {area}",
|
||||
"check_the_logs": "Revisa los registros",
|
||||
"configure": "Configurar",
|
||||
"delete": "Eliminar",
|
||||
"delete_confirm": "¿Estás seguro de que quieres eliminar esta integración?",
|
||||
"depends_on_cloud": "Depende de la nube",
|
||||
"device_unavailable": "Dispositivo no disponible",
|
||||
"devices": "{count} {count, plural,\n one {dispositivo}\n other {dispositivos}\n}",
|
||||
"disable_restart_confirm": "Reinicia Home Assistant para terminar de deshabilitar esta integración",
|
||||
@@ -2180,14 +2183,23 @@
|
||||
"logs": "registros",
|
||||
"manuf": "por {manufacturer}",
|
||||
"no_area": "Ningún área",
|
||||
"not_loaded": "No se ha cargado, comprueba el {logs_link}",
|
||||
"not_loaded": "No se ha cargado",
|
||||
"options": "Opciones",
|
||||
"provided_by_custom_integration": "Proporcionada por una integración personalizada",
|
||||
"reload": "Recargar",
|
||||
"reload_confirm": "La integración se ha recargado",
|
||||
"reload_restart_confirm": "Reinicia Home Assistant para terminar de recargar esta integración",
|
||||
"rename": "Renombrar",
|
||||
"restart_confirm": "Reinicia Home Assistant para terminar de eliminar esta integración.",
|
||||
"services": "{count} {count, plural,\n one {servicio}\n other {servicios}\n}",
|
||||
"state": {
|
||||
"failed_unload": "No se pudo descargar",
|
||||
"loaded": "Cargada",
|
||||
"migration_error": "Error de migración",
|
||||
"not_loaded": "No se ha cargado",
|
||||
"setup_error": "No se pudo configurar",
|
||||
"setup_retry": "Reintentando la configuración"
|
||||
},
|
||||
"system_options": "Opciones del sistema",
|
||||
"unnamed_entry": "Entrada sin nombre"
|
||||
},
|
||||
@@ -2618,7 +2630,7 @@
|
||||
"generic": "Entidades de cámara IP genéricas",
|
||||
"generic_thermostat": "Entidades de termostato genéricas",
|
||||
"group": "Grupos, entidades de grupo, y notificar servicios",
|
||||
"heading": "Recargando la configuración YAML",
|
||||
"heading": "Recarga de la configuración YAML",
|
||||
"history_stats": "Entidades de estadísticas del historial",
|
||||
"homekit": "HomeKit",
|
||||
"input_boolean": "Campos booleanos",
|
||||
@@ -2775,6 +2787,15 @@
|
||||
"manufacturer_code_override": "Anulación del código del fabricante",
|
||||
"value": "Valor"
|
||||
},
|
||||
"configuration_page": {
|
||||
"shortcuts_title": "Atajos",
|
||||
"update_button": "Actualizar la configuración",
|
||||
"zha_options": {
|
||||
"default_light_transition": "Tiempo de transición de la luz por defecto (segundos)",
|
||||
"enable_identify_on_join": "Habilita el efecto de identificación cuando los dispositivos se unen a la red",
|
||||
"title": "Opciones globales"
|
||||
}
|
||||
},
|
||||
"device_pairing_card": {
|
||||
"CONFIGURED": "Configuración completada",
|
||||
"CONFIGURED_status_text": "Inicializando",
|
||||
@@ -3041,12 +3062,12 @@
|
||||
"count_listeners": " ({count} oyentes)",
|
||||
"data": "Datos del evento (YAML, opcional)",
|
||||
"description": "Disparar un evento en el bus de eventos.",
|
||||
"documentation": "Documentación de eventos.",
|
||||
"documentation": "Documentación de eventos",
|
||||
"event_fired": "Evento {name} disparado",
|
||||
"fire_event": "Lanzar evento",
|
||||
"listen_to_events": "Escuchar eventos",
|
||||
"listening_to": "Escuchando",
|
||||
"notification_event_fired": "¡Evento {tipe} disparado con éxito!",
|
||||
"notification_event_fired": "¡Evento {type} disparado con éxito!",
|
||||
"start_listening": "Empezar a escuchar",
|
||||
"stop_listening": "Dejar de escuchar",
|
||||
"subscribe_to": "Evento al que suscribirse",
|
||||
@@ -3073,8 +3094,8 @@
|
||||
"attributes": "Atributos",
|
||||
"copy_id": "Copiar ID al portapapeles",
|
||||
"current_entities": "Entidades actuales",
|
||||
"description1": "Establecer la representación de un dispositivo dentro de Home Assistant.",
|
||||
"description2": "Esto no se comunicará con el dispositivo actual.",
|
||||
"description1": "Establece la representación del estado actual de una entidad dentro de Home Assistant.",
|
||||
"description2": "Si la entidad pertenece a un dispositivo, no habrá comunicación real con ese dispositivo.",
|
||||
"entity": "Entidad",
|
||||
"filter_attributes": "Filtrar atributos",
|
||||
"filter_entities": "Filtrar entidades",
|
||||
|
||||
@@ -2156,8 +2156,11 @@
|
||||
"caption": "Sidumised",
|
||||
"config_entry": {
|
||||
"area": "alas {area}",
|
||||
"check_the_logs": "Kontrolli logisid",
|
||||
"configure": "Seadista",
|
||||
"delete": "Kustuta",
|
||||
"delete_confirm": "Oled kindel, et soovid selle sidumise kustutada?",
|
||||
"depends_on_cloud": "Sõltub pilveteenusest",
|
||||
"device_unavailable": "Seade pole saadaval",
|
||||
"devices": "{count} {count, plural,\n one {seade}\n other {seadet}\n}",
|
||||
"disable_restart_confirm": "Taaskäivita Home Assistant, et lõpetada selle sidumise keelamine",
|
||||
@@ -2180,14 +2183,23 @@
|
||||
"logs": "Logid",
|
||||
"manuf": "{manufacturer}",
|
||||
"no_area": "Ala puudub",
|
||||
"not_loaded": "Laadimine nurjus, lisateavet vaata siit {logs_link}",
|
||||
"not_loaded": "Pole laaditud",
|
||||
"options": "Valikud",
|
||||
"provided_by_custom_integration": "Saadaval välise sidumise kaudu",
|
||||
"reload": "Taaslae",
|
||||
"reload_confirm": "Sidumine on taaslaetud",
|
||||
"reload_restart_confirm": "Taaskäivita Home Assistant, et lõpetada selle sidumise taaslaadimine",
|
||||
"rename": "Nimeta ümber",
|
||||
"restart_confirm": "Selle sidumise lõplikuks eemaldamiseks taaskäivita Home Assistant",
|
||||
"services": "{count} {count, plural,\n one {teenus}\n other {teenust}\n}",
|
||||
"state": {
|
||||
"failed_unload": "Eemaldamine nurjus",
|
||||
"loaded": "Laetud",
|
||||
"migration_error": "Tõrge teisaldamisel",
|
||||
"not_loaded": "Pole laaditud",
|
||||
"setup_error": "Häälestamine nurjus",
|
||||
"setup_retry": "Proovin uuesti seadistada"
|
||||
},
|
||||
"system_options": "Süsteemisuvandid",
|
||||
"unnamed_entry": "Nimetu kanne"
|
||||
},
|
||||
@@ -2775,6 +2787,15 @@
|
||||
"manufacturer_code_override": "Tootja koodi alistamine",
|
||||
"value": "Väärtus"
|
||||
},
|
||||
"configuration_page": {
|
||||
"shortcuts_title": "Otseteed",
|
||||
"update_button": "Seadete värskendamine",
|
||||
"zha_options": {
|
||||
"default_light_transition": "Valguse vaikeülemineku aeg (sekundites)",
|
||||
"enable_identify_on_join": "Luba tuvastamise efekt kui seadmed liituvad võrguga",
|
||||
"title": "Üldised valikud"
|
||||
}
|
||||
},
|
||||
"device_pairing_card": {
|
||||
"CONFIGURED": "Seadistamine on lõpetatud",
|
||||
"CONFIGURED_status_text": "Lähtestan",
|
||||
@@ -3073,8 +3094,8 @@
|
||||
"attributes": "Atribuudid",
|
||||
"copy_id": "Kopeeri ID lõikepuhvrisse",
|
||||
"current_entities": "Praegused olemid",
|
||||
"description1": "Seadke seadme esitus Home Assistendis.",
|
||||
"description2": "See ei suhtle tegeliku seadmega.",
|
||||
"description1": "Sea seadme oleku esitus Home Assistendis.",
|
||||
"description2": "Kui olem kuulub seadmele siis selle seadmega tegelikku suhtlust ei toimu.",
|
||||
"entity": "Olem",
|
||||
"filter_attributes": "Filtreeri atribuute",
|
||||
"filter_entities": "Filtreeri olemeid",
|
||||
|
||||
@@ -473,7 +473,7 @@
|
||||
"unhealthy_title": "Votre installation est défectueuse",
|
||||
"unsupported_description": "Vous trouverez ci-dessous une liste des problèmes rencontrés avec votre installation, cliquez sur les liens pour savoir comment vous pouvez résoudre les problèmes.",
|
||||
"unsupported_reason": {
|
||||
"apparmor": "AppArmo n'est pas activé sur l'hôte",
|
||||
"apparmor": "AppArmor n'est pas activé sur l'hôte",
|
||||
"container": "Conteneurs connus pour causer des problèmes",
|
||||
"content-trust": "La validation de la confiance du contenu est désactivée",
|
||||
"dbus": "DBUS",
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
"logbook": "יומן אירועים",
|
||||
"mailbox": "תיבת דואר",
|
||||
"map": "מפה",
|
||||
"media_browser": "סייר המדיה",
|
||||
"profile": "פרופיל",
|
||||
"shopping_list": "רשימת קניות",
|
||||
"states": "ראשי"
|
||||
@@ -104,14 +105,37 @@
|
||||
"supervisor": {
|
||||
"addon": {
|
||||
"dashboard": {
|
||||
"action_error": {
|
||||
"get_changelog": "קבלת יומן שינויים של התוסף נכשלה",
|
||||
"validate_config": "אימות תצורת התוסף נכשל"
|
||||
},
|
||||
"not_available_version": "אתה מפעיל את Home Assistant {core_version_installed} , כדי לעדכן לגרסה זו של התוסף אתה זקוק לפחות לגרסה {core_version_needed} של Home Assistant",
|
||||
"visit_addon_page": "בקר בדף {name} לפרטים נוספים"
|
||||
},
|
||||
"documentation": {
|
||||
"get_documentation": "קבלת תיעוד התוסף נכשלה, {error}"
|
||||
},
|
||||
"logs": {
|
||||
"get_logs": "קבלת יומני התוסף נכשלה, {error}"
|
||||
}
|
||||
},
|
||||
"common": {
|
||||
"cancel": "בטל",
|
||||
"close": "סגור",
|
||||
"save": "שמור"
|
||||
"description": "תיאור",
|
||||
"error": {
|
||||
"unknown": "בעיה לא ידועה"
|
||||
},
|
||||
"failed_to_restart_name": "אתחול {name} נכשל.",
|
||||
"failed_to_update_name": "עדכון {name} נכשל.",
|
||||
"learn_more": "למד עוד",
|
||||
"new_version_available": "גרסה חדשה זמינה",
|
||||
"no": "לא",
|
||||
"refresh": "רענן",
|
||||
"reload": "טען מחדש",
|
||||
"reset_defaults": "אפס לברירות מחדל",
|
||||
"save": "שמור",
|
||||
"yes": "כן"
|
||||
},
|
||||
"confirm": {
|
||||
"update": {
|
||||
@@ -169,32 +193,93 @@
|
||||
"error_addon_not_found": "התוסף לא נמצא"
|
||||
},
|
||||
"snapshot": {
|
||||
"addons": "תוספים",
|
||||
"available_snapshots": "גיבויים זמינים",
|
||||
"could_not_create": "לא ניתן ליצור גיבוי",
|
||||
"create": "צור",
|
||||
"create_blocked_not_running": "יצירת גיבוי אינה אפשרית כרגע מכיוון שהמערכת במצב {state} .",
|
||||
"create_snapshot": "צור גיבוי",
|
||||
"enter_password": "נא הזן סיסמה.",
|
||||
"folder": {
|
||||
"addons/local": "תוספות מקומיות",
|
||||
"homeassistant": "תצורת Home Assistant",
|
||||
"media": "תוכן",
|
||||
"share": "שיתוף",
|
||||
"ssl": "SSL"
|
||||
}
|
||||
},
|
||||
"folders": "תיקיות",
|
||||
"full_snapshot": "גיבוי מלא",
|
||||
"name": "שם",
|
||||
"no_snapshots": "אין לך גיבויים כרגע.",
|
||||
"partial_snapshot": "גיבוי חלקי",
|
||||
"password": "סיסמה",
|
||||
"password_protected": "מוגן באמצעות סיסמה",
|
||||
"password_protection": "הגנה באמצעות סיסמה",
|
||||
"security": "אבטחה",
|
||||
"type": "סוּג",
|
||||
"upload_snapshot": "טען גיבוי"
|
||||
},
|
||||
"system": {
|
||||
"host": {
|
||||
"change_hostname": "שנה שם מכונה",
|
||||
"confirm_reboot": "האם אתה בטוח שאתה מעוניין לאתחל שרת מארח?",
|
||||
"confirm_shutdown": "האם אתה בטוח שאתה מעוניין לכבות שרת מארח?",
|
||||
"docker_version": "גרסת Docker",
|
||||
"failed_to_get_hardware_list": "קבלת רשימת החומרה נכשלה",
|
||||
"failed_to_import_from_usb": "ייבוא מ- USB נכשל",
|
||||
"failed_to_reboot": "אתחול המכונה נכשל",
|
||||
"failed_to_set_hostname": "הגדרת שם המכונה נכשלה",
|
||||
"failed_to_shutdown": "כיבוי המכונה נכשל",
|
||||
"hardware": "חומרה",
|
||||
"hostname": "שם מכונה",
|
||||
"import_from_usb": "יבא מרכיב USB",
|
||||
"ip_address": "כתובת IP",
|
||||
"new_hostname": "אנא הזן שם מכונה חדש:",
|
||||
"operating_system": "מערכת הפעלה",
|
||||
"reboot_host": "אתחל שרת",
|
||||
"shutdown_host": "כבה שרת מארח",
|
||||
"used_space": "שטח בשימוש"
|
||||
},
|
||||
"supervisor": {
|
||||
"beta_backup": "ודא שיש לך גיבויים של הנתונים לפני הפעלת תכונה זו.",
|
||||
"beta_join_confirm": "האם אתה רוצה להצטרף לערוץ הבטא?",
|
||||
"beta_release_items": "זה כולל גרסאות בטא עבור:",
|
||||
"beta_warning": "גרסאות בטא מיועדות לבודקים ומאמצים מוקדמים ויכולות להכיל שינויי קוד לא יציבים",
|
||||
"channel": "עָרוּץ",
|
||||
"join_beta_action": "הצטרף לערוץ בטא",
|
||||
"join_beta_description": "קבל עדכוני בטא עבור Home Assistant, Supervisor ומערכת ההפעלה",
|
||||
"leave_beta_action": "עזוב את ערוץ הבטא",
|
||||
"leave_beta_description": "קבל עדכונים יציבים עבור Home Assistant, Supervisor ומערכת ההפעלה",
|
||||
"reload_supervisor": "טען מחדש את ה Supervisor",
|
||||
"share_diagnostics": "שתף מידע אבחוני",
|
||||
"share_diagnostics_description": "שתף דוחות קריסה ומידע אבחוני.",
|
||||
"share_diagonstics_description": "האם ברצונך לשתף באופן אוטומטי דוחות קריסה ומידע אבחון כאשר ה Supervisor נתקל בשגיאות בלתי צפויות? {line_break} זה יאפשר לנו לתקן את הבעיות, המידע נגיש רק לצוות הליבה של Home Assistant ולא ישותף עם אחרים. {line_break} הנתונים אינם כוללים מידע פרטי/רגיש ובאפשרותך להפוך מידע זה ללא זמין בהגדרות בכל עת שתרצה.",
|
||||
"share_diagonstics_title": "עזור לשפר את Home Assistant",
|
||||
"unhealthy_description": "הפעלת התקנה לא בריאה תגרום לבעיות. להלן רשימת הבעיות שנמצאו בהתקנה שלך, לחץ על הקישורים כדי ללמוד כיצד תוכל לפתור את הבעיות.",
|
||||
"unhealthy_reason": {
|
||||
"docker": "סביבת ה- Docker אינה פועלת כראוי",
|
||||
"privileged": "ל Supervisor אין הרשאות",
|
||||
"setup": "הגדרת ה Supervisor נכשלה",
|
||||
"supervisor": "עדכון ה Supervisor לא הצליח",
|
||||
"untrusted": "זוהה תוכן לא מהימן"
|
||||
},
|
||||
"unhealthy_title": "ההתקנה שלך אינה תקינה",
|
||||
"unsupported_description": "למטה רשימת נושאים שנימצאו במהלך ההתקנה, לחץ על קישור על מנת ללמוד איך לפתור את הנושא",
|
||||
"unsupported_reason": {
|
||||
"apparmor": "AppArmor אינו זמין במחשב המארח",
|
||||
"container": "ה Container ידוע כבעייתי",
|
||||
"content-trust": "אימות אמון תוכן מושבת",
|
||||
"dbus": "DBUS",
|
||||
"docker_configuration": "תצורת Docker"
|
||||
"docker_configuration": "תצורת Docker",
|
||||
"docker_version": "גרסת Docker",
|
||||
"job_conditions": "התעלם מתנאי ה Job",
|
||||
"lxc": "LXC",
|
||||
"network_manager": "מנהל רשת",
|
||||
"os": "מערכת הפעלה",
|
||||
"privileged": "ל Supervisor אין הרשאות",
|
||||
"systemd": "Systemd"
|
||||
},
|
||||
"update_supervisor": "עדכן את ה Supervisor",
|
||||
"warning": "אזהרה"
|
||||
}
|
||||
}
|
||||
@@ -398,6 +483,9 @@
|
||||
"today": "היום"
|
||||
},
|
||||
"data-table": {
|
||||
"clear": "נקה",
|
||||
"filtering_by": "סינון לפי",
|
||||
"hidden": "{number} חבוי",
|
||||
"no-data": "אין נתונים",
|
||||
"search": "חיפוש"
|
||||
},
|
||||
@@ -505,11 +593,11 @@
|
||||
},
|
||||
"never": "אף פעם לא",
|
||||
"past_duration": {
|
||||
"day": "לפני {count} {count, plural,\n one {day}\n other {days}\n}",
|
||||
"day": "לפני {count} {count, plural,\n one {יום}\n other {ימים}\n}",
|
||||
"hour": "לפני {count} {count, plural,\n one {שעה}\n other {שעות}\n}",
|
||||
"minute": "לפני {count} {count, plural,\n one {minute}\n other {minutes}\n}",
|
||||
"second": "לפני {count} {count, plural,\n one {second}\n other {seconds}\n}",
|
||||
"week": "לפני {count} {count, plural,\n one {week}\n other {weeks}\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": {
|
||||
@@ -685,6 +773,11 @@
|
||||
"perform_action": "{action} שרת",
|
||||
"restart": "הפעל מחדש",
|
||||
"stop": "עצור"
|
||||
},
|
||||
"types": {
|
||||
"navigation": "ניווט",
|
||||
"reload": "טען מחדש",
|
||||
"server_control": "שרת"
|
||||
}
|
||||
},
|
||||
"filter_placeholder": "מסנן ישויות"
|
||||
@@ -1137,6 +1230,8 @@
|
||||
"unsupported_blueprint": "שרטוט זה לא נתמך",
|
||||
"url": "כתובת URL של השרטוט"
|
||||
},
|
||||
"caption": "שרטוטים",
|
||||
"description": "ניהול שרטוטים",
|
||||
"overview": {
|
||||
"add_blueprint": "יבוא שרטוט",
|
||||
"confirm_delete_text": "האם אתה בטוח שברצונך למחוק שרטוט זה?",
|
||||
@@ -1596,10 +1691,20 @@
|
||||
"caption": "אינטגרציות",
|
||||
"config_entry": {
|
||||
"area": "ב-{area}",
|
||||
"check_the_logs": "בדוק את היומנים",
|
||||
"configure": "הגדר",
|
||||
"delete": "מחק",
|
||||
"delete_confirm": "האם אתה בטוח שברצונך למחוק אינטגרציה זו?",
|
||||
"depends_on_cloud": "תלוי בענן",
|
||||
"device_unavailable": "מכשיר אינו זמין",
|
||||
"devices": "{count} {count, plural,\n one {device}\n other {devices}\n}",
|
||||
"disable": {
|
||||
"disabled_by": {
|
||||
"device": "מכשיר",
|
||||
"integration": "אינטגרציה",
|
||||
"user": "משתמש"
|
||||
}
|
||||
},
|
||||
"documentation": "תיעוד",
|
||||
"entities": "{count} {count, plural,\n one {entity}\n other {entities}\n}",
|
||||
"entity_unavailable": "ישות לא זמינה",
|
||||
@@ -1610,8 +1715,17 @@
|
||||
"no_area": "ללא אזור",
|
||||
"not_loaded": "לא נטען, בדוק את {logs_link}",
|
||||
"options": "אפשרויות",
|
||||
"provided_by_custom_integration": "מסופק על ידי אינטגרציה מותאמת אישית",
|
||||
"rename": "שנה שם",
|
||||
"restart_confirm": "הפעל מחדש את Home Assistant כדי להשלים את הסרת האינטגרציה",
|
||||
"state": {
|
||||
"failed_unload": "ביטול הטעינה נכשל",
|
||||
"loaded": "טעון",
|
||||
"migration_error": "שגיאת המרה",
|
||||
"not_loaded": "לא טעון",
|
||||
"setup_error": "ההגדרה נכשלה",
|
||||
"setup_retry": "מנסה להגדיר מחדש"
|
||||
},
|
||||
"system_options": "אפשרויות מערכת",
|
||||
"unnamed_entry": "ערך ללא שם"
|
||||
},
|
||||
@@ -1635,6 +1749,9 @@
|
||||
"configured": "הוגדר",
|
||||
"description": "ניהול והגדרת אינטגרציות",
|
||||
"details": "פרטי האינטגרציה",
|
||||
"disable": {
|
||||
"show": "הצג"
|
||||
},
|
||||
"discovered": "זוהו",
|
||||
"home_assistant_website": "אתר Home Assistant",
|
||||
"ignore": {
|
||||
@@ -1665,8 +1782,10 @@
|
||||
"logs": {
|
||||
"caption": "יומנים",
|
||||
"clear": "נקה",
|
||||
"custom_integration": "אינטגרציה מותאמת אישית",
|
||||
"description": "צפה ביומני Home Assistant",
|
||||
"details": "פרטי יומן האירועים ({level})",
|
||||
"error_from_custom_integration": "מקור השגיאה הזו הוא אינטגרציה מותאמת אישית.",
|
||||
"level": {
|
||||
"critical": "CRITICAL",
|
||||
"debug": "DEBUG",
|
||||
@@ -2185,6 +2304,7 @@
|
||||
"node_config": {
|
||||
"attribution": "פרמטרים ותיאורי תצורת המכשיר מסופקים על ידי {device_database}",
|
||||
"battery_device_notice": "התקני סוללה חייבים להיות ערים כדי לעדכן את תצורתם. נא עיין במדריך למשתמש לקבלת הוראות כיצד להעיר את ההתקן.",
|
||||
"error_device_not_found": "ההתקן לא נמצא",
|
||||
"header": "תצורת מכשיר Z-Wave",
|
||||
"introduction": "נהל והתאם פרמטרי תצורה ספציפיים למכשיר (צומת) עבור ההתקן שנבחר",
|
||||
"parameter_is_read_only": "פרמטר זה מוגדר לקריאה בלבד.",
|
||||
@@ -2311,6 +2431,7 @@
|
||||
"column_parameter": "פרמטר",
|
||||
"description": "כלי הפיתוח של השירותים מאפשר לך לקרוא לכל שירות ב Home Assistant.",
|
||||
"fill_example_data": "מלא נתונים לדוגמה",
|
||||
"no_template_ui_support": "ממשק המשתמש אינו תומך בתבניות, עדיין תוכל להשתמש בעורך ה YAML.",
|
||||
"title": "שירותים"
|
||||
},
|
||||
"states": {
|
||||
@@ -2673,7 +2794,7 @@
|
||||
"delete": "מחק תצוגה",
|
||||
"edit": "ערוך תצוגה",
|
||||
"header": "הצג הגדרות",
|
||||
"header_name": "{name} הצג את תצורת",
|
||||
"header_name": "הצג את תצורת {name}",
|
||||
"move_left": "הזז את התצוגה שמאלה",
|
||||
"move_right": "הזז את התצוגה ימינה",
|
||||
"tab_badges": "תגים",
|
||||
@@ -3036,13 +3157,17 @@
|
||||
"header": "מודלי אימות מרובה גורמים"
|
||||
},
|
||||
"number_format": {
|
||||
"description": "בחר כיצד מוצגים מספרים.",
|
||||
"dropdown_label": "פורמט מספר",
|
||||
"formats": {
|
||||
"comma_decimal": "1,234,567.89",
|
||||
"decimal_comma": "1.234.567,89",
|
||||
"language": "אוטומטי (השתמש בהגדרות השפה)",
|
||||
"none": "לא נבחר",
|
||||
"space_comma": "1 234 567,89",
|
||||
"system": "השתמש ב System Locale"
|
||||
}
|
||||
},
|
||||
"header": "פורמט מספר"
|
||||
},
|
||||
"push_notifications": {
|
||||
"add_device_prompt": {
|
||||
|
||||
@@ -14,24 +14,48 @@
|
||||
"fan_mode": {
|
||||
"off": "बंद",
|
||||
"on": "चालू"
|
||||
},
|
||||
"hvac_action": {
|
||||
"cooling": "थन्दा",
|
||||
"drying": "सुखाना",
|
||||
"fan": "पंखा",
|
||||
"heating": "गरमाना",
|
||||
"idle": "खाली",
|
||||
"off": "बंद"
|
||||
},
|
||||
"preset_mode": {
|
||||
"away": "बाहर",
|
||||
"comfort": "पर्याप्त",
|
||||
"eco": "किफ़ायत",
|
||||
"home": "घर",
|
||||
"none": "कुच भि नहि",
|
||||
"sleep": "निद्रा"
|
||||
}
|
||||
},
|
||||
"humidifier": {
|
||||
"mode": {
|
||||
"normal": "सामान्य"
|
||||
}
|
||||
}
|
||||
},
|
||||
"state_badge": {
|
||||
"alarm_control_panel": {
|
||||
"armed_custom_bypass": "सशस्त्र",
|
||||
"pending": "अपूर्ण"
|
||||
},
|
||||
"default": {
|
||||
"entity_not_found": "Entità non trovata",
|
||||
"error": "Errore",
|
||||
"error": "ग़लती",
|
||||
"unavailable": "अनुपलब्ध",
|
||||
"unknown": "अज्ञात"
|
||||
},
|
||||
"device_tracker": {
|
||||
"home": "घर"
|
||||
"home": "घर",
|
||||
"not_home": "बाहर"
|
||||
},
|
||||
"person": {
|
||||
"home": "घर"
|
||||
"home": "घर",
|
||||
"not_home": "बाहर"
|
||||
}
|
||||
},
|
||||
"state": {
|
||||
@@ -57,6 +81,11 @@
|
||||
"previous": "पिछला",
|
||||
"undo": "पूर्ववत करें"
|
||||
},
|
||||
"components": {
|
||||
"history_charts": {
|
||||
"no_history_found": "स्थिति के कोइ भि पहले रिकार्ड नहि मिले"
|
||||
}
|
||||
},
|
||||
"dialogs": {
|
||||
"mqtt_device_debug_info": {
|
||||
"entities": "संस्थाएं",
|
||||
@@ -75,6 +104,14 @@
|
||||
"day": "{count} {count, plural,\n one {दिन}\n other {दिन}\n}",
|
||||
"week": "{count} {count, plural,\n one {हफ़्ता}\n other {हफ़्ते}\n}"
|
||||
},
|
||||
"errors": {
|
||||
"supervisor": {
|
||||
"ask": "मदद के लिए पूछें",
|
||||
"observer": "प्रेक्षक (Observer) की जाँच करें",
|
||||
"reboot": "मशीन पुनः आरंभ करने का प्रयास करें",
|
||||
"system_health": "उप्करन के स्वस्थ्य कि जाँच करे"
|
||||
}
|
||||
},
|
||||
"notification_drawer": {
|
||||
"empty": "सूचनाएँ नहीं हैं",
|
||||
"title": "सूचनाएँ"
|
||||
@@ -133,12 +170,15 @@
|
||||
"picker": {
|
||||
"headers": {
|
||||
"name": "नाम"
|
||||
}
|
||||
},
|
||||
"no_automations": "हमें कोई AUTOMATION नहीं मिला"
|
||||
}
|
||||
},
|
||||
"cloud": {
|
||||
"account": {
|
||||
"google": {
|
||||
"enable_ha_skill": "Home Assistant Cloud कौशल सक्रिय करे Google Assistant के लिय",
|
||||
"not_configured_title": "Google Assistant सक्रिय नहीं है",
|
||||
"title": "Google Assistant"
|
||||
},
|
||||
"thank_you_note": "होम असिस्टेंट क्लाउड का हिस्सा बनने के लिए धन्यवाद। यह आप जैसे लोगों की वजह से है कि हम हर किसी के लिए एक शानदार होम ऑटोमेशन अनुभव बनाने में सक्षम हैं। धन्यवाद!"
|
||||
@@ -163,6 +203,27 @@
|
||||
"password": "पासवर्ड"
|
||||
}
|
||||
},
|
||||
"core": {
|
||||
"section": {
|
||||
"core": {
|
||||
"analytics": {
|
||||
"learn_more": "हम आपके जानकारी को कैसे संसाधित करते हैं",
|
||||
"preference": {
|
||||
"diagnostics": {
|
||||
"description": "अप्रत्याशित त्रुटि होने पर क्रैश रिपोर्ट साझा करें"
|
||||
},
|
||||
"usage_supervisor": {
|
||||
"description": "नाम, संस्करण और क्षमताएं"
|
||||
},
|
||||
"usage": {
|
||||
"description": "नाम और संस्करण जानकारी",
|
||||
"title": "उप्योग किये एकीकरण"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"filtering": {
|
||||
"clear": "विशद",
|
||||
"filtering_by": "द्वारा छान रहे हैं"
|
||||
@@ -170,8 +231,19 @@
|
||||
"integrations": {
|
||||
"add_integration": "एकीकरण जोड़ें",
|
||||
"config_entry": {
|
||||
"check_the_logs": "अभिलेख जाँच करें",
|
||||
"delete": "हटाएं",
|
||||
"not_loaded": "तैयार नहीं हैं",
|
||||
"provided_by_custom_integration": "विशेष एकीकरण से उप्लब्ध",
|
||||
"rename": "नाम बदलें",
|
||||
"state": {
|
||||
"failed_unload": "खाली करने मे असफल",
|
||||
"loaded": "तैयार",
|
||||
"migration_error": "प्रवसन दोष",
|
||||
"not_loaded": "तैयार नहीं हैं",
|
||||
"setup_error": "एकीकरण करने में विफल",
|
||||
"setup_retry": "एकीकरण के लिय पुन: प्रयास जारी है"
|
||||
},
|
||||
"system_options": "सिस्टम विकल्प"
|
||||
},
|
||||
"integration": "एकीकरण",
|
||||
@@ -180,6 +252,18 @@
|
||||
"rename_input_label": "प्रवेश का नाम"
|
||||
},
|
||||
"introduction": "In questa schermata è possibile configurare Home Assistant e i suoi componenti. Non è ancora possibile configurare tutto tramite l'interfaccia, ma ci stiamo lavorando.",
|
||||
"logs": {
|
||||
"custom_integration": "िशेष एकीकरण",
|
||||
"error_from_custom_integration": "ये दोश िशेष एकीकरण की वजह से है",
|
||||
"level": {
|
||||
"critical": "अभिलेख स्तर CRITICAL",
|
||||
"debug": "अभिलेख स्तर DEBUG",
|
||||
"error": "अभिलेख स्तर ERROR",
|
||||
"info": "अभिलेख स्तर INFO",
|
||||
"warning": "अभिलेख स्तर WARNING"
|
||||
},
|
||||
"no_errors": "कोई त्रुटी नहीं बताई गई है"
|
||||
},
|
||||
"mqtt": {
|
||||
"title": "MQTT"
|
||||
},
|
||||
@@ -188,11 +272,17 @@
|
||||
"unknown": "अनजान"
|
||||
}
|
||||
},
|
||||
"scene": {
|
||||
"picker": {
|
||||
"no_scenes": "हमें कोई SCENE नहीं मिला"
|
||||
}
|
||||
},
|
||||
"script": {
|
||||
"picker": {
|
||||
"headers": {
|
||||
"name": "नाम"
|
||||
}
|
||||
},
|
||||
"no_scripts": "हमें कोई SCRIPT नहीं मिला"
|
||||
}
|
||||
},
|
||||
"users": {
|
||||
@@ -229,6 +319,11 @@
|
||||
"services": {
|
||||
"title": "सेवाएं"
|
||||
},
|
||||
"states": {
|
||||
"filter_states": "स्थिति छन्नी",
|
||||
"set_state": "स्थिति निर्धारित करे",
|
||||
"state": "स्थिति"
|
||||
},
|
||||
"templates": {
|
||||
"title": "टेम्पलेट्स"
|
||||
}
|
||||
@@ -236,6 +331,9 @@
|
||||
},
|
||||
"lovelace": {
|
||||
"cards": {
|
||||
"empty_state": {
|
||||
"title": "स्वागत है घर मै"
|
||||
},
|
||||
"starting": {
|
||||
"description": "होम असिस्टेंट शुरू हो रहा है, कृपया प्रतीक्षा करें ..."
|
||||
}
|
||||
@@ -245,11 +343,21 @@
|
||||
},
|
||||
"editor": {
|
||||
"card": {
|
||||
"alarm-panel": {
|
||||
"available_states": "उप्लब्दह स्थिति"
|
||||
},
|
||||
"conditional": {
|
||||
"current_state": "वरतमान",
|
||||
"state_equal": "स्थिति इस के बराबर है",
|
||||
"state_not_equal": "स्थिति इस के बराबर नहि है"
|
||||
},
|
||||
"entity": {
|
||||
"description": "एंटिटी कार्ड आपको अपनी इकाई की स्थिति का त्वरित अवलोकन देता है।"
|
||||
"description": "स्थिति कार्ड आपको अपनी इकाई की स्थिति का संक्षेप देता है।"
|
||||
},
|
||||
"generic": {
|
||||
"double_tap_action": "डबल टैप एक्शन"
|
||||
"double_tap_action": "डबल टैप एक्शन",
|
||||
"show_state": "स्थिति दिखाएं?",
|
||||
"state": "स्थिति"
|
||||
},
|
||||
"map": {
|
||||
"hours_to_show": "hours to show"
|
||||
|
||||
@@ -707,7 +707,7 @@
|
||||
"text": "Inserisci il nome della nuova area.",
|
||||
"title": "Aggiungi nuova area"
|
||||
},
|
||||
"add_new": "Aggiungi nuova area…",
|
||||
"add_new": "Aggiungi nuova area",
|
||||
"area": "Area",
|
||||
"clear": "Cancella",
|
||||
"no_areas": "Non hai aree",
|
||||
@@ -886,6 +886,7 @@
|
||||
}
|
||||
},
|
||||
"service-control": {
|
||||
"integration_doc": "Documentazione dell'integrazione",
|
||||
"required": "Questo campo è obbligatorio",
|
||||
"service_data": "Dati del servizio",
|
||||
"target": "Destinazioni",
|
||||
@@ -1695,7 +1696,7 @@
|
||||
},
|
||||
"connected": "Connesso",
|
||||
"connection_status": "Stato della connessione cloud",
|
||||
"fetching_subscription": "Recupero abbonamento ...",
|
||||
"fetching_subscription": "Recupero iscrizione in corso...",
|
||||
"google": {
|
||||
"config_documentation": "Documentazione di configurazione",
|
||||
"devices_pin": "PIN dei dispositivi di sicurezza",
|
||||
@@ -1751,7 +1752,7 @@
|
||||
"disable_hook_error_msg": "Impossibile disabilitare webhook:",
|
||||
"info": "Tutto ciò che è configurato per essere attivato da un webhook può essere dotato di un URL accessibile pubblicamente per consentire d'inviare i dati ad Home Assistant da qualsiasi luogo, senza esporre la vostra istanza a Internet.",
|
||||
"link_learn_more": "Ulteriori informazioni sulla creazione di automazioni basate su webhook.",
|
||||
"loading": "Caricamento in corso ...",
|
||||
"loading": "Caricamento in corso...",
|
||||
"manage": "Gestisci",
|
||||
"no_hooks_yet": "Sembra che tu non abbia ancora dei webhook. Per iniziare, configurare un ",
|
||||
"no_hooks_yet_link_automation": "automazione webhook",
|
||||
@@ -2156,8 +2157,11 @@
|
||||
"caption": "Integrazioni",
|
||||
"config_entry": {
|
||||
"area": "In {area}",
|
||||
"check_the_logs": "Controlla i registri",
|
||||
"configure": "Configura",
|
||||
"delete": "Elimina",
|
||||
"delete_confirm": "Sei sicuro di voler eliminare questa integrazione?",
|
||||
"depends_on_cloud": "Dipende dal cloud",
|
||||
"device_unavailable": "Dispositivo non disponibile",
|
||||
"devices": "{count} {count, plural, \none {dispositivo}\nother {dispositivi}\n}",
|
||||
"disable_restart_confirm": "Riavvia Home Assistant per terminare la disabilitazione di questa integrazione",
|
||||
@@ -2180,14 +2184,23 @@
|
||||
"logs": "registri",
|
||||
"manuf": "da {manufacturer}",
|
||||
"no_area": "Nessuna area",
|
||||
"not_loaded": "Non caricato, controlla il {logs_link}",
|
||||
"not_loaded": "Non caricato",
|
||||
"options": "Opzioni",
|
||||
"provided_by_custom_integration": "Fornito da un'integrazione personalizzata",
|
||||
"reload": "Ricarica",
|
||||
"reload_confirm": "L'integrazione è stata ricaricata",
|
||||
"reload_restart_confirm": "Riavvia Home Assistant per completare il ricaricamento di questa integrazione",
|
||||
"rename": "Rinomina",
|
||||
"restart_confirm": "Riavvia Home Assistant per completare la rimozione di questa integrazione",
|
||||
"services": "{count} {count, plural,\n one {servizio}\n other {servizi}\n}",
|
||||
"state": {
|
||||
"failed_unload": "Impossibile scaricare",
|
||||
"loaded": "Caricato",
|
||||
"migration_error": "Errore di migrazione",
|
||||
"not_loaded": "Non caricato",
|
||||
"setup_error": "Configurazione non riuscita",
|
||||
"setup_retry": "Nuovo tentativo di configurazione"
|
||||
},
|
||||
"system_options": "Opzioni di sistema",
|
||||
"unnamed_entry": "Voce senza nome"
|
||||
},
|
||||
@@ -2216,7 +2229,7 @@
|
||||
"configure": "Configura",
|
||||
"configured": "Configurato",
|
||||
"confirm_new": "Vuoi configurare {integration}?",
|
||||
"description": "Gestisci le integrazioni con servizi, dispositivi, ...",
|
||||
"description": "Gestisci le integrazioni con servizi o dispositivi",
|
||||
"details": "Dettagli dell'integrazione",
|
||||
"disable": {
|
||||
"disabled_integrations": "{number} disabilitate",
|
||||
@@ -2255,8 +2268,10 @@
|
||||
"logs": {
|
||||
"caption": "Registri",
|
||||
"clear": "Pulisci",
|
||||
"custom_integration": "integrazione personalizzata",
|
||||
"description": "Vedi i registri di Home Assistant",
|
||||
"details": "Dettagli registro ({level})",
|
||||
"error_from_custom_integration": "Questo errore ha avuto origine da un'integrazione personalizzata.",
|
||||
"level": {
|
||||
"critical": "CRITICO",
|
||||
"debug": "DEBUG",
|
||||
@@ -2646,7 +2661,7 @@
|
||||
"confirm_restart": "Sei sicuro di voler riavviare Home Assistant?",
|
||||
"confirm_stop": "Sei sicuro di voler arrestare Home Assistant?",
|
||||
"heading": "Gestione del Server",
|
||||
"introduction": "Controllare il server Home Assistant... da Home Assistant.",
|
||||
"introduction": "Controlla il tuo Server Home Assistant... da Home Assistant.",
|
||||
"restart": "Riavviare",
|
||||
"stop": "Arrestare"
|
||||
},
|
||||
@@ -2773,6 +2788,15 @@
|
||||
"manufacturer_code_override": "Sostituzione codice produttore",
|
||||
"value": "Valore"
|
||||
},
|
||||
"configuration_page": {
|
||||
"shortcuts_title": "Collegamenti",
|
||||
"update_button": "Aggiorna Configurazione",
|
||||
"zha_options": {
|
||||
"default_light_transition": "Tempo di transizione della luce predefinito (secondi)",
|
||||
"enable_identify_on_join": "Abilita la notifica di identificazione quando i dispositivi si collegano alla rete",
|
||||
"title": "Opzioni Globali"
|
||||
}
|
||||
},
|
||||
"device_pairing_card": {
|
||||
"CONFIGURED": "Configurazione completata",
|
||||
"CONFIGURED_status_text": "Inizializzazione",
|
||||
@@ -3039,12 +3063,12 @@
|
||||
"count_listeners": " ({count} ascoltatori)",
|
||||
"data": "Dati Evento (YAML, opzionale)",
|
||||
"description": "Attiva un evento sul bus eventi.",
|
||||
"documentation": "Documentazione degli Eventi.",
|
||||
"documentation": "Documentazione degli eventi",
|
||||
"event_fired": "Evento {name} generato",
|
||||
"fire_event": "Scatena Evento",
|
||||
"listen_to_events": "Ascoltare gli eventi",
|
||||
"listening_to": "In ascolto di",
|
||||
"notification_event_fired": "Evento {type} eseguito correttamente!",
|
||||
"notification_event_fired": "Evento {type} eseguito con successo!",
|
||||
"start_listening": "Iniziare ad ascoltare",
|
||||
"stop_listening": "Interrompere l'ascolto",
|
||||
"subscribe_to": "Evento a cui iscriversi",
|
||||
@@ -3071,8 +3095,8 @@
|
||||
"attributes": "Attributi",
|
||||
"copy_id": "Copia ID negli appunti",
|
||||
"current_entities": "Entità correnti",
|
||||
"description1": "Impostare la rappresentazione di un dispositivo all'interno di Home Assistant.",
|
||||
"description2": "Questo non comunicherà con il dispositivo attuale.",
|
||||
"description1": "Imposta lo stato corrente di un'entità all'interno di Home Assistant.",
|
||||
"description2": "Se l'entità appartiene a un dispositivo, non si potrà attuare nessuna comunicazione con esso.",
|
||||
"entity": "Entità",
|
||||
"filter_attributes": "Filtra attributi",
|
||||
"filter_entities": "Filtra entità",
|
||||
@@ -3444,7 +3468,7 @@
|
||||
"duplicate": "Duplica scheda",
|
||||
"edit": "Modifica",
|
||||
"header": "Configurazione della scheda",
|
||||
"move": "Sposta nella vista",
|
||||
"move": "Sposta in Visualizza",
|
||||
"move_after": "Sposta la scheda dopo",
|
||||
"move_before": "Sposta la scheda prima",
|
||||
"options": "Altre opzioni",
|
||||
|
||||
@@ -2156,6 +2156,8 @@
|
||||
"caption": "통합 구성요소",
|
||||
"config_entry": {
|
||||
"area": "{area}에 위치",
|
||||
"check_the_logs": "로그 확인",
|
||||
"configure": "구성",
|
||||
"delete": "삭제하기",
|
||||
"delete_confirm": "이 통합 구성요소를 제거하시겠습니까?",
|
||||
"device_unavailable": "기기 사용불가",
|
||||
@@ -2180,7 +2182,7 @@
|
||||
"logs": "로그",
|
||||
"manuf": "{manufacturer} 제조",
|
||||
"no_area": "영역 없음",
|
||||
"not_loaded": "불러오지 못했습니다. {logs_link}을(를) 확인해주세요.",
|
||||
"not_loaded": "로드되지 않음",
|
||||
"options": "옵션",
|
||||
"reload": "다시 읽어오기",
|
||||
"reload_confirm": "통합 구성요소를 다시 읽어 들였습니다",
|
||||
@@ -2188,6 +2190,14 @@
|
||||
"rename": "이름 변경하기",
|
||||
"restart_confirm": "이 통합 구성요소를 제거하려면 Home Assistant를 다시 시작해주세요",
|
||||
"services": "{count} {count, plural,\n one{개의 서비스}\n other{개의 서비스}\n}",
|
||||
"state": {
|
||||
"failed_unload": "언로드 실패",
|
||||
"loaded": "로드 됨",
|
||||
"migration_error": "마이그레이션 오류",
|
||||
"not_loaded": "로드되지 않음",
|
||||
"setup_error": "설정 실패",
|
||||
"setup_retry": "설정 재시도"
|
||||
},
|
||||
"system_options": "시스템 옵션",
|
||||
"unnamed_entry": "이름이 없는 항목"
|
||||
},
|
||||
|
||||
@@ -749,6 +749,9 @@
|
||||
"label": "Bild",
|
||||
"unsupported_format": "Net ënnerstëtzte Format, wiel e JPEG, PNG oder GIF Bild."
|
||||
},
|
||||
"related-filter-menu": {
|
||||
"filter_by_entity": "Der Entitéit no filteren"
|
||||
},
|
||||
"related-items": {
|
||||
"area": "Beräich",
|
||||
"automation": "Deel vun de folgenden Automatismen",
|
||||
@@ -1055,6 +1058,9 @@
|
||||
"zha_device_card": {
|
||||
"device_name_placeholder": "Numm vum Apparat änneren"
|
||||
}
|
||||
},
|
||||
"zha_reconfigure_device": {
|
||||
"heading": "Apparat frësch konfiguréieren"
|
||||
}
|
||||
},
|
||||
"duration": {
|
||||
@@ -1075,6 +1081,10 @@
|
||||
"key_wrong_type": "De Wäert fir \"{key}\" ass net vum visuelle Editeur ënnerstëtzt. Mir ënnerstëtzen ({type_correct}) mee mir kruuten ({type_wrong}).",
|
||||
"no_template_editor_support": "Modeller net ënnerstëtzt am Visuellen Editeur",
|
||||
"no_type_provided": "Keen Typ uginn."
|
||||
},
|
||||
"supervisor": {
|
||||
"ask": "No Hëllef froen",
|
||||
"system_health": "System Zoustand iwwerpréiwen"
|
||||
}
|
||||
},
|
||||
"login-form": {
|
||||
@@ -1889,6 +1899,9 @@
|
||||
"filtering_by": "Filteren anhand vun",
|
||||
"show": "Uweisen"
|
||||
},
|
||||
"hassio": {
|
||||
"button": "Astellen"
|
||||
},
|
||||
"header": "Home Assistant astellen",
|
||||
"helpers": {
|
||||
"caption": "Helper",
|
||||
@@ -1954,8 +1967,11 @@
|
||||
"caption": "Integratiounen",
|
||||
"config_entry": {
|
||||
"area": "An {area}",
|
||||
"check_the_logs": "Logs iwwerpréifen",
|
||||
"configure": "Astellen",
|
||||
"delete": "Läschen",
|
||||
"delete_confirm": "Sécher fir dës Integratioun ze läsche?",
|
||||
"depends_on_cloud": "Cloud ofhängeg",
|
||||
"device_unavailable": "Apparat net erreechbar",
|
||||
"devices": "{count} {count, plural,\n one {Apparat}\n other {Apparaten}\n}",
|
||||
"disable": {
|
||||
@@ -1973,12 +1989,21 @@
|
||||
"hub": "Verbonnen via",
|
||||
"manuf": "vun {manufacturer}",
|
||||
"no_area": "Kee Beräich",
|
||||
"not_loaded": "Net gelueden",
|
||||
"options": "Optiounen",
|
||||
"reload": "Nei lueden",
|
||||
"reload_confirm": "Integratioun gouf frësch gelueden",
|
||||
"reload_restart_confirm": "Start Home Assistant nei fir dës Integratioun fäerdeg ze lueden",
|
||||
"rename": "Ëmbenennen",
|
||||
"restart_confirm": "Start Home Assistant nei fir dës Integratioun ze läschen",
|
||||
"state": {
|
||||
"failed_unload": "Feeler beim entlueden",
|
||||
"loaded": "Gelueden",
|
||||
"migration_error": "Feeler bei der Migratioun",
|
||||
"not_loaded": "Net gelueden",
|
||||
"setup_error": "Feeler beim ariichten",
|
||||
"setup_retry": "Probéiert nach emol anzeriichten"
|
||||
},
|
||||
"system_options": "System Optiounen",
|
||||
"unnamed_entry": "Entrée ouni Numm"
|
||||
},
|
||||
@@ -2038,6 +2063,11 @@
|
||||
"clear": "Läschen",
|
||||
"description": "Home Assistant Logbicher ukucken",
|
||||
"details": "Detailler vum Log ({level})",
|
||||
"level": {
|
||||
"error": "FEELER",
|
||||
"info": "INFO",
|
||||
"warning": "WARNUNG"
|
||||
},
|
||||
"load_full_log": "Kompletten Home Assistant Log lueden",
|
||||
"loading_log": "Feeler Log gëtt gelueden...",
|
||||
"multiple_messages": "Noriicht als éischt opgetrueden um {time} a säit deem {counter} mol opgetrueden",
|
||||
@@ -3446,6 +3476,9 @@
|
||||
}
|
||||
},
|
||||
"page-onboarding": {
|
||||
"analytics": {
|
||||
"finish": "Nächst"
|
||||
},
|
||||
"core-config": {
|
||||
"button_detect": "Entdecken",
|
||||
"finish": "Nächst",
|
||||
@@ -3455,12 +3488,14 @@
|
||||
"location_name": "Numm vun denger Home Assistant Installatioun",
|
||||
"location_name_default": "Doheem"
|
||||
},
|
||||
"finish": "Ofschléissen",
|
||||
"integration": {
|
||||
"finish": "Ofschléissen",
|
||||
"intro": "Apparaten a Servicë ginn am Home Assistant als Integratioune representéiert. Dir kënnt si elo astellen, oder méi spéit vun der Konfiguratioun's Säit aus.",
|
||||
"more_integrations": "Méi"
|
||||
},
|
||||
"intro": "Sidd Dir prett fir Äert Heem interessant ze maachen, Är Privatsphär ze garantéieren an enger weltwäiter Gemeinschaft bei ze trieden?",
|
||||
"next": "Nächst",
|
||||
"restore": {
|
||||
"description": "Alternativ kanns Du aus engem fréiere Snapshot restauréieren.",
|
||||
"hide_log": "Komplette Log verstoppen",
|
||||
|
||||
@@ -658,7 +658,8 @@
|
||||
"integrations": {
|
||||
"config_entry": {
|
||||
"hub": "Prijungtas per",
|
||||
"no_area": "Nėra srities"
|
||||
"no_area": "Nėra srities",
|
||||
"provided_by_custom_integration": "Teikiama naudojant pasirinktinę integraciją"
|
||||
},
|
||||
"config_flow": {
|
||||
"aborted": "Nutraukta",
|
||||
|
||||
@@ -707,7 +707,7 @@
|
||||
"text": "Fyll inn navnet på det nye området.",
|
||||
"title": "Legg til nytt område"
|
||||
},
|
||||
"add_new": "Legg til nytt område ...",
|
||||
"add_new": "Legg til nytt område...",
|
||||
"area": "Område",
|
||||
"clear": "Tøm",
|
||||
"no_areas": "Du har ingen områder",
|
||||
@@ -1237,7 +1237,7 @@
|
||||
"title": "Varsler"
|
||||
},
|
||||
"notification_toast": {
|
||||
"connection_lost": "Forbindelsen ble brutt. Kobler til på nytt...",
|
||||
"connection_lost": "Forbindelse mistet. Koble til igjen...",
|
||||
"dismiss": "Avvis",
|
||||
"intergration_starting": "Starter {integration}. Ikke alt er tilgjengelig før lasting er ferdig.",
|
||||
"service_call_failed": "Kunne ikke tilkalle tjenesten: {service}",
|
||||
@@ -1751,7 +1751,7 @@
|
||||
"disable_hook_error_msg": "Kan ikke deaktivere webhook:",
|
||||
"info": "Alt som er konfigurert til å utløses av en webhook, kan gis en offentlig tilgjengelig URL-adresse for å tillate deg å sende data tilbake til Home Assistent fra hvor som helst, uten å utsette forekomsten din for Internett.",
|
||||
"link_learn_more": "Finn ut mer om hvordan du oppretter webhook-drevne automasjoner.",
|
||||
"loading": "Laster inn ...",
|
||||
"loading": "Laster inn...",
|
||||
"manage": "Administrer",
|
||||
"no_hooks_yet": "Ser ut som du ikke har noen webhook'er ennå. Kom i gang ved å konfigurere en ",
|
||||
"no_hooks_yet_link_automation": "webhook-automasjon",
|
||||
@@ -2156,8 +2156,11 @@
|
||||
"caption": "Integrasjoner",
|
||||
"config_entry": {
|
||||
"area": "I {area}",
|
||||
"check_the_logs": "Kontroller loggene",
|
||||
"configure": "Konfigurer",
|
||||
"delete": "Slett",
|
||||
"delete_confirm": "Er du sikker på at du vil slette denne integrasjonen?",
|
||||
"depends_on_cloud": "Avhenger av skyen",
|
||||
"device_unavailable": "Enheten er utilgjengelig",
|
||||
"devices": "{count} {count, plural,\n one {enhet}\n other {enheter}\n}",
|
||||
"disable_restart_confirm": "Start Home Assistant på nytt for å fullføre deaktiveringen av denne integreringen",
|
||||
@@ -2180,14 +2183,23 @@
|
||||
"logs": "Logger",
|
||||
"manuf": "av {manufacturer}",
|
||||
"no_area": "Intet område",
|
||||
"not_loaded": "Ikke lastet inn, sjekk {logs_link}",
|
||||
"not_loaded": "Ikke lastet",
|
||||
"options": "Alternativer",
|
||||
"provided_by_custom_integration": "Levert av en tilpasset integrasjon",
|
||||
"reload": "Last inn på nytt",
|
||||
"reload_confirm": "Integrasjonen ble lastet på nytt",
|
||||
"reload_restart_confirm": "Start Home Assistant på nytt for å fullføre omlastingen av denne integrasjonen",
|
||||
"rename": "Gi nytt navn",
|
||||
"restart_confirm": "Start Home Assistant på nytt for å fullføre fjerningen av denne integrasjonen",
|
||||
"services": "{count} {count, plural,\n one {tjeneste}\n other {tjenester}\n}",
|
||||
"state": {
|
||||
"failed_unload": "Kan ikke laste ut",
|
||||
"loaded": "Lastet",
|
||||
"migration_error": "Migrasjonsfeil",
|
||||
"not_loaded": "Ikke lastet",
|
||||
"setup_error": "Kunne ikke konfigurere",
|
||||
"setup_retry": "Prøver på nytt for å sette opp"
|
||||
},
|
||||
"system_options": "Systemalternativer",
|
||||
"unnamed_entry": "Ikke navngitt oppføring"
|
||||
},
|
||||
@@ -2267,7 +2279,7 @@
|
||||
"warning": "ADVARSEL"
|
||||
},
|
||||
"load_full_log": "Last inn fullstendig Home Assistant logg",
|
||||
"loading_log": "Laster inn feillogg ...",
|
||||
"loading_log": "Laster inn feillogg...",
|
||||
"multiple_messages": "meldingen oppstod først ved {time} og vist {counter} ganger",
|
||||
"no_errors": "Ingen feil er rapportert",
|
||||
"no_issues": "Det er ingen nye problemer!",
|
||||
@@ -2648,7 +2660,7 @@
|
||||
"confirm_restart": "Er du sikker på at du vil starte Home Assistant på nytt?",
|
||||
"confirm_stop": "Er du sikker på at du vil stoppe Home Assistant?",
|
||||
"heading": "Serveradministrasjon",
|
||||
"introduction": "Kontroller din Home Assistant server... fra Home Assistant.",
|
||||
"introduction": "Kontroller Home Assistant-serveren ... fra Home Assistant.",
|
||||
"restart": "Omstart",
|
||||
"stop": "Stopp"
|
||||
},
|
||||
@@ -3041,12 +3053,12 @@
|
||||
"count_listeners": " ({count} lyttere)",
|
||||
"data": "Hendelsedata (YAML, valgfritt)",
|
||||
"description": "Send ut en hendelse på hendelsesbussen",
|
||||
"documentation": "Hendelsedokumentasjon.",
|
||||
"documentation": "Dokumentasjon for hendelser",
|
||||
"event_fired": "Hendelse {name} utført",
|
||||
"fire_event": "Utfør hendelse",
|
||||
"listen_to_events": "Lytt til hendelser",
|
||||
"listening_to": "Lytte til",
|
||||
"notification_event_fired": "Hendelse {type} vellykket utløst!",
|
||||
"notification_event_fired": "Arrangementet {type} vellykket!",
|
||||
"start_listening": "Begynn å lytte",
|
||||
"stop_listening": "Stopp lytting",
|
||||
"subscribe_to": "Hendelse for å abonnere på",
|
||||
@@ -3073,8 +3085,8 @@
|
||||
"attributes": "Attributter",
|
||||
"copy_id": "Kopier ID til utklippstavlen",
|
||||
"current_entities": "Gjeldende entiteter",
|
||||
"description1": "Angi representasjonen av en enhet i Home Assistant.",
|
||||
"description2": "Dette vil ikke kommunisere med den faktiske enheten.",
|
||||
"description1": "Angi gjeldende tilstandsrepresentasjon for en enhet i Home Assistant.",
|
||||
"description2": "Hvis enheten tilhører en enhet, vil det ikke være noen faktisk kommunikasjon med den enheten.",
|
||||
"entity": "Entitet",
|
||||
"filter_attributes": "Filtrere attributter",
|
||||
"filter_entities": "Filtrere entiteter",
|
||||
@@ -3446,7 +3458,7 @@
|
||||
"duplicate": "Dupliser kort",
|
||||
"edit": "Rediger",
|
||||
"header": "Kortkonfigurasjon",
|
||||
"move": "Flytt til visning",
|
||||
"move": "Gå til visning",
|
||||
"move_after": "Flytt kortet etter",
|
||||
"move_before": "Flytt kortet før",
|
||||
"options": "Flere alternativer",
|
||||
|
||||
@@ -707,7 +707,7 @@
|
||||
"text": "Geef de naam op van het nieuwe gebied.",
|
||||
"title": "Gebied toevoegen"
|
||||
},
|
||||
"add_new": "Gebied toevoegen...",
|
||||
"add_new": "Nieuw gebied toevoegen...",
|
||||
"area": "Gebied",
|
||||
"clear": "Wis",
|
||||
"no_areas": "Je hebt geen gebieden",
|
||||
@@ -2156,8 +2156,11 @@
|
||||
"caption": "Integraties",
|
||||
"config_entry": {
|
||||
"area": "In {area}",
|
||||
"check_the_logs": "Controleer de logboeken",
|
||||
"configure": "Configureer",
|
||||
"delete": "Verwijder",
|
||||
"delete_confirm": "Weet je zeker dat je deze integratie wilt verwijderen?",
|
||||
"depends_on_cloud": "Cloud afhankelijk",
|
||||
"device_unavailable": "Apparaat niet beschikbaar",
|
||||
"devices": "{count} {count, plural,\n one {apparaat}\n other {apparaten}\n}",
|
||||
"disable_restart_confirm": "Start Home Assistant opnieuw op om het uitzetten van deze integratie te voltooien",
|
||||
@@ -2180,14 +2183,23 @@
|
||||
"logs": "logs",
|
||||
"manuf": "door {manufacturer}",
|
||||
"no_area": "Geen Gebied",
|
||||
"not_loaded": "Niet geladen, controleer de {logs_link}",
|
||||
"not_loaded": "Niet geladen",
|
||||
"options": "Opties",
|
||||
"provided_by_custom_integration": "Geleverd door een aangepaste integratie",
|
||||
"reload": "Herlaad",
|
||||
"reload_confirm": "De integratie is opnieuw geladen",
|
||||
"reload_restart_confirm": "Start Home Assistant opnieuw om het opnieuw laden van deze integratie te voltooien",
|
||||
"rename": "Naam wijzigen",
|
||||
"restart_confirm": "Herstart Home Assistant om het verwijderen van deze integratie te voltooien",
|
||||
"services": "{count} {count, plural,\n one {service}\n other {services}\n}",
|
||||
"state": {
|
||||
"failed_unload": "Uitladen mislukt",
|
||||
"loaded": "Geladen",
|
||||
"migration_error": "Migratiefout",
|
||||
"not_loaded": "Niet geladen",
|
||||
"setup_error": "Instellen mislukt",
|
||||
"setup_retry": "Opnieuw proberen in te stellen"
|
||||
},
|
||||
"system_options": "Systeeminstellingen",
|
||||
"unnamed_entry": "Naamloze invoer"
|
||||
},
|
||||
@@ -2775,6 +2787,15 @@
|
||||
"manufacturer_code_override": "Fabrikant Code Override",
|
||||
"value": "Waarde"
|
||||
},
|
||||
"configuration_page": {
|
||||
"shortcuts_title": "Snelkoppelingen",
|
||||
"update_button": "Update configuratie",
|
||||
"zha_options": {
|
||||
"default_light_transition": "Standaard licht transitietijd (seconden)",
|
||||
"enable_identify_on_join": "Schakel het identificatie-effect in wanneer apparaten in het netwerk komen",
|
||||
"title": "Globale opties"
|
||||
}
|
||||
},
|
||||
"device_pairing_card": {
|
||||
"CONFIGURED": "Configuratie voltooid",
|
||||
"CONFIGURED_status_text": "Initialiseren",
|
||||
@@ -3041,7 +3062,7 @@
|
||||
"count_listeners": " ({count} luisteraars)",
|
||||
"data": "Gebeurtenis data (YAML, optioneel)",
|
||||
"description": "Start een evenement op de Home Assistant-gebeurtenisbus",
|
||||
"documentation": "Gebeurtenissen documentatie.",
|
||||
"documentation": "Gebeurtenissen documentatie",
|
||||
"event_fired": "Gebeurtenis {name} uitgevoerd",
|
||||
"fire_event": "Gebeurtenis uitvoeren",
|
||||
"listen_to_events": "Luisteren naar gebeurtenissen",
|
||||
@@ -3073,8 +3094,8 @@
|
||||
"attributes": "Attributen",
|
||||
"copy_id": "Kopieer ID naar klembord",
|
||||
"current_entities": "Huidige entiteiten",
|
||||
"description1": "Stelt de weergave van een apparaat in Home Assistant in.",
|
||||
"description2": "Er vindt geen communicatie met het daadwerkelijke apparaat plaats.",
|
||||
"description1": "Stelt de weergave van een entiteit in Home Assistant in.",
|
||||
"description2": "Als de entiteit tot een apparaat behoort, dan is er geen daadwerkelijke communicatie met dat apparaat.",
|
||||
"entity": "Entiteit",
|
||||
"filter_attributes": "Filter attributen",
|
||||
"filter_entities": "Filter entiteiten",
|
||||
@@ -3446,7 +3467,7 @@
|
||||
"duplicate": "Dupliceer kaart",
|
||||
"edit": "Bewerken",
|
||||
"header": "Kaart configuratie",
|
||||
"move": "Verplaatsen",
|
||||
"move": "Verplaats naar weergave",
|
||||
"move_after": "Verplaats kaart na",
|
||||
"move_before": "Verplaats kaart voor",
|
||||
"options": "Meer opties",
|
||||
|
||||
@@ -667,7 +667,7 @@
|
||||
"enable": "Włącz",
|
||||
"error_required": "To pole jest wymagane",
|
||||
"leave": "Wyjdź",
|
||||
"loading": "Ładowanie",
|
||||
"loading": "Wczytywanie",
|
||||
"menu": "Menu",
|
||||
"next": "Dalej",
|
||||
"no": "Nie",
|
||||
@@ -694,7 +694,7 @@
|
||||
"title": "Błąd podczas pobierania dodatków"
|
||||
},
|
||||
"no_supervisor": {
|
||||
"description": "Nie znaleziono Supervisora, więc nie można załadować dodatków.",
|
||||
"description": "Nie znaleziono Supervisora, więc nie można wczytać dodatków.",
|
||||
"title": "Brak Supervisora"
|
||||
}
|
||||
}
|
||||
@@ -758,7 +758,7 @@
|
||||
},
|
||||
"history_charts": {
|
||||
"history_disabled": "Integracja historia wyłączona",
|
||||
"loading_history": "Ładowanie historii...",
|
||||
"loading_history": "Wczytywanie historii...",
|
||||
"no_history_found": "Nie znaleziono historii."
|
||||
},
|
||||
"logbook": {
|
||||
@@ -1001,7 +1001,7 @@
|
||||
"pattern": "Wyrażenie regularne do sprawdzania poprawności po stronie klienta",
|
||||
"text": "Pole tekstowe"
|
||||
},
|
||||
"platform_not_loaded": "Komponent {platform} nie jest załadowany, dodaj go do swojej konfiguracji dodając 'default_config:' lub ''{platform}:''.",
|
||||
"platform_not_loaded": "Komponent {platform} nie jest wczytany, dodaj go do swojej konfiguracji dodając 'default_config:' lub ''{platform}:''.",
|
||||
"required_error_msg": "To pole jest wymagane",
|
||||
"timer": {
|
||||
"duration": "Czas"
|
||||
@@ -1220,7 +1220,7 @@
|
||||
"observer": "Sprawdź obserwatora",
|
||||
"reboot": "Spróbuj ponownie uruchomić hosta",
|
||||
"system_health": "Sprawdź kondycję systemu",
|
||||
"title": "Nie można załadować panelu Supervisora!",
|
||||
"title": "Nie można wczytać panelu Supervisora!",
|
||||
"wait": "Jeśli właśnie uruchomiłeś system, upewnij się, że dałeś Supervisorowi wystarczająco dużo czasu na start."
|
||||
}
|
||||
},
|
||||
@@ -1481,7 +1481,7 @@
|
||||
"enable_disable": "Włącz/wyłącz automatyzację",
|
||||
"introduction": "Użyj automatyzacji, aby ożywić swój dom",
|
||||
"load_error_not_editable": "Tylko automatyzacje zdefiniowane w pliku automations.yaml są edytowalne",
|
||||
"load_error_unknown": "Wystąpił błąd podczas ładowania automatyzacji ({err_no})",
|
||||
"load_error_unknown": "Wystąpił błąd podczas wczytywania automatyzacji ({err_no})",
|
||||
"max": {
|
||||
"parallel": "Maksymalna liczba równoległych uruchomień",
|
||||
"queued": "Długość kolejki"
|
||||
@@ -1873,15 +1873,15 @@
|
||||
"core": {
|
||||
"analytics": {
|
||||
"documentation": "Zanim to włączysz, odwiedź stronę z dokumentacją analityczną {link}, aby zrozumieć, co wysyłasz i jak to jest przechowywane.",
|
||||
"header": "Analityka",
|
||||
"header": "Dane analityczne",
|
||||
"instance_id": "Identyfikator instancji: {huuid}",
|
||||
"introduction": "Udostępnij informacje o instalacji, aby ulepszyć Home Assistant i pomóż nam przekonać producentów do dodania lokalnych funkcji sterowania i prywatności.",
|
||||
"learn_more": "Jak przetwarzamy Twoje dane",
|
||||
"needs_base": "Aby ta opcja była dostępna, musisz włączyć podstawowe analityki",
|
||||
"needs_base": "Aby ta opcja była dostępna, musisz włączyć podstawowe dane analityczne",
|
||||
"preference": {
|
||||
"base": {
|
||||
"description": "Identyfikator instancji, wersja i typ instalacji.",
|
||||
"title": "Podstawowe analityki"
|
||||
"title": "Podstawowe dane analityczne"
|
||||
},
|
||||
"diagnostics": {
|
||||
"description": "Udostępniaj raporty o awariach, gdy wystąpią nieoczekiwane błędy.",
|
||||
@@ -2139,7 +2139,7 @@
|
||||
"path_configuration": "Ścieżka do pliku configuration.yaml: {path}",
|
||||
"server": "serwer",
|
||||
"source": "Źródło:",
|
||||
"system_health_error": "Komponent kondycji systemu nie jest załadowany. Dodaj 'system_health:' do pliku configuration.yaml",
|
||||
"system_health_error": "Komponent kondycji systemu nie jest wczytany. Dodaj 'system_health:' do pliku configuration.yaml",
|
||||
"system_health": {
|
||||
"manage": "Zarządzaj",
|
||||
"more_info": "więcej info"
|
||||
@@ -2156,8 +2156,11 @@
|
||||
"caption": "Integracje",
|
||||
"config_entry": {
|
||||
"area": "obszar: {area}",
|
||||
"check_the_logs": "Sprawdź logi",
|
||||
"configure": "Konfiguruj",
|
||||
"delete": "Usuń",
|
||||
"delete_confirm": "Czy na pewno chcesz usunąć tę integrację?",
|
||||
"depends_on_cloud": "Zależny od chmury",
|
||||
"device_unavailable": "Urządzenie niedostępne",
|
||||
"devices": "{count} {count, plural,\n one {urządzenie}\n few {urządzenia}\n many {urządzeń}\n other {urządzeń}\n}",
|
||||
"disable_restart_confirm": "Zrestartuj Home Assistanta, aby zakończyć wyłączanie tej integracji",
|
||||
@@ -2180,21 +2183,30 @@
|
||||
"logs": "logi",
|
||||
"manuf": "producent: {manufacturer}",
|
||||
"no_area": "brak",
|
||||
"not_loaded": "Nie załadowano, sprawdź {logs_link}",
|
||||
"not_loaded": "Nie wczytano",
|
||||
"options": "Opcje",
|
||||
"provided_by_custom_integration": "Dostarczone przez niestandardowy komponent",
|
||||
"reload": "Wczytaj ponownie",
|
||||
"reload_confirm": "Integracja została ponownie wczytana",
|
||||
"reload_restart_confirm": "Uruchom ponownie Home Assistanta, aby dokończyć ponowne wczytywanie tej integracji",
|
||||
"rename": "Zmień nazwę",
|
||||
"restart_confirm": "Zrestartuj Home Assistanta, aby zakończyć usuwanie tej integracji",
|
||||
"services": "{count} {count, plural,\n one {usługa}\n few {usługi}\n many {usług}\n other {usług}\n}",
|
||||
"state": {
|
||||
"failed_unload": "Błąd usuwania",
|
||||
"loaded": "Wczytano",
|
||||
"migration_error": "Błąd migracji",
|
||||
"not_loaded": "Nie wczytano",
|
||||
"setup_error": "Błąd konfiguracji",
|
||||
"setup_retry": "Próbuję ponownie skonfigurować"
|
||||
},
|
||||
"system_options": "Opcje systemowe",
|
||||
"unnamed_entry": "Nienazwany wpis"
|
||||
},
|
||||
"config_flow": {
|
||||
"aborted": "Przerwano",
|
||||
"close": "Zamknij",
|
||||
"could_not_load": "Nie można wczytać interfejsu konfiguracji",
|
||||
"could_not_load": "Nie udało się wczytać interfejsu konfiguracji",
|
||||
"created_config": "Utworzono konfigurację dla {name}.",
|
||||
"dismiss": "Okno dialogowe odrzucenia",
|
||||
"error": "Błąd",
|
||||
@@ -2206,7 +2218,7 @@
|
||||
"finish": "Zakończ",
|
||||
"loading_first_time": "Proszę czekać, trwa instalowanie integracji...",
|
||||
"not_all_required_fields": "Nie wszystkie wymagane pola są wypełnione.",
|
||||
"not_loaded": "Nie udało się załadować integracji, spróbuj ponownie uruchomić Home Assistanta.",
|
||||
"not_loaded": "Nie udało się wczytać integracji, spróbuj ponownie uruchomić Home Assistanta.",
|
||||
"pick_flow_step": {
|
||||
"new_flow": "Nie, skonfiguruj inną instancję integracji {integration}",
|
||||
"title": "Odkryliśmy je, chcesz je skonfigurować?"
|
||||
@@ -2216,7 +2228,7 @@
|
||||
"configure": "Konfiguruj",
|
||||
"configured": "Skonfigurowane",
|
||||
"confirm_new": "Czy chcesz skonfigurować {integration}?",
|
||||
"description": "Zarządzaj integracjami z usługami, urządzeniami, ...",
|
||||
"description": "Zarządzaj integracjami z usługami lub urządzeniami",
|
||||
"details": "Szczegóły integracji",
|
||||
"disable": {
|
||||
"disabled_integrations": "Wyłączonych: {number}",
|
||||
@@ -2266,8 +2278,8 @@
|
||||
"info": "INFO",
|
||||
"warning": "OSTRZEŻENIE"
|
||||
},
|
||||
"load_full_log": "Załaduj cały log Home Assistanta",
|
||||
"loading_log": "Ładowanie loga błędów…",
|
||||
"load_full_log": "Wczytaj cały log Home Assistanta",
|
||||
"loading_log": "Wczytywanie loga błędów…",
|
||||
"multiple_messages": "wiadomość pojawiła się po raz pierwszy {time} i powtarzała się {counter} razy",
|
||||
"no_errors": "Nie zgłoszono żadnych błędów",
|
||||
"no_issues": "Nie ma nowych problemów!",
|
||||
@@ -2426,7 +2438,7 @@
|
||||
},
|
||||
"node_query_stages": {
|
||||
"associations": "Odświeżanie grup skojarzeń i członkostwa",
|
||||
"cacheload": "Ładowanie informacji z pliku pamięci podręcznej OpenZWave. Węzły baterii pozostaną na tym etapie, dopóki węzeł się nie wybudzi.",
|
||||
"cacheload": "Wczytywanie informacji z pliku pamięci podręcznej OpenZWave. Węzły baterii pozostaną na tym etapie, dopóki węzeł się nie wybudzi.",
|
||||
"complete": "Proces odpytywania jest zakończony",
|
||||
"configuration": "Pobieranie wartości konfiguracyjnych z węzła",
|
||||
"dynamic": "Pobieranie często zmieniających się wartości z węzła",
|
||||
@@ -2534,7 +2546,7 @@
|
||||
"icon": "Ikona",
|
||||
"introduction": "Użyj scen, aby ożywić swój dom.",
|
||||
"load_error_not_editable": "Tylko sceny zdefiniowane w pliku scenes.yaml są edytowalne",
|
||||
"load_error_unknown": "Błąd ładowania sceny ({err_no})",
|
||||
"load_error_unknown": "Błąd wczytywania sceny ({err_no})",
|
||||
"name": "Nazwa",
|
||||
"save": "Zapisz",
|
||||
"unsaved_confirm": "Masz niezapisane zmiany. Na pewno chcesz wyjść?"
|
||||
@@ -3113,7 +3125,7 @@
|
||||
"observer": "Sprawdź obserwatora",
|
||||
"reboot": "Spróbuj ponownie uruchomić hosta",
|
||||
"system_health": "Sprawdź kondycję systemu",
|
||||
"title": "Nie można załadować panelu Supervisora!",
|
||||
"title": "Nie można wczytać panelu Supervisora!",
|
||||
"wait": "Jeśli właśnie uruchomiłeś system, upewnij się, że dałeś Supervisorowi wystarczająco dużo czasu na start."
|
||||
}
|
||||
},
|
||||
@@ -3167,7 +3179,7 @@
|
||||
"url": "Otwórz okno do {url_path}"
|
||||
},
|
||||
"safe-mode": {
|
||||
"description": "Podczas ładowania konfiguracji Home Assistant napotkał problemy i działa teraz w trybie awaryjnym. Zajrzyj do loga, aby sprawdzić, co poszło nie tak.",
|
||||
"description": "Podczas wczytywania konfiguracji Home Assistant napotkał problemy i działa teraz w trybie awaryjnym. Zajrzyj do loga, aby sprawdzić, co poszło nie tak.",
|
||||
"header": "Tryb awaryjny aktywny"
|
||||
},
|
||||
"shopping-list": {
|
||||
@@ -3522,7 +3534,7 @@
|
||||
"error_remove": "Nie można usunąć konfiguracji: {error}",
|
||||
"error_save_yaml": "Nie można zapisać YAML: {error}",
|
||||
"header": "Edytuj konfigurację",
|
||||
"lovelace_changed": "Konfiguracja Lovelace została zaktualizowana, czy chcesz załadować zaktualizowaną konfigurację do edytora i stracić obecne zmiany?",
|
||||
"lovelace_changed": "Konfiguracja Lovelace została zaktualizowana, czy chcesz wczytać zaktualizowaną konfigurację do edytora i stracić obecne zmiany?",
|
||||
"reload": "Wczytaj ponownie",
|
||||
"resources_moved": "Zasoby nie powinny być już dodawane do konfiguracji Lovelace, można je dodawać w panelu konfiguracji Lovelace.",
|
||||
"save": "Zapisz",
|
||||
|
||||
@@ -1451,6 +1451,14 @@
|
||||
"description": "Sistemul de unități, locația, fusul orar și alți parametri generali",
|
||||
"section": {
|
||||
"core": {
|
||||
"analytics": {
|
||||
"documentation": "Inainte de activare acestuia asigurați-va ca vizitați pagina de documentare {link} pentru a înțelege ce trimiteți si cum este stocată",
|
||||
"preference": {
|
||||
"statistics": {
|
||||
"title": "Statistici de utilizare"
|
||||
}
|
||||
}
|
||||
},
|
||||
"core_config": {
|
||||
"edit_requires_storage": "Editorul a fost dezactivat deoarece configurația a fost stocata în configuration.yaml.",
|
||||
"elevation": "Altitudine",
|
||||
@@ -3169,12 +3177,14 @@
|
||||
"location_name": "Numele instalării Home Assistant",
|
||||
"location_name_default": "Acasă"
|
||||
},
|
||||
"finish": "Finalizare",
|
||||
"integration": {
|
||||
"finish": "Finalizați",
|
||||
"intro": "Dispozitivele și serviciile sunt reprezentate în Home Assistant ca integrări. Aveți posibilitatea să le configurați acum sau să le faceți mai târziu din ecranul de configurare.",
|
||||
"more_integrations": "Mai Mult"
|
||||
},
|
||||
"intro": "Sunteți gata să vă treziți casa, să vă recuperați intimitatea și să vă alăturați unei comunități mondiale de creatori?",
|
||||
"next": "Următorul",
|
||||
"restore": {
|
||||
"hide_log": "Ascundeți jurnalul complet",
|
||||
"in_progress": "Restaurare în curs",
|
||||
|
||||
@@ -444,7 +444,7 @@
|
||||
},
|
||||
"supervisor": {
|
||||
"beta_backup": "Перед активацией этой функции убедитесь, что у Вас есть резервная копия Ваших данных.",
|
||||
"beta_join_confirm": "Перейти на бета-версии?",
|
||||
"beta_join_confirm": "Вы уверены, что хотите перейти на бета-версии?",
|
||||
"beta_release_items": "Канал обновлений включает в себя бета-версии для:",
|
||||
"beta_warning": "Бета-версии предназначены для тестирования и могут содержать нестабильные изменения кода.",
|
||||
"channel": "Канал обновлений",
|
||||
@@ -453,7 +453,7 @@
|
||||
"failed_to_set_option": "Не удалось настроить параметр Supervisor.",
|
||||
"failed_to_update": "Не удалось обновить Supervisor.",
|
||||
"join_beta_action": "Перейти на бета",
|
||||
"join_beta_description": "Получать тестовые версии обновлений для Home Assistant, Supervisor и операционной системы хоста",
|
||||
"join_beta_description": "Получать тестовые версии обновлений для Home Assistant (RC), Supervisor и операционной системы хоста",
|
||||
"leave_beta_action": "Покинуть бета",
|
||||
"leave_beta_description": "Получать стабильные версии обновлений для Home Assistant, Supervisor и операционной системы хоста",
|
||||
"ram_usage": "Использование ОЗУ",
|
||||
@@ -1237,7 +1237,7 @@
|
||||
"title": "Уведомления"
|
||||
},
|
||||
"notification_toast": {
|
||||
"connection_lost": "Соединение потеряно. Повторное подключение ...",
|
||||
"connection_lost": "Соединение потеряно. Повторное подключение...",
|
||||
"dismiss": "Закрыть",
|
||||
"intergration_starting": "Запуск {integration}, пока что не всё может быть доступно.",
|
||||
"service_call_failed": "Не удалось вызвать службу {service}.",
|
||||
@@ -1751,7 +1751,7 @@
|
||||
"disable_hook_error_msg": "Не удалось отключить Webhook",
|
||||
"info": "Всему, что настроено на срабатывание через Webhook, может быть предоставлен общедоступный URL-адрес. Это позволяет отправлять данные в Home Assistant откуда угодно, не выставляя свой сервер в Интернете.",
|
||||
"link_learn_more": "Узнайте больше о создании автоматизации на базе Webhook.",
|
||||
"loading": "Загрузка ...",
|
||||
"loading": "Загрузка...",
|
||||
"manage": "Управление",
|
||||
"no_hooks_yet": "У Вас еще нет добавленных Webhook. Начните с настройки ",
|
||||
"no_hooks_yet_link_automation": "Webhook автоматизацию",
|
||||
@@ -2156,8 +2156,11 @@
|
||||
"caption": "Интеграции",
|
||||
"config_entry": {
|
||||
"area": "Помещение: {area}",
|
||||
"check_the_logs": "Проверить журналы",
|
||||
"configure": "Настроить",
|
||||
"delete": "Удалить",
|
||||
"delete_confirm": "Вы уверены, что хотите удалить эту интеграцию?",
|
||||
"depends_on_cloud": "Зависящие от облачных сервисов",
|
||||
"device_unavailable": "Устройство недоступно",
|
||||
"devices": "{count, plural,\n one {устройств:}\n other {устройств:}\n} {count}",
|
||||
"disable_restart_confirm": "Перезапустите Home Assistant, чтобы завершить деактивацию этой интеграции.",
|
||||
@@ -2180,14 +2183,23 @@
|
||||
"logs": "журналы",
|
||||
"manuf": "{manufacturer}",
|
||||
"no_area": "Не указано",
|
||||
"not_loaded": "Не загружено, проверьте {logs_link}",
|
||||
"not_loaded": "Не загружено",
|
||||
"options": "Настройки",
|
||||
"provided_by_custom_integration": "Предоставляется кастомной интеграцией",
|
||||
"reload": "Перезагрузить",
|
||||
"reload_confirm": "Перезагрузка интеграции выполнена",
|
||||
"reload_restart_confirm": "Перезапустите Home Assistant, чтобы завершить перезагрузку этой интеграции",
|
||||
"rename": "Переименовать",
|
||||
"restart_confirm": "Перезапустите Home Assistant, чтобы завершить удаление этой интеграции",
|
||||
"services": "{count, plural,\n one {служб:}\n other {служб:}\n} {count}",
|
||||
"state": {
|
||||
"failed_unload": "Не удалось выгрузить",
|
||||
"loaded": "Загружено",
|
||||
"migration_error": "Ошибка при миграции",
|
||||
"not_loaded": "Не загружено",
|
||||
"setup_error": "Не удалось настроить",
|
||||
"setup_retry": "Повторная настройка"
|
||||
},
|
||||
"system_options": "Настройки интеграции",
|
||||
"unnamed_entry": "Без названия"
|
||||
},
|
||||
@@ -2775,6 +2787,15 @@
|
||||
"manufacturer_code_override": "Переназначить код производителя",
|
||||
"value": "Значение"
|
||||
},
|
||||
"configuration_page": {
|
||||
"shortcuts_title": "Ярлыки",
|
||||
"update_button": "Обновить конфигурацию",
|
||||
"zha_options": {
|
||||
"default_light_transition": "Время плавного перехода света по умолчанию (в секундах)",
|
||||
"enable_identify_on_join": "Эффект для идентификации присоединения устройства к сети",
|
||||
"title": "Глобальные настройки"
|
||||
}
|
||||
},
|
||||
"device_pairing_card": {
|
||||
"CONFIGURED": "Настройка завершена",
|
||||
"CONFIGURED_status_text": "Инициализация",
|
||||
@@ -3046,7 +3067,7 @@
|
||||
"fire_event": "Создать событие",
|
||||
"listen_to_events": "Подписаться на событие",
|
||||
"listening_to": "Подписано на",
|
||||
"notification_event_fired": "Событие {type} успешно создано",
|
||||
"notification_event_fired": "Событие {type} создано успешно",
|
||||
"start_listening": "Подписаться",
|
||||
"stop_listening": "Отписаться",
|
||||
"subscribe_to": "Событие",
|
||||
@@ -3073,8 +3094,8 @@
|
||||
"attributes": "Атрибуты",
|
||||
"copy_id": "Скопировать ID в буфер обмена",
|
||||
"current_entities": "Список объектов",
|
||||
"description1": "Здесь Вы можете вручную изменить состояние устройства в Home Assistant.",
|
||||
"description2": "Изменённое состояние не будет синхронизировано с устройством.",
|
||||
"description1": "Здесь Вы можете вручную изменить отображение текущего состояния объекта в Home Assistant.",
|
||||
"description2": "Если этот объект связан с устройством, фактической связи с этим устройством не будет.",
|
||||
"entity": "Объект",
|
||||
"filter_attributes": "Поиск",
|
||||
"filter_entities": "Поиск",
|
||||
|
||||
@@ -1594,7 +1594,7 @@
|
||||
"dev_automation": "Hata ayıklama otomasyonu",
|
||||
"dev_only_editable": "Yalnızca automations.yaml'de tanımlanan otomasyonlarda hata ayıklama yapılabilir.",
|
||||
"duplicate": "Çiftleme",
|
||||
"duplicate_automation": "Yinelenen otomasyon",
|
||||
"duplicate_automation": "Otomasyonu çiftleme",
|
||||
"edit_automation": "Otomasyonu düzenle",
|
||||
"header": "Otomasyon Düzenleyici",
|
||||
"headers": {
|
||||
@@ -2136,6 +2136,7 @@
|
||||
"caption": "Entegrasyonlar",
|
||||
"config_entry": {
|
||||
"area": "{area} içinde",
|
||||
"check_the_logs": "Günlükleri kontrol edin",
|
||||
"delete": "Sil",
|
||||
"delete_confirm": "Bu entegrasyonu silmek istediğinizden emin misiniz?",
|
||||
"device_unavailable": "Cihaz kullanılamıyor",
|
||||
@@ -2168,6 +2169,13 @@
|
||||
"rename": "Yeniden adlandır",
|
||||
"restart_confirm": "Bu entegrasyonu kaldırmaya devam etmek için Home Assistant'ı yeniden başlatın",
|
||||
"services": "{count} {count, plural,\n one {hizmet}\n other {hizmetler}\n}",
|
||||
"state": {
|
||||
"failed_unload": "Kaldırılamadı",
|
||||
"loaded": "Yüklendi",
|
||||
"migration_error": "Taşıma hatası",
|
||||
"not_loaded": "Yüklenmedi",
|
||||
"setup_error": "Kurulum başarısız oldu"
|
||||
},
|
||||
"system_options": "Sistem seçenekleri",
|
||||
"unnamed_entry": "Adsız giriş"
|
||||
},
|
||||
|
||||
@@ -2156,8 +2156,11 @@
|
||||
"caption": "集成",
|
||||
"config_entry": {
|
||||
"area": "位于:{area}",
|
||||
"check_the_logs": "检查日志",
|
||||
"configure": "选项",
|
||||
"delete": "删除",
|
||||
"delete_confirm": "您确定要删除此集成吗?",
|
||||
"depends_on_cloud": "取决于云端",
|
||||
"device_unavailable": "设备不可用",
|
||||
"devices": "{count} {count, plural,\n one {个设备}\n other {个设备}\n}",
|
||||
"disable_restart_confirm": "重启 Home Assistant 以完成此集成的禁用",
|
||||
@@ -2180,14 +2183,23 @@
|
||||
"logs": "日志",
|
||||
"manuf": "by {manufacturer}",
|
||||
"no_area": "没有区域",
|
||||
"not_loaded": "未加载,请检查{logs_link}",
|
||||
"not_loaded": "未加载",
|
||||
"options": "选项",
|
||||
"provided_by_custom_integration": "由自定义集成提供",
|
||||
"reload": "重载",
|
||||
"reload_confirm": "集成已重新加载",
|
||||
"reload_restart_confirm": "重启 Home Assistant 以完成此集成的重载",
|
||||
"rename": "重命名",
|
||||
"restart_confirm": "重启 Home Assistant 以完成此集成的删除",
|
||||
"services": "{count} {count, plural,\n one {个服务}\n other {个服务}\n}",
|
||||
"state": {
|
||||
"failed_unload": "卸载失败",
|
||||
"loaded": "已加载",
|
||||
"migration_error": "迁移错误",
|
||||
"not_loaded": "未加载",
|
||||
"setup_error": "设置失败",
|
||||
"setup_retry": "正在重试设置"
|
||||
},
|
||||
"system_options": "系统选项",
|
||||
"unnamed_entry": "未命名条目"
|
||||
},
|
||||
@@ -2255,8 +2267,10 @@
|
||||
"logs": {
|
||||
"caption": "日志",
|
||||
"clear": "清除",
|
||||
"custom_integration": "自定义集成",
|
||||
"description": "查看 Home Assistant 日志",
|
||||
"details": "日志详细信息( {level} )",
|
||||
"error_from_custom_integration": "此错误来自自定义集成。",
|
||||
"level": {
|
||||
"critical": "CRITICAL",
|
||||
"debug": "DEBUG",
|
||||
@@ -2773,6 +2787,15 @@
|
||||
"manufacturer_code_override": "制造商代码覆盖",
|
||||
"value": "值"
|
||||
},
|
||||
"configuration_page": {
|
||||
"shortcuts_title": "捷径",
|
||||
"update_button": "更新配置",
|
||||
"zha_options": {
|
||||
"default_light_transition": "默认灯光过渡时长(秒)",
|
||||
"enable_identify_on_join": "设备加入网络时启用识别效果",
|
||||
"title": "全局选项"
|
||||
}
|
||||
},
|
||||
"device_pairing_card": {
|
||||
"CONFIGURED": "配置完成",
|
||||
"CONFIGURED_status_text": "正在初始化",
|
||||
|
||||
@@ -242,7 +242,7 @@
|
||||
"ram_usage": "附加元件 RAM 使用率",
|
||||
"rebuild": "重建",
|
||||
"restart": "重啟",
|
||||
"start": "開始",
|
||||
"start": "啟動",
|
||||
"stop": "停止",
|
||||
"uninstall": "移除",
|
||||
"visit_addon_page": "參閱 {name} 頁面以獲得更詳細資訊"
|
||||
@@ -2156,8 +2156,11 @@
|
||||
"caption": "整合",
|
||||
"config_entry": {
|
||||
"area": "於 {area}",
|
||||
"check_the_logs": "檢查日誌",
|
||||
"configure": "設定",
|
||||
"delete": "刪除",
|
||||
"delete_confirm": "確定要刪除此整合?",
|
||||
"depends_on_cloud": "跟隨雲服務",
|
||||
"device_unavailable": "裝置不可用",
|
||||
"devices": "{count} {count, plural,\n one {個裝置}\n other {個裝置}\n}",
|
||||
"disable_restart_confirm": "重啟 Home Assistant 以完成整合關閉",
|
||||
@@ -2180,14 +2183,23 @@
|
||||
"logs": "日誌",
|
||||
"manuf": "廠牌:{manufacturer}",
|
||||
"no_area": "無分區",
|
||||
"not_loaded": "未載入,請檢查 {logs_link}",
|
||||
"not_loaded": "未載入",
|
||||
"options": "選項",
|
||||
"provided_by_custom_integration": "由自訂整合提供",
|
||||
"reload": "重新載入",
|
||||
"reload_confirm": "整合已重新載入",
|
||||
"reload_restart_confirm": "重啟 Home Assistant 以完成整合重新載入",
|
||||
"rename": "重新命名",
|
||||
"restart_confirm": "重啟 Home Assistant 以完成此整合移動",
|
||||
"services": "{count} {count, plural,\n one {項服務}\n other {項服務}\n}",
|
||||
"state": {
|
||||
"failed_unload": "卸載失敗",
|
||||
"loaded": "已載入",
|
||||
"migration_error": "遷移錯誤",
|
||||
"not_loaded": "未載入",
|
||||
"setup_error": "設定失敗",
|
||||
"setup_retry": "重新設定中"
|
||||
},
|
||||
"system_options": "系統選項",
|
||||
"unnamed_entry": "未命名實體"
|
||||
},
|
||||
@@ -2255,8 +2267,10 @@
|
||||
"logs": {
|
||||
"caption": "記錄",
|
||||
"clear": "清除",
|
||||
"custom_integration": "自訂整合",
|
||||
"description": "檢視 Home Assistant 日誌",
|
||||
"details": "記錄詳細資料({level})",
|
||||
"error_from_custom_integration": "自訂整合產生錯誤。",
|
||||
"level": {
|
||||
"critical": "緊急",
|
||||
"debug": "除錯",
|
||||
|
||||
@@ -1918,9 +1918,9 @@
|
||||
lezer-tree "^0.13.0"
|
||||
|
||||
"@codemirror/gutter@^0.18.0":
|
||||
version "0.18.0"
|
||||
resolved "https://registry.yarnpkg.com/@codemirror/gutter/-/gutter-0.18.0.tgz#b6fb340f7cc7b4ed1a67687e145489b3ed93098d"
|
||||
integrity sha512-9hcKzBM5EjhWwrau5Xiv0ll/yOvkgiyLnH7DTsjFCUvuyfbS45WVEMhQ6C+HfsoRVR4TJqRVLJjaIktZqaAqnw==
|
||||
version "0.18.1"
|
||||
resolved "https://registry.yarnpkg.com/@codemirror/gutter/-/gutter-0.18.1.tgz#65657be98d8c7183d23c03038a32df22dd631cc2"
|
||||
integrity sha512-OJXT3giUPtMOLKmr3hHoLekEUHjLoGFA+1fIQUw7/39t4UZJr3S/1EQGI3NEPhyCg4+NCEHZJWS33x6dz5oOiA==
|
||||
dependencies:
|
||||
"@codemirror/rangeset" "^0.18.0"
|
||||
"@codemirror/state" "^0.18.0"
|
||||
@@ -2011,16 +2011,16 @@
|
||||
crelt "^1.0.5"
|
||||
|
||||
"@codemirror/state@^0.18.0", "@codemirror/state@^0.18.3":
|
||||
version "0.18.5"
|
||||
resolved "https://registry.yarnpkg.com/@codemirror/state/-/state-0.18.5.tgz#db936f80c40f329fb803bd284cbb5aa556dafb88"
|
||||
integrity sha512-lHR+yE08jEz7MqA5hgNvK4/ksF2mQsJJ/pedKKfB94CUobMX20tsFQ27lZbXCxZDQcz5lO0AZuFuRqrbDlRtKA==
|
||||
version "0.18.6"
|
||||
resolved "https://registry.yarnpkg.com/@codemirror/state/-/state-0.18.6.tgz#46f4cabd7e635dd0f229d0ff1136d79b071354f8"
|
||||
integrity sha512-jBY4KFY6RGPkuRUFXSZtgxpKebju8CJq7SkKYf+NsD8OZzDSauxPPYAL7V2z8ubvw74qLPIKIX2hERvY6WBdbg==
|
||||
dependencies:
|
||||
"@codemirror/text" "^0.18.0"
|
||||
|
||||
"@codemirror/stream-parser@^0.18.0":
|
||||
version "0.18.1"
|
||||
resolved "https://registry.yarnpkg.com/@codemirror/stream-parser/-/stream-parser-0.18.1.tgz#20248b79d0fb9243a1431437dba79112c025058e"
|
||||
integrity sha512-Q7HXbZRbAg5SboM0/3Hw9bKX7UxRWVsrtFjeQXzti2be/VHfMUAykidqWwWHe1SSn3Me3izpw9vLNEoGCm7tBw==
|
||||
version "0.18.2"
|
||||
resolved "https://registry.yarnpkg.com/@codemirror/stream-parser/-/stream-parser-0.18.2.tgz#d96ac5724650719c4a7784f2b94449366b22130e"
|
||||
integrity sha512-3RTRmhIixcC2ps/G8So+BL0qJkwaspjyYt4smVYlSn4eNbxGK9K2RCnSmOPRv0SkuQMu3oUFbprFI/SbtZrPKg==
|
||||
dependencies:
|
||||
"@codemirror/highlight" "^0.18.0"
|
||||
"@codemirror/language" "^0.18.0"
|
||||
@@ -2035,9 +2035,9 @@
|
||||
integrity sha512-HMzHNIAbjCiCf3tEJMRg6ul01KPuXxQGNiHlHgAnqPguq/CX+L4Nvj5JlWQAI91Pupk18zhmM1c6eaazX4YeTg==
|
||||
|
||||
"@codemirror/view@^0.18.0":
|
||||
version "0.18.6"
|
||||
resolved "https://registry.yarnpkg.com/@codemirror/view/-/view-0.18.6.tgz#3598e72658e37b30e3260e4e623a81599b67a9a0"
|
||||
integrity sha512-j0TtJbV+41g/0eGH7Pgx9wtO7Y3Rg0s9shLFGvUtJ4jMIimkCelQsEBtUmfEbNxAVXOsN+CbmsKg8M9p0ISeCA==
|
||||
version "0.18.8"
|
||||
resolved "https://registry.yarnpkg.com/@codemirror/view/-/view-0.18.8.tgz#e8e6b26adf427ce65356b9f1020876d7947d7953"
|
||||
integrity sha512-vzP8oUBiLMbl5OCWUMGQdYtonk0tt9eUzi/xEDpYmo8Ao48/49fxPQUqBpUwy5Rcce9kUnkTi6r44iJKsEHpmg==
|
||||
dependencies:
|
||||
"@codemirror/rangeset" "^0.18.0"
|
||||
"@codemirror/state" "^0.18.0"
|
||||
@@ -3649,11 +3649,6 @@
|
||||
resolved "https://registry.yarnpkg.com/@types/har-format/-/har-format-1.2.4.tgz#3275842095abb60d14b47fa798cc9ff708dab6d4"
|
||||
integrity sha512-iUxzm1meBm3stxUMzRqgOVHjj4Kgpgu5w9fm4X7kPRfSgVRzythsucEN7/jtOo8SQzm+HfcxWWzJS0mJDH/3DQ==
|
||||
|
||||
"@types/hls.js@^0.12.3":
|
||||
version "0.12.3"
|
||||
resolved "https://registry.yarnpkg.com/@types/hls.js/-/hls.js-0.12.3.tgz#09d5d1dbcd78d7dd46deff6db02bd2af9721a4cf"
|
||||
integrity sha512-1QbxVTp7v9bn8MjvbMXV4YbMntC9Dv8v9bI9LgWxfg9Mgib0/jI2XITUEKGgG5Cde48ktyA1UHTSWld7QKKNnQ==
|
||||
|
||||
"@types/http-assert@*":
|
||||
version "1.5.1"
|
||||
resolved "https://registry.yarnpkg.com/@types/http-assert/-/http-assert-1.5.1.tgz#d775e93630c2469c2f980fc27e3143240335db3b"
|
||||
@@ -7182,11 +7177,6 @@ event-target-shim@^5.0.1:
|
||||
resolved "https://registry.yarnpkg.com/event-target-shim/-/event-target-shim-5.0.1.tgz#5d4d3ebdf9583d63a5333ce2deb7480ab2b05789"
|
||||
integrity sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==
|
||||
|
||||
[email protected]:
|
||||
version "3.1.0"
|
||||
resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-3.1.0.tgz#090b4d6cdbd645ed10bf750d4b5407942d7ba163"
|
||||
integrity sha512-ivIvhpq/Y0uSjcHDcOIccjmYjGLcP09MFGE7ysAwkAvkXfpZlC985pH2/ui64DKazbTW/4kN3yqozUxlXzI6cA==
|
||||
|
||||
eventemitter3@^4.0.0:
|
||||
version "4.0.7"
|
||||
resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.7.tgz#2de9b68f6528d5644ef5c59526a1b4a07306169f"
|
||||
@@ -8323,13 +8313,10 @@ [email protected], he@^1.2.0:
|
||||
resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f"
|
||||
integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==
|
||||
|
||||
hls.js@^0.13.2:
|
||||
version "0.13.2"
|
||||
resolved "https://registry.yarnpkg.com/hls.js/-/hls.js-0.13.2.tgz#3e7dd28e3787c69c6aba42b64b11eb2c3c8c29f1"
|
||||
integrity sha512-sIg2t4uGpWQLzuK1Iid9614WOKqxj4OYg+EbFbhhTDCsxpENBN+Du3yBFnoi+a83DuOOHdiQd1ydnti9loSGXw==
|
||||
dependencies:
|
||||
eventemitter3 "3.1.0"
|
||||
url-toolkit "^2.1.6"
|
||||
hls.js@^1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/hls.js/-/hls.js-1.0.1.tgz#d92bd0a9c78760f0f0e90d53c60192800ebc629f"
|
||||
integrity sha512-ElPUW9VMY2uXdX07N872BSrVUcVd4jZGav4nqlY3vinpdMpW8esmdpUaeVA5vv3oIRSmiC/XtL9K+mSt6rlBpA==
|
||||
|
||||
hmac-drbg@^1.0.1:
|
||||
version "1.0.1"
|
||||
@@ -9438,14 +9425,7 @@ lezer-tree@^0.13.0, lezer-tree@^0.13.2:
|
||||
resolved "https://registry.yarnpkg.com/lezer-tree/-/lezer-tree-0.13.2.tgz#00f4671309b15c27b131f637e430ce2d4d5f7065"
|
||||
integrity sha512-15ZxW8TxVNAOkHIo43Iouv4zbSkQQ5chQHBpwXcD2bBFz46RB4jYLEEww5l1V0xyIx9U2clSyyrLes+hAUFrGQ==
|
||||
|
||||
lezer@^0.13.0:
|
||||
version "0.13.3"
|
||||
resolved "https://registry.yarnpkg.com/lezer/-/lezer-0.13.3.tgz#520033a8f8be32872af1030e99ede6d9d3c6c023"
|
||||
integrity sha512-DKYaqt52qx9wjxk+q+CqMMn5InqdwLrSCMqtNs+zYkl/VoTUgU8/BdmB6w/b/u7L5FCwdNybVvDS5t+1AvsD5g==
|
||||
dependencies:
|
||||
lezer-tree "^0.13.2"
|
||||
|
||||
lezer@^0.13.4:
|
||||
lezer@^0.13.0, lezer@^0.13.4:
|
||||
version "0.13.4"
|
||||
resolved "https://registry.yarnpkg.com/lezer/-/lezer-0.13.4.tgz#f0396a3447c7a8f40391623f3f47a4d95559c42f"
|
||||
integrity sha512-cLQxUVY28VBBqKBt/R8CYeH57KQnIvscAnoahzvhlZTK8qxMkIyGExR6ecEpYYDX06ZhROZrEm1IiPvjLAsTig==
|
||||
@@ -13642,11 +13622,6 @@ url-parse@^1.4.7:
|
||||
querystringify "^2.1.1"
|
||||
requires-port "^1.0.0"
|
||||
|
||||
url-toolkit@^2.1.6:
|
||||
version "2.1.6"
|
||||
resolved "https://registry.yarnpkg.com/url-toolkit/-/url-toolkit-2.1.6.tgz#6d03246499e519aad224c44044a4ae20544154f2"
|
||||
integrity sha512-UaZ2+50am4HwrV2crR/JAf63Q4VvPYphe63WGeoJxeu8gmOm0qxPt+KsukfakPNrX9aymGNEkkaoICwn+OuvBw==
|
||||
|
||||
url@^0.11.0:
|
||||
version "0.11.0"
|
||||
resolved "https://registry.yarnpkg.com/url/-/url-0.11.0.tgz#3838e97cfc60521eb73c525a8e55bfdd9e2e28f1"
|
||||
|
||||
Reference in New Issue
Block a user