device-cloud 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,20 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
18
+ .idea
19
+ config
20
+ atlassian-ide-plugin.xml
data/.travis.yml ADDED
@@ -0,0 +1,8 @@
1
+ language: ruby
2
+ rvm:
3
+ - 1.9.3
4
+ - 2.0.0
5
+ env:
6
+ - TZ=UTC
7
+ - TZ=US/Eastern
8
+ script: bundle exec rspec spec
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in device_cloud-cloud.gemspec
4
+ gemspec
data/Guardfile ADDED
@@ -0,0 +1,19 @@
1
+ # A sample Guardfile
2
+ # More info at https://github.com/guard/guard#readme
3
+
4
+ guard :rspec, cmd: 'rspec' do
5
+ watch(%r{^spec/.+_spec\.rb$})
6
+ watch(%r{^lib/device_cloud/(.+)\.rb$}) { |m| "spec/device_cloud/#{m[1]}_spec.rb" }
7
+ watch('spec/spec_helper.rb') { "spec" }
8
+
9
+ watch(%r{^spec/support/(.+)\.rb$}) { "spec" }
10
+ end
11
+
12
+
13
+
14
+ guard :bundler do
15
+ watch('Gemfile')
16
+ watch(/^.+\.gemspec/)
17
+ end
18
+
19
+
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 Eric Nelson
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,35 @@
1
+ [![Code Climate](https://codeclimate.com/github/nelseric/device_cloud.png)](https://codeclimate.com/github/nelseric/device_cloud)
2
+ [![Build Status](https://travis-ci.org/nelseric/device_cloud.png)](https://travis-ci.org/nelseric/device_cloud)
3
+ [![Dependency Status](https://gemnasium.com/nelseric/device_cloud.png)](https://gemnasium.com/nelseric/device_cloud)
4
+ [![Coverage Status](https://coveralls.io/repos/nelseric/device_cloud/badge.png)](https://coveralls.io/r/nelseric/device_cloud)
5
+ # DeviceCloud
6
+
7
+ Provides API Access for the devicecloud platform to ruby
8
+
9
+ ## Installation
10
+
11
+ Add this line to your application's Gemfile:
12
+
13
+ gem 'device_cloud'
14
+
15
+ And then execute:
16
+
17
+ $ bundle
18
+
19
+ Or install it yourself as:
20
+
21
+ $ gem install device_cloud
22
+
23
+ ## Usage
24
+
25
+ [Rubydoc](http://rubydoc.info/github/nelseric/device_cloud/frames)
26
+
27
+ TODO: Write usage instructions here
28
+
29
+ ## Contributing
30
+
31
+ 1. Fork it
32
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
33
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
34
+ 4. Push to the branch (`git push origin my-new-feature`)
35
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,7 @@
1
+ require "bundler/gem_tasks"
2
+ require "pry"
3
+
4
+ require 'device_cloud'
5
+ task :c do
6
+ binding.pry
7
+ end
@@ -0,0 +1,38 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'device_cloud/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "device-cloud"
8
+ spec.version = DeviceCloud::VERSION.dup
9
+ spec.authors = ["Eric Nelson"]
10
+ spec.email = ["eric@clean-logix.com"]
11
+ spec.description = %q{REST Wrapper for Etherios Devicecloud}
12
+ spec.summary = %q{REST Wrapper for Etherios Devicecloud}
13
+ spec.homepage = ""
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files`.split($/)
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_dependency "nokogiri"
22
+ spec.add_dependency "rest-client"
23
+
24
+ spec.add_development_dependency "bundler", "~> 1.3"
25
+ spec.add_development_dependency "rake"
26
+ spec.add_development_dependency "rspec"
27
+ spec.add_development_dependency "webmock"
28
+ spec.add_development_dependency "pry"
29
+ spec.add_development_dependency "debugger"
30
+ spec.add_development_dependency "guard"
31
+ spec.add_development_dependency "guard-rspec"
32
+ spec.add_development_dependency "guard-bundler"
33
+ spec.add_development_dependency "libnotify"
34
+ spec.add_development_dependency "coveralls"
35
+
36
+
37
+
38
+ end
@@ -0,0 +1,52 @@
1
+ require "rest-client"
2
+
3
+ require "device_cloud/config"
4
+
5
+ module DeviceCloud
6
+ class Client
7
+ attr_accessor :config
8
+
9
+ def initialize(config)
10
+ @config = DeviceCloud::Config.new(config)
11
+ end
12
+
13
+ def devices(device, params = {})
14
+ DeviceCore.parse(get "/ws/DeviceCore/#{device}?#{ to_params params }")
15
+ end
16
+
17
+ def data_streams(stream_path, params = {})
18
+ DataStream.parse(get "#{DataStream::RESOURCE_PATH}/#{stream_path}?#{ to_params params }")
19
+ end
20
+
21
+ def data_points(stream_id, params = {})
22
+ DataPoint.parse(get "/ws/DataPoint/#{stream_id}?#{ to_params params }")
23
+ end
24
+
25
+ def get(path, *args)
26
+ RestClient.get base_url + path, args
27
+ end
28
+
29
+ def put(path, *args)
30
+ RestClient.put base_url + path, args
31
+ end
32
+
33
+ def post(path, *args)
34
+ RestClient.post base_url + path, args
35
+ end
36
+
37
+ def delete(path, *args)
38
+ RestClient.delete base_url + path, args
39
+ end
40
+
41
+ def base_url
42
+ "#{config.protocol}://#{config.username}:#{config.password}@#{config.host}"
43
+ end
44
+
45
+ private
46
+ def to_params(hash)
47
+ hash.each_pair.map do |key, value|
48
+ key + "=" + value
49
+ end.inject { |a, b| a + "&" + b }
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,19 @@
1
+ module DeviceCloud
2
+ class Config
3
+ DEFAULT_USERNAME = ""
4
+ DEFAULT_PASSWORD = ""
5
+ DEFAULT_HOST = "login.etherios.com"
6
+ DEFAULT_PROTOCOL = "https"
7
+
8
+ attr_accessor :username, :password, :host, :protocol
9
+
10
+ def initialize(args)
11
+ args = Hash[*args.map { |k, v| [k.to_sym, v] }.flatten]
12
+ @username = args[:username] || DEFAULT_USERNAME
13
+ @password = args[:password] || DEFAULT_PASSWORD
14
+ @host = args[:host] || DEFAULT_HOST
15
+ @protocol = args[:protocol] || DEFAULT_PROTOCOL
16
+ end
17
+
18
+ end
19
+ end
@@ -0,0 +1,21 @@
1
+ require "device_cloud/result"
2
+
3
+ module DeviceCloud
4
+ class DataPoint
5
+ def self.parse(xml)
6
+ result = Result.new(xml)
7
+ result.document.xpath("//result/DataPoint").map do |data_point|
8
+ DeviceCloud::DataPoint.new data_point
9
+ end
10
+ end
11
+
12
+ attr_accessor :element, :id, :value, :timestamp, :stream_id
13
+
14
+ def initialize(element)
15
+ @id = element.xpath("id").text
16
+ @value = element.xpath("data").text
17
+ @timestamp = Time.at(element.xpath("timestamp").text.to_f / 1000)
18
+ @stream_id = element.xpath("streamId").text
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,65 @@
1
+ require "device_cloud/result"
2
+ module DeviceCloud
3
+ class DataStream
4
+ RESOURCE_PATH = '/ws/DataStream'
5
+
6
+ def self.parse(xml)
7
+ result = Result.new(xml)
8
+ result.document.xpath("//result/DataStream").map do |stream|
9
+ DeviceCloud::DataStream.new stream
10
+ end
11
+ end
12
+
13
+ attr_accessor :element
14
+
15
+ def initialize(element)
16
+ @element = element
17
+ end
18
+
19
+ def delete(client)
20
+ client.delete RESOURCE_PATH + "/" + stream_id
21
+ end
22
+
23
+ def value
24
+ current.value
25
+ end
26
+
27
+ def timestamp
28
+ current.timestamp
29
+ end
30
+
31
+ def current_id
32
+ current.id
33
+ end
34
+
35
+ def stream_id
36
+ element.xpath("streamId").text
37
+ end
38
+
39
+ def device_id
40
+ stream_id_parse[1]
41
+ end
42
+
43
+ def mac
44
+ stream_id_parse[2].to_i(16)
45
+ end
46
+
47
+ def name
48
+ stream_id_parse[3]
49
+ end
50
+
51
+ def device_path
52
+ "dia/channel/#{device_id}/#{mac.to_s(16).rjust(16,"0")}/"
53
+ end
54
+
55
+ private
56
+
57
+ def stream_id_parse
58
+ /dia\/channel\/((?:[0-9a-fA-F]{8}-?){4})\/([^\/]*)\/([a-z_]*)/.match stream_id
59
+ end
60
+
61
+ def current
62
+ DataPoint.new(element.xpath("currentValue"))
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,52 @@
1
+ require "device_cloud/result"
2
+
3
+ module DeviceCloud
4
+ class DeviceCore
5
+
6
+ def self.parse(xml)
7
+ result = Result.new(xml)
8
+ result.document.xpath("//result/DeviceCore").map do |device|
9
+ DeviceCloud::DeviceCore.new device
10
+ end
11
+ end
12
+
13
+ attr_accessor :element
14
+
15
+ def initialize element
16
+ @element = element
17
+ end
18
+
19
+ def device_id
20
+ attribute "devConnectwareId"
21
+ end
22
+
23
+ def name
24
+ attribute "dpDescription"
25
+ end
26
+
27
+ def connection_state
28
+ attribute("dpConnectionStatus") == "1"
29
+ end
30
+
31
+ def last_connect_time
32
+ Time.parse attribute "dpLastConnectTime"
33
+ end
34
+
35
+ def last_disconnect_time
36
+ Time.parse attribute "dpLastDisconnectTime"
37
+ end
38
+
39
+ def global_ip
40
+ attribute "dpGlobalIp"
41
+ end
42
+
43
+ def local_ip
44
+ attribute "dpLastKnownIp"
45
+ end
46
+
47
+ private
48
+ def attribute(xpath)
49
+ element.xpath(xpath).text
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,58 @@
1
+ require "nokogiri"
2
+ module DeviceCloud
3
+ class Monitor
4
+
5
+ RESOURCE_PATH = "/ws/Monitor"
6
+
7
+ attr_accessor :topic, :id, :element
8
+
9
+ def self.all(client)
10
+ xml_result = Nokogiri::XML.parse client.get RESOURCE_PATH
11
+ xml_result.xpath("//result/Monitor").map { |monitor| parse monitor }
12
+ end
13
+
14
+ def self.parse(xml)
15
+ topic = xml.xpath("monTopic").text
16
+ DeviceCloud::Monitor.new(topic, xml)
17
+ end
18
+
19
+ def initialize(topic, element = nil)
20
+ @topic = topic
21
+ @element = element
22
+ end
23
+
24
+ def resource_path
25
+ RESOURCE_PATH
26
+ end
27
+
28
+
29
+ def save(client)
30
+ response = client.post resource_path, build.to_xml
31
+ xml_res = Nokogiri::XML.parse response
32
+ @id = xml_res.xpath("//result/location").text
33
+ end
34
+
35
+ def delete(client)
36
+ raise NotImplementedError, "Incomplete"
37
+ puts client.delete resource_path
38
+ end
39
+
40
+ def update
41
+
42
+ end
43
+
44
+ def build
45
+ builder = Nokogiri::XML::Builder.new do |xml|
46
+ xml.Monitor {
47
+ xml.monTopic @topic
48
+ xml.monTransportType "http"
49
+ xml.monTransportUrl "https://staging.cleanintel.com/ivx/sync"
50
+ xml.monFormatType "json"
51
+ xml.monBatchSize 100
52
+ xml.monCompression "none"
53
+ xml.monBatchDuration 10
54
+ }
55
+ end
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,33 @@
1
+ require "nokogiri"
2
+ module DeviceCloud
3
+ class InvalidResultError < StandardError
4
+ end
5
+
6
+ class Result
7
+ include Enumerable
8
+
9
+ attr_accessor :document
10
+
11
+ def initialize(xml)
12
+ @document = Nokogiri::XML.parse(xml)
13
+ raise InvalidResultError if @document.xpath("//result").empty?
14
+ end
15
+
16
+ def total
17
+ @document.xpath("//result/resultTotalRows").text.to_i
18
+ end
19
+
20
+ def offset
21
+ @document.xpath("//result/requestedStartRow").text.to_i
22
+ end
23
+
24
+ def count
25
+ @document.xpath("//result/resultSize").text.to_i
26
+ end
27
+
28
+ def requested
29
+ @document.xpath("//result/requestedSize").text.to_i
30
+ end
31
+
32
+ end
33
+ end
@@ -0,0 +1,3 @@
1
+ module DeviceCloud
2
+ VERSION = "0.2.0"
3
+ end
@@ -0,0 +1,8 @@
1
+ require 'device_cloud/version'
2
+ require 'device_cloud/config'
3
+ require 'device_cloud/client'
4
+ require 'device_cloud/device_core'
5
+ require 'device_cloud/data_stream'
6
+ require 'device_cloud/data_point'
7
+ require 'device_cloud/result'
8
+ require 'device_cloud/monitor'
@@ -0,0 +1,20 @@
1
+ require "spec_helper"
2
+
3
+ describe DeviceCloud::Client do
4
+ subject do
5
+ DeviceCloud::Client.new(:username => username, :password => password, :host => host, :protocol => protocol)
6
+ end
7
+ let(:username) { "test" }
8
+ let(:password) { "test" }
9
+ let(:host) { "login.etherios.com" }
10
+ let(:protocol) { "https" }
11
+
12
+
13
+ it 'will generate a base_url using the configuration' do
14
+ subject.base_url.should == "#{protocol}://#{username}:#{password}@#{host}"
15
+ subject.config.protocol = "http"
16
+ subject.base_url.should == "http://#{username}:#{password}@#{host}"
17
+ subject.config.host = "google.com"
18
+ subject.base_url.should == "http://#{username}:#{password}@google.com"
19
+ end
20
+ end
@@ -0,0 +1,27 @@
1
+ require "spec_helper"
2
+
3
+ describe DeviceCloud::Config do
4
+
5
+ subject do
6
+ DeviceCloud::Config.new(:username => username, :password => password, :host => host, :protocol => protocol)
7
+ end
8
+ let(:username) { "testl-user" }
9
+ let(:password) { "test-pass" }
10
+ let(:host) { "login.etherios.com" }
11
+ let(:protocol) { "https" }
12
+
13
+ it "sets provided values" do
14
+ subject.host.should == host
15
+ subject.protocol.should == protocol
16
+ subject.username.should == username
17
+ subject.password.should == password
18
+ end
19
+
20
+ it "has default values" do
21
+ config = DeviceCloud::Config.new({})
22
+ config.host.should == "login.etherios.com"
23
+ config.protocol.should == "https"
24
+ config.username.should == ""
25
+ config.password.should == ""
26
+ end
27
+ end
@@ -0,0 +1,79 @@
1
+ require 'spec_helper'
2
+
3
+
4
+ describe DeviceCloud::DataPoint do
5
+
6
+ subject(:data_point) do
7
+ DeviceCloud::DataPoint.parse(xml).first
8
+ end
9
+
10
+ let(:xml) do
11
+ <<-XML
12
+ <?xml version="1.0" encoding="ISO-8859-1" ?>
13
+ <result>
14
+ <resultSize>2</resultSize>
15
+ <requestedSize>1000</requestedSize>
16
+ <pageCursor>51075196-53b4-11e3-8b51-bc764e113426</pageCursor>
17
+ <requestedStartTime>-1</requestedStartTime>
18
+ <requestedEndTime>-1</requestedEndTime>
19
+ <DataPoint>
20
+ <id>cfc57f41-520e-11e3-8b51-bc764e113426</id>
21
+ <cstId>5940</cstId>
22
+ <streamId>dia/channel/00000000-00000000-00409DFF-FF725771/0013a2004063d301/battery</streamId>
23
+ <timestamp>1384970932009</timestamp>
24
+ <serverTimestamp>1384970934108</serverTimestamp>
25
+ <data>5.5</data>
26
+ <description/>
27
+ <quality>0</quality>
28
+ </DataPoint>
29
+ <DataPoint>
30
+ <id>13b7174e-520f-11e3-8092-bc764e105279</id>
31
+ <cstId>5940</cstId>
32
+ <streamId>dia/channel/00000000-00000000-00409DFF-FF725771/0013a2004063d301/battery</streamId>
33
+ <timestamp>1384971046000</timestamp>
34
+ <serverTimestamp>1384971052748</serverTimestamp>
35
+ <data>4.45</data>
36
+ <description/>
37
+ <quality>0</quality>
38
+ </DataPoint>
39
+ </result>
40
+ XML
41
+ end
42
+
43
+ describe ".parse" do
44
+ it "will return a data point for each object" do
45
+ result = DeviceCloud::DataPoint.parse xml
46
+ expect(result.count).to eql 2
47
+ end
48
+ context "the xml to parse is empty" do
49
+ it "will raise an InvalidResultError" do
50
+ expect { DeviceCloud::DataPoint.parse "" }.to raise_error DeviceCloud::InvalidResultError
51
+ end
52
+ end
53
+ end
54
+
55
+ describe "#id" do
56
+ it "will return the data point id" do
57
+ expect(data_point.id).to eql "cfc57f41-520e-11e3-8b51-bc764e113426"
58
+ end
59
+ end
60
+
61
+ describe "#value" do
62
+ it "will return the data points value" do
63
+ expect(data_point.value).to eql "5.5"
64
+ end
65
+ end
66
+
67
+ describe "#timestamp" do
68
+ it "returns the timestamp of the data point" do
69
+ expect(Time.at(data_point.timestamp.to_i)).to eql Time.parse("2013-11-20T13:8:52-05:00")
70
+ end
71
+ end
72
+
73
+ describe "#stream_id" do
74
+ it "returns the id for for the DataStream that represents these DataPoints" do
75
+ expect(data_point.stream_id).to eql "dia/channel/00000000-00000000-00409DFF-FF725771/0013a2004063d301/battery"
76
+ end
77
+ end
78
+
79
+ end
@@ -0,0 +1,155 @@
1
+ require 'spec_helper'
2
+
3
+ describe DeviceCloud::DataStream do
4
+
5
+ subject(:data_stream) do
6
+ DeviceCloud::DataStream.parse(xml).first
7
+ end
8
+
9
+ let(:xml) do
10
+ <<-XML
11
+ <?xml version="1.0" encoding="ISO-8859-1"?>
12
+ <result>
13
+ <resultSize>4</resultSize>
14
+ <requestedSize>1000</requestedSize>
15
+ <pageCursor>76cab9ad-5-ebd5ccac</pageCursor>
16
+ <DataStream>
17
+ <cstId>5940</cstId>
18
+ <streamId>dia/channel/00000000-00000000-00409DFF-FF725771/0013a20040a52e1d/distance</streamId>
19
+ <dataType>DOUBLE</dataType>
20
+ <forwardTo/>
21
+ <currentValue>
22
+ <id>5101f920-53b4-11e3-8b51-bc764e113426</id>
23
+ <timestamp>1385151966954</timestamp>
24
+ <serverTimestamp>1385151968249</serverTimestamp>
25
+ <data>44.2</data>
26
+ <description/>
27
+ <quality>0</quality>
28
+ </currentValue>
29
+ <description/>
30
+ <units>in</units>
31
+ <dataTtl>2678400</dataTtl>
32
+ <rollupTtl>8035200</rollupTtl>
33
+ </DataStream>
34
+ <DataStream>
35
+ <cstId>5940</cstId>
36
+ <streamId>dia/channel/00000000-00000000-00409DFF-FF725771/0013a2004063d301/distance</streamId>
37
+ <dataType>DOUBLE</dataType>
38
+ <forwardTo/>
39
+ <currentValue>
40
+ <id>510c337d-53b4-11e3-8b51-bc764e113426</id>
41
+ <timestamp>1385151967021</timestamp>
42
+ <serverTimestamp>1385151968488</serverTimestamp>
43
+ <data>0.0</data>
44
+ <description/>
45
+ <quality>0</quality>
46
+ </currentValue>
47
+ <description/>
48
+ <units>in</units>
49
+ <dataTtl>2678400</dataTtl>
50
+ <rollupTtl>8035200</rollupTtl>
51
+ </DataStream>
52
+ <DataStream>
53
+ <cstId>5940</cstId>
54
+ <streamId>dia/channel/00000000-00000000-00409DFF-FF725771/0013a200408cb965/distance</streamId>
55
+ <dataType>DOUBLE</dataType>
56
+ <forwardTo/>
57
+ <currentValue>
58
+ <id>50e8a4e8-53b4-11e3-8b51-bc764e113426</id>
59
+ <timestamp>1385151966788</timestamp>
60
+ <serverTimestamp>1385151968064</serverTimestamp>
61
+ <data>0.0</data>
62
+ <description/>
63
+ <quality>0</quality>
64
+ </currentValue>
65
+ <description/>
66
+ <units>in</units>
67
+ <dataTtl>2678400</dataTtl>
68
+ <rollupTtl>8035200</rollupTtl>
69
+ </DataStream>
70
+ <DataStream>
71
+ <cstId>5940</cstId>
72
+ <streamId>dia/channel/00000000-00000000-00409DFF-FF725771/0013a20040a52b7a/distance</streamId>
73
+ <dataType>DOUBLE</dataType>
74
+ <forwardTo/>
75
+ <currentValue>
76
+ <id>50f1cc94-53b4-11e3-8b51-bc764e113426</id>
77
+ <timestamp>1385151966848</timestamp>
78
+ <serverTimestamp>1385151968068</serverTimestamp>
79
+ <data>0.0</data>
80
+ <description/>
81
+ <quality>0</quality>
82
+ </currentValue>
83
+ <description/>
84
+ <units>in</units>
85
+ <dataTtl>2678400</dataTtl>
86
+ <rollupTtl>8035200</rollupTtl>
87
+ </DataStream>
88
+ </result>
89
+ XML
90
+ end
91
+
92
+ describe ".parse" do
93
+ it "will return an array containing a data stream for each stream in the xml" do
94
+ expect(DeviceCloud::DataStream.parse(xml).count).to eql 4
95
+ end
96
+ end
97
+
98
+ describe "#delete" do
99
+ it "will use the client to delete the stream" do
100
+ client = double("DeviceCloud::Client", :delete => nil)
101
+
102
+ data_stream.delete(client)
103
+
104
+ expect(client).to have_received(:delete).with("/ws/DataStream/dia/channel/00000000-00000000-00409DFF-FF725771/0013a20040a52e1d/distance")
105
+ end
106
+ end
107
+
108
+ describe "#value" do
109
+ it "returns the current value of the stream" do
110
+ expect(data_stream.value).to eql "44.2"
111
+ end
112
+ end
113
+
114
+ describe "#timestamp" do
115
+ it "returns the timestamp of the current datapoint for the stream" do
116
+ expect(Time.at(data_stream.timestamp.to_i)).to eql Time.parse("2013-11-22T15:26:06-05:00")
117
+ end
118
+ end
119
+
120
+ describe "#current_id" do
121
+ it "returns the id of the current datapoint for the stream" do
122
+ expect(data_stream.current_id).to eql "5101f920-53b4-11e3-8b51-bc764e113426"
123
+ end
124
+ end
125
+
126
+ describe "#stream_id" do
127
+ it "returns the stream id" do
128
+ expect(data_stream.stream_id).to eql "dia/channel/00000000-00000000-00409DFF-FF725771/0013a20040a52e1d/distance"
129
+ end
130
+ end
131
+
132
+ describe "#device_id" do
133
+ it "returns the device id out of the stream_id" do
134
+ expect(data_stream.device_id).to eql "00000000-00000000-00409DFF-FF725771"
135
+ end
136
+ end
137
+
138
+ describe "#mac" do
139
+ it "returns the sensor mac from the device id and converts it to an integer" do
140
+ expect(data_stream.mac).to eql "0013a20040a52e1d".to_i(16)
141
+ end
142
+ end
143
+
144
+ describe "#name" do
145
+ it "returns the stream name" do
146
+ expect(data_stream.name).to eql "distance"
147
+ end
148
+ end
149
+
150
+ describe "#device_path" do
151
+ it "generates the path for the sensor from any stream belonging to the device" do
152
+ expect(data_stream.device_path).to eql "dia/channel/00000000-00000000-00409DFF-FF725771/0013a20040a52e1d/"
153
+ end
154
+ end
155
+ end
@@ -0,0 +1,143 @@
1
+ require "spec_helper"
2
+
3
+ describe DeviceCloud::DeviceCore do
4
+ subject(:device) { DeviceCloud::DeviceCore.parse(xml).first }
5
+
6
+ let(:xml) do
7
+ <<-XML
8
+ <?xml version="1.0" encoding="ISO-8859-1"?>
9
+ <result>
10
+ <resultTotalRows>2</resultTotalRows>
11
+ <requestedStartRow>0</requestedStartRow>
12
+ <resultSize>2</resultSize>
13
+ <requestedSize>1000</requestedSize>
14
+ <remainingSize>0</remainingSize>
15
+ <DeviceCore>
16
+ <id>
17
+ <devId>747711</devId>
18
+ <devVersion>4</devVersion>
19
+ </id>
20
+ <devRecordStartDate>2013-11-18T15:41:00.000Z</devRecordStartDate>
21
+ <devMac>00:40:9D:70:6F:FF</devMac>
22
+ <devConnectwareId>00000000-00000000-00409DFF-FF706FFF</devConnectwareId>
23
+ <cstId>5940</cstId>
24
+ <grpId>8892</grpId>
25
+ <devEffectiveStartDate>2013-11-18T13:53:00.000Z</devEffectiveStartDate>
26
+ <devTerminated>false</devTerminated>
27
+ <dvVendorId>4261412864</dvVendorId>
28
+ <dpDeviceType>ConnectPort X4</dpDeviceType>
29
+ <dpFirmwareLevel>34603011</dpFirmwareLevel>
30
+ <dpFirmwareLevelDesc>2.16.0.3</dpFirmwareLevelDesc>
31
+ <dpRestrictedStatus>0</dpRestrictedStatus>
32
+ <dpLastKnownIp>192.168.2.10</dpLastKnownIp>
33
+ <dpGlobalIp>99.91.75.14</dpGlobalIp>
34
+ <dpConnectionStatus>1</dpConnectionStatus>
35
+ <dpLastConnectTime>2013-11-22T18:05:45.590Z</dpLastConnectTime>
36
+ <dpContact/>
37
+ <dpDescription>IVX 400 Test Rig</dpDescription>
38
+ <dpLocation/>
39
+ <dpPanId>0x679d</dpPanId>
40
+ <xpExtAddr>00:13:A2:00:40:91:99:29</xpExtAddr>
41
+ <dpServerId>ClientID[15]</dpServerId>
42
+ <dpZigbeeCapabilities>383</dpZigbeeCapabilities>
43
+ <dpCapabilities>68154</dpCapabilities>
44
+ <grpPath>IVX</grpPath>
45
+ <dpLastDisconnectTime>2013-11-18T19:22:51.067Z</dpLastDisconnectTime>
46
+ </DeviceCore>
47
+ <DeviceCore>
48
+ <id>
49
+ <devId>873214</devId>
50
+ <devVersion>4</devVersion>
51
+ </id>
52
+ <devRecordStartDate>2013-11-18T15:41:00.000Z</devRecordStartDate>
53
+ <devMac>00:40:9d:72:57:71</devMac>
54
+ <devConnectwareId>00000000-00000000-00409DFF-FF725771</devConnectwareId>
55
+ <cstId>5940</cstId>
56
+ <grpId>8892</grpId>
57
+ <devEffectiveStartDate>2013-11-18T15:32:00.000Z</devEffectiveStartDate>
58
+ <devTerminated>false</devTerminated>
59
+ <dvVendorId>4261412864</dvVendorId>
60
+ <dpDeviceType>ConnectPort X4</dpDeviceType>
61
+ <dpFirmwareLevel>34603011</dpFirmwareLevel>
62
+ <dpFirmwareLevelDesc>2.16.0.3</dpFirmwareLevelDesc>
63
+ <dpRestrictedStatus>0</dpRestrictedStatus>
64
+ <dpLastKnownIp>192.168.2.58</dpLastKnownIp>
65
+ <dpGlobalIp>99.91.75.14</dpGlobalIp>
66
+ <dpConnectionStatus>0</dpConnectionStatus>
67
+ <dpLastConnectTime>2013-11-22T20:13:17.340Z</dpLastConnectTime>
68
+ <dpContact/>
69
+ <dpDescription>DeLaval IVX</dpDescription>
70
+ <dpLocation/>
71
+ <dpPanId>0x123d</dpPanId>
72
+ <xpExtAddr>00:13:A2:00:40:A4:C1:EC</xpExtAddr>
73
+ <dpServerId/>
74
+ <dpZigbeeCapabilities>383</dpZigbeeCapabilities>
75
+ <dpCapabilities>68154</dpCapabilities>
76
+ <grpPath>IVX</grpPath>
77
+ <dpLastDisconnectTime>2013-11-22T20:56:16.547Z</dpLastDisconnectTime>
78
+ </DeviceCore>
79
+ </result>
80
+ XML
81
+ end
82
+
83
+ describe ".parse" do
84
+ it "will return an array containing device core objects" do
85
+ expect(DeviceCloud::DeviceCore.parse(xml).count).to eql 2
86
+ end
87
+ context "the xml is empty" do
88
+ let(:xml) { "" }
89
+ it "will raise an error" do
90
+ expect { DeviceCloud::DeviceCore.parse(xml) }.to raise_error DeviceCloud::InvalidResultError
91
+ end
92
+ end
93
+ end
94
+
95
+ describe "#device_id" do
96
+ it "returns the connectware id" do
97
+ expect(device.device_id).to eql "00000000-00000000-00409DFF-FF706FFF"
98
+ end
99
+ end
100
+
101
+ describe "#name" do
102
+ it "returns the device description" do
103
+ expect(device.name).to eql "IVX 400 Test Rig"
104
+ end
105
+ end
106
+ describe "#connection_state" do
107
+ it "returns true if the device is connected" do
108
+ expect(device.connection_state).to eql true
109
+ end
110
+ context "the device is not connected" do
111
+ subject(:device) { DeviceCloud::DeviceCore.parse(xml).last }
112
+
113
+ it "returns false" do
114
+ expect(device.connection_state).to eql false
115
+ end
116
+ end
117
+ end
118
+
119
+ describe "#last_connect_time" do
120
+ it "returns the time the device last connected" do
121
+ expect(device.last_connect_time).to eql Time.parse("2013-11-22T18:05:45.590Z")
122
+ end
123
+ end
124
+
125
+ describe "#last_disconnect_time" do
126
+ it "returns the time the device last disconnected" do
127
+ expect(device.last_disconnect_time).to eql Time.parse("2013-11-18T19:22:51.067Z")
128
+ end
129
+ end
130
+
131
+ describe "#global_ip" do
132
+ it "returns the global ip address of the device" do
133
+ expect(device.global_ip).to eql "99.91.75.14"
134
+ end
135
+ end
136
+
137
+ describe "#local_ip" do
138
+ it "returns the local ip address of the device" do
139
+ expect(device.local_ip).to eql "192.168.2.10"
140
+ end
141
+ end
142
+
143
+ end
@@ -0,0 +1,43 @@
1
+ require "spec_helper"
2
+
3
+ describe DeviceCloud::Monitor do
4
+ subject(:monitor) do
5
+ DeviceCloud::Monitor.new(topic)
6
+ end
7
+
8
+ let(:client) { DeviceCloud::Client.new(:username => "test", :password => "test") }
9
+
10
+ let(:api_stub) { stub_request :any, /#{client.base_url}.*/ }
11
+
12
+ let(:topic) { "DiaChannelDataFull" }
13
+
14
+ describe "#build" do
15
+ let(:document) { monitor.build.doc }
16
+ it 'it will have the correct topic' do
17
+ document.xpath("//Monitor/monTopic").text.should == topic
18
+ monitor.topic = "DeviceCore,DataStream"
19
+ document.xpath("//Monitor/monTopic").text.should == topic
20
+ end
21
+ end
22
+
23
+
24
+ describe "#save" do
25
+ before do
26
+ api_stub
27
+ end
28
+ it "should POSTed to $base/ws/Monitor" do
29
+ monitor.save(client)
30
+ a_request(:post, client.base_url + "/ws/Monitor").should have_been_made
31
+ end
32
+ end
33
+
34
+ describe "all" do
35
+ before do
36
+ api_stub
37
+ end
38
+ it "should get on the root of monitor" do
39
+ DeviceCloud::Monitor.all(client)
40
+ a_request(:get, client.base_url + "/ws/Monitor").should have_been_made
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,47 @@
1
+ require "spec_helper"
2
+
3
+ describe DeviceCloud::Result do
4
+
5
+ subject do
6
+ DeviceCloud::Result.new xml
7
+ end
8
+
9
+ let(:xml) do
10
+ <<-XML
11
+ <?xml version="1.0" encoding="ISO-8859-1" ?>
12
+ <result>
13
+ <resultSize>2</resultSize>
14
+ <requestedSize>1000</requestedSize>
15
+ <pageCursor>51075196-53b4-11e3-8b51-bc764e113426</pageCursor>
16
+ <requestedStartTime>-1</requestedStartTime>
17
+ <requestedEndTime>-1</requestedEndTime>
18
+ <FooBar>
19
+ <Foo>Bar</Foo>
20
+ </FooBar>
21
+ <FooBar>
22
+ <Foo>Bar</Foo>
23
+ </FooBar>
24
+ </result>
25
+ XML
26
+ end
27
+
28
+
29
+ describe "#count" do
30
+ it "will return the total number of items in the result set" do
31
+ expect(subject.count).to eql 2
32
+ end
33
+ end
34
+
35
+ describe "#requested" do
36
+ it "will return the requested maximum size of the of the result dataset" do
37
+ expect(subject.requested).to eql 1000
38
+ end
39
+ end
40
+
41
+ context("xml is empty or does not contrain a result root element") do
42
+ let(:xml) { "" }
43
+ it "will raise InvalidResultError" do
44
+ expect { subject }.to raise_error DeviceCloud::InvalidResultError
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,13 @@
1
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
2
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
3
+ require 'coveralls'
4
+ Coveralls.wear!
5
+
6
+
7
+ require "device_cloud"
8
+
9
+ require 'webmock/rspec'
10
+
11
+ RSpec.configure do |config|
12
+ config.include WebMock::API
13
+ end
metadata ADDED
@@ -0,0 +1,288 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: device-cloud
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.2.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Eric Nelson
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2014-01-15 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: nokogiri
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: '0'
30
+ - !ruby/object:Gem::Dependency
31
+ name: rest-client
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: '0'
38
+ type: :runtime
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
46
+ - !ruby/object:Gem::Dependency
47
+ name: bundler
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ~>
52
+ - !ruby/object:Gem::Version
53
+ version: '1.3'
54
+ type: :development
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ~>
60
+ - !ruby/object:Gem::Version
61
+ version: '1.3'
62
+ - !ruby/object:Gem::Dependency
63
+ name: rake
64
+ requirement: !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - ! '>='
68
+ - !ruby/object:Gem::Version
69
+ version: '0'
70
+ type: :development
71
+ prerelease: false
72
+ version_requirements: !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ! '>='
76
+ - !ruby/object:Gem::Version
77
+ version: '0'
78
+ - !ruby/object:Gem::Dependency
79
+ name: rspec
80
+ requirement: !ruby/object:Gem::Requirement
81
+ none: false
82
+ requirements:
83
+ - - ! '>='
84
+ - !ruby/object:Gem::Version
85
+ version: '0'
86
+ type: :development
87
+ prerelease: false
88
+ version_requirements: !ruby/object:Gem::Requirement
89
+ none: false
90
+ requirements:
91
+ - - ! '>='
92
+ - !ruby/object:Gem::Version
93
+ version: '0'
94
+ - !ruby/object:Gem::Dependency
95
+ name: webmock
96
+ requirement: !ruby/object:Gem::Requirement
97
+ none: false
98
+ requirements:
99
+ - - ! '>='
100
+ - !ruby/object:Gem::Version
101
+ version: '0'
102
+ type: :development
103
+ prerelease: false
104
+ version_requirements: !ruby/object:Gem::Requirement
105
+ none: false
106
+ requirements:
107
+ - - ! '>='
108
+ - !ruby/object:Gem::Version
109
+ version: '0'
110
+ - !ruby/object:Gem::Dependency
111
+ name: pry
112
+ requirement: !ruby/object:Gem::Requirement
113
+ none: false
114
+ requirements:
115
+ - - ! '>='
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ none: false
122
+ requirements:
123
+ - - ! '>='
124
+ - !ruby/object:Gem::Version
125
+ version: '0'
126
+ - !ruby/object:Gem::Dependency
127
+ name: debugger
128
+ requirement: !ruby/object:Gem::Requirement
129
+ none: false
130
+ requirements:
131
+ - - ! '>='
132
+ - !ruby/object:Gem::Version
133
+ version: '0'
134
+ type: :development
135
+ prerelease: false
136
+ version_requirements: !ruby/object:Gem::Requirement
137
+ none: false
138
+ requirements:
139
+ - - ! '>='
140
+ - !ruby/object:Gem::Version
141
+ version: '0'
142
+ - !ruby/object:Gem::Dependency
143
+ name: guard
144
+ requirement: !ruby/object:Gem::Requirement
145
+ none: false
146
+ requirements:
147
+ - - ! '>='
148
+ - !ruby/object:Gem::Version
149
+ version: '0'
150
+ type: :development
151
+ prerelease: false
152
+ version_requirements: !ruby/object:Gem::Requirement
153
+ none: false
154
+ requirements:
155
+ - - ! '>='
156
+ - !ruby/object:Gem::Version
157
+ version: '0'
158
+ - !ruby/object:Gem::Dependency
159
+ name: guard-rspec
160
+ requirement: !ruby/object:Gem::Requirement
161
+ none: false
162
+ requirements:
163
+ - - ! '>='
164
+ - !ruby/object:Gem::Version
165
+ version: '0'
166
+ type: :development
167
+ prerelease: false
168
+ version_requirements: !ruby/object:Gem::Requirement
169
+ none: false
170
+ requirements:
171
+ - - ! '>='
172
+ - !ruby/object:Gem::Version
173
+ version: '0'
174
+ - !ruby/object:Gem::Dependency
175
+ name: guard-bundler
176
+ requirement: !ruby/object:Gem::Requirement
177
+ none: false
178
+ requirements:
179
+ - - ! '>='
180
+ - !ruby/object:Gem::Version
181
+ version: '0'
182
+ type: :development
183
+ prerelease: false
184
+ version_requirements: !ruby/object:Gem::Requirement
185
+ none: false
186
+ requirements:
187
+ - - ! '>='
188
+ - !ruby/object:Gem::Version
189
+ version: '0'
190
+ - !ruby/object:Gem::Dependency
191
+ name: libnotify
192
+ requirement: !ruby/object:Gem::Requirement
193
+ none: false
194
+ requirements:
195
+ - - ! '>='
196
+ - !ruby/object:Gem::Version
197
+ version: '0'
198
+ type: :development
199
+ prerelease: false
200
+ version_requirements: !ruby/object:Gem::Requirement
201
+ none: false
202
+ requirements:
203
+ - - ! '>='
204
+ - !ruby/object:Gem::Version
205
+ version: '0'
206
+ - !ruby/object:Gem::Dependency
207
+ name: coveralls
208
+ requirement: !ruby/object:Gem::Requirement
209
+ none: false
210
+ requirements:
211
+ - - ! '>='
212
+ - !ruby/object:Gem::Version
213
+ version: '0'
214
+ type: :development
215
+ prerelease: false
216
+ version_requirements: !ruby/object:Gem::Requirement
217
+ none: false
218
+ requirements:
219
+ - - ! '>='
220
+ - !ruby/object:Gem::Version
221
+ version: '0'
222
+ description: REST Wrapper for Etherios Devicecloud
223
+ email:
224
+ - eric@clean-logix.com
225
+ executables: []
226
+ extensions: []
227
+ extra_rdoc_files: []
228
+ files:
229
+ - .gitignore
230
+ - .travis.yml
231
+ - Gemfile
232
+ - Guardfile
233
+ - LICENSE.txt
234
+ - README.md
235
+ - Rakefile
236
+ - device-cloud.gemspec
237
+ - lib/device_cloud.rb
238
+ - lib/device_cloud/client.rb
239
+ - lib/device_cloud/config.rb
240
+ - lib/device_cloud/data_point.rb
241
+ - lib/device_cloud/data_stream.rb
242
+ - lib/device_cloud/device_core.rb
243
+ - lib/device_cloud/monitor.rb
244
+ - lib/device_cloud/result.rb
245
+ - lib/device_cloud/version.rb
246
+ - spec/device_cloud/client_spec.rb
247
+ - spec/device_cloud/config_spec.rb
248
+ - spec/device_cloud/data_point_spec.rb
249
+ - spec/device_cloud/data_stream_spec.rb
250
+ - spec/device_cloud/device_core_spec.rb
251
+ - spec/device_cloud/monitor_spec.rb
252
+ - spec/device_cloud/result_spec.rb
253
+ - spec/spec_helper.rb
254
+ homepage: ''
255
+ licenses:
256
+ - MIT
257
+ post_install_message:
258
+ rdoc_options: []
259
+ require_paths:
260
+ - lib
261
+ required_ruby_version: !ruby/object:Gem::Requirement
262
+ none: false
263
+ requirements:
264
+ - - ! '>='
265
+ - !ruby/object:Gem::Version
266
+ version: '0'
267
+ required_rubygems_version: !ruby/object:Gem::Requirement
268
+ none: false
269
+ requirements:
270
+ - - ! '>='
271
+ - !ruby/object:Gem::Version
272
+ version: '0'
273
+ requirements: []
274
+ rubyforge_project:
275
+ rubygems_version: 1.8.23
276
+ signing_key:
277
+ specification_version: 3
278
+ summary: REST Wrapper for Etherios Devicecloud
279
+ test_files:
280
+ - spec/device_cloud/client_spec.rb
281
+ - spec/device_cloud/config_spec.rb
282
+ - spec/device_cloud/data_point_spec.rb
283
+ - spec/device_cloud/data_stream_spec.rb
284
+ - spec/device_cloud/device_core_spec.rb
285
+ - spec/device_cloud/monitor_spec.rb
286
+ - spec/device_cloud/result_spec.rb
287
+ - spec/spec_helper.rb
288
+ has_rdoc: