Add initial cloud login (#411)

This commit is contained in:
Paulus Schoutsen 2017-08-27 13:08:20 -07:00 committed by GitHub
parent 894a84ba77
commit b3995e63be
8 changed files with 420 additions and 88 deletions

View File

@ -0,0 +1,63 @@
<link rel="import" href='../../../bower_components/polymer/polymer-element.html'>
<link rel="import" href='../../../bower_components/paper-card/paper-card.html'>
<link rel="import" href="../../../bower_components/paper-card/paper-card.html">
<link rel="import" href="../../../bower_components/paper-item/paper-item-body.html">
<link rel="import" href='../../../bower_components/paper-button/paper-button.html'>
<link rel="import" href='../../../src/resources/ha-style.html'>
<dom-module id="ha-config-cloud-account">
<template>
<style include="iron-flex ha-style">
paper-card {
display: block;
}
.account {
display: flex;
padding: 0 16px;
}
paper-button {
align-self: center;
}
.soon {
font-style: italic;
margin-top: 24px;
text-align: center;
}
</style>
<paper-card>
<div class='account'>
<paper-item-body two-line>
[[account.first_name]] [[account.last_name]]
<div secondary>[[account.email]]</div>
</paper-item-body>
<paper-button
class='warning'
on-tap='handleLogout'
>Sign out</paper-button>
</div>
</paper-card>
<div class='soon'>More configuration options coming soon.</div>
</template>
</dom-module>
<script>
class HaConfigCloudAccount extends window.hassMixins.EventsMixin(Polymer.Element) {
static get is() { return 'ha-config-cloud-account'; }
static get properties() {
return {
hass: Object,
account: Object,
};
}
handleLogout() {
this.hass.callApi('post', 'cloud/logout').then(
() => this.fire('ha-account-refreshed', { account: null }));
}
}
customElements.define(HaConfigCloudAccount.is, HaConfigCloudAccount);
</script>

View File

@ -0,0 +1,92 @@
<link rel="import" href='../../../bower_components/polymer/polymer-element.html'>
<link rel="import" href='../../../bower_components/paper-card/paper-card.html'>
<link rel="import" href='../../../bower_components/paper-button/paper-button.html'>
<link rel="import" href='../../../bower_components/paper-input/paper-input.html'>
<link rel="import" href='../../../src/resources/ha-style.html'>
<dom-module id="ha-config-cloud-login">
<template>
<style include="iron-flex ha-style">
paper-card {
display: block;
}
h1 {
@apply(--paper-font-headline);
margin: 0;
}
</style>
<paper-card>
<div class='card-content'>
<h1>Sign In</h1>
<paper-input
label='Username'
value='{{username}}'
error-message='Failed to login'
invalid='[[error]]'
on-keydown='_keyDown'
></paper-input>
<paper-input
label='Password'
value='{{password}}'
type='password'
on-keydown='_keyDown'
></paper-input>
</div>
<div class='card-actions'>
<paper-button
on-tap='_handleLogin'
>Sign in</paper-button>
</div>
</paper-card>
</template>
</dom-module>
<script>
class HaConfigCloudLogin extends window.hassMixins.EventsMixin(Polymer.Element) {
static get is() { return 'ha-config-cloud-login'; }
static get properties() {
return {
hass: Object,
username: String,
password: String,
};
}
static get observers() {
return [
'_inputChanged(username, password)'
];
}
_inputChanged() {
this.error = false;
}
_keyDown(ev) {
// validate on enter
if (ev.keyCode === 13) {
this._handleLogin();
ev.preventDefault();
}
}
_handleLogin() {
this.hass.callApi('post', 'cloud/login', {
username: this.username,
password: this.password,
}).then(
(account) => {
this.fire('ha-account-refreshed', { account: account });
this.username = '';
this.password = '';
}, () => {
this.password = '';
this.error = true;
});
}
}
customElements.define(HaConfigCloudLogin.is, HaConfigCloudLogin);
</script>

View File

@ -0,0 +1,90 @@
<link rel="import" href='../../../bower_components/polymer/polymer-element.html'>
<link rel="import" href="../../../bower_components/app-layout/app-header-layout/app-header-layout.html">
<link rel="import" href="../../../bower_components/app-layout/app-header/app-header.html">
<link rel="import" href="../../../bower_components/app-layout/app-toolbar/app-toolbar.html">
<link rel="import" href="../ha-config-section.html">
<link rel="import" href="./ha-config-cloud-login.html">
<link rel="import" href="./ha-config-cloud-account.html">
<dom-module id="ha-config-cloud">
<template>
<style include="iron-flex ha-style">
.content {
padding-bottom: 32px;
}
</style>
<app-header-layout has-scrolling-region>
<app-header slot="header" fixed>
<app-toolbar>
<paper-icon-button
icon='mdi:arrow-left'
on-tap='_backTapped'
></paper-icon-button>
<div main-title>Cloud</div>
</app-toolbar>
</app-header>
<div class$='[[computeClasses(isWide)]]'>
<ha-config-section
is-wide='[[isWide]]'
>
<span slot='header'>Home Assistant Cloud</span>
<span slot='introduction'>
The Home Assistant Cloud allows you to opt-in to functions that will bring your Home Assistant experience to the next level.
<p><i>
Home Assistant will never share information with our cloud without your prior permission.
</i></p>
</span>
<template is='dom-if' if='[[account]]'>
<ha-config-cloud-account
hass='[[hass]]'
account='[[account]]'
></ha-config-cloud-account>
</template>
<template is='dom-if' if='[[!account]]'>
<ha-config-cloud-login
hass='[[hass]]'
></ha-config-cloud-login>
</template>
</ha-config-section>
</div>
</app-header-layout>
</template>
</dom-module>
<script>
class HaConfigCloud extends Polymer.Element {
static get is() { return 'ha-config-cloud'; }
static get properties() {
return {
hass: Object,
isWide: Boolean,
loadingAccount: {
type: Boolean,
value: false
},
account: {
type: Object,
value: null,
},
};
}
computeClasses(isWide) {
return isWide ? 'content' : 'content narrow';
}
_backTapped() {
history.back();
}
}
customElements.define(HaConfigCloud.is, HaConfigCloud);
</script>

View File

@ -0,0 +1,52 @@
<link rel="import" href='../../../bower_components/polymer/polymer-element.html'>
<link rel="import" href="../../../bower_components/paper-card/paper-card.html">
<link rel="import" href="../../../bower_components/paper-item/paper-item.html">
<link rel="import" href="../../../bower_components/paper-item/paper-item-body.html">
<link rel="import" href="../../../bower_components/iron-icon/iron-icon.html">
<dom-module id="ha-config-cloud-menu">
<template>
<style include="iron-flex">
paper-card {
display: block;
}
paper-item {
cursor: pointer;
}
</style>
<paper-card>
<paper-item on-tap='_navigate'>
<paper-item-body two-line>
Home Assistant Cloud
<template is='dom-if' if='[[account]]'>
<div secondary>Logged in as [[account.first_name]] [[account.last_name]]</div>
</template>
<template is='dom-if' if='[[!account]]'>
<div secondary>Not logged in</div>
</template>
</paper-item-body>
<iron-icon icon='mdi:chevron-right'></iron-icon>
</paper-item>
</paper-card>
</template>
</dom-module>
<script>
class HaConfigCloudMenu extends window.hassMixins.NavigateMixin(Polymer.Element) {
static get is() { return 'ha-config-cloud-menu'; }
static get properties() {
return {
hass: Object,
isWide: Boolean,
account: Object,
};
}
_navigate() {
this.navigate('/config/cloud');
}
}
customElements.define(HaConfigCloudMenu.is, HaConfigCloudMenu);
</script>

View File

@ -1,11 +1,13 @@
<link rel="import" href="../../../bower_components/polymer/polymer.html">
<link rel="import" href='../../../bower_components/polymer/polymer-element.html'>
<link rel="import" href="../../../bower_components/app-layout/app-header-layout/app-header-layout.html">
<link rel="import" href="../../../bower_components/app-layout/app-header/app-header.html">
<link rel="import" href="../../../bower_components/app-layout/app-toolbar/app-toolbar.html">
<link rel="import" href="../../../src/components/ha-menu-button.html">
<link rel="import" href="../ha-config-section.html">
<link rel="import" href="./ha-config-navigation.html">
<link rel="import" href="./ha-config-cloud-menu.html">
<dom-module id="ha-config-dashboard">
<template>
@ -13,16 +15,6 @@
.content {
padding-bottom: 32px;
}
.border {
margin: 32px auto 0;
border-bottom: 1px solid rgba(0, 0, 0, 0.12);
max-width: 1040px;
}
.narrow .border {
max-width: 640px;
}
</style>
<app-header-layout has-scrolling-region>
@ -34,31 +26,53 @@
</app-header>
<div class$='[[computeClasses(isWide)]]'>
<ha-config-navigation
<ha-config-section
is-wide='[[isWide]]'
>
<span slot='header'>Configure Home Assistant</span>
<span slot='introduction'>
Here it is possible to configure your components and Home Assistant. Not everything is possible to configure from the UI yet, but we're working on it.
</span>
<template is='dom-if' if='[[computeIsCloudLoaded(hass)]]'>
<ha-config-cloud-menu
hass='[[hass]]'
account='[[account]]'
></ha-config-cloud-menu>
</template>
<ha-config-navigation
hass='[[hass]]'
></ha-config-navigation>
</ha-config-section>
</div>
</app-header-layout>
</template>
</dom-module>
<script>
Polymer({
is: 'ha-config-dashboard',
class HaConfigDashboard extends Polymer.Element {
static get is() { return 'ha-config-dashboard'; }
properties: {
static get properties() {
return {
hass: Object,
isWide: Boolean,
},
account: {
type: Object,
value: null,
}
};
}
computeClasses: function (isWide) {
computeClasses(isWide) {
return isWide ? 'content' : 'content narrow';
},
}
computeIsThemesLoaded: function (hass) {
return hass.themes && hass.themes.themes &&
Object.keys(hass.themes.themes).length;
},
});
computeIsCloudLoaded(hass) {
return window.hassUtil.isComponentLoaded(hass, 'cloud');
}
}
customElements.define(HaConfigDashboard.is, HaConfigDashboard);
</script>

View File

@ -4,25 +4,16 @@
<link rel="import" href="../../../bower_components/paper-item/paper-item-body.html">
<link rel="import" href="../../../bower_components/iron-icon/iron-icon.html">
<link rel="import" href="../../../src/resources/ha-style.html">
<link rel="import" href="../ha-config-section.html">
<dom-module id="ha-config-navigation">
<template>
<style include="iron-flex ha-style">
<style include="iron-flex">
paper-card {
display: block;
}
paper-item {
cursor: pointer;
}
</style>
<ha-config-section
is-wide='[[isWide]]'
>
<span slot='header'>Configure Home Assistant</span>
<span slot='introduction'>
Here it is possible to configure your components and Home Assistant. Not everything is possible to configure from the UI yet, but we're working on it.
</span>
<paper-card>
<template is='dom-repeat' items='[[pages]]'>
<template is='dom-if' if='[[_computeLoaded(hass, item)]]'>
@ -36,7 +27,6 @@
</template>
</template>
</paper-card>
</ha-config-section>
</template>
</dom-module>
@ -49,11 +39,6 @@ Polymer({
type: Object,
},
isWide: {
type: Boolean,
value: false,
},
pages: {
type: Array,
value: [

View File

@ -1,4 +1,4 @@
<link rel="import" href="../../bower_components/polymer/polymer.html">
<link rel="import" href='../../bower_components/polymer/polymer-element.html'>
<link rel='import' href='../../bower_components/iron-media-query/iron-media-query.html'>
<link rel='import' href='../../bower_components/app-route/app-route.html'>
<link rel="import" href="../../bower_components/iron-pages/iron-pages.html">
@ -7,6 +7,7 @@
<link rel="import" href="./dashboard/ha-config-dashboard.html">
<link rel="import" href="./core/ha-config-core.html">
<link rel="import" href="./cloud/ha-config-cloud.html">
<link rel="import" href="./automation/ha-config-automation.html">
<link rel="import" href="./script/ha-config-script.html">
<link rel="import" href="./zwave/ha-config-zwave.html">
@ -36,10 +37,24 @@
fallback-selection='not-found'
selected-attribute='visible'
>
<ha-config-core
page-name='core'
hass='[[hass]]'
is-wide='[[isWide]]'
></ha-config-core>
<ha-config-cloud
page-name='cloud'
hass='[[hass]]'
is-wide='[[isWide]]'
account='[[account]]'
></ha-config-cloud>
<ha-config-dashboard
page-name='dashboard'
hass='[[hass]]'
is-wide='[[isWide]]'
account='[[account]]'
></ha-config-dashboard>
<ha-config-automation
@ -62,12 +77,6 @@
is-wide='[[isWide]]'
></ha-config-zwave>
<ha-config-core
page-name='core'
hass='[[hass]]'
is-wide='[[isWide]]'
></ha-config-core>
<hass-error-screen
page-name='not-found'
error='Page not found.'
@ -78,13 +87,15 @@
</dom-module>
<script>
Polymer({
is: 'ha-panel-config',
class HaPanelConfig extends window.hassMixins.EventsMixin(Polymer.Element) {
static get is() { return 'ha-panel-config'; }
properties: {
static get properties() {
return {
hass: Object,
narrow: Boolean,
showMenu: Boolean,
account: Object,
route: {
type: Object,
@ -101,17 +112,32 @@ Polymer({
type: Boolean,
computed: 'computeIsWide(showMenu, wideSidebar, wide)'
},
},
};
}
computeIsWide: function (showMenu, wideSidebar, wide) {
ready() {
super.ready();
if (window.hassUtil.isComponentLoaded(this.hass, 'cloud')) {
this.hass.callApi('get', 'cloud/account').then(
(account) => { this.account = account; }
);
}
this.addEventListener('ha-account-refreshed', (ev) => {
this.account = ev.detail.account;
});
}
computeIsWide(showMenu, wideSidebar, wide) {
return showMenu ? wideSidebar : wide;
},
}
_routeChanged: function (route) {
_routeChanged(route) {
if (route.path === '' && route.prefix === '/config') {
history.replaceState(null, null, '/config/dashboard');
this.fire('location-changed');
}
}
});
}
customElements.define(HaPanelConfig.is, HaPanelConfig);
</script>

View File

@ -66,4 +66,14 @@ window.hassMixins.EventsMixin = Polymer.dedupingMixin(
}
}
);
/* @polymerMixin */
window.hassMixins.NavigateMixin = Polymer.dedupingMixin(
superClass => class extends window.hassMixins.EventsMixin(superClass) {
navigate(path) {
history.pushState(null, null, path);
this.fire('location-changed');
}
});
</script>