|
|
|
'use strict';
|
|
|
|
import React from 'react';
|
|
|
|
import { connect } from 'react-redux';
|
|
|
|
import _ from 'lodash';
|
|
|
|
import SensorWidget from '../components/sensor-widget';
|
|
|
|
import { fetchSensorData } from '../actions/action-creators';
|
|
|
|
import { round } from '../utils/format';
|
|
|
|
|
|
|
|
const getTime = function (str) {
|
|
|
|
let date = new Date(str);
|
|
|
|
return Math.floor(date.getTime() / 1000);
|
|
|
|
};
|
|
|
|
|
|
|
|
const sensorProps = React.PropTypes.shape({
|
|
|
|
fetched: React.PropTypes.bool,
|
|
|
|
fetching: React.PropTypes.bool,
|
|
|
|
data: React.PropTypes.array
|
|
|
|
});
|
|
|
|
|
|
|
|
var Home = React.createClass({
|
|
|
|
displayName: 'Home',
|
|
|
|
|
|
|
|
propTypes: {
|
|
|
|
_requestSensorData: React.PropTypes.func,
|
|
|
|
sensorUv: sensorProps,
|
|
|
|
sensorLuminosity: sensorProps,
|
|
|
|
sensorPressure: sensorProps,
|
|
|
|
sensorHumidity: sensorProps,
|
|
|
|
sensorTemperature: sensorProps,
|
|
|
|
sensorPm10: sensorProps,
|
|
|
|
sensorPm25: sensorProps,
|
|
|
|
sensorWindDir: sensorProps,
|
|
|
|
sensorWindSpeed: sensorProps,
|
|
|
|
sensorRain: sensorProps,
|
|
|
|
},
|
|
|
|
|
|
|
|
// Having measurements every minute is too much. Group them.
|
|
|
|
// Max seconds between reading for them to be considered part
|
|
|
|
// of the same group.
|
|
|
|
_mTimeThreshold: 120,
|
|
|
|
_mGroupSize: 6, // Equal to 10 measurements per hour.
|
|
|
|
|
|
|
|
_fetchInterval: null,
|
|
|
|
// In seconds.
|
|
|
|
_fetchRate: 300, // 5 min
|
|
|
|
|
|
|
|
prepareData: function (rawData) {
|
|
|
|
var data = null;
|
|
|
|
|
|
|
|
if (rawData && rawData.length) {
|
|
|
|
data = [];
|
|
|
|
rawData[0].value = +rawData[0].value;
|
|
|
|
let bucket = [rawData[0]];
|
|
|
|
for (var i = 1; i < rawData.length; i++) {
|
|
|
|
rawData[i].value = +rawData[i].value;
|
|
|
|
let prevTime = getTime(rawData[i - 1].createdAt);
|
|
|
|
let currTime = getTime(rawData[i].createdAt);
|
|
|
|
// Having measurements every minute is too much. Group them.
|
|
|
|
// To make sure that the grouped measurements are all around the same
|
|
|
|
// time there can't be more than X seconds difference between them.
|
|
|
|
if (currTime - prevTime > this._mTimeThreshold || bucket.length === this._mGroupSize) {
|
|
|
|
let f = {
|
|
|
|
createdAt: _.last(bucket).createdAt,
|
|
|
|
value: +round(_.meanBy(bucket, 'value'))
|
|
|
|
};
|
|
|
|
data.push(f);
|
|
|
|
bucket = [];
|
|
|
|
}
|
|
|
|
bucket.push(rawData[i]);
|
|
|
|
}
|
|
|
|
// After the loop finished there may still be data to process.
|
|
|
|
// if bucket.length < this._mGroupSize for example.
|
|
|
|
let f = {
|
|
|
|
createdAt: _.last(bucket).createdAt,
|
|
|
|
value: +round(_.meanBy(bucket, 'value'))
|
|
|
|
};
|
|
|
|
data.push(f);
|
|
|
|
}
|
|
|
|
|
|
|
|
let startToday = new Date();
|
|
|
|
startToday.setHours(0);
|
|
|
|
startToday.setMinutes(0);
|
|
|
|
startToday.setSeconds(0);
|
|
|
|
|
|
|
|
startToday = Math.floor(startToday.getTime() / 1000);
|
|
|
|
let startYesterday = startToday - (60 * 60 * 24);
|
|
|
|
|
|
|
|
let dataAll = [];
|
|
|
|
let dataToday = [];
|
|
|
|
let dataYesterday = [];
|
|
|
|
|
|
|
|
_.forEach(data, o => {
|
|
|
|
let date = new Date(o.createdAt);
|
|
|
|
let time = Math.floor(date.getTime() / 1000);
|
|
|
|
dataAll.push({
|
|
|
|
timestep: date,
|
|
|
|
value: +o.value
|
|
|
|
});
|
|
|
|
if (time >= startToday) {
|
|
|
|
dataToday.push({
|
|
|
|
timestep: date,
|
|
|
|
value: +o.value
|
|
|
|
});
|
|
|
|
}
|
|
|
|
if (time < startToday && time >= startYesterday) {
|
|
|
|
dataYesterday.push({
|
|
|
|
timestep: date,
|
|
|
|
value: +o.value
|
|
|
|
});
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
let avgs = {
|
|
|
|
today: _.meanBy(dataToday, 'value'),
|
|
|
|
yesterday: _.meanBy(dataYesterday, 'value')
|
|
|
|
};
|
|
|
|
|
|
|
|
let sums = {
|
|
|
|
today: _.sumBy(dataToday, 'value'),
|
|
|
|
yesterday: _.sumBy(dataYesterday, 'value')
|
|
|
|
};
|
|
|
|
|
|
|
|
let last = _.last(dataAll) || null;
|
|
|
|
|
|
|
|
return {
|
|
|
|
data: dataAll,
|
|
|
|
last,
|
|
|
|
avgs,
|
|
|
|
sums,
|
|
|
|
};
|
|
|
|
},
|
|
|
|
|
|
|
|
fetchData: function () {
|
|
|
|
let daysAgo3 = (new Date()).getTime() - (60 * 60 * 24 * 3 * 1000);
|
|
|
|
daysAgo3 = new Date(daysAgo3).toISOString();
|
|
|
|
this.props._requestSensorData('temperature', daysAgo3);
|
|
|
|
this.props._requestSensorData('humidity', daysAgo3);
|
|
|
|
this.props._requestSensorData('uv', daysAgo3);
|
|
|
|
this.props._requestSensorData('luminosity', daysAgo3);
|
|
|
|
this.props._requestSensorData('pressure', daysAgo3);
|
|
|
|
this.props._requestSensorData('pm10', daysAgo3);
|
|
|
|
this.props._requestSensorData('pm25', daysAgo3);
|
|
|
|
this.props._requestSensorData('windDir', daysAgo3);
|
|
|
|
this.props._requestSensorData('windSpeed', daysAgo3);
|
|
|
|
this.props._requestSensorData('rain', daysAgo3);
|
|
|
|
},
|
|
|
|
|
|
|
|
componentDidMount: function () {
|
|
|
|
this.fetchData();
|
|
|
|
this._fetchInterval = setInterval(() => {
|
|
|
|
this.fetchData();
|
|
|
|
}, this._fetchRate * 1000);
|
|
|
|
},
|
|
|
|
|
|
|
|
componentWillUnmount: function () {
|
|
|
|
if (this._fetchInterval) {
|
|
|
|
clearInterval(this._fetchInterval);
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
render: function () {
|
|
|
|
let sensorTemperatureData = this.prepareData(this.props.sensorTemperature.data);
|
|
|
|
let sensorHumidityData = this.prepareData(this.props.sensorHumidity.data);
|
|
|
|
let sensorUvData = this.prepareData(this.props.sensorUv.data);
|
|
|
|
let sensorLuminosityData = this.prepareData(this.props.sensorLuminosity.data);
|
|
|
|
let sensorPressureData = this.prepareData(this.props.sensorPressure.data);
|
|
|
|
let sensorPm10Data = this.prepareData(this.props.sensorPm10.data);
|
|
|
|
let sensorPm25Data = this.prepareData(this.props.sensorPm25.data);
|
|
|
|
let sensorWindDirData = this.prepareData(this.props.sensorWindDir.data);
|
|
|
|
let sensorWindSpeedData = this.prepareData(this.props.sensorWindSpeed.data);
|
|
|
|
let sensorRainData = this.prepareData(this.props.sensorRain.data);
|
|
|
|
|
|
|
|
return (
|
|
|
|
<section className='page'>
|
|
|
|
<header className='page__header'>
|
|
|
|
<div className='inner'>
|
|
|
|
<div className='page__headline'>
|
|
|
|
<h1 className='page__title'>Sense Dashboard</h1>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</header>
|
|
|
|
<div className='page__body'>
|
|
|
|
|
|
|
|
<section className='page__content'>
|
|
|
|
<div className='inner'>
|
|
|
|
|
|
|
|
<SensorWidget
|
|
|
|
className='card--temp'
|
|
|
|
fetching={this.props.sensorTemperature.fetching}
|
|
|
|
fetched={this.props.sensorTemperature.fetched}
|
|
|
|
title='Temperatur'
|
|
|
|
lastReading={sensorTemperatureData.last}
|
|
|
|
avgs={sensorTemperatureData.avgs}
|
|
|
|
plotData={sensorTemperatureData.data}
|
|
|
|
axisLineMax={40}
|
|
|
|
axisLineVal={20}
|
|
|
|
axisLineMin={0}
|
|
|
|
unit=' ºC'
|
|
|
|
/>
|
|
|
|
|
|
|
|
<SensorWidget
|
|
|
|
className='card--hum'
|
|
|
|
fetching={this.props.sensorPressure.fetching}
|
|
|
|
fetched={this.props.sensorPressure.fetched}
|
|
|
|
title='Luftdruck'
|
|
|
|
lastReading={sensorPressureData.last}
|
|
|
|
avgs={sensorPressureData.avgs}
|
|
|
|
plotData={sensorPressureData.data}
|
|
|
|
axisLineMax={1030}
|
|
|
|
axisLineVal={1000}
|
|
|
|
axisLineMin={970}
|
|
|
|
unit=' hPa'
|
|
|
|
/>
|
|
|
|
|
|
|
|
<SensorWidget
|
|
|
|
className='card--press'
|
|
|
|
fetching={this.props.sensorHumidity.fetching}
|
|
|
|
fetched={this.props.sensorHumidity.fetched}
|
|
|
|
title='rel. Luftfeuchte'
|
|
|
|
lastReading={sensorHumidityData.last}
|
|
|
|
avgs={sensorHumidityData.avgs}
|
|
|
|
plotData={sensorHumidityData.data}
|
|
|
|
axisLineMax={100}
|
|
|
|
axisLineVal={50}
|
|
|
|
axisLineMin={10}
|
|
|
|
unit=' %'
|
|
|
|
/>
|
|
|
|
|
|
|
|
<SensorWidget
|
|
|
|
className='card--press'
|
|
|
|
fetching={this.props.sensorRain.fetching}
|
|
|
|
fetched={this.props.sensorRain.fetched}
|
|
|
|
title='Niederschlag'
|
|
|
|
lastReading={sensorRainData.last}
|
|
|
|
sums={sensorRainData.sums}
|
|
|
|
plotData={sensorRainData.data}
|
|
|
|
axisLineMax={1}
|
|
|
|
axisLineVal={0.28}
|
|
|
|
axisLineMin={0}
|
|
|
|
unit=' L/m² / 5 Min'
|
|
|
|
/>
|
|
|
|
|
|
|
|
<SensorWidget
|
|
|
|
className='card--uv'
|
|
|
|
fetching={this.props.sensorWindDir.fetching}
|
|
|
|
fetched={this.props.sensorWindDir.fetched}
|
|
|
|
title='Windrichtung'
|
|
|
|
lastReading={sensorWindDirData.last}
|
|
|
|
avgs={sensorWindDirData.avgs}
|
|
|
|
plotData={sensorWindDirData.data}
|
|
|
|
axisLineMax={360}
|
|
|
|
axisLineVal={180}
|
|
|
|
axisLineMin={0}
|
|
|
|
unit=' °'
|
|
|
|
/>
|
|
|
|
|
|
|
|
<SensorWidget
|
|
|
|
className='card--uv'
|
|
|
|
fetching={this.props.sensorWindSpeed.fetching}
|
|
|
|
fetched={this.props.sensorWindSpeed.fetched}
|
|
|
|
title='Windgeschwindigkeit'
|
|
|
|
lastReading={sensorWindSpeedData.last}
|
|
|
|
avgs={sensorWindSpeedData.avgs}
|
|
|
|
plotData={sensorWindSpeedData.data}
|
|
|
|
axisLineMax={20}
|
|
|
|
axisLineVal={10}
|
|
|
|
axisLineMin={0}
|
|
|
|
unit=' km/h'
|
|
|
|
/>
|
|
|
|
|
|
|
|
<SensorWidget
|
|
|
|
className='card--lux'
|
|
|
|
fetching={this.props.sensorUv.fetching}
|
|
|
|
fetched={this.props.sensorUv.fetched}
|
|
|
|
title='Uv Licht'
|
|
|
|
lastReading={sensorUvData.last}
|
|
|
|
avgs={sensorUvData.avgs}
|
|
|
|
plotData={sensorUvData.data}
|
|
|
|
axisLineMax={5000}
|
|
|
|
axisLineVal={250}
|
|
|
|
axisLineMin={0}
|
|
|
|
unit=' μW/cm²'
|
|
|
|
/>
|
|
|
|
|
|
|
|
<SensorWidget
|
|
|
|
className='card--lux'
|
|
|
|
fetching={this.props.sensorLuminosity.fetching}
|
|
|
|
fetched={this.props.sensorLuminosity.fetched}
|
|
|
|
title='Helligkeit'
|
|
|
|
lastReading={sensorLuminosityData.last}
|
|
|
|
avgs={sensorLuminosityData.avgs}
|
|
|
|
plotData={sensorLuminosityData.data}
|
|
|
|
axisLineMax={135000}
|
|
|
|
axisLineVal={50000}
|
|
|
|
axisLineMin={0}
|
|
|
|
unit=' lx'
|
|
|
|
/>
|
|
|
|
|
|
|
|
<SensorWidget
|
|
|
|
className='card--pm'
|
|
|
|
fetching={this.props.sensorPm10.fetching}
|
|
|
|
fetched={this.props.sensorPm10.fetched}
|
|
|
|
title='Feinstaub 10 μm'
|
|
|
|
lastReading={sensorPm10Data.last}
|
|
|
|
avgs={sensorPm10Data.avgs}
|
|
|
|
plotData={sensorPm10Data.data}
|
|
|
|
axisLineMax={25}
|
|
|
|
axisLineVal={10}
|
|
|
|
axisLineMin={0}
|
|
|
|
unit=' μg/m³'
|
|
|
|
/>
|
|
|
|
|
|
|
|
<SensorWidget
|
|
|
|
className='card--pm'
|
|
|
|
fetching={this.props.sensorPm25.fetching}
|
|
|
|
fetched={this.props.sensorPm25.fetched}
|
|
|
|
title='Feinstaub 2.5 μm'
|
|
|
|
lastReading={sensorPm25Data.last}
|
|
|
|
avgs={sensorPm25Data.avgs}
|
|
|
|
plotData={sensorPm25Data.data}
|
|
|
|
axisLineMax={25}
|
|
|
|
axisLineVal={10}
|
|
|
|
axisLineMin={0}
|
|
|
|
unit=' μg/m³'
|
|
|
|
/>
|
|
|
|
|
|
|
|
</div>
|
|
|
|
</section>
|
|
|
|
</div>
|
|
|
|
</section>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
// /////////////////////////////////////////////////////////////////// //
|
|
|
|
// Connect functions
|
|
|
|
|
|
|
|
function selector (state) {
|
|
|
|
return {
|
|
|
|
sensorUv: state.sensorUv,
|
|
|
|
sensorLuminosity: state.sensorLuminosity,
|
|
|
|
sensorPressure: state.sensorPressure,
|
|
|
|
sensorHumidity: state.sensorHumidity,
|
|
|
|
sensorTemperature: state.sensorTemperature,
|
|
|
|
sensorPm10: state.sensorPm10,
|
|
|
|
sensorPm25: state.sensorPm25,
|
|
|
|
sensorWindDir: state.sensorWindDir,
|
|
|
|
sensorWindSpeed: state.sensorWindSpeed,
|
|
|
|
sensorRain: state.sensorRain,
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
function dispatcher (dispatch) {
|
|
|
|
return {
|
|
|
|
_requestSensorData: (sensor, toDate) => dispatch(fetchSensorData(sensor, toDate))
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
module.exports = connect(selector, dispatcher)(Home);
|