yourkarma 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +22 -0
- data/.rspec +2 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +28 -0
- data/Rakefile +2 -0
- data/bin/yourkarma +8 -0
- data/lib/yourkarma.rb +5 -0
- data/lib/yourkarma/benchmarker.rb +36 -0
- data/lib/yourkarma/cli.rb +195 -0
- data/lib/yourkarma/client.rb +61 -0
- data/lib/yourkarma/device.rb +35 -0
- data/lib/yourkarma/version.rb +3 -0
- data/spec/cassettes/.keep +0 -0
- data/spec/cassettes/CLI/output/connected_to_a_hotspot/.yml +86 -0
- data/spec/cassettes/CLI/output/connected_to_a_hotspot_without_an_assigned_WAN_IP_address/.yml +45 -0
- data/spec/cassettes/CLI/output/connected_to_the_internet/.yml +86 -0
- data/spec/cassettes/CLI/output/connected_to_the_internet_with_a_slow_connection/.yml +45 -0
- data/spec/cassettes/CLI/status_code/connected_to_a_hotspot/.yml +86 -0
- data/spec/cassettes/CLI/status_code/connected_to_a_hotspot_without_an_assigned_WAN_IP_address/.yml +45 -0
- data/spec/cassettes/CLI/status_code/connected_to_the_internet/.yml +86 -0
- data/spec/cassettes/CLI/status_code/connected_to_the_internet_with_a_slow_connection/.yml +45 -0
- data/spec/features/cli_spec.rb +77 -0
- data/spec/spec_helper.rb +22 -0
- data/spec/support/have_line_matching_matcher.rb +5 -0
- data/spec/support/vcr.rb +12 -0
- data/spec/units/benchmarker_spec.rb +50 -0
- data/spec/units/cli_argument_parser_spec.rb +26 -0
- data/spec/units/cli_reporter_spec.rb +61 -0
- data/spec/units/cli_spec.rb +41 -0
- data/spec/units/client_spec.rb +54 -0
- data/spec/units/device_spec.rb +21 -0
- data/what_karma.gemspec +30 -0
- metadata +167 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 2a01341231ba782a9acafe01f78d90cdc20c2a4d
|
4
|
+
data.tar.gz: 1edbb26ae1729447750964fb557ae781ba2718d6
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 58a1e7efb614a6b5b617e1982b2dba09c3dbaf0988b1cb690272de2119a5274bae2fb45ec3d4c682ef67a462330cb96e14db1fa85e009bcf49facd27a976475d
|
7
|
+
data.tar.gz: c9ca35b5db6487a8c4516a8622bdd13d0de695f09d01707fca6ed7c27f8c8cdd8443b2fda0cc19e9ed8f8961b97814691bc0a277dd895b56bd0f3b2814969c9f
|
data/.gitignore
ADDED
@@ -0,0 +1,22 @@
|
|
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
|
+
*.bundle
|
19
|
+
*.so
|
20
|
+
*.o
|
21
|
+
*.a
|
22
|
+
mkmf.log
|
data/.rspec
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2014 Greg Sterndale
|
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,28 @@
|
|
1
|
+
# yourkarma
|
2
|
+
## Karma hotspot status client & CLI
|
3
|
+
|
4
|
+
Determine connectivity, battery and connection speed for your device.
|
5
|
+
|
6
|
+
## Installation
|
7
|
+
|
8
|
+
$ gem install yourkarma
|
9
|
+
|
10
|
+
## Usage
|
11
|
+
|
12
|
+
$ yourkarma
|
13
|
+
|
14
|
+
## TODO
|
15
|
+
|
16
|
+
- Bar charts for connection speed and battery
|
17
|
+
- Option to poll until connected
|
18
|
+
- Display available data from yourkarma.com/dashboard
|
19
|
+
- Display updates/news from yourkarma.com/dashboard
|
20
|
+
- Display connected user count
|
21
|
+
|
22
|
+
## Contributing
|
23
|
+
|
24
|
+
1. Fork it ( https://github.com/[my-github-username]/yourkarma/fork )
|
25
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
26
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
27
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
28
|
+
5. Create a new Pull Request
|
data/Rakefile
ADDED
data/bin/yourkarma
ADDED
data/lib/yourkarma.rb
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
require 'uri'
|
2
|
+
require 'timeout'
|
3
|
+
require 'net/http'
|
4
|
+
require 'benchmark'
|
5
|
+
|
6
|
+
module YourKarma
|
7
|
+
class Benchmarker
|
8
|
+
class ConnectionError < StandardError; end
|
9
|
+
|
10
|
+
HTTP_ERRORS = [Timeout::Error, Errno::EINVAL, Errno::ECONNRESET,
|
11
|
+
Errno::ETIMEDOUT, EOFError, Net::HTTPBadResponse,
|
12
|
+
Net::HTTPHeaderSyntaxError, Net::ProtocolError, SocketError]
|
13
|
+
|
14
|
+
DEFAULT_OPTIONS = {
|
15
|
+
timeout: 10,
|
16
|
+
benchmark: 'http://yourkarma.com/dashboard'
|
17
|
+
}
|
18
|
+
|
19
|
+
attr_accessor :options
|
20
|
+
|
21
|
+
def initialize(options = {})
|
22
|
+
self.options = DEFAULT_OPTIONS.merge(options)
|
23
|
+
end
|
24
|
+
|
25
|
+
def benchmark
|
26
|
+
uri = URI(options[:benchmark])
|
27
|
+
Timeout::timeout(options[:timeout]) do
|
28
|
+
return Benchmark.realtime do
|
29
|
+
Net::HTTP.get_response(uri)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
rescue *HTTP_ERRORS => e
|
33
|
+
raise ConnectionError
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,195 @@
|
|
1
|
+
require 'optparse'
|
2
|
+
require_relative 'client'
|
3
|
+
require_relative 'device'
|
4
|
+
require_relative 'benchmarker'
|
5
|
+
|
6
|
+
module YourKarma
|
7
|
+
class CLI
|
8
|
+
attr_accessor :options
|
9
|
+
|
10
|
+
def self.run(io, arguments)
|
11
|
+
options = { io: io }
|
12
|
+
options.merge! ArgumentParser.new.parse(arguments)
|
13
|
+
self.new(options).run
|
14
|
+
rescue ArgumentParser::InvalidOption => e
|
15
|
+
options[:io].puts e
|
16
|
+
1
|
17
|
+
end
|
18
|
+
|
19
|
+
def initialize(options = {})
|
20
|
+
self.options = options
|
21
|
+
end
|
22
|
+
|
23
|
+
def run
|
24
|
+
reporter = Reporter.new(options)
|
25
|
+
attrs = Client.new(options).get
|
26
|
+
device = Device.new(attrs)
|
27
|
+
benchmark = Benchmarker.new(options).benchmark
|
28
|
+
reporter.report_on(device, benchmark)
|
29
|
+
rescue Client::ConnectionError => e
|
30
|
+
reporter.report_connectivity_failure e.message
|
31
|
+
rescue Benchmarker::ConnectionError
|
32
|
+
reporter.report_benchmark_failure_on(device)
|
33
|
+
end
|
34
|
+
|
35
|
+
class Reporter
|
36
|
+
DEFAULT_OPTIONS = {
|
37
|
+
io: STDOUT
|
38
|
+
}
|
39
|
+
attr_accessor :options
|
40
|
+
|
41
|
+
def initialize(options = {})
|
42
|
+
self.options = DEFAULT_OPTIONS.merge(options)
|
43
|
+
end
|
44
|
+
|
45
|
+
def report_on(device, benchmark)
|
46
|
+
write "Connected to '#{device.ssid}' and online!"
|
47
|
+
if options[:verbose]
|
48
|
+
ceil = (benchmark * 10).ceil / 10.0
|
49
|
+
write "Benchmark took less than #{ceil} seconds."
|
50
|
+
end
|
51
|
+
0
|
52
|
+
end
|
53
|
+
|
54
|
+
def report_connectivity_failure(message)
|
55
|
+
write "Not connected to a Karma hotspot."
|
56
|
+
write message if options[:verbose]
|
57
|
+
1
|
58
|
+
end
|
59
|
+
|
60
|
+
def report_benchmark_failure_on(device)
|
61
|
+
if device.valid_ipaddress?
|
62
|
+
write "Connected to '#{device.ssid}' and online, but the tubes are slow!"
|
63
|
+
else
|
64
|
+
write "Connected to '#{device.ssid}' and acquiring IP address."
|
65
|
+
end
|
66
|
+
1
|
67
|
+
end
|
68
|
+
|
69
|
+
private
|
70
|
+
|
71
|
+
def write(string)
|
72
|
+
options[:io].puts string
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
class ArgumentParser
|
77
|
+
class InvalidOption < StandardError
|
78
|
+
end
|
79
|
+
|
80
|
+
DEFAULT_OPTIONS = {
|
81
|
+
verbose: false
|
82
|
+
}
|
83
|
+
|
84
|
+
def parse(arguments)
|
85
|
+
options = DEFAULT_OPTIONS.dup
|
86
|
+
optparse = OptionParser.new do |opts|
|
87
|
+
opts.banner = <<-BANNER.gsub(/^\s{4}/, '')
|
88
|
+
Karma hotspot status
|
89
|
+
Usage: yourkarma [options]
|
90
|
+
Example: yourkarma --verbose
|
91
|
+
BANNER
|
92
|
+
|
93
|
+
opts.on('-u', '--url=URL', "Karma hotspot URL") do |url|
|
94
|
+
options[:url] = url
|
95
|
+
end
|
96
|
+
opts.on('-t', '--timeout=TIMEOUT', "Time to wait for HTTP response", Float) do |timeout|
|
97
|
+
options[:timeout] = timeout
|
98
|
+
end
|
99
|
+
opts.on('-v', '--[no-]verbose', 'Run verbosely') do |v|
|
100
|
+
options[:verbose] = true
|
101
|
+
end
|
102
|
+
opts.on('-h', '--help', 'Display this screen') do
|
103
|
+
raise InvalidOption, opts
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
optparse.parse!(arguments)
|
108
|
+
|
109
|
+
options
|
110
|
+
rescue OptionParser::InvalidOption, OptionParser::MissingArgument
|
111
|
+
raise InvalidOption, optparse
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
# require 'json'
|
118
|
+
# require 'uri'
|
119
|
+
# require 'net/http'
|
120
|
+
# require 'timeout'
|
121
|
+
# require 'benchmark'
|
122
|
+
# require 'pp'
|
123
|
+
#
|
124
|
+
# uri = URI('http://hotspot.yourkarma.com/api/status.json')
|
125
|
+
#
|
126
|
+
# VERBOSE = true
|
127
|
+
# VERY_VERBOSE = false
|
128
|
+
# TIMEOUT = 10
|
129
|
+
#
|
130
|
+
# def failure(message, exception, context = nil)
|
131
|
+
# if VERBOSE
|
132
|
+
# puts exception.class if VERY_VERBOSE
|
133
|
+
# puts exception
|
134
|
+
# puts context if context && VERY_VERBOSE
|
135
|
+
# end
|
136
|
+
# puts message
|
137
|
+
# exit 1
|
138
|
+
# end
|
139
|
+
#
|
140
|
+
# HTTP_ERRORS = [Timeout::Error, Errno::EINVAL, Errno::ECONNRESET, EOFError,
|
141
|
+
# Net::HTTPBadResponse, Net::HTTPHeaderSyntaxError, Net::ProtocolError,
|
142
|
+
# SocketError]
|
143
|
+
#
|
144
|
+
# begin
|
145
|
+
# response = nil
|
146
|
+
# Timeout::timeout(TIMEOUT) do
|
147
|
+
# response = Net::HTTP.get_response(uri)
|
148
|
+
# end
|
149
|
+
# parsed = JSON::load(response.body)
|
150
|
+
# rescue Timeout::Error => e
|
151
|
+
# failure "Request to Karma hotspot timed out.", e
|
152
|
+
# rescue *HTTP_ERRORS => e
|
153
|
+
# failure "Not connected to a Karma hotspot.", e
|
154
|
+
# rescue JSON::ParserError
|
155
|
+
# failure "Unexpected response from #{uri}.", e, parsed
|
156
|
+
# end
|
157
|
+
#
|
158
|
+
# begin
|
159
|
+
# message = "Network not connected."
|
160
|
+
#
|
161
|
+
# device = parsed.fetch "device"
|
162
|
+
# wifi = device.fetch "wifiinterface"
|
163
|
+
# ssid = wifi.fetch "ssid"
|
164
|
+
#
|
165
|
+
# message = "Unable to determine battery status."
|
166
|
+
#
|
167
|
+
# charging = device.fetch "charging"
|
168
|
+
# battery = device.fetch "batterypower"
|
169
|
+
# print "Battery #{battery}%. "
|
170
|
+
# puts charging ? "Charging." : "Not charging."
|
171
|
+
#
|
172
|
+
# message = "Connected to '#{ssid}', but WAN IP address is not assigned."
|
173
|
+
#
|
174
|
+
# wan = device.fetch "waninterface"
|
175
|
+
# ip_address = wan.fetch "ipaddress"
|
176
|
+
# rescue KeyError => e
|
177
|
+
# failure message, e, parsed
|
178
|
+
# end
|
179
|
+
#
|
180
|
+
# begin
|
181
|
+
# benchmark = nil
|
182
|
+
# Timeout::timeout(TIMEOUT) do
|
183
|
+
# benchmark = Benchmark.realtime do
|
184
|
+
# Net::HTTP.get_response(URI('https://www.google.com'))
|
185
|
+
# end
|
186
|
+
# end
|
187
|
+
# benchmark = (benchmark * 10).ceil / 10.0
|
188
|
+
# rescue TimeoutError => e
|
189
|
+
# failure "Connected to '#{ssid}' and online, but the tubes are slow!", e, parsed
|
190
|
+
# rescue *HTTP_ERRORS => e
|
191
|
+
# failure "Connected to '#{ssid}' and WAN IP address has be assigned, but request to Google failed.", e, parsed
|
192
|
+
# end
|
193
|
+
#
|
194
|
+
# puts "Connected to '#{ssid}' and online!"
|
195
|
+
# puts "Request to Google took less than #{benchmark} seconds." if VERBOSE
|
@@ -0,0 +1,61 @@
|
|
1
|
+
require 'uri'
|
2
|
+
require 'timeout'
|
3
|
+
require 'net/http'
|
4
|
+
|
5
|
+
module YourKarma
|
6
|
+
class Client
|
7
|
+
class ConnectionError < StandardError; end
|
8
|
+
class BadResponseError < StandardError; end
|
9
|
+
|
10
|
+
HTTP_ERRORS = [Timeout::Error, Errno::EINVAL, Errno::ECONNRESET,
|
11
|
+
Errno::ETIMEDOUT, EOFError, Net::HTTPBadResponse,
|
12
|
+
Net::HTTPHeaderSyntaxError, Net::ProtocolError, SocketError]
|
13
|
+
|
14
|
+
DEFAULT_OPTIONS = {
|
15
|
+
timeout: 10,
|
16
|
+
url: "http://hotspot.yourkarma.com/api/status.json",
|
17
|
+
}
|
18
|
+
|
19
|
+
attr_accessor :options
|
20
|
+
|
21
|
+
def initialize(options = DEFAULT_OPTIONS)
|
22
|
+
self.options = DEFAULT_OPTIONS.merge(options)
|
23
|
+
end
|
24
|
+
|
25
|
+
def get
|
26
|
+
uri = URI(options[:url])
|
27
|
+
response = nil
|
28
|
+
Timeout::timeout(options[:timeout]) do
|
29
|
+
response = Net::HTTP.get_response(uri)
|
30
|
+
end
|
31
|
+
JSON::load(response.body).fetch("device")
|
32
|
+
rescue *HTTP_ERRORS => e
|
33
|
+
raise ConnectionError, e.message
|
34
|
+
rescue JSON::ParserError => e
|
35
|
+
raise BadResponseError, e.message
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
# {
|
41
|
+
# "device"=>{
|
42
|
+
# "name"=>"IMW-C918W",
|
43
|
+
# "swversion"=>"R4855",
|
44
|
+
# "hwversion"=>"R06",
|
45
|
+
# "uptime"=>"P0Y0M0DT0H14M41S",
|
46
|
+
# "batterypower"=>100,
|
47
|
+
# "charging"=>false,
|
48
|
+
# "waninterface"=>{
|
49
|
+
# "macaddress"=>"001E312C42D0",
|
50
|
+
# "ipaddress"=>"74.60.178.162",
|
51
|
+
# "bsid"=>"00:00:02:06:04:30",
|
52
|
+
# "rssi"=>-59,
|
53
|
+
# "cinr"=>20,
|
54
|
+
# "connectionduration"=>"P0Y0M0DT0H9M54S"
|
55
|
+
# },
|
56
|
+
# "wifiinterface"=>{
|
57
|
+
# "ssid"=>"Karma Wi-Fi",
|
58
|
+
# "users"=>2
|
59
|
+
# }
|
60
|
+
# }
|
61
|
+
# }
|
@@ -0,0 +1,35 @@
|
|
1
|
+
require 'json'
|
2
|
+
|
3
|
+
module YourKarma
|
4
|
+
class Device
|
5
|
+
IPADDRESS_PATTERN = /^([1-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])(\.([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])){3}$/
|
6
|
+
|
7
|
+
attr_accessor *%i(name swversion hwversion uptime batterypower charging
|
8
|
+
macaddress ipaddress bsid rssi cinr connectionduration
|
9
|
+
ssid users)
|
10
|
+
|
11
|
+
def initialize(attrs = {})
|
12
|
+
self.attributes = attrs
|
13
|
+
end
|
14
|
+
|
15
|
+
def waninterface=(attrs)
|
16
|
+
self.attributes = attrs
|
17
|
+
end
|
18
|
+
|
19
|
+
def wifiinterface=(attrs)
|
20
|
+
self.attributes = attrs
|
21
|
+
end
|
22
|
+
|
23
|
+
def valid_ipaddress?
|
24
|
+
self.ipaddress && self.ipaddress.match(IPADDRESS_PATTERN)
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
def attributes=(attrs)
|
30
|
+
attrs.each_pair do |name, value|
|
31
|
+
self.public_send("#{name}=", value)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|