geppeto 0.1.0 → 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: b855dd87a61a67b608fe4a597415da91b7d195c5
4
- data.tar.gz: 45f40b4054fd0944be4b1f46910a724379f956a8
3
+ metadata.gz: f338448313065fa0b431a489d521f284d2a0cc34
4
+ data.tar.gz: eb1459535f6430a18769d5db66f08c2f01f0e284
5
5
  SHA512:
6
- metadata.gz: e5128825cc150972dd20da7b10cec5600acc1759aad837c11a8ec40793b89f304d0482f84f01d1f2c43624e42f9c79196cd0a63d6f33bf6dbfb149cf979140cc
7
- data.tar.gz: c6e0de6d7c18b1080d155b49476db67ec5005655ec00076c8a6275e59c155fa2d57da3311966b3ee1028f83b2c34134864b57b1d4ba5d205b938860e958c8381
6
+ metadata.gz: c36ea309872190f52aac21dd622bcdc83175301b397f31dc05e9d8f52e7d3d964506092e7ce5439c544f8c9a3c38f81330e2a82c70b02f97ea4269b5b0976f90
7
+ data.tar.gz: 950a9f443a72c1b1118e02a30d208688be90963be74a0d5b5c630ad8b95d9f53de9492a551a6020ca1338b446fd3ae5bf43f6b0b429cb50f02646fd3cf4b611b
@@ -0,0 +1,37 @@
1
+ *.gem
2
+ *.rbc
3
+ /.config
4
+ /coverage/
5
+ /InstalledFiles
6
+ /pkg/
7
+ /spec/reports/
8
+ /test/tmp/
9
+ /test/version_tmp/
10
+ /tmp/
11
+
12
+ ## Specific to RubyMotion:
13
+ .dat*
14
+ .repl_history
15
+ build/
16
+
17
+ ## Documentation cache and generated files:
18
+ /.yardoc/
19
+ /_yardoc/
20
+ /doc/
21
+ /rdoc/
22
+
23
+ ## Environment normalisation:
24
+ /.bundle/
25
+ /lib/bundler/man/
26
+
27
+ # for a library or gem, you might want to ignore these files since the code is
28
+ # intended to run in multiple environments; otherwise, check them in:
29
+ # Gemfile.lock
30
+ .ruby-version
31
+ .ruby-gemset
32
+
33
+ # rbenv
34
+ .rbenv-gemsets
35
+
36
+ # unless supporting rvm < 1.11.0 or doing something fancy, ignore this:
37
+ .rvmrc
data/Gemfile ADDED
@@ -0,0 +1,9 @@
1
+ source "https://rubygems.org"
2
+
3
+ gem 'celluloid-io', '~>0.16'
4
+ gem 'serialport', '~>1.2.3'
5
+ gem 'revolver', '~> 1.1.1'
6
+
7
+ group :test do
8
+ gem 'rspec'
9
+ end
@@ -0,0 +1,36 @@
1
+ GEM
2
+ remote: https://rubygems.org/
3
+ specs:
4
+ celluloid (0.16.0)
5
+ timers (~> 4.0.0)
6
+ celluloid-io (0.16.1)
7
+ celluloid (>= 0.16.0)
8
+ nio4r (>= 1.0.0)
9
+ diff-lcs (1.2.5)
10
+ hitimes (1.2.2)
11
+ nio4r (1.0.1)
12
+ revolver (1.1.1)
13
+ rspec (3.1.0)
14
+ rspec-core (~> 3.1.0)
15
+ rspec-expectations (~> 3.1.0)
16
+ rspec-mocks (~> 3.1.0)
17
+ rspec-core (3.1.7)
18
+ rspec-support (~> 3.1.0)
19
+ rspec-expectations (3.1.2)
20
+ diff-lcs (>= 1.2.0, < 2.0)
21
+ rspec-support (~> 3.1.0)
22
+ rspec-mocks (3.1.3)
23
+ rspec-support (~> 3.1.0)
24
+ rspec-support (3.1.2)
25
+ serialport (1.2.3)
26
+ timers (4.0.1)
27
+ hitimes
28
+
29
+ PLATFORMS
30
+ ruby
31
+
32
+ DEPENDENCIES
33
+ celluloid-io (~> 0.16)
34
+ revolver (~> 1.1.1)
35
+ rspec
36
+ serialport (~> 1.2.3)
data/LICENSE ADDED
@@ -0,0 +1,13 @@
1
+ Copyright 2014 Matt Heitzenroder
2
+
3
+ Licensed under the Apache License, Version 2.0 (the "License");
4
+ you may not use this file except in compliance with the License.
5
+ You may obtain a copy of the License at
6
+
7
+ http://www.apache.org/licenses/LICENSE-2.0
8
+
9
+ Unless required by applicable law or agreed to in writing, software
10
+ distributed under the License is distributed on an "AS IS" BASIS,
11
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ See the License for the specific language governing permissions and
13
+ limitations under the License.
@@ -0,0 +1,143 @@
1
+ # Geppeto
2
+ Geppeto provides a Ruby API over two different transports, either a TCPSocket
3
+ or a serial connection, to the [Pinoccio microcontroller](https://pinocc.io). It
4
+ was created for Pinoccio applications that do not use Pinoccio's `hq.pinocc.io`
5
+ or `api.pinocc.io`. In my case, the Pinoccio microcontrollers existed on a LAN
6
+ that did not have an uplink or gateway to the internet.
7
+
8
+ ## Install
9
+
10
+ `gem install geppeto`
11
+
12
+ or add it to your bundler file
13
+
14
+ `gem "geppeto", `~>0.1.0`
15
+
16
+ ## Usage
17
+
18
+ The goal with Geppeto is to provide an API that feels like Ruby. Here's an
19
+ example:
20
+
21
+ ```ruby
22
+ require 'geppeto'
23
+
24
+ serial = Geppeto::Connection::Serial.new
25
+ lead = Geppeto::Scout.new serial
26
+ lead.report
27
+ => {"type"=>"scout", "lead"=>true, "version"=>1, "hardware"=>1, "family"=>1000, "serial"=>3001731, "sketch"=>"Custom", "build"=>-1, "revision"=>"unknown", "at"=>12642}
28
+ ```
29
+
30
+ Here's an even better example:
31
+
32
+ ```ruby
33
+ lead.pin.digital
34
+ => {"type"=>"digital", "mode"=>[1, -3, -3, -3, -3, -3, -3], "state"=>[1, -1, -1, -1, -1, -1, -1], "at"=>9303}
35
+ ```
36
+
37
+ You can even issue commands to all or others
38
+ ```ruby
39
+ lead.all.led.on
40
+ => nil
41
+ lead.others.led.off
42
+ => nil
43
+ lead.led.report
44
+ => {"type"=>"led", "led"=>[0, 255, 0], "torch"=>[0, 255, 0], "at"=>106676}
45
+ ```
46
+
47
+ If that wasn't exciting enough, you can also delay commands
48
+ ```ruby
49
+ lead.delay(5000).led.on
50
+ => nil
51
+ # 5 seconds later... the led turned on.
52
+ ```
53
+
54
+ ### Creating a Scout
55
+
56
+ ```ruby
57
+ id = 2
58
+ serial = Geppeto::Connection::Serial.new
59
+ scout = Geppeto::Scout.new serial, id
60
+ scout.report
61
+ => nil
62
+ ```
63
+
64
+ That's all well and good, but scout's that are accessible over a `Serial`
65
+ connection return `nil` all requests by result. One way to obtain data is to
66
+ use a socket server.
67
+
68
+ ### Socket Server
69
+
70
+ ```ruby
71
+ socket = Geppeto::Connection::Server.new
72
+ => #<Celluloid::CellProxy(Geppeto::Connection::Server:0x3fd00c8cb18c) @history=#<Revolver:0x007fa01918fe00 @array=[], @size=10, @unique=false> @logger=#<Logger:0x007fa01918fd88 @progname=nil, @level=0, @default_formatter=#<Logger::Formatter:0x007fa01918fd60 @datetime_format=nil>, @formatter=nil, @logdev=#<Logger::LogDevice:0x007fa01918fd10 @shift_size=nil, @shift_age=nil, @filename=nil, @dev=#<IO:<STDOUT>>, @mutex=#<Logger::LogDevice::LogDeviceMutex:0x007fa01918fce8 @mon_owner=nil, @mon_count=0, @mon_mutex=#<Mutex:0x007fa01918fc98>>>> @server=#<Celluloid::IO::TCPServer:0x007fa01918f950 @server=#<TCPServer:fd 11>>>
73
+ ```
74
+
75
+ Now we have a socket server that will capture and parse the JSON responses.
76
+
77
+ ```ruby
78
+ lead.hq.setaddress("10.0.1.22")
79
+ => nil
80
+ lead.wifi.reassociate
81
+ => nil
82
+ socket.history
83
+ => #<Revolver:0x007fee22ac9fa0
84
+ @array=[{"type"=>"report", "from"=>2, "report"=>{"type"=>"temp", "c"=>31, "f"=>88, "offset"=>0, "at"=>3540003}}],
85
+ @size=10,
86
+ @unique=false>
87
+
88
+ ```
89
+
90
+ Note that the socket server has a `history` attribute. Because of the limitations
91
+ with socket communication with pinoccio, we're capturing all of the responses in
92
+ a fixed sized LIFO array.
93
+
94
+ Nowe we can have our scout send us a report
95
+
96
+ ```ruby
97
+ scout.hq.print("Hello, world!")
98
+ socket.history
99
+ => #<Revolver:0x007fee22ac9fa0
100
+ @array=
101
+ [{"type"=>"report", "from"=>2, "report"=>{"type"=>"temp", "c"=>31, "f"=>88, "offset"=>0, "at"=>3540003}},
102
+ {"type"=>"report", "from"=>1, "report"=>{"type"=>"temp", "c"=>32, "f"=>90, "offset"=>0, "at"=>197667}},
103
+ {"type"=>"report", "from"=>2, "report"=>{"type"=>"temp", "c"=>30, "f"=>88, "offset"=>0, "at"=>3600003}},
104
+ {"type"=>"announce", "from"=>2, "announce"=>[0, "Hello, world!"]}],
105
+ @size=10,
106
+ @unique=false>
107
+ ```
108
+
109
+ ### Constants
110
+
111
+ Most of the time, arguments sent to the scouts are just strings and integers;
112
+ however occasionally we may need to send a constant. This can be achieved by
113
+ sending a ruby `Symbol` instead. Such is the case with pins, as seen here:
114
+
115
+ ```ruby
116
+ scout.pin.save("d2", :OUTPUT, :HIGH)
117
+ socket.history.pop
118
+ => {"type"=>"report",
119
+ "from"=>2,
120
+ "report"=>{"type"=>"digital", "mode"=>[1, -3, -3, -3, -3, -3, -3], "state"=>[1, -1, -1, -1, -1, -1, -1], "at"=>3938541}}
121
+ ```
122
+
123
+ ## What about...
124
+
125
+ ### Tests
126
+ Well this is embarrassing... If I knew how to mock out a Pinoccio, Serial, or
127
+ Socket connection, I'd provide tests. If you're interested in contributing, and
128
+ you know how to test this library, by all means, please send a pull request.
129
+
130
+ ### Where did the "t" go?
131
+ Yes, Geppeto is purposely misspelled. If you're familiar with Pinoccio, you might
132
+ recognize that the "h" is missing. This is explained on [Pinoccio's website](https://pinocc.io/faq#faq-wheres-the-h).
133
+
134
+ ### What's missing?
135
+ Besides tests... a few things:
136
+
137
+ * [Events Callback Support](https://docs.pinocc.io/scoutcommands.html#event-callbacks)
138
+ * [Scout Acknowledgement Callback](https://docs.pinocc.io/scoutcommands.html#command-scout-ack)
139
+ * [Keys](https://docs.pinocc.io/scoutcommands.html#keys-deprecated)
140
+
141
+ ### What's next?
142
+ Should there be a `Thor` backed CLI executable to manage a standalone socket server?
143
+ Or maybe I'll figure out how to write tests.
@@ -0,0 +1,9 @@
1
+ begin
2
+ require 'rspec/core/rake_task'
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
7
+ rescue LoadError
8
+ # no rspec available
9
+ end
@@ -0,0 +1,23 @@
1
+ Gem::Specification.new do |s|
2
+ s.name = 'geppeto'
3
+ s.version = '0.1.1'
4
+ s.date = '2014-10-29'
5
+ s.summary = "Geppeto makes Pinoccio into Ruby."
6
+ s.description = "Geppeto provides a Ruby API to Pinoccio microcontrollers by means of a TCPsocket or a Serial connection, forgoing the need to use api.pinocc.io."
7
+ s.authors = ["Matt Heitzenroder"]
8
+ s.email = 'mheitzenroder@gmail.com'
9
+ s.homepage =
10
+ 'http://github.com/roder/geppeto'
11
+ s.license = 'Apache-2.0'
12
+
13
+ s.files = `git ls-files`.split($/)
14
+ s.executables = s.files.grep(%r{^bin/}) { |f| File.basename(f) }
15
+ s.test_files = s.files.grep(%r{^(test|spec|features)/})
16
+ s.require_paths = ['lib']
17
+
18
+ s.add_runtime_dependency 'serialport', '~> 1.2'
19
+ s.add_runtime_dependency 'celluloid-io', '~> 0.16'
20
+ s.add_runtime_dependency 'revolver', '~> 1.1'
21
+ s.add_development_dependency 'bundler', '~> 1.7'
22
+ s.add_development_dependency 'rake', '~> 10'
23
+ end
@@ -0,0 +1,16 @@
1
+ require 'commands/events'
2
+ require 'commands/hq'
3
+ require 'commands/led'
4
+ require 'commands/memory'
5
+ require 'commands/mesh'
6
+ require 'commands/pin'
7
+ require 'commands/power'
8
+ require 'commands/uptime'
9
+ require 'commands/wifi'
10
+
11
+ module Geppeto
12
+ module Commands
13
+ class NotLeadScout < Exception
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,21 @@
1
+ module Geppeto
2
+ module Commands
3
+ class Events
4
+ def initialize(scout)
5
+ @scout = scout
6
+ end
7
+
8
+ def start
9
+ @scout.request("events.start")
10
+ end
11
+
12
+ def stop
13
+ @scout.request("events.stop")
14
+ end
15
+
16
+ def setcycle(digitalMs, analogMs, peripheralMs)
17
+ @scout.request("events.setcycle", digitalMs, analogMs, peripheralMs )
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,29 @@
1
+ module Geppeto
2
+ module Commands
3
+ class Hq
4
+ def initialize(scout)
5
+ @scout = scout
6
+ end
7
+
8
+ def setaddress(host, port = nil)
9
+ @scout.request("hq.setaddress", host, port)
10
+ end
11
+
12
+ def print(str)
13
+ @scout.request("hq.print", str)
14
+ end
15
+
16
+ def settoken(token)
17
+ @scout.request("hq.settoken", token)
18
+ end
19
+
20
+ def gettoken
21
+ @scout.request("hq.gettoken")
22
+ end
23
+
24
+ def report(report_name, value)
25
+ @scout.request("hq.report", report_name, value)
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,89 @@
1
+ module Geppeto
2
+ module Commands
3
+ class Led
4
+ def initialize(scout)
5
+ @scout = scout
6
+ end
7
+
8
+ def on?
9
+ isoff().to_i != 1
10
+ end
11
+
12
+ def off?
13
+ isoff().to_i == 1
14
+ end
15
+
16
+ def on
17
+ @scout.request("led.on")
18
+ end
19
+
20
+ def off
21
+ @scout.request("led.off")
22
+ end
23
+
24
+ def red(ms = nil, continuous = nil)
25
+ @scout.request("led.red", ms, continuous)
26
+ end
27
+
28
+ def green(ms = nil, continuous = nil)
29
+ @scout.request("led.green", ms, continuous)
30
+ end
31
+
32
+ def blue(ms = nil, continuous = nil)
33
+ @scout.request("led.blue", ms, continuous)
34
+ end
35
+
36
+ def cyan(ms = nil, continuous = nil)
37
+ @scout.request("led.cyan", ms, continuous)
38
+ end
39
+
40
+ def purple(ms = nil, continuous = nil)
41
+ @scout.request("led.purple", ms, continuous)
42
+ end
43
+
44
+ def magenta(ms = nil, continuous = nil)
45
+ @scout.request("led.magenta", ms, continuous)
46
+ end
47
+
48
+ def yellow(ms = nil, continuous = nil)
49
+ @scout.request("led.yellow", ms, continuous)
50
+ end
51
+
52
+ def orange(ms = nil, continuous = nil)
53
+ @scout.request("led.orange", ms, continuous)
54
+ end
55
+
56
+ def white(ms = nil, continuous = nil)
57
+ @scout.request("led.white", ms, continuous)
58
+ end
59
+
60
+ def torch(ms = nil, continuous = nil)
61
+ @scout.request("led.torch", ms, continuous )
62
+ end
63
+
64
+ def blink(red, green, blue, millis=500, continuous = nil)
65
+ @scout.request("led.blink", red, green, blue, millis, continuous)
66
+ end
67
+
68
+ def sethex(hex_value)
69
+ @scout.request("led.sethex", hex_value)
70
+ end
71
+
72
+ def gethex
73
+ @scout.request("led.gethex")
74
+ end
75
+
76
+ def setrgb(red, green, blue)
77
+ @scout.request("led.setrgb", red, green, blue)
78
+ end
79
+
80
+ def isoff
81
+ @scout.request("led.isoff")
82
+ end
83
+
84
+ def report
85
+ @scout.request("led.report")
86
+ end
87
+ end
88
+ end
89
+ end
@@ -0,0 +1,13 @@
1
+ module Geppeto
2
+ module Commands
3
+ class Memory
4
+ def initialize(scout)
5
+ @scout = scout
6
+ end
7
+
8
+ def report
9
+ @scout.request("memory.report")
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,49 @@
1
+ module Geppeto
2
+ module Commands
3
+ class Mesh
4
+ def initialize(scout)
5
+ @scout = scout
6
+ end
7
+
8
+ def config(scout_id, troop_id, channel=20)
9
+ @scout.request("mesh.config", scout_id, troop_id, channel)
10
+ end
11
+
12
+ def setpowerlevel(level)
13
+ @scout.request("mesh.setpowerlevel", level.to_i)
14
+ end
15
+
16
+ def setdatarate(rate)
17
+ @scout.request("mesh.setdatarate", rate.to_i)
18
+ end
19
+
20
+ def setkey(key)
21
+ @scout.request("mesh.setkey", key)
22
+ end
23
+
24
+ def resetkey
25
+ @scout.request("mesh.resetkey")
26
+ end
27
+
28
+ def joingroup(group)
29
+ @scout.request("mesh.joingroup", group.to_i)
30
+ end
31
+
32
+ def leavegroup(group)
33
+ @scout.request("mesh.leavegroup", group.to_i)
34
+ end
35
+
36
+ def ingroup?(group)
37
+ @scout.request("mesh.ingroup").to_i == 1
38
+ end
39
+
40
+ def report
41
+ @scout.request("mesh.report")
42
+ end
43
+
44
+ def routing
45
+ @scout.request("mesh.routing")
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,57 @@
1
+ module Geppeto
2
+ module Commands
3
+ class Pin
4
+ def initialize(scout)
5
+ @scout = scout
6
+ end
7
+
8
+ def makeinput(pin_name, input_type=:INPUT_PULLUP)
9
+ @scout.request("pin.makeinput", pin_name, input_type)
10
+ end
11
+
12
+ def makeoutput(pin_name)
13
+ @scout.request("pin.makeoutput", pin_name)
14
+ end
15
+
16
+ def makepwm(pin_name)
17
+ @scout.request("pin.makepwm", pin_name)
18
+ end
19
+
20
+ def disable(pin_name)
21
+ @scout.request("pin.disable", pin_name)
22
+ end
23
+
24
+ def setmode(pin_name, input_type=:INPUT_PULLUP)
25
+ @scout.request("pin.setmode", pin_name, input_type)
26
+ end
27
+
28
+ def read(pin_name)
29
+ @scout.request("pin.read", pin_name)
30
+ end
31
+
32
+ def write(pin_name, pin_value)
33
+ @scout.request("pin.write", pin_name, pin_value)
34
+ end
35
+
36
+ def save(pin_name, pin_mode, pin_value = nil)
37
+ @scout.request("pin.save", pin_name, pin_mode, pin_value)
38
+ end
39
+
40
+ def status
41
+ @scout.request("pin.status")
42
+ end
43
+
44
+ def report
45
+ self
46
+ end
47
+
48
+ def analog
49
+ @scout.request("pin.report.analog")
50
+ end
51
+
52
+ def digital
53
+ @scout.request("pin.report.digital")
54
+ end
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,45 @@
1
+ module Geppeto
2
+ module Commands
3
+ class Power
4
+ def initialize(scout)
5
+ @scout = scout
6
+ end
7
+
8
+ def charging?
9
+ @scout.request("power.ischarging").to_i == 1
10
+ end
11
+
12
+ def battery?
13
+ @scout.request("power.hasbattery").to_i == 1
14
+ end
15
+
16
+ def percent
17
+ @scout.request("power.percent").to_i
18
+ end
19
+
20
+ def voltage
21
+ @scout.request("power.voltage").to_i
22
+ end
23
+
24
+ def enablevcc
25
+ @scout.request("power.enablevcc")
26
+ end
27
+
28
+ def disablevcc
29
+ @scout.request("power.disablevcc")
30
+ end
31
+
32
+ def vcc?
33
+ @scout.request("power.isvccenabled").to_i == 1
34
+ end
35
+
36
+ def sleep(ms, command = nil)
37
+ @scout.request("power.sleep", ms, command)
38
+ end
39
+
40
+ def report
41
+ @scout.request("power.report")
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,29 @@
1
+ module Geppeto
2
+ module Commands
3
+ class Temperature
4
+ def initialize(scout)
5
+ @scout = scout
6
+ end
7
+
8
+ def c
9
+ @scout.request("temperature.c")
10
+ end
11
+
12
+ def f
13
+ @scout.request("temperature.f")
14
+ end
15
+
16
+ def report
17
+ @scout.request("temperature.report")
18
+ end
19
+
20
+ def setoffset(offset)
21
+ @scout.request("temperature.offset", offset)
22
+ end
23
+
24
+ def calibrate(current_temp_c)
25
+ @scout.request("temperature.calibrate", current_temp_c)
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,47 @@
1
+ module Geppeto
2
+ module Commands
3
+ class Uptime
4
+
5
+ def initialize(scout)
6
+ @context = nil
7
+ @scout = scout
8
+ end
9
+
10
+ def micros
11
+ context ||= ".#{@context}" unless @context.nil?
12
+ command = "uptime#{context}.micros"
13
+ @scout.request(command)
14
+ @context = nil
15
+ end
16
+
17
+ def seconds
18
+ context ||= ".#{@context}" unless @context.nil?
19
+ command = "uptime#{context}.seconds"
20
+ @scout.request(command)
21
+ @context = nil
22
+ end
23
+
24
+ def report
25
+ @scout.request("uptime.report")
26
+ end
27
+
28
+ def getlastreset
29
+ @scout.request("uptime.getlastreset")
30
+ end
31
+
32
+ def status
33
+ @scout.request("uptime.status")
34
+ end
35
+
36
+ def awake
37
+ @context = :awake
38
+ self
39
+ end
40
+
41
+ def sleeping
42
+ @context = :sleeping
43
+ self
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,41 @@
1
+ module Geppeto
2
+ module Commands
3
+ class Wifi
4
+ def initialize(scout)
5
+ @scout = scout
6
+ end
7
+
8
+ def report
9
+ @scout.request("wifi.report")
10
+ end
11
+
12
+ def status
13
+ @scout.request("wifi.status")
14
+ end
15
+
16
+ def list
17
+ @scout.request("wifi.list", ms: 5)
18
+ end
19
+
20
+ def config(ssid, psk = nil)
21
+ @scout.request("wifi.config", ssid, psk)
22
+ end
23
+
24
+ def dhcp(hostname)
25
+ @scout.request("wifi.dhcp", hostname)
26
+ end
27
+
28
+ def static(ip, netmask, gateway, dns)
29
+ @scout.request("wifi.static", ip, netmask, gateway, dns)
30
+ end
31
+
32
+ def reassociate
33
+ @scout.request("wifi.reassociate")
34
+ end
35
+
36
+ def command(wifi_cmd)
37
+ @scout.request("wifi.command", wifi_cmd)
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,9 @@
1
+ require 'connections/serial'
2
+ require 'connections/server'
3
+
4
+ require 'json'
5
+
6
+ module Geppeto
7
+ module Connection
8
+ end
9
+ end
@@ -0,0 +1,60 @@
1
+ require 'serialport'
2
+
3
+ module Geppeto
4
+ module Connection
5
+ class Serial
6
+ attr_accessor :logger
7
+
8
+ def initialize(port = false, baud = 115200, logger = nil)
9
+ @logger = logger.nil? ? Logger.new(STDOUT) : logger
10
+ port ||= `ls /dev`.split("\n").grep(/tty.usb/i).map{|d| "/dev/#{d}"}.first #TODO: Check that this works on RPi/Begalbone OSs
11
+ @serial = SerialPort.new(port, baud)
12
+ @serial.read_timeout = 100
13
+ sleep 1
14
+ begin
15
+ @logger.debug(@serial.read_nonblock(4096))
16
+ rescue EOFError
17
+ # no data
18
+ end
19
+ @logger.info("Pinoccio Serial Connection started: #{port}")
20
+ end
21
+
22
+ def shutdown
23
+ @serial.close if @serial
24
+ rescue IOError
25
+ @logger.warn("Pinoccio Serial Connection closed.")
26
+ end
27
+
28
+ def write(command, ms = nil)
29
+ @serial.write command+"\n"
30
+ ms ||= 0.1
31
+ sleep ms
32
+ read command
33
+ end
34
+
35
+ def read(command = nil)
36
+ begin
37
+ raw_result = @serial.read_nonblock(4096)
38
+ if command.nil?
39
+ raw_result
40
+ else
41
+ cmd = Regexp.escape(command)
42
+ matches = /#{cmd}\r\n(?<response>.*)\r\n>\s.*/m.match(raw_result)
43
+ if matches[:response]
44
+ begin
45
+ JSON.parse(matches[:response])
46
+ rescue
47
+ matches[:response]
48
+ end
49
+ else
50
+ raw_result
51
+ end
52
+ end
53
+ rescue
54
+ nil
55
+ end
56
+ end
57
+
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,73 @@
1
+ require 'celluloid/io'
2
+ require 'revolver'
3
+
4
+ module Geppeto
5
+ module Connection
6
+ class Server
7
+ include Celluloid::IO
8
+ finalizer :shutdown
9
+
10
+ attr_accessor :logger
11
+ attr_accessor :history
12
+
13
+ def initialize(host="0.0.0.0", port = 22756, logger = nil)
14
+ @history = Revolver.new(10)
15
+ @logger = logger.nil? ? ::Logger.new(STDOUT) : logger
16
+ @logger.info("Starting Pinoccio Server on #{host}:#{port}")
17
+ @server = TCPServer.new(host, port)
18
+ async.run
19
+ end
20
+
21
+ def shutdown
22
+ @server.close if @server
23
+ rescue IOError
24
+ @logger.warn("Pinoccio Server shutting down")
25
+ end
26
+
27
+ def format_cmd(scout, command)
28
+ cmd = {
29
+ :to => scout.to_i,
30
+ :command => command.to_s,
31
+ :id => 1,
32
+ :timeout => 10000, # TODO: Make this configurable.
33
+ :type => "command",
34
+ }
35
+ cmd.to_json + "\n"
36
+ end
37
+
38
+ def send(scout, command)
39
+ @logger.debug("Scout #{scout}: #{command}")
40
+ if @socket.nil?
41
+ @logger.warn("No socket connected")
42
+ else
43
+ @socket.write format_cmd(scout, command)
44
+ end
45
+ end
46
+
47
+ def run
48
+ loop { async.handle_connection @server.accept }
49
+ end
50
+
51
+ def handle_connection(socket)
52
+ _, port, host = socket.peeraddr
53
+ @logger.info("Connected: #{host}:#{port}")
54
+ chunk = socket.gets
55
+ data = JSON.parse(chunk)
56
+ token = data["token"]
57
+ @socket = socket
58
+ @logger.debug("Connected token: #{token}")
59
+ loop {
60
+ chunk = @socket.gets.chomp!
61
+ data = JSON.parse(chunk)
62
+ @history << data
63
+ @logger.debug("Received: #{data}")
64
+ }
65
+ rescue
66
+ @logger.info("Disconnected: #{host}:#{port}")
67
+ @socket.close
68
+ @logger.debug("Removed token: #{token}")
69
+ @socket = nil
70
+ end
71
+ end
72
+ end
73
+ end
@@ -0,0 +1,9 @@
1
+ require 'commands/commands'
2
+ require 'connections/connections'
3
+ require 'scout'
4
+
5
+ require 'logger'
6
+
7
+
8
+ module Geppeto
9
+ end
@@ -0,0 +1,127 @@
1
+ module Geppeto
2
+ class Scout
3
+ attr_reader :id
4
+ attr_accessor :last_report
5
+ attr_accessor :logger
6
+
7
+ attr_reader :events
8
+ attr_reader :hq
9
+ attr_reader :led
10
+ attr_reader :memory
11
+ attr_reader :mesh
12
+ attr_reader :pin
13
+ attr_reader :power
14
+ attr_reader :temperature
15
+ attr_reader :uptime
16
+ attr_reader :wifi
17
+
18
+ def initialize(connection, id = nil)
19
+ @id = id
20
+ @conn = connection
21
+ @options = Array.new
22
+ @logger = @conn.logger ? @conn.logger : Logger.new(STDOUT)
23
+
24
+ @events = Geppeto::Commands::Events.new(self)
25
+ @hq = Geppeto::Commands::Hq.new(self)
26
+ @led = Geppeto::Commands::Led.new(self)
27
+ @memory = Geppeto::Commands::Memory.new(self)
28
+ @mesh = Geppeto::Commands::Mesh.new(self)
29
+ @pin = Geppeto::Commands::Pin.new(self)
30
+ @power = Geppeto::Commands::Power.new(self)
31
+ @uptime = Geppeto::Commands::Uptime.new(self)
32
+ @wifi = Geppeto::Commands::Wifi.new(self)
33
+ end
34
+
35
+ def all
36
+ @options << :all
37
+ self
38
+ end
39
+
40
+ def others
41
+ @options << :others
42
+ self
43
+ end
44
+
45
+ def report
46
+ request("scout.report")
47
+ end
48
+
49
+ def lead?
50
+ request("print scout.isleadscout").to_i == 1
51
+ end
52
+
53
+ def randomnumber
54
+ request("randomnumber")
55
+ end
56
+
57
+
58
+
59
+ def delay(ms = nil)
60
+ @options << :delay
61
+ @_ms = ms
62
+ self
63
+ end
64
+
65
+ def boot
66
+ request("scout.boot")
67
+ end
68
+
69
+ def request(command, *args, ms: nil)
70
+ args_string = join_args(args)
71
+ command, args_string = process_options(command, args_string)
72
+
73
+ if args_string.empty?
74
+ unless @id.nil?
75
+ command = "command.scout(#{@id}, \"#{command}\")"
76
+ else
77
+ command = "#{command}()"
78
+ end
79
+ else
80
+ unless @id.nil?
81
+ command = "command.scout(#{@id}, \"#{command}\", #{args_string})"
82
+ else
83
+ command = "#{command}(#{args_string})"
84
+ end
85
+ end
86
+
87
+ @logger.debug("Issuing: #{command}")
88
+ @conn.write(command, ms)
89
+ end
90
+
91
+ private
92
+ def process_options(command, arg_string)
93
+ arg_string = "\"#{command}(#{arg_string})\"" unless @options.empty?
94
+ case @options.shift
95
+ when :all
96
+ command = "command.all"
97
+ process_options(command, arg_string)
98
+ when :others
99
+ command = "command.others"
100
+ process_options(command, arg_string)
101
+ when :delay
102
+ command = "scout.delay"
103
+ arg_string = "#{@_ms}, #{arg_string}"
104
+ @_ms = false
105
+ process_options(command, arg_string)
106
+ else
107
+ arg_string.gsub(/"/, "\\\"") unless arg_string.empty?
108
+ return command, arg_string
109
+ end
110
+ end
111
+
112
+ def join_args(args)
113
+ arg_str = String.new
114
+ n = 0
115
+ args.each { |arg|
116
+ if arg.class == String
117
+ arg_str << "\"#{arg}\""
118
+ else
119
+ arg_str << arg.to_s
120
+ end
121
+ n += 1
122
+ arg_str << ", " if n < args.length && args[n].class != NilClass
123
+ }
124
+ arg_str
125
+ end
126
+ end
127
+ end
@@ -0,0 +1,10 @@
1
+ require './spec/spec_helper.rb'
2
+ require 'rspec'
3
+
4
+ RSpec.describe Geppeto do
5
+ # TODO:
6
+ # Listen, I know, it's bad. I just don't know how to test this. Maybe since
7
+ # you're looking at it, you could help - let me know. I just didn't know where
8
+ # to start.
9
+ #
10
+ end
@@ -0,0 +1,2 @@
1
+ require_relative '../lib/serial'
2
+ require_relative '../lib/server'
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: geppeto
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.1.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Matt Heitzenroder
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-10-11 00:00:00.000000000 Z
11
+ date: 2014-10-29 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: serialport
@@ -86,7 +86,32 @@ email: mheitzenroder@gmail.com
86
86
  executables: []
87
87
  extensions: []
88
88
  extra_rdoc_files: []
89
- files: []
89
+ files:
90
+ - ".gitignore"
91
+ - Gemfile
92
+ - Gemfile.lock
93
+ - LICENSE
94
+ - README.md
95
+ - Rakefile
96
+ - geppeto.gemspec
97
+ - lib/commands/commands.rb
98
+ - lib/commands/events.rb
99
+ - lib/commands/hq.rb
100
+ - lib/commands/led.rb
101
+ - lib/commands/memory.rb
102
+ - lib/commands/mesh.rb
103
+ - lib/commands/pin.rb
104
+ - lib/commands/power.rb
105
+ - lib/commands/temperature.rb
106
+ - lib/commands/uptime.rb
107
+ - lib/commands/wifi.rb
108
+ - lib/connections/connections.rb
109
+ - lib/connections/serial.rb
110
+ - lib/connections/server.rb
111
+ - lib/geppeto.rb
112
+ - lib/scout.rb
113
+ - spec/geppeto_spec.rb
114
+ - spec/spec_helper.rb
90
115
  homepage: http://github.com/roder/geppeto
91
116
  licenses:
92
117
  - Apache-2.0
@@ -107,8 +132,10 @@ required_rubygems_version: !ruby/object:Gem::Requirement
107
132
  version: '0'
108
133
  requirements: []
109
134
  rubyforge_project:
110
- rubygems_version: 2.4.1
135
+ rubygems_version: 2.2.2
111
136
  signing_key:
112
137
  specification_version: 4
113
138
  summary: Geppeto makes Pinoccio into Ruby.
114
- test_files: []
139
+ test_files:
140
+ - spec/geppeto_spec.rb
141
+ - spec/spec_helper.rb