geppeto 0.1.0 → 0.1.1

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 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