savant-scenes 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 +7 -0
- data/bin/scenes +18 -0
- data/bin/src/SceneManager.rb +444 -0
- data/bin/src/glade/SceneManager.glade +429 -0
- metadata +147 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 9143eeadd43302951c43db507ed5340b094970ee
|
4
|
+
data.tar.gz: 1ca8e91f8272abb8e11b9663d0c7e7f647d63b3a
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 1761df1a223088982ebca964a65875b3e055c967c2d28de0afca2a116f4aa369fa12f460e72c1a0a5f753a295588db33b110e2b941ab321a510844d6b72d296e
|
7
|
+
data.tar.gz: d86688aa22863e33de2248cb2a8013989900ced5ef20c88cef5ccc3d5cbaf29f7af0af4629068343a63a4e80f03c3ebd171c6e310f384d7a3e95533bf53bddb5
|
data/bin/scenes
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
#!/usr/bin/ruby
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require "vrlib"
|
5
|
+
require 'net/http'
|
6
|
+
require 'uri'
|
7
|
+
require 'net/scp'
|
8
|
+
require 'socket'
|
9
|
+
require 'timeout'
|
10
|
+
require 'ipaddress'
|
11
|
+
require 'sqlite3'
|
12
|
+
|
13
|
+
# from require_all gem:
|
14
|
+
require_rel 'src/'
|
15
|
+
|
16
|
+
|
17
|
+
SceneManager.new.show_glade()
|
18
|
+
|
@@ -0,0 +1,444 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'net/http'
|
3
|
+
require 'net/scp'
|
4
|
+
require 'uri'
|
5
|
+
require 'socket'
|
6
|
+
require 'timeout'
|
7
|
+
require 'ipaddress'
|
8
|
+
require 'sqlite3'
|
9
|
+
|
10
|
+
|
11
|
+
class SceneManager
|
12
|
+
|
13
|
+
attr_accessor :ftv
|
14
|
+
include GladeGUI
|
15
|
+
|
16
|
+
def initialize
|
17
|
+
#Initialize the Scenes List view
|
18
|
+
@listviewSavantScenes = VR::ListView.new(:modify => TrueClass, :scene => String, :user => String, :id => String, :image => String, :isGlobal => String, :isActive => String, :isScheduled => String, :hasCustomImage => String, :userSceneOrder => String, :tags => String)
|
19
|
+
@listviewSavantScenes.col_sortable(true)
|
20
|
+
@listviewSavantScenes.col_sort_column_id(:scene => @listviewSavantScenes.id(:scene), :user => @listviewSavantScenes.id(:user))
|
21
|
+
@listviewSavantScenes.col_title(:modify => "", :scene => "Scene Name", :tags => "Zones Affected", :isGlobal => "Global", :isActive => "Active", :isScheduled => "Scheduled", :hasCustomImage => "Custom Image", :userSceneOrder => "User Order")
|
22
|
+
@listviewSavantScenes.col_xalign(:isGlobal => 0.5, :isActive => 0.5, :isScheduled => 0.5, :hasCustomImage => 0.5, :userSceneOrder => 0.5)
|
23
|
+
@listviewSavantScenes.col_width(:tags => 100, :image => 100)
|
24
|
+
@listviewSavantScenes.ren_editable(:scene => true, :isActive => true, :user => true, :id => true)
|
25
|
+
|
26
|
+
row = @listviewSavantScenes.add_row
|
27
|
+
row[:scene] = ""
|
28
|
+
@listviewSavantScenes.visible = true
|
29
|
+
|
30
|
+
#Validate user name change to email address (checks for text before and after @ symbol, as well as 3 chars after the last ".""
|
31
|
+
@listviewSavantScenes.renderer(:user).validate_block = Proc.new { |text, model_sym, row, view|
|
32
|
+
x = text.length
|
33
|
+
suffix = text[x-4..x]
|
34
|
+
if text.include?("@") && text.include?(".") && text.split(/[@,.]/).length == 3 && suffix.chr == "."
|
35
|
+
true
|
36
|
+
else
|
37
|
+
false
|
38
|
+
alert("Please enter a valid email address.", :parent => self)
|
39
|
+
end
|
40
|
+
text.include?("@") && text.include?(".") && text.split(/[@,.]/).length == 3 && suffix.chr == "."
|
41
|
+
}
|
42
|
+
|
43
|
+
#Validate id change to allow it to be copied for Bluepritn entry, but not actually changed
|
44
|
+
@listviewSavantScenes.renderer(:id).validate_block = Proc.new { |text, model_sym, row, view|
|
45
|
+
text == "this is never going to validate"
|
46
|
+
}
|
47
|
+
|
48
|
+
#Validate ACtively Scheduled to only be a 0 or 1
|
49
|
+
@listviewSavantScenes.renderer(:isActive).validate_block = Proc.new { |text, model_sym, row, view|
|
50
|
+
text == "0" || text == "1"
|
51
|
+
}
|
52
|
+
|
53
|
+
# Capture user name change, save to SQL, and transfer SQL to host
|
54
|
+
# @listviewSavantScenes.renderer(:user).edited_callback = Proc.new { |model_sym, row, view |
|
55
|
+
# alert "User email changed to : " + row[:user].to_s
|
56
|
+
# }
|
57
|
+
#
|
58
|
+
# Capture scene name change, save to SQL, and transfer SQL to host
|
59
|
+
# @listviewSavantScenes.renderer(:scene).edited_callback = Proc.new { |model_sym, row, view |
|
60
|
+
# alert "Scene name changed to : " + row[:scene].to_s
|
61
|
+
# }
|
62
|
+
#
|
63
|
+
# @listviewSavantScenes.renderer(:isActive).edited_callback = Proc.new { |model_sym, row, view |
|
64
|
+
# alert "Scene is scheduled changed to : " + row[:isScheduled].to_s
|
65
|
+
# }
|
66
|
+
end
|
67
|
+
|
68
|
+
#####Set variables before windows load#####
|
69
|
+
def before_show()
|
70
|
+
@savantURL = "192.168.77.40"
|
71
|
+
@login = 'RPM'
|
72
|
+
@password = 'RPM'
|
73
|
+
|
74
|
+
@ftv = VR::FileTreeView.new(Dir.home, File.join(File.dirname(__FILE__), "/../img"), glob = "*", validate_block = nil)
|
75
|
+
@builder["scrolledwindow_BackupRestore"].add(@ftv)
|
76
|
+
@ftv.set_show_expanders(true)
|
77
|
+
end
|
78
|
+
|
79
|
+
#####Check to see if Racepoint and SCLI bridge ports are open on host#####
|
80
|
+
def is_port_open?(ip, port)
|
81
|
+
if IPAddress.valid? ip
|
82
|
+
begin
|
83
|
+
Timeout::timeout(1) do
|
84
|
+
begin
|
85
|
+
s = TCPSocket.new(ip, port)
|
86
|
+
s.close
|
87
|
+
return true
|
88
|
+
rescue StandardError
|
89
|
+
return false
|
90
|
+
end
|
91
|
+
end
|
92
|
+
rescue Timeout::Error
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
#####Checks to see if Savant version is greater than 8.3 to support scene features.
|
98
|
+
def savantVersion?(ip)
|
99
|
+
begin
|
100
|
+
@builder["labelHostInfo"].label = ""
|
101
|
+
Net::SSH.start(ip, @login, :password => @password, :paranoid => false, :timeout => 3) do |ssh|
|
102
|
+
#Check host type and update sclibridge location for reference, though only Pro Host is supported
|
103
|
+
ssh.open_channel do |ch, success|
|
104
|
+
ch.exec("uname")
|
105
|
+
ch.on_data do |ch, data|
|
106
|
+
if data.to_s.include? 'Darwin'
|
107
|
+
@scliBridge = "~/Applications/RacePointMedia/sclibridge "
|
108
|
+
@dis = "/Users/RPM/Library/Application Support/RacePointMedia/statusfiles/dis/dashboard.sqlite"
|
109
|
+
else
|
110
|
+
@scliBridge = "/usr/local/bin/sclibridge "
|
111
|
+
@dis = "GNUstep/Library/ApplicationSupport/RacePointMedia/statusfiles/dis/dashboard.sqlite"
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
115
|
+
ssh.loop
|
116
|
+
#parse the rpmSystemInfo.plist form the pro host to get host name
|
117
|
+
if @scliBridge == "~/Applications/RacePointMedia/sclibridge "
|
118
|
+
ssh.open_channel do |ch, success|
|
119
|
+
|
120
|
+
ch.exec("plutil -p 'library/application support/racepointmedia/userconfig.rpmconfig/rpmSystemInfo.plist'")
|
121
|
+
ch.on_data do |ch, data|
|
122
|
+
if data.to_s.include? 'module'
|
123
|
+
x = data.split("\"RPMSystemName\" => ")
|
124
|
+
x = x[1].split("\n")
|
125
|
+
@savantHostName = x[0][1..-2]
|
126
|
+
@str = @scliBridge + "readstate \"" + @savantHostName + ".SoftwareVersion\""
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
130
|
+
ssh.loop
|
131
|
+
#parse the rpmSystemInfo.plist from the smart host to get host name
|
132
|
+
elsif @scliBridge == "/usr/local/bin/sclibridge "
|
133
|
+
ssh.open_channel do |ch, success|
|
134
|
+
ch.exec("strings 'GNUstep/Library/ApplicationSupport/RacePointMedia/userConfig.rpmConfig/rpmSystemInfo.plist'")
|
135
|
+
ch.on_data do |ch, data|
|
136
|
+
if data.to_s.include? 'DOCTYPE'
|
137
|
+
x = data.split("RPMUserDefinedName</key><string>")
|
138
|
+
y = x[1].split("</string>")
|
139
|
+
x = x[1].split("VERSION</key><integer>")
|
140
|
+
x = x[1].split("</integer>")
|
141
|
+
alert("Instant host found at specified IP address. Instant hosts are not supported (even by Savant!)", :parent => self)
|
142
|
+
return false
|
143
|
+
@instantHost = true
|
144
|
+
elsif data.to_s.include? 'module'
|
145
|
+
x = data.split('module')
|
146
|
+
x = x[1].split("\n")
|
147
|
+
@savantHostName = x[0][1..-1]
|
148
|
+
@str = @scliBridge + "readstate \"" + @savantHostName + ".SoftwareVersion\""
|
149
|
+
end
|
150
|
+
end
|
151
|
+
end
|
152
|
+
ssh.loop
|
153
|
+
end
|
154
|
+
#Use host name to query SoftwareVersion state from host
|
155
|
+
ssh.open_channel do |ch, success|
|
156
|
+
ch.exec(@str)
|
157
|
+
ch.on_data do |ch, data|
|
158
|
+
@savantHostVersion = data
|
159
|
+
end
|
160
|
+
end
|
161
|
+
ssh.loop
|
162
|
+
end
|
163
|
+
@builder["labelHostInfo"].text = @savantHostName + " Version : " + @savantHostVersion
|
164
|
+
|
165
|
+
#return true if version is 8.3 or later
|
166
|
+
if @savantHostVersion.to_r > 8.2 && !@savantHostVersion.empty?
|
167
|
+
return true
|
168
|
+
else
|
169
|
+
return false
|
170
|
+
end
|
171
|
+
|
172
|
+
rescue Net::SSH::ConnectionTimeout, SocketError, Errno::EHOSTUNREACH, Errno::ECONNREFUSED, Errno::EHOSTDOWN
|
173
|
+
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
#Enter key scans for hsot from IP address field
|
178
|
+
def savantURL__key_press_event(view, evt)
|
179
|
+
return unless evt.keyval == 65293
|
180
|
+
buttonScanHost__clicked()
|
181
|
+
end
|
182
|
+
|
183
|
+
#Rebuild listview window
|
184
|
+
def repaint
|
185
|
+
@builder["listviewSavantScenes"].hide
|
186
|
+
@builder["window1"].show_all
|
187
|
+
unless savantVersion?(@savantURL)
|
188
|
+
@builder["buttonActivateScene"].hide
|
189
|
+
end
|
190
|
+
end
|
191
|
+
|
192
|
+
#Scan the specified IP for scenes
|
193
|
+
def buttonScanHost__clicked(*argv)
|
194
|
+
@savantURL = @builder["savantURL"].text
|
195
|
+
if is_port_open?(@savantURL,"11263") || is_port_open?(@savantURL,"48664") || is_port_open?(@savantURL,"1124")
|
196
|
+
unless savantVersion?(@savantURL)
|
197
|
+
@builder["buttonActivateScene"].hide
|
198
|
+
end
|
199
|
+
begin
|
200
|
+
@listviewSavantScenes.model.clear
|
201
|
+
Net::SSH.start(@savantURL, @login, :password => @password, :paranoid => false, :timeout => 3) do |ssh|
|
202
|
+
# test to see if remote files exist and transfer them if they do
|
203
|
+
fileExists = ssh.exec!("[ -f #{@dis} ] && echo 'true' || echo 'false'")
|
204
|
+
unless fileExists == 'false'
|
205
|
+
ssh.scp.download!(@dis, '/tmp/')
|
206
|
+
#read from sqlite database and add to listview
|
207
|
+
@db = SQLite3::Database.open('/tmp/dashboard.sqlite')
|
208
|
+
@db.execute( "select * from scenes" ) do |rowData|
|
209
|
+
row = @listviewSavantScenes.add_row
|
210
|
+
row[:modify] = false
|
211
|
+
row[:id] = rowData.shift
|
212
|
+
row[:scene] = rowData.shift
|
213
|
+
row[:image] = rowData.shift
|
214
|
+
row[:tags] = rowData.shift
|
215
|
+
row[:isGlobal] = rowData.shift.to_s
|
216
|
+
row[:isActive] = rowData.shift.to_s
|
217
|
+
row[:isScheduled] = rowData.shift.to_s
|
218
|
+
row[:hasCustomImage] = rowData.shift.to_s
|
219
|
+
|
220
|
+
stm = @db.prepare( "select * from sceneusermap where sceneIdentifier=?" )
|
221
|
+
stm.bind_param 1, row[:id]
|
222
|
+
rs = stm.execute.next
|
223
|
+
row[:user] = rs[2]
|
224
|
+
row[:userSceneOrder] = rs[3].to_s
|
225
|
+
stm.close
|
226
|
+
end
|
227
|
+
repaint
|
228
|
+
end
|
229
|
+
end
|
230
|
+
rescue Net::SSH::ConnectionTimeout, SocketError, Errno::EHOSTUNREACH, Errno::ECONNREFUSED, Errno::EHOSTDOWN
|
231
|
+
alert("Host not reached.", :parent => self)
|
232
|
+
end
|
233
|
+
else
|
234
|
+
alert("A Savant host was not found at the specified IP address.", :parent => self)
|
235
|
+
@listviewSavantScenes.model.clear
|
236
|
+
repaint
|
237
|
+
end
|
238
|
+
end
|
239
|
+
|
240
|
+
#Transfer database from tmp folder to remote host
|
241
|
+
def updateDatabase()
|
242
|
+
begin
|
243
|
+
Net::SSH.start(@savantURL, @login, :password => @password, :paranoid => false, :timeout => 3) do |ssh|
|
244
|
+
fileExists = ssh.exec!("[ -f #{@dis} ] && echo 'true' || echo 'false'")
|
245
|
+
unless fileExists == 'false'
|
246
|
+
ssh.scp.upload!('/tmp/dashboard.sqlite', @dis.chomp("dashboard.sqlite"))
|
247
|
+
alert("Please re-open the Savant Pro app to reflect changes.", :parent => self)
|
248
|
+
end
|
249
|
+
end
|
250
|
+
rescue Net::SSH::ConnectionTimeout, SocketError, Errno::EHOSTUNREACH, Errno::ECONNREFUSED, Errno::EHOSTDOWN
|
251
|
+
alert("Host not reached.", :parent => self)
|
252
|
+
end
|
253
|
+
end
|
254
|
+
|
255
|
+
# #
|
256
|
+
# SCENE ACTIONS #
|
257
|
+
# #
|
258
|
+
|
259
|
+
#Activate the selected scene
|
260
|
+
def buttonActivateScene__clicked(*argv)
|
261
|
+
get_glade_variables()
|
262
|
+
activateScenes = []
|
263
|
+
activateIDs = []
|
264
|
+
activateUsers = []
|
265
|
+
@listviewSavantScenes.each_row do |row|
|
266
|
+
if row[:modify]
|
267
|
+
@str = @scliBridge + "activateScene \'" + row[:scene] + "\' \'" + row[:id] + "\' \'" + row[:user] + "\'"
|
268
|
+
begin
|
269
|
+
Net::SSH.start(@savantURL, @login, :password => @password, :paranoid => false, :timeout => 3) do |ssh|
|
270
|
+
ssh.exec!(@str)
|
271
|
+
alert("Scene activated: #{row[:scene]}", :parent => self)
|
272
|
+
end
|
273
|
+
rescue Net::SSH::ConnectionTimeout, SocketError, Errno::EHOSTUNREACH, Errno::ECONNREFUSED, Errno::EHOSTDOWN
|
274
|
+
alert("Host not reached.", :parent => self)
|
275
|
+
end
|
276
|
+
end
|
277
|
+
end
|
278
|
+
end
|
279
|
+
|
280
|
+
#Edit the selected scenes
|
281
|
+
def buttonApplyEdits__clicked(*argv)
|
282
|
+
get_glade_variables()
|
283
|
+
editScenes = []
|
284
|
+
editIDs = []
|
285
|
+
editUsers = []
|
286
|
+
editActive = []
|
287
|
+
count = 0
|
288
|
+
@listviewSavantScenes.each_row do |row|
|
289
|
+
if row[:modify]
|
290
|
+
count += 1
|
291
|
+
editScenes << row[:scene]
|
292
|
+
editIDs << row[:id]
|
293
|
+
editUsers << row[:user]
|
294
|
+
editActive << row[:isActive]
|
295
|
+
end
|
296
|
+
end
|
297
|
+
#make sure a scene was selected, else alert the user
|
298
|
+
if count > 0
|
299
|
+
#confirm the edits should be made
|
300
|
+
strAlert = "Do you want to apply changes to the following scenes on the Savant host?\n\n" + editScenes.join("\n")
|
301
|
+
if alert(strAlert, :headline => "Edit Scenes?", :button_yes => "Apply Changes", :button_cancel => "Cancel", :parent => self)
|
302
|
+
count = 0
|
303
|
+
#edit the database with the updated info
|
304
|
+
editIDs.each do |id|
|
305
|
+
stm = @db.prepare( "update sceneusermap set user=? where sceneIdentifier=?" )
|
306
|
+
stm.bind_param 1, editUsers[count]
|
307
|
+
stm.bind_param 2, id
|
308
|
+
stm.execute
|
309
|
+
stm.close
|
310
|
+
stm = @db.prepare( "update scenes set name=?, isActive=? where identifier=?" )
|
311
|
+
stm.bind_param 1, editScenes[count]
|
312
|
+
stm.bind_param 2, editActive[count]
|
313
|
+
stm.bind_param 3, id
|
314
|
+
stm.execute
|
315
|
+
stm.close
|
316
|
+
|
317
|
+
count += 1
|
318
|
+
end
|
319
|
+
updateDatabase()
|
320
|
+
buttonScanHost__clicked()
|
321
|
+
end
|
322
|
+
else
|
323
|
+
alert("Please select at least 1 row to modify.", :parent => self)
|
324
|
+
end
|
325
|
+
end
|
326
|
+
|
327
|
+
#Delete the selected scenes
|
328
|
+
def buttonDeleteScene__clicked(*argv)
|
329
|
+
get_glade_variables()
|
330
|
+
deleteScenes = []
|
331
|
+
deleteIDs = []
|
332
|
+
count = 0
|
333
|
+
@listviewSavantScenes.each_row do |row|
|
334
|
+
if row[:modify]
|
335
|
+
count += 1
|
336
|
+
deleteScenes << row[:scene]
|
337
|
+
deleteIDs << row[:id]
|
338
|
+
end
|
339
|
+
end
|
340
|
+
#make sure a scene was selected, else alert the user
|
341
|
+
if count > 0
|
342
|
+
#confirm the edits should be made
|
343
|
+
strAlert = "Do you want to delete the following scenes from the Savant host?\n\n" + deleteScenes.join("\n")
|
344
|
+
if alert(strAlert, :headline => "Delete Scenes?", :button_yes => "Delete", :button_cancel => "Cancel", :parent => self)
|
345
|
+
#edit the database with the updated info
|
346
|
+
deleteIDs.each do |id|
|
347
|
+
stm = @db.prepare( "delete from sceneusermap where sceneIdentifier=?" )
|
348
|
+
stm.bind_param 1, id
|
349
|
+
stm.execute
|
350
|
+
stm.close
|
351
|
+
stm = @db.prepare( "delete from scenes where identifier=?" )
|
352
|
+
stm.bind_param 1, id
|
353
|
+
stm.execute
|
354
|
+
stm.close
|
355
|
+
stm = @db.prepare( "delete from scenedefinitions where sceneIdentifier=?" )
|
356
|
+
stm.bind_param 1, id
|
357
|
+
stm.execute
|
358
|
+
stm.close
|
359
|
+
end
|
360
|
+
updateDatabase()
|
361
|
+
buttonScanHost__clicked()
|
362
|
+
end
|
363
|
+
else
|
364
|
+
alert("Please select at least 1 row to modify.", :parent => self)
|
365
|
+
end
|
366
|
+
end
|
367
|
+
|
368
|
+
# #
|
369
|
+
# BACKUP AND RESTORE #
|
370
|
+
# #
|
371
|
+
|
372
|
+
#FileTreeView expand and collapse for file backup and restore
|
373
|
+
def ftv__row_activated(_self, path, col)
|
374
|
+
@ftv.expand_or_collapse_folder()
|
375
|
+
end
|
376
|
+
|
377
|
+
#Restore files from local filesystem to remote host
|
378
|
+
def buttonRestore__clicked(*a)
|
379
|
+
return unless row = @ftv.selected_rows.first
|
380
|
+
backupDir = row[:path]
|
381
|
+
#Get folder name if a file is selected.
|
382
|
+
if File.file?(backupDir)
|
383
|
+
backupDir = File.dirname(backupDir)
|
384
|
+
end
|
385
|
+
#Check that folder contains correct files
|
386
|
+
if File.file?(backupDir + "/dashboard.sqlite")
|
387
|
+
begin
|
388
|
+
Net::SSH.start(@savantURL, @login, :password => @password, :paranoid => false, :timeout => 3) do |ssh|
|
389
|
+
# test to see if remote folder exists, if so, transfer files and restart harbridge
|
390
|
+
dirExists = ssh.exec!("[ -d #{@dis.chomp("dashboard.sqlite")} ] && echo 'true' || echo 'false'")
|
391
|
+
if dirExists
|
392
|
+
ssh.scp.upload!(backupDir + "/dashboard.sqlite", @dis.chomp("dashboard.sqlite"))
|
393
|
+
alert("File has been restored from " + row[:path] + "\n\nPlease re-open the Savant Pro app to reflect changes.", :parent => self)
|
394
|
+
@builder["windowBackupRestore"].hide
|
395
|
+
buttonScanHost__clicked()
|
396
|
+
else
|
397
|
+
alert("Users/RPM/habridge/data folder not found on remote host.", :parent => self)
|
398
|
+
end
|
399
|
+
end
|
400
|
+
end
|
401
|
+
else
|
402
|
+
alert("Selected directory does not contain dashboard.sqlite.", :parent => self)
|
403
|
+
end
|
404
|
+
end
|
405
|
+
|
406
|
+
#Backup files from remote host to local filesystem
|
407
|
+
def buttonBackup__clicked(*a)
|
408
|
+
return unless row = @ftv.selected_rows.first
|
409
|
+
backupDir = row[:path]
|
410
|
+
#Get folder name if a file is selected.
|
411
|
+
if File.file?(backupDir)
|
412
|
+
backupDir = File.dirname(backupDir)
|
413
|
+
end
|
414
|
+
begin
|
415
|
+
Net::SSH.start(@savantURL, @login, :password => @password, :paranoid => false, :timeout => 3) do |ssh|
|
416
|
+
# test to see if remote files exist and transfer them if they do
|
417
|
+
fileExists = ssh.exec!("[ -f #{@dis} ] && echo 'true' || echo 'false'")
|
418
|
+
unless fileExists == 'false'
|
419
|
+
ssh.scp.download!(@dis, backupDir)
|
420
|
+
end
|
421
|
+
repaint
|
422
|
+
end
|
423
|
+
rescue Net::SSH::ConnectionTimeout, SocketError, Errno::EHOSTUNREACH, Errno::ECONNREFUSED, Errno::EHOSTDOWN
|
424
|
+
alert("Host not reached.", :parent => self)
|
425
|
+
end
|
426
|
+
alert("File backed up to " + row[:path], :parent => self)
|
427
|
+
@builder["windowBackupRestore"].hide
|
428
|
+
buttonScanHost__clicked()
|
429
|
+
|
430
|
+
end
|
431
|
+
|
432
|
+
def buttonBackupRestore__clicked(*argv)
|
433
|
+
@builder["windowBackupRestore"].show
|
434
|
+
end
|
435
|
+
|
436
|
+
def buttonBackupRestoreCancel__clicked(*argv)
|
437
|
+
@builder["windowBackupRestore"].hide
|
438
|
+
end
|
439
|
+
|
440
|
+
def buttonExit__clicked(*args)
|
441
|
+
@builder["window1"].destroy
|
442
|
+
end
|
443
|
+
|
444
|
+
end
|
@@ -0,0 +1,429 @@
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
2
|
+
<!-- Generated with glade 3.20.0 -->
|
3
|
+
<interface>
|
4
|
+
<requires lib="gtk+" version="3.0"/>
|
5
|
+
<object class="GtkWindow" id="window1">
|
6
|
+
<property name="can_focus">False</property>
|
7
|
+
<property name="has_focus">True</property>
|
8
|
+
<property name="hexpand">True</property>
|
9
|
+
<property name="vexpand">True</property>
|
10
|
+
<property name="window_position">center</property>
|
11
|
+
<property name="default_width">1200</property>
|
12
|
+
<child>
|
13
|
+
<object class="GtkGrid">
|
14
|
+
<property name="visible">True</property>
|
15
|
+
<property name="can_focus">False</property>
|
16
|
+
<property name="margin_left">12</property>
|
17
|
+
<property name="margin_right">12</property>
|
18
|
+
<property name="hexpand">True</property>
|
19
|
+
<property name="vexpand">False</property>
|
20
|
+
<child>
|
21
|
+
<object class="GtkLabel">
|
22
|
+
<property name="visible">True</property>
|
23
|
+
<property name="can_focus">False</property>
|
24
|
+
<property name="margin_top">12</property>
|
25
|
+
<property name="margin_bottom">12</property>
|
26
|
+
<property name="label" translatable="yes"><big><b>Savant Scene Manager</b></big></property>
|
27
|
+
<property name="use_markup">True</property>
|
28
|
+
</object>
|
29
|
+
<packing>
|
30
|
+
<property name="left_attach">0</property>
|
31
|
+
<property name="top_attach">0</property>
|
32
|
+
</packing>
|
33
|
+
</child>
|
34
|
+
<child>
|
35
|
+
<object class="GtkGrid">
|
36
|
+
<property name="visible">True</property>
|
37
|
+
<property name="can_focus">False</property>
|
38
|
+
<property name="margin_left">12</property>
|
39
|
+
<property name="margin_right">12</property>
|
40
|
+
<property name="column_homogeneous">True</property>
|
41
|
+
<child>
|
42
|
+
<object class="GtkButtonBox">
|
43
|
+
<property name="visible">True</property>
|
44
|
+
<property name="can_focus">False</property>
|
45
|
+
<property name="margin_right">12</property>
|
46
|
+
<property name="margin_top">12</property>
|
47
|
+
<property name="margin_bottom">12</property>
|
48
|
+
<property name="spacing">7</property>
|
49
|
+
<property name="layout_style">end</property>
|
50
|
+
<child>
|
51
|
+
<object class="GtkButton" id="buttonBackupRestore">
|
52
|
+
<property name="label" translatable="yes">Backup/Restore</property>
|
53
|
+
<property name="visible">True</property>
|
54
|
+
<property name="can_focus">True</property>
|
55
|
+
<property name="receives_default">True</property>
|
56
|
+
</object>
|
57
|
+
<packing>
|
58
|
+
<property name="expand">True</property>
|
59
|
+
<property name="fill">True</property>
|
60
|
+
<property name="position">1</property>
|
61
|
+
</packing>
|
62
|
+
</child>
|
63
|
+
<child>
|
64
|
+
<object class="GtkButton" id="buttonExit">
|
65
|
+
<property name="label" translatable="yes">Exit</property>
|
66
|
+
<property name="visible">True</property>
|
67
|
+
<property name="can_focus">True</property>
|
68
|
+
<property name="receives_default">True</property>
|
69
|
+
</object>
|
70
|
+
<packing>
|
71
|
+
<property name="expand">True</property>
|
72
|
+
<property name="fill">True</property>
|
73
|
+
<property name="position">2</property>
|
74
|
+
</packing>
|
75
|
+
</child>
|
76
|
+
</object>
|
77
|
+
<packing>
|
78
|
+
<property name="left_attach">1</property>
|
79
|
+
<property name="top_attach">0</property>
|
80
|
+
</packing>
|
81
|
+
</child>
|
82
|
+
<child>
|
83
|
+
<object class="GtkButtonBox">
|
84
|
+
<property name="visible">True</property>
|
85
|
+
<property name="can_focus">False</property>
|
86
|
+
<property name="margin_left">12</property>
|
87
|
+
<property name="spacing">9</property>
|
88
|
+
<property name="homogeneous">True</property>
|
89
|
+
<property name="layout_style">start</property>
|
90
|
+
<child>
|
91
|
+
<object class="GtkButton" id="buttonApplyEdits">
|
92
|
+
<property name="label" translatable="yes">Apply Edits</property>
|
93
|
+
<property name="visible">True</property>
|
94
|
+
<property name="can_focus">True</property>
|
95
|
+
<property name="receives_default">True</property>
|
96
|
+
</object>
|
97
|
+
<packing>
|
98
|
+
<property name="expand">True</property>
|
99
|
+
<property name="fill">True</property>
|
100
|
+
<property name="position">0</property>
|
101
|
+
</packing>
|
102
|
+
</child>
|
103
|
+
<child>
|
104
|
+
<object class="GtkButton" id="buttonDeleteScene">
|
105
|
+
<property name="label" translatable="yes">Delete Scene</property>
|
106
|
+
<property name="visible">True</property>
|
107
|
+
<property name="can_focus">True</property>
|
108
|
+
<property name="receives_default">True</property>
|
109
|
+
</object>
|
110
|
+
<packing>
|
111
|
+
<property name="expand">True</property>
|
112
|
+
<property name="fill">True</property>
|
113
|
+
<property name="position">1</property>
|
114
|
+
</packing>
|
115
|
+
</child>
|
116
|
+
<child>
|
117
|
+
<object class="GtkButton" id="buttonActivateScene">
|
118
|
+
<property name="label" translatable="yes">Activate Scene</property>
|
119
|
+
<property name="visible">True</property>
|
120
|
+
<property name="can_focus">True</property>
|
121
|
+
<property name="receives_default">True</property>
|
122
|
+
</object>
|
123
|
+
<packing>
|
124
|
+
<property name="expand">True</property>
|
125
|
+
<property name="fill">True</property>
|
126
|
+
<property name="position">2</property>
|
127
|
+
</packing>
|
128
|
+
</child>
|
129
|
+
</object>
|
130
|
+
<packing>
|
131
|
+
<property name="left_attach">0</property>
|
132
|
+
<property name="top_attach">0</property>
|
133
|
+
</packing>
|
134
|
+
</child>
|
135
|
+
</object>
|
136
|
+
<packing>
|
137
|
+
<property name="left_attach">0</property>
|
138
|
+
<property name="top_attach">3</property>
|
139
|
+
</packing>
|
140
|
+
</child>
|
141
|
+
<child>
|
142
|
+
<object class="GtkGrid">
|
143
|
+
<property name="visible">True</property>
|
144
|
+
<property name="can_focus">False</property>
|
145
|
+
<property name="margin_left">12</property>
|
146
|
+
<property name="margin_right">12</property>
|
147
|
+
<property name="hexpand">True</property>
|
148
|
+
<property name="vexpand">True</property>
|
149
|
+
<child>
|
150
|
+
<object class="GtkScrolledWindow" id="listviewSavantScenes">
|
151
|
+
<property name="visible">True</property>
|
152
|
+
<property name="can_focus">True</property>
|
153
|
+
<property name="margin_bottom">12</property>
|
154
|
+
<property name="hexpand">True</property>
|
155
|
+
<property name="vexpand">True</property>
|
156
|
+
<property name="hscrollbar_policy">always</property>
|
157
|
+
<property name="vscrollbar_policy">always</property>
|
158
|
+
<property name="shadow_type">in</property>
|
159
|
+
<property name="min_content_height">200</property>
|
160
|
+
<property name="max_content_height">800</property>
|
161
|
+
<child>
|
162
|
+
<placeholder/>
|
163
|
+
</child>
|
164
|
+
</object>
|
165
|
+
<packing>
|
166
|
+
<property name="left_attach">0</property>
|
167
|
+
<property name="top_attach">1</property>
|
168
|
+
</packing>
|
169
|
+
</child>
|
170
|
+
<child>
|
171
|
+
<object class="GtkGrid">
|
172
|
+
<property name="name">30</property>
|
173
|
+
<property name="visible">True</property>
|
174
|
+
<property name="can_focus">False</property>
|
175
|
+
<property name="margin_left">240</property>
|
176
|
+
<property name="margin_top">12</property>
|
177
|
+
<property name="margin_bottom">12</property>
|
178
|
+
<property name="column_spacing">3</property>
|
179
|
+
<child>
|
180
|
+
<object class="GtkEntry" id="savantURL">
|
181
|
+
<property name="visible">True</property>
|
182
|
+
<property name="can_focus">True</property>
|
183
|
+
<property name="has_focus">True</property>
|
184
|
+
<property name="is_focus">True</property>
|
185
|
+
<property name="events">GDK_KEY_PRESS_MASK | GDK_STRUCTURE_MASK</property>
|
186
|
+
<property name="halign">start</property>
|
187
|
+
<property name="activates_default">True</property>
|
188
|
+
<property name="width_chars">50</property>
|
189
|
+
</object>
|
190
|
+
<packing>
|
191
|
+
<property name="left_attach">1</property>
|
192
|
+
<property name="top_attach">0</property>
|
193
|
+
</packing>
|
194
|
+
</child>
|
195
|
+
<child>
|
196
|
+
<object class="GtkAlignment">
|
197
|
+
<property name="visible">True</property>
|
198
|
+
<property name="can_focus">False</property>
|
199
|
+
<child>
|
200
|
+
<object class="GtkLabel">
|
201
|
+
<property name="visible">True</property>
|
202
|
+
<property name="can_focus">False</property>
|
203
|
+
<property name="halign">end</property>
|
204
|
+
<property name="label" translatable="yes">Savant Host IP Address: </property>
|
205
|
+
<property name="justify">right</property>
|
206
|
+
</object>
|
207
|
+
</child>
|
208
|
+
</object>
|
209
|
+
<packing>
|
210
|
+
<property name="left_attach">0</property>
|
211
|
+
<property name="top_attach">0</property>
|
212
|
+
</packing>
|
213
|
+
</child>
|
214
|
+
<child>
|
215
|
+
<object class="GtkButton" id="buttonScanHost">
|
216
|
+
<property name="label" translatable="yes">Scan</property>
|
217
|
+
<property name="visible">True</property>
|
218
|
+
<property name="can_focus">True</property>
|
219
|
+
<property name="can_default">True</property>
|
220
|
+
<property name="has_default">True</property>
|
221
|
+
<property name="receives_default">True</property>
|
222
|
+
</object>
|
223
|
+
<packing>
|
224
|
+
<property name="left_attach">2</property>
|
225
|
+
<property name="top_attach">0</property>
|
226
|
+
</packing>
|
227
|
+
</child>
|
228
|
+
<child>
|
229
|
+
<object class="GtkAlignment">
|
230
|
+
<property name="visible">True</property>
|
231
|
+
<property name="can_focus">False</property>
|
232
|
+
<property name="halign">end</property>
|
233
|
+
<property name="margin_top">20</property>
|
234
|
+
<property name="hexpand">True</property>
|
235
|
+
<child>
|
236
|
+
<object class="GtkLabel" id="labelHostInfo">
|
237
|
+
<property name="visible">True</property>
|
238
|
+
<property name="can_focus">False</property>
|
239
|
+
<property name="margin_left">12</property>
|
240
|
+
<property name="justify">center</property>
|
241
|
+
</object>
|
242
|
+
</child>
|
243
|
+
</object>
|
244
|
+
<packing>
|
245
|
+
<property name="left_attach">3</property>
|
246
|
+
<property name="top_attach">0</property>
|
247
|
+
</packing>
|
248
|
+
</child>
|
249
|
+
</object>
|
250
|
+
<packing>
|
251
|
+
<property name="left_attach">0</property>
|
252
|
+
<property name="top_attach">0</property>
|
253
|
+
</packing>
|
254
|
+
</child>
|
255
|
+
<child>
|
256
|
+
<object class="GtkLabel">
|
257
|
+
<property name="visible">True</property>
|
258
|
+
<property name="can_focus">False</property>
|
259
|
+
<property name="halign">start</property>
|
260
|
+
<property name="margin_left">35</property>
|
261
|
+
<property name="margin_bottom">5</property>
|
262
|
+
<property name="label" translatable="yes">Check the box next to a scene and choose on option below.</property>
|
263
|
+
<property name="use_markup">True</property>
|
264
|
+
</object>
|
265
|
+
<packing>
|
266
|
+
<property name="left_attach">0</property>
|
267
|
+
<property name="top_attach">2</property>
|
268
|
+
</packing>
|
269
|
+
</child>
|
270
|
+
</object>
|
271
|
+
<packing>
|
272
|
+
<property name="left_attach">0</property>
|
273
|
+
<property name="top_attach">2</property>
|
274
|
+
</packing>
|
275
|
+
</child>
|
276
|
+
<child>
|
277
|
+
<object class="GtkLabel" id="labelDescription">
|
278
|
+
<property name="visible">True</property>
|
279
|
+
<property name="can_focus">False</property>
|
280
|
+
<property name="margin_top">12</property>
|
281
|
+
<property name="margin_bottom">12</property>
|
282
|
+
<property name="label" translatable="yes"> Savant scene manager copies the dashboard.sqlite file from the host to a temporary folder and lists all scenes from all users on that host.
|
283
|
+
You can backup and restore this database, as well as delete scenes or edit certain properties: Scene Name, user email, and whether or not the scene is actively scheduled.
|
284
|
+
|
285
|
+
If the host is running at least 8.3, you can also activate scenes.</property>
|
286
|
+
<property name="justify">center</property>
|
287
|
+
<property name="wrap">True</property>
|
288
|
+
<property name="wrap_mode">word-char</property>
|
289
|
+
<property name="width_chars">60</property>
|
290
|
+
<property name="max_width_chars">60</property>
|
291
|
+
</object>
|
292
|
+
<packing>
|
293
|
+
<property name="left_attach">0</property>
|
294
|
+
<property name="top_attach">1</property>
|
295
|
+
</packing>
|
296
|
+
</child>
|
297
|
+
</object>
|
298
|
+
</child>
|
299
|
+
</object>
|
300
|
+
<object class="GtkWindow" id="windowBackupRestore">
|
301
|
+
<property name="width_request">300</property>
|
302
|
+
<property name="height_request">300</property>
|
303
|
+
<property name="can_focus">False</property>
|
304
|
+
<property name="modal">True</property>
|
305
|
+
<property name="window_position">center-on-parent</property>
|
306
|
+
<property name="gravity">center</property>
|
307
|
+
<property name="transient_for">window1</property>
|
308
|
+
<property name="has_resize_grip">True</property>
|
309
|
+
<child>
|
310
|
+
<object class="GtkBox">
|
311
|
+
<property name="visible">True</property>
|
312
|
+
<property name="can_focus">False</property>
|
313
|
+
<property name="orientation">vertical</property>
|
314
|
+
<child>
|
315
|
+
<object class="GtkLabel">
|
316
|
+
<property name="visible">True</property>
|
317
|
+
<property name="can_focus">False</property>
|
318
|
+
<property name="margin_top">20</property>
|
319
|
+
<property name="margin_bottom">20</property>
|
320
|
+
<property name="label" translatable="yes"><big><b>Backup and Restore</b></big></property>
|
321
|
+
<property name="use_markup">True</property>
|
322
|
+
</object>
|
323
|
+
<packing>
|
324
|
+
<property name="expand">False</property>
|
325
|
+
<property name="fill">True</property>
|
326
|
+
<property name="position">0</property>
|
327
|
+
</packing>
|
328
|
+
</child>
|
329
|
+
<child>
|
330
|
+
<object class="GtkLabel">
|
331
|
+
<property name="visible">True</property>
|
332
|
+
<property name="can_focus">False</property>
|
333
|
+
<property name="margin_left">20</property>
|
334
|
+
<property name="margin_right">20</property>
|
335
|
+
<property name="margin_bottom">12</property>
|
336
|
+
<property name="label" translatable="yes"> This will backup or restore the dashboard.sqlite file.
|
337
|
+
|
338
|
+
To backup the file, select a destination folder.
|
339
|
+
|
340
|
+
To restore the backed up file, select the folder that contains the file. </property>
|
341
|
+
<property name="wrap">True</property>
|
342
|
+
</object>
|
343
|
+
<packing>
|
344
|
+
<property name="expand">False</property>
|
345
|
+
<property name="fill">False</property>
|
346
|
+
<property name="position">1</property>
|
347
|
+
</packing>
|
348
|
+
</child>
|
349
|
+
<child>
|
350
|
+
<object class="GtkScrolledWindow" id="scrolledwindow_BackupRestore">
|
351
|
+
<property name="width_request">300</property>
|
352
|
+
<property name="height_request">300</property>
|
353
|
+
<property name="visible">True</property>
|
354
|
+
<property name="can_focus">True</property>
|
355
|
+
<property name="margin_left">20</property>
|
356
|
+
<property name="margin_right">20</property>
|
357
|
+
<property name="shadow_type">in</property>
|
358
|
+
<property name="min_content_width">300</property>
|
359
|
+
<property name="min_content_height">300</property>
|
360
|
+
<property name="max_content_width">300</property>
|
361
|
+
<property name="max_content_height">300</property>
|
362
|
+
<child>
|
363
|
+
<placeholder/>
|
364
|
+
</child>
|
365
|
+
</object>
|
366
|
+
<packing>
|
367
|
+
<property name="expand">False</property>
|
368
|
+
<property name="fill">False</property>
|
369
|
+
<property name="position">2</property>
|
370
|
+
</packing>
|
371
|
+
</child>
|
372
|
+
<child>
|
373
|
+
<object class="GtkButtonBox">
|
374
|
+
<property name="visible">True</property>
|
375
|
+
<property name="can_focus">False</property>
|
376
|
+
<property name="margin_top">20</property>
|
377
|
+
<property name="margin_bottom">12</property>
|
378
|
+
<property name="layout_style">spread</property>
|
379
|
+
<child>
|
380
|
+
<object class="GtkButton" id="buttonBackup">
|
381
|
+
<property name="label" translatable="yes">Backup</property>
|
382
|
+
<property name="visible">True</property>
|
383
|
+
<property name="can_focus">True</property>
|
384
|
+
<property name="receives_default">True</property>
|
385
|
+
</object>
|
386
|
+
<packing>
|
387
|
+
<property name="expand">True</property>
|
388
|
+
<property name="fill">True</property>
|
389
|
+
<property name="position">0</property>
|
390
|
+
</packing>
|
391
|
+
</child>
|
392
|
+
<child>
|
393
|
+
<object class="GtkButton" id="buttonRestore">
|
394
|
+
<property name="label" translatable="yes">Restore</property>
|
395
|
+
<property name="visible">True</property>
|
396
|
+
<property name="can_focus">True</property>
|
397
|
+
<property name="receives_default">True</property>
|
398
|
+
</object>
|
399
|
+
<packing>
|
400
|
+
<property name="expand">True</property>
|
401
|
+
<property name="fill">True</property>
|
402
|
+
<property name="position">1</property>
|
403
|
+
</packing>
|
404
|
+
</child>
|
405
|
+
<child>
|
406
|
+
<object class="GtkButton" id="buttonBackupRestoreCancel">
|
407
|
+
<property name="label" translatable="yes">Cancel</property>
|
408
|
+
<property name="visible">True</property>
|
409
|
+
<property name="can_focus">True</property>
|
410
|
+
<property name="receives_default">True</property>
|
411
|
+
</object>
|
412
|
+
<packing>
|
413
|
+
<property name="expand">True</property>
|
414
|
+
<property name="fill">True</property>
|
415
|
+
<property name="position">2</property>
|
416
|
+
</packing>
|
417
|
+
</child>
|
418
|
+
</object>
|
419
|
+
<packing>
|
420
|
+
<property name="expand">True</property>
|
421
|
+
<property name="fill">True</property>
|
422
|
+
<property name="pack_type">end</property>
|
423
|
+
<property name="position">3</property>
|
424
|
+
</packing>
|
425
|
+
</child>
|
426
|
+
</object>
|
427
|
+
</child>
|
428
|
+
</object>
|
429
|
+
</interface>
|
metadata
ADDED
@@ -0,0 +1,147 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: savant-scenes
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Corey Miller
|
8
|
+
autorequire:
|
9
|
+
bindir:
|
10
|
+
- bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2017-04-28 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: sqlite3
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
requirements:
|
18
|
+
- - "~>"
|
19
|
+
- !ruby/object:Gem::Version
|
20
|
+
version: '1.3'
|
21
|
+
- - ">="
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: 1.3.13
|
24
|
+
type: :runtime
|
25
|
+
prerelease: false
|
26
|
+
version_requirements: !ruby/object:Gem::Requirement
|
27
|
+
requirements:
|
28
|
+
- - "~>"
|
29
|
+
- !ruby/object:Gem::Version
|
30
|
+
version: '1.3'
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 1.3.13
|
34
|
+
- !ruby/object:Gem::Dependency
|
35
|
+
name: net-scp
|
36
|
+
requirement: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '1.2'
|
41
|
+
- - ">="
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: 1.2.1
|
44
|
+
type: :runtime
|
45
|
+
prerelease: false
|
46
|
+
version_requirements: !ruby/object:Gem::Requirement
|
47
|
+
requirements:
|
48
|
+
- - "~>"
|
49
|
+
- !ruby/object:Gem::Version
|
50
|
+
version: '1.2'
|
51
|
+
- - ">="
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: 1.2.1
|
54
|
+
- !ruby/object:Gem::Dependency
|
55
|
+
name: ipaddress
|
56
|
+
requirement: !ruby/object:Gem::Requirement
|
57
|
+
requirements:
|
58
|
+
- - "~>"
|
59
|
+
- !ruby/object:Gem::Version
|
60
|
+
version: '0.8'
|
61
|
+
- - ">="
|
62
|
+
- !ruby/object:Gem::Version
|
63
|
+
version: 0.8.3
|
64
|
+
type: :runtime
|
65
|
+
prerelease: false
|
66
|
+
version_requirements: !ruby/object:Gem::Requirement
|
67
|
+
requirements:
|
68
|
+
- - "~>"
|
69
|
+
- !ruby/object:Gem::Version
|
70
|
+
version: '0.8'
|
71
|
+
- - ">="
|
72
|
+
- !ruby/object:Gem::Version
|
73
|
+
version: 0.8.3
|
74
|
+
- !ruby/object:Gem::Dependency
|
75
|
+
name: visualruby
|
76
|
+
requirement: !ruby/object:Gem::Requirement
|
77
|
+
requirements:
|
78
|
+
- - "~>"
|
79
|
+
- !ruby/object:Gem::Version
|
80
|
+
version: '3.0'
|
81
|
+
- - ">="
|
82
|
+
- !ruby/object:Gem::Version
|
83
|
+
version: 3.0.23
|
84
|
+
type: :runtime
|
85
|
+
prerelease: false
|
86
|
+
version_requirements: !ruby/object:Gem::Requirement
|
87
|
+
requirements:
|
88
|
+
- - "~>"
|
89
|
+
- !ruby/object:Gem::Version
|
90
|
+
version: '3.0'
|
91
|
+
- - ">="
|
92
|
+
- !ruby/object:Gem::Version
|
93
|
+
version: 3.0.23
|
94
|
+
- !ruby/object:Gem::Dependency
|
95
|
+
name: require_all
|
96
|
+
requirement: !ruby/object:Gem::Requirement
|
97
|
+
requirements:
|
98
|
+
- - "~>"
|
99
|
+
- !ruby/object:Gem::Version
|
100
|
+
version: '1.2'
|
101
|
+
- - ">="
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: 1.2.0
|
104
|
+
type: :runtime
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - "~>"
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '1.2'
|
111
|
+
- - ">="
|
112
|
+
- !ruby/object:Gem::Version
|
113
|
+
version: 1.2.0
|
114
|
+
description: Savant Scene Management
|
115
|
+
email: cmiller@premieresystems.com
|
116
|
+
executables:
|
117
|
+
- scenes
|
118
|
+
extensions: []
|
119
|
+
extra_rdoc_files: []
|
120
|
+
files:
|
121
|
+
- bin/scenes
|
122
|
+
- bin/src/SceneManager.rb
|
123
|
+
- bin/src/glade/SceneManager.glade
|
124
|
+
homepage: http://www.yoursite.org/
|
125
|
+
licenses: []
|
126
|
+
metadata: {}
|
127
|
+
post_install_message:
|
128
|
+
rdoc_options: []
|
129
|
+
require_paths:
|
130
|
+
- lib
|
131
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
132
|
+
requirements:
|
133
|
+
- - ">="
|
134
|
+
- !ruby/object:Gem::Version
|
135
|
+
version: '2.0'
|
136
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
137
|
+
requirements:
|
138
|
+
- - ">="
|
139
|
+
- !ruby/object:Gem::Version
|
140
|
+
version: '0'
|
141
|
+
requirements: []
|
142
|
+
rubyforge_project: nowarning
|
143
|
+
rubygems_version: 2.4.8
|
144
|
+
signing_key:
|
145
|
+
specification_version: 4
|
146
|
+
summary: Savant Scene Manager
|
147
|
+
test_files: []
|