Add Initial Mailbox panel and sensor (#319)

* Add Initial Mailbox panel

* Use sha rather than index to lookup mp3 file

* Popups for audio and delete-confirmation.  Don't load all MP3s on page load

* Show duration of message

* Reload messages on change

* eslint fixes

* Wrap transcription text.  Use 2nd-row per entry for transcription.  Table consumes 100% of window height

* Update to support mailbox component

* Use mailbox component state instead of sensor state.  Fix a bug in delete

* Display mailbox entity

* Use events to trigger mailbox updates

* Support multiple mailbox platforms

* Use REST API for mailbox deletion

* Fix linting issues

* Make dialogs reactive

* Replace vaadin-grid with paper-items

* 1st attempt at truncating lines to two columns

* Apply requested changes from review

* Clean up + Lint
This commit is contained in:
PhracturedBlue 2017-08-08 20:58:37 -07:00 committed by Paulus Schoutsen
parent 2e1d3e1fa7
commit 58e2781412
3 changed files with 264 additions and 0 deletions

View File

@ -0,0 +1,260 @@
<link rel='import' href='../../bower_components/polymer/polymer.html'>
<link rel='import' href='../../bower_components/paper-button/paper-button.html'>
<link rel='import' href='../../bower_components/paper-dialog/paper-dialog.html'>
<link rel='import' href='../../bower_components/paper-input/paper-textarea.html'>
<link rel='import' href='../../bower_components/paper-item/paper-item.html'>
<link rel='import' href='../../bower_components/paper-item/paper-item-body.html'>
<link rel='import' href='../../bower_components/paper-card/paper-card.html'>
<link rel='import' href='../../bower_components/app-layout/app-header-layout/app-header-layout.html'>
<link rel='import' href='../../bower_components/app-layout/app-header/app-header.html'>
<link rel='import' href='../../bower_components/app-layout/app-toolbar/app-toolbar.html'>
<link rel="import" href="../../bower_components/app-storage/app-localstorage/app-localstorage-document.html">
<link rel="import" href="../../bower_components/vaadin-combo-box/vaadin-combo-box.html">
<link rel='import' href='../../src/components/ha-menu-button.html'>
<link rel='import' href='../../src/resources/ha-style.html'>
<dom-module id='ha-panel-mailbox'>
<template>
<style include='ha-style'>
:host {
-ms-user-select: initial;
-webkit-user-select: initial;
-moz-user-select: initial;
}
.content {
padding: 16px;
max-width: 600px;
margin: 0 auto;
}
paper-card {
display: block;
}
paper-item {
cursor: pointer;
}
.header {
@apply(--paper-font-title);
}
.row {
display: flex;
justify-content: space-between;
}
paper-dialog {
border-radius: 2px;
}
#mp3dialog paper-icon-button {
float: right;
}
@media all and (max-width: 450px) {
paper-dialog {
margin: 0;
width: 100%;
max-height: calc(100% - 64px);
position: fixed !important;
bottom: 0px;
left: 0px;
right: 0px;
overflow: scroll;
border-bottom-left-radius: 0px;
border-bottom-right-radius: 0px;
}
.content {
width: auto;
padding: 0;
}
}
.tip {
color: var(--secondary-text-color);
font-size: 14px;
}
.date {
color: var(--primary-text-color);
}
</style>
<app-header-layout has-scrolling-region>
<app-header slot="header" fixed>
<app-toolbar>
<ha-menu-button narrow='[[narrow]]' show-menu='[[showMenu]]'></ha-menu-button>
<div main-title>Mailbox</div>
</app-toolbar>
</app-header>
<div class='content'>
<paper-card>
<template is='dom-if' if='[[!_messages.length]]'>
<div class='card-content'>
You do not have any messages.
</div>
</template>
<template is='dom-repeat' items='[[_messages]]'>
<paper-item on-tap='openMP3Dialog'>
<paper-item-body style="width:100%" two-line>
<div class="row">
<div>[[item.caller]]</div>
<div class="tip">[[item.duration]] secs</div>
</div>
<div secondary>
<span class="date">[[item.timestamp]]</span> - [[item.message]]
</div>
</paper-item-body>
</paper-item>
</template>
</paper-card>
</div>
</app-header-layout>
<paper-dialog with-backdrop id="mp3dialog" on-iron-overlay-closed="_mp3Closed">
<h2>
Message Playback
<paper-icon-button
on-tap='openDeleteDialog'
icon='mdi:delete'
></paper-icon-button>
</h2>
<div id="transcribe">text</div>
<div>
<audio id="mp3" preload="none" controls> <source id="mp3src" src="" type="audio/mpeg"></audio>
</div>
</paper-dialog>
<paper-dialog with-backdrop id="confirmdel">
<h2>Delete Message</h2>
<p>Are you sure you want to delete this message?</p>
<div class="buttons">
<paper-button dialog-dismiss>Decline</paper-button>
<paper-button dialog-confirm autofocus on-tap="deleteSelected">Accept</paper-button>
</div>
</paper-dialog>
</template>
</dom-module>
<script>
Polymer({
is: 'ha-panel-mailbox',
properties: {
hass: {
type: Object,
},
narrow: {
type: Boolean,
value: false,
},
showMenu: {
type: Boolean,
value: false,
},
platforms: {
type: Array,
},
_messages: {
type: Array,
},
currentMessage: {
type: Object,
},
},
attached: function () {
this.hassChanged = this.hassChanged.bind(this);
this.hass.connection.subscribeEvents(this.hassChanged, 'mailbox_updated')
.then(function (unsub) { this._unsubEvents = unsub; }.bind(this));
this.computePlatforms().then(function (platforms) {
this.platforms = platforms;
this.hassChanged();
}.bind(this));
},
detached: function () {
if (this._unsubEvents) this._unsubEvents();
},
hassChanged: function () {
if (!this._messages) {
this._messages = [];
}
this.getMessages().then(function (items) {
this._messages = items;
}.bind(this));
},
openMP3Dialog: function (event) {
var platform = event.model.item.platform;
this.currentMessage = event.model.item;
this.$.mp3dialog.open();
this.$.mp3src.src = '/api/mailbox/media/' + platform + '/' + event.model.item.sha;
this.$.transcribe.innerText = event.model.item.message;
this.$.mp3.load();
this.$.mp3.play();
},
_mp3Closed: function () {
this.$.mp3.pause();
},
openDeleteDialog: function () {
this.$.confirmdel.open();
},
deleteSelected: function () {
var msg = this.currentMessage;
this.hass.callApi('DELETE', 'mailbox/delete/' + msg.platform + '/' + msg.sha);
this.$.mp3dialog.close();
},
getMessages: function () {
const items = this.platforms.map(function (platform) {
return this.hass.callApi('GET', 'mailbox/messages/' + platform).then(function (values) {
var platformItems = [];
var arrayLength = values.length;
for (var i = 0; i < arrayLength; i++) {
var datetime = window.hassUtil.formatDateTime(new Date(values[i].info.origtime * 1000));
platformItems.push({
timestamp: datetime,
caller: values[i].info.callerid,
message: values[i].text,
sha: values[i].sha,
duration: values[i].info.duration,
platform: platform
});
}
return platformItems;
});
}.bind(this));
return Promise.all(items).then(function (platformItems) {
var arrayLength = items.length;
var final = [];
for (var i = 0; i < arrayLength; i++) {
final = final.concat(platformItems[i]);
}
final.sort(function (a, b) {
return new Date(b.timestamp) - new Date(a.timestamp);
});
return final;
});
},
computePlatforms: function () {
return this.hass.callApi('GET', 'mailbox/platforms');
},
});
</script>

View File

@ -106,6 +106,7 @@
alarm_control_panel: 3, alarm_control_panel: 3,
sensor: 5, sensor: 5,
binary_sensor: 6, binary_sensor: 6,
mailbox: 7,
}; };
function getPriority(domain) { function getPriority(domain) {

View File

@ -302,6 +302,9 @@ window.hassUtil.domainIcon = function (domain, state) {
case 'lock': case 'lock':
return state && state === 'unlocked' ? 'mdi:lock-open' : 'mdi:lock'; return state && state === 'unlocked' ? 'mdi:lock-open' : 'mdi:lock';
case 'mailbox':
return 'mdi:mailbox';
case 'media_player': case 'media_player':
return state && state !== 'off' && state !== 'idle' ? return state && state !== 'off' && state !== 'idle' ?
'mdi:cast-connected' : 'mdi:cast'; 'mdi:cast-connected' : 'mdi:cast';