amon 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +76 -0
- data/init.rb +1 -0
- data/lib/amon/default_session.rb +45 -0
- data/lib/amon/document.rb +30 -0
- data/lib/amon/document_part.rb +18 -0
- data/lib/amon/entity.rb +24 -0
- data/lib/amon/json_helper.rb +36 -0
- data/lib/amon/measurement.rb +55 -0
- data/lib/amon/metadata.rb +34 -0
- data/lib/amon/meter.rb +53 -0
- data/lib/amon/metering_point.rb +8 -0
- data/lib/amon/reading.rb +59 -0
- data/lib/amon/session.rb +130 -0
- data/lib/amon/version.rb +10 -0
- data/lib/amon.rb +18 -0
- data/rails/init.rb +1 -0
- metadata +160 -0
data/README.md
ADDED
@@ -0,0 +1,76 @@
|
|
1
|
+
Ruby Client for the AMEE AMON API
|
2
|
+
=================================
|
3
|
+
|
4
|
+
This is a Ruby client library for the [AMEE](http://www.amee.com/) <abbr title="AMEE Monitoring Object Notation">AMON</abbr> <abbr title="Application Programming Interface">API</abbr>. _More details and links to be inserted once information about the API is publicly available_.
|
5
|
+
|
6
|
+
## Installation ##
|
7
|
+
|
8
|
+
### Dependencies ###
|
9
|
+
|
10
|
+
Currently, the library depends on:
|
11
|
+
|
12
|
+
* [json](http://flori.github.com/json/) 1.4.X
|
13
|
+
* [rest-client](http://github.com/archiloque/rest-client) 1.6.X
|
14
|
+
|
15
|
+
These will be automatically installed if installing as a gem.
|
16
|
+
|
17
|
+
### Standard installation: as a rubygem ###
|
18
|
+
|
19
|
+
The library is not currently available via any public gem servers. However, it can be installed as a gem using the following steps:
|
20
|
+
|
21
|
+
1. Grab the code: `git clone git@github.com:AMEE/amon.client.ruby.git`
|
22
|
+
2. Build the gem: `gem build amon.gemspec`
|
23
|
+
3. Install the gem: `sudo gem install amon-0.1.0.gem`
|
24
|
+
|
25
|
+
### Alternative: manual installation ###
|
26
|
+
|
27
|
+
You can manually install the code somewhere, and then ensure that the `lib/` directory is listed in your `LOAD_PATH`.
|
28
|
+
|
29
|
+
### Alternative: vendoring in a Rails app ###
|
30
|
+
|
31
|
+
If you are using the library inside a Rails (2.3) app, clone the code into `vendor/gems/amon-0.1.0`. Then list it as a gem dependency in `config/environment.rb` and Rails will automatically find and load it.
|
32
|
+
|
33
|
+
If using Rails 3, you could do a similar thing using Bundler.
|
34
|
+
|
35
|
+
## Loading the library in your code ##
|
36
|
+
|
37
|
+
Once installed, the library can be loaded using `require "amon"`.
|
38
|
+
|
39
|
+
## Configuration ##
|
40
|
+
|
41
|
+
Interaction with the API is achieved through {AMON::Session Session} objects. For convenience, you can configure some 'default session options' by setting the values in the {AMON.default_session_options} hash. You can then create a session from these options at any time by calling {AMON.create_default_session}.
|
42
|
+
|
43
|
+
See the {AMON::Session} page for a full explanation of the options available.
|
44
|
+
|
45
|
+
### Example default session configuration ###
|
46
|
+
|
47
|
+
AMON.default_session_options[:base_uri] = "http://amon.amee.com/1"
|
48
|
+
AMON.default_session_options[:user] = "fred"
|
49
|
+
AMON.default_session_options[:password] = "s3cr3t"
|
50
|
+
|
51
|
+
## Usage ##
|
52
|
+
|
53
|
+
Methods which can be called on a {AMON::Session Session} can be called directly on the {AMON} module, which will cause a {AMON.create_default_session default session} to be created and used.
|
54
|
+
|
55
|
+
### Simple usage example ###
|
56
|
+
|
57
|
+
This example gets some measurements from an electricity meter over a 1 month period.
|
58
|
+
|
59
|
+
start_date = Time.local(2010, 3)
|
60
|
+
end_date = Time.local(2010, 4)
|
61
|
+
|
62
|
+
property = AMON.entity("da3906c0-7c6b-012d-a6d5-001c23973687")
|
63
|
+
concentrator = property.meters.first
|
64
|
+
electricity_reading = concentrator.readings_by_type["electricalInput"]
|
65
|
+
electricity_reading.measurements(start_date, end_date) # => Array of measurements
|
66
|
+
|
67
|
+
## Development ##
|
68
|
+
|
69
|
+
Tests can be run with RSpec: `spec spec/`.
|
70
|
+
|
71
|
+
The documentation is generated with YARD: `rake yard`.
|
72
|
+
|
73
|
+
## TODO List ##
|
74
|
+
|
75
|
+
* Error handling
|
76
|
+
* Rename Document and DocumentPart to Resource and ResourcePart
|
data/init.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require File.dirname(__FILE__) + "/rails/init"
|
@@ -0,0 +1,45 @@
|
|
1
|
+
require 'forwardable'
|
2
|
+
|
3
|
+
# The AMON module responds to the same method names as an {AMON::Session}. When called,
|
4
|
+
# these methods use a 'default' session returned by {create_default_session}. So the following
|
5
|
+
# method calls are all equivalent:
|
6
|
+
#
|
7
|
+
# AMON::Session.new(AMON.default_session_options).entity("77e50660-93e7-012d-b57d-001c23973687")
|
8
|
+
# AMON.create_default_session.entity("77e50660-93e7-012d-b57d-001c23973687")
|
9
|
+
# AMON.entity("77e50660-93e7-012d-b57d-001c23973687")
|
10
|
+
#
|
11
|
+
# Note that using a default session implicitly (as in the final line above) will cause a new
|
12
|
+
# session to be created each time the method is called. This means caching will not be used.
|
13
|
+
#
|
14
|
+
# If you are likely to be requesting the same resources in short succession, it is advisable to
|
15
|
+
# hold on to a session object yourself. For example:
|
16
|
+
#
|
17
|
+
# @session = AMON.create_default_session
|
18
|
+
# @session.entity("77e50660-93e7-012d-b57d-001c23973687") # => <AMON::Entity> (from HTTP)
|
19
|
+
# @session.entity("77e50660-93e7-012d-b57d-001c23973687") # => <AMON::Entity> (from cache)
|
20
|
+
module AMON
|
21
|
+
class << self
|
22
|
+
# A hash of options which are used by {create_default_session}. The values of this hash
|
23
|
+
# should be configured by users as appropriate.
|
24
|
+
# @return [Hash] an options hash, initially empty
|
25
|
+
def default_session_options
|
26
|
+
@default_session_options ||= {}
|
27
|
+
end
|
28
|
+
|
29
|
+
# Assigns a new options hash as the {default_session_options}.
|
30
|
+
# @param [Hash] options the options hash
|
31
|
+
# @return [Hash] the options hash
|
32
|
+
def default_session_options=(options)
|
33
|
+
@default_session_options = options
|
34
|
+
end
|
35
|
+
|
36
|
+
# Creates a new session using the {default_session_options}.
|
37
|
+
# @return [Session]
|
38
|
+
def create_default_session
|
39
|
+
Session.new(default_session_options)
|
40
|
+
end
|
41
|
+
|
42
|
+
extend Forwardable
|
43
|
+
def_delegators :create_default_session, :entity, :meter, :metering_point, :measurements
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module AMON
|
2
|
+
# A top-level object representing an AMON resource
|
3
|
+
# @abstract
|
4
|
+
class Document
|
5
|
+
include JSONHelper
|
6
|
+
|
7
|
+
# @return [Session] The session associated with this document
|
8
|
+
attr_reader :session
|
9
|
+
|
10
|
+
# @param [Session] session The session
|
11
|
+
# @param [Hash] json The JSON data as a Hash
|
12
|
+
def initialize(session, json)
|
13
|
+
@session, @json = session, json
|
14
|
+
end
|
15
|
+
|
16
|
+
# This should be implemented in subclasses, returning the UUID for the document
|
17
|
+
# @abstract
|
18
|
+
# @private
|
19
|
+
def id
|
20
|
+
raise NotImplementedError
|
21
|
+
end
|
22
|
+
|
23
|
+
# Two documents are equal if they have the same class and their id fields are equal
|
24
|
+
# @param [Document] other
|
25
|
+
# @return [Boolean]
|
26
|
+
def ==(other)
|
27
|
+
self.class == other.class && id == other.id
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module AMON
|
2
|
+
# An object representing part of an AMON {Document}. For example, a {Reading} is part of a
|
3
|
+
# {Meter}, and cannot exist outside of it.
|
4
|
+
#
|
5
|
+
# @abstract
|
6
|
+
class DocumentPart
|
7
|
+
include JSONHelper
|
8
|
+
|
9
|
+
# @return [Document] The parent document of which this object is a part
|
10
|
+
attr_reader :parent
|
11
|
+
|
12
|
+
# @param [Document] parent The parent document
|
13
|
+
# @param [Hash] json The JSON data as a Hash
|
14
|
+
def initialize(parent, json)
|
15
|
+
@parent, @json = parent, json
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
data/lib/amon/entity.rb
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
module AMON
|
2
|
+
# An AMON entity, such as a house, business park, or building floor
|
3
|
+
class Entity < Document
|
4
|
+
# @return [String] The id for this entity
|
5
|
+
field :id, :name => 'entityId'
|
6
|
+
|
7
|
+
# @return [Array<String>] The ids of all the meters associated with this entity
|
8
|
+
field :meter_ids, :name => 'meterIds', :default => []
|
9
|
+
|
10
|
+
# @return [Array<String>] The qualified ids of all the metering points associated with this entity
|
11
|
+
# @todo Rename to qualified_metering_point_ids
|
12
|
+
field :metering_point_ids, :name => 'qualifiedMeteringPointIds', :default => []
|
13
|
+
|
14
|
+
# @return [Array<Meter>] The meters associated with this entity
|
15
|
+
def meters
|
16
|
+
@meters ||= meter_ids.map { |id| session.meter(id) }
|
17
|
+
end
|
18
|
+
|
19
|
+
# @return [Array<MeteringPoint>] The metering points associated with this entity
|
20
|
+
def metering_points
|
21
|
+
@metering_points ||= metering_point_ids.map { |id| session.metering_point(id) }
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
module AMON
|
2
|
+
module JSONHelper
|
3
|
+
# @return [Hash] The raw JSON data for this object, represented as a Hash
|
4
|
+
attr_reader :json
|
5
|
+
|
6
|
+
private
|
7
|
+
|
8
|
+
def self.included(base)
|
9
|
+
base.class_eval do
|
10
|
+
extend ClassMethods
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
# @private
|
15
|
+
module ClassMethods
|
16
|
+
def field(name, options = {})
|
17
|
+
class_eval do
|
18
|
+
define_method(name) do
|
19
|
+
original_value = json[(options[:name] || name).to_s]
|
20
|
+
|
21
|
+
if original_value.nil?
|
22
|
+
options[:default]
|
23
|
+
else
|
24
|
+
case options[:as].to_s
|
25
|
+
when 'Time'
|
26
|
+
Time.parse(original_value)
|
27
|
+
else
|
28
|
+
original_value
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
module AMON
|
2
|
+
# A measurement corresponding to a {Reading} of a {Meter}
|
3
|
+
class Measurement < DocumentPart
|
4
|
+
# @return [String] The measurement's name
|
5
|
+
field :name
|
6
|
+
|
7
|
+
# @return [Numeric] The measurement's value
|
8
|
+
field :value
|
9
|
+
|
10
|
+
# @return [Time] The measurement's timestamp, if provided
|
11
|
+
field :timestamp, :as => Time
|
12
|
+
|
13
|
+
# @return [Time] The measurement's start date, if provided
|
14
|
+
field :start_date, :name => 'startDate', :as => Time
|
15
|
+
|
16
|
+
# @return [Time] The measurement's end date, if provided
|
17
|
+
field :end_date, :name => 'endDate', :as => Time
|
18
|
+
|
19
|
+
# @attr_reader [Meter] meter The meter which took this measurement
|
20
|
+
alias_method :meter, :parent
|
21
|
+
|
22
|
+
# A measurement is instantaneous if it is given with a single timestamp. If, on the other hand,
|
23
|
+
# start and end dates are provided, it is durational.
|
24
|
+
# @return [Boolean]
|
25
|
+
# @see Measurement#durational?
|
26
|
+
def instantaneous?
|
27
|
+
!timestamp.nil?
|
28
|
+
end
|
29
|
+
|
30
|
+
# The opposite of {Measurement#instantaneous? instantaneous?}
|
31
|
+
# @return [Boolean]
|
32
|
+
# @see Measurement#instantaneous?
|
33
|
+
def durational?
|
34
|
+
!instantaneous?
|
35
|
+
end
|
36
|
+
|
37
|
+
# The {Meter meter} {Reading reading} associated with this measurement.
|
38
|
+
# @return [Reading]
|
39
|
+
def reading
|
40
|
+
meter.readings_by_type[name]
|
41
|
+
end
|
42
|
+
|
43
|
+
# A single timestamp in the 'middle' of this measurement. If the measurement is instantaneous,
|
44
|
+
# then the mid timestamp is identical to the {Measurement#timestamp timestamp}. However, if it
|
45
|
+
# is durational then the mid timestamp is the halfway point between the {Measurement#start_date}
|
46
|
+
# and the {Measurement#end_date}.
|
47
|
+
def mid_timestamp
|
48
|
+
if instantaneous?
|
49
|
+
timestamp
|
50
|
+
else
|
51
|
+
start_date + (end_date - start_date) / 2
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module AMON
|
2
|
+
# Metadata associated with a {Meter}. The metadata section of an AMON document can have an
|
3
|
+
# arbitrary number of fields, so this class uses `method_missing` to look them up and respond
|
4
|
+
# appropriately.
|
5
|
+
#
|
6
|
+
# @todo
|
7
|
+
# This doesn't yet support arbitrarily nested metadata, so you can't have JSON like so:
|
8
|
+
#
|
9
|
+
# "metadata": {
|
10
|
+
# "foo": {
|
11
|
+
# "bar": "baz"
|
12
|
+
# }
|
13
|
+
# }
|
14
|
+
#
|
15
|
+
# And then access:
|
16
|
+
#
|
17
|
+
# object.metadata.foo.bar # => "baz"
|
18
|
+
#
|
19
|
+
# However, in the meantime you could do:
|
20
|
+
#
|
21
|
+
# object.metadata.foo[:bar] # => "baz"
|
22
|
+
#
|
23
|
+
# (As the JSON is full parsed into a nested hash.)
|
24
|
+
|
25
|
+
class Metadata < DocumentPart
|
26
|
+
def method_missing(name, *args, &block)
|
27
|
+
if args.empty? && block.nil?
|
28
|
+
json[name.to_s]
|
29
|
+
else
|
30
|
+
raise NoMethodError, "undefined method `#{name}'"
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
data/lib/amon/meter.rb
ADDED
@@ -0,0 +1,53 @@
|
|
1
|
+
module AMON
|
2
|
+
# An AMON meter, containing any number of {Reading readings}, and {Measurement measurements} for those readings.
|
3
|
+
class Meter < Document
|
4
|
+
# @return [String] The id of the meter
|
5
|
+
field :id, :name => 'meterId'
|
6
|
+
|
7
|
+
# @return <String> The description for the meter
|
8
|
+
def description
|
9
|
+
@description ||= json['description']
|
10
|
+
end
|
11
|
+
|
12
|
+
# @return <String> The location for the meter
|
13
|
+
def location_name
|
14
|
+
@location_name ||= json['location']['name']
|
15
|
+
end
|
16
|
+
|
17
|
+
# @return [Metadata] The metadata for the meter
|
18
|
+
def metadata
|
19
|
+
@metadata ||= Metadata.new(self, json['metadata'])
|
20
|
+
end
|
21
|
+
|
22
|
+
# @return [Array<Reading>] The readings for the meter
|
23
|
+
def readings
|
24
|
+
@readings ||= json['readings'].map { |reading| Reading.new(self, reading) }
|
25
|
+
end
|
26
|
+
|
27
|
+
# @return [Hash<String => Reading>] A hash allowing {Meter#readings} to be retrieved by their
|
28
|
+
# {Reading#type}
|
29
|
+
def readings_by_type
|
30
|
+
readings.inject({}) do |readings_by_type, reading|
|
31
|
+
readings_by_type[reading.type] = reading
|
32
|
+
readings_by_type
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
# @return [Array<Measurement>] Measurements for this meter
|
37
|
+
# @see Session#measurements
|
38
|
+
def measurements(start_date, end_date, raw = false)
|
39
|
+
session.measurements(id, start_date, end_date, raw)
|
40
|
+
end
|
41
|
+
|
42
|
+
# @return [Hash<String => Measurement>] A hash allowing {Meter#measurements} to be retrieved
|
43
|
+
# by their {Measurement#name}
|
44
|
+
# @see Meter#measurements
|
45
|
+
def measurements_by_name(start_date, end_date, raw = false)
|
46
|
+
measurements(start_date, end_date, raw).inject({}) do |measurements_by_name, measurement|
|
47
|
+
measurements_by_name[measurement.name] ||= []
|
48
|
+
measurements_by_name[measurement.name] << measurement
|
49
|
+
measurements_by_name
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
data/lib/amon/reading.rb
ADDED
@@ -0,0 +1,59 @@
|
|
1
|
+
module AMON
|
2
|
+
# AMON readings are 'categories' of {Measurement measurements} which are taken by {Meter meters}.
|
3
|
+
# For example, one reading may be 'electricalInput' with the unit 'kWh'.
|
4
|
+
class Reading < DocumentPart
|
5
|
+
# @return [String] The type of reading, which is unique within the parent {Meter meter}
|
6
|
+
field :type
|
7
|
+
|
8
|
+
# @return [String] An arbitrary name for the reading
|
9
|
+
field :name
|
10
|
+
|
11
|
+
# @return [String] The units this reading records {Measurement measurements} in
|
12
|
+
field :unit
|
13
|
+
|
14
|
+
# @return [Numeric] The resolution of the reading
|
15
|
+
field :resolution
|
16
|
+
|
17
|
+
# @return [Numeric] The accuracy of the reading
|
18
|
+
field :accuracy
|
19
|
+
|
20
|
+
# @return ["instant", "duration"] Whether this reading records measurement values taken at a
|
21
|
+
# specific point in time ('instant'), or taken as an average over a period of time ('duration')
|
22
|
+
field :period, :default => "instant"
|
23
|
+
|
24
|
+
# @attr_reader [Meter] meter The meter which took this measurement
|
25
|
+
alias_method :meter, :parent
|
26
|
+
|
27
|
+
# @return [Array<Measurement>] Measurements for this reading
|
28
|
+
# @see Meter#measurements
|
29
|
+
def measurements(start_date, end_date, raw = false)
|
30
|
+
meter.measurements_by_name(start_date, end_date, raw)[type] || []
|
31
|
+
end
|
32
|
+
|
33
|
+
# @return [true] if the {#period} is 'instant'
|
34
|
+
# @return [false] if the {#period} is 'duration'
|
35
|
+
def instantaneous?
|
36
|
+
period == "instant"
|
37
|
+
end
|
38
|
+
|
39
|
+
# @return [Boolean] The opposite of {#instantaneous?}
|
40
|
+
def durational?
|
41
|
+
!instantaneous?
|
42
|
+
end
|
43
|
+
|
44
|
+
# One reading is equal to another if the meters are equal and they are the same type
|
45
|
+
# @return [Boolean]
|
46
|
+
# @param [Reading] other The other reading to compare with.
|
47
|
+
def ==(other)
|
48
|
+
meter == other.meter && type == other.type
|
49
|
+
end
|
50
|
+
|
51
|
+
# A unique id for the reading, composed of the {Meter meter} id, and the {#type}. So an
|
52
|
+
# '<code>electricalInput</code>' reading for a meter with id '<code>b7ddac00-9415-012d-b57e-001c23973687</code>' would
|
53
|
+
# have the id '<code>b7ddac00-9415-012d-b57e-001c23973687-electricalInput</code>'.
|
54
|
+
# @return [String] the id
|
55
|
+
def id
|
56
|
+
"#{meter.id}-#{type}"
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
data/lib/amon/session.rb
ADDED
@@ -0,0 +1,130 @@
|
|
1
|
+
require 'uri'
|
2
|
+
|
3
|
+
module AMON
|
4
|
+
# Session objects take care of making the actual HTTP requests to the AMON API
|
5
|
+
#
|
6
|
+
# ## Caching ##
|
7
|
+
#
|
8
|
+
# Sessions support caching which is enabled by default, but can be optionally turned off. When
|
9
|
+
# caching is enabled, the same request will not be performed twice in a session. Be aware that
|
10
|
+
# this has some important implications:
|
11
|
+
#
|
12
|
+
# * If a long running process uses the same session, the cache may become stale as the backend
|
13
|
+
# data changes, but new API requests are not made.
|
14
|
+
# * There is no mechanism for automatically expiring the cache; this is left up to users of the
|
15
|
+
# library.
|
16
|
+
# * The cache stores the AMON objects, rather than the raw response data. In this sense it
|
17
|
+
# functions as a sort of identity map - getting the same entity twice will return exactly the
|
18
|
+
# same object twice, rather than two different objects representing the same data.
|
19
|
+
#
|
20
|
+
# Often the most natural way to 'expire the cache' is to discard the session and use a new one
|
21
|
+
# at regular intervals. Alternatively, you can use {Session#clear_cache} and keep the same
|
22
|
+
# session.
|
23
|
+
class Session
|
24
|
+
# @return [Hash] The hash of configuration options to be used when making requests
|
25
|
+
attr_reader :options
|
26
|
+
|
27
|
+
# The default options for a session
|
28
|
+
DEFAULT_OPTIONS = {
|
29
|
+
:base_uri => nil,
|
30
|
+
:user => nil,
|
31
|
+
:password => nil,
|
32
|
+
:cache => true
|
33
|
+
}.freeze
|
34
|
+
|
35
|
+
# @param [Hash] options the options for the session
|
36
|
+
# @option options [String] :base_uri The base URI for the AMON API, e.g. "http://amon.amee.com/1"
|
37
|
+
# (note that this includes the API version, as this library only knows about version 1 of the API)
|
38
|
+
# @option options [String] :user The HTTP user for authentication
|
39
|
+
# @option options [String] :password The HTTP password for authentication
|
40
|
+
# @option options [Boolean] :cache (true) Whether to perform caching or not. See above for details.
|
41
|
+
def initialize(options = {})
|
42
|
+
@options = DEFAULT_OPTIONS.merge(options)
|
43
|
+
@options.freeze
|
44
|
+
end
|
45
|
+
|
46
|
+
# Retrieves an entity from the API
|
47
|
+
# @param [String] entity_id the ID of the entity to be requested
|
48
|
+
# @return [Entity]
|
49
|
+
def entity(entity_id)
|
50
|
+
cache(:entities, entity_id) do
|
51
|
+
json = get("/entities/#{URI.escape(entity_id)}")['entity']
|
52
|
+
Entity.new(self, json)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
# Retrieves a meter from the API
|
57
|
+
# @param [String] meter_id the ID of the meter to be requested
|
58
|
+
# @return [Meter]
|
59
|
+
def meter(meter_id)
|
60
|
+
cache(:meters, meter_id) do
|
61
|
+
json = get("/meters/#{URI.escape(meter_id)}")['meter']
|
62
|
+
Meter.new(self, json)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
# Retrieves a metering point from the API
|
67
|
+
# @param [String] metering_point_id the ID of the metering point to be requested
|
68
|
+
# @return [MeteringPoint]
|
69
|
+
def metering_point(metering_point_id)
|
70
|
+
cache(:metering_points, :metering_point_id) do
|
71
|
+
json = get("/metering-points/#{URI.escape(metering_point_id)}")['metering_point']
|
72
|
+
MeteringPoint.new(self, json)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
# Retrieves measurements from the API
|
77
|
+
# @param [String] meter_id The ID of the meter to get the measurements from
|
78
|
+
# @param [Time] start_date The starting point for the measurements
|
79
|
+
# @param [Time] end_date The ending point for the measurements
|
80
|
+
# @param [Boolean] raw Whether to return raw measurements as submitted by the hardware (may
|
81
|
+
# contain a lot of data), or appropriate aggregations over the time period
|
82
|
+
def measurements(meter_id, start_date, end_date, raw = false)
|
83
|
+
cache(:measurements, [meter_id, start_date, end_date, raw]) do
|
84
|
+
meter = meter(meter_id)
|
85
|
+
measurements = get(
|
86
|
+
"/meters/#{meter.id}/measurements?" +
|
87
|
+
"raw=" + raw.to_s + "&" +
|
88
|
+
"startDate=" + start_date.utc.xmlschema + "&" +
|
89
|
+
"endDate=" + end_date.utc.xmlschema
|
90
|
+
)['measurements']
|
91
|
+
measurements.map { |measurement| Measurement.new(meter, measurement) }
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
# Completely clears the cache
|
96
|
+
def clear_cache
|
97
|
+
@cache = nil
|
98
|
+
end
|
99
|
+
|
100
|
+
private
|
101
|
+
|
102
|
+
def cache(group, id, &block)
|
103
|
+
if options[:cache]
|
104
|
+
@cache ||= {}
|
105
|
+
@cache[group] ||= {}
|
106
|
+
@cache[group][id] ||= block.call
|
107
|
+
else
|
108
|
+
block.call
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
def get(url, params = {})
|
113
|
+
JSON.parse(client[url].get(:params => params))
|
114
|
+
end
|
115
|
+
|
116
|
+
def client
|
117
|
+
raise ArgumentError, "Can't make a request without :base_uri option" unless options[:base_uri]
|
118
|
+
|
119
|
+
@client ||= RestClient::Resource.new(
|
120
|
+
options[:base_uri],
|
121
|
+
:user => options[:user],
|
122
|
+
:password => options[:password],
|
123
|
+
:headers => {
|
124
|
+
:content_type => 'application/json',
|
125
|
+
:accept => 'application/json'
|
126
|
+
}
|
127
|
+
)
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
data/lib/amon/version.rb
ADDED
data/lib/amon.rb
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'json'
|
2
|
+
require 'rest-client'
|
3
|
+
|
4
|
+
module AMON
|
5
|
+
autoload :JSONHelper, 'amon/json_helper'
|
6
|
+
autoload :Document, 'amon/document'
|
7
|
+
autoload :DocumentPart, 'amon/document_part'
|
8
|
+
autoload :Entity, 'amon/entity'
|
9
|
+
autoload :Meter, 'amon/meter'
|
10
|
+
autoload :MeteringPoint, 'amon/metering_point'
|
11
|
+
autoload :Metadata, 'amon/metadata'
|
12
|
+
autoload :Reading, 'amon/reading'
|
13
|
+
autoload :Measurement, 'amon/measurement'
|
14
|
+
autoload :Version, 'amon/version'
|
15
|
+
autoload :Session, 'amon/session'
|
16
|
+
|
17
|
+
require 'amon/default_session'
|
18
|
+
end
|
data/rails/init.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require 'amon'
|
metadata
ADDED
@@ -0,0 +1,160 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: amon
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 19
|
5
|
+
prerelease:
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 3
|
9
|
+
- 0
|
10
|
+
version: 0.3.0
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- AMEE
|
14
|
+
autorequire:
|
15
|
+
bindir: bin
|
16
|
+
cert_chain: []
|
17
|
+
|
18
|
+
date: 2011-08-25 00:00:00 Z
|
19
|
+
dependencies:
|
20
|
+
- !ruby/object:Gem::Dependency
|
21
|
+
name: json
|
22
|
+
prerelease: false
|
23
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
24
|
+
none: false
|
25
|
+
requirements:
|
26
|
+
- - ~>
|
27
|
+
- !ruby/object:Gem::Version
|
28
|
+
hash: 7
|
29
|
+
segments:
|
30
|
+
- 1
|
31
|
+
- 4
|
32
|
+
- 0
|
33
|
+
version: 1.4.0
|
34
|
+
type: :runtime
|
35
|
+
version_requirements: *id001
|
36
|
+
- !ruby/object:Gem::Dependency
|
37
|
+
name: rest-client
|
38
|
+
prerelease: false
|
39
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
40
|
+
none: false
|
41
|
+
requirements:
|
42
|
+
- - ~>
|
43
|
+
- !ruby/object:Gem::Version
|
44
|
+
hash: 15
|
45
|
+
segments:
|
46
|
+
- 1
|
47
|
+
- 6
|
48
|
+
- 0
|
49
|
+
version: 1.6.0
|
50
|
+
type: :runtime
|
51
|
+
version_requirements: *id002
|
52
|
+
- !ruby/object:Gem::Dependency
|
53
|
+
name: rspec
|
54
|
+
prerelease: false
|
55
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
56
|
+
none: false
|
57
|
+
requirements:
|
58
|
+
- - ~>
|
59
|
+
- !ruby/object:Gem::Version
|
60
|
+
hash: 27
|
61
|
+
segments:
|
62
|
+
- 1
|
63
|
+
- 3
|
64
|
+
- 0
|
65
|
+
version: 1.3.0
|
66
|
+
type: :development
|
67
|
+
version_requirements: *id003
|
68
|
+
- !ruby/object:Gem::Dependency
|
69
|
+
name: mocha
|
70
|
+
prerelease: false
|
71
|
+
requirement: &id004 !ruby/object:Gem::Requirement
|
72
|
+
none: false
|
73
|
+
requirements:
|
74
|
+
- - ~>
|
75
|
+
- !ruby/object:Gem::Version
|
76
|
+
hash: 43
|
77
|
+
segments:
|
78
|
+
- 0
|
79
|
+
- 9
|
80
|
+
- 8
|
81
|
+
version: 0.9.8
|
82
|
+
type: :development
|
83
|
+
version_requirements: *id004
|
84
|
+
- !ruby/object:Gem::Dependency
|
85
|
+
name: fakeweb
|
86
|
+
prerelease: false
|
87
|
+
requirement: &id005 !ruby/object:Gem::Requirement
|
88
|
+
none: false
|
89
|
+
requirements:
|
90
|
+
- - ~>
|
91
|
+
- !ruby/object:Gem::Version
|
92
|
+
hash: 27
|
93
|
+
segments:
|
94
|
+
- 1
|
95
|
+
- 3
|
96
|
+
- 0
|
97
|
+
version: 1.3.0
|
98
|
+
type: :development
|
99
|
+
version_requirements: *id005
|
100
|
+
description:
|
101
|
+
email:
|
102
|
+
executables: []
|
103
|
+
|
104
|
+
extensions: []
|
105
|
+
|
106
|
+
extra_rdoc_files: []
|
107
|
+
|
108
|
+
files:
|
109
|
+
- lib/amon/default_session.rb
|
110
|
+
- lib/amon/document.rb
|
111
|
+
- lib/amon/document_part.rb
|
112
|
+
- lib/amon/entity.rb
|
113
|
+
- lib/amon/json_helper.rb
|
114
|
+
- lib/amon/measurement.rb
|
115
|
+
- lib/amon/metadata.rb
|
116
|
+
- lib/amon/meter.rb
|
117
|
+
- lib/amon/metering_point.rb
|
118
|
+
- lib/amon/reading.rb
|
119
|
+
- lib/amon/session.rb
|
120
|
+
- lib/amon/version.rb
|
121
|
+
- lib/amon.rb
|
122
|
+
- README.md
|
123
|
+
- init.rb
|
124
|
+
- rails/init.rb
|
125
|
+
homepage: http://amee.com
|
126
|
+
licenses: []
|
127
|
+
|
128
|
+
post_install_message:
|
129
|
+
rdoc_options: []
|
130
|
+
|
131
|
+
require_paths:
|
132
|
+
- lib
|
133
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
134
|
+
none: false
|
135
|
+
requirements:
|
136
|
+
- - ">="
|
137
|
+
- !ruby/object:Gem::Version
|
138
|
+
hash: 3
|
139
|
+
segments:
|
140
|
+
- 0
|
141
|
+
version: "0"
|
142
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
143
|
+
none: false
|
144
|
+
requirements:
|
145
|
+
- - ">="
|
146
|
+
- !ruby/object:Gem::Version
|
147
|
+
hash: 3
|
148
|
+
segments:
|
149
|
+
- 0
|
150
|
+
version: "0"
|
151
|
+
requirements: []
|
152
|
+
|
153
|
+
rubyforge_project:
|
154
|
+
rubygems_version: 1.7.2
|
155
|
+
signing_key:
|
156
|
+
specification_version: 3
|
157
|
+
summary: Ruby client for the AMEE AMON API
|
158
|
+
test_files: []
|
159
|
+
|
160
|
+
has_rdoc:
|