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" | |
| } | |
| } | |
| ] | |
| } |