A graph that interprets IoT temperature device through time, tracking internal to a refrigerator and external to a refrigerator.
Written in D3, and visible here
Gist available here
/* http://meyerweb.com/eric/tools/css/reset/ | |
v2.0 | 20110126 | |
License: none (public domain) | |
*/ | |
html, body, div, span, applet, object, iframe, | |
h1, h2, h3, h4, h5, h6, p, blockquote, pre, | |
a, abbr, acronym, address, big, cite, code, | |
del, dfn, em, img, ins, kbd, q, s, samp, | |
small, strike, strong, sub, sup, tt, var, | |
b, u, i, center, | |
dl, dt, dd, ol, ul, li, | |
fieldset, form, label, legend, | |
table, caption, tbody, tfoot, thead, tr, th, td, | |
article, aside, canvas, details, embed, | |
figure, figcaption, footer, header, hgroup, | |
menu, nav, output, ruby, section, summary, | |
time, mark, audio, video { | |
margin: 0; | |
padding: 0; | |
border: 0; | |
font-size: 100%; | |
font: inherit; | |
vertical-align: baseline; | |
} | |
/* HTML5 display-role reset for older browsers */ | |
article, aside, details, figcaption, figure, | |
footer, header, hgroup, menu, nav, section { | |
display: block; | |
} | |
body { | |
line-height: 1; | |
} | |
ol, ul { | |
list-style: none; | |
} | |
blockquote, q { | |
quotes: none; | |
} | |
blockquote:before, blockquote:after, | |
q:before, q:after { | |
content: ''; | |
content: none; | |
} | |
table { | |
border-collapse: collapse; | |
border-spacing: 0; | |
} | |
.temperature { | |
fill: none; | |
stroke-width: 2px; | |
} | |
.temp-probe { | |
stroke: blue; | |
} | |
.ambient { | |
stroke: #C790E3; | |
} | |
.high-threshold { | |
fill: none; | |
stroke: red; | |
stroke-width: 1px; | |
} | |
.high-threshold { | |
fill: none; | |
stroke: red; | |
stroke-width: 1px; | |
} | |
.low-threshold { | |
fill: none; | |
stroke: #67A9FF; | |
stroke-width: 1px; | |
} | |
div.tooltip { | |
position: absolute; | |
text-align: center; | |
width: 60px; | |
height: 28px; | |
padding: 2px; | |
font: 12px sans-serif; | |
background: lightsteelblue; | |
border: 0px; | |
border-radius: 8px; | |
pointer-events: none; | |
} |
<!DOCTYPE html> | |
<html> | |
<head> | |
<title>D3 Sandbox</title> | |
<link rel="icon" href="data:;base64,iVBORwOKGO=" /> | |
<link rel="stylesheet" href="index.css" /> | |
<script src="http://d3js.org/d3.v4.js"></script> | |
</head> | |
<body> | |
<script src="index.js"></script> | |
</body> | |
</html> |
// set the dimensions and margins of the graph | |
var margin = {top: 20, right: 20, bottom: 30, left: 50}, | |
width = 960 - margin.left - margin.right, | |
height = 500 - margin.top - margin.bottom; | |
// parse the date / time | |
var parseDate = d3.utcParse("%Y-%m-%dT%H:%M:%S.%LZ"); | |
//var parseDate = d3.timeParse("%Y-%m-%d %H:%M:%S"); | |
var formatTime = d3.timeFormat("%e %B"); | |
console.log(parseDate("2018-09-05T05:27:54.212Z")); //2017-09-11 18:35:10")) | |
// set the ranges | |
var x = d3.scaleTime().range([0, width]); | |
var y = d3.scaleLinear().range([height, 0]); | |
// define the div for the tooltip | |
var div = d3.select("body").append("div") | |
.attr("class", "tooltip") | |
.style("opacity", 0); | |
var tempprobe_line = d3.line() | |
.x( function(d) { return x(d.timestamp); }) | |
.y( function(d) { return y(d.tempprobe); }); | |
var high_threshold_line = d3.line() | |
.x(function(d){ return x(d.timestamp); }) | |
.y(function(d){ return y(d.threshold_high); }); | |
var low_threshold_line = d3.line() | |
.x(function(d){ | |
return x(d.timestamp); | |
}) | |
.y(function(d){ | |
return y(d.threshold_low); | |
}) | |
var ambient_line = d3.line() | |
.x(function(d) | |
{ return x(d.timestamp);} | |
) | |
.y( function(d) { | |
return y(d.ambient); | |
}); | |
var svg = d3.select("body").append("svg") | |
.attr("width", width + margin.left + margin.right) | |
.attr("height", height + margin.top + margin.bottom) | |
.append("g") | |
.attr("transform", | |
"translate(" + margin.left + "," + margin.top + ")"); | |
function draw(data, tempdata) { | |
var data = data[tempdata]; | |
data.forEach(function(d, i) { | |
d.timestamp = parseDate(d.recvTime); | |
d.tempprobe = +d.attrValue; | |
d.ambient = +1 //d.ambient; | |
}); | |
console.log(data); | |
data.sort(function(a, b){ | |
return a["timestamp"]-b["timestamp"]; | |
}); | |
// scale the range of data | |
x.domain(d3.extent(data, function(d){ | |
return d.timestamp; | |
})); | |
y.domain([0, d3.max(data, function(d){ | |
return Math.max(d.tempprobe, d.ambient); | |
})]); | |
// Add the tempprobe path. | |
svg.append("path") | |
.data([data]) | |
.attr("class", "line temp-probe temperature") | |
.attr("d", tempprobe_line); | |
// Add the ambient path | |
svg.append("path") | |
.data([data]) | |
.attr("class", "line ambient temperature") | |
.attr("d", ambient_line); | |
/*svg.append("path") | |
.data([data]) | |
.attr("class", "line high-threshold") | |
.attr("d", high_threshold_line) | |
svg.append("path") | |
.data([data]) | |
.attr("class", "line low-threshold") | |
.attr("d", low_threshold_line)*/ | |
// add the X Axis | |
svg.append("g") | |
.attr("transform", "translate(0,"+ height + ")") | |
.call(d3.axisBottom(x)); | |
// add the Y Axis | |
svg.append("g") | |
.call(d3.axisLeft(y)); | |
} | |
d3.json("weather_sth.json", | |
function(error, data){ | |
if (error){ | |
console.log("an error has occurred in d3 JSON"); | |
throw error; | |
} | |
draw(data["contextResponses"][0]["contextElement"]["attributes"][0], "values"); | |
}); |
[{ | |
"tempdata": [{ | |
"tempprobe": 6.5, | |
"ambient": 12, | |
"timestamp": "2017-09-01 18:35:10", | |
"threshold_high": 8, | |
"threshold_low": 2 | |
}, | |
{ | |
"tempprobe": 6.4, | |
"ambient": 24, | |
"timestamp": "2017-09-01 24:38:01", | |
"threshold_high": 8, | |
"threshold_low": 2 | |
}, | |
{ | |
"tempprobe": 6.3, | |
"ambient": 23, | |
"timestamp": "2017-09-02 18:40:10", | |
"threshold_high": 8, | |
"threshold_low": 2 | |
}, | |
{ | |
"tempprobe": 6.0, | |
"ambient": 20, | |
"timestamp": "2017-09-03 18:42:10", | |
"threshold_high": 8, | |
"threshold_low": 2 | |
}, | |
{ | |
"tempprobe": 4.9, | |
"ambient": 15, | |
"timestamp": "2017-09-03 18:44:10", | |
"threshold_high": 8, | |
"threshold_low": 2 | |
}, | |
{ | |
"tempprobe": 6.3, | |
"ambient": 22.1, | |
"timestamp": "2017-09-04 18:46:10", | |
"threshold_high": 8, | |
"threshold_low": 2 | |
}, | |
{ | |
"tempprobe": 5.8, | |
"ambient": 21.1, | |
"timestamp": "2017-09-06 18:48:10", | |
"threshold_high": 8, | |
"threshold_low": 2 | |
} | |
] | |
}] |
{ | |
"contextResponses": [ | |
{ | |
"contextElement": { | |
"attributes": [ | |
{ | |
"name": "temperature:ae68", | |
"values": [ | |
{ | |
"recvTime": "2018-09-05T05:27:54.212Z", | |
"attrType": "Number", | |
"attrValue": "13" | |
}, | |
{ | |
"recvTime": "2018-09-05T05:32:54.817Z", | |
"attrType": "Number", | |
"attrValue": "10" | |
}, | |
{ | |
"recvTime": "2018-09-05T05:38:00.840Z", | |
"attrType": "Number", | |
"attrValue": "11" | |
}, | |
{ | |
"recvTime": "2018-09-05T05:43:01.278Z", | |
"attrType": "Number", | |
"attrValue": "12" | |
}, | |
{ | |
"recvTime": "2018-09-05T05:48:01.856Z", | |
"attrType": "Number", | |
"attrValue": "13" | |
}, | |
{ | |
"recvTime": "2018-09-05T05:53:02.172Z", | |
"attrType": "Number", | |
"attrValue": "13" | |
}, | |
{ | |
"recvTime": "2018-09-05T05:58:04.234Z", | |
"attrType": "Number", | |
"attrValue": "10" | |
}, | |
{ | |
"recvTime": "2018-09-05T06:03:04.743Z", | |
"attrType": "Number", | |
"attrValue": "13" | |
}, | |
{ | |
"recvTime": "2018-09-05T06:08:05.291Z", | |
"attrType": "Number", | |
"attrValue": "12" | |
}, | |
{ | |
"recvTime": "2018-09-05T06:13:05.597Z", | |
"attrType": "Number", | |
"attrValue": "8" | |
} | |
] | |
} | |
], | |
"id": "0e72", | |
"isPattern": false, | |
"type": "WeatherObserved" | |
}, | |
"statusCode": { | |
"code": "200", | |
"reasonPhrase": "OK" | |
} | |
} | |
] | |
} |