botvac 0.1.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.
- checksums.yaml +7 -0
- data/.gitignore +2 -0
- data/Gemfile +3 -0
- data/Gemfile.lock +94 -0
- data/Guardfile +22 -0
- data/LICENSE.txt +21 -0
- data/README.md +90 -0
- data/Rakefile +6 -0
- data/botvac.gemspec +37 -0
- data/cert/neatocloud.com.crt +28 -0
- data/lib/botvac/robot.rb +87 -0
- data/lib/botvac/version.rb +3 -0
- data/lib/botvac/web.rb +21 -0
- data/lib/botvac.rb +6 -0
- data/spec/botvac/robot_spec.rb +25 -0
- data/spec/spec_helper.rb +43 -0
- metadata +204 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 54cc8a85f652eb06c58b8df33abd1ebad8521a3d
|
4
|
+
data.tar.gz: 5462bcce82def5447f40955da15dd2adc4901106
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 071f771d5b2927a1c62a0ac25355307c30946cf12d1be3ecac0af09b54232f0a3ff05786963d42026372967a7bd0a29249855428b326f15936a4fc3c2593e1eb
|
7
|
+
data.tar.gz: 25a0a2fe541e62b834eabb589ed58d6a2e92e66a198e0153eb7090be3a45dbc250b9c98964d6922d9035d6213becedaf9bef0bbebfb00f3694a738d673b0c2f1
|
data/.gitignore
ADDED
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,94 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
botvac (0.1.0)
|
5
|
+
faraday
|
6
|
+
faraday_middleware
|
7
|
+
rack
|
8
|
+
|
9
|
+
GEM
|
10
|
+
remote: https://rubygems.org/
|
11
|
+
specs:
|
12
|
+
addressable (2.4.0)
|
13
|
+
coderay (1.1.0)
|
14
|
+
crack (0.4.2)
|
15
|
+
safe_yaml (~> 1.0.0)
|
16
|
+
diff-lcs (1.2.5)
|
17
|
+
faraday (0.9.2)
|
18
|
+
multipart-post (>= 1.2, < 3)
|
19
|
+
faraday_middleware (0.10.0)
|
20
|
+
faraday (>= 0.7.4, < 0.10)
|
21
|
+
ffi (1.9.10)
|
22
|
+
formatador (0.2.5)
|
23
|
+
guard (2.13.0)
|
24
|
+
formatador (>= 0.2.4)
|
25
|
+
listen (>= 2.7, <= 4.0)
|
26
|
+
lumberjack (~> 1.0)
|
27
|
+
nenv (~> 0.1)
|
28
|
+
notiffany (~> 0.0)
|
29
|
+
pry (>= 0.9.12)
|
30
|
+
shellany (~> 0.0)
|
31
|
+
thor (>= 0.18.1)
|
32
|
+
guard-compat (1.2.1)
|
33
|
+
guard-rspec (4.6.4)
|
34
|
+
guard (~> 2.1)
|
35
|
+
guard-compat (~> 1.1)
|
36
|
+
rspec (>= 2.99.0, < 4.0)
|
37
|
+
hashdiff (0.2.3)
|
38
|
+
listen (3.0.5)
|
39
|
+
rb-fsevent (>= 0.9.3)
|
40
|
+
rb-inotify (>= 0.9)
|
41
|
+
lumberjack (1.0.10)
|
42
|
+
method_source (0.8.2)
|
43
|
+
multipart-post (2.0.0)
|
44
|
+
nenv (0.2.0)
|
45
|
+
notiffany (0.0.8)
|
46
|
+
nenv (~> 0.1)
|
47
|
+
shellany (~> 0.0)
|
48
|
+
pry (0.10.3)
|
49
|
+
coderay (~> 1.1.0)
|
50
|
+
method_source (~> 0.8.1)
|
51
|
+
slop (~> 3.4)
|
52
|
+
rack (1.6.4)
|
53
|
+
rake (10.5.0)
|
54
|
+
rb-fsevent (0.9.7)
|
55
|
+
rb-inotify (0.9.5)
|
56
|
+
ffi (>= 0.5.0)
|
57
|
+
rspec (3.4.0)
|
58
|
+
rspec-core (~> 3.4.0)
|
59
|
+
rspec-expectations (~> 3.4.0)
|
60
|
+
rspec-mocks (~> 3.4.0)
|
61
|
+
rspec-core (3.4.1)
|
62
|
+
rspec-support (~> 3.4.0)
|
63
|
+
rspec-expectations (3.4.0)
|
64
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
65
|
+
rspec-support (~> 3.4.0)
|
66
|
+
rspec-mocks (3.4.1)
|
67
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
68
|
+
rspec-support (~> 3.4.0)
|
69
|
+
rspec-support (3.4.1)
|
70
|
+
safe_yaml (1.0.4)
|
71
|
+
shellany (0.0.1)
|
72
|
+
slop (3.6.0)
|
73
|
+
thor (0.19.1)
|
74
|
+
timecop (0.8.0)
|
75
|
+
webmock (1.22.3)
|
76
|
+
addressable (>= 2.3.6)
|
77
|
+
crack (>= 0.3.2)
|
78
|
+
hashdiff
|
79
|
+
|
80
|
+
PLATFORMS
|
81
|
+
ruby
|
82
|
+
|
83
|
+
DEPENDENCIES
|
84
|
+
botvac!
|
85
|
+
bundler (>= 1.6.2)
|
86
|
+
guard-rspec
|
87
|
+
pry
|
88
|
+
rake (~> 10.0)
|
89
|
+
rspec
|
90
|
+
timecop
|
91
|
+
webmock
|
92
|
+
|
93
|
+
BUNDLED WITH
|
94
|
+
1.11.2
|
data/Guardfile
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
# A sample Guardfile
|
2
|
+
# More info at https://github.com/guard/guard#readme
|
3
|
+
|
4
|
+
## Uncomment and set this to only include directories you want to watch
|
5
|
+
# directories %w(app lib config test spec features) \
|
6
|
+
# .select{|d| Dir.exists?(d) ? d : UI.warning("Directory #{d} does not exist")}
|
7
|
+
|
8
|
+
## Note: if you are using the `directories` clause above and you are not
|
9
|
+
## watching the project directory ('.'), then you will want to move
|
10
|
+
## the Guardfile to a watched dir and symlink it back, e.g.
|
11
|
+
#
|
12
|
+
# $ mkdir config
|
13
|
+
# $ mv Guardfile config/
|
14
|
+
# $ ln -s config/Guardfile .
|
15
|
+
#
|
16
|
+
# and, you'll have to watch "config/Guardfile" instead of "Guardfile"
|
17
|
+
|
18
|
+
guard :rspec, cmd: 'rspec' do
|
19
|
+
watch(%r{^spec/.+_spec\.rb$})
|
20
|
+
watch(%r{^lib/(.+)\.rb$}) { |m| "spec/lib/#{m[1]}_spec.rb" }
|
21
|
+
watch('spec/spec_helper.rb') { "spec" }
|
22
|
+
end
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
Copyright (c) 2016 Lars Brillert
|
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
|
data/README.md
ADDED
@@ -0,0 +1,90 @@
|
|
1
|
+
# Botvac
|
2
|
+
|
3
|
+
This is an unofficial API client which can help you
|
4
|
+
to interact with the Neato cloudservices which are
|
5
|
+
used to control you Neato Connected vaccum robot.
|
6
|
+
|
7
|
+
## Disclaimer
|
8
|
+
|
9
|
+
As this is an official client to the API which required
|
10
|
+
to reverse the HTTP Authentication mechanism used by
|
11
|
+
Neato things are topic to be unstable and maybe unreliable.
|
12
|
+
|
13
|
+
Please don't blame me :) Just drink a beer and relax, things
|
14
|
+
will maybe work out in the future ... and maybe not.
|
15
|
+
|
16
|
+
## Usage
|
17
|
+
|
18
|
+
Add the gem into your project using `bundler`
|
19
|
+
|
20
|
+
|
21
|
+
gem 'botvac'
|
22
|
+
|
23
|
+
### Creating a new robot
|
24
|
+
|
25
|
+
In order to crate a new robot, you'll need to figure out
|
26
|
+
the correct serial (easy) and the associated secret (less easy).
|
27
|
+
|
28
|
+
The serial number of your robot is likly to be shown somewhere
|
29
|
+
in your Android/iOS application.
|
30
|
+
|
31
|
+
The current process to figure out the robots secret is more
|
32
|
+
cumbersome, as it requires you to sniff the traffic between
|
33
|
+
you smartphone and the neato servers. Looks out for a
|
34
|
+
request to
|
35
|
+
|
36
|
+
https://beehive.neatocloud.com/dashboard
|
37
|
+
|
38
|
+
which will hold all necessary data :)
|
39
|
+
|
40
|
+
I'll add a more convinent way to obtain this in
|
41
|
+
future versions.
|
42
|
+
|
43
|
+
Once you have the serial and secret create a new robot object
|
44
|
+
|
45
|
+
mrrobot = Botvac::Robot.new(<serial>, <secret>)
|
46
|
+
|
47
|
+
Congratulatins, you can now inteact with your robot!!!
|
48
|
+
|
49
|
+
mrrobot.get_robot_state
|
50
|
+
|
51
|
+
Will give you some basic information about the status of
|
52
|
+
your connected robot.
|
53
|
+
|
54
|
+
There's more to discover! Currently there's all this avalivale:
|
55
|
+
|
56
|
+
* start_cleaning
|
57
|
+
* pause_cleaning
|
58
|
+
* stop_cleaning
|
59
|
+
* send_to_base
|
60
|
+
* get_robot_state
|
61
|
+
* disable_schedule
|
62
|
+
* enable_schedule
|
63
|
+
* get_schedule
|
64
|
+
|
65
|
+
The method names should give you an idea what the specific action will
|
66
|
+
cause. Still this is not all, but that's what there for the moment.
|
67
|
+
|
68
|
+
## Web Server
|
69
|
+
|
70
|
+
Botvac ships with a ultra basic web server which can be used to let
|
71
|
+
the robot easily interact with differnt systems just with calling
|
72
|
+
a HTTP endpoint.
|
73
|
+
|
74
|
+
rackup -r 'botvac/web' -b "run Botvac::Web.new"
|
75
|
+
|
76
|
+
The environemnt variables `SERIAL` and `SECRET` are
|
77
|
+
mandatory to set!
|
78
|
+
|
79
|
+
curl http://localhost:9292/get_robot_state
|
80
|
+
|
81
|
+
Will make the action start. To trigger other activities, just
|
82
|
+
change the requested URL to one of the above method names.
|
83
|
+
|
84
|
+
## Contributing
|
85
|
+
|
86
|
+
1. Fork it ( http://github.com/<my-github-username>/botvac/fork )
|
87
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
88
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
89
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
90
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
data/botvac.gemspec
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'botvac/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "botvac"
|
8
|
+
spec.version = Botvac::VERSION
|
9
|
+
spec.authors = ["Lars Brillert"]
|
10
|
+
spec.email = ["lars@railslove.com"]
|
11
|
+
spec.summary = %q{talk to your botvac connected robot}
|
12
|
+
spec.description = <<-description
|
13
|
+
Botvac allows you to interact with the Neato cloud service
|
14
|
+
which controls your robot without making use of the Android/iOS
|
15
|
+
application.
|
16
|
+
description
|
17
|
+
|
18
|
+
spec.homepage = "https://github.com/kangguru/botvac"
|
19
|
+
spec.license = "MIT"
|
20
|
+
|
21
|
+
spec.files = `git ls-files -z`.split("\x0")
|
22
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
23
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
24
|
+
spec.require_paths = ["lib"]
|
25
|
+
|
26
|
+
spec.add_dependency "faraday"
|
27
|
+
spec.add_dependency "faraday_middleware"
|
28
|
+
spec.add_dependency "rack"
|
29
|
+
|
30
|
+
spec.add_development_dependency "bundler", ">= 1.6.2"
|
31
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
32
|
+
spec.add_development_dependency "rspec"
|
33
|
+
spec.add_development_dependency "pry"
|
34
|
+
spec.add_development_dependency "webmock"
|
35
|
+
spec.add_development_dependency "guard-rspec"
|
36
|
+
spec.add_development_dependency "timecop"
|
37
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
-----BEGIN CERTIFICATE-----
|
2
|
+
MIIE3DCCA8SgAwIBAgIJALHphD11lrmHMA0GCSqGSIb3DQEBBQUAMIGkMQswCQYD
|
3
|
+
VQQGEwJVUzELMAkGA1UECBMCQ0ExDzANBgNVBAcTBk5ld2FyazEbMBkGA1UEChMS
|
4
|
+
TmVhdG8gUm9ib3RpY3MgSW5jMRcwFQYDVQQLEw5DbG91ZCBTZXJ2aWNlczEZMBcG
|
5
|
+
A1UEAxQQKi5uZWF0b2Nsb3VkLmNvbTEmMCQGCSqGSIb3DQEJARYXY2xvdWRAbmVh
|
6
|
+
dG9yb2JvdGljcy5jb20wHhcNMTUwNDIxMTA1OTA4WhcNNDUwNDEzMTA1OTA4WjCB
|
7
|
+
pDELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAkNBMQ8wDQYDVQQHEwZOZXdhcmsxGzAZ
|
8
|
+
BgNVBAoTEk5lYXRvIFJvYm90aWNzIEluYzEXMBUGA1UECxMOQ2xvdWQgU2Vydmlj
|
9
|
+
ZXMxGTAXBgNVBAMUECoubmVhdG9jbG91ZC5jb20xJjAkBgkqhkiG9w0BCQEWF2Ns
|
10
|
+
b3VkQG5lYXRvcm9ib3RpY3MuY29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB
|
11
|
+
CgKCAQEAur0WFcJ2YvnL3dtXJFv3lfCQtELLHVcux88tH7HN/FTeUvCqdleDNv4S
|
12
|
+
mXWgxVOdUUuhV885wppYyXNzDDrwCyjPmYj0m1EZ4FqTCcjFmk+xdEJsPsKPgRt5
|
13
|
+
QqaO0CA/T7dcIhT/PtQnJtcjn6E6vt2JLhsLz9OazadwjvdkejmfrOL643FGxsIP
|
14
|
+
8hu3+JINcfxnmff85zshe0yQH5yIYkmQGUPQz061T6mMzFrED/hx9zDpiB1mfkUm
|
15
|
+
uG3rBVcZWtrdyMvqB9LB1vqKgcCRANVg5S0GKpySudFlHOZjekXwBsZ+E6tW53qx
|
16
|
+
hvlgmlxX80aybYC5hQaNSQBaV9N4lwIDAQABo4IBDTCCAQkwHQYDVR0OBBYEFM3g
|
17
|
+
l7v7HP6zQgF90eHIl9coH6jhMIHZBgNVHSMEgdEwgc6AFM3gl7v7HP6zQgF90eHI
|
18
|
+
l9coH6jhoYGqpIGnMIGkMQswCQYDVQQGEwJVUzELMAkGA1UECBMCQ0ExDzANBgNV
|
19
|
+
BAcTBk5ld2FyazEbMBkGA1UEChMSTmVhdG8gUm9ib3RpY3MgSW5jMRcwFQYDVQQL
|
20
|
+
Ew5DbG91ZCBTZXJ2aWNlczEZMBcGA1UEAxQQKi5uZWF0b2Nsb3VkLmNvbTEmMCQG
|
21
|
+
CSqGSIb3DQEJARYXY2xvdWRAbmVhdG9yb2JvdGljcy5jb22CCQCx6YQ9dZa5hzAM
|
22
|
+
BgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBBQUAA4IBAQB93p+MUmKH+MQI3pEVvPUW
|
23
|
+
y+VDB5qt1spE5J0awVwUzhQ7QXkEqgFfOk0kzufvxdha9wz+05E1glQ8l5CzlATu
|
24
|
+
kA7V5OsygYB+TgqjvhfFHkSI6TJ8OlKcAJuZ2yQE8s2+LVo92NLwpooZLA6BCahn
|
25
|
+
fX+rzmo6b4ylhyX98Tm3upINNH3whV355PJFgk74fw9N7U6cFlBrqXXssKOse2D2
|
26
|
+
xY65IK7OQxSq5K5OPFLwN3h/eURo5kwl7jhpJhJbFL4I46OkpgqWHxQEqSxQnS0d
|
27
|
+
AC62ApwWkm42i0/DGODms2tnGL/DaCiTkgEE+8EEF9kfvQDtMoUDNvIkl7Vvm914
|
28
|
+
-----END CERTIFICATE-----
|
data/lib/botvac/robot.rb
ADDED
@@ -0,0 +1,87 @@
|
|
1
|
+
require "openssl"
|
2
|
+
require "json"
|
3
|
+
require "faraday"
|
4
|
+
require 'faraday_middleware'
|
5
|
+
|
6
|
+
module Botvac
|
7
|
+
class Robot
|
8
|
+
attr_accessor :serial, :secret
|
9
|
+
|
10
|
+
def initialize(serial, secret)
|
11
|
+
self.serial = serial
|
12
|
+
self.secret = secret
|
13
|
+
end
|
14
|
+
|
15
|
+
def start_cleaning
|
16
|
+
connection.post("messages", JSON.dump(
|
17
|
+
{
|
18
|
+
reqId: "1",
|
19
|
+
cmd: "startCleaning",
|
20
|
+
params: {
|
21
|
+
category: 2,
|
22
|
+
mode: 2,
|
23
|
+
modifier: 2
|
24
|
+
}
|
25
|
+
}
|
26
|
+
)).body
|
27
|
+
end
|
28
|
+
|
29
|
+
def pause_cleaning
|
30
|
+
connection.post("messages", JSON.dump({ reqId: "1",cmd: "pauseCleaning" })).body
|
31
|
+
end
|
32
|
+
|
33
|
+
def stop_cleaning
|
34
|
+
connection.post("messages", JSON.dump({ reqId: "1",cmd: "stopCleaning" })).body
|
35
|
+
end
|
36
|
+
|
37
|
+
def send_to_base
|
38
|
+
connection.post("messages", JSON.dump({ reqId: "1",cmd: "sendToBase" })).body
|
39
|
+
end
|
40
|
+
|
41
|
+
def get_robot_state
|
42
|
+
connection.post("messages", JSON.dump({ reqId: "1", cmd: "getRobotState" })).body
|
43
|
+
end
|
44
|
+
|
45
|
+
def enable_schedule
|
46
|
+
connection.post("messages", JSON.dump({ reqId: "1", cmd: "enableSchedule" })).body
|
47
|
+
end
|
48
|
+
|
49
|
+
def disable_schedule
|
50
|
+
connection.post("messages", JSON.dump({ reqId: "1", cmd: "disableSchedule"})).body
|
51
|
+
end
|
52
|
+
|
53
|
+
def get_schedule
|
54
|
+
connection.post("messages", JSON.dump({ reqId: "1", cmd: "getSchedule" })).body
|
55
|
+
end
|
56
|
+
|
57
|
+
protected
|
58
|
+
|
59
|
+
class Hmac < Faraday::Middleware
|
60
|
+
def initialize(app, options = {})
|
61
|
+
super(app)
|
62
|
+
@digest = OpenSSL::Digest.new('sha256')
|
63
|
+
@serial = options[:serial].downcase
|
64
|
+
@secret = options[:secret]
|
65
|
+
end
|
66
|
+
|
67
|
+
def call(env)
|
68
|
+
"#{Time.now.gmtime.strftime("%a, %d %h %Y %H:%M:%S")} GMT".tap do |date|
|
69
|
+
hmac = OpenSSL::HMAC.hexdigest(@digest, @secret, [@serial, date, env['body']].join("\n"))
|
70
|
+
env[:request_headers]['Date']= date
|
71
|
+
env[:request_headers]['Authorization']= "NEATOAPP #{hmac}"
|
72
|
+
end
|
73
|
+
|
74
|
+
@app.call(env)
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
def connection
|
79
|
+
@connection ||= Faraday.new(ssl: { ca_file: File.join(File.dirname(__FILE__), '..', '..', 'cert', 'neatocloud.com.crt') }) do |faraday|
|
80
|
+
faraday.url_prefix= "#{Botvac::ENDPOINT}/vendors/neato/robots/#{self.serial}/"
|
81
|
+
faraday.use Hmac, { serial: self.serial, secret: self.secret }
|
82
|
+
faraday.headers["Accept"]= 'application/vnd.neato.nucleo.v1'
|
83
|
+
faraday.adapter Faraday.default_adapter
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
data/lib/botvac/web.rb
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'botvac'
|
2
|
+
|
3
|
+
module Botvac
|
4
|
+
class Web
|
5
|
+
def call(env)
|
6
|
+
if supported_actions.include?(env["REQUEST_PATH"][1..-1])
|
7
|
+
[200, {'Content-Type' => 'application/json'}, [robot.send(env["REQUEST_PATH"][1..-1])]]
|
8
|
+
else
|
9
|
+
[404, {'Content-Type' => 'application/json'}, ['{}']]
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
def supported_actions
|
14
|
+
%w(start_cleaning pause_cleaning stop_cleaning send_to_base get_robot_state disable_schedule enable_schedule get_schedule)
|
15
|
+
end
|
16
|
+
|
17
|
+
def robot
|
18
|
+
@robot ||= Botvac::Robot.new(ENV['SERIAL'], ENV['SECRET'])
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
data/lib/botvac.rb
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe Botvac::Robot::Hmac do
|
4
|
+
subject do
|
5
|
+
described_class.new(
|
6
|
+
double('app', call: {}),
|
7
|
+
serial: "OPS39348-C0BCB237AC0F",
|
8
|
+
secret: "702FA35268D2792B2AAF9510B47756D8",
|
9
|
+
)
|
10
|
+
end
|
11
|
+
|
12
|
+
let(:env) { { request_headers: {}, 'body' => '{"reqId":"1","cmd":"getRobotState"}' } }
|
13
|
+
|
14
|
+
it 'adds the HMAC signature to the authorization header' do
|
15
|
+
Timecop.freeze(Time.parse("Tue, 09 Feb 2016 18:06:56 GMT")) do
|
16
|
+
expect{ subject.call(env) }.to change{ env[:request_headers]['Authorization'] }.to include("f93e6a50c3134706ae875adfabc9a14f0cc61e1b11b111499b4e6eeb1f69da70")
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
it 'sets the date header' do
|
21
|
+
Timecop.freeze(Time.parse("Tue, 09 Feb 2016 18:06:56 GMT")) do
|
22
|
+
expect{ subject.call(env) }.to change{ env[:request_headers]['Date'] }.to("Tue, 09 Feb 2016 18:06:56 GMT")
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,43 @@
|
|
1
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
2
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
3
|
+
|
4
|
+
require 'rspec'
|
5
|
+
require 'botvac'
|
6
|
+
require 'webmock/rspec'
|
7
|
+
require 'timecop'
|
8
|
+
require 'rspec/matchers' # req by equivalent-xml custom matcher `be_equivalent_to`
|
9
|
+
|
10
|
+
Dir[File.dirname(__FILE__) + "/support/**/*.rb"].each {|f| require f}
|
11
|
+
|
12
|
+
RSpec.configure do |config|
|
13
|
+
# rspec-expectations config goes here. You can use an alternate
|
14
|
+
# assertion/expectation library such as wrong or the stdlib/minitest
|
15
|
+
# assertions if you prefer.
|
16
|
+
config.expect_with :rspec do |expectations|
|
17
|
+
# This option will default to `true` in RSpec 4. It makes the `description`
|
18
|
+
# and `failure_message` of custom matchers include text for helper methods
|
19
|
+
# defined using `chain`, e.g.:
|
20
|
+
# be_bigger_than(2).and_smaller_than(4).description
|
21
|
+
# # => "be bigger than 2 and smaller than 4"
|
22
|
+
# ...rather than:
|
23
|
+
# # => "be bigger than 2"
|
24
|
+
expectations.include_chain_clauses_in_custom_matcher_descriptions = true
|
25
|
+
end
|
26
|
+
|
27
|
+
# rspec-mocks config goes here. You can use an alternate test double
|
28
|
+
# library (such as bogus or mocha) by changing the `mock_with` option here.
|
29
|
+
config.mock_with :rspec do |mocks|
|
30
|
+
# Prevents you from mocking or stubbing a method that does not exist on
|
31
|
+
# a real object. This is generally recommended, and will default to
|
32
|
+
# `true` in RSpec 4.
|
33
|
+
mocks.verify_partial_doubles = true
|
34
|
+
end
|
35
|
+
|
36
|
+
config.order = :random
|
37
|
+
|
38
|
+
Kernel.srand config.seed
|
39
|
+
|
40
|
+
if config.files_to_run.one?
|
41
|
+
config.default_formatter = 'doc'
|
42
|
+
end
|
43
|
+
end
|
metadata
ADDED
@@ -0,0 +1,204 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: botvac
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Lars Brillert
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2016-02-12 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: faraday
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: faraday_middleware
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rack
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: bundler
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: 1.6.2
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: 1.6.2
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: rake
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - "~>"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '10.0'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - "~>"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '10.0'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: rspec
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ">="
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - ">="
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: pry
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - ">="
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '0'
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - ">="
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '0'
|
111
|
+
- !ruby/object:Gem::Dependency
|
112
|
+
name: webmock
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
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
|
+
requirements:
|
122
|
+
- - ">="
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: '0'
|
125
|
+
- !ruby/object:Gem::Dependency
|
126
|
+
name: guard-rspec
|
127
|
+
requirement: !ruby/object:Gem::Requirement
|
128
|
+
requirements:
|
129
|
+
- - ">="
|
130
|
+
- !ruby/object:Gem::Version
|
131
|
+
version: '0'
|
132
|
+
type: :development
|
133
|
+
prerelease: false
|
134
|
+
version_requirements: !ruby/object:Gem::Requirement
|
135
|
+
requirements:
|
136
|
+
- - ">="
|
137
|
+
- !ruby/object:Gem::Version
|
138
|
+
version: '0'
|
139
|
+
- !ruby/object:Gem::Dependency
|
140
|
+
name: timecop
|
141
|
+
requirement: !ruby/object:Gem::Requirement
|
142
|
+
requirements:
|
143
|
+
- - ">="
|
144
|
+
- !ruby/object:Gem::Version
|
145
|
+
version: '0'
|
146
|
+
type: :development
|
147
|
+
prerelease: false
|
148
|
+
version_requirements: !ruby/object:Gem::Requirement
|
149
|
+
requirements:
|
150
|
+
- - ">="
|
151
|
+
- !ruby/object:Gem::Version
|
152
|
+
version: '0'
|
153
|
+
description: |
|
154
|
+
Botvac allows you to interact with the Neato cloud service
|
155
|
+
which controls your robot without making use of the Android/iOS
|
156
|
+
application.
|
157
|
+
email:
|
158
|
+
- lars@railslove.com
|
159
|
+
executables: []
|
160
|
+
extensions: []
|
161
|
+
extra_rdoc_files: []
|
162
|
+
files:
|
163
|
+
- ".gitignore"
|
164
|
+
- Gemfile
|
165
|
+
- Gemfile.lock
|
166
|
+
- Guardfile
|
167
|
+
- LICENSE.txt
|
168
|
+
- README.md
|
169
|
+
- Rakefile
|
170
|
+
- botvac.gemspec
|
171
|
+
- cert/neatocloud.com.crt
|
172
|
+
- lib/botvac.rb
|
173
|
+
- lib/botvac/robot.rb
|
174
|
+
- lib/botvac/version.rb
|
175
|
+
- lib/botvac/web.rb
|
176
|
+
- spec/botvac/robot_spec.rb
|
177
|
+
- spec/spec_helper.rb
|
178
|
+
homepage: https://github.com/kangguru/botvac
|
179
|
+
licenses:
|
180
|
+
- MIT
|
181
|
+
metadata: {}
|
182
|
+
post_install_message:
|
183
|
+
rdoc_options: []
|
184
|
+
require_paths:
|
185
|
+
- lib
|
186
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
187
|
+
requirements:
|
188
|
+
- - ">="
|
189
|
+
- !ruby/object:Gem::Version
|
190
|
+
version: '0'
|
191
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
192
|
+
requirements:
|
193
|
+
- - ">="
|
194
|
+
- !ruby/object:Gem::Version
|
195
|
+
version: '0'
|
196
|
+
requirements: []
|
197
|
+
rubyforge_project:
|
198
|
+
rubygems_version: 2.5.1
|
199
|
+
signing_key:
|
200
|
+
specification_version: 4
|
201
|
+
summary: talk to your botvac connected robot
|
202
|
+
test_files:
|
203
|
+
- spec/botvac/robot_spec.rb
|
204
|
+
- spec/spec_helper.rb
|