mirror of
https://github.com/home-assistant/core.git
synced 2025-07-19 11:17:21 +00:00
History: Add support to fetch specific days
This commit is contained in:
parent
d34ecd1c25
commit
f26ac070d5
@ -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
@ -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
|
||||||
},
|
},
|
||||||
|
@ -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];
|
||||||
},
|
},
|
||||||
|
@ -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
|
@ -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) {
|
||||||
|
@ -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]]">
|
||||||
|
@ -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())
|
||||||
|
Loading…
x
Reference in New Issue
Block a user