frisky 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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 2deaa661a30948e0d168faa316a7bb81f089ce59
4
+ data.tar.gz: 1cec1f6c3430a00632c11dfb9c1e2f1a1a49fc89
5
+ SHA512:
6
+ metadata.gz: 8715b77ee7c823c6ecad28f0a2110082e8952db901f91cdba1275a59f9fddf8852c5af0dddd35feb49185355a7ef41a0673a3f6d1685981091223b1df987398c
7
+ data.tar.gz: a39f0325d37d0c95ef0222baa92db352d5d2097baa4a7389467c7e678d7910ea2c699257a2b286de73eab003f0e2096294f1fc849fd0e41636651c8c52564681
data/.gemtest ADDED
File without changes
data/.gitignore ADDED
@@ -0,0 +1,19 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+
5
+ .autotest
6
+ .DS_Store
7
+ .loadpath
8
+ .project
9
+ .yardoc/
10
+ .bundle/
11
+ .idea/
12
+
13
+ coverage/
14
+ doc/
15
+ pkg/
16
+ tmp/
17
+
18
+ */.DS_Store
19
+ atlassian-ide-plugin.xml
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ --color
data/.travis.yml ADDED
@@ -0,0 +1,6 @@
1
+ language: ruby
2
+ rvm:
3
+ - 1.9.3
4
+ - 2.0.0
5
+ - 2.1.0
6
+ - 2.2.3
data/Gemfile ADDED
@@ -0,0 +1,23 @@
1
+ # rubocop:disable Style/LeadingCommentSpace
2
+ #ruby-gemset=frisky
3
+ # rubocop:enable Style/LeadingCommentSpace
4
+ source 'http://rubygems.org'
5
+ gemspec
6
+ ruby "2.2.3"
7
+
8
+ #gem "httpi", '>=2.0.0.rc1'
9
+ #gem 'em-synchrony'
10
+
11
+ group :development do
12
+ gem 'coveralls', require: false
13
+ gem 'cucumber', '>=1.0.0', require: false
14
+ gem 'em-websocket', '>=0.3.6'
15
+ gem 'rake', require: false
16
+ gem 'log_buddy'
17
+ gem 'rspec', '>=3.0.0.beta', require: false
18
+ gem 'simplecov', '>=0.4.2', require: false
19
+ gem 'thin'
20
+ gem 'thor', '>=0.1.6', require: false
21
+ gem 'yard', '>=0.7.0', require: false
22
+ gem "pry", require: false
23
+ end
data/History.md ADDED
@@ -0,0 +1,3 @@
1
+ === 0.1.0.alpha.1
2
+
3
+ * I do stuffs!
data/LICENSE.md ADDED
@@ -0,0 +1,23 @@
1
+ (The MIT License)
2
+
3
+ Copyright (c) 2012-2014 Steve Loveless
4
+ Copyright (c) 2015 Jon Frisby
5
+
6
+ Permission is hereby granted, free of charge, to any person obtaining
7
+ a copy of this software and associated documentation files (the
8
+ 'Software'), to deal in the Software without restriction, including
9
+ without limitation the rights to use, copy, modify, merge, publish,
10
+ distribute, sublicense, and/or sell copies of the Software, and to
11
+ permit persons to whom the Software is furnished to do so, subject to
12
+ the following conditions:
13
+
14
+ The above copyright notice and this permission notice shall be
15
+ included in all copies or substantial portions of the Software.
16
+
17
+ THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
18
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
20
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
21
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
22
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
23
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,185 @@
1
+ # Frisky
2
+
3
+ * [Homepage](http://github.com/MrJoy/frisky)
4
+ * [UPnP Device Architecture Documentation](http://upnp.org/specs/arch/UPnP-arch-DeviceArchitecture-v1.0.pdf)
5
+
6
+
7
+ [<img src="https://travis-ci.org/MrJoy/frisky.png?branch=master" alt="Build Status" />](https://travis-ci.org/MrJoy/frisky) [<img src="https://coveralls.io/repos/MrJoy/frisky/badge.png" alt="Coverage Status" />](https://coveralls.io/r/MrJoy/frisky)
8
+
9
+ ## Description
10
+
11
+ Ruby's UPnP RubyGem was outdated in Ruby 1.9 when support for Soap4r was
12
+ dropped. This gem intends to fill that void for Ruby >= 1.9 and allow for
13
+ SSDP search, discovery, advertisement, the ability to act as a UPnP control
14
+ point, as well as provide UPnP devices and services.
15
+
16
+ This uses [EventMachine](http://github.com/eventmachine/eventmachine), so if
17
+ you're not already, getting familiar with its concepts will be helpful here.
18
+
19
+ ### This Fork
20
+
21
+ This fork updates the [playful](http://github.com/turboladen/playful) gem,
22
+ which hasn't seen updates in a while.
23
+
24
+ Specifically:
25
+
26
+ 1. Brings a few dependencies up to date.
27
+ 2. Gets rid of unfinished/broken, overly-ambitious functionality.
28
+
29
+ In short, this fork aims to be a simple, minimal way to run SSDP queries and handle the
30
+ results.
31
+
32
+ ### Er, what's UPnP??
33
+
34
+ "Universal Plug and Play" is a mashup of network protocols that let network
35
+ devices identify themselves and discover and use each other's services.
36
+ Common implementations of UPnP devices are things like:
37
+
38
+ * [Media Servers and Clients](http://en.wikipedia.org/wiki/List_of_UPnP_AV_media_servers_and_clients) like...
39
+ * PS3
40
+ * Slingbox
41
+ * Xbox
42
+ * XBMC
43
+ * Plex
44
+ * VLC
45
+ * Twonky
46
+ * Mediatomb
47
+ * Home Automation
48
+ * Philips Hue
49
+
50
+
51
+ If you have a device that implements UPnP, you can most likely control it
52
+ programmatically with `frisky`. You can't today, but eventually you'll be
53
+ able to build your own devices & services with `frisky` that can be consumed
54
+ by other UPnP clients (ex. build a media server with frisky and listen on
55
+ your PS3...).
56
+
57
+ ## Features
58
+
59
+ ### Implemented
60
+
61
+ * SSDP search, discovery.
62
+
63
+
64
+ ### Coming
65
+
66
+ * UPnP Devices & Services (server)
67
+
68
+
69
+ ## Examples
70
+
71
+ Take a look at the `tasks` directory; I've created some working examples using
72
+ [Thor](https://github.com/wycats/thor). You can get a list of these tasks by
73
+ doing `thor -T`.
74
+
75
+ There's also a more involved, in-progress, working example at
76
+ http://github.com/turboladen/upnp_cp_on_sinatra that uses the Rack middleware
77
+ to build a Sinatra app that allows for controling devices in your network.
78
+
79
+ ### SSDP Searches
80
+
81
+ An SSDP search simply sends the M-SEARCH out to the multicast group and
82
+ listens for responses for a given (or default of 5 seconds) amount of time.
83
+ The return from this depends on if you're running it within an EventMachine
84
+ reactor or not. If not, it returns is an Array of responses as Hashes, where
85
+ keys are the header names, values are the header values. Take a look at the
86
+ SSDP.search docs for more on the options here.
87
+
88
+ ```ruby
89
+ require 'frisky/ssdp'
90
+
91
+ # Search for all devices (do an M-SEARCH with the ST header set to 'ssdp:all')
92
+ all_devices = Frisky::SSDP.search # this is default
93
+ all_devices = Frisky::SSDP.search 'ssdp:all' # or be explicit
94
+ all_devices = Frisky::SSDP.search :all # or use short-hand
95
+
96
+ # Search for root devices (do an M-SEARCH with ST header set to 'upnp:rootdevices')
97
+ root_devices = Frisky::SSDP.search 'upnp:rootdevices'
98
+ root_devices = Frisky::SSDP.search :root # or use short-hand
99
+
100
+ # Search for a device with a specific UUID
101
+ my_device = Frisky::SSDP.search 'uuid:3c202906-992d-3f0f-b94c-90e1902a136d'
102
+
103
+ # Search for devices of a specific type
104
+ my_media_server = Frisky::SSDP.search 'urn:schemas-upnp-org:device:MediaServer:1'
105
+
106
+ # All of these searches will return something that looks like
107
+ # => [
108
+ # {
109
+ # :control => "max-age=1200",
110
+ # :date => "Sun, 23 Sep 2012 20:31:48 GMT",
111
+ # :location => "http://192.168.10.3:5001/description/fetch",
112
+ # :server => "Linux-i386-2.6.38-15-generic-pae, UPnP/1.0, PMS/1.50.0",
113
+ # :st => "upnp:rootdevice",
114
+ # :ext => "",
115
+ # :usn => "uuid:3c202906-992d-3f0f-b94c-90e1902a136d::upnp:rootdevice",
116
+ # :length => "0"
117
+ # }
118
+ # ]
119
+ ```
120
+
121
+ If you do the search inside of an EventMachine reactor, as the
122
+ Frisky::SSDP::Searcher receives and parses responses, it adds them to the
123
+ accessor #discovery_responses, which is an EventMachine::Channel. This lets
124
+ you subscribe to the resposnes and do what you want with them as you receive them.
125
+
126
+ ```ruby
127
+ require 'frisky/ssdp'
128
+
129
+ EM.run do
130
+ searcher = Frisky::SSDP.search 'uuid:3c202906-992d-3f0f-b94c-90e1902a136d'
131
+
132
+ # Create a deferrable object that can be notified when the device we want
133
+ # has been found and created.
134
+ device_controller = EventMachine::DefaultDeferrable.new
135
+
136
+ # This callback will get called when the device_creator callback is called
137
+ # (which is called after the device has been created).
138
+ device_controller.callback do |device|
139
+ p device.service_list.first.service_type # "urn:schemas-upnp-org:service:ContentDirectory:1"
140
+
141
+ # SOAP actions are converted to Ruby methods--show those
142
+ p device.service_list.first.singleton_methods # [:GetSystemUpdateID, :Search, :GetSearchCapabilities, :GetSortCapabilities, :Browse]
143
+
144
+ # Call a SOAP method defined in the service. The response is extracted from the
145
+ # XML SOAP response and the value is converted from the UPnP dataType to
146
+ # the related Ruby type. Reponses are always contained in a Hash, so as
147
+ # to maintain the relation defined in the service.
148
+ p device.service_list.first.GetSystemUpdateID # { :Id => 1 }
149
+ end
150
+
151
+ # Note that you don't have to check for items in the Channel or for when the
152
+ # Channel is empty: EventMachine will pop objects off the Channel as soon as
153
+ # they're put there and stop when there are none left.
154
+ searcher.discovery_responses.pop do |notification|
155
+ # Do stuff here.
156
+ end
157
+ end
158
+ ```
159
+
160
+ ## Requirements
161
+
162
+ * Rubies (tested)
163
+ * 1.9.3
164
+ * 2.0.0
165
+ * 2.1.0
166
+ * Gems
167
+ * eventmachine
168
+ * em-http-request
169
+ * em-synchrony
170
+ * nori
171
+ * log_switch
172
+ * savon
173
+
174
+
175
+
176
+ ## Install
177
+
178
+ $ gem install frisky
179
+
180
+ ## Copyright
181
+
182
+ Copyright (c) 2015 Jon Frisby
183
+ Copyright (c) 2012-2014 Steve Loveless
184
+
185
+ See LICENSE.md for details.
data/Rakefile ADDED
@@ -0,0 +1,16 @@
1
+ require 'bundler/gem_tasks'
2
+ require 'rspec/core/rake_task'
3
+ require 'cucumber/rake/task'
4
+ require 'yard'
5
+
6
+
7
+ YARD::Rake::YardocTask.new
8
+ Cucumber::Rake::Task.new(:features)
9
+ RSpec::Core::RakeTask.new
10
+
11
+ # Alias for rubygems-test
12
+ desc "Run all test suites."
13
+ task test: [:spec, :features]
14
+
15
+ task default: :test
16
+
@@ -0,0 +1,9 @@
1
+ Feature: Device discovery
2
+ As a device controller, I want to be able to discover devices
3
+ so that I can use the services those devices provide
4
+
5
+ Scenario: A single root device
6
+ Given there's at least 1 root device in my network
7
+ When I come online
8
+ Then I should discover at least 1 root device
9
+ And the location of that device should match my fake device's location
File without changes
@@ -0,0 +1,21 @@
1
+ require 'socket'
2
+ require 'log_buddy'
3
+ require 'cucumber/rspec/doubles'
4
+ require_relative "../../lib/frisky/logger"
5
+
6
+ def local_ip_and_port
7
+ orig, Socket.do_not_reverse_lookup = Socket.do_not_reverse_lookup, true
8
+
9
+ UDPSocket.open do |s|
10
+ s.connect '64.233.187.99', 1
11
+ s.addr.last
12
+ [s.addr.last, s.addr[1]]
13
+ end
14
+ ensure
15
+ Socket.do_not_reverse_lookup = orig
16
+ end
17
+
18
+ ENV['RUBY_UPNP_ENV'] = 'testing'
19
+
20
+ Thread.abort_on_exception = true
21
+ Frisky.logging_enabled = false
@@ -0,0 +1,7 @@
1
+ module HelperStuff
2
+ def local_ip
3
+ @local_ip ||= local_ip_and_port.first
4
+ end
5
+ end
6
+
7
+ World(HelperStuff)
data/frisky.gemspec ADDED
@@ -0,0 +1,28 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path('../lib', __FILE__)
3
+ require 'frisky/version'
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = 'frisky'
7
+ s.version = Frisky::VERSION
8
+ s.author = 'Jon Frisby'
9
+ s.email = 'jfrisby@mrjoy.com'
10
+ s.homepage = 'http://github.com/MrJoy/frisky'
11
+ s.summary = 'Use me to build a UPnP app!'
12
+ s.description = %q{frisky provides the tools you need to build an app that runs
13
+ in a UPnP environment.}
14
+
15
+ s.files = `git ls-files`.split("\n")
16
+ s.test_files = `git ls-files -- {spec,features}/*`.split("\n")
17
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
18
+ s.extra_rdoc_files = %w(History.md README.md LICENSE.md)
19
+ s.require_paths = ['lib']
20
+ s.required_ruby_version = Gem::Requirement.new('>=1.9.1')
21
+
22
+ s.add_dependency 'eventmachine', '>=1.0.0'
23
+ s.add_dependency 'em-http-request', '>=1.0.2'
24
+ s.add_dependency 'em-synchrony'
25
+ s.add_dependency 'nori', '>=2.0.2'
26
+ s.add_dependency 'log_switch', '~>1.0.0'
27
+ s.add_dependency 'savon', '~>2.0'
28
+ end
@@ -0,0 +1,5 @@
1
+ class Hash
2
+ def symbolize_keys!
3
+ self.inject({}) { |result, (k, v)| result[k.to_sym] = v; result }
4
+ end
5
+ end
@@ -0,0 +1,16 @@
1
+ require 'socket'
2
+
3
+ # Workaround for missing constants on Windows
4
+ module Socket::Constants
5
+ IP_ADD_MEMBERSHIP = 12 unless defined? IP_ADD_MEMBERSHIP
6
+ IP_MULTICAST_LOOP = 11 unless defined? IP_MULTICAST_LOOP
7
+ IP_MULTICAST_TTL = 10 unless defined? IP_MULTICAST_TTL
8
+ IP_TTL = 4 unless defined? IP_TTL
9
+ end
10
+
11
+ class Socket
12
+ IP_ADD_MEMBERSHIP = 12 unless defined? IP_ADD_MEMBERSHIP
13
+ IP_MULTICAST_LOOP = 11 unless defined? IP_MULTICAST_LOOP
14
+ IP_MULTICAST_TTL = 10 unless defined? IP_MULTICAST_TTL
15
+ IP_TTL = 4 unless defined? IP_TTL
16
+ end
@@ -0,0 +1,65 @@
1
+ class Hash
2
+
3
+ # Converts Hash search targets to SSDP search target String. Conversions are
4
+ # as follows:
5
+ # uuid: "someUUID" # => "uuid:someUUID"
6
+ # device_type: "someDeviceType:1" # => "urn:schemas-upnp-org:device:someDeviceType:1"
7
+ # service_type: "someServiceType:2" # => "urn:schemas-upnp-org:service:someServiceType:2"
8
+ #
9
+ # You can use custom UPnP domain names too:
10
+ # { device_type: "someDeviceType:3",
11
+ # domain_name: "mydomain-com" } # => "urn:my-domain:device:someDeviceType:3"
12
+ # { service_type: "someServiceType:4",
13
+ # domain_name: "mydomain-com" } # => "urn:my-domain:service:someDeviceType:4"
14
+ #
15
+ # @return [String] The converted String, according to the UPnP spec.
16
+ def to_upnp_s
17
+ if self.has_key? :uuid
18
+ return "uuid:#{self[:uuid]}"
19
+ elsif self.has_key? :device_type
20
+ if self.has_key? :domain_name
21
+ return "urn:#{self[:domain_name]}:device:#{self[:device_type]}"
22
+ else
23
+ return "urn:schemas-upnp-org:device:#{self[:device_type]}"
24
+ end
25
+ elsif self.has_key? :service_type
26
+ if self.has_key? :domain_name
27
+ return "urn:#{self[:domain_name]}:service:#{self[:service_type]}"
28
+ else
29
+ return "urn:schemas-upnp-org:service:#{self[:service_type]}"
30
+ end
31
+ else
32
+ self.to_s
33
+ end
34
+ end
35
+ end
36
+
37
+
38
+ class Symbol
39
+
40
+ # Converts Symbol search targets to SSDP search target String. Conversions are
41
+ # as follows:
42
+ # :all # => "ssdp:all"
43
+ # :root # => "upnp:rootdevice"
44
+ # "root" # => "upnp:rootdevice"
45
+ #
46
+ # @return [String] The converted String, according to the UPnP spec.
47
+ def to_upnp_s
48
+ if self == :all
49
+ 'ssdp:all'
50
+ elsif self == :root
51
+ 'upnp:rootdevice'
52
+ else
53
+ self
54
+ end
55
+ end
56
+ end
57
+
58
+
59
+ class String
60
+ # This doesn't do anything to the string; just allows users to call the
61
+ # method without having to check type first.
62
+ def to_upnp_s
63
+ self
64
+ end
65
+ end