The Weather Outside Is Frightful (Or Is It?)

Dashboard Code

Let's take a look at the code behind the dashboard. It's a pretty typical HTML/JavaScript/PHP setup, where the JavaScript calls the PHP functions to get the massaged data for use with Google's Chart API and then draws the charts in the appropriate DIVs in HTML. There is a ton of room for improvement here, but it works. Here are the main code snippets responsible for displaying the data, broken into HTML, JavaScript, and PHP sections.

HTML

This is all there is to this page—just a few elements, with the <div> elements being the most important. These each have their own ID that the JavaScript uses to know which div content to update:


<html>
  <head>
    <title>The Cabin Temperature Dashboard</title>
  </head>

  <body>
    <h1>Current Temperatures (°F):</h1>
    <h2>Last updated: <?=get_currenttempsdate($db);?></h2>
    <div id='currenttemps'></div>

    <h1>Temperature Timeline:</h1>
    <h2>A timeline that displays all temperatures recorded.</h2>
    <div id="timelinetemps" style='width: 1000px; height: 400px;'></div>
  </body>
</html>

Javascript

Diving into the next layer of our website code, we see the JavaScript responsible for loading the Google Charts API and using it. There are a couple inline PHP calls that will insert the JSON data directly into the script, making things fairly clean:


<script type='text/javascript' src='https://www.google.com/jsapi'></script>
<script type='text/javascript'>
  google.load('visualization', '1', {packages: ['corechart', 'table', 'gauge', 'annotatedtimeline']});


  google.setOnLoadCallback(draw_currenttemps);
  google.setOnLoadCallback(draw_timelinetemps);


  function draw_currenttemps() {
     var data = new google.visualization.DataTable();
     data.addColumn('string', 'Label');
     data.addColumn('number', 'Value');
     <?=get_currenttemps($db);?>


     var opts = {
        width: 650, height: 300,
        greenColor: '#6A93D4',
        greenFrom: -20, greenTo: 31,
        redFrom: 90, redTo: 120,
        yellowFrom:60, yellowTo: 90,
        minorTicks: 10, min: -20, max: 110
     };

     var table = new google.visualization.Gauge(document.getElementById('currenttemps'));
     table.draw(data, opts);
  }


  function draw_timelinetemps() {
     var data = new google.visualization.DataTable();


     data.addColumn('datetime', 'Date');
     data.addColumn('number', 'Outside');
     data.addColumn('string', 'title1');
     data.addColumn('string', 'text1');
     data.addColumn('number', 'Pit');
     data.addColumn('number', 'Inside');
     data.addColumn('number', 'Bath');
     <?=get_timelinetemps($db);?>


     var opts = {
         displayRangeSelector: true,
         displayZoomButtons: true,
         thickness: 2,
         scaleType: 'maximized',                                                            
         displayAnnotations: true,
     };


     var table = new google.visualization.AnnotatedTimeLine(document.getElementById('timelinetemps'));
     table.draw(data, opts);
  }
</script>

PHP

Finally, here's the PHP code that reads from the database and massages the data into something Google Charts can understand. Notice I'm not providing all the code for a few reasons: 1) the code was written back before I knew better than to use the deprecated mysql_query() PHP calls instead of PDO, 2) it'd be overwhelming, and 3) it's mostly just helper functions anyway. Just pay attention that I'm passing in a $db object that was instantiated earlier in the PHP script using $db = new DB();. Other than that, there's not much else to this code:


function get_currenttemps($db) {                                                            
   $query_ct = "SELECT timestamp, box, lbath, t_out, t_in FROM cabintemps order by timestamp desc limit 1;";
   $db->run_query($query_ct);
   $temp_time = "";
   $temps_value = "";
   while ($line = mysql_fetch_array($db->get_result(), MYSQL_ASSOC)) {
       $temp_datetime = strtotime($line['timestamp']);
       $temp_time = date("m/d/y g:i A", $temp_datetime);
       $temps_value = "data.addRows([['Outside', %s], ['Pit', %s], ['Inside', %s], ['Bath', %s]]);";
       $temps = sprintf($temps_value, $line['t_out'], $line['t_in'], $line['box'], $line['lbath']);
   }


   return $temps;
}


function get_timelinetemps($db) {
   $interval = "6 MONTH";
   $query_ct = "SELECT timestamp, box, lbath, t_out, t_in FROM cabintemps WHERE timestamp > DATE_SUB(CURDATE(), INTERVAL $interval) order by timestamp;";
   $db->run_query($query_ct);
   $all_temps_array = array();
   $all_temps_value = "";
   while ($line = mysql_fetch_array($db->get_result(), MYSQL_ASSOC)) {
       $temp_datetime = strtotime($line['timestamp']);
       $temp_all_time = date("Y, m-1, d, H, i", $temp_datetime);



       # $all_temps_value = "[new Date ( year, month-1, day, hour, minute ), outside, inside, server, bath],";
       $all_temps_value = "[new Date ( $temp_all_time ), " . $line['t_out'] . ", undefined, undefined, " . $line['t_in'] . ", " . $line['box'] . ", " . $line['lbath'] .
"],";
       array_push($all_temps_array, $all_temps_value);
   }


   #find min and max
   $query_max = "SELECT * from cabintemps WHERE t_out = (SELECT MAX(t_out) FROM cabintemps WHERE timestamp > DATE_SUB(CURDATE(), INTERVAL $interval)) order by timestamp
desc limit 1";
   $db->run_query($query_max);
   while ($line = mysql_fetch_array($db->get_result(), MYSQL_ASSOC)) {
       $temp_datetime = strtotime($line['timestamp']);
       $temp_all_time = date("Y, m-1, d, H, i", $temp_datetime);


       #$all_temps_value = "[new Date ( $temp_all_time ), " . $line['t_out'] . ", " . $line['t_in'] . ", " . $line['box'] . ", " . $line['lbath'] . ", 'High Outside Temp
: ', '" . $line['t_out'] . "\u00B0F'],";
       $all_temps_value = "[new Date ( $temp_all_time ), undefined, 'High Outside Temp: ', '" . $line['t_out'] . "\u00B0F', undefined, undefined, undefined],";
       array_push($all_temps_array, $all_temps_value);
   }


   $query_min = "SELECT * from cabintemps WHERE t_out = (SELECT MIN(t_out) FROM cabintemps WHERE timestamp > DATE_SUB(CURDATE(), INTERVAL $interval)) order by timestamp
desc limit 1";
   $db->run_query($query_min);
   while ($line = mysql_fetch_array($db->get_result(), MYSQL_ASSOC)) {
       $temp_datetime = strtotime($line['timestamp']);
       $temp_all_time = date("Y, m-1, d, H, i", $temp_datetime);


       #$all_temps_value = "[new Date ( $temp_all_time ), " . $line['t_out'] . ", " . $line['t_in'] . ", " . $line['box'] . ", " . $line['lbath'] . ", 'Low Outside Temp:
 ', '" . $line['t_out'] . "\u00B0F'],";
       $all_temps_value = "[new Date ( $temp_all_time ), undefined, 'Low Outside Temp: ', '" . $line['t_out'] . "\u00B0F', undefined, undefined, undefined],";
       array_push($all_temps_array, $all_temps_value);
   }


   $start_string = "data.addRows([\n";
   $data_row_string_all_temps = implode("\n", $all_temps_array);
   $end_string = "\n]);";


   $all_temps_data = $start_string . $data_row_string_all_temps . $end_string;


   return $all_temps_data;
}

Hopefully, this has helped you gain a basic understanding of the different code parts that make up the web dashboard. Again, we have a script to download the raw data and store it into a database, and we have a web page that will read from the database and display it in a human-friendly way. This was the final piece in completing our project.

Conclusion

This project was an interesting and rewarding learning experience. We were able to come up with a robust solution that makes planning and monitoring a home easy and meaningful. The ability to view current conditions of your home (or cabin, in this case) from anywhere in the world is incredibly convenient and helpful for planning so your pipes don't burst. It's also handy in situations where high temperatures are a concern, like for pets or livestock in kennels or barns. Naturally, there's more than one way to do this, some easier than others. For example, if I were to do it again, I'd use more AJAX calls for real-time temperature polling instead of scheduled. I'd encourage you to come up with your own solution that meets your requirements and suits your particular situation using what you've learned from this project and then build upon it. This has potential in both personal and industrial business applications, such as home automation, so feel inspired and start building!

If you have any questions or comments about the project, or if you have ideas to improve it, please be sure to use the comment form below.

______________________