amon 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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:
|