diff --git a/Gemfile b/Gemfile index a364c6a..973c847 100644 --- a/Gemfile +++ b/Gemfile @@ -1,6 +1,6 @@ source 'http://rubygems.org' -gem 'rails', '4.0.2' +gem 'rails', '4.0.3' gem 'jquery-rails', '3.0.4' gem 'rails_autolink' gem 'mysql2' @@ -30,7 +30,7 @@ gem 'rack-utf8_sanitizer' gem 'newrelic_rpm' gem 'actionpack-xml_parser' -# To use debugger +# to use debugger # gem 'ruby-debug' # assets diff --git a/Gemfile.lock b/Gemfile.lock index 9902680..dd16c75 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -17,27 +17,27 @@ GEM remote: http://rubygems.org/ specs: ZenTest (4.9.5) - actionmailer (4.0.2) - actionpack (= 4.0.2) + actionmailer (4.0.3) + actionpack (= 4.0.3) mail (~> 2.5.4) - actionpack (4.0.2) - activesupport (= 4.0.2) + actionpack (4.0.3) + activesupport (= 4.0.3) builder (~> 3.1.0) erubis (~> 2.7.0) rack (~> 1.5.2) rack-test (~> 0.6.2) actionpack-xml_parser (1.0.1) actionpack (>= 4.0.0.rc1) - activemodel (4.0.2) - activesupport (= 4.0.2) + activemodel (4.0.3) + activesupport (= 4.0.3) builder (~> 3.1.0) - activerecord (4.0.2) - activemodel (= 4.0.2) + activerecord (4.0.3) + activemodel (= 4.0.3) activerecord-deprecated_finders (~> 1.0.2) - activesupport (= 4.0.2) + activesupport (= 4.0.3) arel (~> 4.0.0) activerecord-deprecated_finders (1.0.3) - activesupport (4.0.2) + activesupport (4.0.3) i18n (~> 0.6, >= 0.6.4) minitest (~> 4.2) multi_json (~> 1.3) @@ -51,7 +51,7 @@ GEM annotate (2.6.1) activerecord (>= 2.3.0) rake (>= 0.8.7) - arel (4.0.1) + arel (4.0.2) atomic (1.1.14) authlogic (3.3.0) activerecord (>= 3.2) @@ -157,7 +157,7 @@ GEM nokogiri (1.6.1) mini_portile (~> 0.5.0) oauth (0.4.7) - polyglot (0.3.3) + polyglot (0.3.4) quiet_assets (1.0.2) railties (>= 3.1, < 5.0) rack (1.5.2) @@ -167,19 +167,19 @@ GEM rack (>= 1.0) rack-utf8_sanitizer (1.1.0) rack (~> 1.0) - rails (4.0.2) - actionmailer (= 4.0.2) - actionpack (= 4.0.2) - activerecord (= 4.0.2) - activesupport (= 4.0.2) + rails (4.0.3) + actionmailer (= 4.0.3) + actionpack (= 4.0.3) + activerecord (= 4.0.3) + activesupport (= 4.0.3) bundler (>= 1.3.0, < 2.0) - railties (= 4.0.2) + railties (= 4.0.3) sprockets-rails (~> 2.0.0) rails_autolink (1.1.5) rails (> 3.1) - railties (4.0.2) - actionpack (= 4.0.2) - activesupport (= 4.0.2) + railties (4.0.3) + actionpack (= 4.0.3) + activesupport (= 4.0.3) rake (>= 0.8.7) thor (>= 0.18.1, < 2.0) rake (10.1.1) @@ -228,7 +228,7 @@ GEM rack-protection (~> 1.4) tilt (~> 1.3, >= 1.3.4) spork (0.9.2) - sprockets (2.10.1) + sprockets (2.11.0) hike (~> 1.2) multi_json (~> 1.0) rack (~> 1.0) @@ -314,7 +314,7 @@ DEPENDENCIES nokogiri quiet_assets rack-utf8_sanitizer - rails (= 4.0.2) + rails (= 4.0.3) rails_autolink redis resque-scheduler (= 2.3.1) diff --git a/app/assets/images/channel_public_view.png b/app/assets/images/channel_public_view.png new file mode 100644 index 0000000..fa760f5 Binary files /dev/null and b/app/assets/images/channel_public_view.png differ diff --git a/app/assets/javascripts/sidebar.js b/app/assets/javascripts/sidebar.js index 80306ab..bb204a5 100644 --- a/app/assets/javascripts/sidebar.js +++ b/app/assets/javascripts/sidebar.js @@ -1,9 +1,12 @@ -$(document).ready(function() { +// execute on window load (and not document.ready), so that the sidebar is positioned correctly +$(window).load(function() { // if affix function exists if ($.fn.affix) { - // add sidebar affix - $('#bootstrap-sidebar').affix(); + // add sidebar affix, wrapped in a timeout so that it displays correctly + setTimeout(function() { + $('#bootstrap-sidebar').affix(); + }, 100); // add sidebar scrollspy $(document.body).scrollspy({ target: '#leftcol', offset: 300 }); diff --git a/app/assets/stylesheets/custom.css b/app/assets/stylesheets/custom.css index 1bce4c3..9d078bb 100644 --- a/app/assets/stylesheets/custom.css +++ b/app/assets/stylesheets/custom.css @@ -29,6 +29,8 @@ body { padding-top: 70px; } .format-xml { display: none; } .format-block { min-height: 200px; } .format-block-lg { min-height: 350px; } +.format-block-xl { min-height: 400px; } +.format-block-xxl { min-height: 600px; } /* Sticky footer styles @@ -70,7 +72,6 @@ body { .apps a:hover, .apps:hover div { text-decoration: none; } .commentarea { width: 300px; height: 80px; } -#options { float: right; text-align: right; } #login { padding: 6px; border: 1px solid #bbbbbb; diff --git a/app/assets/stylesheets/sidebar.css b/app/assets/stylesheets/sidebar.css index a5acce1..dd24323 100644 --- a/app/assets/stylesheets/sidebar.css +++ b/app/assets/stylesheets/sidebar.css @@ -7,6 +7,13 @@ border: 1px solid #bbb; border-radius: 5px; padding: 2px 0; + margin-top: 20px; + width: 228px; +} + +#bootstrap-sidebar li a { + padding: 2px 8px; + font-size: 16px; } #bootstrap-sidebar li a:hover { @@ -18,11 +25,23 @@ border-right-width:4px; } +#bootstrap-sidebar li.subitem a { + padding-left: 30px; + font-size: 12px; +} + @media (min-width: 979px) { #bootstrap-sidebar.affix-top, #bootstrap-sidebar.affix { position: fixed; top:90px; - width:228px; + width: 228px; + margin-top: 0; + } +} + +@media (max-width: 978px) { + #bootstrap-sidebar.affix-top, #bootstrap-sidebar.affix { + width: 100%; } } diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 85990c6..c1e8276 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -33,8 +33,15 @@ class ApplicationController < ActionController::Base @ssl_api_domain ||= ssl_api_domain @locale ||= get_locale I18n.locale = @locale + # sets timezone for current user, all DateTime outputs will be automatically formatted Time.zone = current_user.time_zone if current_user + + # allows use of daily params + params[:timescale] = '1440' if params[:timescale] == 'daily' + params[:average] = '1440' if params[:average] == 'daily' + params[:median] = '1440' if params[:median] == 'daily' + params[:sum] = '1440' if params[:sum] == 'daily' end # get the locale, but don't fail if header value doesn't exist @@ -90,21 +97,12 @@ class ApplicationController < ActionController::Base return output.join(', ') end - def set_channels_menu - @menu = 'channels' - end - - def set_apps_menu - @menu = 'apps' - end - - def set_plugins_menu - @menu = 'plugins' - end - - def set_devices_menu - @menu = 'devices' - end + # set menus + def set_support_menu; @menu = 'support'; end + def set_channels_menu; @menu = 'channels'; end + def set_apps_menu; @menu = 'apps'; end + def set_plugins_menu; @menu = 'plugins'; end + def set_devices_menu; @menu = 'devices'; end def current_user_session return @current_user_session if defined?(@current_user_session) diff --git a/app/controllers/channels_controller.rb b/app/controllers/channels_controller.rb index f8e69e9..d8a6217 100644 --- a/app/controllers/channels_controller.rb +++ b/app/controllers/channels_controller.rb @@ -29,10 +29,19 @@ class ChannelsController < ApplicationController # list public channels def public + @domain = domain + # default blank response + @channels = Channel.where(:id => 0).paginate :page => params[:page] + # get channels by ids if params[:channel_ids].present? flash[:notice] = t(:selected_channels) @channels = Channel.public_viewable.by_array(params[:channel_ids]).order('ranking desc, updated_at DESC').paginate :page => params[:page] + # get channels that match a user + elsif params[:username].present? + flash[:notice] = "#{t(:user).capitalize}: #{params[:username]}" + searched_user = User.find_by_login(params[:username]) + @channels = searched_user.channels.public_viewable.active.order('ranking desc, updated_at DESC').paginate :page => params[:page] if searched_user.present? # get channels that match a tag elsif params[:tag].present? flash[:notice] = "#{t(:tag).capitalize}: #{params[:tag]}" @@ -40,6 +49,7 @@ class ChannelsController < ApplicationController # normal channel list else flash[:notice] = t(:featured_channels) + respond_with_error(:error_resource_not_found) and return if params[:page] == '0' @channels = Channel.public_viewable.active.order('ranking desc, updated_at DESC').paginate :page => params[:page] end @@ -311,8 +321,12 @@ class ChannelsController < ApplicationController # if there is a talkback_key but no command respond_with_blank and return if params[:talkback_key].present? && command.blank? - # normal route, respond with the entry id of the feed - render :text => status + # normal route, respond with the feed + respond_to do |format| + format.html { render :text => status } + format.json { render :json => feed.to_json } + format.xml { render :xml => feed.to_xml(Feed.public_options) } + end and return end # import view diff --git a/app/controllers/docs_controller.rb b/app/controllers/docs_controller.rb index 3a4b123..c3afcb3 100644 --- a/app/controllers/docs_controller.rb +++ b/app/controllers/docs_controller.rb @@ -1,6 +1,43 @@ class DocsController < ApplicationController + before_filter :set_support_menu - def index; ;end + def index; ; end + def errors; ; end + def tweetcontrol; ; end + def plugins; ; end + def importer; ; end + def charts; ; end + def users; ; end + + def channels + # default values + @channel_api_key = 'XXXXXXXXXXXXXXXX' + + # if user is signed in + if current_user && current_user.channels.any? + @channel_api_key = current_user.channels.order('updated_at desc').first.write_api_key + end + end + + def thinghttp + # default values + @thinghttp_api_key = 'XXXXXXXXXXXXXXXX' + + # if user is signed in + if current_user && current_user.thinghttps.any? + @thinghttp_api_key = current_user.thinghttps.order('updated_at desc').first.api_key + end + end + + def thingtweet + # default values + @thingtweet_api_key = 'XXXXXXXXXXXXXXXX' + + # if user is signed in + if current_user && current_user.twitter_accounts.any? + @thingtweet_api_key = current_user.twitter_accounts.order('updated_at desc').first.api_key + end + end def talkback # default values diff --git a/app/controllers/feed_controller.rb b/app/controllers/feed_controller.rb index 8bbf376..669e01d 100644 --- a/app/controllers/feed_controller.rb +++ b/app/controllers/feed_controller.rb @@ -194,13 +194,13 @@ class FeedController < ApplicationController output = @feed.to_xml elsif params[:format] == 'csv' @csv_headers = Feed.select_options(@channel, params) - elsif (params[:format] == 'txt' or params[:format] == 'text') + elsif (params[:format] == 'txt' || params[:format] == 'text' || params[:format] == 'html') output = add_prepend_append(@feed["field#{params[:field_id]}"]) else output = @feed.to_json - end - # else set error code + + # else set error code else if params[:format] == 'xml' output = bad_feed_xml @@ -211,7 +211,7 @@ class FeedController < ApplicationController # output data in proper format respond_to do |format| - format.html { render :json => output } + format.html { render :text => output } format.json { render :json => output, :callback => params[:callback] } format.xml { render :xml => output } format.csv diff --git a/app/models/channel.rb b/app/models/channel.rb index f1597e6..d6bba02 100644 --- a/app/models/channel.rb +++ b/app/models/channel.rb @@ -81,6 +81,11 @@ class Channel < ActiveRecord::Base cattr_reader :per_page @@per_page = 15 + # write key for a channel + def write_api_key + self.api_keys.where(:write_flag => true).first.api_key + end + # select options def select_options only = [:name, :created_at, :updated_at, :id, :last_entry_id] diff --git a/app/models/feed.rb b/app/models/feed.rb index f7a4563..c9f12a4 100644 --- a/app/models/feed.rb +++ b/app/models/feed.rb @@ -33,6 +33,13 @@ class Feed < ActiveRecord::Base attr_readonly :created_at + # for to_xml, return only the public attributes + def self.public_options + { + :except => [:id, :updated_at] + } + end + # only output these fields for feed def self.select_options(channel, params) only = [:created_at] @@ -96,7 +103,7 @@ class Feed < ActiveRecord::Base # custom json output def as_json(options = {}) - super(options.merge(:except => [:updated_at, :id])) + super(Feed.public_options) end # check if a field value is a number diff --git a/app/views/api_keys/_index.html.erb b/app/views/api_keys/_index.html.erb index 9240fa9..1e1675e 100644 --- a/app/views/api_keys/_index.html.erb +++ b/app/views/api_keys/_index.html.erb @@ -39,7 +39,7 @@
http://api.thingspeak.com+
https://api.thingspeak.com+
http://184.106.153.149+
http://api.thingspeak.com/crossdomain.xml+
<%= @ssl_api_domain %>channels/CHANNEL_ID/feeds.json.xml
,
+replacing CHANNEL_ID with the ID of your Channel.
+
+GET <%= @ssl_api_domain %>channels/9/feeds.json.xml?results=2
+
++{ + "channel": + { + "id": 9, + "name": "my_house", + "description": "Netduino Plus connected to sensors around the house", + "latitude": "40.44", + "longitude": "-79.996", + "field1": "Light", + "field2": "Outside Temperature", + "created_at": "2010-12-13T20:20:06-05:00", + "updated_at": "2014-02-26T12:43:04-05:00", + "last_entry_id": 6060625 + }, + "feeds": + [ + { + "created_at": "2014-02-26T12:42:49-05:00", + "entry_id": 6060624, + "field1": "188", + "field2": "25.902335456475583" + }, + { + "created_at": "2014-02-26T12:43:04-05:00", + "entry_id": 6060625, + "field1": "164", + "field2": "25.222929936305732" + } + ] +} ++
+{ + "channel": + { + "id": 9, + "name": "my_house", + "description": "Netduino Plus connected to sensors around the house", + "latitude": "40.44", + "longitude": "-79.996", + "field1": "Light", + "field2": "Outside Temperature", + "created_at": "2010-12-13T20:20:06-05:00", + "updated_at": "2014-02-26T12:43:04-05:00", + "last_entry_id": 6060625 + }, + "feeds": + [ + { + "created_at": "2014-02-26T12:42:49-05:00", + "entry_id": 6060624, + "field1": "188", + "field2": "25.902335456475583" + }, + { + "created_at": "2014-02-26T12:43:04-05:00", + "entry_id": 6060625, + "field1": "164", + "field2": "25.222929936305732" + } + ] +} ++ +
+<?xml version="1.0" encoding="UTF-8"?> +<channel> + <id type="integer">9</id> + <name>my_house</name> + <description>Netduino Plus connected to sensors around the house</description> + <latitude type="decimal">40.44</latitude> + <longitude type="decimal">-79.996</longitude> + <field1>Light</field1> + <field2>Outside Temperature</field2> + <created-at type="dateTime">2010-12-13T20:20:06-05:00</created-at> + <updated-at type="dateTime">2014-02-26T12:49:19-05:00</updated-at> + <last-entry-id type="integer">6060650</last-entry-id> + <feeds type="array"> + <feed> + <created-at type="dateTime">2014-02-26T12:49:04-05:00</created-at> + <entry-id type="integer">6060649</entry-id> + <field1>160</field1> + <field2>25.307855626326962</field2> + <id type="integer" nil="true"/> + </feed> + <feed> + <created-at type="dateTime">2014-02-26T12:49:19-05:00</created-at> + <entry-id type="integer">6060650</entry-id> + <field1>171</field1> + <field2>22.929936305732483</field2> + <id type="integer" nil="true"/> + </feed> + </feeds> +</channel> ++ +
<%= @ssl_api_domain %>channels/CHANNEL_ID/feeds/last.json.xml
,
+replacing CHANNEL_ID with the ID of your Channel.
+
+GET <%= @ssl_api_domain %>channels/9/feeds/last.json.xml
+
++{ + "created_at": "2014-02-26T21:27:21Z", + "entry_id": 6061519, + "field1": "176", + "field2": "28.195329087048833" +} ++
+{ + "created_at": "2014-02-26T21:27:21Z", + "entry_id": 6061519, + "field1": "176", + "field2": "28.195329087048833" +} ++ +
+<?xml version="1.0" encoding="UTF-8"?> +<feed> + <created-at type="dateTime">2014-02-26T21:28:51Z</created-at> + <entry-id type="integer">6061525</entry-id> + <field1>200</field1> + <field2>28.365180467091296</field2> + <id type="integer" nil="true"/> +</feed> ++ +
<%= @ssl_api_domain %>channels/CHANNEL_ID/feeds/ENTRY_ID.json.xml
,
+replacing CHANNEL_ID with the ID of your Channel and ENTRY_ID with the ID of your entry.
+
+GET <%= @ssl_api_domain %>channels/9/feeds/6061519.json.xml
+
++{ + "created_at": "2014-02-26T21:27:21Z", + "entry_id": 6061519, + "field1": "176", + "field2": "28.195329087048833" +} +
+{ + "created_at": "2014-02-26T21:27:21Z", + "entry_id": 6061519, + "field1": "176", + "field2": "28.195329087048833" +} ++ +
+<?xml version="1.0" encoding="UTF-8"?> +<feed> + <created-at type="dateTime">2014-02-26T21:27:21Z</created-at> + <entry-id type="integer">6061519</entry-id> + <field1>176</field1> + <field2>28.195329087048833</field2> + <id type="integer" nil="true"/> +</feed> ++ +
<%= @ssl_api_domain %>channels/CHANNEL_ID/fields/FIELD_ID.json.xml
,
+replacing CHANNEL_ID with the ID of your Channel and FIELD_ID with the ID of your field.
+
+GET <%= @ssl_api_domain %>channels/9/fields/1.json.xml?results=2
+
++{ + "channel": + { + "id": 9, + "name": "my_house", + "description": "Netduino Plus connected to sensors around the house", + "latitude": "40.44", + "longitude": "-79.996", + "field1": "Light", + "field2": "Outside Temperature", + "created_at": "2010-12-13T20:20:06-05:00", + "updated_at": "2014-02-26T12:43:04-05:00", + "last_entry_id": 6060625 + }, + "feeds": + [ + { + "created_at": "2014-02-26T12:42:49-05:00", + "entry_id": 6060624, + "field1": "188" + }, + { + "created_at": "2014-02-26T12:43:04-05:00", + "entry_id": 6060625, + "field1": "164" + } + ] +} ++
+{ + "channel": + { + "id": 9, + "name": "my_house", + "description": "Netduino Plus connected to sensors around the house", + "latitude": "40.44", + "longitude": "-79.996", + "field1": "Light", + "field2": "Outside Temperature", + "created_at": "2010-12-13T20:20:06-05:00", + "updated_at": "2014-02-26T12:43:04-05:00", + "last_entry_id": 6060625 + }, + "feeds": + [ + { + "created_at": "2014-02-26T12:42:49-05:00", + "entry_id": 6060624, + "field1": "188" + }, + { + "created_at": "2014-02-26T12:43:04-05:00", + "entry_id": 6060625, + "field1": "164" + } + ] +} ++ +
+<?xml version="1.0" encoding="UTF-8"?> +<channel> + <id type="integer">9</id> + <name>my_house</name> + <description>Netduino Plus connected to sensors around the house</description> + <latitude type="decimal">40.44</latitude> + <longitude type="decimal">-79.996</longitude> + <field1>Light</field1> + <field2>Outside Temperature</field2> + <created-at type="dateTime">2010-12-13T20:20:06-05:00</created-at> + <updated-at type="dateTime">2014-02-26T12:49:19-05:00</updated-at> + <last-entry-id type="integer">6060650</last-entry-id> + <feeds type="array"> + <feed> + <created-at type="dateTime">2014-02-26T12:49:04-05:00</created-at> + <entry-id type="integer">6060649</entry-id> + <field1>160</field1> + <id type="integer" nil="true"/> + </feed> + <feed> + <created-at type="dateTime">2014-02-26T12:49:19-05:00</created-at> + <entry-id type="integer">6060650</entry-id> + <field1>171</field1> + <id type="integer" nil="true"/> + </feed> + </feeds> +</channel> ++ +
<%= @ssl_api_domain %>channels/CHANNEL_ID/fields/FIELD_ID/last.json.xml
,
+replacing CHANNEL_ID with the ID of your Channel and FIELD_ID with the ID of your field.
+
+GET <%= @ssl_api_domain %>channels/9/fields/1/last.json.xml
+
++176 ++
+{ + "created_at": "2014-02-26T21:27:21Z", + "entry_id": 6061519, + "field1": "176" +} ++ +
+<?xml version="1.0" encoding="UTF-8"?> +<feed> + <created-at type="dateTime">2014-02-26T21:28:51Z</created-at> + <entry-id type="integer">6061525</entry-id> + <field1>200</field1> + <id type="integer" nil="true"/> +</feed> ++ +
Using the ThingSpeak Importer, you are able to import data from a CSV file directly into a ThingSpeak Channel. The access the Importer, select a Channel, and click Import Data.
+The format for the CSV should be the following:
+datetime,field1,field2,field3,field4,field5,field6,field7,field8,latitude,longitude,elevation,status+
Here is an example CSV file: Sample CSV File
+You only have to send a datetime stamp and at least one field. The datetime stamp can be in many formats such as epoch, ISO 8601, or MySQL time. If the datetime includes a GMT/UTC offset, we will use that to properly import the data. If your datetime stamps do not have a GMT / UTC offset, you can specify a time zone that the data was logged in. + diff --git a/app/views/docs/channels/_public_index.html.erb b/app/views/docs/channels/_public_index.html.erb new file mode 100644 index 0000000..63438e7 --- /dev/null +++ b/app/views/docs/channels/_public_index.html.erb @@ -0,0 +1,149 @@ +<%= @ssl_api_domain %>channels/public.json.xml
.
+
+GET <%= @ssl_api_domain %>channels/public.json.xml
+
++{ + "pagination": + { + "current_page": 1, + "per_page": 15, + "total_entries": 653 + }, + "channels": + [ + { + "id": 9, + "name": "my_house", + "description": "Netduino Plus connected to sensors around the house", + "latitude": "40.44", + "longitude": "-79.996", + "created_at": "2010-12-13T20:20:06-05:00", + "elevation": "", + "last_entry_id": 6062691, + "ranking" :100, + "username":"hans", + "tags": + [ + { + "id": 9, + "name": "temp" + },{ + "id": 25, + "name": "light" + } + ] + }, + { + "id": 5683, + "name": "Residential Data Points", + "description": "Arduino Uno + Ethernet Shield", + "latitude": "35.664548", + "longitude": "-78.654972", + "created_at": "2013-05-15T12:33:57-04:00", + "elevation": "100", + "last_entry_id": 731713, + "ranking": 100, + "username": "samlro", + "tags": + [ + { + "id": 950, + "name": "Analog Inputs" + } + ] + } + ] +} ++ +
+<?xml version="1.0" encoding="UTF-8"?> +<response> + <pagination> + <current-page type="WillPaginate::PageNumber">1</current-page> + <per-page type="integer">15</per-page> + <total-entries type="integer">654</total-entries> + </pagination> + <channels type="array"> + <channel> + <id type="integer">9</id> + <name>my_house</name> + <description> + Netduino Plus connected to sensors around the house + </description> + <latitude type="decimal">40.44</latitude> + <longitude type="decimal">-79.996</longitude> + <created-at type="dateTime">2010-12-13T20:20:06-05:00</created-at> + <elevation/> + <last-entry-id type="integer">6062720</last-entry-id> + <ranking type="integer">100</ranking> + <username>hans</username> + <tags type="array"> + <tag> + <id type="integer">9</id> + <name>temp</name> + </tag> + <tag> + <id type="integer">25</id> + <name>light</name> + </tag> + </tags> + </channel> + <channel> + <id type="integer">5683</id> + <name>Residential Data Points</name> + <description>Arduino Uno + Ethernet Shield</description> + <latitude type="decimal">35.664548</latitude> + <longitude type="decimal">-78.654972</longitude> + <created-at type="dateTime">2013-05-15T12:33:57-04:00</created-at> + <elevation>100</elevation> + <last-entry-id type="integer">731720</last-entry-id> + <ranking type="integer">100</ranking> + <username>samlro</username> + <tags type="array"> + <tag> + <id type="integer">950</id> + <name>Analog Inputs</name> + </tag> + </tags> + </channel> + </channels> +</response> ++ +
<%= @ssl_api_domain %>channels/CHANNEL_ID/status.json.xml
,
+replacing CHANNEL_ID with the ID of your Channel.
+
+GET <%= @ssl_api_domain %>channels/1417/status.json.xml
+
++{ + "channel": + { + "name": "CheerLights", + "latitude": "40.5", + "longitude": "-80.22" + }, + "feeds": + [ + { + "created_at": "2014-02-26T02:28:01Z", + "entry_id": 11888, + "status": "@cheerlights green" + }, + { + "created_at": "2014-02-26T22:05:31Z", + "entry_id" :11889, + "status": "@cheerlights blue" + } + ] +} ++
+{ + "channel": + { + "name": "CheerLights", + "latitude": "40.5", + "longitude": "-80.22" + }, + "feeds": + [ + { + "created_at": "2014-02-26T02:28:01Z", + "entry_id": 11888, + "status": "@cheerlights green" + }, + { + "created_at": "2014-02-26T22:05:31Z", + "entry_id" :11889, + "status": "@cheerlights blue" + } + ] +} ++ +
+<?xml version="1.0" encoding="UTF-8"?> +<channel> + <name>CheerLights</name> + <latitude type="decimal">40.5</latitude> + <longitude type="decimal">-80.22</longitude> + <feeds type="array"> + <feed> + <created-at type="dateTime">2014-02-26T02:28:01Z</created-at> + <entry-id type="integer">11888</entry-id> + <status>@cheerlights green</status> + <id type="integer" nil="true"/> + </feed> + <feed> + <created-at type="dateTime">2014-02-26T22:05:31Z</created-at> + <entry-id type="integer">11889</entry-id> + <status>@cheerlights blue</status> + <id type="integer" nil="true"/> + </feed> + </feeds> +</channel> ++ +
<%= @ssl_api_domain %>update.json.xml
.
+
++POST <%= @ssl_api_domain %>update.json.xml + api_key=<%= @channel_api_key %> + field1=73 ++ +
18
+ 0
then the update failed.
++{ + "channel_id": 3, + "field1": "73", + "field2": null, + "field3": null, + "field4": null, + "field5": null, + "field6": null, + "field7": null, + "field8": null, + "created_at": "2014-02-25T14:13:01-05:00", + "entry_id": 320, + "status": null, + "latitude": null, + "longitude": null, + "elevation": null, + "location":null +} ++ +
+<?xml version="1.0" encoding="UTF-8"?> +<feed> + <channel-id type="integer">3</channel-id> + <field1>73</field1> + <field2 nil="true"/> + <field3 nil="true"/> + <field4 nil="true"/> + <field5 nil="true"/> + <field6 nil="true"/> + <field7 nil="true"/> + <field8 nil="true"/> + <created-at type="dateTime">2014-02-25T14:15:42-05:00</created-at> + <entry-id type="integer">321</entry-id> + <status nil="true"/> + <latitude type="decimal" nil="true"/> + <longitude type="decimal" nil="true"/> + <elevation nil="true"/> + <location nil="true"/> +</feed> ++ +
http://api.thingspeak.com/channels/CHANNEL_ID/charts/FIELD_ID+ +
<iframe width="450" height="250" style="border: 1px solid #cccccc;" src="http://thingspeak.com/channels/CHANNEL_ID/charts/FIELD_ID"></iframe>+
<iframe width="450" height="250" style="border: 1px solid #cccccc;" src="http://thingspeak.com/channels/CHANNEL_ID/charts/FIELD_ID?dynamic=true"></iframe>+
<%= t(:error_code) %> | +<%= t(:error_http_status) %> | +<%= t(:error_message) %> | +<%= t(:error_details) %> | +
---|---|---|---|
<%= key %> |
+ <%= values[:http_status] %> | +<%= values[:message] %> | +<%= values[:details] %> | +
error_auth_required
+ +{ + "status": "401", + "error": + { + "error_code": "error_auth_required", + "message": "Authorization Required", + "details": "Please make sure that your API key is correct." + } +} ++
+<?xml version="1.0" encoding="UTF-8"?> +<error> + <error-code>error_auth_required</error-code> + <message>Authorization Required</message> + <details>Please make sure that your API key is correct.</details> +</error> ++ +
Please post your questions, comments, and feature requests in the ThingSpeak Forum.
+ +The ThingSpeak API is available on GitHub and includes the complete ThingSpeak API for processing HTTP requests, storing numeric and alphanumeric data, numeric data processing, location tracking, and status updates. The open source version follows the same documentation as the ThingSpeak hosted service.
+ + + +Display a Google Gauge Visualization using ThingSpeak Plugins [Source Code]
+ ++ +
+ +<%= @ssl_api_domain %>users/USERNAME.json.xml
,
+replacing USERNAME with the person's username.
+
+
+GET <%= @ssl_api_domain %>users/hans.json.xml
+
+
++{ + "id": 4, + "login": "hans", + "created_at": "2010-12-03T09:17:52-05:00", + "bio": "Web Developer @iobridge, @thingspeak", + "website": "http://www.iamshadowlord.com" +} ++ +
+<?xml version="1.0" encoding="UTF-8"?> +<user> + <id type="integer">4</id> + <login>hans</login> + <created-at type="dateTime">2010-12-03T09:17:52-05:00</created-at> + <bio>Web Developer @iobridge, @thingspeak</bio> + <website>http://www.iamshadowlord.com</website> +</user> ++ +
<%= @ssl_api_domain %>users/USERNAME/channels.json.xml
,
+replacing USERNAME with the person's username.
+
+
+GET <%= @ssl_api_domain %>users/hans/channels.json.xml
+
+
++{ + "channels": + [ + { + "id": 3, + "name": "ioBridge Server", + "description": "ioBridge IO-204 connected to web server", + "latitude": null, + "longitude": null, + "created_at": "2010-12-03T09:26:23-05:00", + "elevation": "", + "last_entry_id": 163690, + "ranking": 85, + "username": "hans", + "tags": + [ + { + "id": 24, + "name": "temperature" + } + ] + }, + { + "id": 9, + "name": "my_house", + "description": "Netduino Plus connected to sensors around the house", + "latitude": "40.44", + "longitude": "-79.996", + "created_at": "2010-12-13T20:20:06-05:00", + "elevation": "", + "last_entry_id": 6062844, + "ranking": 100, + "username": "hans", + "tags": + [ + { + "id": 9, + "name": "temp" + }, + { + "id": 25, + "name": "light" + } + ] + } + ] +} ++ +
+<?xml version="1.0" encoding="UTF-8"?> +<response> + <channels type="array"> + <channel> + <id type="integer">3</id> + <name>ioBridge Server</name> + <description>ioBridge IO-204 connected to web server</description> + <latitude nil="true"/> + <longitude nil="true"/> + <created-at type="dateTime">2010-12-03T09:26:23-05:00</created-at> + <elevation/> + <last-entry-id type="integer">163690</last-entry-id> + <ranking type="integer">85</ranking> + <username>hans</username> + <tags type="array"> + <tag> + <id type="integer">24</id> + <name>temperature</name> + </tag> + </tags> + </channel> + <channel> + <id type="integer">9</id> + <name>my_house</name> + <description>Netduino Plus connected to sensors around the house</description> + <latitude type="decimal">40.44</latitude> + <longitude type="decimal">-79.996</longitude> + <created-at type="dateTime">2010-12-13T20:20:06-05:00</created-at> + <elevation/> + <last-entry-id type="integer">6062860</last-entry-id> + <ranking type="integer">100</ranking> + <username>hans</username> + <tags type="array"> + <tag> + <id type="integer">9</id> + <name>temp</name> + </tag> + <tag> + <id type="integer">25</id> + <name>light</name> + </tag> + </tags> + </channel> + </channels> +</response> ++ +