mirror of
https://github.com/home-assistant/frontend.git
synced 2025-07-28 19:56:42 +00:00
commit
ebe3198c27
@ -197,13 +197,14 @@ gulp.task(
|
||||
// than a base translation + region.
|
||||
const tr = path.basename(file.history[0], ".json");
|
||||
const subtags = tr.split("-");
|
||||
const src = [
|
||||
workDir + "/translationMaster.json",
|
||||
workDir + "/test.json",
|
||||
];
|
||||
const src = [workDir + "/translationMaster.json"];
|
||||
for (let i = 1; i <= subtags.length; i++) {
|
||||
const lang = subtags.slice(0, i).join("-");
|
||||
src.push(inDir + "/" + lang + ".json");
|
||||
if (lang === "test") {
|
||||
src.push(workDir + "/test.json");
|
||||
} else {
|
||||
src.push(inDir + "/" + lang + ".json");
|
||||
}
|
||||
}
|
||||
return gulp
|
||||
.src(src, { allowEmpty: true })
|
||||
|
2
setup.py
2
setup.py
@ -2,7 +2,7 @@ from setuptools import setup, find_packages
|
||||
|
||||
setup(
|
||||
name="home-assistant-frontend",
|
||||
version="20190523.0",
|
||||
version="20190529.0",
|
||||
description="The Home Assistant frontend",
|
||||
url="https://github.com/home-assistant/home-assistant-polymer",
|
||||
author="The Home Assistant Authors",
|
||||
|
@ -2,6 +2,20 @@ import computeDomain from "./compute_domain";
|
||||
|
||||
export type FilterFunc = (entityId: string) => boolean;
|
||||
|
||||
export interface EntityFilter {
|
||||
include_domains: string[];
|
||||
include_entities: string[];
|
||||
exclude_domains: string[];
|
||||
exclude_entities: string[];
|
||||
}
|
||||
|
||||
export const isEmptyFilter = (filter: EntityFilter) =>
|
||||
filter.include_domains.length +
|
||||
filter.include_entities.length +
|
||||
filter.exclude_domains.length +
|
||||
filter.exclude_entities.length ===
|
||||
0;
|
||||
|
||||
export const generateFilter = (
|
||||
includeDomains?: string[],
|
||||
includeEntities?: string[],
|
||||
|
@ -91,13 +91,12 @@ class StateInfo extends PolymerElement {
|
||||
|
||||
static get properties() {
|
||||
return {
|
||||
detailed: {
|
||||
type: Boolean,
|
||||
value: false,
|
||||
},
|
||||
hass: Object,
|
||||
stateObj: Object,
|
||||
inDialog: Boolean,
|
||||
inDialog: {
|
||||
type: Boolean,
|
||||
value: () => false,
|
||||
},
|
||||
rtl: {
|
||||
type: Boolean,
|
||||
reflectToAttribute: true,
|
||||
|
@ -1,27 +1,33 @@
|
||||
import { HomeAssistant } from "../types";
|
||||
import { EntityFilter } from "../common/entity/entity_filter";
|
||||
|
||||
export interface EntityFilter {
|
||||
include_domains: string[];
|
||||
include_entities: string[];
|
||||
exclude_domains: string[];
|
||||
exclude_entities: string[];
|
||||
}
|
||||
interface CloudStatusBase {
|
||||
logged_in: boolean;
|
||||
cloud: "disconnected" | "connecting" | "connected";
|
||||
}
|
||||
|
||||
export interface GoogleEntityConfig {
|
||||
should_expose?: boolean;
|
||||
override_name?: string;
|
||||
aliases?: string[];
|
||||
disable_2fa?: boolean;
|
||||
}
|
||||
|
||||
export interface CertificateInformation {
|
||||
common_name: string;
|
||||
expire_date: string;
|
||||
fingerprint: string;
|
||||
}
|
||||
|
||||
interface CloudPreferences {
|
||||
export interface CloudPreferences {
|
||||
google_enabled: boolean;
|
||||
alexa_enabled: boolean;
|
||||
remote_enabled: boolean;
|
||||
google_secure_devices_pin: string | undefined;
|
||||
cloudhooks: { [webhookId: string]: CloudWebhook };
|
||||
google_entity_configs: {
|
||||
[entityId: string]: GoogleEntityConfig;
|
||||
};
|
||||
}
|
||||
|
||||
export type CloudStatusLoggedIn = CloudStatusBase & {
|
||||
@ -49,6 +55,12 @@ export interface CloudWebhook {
|
||||
managed?: boolean;
|
||||
}
|
||||
|
||||
export interface GoogleEntity {
|
||||
entity_id: string;
|
||||
traits: string[];
|
||||
might_2fa: boolean;
|
||||
}
|
||||
|
||||
export const fetchCloudStatus = (hass: HomeAssistant) =>
|
||||
hass.callWS<CloudStatus>({ type: "cloud/status" });
|
||||
|
||||
@ -89,3 +101,20 @@ export const updateCloudPref = (
|
||||
type: "cloud/update_prefs",
|
||||
...prefs,
|
||||
});
|
||||
|
||||
export const fetchCloudGoogleEntities = (hass: HomeAssistant) =>
|
||||
hass.callWS<GoogleEntity[]>({ type: "cloud/google_assistant/entities" });
|
||||
|
||||
export const updateCloudGoogleEntityConfig = (
|
||||
hass: HomeAssistant,
|
||||
entityId: string,
|
||||
values: GoogleEntityConfig
|
||||
) =>
|
||||
hass.callWS<GoogleEntityConfig>({
|
||||
type: "cloud/google_assistant/entities/update",
|
||||
entity_id: entityId,
|
||||
...values,
|
||||
});
|
||||
|
||||
export const cloudSyncGoogleAssistant = (hass: HomeAssistant) =>
|
||||
hass.callApi("POST", "cloud/google_actions/sync");
|
||||
|
@ -16,8 +16,8 @@ export interface DeviceRegistryEntry {
|
||||
}
|
||||
|
||||
export interface DeviceRegistryEntryMutableParams {
|
||||
area_id?: string;
|
||||
name_by_user?: string;
|
||||
area_id?: string | null;
|
||||
name_by_user?: string | null;
|
||||
}
|
||||
|
||||
export const updateDeviceRegistryEntry = (
|
||||
|
@ -1,4 +1,5 @@
|
||||
import { HomeAssistant } from "../types";
|
||||
import { Connection } from "home-assistant-js-websocket";
|
||||
|
||||
export interface LovelaceConfig {
|
||||
title?: string;
|
||||
@ -76,3 +77,8 @@ export const saveConfig = (
|
||||
type: "lovelace/config/save",
|
||||
config,
|
||||
});
|
||||
|
||||
export const subscribeLovelaceUpdates = (
|
||||
conn: Connection,
|
||||
onChange: () => void
|
||||
) => conn.subscribeEvents(onChange, "lovelace_updated");
|
||||
|
@ -34,6 +34,9 @@ export interface RouterOptions {
|
||||
showLoading?: boolean;
|
||||
// Promise that resolves when the initial data is loaded which is needed to show any route.
|
||||
initialLoad?: () => Promise<unknown>;
|
||||
// Hook that is called before rendering a new route. Allowing redirects.
|
||||
// If string returned, that page will be rendered instead.
|
||||
beforeRender?: (page: string) => string | undefined;
|
||||
routes: {
|
||||
// If it's a string, it is another route whose options should be adopted.
|
||||
[route: string]: RouteOptions | string;
|
||||
@ -48,7 +51,7 @@ export class HassRouterPage extends UpdatingElement {
|
||||
|
||||
protected routerOptions!: RouterOptions;
|
||||
|
||||
private _currentPage = "";
|
||||
protected _currentPage = "";
|
||||
private _currentLoadProm?: Promise<void>;
|
||||
private _cache = {};
|
||||
private _initialLoadDone = false;
|
||||
@ -101,6 +104,25 @@ export class HassRouterPage extends UpdatingElement {
|
||||
routeOptions = routerOptions.routes[newPage];
|
||||
}
|
||||
|
||||
if (routerOptions.beforeRender) {
|
||||
const result = routerOptions.beforeRender(newPage);
|
||||
if (result !== undefined) {
|
||||
newPage = result;
|
||||
routeOptions = routerOptions.routes[newPage];
|
||||
|
||||
// Handle redirects
|
||||
while (typeof routeOptions === "string") {
|
||||
newPage = routeOptions;
|
||||
routeOptions = routerOptions.routes[newPage];
|
||||
}
|
||||
|
||||
// Update the url if we know where we're mounted.
|
||||
if (route) {
|
||||
navigate(this, `${route.prefix}/${result}`, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (this._currentPage === newPage) {
|
||||
if (this.lastChild) {
|
||||
this.updatePageEl(this.lastChild, changedProps);
|
||||
@ -245,6 +267,10 @@ export class HassRouterPage extends UpdatingElement {
|
||||
return this.updateComplete.then(() => this._currentLoadProm);
|
||||
}
|
||||
|
||||
protected createElement(tag: string) {
|
||||
return document.createElement(tag);
|
||||
}
|
||||
|
||||
protected updatePageEl(_pageEl, _changedProps?: PropertyValues) {
|
||||
// default we do nothing
|
||||
}
|
||||
@ -262,8 +288,7 @@ export class HassRouterPage extends UpdatingElement {
|
||||
this.removeChild(this.lastChild);
|
||||
}
|
||||
|
||||
const panelEl =
|
||||
this._cache[page] || document.createElement(routeOptions.tag);
|
||||
const panelEl = this._cache[page] || this.createElement(routeOptions.tag);
|
||||
this.updatePageEl(panelEl);
|
||||
this.appendChild(panelEl);
|
||||
|
||||
|
@ -1,30 +1,95 @@
|
||||
import {
|
||||
LitElement,
|
||||
query,
|
||||
property,
|
||||
TemplateResult,
|
||||
html,
|
||||
css,
|
||||
CSSResult,
|
||||
} from "lit-element";
|
||||
import { computeRTL } from "../common/util/compute_rtl";
|
||||
import "../components/ha-toast";
|
||||
import { LitElement, query, property, TemplateResult, html } from "lit-element";
|
||||
import { HomeAssistant } from "../types";
|
||||
import "@material/mwc-button";
|
||||
import "../components/ha-toast";
|
||||
// Typing
|
||||
// tslint:disable-next-line: no-duplicate-imports
|
||||
import { HaToast } from "../components/ha-toast";
|
||||
|
||||
export interface ShowToastParams {
|
||||
message: string;
|
||||
action?: ToastActionParams;
|
||||
duration?: number;
|
||||
dismissable?: boolean;
|
||||
}
|
||||
|
||||
export interface ToastActionParams {
|
||||
action: () => void;
|
||||
text: string;
|
||||
}
|
||||
|
||||
class NotificationManager extends LitElement {
|
||||
@property() public hass!: HomeAssistant;
|
||||
|
||||
@property() private _action?: ToastActionParams;
|
||||
@property() private _noCancelOnOutsideClick: boolean = false;
|
||||
|
||||
@query("ha-toast") private _toast!: HaToast;
|
||||
|
||||
public showDialog({ message }: ShowToastParams) {
|
||||
public showDialog({
|
||||
message,
|
||||
action,
|
||||
duration,
|
||||
dismissable,
|
||||
}: ShowToastParams) {
|
||||
const toast = this._toast;
|
||||
toast.setAttribute("dir", computeRTL(this.hass) ? "rtl" : "ltr");
|
||||
toast.show(message);
|
||||
this._action = action || undefined;
|
||||
this._noCancelOnOutsideClick =
|
||||
dismissable === undefined ? false : !dismissable;
|
||||
toast.hide();
|
||||
toast.show({
|
||||
text: message,
|
||||
duration: duration === undefined ? 3000 : duration,
|
||||
});
|
||||
}
|
||||
|
||||
protected render(): TemplateResult | void {
|
||||
return html`
|
||||
<ha-toast dir="[[_rtl]]" noCancelOnOutsideClick=${false}></ha-toast>
|
||||
<ha-toast .noCancelOnOutsideClick=${this._noCancelOnOutsideClick}>
|
||||
${this._action
|
||||
? html`
|
||||
<mwc-button
|
||||
.label=${this._action.text}
|
||||
@click=${this.buttonClicked}
|
||||
></mwc-button>
|
||||
`
|
||||
: ""}
|
||||
</ha-toast>
|
||||
`;
|
||||
}
|
||||
|
||||
private buttonClicked() {
|
||||
this._toast.hide();
|
||||
if (this._action) {
|
||||
this._action.action();
|
||||
}
|
||||
}
|
||||
|
||||
static get styles(): CSSResult {
|
||||
return css`
|
||||
mwc-button {
|
||||
color: var(--primary-color);
|
||||
font-weight: bold;
|
||||
}
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define("notification-manager", NotificationManager);
|
||||
|
||||
declare global {
|
||||
// for fire event
|
||||
interface HASSDomEvents {
|
||||
"hass-notification": ShowToastParams;
|
||||
}
|
||||
}
|
||||
|
@ -16,8 +16,8 @@ import computeStateName from "../../../common/entity/compute_state_name";
|
||||
import {
|
||||
FilterFunc,
|
||||
generateFilter,
|
||||
EntityFilter,
|
||||
} from "../../../common/entity/entity_filter";
|
||||
import { EntityFilter } from "../../../data/cloud";
|
||||
|
||||
export class CloudExposedEntities extends LitElement {
|
||||
public hass?: HomeAssistant;
|
||||
|
@ -16,7 +16,6 @@ import "../../../components/ha-card";
|
||||
|
||||
import { fireEvent } from "../../../common/dom/fire_event";
|
||||
import { HomeAssistant } from "../../../types";
|
||||
import "./cloud-exposed-entities";
|
||||
import { CloudStatusLoggedIn, updateCloudPref } from "../../../data/cloud";
|
||||
import { PaperInputElement } from "@polymer/paper-input/paper-input";
|
||||
|
||||
@ -89,12 +88,6 @@ export class CloudGooglePref extends LitElement {
|
||||
@change="${this._pinChanged}"
|
||||
></paper-input>
|
||||
</div>
|
||||
<p>Exposed entities:</p>
|
||||
<cloud-exposed-entities
|
||||
.hass="${this.hass}"
|
||||
.filter="${this.cloudStatus!.google_entities}"
|
||||
.supportedDomains="${this.cloudStatus!.google_domains}"
|
||||
></cloud-exposed-entities>
|
||||
`
|
||||
: ""}
|
||||
</div>
|
||||
@ -103,8 +96,12 @@ export class CloudGooglePref extends LitElement {
|
||||
.hass="${this.hass}"
|
||||
.disabled="${!google_enabled}"
|
||||
path="cloud/google_actions/sync"
|
||||
>Sync devices</ha-call-api-button
|
||||
>
|
||||
Sync entities to Google
|
||||
</ha-call-api-button>
|
||||
<a href="/config/cloud/google-assistant">
|
||||
<mwc-button>Manage Entities</mwc-button>
|
||||
</a>
|
||||
</div>
|
||||
</ha-card>
|
||||
`;
|
||||
@ -154,6 +151,12 @@ export class CloudGooglePref extends LitElement {
|
||||
paper-input {
|
||||
width: 200px;
|
||||
}
|
||||
.card-actions a {
|
||||
text-decoration: none;
|
||||
}
|
||||
.card-actions mwc-button {
|
||||
float: right;
|
||||
}
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
275
src/panels/config/cloud/ha-config-cloud-google-assistant.ts
Normal file
275
src/panels/config/cloud/ha-config-cloud-google-assistant.ts
Normal file
@ -0,0 +1,275 @@
|
||||
import {
|
||||
LitElement,
|
||||
TemplateResult,
|
||||
html,
|
||||
CSSResult,
|
||||
css,
|
||||
customElement,
|
||||
property,
|
||||
} from "lit-element";
|
||||
import "@polymer/paper-toggle-button";
|
||||
import "../../../layouts/hass-subpage";
|
||||
import "../../../layouts/hass-loading-screen";
|
||||
import "../../../components/ha-card";
|
||||
import "../../../components/entity/state-info";
|
||||
import { HomeAssistant } from "../../../types";
|
||||
import {
|
||||
GoogleEntity,
|
||||
fetchCloudGoogleEntities,
|
||||
CloudStatusLoggedIn,
|
||||
CloudPreferences,
|
||||
updateCloudGoogleEntityConfig,
|
||||
cloudSyncGoogleAssistant,
|
||||
GoogleEntityConfig,
|
||||
} from "../../../data/cloud";
|
||||
import memoizeOne from "memoize-one";
|
||||
import {
|
||||
generateFilter,
|
||||
isEmptyFilter,
|
||||
EntityFilter,
|
||||
} from "../../../common/entity/entity_filter";
|
||||
import { compare } from "../../../common/string/compare";
|
||||
import computeStateName from "../../../common/entity/compute_state_name";
|
||||
import { fireEvent } from "../../../common/dom/fire_event";
|
||||
import { showToast } from "../../../util/toast";
|
||||
import { PolymerChangedEvent } from "../../../polymer-types";
|
||||
|
||||
@customElement("ha-config-cloud-google-assistant")
|
||||
class CloudGoogleAssistant extends LitElement {
|
||||
@property() public hass!: HomeAssistant;
|
||||
@property() public cloudStatus!: CloudStatusLoggedIn;
|
||||
@property() public isWide!: boolean;
|
||||
@property() private _entities?: GoogleEntity[];
|
||||
@property()
|
||||
private _entityConfigs: CloudPreferences["google_entity_configs"] = {};
|
||||
private _popstateSyncAttached = false;
|
||||
private _popstateReloadStatusAttached = false;
|
||||
|
||||
private _getEntityFilterFunc = memoizeOne((filter: EntityFilter) =>
|
||||
generateFilter(
|
||||
filter.include_domains,
|
||||
filter.include_entities,
|
||||
filter.exclude_domains,
|
||||
filter.exclude_entities
|
||||
)
|
||||
);
|
||||
|
||||
protected render(): TemplateResult | void {
|
||||
if (this._entities === undefined) {
|
||||
return html`
|
||||
<hass-loading-screen></hass-loading-screen>
|
||||
`;
|
||||
}
|
||||
const emptyFilter = true || isEmptyFilter(this.cloudStatus.google_entities);
|
||||
const filterFunc = this._getEntityFilterFunc(
|
||||
this.cloudStatus.google_entities
|
||||
);
|
||||
let selected = 0;
|
||||
const cards = this._entities.map((entity) => {
|
||||
const stateObj = this.hass.states[entity.entity_id];
|
||||
const config = this._entityConfigs[entity.entity_id] || {};
|
||||
const isExposed = emptyFilter
|
||||
? Boolean(config.should_expose)
|
||||
: filterFunc(entity.entity_id);
|
||||
if (isExposed) {
|
||||
selected++;
|
||||
}
|
||||
|
||||
return html`
|
||||
<ha-card>
|
||||
<div class="card-content">
|
||||
<state-info
|
||||
.hass=${this.hass}
|
||||
.stateObj=${stateObj}
|
||||
secondary-line
|
||||
@click=${this._showMoreInfo}
|
||||
>
|
||||
${entity.traits
|
||||
.map((trait) => trait.substr(trait.lastIndexOf(".") + 1))
|
||||
.join(", ")}
|
||||
</state-info>
|
||||
<paper-toggle-button
|
||||
.entityId=${entity.entity_id}
|
||||
.disabled=${!emptyFilter}
|
||||
.checked=${isExposed}
|
||||
@checked-changed=${this._exposeChanged}
|
||||
>
|
||||
Expose to Google Assistant
|
||||
</paper-toggle-button>
|
||||
${entity.might_2fa
|
||||
? html`
|
||||
<paper-toggle-button
|
||||
.entityId=${entity.entity_id}
|
||||
.checked=${Boolean(config.disable_2fa)}
|
||||
@checked-changed=${this._disable2FAChanged}
|
||||
>
|
||||
Disable two factor authentication
|
||||
</paper-toggle-button>
|
||||
`
|
||||
: ""}
|
||||
</div>
|
||||
</ha-card>
|
||||
`;
|
||||
});
|
||||
|
||||
return html`
|
||||
<hass-subpage header="Google Assistant">
|
||||
<span slot="toolbar-icon">${selected} selected</span>
|
||||
${!emptyFilter
|
||||
? html`
|
||||
<div class="banner">
|
||||
Editing which entities are exposed via this UI is disabled
|
||||
because you have configured entity filters in
|
||||
configuration.yaml.
|
||||
</div>
|
||||
`
|
||||
: ""}
|
||||
<div class="content">
|
||||
${cards}
|
||||
</div>
|
||||
</hass-subpage>
|
||||
`;
|
||||
}
|
||||
|
||||
protected firstUpdated(changedProps) {
|
||||
super.firstUpdated(changedProps);
|
||||
this._fetchData();
|
||||
}
|
||||
|
||||
protected updated(changedProps) {
|
||||
super.updated(changedProps);
|
||||
if (changedProps.has("cloudStatus")) {
|
||||
this._entityConfigs = this.cloudStatus.prefs.google_entity_configs;
|
||||
}
|
||||
}
|
||||
|
||||
private async _fetchData() {
|
||||
const entities = await fetchCloudGoogleEntities(this.hass);
|
||||
entities.sort((a, b) => {
|
||||
const stateA = this.hass.states[a.entity_id];
|
||||
const stateB = this.hass.states[b.entity_id];
|
||||
return compare(
|
||||
stateA ? computeStateName(stateA) : a.entity_id,
|
||||
stateB ? computeStateName(stateB) : b.entity_id
|
||||
);
|
||||
});
|
||||
this._entities = entities;
|
||||
}
|
||||
|
||||
private _showMoreInfo(ev) {
|
||||
const entityId = ev.currentTarget.stateObj.entity_id;
|
||||
fireEvent(this, "hass-more-info", { entityId });
|
||||
}
|
||||
|
||||
private async _exposeChanged(ev: PolymerChangedEvent<boolean>) {
|
||||
const entityId = (ev.currentTarget as any).entityId;
|
||||
const newExposed = ev.detail.value;
|
||||
const curExposed = Boolean(
|
||||
(this._entityConfigs[entityId] || {}).should_expose
|
||||
);
|
||||
if (newExposed === curExposed) {
|
||||
return;
|
||||
}
|
||||
await this._updateConfig(entityId, {
|
||||
should_expose: newExposed,
|
||||
});
|
||||
this._ensureEntitySync();
|
||||
}
|
||||
|
||||
private async _disable2FAChanged(ev: PolymerChangedEvent<boolean>) {
|
||||
const entityId = (ev.currentTarget as any).entityId;
|
||||
const newDisable2FA = ev.detail.value;
|
||||
const curDisable2FA = Boolean(
|
||||
(this._entityConfigs[entityId] || {}).disable_2fa
|
||||
);
|
||||
if (newDisable2FA === curDisable2FA) {
|
||||
return;
|
||||
}
|
||||
await this._updateConfig(entityId, {
|
||||
disable_2fa: newDisable2FA,
|
||||
});
|
||||
}
|
||||
|
||||
private async _updateConfig(entityId: string, values: GoogleEntityConfig) {
|
||||
const updatedConfig = await updateCloudGoogleEntityConfig(
|
||||
this.hass,
|
||||
entityId,
|
||||
values
|
||||
);
|
||||
this._entityConfigs = {
|
||||
...this._entityConfigs,
|
||||
[entityId]: updatedConfig,
|
||||
};
|
||||
this._ensureStatusReload();
|
||||
}
|
||||
|
||||
private _ensureStatusReload() {
|
||||
if (this._popstateReloadStatusAttached) {
|
||||
return;
|
||||
}
|
||||
this._popstateReloadStatusAttached = true;
|
||||
// Cache parent because by the time popstate happens,
|
||||
// this element is detached
|
||||
const parent = this.parentElement!;
|
||||
this.addEventListener(
|
||||
"popstate",
|
||||
() => fireEvent(parent, "ha-refresh-cloud-status"),
|
||||
{ once: true }
|
||||
);
|
||||
}
|
||||
|
||||
private _ensureEntitySync() {
|
||||
if (this._popstateSyncAttached) {
|
||||
return;
|
||||
}
|
||||
this._popstateSyncAttached = true;
|
||||
// Cache parent because by the time popstate happens,
|
||||
// this element is detached
|
||||
const parent = this.parentElement!;
|
||||
window.addEventListener(
|
||||
"popstate",
|
||||
() => {
|
||||
showToast(parent, { message: "Synchronizing changes to Google." });
|
||||
cloudSyncGoogleAssistant(this.hass);
|
||||
},
|
||||
{ once: true }
|
||||
);
|
||||
}
|
||||
|
||||
static get styles(): CSSResult {
|
||||
return css`
|
||||
.banner {
|
||||
color: var(--primary-text-color);
|
||||
background-color: var(--card-background-color);
|
||||
padding: 16px 8px;
|
||||
text-align: center;
|
||||
}
|
||||
.content {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
padding: 4px;
|
||||
--paper-toggle-button-label-spacing: 16px;
|
||||
}
|
||||
ha-card {
|
||||
margin: 4px;
|
||||
width: 100%;
|
||||
max-width: 300px;
|
||||
}
|
||||
.card-content {
|
||||
padding-bottom: 12px;
|
||||
}
|
||||
state-info {
|
||||
cursor: pointer;
|
||||
}
|
||||
paper-toggle-button {
|
||||
padding: 8px 0;
|
||||
}
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"ha-config-cloud-google-assistant": CloudGoogleAssistant;
|
||||
}
|
||||
}
|
@ -1,145 +0,0 @@
|
||||
import "@polymer/app-route/app-route";
|
||||
import { timeOut } from "@polymer/polymer/lib/utils/async";
|
||||
import { Debouncer } from "@polymer/polymer/lib/utils/debounce";
|
||||
import { html } from "@polymer/polymer/lib/utils/html-tag";
|
||||
import { PolymerElement } from "@polymer/polymer/polymer-element";
|
||||
|
||||
import "../ha-config-section";
|
||||
import "./ha-config-cloud-account";
|
||||
import "./ha-config-cloud-forgot-password";
|
||||
import "./ha-config-cloud-login";
|
||||
import "./ha-config-cloud-register";
|
||||
import NavigateMixin from "../../../mixins/navigate-mixin";
|
||||
|
||||
const LOGGED_IN_URLS = ["/account"];
|
||||
const NOT_LOGGED_IN_URLS = ["/login", "/register", "/forgot-password"];
|
||||
|
||||
/*
|
||||
* @appliesMixin NavigateMixin
|
||||
*/
|
||||
class HaConfigCloud extends NavigateMixin(PolymerElement) {
|
||||
static get template() {
|
||||
return html`
|
||||
<app-route
|
||||
route="[[route]]"
|
||||
pattern="/:page"
|
||||
data="{{_routeData}}"
|
||||
tail="{{_routeTail}}"
|
||||
></app-route>
|
||||
|
||||
<template
|
||||
is="dom-if"
|
||||
if='[[_equals(_routeData.page, "account")]]'
|
||||
restamp=""
|
||||
>
|
||||
<ha-config-cloud-account
|
||||
hass="[[hass]]"
|
||||
cloud-status="[[cloudStatus]]"
|
||||
is-wide="[[isWide]]"
|
||||
></ha-config-cloud-account>
|
||||
</template>
|
||||
|
||||
<template
|
||||
is="dom-if"
|
||||
if='[[_equals(_routeData.page, "login")]]'
|
||||
restamp=""
|
||||
>
|
||||
<ha-config-cloud-login
|
||||
page-name="login"
|
||||
hass="[[hass]]"
|
||||
is-wide="[[isWide]]"
|
||||
email="{{_loginEmail}}"
|
||||
flash-message="{{_flashMessage}}"
|
||||
></ha-config-cloud-login>
|
||||
</template>
|
||||
|
||||
<template
|
||||
is="dom-if"
|
||||
if='[[_equals(_routeData.page, "register")]]'
|
||||
restamp=""
|
||||
>
|
||||
<ha-config-cloud-register
|
||||
page-name="register"
|
||||
hass="[[hass]]"
|
||||
is-wide="[[isWide]]"
|
||||
email="{{_loginEmail}}"
|
||||
></ha-config-cloud-register>
|
||||
</template>
|
||||
|
||||
<template
|
||||
is="dom-if"
|
||||
if='[[_equals(_routeData.page, "forgot-password")]]'
|
||||
restamp=""
|
||||
>
|
||||
<ha-config-cloud-forgot-password
|
||||
page-name="forgot-password"
|
||||
hass="[[hass]]"
|
||||
email="{{_loginEmail}}"
|
||||
></ha-config-cloud-forgot-password>
|
||||
</template>
|
||||
`;
|
||||
}
|
||||
|
||||
static get properties() {
|
||||
return {
|
||||
hass: Object,
|
||||
isWide: Boolean,
|
||||
loadingAccount: {
|
||||
type: Boolean,
|
||||
value: false,
|
||||
},
|
||||
cloudStatus: {
|
||||
type: Object,
|
||||
},
|
||||
_flashMessage: {
|
||||
type: String,
|
||||
value: "",
|
||||
},
|
||||
|
||||
route: Object,
|
||||
|
||||
_routeData: Object,
|
||||
_routeTail: Object,
|
||||
_loginEmail: String,
|
||||
};
|
||||
}
|
||||
|
||||
static get observers() {
|
||||
return ["_checkRoute(route, cloudStatus)"];
|
||||
}
|
||||
|
||||
ready() {
|
||||
super.ready();
|
||||
this.addEventListener("cloud-done", (ev) => {
|
||||
this._flashMessage = ev.detail.flashMessage;
|
||||
this.navigate("/config/cloud/login");
|
||||
});
|
||||
}
|
||||
|
||||
_checkRoute(route) {
|
||||
this._debouncer = Debouncer.debounce(
|
||||
this._debouncer,
|
||||
timeOut.after(0),
|
||||
() => {
|
||||
if (
|
||||
!this.cloudStatus ||
|
||||
(!this.cloudStatus.logged_in &&
|
||||
!NOT_LOGGED_IN_URLS.includes(route.path))
|
||||
) {
|
||||
this.navigate("/config/cloud/login", true);
|
||||
} else if (
|
||||
this.cloudStatus.logged_in &&
|
||||
!LOGGED_IN_URLS.includes(route.path)
|
||||
) {
|
||||
this.navigate("/config/cloud/account", true);
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
_equals(a, b) {
|
||||
return a === b;
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define("ha-config-cloud", HaConfigCloud);
|
138
src/panels/config/cloud/ha-config-cloud.ts
Normal file
138
src/panels/config/cloud/ha-config-cloud.ts
Normal file
@ -0,0 +1,138 @@
|
||||
import "./ha-config-cloud-account";
|
||||
import "./ha-config-cloud-login";
|
||||
import {
|
||||
HassRouterPage,
|
||||
RouterOptions,
|
||||
} from "../../../layouts/hass-router-page";
|
||||
import { property, customElement } from "lit-element";
|
||||
import { HomeAssistant, Route } from "../../../types";
|
||||
import { navigate } from "../../../common/navigate";
|
||||
import { CloudStatus } from "../../../data/cloud";
|
||||
import { PolymerChangedEvent } from "../../../polymer-types";
|
||||
import { PolymerElement } from "@polymer/polymer";
|
||||
|
||||
const LOGGED_IN_URLS = ["account", "google-assistant"];
|
||||
const NOT_LOGGED_IN_URLS = ["login", "register", "forgot-password"];
|
||||
|
||||
@customElement("ha-config-cloud")
|
||||
class HaConfigCloud extends HassRouterPage {
|
||||
@property() public hass!: HomeAssistant;
|
||||
@property() public isWide!: boolean;
|
||||
@property() public route!: Route;
|
||||
@property() public cloudStatus!: CloudStatus;
|
||||
|
||||
protected routerOptions: RouterOptions = {
|
||||
defaultPage: "login",
|
||||
showLoading: true,
|
||||
initialLoad: () => this._cloudStatusLoaded,
|
||||
// Guard the different pages based on if we're logged in.
|
||||
beforeRender: (page: string) => {
|
||||
if (this.cloudStatus.logged_in) {
|
||||
if (!LOGGED_IN_URLS.includes(page)) {
|
||||
return "account";
|
||||
}
|
||||
} else {
|
||||
if (!NOT_LOGGED_IN_URLS.includes(page)) {
|
||||
return "login";
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
},
|
||||
routes: {
|
||||
login: {
|
||||
tag: "ha-config-cloud-login",
|
||||
},
|
||||
register: {
|
||||
tag: "ha-config-cloud-register",
|
||||
load: () => import("./ha-config-cloud-register"),
|
||||
},
|
||||
"forgot-password": {
|
||||
tag: "ha-config-cloud-forgot-password",
|
||||
load: () => import("./ha-config-cloud-forgot-password"),
|
||||
},
|
||||
account: {
|
||||
tag: "ha-config-cloud-account",
|
||||
},
|
||||
"google-assistant": {
|
||||
tag: "ha-config-cloud-google-assistant",
|
||||
load: () => import("./ha-config-cloud-google-assistant"),
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
@property() private _flashMessage = "";
|
||||
@property() private _loginEmail = "";
|
||||
private _resolveCloudStatusLoaded!: () => void;
|
||||
private _cloudStatusLoaded = new Promise((resolve) => {
|
||||
this._resolveCloudStatusLoaded = resolve;
|
||||
});
|
||||
|
||||
protected firstUpdated(changedProps) {
|
||||
super.firstUpdated(changedProps);
|
||||
this.addEventListener("cloud-done", (ev) => {
|
||||
this._flashMessage = (ev as any).detail.flashMessage;
|
||||
navigate(this, "/config/cloud/login");
|
||||
});
|
||||
}
|
||||
|
||||
protected updated(changedProps) {
|
||||
super.updated(changedProps);
|
||||
|
||||
if (changedProps.has("cloudStatus")) {
|
||||
const oldStatus = changedProps.get("cloudStatus") as
|
||||
| CloudStatus
|
||||
| undefined;
|
||||
if (oldStatus === undefined) {
|
||||
this._resolveCloudStatusLoaded();
|
||||
} else if (oldStatus.logged_in !== this.cloudStatus.logged_in) {
|
||||
navigate(this, this.route.prefix, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected createElement(tag: string) {
|
||||
const el = super.createElement(tag);
|
||||
el.addEventListener("email-changed", (ev) => {
|
||||
this._loginEmail = (ev as PolymerChangedEvent<string>).detail.value;
|
||||
});
|
||||
el.addEventListener("flash-message-changed", (ev) => {
|
||||
this._flashMessage = (ev as PolymerChangedEvent<string>).detail.value;
|
||||
});
|
||||
return el;
|
||||
}
|
||||
|
||||
protected updatePageEl(el) {
|
||||
// We are not going to update if the current page if we are not logged in
|
||||
// and the current page requires being logged in. Happens when we log out.
|
||||
if (
|
||||
this.cloudStatus &&
|
||||
!this.cloudStatus.logged_in &&
|
||||
LOGGED_IN_URLS.includes(this._currentPage)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ("setProperties" in el) {
|
||||
// As long as we have Polymer pages
|
||||
(el as PolymerElement).setProperties({
|
||||
hass: this.hass,
|
||||
email: this._loginEmail,
|
||||
isWide: this.isWide,
|
||||
cloudStatus: this.cloudStatus,
|
||||
flashMessage: this._flashMessage,
|
||||
});
|
||||
} else {
|
||||
el.hass = this.hass;
|
||||
el.email = this._loginEmail;
|
||||
el.isWide = this.isWide;
|
||||
el.cloudStatus = this.cloudStatus;
|
||||
el.flashMessage = this._flashMessage;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"ha-config-cloud": HaConfigCloud;
|
||||
}
|
||||
}
|
@ -33,8 +33,10 @@ class ConfigCoreForm extends LitElement {
|
||||
@property() private _timeZone!: string;
|
||||
|
||||
protected render(): TemplateResult | void {
|
||||
const isStorage = this.hass.config.config_source === "storage";
|
||||
const disabled = this._working || !isStorage;
|
||||
const canEdit = ["storage", "default"].includes(
|
||||
this.hass.config.config_source
|
||||
);
|
||||
const disabled = this._working || !canEdit;
|
||||
|
||||
return html`
|
||||
<ha-card
|
||||
@ -43,7 +45,7 @@ class ConfigCoreForm extends LitElement {
|
||||
)}
|
||||
>
|
||||
<div class="card-content">
|
||||
${!isStorage
|
||||
${!canEdit
|
||||
? html`
|
||||
<p>
|
||||
${this.hass.localize(
|
||||
|
@ -25,13 +25,15 @@ class ConfigNameForm extends LitElement {
|
||||
@property() private _name!: ConfigUpdateValues["location_name"];
|
||||
|
||||
protected render(): TemplateResult | void {
|
||||
const isStorage = this.hass.config.config_source === "storage";
|
||||
const disabled = this._working || !isStorage;
|
||||
const canEdit = ["storage", "default"].includes(
|
||||
this.hass.config.config_source
|
||||
);
|
||||
const disabled = this._working || !canEdit;
|
||||
|
||||
return html`
|
||||
<ha-card>
|
||||
<div class="card-content">
|
||||
${!isStorage
|
||||
${!canEdit
|
||||
? html`
|
||||
<p>
|
||||
${this.hass.localize(
|
||||
|
203
src/panels/config/integrations/dialog-device-registry-detail.ts
Normal file
203
src/panels/config/integrations/dialog-device-registry-detail.ts
Normal file
@ -0,0 +1,203 @@
|
||||
import {
|
||||
LitElement,
|
||||
html,
|
||||
css,
|
||||
CSSResult,
|
||||
TemplateResult,
|
||||
customElement,
|
||||
property,
|
||||
} from "lit-element";
|
||||
import "@polymer/paper-dialog-scrollable/paper-dialog-scrollable";
|
||||
import "@polymer/paper-input/paper-input";
|
||||
import "@polymer/paper-listbox/paper-listbox";
|
||||
import "@polymer/paper-dropdown-menu/paper-dropdown-menu";
|
||||
import "@polymer/paper-item/paper-item";
|
||||
import "@material/mwc-button/mwc-button";
|
||||
|
||||
import "../../../components/dialog/ha-paper-dialog";
|
||||
|
||||
import { DeviceRegistryDetailDialogParams } from "./show-dialog-device-registry-detail";
|
||||
import { PolymerChangedEvent } from "../../../polymer-types";
|
||||
import { haStyleDialog } from "../../../resources/styles";
|
||||
import { HomeAssistant } from "../../../types";
|
||||
import {
|
||||
subscribeAreaRegistry,
|
||||
AreaRegistryEntry,
|
||||
} from "../../../data/area_registry";
|
||||
|
||||
@customElement("dialog-device-registry-detail")
|
||||
class DialogDeviceRegistryDetail extends LitElement {
|
||||
@property() public hass!: HomeAssistant;
|
||||
|
||||
@property() private _nameByUser!: string;
|
||||
@property() private _error?: string;
|
||||
@property() private _params?: DeviceRegistryDetailDialogParams;
|
||||
@property() private _areas?: AreaRegistryEntry[];
|
||||
@property() private _areaId?: string;
|
||||
|
||||
private _submitting?: boolean;
|
||||
private _unsubAreas?: any;
|
||||
|
||||
public async showDialog(
|
||||
params: DeviceRegistryDetailDialogParams
|
||||
): Promise<void> {
|
||||
this._params = params;
|
||||
this._error = undefined;
|
||||
this._nameByUser = this._params.device.name_by_user || "";
|
||||
this._areaId = this._params.device.area_id;
|
||||
await this.updateComplete;
|
||||
}
|
||||
|
||||
public connectedCallback() {
|
||||
super.connectedCallback();
|
||||
this._unsubAreas = subscribeAreaRegistry(this.hass.connection, (areas) => {
|
||||
this._areas = areas;
|
||||
});
|
||||
}
|
||||
|
||||
public disconnectedCallback() {
|
||||
super.disconnectedCallback();
|
||||
if (this._unsubAreas) {
|
||||
this._unsubAreas();
|
||||
}
|
||||
}
|
||||
|
||||
protected render(): TemplateResult | void {
|
||||
if (!this._params) {
|
||||
return html``;
|
||||
}
|
||||
const device = this._params.device;
|
||||
|
||||
return html`
|
||||
<ha-paper-dialog
|
||||
with-backdrop
|
||||
opened
|
||||
@opened-changed="${this._openedChanged}"
|
||||
>
|
||||
<h2>${device.name}</h2>
|
||||
<paper-dialog-scrollable>
|
||||
${this._error
|
||||
? html`
|
||||
<div class="error">${this._error}</div>
|
||||
`
|
||||
: ""}
|
||||
<div class="form">
|
||||
<paper-input
|
||||
.value=${this._nameByUser}
|
||||
@value-changed=${this._nameChanged}
|
||||
.label=${this.hass.localize("ui.dialogs.more_info_settings.name")}
|
||||
.placeholder=${device.name || ""}
|
||||
.disabled=${this._submitting}
|
||||
></paper-input>
|
||||
<div class="area">
|
||||
<paper-dropdown-menu label="Area" class="flex">
|
||||
<paper-listbox
|
||||
slot="dropdown-content"
|
||||
.selected="${this._computeSelectedArea()}"
|
||||
@iron-select="${this._areaIndexChanged}"
|
||||
>
|
||||
<paper-item>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.integrations.config_entry.no_area"
|
||||
)}
|
||||
</paper-item>
|
||||
${this._renderAreas()}
|
||||
</paper-listbox>
|
||||
</paper-dropdown-menu>
|
||||
</div>
|
||||
</div>
|
||||
</paper-dialog-scrollable>
|
||||
<div class="paper-dialog-buttons">
|
||||
<mwc-button @click="${this._updateEntry}">
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.entity_registry.editor.update"
|
||||
)}
|
||||
</mwc-button>
|
||||
</div>
|
||||
</ha-paper-dialog>
|
||||
`;
|
||||
}
|
||||
|
||||
private _nameChanged(ev: PolymerChangedEvent<string>): void {
|
||||
this._error = undefined;
|
||||
this._nameByUser = ev.detail.value;
|
||||
}
|
||||
|
||||
private _renderAreas() {
|
||||
if (!this._areas) {
|
||||
return;
|
||||
}
|
||||
return this._areas!.map(
|
||||
(area) => html`
|
||||
<paper-item>${area.name}</paper-item>
|
||||
`
|
||||
);
|
||||
}
|
||||
|
||||
private _computeSelectedArea() {
|
||||
if (!this._params || !this._areas) {
|
||||
return -1;
|
||||
}
|
||||
const device = this._params!.device;
|
||||
if (!device.area_id) {
|
||||
return 0;
|
||||
}
|
||||
// +1 because of "No Area" entry
|
||||
return this._areas.findIndex((area) => area.area_id === device.area_id) + 1;
|
||||
}
|
||||
|
||||
private _areaIndexChanged(event): void {
|
||||
const selectedAreaIdx = event.target!.selected;
|
||||
this._areaId =
|
||||
selectedAreaIdx < 1
|
||||
? undefined
|
||||
: this._areas![selectedAreaIdx - 1].area_id;
|
||||
}
|
||||
|
||||
private async _updateEntry(): Promise<void> {
|
||||
this._submitting = true;
|
||||
try {
|
||||
await this._params!.updateEntry({
|
||||
name_by_user: this._nameByUser.trim() || null,
|
||||
area_id: this._areaId || null,
|
||||
});
|
||||
this._params = undefined;
|
||||
} catch (err) {
|
||||
this._error = err.message || "Unknown error";
|
||||
} finally {
|
||||
this._submitting = false;
|
||||
}
|
||||
}
|
||||
|
||||
private _openedChanged(ev: PolymerChangedEvent<boolean>): void {
|
||||
if (!(ev.detail as any).value) {
|
||||
this._params = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
static get styles(): CSSResult[] {
|
||||
return [
|
||||
haStyleDialog,
|
||||
css`
|
||||
ha-paper-dialog {
|
||||
min-width: 400px;
|
||||
}
|
||||
.form {
|
||||
padding-bottom: 24px;
|
||||
}
|
||||
mwc-button.warning {
|
||||
margin-right: auto;
|
||||
}
|
||||
.error {
|
||||
color: var(--google-red-500);
|
||||
}
|
||||
`,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"dialog-device-registry-detail": DialogDeviceRegistryDetail;
|
||||
}
|
||||
}
|
@ -14,7 +14,16 @@ import LocalizeMixin from "../../../mixins/localize-mixin";
|
||||
import computeStateName from "../../../common/entity/compute_state_name";
|
||||
import "../../../components/entity/state-badge";
|
||||
import { compare } from "../../../common/string/compare";
|
||||
import { updateDeviceRegistryEntry } from "../../../data/device_registry";
|
||||
import {
|
||||
subscribeDeviceRegistry,
|
||||
updateDeviceRegistryEntry,
|
||||
} from "../../../data/device_registry";
|
||||
import { subscribeAreaRegistry } from "../../../data/area_registry";
|
||||
|
||||
import {
|
||||
showDeviceRegistryDetailDialog,
|
||||
loadDeviceRegistryDetailDialog,
|
||||
} from "./show-dialog-device-registry-detail";
|
||||
|
||||
function computeEntityName(hass, entity) {
|
||||
if (entity.name) return entity.name;
|
||||
@ -38,6 +47,13 @@ class HaDeviceCard extends EventsMixin(LocalizeMixin(PolymerElement)) {
|
||||
padding-bottom: 10px;
|
||||
min-width: 0;
|
||||
}
|
||||
.card-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
.card-header .name {
|
||||
width: 90%;
|
||||
}
|
||||
.device {
|
||||
width: 30%;
|
||||
}
|
||||
@ -45,9 +61,13 @@ class HaDeviceCard extends EventsMixin(LocalizeMixin(PolymerElement)) {
|
||||
font-weight: bold;
|
||||
}
|
||||
.device .model,
|
||||
.device .manuf {
|
||||
.device .manuf,
|
||||
.device .area {
|
||||
color: var(--secondary-text-color);
|
||||
}
|
||||
.area .extra-info .name {
|
||||
color: var(--primary-text-color);
|
||||
}
|
||||
.extra-info {
|
||||
margin-top: 8px;
|
||||
}
|
||||
@ -57,39 +77,34 @@ class HaDeviceCard extends EventsMixin(LocalizeMixin(PolymerElement)) {
|
||||
padding-bottom: 4px;
|
||||
}
|
||||
.manuf,
|
||||
.entity-id {
|
||||
.entity-id,
|
||||
.area {
|
||||
color: var(--secondary-text-color);
|
||||
}
|
||||
</style>
|
||||
<ha-card header="[[device.name]]">
|
||||
<ha-card>
|
||||
<div class="card-header">
|
||||
<div class="name">[[_deviceName(device)]]</div>
|
||||
<paper-icon-button
|
||||
icon="hass:settings"
|
||||
on-click="_gotoSettings"
|
||||
></paper-icon-button>
|
||||
</div>
|
||||
<div class="card-content">
|
||||
<!--
|
||||
<h1>[[configEntry.title]] ([[_computeIntegrationTitle(localize, configEntry.domain)]])</h1>
|
||||
-->
|
||||
<div class="info">
|
||||
<div class="model">[[device.model]]</div>
|
||||
<div class="manuf">
|
||||
[[localize('ui.panel.config.integrations.config_entry.manuf',
|
||||
'manufacturer', device.manufacturer)]]
|
||||
</div>
|
||||
<div class="area">
|
||||
<paper-dropdown-menu
|
||||
selected-item-label="{{selectedArea}}"
|
||||
label="Area"
|
||||
>
|
||||
<paper-listbox
|
||||
slot="dropdown-content"
|
||||
selected="[[_computeSelectedArea(areas, device)]]"
|
||||
>
|
||||
<paper-item>
|
||||
[[localize('ui.panel.config.integrations.config_entry.no_area')]]
|
||||
</paper-item>
|
||||
<template is="dom-repeat" items="[[areas]]">
|
||||
<paper-item area="[[item]]">[[item.name]]</paper-item>
|
||||
</template>
|
||||
</paper-listbox>
|
||||
</paper-dropdown-menu>
|
||||
</div>
|
||||
<template is="dom-if" if="[[device.area_id]]">
|
||||
<div class="area">
|
||||
<div class="extra-info">
|
||||
[[localize('ui.panel.config.integrations.device_registry.area')]]
|
||||
<span class="name">{{_computeArea(areas, device)}}</span>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
<template is="dom-if" if="[[device.hub_device_id]]">
|
||||
<div class="extra-info">
|
||||
@ -144,41 +159,41 @@ class HaDeviceCard extends EventsMixin(LocalizeMixin(PolymerElement)) {
|
||||
type: Array,
|
||||
computed: "_computeChildDevices(device, devices)",
|
||||
},
|
||||
selectedArea: {
|
||||
type: String,
|
||||
observer: "_selectedAreaChanged",
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
_computeSelectedArea(areas, device) {
|
||||
if (!areas || !device || !device.area_id) {
|
||||
return 0;
|
||||
}
|
||||
// +1 because of "No Area" entry
|
||||
return areas.findIndex((area) => area.area_id === device.area_id) + 1;
|
||||
firstUpdated(changedProps) {
|
||||
super.firstUpdated(changedProps);
|
||||
loadDeviceRegistryDetailDialog();
|
||||
}
|
||||
|
||||
async _selectedAreaChanged(option) {
|
||||
// Selected Option will transition to '' before transitioning to new value
|
||||
if (option === "" || !this.device || !this.areas) {
|
||||
return;
|
||||
}
|
||||
const area =
|
||||
option === "No Area"
|
||||
? undefined
|
||||
: this.areas.find((ar) => ar.name === option);
|
||||
|
||||
if (
|
||||
(!area && !this.device.area_id) ||
|
||||
(area && area.area_id === this.device.area_id)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
await updateDeviceRegistryEntry(this.hass, this.device.id, {
|
||||
area_id: area ? area.area_id : null,
|
||||
connectedCallback() {
|
||||
super.connectedCallback();
|
||||
this._unsubAreas = subscribeAreaRegistry(this.hass, (areas) => {
|
||||
this._areas = areas;
|
||||
});
|
||||
this._unsubDevices = subscribeDeviceRegistry(this.hass, (devices) => {
|
||||
this.devices = devices;
|
||||
this.device = devices.find((device) => device.id === this.device.id);
|
||||
});
|
||||
}
|
||||
|
||||
disconnectedCallback() {
|
||||
super.disconnectedCallback();
|
||||
if (this._unsubAreas) {
|
||||
this._unsubAreas();
|
||||
}
|
||||
if (this._unsubDevices) {
|
||||
this._unsubDevices();
|
||||
}
|
||||
}
|
||||
|
||||
_computeArea(areas, device) {
|
||||
if (!areas || !device || !device.area_id) {
|
||||
return "No Area";
|
||||
}
|
||||
// +1 because of "No Area" entry
|
||||
return areas.find((area) => area.area_id === device.area_id).name;
|
||||
}
|
||||
|
||||
_computeChildDevices(device, devices) {
|
||||
@ -211,15 +226,29 @@ class HaDeviceCard extends EventsMixin(LocalizeMixin(PolymerElement)) {
|
||||
);
|
||||
}
|
||||
|
||||
_deviceName(device) {
|
||||
return device.name_by_user || device.name;
|
||||
}
|
||||
|
||||
_computeDeviceName(devices, deviceId) {
|
||||
const device = devices.find((dev) => dev.id === deviceId);
|
||||
return device
|
||||
? device.name
|
||||
? this._deviceName(device)
|
||||
: `(${this.localize(
|
||||
"ui.panel.config.integrations.config_entry.device_unavailable"
|
||||
)})`;
|
||||
}
|
||||
|
||||
_gotoSettings() {
|
||||
const device = this.device;
|
||||
showDeviceRegistryDetailDialog(this, {
|
||||
device,
|
||||
updateEntry: async (updates) => {
|
||||
await updateDeviceRegistryEntry(this.hass, device.id, updates);
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
_openMoreInfo(ev) {
|
||||
this.fire("hass-more-info", { entityId: ev.model.entity.entity_id });
|
||||
}
|
||||
|
@ -0,0 +1,26 @@
|
||||
import { fireEvent } from "../../../common/dom/fire_event";
|
||||
import {
|
||||
DeviceRegistryEntry,
|
||||
DeviceRegistryEntryMutableParams,
|
||||
} from "../../../data/device_registry";
|
||||
|
||||
export interface DeviceRegistryDetailDialogParams {
|
||||
device: DeviceRegistryEntry;
|
||||
updateEntry: (
|
||||
updates: Partial<DeviceRegistryEntryMutableParams>
|
||||
) => Promise<unknown>;
|
||||
}
|
||||
|
||||
export const loadDeviceRegistryDetailDialog = () =>
|
||||
import(/* webpackChunkName: "device-registry-detail-dialog" */ "./dialog-device-registry-detail");
|
||||
|
||||
export const showDeviceRegistryDetailDialog = (
|
||||
element: HTMLElement,
|
||||
deviceRegistryDetailParams: DeviceRegistryDetailDialogParams
|
||||
): void => {
|
||||
fireEvent(element, "show-dialog", {
|
||||
dialogTag: "dialog-device-registry-detail",
|
||||
dialogImport: loadDeviceRegistryDetailDialog,
|
||||
dialogParams: deviceRegistryDetailParams,
|
||||
});
|
||||
};
|
@ -1,6 +1,11 @@
|
||||
import "@material/mwc-button";
|
||||
|
||||
import { fetchConfig, LovelaceConfig, saveConfig } from "../../data/lovelace";
|
||||
import {
|
||||
fetchConfig,
|
||||
LovelaceConfig,
|
||||
saveConfig,
|
||||
subscribeLovelaceUpdates,
|
||||
} from "../../data/lovelace";
|
||||
import "../../layouts/hass-loading-screen";
|
||||
import "../../layouts/hass-error-screen";
|
||||
import "./hui-root";
|
||||
@ -15,6 +20,7 @@ import {
|
||||
} from "lit-element";
|
||||
import { showSaveDialog } from "./editor/show-save-config-dialog";
|
||||
import { generateLovelaceConfig } from "./common/generate-lovelace-config";
|
||||
import { showToast } from "../../util/toast";
|
||||
|
||||
interface LovelacePanelConfig {
|
||||
mode: "yaml" | "storage";
|
||||
@ -42,6 +48,8 @@ class LovelacePanel extends LitElement {
|
||||
|
||||
private mqls?: MediaQueryList[];
|
||||
|
||||
private _saving: boolean = false;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this._closeEditor = this._closeEditor.bind(this);
|
||||
@ -66,7 +74,11 @@ class LovelacePanel extends LitElement {
|
||||
if (state === "error") {
|
||||
return html`
|
||||
<hass-error-screen title="Lovelace" .error="${this._errorMsg}">
|
||||
<mwc-button on-click="_forceFetchConfig">Reload Lovelace</mwc-button>
|
||||
<mwc-button on-click="_forceFetchConfig"
|
||||
>${this.hass!.localize(
|
||||
"ui.panel.lovelace.reload_lovelace"
|
||||
)}</mwc-button
|
||||
>
|
||||
</hass-error-screen>
|
||||
`;
|
||||
}
|
||||
@ -107,6 +119,16 @@ class LovelacePanel extends LitElement {
|
||||
|
||||
public firstUpdated() {
|
||||
this._fetchConfig(false);
|
||||
// we don't want to unsub as we want to stay informed of updates
|
||||
subscribeLovelaceUpdates(this.hass!.connection, () =>
|
||||
this._lovelaceChanged()
|
||||
);
|
||||
// reload lovelace on reconnect so we are sure we have the latest config
|
||||
window.addEventListener("connection-status", (ev) => {
|
||||
if (ev.detail === "connected") {
|
||||
this._fetchConfig(false);
|
||||
}
|
||||
});
|
||||
this._updateColumns = this._updateColumns.bind(this);
|
||||
this.mqls = [300, 600, 900, 1200].map((width) => {
|
||||
const mql = matchMedia(`(min-width: ${width}px)`);
|
||||
@ -128,6 +150,7 @@ class LovelacePanel extends LitElement {
|
||||
} 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.
|
||||
this._state = "loading";
|
||||
this._regenerateConfig();
|
||||
}
|
||||
}
|
||||
@ -135,6 +158,7 @@ class LovelacePanel extends LitElement {
|
||||
private async _regenerateConfig() {
|
||||
const conf = await generateLovelaceConfig(this.hass!, this.hass!.localize);
|
||||
this._setLovelaceConfig(conf, "generated");
|
||||
this._state = "loaded";
|
||||
}
|
||||
|
||||
private _closeEditor() {
|
||||
@ -153,6 +177,22 @@ class LovelacePanel extends LitElement {
|
||||
);
|
||||
}
|
||||
|
||||
private _lovelaceChanged() {
|
||||
if (this._saving) {
|
||||
this._saving = false;
|
||||
} else {
|
||||
showToast(this, {
|
||||
message: this.hass!.localize("ui.panel.lovelace.changed_toast.message"),
|
||||
action: {
|
||||
action: () => this._fetchConfig(false),
|
||||
text: this.hass!.localize("ui.panel.lovelace.changed_toast.refresh"),
|
||||
},
|
||||
duration: 0,
|
||||
dismissable: false,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private _forceFetchConfig() {
|
||||
this._fetchConfig(true);
|
||||
}
|
||||
@ -209,6 +249,7 @@ class LovelacePanel extends LitElement {
|
||||
config: newConfig,
|
||||
mode: "storage",
|
||||
});
|
||||
this._saving = true;
|
||||
await saveConfig(this.hass!, newConfig);
|
||||
} catch (err) {
|
||||
// tslint:disable-next-line
|
||||
|
@ -399,6 +399,7 @@ class HUIRoot extends LitElement {
|
||||
views
|
||||
) {
|
||||
navigate(this, `/lovelace/${views[0].path || 0}`, true);
|
||||
newSelectView = 0;
|
||||
} else if (this._routeData!.view === "hass-unused-entities") {
|
||||
newSelectView = "hass-unused-entities";
|
||||
} else if (this._routeData!.view) {
|
||||
|
@ -31,9 +31,6 @@ declare global {
|
||||
"iron-resize": undefined;
|
||||
"config-refresh": undefined;
|
||||
"ha-refresh-cloud-status": undefined;
|
||||
"hass-notification": {
|
||||
message: string;
|
||||
};
|
||||
"hass-api-called": {
|
||||
success: boolean;
|
||||
response: unknown;
|
||||
|
@ -1,37 +1,31 @@
|
||||
import { Constructor, LitElement } from "lit-element";
|
||||
import { HassBaseEl } from "./hass-base-mixin";
|
||||
import { HaToast } from "../components/ha-toast";
|
||||
import { computeRTL } from "../common/util/compute_rtl";
|
||||
import { showToast } from "../util/toast";
|
||||
|
||||
export default (superClass: Constructor<LitElement & HassBaseEl>) =>
|
||||
class extends superClass {
|
||||
private _discToast?: HaToast;
|
||||
|
||||
protected firstUpdated(changedProps) {
|
||||
super.firstUpdated(changedProps);
|
||||
// Need to load in advance because when disconnected, can't dynamically load code.
|
||||
import(/* webpackChunkName: "ha-toast" */ "../components/ha-toast");
|
||||
import(/* webpackChunkName: "notification-manager" */ "../managers/notification-manager");
|
||||
}
|
||||
|
||||
protected hassReconnected() {
|
||||
super.hassReconnected();
|
||||
if (this._discToast) {
|
||||
this._discToast.opened = false;
|
||||
}
|
||||
|
||||
showToast(this, {
|
||||
message: "",
|
||||
duration: 1,
|
||||
});
|
||||
}
|
||||
|
||||
protected hassDisconnected() {
|
||||
super.hassDisconnected();
|
||||
if (!this._discToast) {
|
||||
const el = document.createElement("ha-toast");
|
||||
el.duration = 0;
|
||||
this._discToast = el;
|
||||
this.shadowRoot!.appendChild(el as any);
|
||||
}
|
||||
this._discToast.dir = computeRTL(this.hass!);
|
||||
this._discToast.text = this.hass!.localize(
|
||||
"ui.notification_toast.connection_lost"
|
||||
);
|
||||
this._discToast.opened = true;
|
||||
|
||||
showToast(this, {
|
||||
message: this.hass!.localize("ui.notification_toast.connection_lost"),
|
||||
duration: 0,
|
||||
dismissable: false,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
@ -999,7 +999,12 @@
|
||||
"warning": {
|
||||
"entity_not_found": "Entity not available: {entity}",
|
||||
"entity_non_numeric": "Entity is non-numeric: {entity}"
|
||||
}
|
||||
},
|
||||
"changed_toast": {
|
||||
"message": "The Lovelace config was updated, would you like to refresh?",
|
||||
"refresh": "Refresh"
|
||||
},
|
||||
"reload_lovelace": "Reload Lovelace"
|
||||
},
|
||||
"mailbox": {
|
||||
"empty": "You do not have any messages",
|
||||
|
@ -1,3 +1,6 @@
|
||||
import { HassElement } from "../state/hass-element";
|
||||
import { showToast } from "./toast";
|
||||
|
||||
export const registerServiceWorker = (notifyUpdate = true) => {
|
||||
if (
|
||||
!("serviceWorker" in navigator) ||
|
||||
@ -20,9 +23,19 @@ export const registerServiceWorker = (notifyUpdate = true) => {
|
||||
!__DEMO__
|
||||
) {
|
||||
// Notify users here of a new frontend being available.
|
||||
import(/* webpackChunkName: "show-new-frontend-toast" */ "./show-new-frontend-toast").then(
|
||||
(mod) => mod.default(installingWorker)
|
||||
);
|
||||
const haElement = window.document.querySelector(
|
||||
"home-assistant, ha-onboarding"
|
||||
)! as HassElement;
|
||||
showToast(haElement, {
|
||||
message: "A new version of the frontend is available.",
|
||||
action: {
|
||||
action: () =>
|
||||
installingWorker.postMessage({ type: "skipWaiting" }),
|
||||
text: "reload",
|
||||
},
|
||||
duration: 0,
|
||||
dismissable: false,
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
|
@ -1,20 +0,0 @@
|
||||
import "@material/mwc-button";
|
||||
import "../components/ha-toast";
|
||||
|
||||
export default (installingWorker) => {
|
||||
const toast = document.createElement("ha-toast");
|
||||
toast.opened = true;
|
||||
toast.text = "A new version of the frontend is available.";
|
||||
toast.duration = 0;
|
||||
|
||||
const button = document.createElement("mwc-button");
|
||||
button.addEventListener("click", () =>
|
||||
installingWorker.postMessage({ type: "skipWaiting" })
|
||||
);
|
||||
button.style.color = "var(--primary-color)";
|
||||
button.style.fontWeight = "bold";
|
||||
button.label = "reload";
|
||||
toast.appendChild(button);
|
||||
|
||||
document.body.appendChild(toast);
|
||||
};
|
@ -355,6 +355,21 @@
|
||||
"introduction": "Controla el servidor de Home Assistant... des de Home Assistant.",
|
||||
"restart": "Reiniciar",
|
||||
"stop": "Aturar"
|
||||
},
|
||||
"core_config": {
|
||||
"edit_requires_storage": "L'editor està desactivat ja que la configuració es troba a configuration.yaml.",
|
||||
"location_name": "Nom de la instal·lació de Home Assistant",
|
||||
"latitude": "Latitud",
|
||||
"longitude": "Longitud",
|
||||
"elevation": "Altitud",
|
||||
"elevation_meters": "metres",
|
||||
"time_zone": "Zona horària",
|
||||
"unit_system": "Sistema d'unitats",
|
||||
"unit_system_imperial": "Imperial",
|
||||
"unit_system_metric": "Mètric",
|
||||
"imperial_example": "Fahrenheit, lliures",
|
||||
"metric_example": "Celsius, quilograms",
|
||||
"save_button": "Desa"
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -381,7 +396,7 @@
|
||||
"editor": {
|
||||
"introduction": "Utilitza els automatismes per donar més vida a la teva casa",
|
||||
"default_name": "Nou automatisme",
|
||||
"save": "Desar",
|
||||
"save": "Desa",
|
||||
"unsaved_confirm": "Hi ha canvis no desats. Segur que vols sortir?",
|
||||
"alias": "Nom",
|
||||
"triggers": {
|
||||
@ -545,7 +560,9 @@
|
||||
}
|
||||
},
|
||||
"learn_more": "Més informació sobre les accions"
|
||||
}
|
||||
},
|
||||
"load_error_not_editable": "Només es poden editar les automatitzacions de l'arxiu automations.yaml.",
|
||||
"load_error_unknown": "Error en carregar l’automatització ({err_no})."
|
||||
}
|
||||
},
|
||||
"script": {
|
||||
@ -866,6 +883,14 @@
|
||||
"intro": "Els dispositius i serveis es representen a Home Assistant com a integracions. Pots configurar-los ara o més tard des de la pàgina de configuració.",
|
||||
"more_integrations": "Més",
|
||||
"finish": "Finalitza"
|
||||
},
|
||||
"core-config": {
|
||||
"intro": "Hola {name}, benvingut\/uda a Home Assistant. Quin nom t'agradaria posar a la teva casa?",
|
||||
"intro_location": "Voldirem saber la zona on vius. Aquesta informació servirà per poder mostrar certa informació i configurar automatitzacions relatives a la posició del sol. Aquestes dades mai es compartiran fora de la teva xarxa personal.",
|
||||
"intro_location_detect": "Et podem ajudar a completar aquesta informació fent una única sol·licitud a un servei extern.",
|
||||
"location_name_default": "Casa",
|
||||
"button_detect": "Detecta",
|
||||
"finish": "Següent"
|
||||
}
|
||||
},
|
||||
"lovelace": {
|
||||
@ -892,7 +917,7 @@
|
||||
"editor": {
|
||||
"edit_card": {
|
||||
"header": "Targeta de Configuració",
|
||||
"save": "Desar",
|
||||
"save": "Desa",
|
||||
"toggle_editor": "Commutar l'editor",
|
||||
"pick_card": "Tria la targeta que vols afegir.",
|
||||
"add": "Afegir targeta",
|
||||
@ -925,7 +950,7 @@
|
||||
},
|
||||
"raw_editor": {
|
||||
"header": "Edita la configuració",
|
||||
"save": "Desar",
|
||||
"save": "Desa",
|
||||
"unsaved_changes": "Canvis sense desar",
|
||||
"saved": "Desat"
|
||||
}
|
||||
@ -950,7 +975,7 @@
|
||||
"common": {
|
||||
"loading": "Carregant",
|
||||
"cancel": "Cancel·la",
|
||||
"save": "Desar"
|
||||
"save": "Desa"
|
||||
},
|
||||
"duration": {
|
||||
"day": "{count} {count, plural,\n one {dia}\n other {dies}\n}",
|
||||
@ -1107,7 +1132,7 @@
|
||||
},
|
||||
"dialogs": {
|
||||
"more_info_settings": {
|
||||
"save": "Desar",
|
||||
"save": "Desa",
|
||||
"name": "Substitució de Nom",
|
||||
"entity_id": "ID de l'entitat"
|
||||
},
|
||||
@ -1128,7 +1153,7 @@
|
||||
"auth_store": {
|
||||
"ask": "Voleu desar aquest inici de sessió?",
|
||||
"decline": "No, gràcies",
|
||||
"confirm": "Desar inici de sessió"
|
||||
"confirm": "Desa inici de sessió"
|
||||
},
|
||||
"notification_drawer": {
|
||||
"click_to_configure": "Prem el botó per configurar {entity}",
|
||||
|
@ -279,7 +279,8 @@
|
||||
"state_badge": {
|
||||
"default": {
|
||||
"unknown": "?",
|
||||
"unavailable": "Kadunud"
|
||||
"unavailable": "Kadunud",
|
||||
"error": "Viga"
|
||||
},
|
||||
"alarm_control_panel": {
|
||||
"armed": "Valves",
|
||||
@ -353,6 +354,20 @@
|
||||
"introduction": "Kontrolli oma Home Assistant serverit... Home Assistant-ist.",
|
||||
"restart": "Taaskäivita",
|
||||
"stop": "Peata"
|
||||
},
|
||||
"core_config": {
|
||||
"location_name": "Sinu Home Assistant paigalduse nimi",
|
||||
"latitude": "Laius",
|
||||
"longitude": "Pikkus",
|
||||
"elevation": "Kõrgus",
|
||||
"elevation_meters": "meetrit",
|
||||
"time_zone": "Ajavöönd",
|
||||
"unit_system": "Ühikute süsteem",
|
||||
"unit_system_imperial": "Imperiaalne süsteem",
|
||||
"unit_system_metric": "Meetermõõdustik",
|
||||
"imperial_example": "Fahrenheit, naelad",
|
||||
"metric_example": "Celsius, kilogrammid",
|
||||
"save_button": "Salvesta"
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -369,7 +384,7 @@
|
||||
"description": "Loo ja redigeeri automatiseeringuid",
|
||||
"picker": {
|
||||
"header": "Automaatika redaktor",
|
||||
"introduction": "Automaatika redaktor võimaldab teil luua ja muuta automatiseeringuid. Palun loe läbi [kasutusjuhend](https:\/\/home-assistant.io\/docs\/automation\/editor\/), veendumaks, et oled Home Assistant-i õigesti seadistanud.",
|
||||
"introduction": "Automaatika redaktor võimaldab teil luua ja muuta automatiseeringuid. Palun järgige allolevat linki, veendumaks, et oled Home Assistant-i õigesti seadistanud.",
|
||||
"pick_automation": "Vali muudetav automatiseering",
|
||||
"no_automations": "Me ei leidnud ühtegi muudetavat automatiseeringut",
|
||||
"add_automation": "Lisa automatiseering",
|
||||
@ -383,7 +398,7 @@
|
||||
"alias": "Nimi",
|
||||
"triggers": {
|
||||
"header": "Päästikud",
|
||||
"introduction": "Päästikud on need, millest algab automatiseeringu töö. Ühe automatiseeringu jaoks on võimalik määrata mitu päästikut. Kui päästik vallandub, kontrollib Home Assistant võimalike tingimuste täidetust ja käivitab tegevuse.\n\n[Lisateave päästikutest.](https:\/\/home-assistant.io\/docs\/automation\/trigger\/)",
|
||||
"introduction": "Päästikud on need, millest algab automatiseeringu töö. Ühe automatiseeringu jaoks on võimalik määrata mitu päästikut. Kui päästik vallandub, kontrollib Home Assistant võimalike tingimuste täidetust ja käivitab tegevuse.",
|
||||
"add": "Lisa päästik",
|
||||
"duplicate": "Paljunda",
|
||||
"delete": "Kustuta",
|
||||
@ -465,7 +480,7 @@
|
||||
},
|
||||
"conditions": {
|
||||
"header": "Tingimused",
|
||||
"introduction": "Tingimused on automatiseeringu valikuline osa ja neid saab kasutada, et vältida tegevuse käivitumist päästiku vallandumisel. Tingimused tunduvad väga sarnased päästikutele, kuid on siiski erinevad. Päästik jälgib süsteemis toimuvaid sündmusi, tingimus jälgib ainult süsteem praegust olekut. Päästik võib märgata, et lülitit ollakse parasjagu lülitamas. Tingimus võib näha ainult seda, kas lüliti on praegu sisse või välja lülitatud. \n\n [Lisateave tingimustest.](https:\/\/home-assistant.io\/docs\/scripts\/conditions\/)",
|
||||
"introduction": "Tingimused on automatiseeringu valikuline osa ja neid saab kasutada, et vältida tegevuse käivitumist päästiku vallandumisel. Tingimused tunduvad väga sarnased päästikutele, kuid on siiski erinevad. Päästik jälgib süsteemis toimuvaid sündmusi, tingimus jälgib ainult süsteem praegust olekut. Päästik võib märgata, et lülitit ollakse parasjagu lülitamas. Tingimus võib näha ainult seda, kas lüliti on praegu sisse või välja lülitatud.",
|
||||
"add": "Lisa tingimus",
|
||||
"duplicate": "Duubelda",
|
||||
"delete": "Kustuta",
|
||||
@ -511,7 +526,7 @@
|
||||
},
|
||||
"actions": {
|
||||
"header": "Tegevused",
|
||||
"introduction": "Tegevused on need, mida Home Assistant teeb kui päästik käivitab automatiseeringu.\n\n[Lisateave tegevustest.](https:\/\/home-assistant.io\/docs\/automation\/action\/)",
|
||||
"introduction": "Tegevused on need, mida Home Assistant teeb kui päästik käivitab automatiseeringu.",
|
||||
"add": "Lisa toiming",
|
||||
"duplicate": "Paljunda",
|
||||
"delete": "Kustuta",
|
||||
@ -602,7 +617,10 @@
|
||||
}
|
||||
},
|
||||
"zha": {
|
||||
"caption": "ZHA"
|
||||
"caption": "ZHA",
|
||||
"device_card": {
|
||||
"area_picker_label": "Ala"
|
||||
}
|
||||
},
|
||||
"area_registry": {
|
||||
"caption": "Alade register",
|
||||
@ -822,6 +840,18 @@
|
||||
"required_fields": "Täida kõik nõutud väljad",
|
||||
"password_not_match": "Salasõnad ei ühti"
|
||||
}
|
||||
},
|
||||
"integration": {
|
||||
"more_integrations": "Rohkem",
|
||||
"finish": "Lõpeta"
|
||||
},
|
||||
"core-config": {
|
||||
"intro": "Tere tulemast Home Assistant'i kasutama, {name}. Kuidas sa soovid oma kodu nimetada?",
|
||||
"intro_location": "Soovime teada, kus sa elad. See teave aitab informatsiooni kuvamisel ja päikesepõhiste automatiseeringute loomisel. Neid andmeid ei jagata kunagi väljaspool teie võrku.",
|
||||
"intro_location_detect": "Me saame aidata sul seda teavet leida, tehes ühekordse päringu välisele teenusele.",
|
||||
"location_name_default": "Kodu",
|
||||
"button_detect": "Tuvasta",
|
||||
"finish": "Edasi"
|
||||
}
|
||||
},
|
||||
"lovelace": {
|
||||
|
@ -260,6 +260,19 @@
|
||||
"introduction": "Zure Home Assistant zerbitzaria... Home Assistantetik kontrolatu",
|
||||
"restart": "Berrabiarazi",
|
||||
"stop": "Gelditu"
|
||||
},
|
||||
"core_config": {
|
||||
"location_name": "Zure Home Assistant instalazioaren izena",
|
||||
"latitude": "Latitudea",
|
||||
"longitude": "Longitudea",
|
||||
"elevation_meters": "metro",
|
||||
"time_zone": "Ordu-eremua",
|
||||
"unit_system": "Unitate Sistema",
|
||||
"unit_system_imperial": "Inperiala",
|
||||
"unit_system_metric": "Metrikoa",
|
||||
"imperial_example": "Fahrenheit, librak",
|
||||
"metric_example": "Celsius, kilogramoak",
|
||||
"save_button": "Gorde"
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -326,6 +339,10 @@
|
||||
"enter": "Sartu",
|
||||
"leave": "Utzi"
|
||||
},
|
||||
"webhook": {
|
||||
"label": "Webhook",
|
||||
"webhook_id": "Webhook IDa"
|
||||
},
|
||||
"time_pattern": {
|
||||
"label": "Denbora Eredua",
|
||||
"hours": "Orduak",
|
||||
@ -392,7 +409,9 @@
|
||||
}
|
||||
},
|
||||
"learn_more": "Ekintzei buruz gehiago ikasi"
|
||||
}
|
||||
},
|
||||
"load_error_not_editable": "Soilik automations.yaml fitxategian dauden automatizazioak dira editagarriak.",
|
||||
"load_error_unknown": "Errorea automatizazioa kargatzean ({err_no})."
|
||||
}
|
||||
},
|
||||
"users": {
|
||||
@ -430,8 +449,15 @@
|
||||
"none": "Ez dago ezer konfiguratuta",
|
||||
"config_entry": {
|
||||
"no_devices": "Integrazio honek ez du gailurik.",
|
||||
"no_device": "Gailurik gabeko entitateak",
|
||||
"firmware": "Firmware: {version}",
|
||||
"no_area": "Ez dago gunerik"
|
||||
},
|
||||
"config_flow": {
|
||||
"external_step": {
|
||||
"description": "Urrats hau betetzeko kanpoko webgune bat bisitatu beharko duzu.",
|
||||
"open_site": "Webgunea ireki"
|
||||
}
|
||||
}
|
||||
},
|
||||
"zha": {
|
||||
@ -452,6 +478,7 @@
|
||||
},
|
||||
"area_registry": {
|
||||
"caption": "Gune Erregistroa",
|
||||
"description": "Zure etxeko gune guztien ikuspegi orokorra.",
|
||||
"picker": {
|
||||
"header": "Gune Erregistroa",
|
||||
"integrations_page": "Integrazioen orria",
|
||||
@ -474,6 +501,7 @@
|
||||
"integrations_page": "Integrazioak"
|
||||
},
|
||||
"editor": {
|
||||
"unavailable": "Entitate hau ez dago eskuragarri une honetan.",
|
||||
"default_name": "Gune berria",
|
||||
"delete": "EZABATU",
|
||||
"update": "EGUNERATU"
|
||||
@ -481,8 +509,12 @@
|
||||
},
|
||||
"person": {
|
||||
"caption": "Pertsonak",
|
||||
"description": "Kudeatu Home Assistantek jarraituko dituen pertsonak.",
|
||||
"detail": {
|
||||
"name": "Izena"
|
||||
"name": "Izena",
|
||||
"device_tracker_intro": "Hautatu pertsona horri dagozkion gailuak.",
|
||||
"device_tracker_picked": "Gailua jarraitu",
|
||||
"device_tracker_pick": "Jarraituko diren gailua aukeratu"
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -613,6 +645,17 @@
|
||||
"required_fields": "Beharrezkoak diren eremu guztiak bete",
|
||||
"password_not_match": "Pasahitzak ez datoz bat"
|
||||
}
|
||||
},
|
||||
"integration": {
|
||||
"more_integrations": "Gehiago",
|
||||
"finish": "Amaitu"
|
||||
},
|
||||
"core-config": {
|
||||
"intro": "Kaixo {name}, ongi etorri Home Assistantera. Nola izendatu nahi duzu zure etxea?",
|
||||
"intro_location_detect": "Informazio hau betetzen lagundu diezazukegu kanpoko zerbitzu batera esakera bakarra eginez.",
|
||||
"location_name_default": "Etxea",
|
||||
"button_detect": "Detektatu",
|
||||
"finish": "Hurregoa"
|
||||
}
|
||||
},
|
||||
"lovelace": {
|
||||
@ -624,12 +667,20 @@
|
||||
"empty_state": {
|
||||
"title": "Ongi etorri Etxera",
|
||||
"go_to_integrations_page": "Integrazioen orrira joan."
|
||||
},
|
||||
"picture-elements": {
|
||||
"hold": "Eutsi:",
|
||||
"tap": "Ukitu:",
|
||||
"navigate_to": "{location}-ra nabigatu",
|
||||
"call_service": "{name} zerbitzua deitu",
|
||||
"more_info": "Informazio gehiago erakutsi: {name}"
|
||||
}
|
||||
},
|
||||
"editor": {
|
||||
"edit_card": {
|
||||
"header": "Txartelaren konfigurazioa",
|
||||
"save": "Gorde",
|
||||
"pick_card": "Gehitu nahi duzun txartela aukeratu.",
|
||||
"add": "Txartela gehitu",
|
||||
"edit": "Editatu",
|
||||
"delete": "Ezabatu",
|
||||
@ -647,6 +698,7 @@
|
||||
"delete": "Bista ezabatu"
|
||||
},
|
||||
"save_config": {
|
||||
"header": "Hartu zure Lovelace UI-aren kontrola",
|
||||
"cancel": "Berdin dio",
|
||||
"save": "Kontrola hartu"
|
||||
},
|
||||
@ -674,7 +726,8 @@
|
||||
},
|
||||
"sidebar": {
|
||||
"log_out": "Saioa itxi",
|
||||
"developer_tools": "Garatzaileentzako tresnak"
|
||||
"developer_tools": "Garatzaileentzako tresnak",
|
||||
"external_app_configuration": "Aplikazioaren Konfigurazioa"
|
||||
},
|
||||
"common": {
|
||||
"loading": "Kargatzen",
|
||||
|
@ -355,6 +355,21 @@
|
||||
"introduction": "Contrôlez votre serveur Home Assistant... depuis Home Assistant.",
|
||||
"restart": "Redémarrer",
|
||||
"stop": "Arrêter"
|
||||
},
|
||||
"core_config": {
|
||||
"edit_requires_storage": "L'éditeur est désactivé car la configuration est stockée dans configuration.yaml.",
|
||||
"location_name": "Nom de votre installation Home Assistant",
|
||||
"latitude": "Latitude",
|
||||
"longitude": "Longitude",
|
||||
"elevation": "Élévation",
|
||||
"elevation_meters": "mètres",
|
||||
"time_zone": "Fuseau horaire",
|
||||
"unit_system": "Système d'unité",
|
||||
"unit_system_imperial": "Impérial",
|
||||
"unit_system_metric": "Métrique",
|
||||
"imperial_example": "Fahrenheit, livres",
|
||||
"metric_example": "Celsius, kilogrammes",
|
||||
"save_button": "Enregistrer"
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -545,7 +560,9 @@
|
||||
}
|
||||
},
|
||||
"learn_more": "En savoir plus sur les actions"
|
||||
}
|
||||
},
|
||||
"load_error_not_editable": "Seules les automatisations dans automations.yaml sont modifiables.",
|
||||
"load_error_unknown": "Erreur lors du chargement de l'automatisation ( {err_no} )."
|
||||
}
|
||||
},
|
||||
"script": {
|
||||
@ -866,6 +883,14 @@
|
||||
"intro": "Les appareils et les services sont représentés dans Home Assistant sous forme d’intégrations. Vous pouvez les configurer maintenant ou le faire plus tard à partir de l'écran de configuration.",
|
||||
"more_integrations": "Plus",
|
||||
"finish": "Terminer"
|
||||
},
|
||||
"core-config": {
|
||||
"intro": "Bonjour {name} , bienvenue dans Home Assistant. Comment voudriez-vous nommer votre maison?",
|
||||
"intro_location": "Nous aimerions savoir où vous habitez. Ces informations vous aideront à afficher des informations et à configurer des automatismes basés sur le soleil. Ces données ne sont jamais partagées en dehors de votre réseau.",
|
||||
"intro_location_detect": "Nous pouvons vous aider à renseigner ces informations en adressant une demande unique à un service externe.",
|
||||
"location_name_default": "Maison",
|
||||
"button_detect": "Détecter",
|
||||
"finish": "Suivant"
|
||||
}
|
||||
},
|
||||
"lovelace": {
|
||||
|
@ -355,6 +355,21 @@
|
||||
"introduction": "Home Assistant szerver vezérlése... a Home Assistant-ból.",
|
||||
"restart": "Újraindítás",
|
||||
"stop": "Leállítás"
|
||||
},
|
||||
"core_config": {
|
||||
"edit_requires_storage": "A szerkesztő le van tiltva, mert a konfiguráció a configuration.yaml fájlban van tárolva.",
|
||||
"location_name": "A Home Assistant rendszered neve",
|
||||
"latitude": "Szélesség",
|
||||
"longitude": "Hosszúság",
|
||||
"elevation": "Magasság",
|
||||
"elevation_meters": "méter",
|
||||
"time_zone": "Időzóna",
|
||||
"unit_system": "Egységrendszer",
|
||||
"unit_system_imperial": "Angolszász",
|
||||
"unit_system_metric": "Metrikus",
|
||||
"imperial_example": "Fahrenheit, font",
|
||||
"metric_example": "Celsius, kilogramm",
|
||||
"save_button": "Mentés"
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -545,7 +560,9 @@
|
||||
}
|
||||
},
|
||||
"learn_more": "Tudj meg többet a műveletekről"
|
||||
}
|
||||
},
|
||||
"load_error_not_editable": "Csak az automations.yaml fájlban lévő automatizálások szerkeszthetőek.",
|
||||
"load_error_unknown": "Hiba történt az automatizálás betöltésekor ({err_no})."
|
||||
}
|
||||
},
|
||||
"script": {
|
||||
@ -866,6 +883,14 @@
|
||||
"intro": "Az eszközöket és szolgáltatásokat a Home Assistant integrációként kezeli. Beállíthatod őket most, vagy később a konfigurációs képernyőn.",
|
||||
"more_integrations": "Több",
|
||||
"finish": "Befejezés"
|
||||
},
|
||||
"core-config": {
|
||||
"intro": "Hello {name}, üdvözöl a Home Assistant. Hogyan szeretnéd elnevezni az otthonodat?",
|
||||
"intro_location": "Szeretnénk tudni, hogy hol élsz. Ez segít az információk megjelenítésében és a nap alapú automatizálások beállításában. Ez az adat soha nem lesz megosztva a hálózatodon kívül.",
|
||||
"intro_location_detect": "Segíthetünk neked kitölteni ezt az információt egy külső szolgáltatás egyszeri lekérdezésével.",
|
||||
"location_name_default": "Otthon",
|
||||
"button_detect": "Érzékelés",
|
||||
"finish": "Tovább"
|
||||
}
|
||||
},
|
||||
"lovelace": {
|
||||
|
@ -264,6 +264,10 @@
|
||||
"on": "Menyala",
|
||||
"paused": "Berhenti",
|
||||
"returning": "Kembali ke dock"
|
||||
},
|
||||
"person": {
|
||||
"home": "Di rumah",
|
||||
"not_home": "Mode jauh"
|
||||
}
|
||||
},
|
||||
"state_badge": {
|
||||
@ -343,6 +347,9 @@
|
||||
"introduction": "Kontrol server Home Assistant anda ... dari Home Assistant.",
|
||||
"restart": "Ulang kembali",
|
||||
"stop": "Berhenti"
|
||||
},
|
||||
"core_config": {
|
||||
"save_button": "Simpan"
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -573,7 +580,12 @@
|
||||
"configured": "Terkonfigurasi",
|
||||
"new": "Mengatur integrasi baru",
|
||||
"configure": "Konfigurasi",
|
||||
"none": "Belum ada yang dikonfigurasi"
|
||||
"none": "Belum ada yang dikonfigurasi",
|
||||
"config_flow": {
|
||||
"external_step": {
|
||||
"open_site": "Buka situs web"
|
||||
}
|
||||
}
|
||||
},
|
||||
"area_registry": {
|
||||
"caption": "Registry Area",
|
||||
@ -591,6 +603,16 @@
|
||||
"create": "MEMBUAT"
|
||||
}
|
||||
},
|
||||
"entity_registry": {
|
||||
"description": "Ikhtisar semua entitas yang dikenal.",
|
||||
"editor": {
|
||||
"delete": "HAPUS",
|
||||
"update": "UPDATE"
|
||||
},
|
||||
"picker": {
|
||||
"integrations_page": "Halaman integrasi"
|
||||
}
|
||||
},
|
||||
"person": {
|
||||
"caption": "Orang",
|
||||
"description": "Kelola orang yang Home Assistant lacak",
|
||||
@ -600,6 +622,11 @@
|
||||
"device_tracker_picked": "Lacak Perangkat",
|
||||
"device_tracker_pick": "Pilih perangkat untuk dilacak"
|
||||
}
|
||||
},
|
||||
"zha": {
|
||||
"device_card": {
|
||||
"update_name_button": "Perbarui Nama"
|
||||
}
|
||||
}
|
||||
},
|
||||
"profile": {
|
||||
@ -736,12 +763,50 @@
|
||||
"error": {
|
||||
"required_fields": "Isi semua bidang yang wajib diisi"
|
||||
}
|
||||
},
|
||||
"integration": {
|
||||
"finish": "Selesai"
|
||||
},
|
||||
"core-config": {
|
||||
"intro": "Halo {name} , selamat datang di Asisten Rumah. Bagaimana Anda ingin memberi nama rumah Anda?",
|
||||
"intro_location_detect": "Kami dapat membantu Anda mengisi informasi ini dengan membuat permintaan satu kali ke layanan eksternal.",
|
||||
"location_name_default": "Beranda",
|
||||
"button_detect": "Deteksi",
|
||||
"finish": "Berikutnya"
|
||||
}
|
||||
},
|
||||
"lovelace": {
|
||||
"editor": {
|
||||
"edit_card": {
|
||||
"edit": "Edit",
|
||||
"delete": "Hapus"
|
||||
},
|
||||
"save_config": {
|
||||
"para_sure": "Apakah Anda yakin ingin mengendalikan antarmuka pengguna Anda?"
|
||||
},
|
||||
"raw_editor": {
|
||||
"header": "Edit Konfigurasi",
|
||||
"save": "Simpan",
|
||||
"unsaved_changes": "Perubahan yang belum disimpan",
|
||||
"saved": "Disimpan"
|
||||
}
|
||||
},
|
||||
"cards": {
|
||||
"empty_state": {
|
||||
"title": "Selamat datang di rumah",
|
||||
"no_devices": "Halaman ini memungkinkan Anda untuk mengontrol perangkat Anda, namun sepertinya Anda belum mengatur perangkat. Buka halaman integrasi untuk memulai.",
|
||||
"go_to_integrations_page": "Buka halaman integrasi."
|
||||
},
|
||||
"picture-elements": {
|
||||
"more_info": "Tampilkan lebih banyak info: {name}"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"sidebar": {
|
||||
"log_out": "Keluar",
|
||||
"developer_tools": "Alat pengembang"
|
||||
"developer_tools": "Alat pengembang",
|
||||
"external_app_configuration": "Konfigurasi Aplikasi"
|
||||
},
|
||||
"common": {
|
||||
"loading": "Memuat",
|
||||
@ -908,7 +973,8 @@
|
||||
},
|
||||
"sun": {
|
||||
"elevation": "Ketinggian",
|
||||
"rising": "Terbit"
|
||||
"rising": "Terbit",
|
||||
"setting": "Pengaturan"
|
||||
},
|
||||
"updater": {
|
||||
"title": "Petunjuk Pembaruan"
|
||||
@ -962,7 +1028,8 @@
|
||||
"updater": "Updater",
|
||||
"weblink": "Tautan web",
|
||||
"zwave": "Z-Wave",
|
||||
"vacuum": "Vakum"
|
||||
"vacuum": "Vakum",
|
||||
"person": "Orang"
|
||||
},
|
||||
"attribute": {
|
||||
"weather": {
|
||||
@ -970,5 +1037,9 @@
|
||||
"visibility": "Jarak pandang",
|
||||
"wind_speed": "Kecepatan angin"
|
||||
}
|
||||
},
|
||||
"groups": {
|
||||
"system-admin": "Administrator",
|
||||
"system-users": "Pengguna"
|
||||
}
|
||||
}
|
@ -355,6 +355,21 @@
|
||||
"introduction": "Controllare il server Home Assistant... da Home Assistant.",
|
||||
"restart": "Riavviare",
|
||||
"stop": "Stop"
|
||||
},
|
||||
"core_config": {
|
||||
"edit_requires_storage": "Editor disabilitato perché la configurazione memorizzata in configuration.yaml.",
|
||||
"location_name": "Nome della tua installazione di Home Assistant",
|
||||
"latitude": "Latitudine",
|
||||
"longitude": "Logitudine",
|
||||
"elevation": "Altitudine",
|
||||
"elevation_meters": "Metri",
|
||||
"time_zone": "Fuso orario",
|
||||
"unit_system": "Unità di misura",
|
||||
"unit_system_imperial": "Imperiale",
|
||||
"unit_system_metric": "metrico",
|
||||
"imperial_example": "Fahrenheit, libbre",
|
||||
"metric_example": "Celsius, chilogrammi",
|
||||
"save_button": "Salva"
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -545,7 +560,9 @@
|
||||
}
|
||||
},
|
||||
"learn_more": "Per saperne di più sulle azioni"
|
||||
}
|
||||
},
|
||||
"load_error_not_editable": "Solo le automazioni in automations.yaml sono modificabili.",
|
||||
"load_error_unknown": "Errore durante il caricamento dell'automazione ( {err_no} )."
|
||||
}
|
||||
},
|
||||
"script": {
|
||||
@ -606,6 +623,7 @@
|
||||
},
|
||||
"config_flow": {
|
||||
"external_step": {
|
||||
"description": "Questo passaggio richiede di visitare un sito Web esterno per essere completato.",
|
||||
"open_site": "Apri sito Web"
|
||||
}
|
||||
}
|
||||
@ -863,7 +881,16 @@
|
||||
},
|
||||
"integration": {
|
||||
"intro": "Dispositivi e servizi sono rappresentati in Home Assistant come integrazioni. È possibile impostarli ora, o farlo in seguito dalla schermata di configurazione.",
|
||||
"more_integrations": "Di Più",
|
||||
"finish": "finire"
|
||||
},
|
||||
"core-config": {
|
||||
"intro": "Salve {name} , benvenuto su Home Assistant. Come vorresti nominare la tua casa?",
|
||||
"intro_location": "Vorremmo sapere dove vivete. Queste informazioni aiuteranno a visualizzare informazioni e automazioni basate sul sole. Questi dati non vengono mai condivisi al di fuori della vostra rete.",
|
||||
"intro_location_detect": "Possiamo aiutarti a compilare queste informazioni effettuando una richiesta una volta a un servizio esterno.",
|
||||
"location_name_default": "Casa",
|
||||
"button_detect": "Rileva",
|
||||
"finish": "Prossimo"
|
||||
}
|
||||
},
|
||||
"lovelace": {
|
||||
@ -879,6 +906,11 @@
|
||||
"go_to_integrations_page": "Vai alla pagina delle integrazioni."
|
||||
},
|
||||
"picture-elements": {
|
||||
"hold": "Tenere premuto:",
|
||||
"tap": "Tocca:",
|
||||
"navigate_to": "Vai a {location}",
|
||||
"toggle": "Commuta {name}",
|
||||
"call_service": "Chiama il servizio {name}",
|
||||
"more_info": "Mostra più informazioni: {name}"
|
||||
}
|
||||
},
|
||||
|
@ -355,6 +355,21 @@
|
||||
"introduction": "Home Assistant 서버를 제어합니다",
|
||||
"restart": "재시작",
|
||||
"stop": "중지"
|
||||
},
|
||||
"core_config": {
|
||||
"edit_requires_storage": "구성내용이 configuration.yaml 에 저장되어있기 때문에 편집기가 비활성화 되었습니다.",
|
||||
"location_name": "Home Assistant 설치 이름",
|
||||
"latitude": "위도",
|
||||
"longitude": "경도",
|
||||
"elevation": "고도",
|
||||
"elevation_meters": "미터",
|
||||
"time_zone": "시간대",
|
||||
"unit_system": "단위",
|
||||
"unit_system_imperial": "야드파운드법",
|
||||
"unit_system_metric": "미터법",
|
||||
"imperial_example": "화씨, 파운드",
|
||||
"metric_example": "섭씨, 킬로그램",
|
||||
"save_button": "저장"
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -545,7 +560,9 @@
|
||||
}
|
||||
},
|
||||
"learn_more": "동작에 대해 더 알아보기"
|
||||
}
|
||||
},
|
||||
"load_error_not_editable": "automations.yaml 의 자동화만 편집 할 수 있습니다.",
|
||||
"load_error_unknown": "자동화를 읽어오는 도중 오류가 발생했습니다 ({err_no})."
|
||||
}
|
||||
},
|
||||
"script": {
|
||||
@ -866,6 +883,14 @@
|
||||
"intro": "기기 및 서비스는 Home Assistant 에서 통합 구성요소로 표시됩니다. 지금 구성하거나 설정 화면에서 나중에 구성할 수 있습니다.",
|
||||
"more_integrations": "더보기",
|
||||
"finish": "완료"
|
||||
},
|
||||
"core-config": {
|
||||
"intro": "{name} 님, 안녕하세요! Home Assistant 에 오신 것을 환영합니다. 집에 어떤 이름을 지어주시겠어요?",
|
||||
"intro_location": "현재 살고 계시는 위치를 알려주세요. 이는 태양 위치를 기반으로 하는 자동화를 설정하는데 도움이 됩니다. 위치 정보는 외부 네트워크와 절대 공유되지 않습니다.",
|
||||
"intro_location_detect": "외부 서비스에 일회성 요청을 해서 위치 정보를 넣는데 도움을 드릴 수 있습니다.",
|
||||
"location_name_default": "집",
|
||||
"button_detect": "탐색",
|
||||
"finish": "다음"
|
||||
}
|
||||
},
|
||||
"lovelace": {
|
||||
|
@ -355,6 +355,21 @@
|
||||
"introduction": "Kontrolléiert ären Home Assistant Server ... vun Home Assistant aus.",
|
||||
"restart": "Neistart",
|
||||
"stop": "Stop"
|
||||
},
|
||||
"core_config": {
|
||||
"edit_requires_storage": "Editeur ass desaktivéiert well d'Konfiguratioun an der configuration.yaml gespäichert ass.",
|
||||
"location_name": "Numm vun der Home Assistant Installatioun",
|
||||
"latitude": "Breedegrad",
|
||||
"longitude": "Längegrad",
|
||||
"elevation": "Héicht",
|
||||
"elevation_meters": "Meter",
|
||||
"time_zone": "Zäitzone",
|
||||
"unit_system": "Eenheetesystem",
|
||||
"unit_system_imperial": "Imperial",
|
||||
"unit_system_metric": "Metresch",
|
||||
"imperial_example": "Fahrenheit, Pënner",
|
||||
"metric_example": "Celsius, Kilogramm",
|
||||
"save_button": "Späicheren"
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -545,7 +560,9 @@
|
||||
}
|
||||
},
|
||||
"learn_more": "Méi iwwert Aktioune liesen"
|
||||
}
|
||||
},
|
||||
"load_error_not_editable": "Nëmmen Automatiounen am automations.yaml kënnen editéiert ginn.",
|
||||
"load_error_unknown": "Feeler beim luede vun der Automatioun ({err_no})."
|
||||
}
|
||||
},
|
||||
"script": {
|
||||
@ -866,6 +883,14 @@
|
||||
"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",
|
||||
"finish": "Ofschléissen"
|
||||
},
|
||||
"core-config": {
|
||||
"intro": "Hallo {name}, wëllkomm zu Home Assistant. Wéi wëllt dir äert Doheem benennen?",
|
||||
"intro_location": "Mir wëlle wësse wou dir wunnt. Dës Informatioun hëlleft fir Informatiounen unzeweisen an Automatiounen anzeriichten déi op d'Sonne baséieren. Dës Donnéeë ginn nimools ausserhalb vun ärem Netzwierk gedeelt.",
|
||||
"intro_location_detect": "Mir kënne beim Ausfëlle vun dësen Informatiounen hëllefen andeems eng eemoleg Demande bei engem externe Service ugefrot gëtt.",
|
||||
"location_name_default": "Doheem",
|
||||
"button_detect": "Entdecken",
|
||||
"finish": "Nächst"
|
||||
}
|
||||
},
|
||||
"lovelace": {
|
||||
|
@ -355,6 +355,21 @@
|
||||
"introduction": "Kontroller din Home Assistant server... fra Home Assistant.",
|
||||
"restart": "Start på nytt",
|
||||
"stop": "Stopp"
|
||||
},
|
||||
"core_config": {
|
||||
"edit_requires_storage": "Redigering deaktivert da konfigurasjonen er lagret i configuration.yaml.",
|
||||
"location_name": "Navn på installasjonen av Home Assistant",
|
||||
"latitude": "Breddegrad",
|
||||
"longitude": "Lengdegrad",
|
||||
"elevation": "Høyde",
|
||||
"elevation_meters": "meter",
|
||||
"time_zone": "Tidssone",
|
||||
"unit_system": "Enhetssystem",
|
||||
"unit_system_imperial": "Imperisk",
|
||||
"unit_system_metric": "Metrisk",
|
||||
"imperial_example": "Fahrenheit, pund",
|
||||
"metric_example": "Celsius, kilogram",
|
||||
"save_button": "Lagre"
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -545,7 +560,9 @@
|
||||
}
|
||||
},
|
||||
"learn_more": "Lær mer om handlinger"
|
||||
}
|
||||
},
|
||||
"load_error_not_editable": "Kun automatiseringer i automations.yaml kan redigeres.",
|
||||
"load_error_unknown": "Feil ved lasting av automatisering ( {err_no} )."
|
||||
}
|
||||
},
|
||||
"script": {
|
||||
@ -866,6 +883,14 @@
|
||||
"intro": "Enheter og tjenester er representert i Home Assistant som integrasjoner. Du kan sette dem opp nå eller gjøre det senere fra konfigurasjonen.",
|
||||
"more_integrations": "Mer",
|
||||
"finish": "Fullfør"
|
||||
},
|
||||
"core-config": {
|
||||
"intro": "Hei {Name}, velkommen til Home Assistant. Hva ønsker du å kalle hjemmet ditt?",
|
||||
"intro_location": "Vi vil gjerne vite hvor du bor. Denne informasjonen vil bidra til å vise informasjon og sette opp solbaserte automatiseringer. Denne dataen deles ikke utenfor nettverket ditt.",
|
||||
"intro_location_detect": "Vi kan hjelpe deg med å fylle ut denne informasjonen ved å gjøre en engangsforespørsel til en ekstern tjeneste.",
|
||||
"location_name_default": "Hjem",
|
||||
"button_detect": "Oppdag",
|
||||
"finish": "Neste"
|
||||
}
|
||||
},
|
||||
"lovelace": {
|
||||
|
@ -355,6 +355,21 @@
|
||||
"introduction": "Kontroluj swój serwer Home Assistant",
|
||||
"restart": "Uruchom ponownie",
|
||||
"stop": "Zatrzymaj"
|
||||
},
|
||||
"core_config": {
|
||||
"edit_requires_storage": "Edytor wyłączony, ponieważ konfiguracja jest przechowywana w pliku configuration.yaml.",
|
||||
"location_name": "Nazwa instalacji Home Assistant",
|
||||
"latitude": "Szerokość geograficzna",
|
||||
"longitude": "Długość geograficzna",
|
||||
"elevation": "Wysokość",
|
||||
"elevation_meters": "metry\/-ów",
|
||||
"time_zone": "Strefa czasowa",
|
||||
"unit_system": "System metryczny",
|
||||
"unit_system_imperial": "Imperialny",
|
||||
"unit_system_metric": "Metryczny",
|
||||
"imperial_example": "stopnie Fahrenheita, funty",
|
||||
"metric_example": "stopnie Celsjusza, kilogramy",
|
||||
"save_button": "Zapisz"
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -545,7 +560,9 @@
|
||||
}
|
||||
},
|
||||
"learn_more": "Dowiedz się więcej o akcjach"
|
||||
}
|
||||
},
|
||||
"load_error_not_editable": "Edytowalne są tylko automatyzacje w pliku automations.yaml.",
|
||||
"load_error_unknown": "Wystąpił błąd podczas ładowania automatyzacji ({err_no})."
|
||||
}
|
||||
},
|
||||
"script": {
|
||||
@ -866,6 +883,14 @@
|
||||
"intro": "Urządzenia i usługi są reprezentowane w Home Assistant jako integracje. Możesz je teraz skonfigurować lub zrobić to później w konfiguracji.",
|
||||
"more_integrations": "Więcej",
|
||||
"finish": "Koniec"
|
||||
},
|
||||
"core-config": {
|
||||
"intro": "{name}, witamy w Home Assistant. Jak chcesz nazwać swój dom?",
|
||||
"intro_location": "Chcielibyśmy wiedzieć gdzie mieszkasz. Te dane pomogą w wyświetlaniu informacji i konfigurowaniu automatyki opartej na położeniu słońca. Te dane nigdy nie będą udostępniane poza Twoją sieć lokalną.",
|
||||
"intro_location_detect": "Możemy pomóc Ci wprowadzić te informacje, wysyłając jednorazowe zapytanie do usługi zewnętrznej.",
|
||||
"location_name_default": "Dom",
|
||||
"button_detect": "Wykryj",
|
||||
"finish": "Dalej"
|
||||
}
|
||||
},
|
||||
"lovelace": {
|
||||
|
@ -355,6 +355,21 @@
|
||||
"introduction": "Управляйте Вашим сервером Home Assistant... из Home Assistant",
|
||||
"restart": "Перезапустить",
|
||||
"stop": "Остановить"
|
||||
},
|
||||
"core_config": {
|
||||
"edit_requires_storage": "Редактор отключен, поскольку конфигурация уже хранится в файле configuration.yaml.",
|
||||
"location_name": "Название для Вашего Home Assistant",
|
||||
"latitude": "Широта",
|
||||
"longitude": "Долгота",
|
||||
"elevation": "Высота",
|
||||
"elevation_meters": "метров",
|
||||
"time_zone": "Часовой пояс",
|
||||
"unit_system": "Система мер",
|
||||
"unit_system_imperial": "Имперская",
|
||||
"unit_system_metric": "Метрическая",
|
||||
"imperial_example": "Градус Фаренгейта, фунт",
|
||||
"metric_example": "Градус Цельсия, килограмм",
|
||||
"save_button": "Сохранить"
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -545,7 +560,9 @@
|
||||
}
|
||||
},
|
||||
"learn_more": "Узнайте больше о действиях"
|
||||
}
|
||||
},
|
||||
"load_error_not_editable": "Доступны для редактирования только автоматизации из automations.yaml.",
|
||||
"load_error_unknown": "Ошибка загрузки автоматизации ({err_no})."
|
||||
}
|
||||
},
|
||||
"script": {
|
||||
@ -866,6 +883,14 @@
|
||||
"intro": "Устройства и сервисы представлены в Home Assistant как интеграции. Вы можете добавить их сейчас или сделать это позже в разделе настроек.",
|
||||
"more_integrations": "Ещё",
|
||||
"finish": "Готово"
|
||||
},
|
||||
"core-config": {
|
||||
"intro": "Добро пожаловать, {name}! Как Вы хотите назвать свой Home Assistant?",
|
||||
"intro_location": "Мы хотели бы знать, где Вы живете. Это поможет нам правильно отображать информацию и позволит Вам настраивать автоматизацию на основе данных о рассвете и закате. Эти данные никогда не будут переданы за пределы Вашей локальной сети.",
|
||||
"intro_location_detect": "Мы можем помочь Вам заполнить эту информацию, сделав однократный запрос во внешнюю службу.",
|
||||
"location_name_default": "Home Assistant",
|
||||
"button_detect": "Заполнить",
|
||||
"finish": "Далее"
|
||||
}
|
||||
},
|
||||
"lovelace": {
|
||||
|
@ -355,6 +355,21 @@
|
||||
"introduction": "Nadzirajte strežnik Home Assistant ... iz Home Assistanta",
|
||||
"restart": "Ponovni zagon",
|
||||
"stop": "Ustavi"
|
||||
},
|
||||
"core_config": {
|
||||
"edit_requires_storage": "Urejevalnik je onemogočen, ker je konfiguracija shranjena v config.yaml.",
|
||||
"location_name": "Ime vašega instalacije vašega Home Assistant-a.",
|
||||
"latitude": "Zemljepisna širina",
|
||||
"longitude": "Zemljepisna dolžina",
|
||||
"elevation": "Nadmorska višina",
|
||||
"elevation_meters": "metrov",
|
||||
"time_zone": "Časovni pas",
|
||||
"unit_system": "Sistem enot",
|
||||
"unit_system_imperial": "Imperial",
|
||||
"unit_system_metric": "Metrično",
|
||||
"imperial_example": "Fahrenheit, funtov",
|
||||
"metric_example": "Celzija, kilogramov",
|
||||
"save_button": "Shrani"
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -545,7 +560,9 @@
|
||||
}
|
||||
},
|
||||
"learn_more": "Več o akcijah"
|
||||
}
|
||||
},
|
||||
"load_error_not_editable": "Urejati je mogoče le avtomatizacije v automations.yaml.",
|
||||
"load_error_unknown": "Napaka pri nalaganju avtomatizacije ( {err_no})."
|
||||
}
|
||||
},
|
||||
"script": {
|
||||
@ -603,6 +620,12 @@
|
||||
"device_unavailable": "naprava ni na voljo",
|
||||
"entity_unavailable": "subjekt ni na voljo",
|
||||
"no_area": "Brez območja"
|
||||
},
|
||||
"config_flow": {
|
||||
"external_step": {
|
||||
"description": "Ta korak zahteva, da za končanje obiščete zunanjo spletno stran.",
|
||||
"open_site": "Odprite spletno stran"
|
||||
}
|
||||
}
|
||||
},
|
||||
"zha": {
|
||||
@ -855,6 +878,19 @@
|
||||
"required_fields": "Izpolnite vsa zahtevana polja",
|
||||
"password_not_match": "Gesli se ne ujemata"
|
||||
}
|
||||
},
|
||||
"integration": {
|
||||
"intro": "Naprave in storitve so v Home Assistantu predstavljene kot integracije. Zdaj jih lahko nastavite ali pa to storite kasneje iz konfiguracijske strani.",
|
||||
"more_integrations": "Več",
|
||||
"finish": "Dokončaj"
|
||||
},
|
||||
"core-config": {
|
||||
"intro": "Pozdravljeni {name}, dobrodošli v Home Assistantu. Kako bi radi poimenovali vaš dom?",
|
||||
"intro_location": "Radi bi vedeli, kje živite. Te informacije vam bodo pomagale pri prikazovanju informacij in postavitvi avtomatizacij, ki temeljijo na soncu. Ti podatki se nikoli ne delijo izven vašega omrežja.",
|
||||
"intro_location_detect": "Lahko vam pomagamo izpolniti te informacije z enkratno zahtevo za zunanjo storitev.",
|
||||
"location_name_default": "Domov",
|
||||
"button_detect": "Odkrij",
|
||||
"finish": "Naslednji"
|
||||
}
|
||||
},
|
||||
"lovelace": {
|
||||
@ -868,6 +904,14 @@
|
||||
"title": "Dobrodošli Doma",
|
||||
"no_devices": "Ta stran vam omogoča nadzor nad napravami, vendar je videti, da še niste nastavili nobenih naprav. Pojdite na stran za integracije, da začnete.",
|
||||
"go_to_integrations_page": "Pojdite na stran za integracije."
|
||||
},
|
||||
"picture-elements": {
|
||||
"hold": "Pridržite:",
|
||||
"tap": "Tapnite:",
|
||||
"navigate_to": "Pojdite na {location}",
|
||||
"toggle": "Preklopi {name}",
|
||||
"call_service": "Kličite storitev {name}",
|
||||
"more_info": "Pokaži več informacij: {name}"
|
||||
}
|
||||
},
|
||||
"editor": {
|
||||
@ -925,7 +969,8 @@
|
||||
},
|
||||
"sidebar": {
|
||||
"log_out": "Odjava",
|
||||
"developer_tools": "Orodja za razvijalce"
|
||||
"developer_tools": "Orodja za razvijalce",
|
||||
"external_app_configuration": "Konfiguracija aplikacije"
|
||||
},
|
||||
"common": {
|
||||
"loading": "Nalaganje",
|
||||
|
@ -355,6 +355,21 @@
|
||||
"introduction": "Hantera din Home Assistant server... från Home Assistant.",
|
||||
"restart": "Starta om",
|
||||
"stop": "Stoppa"
|
||||
},
|
||||
"core_config": {
|
||||
"edit_requires_storage": "Redigeraren är inaktiverad eftersom konfigurationen lagras i configuration.yaml.",
|
||||
"location_name": "Namn på din Home Assistant installation",
|
||||
"latitude": "Latitud",
|
||||
"longitude": "Longitud",
|
||||
"elevation": "Höjd över havet",
|
||||
"elevation_meters": "meter",
|
||||
"time_zone": "Tidszon",
|
||||
"unit_system": "Enhets system",
|
||||
"unit_system_imperial": "Imperial",
|
||||
"unit_system_metric": "Metrisk",
|
||||
"imperial_example": "Fahrenheit, pounds",
|
||||
"metric_example": "Celsius, kilogram",
|
||||
"save_button": "Spara"
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -545,7 +560,9 @@
|
||||
}
|
||||
},
|
||||
"learn_more": "Lär dig mer om händelser"
|
||||
}
|
||||
},
|
||||
"load_error_not_editable": "Endast automatiseringar i automations.yaml kan redigeras.",
|
||||
"load_error_unknown": "Fel vid inläsning av automatisering ({err_no})."
|
||||
}
|
||||
},
|
||||
"script": {
|
||||
@ -866,6 +883,14 @@
|
||||
"intro": "Enheter och tjänster representeras i Home Assistant som integrationer. Du kan ställa in dem nu eller göra det senare från konfigurations-skärmen.",
|
||||
"more_integrations": "Mer",
|
||||
"finish": "Avsluta"
|
||||
},
|
||||
"core-config": {
|
||||
"intro": "Hej {name} , välkommen till Home Assistant. Hur skulle du vilja namnge ditt hem?",
|
||||
"intro_location": "Vi skulle vilja veta var du bor. Den här informationen hjälper dig att visa information och ställa in solbaserade automatiseringar. Dessa data delas aldrig utanför nätverket.",
|
||||
"intro_location_detect": "Vi kan hjälpa dig att fylla i denna information genom att göra en engångsförfrågan till en extern tjänst.",
|
||||
"location_name_default": "Hem",
|
||||
"button_detect": "Upptäcka",
|
||||
"finish": "Nästa"
|
||||
}
|
||||
},
|
||||
"lovelace": {
|
||||
|
@ -355,6 +355,21 @@
|
||||
"introduction": "控制 Home Assistant 伺服器。",
|
||||
"restart": "重啟",
|
||||
"stop": "停止"
|
||||
},
|
||||
"core_config": {
|
||||
"edit_requires_storage": "由於設定儲存於 configuration.yaml 內,編輯功能已關閉。",
|
||||
"location_name": "Home Assistant 安裝名稱",
|
||||
"latitude": "緯度",
|
||||
"longitude": "經度",
|
||||
"elevation": "海拔",
|
||||
"elevation_meters": "公尺",
|
||||
"time_zone": "時區",
|
||||
"unit_system": "單位制",
|
||||
"unit_system_imperial": "英制",
|
||||
"unit_system_metric": "公制",
|
||||
"imperial_example": "華氏、磅",
|
||||
"metric_example": "攝氏、公斤",
|
||||
"save_button": "儲存"
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -545,7 +560,9 @@
|
||||
}
|
||||
},
|
||||
"learn_more": "詳細了解動作"
|
||||
}
|
||||
},
|
||||
"load_error_not_editable": "僅有 automations.yaml 內的自動化方能編輯。",
|
||||
"load_error_unknown": "載入自動化錯誤({err_no})。"
|
||||
}
|
||||
},
|
||||
"script": {
|
||||
@ -866,6 +883,14 @@
|
||||
"intro": "將會於 Home Assistant 整合中呈現的裝置與服務。可以現在進行設定,或者稍後於設定選單中進行。",
|
||||
"more_integrations": "更多",
|
||||
"finish": "完成"
|
||||
},
|
||||
"core-config": {
|
||||
"intro": "{name}、你好,歡迎使用 Home Assistant。為您的家庭取個名字吧?",
|
||||
"intro_location": "希望能夠提供您所居住的區域,資料將協助所顯示的資訊內容、設定以日出日落為基礎的自動化。此資料將絕不會與外部網路進行分享。",
|
||||
"intro_location_detect": "設定可以藉由一次性請求外部服務、以協助您填寫此項資料。",
|
||||
"location_name_default": "首頁",
|
||||
"button_detect": "偵測",
|
||||
"finish": "下一步"
|
||||
}
|
||||
},
|
||||
"lovelace": {
|
||||
|
Loading…
x
Reference in New Issue
Block a user