History: Add support to fetch specific days

This commit is contained in:
Paulus Schoutsen 2015-06-15 22:40:57 -07:00
parent d34ecd1c25
commit f26ac070d5
9 changed files with 503 additions and 225 deletions

View File

@ -1,2 +1,2 @@
""" DO NOT MODIFY. Auto-generated by build_frontend script """ """ DO NOT MODIFY. Auto-generated by build_frontend script """
VERSION = "9472014df7b663c70bf33bb456bd8245" VERSION = "a6643dc82e02ec14b6c1b662f1aab661"

File diff suppressed because one or more lines are too long

View File

@ -1,5 +1,14 @@
<link rel="import" href="../bower_components/polymer/polymer.html"> <link rel="import" href="../bower_components/polymer/polymer.html">
<dom-module is='state-history-chart-timeline'>
<style>
:host {
display: block;
}
</style>
<template></template>
</dom-module>
<script> <script>
(function() { (function() {
Polymer({ Polymer({
@ -18,10 +27,6 @@
}, },
}, },
created: function() {
this.style.display = 'block';
},
attached: function() { attached: function() {
this.isAttached = true; this.isAttached = true;
}, },
@ -45,7 +50,7 @@
if (!stateHistory || stateHistory.length === 0) { if (!stateHistory || stateHistory.length === 0) {
return; return;
} }
// debugger;
var chart = new google.visualization.Timeline(this); var chart = new google.visualization.Timeline(this);
var dataTable = new google.visualization.DataTable(); var dataTable = new google.visualization.DataTable();
@ -59,15 +64,6 @@
dataTable.addRow([entityDisplay, stateStr, start, end]); dataTable.addRow([entityDisplay, stateStr, start, end]);
}; };
// people can pass in history of 1 entityId or a collection.
// var stateHistory;
// if (_.isArray(data[0])) {
// stateHistory = data;
// } else {
// stateHistory = [data];
// isSingleDevice = true;
// }
var numTimelines = 0; var numTimelines = 0;
// stateHistory is a list of lists of sorted state objects // stateHistory is a list of lists of sorted state objects
stateHistory.forEach(function(stateInfo) { stateHistory.forEach(function(stateInfo) {
@ -90,17 +86,17 @@
} }
}); });
addRow(entityDisplay, prevState, prevLastChanged, new Date()); // end time is start time + 1 day
var endTime = new Date(stateInfo[0].lastChangedAsDate);
endTime.setDate(endTime.getDate()+1);
addRow(entityDisplay, prevState, prevLastChanged, endTime);
numTimelines++; numTimelines++;
}.bind(this)); }.bind(this));
chart.draw(dataTable, { chart.draw(dataTable, {
height: 55 + numTimelines * 42, height: 55 + numTimelines * 42,
// interactive properties require CSS, the JS api puts it on the document
// instead of inside our Shadow DOM.
enableInteractivity: false,
timeline: { timeline: {
showRowLabels: stateHistory.length > 1 showRowLabels: stateHistory.length > 1
}, },

View File

@ -16,29 +16,34 @@
text-align: center; text-align: center;
padding: 8px; padding: 8px;
} }
.loading {
height: 0px;
overflow: hidden;
}
</style> </style>
<template> <template>
<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'> <div hidden$="{{!isLoading}}" class='loading-container'>
<loading-box>Loading history data</loading-box> <loading-box>Updating history data</loading-box>
</div> </div>
<template is='dom-if' if='[[!isLoading]]'> <div class$='[[computeContentClasses(isLoading)]]'>
<template is='dom-if' if='[[groupedStateHistory.timeline]]'> <template is='dom-if' if='[[computeIsEmpty(stateHistory)]]'>
<state-history-chart-timeline data='[[groupedStateHistory.timeline]]' No state history found.
is-single-device='[[isSingleDevice]]'>
</state-history-chart-timeline>
</template> </template>
<template is='dom-if' if='[[groupedStateHistory.line]]'> <state-history-chart-timeline data='[[groupedStateHistory.timeline]]'
<template is='dom-repeat' items='[[groupedStateHistory.line]]'> is-single-device='[[isSingleDevice]]'>
<state-history-chart-line unit='[[extractUnit(item)]]' </state-history-chart-timeline>
data='[[extractData(item)]]' is-single-device='[[isSingleDevice]]'>
</state-history-chart-line> <template is='dom-repeat' items='[[groupedStateHistory.line]]'>
</template> <state-history-chart-line unit='[[extractUnit(item)]]'
data='[[extractData(item)]]' is-single-device='[[isSingleDevice]]'>
</state-history-chart-line>
</template> </template>
</template> </div>
</template> </template>
</dom-module> </dom-module>
@ -69,7 +74,7 @@
groupedStateHistory: { groupedStateHistory: {
type: Object, type: Object,
computed: 'computeGroupedStateHistory(stateHistory)', computed: 'computeGroupedStateHistory(isLoading, stateHistory)',
}, },
isSingleDevice: { isSingleDevice: {
@ -82,11 +87,11 @@
return stateHistory && stateHistory.length == 1; return stateHistory && stateHistory.length == 1;
}, },
computeGroupedStateHistory: function(stateHistory) { computeGroupedStateHistory: function(isLoading, stateHistory) {
var lineChartDevices = {}; var lineChartDevices = {};
var timelineDevices = []; var timelineDevices = [];
if (!stateHistory) { if (isLoading || !stateHistory) {
return {line: unitStates, timeline: timelineDevices}; return {line: unitStates, timeline: timelineDevices};
} }
@ -129,10 +134,18 @@
}); });
}, },
computeContentClasses: function(isLoading) {
return isLoading ? 'loading' : '';
},
computeIsLoading: function(isLoadingData, apiLoaded) { computeIsLoading: function(isLoadingData, apiLoaded) {
return isLoadingData || !apiLoaded; return isLoadingData || !apiLoaded;
}, },
computeIsEmpty: function(stateHistory) {
return stateHistory && stateHistory.length === 0;
},
extractUnit: function(arr) { extractUnit: function(arr) {
return arr[0]; return arr[0];
}, },

View File

@ -134,7 +134,7 @@
this.stateStoreChanged(); this.stateStoreChanged();
this.stateHistoryStoreChanged(); this.stateHistoryStoreChanged();
if (this.hasHistoryComponent && stateHistoryStore.isStale(entityId)) { if (this.hasHistoryComponent && stateHistoryStore.shouldFetchEntity(entityId)) {
this.isLoadingHistoryData = true; this.isLoadingHistoryData = true;
stateHistoryActions.fetch(entityId); stateHistoryActions.fetch(entityId);
} }

@ -1 +1 @@
Subproject commit cec99925399b1c8e8cea15bbbba5d873522ccbd6 Subproject commit 48cbca40dda47dba296e764fc4c9157652dbbbf1

View File

@ -1,5 +1,6 @@
<link rel="import" href="../bower_components/polymer/polymer.html"> <link rel="import" href="../bower_components/polymer/polymer.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/paper-input/paper-input.html">
<link rel="import" href="./partial-base.html"> <link rel="import" href="./partial-base.html">
@ -14,6 +15,14 @@
.content.wide { .content.wide {
padding: 8px; padding: 8px;
} }
paper-input {
max-width: 200px;
}
.narrow paper-input {
margin-left: 8px;
}
</style> </style>
<template> <template>
<partial-base narrow="[[narrow]]"> <partial-base narrow="[[narrow]]">
@ -23,6 +32,9 @@
on-click="handleRefreshClick"></paper-icon-button> on-click="handleRefreshClick"></paper-icon-button>
<div class$="[[computeContentClasses(narrow)]]"> <div class$="[[computeContentClasses(narrow)]]">
<paper-input label='Showing entries for' on-click='handleShowDatePicker'
value='[[computeDateCaption(selectedDate)]]'></paper-input>
<state-history-charts state-history="[[stateHistory]]" <state-history-charts state-history="[[stateHistory]]"
is-loading-data="[[isLoadingData]]"></state-history-charts> is-loading-data="[[isLoadingData]]"></state-history-charts>
</div> </div>
@ -32,6 +44,12 @@
<script> <script>
(function() { (function() {
var stateHistoryActions = window.hass.stateHistoryActions; var stateHistoryActions = window.hass.stateHistoryActions;
var stateHistoryStore = window.hass.stateHistoryStore;
var uiActions = window.hass.uiActions;
function date_to_str(date) {
return date.getFullYear() + '-' + (date.getMonth()+1) + '-' + date.getDate();
}
Polymer({ Polymer({
is: 'partial-history', is: 'partial-history',
@ -51,23 +69,42 @@
type: Boolean, type: Boolean,
value: false, value: false,
}, },
selectedDate: {
type: String,
value: null,
observer: 'fetchIfNeeded',
},
}, },
stateHistoryStoreChanged: function(stateHistoryStore) { stateHistoryStoreChanged: function() {
if (stateHistoryStore.isStale()) { this.isLoadingData = this.fetchIfNeeded();
this.isLoadingData = true; this.stateHistory = this.isLoadingData ?
stateHistoryActions.fetchAll(); [] : stateHistoryStore.all(this.selectedDate);
} },
else {
this.isLoadingData = false;
}
this.stateHistory = stateHistoryStore.all; computeDateCaption: function(selectedDate) {
return selectedDate || 'today';
},
fetchIfNeeded: function() {
if (stateHistoryStore.shouldFetch(this.selectedDate)) {
this.isLoadingData = true;
stateHistoryActions.fetchAll(this.selectedDate);
return true;
}
return false;
}, },
handleRefreshClick: function() { handleRefreshClick: function() {
this.isLoadingData = true; this.isLoadingData = true;
stateHistoryActions.fetchAll(); stateHistoryActions.fetchAll(this.selectedDate);
},
handleShowDatePicker: function() {
uiActions.showDatePicker(function(selectedDate) {
this.selectedDate = date_to_str(selectedDate);
}.bind(this), this.selectedDate);
}, },
computeContentClasses: function(narrow) { computeContentClasses: function(narrow) {

View File

@ -13,6 +13,10 @@
.selected-date-container { .selected-date-container {
padding: 0 16px; padding: 0 16px;
} }
paper-input {
max-width: 200px;
}
</style> </style>
<template> <template>
<partial-base narrow="[[narrow]]"> <partial-base narrow="[[narrow]]">

View File

@ -9,12 +9,16 @@ from datetime import timedelta
from itertools import groupby from itertools import groupby
from collections import defaultdict from collections import defaultdict
import homeassistant.util.dt as date_util import homeassistant.util.dt as dt_util
import homeassistant.components.recorder as recorder import homeassistant.components.recorder as recorder
from homeassistant.const import HTTP_BAD_REQUEST
DOMAIN = 'history' DOMAIN = 'history'
DEPENDENCIES = ['recorder', 'http'] DEPENDENCIES = ['recorder', 'http']
URL_HISTORY_PERIOD = re.compile(
r'/api/history/period(?:/(?P<date>\d{4}-\d{1,2}-\d{1,2})|)')
def last_5_states(entity_id): def last_5_states(entity_id):
""" Return the last 5 states for entity_id. """ """ Return the last 5 states for entity_id. """
@ -111,8 +115,7 @@ def setup(hass, config):
r'recent_states'), r'recent_states'),
_api_last_5_states) _api_last_5_states)
hass.http.register_path( hass.http.register_path('GET', URL_HISTORY_PERIOD, _api_history_period)
'GET', re.compile(r'/api/history/period'), _api_history_period)
return True return True
@ -128,10 +131,25 @@ def _api_last_5_states(handler, path_match, data):
def _api_history_period(handler, path_match, data): def _api_history_period(handler, path_match, data):
""" Return history over a period of time. """ """ Return history over a period of time. """
# 1 day for now.. date_str = path_match.group('date')
start_time = date_util.utcnow() - timedelta(seconds=86400) one_day = timedelta(seconds=86400)
if date_str:
start_date = dt_util.date_str_to_date(date_str)
if start_date is None:
handler.write_json_message("Error parsing JSON", HTTP_BAD_REQUEST)
return
start_time = dt_util.as_utc(dt_util.start_of_local_day(start_date))
else:
start_time = dt_util.utcnow() - one_day
end_time = start_time + one_day
print("Fetchign", start_time, end_time)
entity_id = data.get('filter_entity_id') entity_id = data.get('filter_entity_id')
handler.write_json( handler.write_json(
state_changes_during_period(start_time, entity_id=entity_id).values()) state_changes_during_period(start_time, end_time, entity_id).values())