continued updates

This commit is contained in:
Zack Arnett 2020-09-22 20:28:26 -05:00
parent 49725b4fd3
commit 359ddbf284
3 changed files with 293 additions and 160 deletions

View File

@ -58,6 +58,7 @@ export type CloudStatus = CloudStatusBase | CloudStatusLoggedIn;
export interface SubscriptionInfo { export interface SubscriptionInfo {
human_description: string; human_description: string;
provider: string;
} }
export interface CloudWebhook { export interface CloudWebhook {

View File

@ -17,6 +17,7 @@ import {
} from "lit-element"; } from "lit-element";
import memoizeOne from "memoize-one"; import memoizeOne from "memoize-one";
import { formatDateTime } from "../../../common/datetime/format_date_time"; import { formatDateTime } from "../../../common/datetime/format_date_time";
import { listenMediaQuery } from "../../../common/dom/media_query";
import { computeStateDomain } from "../../../common/entity/compute_state_domain"; import { computeStateDomain } from "../../../common/entity/compute_state_domain";
import { import {
caseInsensitiveCompare, caseInsensitiveCompare,
@ -27,7 +28,13 @@ import "../../../components/ha-icon-next";
import "../../../components/ha-menu-button"; import "../../../components/ha-menu-button";
import "../../../components/user/ha-person-badge"; import "../../../components/user/ha-person-badge";
import { AutomationEntity } from "../../../data/automation"; import { AutomationEntity } from "../../../data/automation";
import { CloudStatus } from "../../../data/cloud"; import {
CloudStatus,
CloudStatusLoggedIn,
fetchCloudStatus,
fetchCloudSubscriptionInfo,
SubscriptionInfo,
} from "../../../data/cloud";
import { ConfigEntry, getConfigEntries } from "../../../data/config_entries"; import { ConfigEntry, getConfigEntries } from "../../../data/config_entries";
import { import {
DeviceRegistryEntry, DeviceRegistryEntry,
@ -45,6 +52,7 @@ import {
} from "../../../data/lovelace"; } from "../../../data/lovelace";
import { fetchPersons, Person } from "../../../data/person"; import { fetchPersons, Person } from "../../../data/person";
import { fetchTags, Tag } from "../../../data/tag"; import { fetchTags, Tag } from "../../../data/tag";
import { fetchWebhooks, Webhook } from "../../../data/webhook";
import "../../../layouts/ha-app-layout"; import "../../../layouts/ha-app-layout";
import { haStyle } from "../../../resources/styles"; import { haStyle } from "../../../resources/styles";
import { HomeAssistant } from "../../../types"; import { HomeAssistant } from "../../../types";
@ -60,11 +68,11 @@ class HaConfigDashboard extends LitElement {
@property({ type: Boolean, reflect: true }) @property({ type: Boolean, reflect: true })
public narrow!: boolean; public narrow!: boolean;
@property() public isWide!: boolean; @property({ type: Boolean }) public isWide!: boolean;
@property() public cloudStatus?: CloudStatus; @property({ attribute: false }) public cloudStatus?: CloudStatus;
@property() public showAdvanced!: boolean; @property({ type: Boolean }) public showAdvanced!: boolean;
@internalProperty() private _persons?: Person[]; @internalProperty() private _persons?: Person[];
@ -80,13 +88,34 @@ class HaConfigDashboard extends LitElement {
@internalProperty() private _dashboards: LovelaceDashboard[] = []; @internalProperty() private _dashboards: LovelaceDashboard[] = [];
@internalProperty() private _width: number; @internalProperty() private _veryWide = false;
@internalProperty() private mqls?: MediaQueryList[]; @internalProperty() private _cloudStatus?: CloudStatus;
@internalProperty() private _subscription?: SubscriptionInfo;
@internalProperty() private _localHooks?: Webhook[];
private _listeners: Array<() => void> = [];
public connectedCallback() {
super.connectedCallback();
this._listeners.push(
listenMediaQuery("(min-width: 1525px)", (matches) => {
this._veryWide = matches;
})
);
}
public disconnectedCallback() {
super.disconnectedCallback();
while (this._listeners.length) {
this._listeners.pop()!();
}
}
protected render(): TemplateResult { protected render(): TemplateResult {
const integrationsToShow = const integrationsToShow = this._veryWide ? 6 : this.narrow ? 2 : 4;
this._width === 4 ? 6 : this._width === 3 ? 4 : this.narrow ? 2 : 6;
return html` return html`
<ha-app-layout> <ha-app-layout>
<app-header fixed slot="header"> <app-header fixed slot="header">
@ -116,18 +145,37 @@ class HaConfigDashboard extends LitElement {
)} )}
</mwc-list> </mwc-list>
<div class="footer"> <div class="footer">
<mwc-button>Manage Persons, Users & Zones</mwc-button> <a class="config-link" href="/config/person">
<mwc-button>Manage Persons, Users & Zones</mwc-button>
</a>
</div> </div>
</ha-card> </ha-card>
<ha-card outlined id="CloudCard"> <ha-card outlined id="CloudCard">
<div class="card-header"> <div class="card-header">
<div class="header">Cloud</div> <div class="header">Cloud</div>
<div class="secondary">zackbarett@hey.com</div> ${this._cloudStatus && "email" in this._cloudStatus
? html`
<div class="secondary">
${this._cloudStatus.email}
</div>
`
: ""}
</div> </div>
<mwc-list> <mwc-list>
<mwc-list-item twoline hasMeta> <mwc-list-item twoline hasMeta>
<span>Remote UI</span> <span>Remote UI</span>
<span slot="secondary">Connected</span> <span slot="secondary"
>${this._cloudStatus &&
"remote_connected" in this._cloudStatus
? this._cloudStatus?.remote_connected
? this.hass.localize(
"ui.panel.config.cloud.account.connected"
)
: this.hass.localize(
"ui.panel.config.cloud.account.not_connected"
)
: ""}</span
>
<ha-svg-icon <ha-svg-icon
class="meta-icon" class="meta-icon"
slot="meta" slot="meta"
@ -136,7 +184,15 @@ class HaConfigDashboard extends LitElement {
</mwc-list-item> </mwc-list-item>
<mwc-list-item twoline hasMeta> <mwc-list-item twoline hasMeta>
<span>Google Assistant</span> <span>Google Assistant</span>
<span slot="secondary">Enabled</span> <span slot="secondary"
>${this._cloudStatus &&
(this._cloudStatus as CloudStatusLoggedIn).prefs
.google_enabled
? this.hass.localize("ui.panel.config.cloud.enabled")
: this.hass.localize(
"ui.panel.config.cloud.disabled"
)}</span
>
<ha-svg-icon <ha-svg-icon
class="meta-icon" class="meta-icon"
slot="meta" slot="meta"
@ -145,7 +201,15 @@ class HaConfigDashboard extends LitElement {
</mwc-list-item> </mwc-list-item>
<mwc-list-item twoline hasMeta> <mwc-list-item twoline hasMeta>
<span>Amazon Alexa</span> <span>Amazon Alexa</span>
<span slot="secondary">Disabled</span> <span slot="secondary"
>${this._cloudStatus &&
(this._cloudStatus as CloudStatusLoggedIn).prefs
.alexa_enabled
? this.hass.localize("ui.panel.config.cloud.enabled")
: this.hass.localize(
"ui.panel.config.cloud.disabled"
)}</span
>
<ha-svg-icon <ha-svg-icon
class="meta-icon" class="meta-icon"
slot="meta" slot="meta"
@ -154,7 +218,9 @@ class HaConfigDashboard extends LitElement {
</mwc-list-item> </mwc-list-item>
<mwc-list-item twoline hasMeta> <mwc-list-item twoline hasMeta>
<span>Webhooks</span> <span>Webhooks</span>
<span slot="secondary">3 active</span> <span slot="secondary"
>${this._localHooks?.length} active</span
>
<ha-svg-icon <ha-svg-icon
class="meta-icon" class="meta-icon"
slot="meta" slot="meta"
@ -163,59 +229,74 @@ class HaConfigDashboard extends LitElement {
</mwc-list-item> </mwc-list-item>
</mwc-list> </mwc-list>
<div class="footer"> <div class="footer">
<mwc-button>Manage Cloud Services</mwc-button> <a class="config-link" href="/config/cloud">
<mwc-button>Manage Cloud Services</mwc-button></a
>
</div> </div>
</ha-card> </ha-card>
<ha-card outlined id="ServerCard" .header=${"Server"}> <ha-card outlined id="ServerCard" .header=${"Server"}>
<mwc-list> <mwc-list>
<mwc-list-item twoline hasMeta> <a class="config-link" href="/config/core">
<span>Location Settings</span> <mwc-list-item twoline hasMeta>
<span slot="secondary">Unit system, timezone, etc</span> <span>Location Settings</span>
<ha-svg-icon <span slot="secondary">Unit system, timezone, etc</span>
class="meta-icon" <ha-svg-icon
slot="meta" class="meta-icon"
.path=${mdiChevronRight} slot="meta"
></ha-svg-icon> .path=${mdiChevronRight}
</mwc-list-item> ></ha-svg-icon>
<mwc-list-item twoline hasMeta> </mwc-list-item>
<span>Server Control</span> </a>
<span slot="secondary">Stop and Start Home Assistant</span> <a class="config-link" href="/config/server_control">
<ha-svg-icon <mwc-list-item twoline hasMeta>
class="meta-icon" <span>Server Control</span>
slot="meta" <span slot="secondary">Stop and Start Home Assistant</span>
.path=${mdiChevronRight} <ha-svg-icon
></ha-svg-icon> class="meta-icon"
</mwc-list-item> slot="meta"
<mwc-list-item twoline hasMeta> .path=${mdiChevronRight}
<span>Logs</span> ></ha-svg-icon>
<span slot="secondary">Server Logs</span> </mwc-list-item>
<ha-svg-icon </a>
class="meta-icon" <a class="config-link" href="/config/logs">
slot="meta" <mwc-list-item twoline hasMeta>
.path=${mdiChevronRight} <span>Logs</span>
></ha-svg-icon> <span slot="secondary">Server Logs</span>
</mwc-list-item> <ha-svg-icon
<mwc-list-item twoline hasMeta> class="meta-icon"
<span>Add-ons</span> slot="meta"
<span slot="secondary">Manage Addons</span> .path=${mdiChevronRight}
<ha-svg-icon ></ha-svg-icon>
class="meta-icon" </mwc-list-item>
slot="meta" </a>
.path=${mdiChevronRight}
></ha-svg-icon> <a class="config-link" href="/config/customize">
</mwc-list-item> <mwc-list-item twoline hasMeta>
<mwc-list-item twoline hasMeta> <span>Customizations</span>
<span>About</span> <span slot="secondary">Manage Customizations</span>
<span slot="secondary">Info about the server</span> <ha-svg-icon
<ha-svg-icon class="meta-icon"
class="meta-icon" slot="meta"
slot="meta" .path=${mdiChevronRight}
.path=${mdiChevronRight} ></ha-svg-icon>
></ha-svg-icon> </mwc-list-item>
</mwc-list-item> </a>
<a class="config-link" href="/config/info">
<mwc-list-item twoline hasMeta>
<span>About</span>
<span slot="secondary">Info about the server</span>
<ha-svg-icon
class="meta-icon"
slot="meta"
.path=${mdiChevronRight}
></ha-svg-icon>
</mwc-list-item>
</a>
</mwc-list> </mwc-list>
<div class="footer"> <div class="footer">
<mwc-button>Manage Server</mwc-button> <a class="config-link" href="/config/core">
<mwc-button>Manage Server</mwc-button>
</a>
</div> </div>
</ha-card> </ha-card>
</div> </div>
@ -281,103 +362,126 @@ class HaConfigDashboard extends LitElement {
})} })}
</div> </div>
<div class="footer"> <div class="footer">
<mwc-button>Manage integrations</mwc-button> <a class="config-link" href="/config/integrations">
<mwc-button>Manage integrations</mwc-button>
</a>
</div> </div>
</ha-card> </ha-card>
<ha-card outlined id="AutomationCard" .header=${"Automations"}> <ha-card outlined id="AutomationCard" .header=${"Automations"}>
<mwc-list> <mwc-list>
${this._getAutomations(this.hass.states).map( ${this._getAutomations(this.hass.states).map(
(automation) => html` (automation) => html`
<mwc-list-item twoline hasMeta> <a
<span>${automation.attributes.friendly_name}</span> class="config-link"
<span slot="secondary" href=${`/config/automation/edit/${automation.attributes.id}`}
>${this.hass.localize( >
"ui.card.automation.last_triggered" <mwc-list-item twoline hasMeta>
)}: <span>${automation.attributes.friendly_name}</span>
${automation.attributes.last_triggered <span slot="secondary"
? formatDateTime( >${this.hass.localize(
new Date(automation.attributes.last_triggered), "ui.card.automation.last_triggered"
this.hass.language )}:
) ${automation.attributes.last_triggered
: this.hass.localize( ? formatDateTime(
"ui.components.relative_time.never" new Date(automation.attributes.last_triggered),
)} this.hass.language
</span> )
</mwc-list-item> : this.hass.localize(
<ha-svg-icon "ui.components.relative_time.never"
class="meta-icon" )}
slot="meta" </span>
.path=${mdiChevronRight} <ha-svg-icon
></ha-svg-icon> class="meta-icon"
slot="meta"
.path=${mdiChevronRight}
></ha-svg-icon>
</mwc-list-item>
</a>
` `
)} )}
</mwc-list> </mwc-list>
<div class="footer"> <div class="footer">
<mwc-button>Manage Automations</mwc-button> <a class="config-link" href="/config/automation">
<mwc-button>Manage Automations</mwc-button>
</a>
</div> </div>
</ha-card> </ha-card>
<ha-card outlined id="ScriptCard" .header=${"Scripts"}> <ha-card outlined id="ScriptCard" .header=${"Scripts"}>
<mwc-list> <mwc-list>
${this._getScripts(this.hass.states).map( ${this._getScripts(this.hass.states).map(
(script) => html` (script) => html`
<mwc-list-item twoline hasMeta> <a
<span>${script.attributes.friendly_name}</span> class="config-link"
<span slot="secondary" href=${`/config/script/edit/${script.entity_id}`}
>${this.hass.localize( >
"ui.card.automation.last_triggered" <mwc-list-item twoline hasMeta>
)}: <span>${script.attributes.friendly_name}</span>
${script.attributes.last_triggered <span slot="secondary"
? formatDateTime( >${this.hass.localize(
new Date(script.attributes.last_triggered), "ui.card.automation.last_triggered"
this.hass.language )}:
) ${script.attributes.last_triggered
: this.hass.localize( ? formatDateTime(
"ui.components.relative_time.never" new Date(script.attributes.last_triggered),
)} this.hass.language
</span> )
</mwc-list-item> : this.hass.localize(
<ha-svg-icon "ui.components.relative_time.never"
class="meta-icon" )}
slot="meta" </span>
.path=${mdiChevronRight} <ha-svg-icon
></ha-svg-icon> class="meta-icon"
slot="meta"
.path=${mdiChevronRight}
></ha-svg-icon>
</mwc-list-item>
</a>
` `
)} )}
</mwc-list> </mwc-list>
<div class="footer"> <div class="footer">
<mwc-button>Manage Scripts</mwc-button> <a class="config-link" href="/config/script">
<mwc-button>Manage Scripts</mwc-button>
</a>
</div> </div>
</ha-card> </ha-card>
<ha-card outlined id="SceneCard" .header=${"Scenes"}> <ha-card outlined id="SceneCard" .header=${"Scenes"}>
<mwc-list> <mwc-list>
${this._getScenes(this.hass.states).map( ${this._getScenes(this.hass.states).map(
(scene) => html` (scene) => html`
<mwc-list-item twoline hasMeta> <a
<span>${scene.attributes.friendly_name}</span> class="config-link"
<span slot="secondary" href=${`/config/scene/edit/${scene.attributes.id}`}
>${this.hass.localize( >
"ui.card.automation.last_triggered" <mwc-list-item twoline hasMeta>
)}: <span>${scene.attributes.friendly_name}</span>
${scene.attributes.last_triggered <span slot="secondary"
? formatDateTime( >${this.hass.localize(
new Date(scene.attributes.last_triggered), "ui.card.automation.last_triggered"
this.hass.language )}:
) ${scene.attributes.last_triggered
: this.hass.localize( ? formatDateTime(
"ui.components.relative_time.never" new Date(scene.attributes.last_triggered),
)} this.hass.language
</span> )
</mwc-list-item> : this.hass.localize(
<ha-svg-icon "ui.components.relative_time.never"
class="meta-icon" )}
slot="meta" </span>
.path=${mdiChevronRight} <ha-svg-icon
></ha-svg-icon> class="meta-icon"
slot="meta"
.path=${mdiChevronRight}
></ha-svg-icon>
</mwc-list-item>
</a>
` `
)} )}
</mwc-list> </mwc-list>
<div class="footer"> <div class="footer">
<mwc-button>Manage Scenes</mwc-button> <a class="config-link" href="/config/scene">
<mwc-button>Manage Scenes</mwc-button>
</a>
</div> </div>
</ha-card> </ha-card>
<ha-card outlined id="HelperCard" .header=${"Helpers"}> <ha-card outlined id="HelperCard" .header=${"Helpers"}>
@ -399,17 +503,19 @@ class HaConfigDashboard extends LitElement {
"ui.components.relative_time.never" "ui.components.relative_time.never"
)} )}
</span> </span>
<ha-svg-icon
class="meta-icon"
slot="meta"
.path=${mdiChevronRight}
></ha-svg-icon>
</mwc-list-item> </mwc-list-item>
<ha-svg-icon
class="meta-icon"
slot="meta"
.path=${mdiChevronRight}
></ha-svg-icon>
` `
)} )}
</mwc-list> </mwc-list>
<div class="footer"> <div class="footer">
<mwc-button>Manage Helpers</mwc-button> <a class="config-link" href="/config/helpers">
<mwc-button>Manage Helpers</mwc-button>
</a>
</div> </div>
</ha-card> </ha-card>
<ha-card outlined id="TagsCard" .header=${"Tags"}> <ha-card outlined id="TagsCard" .header=${"Tags"}>
@ -435,17 +541,19 @@ class HaConfigDashboard extends LitElement {
"ui.panel.config.tags.never_scanned" "ui.panel.config.tags.never_scanned"
)} )}
</span> </span>
<ha-svg-icon
class="meta-icon"
slot="meta"
.path=${mdiChevronRight}
></ha-svg-icon>
</mwc-list-item> </mwc-list-item>
<ha-svg-icon
class="meta-icon"
slot="meta"
.path=${mdiChevronRight}
></ha-svg-icon>
` `
)} )}
</mwc-list> </mwc-list>
<div class="footer"> <div class="footer">
<mwc-button>Manage Tags</mwc-button> <a class="config-link" href="/config/tags">
<mwc-button>Manage Tags</mwc-button>
</a>
</div> </div>
</ha-card> </ha-card>
<ha-card <ha-card
@ -466,17 +574,21 @@ class HaConfigDashboard extends LitElement {
? html` - ${dashboard.filename} ` ? html` - ${dashboard.filename} `
: ""} : ""}
</span> </span>
<ha-svg-icon
class="meta-icon"
slot="meta"
.path=${mdiChevronRight}
></ha-svg-icon>
</mwc-list-item> </mwc-list-item>
<ha-svg-icon
class="meta-icon"
slot="meta"
.path=${mdiChevronRight}
></ha-svg-icon>
` `
)} )}
</mwc-list> </mwc-list>
<div class="footer"> <div class="footer">
<mwc-button>Manage Lovelace Dashboards & Resources</mwc-button> <a class="config-link" href="/config/lovelace">
<mwc-button
>Manage Lovelace Dashboards & Resources</mwc-button
>
</a>
</div> </div>
</ha-card> </ha-card>
</div> </div>
@ -491,26 +603,40 @@ class HaConfigDashboard extends LitElement {
this._fetchIntegrationData(); this._fetchIntegrationData();
this._fetchTags(); this._fetchTags();
this._fetchDasboards(); this._fetchDasboards();
this._fetchSubscriptionInfo();
this._updateCloudStatus();
this._fetchWebhooks();
subscribeEntityRegistry(this.hass.connection, (entries) => { subscribeEntityRegistry(this.hass.connection, (entries) => {
this._entityRegistryEntries = entries; this._entityRegistryEntries = entries;
}); });
subscribeDeviceRegistry(this.hass.connection, (entries) => { subscribeDeviceRegistry(this.hass.connection, (entries) => {
this._deviceRegistryEntries = entries; this._deviceRegistryEntries = entries;
}); });
this.mqls = [300, 600, 900, 1525].map((width) => { }
const mql = matchMedia(`(min-width: ${width}px)`);
mql.addEventListener("change", () => { private async _fetchSubscriptionInfo() {
this._width = this.mqls!.reduce( this._subscription = await fetchCloudSubscriptionInfo(this.hass);
(cols, _mql) => cols + Number(_mql.matches), if (
0 this._subscription.provider &&
); this.cloudStatus &&
}); this.cloudStatus.cloud !== "connected"
return mql; ) {
}); this._updateCloudStatus();
this._width = this.mqls!.reduce( }
(cols, _mql) => cols + Number(_mql.matches), }
0
); private async _updateCloudStatus() {
this._cloudStatus = await fetchCloudStatus(this.hass);
if (this._cloudStatus.cloud === "connecting") {
setTimeout(() => this._updateCloudStatus(), 5000);
}
}
private async _fetchWebhooks() {
this._localHooks = this.hass!.config.components.includes("webhook")
? await fetchWebhooks(this.hass!)
: [];
} }
private async _fetchPersonData() { private async _fetchPersonData() {
@ -785,6 +911,10 @@ class HaConfigDashboard extends LitElement {
line-height: 1.2; line-height: 1.2;
} }
.config-link {
text-decoration: none;
}
.meta-icon { .meta-icon {
color: var(--secondary-text-color); color: var(--secondary-text-color);
} }

View File

@ -1321,6 +1321,8 @@
"description_login": "Logged in as {email}", "description_login": "Logged in as {email}",
"description_not_login": "Not logged in", "description_not_login": "Not logged in",
"description_features": "Control away from home, integrate with Alexa and Google Assistant.", "description_features": "Control away from home, integrate with Alexa and Google Assistant.",
"enabled": "Enabled",
"disabled": "Disabled",
"login": { "login": {
"title": "Cloud Login", "title": "Cloud Login",
"introduction": "Home Assistant Cloud provides you with a secure remote connection to your instance while away from home. It also allows you to connect with cloud-only services: Amazon Alexa and Google Assistant.", "introduction": "Home Assistant Cloud provides you with a secure remote connection to your instance while away from home. It also allows you to connect with cloud-only services: Amazon Alexa and Google Assistant.",