add templates for plugins: Google gauge and multiline charts
This commit is contained in:
		@@ -1,5 +1,6 @@
 | 
			
		||||
class ChartsController < ApplicationController
 | 
			
		||||
  before_filter :require_user, :only => [:edit]
 | 
			
		||||
 | 
			
		||||
  def edit
 | 
			
		||||
    # params[:id] is the windows ID
 | 
			
		||||
    @channel = current_user.channels.find(params[:channel_id])
 | 
			
		||||
@@ -22,7 +23,6 @@ class ChartsController < ApplicationController
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def index
 | 
			
		||||
 | 
			
		||||
    set_channels_menu
 | 
			
		||||
    @channel = Channel.find(params[:channel_id])
 | 
			
		||||
    @channel_id = params[:channel_id]
 | 
			
		||||
@@ -35,8 +35,12 @@ class ChartsController < ApplicationController
 | 
			
		||||
    check_permissions(@channel)
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def show
 | 
			
		||||
  # show a chart with multiple series
 | 
			
		||||
  def multiple_series
 | 
			
		||||
    render :layout => false
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def show
 | 
			
		||||
    # allow these parameters when creating feed querystring
 | 
			
		||||
    feed_params = ['key', 'api_key', 'apikey', 'days','start','end','round','timescale','average','median','sum','results','location','status','timezone']
 | 
			
		||||
 | 
			
		||||
@@ -66,7 +70,6 @@ class ChartsController < ApplicationController
 | 
			
		||||
    render :layout => false
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
  # save chart options
 | 
			
		||||
  def update
 | 
			
		||||
    @channel = Channel.find(params[:channel_id])
 | 
			
		||||
 
 | 
			
		||||
@@ -8,13 +8,14 @@ class PluginsController < ApplicationController
 | 
			
		||||
    respond_with_error(:error_auth_required) and return if current_user.blank? || (@plugin.user_id != current_user.id)
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def new; ; end
 | 
			
		||||
  def edit; ; end
 | 
			
		||||
 | 
			
		||||
  def index
 | 
			
		||||
    @plugins = current_user.plugins
 | 
			
		||||
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def public_plugins
 | 
			
		||||
 | 
			
		||||
    channel_id = params[:channel_id].to_i
 | 
			
		||||
    return if channel_id.nil?
 | 
			
		||||
    #private page should display all plugins
 | 
			
		||||
@@ -24,7 +25,6 @@ class PluginsController < ApplicationController
 | 
			
		||||
    plugins.each do |plugin|
 | 
			
		||||
      plugin.make_windows channel_id, api_domain #will only make the window the first time
 | 
			
		||||
      @plugin_windows = @plugin_windows + plugin.public_dashboard_windows(channel_id)
 | 
			
		||||
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    respond_to do |format|
 | 
			
		||||
@@ -53,9 +53,21 @@ class PluginsController < ApplicationController
 | 
			
		||||
  def create
 | 
			
		||||
    # add plugin with defaults
 | 
			
		||||
    @plugin = Plugin.new
 | 
			
		||||
    @plugin.html = read_file('app/views/plugins/default.html')
 | 
			
		||||
    @plugin.css = read_file('app/views/plugins/default.css')
 | 
			
		||||
    @plugin.js = read_file('app/views/plugins/default.js')
 | 
			
		||||
 | 
			
		||||
    # set default template
 | 
			
		||||
    template = 'default'
 | 
			
		||||
 | 
			
		||||
    # use case statement to set template, since user input is untrusted
 | 
			
		||||
    case params[:template]
 | 
			
		||||
    when 'gauge' then template = 'gauge'
 | 
			
		||||
    when 'chart' then template = 'chart'
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    # set template dynamically
 | 
			
		||||
    @plugin.html = read_file("app/views/plugins/templates/#{template}.html")
 | 
			
		||||
    @plugin.css = read_file("app/views/plugins/templates/#{template}.css")
 | 
			
		||||
    @plugin.js = read_file("app/views/plugins/templates/#{template}.js")
 | 
			
		||||
 | 
			
		||||
    @plugin.user_id = current_user.id
 | 
			
		||||
    @plugin.private_flag = true
 | 
			
		||||
    @plugin.save
 | 
			
		||||
@@ -82,7 +94,6 @@ class PluginsController < ApplicationController
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def show_public
 | 
			
		||||
 | 
			
		||||
    @plugin = Plugin.find(params[:id])
 | 
			
		||||
    @output = @plugin.html.sub('%%PLUGIN_CSS%%', @plugin.css).sub('%%PLUGIN_JAVASCRIPT%%', @plugin.js)
 | 
			
		||||
    if @plugin.private?
 | 
			
		||||
@@ -100,9 +111,6 @@ class PluginsController < ApplicationController
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def edit
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def update
 | 
			
		||||
    @plugin.update_attribute(:name, params[:plugin][:name])
 | 
			
		||||
    @plugin.update_attribute(:private_flag, params[:plugin][:private_flag])
 | 
			
		||||
@@ -111,11 +119,9 @@ class PluginsController < ApplicationController
 | 
			
		||||
    @plugin.update_attribute(:html,params[:plugin][:html])
 | 
			
		||||
 | 
			
		||||
    if @plugin.save
 | 
			
		||||
 | 
			
		||||
      @plugin.update_all_windows
 | 
			
		||||
      redirect_to plugins_path and return
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def ajax_update
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										136
									
								
								app/views/charts/multiple_series.html.erb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										136
									
								
								app/views/charts/multiple_series.html.erb
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,136 @@
 | 
			
		||||
<!DOCTYPE html>
 | 
			
		||||
<html style="height: 100%;">
 | 
			
		||||
  <head>
 | 
			
		||||
    <script type="text/javascript" src="//ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
 | 
			
		||||
    <script type="text/javascript" src="//thingspeak.com/highcharts-3.0.8.js"></script>
 | 
			
		||||
    <script type="text/javascript" src="//thingspeak.com/exporting.js"></script>
 | 
			
		||||
 | 
			
		||||
    <script type="text/javascript">
 | 
			
		||||
      // variables for the first series
 | 
			
		||||
      var series_1_channel_id = 9;
 | 
			
		||||
      var series_1_field_number = 1;
 | 
			
		||||
      var series_1_read_api_key = '';
 | 
			
		||||
      var series_1_results = 10;
 | 
			
		||||
      var series_1_color = '#d62020';
 | 
			
		||||
 | 
			
		||||
      // variables for the second series
 | 
			
		||||
      var series_2_channel_id = 9;
 | 
			
		||||
      var series_2_field_number = 2;
 | 
			
		||||
      var series_2_read_api_key = '';
 | 
			
		||||
      var series_2_results = 10;
 | 
			
		||||
      var series_2_color = '#00aaff';
 | 
			
		||||
 | 
			
		||||
      // chart title
 | 
			
		||||
      var chart_title = 'Light & Temperature';
 | 
			
		||||
      // y axis title
 | 
			
		||||
      var y_axis_title = 'Values';
 | 
			
		||||
 | 
			
		||||
      // user's timezone offset
 | 
			
		||||
      var my_offset = new Date().getTimezoneOffset();
 | 
			
		||||
      // chart variable
 | 
			
		||||
      var my_chart;
 | 
			
		||||
 | 
			
		||||
      // when the document is ready
 | 
			
		||||
      $(document).on('ready', function() {
 | 
			
		||||
        // add a blank chart
 | 
			
		||||
        addChart();
 | 
			
		||||
        // add the first series
 | 
			
		||||
        addSeries(series_1_channel_id, series_1_field_number, series_1_read_api_key, series_1_results, series_1_color);
 | 
			
		||||
        // add the second series
 | 
			
		||||
        addSeries(series_2_channel_id, series_2_field_number, series_2_read_api_key, series_2_results, series_2_color);
 | 
			
		||||
      });
 | 
			
		||||
 | 
			
		||||
      // add the base chart
 | 
			
		||||
      function addChart() {
 | 
			
		||||
        // variable for the local date in milliseconds
 | 
			
		||||
        var localDate;
 | 
			
		||||
 | 
			
		||||
        // specify the chart options
 | 
			
		||||
        var chartOptions = {
 | 
			
		||||
          chart: {
 | 
			
		||||
            renderTo: 'chart-container',
 | 
			
		||||
            defaultSeriesType: 'line',
 | 
			
		||||
            backgroundColor: '#ffffff',
 | 
			
		||||
            events: { }
 | 
			
		||||
          },
 | 
			
		||||
          title: { text: chart_title },
 | 
			
		||||
          plotOptions: {
 | 
			
		||||
            series: {
 | 
			
		||||
              marker: { radius: 3 },
 | 
			
		||||
              animation: true,
 | 
			
		||||
              step: false,
 | 
			
		||||
              borderWidth: 0,
 | 
			
		||||
              turboThreshold: 0
 | 
			
		||||
            }
 | 
			
		||||
          },
 | 
			
		||||
          tooltip: {
 | 
			
		||||
            // reformat the tooltips so that local times are displayed
 | 
			
		||||
            formatter: function() {
 | 
			
		||||
              var d = new Date(this.x + (my_offset*60000));
 | 
			
		||||
              var n = (this.point.name === undefined) ? '' : '<br>' + this.point.name;
 | 
			
		||||
              return this.series.name + ':<b>' + this.y + '</b>' + n + '<br>' + d.toDateString() + '<br>' + d.toTimeString().replace(/\(.*\)/, "");
 | 
			
		||||
            }
 | 
			
		||||
          },
 | 
			
		||||
          xAxis: {
 | 
			
		||||
            type: 'datetime',
 | 
			
		||||
            title: { text: 'Date' }
 | 
			
		||||
          },
 | 
			
		||||
          yAxis: { title: { text: y_axis_title } },
 | 
			
		||||
          exporting: { enabled: false },
 | 
			
		||||
          legend: { enabled: false },
 | 
			
		||||
          credits: {
 | 
			
		||||
            text: 'ThingSpeak.com',
 | 
			
		||||
            href: 'https://thingspeak.com/',
 | 
			
		||||
            style: { color: '#D62020' }
 | 
			
		||||
          }
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        // draw the chart
 | 
			
		||||
        my_chart = new Highcharts.Chart(chartOptions);
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      // add a series to the chart
 | 
			
		||||
      function addSeries(channel_id, field_number, api_key, results, color) {
 | 
			
		||||
        var field_name = 'field' + field_number;
 | 
			
		||||
 | 
			
		||||
        // get the data with a webservice call
 | 
			
		||||
        $.getJSON('https://api.thingspeak.com/channels/' + channel_id + '/fields/' + field_number + '.json?offset=0&round=2&results=' + results + '&api_key=' + api_key, function(data) {
 | 
			
		||||
 | 
			
		||||
          // blank array for holding chart data
 | 
			
		||||
          var chart_data = [];
 | 
			
		||||
 | 
			
		||||
          // iterate through each feed
 | 
			
		||||
          $.each(data.feeds, function() {
 | 
			
		||||
            var point = new Highcharts.Point();
 | 
			
		||||
            // set the proper values
 | 
			
		||||
            var value = this[field_name];
 | 
			
		||||
            point.x = getChartDate(this.created_at);
 | 
			
		||||
            point.y = parseFloat(value);
 | 
			
		||||
            // add location if possible
 | 
			
		||||
            if (this.location) { point.name = this.location; }
 | 
			
		||||
            // if a numerical value exists add it
 | 
			
		||||
            if (!isNaN(parseInt(value))) { chart_data.push(point); }
 | 
			
		||||
          });
 | 
			
		||||
 | 
			
		||||
          // add the chart data
 | 
			
		||||
          my_chart.addSeries({ data: chart_data, name: data.channel[field_name], color: color });
 | 
			
		||||
        });
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      // converts date format from JSON
 | 
			
		||||
      function getChartDate(d) {
 | 
			
		||||
        // get the data using javascript's date object (year, month, day, hour, minute, second)
 | 
			
		||||
        // months in javascript start at 0, so remember to subtract 1 when specifying the month
 | 
			
		||||
        // offset in minutes is converted to milliseconds and subtracted so that chart's x-axis is correct
 | 
			
		||||
        return Date.UTC(d.substring(0,4), d.substring(5,7)-1, d.substring(8,10), d.substring(11,13), d.substring(14,16), d.substring(17,19)) - (my_offset * 60000);
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
  </script>
 | 
			
		||||
</head>
 | 
			
		||||
<body style='background-color: white; height: 100%; margin: 0; padding: 0;'>
 | 
			
		||||
  <div id="chart-container" style="width: 800px; height: 400px; display: block; position:absolute; bottom:0; top:0; left:0; right:0; margin: 5px 15px 15px 0; overflow: hidden;">
 | 
			
		||||
    <%= image_tag 'ajax-loader.gif', :style => "position: absolute; margin: auto; top: 0; left: 0; right: 0; bottom: 0;" %>
 | 
			
		||||
  </div>
 | 
			
		||||
</body>
 | 
			
		||||
</html>
 | 
			
		||||
 | 
			
		||||
@@ -14,7 +14,7 @@
 | 
			
		||||
  .CodeMirror-scroll { height: 300px; }
 | 
			
		||||
</style>
 | 
			
		||||
 | 
			
		||||
<div class="col-sm-8 col-xs-8">
 | 
			
		||||
<div class="col-sm-8 col-xs-12">
 | 
			
		||||
 | 
			
		||||
  <ol class="breadcrumb" data-no-turbolink>
 | 
			
		||||
    <li><%= link_to t(:plugins), plugins_path %></li>
 | 
			
		||||
@@ -27,7 +27,7 @@
 | 
			
		||||
    <input name='userlogin' class='userlogin' />
 | 
			
		||||
 | 
			
		||||
    <div class="form-group">
 | 
			
		||||
      <div class="col-sm-offset-2 col-sm-10 col-xs-offset-3 col-xs-6">
 | 
			
		||||
      <div class="col-sm-offset-2 col-sm-10 col-xs-12">
 | 
			
		||||
        <div class="checkbox">
 | 
			
		||||
          <label>
 | 
			
		||||
            <%= c.check_box :private_flag %>
 | 
			
		||||
@@ -38,23 +38,23 @@
 | 
			
		||||
    </div>
 | 
			
		||||
 | 
			
		||||
    <div class="form-group">
 | 
			
		||||
      <label class="col-sm-2 col-xs-3 control-label"><%= t(:plugin_name) %></label>
 | 
			
		||||
      <div class="col-sm-10 col-xs-6"><%= c.text_field :name, :class => 'form-control' %></div>
 | 
			
		||||
      <label class="col-sm-2 col-xs-12 control-label"><%= t(:plugin_name) %></label>
 | 
			
		||||
      <div class="col-sm-10 col-xs-12"><%= c.text_field :name, :class => 'form-control' %></div>
 | 
			
		||||
    </div>
 | 
			
		||||
 | 
			
		||||
    <div class="form-group">
 | 
			
		||||
      <label class="col-sm-2 col-xs-3 control-label"><%= t(:plugin_html) %></label>
 | 
			
		||||
      <div class="col-sm-10 col-xs-6"><%= c.text_area :html, :class => 'form-control', :rows => 14 %></div>
 | 
			
		||||
      <label class="col-sm-2 col-xs-12 control-label"><%= t(:plugin_html) %></label>
 | 
			
		||||
      <div class="col-sm-10 col-xs-12"><%= c.text_area :html, :class => 'form-control', :rows => 14 %></div>
 | 
			
		||||
    </div>
 | 
			
		||||
 | 
			
		||||
    <div class="form-group">
 | 
			
		||||
      <label class="col-sm-2 col-xs-3 control-label"><%= t(:plugin_css) %></label>
 | 
			
		||||
      <div class="col-sm-10 col-xs-6"><%= c.text_area :css, :class => 'form-control', :rows => 14 %></div>
 | 
			
		||||
      <label class="col-sm-2 col-xs-12 control-label"><%= t(:plugin_css) %></label>
 | 
			
		||||
      <div class="col-sm-10 col-xs-12"><%= c.text_area :css, :class => 'form-control', :rows => 14 %></div>
 | 
			
		||||
    </div>
 | 
			
		||||
 | 
			
		||||
    <div class="form-group">
 | 
			
		||||
      <label class="col-sm-2 col-xs-3 control-label"><%= t(:plugin_js) %></label>
 | 
			
		||||
      <div class="col-sm-10 col-xs-6"><%= c.text_area :js, :class => 'form-control', :rows => 14 %></div>
 | 
			
		||||
      <label class="col-sm-2 col-xs-12 control-label"><%= t(:plugin_js) %></label>
 | 
			
		||||
      <div class="col-sm-10 col-xs-12"><%= c.text_area :js, :class => 'form-control', :rows => 14 %></div>
 | 
			
		||||
    </div>
 | 
			
		||||
 | 
			
		||||
    <div class="form-group">
 | 
			
		||||
 
 | 
			
		||||
@@ -3,11 +3,8 @@
 | 
			
		||||
 | 
			
		||||
    <h4 class='breadcrumb'><%= t(:plugins) %></h4>
 | 
			
		||||
 | 
			
		||||
    <%= form_for :plugin do |p| %>
 | 
			
		||||
      <input name='userlogin' class='userlogin' />
 | 
			
		||||
      <%= p.submit t(:plugin_create), :class => 'btn btn-primary' %>
 | 
			
		||||
    <% end %>
 | 
			
		||||
    <br>
 | 
			
		||||
    <%= link_to t(:plugin_new), new_plugin_path, class: 'btn btn-primary' %>
 | 
			
		||||
    <br><br>
 | 
			
		||||
 | 
			
		||||
    <% if @plugins.length > 0 %>
 | 
			
		||||
      <table class="table table-striped table-bordered tablesorter">
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										61
									
								
								app/views/plugins/new.html.erb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										61
									
								
								app/views/plugins/new.html.erb
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,61 @@
 | 
			
		||||
<div class="row">
 | 
			
		||||
  <div class="col-xs-12 col-sm-6">
 | 
			
		||||
 | 
			
		||||
    <ol class="breadcrumb">
 | 
			
		||||
      <li><%= link_to t(:plugins), plugins_path %></li>
 | 
			
		||||
      <li class="active"><%= t(:new) %></li>
 | 
			
		||||
    </ol>
 | 
			
		||||
 | 
			
		||||
    <h3><%= t(:plugin_new) %></h3>
 | 
			
		||||
    <br>
 | 
			
		||||
    <div><%= t(:plugin_new_message) %></div>
 | 
			
		||||
    <br>
 | 
			
		||||
 | 
			
		||||
    <%= form_for Plugin.new, :html => {:class => 'form-horizontal'} do |f| %>
 | 
			
		||||
      <div class="form-group">
 | 
			
		||||
        <label class="col-sm-3 col-xs-12 control-label"><%= t(:plugin_template) %></label>
 | 
			
		||||
        <div class="col-sm-9 col-xs-12">
 | 
			
		||||
          <div class="radio">
 | 
			
		||||
            <label>
 | 
			
		||||
              <%= radio_button_tag :template, 'default', :checked => 'checked' %> <%= T(:default) %>
 | 
			
		||||
            </label>
 | 
			
		||||
          </div>
 | 
			
		||||
          <div class="radio">
 | 
			
		||||
            <label>
 | 
			
		||||
              <%= radio_button_tag :template, 'gauge' %> Google Gauge
 | 
			
		||||
            </label>
 | 
			
		||||
          </div>
 | 
			
		||||
          <div class="radio">
 | 
			
		||||
            <label>
 | 
			
		||||
              <%= radio_button_tag :template, 'chart' %> <%= t(:plugin_chart) %>
 | 
			
		||||
            </label>
 | 
			
		||||
          </div>
 | 
			
		||||
        </div>
 | 
			
		||||
      </div>
 | 
			
		||||
 | 
			
		||||
      <div class="form-group">
 | 
			
		||||
        <div class="col-sm-offset-3 col-sm-9">
 | 
			
		||||
          <p class="form-control-static">
 | 
			
		||||
            <%= f.submit t(:plugin_create), :class => 'btn btn-primary' %>
 | 
			
		||||
          </p>
 | 
			
		||||
        </div>
 | 
			
		||||
      </div>
 | 
			
		||||
    <% end %>
 | 
			
		||||
    <br>
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
  </div>
 | 
			
		||||
 | 
			
		||||
  <div id="sidebar" class="col-xs-12 col-sm-6">
 | 
			
		||||
    <h4 class="breadcrumb"><%= t(:help) %></h4>
 | 
			
		||||
 | 
			
		||||
    <div class="col-pad">
 | 
			
		||||
      <%= t(:help_plugins) %>
 | 
			
		||||
      <ul>
 | 
			
		||||
        <li><a href="http://community.thingspeak.com/tutorials/google/display-a-google-gauge-visualization-using-thingspeak-plugins/" target="_blank">Google Gauge Tutorial</a></li>
 | 
			
		||||
      </ul>
 | 
			
		||||
    </div>
 | 
			
		||||
  </div>
 | 
			
		||||
 | 
			
		||||
</div>
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										5
									
								
								app/views/plugins/templates/chart.css
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								app/views/plugins/templates/chart.css
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,5 @@
 | 
			
		||||
<style type="text/css">
 | 
			
		||||
  body { background-color: white; height: 100%; margin: 0; padding: 0; }
 | 
			
		||||
  #chart-container { width: 425px; height: 235px; display: block; position:absolute; bottom:0; top:0; left:0; right:0; margin: 5px 15px 15px 0; overflow: hidden; }
 | 
			
		||||
</style>
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										18
									
								
								app/views/plugins/templates/chart.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								app/views/plugins/templates/chart.html
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,18 @@
 | 
			
		||||
<!DOCTYPE html>
 | 
			
		||||
<html style="height: 100%;">
 | 
			
		||||
  <head>
 | 
			
		||||
    <script type="text/javascript" src="//ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
 | 
			
		||||
    <script type="text/javascript" src="//thingspeak.com/highcharts-3.0.8.js"></script>
 | 
			
		||||
    <script type="text/javascript" src="//thingspeak.com/exporting.js"></script>
 | 
			
		||||
 | 
			
		||||
    %%PLUGIN_CSS%%
 | 
			
		||||
    %%PLUGIN_JAVASCRIPT%%
 | 
			
		||||
 | 
			
		||||
</head>
 | 
			
		||||
<body>
 | 
			
		||||
  <div id="chart-container">
 | 
			
		||||
    <img alt="Ajax loader" src="/assets/ajax-loader.gif" style="position: absolute; margin: auto; top: 0; left: 0; right: 0; bottom: 0;" />
 | 
			
		||||
  </div>
 | 
			
		||||
</body>
 | 
			
		||||
</html>
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										122
									
								
								app/views/plugins/templates/chart.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										122
									
								
								app/views/plugins/templates/chart.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,122 @@
 | 
			
		||||
<script type="text/javascript">
 | 
			
		||||
  // variables for the first series
 | 
			
		||||
  var series_1_channel_id = 9;
 | 
			
		||||
  var series_1_field_number = 1;
 | 
			
		||||
  var series_1_read_api_key = '';
 | 
			
		||||
  var series_1_results = 10;
 | 
			
		||||
  var series_1_color = '#d62020';
 | 
			
		||||
 | 
			
		||||
  // variables for the second series
 | 
			
		||||
  var series_2_channel_id = 9;
 | 
			
		||||
  var series_2_field_number = 2;
 | 
			
		||||
  var series_2_read_api_key = '';
 | 
			
		||||
  var series_2_results = 10;
 | 
			
		||||
  var series_2_color = '#00aaff';
 | 
			
		||||
 | 
			
		||||
  // chart title
 | 
			
		||||
  var chart_title = 'Light & Temperature';
 | 
			
		||||
  // y axis title
 | 
			
		||||
  var y_axis_title = 'Values';
 | 
			
		||||
 | 
			
		||||
  // user's timezone offset
 | 
			
		||||
  var my_offset = new Date().getTimezoneOffset();
 | 
			
		||||
  // chart variable
 | 
			
		||||
  var my_chart;
 | 
			
		||||
 | 
			
		||||
  // when the document is ready
 | 
			
		||||
  $(document).on('ready', function() {
 | 
			
		||||
    // add a blank chart
 | 
			
		||||
    addChart();
 | 
			
		||||
    // add the first series
 | 
			
		||||
    addSeries(series_1_channel_id, series_1_field_number, series_1_read_api_key, series_1_results, series_1_color);
 | 
			
		||||
    // add the second series
 | 
			
		||||
    addSeries(series_2_channel_id, series_2_field_number, series_2_read_api_key, series_2_results, series_2_color);
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  // add the base chart
 | 
			
		||||
  function addChart() {
 | 
			
		||||
    // variable for the local date in milliseconds
 | 
			
		||||
    var localDate;
 | 
			
		||||
 | 
			
		||||
    // specify the chart options
 | 
			
		||||
    var chartOptions = {
 | 
			
		||||
      chart: {
 | 
			
		||||
        renderTo: 'chart-container',
 | 
			
		||||
        defaultSeriesType: 'line',
 | 
			
		||||
        backgroundColor: '#ffffff',
 | 
			
		||||
        events: { }
 | 
			
		||||
      },
 | 
			
		||||
      title: { text: chart_title },
 | 
			
		||||
      plotOptions: {
 | 
			
		||||
        series: {
 | 
			
		||||
          marker: { radius: 3 },
 | 
			
		||||
          animation: true,
 | 
			
		||||
          step: false,
 | 
			
		||||
          borderWidth: 0,
 | 
			
		||||
          turboThreshold: 0
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      tooltip: {
 | 
			
		||||
        // reformat the tooltips so that local times are displayed
 | 
			
		||||
        formatter: function() {
 | 
			
		||||
          var d = new Date(this.x + (my_offset*60000));
 | 
			
		||||
          var n = (this.point.name === undefined) ? '' : '<br>' + this.point.name;
 | 
			
		||||
          return this.series.name + ':<b>' + this.y + '</b>' + n + '<br>' + d.toDateString() + '<br>' + d.toTimeString().replace(/\(.*\)/, "");
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      xAxis: {
 | 
			
		||||
        type: 'datetime',
 | 
			
		||||
        title: { text: 'Date' }
 | 
			
		||||
      },
 | 
			
		||||
      yAxis: { title: { text: y_axis_title } },
 | 
			
		||||
      exporting: { enabled: false },
 | 
			
		||||
      legend: { enabled: false },
 | 
			
		||||
      credits: {
 | 
			
		||||
        text: 'ThingSpeak.com',
 | 
			
		||||
        href: 'https://thingspeak.com/',
 | 
			
		||||
        style: { color: '#D62020' }
 | 
			
		||||
      }
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    // draw the chart
 | 
			
		||||
    my_chart = new Highcharts.Chart(chartOptions);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // add a series to the chart
 | 
			
		||||
  function addSeries(channel_id, field_number, api_key, results, color) {
 | 
			
		||||
    var field_name = 'field' + field_number;
 | 
			
		||||
 | 
			
		||||
    // get the data with a webservice call
 | 
			
		||||
    $.getJSON('https://api.thingspeak.com/channels/' + channel_id + '/fields/' + field_number + '.json?offset=0&round=2&results=' + results + '&api_key=' + api_key, function(data) {
 | 
			
		||||
 | 
			
		||||
      // blank array for holding chart data
 | 
			
		||||
      var chart_data = [];
 | 
			
		||||
 | 
			
		||||
      // iterate through each feed
 | 
			
		||||
      $.each(data.feeds, function() {
 | 
			
		||||
        var point = new Highcharts.Point();
 | 
			
		||||
        // set the proper values
 | 
			
		||||
        var value = this[field_name];
 | 
			
		||||
        point.x = getChartDate(this.created_at);
 | 
			
		||||
        point.y = parseFloat(value);
 | 
			
		||||
        // add location if possible
 | 
			
		||||
        if (this.location) { point.name = this.location; }
 | 
			
		||||
        // if a numerical value exists add it
 | 
			
		||||
        if (!isNaN(parseInt(value))) { chart_data.push(point); }
 | 
			
		||||
      });
 | 
			
		||||
 | 
			
		||||
      // add the chart data
 | 
			
		||||
      my_chart.addSeries({ data: chart_data, name: data.channel[field_name], color: color });
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // converts date format from JSON
 | 
			
		||||
  function getChartDate(d) {
 | 
			
		||||
    // get the data using javascript's date object (year, month, day, hour, minute, second)
 | 
			
		||||
    // months in javascript start at 0, so remember to subtract 1 when specifying the month
 | 
			
		||||
    // offset in minutes is converted to milliseconds and subtracted so that chart's x-axis is correct
 | 
			
		||||
    return Date.UTC(d.substring(0,4), d.substring(5,7)-1, d.substring(8,10), d.substring(11,13), d.substring(14,16), d.substring(17,19)) - (my_offset * 60000);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										7
									
								
								app/views/plugins/templates/gauge.css
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								app/views/plugins/templates/gauge.css
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,7 @@
 | 
			
		||||
<style type="text/css">
 | 
			
		||||
  body { background-color: #ddd; }
 | 
			
		||||
  #container { height: 100%; width: 100%; display: table; }
 | 
			
		||||
  #inner { vertical-align: middle; display: table-cell; }
 | 
			
		||||
  #gauge_div { width: 120px; margin: 0 auto; }
 | 
			
		||||
</style>
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										19
									
								
								app/views/plugins/templates/gauge.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								app/views/plugins/templates/gauge.html
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,19 @@
 | 
			
		||||
<html>
 | 
			
		||||
  <head>
 | 
			
		||||
 | 
			
		||||
  <title>Google Gauge - ThingSpeak</title>
 | 
			
		||||
 | 
			
		||||
  %%PLUGIN_CSS%%
 | 
			
		||||
  %%PLUGIN_JAVASCRIPT%%
 | 
			
		||||
 | 
			
		||||
  </head>
 | 
			
		||||
 | 
			
		||||
  <body>
 | 
			
		||||
    <div id="container">
 | 
			
		||||
      <div id="inner">
 | 
			
		||||
        <div id="gauge_div"></div>
 | 
			
		||||
      </div>
 | 
			
		||||
    </div>
 | 
			
		||||
  </body>
 | 
			
		||||
</html>
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										66
									
								
								app/views/plugins/templates/gauge.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										66
									
								
								app/views/plugins/templates/gauge.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,66 @@
 | 
			
		||||
<script type='text/javascript' src='https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js'></script>
 | 
			
		||||
<script type='text/javascript' src='https://www.google.com/jsapi'></script>
 | 
			
		||||
<script type='text/javascript'>
 | 
			
		||||
 | 
			
		||||
  // set your channel id here
 | 
			
		||||
  var channel_id = 9;
 | 
			
		||||
  // set your channel's read api key here if necessary
 | 
			
		||||
  var api_key = '';
 | 
			
		||||
  // maximum value for the gauge
 | 
			
		||||
  var max_gauge_value = 1023;
 | 
			
		||||
  // name of the gauge
 | 
			
		||||
  var gauge_name = 'Light Level';
 | 
			
		||||
 | 
			
		||||
  // global variables
 | 
			
		||||
  var chart, charts, data;
 | 
			
		||||
 | 
			
		||||
  // load the google gauge visualization
 | 
			
		||||
  google.load('visualization', '1', {packages:['gauge']});
 | 
			
		||||
  google.setOnLoadCallback(initChart);
 | 
			
		||||
 | 
			
		||||
  // display the data
 | 
			
		||||
  function displayData(point) {
 | 
			
		||||
    data.setValue(0, 0, gauge_name);
 | 
			
		||||
    data.setValue(0, 1, point);
 | 
			
		||||
    chart.draw(data, options);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // load the data
 | 
			
		||||
  function loadData() {
 | 
			
		||||
    // variable for the data point
 | 
			
		||||
    var p;
 | 
			
		||||
 | 
			
		||||
    // get the data from thingspeak
 | 
			
		||||
    $.getJSON('https://api.thingspeak.com/channels/' + channel_id + '/feed/last.json?api_key=' + api_key, function(data) {
 | 
			
		||||
 | 
			
		||||
      // get the data point
 | 
			
		||||
      p = data.field1;
 | 
			
		||||
 | 
			
		||||
      // if there is a data point display it
 | 
			
		||||
      if (p) {
 | 
			
		||||
        p = Math.round((p / max_gauge_value) * 100);
 | 
			
		||||
        displayData(p);
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // initialize the chart
 | 
			
		||||
  function initChart() {
 | 
			
		||||
 | 
			
		||||
    data = new google.visualization.DataTable();
 | 
			
		||||
    data.addColumn('string', 'Label');
 | 
			
		||||
    data.addColumn('number', 'Value');
 | 
			
		||||
    data.addRows(1);
 | 
			
		||||
 | 
			
		||||
    chart = new google.visualization.Gauge(document.getElementById('gauge_div'));
 | 
			
		||||
    options = {width: 120, height: 120, redFrom: 90, redTo: 100, yellowFrom:75, yellowTo: 90, minorTicks: 5};
 | 
			
		||||
 | 
			
		||||
    loadData();
 | 
			
		||||
 | 
			
		||||
    // load new data every 15 seconds
 | 
			
		||||
    setInterval('loadData()', 15000);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
@@ -108,6 +108,7 @@ en:
 | 
			
		||||
  created: "Created"
 | 
			
		||||
  data: "Data"
 | 
			
		||||
  days: "Days"
 | 
			
		||||
  default: 'default'
 | 
			
		||||
  delete: "delete"
 | 
			
		||||
  developer_info: "Developer Info"
 | 
			
		||||
  device_create: "Add New Device"
 | 
			
		||||
@@ -186,7 +187,8 @@ en:
 | 
			
		||||
  password_reset_subject: "ThingSpeak password reset instructions"
 | 
			
		||||
  permission: "You don't have permission to access this"
 | 
			
		||||
  plugin: "Plugin"
 | 
			
		||||
  plugin_create: "Create New Plugin"
 | 
			
		||||
  plugin_chart: "Chart With Multiple Series"
 | 
			
		||||
  plugin_create: "Create Plugin"
 | 
			
		||||
  plugin_css: "CSS"
 | 
			
		||||
  plugin_default_name: "Plugin"
 | 
			
		||||
  plugin_delete: "Delete Plugin"
 | 
			
		||||
@@ -195,7 +197,10 @@ en:
 | 
			
		||||
  plugin_html: "HTML"
 | 
			
		||||
  plugin_js: "JavaScript"
 | 
			
		||||
  plugin_name: "Name"
 | 
			
		||||
  plugin_new: "New Plugin"
 | 
			
		||||
  plugin_new_message: "Please select a template to use with your new plugin."
 | 
			
		||||
  plugin_save: "Save Plugin"
 | 
			
		||||
  plugin_template: "Plugin Template"
 | 
			
		||||
  plugins: "Plugins"
 | 
			
		||||
  plugin_private_flag: "Private?"
 | 
			
		||||
  profile_bio: "Bio"
 | 
			
		||||
 
 | 
			
		||||
@@ -75,6 +75,9 @@ Thingspeak::Application.routes.draw do
 | 
			
		||||
  get 'channels/:channel_id/maps/channel_show' => 'maps#channel_show'
 | 
			
		||||
  get 'channels/:channel_id/status/recent' => 'status#recent'
 | 
			
		||||
 | 
			
		||||
  # multiple series on a chart demo
 | 
			
		||||
  get 'charts/multiple_series' => 'charts#multiple_series'
 | 
			
		||||
 | 
			
		||||
  # nest the following controllers inside channels
 | 
			
		||||
  resources :channels do
 | 
			
		||||
    collection do
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user