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"