openplacos 0.0.8 → 0.0.9
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/VERSION +1 -1
- data/lib/openplacos.rb +1 -2
- data/lib/openplacos/libclient.rb +364 -127
- data/lib/openplacos/libcomponent.rb +550 -0
- data/lib/openplacos/template_client.rb +17 -0
- data/lib/openplacos/widget/Analog.rb +63 -0
- data/lib/openplacos/widget/Digital.rb +55 -0
- data/lib/openplacos/widget/modules.rb +19 -0
- data/openplacos.gemspec +5 -3
- metadata +75 -58
- data/lib/openplacos/libdriver.rb +0 -129
- data/lib/openplacos/libplugin.rb +0 -76
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.0.
|
1
|
+
0.0.9
|
data/lib/openplacos.rb
CHANGED
data/lib/openplacos/libclient.rb
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
1
2
|
# This file is part of Openplacos.
|
2
3
|
#
|
3
4
|
# Openplacos is free software: you can redistribute it and/or modify
|
@@ -13,177 +14,413 @@
|
|
13
14
|
# You should have received a copy of the GNU General Public License
|
14
15
|
# along with Openplacos. If not, see <http://www.gnu.org/licenses/>.
|
15
16
|
|
17
|
+
require 'socket'
|
18
|
+
require 'net/http'
|
19
|
+
require 'json'
|
20
|
+
require 'oauth2'
|
21
|
+
require 'yaml'
|
22
|
+
|
23
|
+
require File.dirname(__FILE__) + "/widget/modules.rb"
|
24
|
+
|
25
|
+
|
26
|
+
module OAuth2
|
27
|
+
class AccessToken
|
28
|
+
def to_hash()
|
29
|
+
token_params = @params
|
30
|
+
token_params['access_token'] = @token
|
31
|
+
token_params.merge!(@options)
|
32
|
+
token_params
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
36
|
+
end
|
16
37
|
|
17
|
-
ENV["DBUS_THREADED_ACCESS"] = "1" #activate threaded dbus
|
18
|
-
require 'dbus-openplacos'
|
19
38
|
|
20
39
|
module Openplacos
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
40
|
+
|
41
|
+
module String
|
42
|
+
|
43
|
+
# this method extend string module.
|
44
|
+
# this module find out a module instance from it's name (=string)
|
45
|
+
# ( "Openplacos::Analog" (=string) => Oenplacos::Analog (= module)
|
46
|
+
def get_max_const
|
47
|
+
array = self.split("::")
|
48
|
+
out = Kernel
|
49
|
+
array.each { |mod|
|
50
|
+
if out.const_defined?(mod)
|
51
|
+
out = out.const_get(mod)
|
52
|
+
else
|
53
|
+
return out
|
54
|
+
end
|
55
|
+
}
|
56
|
+
return out #Should never be here
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
module Connection
|
61
|
+
|
62
|
+
# register the client (automatic way)
|
63
|
+
def register( )
|
64
|
+
postData = Net::HTTP.post_form(URI.parse("#{@url}/oauth/apps"),
|
65
|
+
{ 'name' =>"#{@name}",
|
66
|
+
'redirect_uri'=>@redirect_uri,
|
67
|
+
'format' =>'json'
|
68
|
+
}
|
69
|
+
)
|
70
|
+
if postData.code == "200" # Check the status code
|
71
|
+
client_param = JSON.parse( postData.body)
|
27
72
|
else
|
28
|
-
|
73
|
+
puts "error code"
|
74
|
+
exit 0
|
29
75
|
end
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
76
|
+
|
77
|
+
@client_id = client_param["client_id"]
|
78
|
+
@client_secret = client_param["client_secret"]
|
79
|
+
end
|
80
|
+
|
81
|
+
def create_client
|
82
|
+
@client = OAuth2::Client.new(@client_id,
|
83
|
+
@client_secret,
|
84
|
+
{:site => "#{@url}",
|
85
|
+
:token_url => '/oauth/authorize'
|
86
|
+
}
|
87
|
+
)
|
88
|
+
end
|
89
|
+
|
90
|
+
def get_grant_url(type_)
|
91
|
+
@client.auth_code.authorize_url(:redirect_uri => @redirect_uri, :scope => @scope.join(" "))
|
92
|
+
end
|
93
|
+
|
94
|
+
# save config with token, client_id and secret into a userspace directory
|
95
|
+
# needed to not regrant client every connection
|
96
|
+
def save_config
|
97
|
+
@token_params[@url] = @token ? @token.to_hash : {}
|
98
|
+
@token_params[@url][:client_id] = @client_id
|
99
|
+
@token_params[@url][:client_secret] = @client_secret
|
100
|
+
|
101
|
+
File.open(@file_config, 'w') do |out|
|
102
|
+
YAML::dump( @token_params, out )
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
# restore config
|
107
|
+
def load_config
|
108
|
+
if File.exists?(@file_config)
|
109
|
+
@token_params = YAML::load(File.read(@file_config))
|
110
|
+
if @token_params[@url]
|
111
|
+
@client_id = @token_params[@url][:client_id]
|
112
|
+
@client_secret = @token_params[@url][:client_secret]
|
113
|
+
end
|
45
114
|
else
|
46
|
-
|
47
|
-
Process.exit 1
|
115
|
+
@token_params = Hash.new
|
48
116
|
end
|
117
|
+
end
|
118
|
+
|
119
|
+
def recreate_token
|
120
|
+
@token = OAuth2::AccessToken.from_hash(@client, @token_params[@url])
|
121
|
+
end
|
122
|
+
|
123
|
+
end
|
124
|
+
|
125
|
+
class Connection_password
|
126
|
+
include Connection
|
127
|
+
attr_reader :token
|
128
|
+
def initialize(url_, name_, scope_, id_, port_, username_, password_)
|
129
|
+
@url = url_
|
130
|
+
@name = name_
|
131
|
+
@scope = scope_
|
132
|
+
@id = id_
|
133
|
+
@redirect_uri = "http://0.0.0.0:#{port_}"
|
134
|
+
@port = port_
|
135
|
+
@username = username_
|
136
|
+
@password = password_
|
49
137
|
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
138
|
+
|
139
|
+
dir_config = "#{ENV['HOME']}/.openplacos"
|
140
|
+
if !Dir.exists?(dir_config)
|
141
|
+
Dir.mkdir(dir_config)
|
142
|
+
end
|
143
|
+
|
144
|
+
@file_config = "#{dir_config}/#{@name}-#{id_}.yaml"
|
145
|
+
|
146
|
+
load_config
|
147
|
+
if @token_params[@url].nil? #create -- first time
|
148
|
+
register
|
149
|
+
create_client
|
150
|
+
save_config
|
151
|
+
else # persistant mode
|
152
|
+
create_client
|
153
|
+
end
|
154
|
+
get_token
|
155
|
+
|
67
156
|
end
|
68
157
|
|
69
|
-
|
70
|
-
cfg = Hash.new
|
71
|
-
objects.each_pair{ |key, obj|
|
72
|
-
if not(obj["org.openplacos.server.config"]==nil)
|
73
|
-
cfg[key] = obj["org.openplacos.server.config"].getConfig[0]
|
74
|
-
end
|
75
|
-
}
|
76
|
-
cfg
|
77
|
-
end
|
158
|
+
private
|
78
159
|
|
79
|
-
def
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
sensors
|
160
|
+
def get_token
|
161
|
+
begin
|
162
|
+
@token = @client.password.get_token(@username, @password, {:redirect_uri => @redirect_uri},{:mode=>:header, :header_format=>"OAuth %s", :param_name=>"oauth_token"})
|
163
|
+
rescue => e
|
164
|
+
puts e
|
165
|
+
retry
|
166
|
+
end
|
87
167
|
end
|
88
168
|
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
169
|
+
end
|
170
|
+
|
171
|
+
class Connection_auth_code
|
172
|
+
include Connection
|
173
|
+
attr_reader :token
|
174
|
+
|
175
|
+
# Open a connection to openplacos server
|
176
|
+
# Please give:
|
177
|
+
# * opos url
|
178
|
+
# * an application name that identify the client oath2 talking
|
179
|
+
# * a scope, typically ["read", "write", "user"]
|
180
|
+
# * an optionnal id, to manage several clients
|
181
|
+
# * port of openplacos server
|
182
|
+
def initialize(url_, name_, scope_, id_, port_)
|
183
|
+
@url = url_
|
184
|
+
@name = name_
|
185
|
+
@scope = scope_
|
186
|
+
@redirect_uri = "http://0.0.0.0:#{port_}"
|
187
|
+
@port = port_
|
188
|
+
|
189
|
+
dir_config = "#{ENV['HOME']}/.openplacos"
|
190
|
+
if !Dir.exists?(dir_config)
|
191
|
+
Dir.mkdir(dir_config)
|
94
192
|
end
|
95
193
|
|
96
|
-
#
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
194
|
+
# config saved to avoir re-grant at each connection
|
195
|
+
@file_config = "#{dir_config}/#{@name}-#{id_}.yaml"
|
196
|
+
|
197
|
+
load_config
|
198
|
+
if @token_params[@url].nil? #get token -- first time
|
199
|
+
register
|
200
|
+
create_client
|
201
|
+
grant
|
202
|
+
get_token
|
203
|
+
save_config
|
204
|
+
else # persistant mode
|
205
|
+
create_client
|
206
|
+
recreate_token
|
101
207
|
end
|
102
208
|
end
|
103
209
|
|
104
|
-
|
105
|
-
|
106
|
-
|
210
|
+
private
|
211
|
+
# display a message indicating url for grant
|
212
|
+
# this method can be overloaded by client depending it's interface with user
|
213
|
+
def grant ()
|
214
|
+
go_to_url = get_grant_url("code")
|
215
|
+
|
216
|
+
puts "***************"
|
217
|
+
puts "Please open your web browser and got to :"
|
218
|
+
puts go_to_url
|
219
|
+
puts "***************"
|
220
|
+
|
221
|
+
@auth_code = get_auth_code
|
222
|
+
end
|
223
|
+
|
224
|
+
def get_auth_code()
|
225
|
+
# listen to get the auth code
|
226
|
+
server = TCPServer.new(@port)
|
227
|
+
re=/code=(.*)&scope/
|
228
|
+
authcode = nil
|
229
|
+
while (session = server.accept)
|
230
|
+
request = session.gets
|
231
|
+
authcode = re.match(request)
|
232
|
+
if !authcode.nil?
|
233
|
+
session.print "HTTP/1.1 200/OK\rContent-type: text/html\r\n\r\n"
|
234
|
+
session.puts "<h1>Auth successfull</h1>"
|
235
|
+
session.close
|
236
|
+
break
|
237
|
+
end
|
238
|
+
end
|
239
|
+
authcode[1]
|
240
|
+
end
|
241
|
+
|
242
|
+
def get_token()
|
243
|
+
begin
|
244
|
+
@token = @client.auth_code.get_token(@auth_code, {:redirect_uri => @redirect_uri},{:mode=>:header, :header_format=>"OAuth %s", :param_name=>"oauth_token"})
|
245
|
+
rescue => e
|
246
|
+
puts e.description
|
247
|
+
Process.exit 42
|
107
248
|
end
|
108
|
-
key = get_sensors.index(sensor)
|
109
|
-
return @objects[key]['org.openplacos.server.regul']
|
110
249
|
end
|
250
|
+
|
251
|
+
end
|
111
252
|
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
253
|
+
|
254
|
+
class Connection_from_token
|
255
|
+
attr_accessor :token
|
256
|
+
def initialize(tok_)
|
257
|
+
@token = tok_
|
258
|
+
end
|
259
|
+
end
|
260
|
+
|
261
|
+
class Client
|
262
|
+
|
263
|
+
attr_accessor :config, :objects, :service, :sensors, :actuators, :rooms, :reguls, :initial_room
|
264
|
+
|
265
|
+
# Initialize a connection to server with OAuth2 in a automatic way
|
266
|
+
# Please provide url server, application name, permission needed for application
|
267
|
+
# Set connection_type to auth_code to use with oauth2 flow
|
268
|
+
# You can access to proxyfied objects with .objects attribute
|
269
|
+
# Please give:
|
270
|
+
# * opos url
|
271
|
+
# * an application name that identify the client oath2 talking
|
272
|
+
# * a scope, typically ["read", "write", "user"]
|
273
|
+
# * a connection_type, set it to "auth_code" to use oauth2 with classic flow (recommanded)
|
274
|
+
# or with "password" to use with password flow. Set to "inception" to pass a connection
|
275
|
+
# object through opt{:connection}
|
276
|
+
# * an optionnal id, to manage several clients
|
277
|
+
# * an optionnal option hash, in which you can specify openplacos port { :port => 5454 }
|
278
|
+
# * You can also pass a token object through opt[:token] that is an oauth2 object.
|
279
|
+
def initialize(url_, name_, scope_, connection_type_, id_ = "0", opt_={})
|
280
|
+
|
281
|
+
@objects = Hash.new
|
282
|
+
@connection_type = connection_type_
|
283
|
+
if opt_[:token].nil?
|
284
|
+
case @connection_type
|
285
|
+
when "auth_code" then
|
286
|
+
@connection = Connection_auth_code.new(url_, name_, scope_, id_, opt_[:port] || 2000)
|
287
|
+
when "password" then
|
288
|
+
@connection = Connection_password.new(url_, name_, scope_, id_, opt_[:port] || 2000, opt_[:username], opt_[:password])
|
289
|
+
else
|
290
|
+
raise "unknow grant type"
|
117
291
|
end
|
292
|
+
else
|
293
|
+
@connection = Connection_from_token.new(opt_[:token])
|
294
|
+
end
|
295
|
+
introspect
|
296
|
+
extend_objects
|
297
|
+
end
|
298
|
+
|
299
|
+
# Intropect the distant server
|
300
|
+
def introspect
|
301
|
+
@introspect = JSON.parse( @connection.token.get('/ressources').body)
|
302
|
+
@introspect.each { |obj|
|
303
|
+
@objects[obj["name"]] = ProxyObject.new(@connection, obj)
|
118
304
|
}
|
119
|
-
actuators
|
120
305
|
end
|
121
306
|
|
122
|
-
def
|
123
|
-
|
124
|
-
|
125
|
-
if
|
126
|
-
|
307
|
+
def get_iface_type(obj, det_)
|
308
|
+
a = Array.new
|
309
|
+
obj.interfaces.each { |iface|
|
310
|
+
if(iface.include?(det_))
|
311
|
+
a << iface
|
127
312
|
end
|
128
313
|
}
|
129
|
-
|
314
|
+
a
|
130
315
|
end
|
131
316
|
|
132
|
-
def
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
317
|
+
def extend_objects
|
318
|
+
@objects.each_pair{ |key, obj|
|
319
|
+
if (key != "/informations")
|
320
|
+
obj.interfaces.each { |iface|
|
321
|
+
extend_iface(iface, obj[iface])
|
322
|
+
}
|
138
323
|
end
|
139
|
-
|
140
|
-
return ack
|
324
|
+
}
|
141
325
|
end
|
142
326
|
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
327
|
+
# Extend an object to a ruby module according to iface_name_
|
328
|
+
# if iface_name_ is "org.openplacos.analog.order"
|
329
|
+
# => object will inherit Openplacos::Analog::Order
|
330
|
+
def extend_iface(iface_name_,obj_ )
|
331
|
+
mod = "Openplacos::"+ construct_module_name(iface_name_)
|
332
|
+
mod.extend(Openplacos::String)
|
333
|
+
obj_.extend(mod.get_max_const)
|
334
|
+
end
|
335
|
+
|
336
|
+
# transform iface_name to a module name that obj will inherit
|
337
|
+
def construct_module_name(iface_name_)
|
338
|
+
iface_heritage = iface_name_.sub(/org.openplacos./, '').split('.')
|
339
|
+
iface_heritage.each { |s|
|
340
|
+
s.capitalize!
|
148
341
|
}
|
149
|
-
|
342
|
+
iface_heritage.join('::')
|
150
343
|
end
|
151
344
|
|
152
|
-
|
153
|
-
|
345
|
+
# return the user name
|
346
|
+
def me
|
347
|
+
JSON.parse(@connection.token.get("/me").body)["username"]
|
154
348
|
end
|
155
|
-
|
349
|
+
|
156
350
|
end
|
157
351
|
|
158
|
-
class Room
|
159
|
-
attr_accessor :father, :childs, :path, :objects
|
160
352
|
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
353
|
+
class ProxyObject
|
354
|
+
|
355
|
+
attr_reader :path
|
356
|
+
|
357
|
+
# Object abstraction of a ressources
|
358
|
+
# Contructed from instrospect
|
359
|
+
# Has interfaces
|
360
|
+
def initialize(connection_, introspect_)
|
361
|
+
@path = introspect_["name"]
|
362
|
+
@interfaces = Hash.new
|
363
|
+
introspect_["interfaces"].each_pair { |name, methods|
|
364
|
+
@interfaces[name]= ProxyObjectInterface.new(connection_, self, name, methods)
|
365
|
+
}
|
366
|
+
end
|
367
|
+
|
368
|
+
# Returns the interfaces of the object.
|
369
|
+
def interfaces
|
370
|
+
@interfaces.keys
|
371
|
+
end
|
167
372
|
|
168
|
-
|
169
|
-
|
170
|
-
@
|
171
|
-
return children
|
373
|
+
# Retrieves an interface of the proxy object (ProxyObjectInterface instance).
|
374
|
+
def [](intfname)
|
375
|
+
@interfaces[intfname]
|
172
376
|
end
|
173
377
|
|
174
|
-
|
175
|
-
|
378
|
+
# Maps the given interface name _intfname_ to the given interface _intf.
|
379
|
+
def []=(intfname, intf)
|
380
|
+
@interfaces[intfname] = intf
|
381
|
+
end
|
382
|
+
|
383
|
+
def has_iface?(iface_)
|
384
|
+
return interfaces.include?(iface_)
|
176
385
|
end
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
386
|
+
|
387
|
+
end
|
388
|
+
|
389
|
+
class ProxyObjectInterface
|
390
|
+
|
391
|
+
# Interface abstraction
|
392
|
+
# contruct from introspect
|
393
|
+
def initialize(connection_, proxyobj_, name_, methods_)
|
394
|
+
@connection = connection_
|
395
|
+
@proxyobject = proxyobj_
|
396
|
+
@name = name_
|
397
|
+
@methods = Hash.new
|
398
|
+
methods_.each { |meth|
|
399
|
+
@methods[meth] = define_method(meth)
|
183
400
|
}
|
184
|
-
return hash
|
185
401
|
end
|
186
|
-
|
402
|
+
|
403
|
+
# Define a proxyfied method from its name
|
404
|
+
def define_method(name_)
|
405
|
+
if name_=="read"
|
406
|
+
methdef = <<-eos
|
407
|
+
def read(option_ = {})
|
408
|
+
res = JSON.parse(@connection.token.get('/ressources/#{@proxyobject.path}', :params => {'iface'=> '#{@name}', 'options' => (option_).to_json}).body)
|
409
|
+
res["value"]
|
410
|
+
end
|
411
|
+
eos
|
412
|
+
end
|
413
|
+
if name_=="write"
|
414
|
+
methdef = <<-eos
|
415
|
+
def write(value_,option_ = {})
|
416
|
+
res = JSON.parse(@connection.token.post('/ressources/#{@proxyobject.path}', :params => {'iface'=> '#{@name}', 'value' => [value_].to_json, 'options' => (option_).to_json}).body)
|
417
|
+
res["status"]
|
418
|
+
end
|
419
|
+
eos
|
420
|
+
end
|
421
|
+
|
422
|
+
instance_eval( methdef )
|
423
|
+
end
|
187
424
|
end
|
188
|
-
|
425
|
+
|
189
426
|
end
|