Fast Ruby parser and API client for Green Button data
Green Button Data is a Ruby gem that can consume Green Button APIs and parse
the Green Button data XML schema very quickly. It uses an event-driven
SAX parser which parses XML data without
building an entire DOM in memory.
On a 2.3 GHz Core i7 processor, this gem is capable of parsing at a rate of
~1.7 MB/s which is fast enough to parse a 1 year 12-hour interval data in just
over a second.
You may run the benchmarks by cloning this project and running rake benchmark
.
Add the Green Button Data gem to your Gemfile:
gem 'green-button-data'
Then run Bundler:
$ bundle
Unless you have a project that auto loads all gems in the Gemfile (e.g. a Rails
project), you will need to require it:
require 'green-button-data'
This will expose the GreenButtonData module namespace.
GreenButtonData gem provides a familiar interface to consuming API endpoints.
Method names are similar to Rails’ ActiveRecord models and can be easily
integrated into existing applications.
You can add configuration options like the following:
GreenButtonData.configure do |config|
config.base_url = "https://api.example.com/DataCustodian/espi/1_1/resource/"
config.application_information_path = "ApplicationInformation/"
config.authorization_path = "Authorization/"
config.subscription_path = "Subscription/"
config.usage_point_path = "UsagePoint/"
end
Note that each path must end with a trailing slash.
IMPORTANT: Version 0.3.0 has a bug which causes Rails not to boot
correctly. Please use 0.3.1 or higher when integrating into Rails by
runningbundle update green-button-data
.
If you are developing a Rails app, create a file atconfig/initializers/green_button_data.rb
from your Rails project root and
add the configuration there.
Green Button Data specification states that all API endpoints be secured with
OAuth2 which means most fetch operations will require auth tokens.
Some endpoints are secured further by utilizing client SSL certificates (e.g.
Pacific Gas & Electric). You may pass in ssl
options in addition to
the token
option in this case.
DISCLAIMER: Green Button Data is NOT responsible for managing OAuth
tokens to make authenticated requests. There are other gems that provide mature,
production proven OAuth 2 functionalities such as OmniAuth.
With version 0.4.0, there are two ways to query data on a remote endpoint:
This is the recommended method to query data from multiple
Data Custodians.
Each instance of GreenButtonData::Client
uses scoped configuration suitable
for each Data Custodians.
This allows you to make requests to different Data Custodians that have
different URL path layouts.
The example below creates two clients: one for
ESPI Green Button Data reference API and the other for Pacific Gas & Electric:
espi_base_url = "https://services.greenbuttondata.org/DataCustodian/espi/" +
"1_1/resource/"
pge_base_url = "https://api.pge.com/GreenButtonConnect/espi/1_1/resource/"
espi_client = GreenButtonData.connect base_url: "https://foo.com" do |client|
# Override base_url configuration
client.configuration.base_url = espi_base_url
# Equivalent to passing in as an argument hash in connect method:
# GreenButtonData.connect subscription_path: "Subscription/" do |client|
client.configuration.subscription_path = "Subscription/"
client.configuration.usage_point_path = "UsagePoint/"
client.configuration.usage_summary_path = "ElectricPowerUsageSummary/"
# Set OAuth 2.0 bearer token
client.token = "12345678-5672-abcd-567890abcdef"
end
pge_client = GreenButtonData.connect base_url: pge_base_url do |client|
client.configuration.subscription_path = "Subscription/"
client.configuration.usage_point_path = "UsagePoint/"
client.configuration.usage_summary_path = "UsageSummary/"
client.token = "098765432-abcd-ef00-12345678900"
end
The Client
class provides query methods to query each resources:
GreenButtonData::Client#application_information
Returns a collection of GreenButtonData::ApplicationInformation
instances if
id is not specified. If id is specified, returns a single instance.
GreenButtonData::Client#authorization
Returns a collection of GreenButtonData::Authorization
instances if id is
not specified. If id is specified, returns a single instance.
GreenButtonData::Client#interval_block
Returns a collection of GreenButtonData::IntervalBlock
instances if id is
not specified. If id is specified, returns a single instance.
GreenButtonData::Client#local_time_parameters
Returns a collection of GreenButtonData::LocalTimeParameters
instances if id
is not specified. If id is specified, returns a single instance.
GreenButtonData::Client#meter_reading
Returns a collection of GreenButtonData::MeterReading
instances if id is not
specified. If id is specified, returns a single instance.
GreenButtonData::Client#reading_type
Returns a collection of GreenButtonData::ReadingType
instances if id is not
specified. If id is specified, returns a single instance.
GreenButtonData::Client#usage_point
Returns a collection of GreenButtonData::UsagePoint
instances if id is not
specified. If id is specified, returns a single instance.
GreenButtonData::Client#usage_summary
Returns a collection of GreenButtonData::UsageSummary
instances if id is not
specified. If id is specified, returns a single instance.
Following the example from previous section, to retrieve all entries:
# Generally, Data Custodian access token is required to do this query which you
# can override with `token` keyword argument
# Makes a request to:
# https://services.greenbuttondata.org/DataCustodian/espi/1_1/resources/UsagePoint
espi_all_usage_points = espi_client.usage_point
To retrieve a specific entry, specify the resource ID:
# Get a Usage Point 2
# Makes a request to:
# https://services.greenbuttondata.org/DataCustodian/espi/1_1/resources/UsagePoint/2
espi_usage_point = espi_client.usage_point 2, token: data_custodian_access_token
# Get a UsagePoint 1234 for subscription 3401
# Makes a request to:
# https://api.pge.com/GreenButtonConnect/espi/1_1/resources/Subscription/3401/UsagePoint/1234
pge_usage_point = pge_client.usage_point 1234, subscription_id: 3401
You can query for related resources as well. There are couple ways you can do
this:
# Gets all meter readings for this instance of UsagePoint
meter_readings_1 = pge_usage_point.meter_readings
# Same as above but requires knowledge of UsagePoint and Subscription ids
meter_readings_2 = pge_client.meter_reading usage_point_id: 1234, subscription_id: 3401
This method allows you to query for data using the global configuration. This
has limitations as you can only query data from one Data Custodian at a time
unless you override the global configuration per method call.
The resource classes are as follows:
GreenButtonData::ApplicationInformation
GreenButtonData::Authorization
GreenButtonData::IntervalBlock
GreenButtonData::LocalTimeParameters
GreenButtonData::MeterReading
GreenButtonData::ReadingType
GreenButtonData::UsagePoint
GreenButtonData::UsageSummary
By default, the .all
method attempts to use the URL path set by configuration:
require 'green-button-data'
# Ideally obtained from OmniAuth gem
access_token = "12345678-1024-2048-abcdef001234"
# Return all usage points; URL is specified in GreenButtonData.configuration.usage_point_url
usage_points = UsagePoint.all token: access_token
You may override the global configuration by specifying the URL in the method:
usage_points = UsagePoint.all "https://someotherapi.org/espi/Authorization",
token: access_token
If you have URL defined in configuration, the .find
method appends the ID to
the URL:
GreenButtonData.configure do |config|
config.base_url = "https://services.greenbuttondata.org/"
config.usage_point_path = "DataCustodian/espi/1_1/resource/UsagePoint/"
end
# GET request to https://services.greenbuttondata.org/DataCustodian/espi/1_1/resource/UsagePoint/2
usage_point = GreenButtonData::UsagePoint.find 2, token: access_token
As with .all
method, URL can be overridden per request in .find
:
usage_point = GreenButtonData::UsagePoint.find "https://someotherapi.org/espi/UsagePoint/1",
token: access_token
Almost all of the functionality for parsing data is wrapped in theGreenButtonData::Feed
class.
To parse a Green Button Atom feed, simply call the parse
method and pass in
the entire XML document:
require 'green-button-data'
xml_text = File.read "#{File.dirname __FILE__}/gb_example_interval_block.xml"
data = GreenButtonData::Feed.parse xml_text
You can then access the data like this:
# Print all the interval readings from the feed
data.entries.each do |entry|
p "Entry: #{entry.title}"
entry.content.interval_block.interval_readings.each do |reading|
time_period = reading.time_period
p "[#{time_period.starts_at} - #{time_period.ends_at}]: #{reading.value}"
end
end
git checkout -b my-awesome-feature
git commit -am "Add awesome feature"
git push origin my-awesome-feature
Green Button Data gem follows Semantic Versioning 2.0. As
such, you can specify a pessimistic version constraint on this gem with two
digits of precision and be guaranteed latest features and bug fixes without
backwards breaking changes:
gem 'green-button-data', '~> 0.1'
Exception to this rule as per the SemVer specification is major version zero for
initial development. This gem’s API should NOT be considered stable until 1.0
release.
This software is distributed AS IS WITHOUT WARRANTY under Simplified BSD
license.
Verdigris Technologies Inc. assumes NO RESPONSIBILITY OR LIABILITY
UNDER ANY CIRCUMSTANCES for usage of this software. See the LICENSE.txt
file for detailed legal information.
Do you have passion for solving challenging engineering problems? You may just be the right fit at Verdigris! We are hiring software engineers for our Mountain View, California, USA office.
For more information, check out our Careers page.
Copyright © 2015-2020, Verdigris Technologies Inc. All rights reserved.