Thursday, April 9, 2015

Building a SolarEdge Windows Desktop Widget - Part 2

Part 1: http://iamwyza.blogspot.com/2015/04/building-solaredge-windows-desktop.html

Onwards and upwards we go.

The next step in the adventure was to start working on the gadget code itself.  Chrome is very frustrating to deal with when you are doing localhost ajax calls.  There are ways to do it, but they all involve adding a little risk to your own security. Since I use chrome for everything else, I figured I'd suck it up and do it in IE.  After all Windows Gadgets are rendered with the IE engine. (though I'll later find out it is the IE 7 engine, *shutter*).

Whenever I'm dealing with web related stuff I almost always pull in at least 1 javascript library.  Realistically there isn't a good reason not to.  In my case it is nearly always jQuery.  I'm familiar with it and it is has been quite stable.  The other library I used was Flot for the graphing functions.  I also needed excanvas in order to provide HTML 5's canvas support to IE7 (which Flot needs).


So the first area to look at is the basic setup of the javascript.  We have a few variables here are going to be used with the scope of this page.  We next have a function defined "Update()" which we'll use any time we want to request new data from the server and update the various fields and plots.


var first = true;

// Last 6 days of power setup:
jQuery.support.cors = true;
$(function(){
 var previousPoint = null, previousLabel = null;
 var powerpoints = [];
 var days = [];
 var daysrendered = false;
 var monthsrendered = false;
    
 //Here we fetch the data and render it
 function Update() {
  $.ajax({ 
   url: "http://localhost:8001", 
   type: "GET", 
   dataType:"json", 
   success: function(data) { 
    //Set the simple values
    $('#currentPower').html(data.overviewData.currentPower);
    $('#powerToday').html(data.overviewData.lastDayEnergy);
    $('#powerMonth').html(data.overviewData.lastMonthEnergy);
    $('#powerTotal').html(data.overviewData.lifeTimeEnergy);
    
    //Setup the power production per point of time for the last week
    $.each(data.energyChartData.power_chart_week, function(i, e) {
     powerpoints.push([data.energyChartData.start_week+(i*1000*60*15), e]);
    });
    
    //Month long daily power production
    $.each(data.energyChartData.energy_chart_month_by_day.production.data, function(i,e){
     days.push([i,e]);
    });
    
    //Determine which plot was visiable.  We need to know this because rendering a plot when you can't see it will cause issues.
    if ($('#previousDays').is(':visible')){
     renderDays();
     monthsrendered = false;
    }else{
     renderMonths();
     daysrendered = false;
    }
    
   // Show current datetime to make sure it is still running
   var currentdate = new Date(); 
   $('#lastUpdated').html(currentdate.getDate() + "/"
   + (currentdate.getMonth()+1)  + "/" 
   + currentdate.getFullYear() + " "  
   + currentdate.getHours() + ":"  
   + currentdate.getMinutes() + ":" 
   + currentdate.getSeconds());
   
   setTimeout(function() { Update(); }, 300000);
   }
   
  });
 }

The following 2 functions are in charge of setting up the plots themselves.  Each plot serves a different purpose.  The "days" plot is a line plot that shows how much power is being produced at that point in time.  It has data points in 15 minute intervals.   The "month" plot shows how much power was generated each day in the current month.

 //Renders the plot for the last week's power production over time (kW)
 function renderDays() {
  $.plot('#previousDays', [powerpoints], { 
    xaxis: { mode: "time",
      axisLabel: "Day"
      },
    yaxis: {
       tickFormatter: function (v, axis) {
        return v + " kW";
       },
       axisLabel: "kW"
    },
    grid: {
     hoverable: true,
     borderWidth: 0
    },
    colors: ["#5DFF00"],
    series: {
     lines:{
      fill:true,
      fillColor: {colors: [{opacity: 0.1}, {opacity: 0.7}]}
     }
    }
   });
   $("#previousDays").UseTooltip();
   daysrendered = true;
 }

 //renders the plot for the current months power production per day (kWh)
 function renderMonths() {
  $.plot('#month', [days], {
   series:{
    bars:{
     show: true
    }
   },
   yaxis: {
      tickFormatter: function (v, axis) {
       return v + " kWh";
      },
      axisLabel: "kWh",
      axisLabelPadding: 10
   },
   grid: {
    hoverable: true,
    borderWidth: 0
   },
   colors: ["#5DFF00"]
  });
  
  
  $("#month").UseTooltip();
  monthsrendered = true;
 }  

Next we bind the useToolTip function to our own functionality.  We want to show the tooltip up and over a little with  the same colors that the graph uses.  It also determines which graph we are on and uses the appropriate unit of measure.

   
 //Describes and sets up the tooltip display
 $.fn.UseTooltip = function () {
  $(this).bind("plothover", function (event, pos, item) {
   if (item) {
    if ((previousLabel != item.series.label) || (previousPoint != item.dataIndex)) {
     previousPoint = item.dataIndex;
     previousLabel = item.series.label;
     $("#tooltip").remove();

     var x = item.datapoint[0];
     var y = item.datapoint[1];

     var color = item.series.color;

     if ($('#previousDays').is(':visible')){
     
      showTooltip(item.pageX,
      item.pageY,
      color,
      "" + y + " kW");
     }else{
      showBarTooltip(item.pageX,
      item.pageY,
      color,
      "" + y + " kWh");
     }
    }
   } else {
    $("#tooltip").remove();
    previousPoint = null;
   }
  });
 };
 


 
 //Show Tooltip on hover
 function showTooltip(x, y, color, contents) {
  $('' + contents + '').css({
   position: 'absolute',
   display: 'none',
   top: y - 20,
   left: x - 120,
   border: '2px solid ' + color,
   padding: '3px',
   'font-size': '9px',
   'border-radius': '5px',
   'background-color': '#000000',
   'font-family': 'Verdana, Arial, Helvetica, Tahoma, sans-serif',
   opacity: 0.9
  }).appendTo("body").fadeIn(200);
 }
 
 //Show Tooltip on hover
 function showBarTooltip(x, y, color, contents) {
  $('' + contents + '').css({
   position: 'absolute',
   display: 'none',
   top: y + 20,
   left: x + 60,
   border: '2px solid ' + color,
   padding: '3px',
   'font-size': '9px',
   'border-radius': '5px',
   'background-color': '#000000',
   'font-family': 'Verdana, Arial, Helvetica, Tahoma, sans-serif',
   opacity: 0.9
  }).appendTo("body").fadeIn(200);
 }
Lastly we have the click events on a couple buttons to switch between week and month graphs. Also we need to run Update() for the first time.

 //Normally I'd use jQuery.on('click') here, but IE 7 + Gadgets doesn't work for that.  So back in time we go.
 document.getElementById("weakLink").onclick = function(){
  $('#previousDays').show();
  $('#month').hide();
  if (! daysrendered){
   renderDays();
  }
 };
 document.getElementById("monthLink").onclick = function(){
  $('#previousDays').hide();
  $('#month').show();
  if (! monthsrendered) {
   renderMonths();
  }
 };  

 Update();
 });


A little bit of CSS.

* {
 color: #e4e4e4;
}

.currentSubDiv {
 border-top: 1px solid #0083FF;
 padding-top:5px; 
 margin-top:5px;
 margin-left:5px;
}
.currentSubDivValue { 
 color: #5DFF00;
 font-style:bolder;
}
.previousDayDiv {
 width: 1000px;
 height:325px;
 float:left;clear:left;
}


And finally some basic and mostly unpolished html.

<body style="background-color:#000000; width:1200px; height:400px;">
<div style="width:1175px; height: 375px; border: 1px solid #000000">
 <div style="float:left;text-align:center;">
  <div style="border-bottom: 1px solid #0083FF;float:left;clear:left;">
   <div style="width:495px; cursor:pointer; float:left;" id="weakLink">Week</div>
   <div style="float:left">|</div>
   <div style="width:495px; cursor:pointer; float:left;" id="monthLink">Month</div>
  </div>
  <div class="previousDayDiv" id="previousDays"></div>
  <div class="previousDayDiv" id="month" style="display:none;"></div>
 </div>
 <div style="float:right;border-left:1px solid #0083FF; width:125px;height: 375px; text-align:center;font-size:22px;">
  <div>
   Current
  </div>
  <div class="currentSubDiv">
   Power<br />
   <span id="currentPower" class="currentSubDivValue">N/A</span>
  </div>
  <div class="currentSubDiv">
   Today<br />
   <span id="powerToday" class="currentSubDivValue">N/A</span>
  </div>
  <div class="currentSubDiv">
   Month<br />
   <span id="powerMonth" class="currentSubDivValue">N/A</span>
  </div>
  <div class="currentSubDiv">
   Lifetime<br />
   <span id="powerTotal" class="currentSubDivValue">N/A</span>
  </div>
  <div class="currentSubDiv">
   Last Updated<br />
   <span id="lastUpdated" class="currentSubDivValue">N/A</span>
  </div>
 </div>
</div>
</body>
</html>

So there we have it.  All the code to make this stuff work right in theory.  When it renders in IE, life is good and it looks like this:




Next step was to get it working as a gadget.  I setup the necessary files (you can google the layout) and dropped it in.  I then found that I needed an older version of jQuery to support IE7.  Fixed that and I had a gadget.  However over the last few days I've had issues with the gadget not refreshing itself every 5 minutes as planned.  Over time it slows down and eventually crashes. Doing research on this problem, it is apparently common, even with the default gadgets, to suck up memory due to leaks and poor design in the engine.  The only fix anyone has really given for it is to restart gadgets with a batch file or vbs file.  I attempted that route, but when it restarted the widget wouldn't come back (in fact no widgets worked). The only way to get them to come back was to reboot.

So almost all of the work in this post has been for naught.  However I do have a new direction I started taking yesterday and today (when I wasn't feeling crappy).  That'll be for the next post.

No comments:

Post a Comment