parent
2f71369dae
commit
a1057681f1
|
@ -2,7 +2,9 @@
|
||||||
"extends": "airbnb-base",
|
"extends": "airbnb-base",
|
||||||
"globals": {
|
"globals": {
|
||||||
"__DEV__": false,
|
"__DEV__": false,
|
||||||
"Polymer": true
|
"__DEMO__": false,
|
||||||
|
"Polymer": true,
|
||||||
|
"webkitSpeechRecognition": false,
|
||||||
},
|
},
|
||||||
"env": {
|
"env": {
|
||||||
"browser": true
|
"browser": true
|
||||||
|
@ -19,7 +21,10 @@
|
||||||
"prefer-spread": 0,
|
"prefer-spread": 0,
|
||||||
"no-plusplus": 0,
|
"no-plusplus": 0,
|
||||||
"no-bitwise": 0,
|
"no-bitwise": 0,
|
||||||
"comma-dangle": 0
|
"comma-dangle": 0,
|
||||||
|
"vars-on-top": 0,
|
||||||
|
"no-continue": 0,
|
||||||
|
"no-param-reassign": 0
|
||||||
},
|
},
|
||||||
plugins: [
|
plugins: [
|
||||||
"html"
|
"html"
|
||||||
|
|
|
@ -1,3 +0,0 @@
|
||||||
[submodule "home-assistant-js"]
|
|
||||||
path = home-assistant-js
|
|
||||||
url = https://github.com/home-assistant/home-assistant-js.git
|
|
|
@ -1 +0,0 @@
|
||||||
Subproject commit e5b4d6e5a3d1348c82a581dc728709a44a5fcc4a
|
|
|
@ -30,11 +30,7 @@
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"_depComment": "keymirror, nuclear-js, object-assign, ha-js-ws are for ha-js",
|
"_depComment": "keymirror, nuclear-js, object-assign, ha-js-ws are for ha-js",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"classnames": "^2.2.5",
|
"home-assistant-js-websocket": "^0.7.3"
|
||||||
"home-assistant-js-websocket": "0.5.0",
|
|
||||||
"keymirror": "^0.1.1",
|
|
||||||
"nuclear-js": "^1.4.0",
|
|
||||||
"object-assign": "^4.1.1"
|
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"bower": "^1.8.0",
|
"bower": "^1.8.0",
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
<link rel="import" href="../../bower_components/polymer/polymer.html">
|
<link rel="import" href="../../bower_components/polymer/polymer.html">
|
||||||
|
|
||||||
<link rel="import" href="../../src/util/hass-behavior.html">
|
|
||||||
|
|
||||||
<dom-module id="events-list">
|
<dom-module id="events-list">
|
||||||
<template>
|
<template>
|
||||||
|
@ -24,7 +23,7 @@
|
||||||
<template is='dom-repeat' items='[[events]]' as='event'>
|
<template is='dom-repeat' items='[[events]]' as='event'>
|
||||||
<li>
|
<li>
|
||||||
<a href='#' on-click='eventSelected'>{{event.event}}</a>
|
<a href='#' on-click='eventSelected'>{{event.event}}</a>
|
||||||
<span> (</span><span>{{event.listenerCount}}</span><span> listeners)</span>
|
<span> (</span><span>{{event.listener_count}}</span><span> listeners)</span>
|
||||||
</li>
|
</li>
|
||||||
</template>
|
</template>
|
||||||
</ul>
|
</ul>
|
||||||
|
@ -35,8 +34,6 @@
|
||||||
Polymer({
|
Polymer({
|
||||||
is: 'events-list',
|
is: 'events-list',
|
||||||
|
|
||||||
behaviors: [window.hassBehavior],
|
|
||||||
|
|
||||||
properties: {
|
properties: {
|
||||||
hass: {
|
hass: {
|
||||||
type: Object,
|
type: Object,
|
||||||
|
@ -44,17 +41,15 @@
|
||||||
|
|
||||||
events: {
|
events: {
|
||||||
type: Array,
|
type: Array,
|
||||||
bindNuclear: function (hass) {
|
|
||||||
return [
|
|
||||||
hass.eventGetters.entityMap,
|
|
||||||
function (map) {
|
|
||||||
return map.valueSeq().sortBy(function (event) { return event.event; }).toArray();
|
|
||||||
},
|
|
||||||
];
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
|
attached: function () {
|
||||||
|
this.hass.callApi('GET', 'events').then(function (events) {
|
||||||
|
this.events = events;
|
||||||
|
}.bind(this));
|
||||||
|
},
|
||||||
|
|
||||||
eventSelected: function (ev) {
|
eventSelected: function (ev) {
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
this.fire('event-selected', { eventType: ev.model.event.event });
|
this.fire('event-selected', { eventType: ev.model.event.event });
|
||||||
|
|
|
@ -116,7 +116,12 @@ Polymer({
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.hass.eventActions.fireEvent(this.eventType, eventData);
|
this.hass.callApi('POST', 'events/' + this.eventType, eventData)
|
||||||
|
.then(function () {
|
||||||
|
this.fire('hass-notification', {
|
||||||
|
message: 'Event ' + this.eventType + ' successful fired!',
|
||||||
|
});
|
||||||
|
}.bind(this));
|
||||||
},
|
},
|
||||||
|
|
||||||
computeFormClasses: function (narrow) {
|
computeFormClasses: function (narrow) {
|
||||||
|
|
|
@ -7,7 +7,6 @@
|
||||||
<link rel="import" href="../../bower_components/app-layout/app-toolbar/app-toolbar.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="../../src/components/ha-menu-button.html">
|
||||||
<link rel="import" href="../../src/util/hass-behavior.html">
|
|
||||||
<link rel="import" href="../../src/resources/ha-style.html">
|
<link rel="import" href="../../src/resources/ha-style.html">
|
||||||
|
|
||||||
<dom-module id="ha-panel-dev-info">
|
<dom-module id="ha-panel-dev-info">
|
||||||
|
@ -71,10 +70,10 @@
|
||||||
<p class='version'>
|
<p class='version'>
|
||||||
<a href='https://home-assistant.io'><img src="/static/icons/favicon-192x192.png" height="192" /></a><br />
|
<a href='https://home-assistant.io'><img src="/static/icons/favicon-192x192.png" height="192" /></a><br />
|
||||||
Home Assistant<br />
|
Home Assistant<br />
|
||||||
[[hassVersion]]
|
[[hass.config.core.version]]
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
Path to configuration.yaml: [[hassConfigDir]]
|
Path to configuration.yaml: [[hass.config.core.config_dir]]
|
||||||
</p>
|
</p>
|
||||||
<p class='develop'>
|
<p class='develop'>
|
||||||
<a href='https://home-assistant.io/developers/credits/' target='_blank'>
|
<a href='https://home-assistant.io/developers/credits/' target='_blank'>
|
||||||
|
@ -92,7 +91,6 @@
|
||||||
Built using
|
Built using
|
||||||
<a href='https://www.python.org'>Python 3</a>,
|
<a href='https://www.python.org'>Python 3</a>,
|
||||||
<a href='https://www.polymer-project.org' target='_blank'>Polymer [[polymerVersion]]</a>,
|
<a href='https://www.polymer-project.org' target='_blank'>Polymer [[polymerVersion]]</a>,
|
||||||
<a href='https://optimizely.github.io/nuclear-js/' target='_blank'>NuclearJS [[nuclearVersion]]</a><br />
|
|
||||||
Icons by <a href='https://www.google.com/design/icons/' target='_blank'>Google</a> and <a href='https://MaterialDesignIcons.com' target='_blank'>MaterialDesignIcons.com</a>.
|
Icons by <a href='https://www.google.com/design/icons/' target='_blank'>Google</a> and <a href='https://MaterialDesignIcons.com' target='_blank'>MaterialDesignIcons.com</a>.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
@ -110,8 +108,6 @@
|
||||||
Polymer({
|
Polymer({
|
||||||
is: 'ha-panel-dev-info',
|
is: 'ha-panel-dev-info',
|
||||||
|
|
||||||
behaviors: [window.hassBehavior],
|
|
||||||
|
|
||||||
properties: {
|
properties: {
|
||||||
hass: {
|
hass: {
|
||||||
type: Object,
|
type: Object,
|
||||||
|
@ -127,30 +123,11 @@ Polymer({
|
||||||
value: false,
|
value: false,
|
||||||
},
|
},
|
||||||
|
|
||||||
hassVersion: {
|
|
||||||
type: String,
|
|
||||||
bindNuclear: function (hass) {
|
|
||||||
return hass.configGetters.serverVersion;
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
hassConfigDir: {
|
|
||||||
type: String,
|
|
||||||
bindNuclear: function (hass) {
|
|
||||||
return hass.configGetters.configDir;
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
polymerVersion: {
|
polymerVersion: {
|
||||||
type: String,
|
type: String,
|
||||||
value: Polymer.version,
|
value: Polymer.version,
|
||||||
},
|
},
|
||||||
|
|
||||||
nuclearVersion: {
|
|
||||||
type: String,
|
|
||||||
value: '1.4.0',
|
|
||||||
},
|
|
||||||
|
|
||||||
errorLog: {
|
errorLog: {
|
||||||
type: String,
|
type: String,
|
||||||
value: '',
|
value: '',
|
||||||
|
@ -166,7 +143,7 @@ Polymer({
|
||||||
|
|
||||||
this.errorLog = 'Loading error log…';
|
this.errorLog = 'Loading error log…';
|
||||||
|
|
||||||
this.hass.errorLogActions.fetchErrorLog().then(
|
this.hass.callApi('GET', 'error_log').then(
|
||||||
function (log) {
|
function (log) {
|
||||||
this.errorLog = log || 'No errors have been reported.';
|
this.errorLog = log || 'No errors have been reported.';
|
||||||
}.bind(this));
|
}.bind(this));
|
||||||
|
|
|
@ -12,7 +12,6 @@
|
||||||
|
|
||||||
<link rel='import' href='../../src/components/ha-menu-button.html'>
|
<link rel='import' href='../../src/components/ha-menu-button.html'>
|
||||||
<link rel='import' href='../../src/resources/ha-style.html'>
|
<link rel='import' href='../../src/resources/ha-style.html'>
|
||||||
<link rel='import' href='../../src/util/hass-behavior.html'>
|
|
||||||
|
|
||||||
<dom-module id='ha-panel-dev-service'>
|
<dom-module id='ha-panel-dev-service'>
|
||||||
<template>
|
<template>
|
||||||
|
@ -142,8 +141,6 @@
|
||||||
Polymer({
|
Polymer({
|
||||||
is: 'ha-panel-dev-service',
|
is: 'ha-panel-dev-service',
|
||||||
|
|
||||||
behaviors: [window.hassBehavior],
|
|
||||||
|
|
||||||
properties: {
|
properties: {
|
||||||
hass: {
|
hass: {
|
||||||
type: Object,
|
type: Object,
|
||||||
|
@ -178,56 +175,37 @@ Polymer({
|
||||||
|
|
||||||
_attributes: {
|
_attributes: {
|
||||||
type: Array,
|
type: Array,
|
||||||
computed: 'computeAttributesArray(hass, domain, service)',
|
computed: 'computeAttributesArray(serviceDomains, domain, service)',
|
||||||
},
|
},
|
||||||
|
|
||||||
serviceDomains: {
|
serviceDomains: {
|
||||||
type: Array,
|
type: Object,
|
||||||
bindNuclear: function (hass) {
|
computed: 'computeServiceDomains(hass)',
|
||||||
return hass.serviceGetters.entityMap;
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
computeAttributesArray: function (hass, domain, service) {
|
computeServiceDomains: function (hass) {
|
||||||
return hass.reactor.evaluate([
|
return hass.config.services;
|
||||||
hass.serviceGetters.entityMap,
|
},
|
||||||
function (map) {
|
|
||||||
if (map.has(domain) && map.get(domain).get('services').has(service)) {
|
computeAttributesArray: function (serviceDomains, domain, service) {
|
||||||
return map
|
if (!(domain in serviceDomains)) return [];
|
||||||
.get(domain)
|
if (!(service in serviceDomains[domain])) return [];
|
||||||
.get('services')
|
|
||||||
.get(service)
|
var fields = serviceDomains[domain][service].fields;
|
||||||
.get('fields')
|
return Object.keys(fields).map(function (field) {
|
||||||
.map(function (field, key) {
|
return Object.assign({}, fields[field], { key: field });
|
||||||
var fieldCopy = field.toJS();
|
});
|
||||||
fieldCopy.key = key;
|
|
||||||
return fieldCopy;
|
|
||||||
})
|
|
||||||
.toArray();
|
|
||||||
}
|
|
||||||
return [];
|
|
||||||
},
|
|
||||||
]);
|
|
||||||
},
|
},
|
||||||
|
|
||||||
computeDomains: function (serviceDomains) {
|
computeDomains: function (serviceDomains) {
|
||||||
return serviceDomains
|
return Object.keys(serviceDomains).sort();
|
||||||
.valueSeq()
|
|
||||||
.map(function (domain) { return domain.domain; })
|
|
||||||
.sort()
|
|
||||||
.toJS();
|
|
||||||
},
|
},
|
||||||
|
|
||||||
computeServices: function (serviceDomains, domain) {
|
computeServices: function (serviceDomains, domain) {
|
||||||
if (domain) {
|
if (!(domain in serviceDomains)) return [];
|
||||||
return serviceDomains
|
|
||||||
.get(domain)
|
return Object.keys(serviceDomains[domain]).sort();
|
||||||
.get('services')
|
|
||||||
.keySeq()
|
|
||||||
.toArray();
|
|
||||||
}
|
|
||||||
return '';
|
|
||||||
},
|
},
|
||||||
|
|
||||||
domainChanged: function () {
|
domainChanged: function () {
|
||||||
|
@ -250,7 +228,7 @@ Polymer({
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.hass.serviceActions.callService(this.domain, this.service, serviceData);
|
this.hass.callService(this.domain, this.service, serviceData);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -10,7 +10,6 @@
|
||||||
<link rel="import" href="../../bower_components/app-layout/app-toolbar/app-toolbar.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="../../src/components/ha-menu-button.html">
|
||||||
<link rel="import" href="../../src/util/hass-behavior.html">
|
|
||||||
<link rel="import" href="../../src/resources/ha-style.html">
|
<link rel="import" href="../../src/resources/ha-style.html">
|
||||||
|
|
||||||
<dom-module id="ha-panel-dev-state">
|
<dom-module id="ha-panel-dev-state">
|
||||||
|
@ -82,7 +81,7 @@
|
||||||
</tr>
|
</tr>
|
||||||
<template is='dom-repeat' items='[[_entities]]' as='entity'>
|
<template is='dom-repeat' items='[[_entities]]' as='entity'>
|
||||||
<tr>
|
<tr>
|
||||||
<td><a href='#' on-tap='entitySelected'>[[entity.entityId]]</a></td>
|
<td><a href='#' on-tap='entitySelected'>[[entity.entity_id]]</a></td>
|
||||||
<td>[[entity.state]]</td>
|
<td>[[entity.state]]</td>
|
||||||
<template is='dom-if' if='[[computeShowAttributes(narrow, _showAttributes)]]'>
|
<template is='dom-if' if='[[computeShowAttributes(narrow, _showAttributes)]]'>
|
||||||
<td>[[attributeString(entity)]]</td>
|
<td>[[attributeString(entity)]]</td>
|
||||||
|
@ -99,8 +98,6 @@
|
||||||
Polymer({
|
Polymer({
|
||||||
is: 'ha-panel-dev-state',
|
is: 'ha-panel-dev-state',
|
||||||
|
|
||||||
behaviors: [window.hassBehavior],
|
|
||||||
|
|
||||||
properties: {
|
properties: {
|
||||||
hass: {
|
hass: {
|
||||||
type: Object,
|
type: Object,
|
||||||
|
@ -138,20 +135,13 @@ Polymer({
|
||||||
|
|
||||||
_entities: {
|
_entities: {
|
||||||
type: Array,
|
type: Array,
|
||||||
bindNuclear: function (hass) {
|
computed: 'computeEntities(hass)',
|
||||||
return [
|
|
||||||
hass.entityGetters.entityMap,
|
|
||||||
function (map) {
|
|
||||||
return map.valueSeq().sortBy(function (entity) { return entity.entityId; }).toArray();
|
|
||||||
},
|
|
||||||
];
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
entitySelected: function (ev) {
|
entitySelected: function (ev) {
|
||||||
var state = ev.model.entity;
|
var state = ev.model.entity;
|
||||||
this._entityId = state.entityId;
|
this._entityId = state.entity_id;
|
||||||
this._state = state.state;
|
this._state = state.state;
|
||||||
this._stateAttributes = JSON.stringify(state.attributes, null, ' ');
|
this._stateAttributes = JSON.stringify(state.attributes, null, ' ');
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
|
@ -168,13 +158,25 @@ Polymer({
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.hass.entityActions.save({
|
this.hass.callApi('POST', 'states/' + this._entityId, {
|
||||||
entityId: this._entityId,
|
|
||||||
state: this._state,
|
state: this._state,
|
||||||
attributes: attr,
|
attributes: attr,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
computeEntities: function (hass) {
|
||||||
|
return Object.keys(hass.states).map(function (key) { return hass.states[key]; })
|
||||||
|
.sort(function (entityA, entityB) {
|
||||||
|
if (entityA.entity_id < entityB.entity_id) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (entityB.entity_id > entityA.entity_id) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
computeShowAttributes: function (narrow, _showAttributes) {
|
computeShowAttributes: function (narrow, _showAttributes) {
|
||||||
return !narrow && _showAttributes;
|
return !narrow && _showAttributes;
|
||||||
},
|
},
|
||||||
|
|
|
@ -8,7 +8,6 @@
|
||||||
<link rel="import" href="../../bower_components/app-layout/app-toolbar/app-toolbar.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="../../src/components/ha-menu-button.html">
|
||||||
<link rel="import" href="../../src/util/hass-behavior.html">
|
|
||||||
<link rel="import" href="../../src/resources/ha-style.html">
|
<link rel="import" href="../../src/resources/ha-style.html">
|
||||||
|
|
||||||
<dom-module id="ha-panel-dev-template">
|
<dom-module id="ha-panel-dev-template">
|
||||||
|
@ -93,8 +92,6 @@
|
||||||
Polymer({
|
Polymer({
|
||||||
is: 'ha-panel-dev-template',
|
is: 'ha-panel-dev-template',
|
||||||
|
|
||||||
behaviors: [window.hassBehavior],
|
|
||||||
|
|
||||||
properties: {
|
properties: {
|
||||||
hass: {
|
hass: {
|
||||||
type: Object,
|
type: Object,
|
||||||
|
@ -170,14 +167,15 @@ Polymer({
|
||||||
renderTemplate: function () {
|
renderTemplate: function () {
|
||||||
this.rendering = true;
|
this.rendering = true;
|
||||||
|
|
||||||
this.hass.templateActions.render(this.template).then(function (processed) {
|
this.hass.callApi('POST', 'template', { template: this.template })
|
||||||
this.processed = processed;
|
.then(function (processed) {
|
||||||
this.rendering = false;
|
this.processed = processed;
|
||||||
}.bind(this), function (error) {
|
this.rendering = false;
|
||||||
this.processed = error.message;
|
}.bind(this), function (error) {
|
||||||
this.error = true;
|
this.processed = error.message;
|
||||||
this.rendering = false;
|
this.error = true;
|
||||||
}.bind(this));
|
this.rendering = false;
|
||||||
|
}.bind(this));
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -9,7 +9,7 @@
|
||||||
<link rel="import" href="../../src/components/state-history-charts.html">
|
<link rel="import" href="../../src/components/state-history-charts.html">
|
||||||
<link rel="import" href="../../src/resources/pikaday-js.html">
|
<link rel="import" href="../../src/resources/pikaday-js.html">
|
||||||
<link rel="import" href="../../src/components/ha-menu-button.html">
|
<link rel="import" href="../../src/components/ha-menu-button.html">
|
||||||
<link rel="import" href="../../src/util/hass-behavior.html">
|
<link rel="import" href="../../src/data/ha-state-history-data.html">
|
||||||
<link rel="import" href="../../src/resources/ha-style.html">
|
<link rel="import" href="../../src/resources/ha-style.html">
|
||||||
|
|
||||||
<dom-module id="ha-panel-history">
|
<dom-module id="ha-panel-history">
|
||||||
|
@ -24,15 +24,19 @@
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
<ha-state-history-data
|
||||||
|
hass='[[hass]]'
|
||||||
|
filter-type='[[_filterType]]'
|
||||||
|
filter-value='[[_computeFilterDate(_selectedDate)]]'
|
||||||
|
data='{{stateHistory}}'
|
||||||
|
isLoading='{{isLoadingData}}'
|
||||||
|
></ha-state-history-data>
|
||||||
|
|
||||||
<app-header-layout has-scrolling-region>
|
<app-header-layout has-scrolling-region>
|
||||||
<app-header fixed>
|
<app-header fixed>
|
||||||
<app-toolbar>
|
<app-toolbar>
|
||||||
<ha-menu-button narrow='[[narrow]]' show-menu='[[showMenu]]'></ha-menu-button>
|
<ha-menu-button narrow='[[narrow]]' show-menu='[[showMenu]]'></ha-menu-button>
|
||||||
<div main-title>History</div>
|
<div main-title>History</div>
|
||||||
<paper-icon-button
|
|
||||||
icon="mdi:refresh"
|
|
||||||
on-tap="handleRefreshClick"
|
|
||||||
></paper-icon-button>
|
|
||||||
</app-toolbar>
|
</app-toolbar>
|
||||||
</app-header>
|
</app-header>
|
||||||
|
|
||||||
|
@ -40,11 +44,11 @@
|
||||||
<paper-input
|
<paper-input
|
||||||
label='Showing entries for'
|
label='Showing entries for'
|
||||||
id='datePicker'
|
id='datePicker'
|
||||||
value='[[selectedDateStr]]'
|
value='[[_computeDateDisplay(_selectedDate)]]'
|
||||||
on-focus='datepickerFocus'
|
on-focus='datepickerFocus'
|
||||||
></paper-input>
|
></paper-input>
|
||||||
|
|
||||||
<state-history-charts state-history="[[stateHistory]]"
|
<state-history-charts history-data="[[stateHistory]]"
|
||||||
is-loading-data="[[isLoadingData]]"></state-history-charts>
|
is-loading-data="[[isLoadingData]]"></state-history-charts>
|
||||||
</div>
|
</div>
|
||||||
</app-header-layout>
|
</app-header-layout>
|
||||||
|
@ -55,8 +59,6 @@
|
||||||
Polymer({
|
Polymer({
|
||||||
is: 'ha-panel-history',
|
is: 'ha-panel-history',
|
||||||
|
|
||||||
behaviors: [window.hassBehavior],
|
|
||||||
|
|
||||||
properties: {
|
properties: {
|
||||||
hass: {
|
hass: {
|
||||||
type: Object,
|
type: Object,
|
||||||
|
@ -71,61 +73,27 @@ Polymer({
|
||||||
value: false,
|
value: false,
|
||||||
},
|
},
|
||||||
|
|
||||||
isDataLoaded: {
|
|
||||||
type: Boolean,
|
|
||||||
bindNuclear: function (hass) {
|
|
||||||
return hass.entityHistoryGetters.hasDataForCurrentDate;
|
|
||||||
},
|
|
||||||
observer: 'isDataLoadedChanged',
|
|
||||||
},
|
|
||||||
|
|
||||||
stateHistory: {
|
stateHistory: {
|
||||||
type: Object,
|
type: Object,
|
||||||
bindNuclear: function (hass) {
|
value: null,
|
||||||
return hass.entityHistoryGetters.entityHistoryForCurrentDate;
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
|
|
||||||
isLoadingData: {
|
isLoadingData: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
bindNuclear: function (hass) {
|
value: false,
|
||||||
return hass.entityHistoryGetters.isLoadingEntityHistory;
|
},
|
||||||
|
|
||||||
|
_selectedDate: {
|
||||||
|
type: Date,
|
||||||
|
value: function () {
|
||||||
|
return new Date();
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
selectedDate: {
|
_filterType: {
|
||||||
type: String,
|
type: String,
|
||||||
value: null,
|
value: 'date',
|
||||||
bindNuclear: function (hass) {
|
|
||||||
return hass.entityHistoryGetters.currentDate;
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
|
|
||||||
selectedDateStr: {
|
|
||||||
type: String,
|
|
||||||
value: null,
|
|
||||||
bindNuclear: function (hass) {
|
|
||||||
return [
|
|
||||||
hass.entityHistoryGetters.currentDate,
|
|
||||||
function (currentDate) {
|
|
||||||
var dateObj = new Date(currentDate);
|
|
||||||
return window.hassUtil.formatDate(dateObj);
|
|
||||||
},
|
|
||||||
];
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
isDataLoadedChanged: function (newVal) {
|
|
||||||
if (!newVal) {
|
|
||||||
this.async(function () {
|
|
||||||
this.hass.entityHistoryActions.fetchSelectedDate();
|
|
||||||
}.bind(this), 1);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
handleRefreshClick: function () {
|
|
||||||
this.hass.entityHistoryActions.fetchSelectedDate();
|
|
||||||
},
|
},
|
||||||
|
|
||||||
datepickerFocus: function () {
|
datepickerFocus: function () {
|
||||||
|
@ -138,15 +106,30 @@ Polymer({
|
||||||
// field value with its internal formatting.
|
// field value with its internal formatting.
|
||||||
field: document.createElement('input'),
|
field: document.createElement('input'),
|
||||||
trigger: this.$.datePicker.inputElement,
|
trigger: this.$.datePicker.inputElement,
|
||||||
onSelect: this.hass.entityHistoryActions.changeCurrentDate,
|
onSelect: function (newDate) {
|
||||||
|
newDate.setDate(newDate.getDate() + 1);
|
||||||
|
|
||||||
|
if (newDate > new Date()) {
|
||||||
|
newDate = new Date();
|
||||||
|
}
|
||||||
|
|
||||||
|
this._selectedDate = newDate;
|
||||||
|
}.bind(this),
|
||||||
});
|
});
|
||||||
// Set the initial datePicker date, without triggering onSelect handler.
|
// Set the initial datePicker date, without triggering onSelect handler.
|
||||||
this.datePicker.setDate(this.selectedDate, true);
|
this.datePicker.setDate(this._selectedDate, true);
|
||||||
},
|
},
|
||||||
|
|
||||||
detached: function () {
|
detached: function () {
|
||||||
this.datePicker.destroy();
|
this.datePicker.destroy();
|
||||||
},
|
},
|
||||||
|
|
||||||
|
_computeDateDisplay: function (date) {
|
||||||
|
return window.hassUtil.formatDate(new Date(date));
|
||||||
|
},
|
||||||
|
|
||||||
|
_computeFilterDate: function (_selectedDate) {
|
||||||
|
return _selectedDate.toISOString();
|
||||||
|
},
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -0,0 +1,70 @@
|
||||||
|
<link rel="import" href="../../bower_components/polymer/polymer.html">
|
||||||
|
|
||||||
|
<script>
|
||||||
|
(function () {
|
||||||
|
var DATE_CACHE = {};
|
||||||
|
|
||||||
|
Polymer({
|
||||||
|
is: 'ha-logbook-data',
|
||||||
|
|
||||||
|
properties: {
|
||||||
|
hass: {
|
||||||
|
type: Object,
|
||||||
|
observer: 'hassChanged',
|
||||||
|
},
|
||||||
|
|
||||||
|
filterDate: {
|
||||||
|
type: String,
|
||||||
|
observer: 'filterDateChanged',
|
||||||
|
},
|
||||||
|
|
||||||
|
isLoading: {
|
||||||
|
type: Boolean,
|
||||||
|
value: true,
|
||||||
|
readOnly: true,
|
||||||
|
notify: true,
|
||||||
|
},
|
||||||
|
|
||||||
|
entries: {
|
||||||
|
type: Object,
|
||||||
|
value: null,
|
||||||
|
readOnly: true,
|
||||||
|
notify: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
hassChanged: function (newHass, oldHass) {
|
||||||
|
if (!oldHass && this.filterDate) {
|
||||||
|
this.filterDateChanged(this.filterDate);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
filterDateChanged: function (filterDate) {
|
||||||
|
if (!this.hass) return;
|
||||||
|
|
||||||
|
this._setIsLoading(true);
|
||||||
|
|
||||||
|
this.getDate(filterDate).then(function (logbookEntries) {
|
||||||
|
this._setEntries(logbookEntries);
|
||||||
|
this._setIsLoading(false);
|
||||||
|
}.bind(this));
|
||||||
|
},
|
||||||
|
|
||||||
|
getDate: function (date) {
|
||||||
|
if (!DATE_CACHE[date]) {
|
||||||
|
DATE_CACHE[date] = this.hass.callApi('GET', 'logbook/' + date).then(
|
||||||
|
function (logbookEntries) {
|
||||||
|
return logbookEntries;
|
||||||
|
},
|
||||||
|
function () {
|
||||||
|
DATE_CACHE[date] = false;
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return DATE_CACHE[date];
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}());
|
||||||
|
</script>
|
|
@ -47,10 +47,10 @@
|
||||||
<div class='time'>[[formatTime(item.when)]]</div>
|
<div class='time'>[[formatTime(item.when)]]</div>
|
||||||
<domain-icon domain="[[item.domain]]" class='icon'></domain-icon>
|
<domain-icon domain="[[item.domain]]" class='icon'></domain-icon>
|
||||||
<div class='message' flex>
|
<div class='message' flex>
|
||||||
<template is='dom-if' if="[[!item.entityId]]">
|
<template is='dom-if' if="[[!item.entity_id]]">
|
||||||
<span class='name'>[[item.name]]</span>
|
<span class='name'>[[item.name]]</span>
|
||||||
</template>
|
</template>
|
||||||
<template is='dom-if' if="[[item.entityId]]">
|
<template is='dom-if' if="[[item.entity_id]]">
|
||||||
<a href='#' on-tap="entityClicked" class='name'>[[item.name]]</a>
|
<a href='#' on-tap="entityClicked" class='name'>[[item.name]]</a>
|
||||||
</template>
|
</template>
|
||||||
<span> </span>
|
<span> </span>
|
||||||
|
@ -76,13 +76,13 @@ Polymer({
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
formatTime: function (dateObj) {
|
formatTime: function (date) {
|
||||||
return window.hassUtil.formatTime(dateObj);
|
return window.hassUtil.formatTime(new Date(date));
|
||||||
},
|
},
|
||||||
|
|
||||||
entityClicked: function (ev) {
|
entityClicked: function (ev) {
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
this.hass.moreInfoActions.selectEntity(ev.model.item.entityId);
|
this.fire('hass-more-info', { entityId: ev.model.item.entity_id });
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -10,10 +10,10 @@
|
||||||
|
|
||||||
<link rel="import" href="../../src/components/ha-menu-button.html">
|
<link rel="import" href="../../src/components/ha-menu-button.html">
|
||||||
<link rel="import" href="../../src/resources/pikaday-js.html">
|
<link rel="import" href="../../src/resources/pikaday-js.html">
|
||||||
<link rel="import" href="../../src/util/hass-behavior.html">
|
|
||||||
<link rel="import" href="../../src/resources/ha-style.html">
|
<link rel="import" href="../../src/resources/ha-style.html">
|
||||||
|
|
||||||
<link rel="import" href="./ha-logbook.html">
|
<link rel="import" href="./ha-logbook.html">
|
||||||
|
<link rel="import" href="./ha-logbook-data.html">
|
||||||
|
|
||||||
<dom-module id="ha-panel-logbook">
|
<dom-module id="ha-panel-logbook">
|
||||||
<template>
|
<template>
|
||||||
|
@ -31,15 +31,18 @@
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
<ha-logbook-data
|
||||||
|
hass='[[hass]]'
|
||||||
|
is-loading='{{isLoading}}'
|
||||||
|
entries='{{entries}}'
|
||||||
|
filter-date='[[_computeFilterDate(_selectedDate)]]'
|
||||||
|
></ha-logbook-data>
|
||||||
|
|
||||||
<app-header-layout has-scrolling-region>
|
<app-header-layout has-scrolling-region>
|
||||||
<app-header fixed>
|
<app-header fixed>
|
||||||
<app-toolbar>
|
<app-toolbar>
|
||||||
<ha-menu-button narrow='[[narrow]]' show-menu='[[showMenu]]'></ha-menu-button>
|
<ha-menu-button narrow='[[narrow]]' show-menu='[[showMenu]]'></ha-menu-button>
|
||||||
<div main-title>Logbook</div>
|
<div main-title>Logbook</div>
|
||||||
<paper-icon-button
|
|
||||||
icon="mdi:refresh"
|
|
||||||
on-tap="handleRefresh"
|
|
||||||
></paper-icon-button>
|
|
||||||
</app-toolbar>
|
</app-toolbar>
|
||||||
</app-header>
|
</app-header>
|
||||||
|
|
||||||
|
@ -48,7 +51,7 @@
|
||||||
<paper-input
|
<paper-input
|
||||||
label='Showing entries for'
|
label='Showing entries for'
|
||||||
id='datePicker'
|
id='datePicker'
|
||||||
value='[[selectedDateStr]]'
|
value='[[_computeDateDisplay(_selectedDate)]]'
|
||||||
on-focus='datepickerFocus'
|
on-focus='datepickerFocus'
|
||||||
></paper-input>
|
></paper-input>
|
||||||
|
|
||||||
|
@ -68,8 +71,6 @@
|
||||||
Polymer({
|
Polymer({
|
||||||
is: 'ha-panel-logbook',
|
is: 'ha-panel-logbook',
|
||||||
|
|
||||||
behaviors: [window.hassBehavior],
|
|
||||||
|
|
||||||
properties: {
|
properties: {
|
||||||
hass: {
|
hass: {
|
||||||
type: Object,
|
type: Object,
|
||||||
|
@ -85,50 +86,19 @@ Polymer({
|
||||||
value: false,
|
value: false,
|
||||||
},
|
},
|
||||||
|
|
||||||
selectedDate: {
|
_selectedDate: {
|
||||||
type: String,
|
type: String,
|
||||||
bindNuclear: function (hass) {
|
value: function () {
|
||||||
return hass.logbookGetters.currentDate;
|
return new Date();
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
selectedDateStr: {
|
|
||||||
type: String,
|
|
||||||
value: null,
|
|
||||||
bindNuclear: function (hass) {
|
|
||||||
return [
|
|
||||||
hass.logbookGetters.currentDate,
|
|
||||||
function (currentDate) {
|
|
||||||
var dateObj = new Date(currentDate);
|
|
||||||
return window.hassUtil.formatDate(dateObj);
|
|
||||||
},
|
|
||||||
];
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
isLoading: {
|
isLoading: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
bindNuclear: function (hass) {
|
|
||||||
return hass.logbookGetters.isLoadingEntries;
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
isStale: {
|
|
||||||
type: Boolean,
|
|
||||||
bindNuclear: function (hass) {
|
|
||||||
return hass.logbookGetters.isCurrentStale;
|
|
||||||
},
|
|
||||||
observer: 'isStaleChanged',
|
|
||||||
},
|
},
|
||||||
|
|
||||||
entries: {
|
entries: {
|
||||||
type: Array,
|
type: Array,
|
||||||
bindNuclear: function (hass) {
|
|
||||||
return [
|
|
||||||
hass.logbookGetters.currentEntries,
|
|
||||||
function (entries) { return entries.reverse().toArray(); },
|
|
||||||
];
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
|
|
||||||
datePicker: {
|
datePicker: {
|
||||||
|
@ -136,18 +106,6 @@ Polymer({
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
isStaleChanged: function (newVal) {
|
|
||||||
if (newVal) {
|
|
||||||
this.async(function () {
|
|
||||||
this.hass.logbookActions.fetchDate(this.selectedDate);
|
|
||||||
}.bind(this), 1);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
handleRefresh: function () {
|
|
||||||
this.hass.logbookActions.fetchDate(this.selectedDate);
|
|
||||||
},
|
|
||||||
|
|
||||||
datepickerFocus: function () {
|
datepickerFocus: function () {
|
||||||
this.datePicker.adjustPosition();
|
this.datePicker.adjustPosition();
|
||||||
},
|
},
|
||||||
|
@ -158,7 +116,15 @@ Polymer({
|
||||||
// field value with its internal formatting.
|
// field value with its internal formatting.
|
||||||
field: document.createElement('input'),
|
field: document.createElement('input'),
|
||||||
trigger: this.$.datePicker.inputElement,
|
trigger: this.$.datePicker.inputElement,
|
||||||
onSelect: this.hass.logbookActions.changeCurrentDate,
|
onSelect: function (newDate) {
|
||||||
|
newDate.setDate(newDate.getDate() + 1);
|
||||||
|
|
||||||
|
if (newDate > new Date()) {
|
||||||
|
newDate = new Date();
|
||||||
|
}
|
||||||
|
|
||||||
|
this._selectedDate = newDate;
|
||||||
|
}.bind(this),
|
||||||
});
|
});
|
||||||
// Set the initial datePicker date, without triggering onSelect handler.
|
// Set the initial datePicker date, without triggering onSelect handler.
|
||||||
this.datePicker.setDate(this.selectedDate, true);
|
this.datePicker.setDate(this.selectedDate, true);
|
||||||
|
@ -168,5 +134,12 @@ Polymer({
|
||||||
this.datePicker.destroy();
|
this.datePicker.destroy();
|
||||||
},
|
},
|
||||||
|
|
||||||
|
_computeDateDisplay: function (date) {
|
||||||
|
return window.hassUtil.formatDate(new Date(date));
|
||||||
|
},
|
||||||
|
|
||||||
|
_computeFilterDate: function (_selectedDate) {
|
||||||
|
return _selectedDate.toISOString();
|
||||||
|
},
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -27,27 +27,24 @@
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<div class='marker'>
|
<div class='marker'>
|
||||||
<template is='dom-if' if='[[icon]]'>
|
<template is='dom-if' if='[[entityName]]'>[[entityName]]</template>
|
||||||
<iron-icon icon='[[icon]]'></iron-icon>
|
<template is='dom-if' if='[[entityPicture]]'>
|
||||||
</template>
|
<iron-image sizing='cover' class='fit' src='[[entityPicture]]'></iron-image>
|
||||||
<template is='dom-if' if='[[value]]'>[[value]]</template>
|
|
||||||
<template is='dom-if' if='[[image]]'>
|
|
||||||
<iron-image sizing='cover' class='fit' src='[[image]]'></iron-image>
|
|
||||||
</template>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</dom-module>
|
</dom-module>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
/*
|
|
||||||
Leaflet clones this element before adding it to the map. This messes up
|
|
||||||
our Poylmer object and we lose the reference to the `hass` object.
|
|
||||||
|
|
||||||
That's why we refer here to window.hass instead of the hass property.
|
|
||||||
*/
|
|
||||||
Polymer({
|
Polymer({
|
||||||
is: 'ha-entity-marker',
|
is: 'ha-entity-marker',
|
||||||
|
|
||||||
|
hostAttributes: {
|
||||||
|
entityId: null,
|
||||||
|
entityName: null,
|
||||||
|
entityPicture: null,
|
||||||
|
},
|
||||||
|
|
||||||
properties: {
|
properties: {
|
||||||
hass: {
|
hass: {
|
||||||
type: Object,
|
type: Object,
|
||||||
|
@ -56,28 +53,17 @@ Polymer({
|
||||||
entityId: {
|
entityId: {
|
||||||
type: String,
|
type: String,
|
||||||
value: '',
|
value: '',
|
||||||
reflectToAttribute: true,
|
|
||||||
},
|
},
|
||||||
|
|
||||||
state: {
|
entityName: {
|
||||||
type: Object,
|
|
||||||
computed: 'computeState(entityId)',
|
|
||||||
},
|
|
||||||
|
|
||||||
icon: {
|
|
||||||
type: Object,
|
|
||||||
computed: 'computeIcon(state)',
|
|
||||||
},
|
|
||||||
|
|
||||||
image: {
|
|
||||||
type: Object,
|
|
||||||
computed: 'computeImage(state)',
|
|
||||||
},
|
|
||||||
|
|
||||||
value: {
|
|
||||||
type: String,
|
type: String,
|
||||||
computed: 'computeValue(state)',
|
value: null,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
entityPicture: {
|
||||||
|
type: String,
|
||||||
|
value: null,
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
listeners: {
|
listeners: {
|
||||||
|
@ -87,29 +73,8 @@ Polymer({
|
||||||
badgeTap: function (ev) {
|
badgeTap: function (ev) {
|
||||||
ev.stopPropagation();
|
ev.stopPropagation();
|
||||||
if (this.entityId) {
|
if (this.entityId) {
|
||||||
this.async(function () {
|
this.fire('hass-more-info', { entityId: this.entityId });
|
||||||
window.hass.moreInfoActions.selectEntity(this.entityId);
|
|
||||||
}, 1);
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
computeState: function (entityId) {
|
|
||||||
return entityId && window.hass.reactor.evaluate(window.hass.entityGetters.byId(entityId));
|
|
||||||
},
|
|
||||||
|
|
||||||
computeIcon: function (state) {
|
|
||||||
return !state && 'home';
|
|
||||||
},
|
|
||||||
|
|
||||||
computeImage: function (state) {
|
|
||||||
return state && state.attributes.entity_picture;
|
|
||||||
},
|
|
||||||
|
|
||||||
computeValue: function (state) {
|
|
||||||
return state &&
|
|
||||||
state.entityDisplay.split(' ').map(function (part) {
|
|
||||||
return part.substr(0, 1);
|
|
||||||
}).join('');
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -8,7 +8,6 @@
|
||||||
<link rel="stylesheet" href="../../bower_components/leaflet/dist/leaflet.css" />
|
<link rel="stylesheet" href="../../bower_components/leaflet/dist/leaflet.css" />
|
||||||
|
|
||||||
<link rel="import" href="../../src/components/ha-menu-button.html">
|
<link rel="import" href="../../src/components/ha-menu-button.html">
|
||||||
<link rel="import" href="../../src/util/hass-behavior.html">
|
|
||||||
|
|
||||||
<link rel="import" href="./ha-entity-marker.html">
|
<link rel="import" href="./ha-entity-marker.html">
|
||||||
|
|
||||||
|
@ -37,41 +36,9 @@ window.L.Icon.Default.imagePath = '/static/images/leaflet';
|
||||||
Polymer({
|
Polymer({
|
||||||
is: 'ha-panel-map',
|
is: 'ha-panel-map',
|
||||||
|
|
||||||
behaviors: [window.hassBehavior],
|
|
||||||
|
|
||||||
properties: {
|
properties: {
|
||||||
hass: {
|
hass: {
|
||||||
type: Object,
|
type: Object,
|
||||||
},
|
|
||||||
|
|
||||||
locationGPS: {
|
|
||||||
type: Number,
|
|
||||||
bindNuclear: function (hass) {
|
|
||||||
return hass.configGetters.locationGPS;
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
locationName: {
|
|
||||||
type: String,
|
|
||||||
bindNuclear: function (hass) {
|
|
||||||
return hass.configGetters.locationName;
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
locationEntities: {
|
|
||||||
type: Array,
|
|
||||||
bindNuclear: function (hass) {
|
|
||||||
return [
|
|
||||||
hass.entityGetters.entityMap,
|
|
||||||
function (entities) {
|
|
||||||
return entities.valueSeq().filter(
|
|
||||||
function (entity) {
|
|
||||||
return 'latitude' in entity.attributes;
|
|
||||||
}
|
|
||||||
);
|
|
||||||
},
|
|
||||||
];
|
|
||||||
},
|
|
||||||
observer: 'drawEntities',
|
observer: 'drawEntities',
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -96,7 +63,7 @@ Polymer({
|
||||||
}
|
}
|
||||||
).addTo(map);
|
).addTo(map);
|
||||||
|
|
||||||
this.drawEntities(this.locationEntities);
|
this.drawEntities(this.hass);
|
||||||
|
|
||||||
this.async(function () {
|
this.async(function () {
|
||||||
map.invalidateSize();
|
map.invalidateSize();
|
||||||
|
@ -118,7 +85,7 @@ Polymer({
|
||||||
this._map.fitBounds(bounds.pad(0.5));
|
this._map.fitBounds(bounds.pad(0.5));
|
||||||
},
|
},
|
||||||
|
|
||||||
drawEntities: function (entities) {
|
drawEntities: function (hass) {
|
||||||
/* eslint-disable vars-on-top */
|
/* eslint-disable vars-on-top */
|
||||||
var map = this._map;
|
var map = this._map;
|
||||||
if (!map) return;
|
if (!map) return;
|
||||||
|
@ -128,10 +95,21 @@ Polymer({
|
||||||
}
|
}
|
||||||
var mapItems = this._mapItems = [];
|
var mapItems = this._mapItems = [];
|
||||||
|
|
||||||
entities.forEach(function (entity) {
|
Object.keys(hass.states).forEach(function (entityId) {
|
||||||
|
var entity = hass.states[entityId];
|
||||||
|
var title = window.hassUtil.computeStateName(entity);
|
||||||
|
|
||||||
|
if ((entity.attributes.hidden &&
|
||||||
|
window.hassUtil.computeDomain(entity) !== 'zone') ||
|
||||||
|
entity.state === 'home' ||
|
||||||
|
!('latitude' in entity.attributes) ||
|
||||||
|
!('longitude' in entity.attributes)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
var icon;
|
var icon;
|
||||||
|
|
||||||
if (entity.domain === 'zone') {
|
if (window.hassUtil.computeDomain(entity) === 'zone') {
|
||||||
// DRAW ZONE
|
// DRAW ZONE
|
||||||
if (entity.attributes.passive) return;
|
if (entity.attributes.passive) return;
|
||||||
|
|
||||||
|
@ -141,7 +119,7 @@ Polymer({
|
||||||
iconHTML = (
|
iconHTML = (
|
||||||
"<iron-icon icon='" + entity.attributes.icon + "'></iron-icon>");
|
"<iron-icon icon='" + entity.attributes.icon + "'></iron-icon>");
|
||||||
} else {
|
} else {
|
||||||
iconHTML = entity.entityDisplay;
|
iconHTML = title;
|
||||||
}
|
}
|
||||||
|
|
||||||
icon = window.L.divIcon({
|
icon = window.L.divIcon({
|
||||||
|
@ -156,7 +134,7 @@ Polymer({
|
||||||
{
|
{
|
||||||
icon,
|
icon,
|
||||||
interactive: false,
|
interactive: false,
|
||||||
title: entity.entityDisplay,
|
title: title,
|
||||||
}
|
}
|
||||||
).addTo(map));
|
).addTo(map));
|
||||||
|
|
||||||
|
@ -173,13 +151,14 @@ Polymer({
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Filter out entities at home
|
|
||||||
if (entity.state === 'home' || entity.attributes.hidden) return;
|
|
||||||
|
|
||||||
// DRAW ENTITY
|
// DRAW ENTITY
|
||||||
// create icon
|
// create icon
|
||||||
|
var entityPicture = entity.attributes.entity_picture || '';
|
||||||
|
var entityName = title.split(' ').map(function (part) { return part.substr(0, 1); }).join('');
|
||||||
|
/* Leaflet clones this element before adding it to the map. This messes up
|
||||||
|
our Poylmer object and we can't pass data through. Thus we hack like this. */
|
||||||
icon = window.L.divIcon({
|
icon = window.L.divIcon({
|
||||||
html: "<ha-entity-marker entity-id='" + entity.entityId + "'></ha-entity-marker>",
|
html: "<ha-entity-marker entity-id='" + entity.entity_id + "' entity-name='" + entityName + "' entity-picture='" + entityPicture + "'></ha-entity-marker>",
|
||||||
iconSize: [45, 45],
|
iconSize: [45, 45],
|
||||||
className: '',
|
className: '',
|
||||||
});
|
});
|
||||||
|
@ -189,7 +168,7 @@ Polymer({
|
||||||
[entity.attributes.latitude, entity.attributes.longitude],
|
[entity.attributes.latitude, entity.attributes.longitude],
|
||||||
{
|
{
|
||||||
icon,
|
icon,
|
||||||
title: entity.entityDisplay,
|
title: window.hassUtil.computeStateName(entity),
|
||||||
}
|
}
|
||||||
).addTo(map));
|
).addTo(map));
|
||||||
|
|
||||||
|
@ -212,7 +191,7 @@ Polymer({
|
||||||
},
|
},
|
||||||
|
|
||||||
toggleMenu: function () {
|
toggleMenu: function () {
|
||||||
this.fire('open-menu');
|
this.fire('hass-open-menu');
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -18,11 +18,10 @@ const plugins = [
|
||||||
__DEMO__: JSON.stringify(DEMO),
|
__DEMO__: JSON.stringify(DEMO),
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
|
|
||||||
buble(),
|
|
||||||
];
|
];
|
||||||
|
|
||||||
if (!DEV) {
|
if (!DEV) {
|
||||||
|
plugins.push(buble());
|
||||||
plugins.push(uglify());
|
plugins.push(uglify());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,41 +1,28 @@
|
||||||
import HomeAssistant from '../home-assistant-js/src/index';
|
import * as HAWS from 'home-assistant-js-websocket';
|
||||||
|
|
||||||
const hass = new HomeAssistant();
|
window.HAWS = HAWS;
|
||||||
|
window.HASS_DEMO = __DEMO__;
|
||||||
|
|
||||||
window.validateAuth = function validateAuth(authToken, rememberAuth) {
|
const init = window.createHassConnection = function (password) {
|
||||||
hass.authActions.validate(authToken, {
|
const proto = window.location.protocol === 'https:' ? 'wss' : 'ws';
|
||||||
rememberAuth,
|
const url = `${proto}://${window.location.host}/api/websocket`;
|
||||||
useStreaming: hass.localStoragePreferences.useStreaming,
|
const options = {};
|
||||||
});
|
if (password !== undefined) {
|
||||||
|
options.authToken = password;
|
||||||
|
}
|
||||||
|
|
||||||
|
return HAWS.createConnection(url, options)
|
||||||
|
.then(function (conn) {
|
||||||
|
HAWS.subscribeEntities(conn);
|
||||||
|
HAWS.subscribeConfig(conn);
|
||||||
|
return conn;
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
window.removeInitMsg = function removeInitMessage() {
|
if (window.noAuth) {
|
||||||
// remove the HTML init message
|
window.hassConnection = init();
|
||||||
const initMsg = document.getElementById('ha-init-skeleton');
|
} else if (window.localStorage.authToken) {
|
||||||
if (initMsg) {
|
window.hassConnection = init(window.localStorage.authToken);
|
||||||
initMsg.parentElement.removeChild(initMsg);
|
} else {
|
||||||
}
|
window.hassConnection = null;
|
||||||
};
|
|
||||||
|
|
||||||
hass.reactor.batch(function () {
|
|
||||||
hass.navigationActions.showSidebar(
|
|
||||||
hass.localStoragePreferences.showSidebar);
|
|
||||||
|
|
||||||
// if auth was given, tell the backend
|
|
||||||
if (window.noAuth) {
|
|
||||||
window.validateAuth('', false);
|
|
||||||
} else if (hass.localStoragePreferences.authToken) {
|
|
||||||
window.validateAuth(hass.localStoragePreferences.authToken, true);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
setTimeout(hass.startLocalStoragePreferencesSync, 5000);
|
|
||||||
|
|
||||||
if ('serviceWorker' in navigator) {
|
|
||||||
window.addEventListener('load', function () {
|
|
||||||
navigator.serviceWorker.register('/service_worker.js');
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// While we figure out how ha-entity-marker can keep it's references
|
|
||||||
window.hass = hass;
|
|
||||||
|
|
|
@ -38,9 +38,9 @@
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<img src='[[cameraFeedSrc]]' class='camera-feed' hidden$='[[!imageLoaded]]'
|
<img src='[[cameraFeedSrc]]' class='camera-feed' hidden$='[[!imageLoaded]]'
|
||||||
on-load='imageLoadSuccess' on-error='imageLoadFail' alt='[[stateObj.entityDisplay]]'>
|
on-load='imageLoadSuccess' on-error='imageLoadFail' alt='[[computeStateName(stateObj)]]'>
|
||||||
<div class='caption'>
|
<div class='caption'>
|
||||||
[[stateObj.entityDisplay]]
|
[[computeStateName(stateObj)]]
|
||||||
<template is='dom-if' if='[[!imageLoaded]]'>
|
<template is='dom-if' if='[[!imageLoaded]]'>
|
||||||
(Error loading image)
|
(Error loading image)
|
||||||
</template>
|
</template>
|
||||||
|
@ -99,9 +99,7 @@ Polymer({
|
||||||
},
|
},
|
||||||
|
|
||||||
cardTapped: function () {
|
cardTapped: function () {
|
||||||
this.async(function () {
|
this.fire('hass-more-info', { entityId: this.stateObj.entity_id });
|
||||||
this.hass.moreInfoActions.selectEntity(this.stateObj.entityId);
|
|
||||||
}.bind(this), 1);
|
|
||||||
},
|
},
|
||||||
|
|
||||||
updateCameraFeedSrc: function (stateObj) {
|
updateCameraFeedSrc: function (stateObj) {
|
||||||
|
@ -117,5 +115,9 @@ Polymer({
|
||||||
imageLoadFail: function () {
|
imageLoadFail: function () {
|
||||||
this.imageLoaded = false;
|
this.imageLoaded = false;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
computeStateName: function (stateObj) {
|
||||||
|
return window.hassUtil.computeStateName(stateObj);
|
||||||
|
},
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -75,14 +75,16 @@ Polymer({
|
||||||
states: {
|
states: {
|
||||||
type: Array,
|
type: Array,
|
||||||
},
|
},
|
||||||
|
|
||||||
groupEntity: {
|
groupEntity: {
|
||||||
type: Object,
|
type: Object,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
computeTitle: function (states, groupEntity) {
|
computeTitle: function (states, groupEntity) {
|
||||||
return groupEntity ? groupEntity.entityDisplay :
|
return groupEntity ?
|
||||||
states[0].domain.replace(/_/g, ' ');
|
window.hassUtil.computeStateName(groupEntity) :
|
||||||
|
window.hassUtil.computeDomain(states[0]).replace(/_/g, ' ');
|
||||||
},
|
},
|
||||||
|
|
||||||
computeTitleClass: function (groupEntity) {
|
computeTitleClass: function (groupEntity) {
|
||||||
|
@ -105,26 +107,32 @@ Polymer({
|
||||||
ev.stopPropagation();
|
ev.stopPropagation();
|
||||||
|
|
||||||
if (ev.model) {
|
if (ev.model) {
|
||||||
entityId = ev.model.item.entityId;
|
entityId = ev.model.item.entity_id;
|
||||||
} else {
|
} else {
|
||||||
entityId = this.groupEntity.entityId;
|
entityId = this.groupEntity.entity_id;
|
||||||
}
|
}
|
||||||
this.async(function () { this.hass.moreInfoActions.selectEntity(entityId); }.bind(this), 1);
|
this.fire('hass-more-info', { entityId: entityId });
|
||||||
},
|
},
|
||||||
|
|
||||||
showGroupToggle: function (groupEntity, states) {
|
showGroupToggle: function (groupEntity, states) {
|
||||||
var canToggleCount;
|
|
||||||
|
|
||||||
if (!groupEntity || !states || groupEntity.attributes.control === 'hidden' ||
|
if (!groupEntity || !states || groupEntity.attributes.control === 'hidden' ||
|
||||||
(groupEntity.state !== 'on' && groupEntity.state !== 'off')) {
|
(groupEntity.state !== 'on' && groupEntity.state !== 'off')) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// only show if we can toggle 2+ entities in group
|
// only show if we can toggle 2+ entities in group
|
||||||
canToggleCount = states.reduce(
|
var canToggleCount = 0;
|
||||||
function (sum, state) {
|
for (var i = 0; i < states.length; i++) {
|
||||||
return sum + window.hassUtil.canToggle(this.hass, state.entityId);
|
if (!window.hassUtil.canToggleState(this.hass, states[i])) {
|
||||||
}, 0);
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
canToggleCount++;
|
||||||
|
|
||||||
|
if (canToggleCount > 1) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return canToggleCount > 1;
|
return canToggleCount > 1;
|
||||||
},
|
},
|
||||||
|
|
|
@ -6,6 +6,8 @@
|
||||||
<link rel='import' href='../../bower_components/paper-icon-button/paper-icon-button.html'>
|
<link rel='import' href='../../bower_components/paper-icon-button/paper-icon-button.html'>
|
||||||
<link rel='import' href='../../bower_components/paper-progress/paper-progress.html'>
|
<link rel='import' href='../../bower_components/paper-progress/paper-progress.html'>
|
||||||
|
|
||||||
|
<link rel='import' href='../util/media-player-model.html'>
|
||||||
|
|
||||||
<dom-module id='ha-media_player-card'>
|
<dom-module id='ha-media_player-card'>
|
||||||
<template>
|
<template>
|
||||||
<style include="paper-material iron-flex iron-flex-alignment iron-positioning">
|
<style include="paper-material iron-flex iron-flex-alignment iron-positioning">
|
||||||
|
@ -149,7 +151,7 @@
|
||||||
<div class='cover' id='cover'></div>
|
<div class='cover' id='cover'></div>
|
||||||
|
|
||||||
<div class='caption'>
|
<div class='caption'>
|
||||||
[[stateObj.entityDisplay]]
|
[[computeStateName(stateObj)]]
|
||||||
<div class='title'>[[playerObj.primaryText]]</div>
|
<div class='title'>[[playerObj.primaryText]]</div>
|
||||||
[[playerObj.secondaryText]]<br />
|
[[playerObj.secondaryText]]<br />
|
||||||
</div>
|
</div>
|
||||||
|
@ -217,7 +219,7 @@ Polymer({
|
||||||
|
|
||||||
playerObj: {
|
playerObj: {
|
||||||
type: Object,
|
type: Object,
|
||||||
computed: 'computePlayerObj(stateObj)',
|
computed: 'computePlayerObj(hass, stateObj)',
|
||||||
observer: 'playerObjChanged',
|
observer: 'playerObjChanged',
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -290,8 +292,8 @@ Polymer({
|
||||||
return playerObj.isOff ? !playerObj.supportsTurnOn : !playerObj.supportsTurnOff;
|
return playerObj.isOff ? !playerObj.supportsTurnOn : !playerObj.supportsTurnOff;
|
||||||
},
|
},
|
||||||
|
|
||||||
computePlayerObj: function (stateObj) {
|
computePlayerObj: function (hass, stateObj) {
|
||||||
return stateObj.domainModel(this.hass);
|
return new window.MediaPlayerEntity(hass, stateObj);
|
||||||
},
|
},
|
||||||
|
|
||||||
computePlaybackControlIcon: function (playerObj) {
|
computePlaybackControlIcon: function (playerObj) {
|
||||||
|
@ -303,6 +305,10 @@ Polymer({
|
||||||
return '';
|
return '';
|
||||||
},
|
},
|
||||||
|
|
||||||
|
computeStateName: function (stateObj) {
|
||||||
|
return window.hassUtil.computeStateName(stateObj);
|
||||||
|
},
|
||||||
|
|
||||||
handleNext: function (ev) {
|
handleNext: function (ev) {
|
||||||
ev.stopPropagation();
|
ev.stopPropagation();
|
||||||
this.playerObj.nextTrack();
|
this.playerObj.nextTrack();
|
||||||
|
@ -310,9 +316,7 @@ Polymer({
|
||||||
|
|
||||||
handleOpenMoreInfo: function (ev) {
|
handleOpenMoreInfo: function (ev) {
|
||||||
ev.stopPropagation();
|
ev.stopPropagation();
|
||||||
this.async(function () {
|
this.fire('hass-more-info', { entityId: this.stateObj.entity_id });
|
||||||
this.hass.moreInfoActions.selectEntity(this.stateObj.entityId);
|
|
||||||
}, 1);
|
|
||||||
},
|
},
|
||||||
|
|
||||||
handlePlaybackControl: function (ev) {
|
handlePlaybackControl: function (ev) {
|
||||||
|
|
|
@ -49,7 +49,8 @@ Polymer({
|
||||||
],
|
],
|
||||||
|
|
||||||
computeTitle: function (stateObj) {
|
computeTitle: function (stateObj) {
|
||||||
return stateObj.attributes.title || stateObj.entityDisplay;
|
return (stateObj.attributes.title ||
|
||||||
|
window.hassUtil.computeStateName(stateObj));
|
||||||
},
|
},
|
||||||
|
|
||||||
loadScript: function () {
|
loadScript: function () {
|
||||||
|
@ -86,7 +87,7 @@ Polymer({
|
||||||
dismissTap: function (ev) {
|
dismissTap: function (ev) {
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
|
|
||||||
this.hass.entityActions.delete(this.stateObj);
|
this.hass.callApi('DELETE', 'states/' + this.stateObj.entity_id);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -110,24 +110,26 @@ Polymer({
|
||||||
// result in the entity to be turned on. Since the state is not changing,
|
// result in the entity to be turned on. Since the state is not changing,
|
||||||
// the resync is not called automatic.
|
// the resync is not called automatic.
|
||||||
callService: function (turnOn) {
|
callService: function (turnOn) {
|
||||||
var domain;
|
var stateDomain = window.hassUtil.computeDomain(this.stateObj);
|
||||||
|
var serviceDomain;
|
||||||
var service;
|
var service;
|
||||||
var currentState;
|
var currentState;
|
||||||
|
|
||||||
if (this.stateObj.domain === 'lock') {
|
if (stateDomain === 'lock') {
|
||||||
domain = 'lock';
|
serviceDomain = 'lock';
|
||||||
service = turnOn ? 'lock' : 'unlock';
|
service = turnOn ? 'lock' : 'unlock';
|
||||||
} else if (this.stateObj.domain === 'garage_door') {
|
} else if (stateDomain === 'cover') {
|
||||||
domain = 'garage_door';
|
serviceDomain = 'cover';
|
||||||
service = turnOn ? 'open' : 'close';
|
service = turnOn ? 'open' : 'close';
|
||||||
} else {
|
} else {
|
||||||
domain = 'homeassistant';
|
serviceDomain = 'homeassistant';
|
||||||
service = turnOn ? 'turn_on' : 'turn_off';
|
service = turnOn ? 'turn_on' : 'turn_off';
|
||||||
}
|
}
|
||||||
|
|
||||||
currentState = this.stateObj;
|
currentState = this.stateObj;
|
||||||
this.hass.serviceActions.callService(domain, service,
|
this.hass.callService(
|
||||||
{ entity_id: this.stateObj.entityId })
|
serviceDomain, service,
|
||||||
|
{ entity_id: this.stateObj.entity_id })
|
||||||
.then(function () {
|
.then(function () {
|
||||||
setTimeout(function () {
|
setTimeout(function () {
|
||||||
// If after 2 seconds we have not received a state update
|
// If after 2 seconds we have not received a state update
|
||||||
|
|
|
@ -57,13 +57,11 @@ Polymer({
|
||||||
|
|
||||||
badgeTap: function (ev) {
|
badgeTap: function (ev) {
|
||||||
ev.stopPropagation();
|
ev.stopPropagation();
|
||||||
this.async(function () {
|
this.fire('hass-more-info', { entityId: this.state.entity_id });
|
||||||
this.hass.moreInfoActions.selectEntity(this.state.entityId);
|
|
||||||
}, 1);
|
|
||||||
},
|
},
|
||||||
|
|
||||||
computeClasses: function (state) {
|
computeClasses: function (state) {
|
||||||
switch (state.domain) {
|
switch (window.hassUtil.computeDomain(state)) {
|
||||||
case 'binary_sensor':
|
case 'binary_sensor':
|
||||||
case 'updater':
|
case 'updater':
|
||||||
return 'blue';
|
return 'blue';
|
||||||
|
@ -73,7 +71,7 @@ Polymer({
|
||||||
},
|
},
|
||||||
|
|
||||||
computeValue: function (state) {
|
computeValue: function (state) {
|
||||||
switch (state.domain) {
|
switch (window.hassUtil.computeDomain(state)) {
|
||||||
case 'binary_sensor':
|
case 'binary_sensor':
|
||||||
case 'device_tracker':
|
case 'device_tracker':
|
||||||
case 'updater':
|
case 'updater':
|
||||||
|
@ -90,7 +88,7 @@ Polymer({
|
||||||
if (state.state === 'unavailable') {
|
if (state.state === 'unavailable') {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
switch (state.domain) {
|
switch (window.hassUtil.computeDomain(state)) {
|
||||||
case 'alarm_control_panel':
|
case 'alarm_control_panel':
|
||||||
if (state.state === 'pending') {
|
if (state.state === 'pending') {
|
||||||
return 'mdi:clock-fast';
|
return 'mdi:clock-fast';
|
||||||
|
@ -123,7 +121,7 @@ Polymer({
|
||||||
if (state.state === 'unavailable') {
|
if (state.state === 'unavailable') {
|
||||||
return 'unavai';
|
return 'unavai';
|
||||||
}
|
}
|
||||||
switch (state.domain) {
|
switch (window.hassUtil.computeDomain(state)) {
|
||||||
case 'device_tracker':
|
case 'device_tracker':
|
||||||
return state.state === 'not_home' ? 'Away' : state.state;
|
return state.state === 'not_home' ? 'Away' : state.state;
|
||||||
case 'alarm_control_panel':
|
case 'alarm_control_panel':
|
||||||
|
@ -142,7 +140,7 @@ Polymer({
|
||||||
},
|
},
|
||||||
|
|
||||||
computeDescription: function (state) {
|
computeDescription: function (state) {
|
||||||
return state.entityDisplay;
|
return window.hassUtil.computeStateName(state);
|
||||||
},
|
},
|
||||||
|
|
||||||
stateChanged: function () {
|
stateChanged: function () {
|
||||||
|
|
|
@ -38,7 +38,7 @@
|
||||||
<ha-state-icon
|
<ha-state-icon
|
||||||
id='icon'
|
id='icon'
|
||||||
state-obj='[[stateObj]]'
|
state-obj='[[stateObj]]'
|
||||||
data-domain$='[[stateObj.domain]]'
|
data-domain$='[[computeDomain(stateObj)]]'
|
||||||
data-state$='[[stateObj.state]]'
|
data-state$='[[stateObj.state]]'
|
||||||
></ha-state-icon>
|
></ha-state-icon>
|
||||||
</template>
|
</template>
|
||||||
|
@ -55,6 +55,10 @@ Polymer({
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
|
computeDomain: function (stateObj) {
|
||||||
|
return window.hassUtil.computeDomain(stateObj);
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called when an attribute changes that influences the color of the icon.
|
* Called when an attribute changes that influences the color of the icon.
|
||||||
*/
|
*/
|
||||||
|
@ -71,9 +75,10 @@ Polymer({
|
||||||
|
|
||||||
// for domain light, set color of icon to light color if available and it is
|
// for domain light, set color of icon to light color if available and it is
|
||||||
// not very white (sum rgb colors < 730)
|
// not very white (sum rgb colors < 730)
|
||||||
if (newVal.domain === 'light' && newVal.state === 'on' &&
|
if (window.hassUtil.computeDomain(newVal) === 'light' &&
|
||||||
newVal.attributes.rgb_color &&
|
newVal.state === 'on' &&
|
||||||
newVal.attributes.rgb_color.reduce(function (cur, tot) { return cur + tot; }, 0) < 730) {
|
newVal.attributes.rgb_color &&
|
||||||
|
newVal.attributes.rgb_color.reduce(function (cur, tot) { return cur + tot; }, 0) < 730) {
|
||||||
this.$.icon.style.color = 'rgb(' + newVal.attributes.rgb_color.join(',') + ')';
|
this.$.icon.style.color = 'rgb(' + newVal.attributes.rgb_color.join(',') + ')';
|
||||||
} else {
|
} else {
|
||||||
this.$.icon.style.color = null;
|
this.$.icon.style.color = null;
|
||||||
|
|
|
@ -40,7 +40,7 @@
|
||||||
<state-badge state-obj='[[stateObj]]'></state-badge>
|
<state-badge state-obj='[[stateObj]]'></state-badge>
|
||||||
|
|
||||||
<div class='info'>
|
<div class='info'>
|
||||||
<div class='name' in-dialog$='[[inDialog]]'>[[stateObj.entityDisplay]]</div>
|
<div class='name' in-dialog$='[[inDialog]]'>[[computeStateName(stateObj)]]</div>
|
||||||
|
|
||||||
<template is='dom-if' if='[[inDialog]]'>
|
<template is='dom-if' if='[[inDialog]]'>
|
||||||
<div class='time-ago'>
|
<div class='time-ago'>
|
||||||
|
@ -71,5 +71,9 @@ Polymer({
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
|
computeStateName: function (stateObj) {
|
||||||
|
return window.hassUtil.computeStateName(stateObj);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -88,11 +88,18 @@
|
||||||
weather: 4,
|
weather: 4,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 4 types:
|
||||||
|
// badges: 0 .. 10
|
||||||
|
// before groups < 0
|
||||||
|
// groups: X
|
||||||
|
// rest: 100
|
||||||
|
|
||||||
var PRIORITY = {
|
var PRIORITY = {
|
||||||
|
// before groups < 0
|
||||||
configurator: -20,
|
configurator: -20,
|
||||||
persistent_notification: -15,
|
persistent_notification: -15,
|
||||||
group: -10,
|
|
||||||
a: -1,
|
// badges have priority >= 0
|
||||||
updater: 0,
|
updater: 0,
|
||||||
sun: 1,
|
sun: 1,
|
||||||
device_tracker: 2,
|
device_tracker: 2,
|
||||||
|
@ -102,14 +109,40 @@
|
||||||
};
|
};
|
||||||
|
|
||||||
function getPriority(domain) {
|
function getPriority(domain) {
|
||||||
return (domain in PRIORITY) ? PRIORITY[domain] : 30;
|
return (domain in PRIORITY) ? PRIORITY[domain] : 100;
|
||||||
}
|
}
|
||||||
|
|
||||||
function entitySortBy(entity) {
|
function sortPriority(domainA, domainB) {
|
||||||
return entity.domain === 'group' ? entity.attributes.order :
|
return domainA.priority - domainB.priority;
|
||||||
entity.entityDisplay.toLowerCase();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function entitySortBy(entityA, entityB) {
|
||||||
|
var nameA = (entityA.attributes.friendly_name ||
|
||||||
|
entityA.entity_id).toLowerCase();
|
||||||
|
var nameB = (entityB.attributes.friendly_name ||
|
||||||
|
entityB.entity_id).toLowerCase();
|
||||||
|
|
||||||
|
if (nameA < nameB) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (nameB > nameA) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
function iterateDomainSorted(collection, func) {
|
||||||
|
Object.keys(collection)
|
||||||
|
.map(function (key) { return collection[key]; })
|
||||||
|
.sort(sortPriority)
|
||||||
|
.forEach(function (domain) {
|
||||||
|
domain.states.sort(entitySortBy);
|
||||||
|
func(domain);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
var computeDomain = window.hassUtil.computeDomain;
|
||||||
|
|
||||||
Polymer({
|
Polymer({
|
||||||
is: 'ha-cards',
|
is: 'ha-cards',
|
||||||
|
|
||||||
|
@ -138,6 +171,7 @@
|
||||||
|
|
||||||
viewVisible: {
|
viewVisible: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
|
value: false,
|
||||||
},
|
},
|
||||||
|
|
||||||
cards: {
|
cards: {
|
||||||
|
@ -160,13 +194,11 @@
|
||||||
if (this.panelVisible && this.viewVisible) {
|
if (this.panelVisible && this.viewVisible) {
|
||||||
this.cards = this.computeCards(columns, states, showIntroduction);
|
this.cards = this.computeCards(columns, states, showIntroduction);
|
||||||
}
|
}
|
||||||
}.bind(this));
|
}.bind(this), 10);
|
||||||
},
|
},
|
||||||
|
|
||||||
computeCards: function (columns, states, showIntroduction) {
|
computeCards: function (columns, states, showIntroduction) {
|
||||||
var hass = this.hass;
|
var hass = this.hass;
|
||||||
var byDomain = states.groupBy(function (entity) { return entity.domain; });
|
|
||||||
var hasGroup = {};
|
|
||||||
|
|
||||||
var cards = {
|
var cards = {
|
||||||
demo: false,
|
demo: false,
|
||||||
|
@ -174,17 +206,12 @@
|
||||||
columns: [],
|
columns: [],
|
||||||
};
|
};
|
||||||
var entityCount = [];
|
var entityCount = [];
|
||||||
var expandGroup;
|
|
||||||
var i;
|
var i;
|
||||||
for (i = 0; i < columns; i++) {
|
for (i = 0; i < columns; i++) {
|
||||||
cards.columns.push([]);
|
cards.columns.push([]);
|
||||||
entityCount.push(0);
|
entityCount.push(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
function filterGrouped(entities) {
|
|
||||||
return entities.filter(function (entity) { return !(entity.entityId in hasGroup); });
|
|
||||||
}
|
|
||||||
|
|
||||||
// Find column with < 5 entities, else column with lowest count
|
// Find column with < 5 entities, else column with lowest count
|
||||||
function getIndex(size) {
|
function getIndex(size) {
|
||||||
var minIndex = 0;
|
var minIndex = 0;
|
||||||
|
@ -206,7 +233,7 @@
|
||||||
cards.columns[getIndex(5)].push({
|
cards.columns[getIndex(5)].push({
|
||||||
hass: hass,
|
hass: hass,
|
||||||
cardType: 'introduction',
|
cardType: 'introduction',
|
||||||
showHideInstruction: states.size > 0 && !hass.demo,
|
showHideInstruction: states.size > 0 && !window.HASS_DEMO,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -223,9 +250,11 @@
|
||||||
size = 0;
|
size = 0;
|
||||||
|
|
||||||
entities.forEach(function (entity) {
|
entities.forEach(function (entity) {
|
||||||
if (entity.domain in DOMAINS_WITH_CARD) {
|
var domain = computeDomain(entity);
|
||||||
|
|
||||||
|
if (domain in DOMAINS_WITH_CARD) {
|
||||||
owncard.push(entity);
|
owncard.push(entity);
|
||||||
size += DOMAINS_WITH_CARD[entity.domain];
|
size += DOMAINS_WITH_CARD[domain];
|
||||||
} else {
|
} else {
|
||||||
other.push(entity);
|
other.push(entity);
|
||||||
size++;
|
size++;
|
||||||
|
@ -249,43 +278,71 @@
|
||||||
owncard.forEach(function (entity) {
|
owncard.forEach(function (entity) {
|
||||||
cards.columns[curIndex].push({
|
cards.columns[curIndex].push({
|
||||||
hass: hass,
|
hass: hass,
|
||||||
cardType: entity.domain,
|
cardType: computeDomain(entity),
|
||||||
stateObj: entity,
|
stateObj: entity,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
expandGroup = this.hass.util.expandGroup;
|
var sorted = window.HAWS.splitByGroups(states);
|
||||||
|
|
||||||
byDomain.keySeq().sortBy(function (domain) { return getPriority(domain); })
|
var badgesColl = {};
|
||||||
.forEach(function (domain) {
|
var beforeGroupColl = {};
|
||||||
var priority;
|
var afterGroupedColl = {};
|
||||||
|
|
||||||
if (domain === 'a') {
|
Object.keys(sorted.ungrouped).forEach(function (key) {
|
||||||
cards.demo = true;
|
var state = sorted.ungrouped[key];
|
||||||
return;
|
var domain = computeDomain(state);
|
||||||
}
|
|
||||||
|
|
||||||
priority = getPriority(domain);
|
if (domain === 'a') {
|
||||||
|
cards.demo = true;
|
||||||
if (priority >= 0 && priority < 10) {
|
return;
|
||||||
cards.badges.push.apply(
|
|
||||||
cards.badges, filterGrouped(byDomain.get(domain)).sortBy(
|
|
||||||
entitySortBy).toArray());
|
|
||||||
} else if (domain === 'group') {
|
|
||||||
byDomain.get(domain).sortBy(entitySortBy)
|
|
||||||
.forEach(function (groupState) {
|
|
||||||
var entities = expandGroup(groupState, states);
|
|
||||||
entities.forEach(function (entity) { hasGroup[entity.entityId] = true; });
|
|
||||||
addEntitiesCard(groupState.entityId, entities.toArray(), groupState);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
addEntitiesCard(
|
|
||||||
domain, filterGrouped(byDomain.get(domain)).sortBy(entitySortBy).toArray());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
);
|
|
||||||
|
var priority = getPriority(domain);
|
||||||
|
var coll;
|
||||||
|
|
||||||
|
if (priority < 0) {
|
||||||
|
coll = beforeGroupColl;
|
||||||
|
} else if (priority < 10) {
|
||||||
|
coll = badgesColl;
|
||||||
|
} else {
|
||||||
|
coll = afterGroupedColl;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(domain in coll)) {
|
||||||
|
coll[domain] = {
|
||||||
|
domain: domain,
|
||||||
|
priority: priority,
|
||||||
|
states: [],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
coll[domain].states.push(state);
|
||||||
|
});
|
||||||
|
|
||||||
|
iterateDomainSorted(badgesColl, function (domain) {
|
||||||
|
cards.badges.push.apply(cards.badges, domain.states);
|
||||||
|
});
|
||||||
|
|
||||||
|
iterateDomainSorted(beforeGroupColl, function (domain) {
|
||||||
|
addEntitiesCard(domain.domain, domain.states);
|
||||||
|
});
|
||||||
|
|
||||||
|
sorted.groups.forEach(function (groupState) {
|
||||||
|
var entities = window.HAWS.getGroupEntities(states, groupState);
|
||||||
|
addEntitiesCard(
|
||||||
|
groupState.entity_id,
|
||||||
|
Object.keys(entities).map(function (key) {
|
||||||
|
return entities[key];
|
||||||
|
}),
|
||||||
|
groupState
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
iterateDomainSorted(afterGroupedColl, function (domain) {
|
||||||
|
addEntitiesCard(domain.domain, domain.states);
|
||||||
|
});
|
||||||
|
|
||||||
// Remove empty columns
|
// Remove empty columns
|
||||||
cards.columns = cards.columns.filter(function (val) {
|
cards.columns = cards.columns.filter(function (val) {
|
||||||
|
|
|
@ -36,8 +36,9 @@ Polymer({
|
||||||
return !narrow && showMenu ? 'invisible' : '';
|
return !narrow && showMenu ? 'invisible' : '';
|
||||||
},
|
},
|
||||||
|
|
||||||
toggleMenu: function () {
|
toggleMenu: function (ev) {
|
||||||
this.fire('open-menu');
|
ev.stopPropagation();
|
||||||
|
this.fire('hass-open-menu');
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -0,0 +1,134 @@
|
||||||
|
<link rel="import" href="../../bower_components/polymer/polymer.html">
|
||||||
|
<link rel="import" href="../../bower_components/paper-toggle-button/paper-toggle-button.html">
|
||||||
|
|
||||||
|
<dom-module id='ha-push-notifications-toggle'>
|
||||||
|
<template>
|
||||||
|
<paper-toggle-button
|
||||||
|
hidden$='[[!pushSupported]]'
|
||||||
|
disabled='[[loading]]'
|
||||||
|
on-change='handlePushChange'
|
||||||
|
checked='[[pushActive]]'
|
||||||
|
></paper-toggle-button>
|
||||||
|
</template>
|
||||||
|
</dom-module>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
Polymer({
|
||||||
|
is: 'ha-push-notifications-toggle',
|
||||||
|
properties: {
|
||||||
|
hass: { type: Object, value: null },
|
||||||
|
pushSupported: {
|
||||||
|
type: Boolean,
|
||||||
|
readOnly: true,
|
||||||
|
notify: true,
|
||||||
|
value: (
|
||||||
|
'PushManager' in window &&
|
||||||
|
(document.location.protocol === 'https:' ||
|
||||||
|
document.location.hostname === 'localhost' ||
|
||||||
|
document.location.hostname === '127.0.0.1')
|
||||||
|
)
|
||||||
|
},
|
||||||
|
pushActive: {
|
||||||
|
type: Boolean,
|
||||||
|
value: 'Notification' in window && Notification.permission === 'granted'
|
||||||
|
},
|
||||||
|
loading: {
|
||||||
|
type: Boolean,
|
||||||
|
value: true,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
attached: function () {
|
||||||
|
if (!this.pushSupported) return;
|
||||||
|
|
||||||
|
var el = this;
|
||||||
|
|
||||||
|
navigator.serviceWorker.ready.then(
|
||||||
|
function (reg) {
|
||||||
|
reg.pushManager.getSubscription().then(function (subscription) {
|
||||||
|
el.loading = false;
|
||||||
|
el.pushActive = !!subscription;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
function () {
|
||||||
|
// no service worker.
|
||||||
|
el._setPushSupported(false);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
handlePushChange: function (ev) {
|
||||||
|
if (ev.target.checked) {
|
||||||
|
this.subscribePushNotifications();
|
||||||
|
} else {
|
||||||
|
this.unsubscribePushNotifications();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
subscribePushNotifications: function () {
|
||||||
|
var el = this;
|
||||||
|
|
||||||
|
navigator.serviceWorker.ready
|
||||||
|
.then(function (reg) {
|
||||||
|
return reg.pushManager.subscribe({ userVisibleOnly: true });
|
||||||
|
})
|
||||||
|
.then(
|
||||||
|
function (sub) {
|
||||||
|
var browserName;
|
||||||
|
if (navigator.userAgent.toLowerCase().indexOf('firefox') > -1) {
|
||||||
|
browserName = 'firefox';
|
||||||
|
} else {
|
||||||
|
browserName = 'chrome';
|
||||||
|
}
|
||||||
|
|
||||||
|
return el.hass.callApi('POST', 'notify.html5', {
|
||||||
|
subscription: sub,
|
||||||
|
browser: browserName
|
||||||
|
}).then(function () {
|
||||||
|
el.pushActive = true;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
function (err) {
|
||||||
|
var message;
|
||||||
|
if (err.message && err.message.indexOf('gcm_sender_id') !== -1) {
|
||||||
|
message = 'Please setup the notify.html5 platform.';
|
||||||
|
} else {
|
||||||
|
message = 'Notification registration failed.';
|
||||||
|
}
|
||||||
|
|
||||||
|
/* eslint-disable no-console */
|
||||||
|
console.error(err);
|
||||||
|
/* eslint-enable no-console */
|
||||||
|
|
||||||
|
el.fire('hass-notification', { message: message });
|
||||||
|
el.pushActive = false;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
},
|
||||||
|
unsubscribePushNotifications: function () {
|
||||||
|
var el = this;
|
||||||
|
|
||||||
|
navigator.serviceWorker.ready
|
||||||
|
.then(function (reg) {
|
||||||
|
return reg.pushManager.getSubscription();
|
||||||
|
})
|
||||||
|
.then(function (sub) {
|
||||||
|
if (!sub) return Promise.resolve();
|
||||||
|
|
||||||
|
return el.hass
|
||||||
|
.callApi('DELETE', 'notify.html5', { subscription: sub })
|
||||||
|
.then(function () {
|
||||||
|
sub.unsubscribe();
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.then(function () {
|
||||||
|
el.pushActive = false;
|
||||||
|
})
|
||||||
|
.catch(function (err) {
|
||||||
|
/* eslint-disable no-console */
|
||||||
|
console.error('Error in unsub push', err);
|
||||||
|
/* eslint-enable no-console */
|
||||||
|
|
||||||
|
el.fire('hass-notification', {
|
||||||
|
message: 'Failed unsubscribing for push notifications.'
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
|
@ -3,13 +3,12 @@
|
||||||
<link rel='import' href='../../bower_components/iron-icon/iron-icon.html'>
|
<link rel='import' href='../../bower_components/iron-icon/iron-icon.html'>
|
||||||
<link rel='import' href='../../bower_components/paper-menu/paper-menu.html'>
|
<link rel='import' href='../../bower_components/paper-menu/paper-menu.html'>
|
||||||
<link rel='import' href='../../bower_components/paper-item/paper-item.html'>
|
<link rel='import' href='../../bower_components/paper-item/paper-item.html'>
|
||||||
<link rel='import' href='../../bower_components/paper-toggle-button/paper-toggle-button.html'>
|
|
||||||
<link rel='import' href='../../bower_components/paper-item/paper-icon-item.html'>
|
<link rel='import' href='../../bower_components/paper-item/paper-icon-item.html'>
|
||||||
<link rel='import' href='../../bower_components/paper-icon-button/paper-icon-button.html'>
|
<link rel='import' href='../../bower_components/paper-icon-button/paper-icon-button.html'>
|
||||||
|
|
||||||
<link rel="import" href="../../bower_components/app-layout/app-toolbar/app-toolbar.html">
|
<link rel="import" href="../../bower_components/app-layout/app-toolbar/app-toolbar.html">
|
||||||
|
|
||||||
<link rel='import' href='../util/hass-behavior.html'>
|
<link rel='import' href='./ha-push-notifications-toggle.html'>
|
||||||
|
|
||||||
<dom-module id='ha-sidebar'>
|
<dom-module id='ha-sidebar'>
|
||||||
<template>
|
<template>
|
||||||
|
@ -98,13 +97,13 @@
|
||||||
<paper-icon-button icon='mdi:chevron-left' hidden$='[[narrow]]' on-tap='toggleMenu'></paper-icon-button>
|
<paper-icon-button icon='mdi:chevron-left' hidden$='[[narrow]]' on-tap='toggleMenu'></paper-icon-button>
|
||||||
</app-toolbar>
|
</app-toolbar>
|
||||||
|
|
||||||
<paper-menu attr-for-selected='data-panel' selected='[[selected]]' on-iron-select='menuSelect'>
|
<paper-menu attr-for-selected='data-panel' selected='[[hass.currentPanel]]' on-iron-select='menuSelect'>
|
||||||
<paper-icon-item on-tap='menuClicked' data-panel='states'>
|
<paper-icon-item on-tap='menuClicked' data-panel='states'>
|
||||||
<iron-icon item-icon icon='mdi:apps'></iron-icon>
|
<iron-icon item-icon icon='mdi:apps'></iron-icon>
|
||||||
<span class='item-text'>States</span>
|
<span class='item-text'>States</span>
|
||||||
</paper-icon-item>
|
</paper-icon-item>
|
||||||
|
|
||||||
<template is='dom-repeat' items='[[computePanels(panels)]]'>
|
<template is='dom-repeat' items='[[panels]]'>
|
||||||
<paper-icon-item on-tap='menuClicked' data-panel$='[[item.url_path]]'>
|
<paper-icon-item on-tap='menuClicked' data-panel$='[[item.url_path]]'>
|
||||||
<iron-icon item-icon icon='[[item.icon]]'></iron-icon>
|
<iron-icon item-icon icon='[[item.icon]]'></iron-icon>
|
||||||
<span class='item-text'>[[item.title]]</span>
|
<span class='item-text'>[[item.title]]</span>
|
||||||
|
@ -118,15 +117,15 @@
|
||||||
</paper-menu>
|
</paper-menu>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<template is='dom-if' if='[[supportPush]]'>
|
<template is='dom-if' if='[[pushSupported]]'>
|
||||||
<div class='divider'></div>
|
<div class='divider'></div>
|
||||||
|
|
||||||
<paper-item class='horizontal layout justified'>
|
<paper-item class='horizontal layout justified'>
|
||||||
<div class='setting'>Push Notifications</div>
|
<div class='setting'>Push Notifications</div>
|
||||||
<paper-toggle-button
|
<ha-push-notifications-toggle
|
||||||
on-change='handlePushChange'
|
hass='[[hass]]'
|
||||||
checked='{{pushToggleChecked}}'
|
push-supported='{{pushSupported}}'
|
||||||
></paper-toggle-button>
|
></ha-push-notifications-toggle>
|
||||||
</paper-item>
|
</paper-item>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
@ -164,8 +163,6 @@
|
||||||
Polymer({
|
Polymer({
|
||||||
is: 'ha-sidebar',
|
is: 'ha-sidebar',
|
||||||
|
|
||||||
behaviors: [window.hassBehavior],
|
|
||||||
|
|
||||||
properties: {
|
properties: {
|
||||||
hass: {
|
hass: {
|
||||||
type: Object,
|
type: Object,
|
||||||
|
@ -183,36 +180,14 @@ Polymer({
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
},
|
},
|
||||||
|
|
||||||
selected: {
|
|
||||||
type: String,
|
|
||||||
bindNuclear: function (hass) {
|
|
||||||
return hass.navigationGetters.activePanelName;
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
panels: {
|
panels: {
|
||||||
type: Array,
|
type: Array,
|
||||||
bindNuclear: function (hass) {
|
computed: 'computePanels(hass)',
|
||||||
return [
|
|
||||||
hass.navigationGetters.panels,
|
|
||||||
function (res) { return res.toJS(); },
|
|
||||||
];
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
|
|
||||||
supportPush: {
|
pushSupported: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
value: false,
|
value: true,
|
||||||
bindNuclear: function (hass) {
|
|
||||||
return hass.pushNotificationGetters.isSupported;
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
pushToggleChecked: {
|
|
||||||
type: Boolean,
|
|
||||||
bindNuclear: function (hass) {
|
|
||||||
return hass.pushNotificationGetters.isActive;
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -220,7 +195,8 @@ Polymer({
|
||||||
this._boundUpdateStyles = this.updateStyles.bind(this);
|
this._boundUpdateStyles = this.updateStyles.bind(this);
|
||||||
},
|
},
|
||||||
|
|
||||||
computePanels: function (panels) {
|
computePanels: function (hass) {
|
||||||
|
var panels = hass.config.panels;
|
||||||
var sortValue = {
|
var sortValue = {
|
||||||
map: 1,
|
map: 1,
|
||||||
logbook: 2,
|
logbook: 2,
|
||||||
|
@ -280,36 +256,22 @@ Polymer({
|
||||||
},
|
},
|
||||||
|
|
||||||
toggleMenu: function () {
|
toggleMenu: function () {
|
||||||
this.fire('close-menu');
|
this.fire('hass-close-menu');
|
||||||
},
|
},
|
||||||
|
|
||||||
selectPanel: function (newChoice) {
|
selectPanel: function (newChoice) {
|
||||||
if (newChoice === this.selected) {
|
if (newChoice === this.hass.currentPanel) {
|
||||||
return;
|
return;
|
||||||
} else if (newChoice === 'logout') {
|
} else if (newChoice === 'logout') {
|
||||||
this.handleLogOut();
|
this.handleLogOut();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.hass.navigationActions.navigate.apply(null, newChoice.split('/'));
|
this.fire('hass-navigate', { panel: newChoice });
|
||||||
this.debounce('updateStyles', this._boundUpdateStyles, 1);
|
this.debounce('updateStyles', this._boundUpdateStyles, 1);
|
||||||
},
|
},
|
||||||
|
|
||||||
handlePushChange: function (ev) {
|
|
||||||
if (ev.target.checked) {
|
|
||||||
this.hass.pushNotificationActions.subscribePushNotifications()
|
|
||||||
.then(function (success) {
|
|
||||||
this.pushToggleChecked = success;
|
|
||||||
}.bind(this));
|
|
||||||
} else {
|
|
||||||
this.hass.pushNotificationActions.unsubscribePushNotifications()
|
|
||||||
.then(function (success) {
|
|
||||||
this.pushToggleChecked = !success;
|
|
||||||
}.bind(this));
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
handleLogOut: function () {
|
handleLogOut: function () {
|
||||||
this.hass.authActions.logOut();
|
this.fire('hass-logout');
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -0,0 +1,39 @@
|
||||||
|
<link rel="import" href="../../bower_components/polymer/polymer.html">
|
||||||
|
<link rel="import" href="../../bower_components/paper-icon-button/paper-icon-button.html">
|
||||||
|
|
||||||
|
<dom-module id='ha-start-voice-button'>
|
||||||
|
<template>
|
||||||
|
<paper-icon-button
|
||||||
|
icon="mdi:microphone" hidden$='[[!canListen]]'
|
||||||
|
on-tap="handleListenClick"
|
||||||
|
></paper-icon-button>
|
||||||
|
|
||||||
|
</template>
|
||||||
|
</dom-module>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
Polymer({
|
||||||
|
is: 'ha-start-voice-button',
|
||||||
|
|
||||||
|
properties: {
|
||||||
|
hass: {
|
||||||
|
type: Object,
|
||||||
|
value: null,
|
||||||
|
},
|
||||||
|
|
||||||
|
canListen: {
|
||||||
|
type: Boolean,
|
||||||
|
computed: 'computeCanListen(hass)',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
computeCanListen: function (hass) {
|
||||||
|
return ('webkitSpeechRecognition' in window &&
|
||||||
|
window.hassUtil.isComponentLoaded(hass, 'conversation'));
|
||||||
|
},
|
||||||
|
|
||||||
|
handleListenClick: function () {
|
||||||
|
this.fire('hass-start-voice');
|
||||||
|
},
|
||||||
|
});
|
||||||
|
</script>
|
|
@ -112,7 +112,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
startTime = new Date(Math.min.apply(null, deviceStates.map(function (states) {
|
startTime = new Date(Math.min.apply(null, deviceStates.map(function (states) {
|
||||||
return states[0].lastChangedAsDate;
|
return new Date(states[0].last_changed);
|
||||||
})));
|
})));
|
||||||
|
|
||||||
endTime = new Date(startTime);
|
endTime = new Date(startTime);
|
||||||
|
@ -123,8 +123,8 @@
|
||||||
|
|
||||||
dataTables = deviceStates.map(function (states) {
|
dataTables = deviceStates.map(function (states) {
|
||||||
var last = states[states.length - 1];
|
var last = states[states.length - 1];
|
||||||
var domain = last.domain;
|
var domain = window.hassUtil.computeDomain(last);
|
||||||
var name = last.entityDisplay;
|
var name = window.hassUtil.computeStateName(last);
|
||||||
var data = [];
|
var data = [];
|
||||||
var dataTable = new window.google.visualization.DataTable();
|
var dataTable = new window.google.visualization.DataTable();
|
||||||
// array containing [time, value1, value2, etc]
|
// array containing [time, value1, value2, etc]
|
||||||
|
@ -168,7 +168,9 @@
|
||||||
var curTemp = saveParseFloat(state.attributes.current_temperature);
|
var curTemp = saveParseFloat(state.attributes.current_temperature);
|
||||||
var targetHigh = saveParseFloat(state.attributes.target_temp_high);
|
var targetHigh = saveParseFloat(state.attributes.target_temp_high);
|
||||||
var targetLow = saveParseFloat(state.attributes.target_temp_low);
|
var targetLow = saveParseFloat(state.attributes.target_temp_low);
|
||||||
pushData([state.lastUpdatedAsDate, curTemp, targetHigh, targetLow], noInterpolations);
|
pushData(
|
||||||
|
[new Date(state.last_updated), curTemp, targetHigh, targetLow],
|
||||||
|
noInterpolations);
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
dataTable.addColumn('number', name + ' target temperature');
|
dataTable.addColumn('number', name + ' target temperature');
|
||||||
|
@ -178,7 +180,7 @@
|
||||||
processState = function (state) {
|
processState = function (state) {
|
||||||
var curTemp = saveParseFloat(state.attributes.current_temperature);
|
var curTemp = saveParseFloat(state.attributes.current_temperature);
|
||||||
var target = saveParseFloat(state.attributes.temperature);
|
var target = saveParseFloat(state.attributes.temperature);
|
||||||
pushData([state.lastUpdatedAsDate, curTemp, target], noInterpolations);
|
pushData([new Date(state.last_updated), curTemp, target], noInterpolations);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -203,7 +205,9 @@
|
||||||
var curTemp = saveParseFloat(state.attributes.current_temperature);
|
var curTemp = saveParseFloat(state.attributes.current_temperature);
|
||||||
var targetHigh = saveParseFloat(state.attributes.target_temp_high);
|
var targetHigh = saveParseFloat(state.attributes.target_temp_high);
|
||||||
var targetLow = saveParseFloat(state.attributes.target_temp_low);
|
var targetLow = saveParseFloat(state.attributes.target_temp_low);
|
||||||
pushData([state.lastUpdatedAsDate, curTemp, targetHigh, targetLow], noInterpolations);
|
pushData(
|
||||||
|
[new Date(state.last_updated), curTemp, targetHigh, targetLow],
|
||||||
|
noInterpolations);
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
dataTable.addColumn('number', name + ' target temperature');
|
dataTable.addColumn('number', name + ' target temperature');
|
||||||
|
@ -213,7 +217,7 @@
|
||||||
processState = function (state) {
|
processState = function (state) {
|
||||||
var curTemp = saveParseFloat(state.attributes.current_temperature);
|
var curTemp = saveParseFloat(state.attributes.current_temperature);
|
||||||
var target = saveParseFloat(state.attributes.temperature);
|
var target = saveParseFloat(state.attributes.temperature);
|
||||||
pushData([state.lastUpdatedAsDate, curTemp, target], noInterpolations);
|
pushData([new Date(state.last_updated), curTemp, target], noInterpolations);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -226,7 +230,7 @@
|
||||||
|
|
||||||
states.forEach(function (state) {
|
states.forEach(function (state) {
|
||||||
var value = saveParseFloat(state.state);
|
var value = saveParseFloat(state.state);
|
||||||
pushData([state.lastChangedAsDate, value], noInterpolations);
|
pushData([new Date(state.last_changed), value], noInterpolations);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -73,7 +73,7 @@ Polymer({
|
||||||
startTime = new Date(
|
startTime = new Date(
|
||||||
stateHistory.reduce(
|
stateHistory.reduce(
|
||||||
function (minTime, stateInfo) {
|
function (minTime, stateInfo) {
|
||||||
return Math.min(minTime, stateInfo[0].lastChangedAsDate);
|
return Math.min(minTime, new Date(stateInfo[0].last_changed));
|
||||||
}, new Date()));
|
}, new Date()));
|
||||||
|
|
||||||
// end time is Math.min(curTime, start time + 1 day)
|
// end time is Math.min(curTime, start time + 1 day)
|
||||||
|
@ -93,11 +93,11 @@ Polymer({
|
||||||
|
|
||||||
if (stateInfo.length === 0) return;
|
if (stateInfo.length === 0) return;
|
||||||
|
|
||||||
entityDisplay = stateInfo[0].entityDisplay;
|
entityDisplay = window.hassUtil.computeStateName(stateInfo[0]);
|
||||||
|
|
||||||
stateInfo.forEach(function (state) {
|
stateInfo.forEach(function (state) {
|
||||||
if (prevState !== null && state.state !== prevState) {
|
if (prevState !== null && state.state !== prevState) {
|
||||||
newLastChanged = state.lastChangedAsDate;
|
newLastChanged = new Date(state.last_changed);
|
||||||
|
|
||||||
addRow(entityDisplay, prevState, prevLastChanged, newLastChanged);
|
addRow(entityDisplay, prevState, prevLastChanged, newLastChanged);
|
||||||
|
|
||||||
|
@ -105,7 +105,7 @@ Polymer({
|
||||||
prevLastChanged = newLastChanged;
|
prevLastChanged = newLastChanged;
|
||||||
} else if (prevState === null) {
|
} else if (prevState === null) {
|
||||||
prevState = state.state;
|
prevState = state.state;
|
||||||
prevLastChanged = state.lastChangedAsDate;
|
prevLastChanged = new Date(state.last_changed);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -24,30 +24,31 @@
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<google-legacy-loader on-api-load="googleApiLoaded"></google-legacy-loader>
|
<google-legacy-loader on-api-load="_googleApiLoaded"></google-legacy-loader>
|
||||||
|
|
||||||
<div hidden$="[[!isLoading]]" class='loading-container'>
|
<template is='dom-if' if='[[_isLoading]]'>
|
||||||
<paper-spinner active alt='Updating history data'></paper-spinner>
|
<div class='loading-container'>
|
||||||
</div>
|
<paper-spinner active alt='Updating history data'></paper-spinner>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
<div class$='[[computeContentClasses(isLoading)]]'>
|
<template is='dom-if' if='[[!_isLoading]]'>
|
||||||
<template is='dom-if' if='[[computeIsEmpty(stateHistory)]]'>
|
<template is='dom-if' if='[[_computeIsEmpty(historyData)]]'>
|
||||||
No state history found.
|
No state history found.
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<state-history-chart-timeline
|
<state-history-chart-timeline
|
||||||
data='[[groupedStateHistory.timeline]]'
|
data='[[historyData.timeline]]'>
|
||||||
is-single-device='[[isSingleDevice]]'>
|
|
||||||
</state-history-chart-timeline>
|
</state-history-chart-timeline>
|
||||||
|
|
||||||
<template is='dom-repeat' items='[[groupedStateHistory.line]]'>
|
<template is='dom-repeat' items='[[historyData.line]]'>
|
||||||
<state-history-chart-line
|
<state-history-chart-line
|
||||||
unit='[[item.unit]]'
|
unit='[[item.unit]]'
|
||||||
data='[[item.data]]'
|
data='[[item.data]]'
|
||||||
is-single-device='[[isSingleDevice]]'>
|
is-single-device='[[_computeIsSingleLineChart(historyData)]]'>
|
||||||
</state-history-chart-line>
|
</state-history-chart-line>
|
||||||
</template>
|
</template>
|
||||||
</div>
|
</template>
|
||||||
</template>
|
</template>
|
||||||
</dom-module>
|
</dom-module>
|
||||||
|
|
||||||
|
@ -56,101 +57,48 @@ Polymer({
|
||||||
is: 'state-history-charts',
|
is: 'state-history-charts',
|
||||||
|
|
||||||
properties: {
|
properties: {
|
||||||
stateHistory: {
|
historyData: {
|
||||||
type: Object,
|
type: Object,
|
||||||
|
value: null,
|
||||||
},
|
},
|
||||||
|
|
||||||
isLoadingData: {
|
isLoadingData: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
value: false,
|
value: true,
|
||||||
},
|
},
|
||||||
|
|
||||||
apiLoaded: {
|
_apiLoaded: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
value: false,
|
value: false,
|
||||||
},
|
},
|
||||||
|
|
||||||
isLoading: {
|
_isLoading: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
computed: 'computeIsLoading(isLoadingData, apiLoaded)',
|
computed: '_computeIsLoading(isLoadingData, _apiLoaded)',
|
||||||
},
|
|
||||||
|
|
||||||
groupedStateHistory: {
|
|
||||||
type: Object,
|
|
||||||
computed: 'computeGroupedStateHistory(isLoading, stateHistory)',
|
|
||||||
},
|
|
||||||
|
|
||||||
isSingleDevice: {
|
|
||||||
type: Boolean,
|
|
||||||
computed: 'computeIsSingleDevice(stateHistory)',
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
computeIsSingleDevice: function (stateHistory) {
|
_computeIsSingleLineChart: function (historyData) {
|
||||||
return stateHistory && stateHistory.size === 1;
|
return historyData && historyData.line.length === 1;
|
||||||
},
|
},
|
||||||
|
|
||||||
computeGroupedStateHistory: function (isLoading, stateHistory) {
|
_googleApiLoaded: function () {
|
||||||
var lineChartDevices = {};
|
|
||||||
var timelineDevices = [];
|
|
||||||
var unitStates;
|
|
||||||
|
|
||||||
if (isLoading || !stateHistory) {
|
|
||||||
return { line: [], timeline: [] };
|
|
||||||
}
|
|
||||||
|
|
||||||
stateHistory.forEach(function (stateInfo) {
|
|
||||||
var stateWithUnit;
|
|
||||||
var unit;
|
|
||||||
|
|
||||||
if (!stateInfo || stateInfo.size === 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
stateWithUnit = stateInfo.find(
|
|
||||||
function (state) { return 'unit_of_measurement' in state.attributes; });
|
|
||||||
|
|
||||||
unit = stateWithUnit ?
|
|
||||||
stateWithUnit.attributes.unit_of_measurement : false;
|
|
||||||
|
|
||||||
if (!unit) {
|
|
||||||
timelineDevices.push(stateInfo.toArray());
|
|
||||||
} else if (unit in lineChartDevices) {
|
|
||||||
lineChartDevices[unit].push(stateInfo.toArray());
|
|
||||||
} else {
|
|
||||||
lineChartDevices[unit] = [stateInfo.toArray()];
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
timelineDevices = timelineDevices.length > 0 && timelineDevices;
|
|
||||||
|
|
||||||
unitStates = Object.keys(lineChartDevices).map(
|
|
||||||
function (unit) {
|
|
||||||
return { unit: unit, data: lineChartDevices[unit] };
|
|
||||||
});
|
|
||||||
|
|
||||||
return { line: unitStates, timeline: timelineDevices };
|
|
||||||
},
|
|
||||||
|
|
||||||
googleApiLoaded: function () {
|
|
||||||
window.google.load('visualization', '1', {
|
window.google.load('visualization', '1', {
|
||||||
packages: ['timeline', 'corechart'],
|
packages: ['timeline', 'corechart'],
|
||||||
callback: function () {
|
callback: function () {
|
||||||
this.apiLoaded = true;
|
this._apiLoaded = true;
|
||||||
}.bind(this),
|
}.bind(this),
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
computeContentClasses: function (isLoading) {
|
_computeIsLoading: function (_isLoadingData, _apiLoaded) {
|
||||||
return isLoading ? 'loading' : '';
|
return _isLoadingData || !_apiLoaded;
|
||||||
},
|
},
|
||||||
|
|
||||||
computeIsLoading: function (isLoadingData, apiLoaded) {
|
_computeIsEmpty: function (historyData) {
|
||||||
return isLoadingData || !apiLoaded;
|
return (historyData &&
|
||||||
},
|
historyData.timeline.length === 0 &&
|
||||||
|
historyData.line.length === 0);
|
||||||
computeIsEmpty: function (stateHistory) {
|
|
||||||
return stateHistory && stateHistory.size === 0;
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -0,0 +1,162 @@
|
||||||
|
<link rel="import" href="../../bower_components/polymer/polymer.html">
|
||||||
|
|
||||||
|
<script>
|
||||||
|
(function () {
|
||||||
|
var RECENT_THRESHOLD = 60000; // 1 minute
|
||||||
|
var DATE_CACHE = {};
|
||||||
|
var RECENT_CACHE = {};
|
||||||
|
|
||||||
|
function computeHistory(stateHistory) {
|
||||||
|
var lineChartDevices = {};
|
||||||
|
var timelineDevices = [];
|
||||||
|
var unitStates;
|
||||||
|
|
||||||
|
if (!stateHistory) {
|
||||||
|
return { line: [], timeline: [] };
|
||||||
|
}
|
||||||
|
|
||||||
|
stateHistory.forEach(function (stateInfo) {
|
||||||
|
var stateWithUnit;
|
||||||
|
var unit;
|
||||||
|
|
||||||
|
if (stateInfo.size === 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
stateWithUnit = stateInfo.find(
|
||||||
|
function (state) { return 'unit_of_measurement' in state.attributes; });
|
||||||
|
|
||||||
|
unit = stateWithUnit ?
|
||||||
|
stateWithUnit.attributes.unit_of_measurement : false;
|
||||||
|
|
||||||
|
if (!unit) {
|
||||||
|
timelineDevices.push(stateInfo);
|
||||||
|
} else if (unit in lineChartDevices) {
|
||||||
|
lineChartDevices[unit].push(stateInfo);
|
||||||
|
} else {
|
||||||
|
lineChartDevices[unit] = [stateInfo];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
timelineDevices = timelineDevices.length > 0 && timelineDevices;
|
||||||
|
|
||||||
|
unitStates = Object.keys(lineChartDevices).map(
|
||||||
|
function (unit) {
|
||||||
|
return { unit: unit, data: lineChartDevices[unit] };
|
||||||
|
});
|
||||||
|
|
||||||
|
return { line: unitStates, timeline: timelineDevices };
|
||||||
|
}
|
||||||
|
|
||||||
|
Polymer({
|
||||||
|
is: 'ha-state-history-data',
|
||||||
|
|
||||||
|
properties: {
|
||||||
|
hass: {
|
||||||
|
type: Object,
|
||||||
|
observer: 'hassChanged',
|
||||||
|
},
|
||||||
|
|
||||||
|
filterType: {
|
||||||
|
type: String,
|
||||||
|
},
|
||||||
|
|
||||||
|
filterValue: {
|
||||||
|
type: String,
|
||||||
|
},
|
||||||
|
|
||||||
|
isLoading: {
|
||||||
|
type: Boolean,
|
||||||
|
value: true,
|
||||||
|
readOnly: true,
|
||||||
|
notify: true,
|
||||||
|
},
|
||||||
|
|
||||||
|
data: {
|
||||||
|
type: Object,
|
||||||
|
value: null,
|
||||||
|
readOnly: true,
|
||||||
|
notify: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
observers: [
|
||||||
|
'filterChanged(filterType, filterValue)',
|
||||||
|
],
|
||||||
|
|
||||||
|
hassChanged: function (newHass, oldHass) {
|
||||||
|
if (!oldHass && this.filterType && this.filterValue) {
|
||||||
|
this.filterChanged(this.filterType, this.filterValue);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
filterChanged: function (filterType, filterValue) {
|
||||||
|
if (!this.hass) return;
|
||||||
|
|
||||||
|
var data;
|
||||||
|
|
||||||
|
if (filterType === 'date') {
|
||||||
|
data = this.getDate(filterValue);
|
||||||
|
} else if (filterType === 'recent-entity') {
|
||||||
|
data = this.getRecent(filterValue);
|
||||||
|
} else {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this._setIsLoading(true);
|
||||||
|
|
||||||
|
data.then(function (stateHistory) {
|
||||||
|
this._setData(stateHistory);
|
||||||
|
this._setIsLoading(false);
|
||||||
|
}.bind(this));
|
||||||
|
},
|
||||||
|
|
||||||
|
getRecent: function (entityId) {
|
||||||
|
var cache = RECENT_CACHE[entityId];
|
||||||
|
|
||||||
|
if (cache && Date.now() - cache.created < RECENT_THRESHOLD) {
|
||||||
|
return cache.data;
|
||||||
|
}
|
||||||
|
|
||||||
|
var url = 'history/period';
|
||||||
|
|
||||||
|
if (entityId) {
|
||||||
|
url += '?filter_entity_id=' + entityId;
|
||||||
|
}
|
||||||
|
|
||||||
|
var prom = this.hass.callApi('GET', url).then(
|
||||||
|
function (stateHistory) {
|
||||||
|
return computeHistory(stateHistory);
|
||||||
|
},
|
||||||
|
function () {
|
||||||
|
RECENT_CACHE[entityId] = false;
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
RECENT_CACHE[entityId] = {
|
||||||
|
created: Date.now(),
|
||||||
|
data: prom,
|
||||||
|
};
|
||||||
|
|
||||||
|
return prom;
|
||||||
|
},
|
||||||
|
|
||||||
|
getDate: function (date) {
|
||||||
|
if (!DATE_CACHE[date]) {
|
||||||
|
DATE_CACHE[date] = this.hass.callApi('GET', 'history/period/' + date).then(
|
||||||
|
function (stateHistory) {
|
||||||
|
return computeHistory(stateHistory);
|
||||||
|
},
|
||||||
|
function () {
|
||||||
|
DATE_CACHE[date] = false;
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return DATE_CACHE[date];
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}());
|
||||||
|
</script>
|
|
@ -5,8 +5,6 @@
|
||||||
<link rel="import" href="../../bower_components/iron-icon/iron-icon.html">
|
<link rel="import" href="../../bower_components/iron-icon/iron-icon.html">
|
||||||
<link rel="import" href="../../bower_components/paper-spinner/paper-spinner.html">
|
<link rel="import" href="../../bower_components/paper-spinner/paper-spinner.html">
|
||||||
|
|
||||||
<link rel='import' href='../util/hass-behavior.html'>
|
|
||||||
|
|
||||||
<dom-module id="ha-voice-command-dialog">
|
<dom-module id="ha-voice-command-dialog">
|
||||||
<template>
|
<template>
|
||||||
<style>
|
<style>
|
||||||
|
@ -29,6 +27,10 @@
|
||||||
margin-right: 24px;
|
margin-right: 24px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.error {
|
||||||
|
color: red;
|
||||||
|
}
|
||||||
|
|
||||||
.interimTranscript {
|
.interimTranscript {
|
||||||
color: darkgrey;
|
color: darkgrey;
|
||||||
}
|
}
|
||||||
|
@ -54,11 +56,14 @@
|
||||||
<iron-icon icon="mdi:text-to-speech" hidden$="[[isTransmitting]]"></iron-icon>
|
<iron-icon icon="mdi:text-to-speech" hidden$="[[isTransmitting]]"></iron-icon>
|
||||||
<paper-spinner active$="[[isTransmitting]]" hidden$="[[!isTransmitting]]"></paper-spinner>
|
<paper-spinner active$="[[isTransmitting]]" hidden$="[[!isTransmitting]]"></paper-spinner>
|
||||||
</div>
|
</div>
|
||||||
<div class='text'>
|
<div class='text' hidden$='[[hasError]]'>
|
||||||
<span>{{finalTranscript}}</span>
|
<span>{{results.final}}</span>
|
||||||
<span class='interimTranscript'>[[interimTranscript]]</span>
|
<span class='interimTranscript'>[[results.interim]]</span>
|
||||||
…
|
…
|
||||||
</div>
|
</div>
|
||||||
|
<div class='text red' hidden$='[[!hasError]]'>
|
||||||
|
An error occurred. Unable to fulfill request.
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</paper-dialog>
|
</paper-dialog>
|
||||||
</template>
|
</template>
|
||||||
|
@ -69,8 +74,6 @@
|
||||||
Polymer({
|
Polymer({
|
||||||
is: 'ha-voice-command-dialog',
|
is: 'ha-voice-command-dialog',
|
||||||
|
|
||||||
behaviors: [window.hassBehavior],
|
|
||||||
|
|
||||||
properties: {
|
properties: {
|
||||||
hass: {
|
hass: {
|
||||||
type: Object,
|
type: Object,
|
||||||
|
@ -82,32 +85,23 @@ Polymer({
|
||||||
observer: 'dialogOpenChanged',
|
observer: 'dialogOpenChanged',
|
||||||
},
|
},
|
||||||
|
|
||||||
finalTranscript: {
|
results: {
|
||||||
type: String,
|
type: Object,
|
||||||
bindNuclear: function (hass) {
|
|
||||||
return hass.voiceGetters.finalTranscript;
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
interimTranscript: {
|
|
||||||
type: String,
|
|
||||||
bindNuclear: function (hass) {
|
|
||||||
return hass.voiceGetters.extraInterimTranscript;
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
|
|
||||||
isTransmitting: {
|
isTransmitting: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
bindNuclear: function (hass) {
|
value: false,
|
||||||
return hass.voiceGetters.isTransmitting;
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
|
|
||||||
isListening: {
|
isListening: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
bindNuclear: function (hass) {
|
value: false,
|
||||||
return hass.voiceGetters.isListening;
|
},
|
||||||
},
|
|
||||||
|
hasError: {
|
||||||
|
type: Boolean,
|
||||||
|
value: false,
|
||||||
},
|
},
|
||||||
|
|
||||||
showListenInterface: {
|
showListenInterface: {
|
||||||
|
@ -117,13 +111,72 @@ Polymer({
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
|
initRecognition: function () {
|
||||||
|
/* eslint-disable new-cap */
|
||||||
|
this.recognition = new webkitSpeechRecognition();
|
||||||
|
/* eslint-enable new-cap */
|
||||||
|
|
||||||
|
this.recognition.onstart = function () {
|
||||||
|
this.isListening = true;
|
||||||
|
this.isTransmitting = false;
|
||||||
|
this.hasError = false;
|
||||||
|
this.results = {
|
||||||
|
final: '',
|
||||||
|
interim: '',
|
||||||
|
};
|
||||||
|
}.bind(this);
|
||||||
|
this.recognition.onerror = function () {
|
||||||
|
this.recognition.abort();
|
||||||
|
this.hasError = true;
|
||||||
|
}.bind(this);
|
||||||
|
this.recognition.onend = function () {
|
||||||
|
this.isListening = false;
|
||||||
|
this.isTransmitting = true;
|
||||||
|
var text = this.results.final || this.results.interim;
|
||||||
|
|
||||||
|
var listeningDone = function () {
|
||||||
|
this.isTransmitting = false;
|
||||||
|
}.bind(this);
|
||||||
|
|
||||||
|
this.hass.callService('conversation', 'process', { text: text })
|
||||||
|
.then(listeningDone, listeningDone);
|
||||||
|
}.bind(this);
|
||||||
|
|
||||||
|
this.recognition.onresult = function (event) {
|
||||||
|
var oldResults = this.results;
|
||||||
|
var finalTranscript = '';
|
||||||
|
var interimTranscript = '';
|
||||||
|
|
||||||
|
for (var ind = event.resultIndex; ind < event.results.length; ind++) {
|
||||||
|
if (event.results[ind].isFinal) {
|
||||||
|
finalTranscript += event.results[ind][0].transcript;
|
||||||
|
} else {
|
||||||
|
interimTranscript += event.results[ind][0].transcript;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.results = {
|
||||||
|
interim: interimTranscript,
|
||||||
|
final: oldResults.final + finalTranscript,
|
||||||
|
};
|
||||||
|
}.bind(this);
|
||||||
|
},
|
||||||
|
|
||||||
|
startListening: function () {
|
||||||
|
if (!this.recognition) {
|
||||||
|
this.initRecognition();
|
||||||
|
}
|
||||||
|
|
||||||
|
this.recognition.start();
|
||||||
|
},
|
||||||
|
|
||||||
computeShowListenInterface: function (isListening, isTransmitting) {
|
computeShowListenInterface: function (isListening, isTransmitting) {
|
||||||
return isListening || isTransmitting;
|
return isListening || isTransmitting;
|
||||||
},
|
},
|
||||||
|
|
||||||
dialogOpenChanged: function (newVal) {
|
dialogOpenChanged: function (newVal) {
|
||||||
if (!newVal && this.isListening) {
|
if (!newVal && this.isListening) {
|
||||||
this.hass.voiceActions.stop();
|
this.recognition.abort();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -135,4 +188,4 @@ Polymer({
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -8,8 +8,7 @@
|
||||||
<link rel="import" href="../state-summary/state-card-content.html">
|
<link rel="import" href="../state-summary/state-card-content.html">
|
||||||
<link rel="import" href="../components/state-history-charts.html">
|
<link rel="import" href="../components/state-history-charts.html">
|
||||||
<link rel="import" href="../more-infos/more-info-content.html">
|
<link rel="import" href="../more-infos/more-info-content.html">
|
||||||
|
<link rel="import" href="../data/ha-state-history-data.html">
|
||||||
<link rel='import' href='../util/hass-behavior.html'>
|
|
||||||
|
|
||||||
<dom-module id="more-info-dialog">
|
<dom-module id="more-info-dialog">
|
||||||
<template>
|
<template>
|
||||||
|
@ -51,15 +50,24 @@
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<!-- entry-animation='slide-up-animation' exit-animation='slide-down-animation' -->
|
<!-- entry-animation='slide-up-animation' exit-animation='slide-down-animation' -->
|
||||||
<paper-dialog id="dialog" with-backdrop opened='{{dialogOpen}}' data-domain$='[[stateObj.domain]]'>
|
<paper-dialog id="dialog" with-backdrop opened='{{dialogOpen}}' data-domain$='[[computeDomain(stateObj)]]'>
|
||||||
<h2>
|
<h2>
|
||||||
<state-card-content
|
<state-card-content
|
||||||
state-obj="[[stateObj]]"
|
state-obj="[[stateObj]]"
|
||||||
hass='[[hass]]' in-dialog></state-card-content>
|
hass='[[hass]]' in-dialog></state-card-content>
|
||||||
</h2>
|
</h2>
|
||||||
<template is='dom-if' if="[[showHistoryComponent]]">
|
<template is='dom-if' if="[[showHistoryComponent]]">
|
||||||
<state-history-charts state-history="[[stateHistory]]"
|
<div>
|
||||||
is-loading-data="[[isLoadingHistoryData]]"></state-history-charts>
|
<ha-state-history-data
|
||||||
|
hass='[[hass]]'
|
||||||
|
filter-type='[[_filterType]]'
|
||||||
|
filter-value='[[stateObj.entity_id]]'
|
||||||
|
data='{{stateHistory}}'
|
||||||
|
is-loading='{{stateHistoryLoading}}'
|
||||||
|
></ha-state-history-data>
|
||||||
|
<state-history-charts history-data="[[stateHistory]]"
|
||||||
|
is-loading-data="[[isLoadingHistoryData]]"></state-history-charts>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<paper-dialog-scrollable id='scrollable'>
|
<paper-dialog-scrollable id='scrollable'>
|
||||||
<more-info-content
|
<more-info-content
|
||||||
|
@ -73,8 +81,6 @@
|
||||||
Polymer({
|
Polymer({
|
||||||
is: 'more-info-dialog',
|
is: 'more-info-dialog',
|
||||||
|
|
||||||
behaviors: [window.hassBehavior],
|
|
||||||
|
|
||||||
properties: {
|
properties: {
|
||||||
hass: {
|
hass: {
|
||||||
type: Object,
|
type: Object,
|
||||||
|
@ -82,48 +88,26 @@ Polymer({
|
||||||
|
|
||||||
stateObj: {
|
stateObj: {
|
||||||
type: Object,
|
type: Object,
|
||||||
bindNuclear: function (hass) {
|
computed: 'computeStateObj(hass)',
|
||||||
return hass.moreInfoGetters.currentEntity;
|
|
||||||
},
|
|
||||||
observer: 'stateObjChanged',
|
observer: 'stateObjChanged',
|
||||||
},
|
},
|
||||||
|
|
||||||
stateHistory: {
|
stateHistory: {
|
||||||
type: Object,
|
type: Object,
|
||||||
bindNuclear: function (hass) {
|
},
|
||||||
return [
|
|
||||||
hass.moreInfoGetters.currentEntityHistory,
|
stateHistoryLoading: {
|
||||||
function (history) { return history ? [history] : false; },
|
type: Boolean,
|
||||||
];
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
|
|
||||||
isLoadingHistoryData: {
|
isLoadingHistoryData: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
computed: 'computeIsLoadingHistoryData(delayedDialogOpen, isLoadingEntityHistoryData)',
|
computed: 'computeIsLoadingHistoryData(delayedDialogOpen, stateHistoryLoading)',
|
||||||
},
|
|
||||||
|
|
||||||
isLoadingEntityHistoryData: {
|
|
||||||
type: Boolean,
|
|
||||||
bindNuclear: function (hass) {
|
|
||||||
return hass.entityHistoryGetters.isLoadingEntityHistory;
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
|
|
||||||
hasHistoryComponent: {
|
hasHistoryComponent: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
bindNuclear: function (hass) {
|
computed: 'computeHasHistoryComponent(hass)',
|
||||||
return hass.configGetters.isComponentLoaded('history');
|
|
||||||
},
|
|
||||||
observer: 'fetchHistoryData',
|
|
||||||
},
|
|
||||||
|
|
||||||
shouldFetchHistory: {
|
|
||||||
type: Boolean,
|
|
||||||
bindNuclear: function (hass) {
|
|
||||||
return hass.moreInfoGetters.isCurrentEntityHistoryStale;
|
|
||||||
},
|
|
||||||
observer: 'fetchHistoryData',
|
|
||||||
},
|
},
|
||||||
|
|
||||||
showHistoryComponent: {
|
showHistoryComponent: {
|
||||||
|
@ -142,32 +126,43 @@ Polymer({
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
value: false,
|
value: false,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
_filterType: {
|
||||||
|
type: String,
|
||||||
|
value: 'recent-entity',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
ready: function () {
|
ready: function () {
|
||||||
this.$.scrollable.dialogElement = this.$.dialog;
|
this.$.scrollable.dialogElement = this.$.dialog;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
computeDomain: function (stateObj) {
|
||||||
|
return stateObj ? window.hassUtil.computeDomain(stateObj) : '';
|
||||||
|
},
|
||||||
|
|
||||||
|
computeStateObj: function (hass) {
|
||||||
|
return hass.states[hass.moreInfoEntityId] || null;
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* We depend on a delayed dialogOpen value to tell the chart component
|
* We depend on a delayed dialogOpen value to tell the chart component
|
||||||
* that the data is there. Otherwise the chart component will render
|
* that the data is there. Otherwise the chart component will render
|
||||||
* before the dialog is attached to the screen and is unable to determine
|
* before the dialog is attached to the screen and is unable to determine
|
||||||
* graph size resulting in scroll bars.
|
* graph size resulting in scroll bars.
|
||||||
*/
|
*/
|
||||||
computeIsLoadingHistoryData: function (delayedDialogOpen, isLoadingEntityHistoryData) {
|
computeIsLoadingHistoryData: function (delayedDialogOpen, stateHistoryLoading) {
|
||||||
return !delayedDialogOpen || isLoadingEntityHistoryData;
|
return !delayedDialogOpen || stateHistoryLoading;
|
||||||
|
},
|
||||||
|
|
||||||
|
computeHasHistoryComponent: function (hass) {
|
||||||
|
return window.hassUtil.isComponentLoaded(hass, 'history');
|
||||||
},
|
},
|
||||||
|
|
||||||
computeShowHistoryComponent: function (hasHistoryComponent, stateObj) {
|
computeShowHistoryComponent: function (hasHistoryComponent, stateObj) {
|
||||||
return this.hasHistoryComponent && stateObj &&
|
return this.hasHistoryComponent && stateObj &&
|
||||||
window.hassUtil.DOMAINS_WITH_NO_HISTORY.indexOf(stateObj.domain) === -1;
|
window.hassUtil.DOMAINS_WITH_NO_HISTORY.indexOf(
|
||||||
},
|
window.hassUtil.computeDomain(stateObj)) === -1;
|
||||||
|
|
||||||
fetchHistoryData: function () {
|
|
||||||
if (this.stateObj && this.hasHistoryComponent &&
|
|
||||||
this.shouldFetchHistory) {
|
|
||||||
this.hass.entityHistoryActions.fetchRecent(this.stateObj.entityId);
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
|
|
||||||
stateObjChanged: function (newVal) {
|
stateObjChanged: function (newVal) {
|
||||||
|
@ -177,8 +172,6 @@ Polymer({
|
||||||
}
|
}
|
||||||
|
|
||||||
this.async(function () {
|
this.async(function () {
|
||||||
// Firing action while other action is happening confuses nuclear
|
|
||||||
this.fetchHistoryData();
|
|
||||||
// allow dialog to render content before showing it so it is
|
// allow dialog to render content before showing it so it is
|
||||||
// positioned correctly.
|
// positioned correctly.
|
||||||
this.dialogOpen = true;
|
this.dialogOpen = true;
|
||||||
|
@ -189,7 +182,7 @@ Polymer({
|
||||||
if (newVal) {
|
if (newVal) {
|
||||||
this.async(function () { this.delayedDialogOpen = true; }.bind(this), 10);
|
this.async(function () { this.delayedDialogOpen = true; }.bind(this), 10);
|
||||||
} else if (!newVal && this.stateObj) {
|
} else if (!newVal && this.stateObj) {
|
||||||
this.async(function () { this.hass.moreInfoActions.deselectEntity(); }.bind(this), 10);
|
this.fire('hass-more-info', { entityId: null });
|
||||||
this.delayedDialogOpen = false;
|
this.delayedDialogOpen = false;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
|
@ -6,28 +6,47 @@
|
||||||
<link rel='import' href='../bower_components/iron-flex-layout/iron-flex-layout-classes.html'>
|
<link rel='import' href='../bower_components/iron-flex-layout/iron-flex-layout-classes.html'>
|
||||||
|
|
||||||
<link rel='import' href='./util/hass-util.html'>
|
<link rel='import' href='./util/hass-util.html'>
|
||||||
<link rel='import' href='./util/hass-behavior.html'>
|
<link rel='import' href='./util/ha-pref-storage.html'>
|
||||||
|
<link rel='import' href='./util/hass-call-api.html'>
|
||||||
<link rel='import' href='./layouts/login-form.html'>
|
<link rel='import' href='./layouts/login-form.html'>
|
||||||
<link rel='import' href='./layouts/home-assistant-main.html'>
|
<link rel='import' href='./layouts/home-assistant-main.html'>
|
||||||
<link rel='import' href='./resources/ha-style.html'>
|
<link rel='import' href='./resources/ha-style.html'>
|
||||||
<link rel="import" href="./resources/panel-imports.html">
|
<link rel="import" href="./resources/panel-imports.html">
|
||||||
|
<link rel='import' href='./managers/notification-manager.html'>
|
||||||
|
|
||||||
<dom-module id='home-assistant'>
|
<dom-module id='home-assistant'>
|
||||||
<template>
|
<template>
|
||||||
<template is='dom-if' if='[[loaded]]'>
|
<ha-pref-storage hass='[[hass]]' id='storage'></ha-pref-storage>
|
||||||
<home-assistant-main hass='[[hass]]'></home-assistant-main>
|
<notification-manager id='notifications' hass='[[hass]]'></notification-manager>
|
||||||
|
<template is='dom-if' if='[[showMain]]' restamp>
|
||||||
|
<home-assistant-main
|
||||||
|
on-hass-more-info='handleMoreInfo'
|
||||||
|
on-hass-navigate='handleNavigate'
|
||||||
|
on-hass-dock-sidebar='handleDockSidebar'
|
||||||
|
on-hass-notification='handleNotification'
|
||||||
|
on-hass-logout='handleLogout'
|
||||||
|
hass='[[hass]]'
|
||||||
|
></home-assistant-main>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<template is='dom-if' if='[[!loaded]]'>
|
<template is='dom-if' if='[[!showMain]]'>
|
||||||
<login-form
|
<login-form
|
||||||
hass='[[hass]]'
|
hass='[[hass]]'
|
||||||
force-show-loading='[[computeForceShowLoading(dataLoaded, iconsLoaded)]]'>
|
connection-promise='{{connectionPromise}}'
|
||||||
|
show-loading='[[computeShowLoading(connectionPromise, iconsLoaded)]]'>
|
||||||
</login-form>
|
</login-form>
|
||||||
</template>
|
</template>
|
||||||
</template>
|
</template>
|
||||||
</dom-module>
|
</dom-module>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
window.removeInitMsg = function () {
|
||||||
|
var initMsg = document.getElementById('ha-init-skeleton');
|
||||||
|
if (initMsg) {
|
||||||
|
initMsg.parentElement.removeChild(initMsg);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
Polymer({
|
Polymer({
|
||||||
is: 'home-assistant',
|
is: 'home-assistant',
|
||||||
|
|
||||||
|
@ -35,36 +54,40 @@ Polymer({
|
||||||
icons: null,
|
icons: null,
|
||||||
},
|
},
|
||||||
|
|
||||||
behaviors: [window.hassBehavior],
|
|
||||||
|
|
||||||
properties: {
|
properties: {
|
||||||
|
connectionPromise: {
|
||||||
|
type: Object,
|
||||||
|
value: window.hassConnection || null,
|
||||||
|
observer: 'handleConnectionPromise',
|
||||||
|
},
|
||||||
|
connection: {
|
||||||
|
type: Object,
|
||||||
|
value: null,
|
||||||
|
observer: 'connectionChanged',
|
||||||
|
},
|
||||||
hass: {
|
hass: {
|
||||||
type: Object,
|
type: Object,
|
||||||
value: window.hass,
|
value: null,
|
||||||
},
|
},
|
||||||
icons: {
|
icons: {
|
||||||
type: String,
|
type: String,
|
||||||
},
|
},
|
||||||
dataLoaded: {
|
|
||||||
type: Boolean,
|
|
||||||
bindNuclear: function (hass) { return hass.syncGetters.isDataLoaded; },
|
|
||||||
},
|
|
||||||
iconsLoaded: {
|
iconsLoaded: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
value: false,
|
value: false,
|
||||||
},
|
},
|
||||||
loaded: {
|
showMain: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
computed: 'computeLoaded(dataLoaded, iconsLoaded)',
|
computed: 'computeShowMain(hass, iconsLoaded)',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
computeLoaded: function (dataLoaded, iconsLoaded) {
|
computeShowMain: function (hass, iconsLoaded) {
|
||||||
return dataLoaded && iconsLoaded;
|
return hass && hass.states && hass.config && iconsLoaded;
|
||||||
},
|
},
|
||||||
|
|
||||||
computeForceShowLoading: function (dataLoaded, iconsLoaded) {
|
computeShowLoading: function (connectionPromise) {
|
||||||
return dataLoaded && !iconsLoaded;
|
return connectionPromise != null;
|
||||||
},
|
},
|
||||||
|
|
||||||
loadIcons: function () {
|
loadIcons: function () {
|
||||||
|
@ -81,8 +104,125 @@ Polymer({
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
connectionChanged: function (conn) {
|
||||||
|
if (!conn) {
|
||||||
|
this.hass = null;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var notifications = this.$.notifications;
|
||||||
|
this.hass = Object.assign({
|
||||||
|
connection: conn,
|
||||||
|
connected: true,
|
||||||
|
states: null,
|
||||||
|
config: null,
|
||||||
|
dockedSidebar: false,
|
||||||
|
currentPanel: 'states',
|
||||||
|
currentView: null,
|
||||||
|
moreInfoEntityId: null,
|
||||||
|
callService: function (domain, service, serviceData) {
|
||||||
|
return conn.callService(domain, service, serviceData || {})
|
||||||
|
.then(function () {
|
||||||
|
var message;
|
||||||
|
if (service === 'turn_on' && serviceData.entity_id) {
|
||||||
|
message = 'Turned on ' + serviceData.entity_id + '.';
|
||||||
|
} else if (service === 'turn_off' && serviceData.entity_id) {
|
||||||
|
message = 'Turned off ' + serviceData.entity_id + '.';
|
||||||
|
} else {
|
||||||
|
message = 'Service ' + domain + '/' + service + ' called.';
|
||||||
|
}
|
||||||
|
notifications.showNotification(message);
|
||||||
|
},
|
||||||
|
function () {
|
||||||
|
notifications.showNotification(
|
||||||
|
'Failed to call service ' + domain + '/' + service);
|
||||||
|
return Promise.reject();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
callApi: function (method, path, parameters) {
|
||||||
|
var host = window.location.protocol + '//' + window.location.host;
|
||||||
|
return window.hassCallApi(host, {}, method, path, parameters);
|
||||||
|
},
|
||||||
|
}, this.$.storage.getStoredState());
|
||||||
|
|
||||||
|
conn.addEventListener('ready', function () {
|
||||||
|
this.hass = Object.assign({}, this.hass, { connected: true });
|
||||||
|
}.bind(this));
|
||||||
|
|
||||||
|
conn.addEventListener('disconnected', function () {
|
||||||
|
this.hass = Object.assign({}, this.hass, { connected: false });
|
||||||
|
}.bind(this));
|
||||||
|
|
||||||
|
window.HAWS.subscribeEntities(conn, function (states) {
|
||||||
|
this.hass = Object.assign({}, this.hass, { states: states });
|
||||||
|
}.bind(this));
|
||||||
|
|
||||||
|
window.HAWS.subscribeConfig(conn, function (config) {
|
||||||
|
this.hass = Object.assign({}, this.hass, { config: config });
|
||||||
|
}.bind(this));
|
||||||
|
},
|
||||||
|
|
||||||
|
handleConnectionPromise: function (prom) {
|
||||||
|
if (!prom) return;
|
||||||
|
|
||||||
|
var el = this;
|
||||||
|
|
||||||
|
prom.then(function (conn) {
|
||||||
|
el.connection = conn;
|
||||||
|
}, function () {
|
||||||
|
el.connectionPromise = null;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
handleMoreInfo: function (ev) {
|
||||||
|
ev.stopPropagation();
|
||||||
|
|
||||||
|
this.hass = Object.assign(
|
||||||
|
{}, this.hass,
|
||||||
|
{ moreInfoEntityId: ev.detail.entityId });
|
||||||
|
},
|
||||||
|
|
||||||
|
handleNavigate: function (ev) {
|
||||||
|
ev.stopPropagation();
|
||||||
|
|
||||||
|
var hass = Object.assign({}, this.hass);
|
||||||
|
|
||||||
|
if ('panel' in ev.detail) {
|
||||||
|
hass.currentPanel = ev.detail.panel;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ('view' in ev.detail) {
|
||||||
|
hass.currentView = ev.detail.view;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.hass = hass;
|
||||||
|
},
|
||||||
|
|
||||||
|
handleDockSidebar: function (ev) {
|
||||||
|
ev.stopPropagation();
|
||||||
|
this.hass = Object.assign(
|
||||||
|
{}, this.hass,
|
||||||
|
{ dockedSidebar: ev.detail.dock });
|
||||||
|
this.$.storage.storeState();
|
||||||
|
},
|
||||||
|
|
||||||
|
handleNotification: function (ev) {
|
||||||
|
this.$.notifications.showNotification(ev.detail.message);
|
||||||
|
},
|
||||||
|
|
||||||
|
handleLogout: function () {
|
||||||
|
this.connection.close();
|
||||||
|
delete localStorage.authToken;
|
||||||
|
this.connection = null;
|
||||||
|
this.connectionPromise = null;
|
||||||
|
this.hass = null;
|
||||||
|
},
|
||||||
|
|
||||||
ready: function () {
|
ready: function () {
|
||||||
this.loadIcons();
|
this.loadIcons();
|
||||||
|
|
||||||
|
if (this.connectionPromise !== null) {
|
||||||
|
this.handleConnectionPromise(this.connectionPromise);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -5,22 +5,25 @@
|
||||||
|
|
||||||
<link rel='import' href='../layouts/partial-cards.html'>
|
<link rel='import' href='../layouts/partial-cards.html'>
|
||||||
<link rel='import' href='../layouts/partial-panel-resolver.html'>
|
<link rel='import' href='../layouts/partial-panel-resolver.html'>
|
||||||
<link rel='import' href='../managers/notification-manager.html'>
|
|
||||||
<link rel="import" href="../dialogs/more-info-dialog.html">
|
<link rel="import" href="../dialogs/more-info-dialog.html">
|
||||||
<link rel="import" href="../dialogs/ha-voice-command-dialog.html">
|
<link rel="import" href="../dialogs/ha-voice-command-dialog.html">
|
||||||
|
<link rel='import' href='../util/ha-url-sync.html'>
|
||||||
|
|
||||||
<link rel='import' href='../components/ha-sidebar.html'>
|
<link rel='import' href='../components/ha-sidebar.html'>
|
||||||
|
|
||||||
<dom-module id='home-assistant-main'>
|
<dom-module id='home-assistant-main'>
|
||||||
<template>
|
<template>
|
||||||
<notification-manager hass='[[hass]]'></notification-manager>
|
|
||||||
<more-info-dialog hass='[[hass]]'></more-info-dialog>
|
<more-info-dialog hass='[[hass]]'></more-info-dialog>
|
||||||
<ha-voice-command-dialog hass='[[hass]]'></ha-voice-command-dialog>
|
<ha-url-sync hass='[[hass]]'></ha-url-sync>
|
||||||
|
<ha-voice-command-dialog
|
||||||
|
hass='[[hass]]'
|
||||||
|
id='voiceDialog'
|
||||||
|
></ha-voice-command-dialog>
|
||||||
<iron-media-query query="(max-width: 870px)" query-matches="{{narrow}}">
|
<iron-media-query query="(max-width: 870px)" query-matches="{{narrow}}">
|
||||||
</iron-media-query>
|
</iron-media-query>
|
||||||
|
|
||||||
<paper-drawer-panel id='drawer'
|
<paper-drawer-panel id='drawer'
|
||||||
force-narrow='[[computeForceNarrow(narrow, showSidebar)]]'
|
force-narrow='[[computeForceNarrow(narrow, dockedSidebar)]]'
|
||||||
responsive-width='0' disable-swipe='[[isSelectedMap]]'
|
responsive-width='0' disable-swipe='[[isSelectedMap]]'
|
||||||
disable-edge-swipe='[[isSelectedMap]]'>
|
disable-edge-swipe='[[isSelectedMap]]'>
|
||||||
<ha-sidebar drawer narrow='[[narrow]]' hass='[[hass]]'></ha-sidebar>
|
<ha-sidebar drawer narrow='[[narrow]]' hass='[[hass]]'></ha-sidebar>
|
||||||
|
@ -29,21 +32,21 @@
|
||||||
main
|
main
|
||||||
attr-for-selected='id'
|
attr-for-selected='id'
|
||||||
fallback-selection='panel-resolver'
|
fallback-selection='panel-resolver'
|
||||||
selected='[[activePanel]]'
|
selected='[[currentPanel]]'
|
||||||
selected-attribute='panel-visible'
|
selected-attribute='panel-visible'
|
||||||
>
|
>
|
||||||
<partial-cards
|
<partial-cards
|
||||||
id='states'
|
id='states'
|
||||||
narrow='[[narrow]]'
|
narrow='[[narrow]]'
|
||||||
hass='[[hass]]'
|
hass='[[hass]]'
|
||||||
show-menu='[[showSidebar]]'
|
show-menu='[[dockedSidebar]]'
|
||||||
></partial-cards>
|
></partial-cards>
|
||||||
|
|
||||||
<partial-panel-resolver
|
<partial-panel-resolver
|
||||||
id='panel-resolver'
|
id='panel-resolver'
|
||||||
narrow='[[narrow]]'
|
narrow='[[narrow]]'
|
||||||
hass='[[hass]]'
|
hass='[[hass]]'
|
||||||
show-menu='[[showSidebar]]'
|
show-menu='[[dockedSidebar]]'
|
||||||
></partial-panel-resolver>
|
></partial-panel-resolver>
|
||||||
|
|
||||||
</iron-pages>
|
</iron-pages>
|
||||||
|
@ -56,56 +59,55 @@
|
||||||
Polymer({
|
Polymer({
|
||||||
is: 'home-assistant-main',
|
is: 'home-assistant-main',
|
||||||
|
|
||||||
behaviors: [window.hassBehavior],
|
|
||||||
|
|
||||||
properties: {
|
properties: {
|
||||||
hass: {
|
hass: {
|
||||||
type: Object,
|
type: Object,
|
||||||
|
value: null,
|
||||||
},
|
},
|
||||||
|
|
||||||
narrow: {
|
narrow: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
value: true,
|
|
||||||
},
|
},
|
||||||
|
|
||||||
activePanel: {
|
currentPanel: {
|
||||||
type: String,
|
type: String,
|
||||||
bindNuclear: function (hass) {
|
computed: 'computeCurrentPanel(hass)',
|
||||||
return hass.navigationGetters.activePanelName;
|
observer: 'currentPanelChanged',
|
||||||
},
|
|
||||||
observer: 'activePanelChanged',
|
|
||||||
},
|
},
|
||||||
|
|
||||||
showSidebar: {
|
dockedSidebar: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
value: false,
|
computed: 'computeDockedSidebar(hass)',
|
||||||
bindNuclear: function (hass) {
|
|
||||||
return hass.navigationGetters.showSidebar;
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
listeners: {
|
listeners: {
|
||||||
'open-menu': 'openMenu',
|
'hass-open-menu': 'handleOpenMenu',
|
||||||
'close-menu': 'closeMenu',
|
'hass-close-menu': 'handleCloseMenu',
|
||||||
|
'hass-start-voice': 'handleStartVoice',
|
||||||
},
|
},
|
||||||
|
|
||||||
openMenu: function () {
|
handleStartVoice: function (ev) {
|
||||||
|
ev.stopPropagation();
|
||||||
|
this.$.voiceDialog.startListening();
|
||||||
|
},
|
||||||
|
|
||||||
|
handleOpenMenu: function () {
|
||||||
if (this.narrow) {
|
if (this.narrow) {
|
||||||
this.$.drawer.openDrawer();
|
this.$.drawer.openDrawer();
|
||||||
} else {
|
} else {
|
||||||
this.hass.navigationActions.showSidebar(true);
|
this.fire('hass-dock-sidebar', { dock: true });
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
closeMenu: function () {
|
handleCloseMenu: function () {
|
||||||
this.$.drawer.closeDrawer();
|
this.$.drawer.closeDrawer();
|
||||||
if (this.showSidebar) {
|
if (this.dockedSidebar) {
|
||||||
this.hass.navigationActions.showSidebar(false);
|
this.fire('hass-dock-sidebar', { dock: false });
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
activePanelChanged: function () {
|
currentPanelChanged: function () {
|
||||||
if (this.narrow) {
|
if (this.narrow) {
|
||||||
this.$.drawer.closeDrawer();
|
this.$.drawer.closeDrawer();
|
||||||
}
|
}
|
||||||
|
@ -113,15 +115,18 @@ Polymer({
|
||||||
|
|
||||||
attached: function () {
|
attached: function () {
|
||||||
window.removeInitMsg();
|
window.removeInitMsg();
|
||||||
this.hass.startUrlSync();
|
|
||||||
},
|
},
|
||||||
|
|
||||||
computeForceNarrow: function (narrow, showSidebar) {
|
computeForceNarrow: function (narrow, dockedSidebar) {
|
||||||
return narrow || !showSidebar;
|
return narrow || !dockedSidebar;
|
||||||
},
|
},
|
||||||
|
|
||||||
detached: function () {
|
computeCurrentPanel: function (hass) {
|
||||||
this.hass.stopUrlSync();
|
return hass.currentPanel;
|
||||||
|
},
|
||||||
|
|
||||||
|
computeDockedSidebar: function (hass) {
|
||||||
|
return hass.dockedSidebar;
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -9,8 +9,6 @@
|
||||||
<link rel="import" href="../../bower_components/iron-input/iron-input.html">
|
<link rel="import" href="../../bower_components/iron-input/iron-input.html">
|
||||||
<link rel="import" href="../../bower_components/paper-spinner/paper-spinner.html">
|
<link rel="import" href="../../bower_components/paper-spinner/paper-spinner.html">
|
||||||
|
|
||||||
<link rel='import' href='../util/hass-behavior.html'>
|
|
||||||
|
|
||||||
<dom-module id="login-form">
|
<dom-module id="login-form">
|
||||||
<template>
|
<template>
|
||||||
<style is="custom-style" include="iron-flex iron-positioning"></style>
|
<style is="custom-style" include="iron-flex iron-positioning"></style>
|
||||||
|
@ -51,10 +49,18 @@
|
||||||
<a href="#" id="hideKeyboardOnFocus"></a>
|
<a href="#" id="hideKeyboardOnFocus"></a>
|
||||||
<div class='interact'>
|
<div class='interact'>
|
||||||
<div id='loginform' hidden$="[[showLoading]]">
|
<div id='loginform' hidden$="[[showLoading]]">
|
||||||
<paper-input-container id="passwordDecorator" invalid="[[isInvalid]]">
|
<paper-input-container
|
||||||
|
id="passwordDecorator"
|
||||||
|
invalid="[[errorMessage]]"
|
||||||
|
>
|
||||||
<label>Password</label>
|
<label>Password</label>
|
||||||
<input is="iron-input" type="password" id="passwordInput" />
|
<input
|
||||||
<paper-input-error invalid="[[isInvalid]]">[[errorMessage]]</paper-input-error>
|
is="iron-input"
|
||||||
|
type="password"
|
||||||
|
id="passwordInput"
|
||||||
|
bind-value="{{password}}"
|
||||||
|
/>
|
||||||
|
<paper-input-error invalid="[[errorMessage]]">[[errorMessage]]</paper-input-error>
|
||||||
</paper-input-container>
|
</paper-input-container>
|
||||||
<div class="layout horizontal center">
|
<div class="layout horizontal center">
|
||||||
<paper-checkbox for id='rememberLogin'>Remember</paper-checkbox>
|
<paper-checkbox for id='rememberLogin'>Remember</paper-checkbox>
|
||||||
|
@ -74,31 +80,25 @@
|
||||||
Polymer({
|
Polymer({
|
||||||
is: 'login-form',
|
is: 'login-form',
|
||||||
|
|
||||||
behaviors: [window.hassBehavior],
|
|
||||||
|
|
||||||
properties: {
|
properties: {
|
||||||
hass: {
|
hass: {
|
||||||
type: Object,
|
type: Object,
|
||||||
},
|
},
|
||||||
|
|
||||||
errorMessage: {
|
connectionPromise: {
|
||||||
type: String,
|
type: Object,
|
||||||
bindNuclear: function (hass) { return hass.authGetters.attemptErrorMessage; },
|
notify: true,
|
||||||
|
observer: 'handleConnectionPromiseChanged',
|
||||||
},
|
},
|
||||||
|
|
||||||
isInvalid: {
|
errorMessage: {
|
||||||
type: Boolean,
|
type: String,
|
||||||
bindNuclear: function (hass) { return hass.authGetters.isInvalidAttempt; },
|
value: '',
|
||||||
},
|
},
|
||||||
|
|
||||||
isValidating: {
|
isValidating: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
observer: 'isValidatingChanged',
|
observer: 'isValidatingChanged',
|
||||||
bindNuclear: function (hass) { return hass.authGetters.isValidating; },
|
|
||||||
},
|
|
||||||
|
|
||||||
loadingResources: {
|
|
||||||
type: Boolean,
|
|
||||||
value: false,
|
value: false,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -111,6 +111,11 @@ Polymer({
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
computed: 'computeShowSpinner(forceShowLoading, isValidating)',
|
computed: 'computeShowSpinner(forceShowLoading, isValidating)',
|
||||||
},
|
},
|
||||||
|
|
||||||
|
password: {
|
||||||
|
type: String,
|
||||||
|
value: '',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
listeners: {
|
listeners: {
|
||||||
|
@ -118,10 +123,6 @@ Polymer({
|
||||||
'loginButton.tap': 'validatePassword',
|
'loginButton.tap': 'validatePassword',
|
||||||
},
|
},
|
||||||
|
|
||||||
observers: [
|
|
||||||
'validatingChanged(isValidating, isInvalid)',
|
|
||||||
],
|
|
||||||
|
|
||||||
attached: function () {
|
attached: function () {
|
||||||
window.removeInitMsg();
|
window.removeInitMsg();
|
||||||
},
|
},
|
||||||
|
@ -130,15 +131,11 @@ Polymer({
|
||||||
return forceShowLoading || isValidating;
|
return forceShowLoading || isValidating;
|
||||||
},
|
},
|
||||||
|
|
||||||
validatingChanged: function (isValidating, isInvalid) {
|
|
||||||
if (!isValidating && !isInvalid) {
|
|
||||||
this.$.passwordInput.value = '';
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
isValidatingChanged: function (newVal) {
|
isValidatingChanged: function (newVal) {
|
||||||
if (!newVal) {
|
if (!newVal) {
|
||||||
this.async(function () { this.$.passwordInput.focus(); }.bind(this), 10);
|
this.async(function () {
|
||||||
|
this.$.passwordInput.focus();
|
||||||
|
}.bind(this), 10);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -148,16 +145,46 @@ Polymer({
|
||||||
this.validatePassword();
|
this.validatePassword();
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
// clear error after we start typing again
|
// clear error after we start typing again
|
||||||
} else if (this.isInvalid) {
|
} else if (this.errorMessage) {
|
||||||
this.isInvalid = false;
|
this.errorMessage = '';
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
validatePassword: function () {
|
validatePassword: function () {
|
||||||
|
var auth = this.password;
|
||||||
this.$.hideKeyboardOnFocus.focus();
|
this.$.hideKeyboardOnFocus.focus();
|
||||||
|
this.connectionPromise = window.createHassConnection(auth);
|
||||||
|
|
||||||
window.validateAuth(this.$.passwordInput.value,
|
if (this.$.rememberLogin.checked) {
|
||||||
this.$.rememberLogin.checked);
|
this.connectionPromise.then(function () {
|
||||||
|
localStorage.authToken = auth;
|
||||||
|
});
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
handleConnectionPromiseChanged: function (newVal) {
|
||||||
|
if (!newVal) return;
|
||||||
|
|
||||||
|
var el = this;
|
||||||
|
this.isValidating = true;
|
||||||
|
|
||||||
|
this.connectionPromise.then(
|
||||||
|
function () {
|
||||||
|
el.isValidating = false;
|
||||||
|
el.password = '';
|
||||||
|
},
|
||||||
|
function (errCode) {
|
||||||
|
el.isValidating = false;
|
||||||
|
|
||||||
|
if (errCode === window.HAWS.ERR_CANNOT_CONNECT) {
|
||||||
|
el.errorMessage = 'Unable to connect';
|
||||||
|
} else if (errCode === window.HAWS.ERR_INVALID_AUTH) {
|
||||||
|
el.errorMessage = 'Invalid password';
|
||||||
|
} else {
|
||||||
|
el.errorMessage = 'Unknown error: ' + errCode;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -4,7 +4,6 @@
|
||||||
<link rel="import" href="../../bower_components/iron-icon/iron-icon.html">
|
<link rel="import" href="../../bower_components/iron-icon/iron-icon.html">
|
||||||
<link rel="import" href="../../bower_components/iron-pages/iron-pages.html">
|
<link rel="import" href="../../bower_components/iron-pages/iron-pages.html">
|
||||||
|
|
||||||
<link rel="import" href="../../bower_components/paper-icon-button/paper-icon-button.html">
|
|
||||||
<link rel="import" href="../../bower_components/paper-tabs/paper-tabs.html">
|
<link rel="import" href="../../bower_components/paper-tabs/paper-tabs.html">
|
||||||
<link rel="import" href="../../bower_components/paper-tabs/paper-tab.html">
|
<link rel="import" href="../../bower_components/paper-tabs/paper-tab.html">
|
||||||
|
|
||||||
|
@ -14,8 +13,8 @@
|
||||||
<link rel="import" href="../../bower_components/app-layout/app-toolbar/app-toolbar.html">
|
<link rel="import" href="../../bower_components/app-layout/app-toolbar/app-toolbar.html">
|
||||||
|
|
||||||
<link rel="import" href="../components/ha-menu-button.html">
|
<link rel="import" href="../components/ha-menu-button.html">
|
||||||
|
<link rel="import" href="../components/ha-start-voice-button.html">
|
||||||
<link rel="import" href="../components/ha-cards.html">
|
<link rel="import" href="../components/ha-cards.html">
|
||||||
<link rel="import" href="../util/hass-behavior.html">
|
|
||||||
|
|
||||||
<dom-module id="partial-cards">
|
<dom-module id="partial-cards">
|
||||||
<template>
|
<template>
|
||||||
|
@ -42,9 +41,7 @@
|
||||||
<app-toolbar>
|
<app-toolbar>
|
||||||
<ha-menu-button narrow='[[narrow]]' show-menu='[[showMenu]]'></ha-menu-button>
|
<ha-menu-button narrow='[[narrow]]' show-menu='[[showMenu]]'></ha-menu-button>
|
||||||
<div main-title>[[computeTitle(views, locationName)]]</div>
|
<div main-title>[[computeTitle(views, locationName)]]</div>
|
||||||
<paper-icon-button
|
<ha-start-voice-button hass='[[hass]]'></ha-start-voice-button>
|
||||||
icon="mdi:microphone" hidden$='[[!canListen]]'
|
|
||||||
on-tap="handleListenClick"></paper-icon-button>
|
|
||||||
</app-toolbar>
|
</app-toolbar>
|
||||||
|
|
||||||
<div sticky hidden$='[[!views.length]]'>
|
<div sticky hidden$='[[!views.length]]'>
|
||||||
|
@ -60,14 +57,14 @@
|
||||||
>[[locationName]]</paper-tab>
|
>[[locationName]]</paper-tab>
|
||||||
<template is='dom-repeat' items='[[views]]'>
|
<template is='dom-repeat' items='[[views]]'>
|
||||||
<paper-tab
|
<paper-tab
|
||||||
data-entity$='[[item.entityId]]'
|
data-entity$='[[item.entity_id]]'
|
||||||
on-tap='scrollToTop'
|
on-tap='scrollToTop'
|
||||||
>
|
>
|
||||||
<template is='dom-if' if='[[item.attributes.icon]]'>
|
<template is='dom-if' if='[[item.attributes.icon]]'>
|
||||||
<iron-icon icon='[[item.attributes.icon]]'></iron-icon>
|
<iron-icon icon='[[item.attributes.icon]]'></iron-icon>
|
||||||
</template>
|
</template>
|
||||||
<template is='dom-if' if='[[!item.attributes.icon]]'>
|
<template is='dom-if' if='[[!item.attributes.icon]]'>
|
||||||
[[item.entityDisplay]]
|
[[computeStateName(item)]]
|
||||||
</template>
|
</template>
|
||||||
</paper-tab>
|
</paper-tab>
|
||||||
</template>
|
</template>
|
||||||
|
@ -82,8 +79,8 @@
|
||||||
>
|
>
|
||||||
<ha-cards
|
<ha-cards
|
||||||
data-view=''
|
data-view=''
|
||||||
show-introduction='[[computeShowIntroduction(currentView, introductionLoaded, states)]]'
|
show-introduction='[[computeShowIntroduction(currentView, introductionLoaded, viewStates)]]'
|
||||||
states='[[states]]'
|
states='[[viewStates]]'
|
||||||
columns='[[_columns]]'
|
columns='[[_columns]]'
|
||||||
hass='[[hass]]'
|
hass='[[hass]]'
|
||||||
panel-visible='[[panelVisible]]'
|
panel-visible='[[panelVisible]]'
|
||||||
|
@ -91,9 +88,9 @@
|
||||||
|
|
||||||
<template is='dom-repeat' items='[[views]]'>
|
<template is='dom-repeat' items='[[views]]'>
|
||||||
<ha-cards
|
<ha-cards
|
||||||
data-view$='[[item.entityId]]'
|
data-view$='[[item.entity_id]]'
|
||||||
show-introduction='[[computeShowIntroduction(currentView, introductionLoaded, states)]]'
|
show-introduction='[[computeShowIntroduction(currentView, introductionLoaded, viewStates)]]'
|
||||||
states='[[states]]'
|
states='[[viewStates]]'
|
||||||
columns='[[_columns]]'
|
columns='[[_columns]]'
|
||||||
hass='[[hass]]'
|
hass='[[hass]]'
|
||||||
panel-visible='[[panelVisible]]'
|
panel-visible='[[panelVisible]]'
|
||||||
|
@ -108,13 +105,15 @@
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
Polymer({
|
Polymer({
|
||||||
is: 'partial-cards',
|
DEFAULT_VIEW_ENTITY_ID: 'group.default_view',
|
||||||
|
ALWAYS_SHOW_DOMAIN: ['persistent_notification', 'configurator'],
|
||||||
|
|
||||||
behaviors: [window.hassBehavior],
|
is: 'partial-cards',
|
||||||
|
|
||||||
properties: {
|
properties: {
|
||||||
hass: {
|
hass: {
|
||||||
type: Object,
|
type: Object,
|
||||||
|
value: null,
|
||||||
},
|
},
|
||||||
|
|
||||||
narrow: {
|
narrow: {
|
||||||
|
@ -124,7 +123,6 @@ Polymer({
|
||||||
|
|
||||||
showMenu: {
|
showMenu: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
value: false,
|
|
||||||
observer: 'handleWindowChange',
|
observer: 'handleWindowChange',
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -138,62 +136,30 @@ Polymer({
|
||||||
value: 1,
|
value: 1,
|
||||||
},
|
},
|
||||||
|
|
||||||
canListen: {
|
|
||||||
type: Boolean,
|
|
||||||
bindNuclear: function (hass) {
|
|
||||||
return [
|
|
||||||
hass.voiceGetters.isVoiceSupported,
|
|
||||||
hass.configGetters.isComponentLoaded('conversation'),
|
|
||||||
function (isVoiceSupported, componentLoaded) {
|
|
||||||
return isVoiceSupported && componentLoaded;
|
|
||||||
},
|
|
||||||
];
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
introductionLoaded: {
|
introductionLoaded: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
bindNuclear: function (hass) {
|
computed: 'computeIntroductionLoaded(hass)',
|
||||||
return hass.configGetters.isComponentLoaded('introduction');
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
|
|
||||||
locationName: {
|
locationName: {
|
||||||
type: String,
|
type: String,
|
||||||
bindNuclear: function (hass) {
|
value: '',
|
||||||
return hass.configGetters.locationName;
|
computed: 'computeLocationName(hass)',
|
||||||
},
|
|
||||||
},
|
},
|
||||||
|
|
||||||
currentView: {
|
currentView: {
|
||||||
type: String,
|
type: String,
|
||||||
bindNuclear: function (hass) {
|
computed: 'computeCurrentView(hass)',
|
||||||
return [
|
|
||||||
hass.viewGetters.currentView,
|
|
||||||
function (view) { return view || ''; },
|
|
||||||
];
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
|
|
||||||
views: {
|
views: {
|
||||||
type: Array,
|
type: Array,
|
||||||
bindNuclear: function (hass) {
|
computed: 'computeViews(hass)',
|
||||||
return [
|
|
||||||
hass.viewGetters.views,
|
|
||||||
function (views) {
|
|
||||||
return views.valueSeq()
|
|
||||||
.sortBy(function (view) { return view.attributes.order; })
|
|
||||||
.toArray();
|
|
||||||
},
|
|
||||||
];
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
|
|
||||||
states: {
|
viewStates: {
|
||||||
type: Object,
|
type: Object,
|
||||||
bindNuclear: function (hass) {
|
computed: 'computeViewStates(currentView, hass)',
|
||||||
return hass.viewGetters.currentViewEntities;
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -259,17 +225,11 @@ Polymer({
|
||||||
}).call(this);
|
}).call(this);
|
||||||
},
|
},
|
||||||
|
|
||||||
handleListenClick: function () {
|
|
||||||
this.hass.voiceActions.listen();
|
|
||||||
},
|
|
||||||
|
|
||||||
handleViewSelected: function (ev) {
|
handleViewSelected: function (ev) {
|
||||||
var view = ev.detail.item.getAttribute('data-entity') || null;
|
var view = ev.detail.item.getAttribute('data-entity') || null;
|
||||||
var current = this.currentView || null;
|
var current = this.currentView || null;
|
||||||
if (view !== current) {
|
if (view !== current) {
|
||||||
this.async(function () {
|
this.fire('hass-navigate', { view: view });
|
||||||
this.hass.viewActions.selectView(view);
|
|
||||||
}.bind(this), 0);
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -280,5 +240,72 @@ Polymer({
|
||||||
computeShowIntroduction: function (currentView, introductionLoaded, states) {
|
computeShowIntroduction: function (currentView, introductionLoaded, states) {
|
||||||
return currentView === '' && (introductionLoaded || states.size === 0);
|
return currentView === '' && (introductionLoaded || states.size === 0);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
computeStateName: function (stateObj) {
|
||||||
|
return window.hassUtil.computeStateName(stateObj);
|
||||||
|
},
|
||||||
|
|
||||||
|
computeLocationName: function (hass) {
|
||||||
|
return window.hassUtil.computeLocationName(hass);
|
||||||
|
},
|
||||||
|
|
||||||
|
computeIntroductionLoaded: function (hass) {
|
||||||
|
return window.hassUtil.isComponentLoaded(hass, 'introduction');
|
||||||
|
},
|
||||||
|
|
||||||
|
computeViews: function (hass) {
|
||||||
|
return window.HAWS.extractViews(hass.states);
|
||||||
|
},
|
||||||
|
|
||||||
|
/*
|
||||||
|
Compute the states to show for current view.
|
||||||
|
|
||||||
|
Will make sure we always show entities from ALWAYS_SHOW_DOMAINS domains.
|
||||||
|
*/
|
||||||
|
computeViewStates: function (currentView, hass) {
|
||||||
|
var i;
|
||||||
|
var entityId;
|
||||||
|
var state;
|
||||||
|
var states;
|
||||||
|
var entityIds = Object.keys(hass.states);
|
||||||
|
|
||||||
|
// If we base off all entities, only have to filter out hidden
|
||||||
|
if (!currentView && !(this.DEFAULT_VIEW_ENTITY_ID in hass.states)) {
|
||||||
|
states = {};
|
||||||
|
for (i = 0; i < entityIds.length; i++) {
|
||||||
|
entityId = entityIds[i];
|
||||||
|
state = hass.states[entityId];
|
||||||
|
|
||||||
|
// We can filter out hidden and domain at the same time.
|
||||||
|
if (!state.attributes.hidden) {
|
||||||
|
states[entityId] = state;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return states;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (currentView) {
|
||||||
|
states = window.HAWS.getViewEntities(hass.states, hass.states[currentView]);
|
||||||
|
} else {
|
||||||
|
states = window.HAWS.getViewEntities(
|
||||||
|
hass.states, hass.states[this.DEFAULT_VIEW_ENTITY_ID]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make sure certain domains are always shown.
|
||||||
|
for (i = 0; i < entityIds.length; i++) {
|
||||||
|
entityId = entityIds[i];
|
||||||
|
state = hass.states[entityId];
|
||||||
|
|
||||||
|
if (this.ALWAYS_SHOW_DOMAIN.indexOf(window.hassUtil.computeDomain(state)) !== -1) {
|
||||||
|
states[entityId] = state;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return states;
|
||||||
|
},
|
||||||
|
|
||||||
|
computeCurrentView: function (hass) {
|
||||||
|
return hass.currentView || '';
|
||||||
|
},
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -5,7 +5,6 @@
|
||||||
<link rel="import" href="../../bower_components/app-layout/app-toolbar/app-toolbar.html">
|
<link rel="import" href="../../bower_components/app-layout/app-toolbar/app-toolbar.html">
|
||||||
|
|
||||||
<link rel='import' href='../components/ha-menu-button.html'>
|
<link rel='import' href='../components/ha-menu-button.html'>
|
||||||
<link rel='import' href='../util/hass-behavior.html'>
|
|
||||||
<link rel="import" href="../resources/ha-style.html">
|
<link rel="import" href="../resources/ha-style.html">
|
||||||
|
|
||||||
<dom-module id='partial-panel-resolver'>
|
<dom-module id='partial-panel-resolver'>
|
||||||
|
@ -46,8 +45,6 @@
|
||||||
Polymer({
|
Polymer({
|
||||||
is: 'partial-panel-resolver',
|
is: 'partial-panel-resolver',
|
||||||
|
|
||||||
behaviors: [window.hassBehavior],
|
|
||||||
|
|
||||||
properties: {
|
properties: {
|
||||||
hass: {
|
hass: {
|
||||||
type: Object,
|
type: Object,
|
||||||
|
@ -78,13 +75,15 @@ Polymer({
|
||||||
|
|
||||||
panel: {
|
panel: {
|
||||||
type: Object,
|
type: Object,
|
||||||
bindNuclear: function (hass) {
|
computed: 'computeCurrentPanel(hass)',
|
||||||
return hass.navigationGetters.activePanel;
|
|
||||||
},
|
|
||||||
observer: 'panelChanged',
|
observer: 'panelChanged',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
|
computeCurrentPanel: function (hass) {
|
||||||
|
return hass.config.panels[hass.currentPanel];
|
||||||
|
},
|
||||||
|
|
||||||
panelChanged: function (panel) {
|
panelChanged: function (panel) {
|
||||||
if (!panel) {
|
if (!panel) {
|
||||||
if (this.$.panel.lastChild) {
|
if (this.$.panel.lastChild) {
|
||||||
|
@ -97,15 +96,15 @@ Polymer({
|
||||||
this.errorLoading = false;
|
this.errorLoading = false;
|
||||||
|
|
||||||
this.importHref(
|
this.importHref(
|
||||||
panel.get('url'),
|
panel.url,
|
||||||
|
|
||||||
function success() {
|
function success() {
|
||||||
window.hassUtil.dynamicContentUpdater(
|
window.hassUtil.dynamicContentUpdater(
|
||||||
this.$.panel, 'ha-panel-' + panel.get('component_name'), {
|
this.$.panel, 'ha-panel-' + panel.component_name, {
|
||||||
hass: this.hass,
|
hass: this.hass,
|
||||||
narrow: this.narrow,
|
narrow: this.narrow,
|
||||||
showMenu: this.showMenu,
|
showMenu: this.showMenu,
|
||||||
panel: panel.toJS(),
|
panel: panel,
|
||||||
});
|
});
|
||||||
this.resolved = true;
|
this.resolved = true;
|
||||||
}.bind(this),
|
}.bind(this),
|
||||||
|
|
|
@ -2,8 +2,6 @@
|
||||||
|
|
||||||
<link rel="import" href="../../bower_components/paper-toast/paper-toast.html">
|
<link rel="import" href="../../bower_components/paper-toast/paper-toast.html">
|
||||||
|
|
||||||
<link rel='import' href='../util/hass-behavior.html'>
|
|
||||||
|
|
||||||
<dom-module id="notification-manager">
|
<dom-module id="notification-manager">
|
||||||
<template>
|
<template>
|
||||||
<style>
|
<style>
|
||||||
|
@ -14,8 +12,8 @@
|
||||||
|
|
||||||
<paper-toast
|
<paper-toast
|
||||||
id="toast"
|
id="toast"
|
||||||
text='{{text}}'
|
text='[[_text]]'
|
||||||
no-cancel-on-outside-click='[[neg]]'
|
no-cancel-on-outside-click='[[_cancelOnOutsideClick]]'
|
||||||
></paper-toast>
|
></paper-toast>
|
||||||
<paper-toast
|
<paper-toast
|
||||||
id='connToast'
|
id='connToast'
|
||||||
|
@ -30,8 +28,6 @@
|
||||||
Polymer({
|
Polymer({
|
||||||
is: 'notification-manager',
|
is: 'notification-manager',
|
||||||
|
|
||||||
behaviors: [window.hassBehavior],
|
|
||||||
|
|
||||||
properties: {
|
properties: {
|
||||||
hass: {
|
hass: {
|
||||||
type: Object,
|
type: Object,
|
||||||
|
@ -39,21 +35,17 @@ Polymer({
|
||||||
|
|
||||||
isStreaming: {
|
isStreaming: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
bindNuclear: function (hass) { return hass.streamGetters.isStreamingEvents; },
|
computed: 'computeIsStreaming(hass)',
|
||||||
},
|
},
|
||||||
|
|
||||||
// Otherwise we cannot close a modal when a notification is being shown.
|
_cancelOnOutsideClick: {
|
||||||
neg: {
|
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
value: false,
|
value: false,
|
||||||
},
|
},
|
||||||
|
|
||||||
text: {
|
_text: {
|
||||||
type: String,
|
type: String,
|
||||||
bindNuclear: function (hass) {
|
readOnly: true,
|
||||||
return hass.notificationGetters.lastNotificationMessage;
|
|
||||||
},
|
|
||||||
observer: 'showNotification',
|
|
||||||
},
|
},
|
||||||
|
|
||||||
toastClass: {
|
toastClass: {
|
||||||
|
@ -62,6 +54,10 @@ Polymer({
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
|
computeIsStreaming: function (hass) {
|
||||||
|
return !hass || hass.connected;
|
||||||
|
},
|
||||||
|
|
||||||
created: function () {
|
created: function () {
|
||||||
this.handleWindowChange = this.handleWindowChange.bind(this);
|
this.handleWindowChange = this.handleWindowChange.bind(this);
|
||||||
this._mediaq = window.matchMedia('(max-width: 599px)');
|
this._mediaq = window.matchMedia('(max-width: 599px)');
|
||||||
|
@ -81,10 +77,9 @@ Polymer({
|
||||||
this.$.connToast.classList.toggle('fit-bottom', ev.matches);
|
this.$.connToast.classList.toggle('fit-bottom', ev.matches);
|
||||||
},
|
},
|
||||||
|
|
||||||
showNotification: function (newText) {
|
showNotification: function (message) {
|
||||||
if (newText) {
|
this._set_text(message);
|
||||||
this.$.toast.show();
|
this.$.toast.show();
|
||||||
}
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -129,8 +129,8 @@ Polymer({
|
||||||
|
|
||||||
callService: function (service, data) {
|
callService: function (service, data) {
|
||||||
var serviceData = data || {};
|
var serviceData = data || {};
|
||||||
serviceData.entity_id = this.stateObj.entityId;
|
serviceData.entity_id = this.stateObj.entity_id;
|
||||||
this.hass.serviceActions.callService('alarm_control_panel', service, serviceData)
|
this.hass.callService('alarm_control_panel', service, serviceData)
|
||||||
.then(function () {
|
.then(function () {
|
||||||
this.enteredCode = '';
|
this.enteredCode = '';
|
||||||
}.bind(this));
|
}.bind(this));
|
||||||
|
|
|
@ -42,8 +42,8 @@ Polymer({
|
||||||
},
|
},
|
||||||
|
|
||||||
handleTriggerTapped: function () {
|
handleTriggerTapped: function () {
|
||||||
this.hass.serviceActions.callService('automation', 'trigger', {
|
this.hass.callService('automation', 'trigger', {
|
||||||
entity_id: this.stateObj.entityId,
|
entity_id: this.stateObj.entity_id,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
|
@ -13,7 +13,7 @@
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<img class='camera-image' src="[[computeCameraImageUrl(hass, stateObj, isVisible)]]"
|
<img class='camera-image' src="[[computeCameraImageUrl(hass, stateObj, isVisible)]]"
|
||||||
on-load='imageLoaded' alt='[[stateObj.entityDisplay]]' />
|
on-load='imageLoaded' alt='[[computeStateName(stateObj)]]' />
|
||||||
</template>
|
</template>
|
||||||
</dom-module>
|
</dom-module>
|
||||||
|
|
||||||
|
@ -40,11 +40,15 @@ Polymer({
|
||||||
this.fire('iron-resize');
|
this.fire('iron-resize');
|
||||||
},
|
},
|
||||||
|
|
||||||
|
computeStateName: function (stateObj) {
|
||||||
|
return window.hassUtil.computeStateName(stateObj);
|
||||||
|
},
|
||||||
|
|
||||||
computeCameraImageUrl: function (hass, stateObj, isVisible) {
|
computeCameraImageUrl: function (hass, stateObj, isVisible) {
|
||||||
if (hass.demo) {
|
if (hass.demo) {
|
||||||
return '/demo/webcam.jpg';
|
return '/demo/webcam.jpg';
|
||||||
} else if (stateObj && isVisible) {
|
} else if (stateObj && isVisible) {
|
||||||
return '/api/camera_proxy_stream/' + stateObj.entityId +
|
return '/api/camera_proxy_stream/' + stateObj.entity_id +
|
||||||
'?token=' + stateObj.attributes.access_token;
|
'?token=' + stateObj.attributes.access_token;
|
||||||
}
|
}
|
||||||
// Return an empty image if no stateObj (= dialog not open) or in cleanup mode.
|
// Return an empty image if no stateObj (= dialog not open) or in cleanup mode.
|
||||||
|
|
|
@ -376,9 +376,9 @@ Polymer({
|
||||||
// result in the entity to be turned on. Since the state is not changing,
|
// result in the entity to be turned on. Since the state is not changing,
|
||||||
// the resync is not called automatic.
|
// the resync is not called automatic.
|
||||||
/* eslint-disable no-param-reassign */
|
/* eslint-disable no-param-reassign */
|
||||||
data.entity_id = this.stateObj.entityId;
|
data.entity_id = this.stateObj.entity_id;
|
||||||
/* eslint-enable no-param-reassign */
|
/* eslint-enable no-param-reassign */
|
||||||
this.hass.serviceActions.callService('climate', service, data)
|
this.hass.callService('climate', service, data)
|
||||||
.then(function () {
|
.then(function () {
|
||||||
this.stateObjChanged(this.stateObj);
|
this.stateObjChanged(this.stateObj);
|
||||||
}.bind(this));
|
}.bind(this));
|
||||||
|
|
|
@ -7,8 +7,6 @@
|
||||||
<link rel='import' href='../../bower_components/paper-button/paper-button.html'>
|
<link rel='import' href='../../bower_components/paper-button/paper-button.html'>
|
||||||
<link rel='import' href='../../bower_components/paper-input/paper-input-container.html'>
|
<link rel='import' href='../../bower_components/paper-input/paper-input-container.html'>
|
||||||
|
|
||||||
<link rel='import' href='../util/hass-behavior.html'>
|
|
||||||
|
|
||||||
<dom-module id='more-info-configurator'>
|
<dom-module id='more-info-configurator'>
|
||||||
<template>
|
<template>
|
||||||
<style is="custom-style" include="iron-flex"></style>
|
<style is="custom-style" include="iron-flex"></style>
|
||||||
|
@ -94,8 +92,6 @@
|
||||||
Polymer({
|
Polymer({
|
||||||
is: 'more-info-configurator',
|
is: 'more-info-configurator',
|
||||||
|
|
||||||
behaviors: [window.hassBehavior],
|
|
||||||
|
|
||||||
properties: {
|
properties: {
|
||||||
stateObj: {
|
stateObj: {
|
||||||
type: Object,
|
type: Object,
|
||||||
|
@ -106,13 +102,6 @@ Polymer({
|
||||||
value: 'display',
|
value: 'display',
|
||||||
},
|
},
|
||||||
|
|
||||||
isStreaming: {
|
|
||||||
type: Boolean,
|
|
||||||
bindNuclear: function (hass) {
|
|
||||||
return hass.streamGetters.isStreamingEvents;
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
isConfigurable: {
|
isConfigurable: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
computed: 'computeIsConfigurable(stateObj)',
|
computed: 'computeIsConfigurable(stateObj)',
|
||||||
|
@ -154,13 +143,9 @@ Polymer({
|
||||||
|
|
||||||
this.isConfiguring = true;
|
this.isConfiguring = true;
|
||||||
|
|
||||||
this.hass.serviceActions.callService('configurator', 'configure', data).then(
|
this.hass.callService('configurator', 'configure', data).then(
|
||||||
function () {
|
function () {
|
||||||
this.isConfiguring = false;
|
this.isConfiguring = false;
|
||||||
|
|
||||||
if (!this.isStreaming) {
|
|
||||||
this.hass.syncActions.fetchAll();
|
|
||||||
}
|
|
||||||
}.bind(this),
|
}.bind(this),
|
||||||
function () {
|
function () {
|
||||||
this.isConfiguring = false;
|
this.isConfiguring = false;
|
||||||
|
|
|
@ -84,15 +84,15 @@ Polymer({
|
||||||
},
|
},
|
||||||
|
|
||||||
coverPositionSliderChanged: function (ev) {
|
coverPositionSliderChanged: function (ev) {
|
||||||
this.hass.serviceActions.callService('cover', 'set_cover_position', {
|
this.hass.callService('cover', 'set_cover_position', {
|
||||||
entity_id: this.stateObj.entityId,
|
entity_id: this.stateObj.entity_id,
|
||||||
position: ev.target.value,
|
position: ev.target.value,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
coverTiltPositionSliderChanged: function (ev) {
|
coverTiltPositionSliderChanged: function (ev) {
|
||||||
this.hass.serviceActions.callService('cover', 'set_cover_tilt_position', {
|
this.hass.callService('cover', 'set_cover_tilt_position', {
|
||||||
entity_id: this.stateObj.entityId,
|
entity_id: this.stateObj.entity_id,
|
||||||
tilt_position: ev.target.value,
|
tilt_position: ev.target.value,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
@ -106,18 +106,18 @@ Polymer({
|
||||||
},
|
},
|
||||||
|
|
||||||
onOpenTiltTap: function () {
|
onOpenTiltTap: function () {
|
||||||
this.hass.serviceActions.callService('cover', 'open_cover_tilt',
|
this.hass.callService('cover', 'open_cover_tilt',
|
||||||
{ entity_id: this.stateObj.entityId });
|
{ entity_id: this.stateObj.entity_id });
|
||||||
},
|
},
|
||||||
|
|
||||||
onCloseTiltTap: function () {
|
onCloseTiltTap: function () {
|
||||||
this.hass.serviceActions.callService('cover', 'close_cover_tilt',
|
this.hass.callService('cover', 'close_cover_tilt',
|
||||||
{ entity_id: this.stateObj.entityId });
|
{ entity_id: this.stateObj.entity_id });
|
||||||
},
|
},
|
||||||
|
|
||||||
onStopTiltTap: function () {
|
onStopTiltTap: function () {
|
||||||
this.hass.serviceActions.callService('cover', 'stop_cover_tilt',
|
this.hass.callService('cover', 'stop_cover_tilt',
|
||||||
{ entity_id: this.stateObj.entityId });
|
{ entity_id: this.stateObj.entity_id });
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -114,8 +114,8 @@ Polymer({
|
||||||
speedInput = this.stateObj.attributes.speed_list[speedIndex];
|
speedInput = this.stateObj.attributes.speed_list[speedIndex];
|
||||||
if (speedInput === this.stateObj.attributes.speed) return;
|
if (speedInput === this.stateObj.attributes.speed) return;
|
||||||
|
|
||||||
this.hass.serviceActions.callService('fan', 'turn_on', {
|
this.hass.callService('fan', 'turn_on', {
|
||||||
entity_id: this.stateObj.entityId,
|
entity_id: this.stateObj.entity_id,
|
||||||
speed: speedInput,
|
speed: speedInput,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
@ -126,22 +126,22 @@ Polymer({
|
||||||
|
|
||||||
if (oldVal === newVal) return;
|
if (oldVal === newVal) return;
|
||||||
|
|
||||||
this.hass.serviceActions.callService('fan', 'oscillate', {
|
this.hass.callService('fan', 'oscillate', {
|
||||||
entity_id: this.stateObj.entityId,
|
entity_id: this.stateObj.entity_id,
|
||||||
oscillating: newVal,
|
oscillating: newVal,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
onDirectionLeft: function () {
|
onDirectionLeft: function () {
|
||||||
this.hass.serviceActions.callService('fan', 'set_direction', {
|
this.hass.callService('fan', 'set_direction', {
|
||||||
entity_id: this.stateObj.entityId,
|
entity_id: this.stateObj.entity_id,
|
||||||
direction: 'left'
|
direction: 'left'
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
onDirectionRight: function () {
|
onDirectionRight: function () {
|
||||||
this.hass.serviceActions.callService('fan', 'set_direction', {
|
this.hass.callService('fan', 'set_direction', {
|
||||||
entity_id: this.stateObj.entityId,
|
entity_id: this.stateObj.entity_id,
|
||||||
direction: 'right'
|
direction: 'right'
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
|
@ -2,8 +2,6 @@
|
||||||
|
|
||||||
<link rel="import" href="../state-summary/state-card-content.html">
|
<link rel="import" href="../state-summary/state-card-content.html">
|
||||||
|
|
||||||
<link rel='import' href='../util/hass-behavior.html'>
|
|
||||||
|
|
||||||
<dom-module id="more-info-group">
|
<dom-module id="more-info-group">
|
||||||
<template>
|
<template>
|
||||||
<style>
|
<style>
|
||||||
|
@ -29,8 +27,6 @@
|
||||||
Polymer({
|
Polymer({
|
||||||
is: 'more-info-group',
|
is: 'more-info-group',
|
||||||
|
|
||||||
behaviors: [window.hassBehavior],
|
|
||||||
|
|
||||||
properties: {
|
properties: {
|
||||||
hass: {
|
hass: {
|
||||||
type: Object,
|
type: Object,
|
||||||
|
@ -42,20 +38,7 @@ Polymer({
|
||||||
|
|
||||||
states: {
|
states: {
|
||||||
type: Array,
|
type: Array,
|
||||||
bindNuclear: function (hass) {
|
computed: 'computeStates(stateObj, hass)',
|
||||||
return [
|
|
||||||
hass.moreInfoGetters.currentEntity,
|
|
||||||
hass.entityGetters.entityMap,
|
|
||||||
function (currentEntity, entities) {
|
|
||||||
// weird bug??
|
|
||||||
if (!currentEntity) {
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
return currentEntity.attributes.entity_id.map(
|
|
||||||
entities.get.bind(entities));
|
|
||||||
},
|
|
||||||
];
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -63,6 +46,21 @@ Polymer({
|
||||||
'statesChanged(stateObj, states)',
|
'statesChanged(stateObj, states)',
|
||||||
],
|
],
|
||||||
|
|
||||||
|
computeStates: function (stateObj, hass) {
|
||||||
|
var states = [];
|
||||||
|
var entIds = stateObj.attributes.entity_id;
|
||||||
|
|
||||||
|
for (var i = 0; i < entIds.length; i++) {
|
||||||
|
var state = hass.states[entIds[i]];
|
||||||
|
|
||||||
|
if (state) {
|
||||||
|
states.push(state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return states;
|
||||||
|
},
|
||||||
|
|
||||||
statesChanged: function (stateObj, states) {
|
statesChanged: function (stateObj, states) {
|
||||||
var groupDomainStateObj = false;
|
var groupDomainStateObj = false;
|
||||||
var baseStateObj;
|
var baseStateObj;
|
||||||
|
@ -73,15 +71,18 @@ Polymer({
|
||||||
if (states && states.length > 0) {
|
if (states && states.length > 0) {
|
||||||
baseStateObj = states[0];
|
baseStateObj = states[0];
|
||||||
|
|
||||||
groupDomainStateObj = baseStateObj.set('entityId', stateObj.entityId).set(
|
groupDomainStateObj = Object.assign(baseStateObj, {
|
||||||
'attributes', Object.assign({}, baseStateObj.attributes));
|
entity_id: stateObj.entity_id,
|
||||||
|
attributes: Object.assign({}, baseStateObj.attributes)
|
||||||
|
});
|
||||||
|
var groupDomain = window.hassUtil.computeDomain(groupDomainStateObj);
|
||||||
|
|
||||||
for (i = 0; i < states.length; i++) {
|
for (i = 0; i < states.length; i++) {
|
||||||
state = states[i];
|
state = states[i];
|
||||||
if (state && state.domain) {
|
|
||||||
if (groupDomainStateObj.domain !== state.domain) {
|
if (groupDomain !== window.hassUtil.computeDomain(state)) {
|
||||||
groupDomainStateObj = false;
|
groupDomainStateObj = false;
|
||||||
}
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -165,8 +165,8 @@ Polymer({
|
||||||
effectInput = this.stateObj.attributes.effect_list[effectIndex];
|
effectInput = this.stateObj.attributes.effect_list[effectIndex];
|
||||||
if (effectInput === this.stateObj.attributes.effect) return;
|
if (effectInput === this.stateObj.attributes.effect) return;
|
||||||
|
|
||||||
this.hass.serviceActions.callService('light', 'turn_on', {
|
this.hass.callService('light', 'turn_on', {
|
||||||
entity_id: this.stateObj.entityId,
|
entity_id: this.stateObj.entity_id,
|
||||||
effect: effectInput,
|
effect: effectInput,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
@ -177,10 +177,12 @@ Polymer({
|
||||||
if (isNaN(bri)) return;
|
if (isNaN(bri)) return;
|
||||||
|
|
||||||
if (bri === 0) {
|
if (bri === 0) {
|
||||||
this.hass.serviceActions.callTurnOff(this.stateObj.entityId);
|
this.hass.callService('light', 'turn_off', {
|
||||||
|
entity_id: this.stateObj.entity_id,
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
this.hass.serviceActions.callService('light', 'turn_on', {
|
this.hass.callService('light', 'turn_on', {
|
||||||
entity_id: this.stateObj.entityId,
|
entity_id: this.stateObj.entity_id,
|
||||||
brightness: bri,
|
brightness: bri,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -191,8 +193,8 @@ Polymer({
|
||||||
|
|
||||||
if (isNaN(ct)) return;
|
if (isNaN(ct)) return;
|
||||||
|
|
||||||
this.hass.serviceActions.callService('light', 'turn_on', {
|
this.hass.callService('light', 'turn_on', {
|
||||||
entity_id: this.stateObj.entityId,
|
entity_id: this.stateObj.entity_id,
|
||||||
color_temp: ct,
|
color_temp: ct,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
@ -202,14 +204,14 @@ Polymer({
|
||||||
|
|
||||||
if (isNaN(wv)) return;
|
if (isNaN(wv)) return;
|
||||||
|
|
||||||
this.hass.serviceActions.callService('light', 'turn_on', {
|
this.hass.callService('light', 'turn_on', {
|
||||||
entity_id: this.stateObj.entityId,
|
entity_id: this.stateObj.entity_id,
|
||||||
white_value: wv,
|
white_value: wv,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
serviceChangeColor: function (hass, entityId, color) {
|
serviceChangeColor: function (hass, entityId, color) {
|
||||||
hass.serviceActions.callService('light', 'turn_on', {
|
hass.callService('light', 'turn_on', {
|
||||||
entity_id: entityId,
|
entity_id: entityId,
|
||||||
rgb_color: [color.r, color.g, color.b],
|
rgb_color: [color.r, color.g, color.b],
|
||||||
});
|
});
|
||||||
|
@ -227,14 +229,14 @@ Polymer({
|
||||||
|
|
||||||
this.color = ev.detail.rgb;
|
this.color = ev.detail.rgb;
|
||||||
|
|
||||||
this.serviceChangeColor(this.hass, this.stateObj.entityId, this.color);
|
this.serviceChangeColor(this.hass, this.stateObj.entity_id, this.color);
|
||||||
|
|
||||||
this.colorChanged = false;
|
this.colorChanged = false;
|
||||||
this.skipColorPicked = true;
|
this.skipColorPicked = true;
|
||||||
|
|
||||||
this.colorDebounce = setTimeout(function () {
|
this.colorDebounce = setTimeout(function () {
|
||||||
if (this.colorChanged) {
|
if (this.colorChanged) {
|
||||||
this.serviceChangeColor(this.hass, this.stateObj.entityId, this.color);
|
this.serviceChangeColor(this.hass, this.stateObj.entity_id, this.color);
|
||||||
}
|
}
|
||||||
this.skipColorPicked = false;
|
this.skipColorPicked = false;
|
||||||
}.bind(this), 500);
|
}.bind(this), 500);
|
||||||
|
|
|
@ -58,8 +58,8 @@ Polymer({
|
||||||
|
|
||||||
callService: function (service, data) {
|
callService: function (service, data) {
|
||||||
var serviceData = data || {};
|
var serviceData = data || {};
|
||||||
serviceData.entity_id = this.stateObj.entityId;
|
serviceData.entity_id = this.stateObj.entity_id;
|
||||||
this.hass.serviceActions.callService('lock', service, serviceData);
|
this.hass.callService('lock', service, serviceData);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -10,8 +10,6 @@
|
||||||
<link rel='import' href='../../bower_components/paper-menu/paper-menu.html'>
|
<link rel='import' href='../../bower_components/paper-menu/paper-menu.html'>
|
||||||
<link rel='import' href='../../bower_components/paper-item/paper-item.html'>
|
<link rel='import' href='../../bower_components/paper-item/paper-item.html'>
|
||||||
|
|
||||||
<link rel='import' href='../util/hass-behavior.html'>
|
|
||||||
|
|
||||||
<dom-module id='more-info-media_player'>
|
<dom-module id='more-info-media_player'>
|
||||||
<template>
|
<template>
|
||||||
<style is="custom-style" include="iron-flex iron-flex-alignment"></style>
|
<style is="custom-style" include="iron-flex iron-flex-alignment"></style>
|
||||||
|
@ -115,14 +113,10 @@
|
||||||
Polymer({
|
Polymer({
|
||||||
is: 'more-info-media_player',
|
is: 'more-info-media_player',
|
||||||
|
|
||||||
behaviors: [window.hassBehavior],
|
|
||||||
|
|
||||||
properties: {
|
properties: {
|
||||||
ttsLoaded: {
|
ttsLoaded: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
bindNuclear: function (hass) {
|
computed: 'computeTTSLoaded(hass)',
|
||||||
return hass.configGetters.isComponentLoaded('tts');
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
|
|
||||||
hass: {
|
hass: {
|
||||||
|
@ -307,6 +301,10 @@ Polymer({
|
||||||
return !ttsLoaded || !supportsPlayMedia;
|
return !ttsLoaded || !supportsPlayMedia;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
computeTTSLoaded: function (hass) {
|
||||||
|
return window.hassUtil.isComponentLoaded(hass, 'tts');
|
||||||
|
},
|
||||||
|
|
||||||
handleTogglePower: function () {
|
handleTogglePower: function () {
|
||||||
this.callService(this.isOff ? 'turn_on' : 'turn_off');
|
this.callService(this.isOff ? 'turn_on' : 'turn_off');
|
||||||
},
|
},
|
||||||
|
@ -376,15 +374,14 @@ Polymer({
|
||||||
},
|
},
|
||||||
|
|
||||||
sendTTS: function () {
|
sendTTS: function () {
|
||||||
var services = this.hass.reactor.evaluate(
|
var services = this.hass.config.services.tts;
|
||||||
this.hass.serviceGetters.entityMap).get('tts').get('services').keySeq()
|
var serviceKeys = Object.keys(services).sort();
|
||||||
.toArray();
|
|
||||||
var service;
|
var service;
|
||||||
var i;
|
var i;
|
||||||
|
|
||||||
for (i = 0; i < services.length; i++) {
|
for (i = 0; i < serviceKeys.length; i++) {
|
||||||
if (services[i].indexOf('_say') !== -1) {
|
if (services[serviceKeys[i]].indexOf('_say') !== -1) {
|
||||||
service = services[i];
|
service = services[serviceKeys[i]];
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -393,8 +390,8 @@ Polymer({
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.hass.serviceActions.callService('tts', service, {
|
this.hass.callService('tts', service, {
|
||||||
entity_id: this.stateObj.entityId,
|
entity_id: this.stateObj.entity_id,
|
||||||
message: this.ttsMessage,
|
message: this.ttsMessage,
|
||||||
});
|
});
|
||||||
this.ttsMessage = '';
|
this.ttsMessage = '';
|
||||||
|
@ -403,8 +400,8 @@ Polymer({
|
||||||
|
|
||||||
callService: function (service, data) {
|
callService: function (service, data) {
|
||||||
var serviceData = data || {};
|
var serviceData = data || {};
|
||||||
serviceData.entity_id = this.stateObj.entityId;
|
serviceData.entity_id = this.stateObj.entity_id;
|
||||||
this.hass.serviceActions.callService('media_player', service, serviceData);
|
this.hass.callService('media_player', service, serviceData);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -36,12 +36,12 @@ Polymer({
|
||||||
],
|
],
|
||||||
|
|
||||||
inputChanged: function (hass, inDialog, stateObj) {
|
inputChanged: function (hass, inDialog, stateObj) {
|
||||||
if (!stateObj) return;
|
if (!stateObj || !hass) return;
|
||||||
|
|
||||||
window.hassUtil.dynamicContentUpdater(
|
window.hassUtil.dynamicContentUpdater(
|
||||||
this,
|
this,
|
||||||
('STATE-CARD-' +
|
('STATE-CARD-' +
|
||||||
window.hassUtil.stateCardType(this.hass, stateObj).toUpperCase()),
|
window.hassUtil.stateCardType(hass, stateObj).toUpperCase()),
|
||||||
{
|
{
|
||||||
hass: hass,
|
hass: hass,
|
||||||
stateObj: stateObj,
|
stateObj: stateObj,
|
||||||
|
|
|
@ -68,18 +68,18 @@ Polymer({
|
||||||
},
|
},
|
||||||
|
|
||||||
onOpenTap: function () {
|
onOpenTap: function () {
|
||||||
this.hass.serviceActions.callService('cover', 'open_cover',
|
this.hass.callService('cover', 'open_cover',
|
||||||
{ entity_id: this.stateObj.entityId });
|
{ entity_id: this.stateObj.entity_id });
|
||||||
},
|
},
|
||||||
|
|
||||||
onCloseTap: function () {
|
onCloseTap: function () {
|
||||||
this.hass.serviceActions.callService('cover', 'close_cover',
|
this.hass.callService('cover', 'close_cover',
|
||||||
{ entity_id: this.stateObj.entityId });
|
{ entity_id: this.stateObj.entity_id });
|
||||||
},
|
},
|
||||||
|
|
||||||
onStopTap: function () {
|
onStopTap: function () {
|
||||||
this.hass.serviceActions.callService('cover', 'stop_cover',
|
this.hass.callService('cover', 'stop_cover',
|
||||||
{ entity_id: this.stateObj.entityId });
|
{ entity_id: this.stateObj.entity_id });
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -20,7 +20,7 @@
|
||||||
|
|
||||||
<div class='horizontal justified layout'>
|
<div class='horizontal justified layout'>
|
||||||
<state-info state-obj="[[stateObj]]" in-dialog='[[inDialog]]'></state-info>
|
<state-info state-obj="[[stateObj]]" in-dialog='[[inDialog]]'></state-info>
|
||||||
<div class='state'>[[stateObj.stateDisplay]]</div>
|
<div class='state'>[[computeStateDisplay(stateObj)]]</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</dom-module>
|
</dom-module>
|
||||||
|
@ -39,5 +39,9 @@ Polymer({
|
||||||
type: Object,
|
type: Object,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
|
computeStateDisplay: function (stateObj) {
|
||||||
|
return window.hassUtil.computeStateState(stateObj);
|
||||||
|
},
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -27,7 +27,7 @@
|
||||||
<paper-dropdown-menu
|
<paper-dropdown-menu
|
||||||
on-tap='stopPropagation'
|
on-tap='stopPropagation'
|
||||||
selected-item-label='{{selectedOption}}'
|
selected-item-label='{{selectedOption}}'
|
||||||
label='[[stateObj.entityDisplay]]'>
|
label='[[computeStateName(stateObj)]]'>
|
||||||
<paper-menu class="dropdown-content" selected="[[computeSelected(stateObj)]]">
|
<paper-menu class="dropdown-content" selected="[[computeSelected(stateObj)]]">
|
||||||
<template is='dom-repeat' items='[[stateObj.attributes.options]]'>
|
<template is='dom-repeat' items='[[stateObj.attributes.options]]'>
|
||||||
<paper-item>[[item]]</paper-item>
|
<paper-item>[[item]]</paper-item>
|
||||||
|
@ -61,6 +61,10 @@ Polymer({
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
|
computeStateName: function (stateObj) {
|
||||||
|
return window.hassUtil.computeStateName(stateObj);
|
||||||
|
},
|
||||||
|
|
||||||
computeSelected: function (stateObj) {
|
computeSelected: function (stateObj) {
|
||||||
return stateObj.attributes.options.indexOf(stateObj.state);
|
return stateObj.attributes.options.indexOf(stateObj.state);
|
||||||
},
|
},
|
||||||
|
@ -70,9 +74,9 @@ Polymer({
|
||||||
if (option === '' || option === this.stateObj.state) {
|
if (option === '' || option === this.stateObj.state) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.hass.serviceActions.callService('input_select', 'select_option', {
|
this.hass.callService('input_select', 'select_option', {
|
||||||
option: option,
|
option: option,
|
||||||
entity_id: this.stateObj.entityId,
|
entity_id: this.stateObj.entity_id,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
@ -68,9 +68,9 @@ Polymer({
|
||||||
if (this.value === Number(this.stateObj.state)) {
|
if (this.value === Number(this.stateObj.state)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.hass.serviceActions.callService('input_slider', 'select_value', {
|
this.hass.callService('input_slider', 'select_value', {
|
||||||
value: this.value,
|
value: this.value,
|
||||||
entity_id: this.stateObj.entityId,
|
entity_id: this.stateObj.entity_id,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
@ -38,8 +38,8 @@
|
||||||
<div class='horizontal justified layout'>
|
<div class='horizontal justified layout'>
|
||||||
<state-info state-obj="[[stateObj]]" in-dialog='[[inDialog]]'></state-info>
|
<state-info state-obj="[[stateObj]]" in-dialog='[[inDialog]]'></state-info>
|
||||||
<div class='state'>
|
<div class='state'>
|
||||||
<div class='main-text' take-height$='[[!secondaryText]]'>[[computePrimaryText(stateObj, isPlaying)]]</div>
|
<div class='main-text' take-height$='[[!playerObj.secondaryText]]'>[[playerObj.primaryText]]</div>
|
||||||
<div class='secondary-text'>[[secondaryText]]</div>
|
<div class='secondary-text'>[[playerObj.secondaryText]]</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
@ -51,6 +51,10 @@ Polymer({
|
||||||
is: 'state-card-media_player',
|
is: 'state-card-media_player',
|
||||||
|
|
||||||
properties: {
|
properties: {
|
||||||
|
hass: {
|
||||||
|
type: Object,
|
||||||
|
},
|
||||||
|
|
||||||
inDialog: {
|
inDialog: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
value: false,
|
value: false,
|
||||||
|
@ -60,41 +64,14 @@ Polymer({
|
||||||
type: Object,
|
type: Object,
|
||||||
},
|
},
|
||||||
|
|
||||||
isPlaying: {
|
playerObj: {
|
||||||
type: Boolean,
|
type: Object,
|
||||||
computed: 'computeIsPlaying(stateObj)',
|
computed: 'computePlayerObj(hass, stateObj)',
|
||||||
},
|
|
||||||
|
|
||||||
secondaryText: {
|
|
||||||
type: String,
|
|
||||||
computed: 'computeSecondaryText(stateObj)',
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
computeIsPlaying: function (stateObj) {
|
computePlayerObj: function (hass, stateObj) {
|
||||||
return this.PLAYING_STATES.indexOf(stateObj.state) !== -1;
|
return new window.MediaPlayerEntity(hass, stateObj);
|
||||||
},
|
|
||||||
|
|
||||||
computePrimaryText: function (stateObj, isPlaying) {
|
|
||||||
return isPlaying ? stateObj.attributes.media_title : stateObj.stateDisplay;
|
|
||||||
},
|
|
||||||
|
|
||||||
computeSecondaryText: function (stateObj) {
|
|
||||||
var text;
|
|
||||||
|
|
||||||
if (stateObj.attributes.media_content_type === 'music') {
|
|
||||||
return stateObj.attributes.media_artist;
|
|
||||||
} else if (stateObj.attributes.media_content_type === 'tvshow') {
|
|
||||||
text = stateObj.attributes.media_series_title;
|
|
||||||
|
|
||||||
if (stateObj.attributes.media_season && stateObj.attributes.media_episode) {
|
|
||||||
text += ' S' + stateObj.attributes.media_season + 'E' + stateObj.attributes.media_episode;
|
|
||||||
}
|
|
||||||
return text;
|
|
||||||
} else if (stateObj.attributes.app_name) {
|
|
||||||
return stateObj.attributes.app_name;
|
|
||||||
}
|
|
||||||
return '';
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -47,7 +47,9 @@ Polymer({
|
||||||
|
|
||||||
activateScene: function (ev) {
|
activateScene: function (ev) {
|
||||||
ev.stopPropagation();
|
ev.stopPropagation();
|
||||||
this.hass.serviceActions.callTurnOn(this.stateObj.entityId);
|
this.hass.callService(
|
||||||
|
'scene', 'turn_on',
|
||||||
|
{ entity_id: this.stateObj.entity_id });
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -53,7 +53,9 @@ Polymer({
|
||||||
|
|
||||||
fireScript: function (ev) {
|
fireScript: function (ev) {
|
||||||
ev.stopPropagation();
|
ev.stopPropagation();
|
||||||
this.hass.serviceActions.callTurnOn(this.stateObj.entityId);
|
this.hass.callService(
|
||||||
|
'script', 'turn_on',
|
||||||
|
{ entity_id: this.stateObj.entity_id });
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -20,7 +20,7 @@
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<state-badge state-obj='[[stateObj]]' in-dialog='[[inDialog]]'></state-badge>
|
<state-badge state-obj='[[stateObj]]' in-dialog='[[inDialog]]'></state-badge>
|
||||||
<a href$='[[stateObj.state]]' target='_blank' class='name' id='link'>[[stateObj.entityDisplay]]</a>
|
<a href$='[[stateObj.state]]' target='_blank' class='name' id='link'>[[computeStateName(stateObj)]]</a>
|
||||||
</template>
|
</template>
|
||||||
</dom-module>
|
</dom-module>
|
||||||
|
|
||||||
|
@ -43,6 +43,10 @@ Polymer({
|
||||||
tap: 'onTap',
|
tap: 'onTap',
|
||||||
},
|
},
|
||||||
|
|
||||||
|
computeStateName: function (stateObj) {
|
||||||
|
return window.hassUtil.computeStateName(stateObj);
|
||||||
|
},
|
||||||
|
|
||||||
onTap: function (ev) {
|
onTap: function (ev) {
|
||||||
ev.stopPropagation();
|
ev.stopPropagation();
|
||||||
if (ev.target === this.$.link) {
|
if (ev.target === this.$.link) {
|
||||||
|
|
|
@ -0,0 +1,48 @@
|
||||||
|
<script>
|
||||||
|
(function () {
|
||||||
|
var STORED_STATE = [
|
||||||
|
'dockedSidebar',
|
||||||
|
];
|
||||||
|
|
||||||
|
Polymer({
|
||||||
|
is: 'ha-pref-storage',
|
||||||
|
|
||||||
|
properties: {
|
||||||
|
hass: {
|
||||||
|
type: Object,
|
||||||
|
},
|
||||||
|
|
||||||
|
storage: {
|
||||||
|
type: Object,
|
||||||
|
value: window.localStorage || {},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
storeState: function () {
|
||||||
|
if (!this.hass) return;
|
||||||
|
|
||||||
|
try {
|
||||||
|
for (var i = 0; i < STORED_STATE.length; i++) {
|
||||||
|
var key = STORED_STATE[i];
|
||||||
|
this.storage[key] = JSON.stringify(this.hass[key]);
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
// Safari throws exception in private mode
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
getStoredState: function () {
|
||||||
|
var state = {};
|
||||||
|
|
||||||
|
for (var i = 0; i < STORED_STATE.length; i++) {
|
||||||
|
var key = STORED_STATE[i];
|
||||||
|
if (key in this.storage) {
|
||||||
|
state[key] = JSON.parse(this.storage[key]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}());
|
||||||
|
</script>
|
|
@ -0,0 +1,89 @@
|
||||||
|
<script>
|
||||||
|
(function () {
|
||||||
|
var PAGE_TITLE = 'Home Assistant';
|
||||||
|
|
||||||
|
function pageState(panel, view) {
|
||||||
|
var state = { panel: panel };
|
||||||
|
if (panel === 'states') {
|
||||||
|
state.view = view || null;
|
||||||
|
}
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
|
||||||
|
function pageUrl(pane, view) {
|
||||||
|
return pane === 'states' && view ?
|
||||||
|
'/' + pane + '/' + view : '/' + pane;
|
||||||
|
}
|
||||||
|
|
||||||
|
Polymer({
|
||||||
|
is: 'ha-url-sync',
|
||||||
|
|
||||||
|
properties: {
|
||||||
|
hass: {
|
||||||
|
type: Object,
|
||||||
|
observer: 'hassChanged',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
hassChanged: function (newHass, oldHass) {
|
||||||
|
if (!oldHass) {
|
||||||
|
return;
|
||||||
|
} else if (newHass.currentPanel === oldHass.currentPanel &&
|
||||||
|
newHass.currentView === oldHass.currentView) {
|
||||||
|
// did the more info entity change?
|
||||||
|
if (oldHass.moreInfoEntityId !== newHass.moreInfoEntityId) {
|
||||||
|
if (newHass.moreInfoEntityId) {
|
||||||
|
// push same state so that back button works.
|
||||||
|
history.pushState(history.state, PAGE_TITLE, window.location.pathname);
|
||||||
|
} else if (this.ignoreNextDeselectEntity) {
|
||||||
|
this.ignoreNextDeselectEntity = false;
|
||||||
|
} else {
|
||||||
|
history.back();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
} else if (this.ignoreNextNav) {
|
||||||
|
this.ignoreNextNav = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
history.pushState(
|
||||||
|
pageState(newHass.currentPanel, newHass.currentView), PAGE_TITLE,
|
||||||
|
pageUrl(newHass.currentPanel, newHass.currentView));
|
||||||
|
},
|
||||||
|
|
||||||
|
popstateChangeListener: function (ev) {
|
||||||
|
if (this.hass.moreInfoEntityId) {
|
||||||
|
this.ignoreNextDeselectEntity = true;
|
||||||
|
this.fire('hass-more-info', { entityId: null });
|
||||||
|
} else if (this.hass.currentPanel !== ev.state.panel ||
|
||||||
|
this.hass.currentView !== ev.state.view) {
|
||||||
|
this.ignoreNextNav = true;
|
||||||
|
this.fire('hass-navigate', ev.state);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// initial url sync
|
||||||
|
attached: function () {
|
||||||
|
this.popstateChangeListener = this.popstateChangeListener.bind(this);
|
||||||
|
|
||||||
|
// keep state in sync when url changes via forward/back buttons
|
||||||
|
window.addEventListener('popstate', this.popstateChangeListener);
|
||||||
|
|
||||||
|
// store current view / panel
|
||||||
|
if (window.location.pathname === '/') {
|
||||||
|
var currentPanel = this.hass.currentPanel;
|
||||||
|
var currentView = this.hass.currentView;
|
||||||
|
|
||||||
|
history.replaceState(
|
||||||
|
pageState(currentPanel, currentView), PAGE_TITLE,
|
||||||
|
pageUrl(currentPanel, currentView));
|
||||||
|
} else {
|
||||||
|
var parts = window.location.pathname.substr(1).split('/');
|
||||||
|
this.fire('hass-navigate', pageState(parts[0], parts[1]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}());
|
||||||
|
</script>
|
|
@ -1,40 +0,0 @@
|
||||||
<script>
|
|
||||||
/** @polymerBehavior */
|
|
||||||
window.hassBehavior = {
|
|
||||||
attached: function attached() {
|
|
||||||
var hass = this.hass;
|
|
||||||
|
|
||||||
if (!hass) {
|
|
||||||
throw new Error('No hass property found on ' + this.nodeName);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.nuclearUnwatchFns = Object.keys(this.properties).reduce(
|
|
||||||
function bindGetters(unwatchFns, key) {
|
|
||||||
var getter;
|
|
||||||
|
|
||||||
if (!('bindNuclear' in this.properties[key])) {
|
|
||||||
return unwatchFns;
|
|
||||||
}
|
|
||||||
|
|
||||||
getter = this.properties[key].bindNuclear(hass);
|
|
||||||
|
|
||||||
if (!getter) {
|
|
||||||
throw new Error('Undefined getter specified for key ' + key +
|
|
||||||
' on ' + this.nodeName);
|
|
||||||
}
|
|
||||||
|
|
||||||
this[key] = hass.reactor.evaluate(getter);
|
|
||||||
|
|
||||||
return unwatchFns.concat(hass.reactor.observe(getter, function updateAttribute(val) {
|
|
||||||
this[key] = val;
|
|
||||||
}.bind(this)));
|
|
||||||
}.bind(this), []);
|
|
||||||
},
|
|
||||||
|
|
||||||
detached: function detached() {
|
|
||||||
while (this.nuclearUnwatchFns.length) {
|
|
||||||
this.nuclearUnwatchFns.shift()();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
};
|
|
||||||
</script>
|
|
|
@ -0,0 +1,81 @@
|
||||||
|
<script>
|
||||||
|
window.hassCallApi = function (host, auth, method, path, parameters) {
|
||||||
|
var url = host + '/api/' + path;
|
||||||
|
|
||||||
|
if (window.HASS_DEMO) {
|
||||||
|
var component = path.split('/', 1)[0];
|
||||||
|
var data;
|
||||||
|
switch (component) {
|
||||||
|
case 'bootstrap':
|
||||||
|
data = window.hassDemoData.bootstrap;
|
||||||
|
break;
|
||||||
|
case 'logbook':
|
||||||
|
data = window.hassDemoData.logbook;
|
||||||
|
break;
|
||||||
|
case 'history':
|
||||||
|
data = window.hassDemoData.stateHistory;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
data = false;
|
||||||
|
}
|
||||||
|
return new Promise(function (resolve, reject) {
|
||||||
|
if (data) {
|
||||||
|
resolve(data);
|
||||||
|
} else {
|
||||||
|
reject('Request not allowed in demo mode.');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Promise(function (resolve, reject) {
|
||||||
|
var req = new XMLHttpRequest();
|
||||||
|
req.open(method, url, true);
|
||||||
|
|
||||||
|
if (auth.authToken) {
|
||||||
|
req.setRequestHeader('X-HA-access', auth.authToken);
|
||||||
|
}
|
||||||
|
|
||||||
|
req.onload = function () {
|
||||||
|
var body = req.responseText;
|
||||||
|
|
||||||
|
if (req.getResponseHeader('content-type') === 'application/json') {
|
||||||
|
try {
|
||||||
|
body = JSON.parse(req.responseText);
|
||||||
|
} catch (err) {
|
||||||
|
reject({
|
||||||
|
error: 'Unable to parse JSON response',
|
||||||
|
status_code: req.status,
|
||||||
|
body: body,
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (req.status > 199 && req.status < 300) {
|
||||||
|
resolve(body);
|
||||||
|
} else {
|
||||||
|
reject({
|
||||||
|
error: 'Response error: ' + req.status,
|
||||||
|
status_code: req.status,
|
||||||
|
body: body
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
req.onerror = function () {
|
||||||
|
reject({
|
||||||
|
error: 'Request error',
|
||||||
|
status_code: req.status,
|
||||||
|
body: req.responseText,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
if (parameters) {
|
||||||
|
req.setRequestHeader('Content-Type', 'application/json;charset=UTF-8');
|
||||||
|
req.send(JSON.stringify(parameters));
|
||||||
|
} else {
|
||||||
|
req.send();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
</script>
|
|
@ -46,8 +46,28 @@ window.hassUtil.attributeClassNames = function (stateObj, attributes) {
|
||||||
).join(' ');
|
).join(' ');
|
||||||
};
|
};
|
||||||
|
|
||||||
window.hassUtil.canToggle = function (hass, entityId) {
|
window.hassUtil.canToggleState = function (hass, stateObj) {
|
||||||
return hass.reactor.evaluate(hass.serviceGetters.canToggleEntity(entityId));
|
var domain = window.hassUtil.computeDomain(stateObj);
|
||||||
|
if (domain === 'group') {
|
||||||
|
return stateObj.state === 'on' || stateObj.state === 'off';
|
||||||
|
}
|
||||||
|
|
||||||
|
return window.hassUtil.canToggleDomain(hass, domain);
|
||||||
|
};
|
||||||
|
|
||||||
|
window.hassUtil.canToggleDomain = function (hass, domain) {
|
||||||
|
var turnOnService;
|
||||||
|
var services = hass.config.services[domain];
|
||||||
|
|
||||||
|
if (domain === 'lock') {
|
||||||
|
turnOnService = 'lock';
|
||||||
|
} else if (domain === 'cover') {
|
||||||
|
turnOnService = 'open_cover';
|
||||||
|
} else {
|
||||||
|
turnOnService = 'turn_on';
|
||||||
|
}
|
||||||
|
|
||||||
|
return services && turnOnService in services;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Update root's child element to be newElementTag replacing another existing child if any.
|
// Update root's child element to be newElementTag replacing another existing child if any.
|
||||||
|
@ -168,22 +188,29 @@ window.hassUtil.relativeTime.tests = [
|
||||||
7, 'day',
|
7, 'day',
|
||||||
];
|
];
|
||||||
|
|
||||||
window.hassUtil.stateCardType = function (hass, state) {
|
window.hassUtil.stateCardType = function (hass, stateObj) {
|
||||||
if (state.state === 'unavailable') {
|
if (stateObj.state === 'unavailable') {
|
||||||
return 'display';
|
return 'display';
|
||||||
} else if (window.hassUtil.DOMAINS_WITH_CARD.indexOf(state.domain) !== -1) {
|
}
|
||||||
return state.domain;
|
|
||||||
} else if (window.hassUtil.canToggle(hass, state.entityId) && state.attributes.control !== 'hidden') {
|
var domain = window.hassUtil.computeDomain(stateObj);
|
||||||
|
|
||||||
|
if (window.hassUtil.DOMAINS_WITH_CARD.indexOf(domain) !== -1) {
|
||||||
|
return domain;
|
||||||
|
} else if (window.hassUtil.canToggleState(hass, stateObj) &&
|
||||||
|
stateObj.attributes.control !== 'hidden') {
|
||||||
return 'toggle';
|
return 'toggle';
|
||||||
}
|
}
|
||||||
return 'display';
|
return 'display';
|
||||||
};
|
};
|
||||||
|
|
||||||
window.hassUtil.stateMoreInfoType = function (state) {
|
window.hassUtil.stateMoreInfoType = function (stateObj) {
|
||||||
if (window.hassUtil.DOMAINS_WITH_MORE_INFO.indexOf(state.domain) !== -1) {
|
var domain = window.hassUtil.computeDomain(stateObj);
|
||||||
return state.domain;
|
|
||||||
|
if (window.hassUtil.DOMAINS_WITH_MORE_INFO.indexOf(domain) !== -1) {
|
||||||
|
return domain;
|
||||||
}
|
}
|
||||||
if (window.hassUtil.HIDE_MORE_INFO.indexOf(state.domain) !== -1) {
|
if (window.hassUtil.HIDE_MORE_INFO.indexOf(domain) !== -1) {
|
||||||
return 'hidden';
|
return 'hidden';
|
||||||
}
|
}
|
||||||
return 'default';
|
return 'default';
|
||||||
|
@ -318,27 +345,65 @@ window.hassUtil.binarySensorIcon = function (state) {
|
||||||
};
|
};
|
||||||
|
|
||||||
window.hassUtil.stateIcon = function (state) {
|
window.hassUtil.stateIcon = function (state) {
|
||||||
var unit;
|
|
||||||
|
|
||||||
if (!state) {
|
if (!state) {
|
||||||
return window.hassUtil.DEFAULT_ICON;
|
return window.hassUtil.DEFAULT_ICON;
|
||||||
} else if (state.attributes.icon) {
|
} else if (state.attributes.icon) {
|
||||||
return state.attributes.icon;
|
return state.attributes.icon;
|
||||||
}
|
}
|
||||||
|
|
||||||
unit = state.attributes.unit_of_measurement;
|
var unit = state.attributes.unit_of_measurement;
|
||||||
|
var domain = window.hassUtil.computeDomain(state);
|
||||||
|
|
||||||
if (unit && state.domain === 'sensor') {
|
if (unit && domain === 'sensor') {
|
||||||
if (unit === '°C' || unit === '°F') {
|
if (unit === '°C' || unit === '°F') {
|
||||||
return 'mdi:thermometer';
|
return 'mdi:thermometer';
|
||||||
} else if (unit === 'Mice') {
|
} else if (unit === 'Mice') {
|
||||||
return 'mdi:mouse-variant';
|
return 'mdi:mouse-variant';
|
||||||
}
|
}
|
||||||
} else if (state.domain === 'binary_sensor') {
|
} else if (domain === 'binary_sensor') {
|
||||||
return window.hassUtil.binarySensorIcon(state);
|
return window.hassUtil.binarySensorIcon(state);
|
||||||
}
|
}
|
||||||
|
|
||||||
return window.hassUtil.domainIcon(state.domain, state.state);
|
return window.hassUtil.domainIcon(domain, state.state);
|
||||||
|
};
|
||||||
|
|
||||||
|
window.hassUtil.computeDomain = function (stateObj) {
|
||||||
|
if (!stateObj._domain) {
|
||||||
|
stateObj._domain = window.HAWS.extractDomain(stateObj.entity_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
return stateObj._domain;
|
||||||
|
};
|
||||||
|
|
||||||
|
window.hassUtil.computeStateName = function (stateObj) {
|
||||||
|
if (!stateObj._entityDisplay) {
|
||||||
|
stateObj._entityDisplay = (
|
||||||
|
stateObj.attributes.friendly_name ||
|
||||||
|
window.HAWS.extractObjectId(stateObj.entity_id)
|
||||||
|
.replace(/_/g, ' '));
|
||||||
|
}
|
||||||
|
|
||||||
|
return stateObj._entityDisplay;
|
||||||
|
};
|
||||||
|
|
||||||
|
window.hassUtil.computeStateState = function (stateObj) {
|
||||||
|
if (!stateObj._stateDisplay) {
|
||||||
|
stateObj._stateDisplay = stateObj.state.replace(/_/g, ' ');
|
||||||
|
|
||||||
|
if (stateObj.attributes.unit_of_measurement) {
|
||||||
|
stateObj._stateDisplay += ` ${stateObj.attributes.unit_of_measurement}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return stateObj._stateDisplay;
|
||||||
|
};
|
||||||
|
|
||||||
|
window.hassUtil.isComponentLoaded = function (hass, component) {
|
||||||
|
return hass.config.core.components.indexOf(component) !== -1;
|
||||||
|
};
|
||||||
|
|
||||||
|
window.hassUtil.computeLocationName = function (hass) {
|
||||||
|
return hass.config.core.location_name;
|
||||||
};
|
};
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -0,0 +1,195 @@
|
||||||
|
<script>
|
||||||
|
(function () {
|
||||||
|
window.MediaPlayerEntity = function (hass, stateObj) {
|
||||||
|
this.hass = hass;
|
||||||
|
this.stateObj = stateObj;
|
||||||
|
};
|
||||||
|
|
||||||
|
function addGetter(name, getter) {
|
||||||
|
Object.defineProperty(window.MediaPlayerEntity.prototype, name,
|
||||||
|
{ get: getter });
|
||||||
|
}
|
||||||
|
|
||||||
|
addGetter('isOff', function () {
|
||||||
|
return this.stateObj.state === 'off';
|
||||||
|
});
|
||||||
|
|
||||||
|
addGetter('isIdle', function () {
|
||||||
|
return this.stateObj.state === 'idle';
|
||||||
|
});
|
||||||
|
|
||||||
|
addGetter('isMuted', function () {
|
||||||
|
return this.stateObj.attributes.is_volume_muted;
|
||||||
|
});
|
||||||
|
|
||||||
|
addGetter('isPaused', function () {
|
||||||
|
return this.stateObj.state === 'paused';
|
||||||
|
});
|
||||||
|
|
||||||
|
addGetter('isPlaying', function () {
|
||||||
|
return this.stateObj.state === 'playing';
|
||||||
|
});
|
||||||
|
|
||||||
|
addGetter('isMusic', function () {
|
||||||
|
return this.stateObj.attributes.media_content_type === 'music';
|
||||||
|
});
|
||||||
|
|
||||||
|
addGetter('isTVShow', function () {
|
||||||
|
return this.stateObj.attributes.media_content_type === 'tvshow';
|
||||||
|
});
|
||||||
|
|
||||||
|
addGetter('hasMediaControl', function () {
|
||||||
|
return ['playing', 'paused', 'unknown'].indexOf(
|
||||||
|
this.stateObj.state) !== -1;
|
||||||
|
});
|
||||||
|
|
||||||
|
addGetter('volumeSliderValue', function () {
|
||||||
|
return this.stateObj.attributes.volume_level * 100;
|
||||||
|
});
|
||||||
|
|
||||||
|
addGetter('showProgress', function () {
|
||||||
|
return (
|
||||||
|
(this.isPlaying || this.isPaused) &&
|
||||||
|
'media_position' in this.stateObj.attributes &&
|
||||||
|
'media_position_updated_at' in this.stateObj.attributes);
|
||||||
|
});
|
||||||
|
|
||||||
|
addGetter('currentProgress', function () {
|
||||||
|
return (
|
||||||
|
this.stateObj.attributes.media_position +
|
||||||
|
((Date.now() -
|
||||||
|
new Date(this.stateObj.attributes.media_position_updated_at)) / 1000));
|
||||||
|
});
|
||||||
|
|
||||||
|
/* eslint-disable no-bitwise */
|
||||||
|
|
||||||
|
addGetter('supportsPause', function () {
|
||||||
|
return (this.stateObj.attributes.supported_media_commands & 1) !== 0;
|
||||||
|
});
|
||||||
|
|
||||||
|
addGetter('supportsVolumeSet', function () {
|
||||||
|
return (this.stateObj.attributes.supported_media_commands & 4) !== 0;
|
||||||
|
});
|
||||||
|
|
||||||
|
addGetter('supportsVolumeMute', function () {
|
||||||
|
return (this.stateObj.attributes.supported_media_commands & 8) !== 0;
|
||||||
|
});
|
||||||
|
|
||||||
|
addGetter('supportsPreviousTrack', function () {
|
||||||
|
return (this.stateObj.attributes.supported_media_commands & 16) !== 0;
|
||||||
|
});
|
||||||
|
|
||||||
|
addGetter('supportsNextTrack', function () {
|
||||||
|
return (this.stateObj.attributes.supported_media_commands & 32) !== 0;
|
||||||
|
});
|
||||||
|
|
||||||
|
addGetter('supportsTurnOn', function () {
|
||||||
|
return (this.stateObj.attributes.supported_media_commands & 128) !== 0;
|
||||||
|
});
|
||||||
|
|
||||||
|
addGetter('supportsTurnOff', function () {
|
||||||
|
return (this.stateObj.attributes.supported_media_commands & 256) !== 0;
|
||||||
|
});
|
||||||
|
|
||||||
|
addGetter('supportsPlayMedia', function () {
|
||||||
|
return (this.stateObj.attributes.supported_media_commands & 512) !== 0;
|
||||||
|
});
|
||||||
|
|
||||||
|
addGetter('supportsVolumeButtons', function () {
|
||||||
|
return (this.stateObj.attributes.supported_media_commands & 1024) !== 0;
|
||||||
|
});
|
||||||
|
|
||||||
|
addGetter('supportsPlay', function () {
|
||||||
|
return (this.stateObj.attributes.supported_media_commands & 16384) !== 0;
|
||||||
|
});
|
||||||
|
|
||||||
|
/* eslint-enable no-bitwise */
|
||||||
|
|
||||||
|
addGetter('primaryText', function () {
|
||||||
|
return this.stateObj.attributes.media_title ||
|
||||||
|
window.hassUtil.computeStateState(this.stateObj);
|
||||||
|
});
|
||||||
|
|
||||||
|
addGetter('secondaryText', function () {
|
||||||
|
if (this.isMusic) {
|
||||||
|
return this.stateObj.attributes.media_artist;
|
||||||
|
} else if (this.isTVShow) {
|
||||||
|
var text = this.stateObj.attributes.media_series_title;
|
||||||
|
|
||||||
|
if (this.stateObj.attributes.media_season) {
|
||||||
|
text += ' S' + this.stateObj.attributes.media_season;
|
||||||
|
|
||||||
|
if (this.stateObj.attributes.media_episode) {
|
||||||
|
text += 'E' + this.stateObj.attributes.media_episode;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return text;
|
||||||
|
} else if (this.stateObj.attributes.app_name) {
|
||||||
|
return this.stateObj.attributes.app_name;
|
||||||
|
}
|
||||||
|
return '';
|
||||||
|
});
|
||||||
|
|
||||||
|
Object.assign(window.MediaPlayerEntity.prototype, {
|
||||||
|
mediaPlayPause() {
|
||||||
|
this.callService('media_play_pause');
|
||||||
|
},
|
||||||
|
|
||||||
|
nextTrack() {
|
||||||
|
this.callService('media_next_track');
|
||||||
|
},
|
||||||
|
|
||||||
|
playbackControl() {
|
||||||
|
this.callService('media_play_pause');
|
||||||
|
},
|
||||||
|
|
||||||
|
previousTrack() {
|
||||||
|
this.callService('media_previous_track');
|
||||||
|
},
|
||||||
|
|
||||||
|
setVolume(volume) {
|
||||||
|
this.callService('volume_set', { volume_level: volume });
|
||||||
|
},
|
||||||
|
|
||||||
|
togglePower() {
|
||||||
|
if (this.isOff) {
|
||||||
|
this.turnOn();
|
||||||
|
} else {
|
||||||
|
this.turnOff();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
turnOff() {
|
||||||
|
this.callService('turn_off');
|
||||||
|
},
|
||||||
|
|
||||||
|
turnOn() {
|
||||||
|
this.callService('turn_on');
|
||||||
|
},
|
||||||
|
|
||||||
|
volumeDown() {
|
||||||
|
this.callService('volume_down');
|
||||||
|
},
|
||||||
|
|
||||||
|
volumeMute(mute) {
|
||||||
|
if (!this.supportsVolumeMute) {
|
||||||
|
throw new Error('Muting volume not supported');
|
||||||
|
}
|
||||||
|
this.callService('volume_mute', { is_volume_muted: mute });
|
||||||
|
},
|
||||||
|
|
||||||
|
volumeUp() {
|
||||||
|
this.callService('volume_down');
|
||||||
|
},
|
||||||
|
|
||||||
|
// helper method
|
||||||
|
|
||||||
|
callService(service, data) {
|
||||||
|
var serviceData = data || {};
|
||||||
|
serviceData.entity_id = this.stateObj.entity_id;
|
||||||
|
this.hass.callService('media_player', service, serviceData);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}());
|
||||||
|
</script>
|
30
yarn.lock
30
yarn.lock
|
@ -905,10 +905,6 @@ class-extend@^0.1.0, class-extend@^0.1.1:
|
||||||
dependencies:
|
dependencies:
|
||||||
object-assign "^2.0.0"
|
object-assign "^2.0.0"
|
||||||
|
|
||||||
classnames@^2.2.5:
|
|
||||||
version "2.2.5"
|
|
||||||
resolved "https://registry.yarnpkg.com/classnames/-/classnames-2.2.5.tgz#fb3801d453467649ef3603c7d61a02bd129bde6d"
|
|
||||||
|
|
||||||
clean-css@3.4.x:
|
clean-css@3.4.x:
|
||||||
version "3.4.24"
|
version "3.4.24"
|
||||||
resolved "https://registry.yarnpkg.com/clean-css/-/clean-css-3.4.24.tgz#89f5a5e9da37ae02394fe049a41388abbe72c3b5"
|
resolved "https://registry.yarnpkg.com/clean-css/-/clean-css-3.4.24.tgz#89f5a5e9da37ae02394fe049a41388abbe72c3b5"
|
||||||
|
@ -2806,9 +2802,9 @@ hoek@2.x.x:
|
||||||
version "2.16.3"
|
version "2.16.3"
|
||||||
resolved "https://registry.yarnpkg.com/hoek/-/hoek-2.16.3.tgz#20bb7403d3cea398e91dc4710a8ff1b8274a25ed"
|
resolved "https://registry.yarnpkg.com/hoek/-/hoek-2.16.3.tgz#20bb7403d3cea398e91dc4710a8ff1b8274a25ed"
|
||||||
|
|
||||||
home-assistant-js-websocket@0.5.0:
|
home-assistant-js-websocket@^0.7.3:
|
||||||
version "0.5.0"
|
version "0.7.3"
|
||||||
resolved "https://registry.yarnpkg.com/home-assistant-js-websocket/-/home-assistant-js-websocket-0.5.0.tgz#f78b31d160d7bdd083cf53ae01328483e7046ef5"
|
resolved "https://registry.yarnpkg.com/home-assistant-js-websocket/-/home-assistant-js-websocket-0.7.3.tgz#e65ac99b59a0b2c623048457c259808d67030c7f"
|
||||||
|
|
||||||
homedir-polyfill@^1.0.0:
|
homedir-polyfill@^1.0.0:
|
||||||
version "1.0.1"
|
version "1.0.1"
|
||||||
|
@ -2934,10 +2930,6 @@ ignore@^3.2.0:
|
||||||
version "3.2.0"
|
version "3.2.0"
|
||||||
resolved "https://registry.yarnpkg.com/ignore/-/ignore-3.2.0.tgz#8d88f03c3002a0ac52114db25d2c673b0bf1e435"
|
resolved "https://registry.yarnpkg.com/ignore/-/ignore-3.2.0.tgz#8d88f03c3002a0ac52114db25d2c673b0bf1e435"
|
||||||
|
|
||||||
immutable@^3.8.1:
|
|
||||||
version "3.8.1"
|
|
||||||
resolved "https://registry.yarnpkg.com/immutable/-/immutable-3.8.1.tgz#200807f11ab0f72710ea485542de088075f68cd2"
|
|
||||||
|
|
||||||
imurmurhash@^0.1.4:
|
imurmurhash@^0.1.4:
|
||||||
version "0.1.4"
|
version "0.1.4"
|
||||||
resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea"
|
resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea"
|
||||||
|
@ -3336,10 +3328,6 @@ keep-alive-agent@^0.0.1:
|
||||||
version "0.0.1"
|
version "0.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/keep-alive-agent/-/keep-alive-agent-0.0.1.tgz#44847ca394ce8d6b521ae85816bd64509942b385"
|
resolved "https://registry.yarnpkg.com/keep-alive-agent/-/keep-alive-agent-0.0.1.tgz#44847ca394ce8d6b521ae85816bd64509942b385"
|
||||||
|
|
||||||
keymirror@^0.1.1:
|
|
||||||
version "0.1.1"
|
|
||||||
resolved "https://registry.yarnpkg.com/keymirror/-/keymirror-0.1.1.tgz#918889ea13f8d0a42e7c557250eee713adc95c35"
|
|
||||||
|
|
||||||
kind-of@^3.0.2:
|
kind-of@^3.0.2:
|
||||||
version "3.1.0"
|
version "3.1.0"
|
||||||
resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-3.1.0.tgz#475d698a5e49ff5e53d14e3e732429dc8bf4cf47"
|
resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-3.1.0.tgz#475d698a5e49ff5e53d14e3e732429dc8bf4cf47"
|
||||||
|
@ -3997,12 +3985,6 @@ nth-check@~1.0.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
boolbase "~1.0.0"
|
boolbase "~1.0.0"
|
||||||
|
|
||||||
nuclear-js@^1.4.0:
|
|
||||||
version "1.4.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/nuclear-js/-/nuclear-js-1.4.0.tgz#6c9c001b0673f0ae9d8f8b188c4da04ed693a7be"
|
|
||||||
dependencies:
|
|
||||||
immutable "^3.8.1"
|
|
||||||
|
|
||||||
number-is-nan@^1.0.0:
|
number-is-nan@^1.0.0:
|
||||||
version "1.0.1"
|
version "1.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/number-is-nan/-/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d"
|
resolved "https://registry.yarnpkg.com/number-is-nan/-/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d"
|
||||||
|
@ -4027,7 +4009,7 @@ object-assign@^3.0.0:
|
||||||
version "3.0.0"
|
version "3.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-3.0.0.tgz#9bedd5ca0897949bca47e7ff408062d549f587f2"
|
resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-3.0.0.tgz#9bedd5ca0897949bca47e7ff408062d549f587f2"
|
||||||
|
|
||||||
object-assign@^4.0.0, object-assign@^4.0.1, object-assign@^4.1.0, object-assign@^4.1.1:
|
object-assign@^4.0.0, object-assign@^4.0.1, object-assign@^4.1.0:
|
||||||
version "4.1.1"
|
version "4.1.1"
|
||||||
resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863"
|
resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863"
|
||||||
|
|
||||||
|
@ -5535,9 +5517,9 @@ ternary-stream@^2.0.1:
|
||||||
merge-stream "^1.0.0"
|
merge-stream "^1.0.0"
|
||||||
through2 "^2.0.1"
|
through2 "^2.0.1"
|
||||||
|
|
||||||
"test-fixture@github:polymerelements/test-fixture":
|
test-fixture@PolymerElements/test-fixture:
|
||||||
version "2.0.1"
|
version "2.0.1"
|
||||||
resolved "https://codeload.github.com/polymerelements/test-fixture/tar.gz/f72f0ff4e83b83b0157afed664f560da91d20f31"
|
resolved "https://codeload.github.com/PolymerElements/test-fixture/tar.gz/f72f0ff4e83b83b0157afed664f560da91d20f31"
|
||||||
|
|
||||||
test-value@^1.1.0:
|
test-value@^1.1.0:
|
||||||
version "1.1.0"
|
version "1.1.0"
|
||||||
|
|
Loading…
Reference in New Issue