ruby-macrodroid 0.8.9 → 0.9.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,630 @@
1
+ # file: ruby-macrodroid/macro.rb
2
+
3
+
4
+ # This file contains the following classes:
5
+ #
6
+ # ## Macro class
7
+ #
8
+ # Macro
9
+
10
+
11
+
12
+ VAR_TYPES = {
13
+ String: [2, :string_value],
14
+ TrueClass: [0, :boolean_value],
15
+ TrueClass: [0, :boolean_value],
16
+ Integer: [1, :int_value],
17
+ Float: [3, :decimal_value]
18
+ }
19
+
20
+
21
+ class Macro
22
+ using ColouredText
23
+ using Params
24
+
25
+ attr_reader :local_variables, :triggers, :actions, :constraints,
26
+ :guid, :deviceid
27
+ attr_accessor :title, :description, :remote_url
28
+
29
+ def initialize(name=nil, geofences: nil, deviceid: nil, remote_url: nil,
30
+ debug: false)
31
+
32
+ @title, @geofences, @deviceid, @debug = name, geofences, deviceid, debug
33
+ @remote_url = remote_url
34
+
35
+ puts 'inside Macro#initialize' if @debug
36
+
37
+ @local_variables, @triggers, @actions, @constraints = [], [], [], []
38
+ @h = {}
39
+
40
+ end
41
+
42
+ def add(obj)
43
+
44
+ if obj.kind_of? Trigger then
45
+
46
+ puts 'trigger found' if @debug
47
+ @triggers << obj
48
+
49
+ elsif obj.kind_of? Action
50
+
51
+ puts 'action found' if @debug
52
+ @actions << obj
53
+
54
+ elsif obj.kind_of? Constraint
55
+
56
+ puts 'constraint found' if @debug
57
+ @constraints << obj
58
+
59
+ end
60
+
61
+ end
62
+
63
+ def to_h()
64
+
65
+ h = {
66
+ local_variables: varify(@local_variables),
67
+ m_trigger_list: @triggers.map(&:to_h),
68
+ m_action_list: @actions.map(&:to_h),
69
+ m_category: @category,
70
+ m_constraint_list: @constraints.map(&:to_h),
71
+ m_description: '',
72
+ m_name: title(),
73
+ m_excludeLog: false,
74
+ m_GUID: guid(),
75
+ m_isOrCondition: false,
76
+ m_enabled: false,
77
+ m_descriptionOpen: false,
78
+ m_headingColor: 0
79
+ }
80
+
81
+ puts 'h: ' + h.inspect if @debug
82
+
83
+ @h.merge(h)
84
+ end
85
+
86
+ def import_h(h)
87
+
88
+ if @debug then
89
+ puts 'inside import_h'
90
+ puts 'h:' + h.inspect
91
+ end
92
+
93
+ @category = h[:category]
94
+ @title = h[:name]
95
+ @description = h[:description]
96
+
97
+ # fetch the local variables
98
+ if h[:local_variables].any? and h[:local_variables].first.any? then
99
+
100
+ @local_variables = h[:local_variables].map do |var|
101
+
102
+ val = case var[:type]
103
+ when 0 # boolean
104
+ var[:boolean_value]
105
+ when 1 # integer
106
+ var[:int_value]
107
+ when 2 # string
108
+ var[:string_value]
109
+ when 3 # decimal
110
+ var[:decimal_Value]
111
+ end
112
+
113
+ [var[:name], val]
114
+
115
+ end.to_h
116
+ end
117
+
118
+ # fetch the triggers
119
+ @triggers = h[:trigger_list].map do |trigger|
120
+ puts 'trigger: ' + trigger.inspect
121
+ #exit
122
+ object(trigger.to_snake_case)
123
+
124
+ end
125
+
126
+ @actions = h[:action_list].map do |action|
127
+ object(action.to_snake_case)
128
+ end
129
+ puts 'before fetch constraints' if @debug
130
+ # fetch the constraints
131
+ @constraints = h[:constraint_list].map do |constraint|
132
+ object(constraint.to_snake_case)
133
+ end
134
+ puts 'after fetch constraints' if @debug
135
+ @h = h
136
+
137
+ %i(local_variables m_trigger_list m_action_list m_constraint_list)\
138
+ .each {|x| @h[x] = [] }
139
+ puts 'after @h set' if @debug
140
+ @h
141
+
142
+ end
143
+
144
+ def import_xml(node)
145
+
146
+ if @debug then
147
+ puts 'inside Macro#import_xml'
148
+ puts 'node: ' + node.xml.inspect
149
+ end
150
+
151
+ if node.element('triggers') then
152
+
153
+ # level 2
154
+
155
+ @title = node.attributes[:name]
156
+ @category = node.attributes[:category]
157
+ @description = node.attributes[:description]
158
+
159
+
160
+ # get all the triggers
161
+ @triggers = node.xpath('triggers/*').map do |e|
162
+
163
+ puts 'e.name: ' + e.name.inspect if @debug
164
+ {timer: TimerTrigger}[e.name.to_sym].new(e.attributes.to_h)
165
+
166
+ end
167
+
168
+ # get all the actions
169
+ @actions = node.xpath('actions/*').map do |e|
170
+
171
+ if e.name == 'notification' then
172
+
173
+ case e.attributes[:type].to_sym
174
+ when :popup
175
+ e.attributes.delete :type
176
+ ToastAction.new e.attributes.to_h
177
+ end
178
+
179
+ end
180
+
181
+ end
182
+
183
+ # get all the constraints
184
+ @constraints = node.xpath('constraints/*').map do |e|
185
+
186
+ puts 'e.name: ' + e.name.inspect if @debug
187
+ {airplanemode: AirplaneModeConstraint}[e.name.to_sym].new(e.attributes.to_h)
188
+
189
+ end
190
+
191
+ else
192
+
193
+ # Level 1
194
+
195
+ puts 'import_xml: inside level 1' if @debug
196
+
197
+ @title = node.text('macro') || node.attributes[:name]
198
+
199
+ @local_variables = node.xpath('variable').map do |e|
200
+
201
+ label, v = e.text.to_s.split(/: */,2)
202
+
203
+ value = if v.to_f.to_s == v
204
+ v.to_f
205
+ elsif v.downcase == 'true'
206
+ true
207
+ elsif v.downcase == 'false'
208
+ false
209
+ elsif v.to_i.to_s == v
210
+ v.to_i
211
+ else
212
+ v
213
+ end
214
+
215
+ [label, value]
216
+ end
217
+
218
+ #@description = node.attributes[:description]
219
+
220
+ tp = TriggersNlp.new(self)
221
+
222
+ @triggers = node.xpath('trigger').flat_map do |e|
223
+
224
+ r = tp.find_trigger e.text
225
+
226
+ puts 'found trigger ' + r.inspect if @debug
227
+
228
+ item = e.element('item')
229
+ if item then
230
+
231
+ if item.element('description') then
232
+
233
+ item.xpath('description').map do |description|
234
+
235
+ inner_lines = description.text.to_s.strip.lines
236
+ puts 'inner_lines: ' + inner_lines.inspect if @debug
237
+
238
+ trigger = if e.text.to_s.strip.empty? then
239
+ inner_lines.shift.strip
240
+ else
241
+ e.text.strip
242
+ end
243
+
244
+ puts 'trigger: ' + trigger.inspect if @debug
245
+
246
+ r = tp.find_trigger trigger
247
+ puts 'r: ' + r.inspect if @debug
248
+ o = r[0].new([description, self]) if r
249
+ puts 'after o' if @debug
250
+ o
251
+
252
+ end
253
+
254
+ else
255
+
256
+ trigger = e.text.strip
257
+ r = tp.find_trigger trigger
258
+
259
+ a = e.xpath('item/*')
260
+
261
+ h = if a.any? then
262
+ a.map {|node| [node.name.to_sym, node.text.to_s]}.to_h
263
+ else
264
+ {}
265
+ end
266
+
267
+ r = tp.find_trigger trigger
268
+ r[0].new(h) if r
269
+
270
+ end
271
+
272
+ else
273
+
274
+ trigger = e.text.strip
275
+ r = tp.find_trigger trigger
276
+ r[0].new(r[1]) if r
277
+
278
+ end
279
+
280
+ end
281
+
282
+ ap = ActionsNlp.new self
283
+
284
+ @actions = node.xpath('action').flat_map do |e|
285
+
286
+ puts 'action e: ' + e.xml.inspect if @debug
287
+ puts 'e.text ' + e.text if @debug
288
+
289
+ item = e.element('item')
290
+ if item then
291
+
292
+ if item.element('description') then
293
+
294
+ item.xpath('description').map do |description|
295
+
296
+ inner_lines = description.text.to_s.strip.lines
297
+ puts 'inner_lines: ' + inner_lines.inspect if @debug
298
+
299
+ action = if e.text.to_s.strip.empty? then
300
+ inner_lines.shift.strip
301
+ else
302
+ e.text.strip
303
+ end
304
+
305
+ puts 'action: ' + action.inspect if @debug
306
+
307
+ r = ap.find_action action
308
+ puts 'r: ' + r.inspect if @debug
309
+ o = r[0].new([description, self]) if r
310
+ puts 'after o' if @debug
311
+ o
312
+
313
+ end
314
+
315
+ else
316
+
317
+ action = e.text.strip
318
+ r = ap.find_action action
319
+
320
+ a = e.xpath('item/*')
321
+
322
+ h = if a.any? then
323
+ a.map {|node| [node.name.to_sym, node.text.to_s]}.to_h
324
+ else
325
+ {}
326
+ end
327
+
328
+ r = ap.find_action action
329
+ r[0].new(h) if r
330
+
331
+ end
332
+
333
+ else
334
+
335
+ action = e.text.strip
336
+ r = ap.find_action action
337
+ r[0].new(r[1]) if r
338
+
339
+ end
340
+
341
+ end
342
+
343
+ cp = ConstraintsNlp.new
344
+
345
+ @constraints = node.xpath('constraint').map do |e|
346
+
347
+ r = cp.find_constraint e.text
348
+ puts 'found constraint ' + r.inspect if @debug
349
+
350
+ if r then
351
+ r[0].new(r[1])
352
+ end
353
+
354
+ end
355
+
356
+ end
357
+
358
+ self
359
+
360
+ end
361
+
362
+ def match?(triggerx, detail={time: $env[:time]}, model=nil )
363
+
364
+ if @triggers.any? {|x| x.type == triggerx and x.match?(detail, model) } then
365
+
366
+ if @debug then
367
+ puts 'checking constraints ...'
368
+ puts '@constraints: ' + @constraints.inspect
369
+ end
370
+
371
+ if @constraints.all? {|x| x.match?($env.merge(detail), model) } then
372
+
373
+ true
374
+
375
+ else
376
+
377
+ return false
378
+
379
+ end
380
+
381
+ end
382
+
383
+ end
384
+
385
+ # invokes the actions
386
+ #
387
+ def run()
388
+ @actions.map(&:invoke)
389
+ end
390
+
391
+ # prepares the environment in order for triggers to test fire successfully
392
+ # Used for testing
393
+ #
394
+ def set_env()
395
+ @triggers.each(&:set_env)
396
+ end
397
+
398
+ def to_pc()
399
+
400
+ heading = '# ' + @title
401
+ heading += '\n# ' + @description if @description
402
+ condition = @triggers.first.to_pc
403
+ actions = @actions.map(&:to_pc).join("\n")
404
+
405
+ <<EOF
406
+ #{heading}
407
+
408
+ if #{condition} then
409
+ #{actions}
410
+ end
411
+ EOF
412
+ end
413
+
414
+ def to_s(colour: false)
415
+
416
+ indent = 0 #@actions.map(&:to_s).join.lines.length > 0 ? 1 : 0
417
+
418
+ a = []
419
+ a << '# ' + @category + "\n" if @category
420
+ a << (colour ? "m".bg_cyan.gray.bold : 'm') + ': ' + @title
421
+
422
+
423
+ if @description and @description.length >= 1 then
424
+ a << (colour ? "d".bg_gray.gray.bold : 'd') + ': ' \
425
+ + @description.gsub(/\n/,"\n ")
426
+ end
427
+
428
+ if @local_variables.length >= 1 then
429
+
430
+ vars = @local_variables.map do |k,v|
431
+ label = colour ? 'v'.bg_magenta : 'v'
432
+ label += ': '
433
+ label + "%s: %s" % [k,v]
434
+ end
435
+
436
+ a << vars.join("\n")
437
+ end
438
+
439
+ puts 'before triggers' if @debug
440
+
441
+ a << @triggers.map do |x|
442
+
443
+ puts 'x: ' + x.inspect if @debug
444
+
445
+ s =-x.to_s(colour: colour)
446
+ puts 's: ' + s.inspect if @debug
447
+
448
+ s2 = if s.lines.length > 1 then
449
+ "\n" + s.lines.map {|x| x.prepend (' ' * (indent+1)) }.join
450
+ else
451
+ ' ' + s
452
+ end
453
+
454
+ puts 's2: ' + s2.inspect if @debug
455
+
456
+ #s.lines > 1 ? "\n" + x : x
457
+ (colour ? "t".bg_red.gray.bold : 't') + ":" + s2
458
+ end.join("\n")
459
+
460
+ puts 'before actions' if @debug
461
+ actions = @actions.map do |x|
462
+
463
+ puts 'x: ' + x.inspect if @debug
464
+ raise 'Macro#to_s action cannot be nil' if x.nil?
465
+ s = x.to_s(colour: colour)
466
+ #puts 's: ' + s.inspect
467
+
468
+
469
+
470
+ r = if indent <= 0 then
471
+
472
+ lines = s.lines
473
+
474
+ if lines.length > 1 then
475
+ s = lines.map {|x| x.prepend (' ' * (indent+1)) }.join
476
+ end
477
+
478
+ s2 = s.lines.length > 1 ? "\n" + s : ' ' + s
479
+
480
+ if colour then
481
+ "a".bg_blue.gray.bold + ":" + s2
482
+ else
483
+ "a:" + s2
484
+ end
485
+
486
+ elsif indent > 0
487
+
488
+ if s =~ /^Else/ then
489
+ (' ' * (indent-1)) + "%s" % s
490
+ elsif s =~ /^End/
491
+ indent -= 1
492
+ (' ' * indent) + "%s" % s
493
+ else
494
+ s2 = s.lines[0] + s.lines[1..-1].map {|x| (' ' * indent) + x }.join
495
+ (' ' * indent) + "%s" % s2
496
+ end
497
+
498
+ end
499
+
500
+ if s =~ /^(?:If|DO \/ WHILE)/i then
501
+
502
+ if indent < 1 then
503
+
504
+ r = if colour then
505
+ "a".bg_blue.gray.bold + ":\n %s" % s
506
+ else
507
+ "a:\n %s" % s
508
+ end
509
+
510
+ indent += 1
511
+ else
512
+ r = (' ' * indent) + "%s" % s
513
+ end
514
+
515
+ indent += 1
516
+ end
517
+
518
+ r
519
+
520
+ end.join("\n")
521
+
522
+
523
+
524
+
525
+ a << actions
526
+
527
+
528
+ puts 'before constraints' if @debug
529
+ if @constraints.any? then
530
+ a << @constraints.map do |x|
531
+ (colour ? "c".bg_green.gray.bold : 'c') + ": %s" % x
532
+ end.join("\n")
533
+ end
534
+
535
+
536
+
537
+
538
+
539
+ a.join("\n") + "\n"
540
+
541
+ end
542
+
543
+ def to_summary(colour: false)
544
+
545
+ if colour then
546
+
547
+ a = [
548
+ 'm'.bg_cyan.gray.bold + ': ' + @title,
549
+ 't'.bg_red.gray.bold + ': ' + @triggers.map \
550
+ {|x| x.to_summary(colour: false)}.join(", "),
551
+ 'a'.bg_blue.gray.bold + ': ' + @actions.map \
552
+ {|x| x.to_summary(colour: false)}.join(", ")
553
+ ]
554
+
555
+ if @constraints.any? then
556
+ a << 'c'.bg_green.gray.bold + ': ' + @constraints.map \
557
+ {|x| x.to_summary(colour: false)}.join(", ")
558
+ end
559
+
560
+ else
561
+
562
+ a = [
563
+ 'm: ' + @title,
564
+ 't: ' + @triggers.map {|x| x.to_summary(colour: false)}.join(", "),
565
+ 'a: ' + @actions.map {|x| x.to_summary(colour: false)}.join(", ")
566
+ ]
567
+
568
+ if @constraints.any? then
569
+ a << 'c: ' + @constraints.map \
570
+ {|x| x.to_summary(colour: false)}.join(", ")
571
+ end
572
+ end
573
+
574
+
575
+
576
+ a.join("\n") + "\n"
577
+
578
+ end
579
+
580
+ private
581
+
582
+ def guid()
583
+ '-' + rand(1..9).to_s + 18.times.map { rand 9 }.join
584
+ end
585
+
586
+ def object(h={})
587
+
588
+ puts ('inside object h:' + h.inspect).debug if @debug
589
+ klass = Object.const_get h[:class_type]
590
+ puts klass.inspect.highlight if $debug
591
+
592
+ if klass == GeofenceTrigger then
593
+ puts 'GeofenceTrigger found'.highlight if $debug
594
+ GeofenceTrigger.new(h, geofences: @geofences)
595
+ else
596
+ puts 'before klass'
597
+ h2 = h.merge( macro: self)
598
+ puts 'h2: ' + h2.inspect
599
+ r = klass.new h2
600
+ puts 'r:' + r.inspect
601
+ r
602
+
603
+ end
604
+
605
+ end
606
+
607
+ def varify(local_variables)
608
+
609
+
610
+ local_variables.map do |key, value|
611
+
612
+ puts 'value ' + value.class.to_s.to_sym.inspect
613
+ puts 'VAR_TYPES: ' + VAR_TYPES.inspect
614
+ type = VAR_TYPES[value.class.to_s.to_sym]
615
+ puts 'type: ' + type.inspect
616
+ h = {
617
+ boolean_value: false,
618
+ decimal_value: 0.0,
619
+ int_value: 0,
620
+ name: key,
621
+ string_value: '',
622
+ type: type[0]
623
+ }
624
+ h[type[1]] = value
625
+ h
626
+ end
627
+
628
+ end
629
+
630
+ end