@ -1,305 +1,168 @@
import React , { useState , useEffect } from 'react' ;
import { ComposedChart , Bar , Label , LineChart , ReferenceLine , Line , XAxis , YAxis , CartesianGrid , Tooltip , Legend , ResponsiveContainer } from 'recharts' ;
import Datetime from 'react-datetime' ;
import 'react-datetime/css/react-datetime.css' ;
import axios from 'axios' ;
import moment from 'moment' ;
import './App.css' ;
const durations = [
{ id : 0 , len : 'Day' , win : '10m' , full : '10 min' },
{ id : 1 , len : 'Day' , win : '1h' , full : '1 hour' },
{ id : 2 , len : 'Week' , win : '1h' , full : '1 hour' },
{ id : 3 , len : 'Week' , win : '1d' , full : '1 day' },
{ id : 4 , len : 'Month' , win : '1d' , full : '1 day' },
{ id : 5 , len : 'Month' , win : '7d' , full : '7 day' },
{ id : 6 , len : 'Year' , win : '1d' , full : '1 day' },
{ id : 7 , len : 'Year' , win : '30d' , full : '30 day' },
{ id : 0 , len : 'Day' , win : '10m' , full : '10 min' , delta : [ 1 , 'days' ] },
{ id : 1 , len : 'Day' , win : '1h' , full : '1 hour' , delta : [ 1 , 'days' ] },
{ id : 2 , len : 'Week' , win : '1h' , full : '1 hour' , delta : [ 7 , 'days' ] },
{ id : 3 , len : 'Week' , win : '1d' , full : '1 day' , delta : [ 7 , 'days' ] },
{ id : 4 , len : 'Month' , win : '1d' , full : '1 day' , delta : [ 1 , 'months' ] },
{ id : 5 , len : 'Month' , win : '7d' , full : '7 day' , delta : [ 1 , 'months' ] },
{ id : 6 , len : 'Year' , win : '1d' , full : '1 day' , delta : [ 1 , 'years' ] },
{ id : 7 , len : 'Year' , win : '30d' , full : '30 day' , delta : [ 1 , 'years' ] },
] ;
function Graphs ( { data , loading } ) {
function useSensor ( measurement , name , end , duration ) {
if ( ! data ) {
return (
< >
< h2 > Water Usage < / h 2 >
< p > Loading ... < / p >
< / >
) ;
}
return (
< div className = 'container' >
< h2 > Outside Temperature { loading ? 'Loading...' : '' } < / h 2 >
{ data . Outside ?
< ResponsiveContainer width = '100%' height = { 300 } >
< LineChart syncId = { 1 } data = { data . Outside } margin = { { top : 25 , left : 0 , right : 30 , bottom : 0 } } >
< XAxis
dataKey = 'time'
minTickGap = { 10 }
tickFormatter = { timeStr => moment ( timeStr ) . format ( 'HH:mm' ) }
/ >
< YAxis
domain = { [ - 40 , 40 ] }
/ >
< CartesianGrid strokeDasharray = '3 3' / >
< Tooltip
formatter = { v => v . toFixed ( 1 ) + ' °C' }
labelFormatter = { timeStr => moment ( timeStr ) . format ( 'ddd MMM DD h:mm A' ) }
separator = ': '
/ >
< ReferenceLine y = { 0 } stroke = 'blue' >
< Label value = 'Freezing' offset = { 7 } position = 'bottom' / >
< / R e f e r e n c e L i n e >
< ReferenceLine x = { moment ( ) . startOf ( 'day' ) . toISOString ( ) . replace ( '.000' , '' ) } stroke = 'blue' >
< Label value = 'Midnight' offset = { 7 } position = 'top' / >
< / R e f e r e n c e L i n e >
< Line
type = 'monotone'
dataKey = 'temperature_C'
name = 'Temperature'
stroke = 'black'
strokeWidth = { 2 }
dot = { false }
isAnimationActive = { false }
/ >
< / L i n e C h a r t >
< / R e s p o n s i v e C o n t a i n e r >
:
< p > Loading ... < / p >
}
< h2 > Bedroom Temperature { loading ? 'Loading...' : '' } < / h 2 >
{ data . Bedroom ?
< ResponsiveContainer width = '100%' height = { 300 } >
< LineChart syncId = { 1 } data = { data . Bedroom } margin = { { top : 5 , left : 0 , right : 30 , bottom : 0 } } >
< XAxis
dataKey = 'time'
minTickGap = { 10 }
tickFormatter = { timeStr => moment ( timeStr ) . format ( 'HH:mm' ) }
/ >
< YAxis
domain = { [ 15 , 25 ] }
/ >
< CartesianGrid strokeDasharray = '3 3' / >
< Tooltip
formatter = { v => v . toFixed ( 1 ) + ' °C' }
labelFormatter = { timeStr => moment ( timeStr ) . format ( 'ddd MMM DD h:mm A' ) }
separator = ': '
/ >
< ReferenceLine y = { 0 } stroke = 'blue' >
< Label value = 'Freezing' offset = { 7 } position = 'bottom' / >
< / R e f e r e n c e L i n e >
< ReferenceLine x = { moment ( ) . startOf ( 'day' ) . toISOString ( ) . replace ( '.000' , '' ) } stroke = 'blue' / >
< Line
type = 'monotone'
dataKey = 'temperature_C'
name = 'Temperature'
stroke = 'black'
strokeWidth = { 2 }
dot = { false }
isAnimationActive = { false }
/ >
< / L i n e C h a r t >
< / R e s p o n s i v e C o n t a i n e r >
:
< p > Loading ... < / p >
}
< h2 > Thermostat { loading ? 'Loading...' : '' } < / h 2 >
{ data . Venstar ?
< ResponsiveContainer width = '100%' height = { 300 } >
< LineChart syncId = { 1 } data = { data . Venstar } margin = { { top : 5 , left : 0 , right : 30 , bottom : 0 } } >
< XAxis
dataKey = 'time'
minTickGap = { 10 }
tickFormatter = { timeStr => moment ( timeStr ) . format ( 'HH:mm' ) }
/ >
< YAxis
domain = { [ 15 , 25 ] }
/ >
< CartesianGrid strokeDasharray = '3 3' / >
< Tooltip
formatter = { v => v . toFixed ( 1 ) + ' °C' }
labelFormatter = { timeStr => moment ( timeStr ) . format ( 'ddd MMM DD h:mm A' ) }
separator = ': '
/ >
< ReferenceLine x = { moment ( ) . startOf ( 'day' ) . toISOString ( ) . replace ( '.000' , '' ) } stroke = 'blue' / >
< Line
type = 'monotone'
dataKey = 'heattemp'
name = 'Setpoint'
stroke = 'red'
strokeWidth = { 2 }
dot = { false }
isAnimationActive = { false }
/ >
< Line
type = 'monotone'
dataKey = 'spacetemp'
name = 'Temperature'
stroke = 'black'
strokeWidth = { 2 }
dot = { false }
isAnimationActive = { false }
/ >
< / L i n e C h a r t >
< / R e s p o n s i v e C o n t a i n e r >
:
< p > Loading ... < / p >
}
< h2 > Gas Usage { loading ? 'Loading...' : '' } < / h 2 >
{ data . Gas ?
< ResponsiveContainer width = '100%' height = { 300 } >
< ComposedChart syncId = { 1 } data = { data . Gas } margin = { { top : 5 , left : 0 , right : 30 , bottom : 0 } } >
< XAxis
dataKey = 'time'
minTickGap = { 10 }
tickFormatter = { timeStr => moment ( timeStr ) . format ( 'HH:mm' ) }
/ >
< YAxis
yAxisId = "right"
domain = { [ data . Gas [ 0 ] . consumption _data , data . Gas . slice ( - 1 ) [ 0 ] . consumption _data ] }
orientation = "right"
hide = { true }
/ >
< YAxis
yAxisId = "left"
/ >
< CartesianGrid strokeDasharray = '3 3' / >
< Tooltip
formatter = { v => v . toFixed ( 1 ) + ' MJ' }
labelFormatter = { timeStr => moment ( timeStr ) . format ( 'ddd MMM DD h:mm A' ) }
separator = ': '
/ >
< ReferenceLine yAxisId = "right" x = { moment ( ) . startOf ( 'day' ) . toISOString ( ) . replace ( '.000' , '' ) } stroke = 'blue' / >
< Bar
yAxisId = "left"
type = 'monotone'
dataKey = 'delta'
name = 'Delta'
fill = 'green'
isAnimationActive = { false }
/ >
< Line
yAxisId = "right"
type = 'monotone'
dataKey = 'max'
name = 'Total'
stroke = 'black'
strokeWidth = { 2 }
dot = { false }
isAnimationActive = { false }
/ >
< / C o m p o s e d C h a r t >
< / R e s p o n s i v e C o n t a i n e r >
:
< p > Loading ... < / p >
}
< >
< h2 > Water Usage { loading ? 'Loading...' : '' } < / h 2 >
{ data . Water ?
< ResponsiveContainer width = '100%' height = { 300 } >
< ComposedChart syncId = { 1 } data = { data . Water } margin = { { top : 5 , left : 0 , right : 30 , bottom : 0 } } >
< XAxis
dataKey = 'time'
minTickGap = { 10 }
tickFormatter = { timeStr => moment ( timeStr ) . format ( 'HH:mm' ) }
/ >
< ResponsiveContainer width = '100%' height = { 300 } >
< ComposedChart syncId = { 1 } data = { data } margin = { { top : 5 , left : 0 , right : 30 , bottom : 0 } } >
< XAxis
dataKey = 'time'
minTickGap = { 10 }
tickFormatter = { timeStr => moment ( timeStr ) . format ( 'HH:mm' ) }
/ >
< YAxis
yAxisId = "right"
domain = { [ data . Water [ 0 ] . consumption _data , data . Water. slice( - 1 ) [ 0 ] . consumption _data ] }
orientation = "right"
hide = { true }
/ >
< YAxis
yAxisId = "left"
/ >
< YAxis
yAxisId = 'right'
domain = { data . length ? [ data [ 0 ] . consumption _data , data . slice ( - 1 ) [ 0 ] . consumption _data ] : [ 100 , 0 ] }
orientation = 'right'
hide = { true }
/ >
< YAxis
yAxisId = 'left'
/ >
< CartesianGrid strokeDasharray = '3 3' / >
< Tooltip
formatter = { v => v + ' L' }
labelFormatter = { timeStr => moment ( timeStr ) . format ( 'ddd MMM DD h:mm A' ) }
separator = ': '
/ >
< CartesianGrid strokeDasharray = '3 3' / >
< Tooltip
formatter = { v => v + ' L' }
labelFormatter = { timeStr => moment ( timeStr ) . format ( 'ddd MMM DD h:mm A' ) }
separator = ': '
/ >
< ReferenceLine yAxisId = "right" x = { moment ( ) . startOf ( 'day' ) . toISOString ( ) . replace ( '.000' , '' ) } stroke = 'blue' / >
< ReferenceLine yAxisId = 'right' x = { moment ( ) . startOf ( 'day' ) . toISOString ( ) . replace ( '.000' , '' ) } stroke = 'blue' / >
< Bar
yAxisId = "left"
type = 'monotone'
dataKey = 'delta'
name = 'Delta'
fill = 'green'
isAnimationActive = { false }
/ >
< Bar
yAxisId = 'left'
type = 'monotone'
dataKey = 'delta'
name = 'Delta'
fill = 'green'
isAnimationActive = { false }
/ >
< Line
yAxisId = "right"
type = 'monotone'
dataKey = 'max'
name = 'Total'
stroke = 'black'
strokeWidth = { 2 }
dot = { false }
isAnimationActive = { false }
/ >
< / C o m p o s e d C h a r t >
< / R e s p o n s i v e C o n t a i n e r >
:
< p > Loading ... < / p >
}
< Line
yAxisId = 'right'
type = 'monotone'
dataKey = 'max'
name = 'Total'
stroke = 'black'
strokeWidth = { 2 }
dot = { false }
isAnimationActive = { false }
/ >
< / C o m p o s e d C h a r t >
< / R e s p o n s i v e C o n t a i n e r >
< / >
) ;
}
function Graphs ( { end , duration } ) {
return (
< div className = 'container' >
< OutsideTemperature end = { end } duration = { duration } / >
< BedroomTemperature end = { end } duration = { duration } / >
< Thermostat end = { end } duration = { duration } / >
< Gas end = { end } duration = { duration } / >
< Water end = { end } duration = { duration } / >
< / d i v >
) ;
}
function Menu ( { duration , setDuration , end , setEnd , setLoading } ) {
function Menu ( { duration , setDuration , end , setEnd } ) {
const [ submenu , setSubmenu ] = useState ( false ) ;
const chooseDuration = ( x ) => {
setLoading ( true ) ;
setSubmenu ( false ) ;
setDuration ( x ) ;
} ;
const chooseEnd = ( x ) => {
setSubmenu ( false ) ;
const newEnd = x . add ( ... duration . delta ) ;
setEnd ( newEnd ) ;
} ;
const next = ( ) => {
setSubmenu ( false ) ;
setEnd ( prevEnd => moment ( prevEnd ) . add ( ... duration . delta ) ) ;
}
const prev = ( ) => {
setSubmenu ( false ) ;
setEnd ( prevEnd => moment ( prevEnd ) . subtract ( duration . delta [ 0 ] , duration . delta [ 1 ] ) ) ;
}
return (
< div className = 'menu' >
{ ! ! submenu && < div className = 'submenu' >
< div className = 'submenu-close' >
< button onClick = { ( ) => setSubmenu ( false ) } > × < / b u t t o n >
< / d i v >
{ submenu === 'end' &&
< >
< p > Choose end date : < / p >
< p > epic date picker < / p >
< div className = 'submenu-header' >
< h2 > Choose start date : < / h 2 >
< button onClick = { ( ) => setSubmenu ( false ) } > × < / b u t t o n >
< / d i v >
< div className = 'datepicker' >
< Datetime
input = { false }
timeFormat = { false }
onChange = { ( x ) => chooseEnd ( x ) }
/ >
< / d i v >
< / >
}
{ submenu === 'duration' &&
durations . map ( x =>
< button id = { x . id } onClick = { ( ) => chooseDuration ( x ) } > Last { x . len } / { x . full } data < / b u t t o n >
)
< >
< div className = 'submenu-header' >
< h2 > Choose duration : < / h 2 >
< button onClick = { ( ) => setSubmenu ( false ) } > × < / b u t t o n >
< / d i v >
{ durations . map ( x =>
< button key = { x . id } onClick = { ( ) => chooseDuration ( x ) } > Last { x . len } / { x . full } data < / b u t t o n >
) }
< / >
}
< / d i v > }
< div className = 'menu-container' >
< button onClick = { ( ) => setDuration ( 'day' ) } > & lt ; < / b u t t o n >
< button onClick = { ( ) => prev ( ) } > & lt ; < / b u t t o n >
< button
onClick = { ( ) => setSubmenu ( 'end' ) }
className = { submenu === 'end' ? 'active' : '' }
>
{ end. format ( 'ddd MMM DD' ) }
{ moment ( end ) . subtract ( duration . delta [ 0 ] , duration . delta [ 1 ] ) . format ( 'ddd MMM DD' ) }
< / b u t t o n >
< button
@ -309,7 +172,7 @@ function Menu({duration, setDuration, end, setEnd, setLoading}) {
{ duration . len } / { duration . win }
< / b u t t o n >
< button onClick = { ( ) => setDuration( 'day' ) } > & gt ; < / b u t t o n >
< button onClick = { ( ) => next( ) } > & gt ; < / b u t t o n >
< / d i v >
< / d i v >
) ;
@ -317,49 +180,7 @@ function Menu({duration, setDuration, end, setEnd, setLoading}) {
function App ( ) {
const [ duration , setDuration ] = useState ( durations [ 0 ] ) ;
const [ loading , setLoading ] = useState ( false ) ;
const [ end , setEnd ] = useState ( moment ( ) ) ;
const [ data , setData ] = useState ( false ) ;
const setupGetter = ( measurement , name ) => {
const get = async ( ) => {
try {
const params = { end : end . unix ( ) , duration : duration . len . toLowerCase ( ) , window : duration . win } ;
const res = await axios . get (
'https://sensors-api.dns.t0.vc/history/' + measurement + '/' + name ,
{ params : params } ,
) ;
setData ( ( d ) => ( { ... d , [ name ] : res . data } ) ) ;
setLoading ( false ) ;
} catch ( error ) {
console . log ( error ) ;
}
} ;
get ( ) ;
const interval = setInterval ( get , 30000 ) ;
return ( ) => clearInterval ( interval ) ;
} ;
useEffect ( ( ) => {
return setupGetter ( 'temperature' , 'Outside' ) ;
} , [ duration ] ) ;
useEffect ( ( ) => {
return setupGetter ( 'temperature' , 'Bedroom' ) ;
} , [ duration ] ) ;
useEffect ( ( ) => {
return setupGetter ( 'thermostat' , 'Venstar' ) ;
} , [ duration ] ) ;
useEffect ( ( ) => {
return setupGetter ( 'ertscm' , 'Gas' ) ;
} , [ duration ] ) ;
useEffect ( ( ) => {
return setupGetter ( 'ertscm' , 'Water' ) ;
} , [ duration ] ) ;
return (
< div >
@ -368,12 +189,11 @@ function App() {
setDuration = { setDuration }
end = { end }
setEnd = { setEnd }
setLoading = { setLoading }
/ >
< Graphs
data = { data }
loading= { loading }
en d= { en d}
duration= { duration }
/ >
< / d i v >
) ;