rbupnptools 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.
data/lib/upnp_event.rb ADDED
@@ -0,0 +1,86 @@
1
+ require 'nokogiri'
2
+ require 'net/http'
3
+ require_relative 'upnp_xml.rb'
4
+
5
+ class NotifyRequest < Net::HTTPRequest
6
+ METHOD = 'NOTIFY'
7
+ REQUEST_HAS_BODY = true
8
+ RESPONSE_HAS_BODY = false
9
+ end
10
+
11
+ class SubscribeRequest < Net::HTTPRequest
12
+ METHOD = 'SUBSCRIBE'
13
+ REQUEST_HAS_BODY = false
14
+ RESPONSE_HAS_BODY = false
15
+ end
16
+
17
+ class UnsubscribeRequest < Net::HTTPRequest
18
+ METHOD = 'UNSUBSCRIBE'
19
+ REQUEST_HAS_BODY = false
20
+ RESPONSE_HAS_BODY = false
21
+ end
22
+
23
+ class UPnPEventProperty < Hash
24
+ def to_xml
25
+ propertyset = XmlTag.new 'e:propertyset'
26
+ propertyset.attributes = {
27
+ 'xmlns:e' => "urn:schemas-upnp-org:event-1-0"
28
+ }
29
+ self.each do |k,v|
30
+ prop = propertyset.append XmlTag.new 'e:property'
31
+ prop.append(XmlTag.new(k)).append(XmlText.new(v))
32
+ end
33
+ return propertyset.to_s
34
+ end
35
+ end
36
+
37
+ def UPnPEventProperty.to_xml_doc(property)
38
+ return '<?xml version="1.0" encoding="UTF-8"?>' + "\n#{property.to_xml}"
39
+ end
40
+
41
+ def UPnPEventProperty.read(xml)
42
+ props = UPnPEventProperty.new
43
+ doc = Nokogiri::XML(xml)
44
+ doc.root.elements.each do |elem|
45
+ if elem.name == 'property'
46
+ name = elem.elements.first.name
47
+ value = elem.elements.first.text
48
+ props[name] = value
49
+ end
50
+ end
51
+ props
52
+ end
53
+
54
+ class UPnPEventListener
55
+ def initialize(on_event_notify = nil)
56
+ @on_event_notify = on_event_notify
57
+ end
58
+
59
+ def on_event_notify(sid, props)
60
+ if @on_event_notify
61
+ @on_event_notify.call sid, props
62
+ end
63
+ end
64
+ end
65
+
66
+ class UPnPEventSubscription
67
+ def initialize(device, service, sid, timeout, callback_urls = nil)
68
+ @device = device
69
+ @service = service
70
+ @sid = sid
71
+ @timeout = timeout
72
+ @reg_date = Process.clock_gettime(Process::CLOCK_MONOTONIC, :second)
73
+ @callback_urls = callback_urls
74
+ end
75
+
76
+ attr_accessor :device, :service, :sid, :timeout, :callback_urls
77
+
78
+ def renew_timeout
79
+ @reg_date = Process.clock_gettime(Process::CLOCK_MONOTONIC, :second)
80
+ end
81
+
82
+ def expired?
83
+ now = Process.clock_gettime(Process::CLOCK_MONOTONIC, :second)
84
+ return (now - @reg_date) >= @tiemout
85
+ end
86
+ end
data/lib/upnp_model.rb ADDED
@@ -0,0 +1,419 @@
1
+ require 'nokogiri'
2
+ require 'securerandom'
3
+ require_relative 'upnp_xml.rb'
4
+
5
+
6
+ class UPnPModel < Hash
7
+ def self.to_method_name(name)
8
+ return name.gsub(/([a-z])([A-Z]+)/, '\1_\2').downcase
9
+ end
10
+
11
+ def self.define_xml_attr(*names)
12
+ names.each do |name|
13
+ name = "#{name}"
14
+ define_method "#{to_method_name(name)}" do
15
+ self[name]
16
+ end
17
+ define_method "#{to_method_name(name)}=" do |v|
18
+ self[name] = v
19
+ end
20
+ end
21
+ end
22
+ end
23
+
24
+ class UPnPSpecVersion
25
+
26
+ attr_accessor :major, :minor
27
+
28
+ def to_s
29
+ "UPnPSpecVersion -- 'major: #{@major}, minor: #{minor}'"
30
+ end
31
+
32
+ def to_xml
33
+ spec_version = XmlTag.new('specVersion')
34
+ spec_version.append(XmlTag.new('major')).append(XmlText.new(@major))
35
+ spec_version.append(XmlTag.new('minor')).append(XmlText.new(@minor))
36
+ return spec_version.to_s
37
+ end
38
+
39
+ end
40
+
41
+ def UPnPSpecVersion.read_xml_node(node)
42
+ spec_version = UPnPSpecVersion.new
43
+ node.elements.each { |elem|
44
+ if elem.name == 'major'
45
+ spec_version.major = elem.text
46
+ elsif elem.name == 'minor'
47
+ spec_version.minor = elem.text
48
+ end
49
+ }
50
+ return spec_version
51
+ end
52
+
53
+
54
+ class UPnPDevice < UPnPModel
55
+ def initialize
56
+ @child_devices = []
57
+ @services = []
58
+ end
59
+
60
+ attr_accessor :base_url
61
+ attr_accessor :child_devices
62
+ attr_accessor :services
63
+
64
+ define_xml_attr :deviceType, :UDN, :friendlyName, :manufacturer, :manufacturerURL,
65
+ :modelDescription, :modelName, :modelNumber, :modelURL, :serialNumber
66
+
67
+ def usn
68
+ return udn + '::' + device_type
69
+ end
70
+
71
+ def all_usn
72
+ usn_list = []
73
+ usn_list << usn
74
+ @services.each do |service|
75
+ usn_list << udn + '::' + service.service_type
76
+ end
77
+ @child_devices.each do |device|
78
+ usn_list += device.all_usn
79
+ end
80
+ return usn_list
81
+ end
82
+
83
+ def get_device(type)
84
+ all_devices.each do |device|
85
+ if device.device_type == type
86
+ return device
87
+ end
88
+ end
89
+ nil
90
+ end
91
+
92
+ def get_service(type)
93
+ all_services.each do |service|
94
+ if service.service_type == type
95
+ return service
96
+ end
97
+ end
98
+ nil
99
+ end
100
+
101
+ def all_devices
102
+ devices = []
103
+ devices << self
104
+ @child_devices.each do |device|
105
+ devices += device.all_devices
106
+ end
107
+ return devices
108
+ end
109
+
110
+ def all_services
111
+ services = @services
112
+ @child_devices.each do |device|
113
+ services += device.all_services
114
+ end
115
+ return services
116
+ end
117
+
118
+ def to_s
119
+ "UPnPDevice -- '#{friendly_name}' (#{udn})"
120
+ end
121
+
122
+ def to_xml
123
+ device = XmlTag.new 'device'
124
+ self.each { |k,v|
125
+ device.append(XmlTag.new(k)).append(XmlText.new(v))
126
+ }
127
+
128
+ if @services.any?
129
+ serviceList = device.append XmlTag.new('serviceList')
130
+ @services.each { |service|
131
+ serviceList.append service.to_xml
132
+ }
133
+ end
134
+
135
+ if @child_devices.any?
136
+ deviceList = device.append XmlTag.new('deviceList')
137
+ @child_devices.each { |device|
138
+ deviceList.append device.to_xml
139
+ }
140
+ end
141
+ return device.to_s
142
+ end
143
+ end
144
+
145
+ def UPnPDevice.to_xml_doc(device)
146
+ root = XmlTag.new 'root'
147
+ root.attributes['xmlns'] = 'urn:schemas-upnp-org:device-1-0'
148
+
149
+ specVersion = root.append(XmlTag.new('specVersion'))
150
+ specVersion.append(XmlTag.new('major')).append(XmlText.new(1))
151
+ specVersion.append(XmlTag.new('major')).append(XmlText.new(0))
152
+
153
+ root.append device.to_xml
154
+ return '<?xml version="1.0" encoding="UTF-8"?>' + "\n#{root}"
155
+ end
156
+
157
+ def UPnPDevice.read(xml)
158
+ doc = Nokogiri::XML(xml)
159
+ doc.root.elements.each do |elem|
160
+ if elem.name == 'specVersion'
161
+ spec_version = UPnPSpecVersion.read_xml_node elem
162
+ elsif elem.name == 'device'
163
+ return UPnPDevice.read_xml_node elem
164
+ end
165
+ end
166
+ nil
167
+ end
168
+
169
+ def UPnPDevice.read_xml_node(node)
170
+ device = UPnPDevice.new
171
+ node.elements.each do |elem|
172
+ if elem.elements.empty?
173
+ device[elem.name] = elem.text
174
+ elsif elem.name == 'deviceList'
175
+ elem.elements.each do |device_node|
176
+ if device_node.name == 'device'
177
+ device.child_devices << UPnPDevice.read_xml_node(device_node)
178
+ end
179
+ end
180
+ elsif elem.name == 'serviceList'
181
+ elem.elements.each do |service_node|
182
+ if service_node.name == 'service'
183
+ device.services << UPnPService.read_xml_node(service_node)
184
+ end
185
+ end
186
+ end
187
+ end
188
+ device
189
+ end
190
+
191
+
192
+ class UPnPService < UPnPModel
193
+ attr_accessor :scpd
194
+
195
+ define_xml_attr :serviceId, :serviceType, :SCPDURL, :controlURL, :eventSubURL
196
+
197
+ def to_xml
198
+ service = XmlTag.new 'service'
199
+ self.each { |k,v|
200
+ service.append(XmlTag.new(k)).append(XmlText.new(v))
201
+ }
202
+ return service.to_s
203
+ end
204
+ end
205
+
206
+ def UPnPService.read_xml_node(node)
207
+ service = UPnPService.new
208
+ node.elements.each do |elem|
209
+ if elem.elements.empty?
210
+ service[elem.name] = elem.text
211
+ end
212
+ end
213
+ service
214
+ end
215
+
216
+
217
+ class UPnPScpd < UPnPModel
218
+
219
+ def initialize
220
+ @actions = []
221
+ @state_variables = []
222
+ end
223
+
224
+ attr_accessor :spec_version, :actions, :state_variables
225
+
226
+ def to_xml
227
+ scpd = XmlTag.new 'scpd'
228
+ scpd.attributes['xmlns'] = 'urn:schemas-upnp-org:service-1-0'
229
+
230
+ spec_version = scpd.append XmlTag.new('specVersion')
231
+ spec_version.append(XmlTag.new('major')).append(XmlText.new(1))
232
+ spec_version.append(XmlTag.new('minor')).append(XmlText.new(0))
233
+
234
+ action_list = scpd.append XmlTag.new('actionList')
235
+ @actions.each { |action|
236
+ action_list.append action.to_xml
237
+ }
238
+
239
+ service_state_table = scpd.append XmlTag.new('serviceStateTable');
240
+ @state_variables.each { |state_variable|
241
+ service_state_table.append state_variable.to_xml
242
+ }
243
+ return scpd.to_s
244
+
245
+ end
246
+ end
247
+
248
+ def UPnPScpd.to_xml_doc(scpd)
249
+ return '<?xml version="1.0" encoding="UTF-8"?>' + "\n#{scpd.to_xml}"
250
+ end
251
+
252
+ def UPnPScpd.read(xml)
253
+ scpd = UPnPScpd.new
254
+ doc = Nokogiri::XML(xml)
255
+ doc.root.elements.each do |elem|
256
+ case elem.name
257
+ when 'specVersion'
258
+ scpd.spec_version = [1,0]
259
+ when 'actionList'
260
+ elem.elements.each { |action_node|
261
+ if action_node.name == 'action'
262
+ action = UPnPAction.read_xml_node(action_node)
263
+ scpd.actions << action
264
+ end
265
+ }
266
+ when 'serviceStateTable'
267
+ elem.elements.each { |state_variable_node|
268
+ if state_variable_node.name == 'stateVariable'
269
+ state_variable = UPnPStateVariable.read_xml_node(state_variable_node)
270
+ scpd.state_variables << state_variable
271
+ end
272
+ }
273
+ end
274
+ end
275
+ return scpd
276
+ end
277
+
278
+
279
+ class UPnPAction < UPnPModel
280
+ def initialize
281
+ @arguments = []
282
+ end
283
+
284
+ attr_accessor :name
285
+ attr_accessor :arguments
286
+
287
+ def get_argument(name)
288
+ @arguments.each { |argument|
289
+ if argument.name == name
290
+ return argument
291
+ end
292
+ }
293
+ nil
294
+ end
295
+
296
+ def in_arguments
297
+ @arguments.select { |argument| argument.direction == 'in' }
298
+ end
299
+
300
+ def out_arguments
301
+ @arguments.select { |argument| argument.direction == 'out' }
302
+ end
303
+
304
+ def to_s
305
+ "UPnPAction -- #{@name}"
306
+ end
307
+
308
+ def to_xml
309
+ action = XmlTag.new 'action'
310
+
311
+ prop = action.append XmlTag.new 'name'
312
+ prop.append XmlText.new @name
313
+
314
+ argument_list = action.append XmlTag.new 'argumentList'
315
+ @arguments.each { |argument|
316
+ argument_list.append argument.to_xml
317
+ }
318
+
319
+ return action.to_s
320
+ end
321
+ end
322
+
323
+ def UPnPAction.read_xml_node(node)
324
+ action = UPnPAction.new
325
+ node.elements.each do |elem|
326
+ if elem.name == 'name'
327
+ action.name = elem.text
328
+ elsif elem.name == 'argumentList'
329
+ elem.elements.each { |argument_node|
330
+ if argument_node.name == 'argument'
331
+ action.arguments << UPnPActionArgument.read_xml_node(argument_node)
332
+ end
333
+ }
334
+ end
335
+ end
336
+ return action
337
+ end
338
+
339
+
340
+ class UPnPActionArgument < UPnPModel
341
+ attr_accessor :name, :direction, :related_state_variable
342
+
343
+ def to_s
344
+ "UPnPActionArgument -- #{@name} (direction: '#{@direction}' related state variable: '#{@related_state_variable}')"
345
+ end
346
+
347
+ def to_xml
348
+ argument = XmlTag.new 'argument'
349
+
350
+ prop = argument.append XmlTag.new 'name'
351
+ prop.append XmlText.new @name
352
+
353
+ prop = argument.append XmlTag.new 'direction'
354
+ prop.append XmlText.new @direction
355
+
356
+ prop = argument.append XmlTag.new 'relatedStateVariable'
357
+ prop.append XmlText.new @related_state_variable
358
+
359
+ return argument.to_s
360
+ end
361
+ end
362
+
363
+ def UPnPActionArgument.read_xml_node(node)
364
+ argument = UPnPActionArgument.new
365
+ node.elements.each do |elem|
366
+ case elem.name
367
+ when 'name'
368
+ argument.name = elem.text
369
+ when 'direction'
370
+ argument.direction = elem.text
371
+ when 'relatedStateVariable'
372
+ argument.related_state_variable = elem.text
373
+ end
374
+ end
375
+ return argument
376
+ end
377
+
378
+
379
+ class UPnPStateVariable < UPnPModel
380
+ attr_accessor :name, :data_type, :send_events, :multicast
381
+
382
+ def to_s
383
+ "State Variable -- #{@name} (data type: '#{@data_type}' send events? '#{@send_events}' multicast? '#{@multicast}')"
384
+ end
385
+
386
+ def to_xml
387
+ state_variable = XmlTag.new 'stateVariable'
388
+ if @send_events != nil
389
+ state_variable.attributes['sendEvents'] = @send_events
390
+ end
391
+
392
+ if @multicast != nil
393
+ state_variable.attributes['multicast'] = @multicast
394
+ end
395
+
396
+ prop = state_variable.append XmlTag.new 'name'
397
+ prop.append XmlText.new @name
398
+
399
+ prop = state_variable.append XmlTag.new 'dataType'
400
+ prop.append XmlText.new @data_type
401
+
402
+ return state_variable.to_s
403
+ end
404
+ end
405
+
406
+ def UPnPStateVariable.read_xml_node(node)
407
+ state_variable = UPnPStateVariable.new
408
+ state_variable.send_events = node.attribute('sendEvents')
409
+ state_variable.multicast = node.attribute('multicast')
410
+ node.elements.each do |elem|
411
+ case elem.name
412
+ when 'name'
413
+ state_variable.name = elem.text
414
+ when 'dataType'
415
+ state_variable.data_type = elem.text
416
+ end
417
+ end
418
+ return state_variable
419
+ end