From be0bef3f1b2380ae4a771b8a6c4f0fcd6ed63597 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Thu, 16 May 2019 17:44:46 +0200 Subject: [PATCH 1/8] Allow automation/script delete (#3194) --- src/data/automation.ts | 4 +++ src/data/script.ts | 3 ++ .../config/automation/ha-automation-editor.ts | 29 +++++++++++++++---- .../config/automation/ha-automation-picker.ts | 5 +++- src/panels/config/script/ha-script-editor.js | 17 ++++++++++- src/panels/config/script/ha-script-picker.ts | 5 +++- 6 files changed, 55 insertions(+), 8 deletions(-) diff --git a/src/data/automation.ts b/src/data/automation.ts index ab1cb44cbb..ad968cc1df 100644 --- a/src/data/automation.ts +++ b/src/data/automation.ts @@ -2,6 +2,7 @@ import { HassEntityBase, HassEntityAttributeBase, } from "home-assistant-js-websocket"; +import { HomeAssistant } from "../types"; export interface AutomationEntity extends HassEntityBase { attributes: HassEntityAttributeBase & { @@ -16,3 +17,6 @@ export interface AutomationConfig { condition?: any[]; action: any[]; } + +export const deleteAutomation = (hass: HomeAssistant, id: string) => + hass.callApi("DELETE", `config/automation/config/${id}`); diff --git a/src/data/script.ts b/src/data/script.ts index f6820be973..a86a62a153 100644 --- a/src/data/script.ts +++ b/src/data/script.ts @@ -12,3 +12,6 @@ export const triggerScript = ( entityId: string, variables?: {} ) => hass.callService("script", computeObjectId(entityId), variables); + +export const deleteScript = (hass: HomeAssistant, objectId: string) => + hass.callApi("DELETE", `config/script/config/${objectId}`); diff --git a/src/panels/config/automation/ha-automation-editor.ts b/src/panels/config/automation/ha-automation-editor.ts index dfc7b8d632..78f9ae8e69 100644 --- a/src/panels/config/automation/ha-automation-editor.ts +++ b/src/panels/config/automation/ha-automation-editor.ts @@ -24,7 +24,11 @@ import computeStateName from "../../../common/entity/compute_state_name"; import { haStyle } from "../../../resources/styles"; import { HomeAssistant } from "../../../types"; -import { AutomationEntity, AutomationConfig } from "../../../data/automation"; +import { + AutomationEntity, + AutomationConfig, + deleteAutomation, +} from "../../../data/automation"; import { navigate } from "../../../common/navigate"; import { computeRTL } from "../../../common/util/compute_rtl"; @@ -33,8 +37,8 @@ function AutomationEditor(mountEl, props, mergeEl) { } class HaAutomationEditor extends LitElement { - public hass?: HomeAssistant; - public automation?: AutomationEntity; + public hass!: HomeAssistant; + public automation!: AutomationEntity; public isWide?: boolean; public creatingNew?: boolean; private _config?: AutomationConfig; @@ -85,6 +89,14 @@ class HaAutomationEditor extends LitElement { "ui.panel.config.automation.editor.default_name" )} + ${this.creatingNew + ? "" + : html` + + `} @@ -184,7 +196,6 @@ class HaAutomationEditor extends LitElement { this._config = config; this._errors = undefined; this._dirty = true; - // this._updateComponent(); } private _backTapped(): void { @@ -199,10 +210,18 @@ class HaAutomationEditor extends LitElement { history.back(); } + private async _delete() { + if (!confirm("Are you sure you want to delete this automation?")) { + return; + } + await deleteAutomation(this.hass, this.automation.attributes.id!); + history.back(); + } + private _saveAutomation(): void { const id = this.creatingNew ? "" + Date.now() - : this.automation!.attributes.id; + : this.automation.attributes.id; this.hass!.callApi( "POST", "config/automation/config/" + id, diff --git a/src/panels/config/automation/ha-automation-picker.ts b/src/panels/config/automation/ha-automation-picker.ts index d8c6259bfb..b0ef0ba577 100644 --- a/src/panels/config/automation/ha-automation-picker.ts +++ b/src/panels/config/automation/ha-automation-picker.ts @@ -26,6 +26,7 @@ import { HomeAssistant } from "../../../types"; import { AutomationEntity } from "../../../data/automation"; import format_date_time from "../../../common/datetime/format_date_time"; import { fireEvent } from "../../../common/dom/fire_event"; +import { repeat } from "lit-html/directives/repeat"; @customElement("ha-automation-picker") class HaAutomationPicker extends LitElement { @@ -73,7 +74,9 @@ class HaAutomationPicker extends LitElement {

` - : this.automations.map( + : repeat( + this.automations, + (automation) => automation.entity_id, (automation) => html`
diff --git a/src/panels/config/script/ha-script-editor.js b/src/panels/config/script/ha-script-editor.js index bf1d016f9f..5b550094f7 100644 --- a/src/panels/config/script/ha-script-editor.js +++ b/src/panels/config/script/ha-script-editor.js @@ -18,6 +18,7 @@ import NavigateMixin from "../../../mixins/navigate-mixin"; import LocalizeMixin from "../../../mixins/localize-mixin"; import { computeRTL } from "../../../common/util/compute_rtl"; +import { deleteScript } from "../../../data/script"; function ScriptEditor(mountEl, props, mergeEl) { return render(h(Script, props), mountEl, mergeEl); @@ -99,7 +100,13 @@ class HaScriptEditor extends LocalizeMixin(NavigateMixin(PolymerElement)) { -
Script [[computeName(script)]]
+
Script [[computeName(script)]]
+
@@ -273,6 +280,14 @@ class HaScriptEditor extends LocalizeMixin(NavigateMixin(PolymerElement)) { }); } + async _delete() { + if (!confirm("Are you sure you want to delete this script?")) { + return; + } + await deleteScript(this.hass, computeObjectId(this.script.entity_id)); + history.back(); + } + saveScript() { var id = this.creatingNew ? "" + Date.now() diff --git a/src/panels/config/script/ha-script-picker.ts b/src/panels/config/script/ha-script-picker.ts index bdba5f7621..570cc2faa1 100644 --- a/src/panels/config/script/ha-script-picker.ts +++ b/src/panels/config/script/ha-script-picker.ts @@ -25,6 +25,7 @@ import { haStyle } from "../../../resources/styles"; import { HomeAssistant } from "../../../types"; import { triggerScript } from "../../../data/script"; import { showToast } from "../../../util/toast"; +import { repeat } from "lit-html/directives/repeat"; @customElement("ha-script-picker") class HaScriptPicker extends LitElement { @@ -56,7 +57,9 @@ class HaScriptPicker extends LitElement {

We couldn't find any scripts.

` - : this.scripts.map( + : repeat( + this.scripts, + (script) => script.entity_id, (script) => html`
Date: Tue, 21 May 2019 20:12:07 -0700 Subject: [PATCH 2/8] Add UI to set/update core config (#3208) * Add UI to set/update core config * Types * Disable editor in config.yaml mode * Fix type --- package.json | 3 +- src/common/util/subscribe-one.ts | 12 +- src/components/timezone-datalist.ts | 13 + src/data/area_registry.ts | 6 +- src/data/core.ts | 25 ++ src/data/device_registry.ts | 6 +- src/data/entity_registry.ts | 6 +- src/data/onboarding.ts | 10 + src/dialogs/config-flow/dialog-config-flow.ts | 15 +- src/fake_data/demo_config.ts | 1 + src/onboarding/ha-onboarding.ts | 20 ++ src/onboarding/onboarding-core-config.ts | 320 ++++++++++++++++++ .../area_registry/ha-config-area-registry.ts | 9 +- src/panels/config/core/ha-config-core-form.ts | 255 ++++++++++++++ src/panels/config/core/ha-config-name-form.ts | 94 +++++ .../config/core/ha-config-section-core.js | 6 + .../ha-config-entity-registry.ts | 13 +- .../integrations/ha-config-integrations.js | 2 +- src/panels/config/zha/zha-device-card.ts | 9 +- .../lovelace/cards/hui-empty-state-card.ts | 4 + .../common/generate-lovelace-config.ts | 12 +- src/panels/lovelace/hui-root.ts | 3 + src/translations/en.json | 23 ++ yarn.lock | 13 +- 24 files changed, 834 insertions(+), 46 deletions(-) create mode 100644 src/components/timezone-datalist.ts create mode 100644 src/data/core.ts create mode 100644 src/onboarding/onboarding-core-config.ts create mode 100644 src/panels/config/core/ha-config-core-form.ts create mode 100644 src/panels/config/core/ha-config-name-form.ts diff --git a/package.json b/package.json index 22c68baa9b..a88f82772b 100644 --- a/package.json +++ b/package.json @@ -75,8 +75,9 @@ "es6-object-assign": "^1.1.0", "fecha": "^3.0.2", "fuse.js": "^3.4.4", + "google-timezones-json": "^1.0.2", "hls.js": "^0.12.4", - "home-assistant-js-websocket": "^4.1.2", + "home-assistant-js-websocket": "^4.2.1", "intl-messageformat": "^2.2.0", "jquery": "^3.3.1", "js-yaml": "^3.13.0", diff --git a/src/common/util/subscribe-one.ts b/src/common/util/subscribe-one.ts index f7fee163a7..7480a517db 100644 --- a/src/common/util/subscribe-one.ts +++ b/src/common/util/subscribe-one.ts @@ -1,15 +1,11 @@ -import { HomeAssistant } from "../../types"; -import { UnsubscribeFunc } from "home-assistant-js-websocket"; +import { UnsubscribeFunc, Connection } from "home-assistant-js-websocket"; export const subscribeOne = async ( - hass: HomeAssistant, - subscribe: ( - hass: HomeAssistant, - onChange: (items: T) => void - ) => UnsubscribeFunc + conn: Connection, + subscribe: (conn: Connection, onChange: (items: T) => void) => UnsubscribeFunc ) => new Promise((resolve) => { - const unsub = subscribe(hass, (items) => { + const unsub = subscribe(conn, (items) => { unsub(); resolve(items); }); diff --git a/src/components/timezone-datalist.ts b/src/components/timezone-datalist.ts new file mode 100644 index 0000000000..05bfa1c0ab --- /dev/null +++ b/src/components/timezone-datalist.ts @@ -0,0 +1,13 @@ +import timezones from "google-timezones-json"; + +export const createTimezoneListEl = () => { + const list = document.createElement("datalist"); + list.id = "timezones"; + Object.keys(timezones).forEach((key) => { + const option = document.createElement("option"); + option.value = key; + option.innerHTML = timezones[key]; + list.appendChild(option); + }); + return list; +}; diff --git a/src/data/area_registry.ts b/src/data/area_registry.ts index 3347232028..ac9ed17504 100644 --- a/src/data/area_registry.ts +++ b/src/data/area_registry.ts @@ -1,4 +1,4 @@ -import { createCollection } from "home-assistant-js-websocket"; +import { createCollection, Connection } from "home-assistant-js-websocket"; import { HomeAssistant } from "../types"; import { compare } from "../common/string/compare"; import { debounce } from "../common/util/debounce"; @@ -57,13 +57,13 @@ const subscribeAreaRegistryUpdates = (conn, store) => ); export const subscribeAreaRegistry = ( - hass: HomeAssistant, + conn: Connection, onChange: (areas: AreaRegistryEntry[]) => void ) => createCollection( "_areaRegistry", fetchAreaRegistry, subscribeAreaRegistryUpdates, - hass.connection, + conn, onChange ); diff --git a/src/data/core.ts b/src/data/core.ts new file mode 100644 index 0000000000..8a6d9c099e --- /dev/null +++ b/src/data/core.ts @@ -0,0 +1,25 @@ +import { HomeAssistant } from "../types"; +import { HassConfig } from "home-assistant-js-websocket"; + +export interface ConfigUpdateValues { + location_name: string; + latitude: number; + longitude: number; + elevation: number; + unit_system: "metric" | "imperial"; + time_zone: string; +} + +export const saveCoreConfig = ( + hass: HomeAssistant, + values: Partial +) => + hass.callWS({ + type: "config/core/update", + ...values, + }); + +export const detectCoreConfig = (hass: HomeAssistant) => + hass.callWS>({ + type: "config/core/detect", + }); diff --git a/src/data/device_registry.ts b/src/data/device_registry.ts index 51084a802b..28161f0e91 100644 --- a/src/data/device_registry.ts +++ b/src/data/device_registry.ts @@ -1,5 +1,5 @@ import { HomeAssistant } from "../types"; -import { createCollection } from "home-assistant-js-websocket"; +import { createCollection, Connection } from "home-assistant-js-websocket"; import { debounce } from "../common/util/debounce"; export interface DeviceRegistryEntry { @@ -50,13 +50,13 @@ const subscribeDeviceRegistryUpdates = (conn, store) => ); export const subscribeDeviceRegistry = ( - hass: HomeAssistant, + conn: Connection, onChange: (devices: DeviceRegistryEntry[]) => void ) => createCollection( "_dr", fetchDeviceRegistry, subscribeDeviceRegistryUpdates, - hass.connection, + conn, onChange ); diff --git a/src/data/entity_registry.ts b/src/data/entity_registry.ts index 4ca2c76d23..f6549ee533 100644 --- a/src/data/entity_registry.ts +++ b/src/data/entity_registry.ts @@ -1,4 +1,4 @@ -import { createCollection } from "home-assistant-js-websocket"; +import { createCollection, Connection } from "home-assistant-js-websocket"; import { HomeAssistant } from "../types"; import computeStateName from "../common/entity/compute_state_name"; import { debounce } from "../common/util/debounce"; @@ -67,13 +67,13 @@ const subscribeEntityRegistryUpdates = (conn, store) => ); export const subscribeEntityRegistry = ( - hass: HomeAssistant, + conn: Connection, onChange: (entities: EntityRegistryEntry[]) => void ) => createCollection( "_entityRegistry", fetchEntityRegistry, subscribeEntityRegistryUpdates, - hass.connection, + conn, onChange ); diff --git a/src/data/onboarding.ts b/src/data/onboarding.ts index 80e6dce995..5ac72b2ba1 100644 --- a/src/data/onboarding.ts +++ b/src/data/onboarding.ts @@ -1,6 +1,9 @@ import { handleFetchPromise } from "../util/hass-call-api"; import { HomeAssistant } from "../types"; +// tslint:disable-next-line: no-empty-interface +export interface OnboardingCoreConfigStepResponse {} + export interface OnboardingUserStepResponse { auth_code: string; } @@ -11,6 +14,7 @@ export interface OnboardingIntegrationStepResponse { export interface OnboardingResponses { user: OnboardingUserStepResponse; + core_config: OnboardingCoreConfigStepResponse; integration: OnboardingIntegrationStepResponse; } @@ -39,6 +43,12 @@ export const onboardUserStep = (params: { }) ); +export const onboardCoreConfigStep = (hass: HomeAssistant) => + hass.callApi( + "POST", + "onboarding/core_config" + ); + export const onboardIntegrationStep = ( hass: HomeAssistant, params: { client_id: string } diff --git a/src/dialogs/config-flow/dialog-config-flow.ts b/src/dialogs/config-flow/dialog-config-flow.ts index dc49c96c51..1ca5932d34 100644 --- a/src/dialogs/config-flow/dialog-config-flow.ts +++ b/src/dialogs/config-flow/dialog-config-flow.ts @@ -219,15 +219,18 @@ class ConfigFlowDialog extends LitElement { } private async _fetchDevices(configEntryId) { - this._unsubDevices = subscribeDeviceRegistry(this.hass, (devices) => { - this._devices = devices.filter((device) => - device.config_entries.includes(configEntryId) - ); - }); + this._unsubDevices = subscribeDeviceRegistry( + this.hass.connection, + (devices) => { + this._devices = devices.filter((device) => + device.config_entries.includes(configEntryId) + ); + } + ); } private async _fetchAreas() { - this._unsubAreas = subscribeAreaRegistry(this.hass, (areas) => { + this._unsubAreas = subscribeAreaRegistry(this.hass.connection, (areas) => { this._areas = areas; }); } diff --git a/src/fake_data/demo_config.ts b/src/fake_data/demo_config.ts index ef85b37244..2b2b03f4da 100644 --- a/src/fake_data/demo_config.ts +++ b/src/fake_data/demo_config.ts @@ -16,4 +16,5 @@ export const demoConfig: HassConfig = { config_dir: "/config", version: "DEMO", whitelist_external_dirs: [], + config_source: "storage", }; diff --git a/src/onboarding/ha-onboarding.ts b/src/onboarding/ha-onboarding.ts index b37040bd45..48f6d266ef 100644 --- a/src/onboarding/ha-onboarding.ts +++ b/src/onboarding/ha-onboarding.ts @@ -10,6 +10,7 @@ import { createConnection, genClientId, Auth, + subscribeConfig, } from "home-assistant-js-websocket"; import { litLocalizeLiteMixin } from "../mixins/lit-localize-lite-mixin"; import { @@ -24,6 +25,8 @@ import "./onboarding-create-user"; import "./onboarding-loading"; import { hassUrl } from "../data/auth"; import { HassElement } from "../state/hass-element"; +import { subscribeOne } from "../common/util/subscribe-one"; +import { subscribeUser } from "../data/ws-user"; interface OnboardingEvent { type: T; @@ -61,6 +64,13 @@ class HaOnboarding extends litLocalizeLiteMixin(HassElement) { .language=${this.language} > `; + } else if (step.step === "core_config") { + return html` + + `; } else if (step.step === "integration") { return html` this._handleStepDone(ev)); } @@ -106,6 +117,7 @@ class HaOnboarding extends litLocalizeLiteMixin(HassElement) { const auth = await getAuth({ hassUrl, }); + history.replaceState(null, "", location.pathname); await this._connectHass(auth); } @@ -138,6 +150,8 @@ class HaOnboarding extends litLocalizeLiteMixin(HassElement) { } finally { this._loading = false; } + } else if (stepResult.type === "core_config") { + // We do nothing } else if (stepResult.type === "integration") { const result = stepResult.result as OnboardingResponses["integration"]; this._loading = true; @@ -161,6 +175,12 @@ class HaOnboarding extends litLocalizeLiteMixin(HassElement) { private async _connectHass(auth: Auth) { const conn = await createConnection({ auth }); + // Make sure config and user info is loaded before we initialize. + // It is needed for the core config step. + await Promise.all([ + subscribeOne(conn, subscribeConfig), + subscribeOne(conn, subscribeUser), + ]); this.initializeHass(auth, conn); // Load config strings for integrations (this as any)._loadFragmentTranslations(this.hass!.language, "config"); diff --git a/src/onboarding/onboarding-core-config.ts b/src/onboarding/onboarding-core-config.ts new file mode 100644 index 0000000000..bdba99a0c6 --- /dev/null +++ b/src/onboarding/onboarding-core-config.ts @@ -0,0 +1,320 @@ +import { + LitElement, + customElement, + property, + TemplateResult, + html, + CSSResult, + css, +} from "lit-element"; +import "@material/mwc-button/mwc-button"; +import "@polymer/paper-input/paper-input"; +import "@polymer/paper-radio-group/paper-radio-group"; +import "@polymer/paper-radio-button/paper-radio-button"; +// tslint:disable-next-line: no-duplicate-imports +import { PaperInputElement } from "@polymer/paper-input/paper-input"; +import { HomeAssistant } from "../types"; +import { + ConfigUpdateValues, + detectCoreConfig, + saveCoreConfig, +} from "../data/core"; +import { UNIT_C } from "../common/const"; +import { PolymerChangedEvent } from "../polymer-types"; +import { onboardCoreConfigStep } from "../data/onboarding"; +import { fireEvent } from "../common/dom/fire_event"; +import { LocalizeFunc } from "../common/translations/localize"; +import { createTimezoneListEl } from "../components/timezone-datalist"; + +@customElement("onboarding-core-config") +class OnboardingCoreConfig extends LitElement { + @property() public hass!: HomeAssistant; + @property() public onboardingLocalize!: LocalizeFunc; + + @property() private _working = false; + + @property() private _name!: ConfigUpdateValues["location_name"]; + @property() private _latitude!: string; + @property() private _longitude!: string; + @property() private _elevation!: string; + @property() private _unitSystem!: ConfigUpdateValues["unit_system"]; + @property() private _timeZone!: string; + + protected render(): TemplateResult | void { + return html` +

+ ${this.onboardingLocalize( + "ui.panel.page-onboarding.core-config.intro", + "name", + this.hass.user!.name + )} +

+ + + +
+

+ ${this.onboardingLocalize( + "ui.panel.page-onboarding.core-config.intro_location" + )} +

+ +
+
+ ${this.onboardingLocalize( + "ui.panel.page-onboarding.core-config.intro_location_detect" + )} +
+ + ${this.onboardingLocalize( + "ui.panel.page-onboarding.core-config.button_detect" + )} + +
+
+ +
+ + +
+ +
+ + + + + ${this.hass.localize( + "ui.panel.config.core.section.core.core_config.elevation_meters" + )} + + +
+ +
+
+ ${this.hass.localize( + "ui.panel.config.core.section.core.core_config.unit_system" + )} +
+ + + ${this.hass.localize( + "ui.panel.config.core.section.core.core_config.unit_system_metric" + )} +
+ ${this.hass.localize( + "ui.panel.config.core.section.core.core_config.metric_example" + )} +
+
+ + ${this.hass.localize( + "ui.panel.config.core.section.core.core_config.unit_system_imperial" + )} +
+ ${this.hass.localize( + "ui.panel.config.core.section.core.core_config.imperial_example" + )} +
+
+
+
+ + + `; + } + + protected firstUpdated(changedProps) { + super.firstUpdated(changedProps); + setTimeout( + () => this.shadowRoot!.querySelector("paper-input")!.focus(), + 100 + ); + this.addEventListener("keypress", (ev) => { + if (ev.keyCode === 13) { + this._save(ev); + } + }); + const input = this.shadowRoot!.querySelector( + "[name=timeZone]" + ) as PaperInputElement; + input.inputElement.appendChild(createTimezoneListEl()); + } + + private get _nameValue() { + return this._name !== undefined + ? this._name + : this.onboardingLocalize( + "ui.panel.page-onboarding.core-config.location_name_default" + ); + } + + private get _latitudeValue() { + return this._latitude !== undefined + ? this._latitude + : this.hass.config.latitude; + } + + private get _longitudeValue() { + return this._longitude !== undefined + ? this._longitude + : this.hass.config.longitude; + } + + private get _elevationValue() { + return this._elevation !== undefined + ? this._elevation + : this.hass.config.elevation; + } + + private get _timeZoneValue() { + return this._timeZone !== undefined + ? this._timeZone + : this.hass.config.time_zone; + } + + private get _unitSystemValue() { + return this._unitSystem !== undefined + ? this._unitSystem + : this.hass.config.unit_system.temperature === UNIT_C + ? "metric" + : "imperial"; + } + + private _handleChange(ev: PolymerChangedEvent) { + const target = ev.currentTarget as PaperInputElement; + this[`_${target.name}`] = target.value; + } + + private async _detect() { + this._working = true; + try { + const values = await detectCoreConfig(this.hass); + for (const key in values) { + if (key === "unit_system") { + this._unitSystem = values[key]!; + } else if (key === "time_zone") { + this._timeZone = values[key]!; + } else { + this[`_${key}`] = values[key]; + } + } + } catch (err) { + alert(`Failed to detect location information: ${err.message}`); + } finally { + this._working = false; + } + } + + private async _save(ev) { + ev.preventDefault(); + this._working = true; + try { + await saveCoreConfig(this.hass, { + location_name: this._nameValue, + latitude: Number(this._latitudeValue), + longitude: Number(this._longitudeValue), + elevation: Number(this._elevationValue), + unit_system: this._unitSystemValue, + time_zone: this._timeZoneValue, + }); + const result = await onboardCoreConfigStep(this.hass); + fireEvent(this, "onboarding-step", { + type: "core_config", + result, + }); + } catch (err) { + this._working = false; + alert("FAIL"); + } + } + + static get styles(): CSSResult { + return css` + .row { + display: flex; + flex-direction: row; + margin: 0 -8px; + align-items: center; + } + + .secondary { + color: var(--secondary-text-color); + } + + .flex { + flex: 1; + } + + .middle-text { + margin: 24px 0; + } + + .row > * { + margin: 0 8px; + } + .footer { + margin-top: 16px; + text-align: right; + } + `; + } +} + +declare global { + interface HTMLElementTagNameMap { + "onboarding-core-config": OnboardingCoreConfig; + } +} diff --git a/src/panels/config/area_registry/ha-config-area-registry.ts b/src/panels/config/area_registry/ha-config-area-registry.ts index 598b884cde..840655482c 100644 --- a/src/panels/config/area_registry/ha-config-area-registry.ts +++ b/src/panels/config/area_registry/ha-config-area-registry.ts @@ -122,9 +122,12 @@ class HaConfigAreaRegistry extends LitElement { protected updated(changedProps) { super.updated(changedProps); if (!this._unsubAreas) { - this._unsubAreas = subscribeAreaRegistry(this.hass, (areas) => { - this._areas = areas; - }); + this._unsubAreas = subscribeAreaRegistry( + this.hass.connection, + (areas) => { + this._areas = areas; + } + ); } } diff --git a/src/panels/config/core/ha-config-core-form.ts b/src/panels/config/core/ha-config-core-form.ts new file mode 100644 index 0000000000..7ccb572dcc --- /dev/null +++ b/src/panels/config/core/ha-config-core-form.ts @@ -0,0 +1,255 @@ +import { + LitElement, + customElement, + property, + TemplateResult, + html, + CSSResult, + css, +} from "lit-element"; +import "@material/mwc-button/mwc-button"; +import "@polymer/paper-input/paper-input"; +import "@polymer/paper-radio-group/paper-radio-group"; +import "@polymer/paper-radio-button/paper-radio-button"; +import { HomeAssistant } from "../../../types"; +import "../../../components/ha-card"; +import { PolymerChangedEvent } from "../../../polymer-types"; +// tslint:disable-next-line: no-duplicate-imports +import { PaperInputElement } from "@polymer/paper-input/paper-input"; +import { UNIT_C } from "../../../common/const"; +import { ConfigUpdateValues, saveCoreConfig } from "../../../data/core"; +import { createTimezoneListEl } from "../../../components/timezone-datalist"; + +@customElement("ha-config-core-form") +class ConfigCoreForm extends LitElement { + @property() public hass!: HomeAssistant; + + @property() private _working = false; + + @property() private _latitude!: string; + @property() private _longitude!: string; + @property() private _elevation!: string; + @property() private _unitSystem!: ConfigUpdateValues["unit_system"]; + @property() private _timeZone!: string; + + protected render(): TemplateResult | void { + const isStorage = this.hass.config.config_source === "storage"; + const disabled = this._working || !isStorage; + + return html` + +
+ ${!isStorage + ? html` +

+ ${this.hass.localize( + "ui.panel.config.core.section.core.core_config.edit_requires_storage" + )} +

+ ` + : ""} + +
+ + +
+ +
+
+ ${this.hass.localize( + "ui.panel.config.core.section.core.core_config.time_zone" + )} +
+ + +
+
+
+ ${this.hass.localize( + "ui.panel.config.core.section.core.core_config.elevation" + )} +
+ + + + ${this.hass.localize( + "ui.panel.config.core.section.core.core_config.elevation_meters" + )} + + +
+ +
+
+ ${this.hass.localize( + "ui.panel.config.core.section.core.core_config.unit_system" + )} +
+ + + ${this.hass.localize( + "ui.panel.config.core.section.core.core_config.unit_system_metric" + )} +
+ ${this.hass.localize( + "ui.panel.config.core.section.core.core_config.metric_example" + )} +
+
+ + ${this.hass.localize( + "ui.panel.config.core.section.core.core_config.unit_system_imperial" + )} +
+ ${this.hass.localize( + "ui.panel.config.core.section.core.core_config.imperial_example" + )} +
+
+
+
+
+
+ + ${this.hass.localize( + "ui.panel.config.core.section.core.core_config.save_button" + )} + +
+
+ `; + } + + protected firstUpdated(changedProps) { + super.firstUpdated(changedProps); + const input = this.shadowRoot!.querySelector( + "[name=timeZone]" + ) as PaperInputElement; + input.inputElement.appendChild(createTimezoneListEl()); + } + + private get _latitudeValue() { + return this._latitude !== undefined + ? this._latitude + : this.hass.config.latitude; + } + + private get _longitudeValue() { + return this._longitude !== undefined + ? this._longitude + : this.hass.config.longitude; + } + + private get _elevationValue() { + return this._elevation !== undefined + ? this._elevation + : this.hass.config.elevation; + } + + private get _timeZoneValue() { + return this._timeZone !== undefined + ? this._timeZone + : this.hass.config.time_zone; + } + + private get _unitSystemValue() { + return this._unitSystem !== undefined + ? this._unitSystem + : this.hass.config.unit_system.temperature === UNIT_C + ? "metric" + : "imperial"; + } + + private _handleChange(ev: PolymerChangedEvent) { + const target = ev.currentTarget as PaperInputElement; + this[`_${target.name}`] = target.value; + } + + private async _save() { + this._working = true; + try { + await saveCoreConfig(this.hass, { + latitude: Number(this._latitudeValue), + longitude: Number(this._longitudeValue), + elevation: Number(this._elevationValue), + unit_system: this._unitSystemValue, + time_zone: this._timeZoneValue, + }); + } catch (err) { + alert("FAIL"); + } finally { + this._working = false; + } + } + + static get styles(): CSSResult { + return css` + .row { + display: flex; + flex-direction: row; + margin: 0 -8px; + align-items: center; + } + + .secondary { + color: var(--secondary-text-color); + } + + .flex { + flex: 1; + } + + .row > * { + margin: 0 8px; + } + `; + } +} + +declare global { + interface HTMLElementTagNameMap { + "ha-config-core-form": ConfigCoreForm; + } +} diff --git a/src/panels/config/core/ha-config-name-form.ts b/src/panels/config/core/ha-config-name-form.ts new file mode 100644 index 0000000000..e2cef443fa --- /dev/null +++ b/src/panels/config/core/ha-config-name-form.ts @@ -0,0 +1,94 @@ +import { + LitElement, + customElement, + property, + TemplateResult, + html, +} from "lit-element"; +import "@material/mwc-button/mwc-button"; +import "@polymer/paper-input/paper-input"; +import "@polymer/paper-radio-group/paper-radio-group"; +import "@polymer/paper-radio-button/paper-radio-button"; +import { HomeAssistant } from "../../../types"; +import "../../../components/ha-card"; +import { PolymerChangedEvent } from "../../../polymer-types"; +// tslint:disable-next-line: no-duplicate-imports +import { PaperInputElement } from "@polymer/paper-input/paper-input"; +import { ConfigUpdateValues, saveCoreConfig } from "../../../data/core"; + +@customElement("ha-config-name-form") +class ConfigNameForm extends LitElement { + @property() public hass!: HomeAssistant; + + @property() private _working = false; + + @property() private _name!: ConfigUpdateValues["location_name"]; + + protected render(): TemplateResult | void { + const isStorage = this.hass.config.config_source === "storage"; + const disabled = this._working || !isStorage; + + return html` + +
+ ${!isStorage + ? html` +

+ ${this.hass.localize( + "ui.panel.config.core.section.core.core_config.edit_requires_storage" + )} +

+ ` + : ""} + +
+
+ + ${this.hass.localize( + "ui.panel.config.core.section.core.core_config.save_button" + )} + +
+
+ `; + } + + private get _nameValue() { + return this._name !== undefined + ? this._name + : this.hass.config.location_name; + } + + private _handleChange(ev: PolymerChangedEvent) { + const target = ev.currentTarget as PaperInputElement; + this[`_${target.name}`] = target.value; + } + + private async _save() { + this._working = true; + try { + await saveCoreConfig(this.hass, { + location_name: this._nameValue, + }); + } catch (err) { + alert("FAIL"); + } finally { + this._working = false; + } + } +} + +declare global { + interface HTMLElementTagNameMap { + "ha-config-name-form": ConfigNameForm; + } +} diff --git a/src/panels/config/core/ha-config-section-core.js b/src/panels/config/core/ha-config-section-core.js index 4824b74fc5..9b55d253a0 100644 --- a/src/panels/config/core/ha-config-section-core.js +++ b/src/panels/config/core/ha-config-section-core.js @@ -12,6 +12,9 @@ import "../ha-config-section"; import isComponentLoaded from "../../../common/config/is_component_loaded"; import LocalizeMixin from "../../../mixins/localize-mixin"; +import "./ha-config-name-form"; +import "./ha-config-core-form"; + /* * @appliesMixin LocalizeMixin */ @@ -57,6 +60,9 @@ class HaConfigSectionCore extends LocalizeMixin(PolymerElement) { >[[localize('ui.panel.config.core.section.core.introduction')]] + + + diff --git a/src/panels/config/entity_registry/ha-config-entity-registry.ts b/src/panels/config/entity_registry/ha-config-entity-registry.ts index 86f4dd1e78..776e41e1a0 100644 --- a/src/panels/config/entity_registry/ha-config-entity-registry.ts +++ b/src/panels/config/entity_registry/ha-config-entity-registry.ts @@ -118,11 +118,14 @@ class HaConfigEntityRegistry extends LitElement { protected updated(changedProps) { super.updated(changedProps); if (!this._unsubEntities) { - this._unsubEntities = subscribeEntityRegistry(this.hass, (entities) => { - this._entities = entities.sort((ent1, ent2) => - compare(ent1.entity_id, ent2.entity_id) - ); - }); + this._unsubEntities = subscribeEntityRegistry( + this.hass.connection, + (entities) => { + this._entities = entities.sort((ent1, ent2) => + compare(ent1.entity_id, ent2.entity_id) + ); + } + ); } } diff --git a/src/panels/config/integrations/ha-config-integrations.js b/src/panels/config/integrations/ha-config-integrations.js index bd759f4ded..80ce069611 100644 --- a/src/panels/config/integrations/ha-config-integrations.js +++ b/src/panels/config/integrations/ha-config-integrations.js @@ -96,7 +96,7 @@ class HaConfigIntegrations extends NavigateMixin(PolymerElement) { connectedCallback() { super.connectedCallback(); this._loadData(); - this._unsubAreas = subscribeAreaRegistry(this.hass, (areas) => { + this._unsubAreas = subscribeAreaRegistry(this.hass.connection, (areas) => { this._areas = areas; }); diff --git a/src/panels/config/zha/zha-device-card.ts b/src/panels/config/zha/zha-device-card.ts index ee5cfd4650..33b1f2cfbd 100644 --- a/src/panels/config/zha/zha-device-card.ts +++ b/src/panels/config/zha/zha-device-card.ts @@ -90,9 +90,12 @@ class ZHADeviceCard extends LitElement { this._userGivenName = this.device!.user_given_name; } if (!this._unsubAreas) { - this._unsubAreas = subscribeAreaRegistry(this.hass, (areas) => { - this._areas = areas; - }); + this._unsubAreas = subscribeAreaRegistry( + this.hass.connection, + (areas) => { + this._areas = areas; + } + ); } super.update(changedProperties); } diff --git a/src/panels/lovelace/cards/hui-empty-state-card.ts b/src/panels/lovelace/cards/hui-empty-state-card.ts index da38872bbd..9c81e114de 100644 --- a/src/panels/lovelace/cards/hui-empty-state-card.ts +++ b/src/panels/lovelace/cards/hui-empty-state-card.ts @@ -62,6 +62,10 @@ export class HuiEmptyStateCard extends LitElement implements LovelaceCard { padding: 16px; } + .card-actions a { + text-decoration: none; + } + mwc-button { margin-left: -8px; } diff --git a/src/panels/lovelace/common/generate-lovelace-config.ts b/src/panels/lovelace/common/generate-lovelace-config.ts index 666f9fe3ff..bd8b91821c 100644 --- a/src/panels/lovelace/common/generate-lovelace-config.ts +++ b/src/panels/lovelace/common/generate-lovelace-config.ts @@ -352,15 +352,15 @@ export const generateLovelaceConfig = async ( // so that we don't serve up stale data after changing areas. if (!subscribedRegistries) { subscribedRegistries = true; - subscribeAreaRegistry(hass, () => undefined); - subscribeDeviceRegistry(hass, () => undefined); - subscribeEntityRegistry(hass, () => undefined); + subscribeAreaRegistry(hass.connection, () => undefined); + subscribeDeviceRegistry(hass.connection, () => undefined); + subscribeEntityRegistry(hass.connection, () => undefined); } const [areas, devices, entities] = await Promise.all([ - subscribeOne(hass, subscribeAreaRegistry), - subscribeOne(hass, subscribeDeviceRegistry), - subscribeOne(hass, subscribeEntityRegistry), + subscribeOne(hass.connection, subscribeAreaRegistry), + subscribeOne(hass.connection, subscribeDeviceRegistry), + subscribeOne(hass.connection, subscribeEntityRegistry), ]); const registries = { areas, devices, entities }; diff --git a/src/panels/lovelace/hui-root.ts b/src/panels/lovelace/hui-root.ts index 4dfbad5940..f6f887d4a8 100644 --- a/src/panels/lovelace/hui-root.ts +++ b/src/panels/lovelace/hui-root.ts @@ -310,6 +310,9 @@ class HUIRoot extends LitElement { ha-app-layout { min-height: 100%; } + paper-menu-button { + padding: 0; + } paper-tabs { margin-left: 12px; --paper-tabs-selection-bar-color: var(--text-primary-color, #fff); diff --git a/src/translations/en.json b/src/translations/en.json index ffb9016cd7..bdc584030b 100644 --- a/src/translations/en.json +++ b/src/translations/en.json @@ -581,6 +581,21 @@ "core": { "header": "Configuration and Server Control", "introduction": "Changing your configuration can be a tiresome process. We know. This section will try to make your life a little bit easier.", + "core_config": { + "edit_requires_storage": "Editor disabled because config stored in configuration.yaml.", + "location_name": "Name of your Home Assistant installation", + "latitude": "Latitude", + "longitude": "Longitude", + "elevation": "Elevation", + "elevation_meters": "meters", + "time_zone": "Time Zone", + "unit_system": "Unit System", + "unit_system_imperial": "Imperial", + "unit_system_metric": "Metric", + "imperial_example": "Fahrenheit, pounds", + "metric_example": "Celsius, kilograms", + "save_button": "Save" + }, "validation": { "heading": "Configuration validation", "introduction": "Validate your configuration if you recently made some changes to your configuration and want to make sure that it is all valid", @@ -1179,6 +1194,14 @@ "password_not_match": "Passwords don't match" } }, + "core-config": { + "intro": "Hello {name}, welcome to Home Assistant. How would you like to name your home?", + "intro_location": "We would like to know where you live. This information will help with displaying information and setting up sun-based automations. This data is never shared outside of your network.", + "intro_location_detect": "We can help you fill in this information by making a one-time request to an external service.", + "location_name_default": "Home", + "button_detect": "Detect", + "finish": "Next" + }, "integration": { "intro": "Devices and services are represented in Home Assistant as integrations. You can set them up now, or do it later from the configuration screen.", "more_integrations": "More", diff --git a/yarn.lock b/yarn.lock index f35c71ba6c..e828bb791f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6807,6 +6807,11 @@ glogg@^1.0.0: dependencies: sparkles "^1.0.0" +google-timezones-json@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/google-timezones-json/-/google-timezones-json-1.0.2.tgz#6800000d7ebc2dd660611aad8c1c68196db78cab" + integrity sha512-UWXQ7BpSCW8erDespU2I4cri22xsKgwOCyhsJal0OJhi2tFpwJpsYNJt4vCiFPL1p2HzCGiS713LKpNR25n9Kg== + got@^5.0.0: version "5.7.1" resolved "https://registry.yarnpkg.com/got/-/got-5.7.1.tgz#5f81635a61e4a6589f180569ea4e381680a51f35" @@ -7242,10 +7247,10 @@ hoek@6.x.x: resolved "https://registry.yarnpkg.com/hoek/-/hoek-6.1.3.tgz#73b7d33952e01fe27a38b0457294b79dd8da242c" integrity sha512-YXXAAhmF9zpQbC7LEcREFtXfGq5K1fmd+4PHkBq8NUqmzW3G+Dq10bI/i0KucLRwss3YYFQ0fSfoxBZYiGUqtQ== -home-assistant-js-websocket@^4.1.2: - version "4.1.2" - resolved "https://registry.yarnpkg.com/home-assistant-js-websocket/-/home-assistant-js-websocket-4.1.2.tgz#dbcdb4b67df8d189d29bbf5603771d5bc80ef031" - integrity sha512-/I0m6FTDEq3LkzFc4tmgHJHTj9gWA6Wn/fgaa1ghIJJY0Yqb3x6whovN5pRNFsl6bnKzOCR+nmJ2ruVTBa5mVQ== +home-assistant-js-websocket@^4.2.1: + version "4.2.1" + resolved "https://registry.yarnpkg.com/home-assistant-js-websocket/-/home-assistant-js-websocket-4.2.1.tgz#8acdf2a404b4204669213d8405cca027b8b1de1c" + integrity sha512-lF4owDhAAUY70FNvTzgg6MAEOpKbJDLsRDX3gW48muna03s3CRGQzbLmy621pJWK757CkXSW/rWbr34r3Wyi8Q== homedir-polyfill@^1.0.0, homedir-polyfill@^1.0.1: version "1.0.3" From 9309c5a1b601f7435c307e32c5db30d12d6a1d4e Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Wed, 22 May 2019 19:36:07 -0700 Subject: [PATCH 3/8] Update Lit-HTML to 1.1 (#3210) --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index e828bb791f..5ba51fba2f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -8523,9 +8523,9 @@ lit-element@^2.0.1, lit-element@^2.1.0: lit-html "^1.0.0" lit-html@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/lit-html/-/lit-html-1.0.0.tgz#3dc3781a8ca68a9b5c2ff2a61e263662b9b2267b" - integrity sha512-oeWlpLmBW3gFl7979Wol2LKITpmKTUFNn7PnFbh6YNynF61W74l6x5WhwItAwPRSATpexaX1egNnRzlN4GOtfQ== + version "1.1.0" + resolved "https://registry.yarnpkg.com/lit-html/-/lit-html-1.1.0.tgz#6951fb717fb48fe34d915ae163448a04da321562" + integrity sha512-ZDJHpJi09yknMpjwPI8fuSl5sUG7+pF+eE5WciFtgyX7zebvgMDBgSLq4knXa7grxM00RkQ7PBd7UZQiruA78Q== load-json-file@^1.0.0: version "1.1.0" From fe80c7fe0e87a65a989baca8c1191c95f37b5c23 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Thu, 23 May 2019 12:28:17 -0700 Subject: [PATCH 4/8] Warn that only automations in automations.yaml are editable (#3200) --- .../config/automation/ha-automation-editor.ts | 36 +++++++++++++------ src/translations/en.json | 2 ++ 2 files changed, 28 insertions(+), 10 deletions(-) diff --git a/src/panels/config/automation/ha-automation-editor.ts b/src/panels/config/automation/ha-automation-editor.ts index 78f9ae8e69..be52d70603 100644 --- a/src/panels/config/automation/ha-automation-editor.ts +++ b/src/panels/config/automation/ha-automation-editor.ts @@ -147,18 +147,34 @@ class HaAutomationEditor extends LitElement { "GET", `config/automation/config/${this.automation.attributes.id}` ) - .then((config) => { - // Normalize data: ensure trigger, action and condition are lists - // Happens when people copy paste their automations into the config - for (const key of ["trigger", "condition", "action"]) { - const value = config[key]; - if (value && !Array.isArray(value)) { - config[key] = [value]; + .then( + (config) => { + // Normalize data: ensure trigger, action and condition are lists + // Happens when people copy paste their automations into the config + for (const key of ["trigger", "condition", "action"]) { + const value = config[key]; + if (value && !Array.isArray(value)) { + config[key] = [value]; + } } + this._dirty = false; + this._config = config; + }, + (resp) => { + alert( + resp.status_code === 404 + ? this.hass.localize( + "ui.panel.config.automation.editor.load_error_not_editable" + ) + : this.hass.localize( + "ui.panel.config.automation.editor.load_error_unknown", + "err_no", + resp.status_code + ) + ); + history.back(); } - this._dirty = false; - this._config = config; - }); + ); } if (changedProps.has("creatingNew") && this.creatingNew && this.hass) { diff --git a/src/translations/en.json b/src/translations/en.json index bdc584030b..e8fddbcaaa 100644 --- a/src/translations/en.json +++ b/src/translations/en.json @@ -642,6 +642,8 @@ "editor": { "introduction": "Use automations to bring your home alive", "default_name": "New Automation", + "load_error_not_editable": "Only automations in automations.yaml are editable.", + "load_error_unknown": "Error loading automation ({err_no}).", "save": "Save", "unsaved_confirm": "You have unsaved changes. Are you sure you want to leave?", "alias": "Name", From 642ba1adc39993f23cc9e95fa7768b41549f1947 Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Thu, 23 May 2019 22:16:24 +0200 Subject: [PATCH 5/8] Create dummy translation on dev (#3213) --- build-scripts/gulp/app.js | 2 +- build-scripts/gulp/translations.js | 117 +++++++++++++++++++++-------- 2 files changed, 87 insertions(+), 32 deletions(-) diff --git a/build-scripts/gulp/app.js b/build-scripts/gulp/app.js index 37aad5fe34..1396af5528 100644 --- a/build-scripts/gulp/app.js +++ b/build-scripts/gulp/app.js @@ -21,7 +21,7 @@ gulp.task( "gen-icons", "gen-pages-dev", "gen-index-app-dev", - "build-translations" + gulp.series("create-test-translation", "build-translations") ), "copy-static", "webpack-watch-app" diff --git a/build-scripts/gulp/translations.js b/build-scripts/gulp/translations.js index 3cf30958b1..c02a158c75 100755 --- a/build-scripts/gulp/translations.js +++ b/build-scripts/gulp/translations.js @@ -1,6 +1,7 @@ const del = require("del"); const path = require("path"); const gulp = require("gulp"); +const fs = require("fs"); const foreach = require("gulp-foreach"); const hash = require("gulp-hash"); const hashFilename = require("gulp-hash-filename"); @@ -72,6 +73,20 @@ function emptyFilter(data) { return newData; } +function recursiveEmpty(data) { + const newData = {}; + Object.keys(data).forEach((key) => { + if (data[key]) { + if (typeof data[key] === "object") { + newData[key] = recursiveEmpty(data[key]); + } else { + newData[key] = "TRANSLATED"; + } + } + }); + return newData; +} + /** * Replace Lokalise key placeholders with their actual values. * @@ -108,6 +123,37 @@ gulp.task(taskName, function() { }); tasks.push(taskName); +taskName = "create-test-metadata"; +gulp.task(taskName, function(cb) { + fs.writeFile( + workDir + "/testMetadata.json", + JSON.stringify({ + test: { + nativeName: "Test", + }, + }), + cb + ); +}); +tasks.push(taskName); + +taskName = "create-test-translation"; +gulp.task( + taskName, + gulp.series("create-test-metadata", function() { + return gulp + .src("src/translations/en.json") + .pipe( + transform(function(data, file) { + return recursiveEmpty(data); + }) + ) + .pipe(rename("test.json")) + .pipe(gulp.dest(workDir)); + }) +); +tasks.push(taskName); + /** * This task will build a master translation file, to be used as the base for * all languages. This starts with src/translations/en.json, and replaces all @@ -138,33 +184,38 @@ taskName = "build-merged-translations"; gulp.task( taskName, gulp.series("build-master-translation", function() { - return gulp.src(inDir + "/*.json").pipe( - foreach(function(stream, file) { - // For each language generate a merged json file. It begins with the master - // translation as a failsafe for untranslated strings, and merges all parent - // tags into one file for each specific subtag - // - // TODO: This is a naive interpretation of BCP47 that should be improved. - // Will be OK for now as long as we don't have anything more complicated - // than a base translation + region. - const tr = path.basename(file.history[0], ".json"); - const subtags = tr.split("-"); - 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"); - } - return gulp - .src(src, { allowEmpty: true }) - .pipe(transform((data) => emptyFilter(data))) - .pipe( - merge({ - fileName: tr + ".json", - }) - ) - .pipe(gulp.dest(fullDir)); - }) - ); + return gulp + .src([inDir + "/*.json", workDir + "/test.json"], { allowEmpty: true }) + .pipe( + foreach(function(stream, file) { + // For each language generate a merged json file. It begins with the master + // translation as a failsafe for untranslated strings, and merges all parent + // tags into one file for each specific subtag + // + // TODO: This is a naive interpretation of BCP47 that should be improved. + // Will be OK for now as long as we don't have anything more complicated + // 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", + ]; + for (let i = 1; i <= subtags.length; i++) { + const lang = subtags.slice(0, i).join("-"); + src.push(inDir + "/" + lang + ".json"); + } + return gulp + .src(src, { allowEmpty: true }) + .pipe(transform((data) => emptyFilter(data))) + .pipe( + merge({ + fileName: tr + ".json", + }) + ) + .pipe(gulp.dest(fullDir)); + }) + ); }) ); tasks.push(taskName); @@ -299,10 +350,14 @@ gulp.task( taskName, gulp.series("build-translation-fingerprints", function() { return gulp - .src([ - "src/translations/translationMetadata.json", - workDir + "/translationFingerprints.json", - ]) + .src( + [ + "src/translations/translationMetadata.json", + workDir + "/testMetadata.json", + workDir + "/translationFingerprints.json", + ], + { allowEmpty: true } + ) .pipe(merge({})) .pipe( transform(function(data) { From 7f470797505ef61e96623e6362e58620e08ff33e Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Thu, 23 May 2019 13:16:50 -0700 Subject: [PATCH 6/8] Notify about remote portal (#3211) * Notify about remote portal * Update text --- src/layouts/ha-init-page.ts | 46 +++++++++++++++++++++---------------- 1 file changed, 26 insertions(+), 20 deletions(-) diff --git a/src/layouts/ha-init-page.ts b/src/layouts/ha-init-page.ts index bca3480dfc..6d34ae4e14 100644 --- a/src/layouts/ha-init-page.ts +++ b/src/layouts/ha-init-page.ts @@ -1,37 +1,37 @@ import "@polymer/paper-spinner/paper-spinner-lite"; import "@material/mwc-button"; -import { - LitElement, - PropertyDeclarations, - html, - CSSResult, - css, -} from "lit-element"; +import { LitElement, html, CSSResult, css, property } from "lit-element"; import { removeInitSkeleton } from "../util/init-skeleton"; class HaInitPage extends LitElement { - public error?: boolean; - - static get properties(): PropertyDeclarations { - return { - error: { - type: Boolean, - }, - }; - } + @property({ type: Boolean }) public error = false; protected render() { return html`
- ${this.error ? html` - Unable to connect to Home Assistant. +

Unable to connect to Home Assistant.

Retry + ${location.host.includes("ui.nabu.casa") + ? html` +

+ It is possible that you are seeing this screen because + your Home Assistant is not currently connected. You can + ask it to come online via + the Remote UI portal. +

+ ` + : ""} ` - : "Loading data"} + : html` + +

Loading data

+ `}
`; } @@ -54,7 +54,13 @@ class HaInitPage extends LitElement { align-items: center; } paper-spinner-lite { - margin-bottom: 10px; + margin-top: 9px; + } + a { + color: var(--primary-color); + } + p { + max-width: 350px; } `; } From c826596529a716b6b8251d7a1ea5e976f4d2f895 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Thu, 23 May 2019 13:20:55 -0700 Subject: [PATCH 7/8] Update translations --- translations/de.json | 2 +- translations/el.json | 22 ++++++++++- translations/en.json | 27 +++++++++++++- translations/es.json | 12 ++++-- translations/fr.json | 2 +- translations/he.json | 42 +++++++++++++++++++-- translations/it.json | 20 ++++++++-- translations/nb.json | 14 +++---- translations/nl.json | 6 +++ translations/pl.json | 26 ++++++------- translations/ru.json | 2 +- translations/sr.json | 78 ++++++++++++++++++++++++++++++++++++++- translations/th.json | 24 +++++++++++- translations/zh-Hans.json | 10 +++-- translations/zh-Hant.json | 2 +- 15 files changed, 245 insertions(+), 44 deletions(-) diff --git a/translations/de.json b/translations/de.json index 8a2a8e33c7..b6cbdcad50 100644 --- a/translations/de.json +++ b/translations/de.json @@ -1118,7 +1118,7 @@ "sun": { "elevation": "Höhe", "rising": "Aufgang", - "setting": "Einstellung" + "setting": "Untergang" }, "updater": { "title": "Update-Anweisungen" diff --git a/translations/el.json b/translations/el.json index e47d0690c1..3673f15137 100644 --- a/translations/el.json +++ b/translations/el.json @@ -603,6 +603,12 @@ "device_unavailable": "συσκευή μη διαθέσιμη", "entity_unavailable": "οντότητα μη διαθέσιμη", "no_area": "Καμία περιοχή" + }, + "config_flow": { + "external_step": { + "description": "Αυτό το βήμα απαιτεί να επισκεφτείτε μια εξωτερική ιστοσελίδα για να ολοκληρωθεί.", + "open_site": "Άνοιγμα ιστότοπου" + } } }, "zha": { @@ -855,6 +861,11 @@ "required_fields": "Συμπληρώστε όλα τα υποχρεωτικά πεδία", "password_not_match": "Οι κωδικοί πρόσβασης δεν ταιριάζουν" } + }, + "integration": { + "intro": "Οι συσκευές και οι υπηρεσίες εκπροσωπούνται στο Home Assistant ως ενσωματώσεις. Μπορείτε να τα ρυθμίσετε τώρα ή αργότερα από την οθόνη διαμόρφωσης.", + "more_integrations": "Περισσότερα", + "finish": "Τέλος" } }, "lovelace": { @@ -868,6 +879,14 @@ "title": "Καλωσορίσατε στην αρχική σελίδα", "no_devices": "Αυτή η σελίδα σάς επιτρέπει να ελέγχετε τις συσκευές σας, ωστόσο φαίνεται ότι δεν έχετε ακόμα ρυθμίσει συσκευές. Μεταβείτε στη σελίδα ενοποίησης για να ξεκινήσετε.", "go_to_integrations_page": "Μεταβείτε στη σελίδα ενοποίησης." + }, + "picture-elements": { + "hold": "Αναστολή:", + "tap": "Ταπ:", + "navigate_to": "Μεταβείτε στην {location}", + "toggle": "Εναλλαγή {name}", + "call_service": "Κλήση υπηρεσίας {name}", + "more_info": "Εμφάνιση περισσότερων πληροφοριών: {name}" } }, "editor": { @@ -925,7 +944,8 @@ }, "sidebar": { "log_out": "Αποσύνδεση", - "developer_tools": "Εργαλεία προγραμματιστή" + "developer_tools": "Εργαλεία προγραμματιστή", + "external_app_configuration": "Διαμόρφωση Εφαρμογής" }, "common": { "loading": "Φόρτωση", diff --git a/translations/en.json b/translations/en.json index 94e1ccbf3e..d7470342c0 100644 --- a/translations/en.json +++ b/translations/en.json @@ -355,6 +355,21 @@ "introduction": "Control your Home Assistant server… from Home Assistant.", "restart": "Restart", "stop": "Stop" + }, + "core_config": { + "edit_requires_storage": "Editor disabled because config stored in configuration.yaml.", + "location_name": "Name of your Home Assistant installation", + "latitude": "Latitude", + "longitude": "Longitude", + "elevation": "Elevation", + "elevation_meters": "meters", + "time_zone": "Time Zone", + "unit_system": "Unit System", + "unit_system_imperial": "Imperial", + "unit_system_metric": "Metric", + "imperial_example": "Fahrenheit, pounds", + "metric_example": "Celsius, kilograms", + "save_button": "Save" } } } @@ -545,7 +560,9 @@ } }, "learn_more": "Learn more about actions" - } + }, + "load_error_not_editable": "Only automations in automations.yaml are editable.", + "load_error_unknown": "Error loading automation ({err_no})." } }, "script": { @@ -866,6 +883,14 @@ "intro": "Devices and services are represented in Home Assistant as integrations. You can set them up now, or do it later from the configuration screen.", "more_integrations": "More", "finish": "Finish" + }, + "core-config": { + "intro": "Hello {name}, welcome to Home Assistant. How would you like to name your home?", + "intro_location": "We would like to know where you live. This information will help with displaying information and setting up sun-based automations. This data is never shared outside of your network.", + "intro_location_detect": "We can help you fill in this information by making a one-time request to an external service.", + "location_name_default": "Home", + "button_detect": "Detect", + "finish": "Next" } }, "lovelace": { diff --git a/translations/es.json b/translations/es.json index 30461a237a..fac70b22e4 100644 --- a/translations/es.json +++ b/translations/es.json @@ -286,9 +286,9 @@ "alarm_control_panel": { "armed": "Armado", "disarmed": "Desarmar", - "armed_home": "Activada", - "armed_away": "Activada", - "armed_night": "Activada", + "armed_home": "Armada", + "armed_away": "Armada", + "armed_night": "Armada", "pending": "Pendiente", "arming": "Armando", "disarming": "Desarmar", @@ -603,6 +603,12 @@ "device_unavailable": "dispositivo no disponible", "entity_unavailable": "entidad no disponible", "no_area": "Ninguna área" + }, + "config_flow": { + "external_step": { + "description": "Este paso requiere que visites una web externa para ser completado.", + "open_site": "Abrir sitio web" + } } }, "zha": { diff --git a/translations/fr.json b/translations/fr.json index 30bd95f16f..b7fe7eaf3b 100644 --- a/translations/fr.json +++ b/translations/fr.json @@ -484,7 +484,7 @@ "label": "Valeur numérique", "above": "Dessus", "below": "Sous", - "value_template": "Contenu du template (optionnel)" + "value_template": "Contenu du modèle (optionnel)" }, "sun": { "label": "Soleil", diff --git a/translations/he.json b/translations/he.json index 43d14f146c..c627e9d87c 100644 --- a/translations/he.json +++ b/translations/he.json @@ -279,7 +279,9 @@ "state_badge": { "default": { "unknown": "לא ידוע", - "unavailable": "לא זמין" + "unavailable": "לא זמין", + "error": "שגיאה", + "entity_not_found": "ישות לא נמצאה" }, "alarm_control_panel": { "armed": "מופעל", @@ -579,7 +581,8 @@ "cloud": { "caption": "ענן Home Assistant", "description_login": "מחובר בתור {email} מה", - "description_not_login": "לא מחובר" + "description_not_login": "לא מחובר", + "description_features": "שליטה הרחק מהבית, משתלב עם Alexa ו Google Assistant." }, "integrations": { "caption": "אינטגרציות", @@ -600,6 +603,12 @@ "device_unavailable": "התקן אינו זמין", "entity_unavailable": "ישות לא זמינה", "no_area": "ללא אזור" + }, + "config_flow": { + "external_step": { + "description": "שלב זה מחייב אותך לבקר באתר אינטרנט חיצוני להשלמת הפעולה.", + "open_site": "פתח אתר" + } } }, "zha": { @@ -607,7 +616,18 @@ "description": "ניהול רשת Zigbee לאוטומציה ביתית", "services": { "reconfigure": "התקן מחדש את התקן ה ZHA. השתמש באפשרות זו אם אתה נתקל בבעיות בהתקן. אם ההתקן המדובר הוא התקן המופעל באמצעות סוללות, ודא שהוא ער ויכול לקבל פקודות בעת שימוש בשירות זה.", - "updateDeviceName": "הגדר שם מותאם אישית עבור התקן זה במאגר ההתקנים." + "updateDeviceName": "הגדר שם מותאם אישית עבור התקן זה במאגר ההתקנים.", + "remove": "הסר התקן מרשת ZigBee." + }, + "device_card": { + "device_name_placeholder": "שם פרטי", + "area_picker_label": "אזור", + "update_name_button": "עדכן שם" + }, + "add_device_page": { + "header": "אוטומציית Zigbee - הוספת התקנים", + "spinner": "מחפש מכשירי ZHA Zigbee...", + "discovery_text": ". המכשירים שהתגלו יופיעו כאן. עקוב אחר ההוראות עבור ההתקנים שלך והצב את ההתקנים במצב תיאום." } }, "area_registry": { @@ -841,6 +861,11 @@ "required_fields": "מלא את כל השדות הדרושים", "password_not_match": "הסיסמאות אינן תואמות" } + }, + "integration": { + "intro": "התקנים ושירותים מיוצגים ב- Home Assistant כאינטגרציות. באפשרותך להגדיר אותם כעת, או לעשות זאת מאוחר יותר ממסך התצורה.", + "more_integrations": "עוד", + "finish": "סיום" } }, "lovelace": { @@ -854,6 +879,14 @@ "title": "ברוך הבא הביתה", "no_devices": "דף זה מאפשר לך לשלוט במכשירים שלך, אך נראה שעדיין לא הוגדרו מכשירים. עבור אל דף האינטגרציות כדי להתחיל.", "go_to_integrations_page": "עבור אל דף האינטגרציות." + }, + "picture-elements": { + "hold": "החזק:", + "tap": "הקש:", + "navigate_to": "נווט אל {location}", + "toggle": "החלף מצב {name}", + "call_service": "קריאה לשירות {name}", + "more_info": "הצג מידע נוסף: {name}" } }, "editor": { @@ -911,7 +944,8 @@ }, "sidebar": { "log_out": "התנתק", - "developer_tools": "כלים למפתחים" + "developer_tools": "כלים למפתחים", + "external_app_configuration": "הגדרות היישום" }, "common": { "loading": "טוען", diff --git a/translations/it.json b/translations/it.json index 8c71c84a8b..fa2fb097e3 100644 --- a/translations/it.json +++ b/translations/it.json @@ -603,6 +603,11 @@ "device_unavailable": "dispositivo non disponibile", "entity_unavailable": "entità non disponibile", "no_area": "Nessuna area" + }, + "config_flow": { + "external_step": { + "open_site": "Apri sito Web" + } } }, "zha": { @@ -610,7 +615,7 @@ "description": "Gestione rete Zigbee Home Automation", "services": { "reconfigure": "Riconfigurare il dispositivo ZHA (dispositivo di guarigione). Utilizzare questa opzione se si verificano problemi con il dispositivo. Se il dispositivo in questione è un dispositivo alimentato a batteria, assicurarsi che sia sveglio e che accetti i comandi quando si utilizza questo servizio.", - "updateDeviceName": "Imposta un nome personalizzato per questo dispositivo nel registro del dispositivo.", + "updateDeviceName": "Imposta un nome personalizzato per questo dispositivo nel registro dispositivi.", "remove": "Rimuovi un dispositivo dalla rete ZigBee." }, "device_card": { @@ -630,7 +635,7 @@ "picker": { "header": "Registro di area", "introduction": "Le aree sono utilizzate per organizzare dove sono i dispositivi. Queste informazioni saranno utilizzate in Home Assistant per aiutarti ad organizzare la tua interfaccia, permessi e integrazioni con altri sistemi.", - "introduction2": "Per posizionare i dispositivi in un'area, utilizzare il collegamento seguente per accedere alla pagina delle integrazioni e quindi fare clic su un'integrazione configurata per accedere alle schede del dispositivo.", + "introduction2": "Per posizionare i dispositivi in un'area, utilizzare il seguente collegamento per accedere alla pagina delle integrazioni e quindi fare clic su un'integrazione configurata per accedere alle schede del dispositivo.", "integrations_page": "Integrazioni", "no_areas": "Sembra che tu non abbia ancora delle aree!", "create_area": "CREA AREA" @@ -650,7 +655,7 @@ "picker": { "header": "Registro delle entità", "unavailable": "(non disponibile)", - "introduction": "Home Assistant mantiene un registro di tutte le entità che ha mai localizzato che possono essere identificate in modo univoco. A ciascuna di queste entità sarà assegnato un ID che sarà riservato solo a questa entità.", + "introduction": "Home Assistant mantiene un registro di tutte le entità che ha mai localizzato e possono essere identificate in modo univoco. A ciascuna di queste entità sarà assegnato un ID che sarà riservato solo a questa entità.", "introduction2": "Utilizzare il registro delle entità per sovrascrivere il nome, modificare l'entity ID o rimuovere la voce da Home Assistant. Nota, la rimozione della voce del registro entità non rimuoverà l'entità. Per farlo, segui il link sottostante e rimuovilo dalla pagina delle integrazioni.", "integrations_page": "Integrazioni" }, @@ -855,6 +860,10 @@ "required_fields": "Compila tutti i campi richiesti", "password_not_match": "Le password non corrispondono" } + }, + "integration": { + "intro": "Dispositivi e servizi sono rappresentati in Home Assistant come integrazioni. È possibile impostarli ora, o farlo in seguito dalla schermata di configurazione.", + "finish": "finire" } }, "lovelace": { @@ -868,6 +877,9 @@ "title": "Benvenuto a casa", "no_devices": "Questa pagina ti consente di controllare i tuoi dispositivi, tuttavia sembra che tu non abbia ancora configurato uno. Vai alla pagina delle integrazioni per iniziare.", "go_to_integrations_page": "Vai alla pagina delle integrazioni." + }, + "picture-elements": { + "more_info": "Mostra più informazioni: {name}" } }, "editor": { @@ -926,7 +938,7 @@ "sidebar": { "log_out": "Esci", "developer_tools": "Strumenti per gli sviluppatori", - "external_app_configuration": "Configurazione dell'App" + "external_app_configuration": "Configurazione App" }, "common": { "loading": "Caricamento", diff --git a/translations/nb.json b/translations/nb.json index f89c069eb2..342b844ca9 100644 --- a/translations/nb.json +++ b/translations/nb.json @@ -602,7 +602,7 @@ "firmware": "Fastvare: {version}", "device_unavailable": "enheten er utilgjengelig", "entity_unavailable": "oppføringen er utilgjengelig", - "no_area": "Ingen område" + "no_area": "Intet område" }, "config_flow": { "external_step": { @@ -622,12 +622,12 @@ "device_card": { "device_name_placeholder": "Brukerdefinert navn", "area_picker_label": "Område", - "update_name_button": "Oppdater navn" + "update_name_button": "Oppdatér navn" }, "add_device_page": { "header": "ZigBee Home Automation - Legg til enheter", - "spinner": "Søker etter ZHA Zigbee enheter...", - "discovery_text": "Oppdagede enheter vises her. Følg instruksjonene for enheten(e) og sett enheten(e) i parringsmodus." + "spinner": "Søker etter ZHA Zigbee-enheter...", + "discovery_text": "Oppdagede enheter vises her. Følg instruksjonene for enheten(e) og sett enheten(e) i paringsmodus." } }, "area_registry": { @@ -656,7 +656,7 @@ "picker": { "header": "Enhetsregister", "unavailable": "(utilgjengelig)", - "introduction": "Home Assistant beholder et register over hver enhet som den noensinne har sett som kan identifiseres unikt. Hver av disse enhetene vil ha en enhets ID tilknyttet som vil bli reservert for bare denne enheten.", + "introduction": "Home Assistant bygger opp et register over hver enhet den har sett som kan identifiseres unikt. Hver av disse enhetene vil ha en enhets-ID tilknyttet som vil bli reservert for bare denne enheten.", "introduction2": "Bruk entitetsregisteret for å overskrive navnet, endre enhets ID eller fjern oppføringen fra Home Assistant. Merk at fjerning av enhetsregisteroppføringer ikke fjerner enheten. For å gjøre det, følg linken under og fjern den fra integrasjonssiden.", "integrations_page": "Integrasjonsside" }, @@ -863,7 +863,7 @@ } }, "integration": { - "intro": "Enheter og tjenester er representert i Home Assistant som integrasjoner. Du kan sette dem opp nå, eller gjøre det senere fra konfigurasjonen skjermen.", + "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" } @@ -1199,6 +1199,6 @@ "groups": { "system-admin": "Administratorer", "system-users": "Brukere", - "system-read-only": "Lesetilgang brukere" + "system-read-only": "Brukere med lesetilgang" } } \ No newline at end of file diff --git a/translations/nl.json b/translations/nl.json index 3f880bd706..01d0c6bdd3 100644 --- a/translations/nl.json +++ b/translations/nl.json @@ -603,6 +603,12 @@ "device_unavailable": "apparaat niet beschikbaar", "entity_unavailable": "entiteit niet beschikbaar", "no_area": "Geen gebied" + }, + "config_flow": { + "external_step": { + "description": "Deze stap vereist dat u een externe website bezoekt om dit te voltooien.", + "open_site": "Open website" + } } }, "zha": { diff --git a/translations/pl.json b/translations/pl.json index 7946c28697..2d9db0572c 100644 --- a/translations/pl.json +++ b/translations/pl.json @@ -347,7 +347,7 @@ "introduction": "Niektóre części Home Assistant'a można przeładować bez konieczności ponownego uruchomienia. Kliknięcie przeładuj spowoduje ponowne wczytanie konfiguracji.", "core": "Przeładuj rdzeń", "group": "Przeładuj grupy", - "automation": "Przeładuj reguły automatyzacji", + "automation": "Przeładuj automatyzacje", "script": "Przeładuj skrypty" }, "server_management": { @@ -368,25 +368,25 @@ } }, "automation": { - "caption": "Reguły automatyzacji", - "description": "Twórz i edytuj reguły automatyzacji", + "caption": "Automatyzacje", + "description": "Twórz i edytuj automatyzacje", "picker": { "header": "Edytor automatyzacji", - "introduction": "Edytor automatyzacji pozwala tworzyć i edytować reguły automatyzacji. Kliknij poniższy link, aby przeczytać instrukcję, jak poprawnie skonfigurować reguły automatyzacji w Home Assistant.", - "pick_automation": "Wybierz regułę automatyzacji do edycji", - "no_automations": "Nie znaleziono żadnych edytowalnych reguł automatyzacji", - "add_automation": "Dodaj regułę automatyzacji", - "learn_more": "Dowiedz się więcej o regułach automatyki" + "introduction": "Edytor automatyzacji pozwala tworzyć i edytować automatyzacje Kliknij poniższy link, aby przeczytać instrukcję, jak poprawnie skonfigurować automatyzacje w Home Assistant.", + "pick_automation": "Wybierz automatyzację do edycji", + "no_automations": "Nie znaleziono żadnych edytowalnych automatyzacji", + "add_automation": "Dodaj automatyzację", + "learn_more": "Dowiedz się więcej o automatyzacjach" }, "editor": { - "introduction": "Użyj reguł automatyzacji, aby utrzymać swój dom przy życiu", - "default_name": "Nowa reguła automatyzacji", + "introduction": "Użyj automatyzacji, aby utrzymać swój dom przy życiu", + "default_name": "Nowa automatyzacja", "save": "Zapisz", "unsaved_confirm": "Masz niezapisane zmiany. Jesteś pewny, że chcesz wyjść?", "alias": "Nazwa", "triggers": { "header": "Wyzwalacze", - "introduction": "Wyzwalacze uruchamiają przetwarzanie reguły automatyzacji. Możliwe jest stworzenie wielu wyzwalaczy dla tej samej reguły. Po uruchomieniu wyzwalacza Home Assistant sprawdzi ewentualne warunki i wywoła akcje.", + "introduction": "Wyzwalacze uruchamiają przetwarzanie automatyzacji. Możliwe jest stworzenie wielu wyzwalaczy dla tej samej automatyzacji. Po uruchomieniu wyzwalacza Home Assistant sprawdzi ewentualne warunki i wywoła akcje.", "add": "Dodaj wyzwalacz", "duplicate": "Duplikuj", "delete": "Usuń", @@ -468,7 +468,7 @@ }, "conditions": { "header": "Warunki", - "introduction": "Warunki są opcjonalną częścią reguły automatyzacji i można ich użyć, aby zapobiec działaniom po uruchomieniu. Warunki wyglądają bardzo podobnie do wyzwalaczy, ale są bardzo różne. Wyzwalacz będzie analizował zdarzenia zachodzące w systemie, a warunek będzie dotyczył tylko tego, jak wygląda teraz system. Wyzwalacz może zaobserwować, że przełącznik jest przełączany. Warunek może tylko sprawdzić, czy przełącznik jest aktualnie włączony lub wyłączony.", + "introduction": "Warunki są opcjonalną częścią automatyzacji i można ich użyć, aby zapobiec działaniom po uruchomieniu. Warunki wyglądają bardzo podobnie do wyzwalaczy, ale są bardzo różne. Wyzwalacz będzie analizował zdarzenia zachodzące w systemie, a warunek będzie dotyczył tylko tego, jak wygląda teraz system. Wyzwalacz może zaobserwować, że przełącznik jest przełączany. Warunek może tylko sprawdzić, czy przełącznik jest aktualnie włączony lub wyłączony.", "add": "Dodaj warunek", "duplicate": "Duplikuj", "delete": "Usuń", @@ -514,7 +514,7 @@ }, "actions": { "header": "Akcje", - "introduction": "Akcje są wykonywane przez Home Assistant'a po uruchomieniu reguły automatyzacji.", + "introduction": "Akcje są wykonywane przez Home Assistant'a po uruchomieniu automatyzacji.", "add": "Dodaj akcję", "duplicate": "Duplikuj", "delete": "Usuń", diff --git a/translations/ru.json b/translations/ru.json index 63fe536630..0dbe5fab3d 100644 --- a/translations/ru.json +++ b/translations/ru.json @@ -751,7 +751,7 @@ }, "page-authorize": { "initializing": "Инициализация", - "authorizing_client": "Получение доступа к Home Assistant через {clientId}.", + "authorizing_client": "Получение доступа к {clientId}.", "logging_in_with": "Провайдер аутентификации: **{authProviderName}**.", "pick_auth_provider": "Или войти с помощью", "abort_intro": "Вход прерван", diff --git a/translations/sr.json b/translations/sr.json index a382b0b62a..08263d5752 100644 --- a/translations/sr.json +++ b/translations/sr.json @@ -29,6 +29,10 @@ }, "climate": { "manual": "Упутство" + }, + "timer": { + "active": "укључен", + "idle": "неактна чекању" } }, "state_badge": { @@ -36,6 +40,14 @@ "arming": "Aktiviranje", "disarming": "Deaktiviraj", "armed_custom_bypass": "Aktiviran" + }, + "person": { + "home": "Кући", + "not_home": "Одсутан" + }, + "default": { + "error": "Грешка", + "entity_not_found": "Вредност није пронађена" } }, "ui": { @@ -46,6 +58,60 @@ "config": { "zwave": { "caption": "Z-Wave" + }, + "users": { + "editor": { + "caption": "Прикажи корисника" + }, + "add_user": { + "caption": "Додај корисника", + "name": "Име", + "username": "Корисничко име", + "password": "Лозинка", + "create": "Направите" + } + }, + "automation": { + "picker": { + "learn_more": "Сазнајте више о аутоматизацијама" + }, + "editor": { + "triggers": { + "learn_more": "Сазнајте више о окидачима" + } + } + }, + "integrations": { + "config_flow": { + "external_step": { + "description": "Овај корак захтева да посетите екстерни веб-сајт да би био завршен.", + "open_site": "Отворите сајт" + } + } + } + }, + "page-onboarding": { + "user": { + "data": { + "password_confirm": "Потврда лозинке" + }, + "error": { + "password_not_match": "Лозинке се не подударају" + } + }, + "integration": { + "finish": "Крај" + } + }, + "lovelace": { + "cards": { + "picture-elements": { + "hold": "Придржи:", + "tap": "Додирни", + "navigate_to": "Отиђите на ", + "toggle": "Укључи", + "more_info": "Покажи више информација:" + } } } }, @@ -67,6 +133,16 @@ } }, "domain": { - "zwave": "Z-Wave" + "zwave": "Z-Wave", + "person": "Особа" + }, + "state_attributes": { + "climate": { + "fan_mode": { + "off": "Искључен", + "on": "Укључен", + "auto": "Аутоматски" + } + } } } \ No newline at end of file diff --git a/translations/th.json b/translations/th.json index 28edea93f2..d576fee302 100644 --- a/translations/th.json +++ b/translations/th.json @@ -581,7 +581,8 @@ "cloud": { "caption": "Home Assistant Cloud", "description_login": "ลงชื่อเข้าใช้เป็น {email}", - "description_not_login": "ยังไม่ได้เข้าสู่ระบบ" + "description_not_login": "ยังไม่ได้เข้าสู่ระบบ", + "description_features": "ควบคุมการทำงานเมื่อออกจากบ้านร่วมกับ Alexa และ Google Assistant" }, "integrations": { "caption": "การทำงานร่วมกัน", @@ -602,6 +603,12 @@ "device_unavailable": "อุปกรณ์นี้ไม่พร้อมใช้งาน", "entity_unavailable": "Entity นี้ไม่พร้อมใช้งาน", "no_area": "ไม่มีห้อง" + }, + "config_flow": { + "external_step": { + "description": "ขั้นตอนนี้กำหนดให้คุณเยี่ยมชมเว็บไซต์ภายนอกเพื่อทำให้การดำเนินการสมบูรณ์", + "open_site": "เปิดเว็บไซต์" + } } }, "zha": { @@ -854,6 +861,10 @@ "required_fields": "กรอกข้อมูลในฟิลด์ที่จำเป็นทั้งหมด", "password_not_match": "รหัสผ่านไม่ตรงกัน" } + }, + "integration": { + "more_integrations": "เพิ่มเติม", + "finish": "เสร็จสิ้น" } }, "lovelace": { @@ -867,6 +878,14 @@ "title": "ยินดีต้อนรับกลับบ้าน", "no_devices": "หน้านี้ช่วยให้คุณควบคุมอุปกรณ์ของคุณได้ แต่ยังไงก็ตามมันดูเหมือนว่าคุณยังไม่ได้ติดตั้งอุปกรณ์อะไรไว้เลย ไปยัง 'หน้าการทำงานด้วยกัน' เพื่อเริ่มติดตั้ง", "go_to_integrations_page": "ไปที่หน้าการรวมระบบ" + }, + "picture-elements": { + "hold": "ถือ:", + "tap": "แตะ:", + "navigate_to": "นำทางไปยัง {location}", + "toggle": "สลับ {name}", + "call_service": "เรียกบริการ {name}", + "more_info": "แสดงข้อมูลเพิ่มเติม: {name}" } }, "editor": { @@ -924,7 +943,8 @@ }, "sidebar": { "log_out": "ออกจากระบบ", - "developer_tools": "เครื่องมือสำหรับนักพัฒนา" + "developer_tools": "เครื่องมือสำหรับนักพัฒนา", + "external_app_configuration": "การกำหนดค่าแอพ" }, "common": { "loading": "กำลังโหลด", diff --git a/translations/zh-Hans.json b/translations/zh-Hans.json index 4a972f923d..90a4bb3772 100644 --- a/translations/zh-Hans.json +++ b/translations/zh-Hans.json @@ -173,7 +173,7 @@ "not_home": "离开", "open": "开启", "opening": "正在打开", - "closed": "关闭", + "closed": "已关闭", "closing": "正在关闭", "stopped": "已停止", "locked": "已锁定", @@ -372,7 +372,7 @@ "description": "创建和编辑自动化", "picker": { "header": "自动化编辑器", - "introduction": "自动化编辑器方便你创建及编辑自动化。请阅读相关[说明](https:\/\/home-assistant.io\/docs\/automation\/editor\/)以确保正确配置。", + "introduction": "自动化编辑器方便你创建及编辑自动化。请按照下面的链接阅读说明,以确保您已正确配置Home Assistant。", "pick_automation": "选择要编辑的自动化", "no_automations": "未找到可编辑的自动化", "add_automation": "添加自动化", @@ -468,7 +468,7 @@ }, "conditions": { "header": "环境条件", - "introduction": "环境条件是自动化流程中的可选条件,它可用于避免触发条件满足时动作的执行。环境条件看似像触发条件实则大有不同。触发条件监测系统中事件的发生,也就是瞬时动作;而环境条件监测的是系统的状态。例如,“灯被打开”这个事件属于触发条件,“灯是开着的”这个状态就属于环境条件。\n\n[了解更多内容](https:\/\/home-assistant.io\/docs\/scripts\/conditions\/)", + "introduction": "环境条件是自动化流程中的可选部分,它可用于触发条件触发时拦截不符合条件的动作。环境条件看似像触发条件实则大有不同。触发条件监测系统中事件的发生,也就是瞬时动作;而环境条件监测的是系统当前的状态。触发条件可以观察到开关打开的动作。条件只能查看当前开关是开还是关。", "add": "添加环境条件", "duplicate": "复制", "delete": "删除", @@ -606,7 +606,7 @@ }, "config_flow": { "external_step": { - "description": "此步骤要求您访问要完成的外部网站。", + "description": "此步骤需要访问外部网站才能完成。", "open_site": "打开网站" } } @@ -621,6 +621,7 @@ }, "device_card": { "device_name_placeholder": "用户指定的名称", + "area_picker_label": "区域", "update_name_button": "更新名称" }, "add_device_page": { @@ -862,6 +863,7 @@ } }, "integration": { + "intro": "设备和服务在 Home Assistant 中表示为集成。您可以立即设置它们,也可以稍后在配置屏幕进行设置。", "more_integrations": "更多", "finish": "完成" } diff --git a/translations/zh-Hant.json b/translations/zh-Hant.json index 6dfc63d41a..0738ed0558 100644 --- a/translations/zh-Hant.json +++ b/translations/zh-Hant.json @@ -1052,7 +1052,7 @@ }, "lock": { "code": "密碼", - "lock": "鎖", + "lock": "上鎖", "unlock": "解鎖" }, "vacuum": { From a1e9b4938f7dbf224cf0d522c2e4950e0e228e4e Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Thu, 23 May 2019 13:20:59 -0700 Subject: [PATCH 8/8] Bumped version to 20190523.0 --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 2ff7821551..c856037abb 100644 --- a/setup.py +++ b/setup.py @@ -2,7 +2,7 @@ from setuptools import setup, find_packages setup( name="home-assistant-frontend", - version="20190514.0", + version="20190523.0", description="The Home Assistant frontend", url="https://github.com/home-assistant/home-assistant-polymer", author="The Home Assistant Authors",