ruby-macrodroid 0.9.1 → 0.9.6
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 +4 -4
- checksums.yaml.gz.sig +0 -0
- data.tar.gz.sig +0 -0
- data/lib/ruby-macrodroid.rb +11 -7
- data/lib/ruby-macrodroid/actions.rb +231 -43
- data/lib/ruby-macrodroid/base.rb +118 -15
- data/lib/ruby-macrodroid/constraints.rb +28 -5
- data/lib/ruby-macrodroid/macro.rb +510 -95
- data/lib/ruby-macrodroid/triggers.rb +16 -10
- metadata +2 -2
- metadata.gz.sig +0 -0
data/lib/ruby-macrodroid/base.rb
CHANGED
@@ -7,7 +7,104 @@
|
|
7
7
|
# MacroObject
|
8
8
|
#
|
9
9
|
|
10
|
+
module ObjectX
|
11
|
+
|
12
|
+
|
13
|
+
def action_to_object(ap, e, item, macro)
|
14
|
+
|
15
|
+
debug = false
|
16
|
+
|
17
|
+
puts 'inside action_to_object: item.xml: ' + item.xml if debug
|
18
|
+
|
19
|
+
if item.element('description') then
|
20
|
+
|
21
|
+
item.xpath('description').map do |description|
|
22
|
+
|
23
|
+
inner_lines = description.text.to_s.strip.lines
|
24
|
+
puts 'inner_lines: ' + inner_lines.inspect if debug
|
25
|
+
|
26
|
+
action = if e.text.to_s.strip.empty? then
|
27
|
+
inner_lines.shift.strip
|
28
|
+
else
|
29
|
+
e.text.strip
|
30
|
+
end
|
31
|
+
|
32
|
+
puts 'action: ' + action.inspect if debug
|
33
|
+
|
34
|
+
r = ap.find_action action
|
35
|
+
puts 'r: ' + r.inspect if debug
|
36
|
+
|
37
|
+
if r[1].any? then
|
38
|
+
|
39
|
+
macro.add r[0].new(r[1])
|
40
|
+
|
41
|
+
else
|
42
|
+
|
43
|
+
puts 'description: ' + description.xml.inspect if debug
|
44
|
+
#o = r[0].new([description, self]) if r
|
45
|
+
index = macro.actions.length
|
46
|
+
macro.add Action.new
|
47
|
+
o = object_create(r[0],[description, macro]) if r
|
48
|
+
macro.actions[index] = o
|
49
|
+
puts 'after o' if debug
|
50
|
+
o
|
51
|
+
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
else
|
56
|
+
|
57
|
+
action = e.text.strip
|
58
|
+
puts 'action: ' + action.inspect if $debug
|
59
|
+
r = ap.find_action action
|
60
|
+
|
61
|
+
a = e.xpath('item/*')
|
62
|
+
|
63
|
+
h = if a.any? then
|
64
|
+
a.map {|node| [node.name.to_sym, node.text.to_s]}.to_h
|
65
|
+
else
|
66
|
+
{}
|
67
|
+
end
|
68
|
+
puts 'h: ' + h.inspect if $debug
|
69
|
+
|
70
|
+
#r = ap.find_action action
|
71
|
+
#r[0].new(h.merge(macro: self)) if r
|
72
|
+
o = object_create(r[0], h.merge(macro: macro)) if r
|
73
|
+
macro.add o
|
74
|
+
o
|
75
|
+
|
76
|
+
end
|
77
|
+
|
78
|
+
|
79
|
+
end
|
80
|
+
|
81
|
+
def object_create(klass, *args)
|
82
|
+
|
83
|
+
begin
|
84
|
+
klass.new(*args)
|
85
|
+
rescue
|
86
|
+
raise MacroError, klass.to_s + ': ' + ($!).to_s
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
def varify(label, value='')
|
91
|
+
|
92
|
+
type = VAR_TYPES[value.class.to_s.to_sym]
|
10
93
|
|
94
|
+
h = {
|
95
|
+
boolean_value: false,
|
96
|
+
decimal_value: 0.0,
|
97
|
+
int_value: 0,
|
98
|
+
name: label,
|
99
|
+
string_value: '',
|
100
|
+
type: type[0]
|
101
|
+
}
|
102
|
+
h[type[1]] = value
|
103
|
+
h
|
104
|
+
|
105
|
+
end
|
106
|
+
|
107
|
+
end
|
11
108
|
|
12
109
|
class MacroObject
|
13
110
|
using ColouredText
|
@@ -34,19 +131,7 @@ class MacroObject
|
|
34
131
|
|
35
132
|
def to_h()
|
36
133
|
|
37
|
-
|
38
|
-
|
39
|
-
h2 = h.inject({}) do |r,x|
|
40
|
-
puts 'x: ' + x.inspect if @debug
|
41
|
-
key, value = x
|
42
|
-
puts 'key: ' + key.inspect if @debug
|
43
|
-
new_key = key.to_s.gsub(/\w_\w/){|x| x[0] + x[-1].upcase}
|
44
|
-
new_key = new_key.prepend 'm_' unless @list.include? new_key
|
45
|
-
new_key = 'm_SIGUID' if new_key == 'm_siguid'
|
46
|
-
r.merge(new_key => value)
|
47
|
-
end
|
48
|
-
|
49
|
-
h2.merge('m_classType' => self.class.to_s)
|
134
|
+
hashify(@h)
|
50
135
|
|
51
136
|
end
|
52
137
|
|
@@ -98,13 +183,31 @@ class MacroObject
|
|
98
183
|
|
99
184
|
def object(h={})
|
100
185
|
|
101
|
-
puts ('inside object h:' + h.inspect).debug if
|
186
|
+
puts ('inside object h:' + h.inspect).debug if $debug
|
102
187
|
klass = Object.const_get h[:class_type]
|
103
188
|
puts klass.inspect.highlight if $debug
|
104
189
|
|
105
190
|
klass.new h
|
106
191
|
|
107
|
-
end
|
192
|
+
end
|
193
|
+
|
194
|
+
private
|
195
|
+
|
196
|
+
def hashify(h)
|
197
|
+
puts 'h: ' + h.inspect
|
198
|
+
h2 = h.inject({}) do |r,x|
|
199
|
+
puts 'x: ' + x.inspect #if $debug
|
200
|
+
key, value = x
|
201
|
+
puts 'key: ' + key.inspect #if $debug
|
202
|
+
new_key = key.to_s.gsub(/\w_\w/){|x| x[0] + x[-1].upcase}
|
203
|
+
new_key = new_key.prepend 'm_' unless @list.include? new_key
|
204
|
+
new_key = 'm_SIGUID' if new_key == 'm_siguid'
|
205
|
+
new_val = value.is_a?(Hash) ? hashify(value) : value
|
206
|
+
r.merge(new_key => new_val)
|
207
|
+
end
|
208
|
+
|
209
|
+
h2.merge('m_classType' => self.class.to_s)
|
210
|
+
end
|
108
211
|
|
109
212
|
end
|
110
213
|
|
@@ -794,6 +794,16 @@ class MacroDroidVariableConstraint < Constraint
|
|
794
794
|
|
795
795
|
def initialize(h={})
|
796
796
|
|
797
|
+
if h[:loperand] then
|
798
|
+
h[:variable] = {}
|
799
|
+
h[:variable][:name] = h[:loperand]
|
800
|
+
h[:variable][:type] = 2
|
801
|
+
|
802
|
+
val = h[:roperand] =~ /true|false/ ? h[:roperand].capitalize \
|
803
|
+
: h[:roperand]
|
804
|
+
h[:string_value] = val
|
805
|
+
end
|
806
|
+
|
797
807
|
options = {
|
798
808
|
|
799
809
|
:enable_regex=>false,
|
@@ -825,11 +835,24 @@ class MacroDroidVariableConstraint < Constraint
|
|
825
835
|
|
826
836
|
def to_s(colour: false, indent: 0)
|
827
837
|
|
828
|
-
|
829
|
-
|
830
|
-
|
838
|
+
a = [:int_greater_than, :int_less_than, :int_not_equal,
|
839
|
+
:string_equal].zip(['>','<','!=', '='])
|
840
|
+
operator = a.find {|label,_| @h[label]}.last
|
841
|
+
|
842
|
+
var = @h[:variable]
|
843
|
+
|
844
|
+
type = case var[:type]
|
845
|
+
when 0 # boolean
|
846
|
+
:boolean_value
|
847
|
+
when 1 # integer
|
848
|
+
:int_value
|
849
|
+
when 2 # string
|
850
|
+
:string_value
|
851
|
+
when 3 # decimal
|
852
|
+
:decimal_Value
|
853
|
+
end
|
831
854
|
|
832
|
-
@s = "%s %s %s" % [@h[:variable][:name], operator, @h[
|
855
|
+
@s = "%s %s %s" % [@h[:variable][:name], operator, @h[type]]
|
833
856
|
super()
|
834
857
|
end
|
835
858
|
|
@@ -888,7 +911,7 @@ class TriggerThatInvokedConstraint < Constraint
|
|
888
911
|
|
889
912
|
def initialize(h={})
|
890
913
|
|
891
|
-
puts ('h: ' + h.inspect).green
|
914
|
+
puts ('h: ' + h.inspect).green if $debug
|
892
915
|
@trigger = h[:macro].triggers.find {|x| x.siguid == h[:si_guid_that_invoked] }
|
893
916
|
|
894
917
|
options = {
|
@@ -3,6 +3,10 @@
|
|
3
3
|
|
4
4
|
# This file contains the following classes:
|
5
5
|
#
|
6
|
+
# ## Nlp classes
|
7
|
+
#
|
8
|
+
# TriggersNlp ActionsNlp ConstraintsNlp
|
9
|
+
#
|
6
10
|
# ## Macro class
|
7
11
|
#
|
8
12
|
# Macro
|
@@ -12,15 +16,441 @@
|
|
12
16
|
VAR_TYPES = {
|
13
17
|
String: [2, :string_value],
|
14
18
|
TrueClass: [0, :boolean_value],
|
15
|
-
|
19
|
+
FalseClass: [0, :boolean_value],
|
16
20
|
Integer: [1, :int_value],
|
17
21
|
Float: [3, :decimal_value]
|
18
22
|
}
|
19
23
|
|
20
24
|
|
25
|
+
|
26
|
+
class TriggersNlp
|
27
|
+
include AppRoutes
|
28
|
+
using ColouredText
|
29
|
+
|
30
|
+
def initialize(macro=nil)
|
31
|
+
|
32
|
+
super()
|
33
|
+
params = {macro: macro}
|
34
|
+
triggers(params)
|
35
|
+
|
36
|
+
end
|
37
|
+
|
38
|
+
def triggers(params)
|
39
|
+
|
40
|
+
# -- Battery/Power ---------------------------------------------
|
41
|
+
|
42
|
+
get /^Power Connected: (Wired \([^\)]+\))/i do |s|
|
43
|
+
|
44
|
+
h = {
|
45
|
+
power_connected_options: [true, true, true],
|
46
|
+
has_set_usb_option: true,
|
47
|
+
power_connected: true
|
48
|
+
}
|
49
|
+
|
50
|
+
a = ['Wired (Fast Charge)', 'Wireless', 'Wired (Slow Charge)']
|
51
|
+
|
52
|
+
puts ('s: ' + s.inspect).debug
|
53
|
+
|
54
|
+
options = s.downcase.split(/ \+ /)
|
55
|
+
puts ('options: ' + options.inspect).debug
|
56
|
+
|
57
|
+
h[:power_connected_options] = a.map {|x| options.include? x.downcase }
|
58
|
+
|
59
|
+
[ExternalPowerTrigger, h]
|
60
|
+
end
|
61
|
+
|
62
|
+
get /^Power Connected: Any/i do |s|
|
63
|
+
|
64
|
+
h = {
|
65
|
+
power_connected_options: [true, true, true],
|
66
|
+
has_set_usb_option: true,
|
67
|
+
power_connected: true
|
68
|
+
}
|
69
|
+
|
70
|
+
[ExternalPowerTrigger, h]
|
71
|
+
end
|
72
|
+
# e.g. at 7:30pm daily
|
73
|
+
get /^(?:at )?(\d+:\d+(?:[ap]m)?) daily/i do |time, days|
|
74
|
+
[TimerTrigger, {time: time,
|
75
|
+
days: %w(Mon Tue Wed Thu Fri Sat Sun).join(', ')}]
|
76
|
+
end
|
77
|
+
|
78
|
+
get /^(?:at )?(\d+:\d+(?:[ap]m)?) (?:on )?(.*)/i do |time, days|
|
79
|
+
[TimerTrigger, {time: time, days: days}]
|
80
|
+
end
|
81
|
+
|
82
|
+
# time.is? 'at 18:30pm on Mon or Tue'
|
83
|
+
get /^time.is\? ['"](?:at )?(\d+:\d+(?:[ap]m)?) (?:on )?(.*)['"]/i do |time, days|
|
84
|
+
[TimerTrigger, {time: time, days: days.gsub(' or ',', ')}]
|
85
|
+
end
|
86
|
+
|
87
|
+
get /^shake[ _]device\??$/i do
|
88
|
+
[ShakeDeviceTrigger, {}]
|
89
|
+
end
|
90
|
+
|
91
|
+
get /^Flip Device (.*)$/i do |motion|
|
92
|
+
facedown = motion =~ /Face Up (?:->|to) Face Down/i
|
93
|
+
[FlipDeviceTrigger, {face_down: facedown }]
|
94
|
+
end
|
95
|
+
|
96
|
+
get /^flip_device_down\?$/i do
|
97
|
+
[FlipDeviceTrigger, {face_down: true }]
|
98
|
+
end
|
99
|
+
|
100
|
+
get /^flip_device_up\?$/i do
|
101
|
+
[FlipDeviceTrigger, {face_down: false }]
|
102
|
+
end
|
103
|
+
|
104
|
+
get /^Failed Login Attempt$/i do
|
105
|
+
[FailedLoginTrigger, {}]
|
106
|
+
end
|
107
|
+
|
108
|
+
get /^failed_login?$/i do
|
109
|
+
[FailedLoginTrigger, {}]
|
110
|
+
end
|
111
|
+
|
112
|
+
get /^Geofence (Entry|Exit) \(([^\)]+)/i do |direction, name|
|
113
|
+
enter_area = direction.downcase.to_sym == :entry
|
114
|
+
[GeofenceTrigger, {name: name, enter_area: enter_area}]
|
115
|
+
end
|
116
|
+
|
117
|
+
get /^location (entered|exited) \(([^\)]+)/i do |direction, name|
|
118
|
+
enter_area = direction.downcase.to_sym == :entered
|
119
|
+
[GeofenceTrigger, {name: name, enter_area: enter_area}]
|
120
|
+
end
|
121
|
+
|
122
|
+
# eg. Proximity Sensor (Near)
|
123
|
+
#
|
124
|
+
get /^Proximity Sensor \(([^\)]+)\)/i do |distance|
|
125
|
+
|
126
|
+
[ProximityTrigger, {distance: distance}]
|
127
|
+
end
|
128
|
+
|
129
|
+
# eg. Proximity near
|
130
|
+
#
|
131
|
+
get /^Proximity (near|far|slow wave|fast wave)/i do |distance|
|
132
|
+
|
133
|
+
[ProximityTrigger, {distance: distance}]
|
134
|
+
end
|
135
|
+
|
136
|
+
get /^WebHook \(Url\)/i do
|
137
|
+
[WebHookTrigger, params]
|
138
|
+
end
|
139
|
+
|
140
|
+
get /^WebHook/i do
|
141
|
+
[WebHookTrigger, params]
|
142
|
+
end
|
143
|
+
|
144
|
+
get /^wh/i do
|
145
|
+
[WebHookTrigger, params]
|
146
|
+
end
|
147
|
+
|
148
|
+
# MacroDroid specific ---------------------------------------------------------------
|
149
|
+
|
150
|
+
get /^EmptyTrigger$/i do
|
151
|
+
[EmptyTrigger, params]
|
152
|
+
end
|
153
|
+
|
154
|
+
end
|
155
|
+
|
156
|
+
alias find_trigger run_route
|
157
|
+
|
158
|
+
def to_s(colour: false)
|
159
|
+
'TriggersNlp ' + @h.inspect
|
160
|
+
end
|
161
|
+
|
162
|
+
alias to_summary to_s
|
163
|
+
end
|
164
|
+
|
165
|
+
class ActionsNlp
|
166
|
+
include AppRoutes
|
167
|
+
|
168
|
+
def initialize(macro=nil)
|
169
|
+
|
170
|
+
super()
|
171
|
+
|
172
|
+
params = {macro: macro}
|
173
|
+
actions(params)
|
174
|
+
|
175
|
+
end
|
176
|
+
|
177
|
+
def actions(params)
|
178
|
+
|
179
|
+
# e.g. message popup: hello world!
|
180
|
+
get /^message popup: (.*)/i do |msg|
|
181
|
+
[ToastAction, {msg: msg}]
|
182
|
+
end
|
183
|
+
|
184
|
+
# e.g. Popup Message 'hello world!'
|
185
|
+
get /^Popup[ _]Message ['"]([^'"]+)/i do |msg|
|
186
|
+
[ToastAction, {msg: msg}]
|
187
|
+
end
|
188
|
+
|
189
|
+
# e.g. Popup Message\n hello world!
|
190
|
+
get /^Popup Message\n\s+(.*)/im do |msg|
|
191
|
+
[ToastAction, {msg: msg}]
|
192
|
+
end
|
193
|
+
|
194
|
+
# e.g. Popup Message
|
195
|
+
get /^Popup Message$/i do
|
196
|
+
[ToastAction, {}]
|
197
|
+
end
|
198
|
+
|
199
|
+
# e.g. say current time
|
200
|
+
get /^say current[ _]time/i do
|
201
|
+
[SayTimeAction, {}]
|
202
|
+
end
|
203
|
+
|
204
|
+
get /^Torch :?(.*)/i do |onoffstate|
|
205
|
+
state = %w(on off toggle).index onoffstate.downcase
|
206
|
+
[CameraFlashLightAction, {state: state}]
|
207
|
+
end
|
208
|
+
|
209
|
+
get /^Take Picture/i do
|
210
|
+
[TakePictureAction, {}]
|
211
|
+
end
|
212
|
+
|
213
|
+
get /^take_picture/i do
|
214
|
+
[TakePictureAction, {}]
|
215
|
+
end
|
216
|
+
|
217
|
+
# -- DEVICE ACTIONS ------------------------------------------------------
|
218
|
+
|
219
|
+
get /^Speak text \(([^\)]+)\)/i do |text|
|
220
|
+
[SpeakTextAction, {text: text}]
|
221
|
+
end
|
222
|
+
|
223
|
+
get /^Speak text ['"]([^'"]+)/i do |text|
|
224
|
+
[SpeakTextAction, {text: text}]
|
225
|
+
end
|
226
|
+
|
227
|
+
get /^Speak text$/i do |text|
|
228
|
+
[SpeakTextAction, {}]
|
229
|
+
end
|
230
|
+
|
231
|
+
get /^Vibrate \(([^\)]+)/i do |pattern|
|
232
|
+
[VibrateAction, {pattern: pattern}]
|
233
|
+
end
|
234
|
+
|
235
|
+
get /^Vibrate$/i do |pattern|
|
236
|
+
[VibrateAction, {pattern: 'short buzz'}]
|
237
|
+
end
|
238
|
+
|
239
|
+
# e.g. Display Notification: Hi there: This is the body of the message
|
240
|
+
get /^Display Notification: ([^:]+): [^$]+$/i do |subject, text|
|
241
|
+
[NotificationAction, {subject: subject, text: text}]
|
242
|
+
end
|
243
|
+
|
244
|
+
|
245
|
+
# e.g. Enable Wifi
|
246
|
+
get /^(Enable|Disable) Wifi$/i do |raw_state|
|
247
|
+
|
248
|
+
state = raw_state.downcase.to_sym == :enable ? 0 : 1
|
249
|
+
[SetWifiAction, {state: state}]
|
250
|
+
|
251
|
+
end
|
252
|
+
|
253
|
+
# e.g. Play: Altair
|
254
|
+
get /^Play: (.*)$/i do |name|
|
255
|
+
|
256
|
+
[PlaySoundAction, {file_path: name}]
|
257
|
+
|
258
|
+
end
|
259
|
+
|
260
|
+
# e.g. Launch Settings
|
261
|
+
get /^Launch (.*)$/i do |application|
|
262
|
+
|
263
|
+
h = {
|
264
|
+
application_name: application,
|
265
|
+
package_to_launch: 'com.android.' + application.downcase
|
266
|
+
}
|
267
|
+
[LaunchActivityAction, h]
|
268
|
+
|
269
|
+
end
|
270
|
+
|
271
|
+
# e.g. HTTP GET http://someurl.com/something
|
272
|
+
get /^HTTP GET ([^$]+)$/i do |url|
|
273
|
+
|
274
|
+
[OpenWebPageAction, url_to_open: url]
|
275
|
+
|
276
|
+
end
|
277
|
+
|
278
|
+
get /^HTTP GET$/i do
|
279
|
+
|
280
|
+
[OpenWebPageAction, {}]
|
281
|
+
|
282
|
+
end
|
283
|
+
|
284
|
+
# e.g. webhook entered_kitchen
|
285
|
+
#
|
286
|
+
get /(?:webhook|HTTP GET) ([^$]+)$/i do |s|
|
287
|
+
key = s =~ /^http/ ? :url_to_open : :identifier
|
288
|
+
[OpenWebPageAction, {key => s}]
|
289
|
+
end
|
290
|
+
|
291
|
+
#
|
292
|
+
get /^WebHook \(Url\)/i do
|
293
|
+
[OpenWebPageAction, {}]
|
294
|
+
end
|
295
|
+
|
296
|
+
# e.g. webhook entered_kitchen
|
297
|
+
#
|
298
|
+
get /^webhook$/i do
|
299
|
+
[OpenWebPageAction, {}, params[:macro]]
|
300
|
+
end
|
301
|
+
|
302
|
+
# -- Location ---------------------------------------------------------
|
303
|
+
|
304
|
+
get /^Force Location Update$/i do
|
305
|
+
[ForceLocationUpdateAction, params]
|
306
|
+
end
|
307
|
+
|
308
|
+
get /^Share Location$/i do
|
309
|
+
[ShareLocationAction, {}]
|
310
|
+
end
|
311
|
+
|
312
|
+
#a: Keep Device Awake Screen On Until Disabled
|
313
|
+
#
|
314
|
+
get /Keep Device Awake Screen On Until Disabled/i do
|
315
|
+
[KeepAwakeAction, {enabled: true, permanent: true, screen_option: 0}]
|
316
|
+
end
|
317
|
+
|
318
|
+
|
319
|
+
#a: Keep Device Awake Screen On 1h 1m 1s
|
320
|
+
#
|
321
|
+
get /Keep Device Awake Screen On ([^$]+)/i do |duration|
|
322
|
+
|
323
|
+
a = duration.split.map(&:to_i)
|
324
|
+
secs = Subunit.new(units={minutes:60, hours:60, seconds: 60}, a).to_i
|
325
|
+
|
326
|
+
h = {
|
327
|
+
permanent: true, screen_option: 0, seconds_to_stay_awake_for: secs
|
328
|
+
}
|
329
|
+
[KeepAwakeAction, h]
|
330
|
+
end
|
331
|
+
|
332
|
+
get /Keep Device Awake$/i do
|
333
|
+
[KeepAwakeAction, {}]
|
334
|
+
end
|
335
|
+
|
336
|
+
#a: Disable Keep Awake
|
337
|
+
#
|
338
|
+
get /Disable Keep Awake/i do
|
339
|
+
[KeepAwakeAction, {enabled: false, screen_option: 0}]
|
340
|
+
end
|
341
|
+
|
342
|
+
#e.g a: if Airplane mode enabled
|
343
|
+
#
|
344
|
+
get /if (.*)/i do
|
345
|
+
[IfConditionAction, {}]
|
346
|
+
end
|
347
|
+
|
348
|
+
get /else/i do
|
349
|
+
[ElseAction, {}]
|
350
|
+
end
|
351
|
+
|
352
|
+
get /End If/i do
|
353
|
+
[EndIfAction, {}]
|
354
|
+
end
|
355
|
+
|
356
|
+
# -- MacroDroid Specific ------------------------------------------------
|
357
|
+
#
|
358
|
+
get /^Set Variable$/i do
|
359
|
+
[SetVariableAction, {}]
|
360
|
+
end
|
361
|
+
|
362
|
+
# -- Screen ------------------------------------------------
|
363
|
+
#
|
364
|
+
get /^Screen (On|Off)$/i do |state|
|
365
|
+
[ScreenOnAction, {screen_off: state.downcase == 'off'}]
|
366
|
+
end
|
367
|
+
|
368
|
+
end
|
369
|
+
|
370
|
+
alias find_action run_route
|
371
|
+
|
372
|
+
|
373
|
+
end
|
374
|
+
|
375
|
+
class ConstraintsNlp
|
376
|
+
include AppRoutes
|
377
|
+
|
378
|
+
def initialize()
|
379
|
+
|
380
|
+
super()
|
381
|
+
params = {}
|
382
|
+
constraints(params)
|
383
|
+
|
384
|
+
end
|
385
|
+
|
386
|
+
def constraints(params)
|
387
|
+
|
388
|
+
# Device State
|
389
|
+
|
390
|
+
get /^Device (locked|unlocked)/i do |state|
|
391
|
+
[DeviceLockedConstraint, {locked: state.downcase == 'locked'}]
|
392
|
+
end
|
393
|
+
|
394
|
+
get /^airplane mode (.*)/i do |state|
|
395
|
+
[AirplaneModeConstraint, {enabled: (state =~ /^enabled|on$/i) == 0}]
|
396
|
+
end
|
397
|
+
|
398
|
+
#
|
399
|
+
|
400
|
+
# -- MacroDroid specific -----------------------------------------------------------------------
|
401
|
+
|
402
|
+
get /^(\w+) (=) (\[?\w+\]?)/i do |loperand, operator, roperand|
|
403
|
+
|
404
|
+
h = {
|
405
|
+
loperand: loperand,
|
406
|
+
operator: operator,
|
407
|
+
roperand: roperand
|
408
|
+
}
|
409
|
+
|
410
|
+
[MacroDroidVariableConstraint, h]
|
411
|
+
|
412
|
+
end
|
413
|
+
|
414
|
+
# -- Sensors -----------------------------------
|
415
|
+
#
|
416
|
+
get /^Light Sensor (Less|Greater) than (50.0)lx/i do |operator, val|
|
417
|
+
|
418
|
+
level, option = operator.downcase == 'less' ? [-1,0] : [1,1]
|
419
|
+
|
420
|
+
h = {
|
421
|
+
light_level: level,
|
422
|
+
light_level_float: val,
|
423
|
+
option: option
|
424
|
+
}
|
425
|
+
|
426
|
+
[LightLevelConstraint, h]
|
427
|
+
end
|
428
|
+
|
429
|
+
get /^Proximity Sensor: (Near|Far)/i do |distance|
|
430
|
+
[ProximitySensorConstraint, {near: distance.downcase == 'near'}]
|
431
|
+
end
|
432
|
+
|
433
|
+
|
434
|
+
# -- Screen and Speaker ---------------------------
|
435
|
+
#
|
436
|
+
get /^Screen (On|Off)/i do |state|
|
437
|
+
[ScreenOnOffConstraint, {screen_on: state.downcase == 'on'}]
|
438
|
+
end
|
439
|
+
|
440
|
+
end
|
441
|
+
|
442
|
+
alias find_constraint run_route
|
443
|
+
|
444
|
+
end
|
445
|
+
|
446
|
+
|
447
|
+
class MacroError < Exception
|
448
|
+
end
|
449
|
+
|
21
450
|
class Macro
|
22
451
|
using ColouredText
|
23
452
|
using Params
|
453
|
+
include ObjectX
|
24
454
|
|
25
455
|
attr_reader :local_variables, :triggers, :actions, :constraints,
|
26
456
|
:guid, :deviceid
|
@@ -34,12 +464,14 @@ class Macro
|
|
34
464
|
|
35
465
|
puts 'inside Macro#initialize' if @debug
|
36
466
|
|
37
|
-
@local_variables, @triggers, @actions, @constraints =
|
467
|
+
@local_variables, @triggers, @actions, @constraints = {}, [], [], []
|
38
468
|
@h = {}
|
39
469
|
|
40
470
|
end
|
41
471
|
|
42
472
|
def add(obj)
|
473
|
+
puts 'inside add; ' + obj.inspect if @debug
|
474
|
+
puts '@actions: ' + @actions.inspect if @debug
|
43
475
|
|
44
476
|
if obj.kind_of? Trigger then
|
45
477
|
|
@@ -61,9 +493,13 @@ class Macro
|
|
61
493
|
end
|
62
494
|
|
63
495
|
def to_h()
|
496
|
+
|
497
|
+
a = @local_variables.map do |k,v|
|
498
|
+
varify(k,v).to_camelcase.map{|key,value| ['m_' + key, value]}.to_h
|
499
|
+
end
|
64
500
|
|
65
501
|
h = {
|
66
|
-
local_variables:
|
502
|
+
local_variables: a,
|
67
503
|
m_trigger_list: @triggers.map(&:to_h),
|
68
504
|
m_action_list: @actions.map(&:to_h),
|
69
505
|
m_category: @category,
|
@@ -73,7 +509,7 @@ class Macro
|
|
73
509
|
m_excludeLog: false,
|
74
510
|
m_GUID: guid(),
|
75
511
|
m_isOrCondition: false,
|
76
|
-
m_enabled:
|
512
|
+
m_enabled: true,
|
77
513
|
m_descriptionOpen: false,
|
78
514
|
m_headingColor: 0
|
79
515
|
}
|
@@ -98,7 +534,7 @@ class Macro
|
|
98
534
|
if h[:local_variables].any? and h[:local_variables].first.any? then
|
99
535
|
|
100
536
|
@local_variables = h[:local_variables].map do |var|
|
101
|
-
|
537
|
+
|
102
538
|
val = case var[:type]
|
103
539
|
when 0 # boolean
|
104
540
|
var[:boolean_value]
|
@@ -117,7 +553,7 @@ class Macro
|
|
117
553
|
|
118
554
|
# fetch the triggers
|
119
555
|
@triggers = h[:trigger_list].map do |trigger|
|
120
|
-
puts 'trigger: ' + trigger.inspect
|
556
|
+
puts 'trigger: ' + trigger.inspect if @debug
|
121
557
|
#exit
|
122
558
|
object(trigger.to_snake_case)
|
123
559
|
|
@@ -196,25 +632,23 @@ class Macro
|
|
196
632
|
|
197
633
|
@title = node.text('macro') || node.attributes[:name]
|
198
634
|
|
199
|
-
|
635
|
+
d = node.element('description')
|
636
|
+
|
637
|
+
if d then
|
200
638
|
|
201
|
-
|
639
|
+
desc = []
|
640
|
+
desc << d.text.strip
|
202
641
|
|
203
|
-
|
204
|
-
|
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
|
642
|
+
if d.element('item/description') then
|
643
|
+
desc << d.text('item/description').strip
|
213
644
|
end
|
214
645
|
|
215
|
-
|
646
|
+
@description = desc.join("\n")
|
647
|
+
|
216
648
|
end
|
217
649
|
|
650
|
+
node.xpath('variable').each {|e| set_var(*e.text.to_s.split(/: */,2)) }
|
651
|
+
|
218
652
|
#@description = node.attributes[:description]
|
219
653
|
|
220
654
|
tp = TriggersNlp.new(self)
|
@@ -245,7 +679,8 @@ class Macro
|
|
245
679
|
|
246
680
|
r = tp.find_trigger trigger
|
247
681
|
puts 'r: ' + r.inspect if @debug
|
248
|
-
o = r[0].new([description, self]) if r
|
682
|
+
#o = r[0].new([description, self]) if r
|
683
|
+
o = object_create(r[0], [description, self]) if r
|
249
684
|
puts 'after o' if @debug
|
250
685
|
o
|
251
686
|
|
@@ -265,7 +700,12 @@ class Macro
|
|
265
700
|
end
|
266
701
|
|
267
702
|
r = tp.find_trigger trigger
|
268
|
-
r[0].new(h) if r
|
703
|
+
#r[0].new(h) if r
|
704
|
+
if r then
|
705
|
+
object_create(r[0], h)
|
706
|
+
else
|
707
|
+
raise MacroError, 'App-routes: Trigger "' + trigger + '" not found'
|
708
|
+
end
|
269
709
|
|
270
710
|
end
|
271
711
|
|
@@ -273,70 +713,42 @@ class Macro
|
|
273
713
|
|
274
714
|
trigger = e.text.strip
|
275
715
|
r = tp.find_trigger trigger
|
276
|
-
r[0].new(r[1]) if r
|
716
|
+
#r[0].new(r[1]) if r
|
717
|
+
|
718
|
+
if r then
|
719
|
+
object_create(r[0],r[1])
|
720
|
+
else
|
721
|
+
raise MacroError, 'App-routes: Trigger "' + trigger + '" not found'
|
722
|
+
end
|
277
723
|
|
278
724
|
end
|
279
725
|
|
726
|
+
|
280
727
|
end
|
728
|
+
|
729
|
+
|
281
730
|
|
282
731
|
ap = ActionsNlp.new self
|
283
732
|
|
284
|
-
|
733
|
+
node.xpath('action').each do |e|
|
285
734
|
|
286
735
|
puts 'action e: ' + e.xml.inspect if @debug
|
287
736
|
puts 'e.text ' + e.text if @debug
|
288
737
|
|
289
738
|
item = e.element('item')
|
739
|
+
|
290
740
|
if item then
|
291
741
|
|
292
|
-
|
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
|
742
|
+
action_to_object(ap, e, item, self)
|
332
743
|
|
333
744
|
else
|
334
745
|
|
335
746
|
action = e.text.strip
|
336
747
|
r = ap.find_action action
|
337
|
-
r[0].new(r[1]) if r
|
748
|
+
#r[0].new(r[1]) if r
|
749
|
+
self.add object_create(r[0],*r[1..-1]) if r
|
338
750
|
|
339
|
-
end
|
751
|
+
end
|
340
752
|
|
341
753
|
end
|
342
754
|
|
@@ -347,9 +759,7 @@ class Macro
|
|
347
759
|
r = cp.find_constraint e.text
|
348
760
|
puts 'found constraint ' + r.inspect if @debug
|
349
761
|
|
350
|
-
if r
|
351
|
-
r[0].new(r[1])
|
352
|
-
end
|
762
|
+
object_create(r[0], r[1]) if r
|
353
763
|
|
354
764
|
end
|
355
765
|
|
@@ -394,6 +804,30 @@ class Macro
|
|
394
804
|
def set_env()
|
395
805
|
@triggers.each(&:set_env)
|
396
806
|
end
|
807
|
+
|
808
|
+
def set_var(label, v='')
|
809
|
+
|
810
|
+
value = if v.to_f.to_s == v
|
811
|
+
v.to_f
|
812
|
+
elsif v.downcase == 'true'
|
813
|
+
true
|
814
|
+
elsif v.downcase == 'false'
|
815
|
+
false
|
816
|
+
elsif v.to_i.to_s == v
|
817
|
+
v.to_i
|
818
|
+
else
|
819
|
+
v
|
820
|
+
end
|
821
|
+
|
822
|
+
if not @local_variables.has_key? label.to_sym then
|
823
|
+
@local_variables.merge!({label.to_sym => value})
|
824
|
+
end
|
825
|
+
|
826
|
+
if @debug then
|
827
|
+
puts ("before varify; label: %s value: %s" % [label, value]).debug
|
828
|
+
end
|
829
|
+
varify(label, value)
|
830
|
+
end
|
397
831
|
|
398
832
|
def to_pc()
|
399
833
|
|
@@ -441,6 +875,7 @@ EOF
|
|
441
875
|
a << @triggers.map do |x|
|
442
876
|
|
443
877
|
puts 'x: ' + x.inspect if @debug
|
878
|
+
raise 'Macro#to_s trigger cannot be nil' if x.nil?
|
444
879
|
|
445
880
|
s =-x.to_s(colour: colour)
|
446
881
|
puts 's: ' + s.inspect if @debug
|
@@ -587,44 +1022,24 @@ EOF
|
|
587
1022
|
|
588
1023
|
puts ('inside object h:' + h.inspect).debug if @debug
|
589
1024
|
klass = Object.const_get h[:class_type]
|
590
|
-
puts klass.inspect.highlight if
|
1025
|
+
puts klass.inspect.highlight if @debug
|
591
1026
|
|
592
1027
|
if klass == GeofenceTrigger then
|
593
|
-
puts 'GeofenceTrigger found'.highlight if
|
1028
|
+
puts 'GeofenceTrigger found'.highlight if @debug
|
594
1029
|
GeofenceTrigger.new(h, geofences: @geofences)
|
595
1030
|
else
|
596
|
-
puts 'before klass'
|
1031
|
+
puts 'before klass' if @debug
|
597
1032
|
h2 = h.merge( macro: self)
|
598
|
-
puts 'h2: ' + h2.inspect
|
1033
|
+
puts 'h2: ' + h2.inspect if @debug
|
599
1034
|
r = klass.new h2
|
600
|
-
puts 'r:' + r.inspect
|
1035
|
+
puts 'r:' + r.inspect if @debug
|
601
1036
|
r
|
602
1037
|
|
603
1038
|
end
|
604
1039
|
|
605
1040
|
end
|
606
1041
|
|
607
|
-
|
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
|
1042
|
+
|
1043
|
+
|
629
1044
|
|
630
1045
|
end
|