+
+
${!remote_connected && remote_enabled
? html`
@@ -98,18 +112,6 @@ export class CloudRemotePref extends LitElement {
>
-
- ${this.hass.localize(
- "ui.panel.config.cloud.account.remote.link_learn_how_it_works"
- )}
-
-
${this.hass.localize(
"ui.panel.config.cloud.account.remote.certificate_info"
@@ -158,15 +160,24 @@ export class CloudRemotePref extends LitElement {
a {
color: var(--primary-color);
}
- .switch {
+ .header-actions {
position: absolute;
right: 24px;
top: 24px;
+ display: flex;
+ flex-direction: row;
}
- :host([dir="rtl"]) .switch {
+ :host([dir="rtl"]) .header-actions {
right: auto;
left: 24px;
}
+ .header-actions .icon-link {
+ margin-top: -16px;
+ margin-inline-end: 8px;
+ margin-right: 8px;
+ direction: var(--direction);
+ color: var(--secondary-text-color);
+ }
.warning {
font-weight: bold;
margin-bottom: 1em;
@@ -179,19 +190,12 @@ export class CloudRemotePref extends LitElement {
right: 24px;
top: 24px;
}
- :host([dir="rtl"]) .switch {
- right: auto;
- left: 24px;
- }
.card-actions {
display: flex;
}
.card-actions a {
text-decoration: none;
}
- .spacer {
- flex-grow: 1;
- }
ha-svg-icon {
--mdc-icon-size: 18px;
color: var(--secondary-text-color);
diff --git a/src/panels/config/cloud/alexa/cloud-alexa.ts b/src/panels/config/cloud/alexa/cloud-alexa.ts
index d388466f8d..0ac078e360 100644
--- a/src/panels/config/cloud/alexa/cloud-alexa.ts
+++ b/src/panels/config/cloud/alexa/cloud-alexa.ts
@@ -5,6 +5,9 @@ import {
mdiCheckboxMultipleMarked,
mdiCloseBox,
mdiCloseBoxMultiple,
+ mdiDotsVertical,
+ mdiFormatListChecks,
+ mdiSync,
} from "@mdi/js";
import type { UnsubscribeFunc } from "home-assistant-js-websocket";
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
@@ -26,7 +29,11 @@ import "../../../../components/ha-card";
import "../../../../components/ha-formfield";
import "../../../../components/ha-icon-button";
import "../../../../components/ha-switch";
-import { AlexaEntity, fetchCloudAlexaEntities } from "../../../../data/alexa";
+import {
+ AlexaEntity,
+ fetchCloudAlexaEntities,
+ syncCloudAlexaEntities,
+} from "../../../../data/alexa";
import {
AlexaEntityConfig,
CloudPreferences,
@@ -59,6 +66,8 @@ class CloudAlexa extends SubscribeMixin(LitElement) {
@state() private _entities?: AlexaEntity[];
+ @state() private _syncing = false;
+
@state()
private _entityConfigs: CloudPreferences["alexa_entity_configs"] = {};
@@ -222,74 +231,84 @@ class CloudAlexa extends SubscribeMixin(LitElement) {
}
return html`
-
- ${
- emptyFilter
- ? html`
- ${this.hass!.localize(
- "ui.panel.config.cloud.alexa.manage_defaults"
- )}
- `
- : ""
- }
- ${
- !emptyFilter
- ? html`
-
- ${this.hass!.localize("ui.panel.config.cloud.alexa.banner")}
-
- `
- : ""
- }
- ${
- exposedCards.length > 0
- ? html`
-
- ${exposedCards}
- `
- : ""
- }
- ${
- notExposedCards.length > 0
- ? html`
-
- ${notExposedCards}
- `
- : ""
- }
-
+
+
+
+
+
+ ${this.hass.localize("ui.panel.config.cloud.alexa.manage_defaults")}
+
+
+
+
+ ${this.hass.localize("ui.panel.config.cloud.alexa.sync_entities")}
+
+
+
+ ${!emptyFilter
+ ? html`
+
+ ${this.hass!.localize("ui.panel.config.cloud.alexa.banner")}
+
+ `
+ : ""}
+ ${exposedCards.length > 0
+ ? html`
+
+ ${exposedCards}
+ `
+ : ""}
+ ${notExposedCards.length > 0
+ ? html`
+
+ ${notExposedCards}
+ `
+ : ""}
`;
}
@@ -423,6 +442,21 @@ class CloudAlexa extends SubscribeMixin(LitElement) {
});
}
+ private async _handleSync() {
+ this._syncing = true;
+ try {
+ await syncCloudAlexaEntities(this.hass!);
+ } catch (err: any) {
+ alert(
+ `${this.hass!.localize(
+ "ui.panel.config.cloud.alexa.sync_entities_error"
+ )} ${err.body.message}`
+ );
+ } finally {
+ this._syncing = false;
+ }
+ }
+
private async _updateDomainExposed(domain: string, expose: boolean) {
const defaultExpose =
this.cloudStatus.prefs.alexa_default_expose ||
diff --git a/src/panels/config/cloud/google-assistant/cloud-google-assistant.ts b/src/panels/config/cloud/google-assistant/cloud-google-assistant.ts
index f177094f8a..da22c0fc0b 100644
--- a/src/panels/config/cloud/google-assistant/cloud-google-assistant.ts
+++ b/src/panels/config/cloud/google-assistant/cloud-google-assistant.ts
@@ -5,6 +5,9 @@ import {
mdiCheckboxMultipleMarked,
mdiCloseBox,
mdiCloseBoxMultiple,
+ mdiDotsVertical,
+ mdiFormatListChecks,
+ mdiSync,
} from "@mdi/js";
import type { UnsubscribeFunc } from "home-assistant-js-websocket";
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
@@ -45,6 +48,7 @@ import {
GoogleEntity,
} from "../../../../data/google_assistant";
import { showDomainTogglerDialog } from "../../../../dialogs/domain-toggler/show-dialog-domain-toggler";
+import { showAlertDialog } from "../../../../dialogs/generic/show-dialog-box";
import "../../../../layouts/hass-loading-screen";
import "../../../../layouts/hass-subpage";
import { SubscribeMixin } from "../../../../mixins/subscribe-mixin";
@@ -64,6 +68,8 @@ class CloudGoogleAssistant extends SubscribeMixin(LitElement) {
@state() private _entities?: GoogleEntity[];
+ @state() private _syncing = false;
+
@state()
private _entityConfigs: CloudPreferences["google_entity_configs"] = {};
@@ -249,19 +255,39 @@ class CloudGoogleAssistant extends SubscribeMixin(LitElement) {
.hass=${this.hass}
.header=${this.hass!.localize("ui.panel.config.cloud.google.title")}
.narrow=${this.narrow}>
- ${
- emptyFilter
- ? html`
-
${this.hass!.localize(
- "ui.panel.config.cloud.google.manage_defaults"
- )}
- `
- : ""
- }
+
+
+
+
+ ${this.hass.localize(
+ "ui.panel.config.cloud.google.manage_defaults"
+ )}
+
+
+
+
+ ${this.hass.localize("ui.panel.config.cloud.google.sync_entities")}
+
+
+
${
!emptyFilter
? html`
@@ -506,6 +532,31 @@ class CloudGoogleAssistant extends SubscribeMixin(LitElement) {
);
}
+ private async _handleSync() {
+ this._syncing = true;
+ try {
+ await cloudSyncGoogleAssistant(this.hass!);
+ } catch (err: any) {
+ showAlertDialog(this, {
+ title: this.hass.localize(
+ `ui.panel.config.cloud.google.${
+ err.status_code === 404
+ ? "not_configured_title"
+ : "sync_failed_title"
+ }`
+ ),
+ text: this.hass.localize(
+ `ui.panel.config.cloud.google.${
+ err.status_code === 404 ? "not_configured_text" : "sync_failed_text"
+ }`
+ ),
+ });
+ fireEvent(this, "ha-refresh-cloud-status");
+ } finally {
+ this._syncing = false;
+ }
+ }
+
private _ensureEntitySync() {
if (this._popstateSyncAttached) {
return;
diff --git a/src/translations/en.json b/src/translations/en.json
index 75e0a794af..5bbe3ca627 100755
--- a/src/translations/en.json
+++ b/src/translations/en.json
@@ -2494,14 +2494,13 @@
"config_documentation": "Configuration documentation",
"enable_state_reporting": "Enable State Reporting",
"info_state_reporting": "If you enable state reporting, Home Assistant will send all state changes of exposed entities to Amazon. This allows you to always see the latest states in the Alexa app and use the state changes to create routines.",
- "sync_entities": "Sync Entities to Amazon",
- "manage_entities": "Manage Entities",
- "sync_entities_error": "Failed to sync entities:",
"state_reporting_error": "Unable to {enable_disable} report state.",
+ "manage_entities": "Manage Entities",
"enable": "enable",
"disable": "disable",
"not_configured_title": "Alexa is not activated",
- "not_configured_text": "Before you can use Alexa, you need to activate the Home Assistant skill for Alexa in the Alexa app."
+ "not_configured_text": "Before you can use Alexa, you need to activate the Home Assistant skill for Alexa in the Alexa app.",
+ "link_learn_how_it_works": "[%key:ui::panel::config::cloud::account::remote::link_learn_how_it_works%]"
},
"google": {
"title": "Google Assistant",
@@ -2516,13 +2515,11 @@
"enter_pin_info": "Please enter a PIN to interact with security devices. Security devices are doors, garage doors and locks. You will be asked to say/enter this PIN when interacting with such devices via Google Assistant.",
"devices_pin": "Security Devices PIN",
"enter_pin_hint": "Enter a PIN to use security devices",
- "sync_entities": "Sync Entities to Google",
"manage_entities": "Manage Entities",
"enter_pin_error": "Unable to store PIN:",
"not_configured_title": "Google Assistant is not activated",
"not_configured_text": "Before you can use Google Assistant, you need to activate the Home Assistant Cloud skill for Google Assistant in the Google Home app.",
- "sync_failed_title": "Syncing failed",
- "sync_failed_text": "Syncing your entities failed, try again or check the logs."
+ "link_learn_how_it_works": "[%key:ui::panel::config::cloud::account::remote::link_learn_how_it_works%]"
},
"webhooks": {
"title": "Webhooks",
@@ -2549,7 +2546,9 @@
"follow_domain": "[%key:ui::panel::config::cloud::google::follow_domain%]",
"exposed": "[%key:ui::panel::config::cloud::google::exposed%]",
"not_exposed": "[%key:ui::panel::config::cloud::google::not_exposed%]",
- "expose": "Expose to Alexa"
+ "expose": "Expose to Alexa",
+ "sync_entities": "Synchronize entities",
+ "sync_entities_error": "Failed to sync entities:"
},
"dialog_certificate": {
"certificate_information": "Certificate Information",
@@ -2572,7 +2571,13 @@
"follow_domain": "Follow domain",
"exposed": "{selected} exposed",
"not_exposed": "{selected} not exposed",
- "sync_to_google": "Synchronizing changes to Google."
+ "sync_to_google": "Synchronizing changes to Google.",
+ "sync_entities": "Synchronize entities",
+ "sync_entities_error": "Failed to sync entities:",
+ "not_configured_title": "[%key:ui::panel::config::cloud::account::google::not_configured_title%]",
+ "not_configured_text": "[%key:ui::panel::config::cloud::account::google::not_configured_text%]",
+ "sync_failed_title": "Syncing failed",
+ "sync_failed_text": "Syncing your entities failed, try again or check the logs."
},
"dialog_cloudhook": {
"webhook_for": "Webhook for {name}",