add templates for plugins: Google gauge and multiline charts
This commit is contained in:
parent
f1b6f67323
commit
ee05bbd102
@ -1,5 +1,6 @@
|
|||||||
class ChartsController < ApplicationController
|
class ChartsController < ApplicationController
|
||||||
before_filter :require_user, :only => [:edit]
|
before_filter :require_user, :only => [:edit]
|
||||||
|
|
||||||
def edit
|
def edit
|
||||||
# params[:id] is the windows ID
|
# params[:id] is the windows ID
|
||||||
@channel = current_user.channels.find(params[:channel_id])
|
@channel = current_user.channels.find(params[:channel_id])
|
||||||
@ -22,7 +23,6 @@ class ChartsController < ApplicationController
|
|||||||
end
|
end
|
||||||
|
|
||||||
def index
|
def index
|
||||||
|
|
||||||
set_channels_menu
|
set_channels_menu
|
||||||
@channel = Channel.find(params[:channel_id])
|
@channel = Channel.find(params[:channel_id])
|
||||||
@channel_id = params[:channel_id]
|
@channel_id = params[:channel_id]
|
||||||
@ -35,8 +35,12 @@ class ChartsController < ApplicationController
|
|||||||
check_permissions(@channel)
|
check_permissions(@channel)
|
||||||
end
|
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
|
# 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']
|
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
|
render :layout => false
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
# save chart options
|
# save chart options
|
||||||
def update
|
def update
|
||||||
@channel = Channel.find(params[:channel_id])
|
@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)
|
respond_with_error(:error_auth_required) and return if current_user.blank? || (@plugin.user_id != current_user.id)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def new; ; end
|
||||||
|
def edit; ; end
|
||||||
|
|
||||||
def index
|
def index
|
||||||
@plugins = current_user.plugins
|
@plugins = current_user.plugins
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def public_plugins
|
def public_plugins
|
||||||
|
|
||||||
channel_id = params[:channel_id].to_i
|
channel_id = params[:channel_id].to_i
|
||||||
return if channel_id.nil?
|
return if channel_id.nil?
|
||||||
#private page should display all plugins
|
#private page should display all plugins
|
||||||
@ -24,7 +25,6 @@ class PluginsController < ApplicationController
|
|||||||
plugins.each do |plugin|
|
plugins.each do |plugin|
|
||||||
plugin.make_windows channel_id, api_domain #will only make the window the first time
|
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)
|
@plugin_windows = @plugin_windows + plugin.public_dashboard_windows(channel_id)
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
respond_to do |format|
|
respond_to do |format|
|
||||||
@ -53,9 +53,21 @@ class PluginsController < ApplicationController
|
|||||||
def create
|
def create
|
||||||
# add plugin with defaults
|
# add plugin with defaults
|
||||||
@plugin = Plugin.new
|
@plugin = Plugin.new
|
||||||
@plugin.html = read_file('app/views/plugins/default.html')
|
|
||||||
@plugin.css = read_file('app/views/plugins/default.css')
|
# set default template
|
||||||
@plugin.js = read_file('app/views/plugins/default.js')
|
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.user_id = current_user.id
|
||||||
@plugin.private_flag = true
|
@plugin.private_flag = true
|
||||||
@plugin.save
|
@plugin.save
|
||||||
@ -82,7 +94,6 @@ class PluginsController < ApplicationController
|
|||||||
end
|
end
|
||||||
|
|
||||||
def show_public
|
def show_public
|
||||||
|
|
||||||
@plugin = Plugin.find(params[:id])
|
@plugin = Plugin.find(params[:id])
|
||||||
@output = @plugin.html.sub('%%PLUGIN_CSS%%', @plugin.css).sub('%%PLUGIN_JAVASCRIPT%%', @plugin.js)
|
@output = @plugin.html.sub('%%PLUGIN_CSS%%', @plugin.css).sub('%%PLUGIN_JAVASCRIPT%%', @plugin.js)
|
||||||
if @plugin.private?
|
if @plugin.private?
|
||||||
@ -100,9 +111,6 @@ class PluginsController < ApplicationController
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def edit
|
|
||||||
end
|
|
||||||
|
|
||||||
def update
|
def update
|
||||||
@plugin.update_attribute(:name, params[:plugin][:name])
|
@plugin.update_attribute(:name, params[:plugin][:name])
|
||||||
@plugin.update_attribute(:private_flag, params[:plugin][:private_flag])
|
@plugin.update_attribute(:private_flag, params[:plugin][:private_flag])
|
||||||
@ -111,11 +119,9 @@ class PluginsController < ApplicationController
|
|||||||
@plugin.update_attribute(:html,params[:plugin][:html])
|
@plugin.update_attribute(:html,params[:plugin][:html])
|
||||||
|
|
||||||
if @plugin.save
|
if @plugin.save
|
||||||
|
|
||||||
@plugin.update_all_windows
|
@plugin.update_all_windows
|
||||||
redirect_to plugins_path and return
|
redirect_to plugins_path and return
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def ajax_update
|
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; }
|
.CodeMirror-scroll { height: 300px; }
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<div class="col-sm-8 col-xs-8">
|
<div class="col-sm-8 col-xs-12">
|
||||||
|
|
||||||
<ol class="breadcrumb" data-no-turbolink>
|
<ol class="breadcrumb" data-no-turbolink>
|
||||||
<li><%= link_to t(:plugins), plugins_path %></li>
|
<li><%= link_to t(:plugins), plugins_path %></li>
|
||||||
@ -27,7 +27,7 @@
|
|||||||
<input name='userlogin' class='userlogin' />
|
<input name='userlogin' class='userlogin' />
|
||||||
|
|
||||||
<div class="form-group">
|
<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">
|
<div class="checkbox">
|
||||||
<label>
|
<label>
|
||||||
<%= c.check_box :private_flag %>
|
<%= c.check_box :private_flag %>
|
||||||
@ -38,23 +38,23 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="col-sm-2 col-xs-3 control-label"><%= t(:plugin_name) %></label>
|
<label class="col-sm-2 col-xs-12 control-label"><%= t(:plugin_name) %></label>
|
||||||
<div class="col-sm-10 col-xs-6"><%= c.text_field :name, :class => 'form-control' %></div>
|
<div class="col-sm-10 col-xs-12"><%= c.text_field :name, :class => 'form-control' %></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="col-sm-2 col-xs-3 control-label"><%= t(:plugin_html) %></label>
|
<label class="col-sm-2 col-xs-12 control-label"><%= t(:plugin_html) %></label>
|
||||||
<div class="col-sm-10 col-xs-6"><%= c.text_area :html, :class => 'form-control', :rows => 14 %></div>
|
<div class="col-sm-10 col-xs-12"><%= c.text_area :html, :class => 'form-control', :rows => 14 %></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="col-sm-2 col-xs-3 control-label"><%= t(:plugin_css) %></label>
|
<label class="col-sm-2 col-xs-12 control-label"><%= t(:plugin_css) %></label>
|
||||||
<div class="col-sm-10 col-xs-6"><%= c.text_area :css, :class => 'form-control', :rows => 14 %></div>
|
<div class="col-sm-10 col-xs-12"><%= c.text_area :css, :class => 'form-control', :rows => 14 %></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="col-sm-2 col-xs-3 control-label"><%= t(:plugin_js) %></label>
|
<label class="col-sm-2 col-xs-12 control-label"><%= t(:plugin_js) %></label>
|
||||||
<div class="col-sm-10 col-xs-6"><%= c.text_area :js, :class => 'form-control', :rows => 14 %></div>
|
<div class="col-sm-10 col-xs-12"><%= c.text_area :js, :class => 'form-control', :rows => 14 %></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
|
@ -3,11 +3,8 @@
|
|||||||
|
|
||||||
<h4 class='breadcrumb'><%= t(:plugins) %></h4>
|
<h4 class='breadcrumb'><%= t(:plugins) %></h4>
|
||||||
|
|
||||||
<%= form_for :plugin do |p| %>
|
<%= link_to t(:plugin_new), new_plugin_path, class: 'btn btn-primary' %>
|
||||||
<input name='userlogin' class='userlogin' />
|
<br><br>
|
||||||
<%= p.submit t(:plugin_create), :class => 'btn btn-primary' %>
|
|
||||||
<% end %>
|
|
||||||
<br>
|
|
||||||
|
|
||||||
<% if @plugins.length > 0 %>
|
<% if @plugins.length > 0 %>
|
||||||
<table class="table table-striped table-bordered tablesorter">
|
<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"
|
created: "Created"
|
||||||
data: "Data"
|
data: "Data"
|
||||||
days: "Days"
|
days: "Days"
|
||||||
|
default: 'default'
|
||||||
delete: "delete"
|
delete: "delete"
|
||||||
developer_info: "Developer Info"
|
developer_info: "Developer Info"
|
||||||
device_create: "Add New Device"
|
device_create: "Add New Device"
|
||||||
@ -186,7 +187,8 @@ en:
|
|||||||
password_reset_subject: "ThingSpeak password reset instructions"
|
password_reset_subject: "ThingSpeak password reset instructions"
|
||||||
permission: "You don't have permission to access this"
|
permission: "You don't have permission to access this"
|
||||||
plugin: "Plugin"
|
plugin: "Plugin"
|
||||||
plugin_create: "Create New Plugin"
|
plugin_chart: "Chart With Multiple Series"
|
||||||
|
plugin_create: "Create Plugin"
|
||||||
plugin_css: "CSS"
|
plugin_css: "CSS"
|
||||||
plugin_default_name: "Plugin"
|
plugin_default_name: "Plugin"
|
||||||
plugin_delete: "Delete Plugin"
|
plugin_delete: "Delete Plugin"
|
||||||
@ -195,7 +197,10 @@ en:
|
|||||||
plugin_html: "HTML"
|
plugin_html: "HTML"
|
||||||
plugin_js: "JavaScript"
|
plugin_js: "JavaScript"
|
||||||
plugin_name: "Name"
|
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_save: "Save Plugin"
|
||||||
|
plugin_template: "Plugin Template"
|
||||||
plugins: "Plugins"
|
plugins: "Plugins"
|
||||||
plugin_private_flag: "Private?"
|
plugin_private_flag: "Private?"
|
||||||
profile_bio: "Bio"
|
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/maps/channel_show' => 'maps#channel_show'
|
||||||
get 'channels/:channel_id/status/recent' => 'status#recent'
|
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
|
# nest the following controllers inside channels
|
||||||
resources :channels do
|
resources :channels do
|
||||||
collection do
|
collection do
|
||||||
|
Loading…
Reference in New Issue
Block a user