diff --git a/hassio/src/system/hassio-supervisor-info.ts b/hassio/src/system/hassio-supervisor-info.ts
index 2825e2f9ba..4aaf394d5e 100644
--- a/hassio/src/system/hassio-supervisor-info.ts
+++ b/hassio/src/system/hassio-supervisor-info.ts
@@ -8,6 +8,7 @@ import {
property,
TemplateResult,
} from "lit-element";
+import { atLeastVersion } from "../../../src/common/config/version";
import { fireEvent } from "../../../src/common/dom/fire_event";
import "../../../src/components/buttons/ha-progress-button";
import "../../../src/components/ha-card";
@@ -150,30 +151,32 @@ class HassioSupervisorInfo extends LitElement {
${this.supervisor.supervisor.supported
- ? html`
-
- ${this.supervisor.localize(
- "system.supervisor.share_diagnostics"
- )}
-
-
- ${this.supervisor.localize(
- "system.supervisor.share_diagnostics_description"
- )}
-
-
-
- `
+ ? !atLeastVersion(this.hass.config.version, 2021, 4)
+ ? html`
+
+ ${this.supervisor.localize(
+ "system.supervisor.share_diagnostics"
+ )}
+
+
+ ${this.supervisor.localize(
+ "system.supervisor.share_diagnostics_description"
+ )}
+
+
+
+ `
+ : ""
: html`
${this.supervisor.localize(
"system.supervisor.unsupported_title"
diff --git a/src/components/ha-analytics.ts b/src/components/ha-analytics.ts
new file mode 100644
index 0000000000..d2833940c2
--- /dev/null
+++ b/src/components/ha-analytics.ts
@@ -0,0 +1,188 @@
+import "@polymer/paper-tooltip/paper-tooltip";
+import {
+ css,
+ CSSResult,
+ customElement,
+ html,
+ LitElement,
+ property,
+ TemplateResult,
+} from "lit-element";
+import { isComponentLoaded } from "../common/config/is_component_loaded";
+import { fireEvent } from "../common/dom/fire_event";
+import { Analytics, AnalyticsPreferences } from "../data/analytics";
+import { haStyle } from "../resources/styles";
+import { HomeAssistant } from "../types";
+import { documentationUrl } from "../util/documentation-url";
+import "./ha-checkbox";
+import type { HaCheckbox } from "./ha-checkbox";
+import "./ha-settings-row";
+
+const ADDITIONAL_PREFERENCES = ["usage", "statistics"];
+
+declare global {
+ interface HASSDomEvents {
+ "analytics-preferences-changed": { preferences: AnalyticsPreferences };
+ }
+}
+
+@customElement("ha-analytics")
+export class HaAnalytics extends LitElement {
+ @property({ attribute: false }) public hass!: HomeAssistant;
+
+ @property({ attribute: false }) public analytics!: Analytics;
+
+ protected render(): TemplateResult {
+ if (!this.analytics.huuid) {
+ return html``;
+ }
+
+ const enabled = this.analytics.preferences.base;
+
+ return html`
+
+ ${this.hass.localize(
+ "ui.panel.config.core.section.core.analytics.instance_id",
+ "huuid",
+ this.analytics.huuid
+ )}
+
+
+
+
+
+
+
+ ${this.hass.localize(
+ `ui.panel.config.core.section.core.analytics.preference.base.title`
+ )}
+
+
+ ${this.hass.localize(
+ `ui.panel.config.core.section.core.analytics.preference.base.description`
+ )}
+
+
+ ${ADDITIONAL_PREFERENCES.map(
+ (preference) =>
+ html`
+
+
+
+ ${!enabled
+ ? html`${this.hass.localize(
+ "ui.panel.config.core.section.core.analytics.needs_base"
+ )}
+ `
+ : ""}
+
+
+ ${preference === "usage"
+ ? isComponentLoaded(this.hass, "hassio")
+ ? this.hass.localize(
+ `ui.panel.config.core.section.core.analytics.preference.usage_supervisor.title`
+ )
+ : this.hass.localize(
+ `ui.panel.config.core.section.core.analytics.preference.usage.title`
+ )
+ : this.hass.localize(
+ `ui.panel.config.core.section.core.analytics.preference.${preference}.title`
+ )}
+
+
+ ${preference === "usage"
+ ? isComponentLoaded(this.hass, "hassio")
+ ? this.hass.localize(
+ `ui.panel.config.core.section.core.analytics.preference.usage_supervisor.description`
+ )
+ : this.hass.localize(
+ `ui.panel.config.core.section.core.analytics.preference.usage.description`
+ )
+ : this.hass.localize(
+ `ui.panel.config.core.section.core.analytics.preference.${preference}.description`
+ )}
+
+ `
+ )}
+
+
+
+
+
+
+ ${this.hass.localize(
+ `ui.panel.config.core.section.core.analytics.preference.diagnostics.title`
+ )}
+
+
+ ${this.hass.localize(
+ `ui.panel.config.core.section.core.analytics.preference.diagnostics.description`
+ )}
+
+
+
+ ${this.hass.localize(
+ "ui.panel.config.core.section.core.analytics.documentation",
+ "link",
+ html`
+ ${documentationUrl(this.hass, "/integrations/analytics/")}
+ `
+ )}
+
+ `;
+ }
+
+ private _handleRowCheckboxClick(ev: Event) {
+ const checkbox = ev.currentTarget as HaCheckbox;
+ const preference = (checkbox as any).preference;
+ const preferences = { ...this.analytics.preferences };
+
+ if (checkbox.checked) {
+ if (preferences[preference]) {
+ return;
+ }
+ preferences[preference] = true;
+ } else {
+ preferences[preference] = false;
+ }
+
+ fireEvent(this, "analytics-preferences-changed", { preferences });
+ }
+
+ static get styles(): CSSResult[] {
+ return [
+ haStyle,
+ css`
+ .error {
+ color: var(--error-color);
+ }
+
+ ha-settings-row {
+ padding: 0;
+ }
+ `,
+ ];
+ }
+}
+
+declare global {
+ interface HTMLElementTagNameMap {
+ "ha-analytics": HaAnalytics;
+ }
+}
diff --git a/src/data/analytics.ts b/src/data/analytics.ts
new file mode 100644
index 0000000000..d8e04f69a1
--- /dev/null
+++ b/src/data/analytics.ts
@@ -0,0 +1,27 @@
+import { HomeAssistant } from "../types";
+
+export interface AnalyticsPreferences {
+ base?: boolean;
+ diagnostics?: boolean;
+ usage?: boolean;
+ statistics?: boolean;
+}
+
+export interface Analytics {
+ preferences: AnalyticsPreferences;
+ huuid: string;
+}
+
+export const getAnalyticsDetails = (hass: HomeAssistant) =>
+ hass.callWS
({
+ type: "analytics",
+ });
+
+export const setAnalyticsPreferences = (
+ hass: HomeAssistant,
+ preferences: AnalyticsPreferences
+) =>
+ hass.callWS({
+ type: "analytics/preferences",
+ preferences,
+ });
diff --git a/src/data/onboarding.ts b/src/data/onboarding.ts
index 48f7e42f40..3d9e24ee9b 100644
--- a/src/data/onboarding.ts
+++ b/src/data/onboarding.ts
@@ -12,10 +12,14 @@ export interface OnboardingIntegrationStepResponse {
auth_code: string;
}
+// eslint-disable-next-line @typescript-eslint/no-empty-interface
+export interface OnboardingAnalyticsStepResponse {}
+
export interface OnboardingResponses {
user: OnboardingUserStepResponse;
core_config: OnboardingCoreConfigStepResponse;
integration: OnboardingIntegrationStepResponse;
+ analytics: OnboardingAnalyticsStepResponse;
}
export type ValidOnboardingStep = keyof OnboardingResponses;
@@ -49,6 +53,9 @@ export const onboardCoreConfigStep = (hass: HomeAssistant) =>
"onboarding/core_config"
);
+export const onboardAnalyticsStep = (hass: HomeAssistant) =>
+ hass.callApi("POST", "onboarding/analytics");
+
export const onboardIntegrationStep = (
hass: HomeAssistant,
params: { client_id: string; redirect_uri: string }
diff --git a/src/onboarding/ha-onboarding.ts b/src/onboarding/ha-onboarding.ts
index 8bd4e25210..a6177099be 100644
--- a/src/onboarding/ha-onboarding.ts
+++ b/src/onboarding/ha-onboarding.ts
@@ -31,6 +31,7 @@ import { HomeAssistant } from "../types";
import { registerServiceWorker } from "../util/register-service-worker";
import "./onboarding-create-user";
import "./onboarding-loading";
+import "./onboarding-analytics";
type OnboardingEvent =
| {
@@ -43,6 +44,9 @@ type OnboardingEvent =
}
| {
type: "integration";
+ }
+ | {
+ type: "analytics";
};
declare global {
@@ -102,6 +106,15 @@ class HaOnboarding extends litLocalizeLiteMixin(HassElement) {
>
`;
}
+ if (step.step === "analytics") {
+ return html`
+
+ `;
+ }
+
if (step.step === "integration") {
return html`
+ ${this.localize(
+ "ui.panel.page-onboarding.analytics.intro",
+ "link",
+ html`https://analytics.home-assistant.io`
+ )}
+
+
+
+ ${this._error ? html`${this._error}
` : ""}
+
+ `;
+ }
+
+ protected firstUpdated(changedProps) {
+ super.firstUpdated(changedProps);
+ this.addEventListener("keypress", (ev) => {
+ if (ev.keyCode === 13) {
+ this._save(ev);
+ }
+ });
+ this._load();
+ }
+
+ private _preferencesChanged(event: CustomEvent): void {
+ this._analyticsDetails = {
+ ...this._analyticsDetails!,
+ preferences: event.detail.preferences,
+ };
+ }
+
+ private async _save(ev) {
+ ev.preventDefault();
+ try {
+ await setAnalyticsPreferences(
+ this.hass,
+ this._analyticsDetails!.preferences
+ );
+
+ await onboardAnalyticsStep(this.hass);
+ fireEvent(this, "onboarding-step", {
+ type: "analytics",
+ });
+ } catch (err) {
+ alert(`Failed to save: ${err.message}`);
+ }
+ }
+
+ private async _load() {
+ this._error = undefined;
+ try {
+ this._analyticsDetails = await getAnalyticsDetails(this.hass);
+ } catch (err) {
+ this._error = err.message || err;
+ }
+ }
+
+ static get styles(): CSSResult {
+ return css`
+ .error {
+ color: var(--error-color);
+ }
+
+ .footer {
+ margin-top: 16px;
+ text-align: right;
+ }
+ `;
+ }
+}
+
+declare global {
+ interface HTMLElementTagNameMap {
+ "onboarding-analytics": OnboardingAnalytics;
+ }
+}
diff --git a/src/panels/config/core/ha-config-analytics.ts b/src/panels/config/core/ha-config-analytics.ts
new file mode 100644
index 0000000000..fae605964c
--- /dev/null
+++ b/src/panels/config/core/ha-config-analytics.ts
@@ -0,0 +1,132 @@
+import "@material/mwc-button/mwc-button";
+import {
+ css,
+ CSSResult,
+ customElement,
+ html,
+ internalProperty,
+ LitElement,
+ property,
+ PropertyValues,
+ TemplateResult,
+} from "lit-element";
+import { isComponentLoaded } from "../../../common/config/is_component_loaded";
+import "../../../components/ha-analytics";
+import "../../../components/ha-card";
+import "../../../components/ha-checkbox";
+import "../../../components/ha-settings-row";
+import {
+ Analytics,
+ getAnalyticsDetails,
+ setAnalyticsPreferences,
+} from "../../../data/analytics";
+import { haStyle } from "../../../resources/styles";
+import type { HomeAssistant } from "../../../types";
+
+@customElement("ha-config-analytics")
+class ConfigAnalytics extends LitElement {
+ @property({ attribute: false }) public hass!: HomeAssistant;
+
+ @internalProperty() private _analyticsDetails?: Analytics;
+
+ @internalProperty() private _error?: string;
+
+ protected render(): TemplateResult {
+ if (
+ !isComponentLoaded(this.hass, "analytics") ||
+ !this.hass.user?.is_owner ||
+ !this._analyticsDetails?.huuid
+ ) {
+ return html``;
+ }
+
+ return html`
+
+
+ ${this._error ? html`
${this._error}
` : ""}
+
+ ${this.hass.localize(
+ "ui.panel.config.core.section.core.analytics.introduction",
+ "link",
+ html`https://analytics.home-assistant.io`
+ )}
+
+
+
+
+
+ ${this.hass.localize(
+ "ui.panel.config.core.section.core.core_config.save_button"
+ )}
+
+
+
+ `;
+ }
+
+ protected firstUpdated(changedProps: PropertyValues) {
+ super.firstUpdated(changedProps);
+ if (isComponentLoaded(this.hass, "analytics")) {
+ this._load();
+ }
+ }
+
+ private async _load() {
+ this._error = undefined;
+ try {
+ this._analyticsDetails = await getAnalyticsDetails(this.hass);
+ } catch (err) {
+ this._error = err.message || err;
+ }
+ }
+
+ private async _save() {
+ this._error = undefined;
+ try {
+ await setAnalyticsPreferences(
+ this.hass,
+ this._analyticsDetails?.preferences || {}
+ );
+ } catch (err) {
+ this._error = err.message || err;
+ }
+ }
+
+ private _preferencesChanged(event: CustomEvent): void {
+ this._analyticsDetails = {
+ ...this._analyticsDetails!,
+ preferences: event.detail.preferences,
+ };
+ }
+
+ static get styles(): CSSResult[] {
+ return [
+ haStyle,
+ css`
+ .error {
+ color: var(--error-color);
+ }
+
+ ha-settings-row {
+ padding: 0;
+ }
+ `,
+ ];
+ }
+}
+
+declare global {
+ interface HTMLElementTagNameMap {
+ "ha-config-analytics": ConfigAnalytics;
+ }
+}
diff --git a/src/panels/config/core/ha-config-section-core.js b/src/panels/config/core/ha-config-section-core.js
index d91dda88f1..4e50ab56c4 100644
--- a/src/panels/config/core/ha-config-section-core.js
+++ b/src/panels/config/core/ha-config-section-core.js
@@ -8,6 +8,7 @@ import "../../../components/ha-card";
import LocalizeMixin from "../../../mixins/localize-mixin";
import "../../../styles/polymer-ha-style";
import "../ha-config-section";
+import "./ha-config-analytics";
import "./ha-config-core-form";
import "./ha-config-name-form";
import "./ha-config-url-form";
@@ -29,6 +30,7 @@ class HaConfigSectionCore extends LocalizeMixin(PolymerElement) {
+
`;
}
diff --git a/src/translations/en.json b/src/translations/en.json
index d411fffc92..08383a9dcb 100755
--- a/src/translations/en.json
+++ b/src/translations/en.json
@@ -977,6 +977,35 @@
"save_button": "Save",
"external_url": "External URL",
"internal_url": "Internal URL"
+ },
+ "analytics": {
+ "header": "Analytics",
+ "introduction": "Share analytics from your instance. This data will be publiclly available at {link}",
+ "instance_id": "Instance ID: {huuid}",
+ "needs_base": "You need to enable base analytics for this option to be available",
+ "preference": {
+ "base": {
+ "title": "Basic analytics",
+ "description": "This includes the instance ID, the version and the installation type"
+ },
+ "diagnostics": {
+ "title": "Diagnostics",
+ "description": "Share crash reports and diagnostic information"
+ },
+ "usage": {
+ "title": "Used integrations",
+ "description": "This includes the names of your integrations"
+ },
+ "usage_supervisor": {
+ "title": "Used integrations and add-ons",
+ "description": "This includes the names and capabilities of your integrations and add-ons"
+ },
+ "statistics": {
+ "title": "Usage statistics",
+ "description": "This includes a count of elements in your installation, for a full list look at the documentation"
+ }
+ },
+ "documentation": "Before you enable this make sure you visit the analytics documentation page {link} to understand what you are sending and how it's stored."
}
}
}
@@ -3420,6 +3449,8 @@
},
"page-onboarding": {
"intro": "Are you ready to awaken your home, reclaim your privacy and join a worldwide community of tinkerers?",
+ "next": "Next",
+ "finish": "Finish",
"user": {
"intro": "Let's get started by creating a user account.",
"required_field": "Required",
@@ -3449,6 +3480,10 @@
"more_integrations": "More",
"finish": "Finish"
},
+ "analytics": {
+ "intro": "Share analytics from your instance. This data will be publiclly available at {link}",
+ "finish": "Next"
+ },
"restore": {
"description": "Alternatively you can restore from a previous snapshot.",
"in_progress": "Restore in progress",