ruby-macrodroid 0.5.0 → 0.7.1
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 +800 -89
- metadata +66 -6
- metadata.gz.sig +0 -0
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: a2f43b1d737df8ffc21bb31f9a7df56964963ba3b88a6e51e008c45ca0d4e5f6
|
|
4
|
+
data.tar.gz: 6e587a62b683867b37f28dfba70728971e19647cfa803a804ea123c1eb0955b3
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: b247e2d22eac6c837d9bcacf38697b63ab80a41c2affca2adb7389b39423813c9d1816f9c9dd697a013ad75b5417ca2143ca4e040be819a09a67a939765aae2f
|
|
7
|
+
data.tar.gz: 1a91db74afbbfbe1622d3e19241c9a99ddd05c0ef975e7ac037102104497d12a191c305a4ceb9ab0f5d7d7d476060398b4648ea927bc3135ab52b5633f562e0f
|
checksums.yaml.gz.sig
CHANGED
|
Binary file
|
data.tar.gz.sig
CHANGED
|
Binary file
|
data/lib/ruby-macrodroid.rb
CHANGED
|
@@ -2,11 +2,126 @@
|
|
|
2
2
|
|
|
3
3
|
# file: ruby-macrodroid.rb
|
|
4
4
|
|
|
5
|
+
# This file contains the following classes:
|
|
6
|
+
#
|
|
7
|
+
# ## Nlp classes
|
|
8
|
+
#
|
|
9
|
+
# TriggersNlp ActionsNlp ConstraintsNlp
|
|
10
|
+
#
|
|
11
|
+
#
|
|
12
|
+
# ## Macro class
|
|
13
|
+
#
|
|
14
|
+
# Macro
|
|
15
|
+
#
|
|
16
|
+
#
|
|
17
|
+
# ## Error class
|
|
18
|
+
#
|
|
19
|
+
# MacroDroidError
|
|
20
|
+
#
|
|
21
|
+
#
|
|
22
|
+
# ## Droid class
|
|
23
|
+
#
|
|
24
|
+
# MacroDroid
|
|
25
|
+
#
|
|
26
|
+
#
|
|
27
|
+
# ## Map class
|
|
28
|
+
#
|
|
29
|
+
# GeofenceMap
|
|
30
|
+
#
|
|
31
|
+
#
|
|
32
|
+
# ## Object class
|
|
33
|
+
#
|
|
34
|
+
# MacroObject
|
|
35
|
+
#
|
|
36
|
+
#
|
|
37
|
+
# ## Trigger classes
|
|
38
|
+
#
|
|
39
|
+
# Trigger WebHookTrigger WifiConnectionTrigger
|
|
40
|
+
# ApplicationInstalledRemovedTrigger ApplicationLaunchedTrigger
|
|
41
|
+
# BatteryLevelTrigger BatteryTemperatureTrigger PowerButtonToggleTrigger
|
|
42
|
+
# ExternalPowerTrigger CallActiveTrigger IncomingCallTrigger
|
|
43
|
+
# OutgoingCallTrigger CallEndedTrigger CallMissedTrigger IncomingSMSTrigger
|
|
44
|
+
# WebHookTrigger WifiConnectionTrigger BluetoothTrigger HeadphonesTrigger
|
|
45
|
+
# SignalOnOffTrigger UsbDeviceConnectionTrigger WifiSSIDTrigger
|
|
46
|
+
# CalendarTrigger TimerTrigger StopwatchTrigger DayTrigger
|
|
47
|
+
# RegularIntervalTrigger DeviceEventsTrigger AirplaneModeTrigger
|
|
48
|
+
# AutoSyncChangeTrigger DayDreamTrigger DockTrigger FailedLoginTrigger
|
|
49
|
+
# GPSEnabledTrigger MusicPlayingTrigger DeviceUnlockedTrigger
|
|
50
|
+
# AutoRotateChangeTrigger ClipboardChangeTrigger BootTrigger
|
|
51
|
+
# IntentReceivedTrigger NotificationTrigger ScreenOnOffTrigger
|
|
52
|
+
# SilentModeTrigger WeatherTrigger GeofenceTrigger SunriseSunsetTrigger
|
|
53
|
+
# SensorsTrigger ActivityRecognitionTrigger ProximityTrigger
|
|
54
|
+
# ShakeDeviceTrigger FlipDeviceTrigger OrientationTrigger
|
|
55
|
+
# FloatingButtonTrigger ShortcutTrigger VolumeButtonTrigger
|
|
56
|
+
# MediaButtonPressedTrigger SwipeTrigger
|
|
57
|
+
#
|
|
58
|
+
#
|
|
59
|
+
# ## Action classes
|
|
60
|
+
#
|
|
61
|
+
# Action LocationAction ShareLocationAction ApplicationAction
|
|
62
|
+
# LaunchActivityAction KillBackgroundAppAction OpenWebPageAction CameraAction
|
|
63
|
+
# UploadPhotoAction TakePictureAction ConnectivityAction SetWifiAction
|
|
64
|
+
# SetBluetoothAction SetBluetoothAction SendIntentAction DateTimeAction
|
|
65
|
+
# SetAlarmClockAction StopWatchAction SayTimeAction DeviceAction
|
|
66
|
+
# AndroidShortcutsAction ClipboardAction PressBackAction SpeakTextAction
|
|
67
|
+
# UIInteractionAction VoiceSearchAction DeviceSettingsAction
|
|
68
|
+
# ExpandCollapseStatusBarAction LaunchHomeScreenAction CameraFlashLightAction
|
|
69
|
+
# VibrateAction SetAutoRotateAction DayDreamAction SetKeyboardAction
|
|
70
|
+
# SetKeyguardAction CarModeAction ChangeKeyboardAction SetWallpaperAction
|
|
71
|
+
# FileAction OpenFileAction LocationAction ForceLocationUpdateAction
|
|
72
|
+
# ShareLocationAction SetLocationUpdateRateAction LoggingAction
|
|
73
|
+
# AddCalendarEntryAction LogAction ClearLogAction MediaAction
|
|
74
|
+
# RecordMicrophoneAction PlaySoundAction MessagingAction SendEmailAction
|
|
75
|
+
# SendSMSAction UDPCommandAction NotificationsAction ClearNotificationsAction
|
|
76
|
+
# MessageDialogAction AllowLEDNotificationLightAction
|
|
77
|
+
# SetNotificationSoundAction SetNotificationSoundAction
|
|
78
|
+
# SetNotificationSoundAction NotificationAction ToastAction PhoneAction
|
|
79
|
+
# AnswerCallAction ClearCallLogAction OpenCallLogAction RejectCallAction
|
|
80
|
+
# MakeCallAction SetRingtoneAction ScreenAction SetBrightnessAction
|
|
81
|
+
# ForceScreenRotationAction ScreenOnAction DimScreenAction KeepAwakeAction
|
|
82
|
+
# SetScreenTimeoutAction VolumeAction SilentModeVibrateOffAction
|
|
83
|
+
# SetVibrateAction VolumeIncrementDecrementAction SpeakerPhoneAction
|
|
84
|
+
# SetVolumeAction
|
|
85
|
+
#
|
|
86
|
+
#
|
|
87
|
+
# ## Constraint classes
|
|
88
|
+
#
|
|
89
|
+
# Constraint TimeOfDayConstraint BatteryLevelConstraint
|
|
90
|
+
# BatterySaverStateConstraint BatteryTemperatureConstraint
|
|
91
|
+
# ExternalPowerConstraint BluetoothConstraint GPSEnabledConstraint
|
|
92
|
+
# LocationModeConstraint SignalOnOffConstraint WifiConstraint
|
|
93
|
+
# CellTowerConstraint IsRoamingConstraint DataOnOffConstraint
|
|
94
|
+
# WifiHotSpotConstraint CalendarConstraint DayOfWeekConstraint
|
|
95
|
+
# TimeOfDayConstraint DayOfMonthConstraint MonthOfYearConstraint
|
|
96
|
+
# SunsetSunriseConstraint AirplaneModeConstraint AutoRotateConstraint
|
|
97
|
+
# DeviceLockedConstraint RoamingOnOffConstraint TimeSinceBootConstraint
|
|
98
|
+
# AutoSyncConstraint NFCStateConstraint IsRootedConstraint VpnConstraint
|
|
99
|
+
# MacroEnabledConstraint ModeConstraint TriggerThatInvokedConstraint
|
|
100
|
+
# LastRunTimeConstraint HeadphonesConnectionConstraint MusicActiveConstraint
|
|
101
|
+
# NotificationPresentConstraint PriorityModeConstraint
|
|
102
|
+
# NotificationVolumeConstraint InCallConstraint PhoneRingingConstraint
|
|
103
|
+
# BrightnessConstraint VolumeConstraint SpeakerPhoneConstraint
|
|
104
|
+
# DarkThemeConstraint ScreenOnOffConstraint VolumeLevelConstraint
|
|
105
|
+
# FaceUpDownConstraint LightLevelConstraint DeviceOrientationConstraint
|
|
106
|
+
# ProximitySensorConstraint
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
require 'yaml'
|
|
111
|
+
require 'rowx'
|
|
5
112
|
require 'uuid'
|
|
113
|
+
#require 'glw'
|
|
114
|
+
#require 'geozone'
|
|
6
115
|
require 'rxfhelper'
|
|
7
116
|
require 'chronic_cron'
|
|
8
117
|
|
|
9
118
|
|
|
119
|
+
MODEL =<<EOF
|
|
120
|
+
device
|
|
121
|
+
connectivity
|
|
122
|
+
airplane_mode is disabled
|
|
123
|
+
EOF
|
|
124
|
+
|
|
10
125
|
class TriggersNlp
|
|
11
126
|
include AppRoutes
|
|
12
127
|
|
|
@@ -20,11 +135,50 @@ class TriggersNlp
|
|
|
20
135
|
|
|
21
136
|
def triggers(params)
|
|
22
137
|
|
|
23
|
-
get /^at (\d+:\d+(?:[ap]m)?) on (.*)/i do |time, days|
|
|
138
|
+
get /^(?:at )?(\d+:\d+(?:[ap]m)?) (?:on )?(.*)/i do |time, days|
|
|
24
139
|
[TimerTrigger, {time: time, days: days}]
|
|
25
140
|
end
|
|
26
141
|
|
|
142
|
+
# time.is? 'at 18:30pm on Mon or Tue'
|
|
143
|
+
get /^time.is\? ['"](?:at )?(\d+:\d+(?:[ap]m)?) (?:on )?(.*)['"]/i do |time, days|
|
|
144
|
+
[TimerTrigger, {time: time, days: days.gsub(' or ',', ')}]
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
get /^shake[ _]device\??$/i do
|
|
148
|
+
[ShakeDeviceTrigger, {}]
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
get /^Flip Device (.*)$/i do |motion|
|
|
152
|
+
facedown = motion =~ /Face Up (?:->|to) Face Down/i
|
|
153
|
+
[FlipDeviceTrigger, {face_down: facedown }]
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
get /^flip_device_down\?$/i do
|
|
157
|
+
[FlipDeviceTrigger, {face_down: true }]
|
|
158
|
+
end
|
|
27
159
|
|
|
160
|
+
get /^flip_device_up\?$/i do
|
|
161
|
+
[FlipDeviceTrigger, {face_down: false }]
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
get /^Failed Login Attempt$/i do
|
|
165
|
+
[FailedLoginTrigger, {}]
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
get /^failed_login?$/i do
|
|
169
|
+
[FailedLoginTrigger, {}]
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
get /^Geofence (Entry|Exit) \(([^\)]+)/i do |direction, name|
|
|
173
|
+
enter_area = direction.downcase.to_sym == :entry
|
|
174
|
+
[GeofenceTrigger, {name: name, enter_area: enter_area}]
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
get /^location (entered|exited) \(([^\)]+)/i do |direction, name|
|
|
178
|
+
enter_area = direction.downcase.to_sym == :entered
|
|
179
|
+
[GeofenceTrigger, {name: name, enter_area: enter_area}]
|
|
180
|
+
end
|
|
181
|
+
|
|
28
182
|
end
|
|
29
183
|
|
|
30
184
|
alias find_trigger run_route
|
|
@@ -44,10 +198,79 @@ class ActionsNlp
|
|
|
44
198
|
|
|
45
199
|
def actions(params)
|
|
46
200
|
|
|
201
|
+
# e.g. message popup: hello world!
|
|
47
202
|
get /^message popup: (.*)/i do |msg|
|
|
48
203
|
[ToastAction, {msg: msg}]
|
|
49
204
|
end
|
|
50
205
|
|
|
206
|
+
# e.g. Popup Message 'hello world!'
|
|
207
|
+
get /^Popup[ _]Message ['"]([^'"]+)/i do |msg|
|
|
208
|
+
[ToastAction, {msg: msg}]
|
|
209
|
+
end
|
|
210
|
+
|
|
211
|
+
# e.g. say current time
|
|
212
|
+
get /^say current[ _]time/i do
|
|
213
|
+
[SayTimeAction, {}]
|
|
214
|
+
end
|
|
215
|
+
|
|
216
|
+
get /^Torch :?(.*)/i do |onoffstate|
|
|
217
|
+
state = onoffstate.downcase == 'on' ? 0 : 1
|
|
218
|
+
[CameraFlashLightAction, {state: state}]
|
|
219
|
+
end
|
|
220
|
+
|
|
221
|
+
get /^Take Picture/i do
|
|
222
|
+
[TakePictureAction, {}]
|
|
223
|
+
end
|
|
224
|
+
|
|
225
|
+
get /^take_picture/i do
|
|
226
|
+
[TakePictureAction, {}]
|
|
227
|
+
end
|
|
228
|
+
|
|
229
|
+
# e.g. Display Notification: Hi there: This is the body of the message
|
|
230
|
+
get /^Display Notification: ([^:]+): [^$]+$/i do |subject, text|
|
|
231
|
+
[NotificationAction, {subject: subject, text: text}]
|
|
232
|
+
end
|
|
233
|
+
|
|
234
|
+
|
|
235
|
+
# e.g. Enable Wifi
|
|
236
|
+
get /^(Enable|Disable) Wifi$/i do |raw_state|
|
|
237
|
+
|
|
238
|
+
state = raw_state.downcase.to_sym == :enable ? 0 : 1
|
|
239
|
+
[SetWifiAction, {state: state}]
|
|
240
|
+
|
|
241
|
+
end
|
|
242
|
+
|
|
243
|
+
# e.g. Play: Altair
|
|
244
|
+
get /^Play: (.*)$/i do |name|
|
|
245
|
+
|
|
246
|
+
[PlaySoundAction, {file_path: name}]
|
|
247
|
+
|
|
248
|
+
end
|
|
249
|
+
|
|
250
|
+
# e.g. Launch Settings
|
|
251
|
+
get /^Launch (.*)$/i do |application|
|
|
252
|
+
|
|
253
|
+
h = {
|
|
254
|
+
application_name: application,
|
|
255
|
+
package_to_launch: 'com.android.' + application.downcase
|
|
256
|
+
}
|
|
257
|
+
[LaunchActivityAction, h]
|
|
258
|
+
|
|
259
|
+
end
|
|
260
|
+
|
|
261
|
+
# e.g. HTTP GET http://someurl.com/something
|
|
262
|
+
get /^HTTP GET ([^$]+)$/i do |url|
|
|
263
|
+
|
|
264
|
+
[OpenWebPageAction, url_to_open: url]
|
|
265
|
+
|
|
266
|
+
end
|
|
267
|
+
|
|
268
|
+
# e.g. webhook entered_kitchen
|
|
269
|
+
#
|
|
270
|
+
get /webhook|HTTP GET/i do
|
|
271
|
+
[OpenWebPageAction, {}]
|
|
272
|
+
end
|
|
273
|
+
|
|
51
274
|
|
|
52
275
|
end
|
|
53
276
|
|
|
@@ -136,11 +359,11 @@ class Macro
|
|
|
136
359
|
using Params
|
|
137
360
|
|
|
138
361
|
attr_reader :local_variables, :triggers, :actions, :constraints, :guid
|
|
139
|
-
attr_accessor :title
|
|
362
|
+
attr_accessor :title, :description
|
|
140
363
|
|
|
141
|
-
def initialize(name=nil, debug: false)
|
|
364
|
+
def initialize(name=nil, geofences: geofences, debug: false)
|
|
142
365
|
|
|
143
|
-
@title, @debug = name, debug
|
|
366
|
+
@title, @geofences, @debug = name, geofences, debug
|
|
144
367
|
|
|
145
368
|
puts 'inside Macro#initialize' if @debug
|
|
146
369
|
|
|
@@ -178,7 +401,7 @@ class Macro
|
|
|
178
401
|
m_action_list: @actions.map(&:to_h),
|
|
179
402
|
m_constraint_list: @constraints.map(&:to_h),
|
|
180
403
|
m_description: '',
|
|
181
|
-
m_name:
|
|
404
|
+
m_name: title(),
|
|
182
405
|
m_excludeLog: false,
|
|
183
406
|
m_GUID: guid(),
|
|
184
407
|
m_isOrCondition: false,
|
|
@@ -194,6 +417,14 @@ class Macro
|
|
|
194
417
|
|
|
195
418
|
def import_h(h)
|
|
196
419
|
|
|
420
|
+
if @debug then
|
|
421
|
+
puts 'inside import_h'
|
|
422
|
+
puts 'h:' + h.inspect
|
|
423
|
+
end
|
|
424
|
+
|
|
425
|
+
@title = h[:name]
|
|
426
|
+
@description = h[:description]
|
|
427
|
+
|
|
197
428
|
# fetch the local variables
|
|
198
429
|
@local_variables = h['local_variables']
|
|
199
430
|
|
|
@@ -223,18 +454,20 @@ class Macro
|
|
|
223
454
|
end
|
|
224
455
|
|
|
225
456
|
def import_xml(node)
|
|
226
|
-
|
|
457
|
+
|
|
227
458
|
if @debug then
|
|
228
459
|
puts 'inside Macro#import_xml'
|
|
229
460
|
puts 'node: ' + node.xml.inspect
|
|
230
461
|
end
|
|
231
462
|
|
|
232
|
-
@title = node.attributes[:name]
|
|
233
|
-
|
|
234
463
|
if node.element('triggers') then
|
|
235
|
-
|
|
464
|
+
|
|
236
465
|
# level 2
|
|
237
466
|
|
|
467
|
+
@title = node.attributes[:name]
|
|
468
|
+
@description = node.attributes[:description]
|
|
469
|
+
|
|
470
|
+
|
|
238
471
|
# get all the triggers
|
|
239
472
|
@triggers = node.xpath('triggers/*').map do |e|
|
|
240
473
|
|
|
@@ -270,6 +503,12 @@ class Macro
|
|
|
270
503
|
|
|
271
504
|
# Level 1
|
|
272
505
|
|
|
506
|
+
puts 'import_xml: inside level 1' if @debug
|
|
507
|
+
|
|
508
|
+
@title = node.text('macro') || node.attributes[:name]
|
|
509
|
+
|
|
510
|
+
#@description = node.attributes[:description]
|
|
511
|
+
|
|
273
512
|
tp = TriggersNlp.new
|
|
274
513
|
|
|
275
514
|
@triggers = node.xpath('trigger').map do |e|
|
|
@@ -279,7 +518,11 @@ class Macro
|
|
|
279
518
|
puts 'found trigger ' + r.inspect if @debug
|
|
280
519
|
|
|
281
520
|
if r then
|
|
282
|
-
r[0]
|
|
521
|
+
if r[0] == GeofenceTrigger then
|
|
522
|
+
GeofenceTrigger.new(r[1], geofences: @geofences)
|
|
523
|
+
else
|
|
524
|
+
r[0].new(r[1])
|
|
525
|
+
end
|
|
283
526
|
end
|
|
284
527
|
|
|
285
528
|
end
|
|
@@ -288,11 +531,21 @@ class Macro
|
|
|
288
531
|
|
|
289
532
|
@actions = node.xpath('action').map do |e|
|
|
290
533
|
|
|
534
|
+
puts 'action e: ' + e.xml.inspect if @debug
|
|
291
535
|
r = ap.find_action e.text
|
|
292
536
|
puts 'found action ' + r.inspect if @debug
|
|
293
537
|
|
|
294
538
|
if r then
|
|
295
|
-
|
|
539
|
+
|
|
540
|
+
a = e.xpath('item/*')
|
|
541
|
+
|
|
542
|
+
h = if a.any? then
|
|
543
|
+
a.map {|node| [node.name.to_sym, node.text.to_s]}.to_h
|
|
544
|
+
else
|
|
545
|
+
{}
|
|
546
|
+
end
|
|
547
|
+
|
|
548
|
+
r[0].new(r[1].merge(h))
|
|
296
549
|
end
|
|
297
550
|
|
|
298
551
|
end
|
|
@@ -316,16 +569,16 @@ class Macro
|
|
|
316
569
|
|
|
317
570
|
end
|
|
318
571
|
|
|
319
|
-
def match?(triggerx, detail={time: $env[:time]} )
|
|
572
|
+
def match?(triggerx, detail={time: $env[:time]}, model=nil )
|
|
320
573
|
|
|
321
|
-
if @triggers.any? {|x| x.type == triggerx and x.match?(detail) } then
|
|
574
|
+
if @triggers.any? {|x| x.type == triggerx and x.match?(detail, model) } then
|
|
322
575
|
|
|
323
576
|
if @debug then
|
|
324
577
|
puts 'checking constraints ...'
|
|
325
578
|
puts '@constraints: ' + @constraints.inspect
|
|
326
579
|
end
|
|
327
580
|
|
|
328
|
-
if @constraints.all? {|x| x.match?($env.merge(detail)) } then
|
|
581
|
+
if @constraints.all? {|x| x.match?($env.merge(detail), model) } then
|
|
329
582
|
|
|
330
583
|
true
|
|
331
584
|
|
|
@@ -339,9 +592,65 @@ class Macro
|
|
|
339
592
|
|
|
340
593
|
end
|
|
341
594
|
|
|
595
|
+
# invokes the actions
|
|
596
|
+
#
|
|
342
597
|
def run()
|
|
343
598
|
@actions.map(&:invoke)
|
|
344
|
-
end
|
|
599
|
+
end
|
|
600
|
+
|
|
601
|
+
# prepares the environment in order for triggers to test fire successfully
|
|
602
|
+
# Used for testing
|
|
603
|
+
#
|
|
604
|
+
def set_env()
|
|
605
|
+
@triggers.each(&:set_env)
|
|
606
|
+
end
|
|
607
|
+
|
|
608
|
+
def to_pc()
|
|
609
|
+
|
|
610
|
+
heading = '# ' + @title
|
|
611
|
+
heading += '\n# ' + @description if @description
|
|
612
|
+
condition = @triggers.first.to_pc
|
|
613
|
+
actions = @actions.map(&:to_pc).join("\n")
|
|
614
|
+
|
|
615
|
+
<<EOF
|
|
616
|
+
#{heading}
|
|
617
|
+
|
|
618
|
+
if #{condition} then
|
|
619
|
+
#{actions}
|
|
620
|
+
end
|
|
621
|
+
EOF
|
|
622
|
+
end
|
|
623
|
+
|
|
624
|
+
def to_s()
|
|
625
|
+
|
|
626
|
+
a = [
|
|
627
|
+
'm: ' + @title,
|
|
628
|
+
@triggers.map {|x| "t: %s" % x}.join("\n"),
|
|
629
|
+
@actions.map {|x| "a: %s" % x}.join("\n"),
|
|
630
|
+
@constraints.map {|x| "a: %s" % x}.join("\n")
|
|
631
|
+
]
|
|
632
|
+
|
|
633
|
+
if @description and @description.length >= 1 then
|
|
634
|
+
a.insert(1, 'd: ' + @description)
|
|
635
|
+
end
|
|
636
|
+
|
|
637
|
+
a.join("\n")
|
|
638
|
+
|
|
639
|
+
end
|
|
640
|
+
|
|
641
|
+
def to_summary()
|
|
642
|
+
|
|
643
|
+
a = [
|
|
644
|
+
'm: ' + @title,
|
|
645
|
+
't: ' + @triggers.map(&:to_s).join(", "),
|
|
646
|
+
'a: ' + @actions.map(&:to_s).join(", "),
|
|
647
|
+
]
|
|
648
|
+
|
|
649
|
+
a << 'c: ' + @constraints.map(&:to_s).join(", ") if @constraints.any?
|
|
650
|
+
|
|
651
|
+
a.join("\n") + "\n"
|
|
652
|
+
|
|
653
|
+
end
|
|
345
654
|
|
|
346
655
|
private
|
|
347
656
|
|
|
@@ -353,34 +662,86 @@ class Macro
|
|
|
353
662
|
|
|
354
663
|
puts ('inside object h:' + h.inspect).debug if @debug
|
|
355
664
|
klass = Object.const_get h[:class_type]
|
|
356
|
-
klass.
|
|
665
|
+
puts klass.inspect.highlight if $debug
|
|
666
|
+
|
|
667
|
+
if klass == GeofenceTrigger then
|
|
668
|
+
puts 'GeofenceTrigger found'.highlight if $debug
|
|
669
|
+
GeofenceTrigger.new(h, geofences: @geofences)
|
|
670
|
+
else
|
|
671
|
+
klass.new h
|
|
672
|
+
end
|
|
673
|
+
|
|
357
674
|
end
|
|
358
675
|
|
|
359
676
|
end
|
|
360
677
|
|
|
361
678
|
|
|
679
|
+
class MacroDroidError < Exception
|
|
680
|
+
end
|
|
681
|
+
|
|
362
682
|
class MacroDroid
|
|
363
683
|
using ColouredText
|
|
364
684
|
using Params
|
|
365
685
|
|
|
366
|
-
attr_reader :macros
|
|
686
|
+
attr_reader :macros, :geofences
|
|
367
687
|
|
|
368
688
|
def initialize(obj=nil, debug: false)
|
|
369
689
|
|
|
370
690
|
@debug = debug
|
|
371
691
|
|
|
692
|
+
@geofences = {}
|
|
693
|
+
|
|
372
694
|
if obj then
|
|
373
695
|
|
|
374
|
-
|
|
696
|
+
raw_s, _ = RXFHelper.read(obj)
|
|
697
|
+
|
|
698
|
+
s = raw_s.strip
|
|
375
699
|
|
|
376
700
|
if s[0] == '{' then
|
|
701
|
+
|
|
377
702
|
import_json(s)
|
|
703
|
+
|
|
378
704
|
elsif s[0] == '<'
|
|
705
|
+
|
|
379
706
|
import_xml(s)
|
|
380
707
|
@h = build_h
|
|
708
|
+
|
|
381
709
|
else
|
|
382
|
-
|
|
710
|
+
|
|
711
|
+
puts 's: ' + s.inspect if @debug
|
|
712
|
+
|
|
713
|
+
if s =~ /m(?:acro)?:\s/ then
|
|
714
|
+
|
|
715
|
+
puts 'before RowX.new' if @debug
|
|
716
|
+
|
|
717
|
+
s2 = s.gsub(/^g:/,'geofence:').gsub(/^m:/,'macro:')\
|
|
718
|
+
.gsub(/^t:/,'trigger:').gsub(/^a:/,'action:')\
|
|
719
|
+
.gsub(/^c:/,'constraint:').gsub(/^#.*/,'')
|
|
720
|
+
|
|
721
|
+
raw_macros, raw_geofences = s2.split(/(?=^macro:)/,2).reverse
|
|
722
|
+
|
|
723
|
+
if raw_geofences then
|
|
724
|
+
|
|
725
|
+
geoxml = RowX.new(raw_geofences).to_xml
|
|
726
|
+
|
|
727
|
+
geodoc = Rexle.new(geoxml)
|
|
728
|
+
geofences = geodoc.root.xpath('item/geofence')
|
|
729
|
+
@geofences = fetch_geofences(geofences) if geofences.any?
|
|
730
|
+
|
|
731
|
+
end
|
|
732
|
+
|
|
733
|
+
xml = RowX.new(raw_macros).to_xml
|
|
734
|
+
import_rowxml(xml)
|
|
735
|
+
|
|
736
|
+
elsif s =~ /^# /
|
|
737
|
+
xml = pc_to_xml(s)
|
|
738
|
+
import_xml(xml)
|
|
739
|
+
else
|
|
740
|
+
raise MacroDroidError, 'invalid input'
|
|
741
|
+
end
|
|
742
|
+
|
|
383
743
|
@h = build_h
|
|
744
|
+
|
|
384
745
|
end
|
|
385
746
|
|
|
386
747
|
else
|
|
@@ -432,15 +793,85 @@ class MacroDroid
|
|
|
432
793
|
|
|
433
794
|
alias to_json export_json
|
|
434
795
|
|
|
435
|
-
def import_json(s)
|
|
436
796
|
|
|
437
|
-
|
|
438
|
-
|
|
797
|
+
def to_h()
|
|
798
|
+
|
|
799
|
+
h = {
|
|
800
|
+
geofence_data: {
|
|
801
|
+
geofence_map: @geofences.map {|key, value| [key, value.to_h] }.to_h
|
|
802
|
+
},
|
|
803
|
+
macro_list: @macros.map(&:to_h)
|
|
804
|
+
}
|
|
805
|
+
@h.merge(h).to_camel_case
|
|
806
|
+
|
|
807
|
+
end
|
|
808
|
+
|
|
809
|
+
# returns pseudocode
|
|
810
|
+
#
|
|
811
|
+
def to_pc()
|
|
812
|
+
@macros.map(&:to_pc).join("\n\n")
|
|
813
|
+
end
|
|
814
|
+
|
|
815
|
+
def to_s()
|
|
816
|
+
@macros.map(&:to_s).join("\n")
|
|
817
|
+
end
|
|
818
|
+
|
|
819
|
+
def to_summary()
|
|
820
|
+
@macros.map(&:to_summary).join("\n")
|
|
821
|
+
end
|
|
822
|
+
|
|
823
|
+
private
|
|
824
|
+
|
|
825
|
+
def fetch_geofences(nodes)
|
|
826
|
+
|
|
827
|
+
nodes.map do |e|
|
|
828
|
+
|
|
829
|
+
name = e.text.to_s.strip
|
|
830
|
+
item = e.element('item')
|
|
831
|
+
coordinates = item.text('coordinates')
|
|
832
|
+
latitude, longitude = coordinates.split(/, */,2)
|
|
833
|
+
radius = item.text('radius')
|
|
834
|
+
|
|
835
|
+
id = UUID.new.generate
|
|
836
|
+
|
|
837
|
+
h = {
|
|
838
|
+
name: name,
|
|
839
|
+
longitude: longitude,
|
|
840
|
+
latitude: latitude,
|
|
841
|
+
radius: radius,
|
|
842
|
+
id: id
|
|
843
|
+
}
|
|
844
|
+
|
|
845
|
+
[id.to_sym, GeofenceMap.new(h)]
|
|
846
|
+
|
|
847
|
+
end.to_h
|
|
439
848
|
|
|
849
|
+
end
|
|
850
|
+
|
|
851
|
+
def import_json(s)
|
|
852
|
+
|
|
853
|
+
h = JSON.parse(s, symbolize_names: true)
|
|
854
|
+
puts 'json_to_yaml: ' + h.to_yaml if @debug
|
|
855
|
+
|
|
856
|
+
@h = h.to_snake_case
|
|
857
|
+
puts ('@h: ' + @h.inspect).debug if @debug
|
|
858
|
+
|
|
859
|
+
|
|
860
|
+
# fetch the geofence data
|
|
861
|
+
if @h[:geofence_data] then
|
|
862
|
+
|
|
863
|
+
@geofences = @h[:geofence_data][:geofence_map].map do |id, properties|
|
|
864
|
+
[id, GeofenceMap.new(properties)]
|
|
865
|
+
end.to_h
|
|
866
|
+
|
|
867
|
+
end
|
|
868
|
+
|
|
440
869
|
@macros = @h[:macro_list].map do |macro|
|
|
441
870
|
|
|
442
|
-
puts ('macro: ' + macro.
|
|
443
|
-
|
|
871
|
+
puts ('macro: ' + macro.inspect).debug if @debug
|
|
872
|
+
# puts '@geofences: ' + @geofences.inspect if @debug
|
|
873
|
+
|
|
874
|
+
m = Macro.new(geofences: @geofences.map(&:last), debug: @debug )
|
|
444
875
|
m.import_h(macro)
|
|
445
876
|
m
|
|
446
877
|
|
|
@@ -450,57 +881,92 @@ class MacroDroid
|
|
|
450
881
|
|
|
451
882
|
end
|
|
452
883
|
|
|
884
|
+
def import_rowxml(raws)
|
|
885
|
+
|
|
886
|
+
s = RXFHelper.read(raws).first
|
|
887
|
+
puts 's: ' + s.inspect if @debug
|
|
888
|
+
doc = Rexle.new(s)
|
|
889
|
+
puts 'after doc' if @debug
|
|
890
|
+
puts 'import_rowxml: @geofences: ' + @geofences.inspect if @debug
|
|
891
|
+
geofences = @geofences
|
|
892
|
+
|
|
893
|
+
@macros = doc.root.xpath('item').map do |node|
|
|
894
|
+
puts ('geofences: ' + geofences.inspect).highlight if @debug
|
|
895
|
+
Macro.new(geofences: geofences.map(&:last), debug: @debug).import_xml(node)
|
|
896
|
+
|
|
897
|
+
end
|
|
898
|
+
|
|
899
|
+
end
|
|
900
|
+
|
|
453
901
|
def import_xml(raws)
|
|
454
902
|
|
|
903
|
+
if @debug then
|
|
904
|
+
puts 'inside import_xml'
|
|
905
|
+
|
|
906
|
+
puts 'raws: ' + raws.inspect
|
|
907
|
+
end
|
|
455
908
|
s = RXFHelper.read(raws).first
|
|
456
909
|
puts 's: ' + s.inspect if @debug
|
|
457
910
|
doc = Rexle.new(s)
|
|
458
|
-
puts 'after doc' if @debug
|
|
459
911
|
|
|
912
|
+
if @debug then
|
|
913
|
+
puts 'doc: ' + doc.root.xml
|
|
914
|
+
end
|
|
915
|
+
|
|
460
916
|
@macros = doc.root.xpath('macro').map do |node|
|
|
461
917
|
|
|
462
|
-
|
|
463
|
-
macro.import_xml(node)
|
|
464
|
-
macro
|
|
918
|
+
Macro.new(geofences: @geofences.map(&:last), debug: @debug).import_xml(node)
|
|
465
919
|
|
|
466
920
|
end
|
|
467
921
|
end
|
|
468
922
|
|
|
469
|
-
def
|
|
923
|
+
def pc_to_xml(s)
|
|
470
924
|
|
|
471
|
-
|
|
472
|
-
a.map!(&:chomp)
|
|
473
|
-
|
|
474
|
-
macros = a.map do |x|
|
|
475
|
-
|
|
476
|
-
lines = x.lines
|
|
477
|
-
puts 'lines: ' + lines.inspect if @debug
|
|
478
|
-
|
|
479
|
-
name = lines.shift[/^m: +(.*)/,1]
|
|
480
|
-
h = {t: [], a: [], c: []}
|
|
925
|
+
macros = s.strip.split(/(?=#)/).map do |raw_macro|
|
|
481
926
|
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
927
|
+
a = raw_macro.lines
|
|
928
|
+
name = a.shift[/(?<=# ).*/]
|
|
929
|
+
description = a.shift[/(?<=# ).*/] if a[0][/^# /]
|
|
930
|
+
body = a.join.strip
|
|
486
931
|
|
|
487
|
-
|
|
932
|
+
a2 = body.lines
|
|
933
|
+
# get the trigger
|
|
934
|
+
trigger = [:trigger, {}, a2[0][/^if (.*) then/,1]]
|
|
935
|
+
action = [:action, {}, a2[1].strip]
|
|
936
|
+
[:macro, {name: name, description: description}, trigger, action, []]
|
|
488
937
|
|
|
489
938
|
end
|
|
490
939
|
|
|
491
940
|
doc = Rexle.new([:macros, {}, '', *macros])
|
|
492
941
|
doc.root.xml pretty: true
|
|
493
942
|
|
|
494
|
-
end
|
|
495
|
-
|
|
496
|
-
def to_h()
|
|
943
|
+
end
|
|
497
944
|
|
|
498
|
-
|
|
945
|
+
end
|
|
499
946
|
|
|
947
|
+
class GeofenceMap
|
|
948
|
+
|
|
949
|
+
attr_accessor :name, :longitude, :latitude, :radius, :id
|
|
950
|
+
|
|
951
|
+
def initialize(id: '', longitude: '', latitude: '', name: '', radius: '')
|
|
952
|
+
|
|
953
|
+
@id, @latitude, @longitude, @name, @radius = id, latitude, \
|
|
954
|
+
longitude, name, radius
|
|
955
|
+
|
|
500
956
|
end
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
957
|
+
|
|
958
|
+
def to_h()
|
|
959
|
+
|
|
960
|
+
{
|
|
961
|
+
id: @id,
|
|
962
|
+
longitude: @longitude,
|
|
963
|
+
latitude: @latitude,
|
|
964
|
+
name: @name,
|
|
965
|
+
radius: @radius
|
|
966
|
+
}
|
|
967
|
+
|
|
968
|
+
end
|
|
969
|
+
|
|
504
970
|
end
|
|
505
971
|
|
|
506
972
|
class MacroObject
|
|
@@ -511,6 +977,8 @@ class MacroObject
|
|
|
511
977
|
|
|
512
978
|
def initialize(h={})
|
|
513
979
|
|
|
980
|
+
$env ||= {}
|
|
981
|
+
|
|
514
982
|
@h = {constraint_list: [], is_or_condition: false,
|
|
515
983
|
is_disabled: false}.merge(h)
|
|
516
984
|
@list = []
|
|
@@ -539,6 +1007,10 @@ class MacroObject
|
|
|
539
1007
|
h2.merge('m_classType' => self.class.to_s)
|
|
540
1008
|
|
|
541
1009
|
end
|
|
1010
|
+
|
|
1011
|
+
def to_s()
|
|
1012
|
+
"#<%s %s>" % [self.class, @h.inspect]
|
|
1013
|
+
end
|
|
542
1014
|
|
|
543
1015
|
protected
|
|
544
1016
|
|
|
@@ -562,10 +1034,11 @@ class Trigger < MacroObject
|
|
|
562
1034
|
@list << 'fakeIcon'
|
|
563
1035
|
end
|
|
564
1036
|
|
|
565
|
-
def match?(detail={})
|
|
1037
|
+
def match?(detail={}, model=nil)
|
|
1038
|
+
|
|
1039
|
+
# only match where the key exists in the trigger object
|
|
1040
|
+
detail.select {|k,v| @h.include? k }.all? {|key,value| @h[key] == value}
|
|
566
1041
|
|
|
567
|
-
detail.all? {|key,value| @h[key] == value}
|
|
568
|
-
|
|
569
1042
|
end
|
|
570
1043
|
|
|
571
1044
|
end
|
|
@@ -1052,27 +1525,50 @@ class TimerTrigger < Trigger
|
|
|
1052
1525
|
use_alarm: false
|
|
1053
1526
|
}
|
|
1054
1527
|
|
|
1055
|
-
super(options.merge filter(options,h))
|
|
1528
|
+
super(options.merge filter(options, h))
|
|
1056
1529
|
|
|
1057
1530
|
end
|
|
1058
1531
|
|
|
1059
|
-
def match?(detail={time: $env[:time]})
|
|
1532
|
+
def match?(detail={time: $env[:time]}, model=nil)
|
|
1533
|
+
|
|
1534
|
+
time() == detail[:time]
|
|
1060
1535
|
|
|
1061
|
-
|
|
1062
|
-
|
|
1536
|
+
end
|
|
1537
|
+
|
|
1538
|
+
# sets the environmental conditions for this trigger to fire
|
|
1539
|
+
#
|
|
1540
|
+
def set_env()
|
|
1541
|
+
$env[:time] = time()
|
|
1542
|
+
end
|
|
1543
|
+
|
|
1544
|
+
def to_pc()
|
|
1545
|
+
"time.is? '%s'" % self.to_s.gsub(',', ' or')
|
|
1546
|
+
end
|
|
1547
|
+
|
|
1548
|
+
def to_s()
|
|
1549
|
+
|
|
1550
|
+
dow = @h[:days_of_week]
|
|
1063
1551
|
|
|
1064
|
-
|
|
1552
|
+
a = Date::ABBR_DAYNAMES
|
|
1065
1553
|
|
|
1066
|
-
|
|
1554
|
+
time = Time.parse("%s:%s" % [@h[:hour], @h[:minute]]).strftime("%-H:%M%P")
|
|
1555
|
+
days = (a[1..-1] << a.first).zip(dow).select {|_,b| b}.map(&:first)
|
|
1067
1556
|
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1557
|
+
"at %s on %s" % [time, days.join(', ')]
|
|
1558
|
+
end
|
|
1559
|
+
|
|
1560
|
+
private
|
|
1561
|
+
|
|
1562
|
+
def time()
|
|
1073
1563
|
|
|
1074
|
-
|
|
1564
|
+
a = @h[:days_of_week].clone
|
|
1565
|
+
a.unshift a.pop
|
|
1075
1566
|
|
|
1567
|
+
dow = a.map.with_index {|x, i| x ? i : nil }.compact.join(',')
|
|
1568
|
+
s = "%s %s * * %s" % [@h[:minute], @h[:hour], dow]
|
|
1569
|
+
recent_time = ($env && $env[:time]) ? $env[:time] : Time.now
|
|
1570
|
+
ChronicCron.new(s, recent_time).to_time
|
|
1571
|
+
|
|
1076
1572
|
end
|
|
1077
1573
|
|
|
1078
1574
|
end
|
|
@@ -1145,6 +1641,15 @@ class RegularIntervalTrigger < Trigger
|
|
|
1145
1641
|
|
|
1146
1642
|
end
|
|
1147
1643
|
|
|
1644
|
+
class DeviceEventsTrigger < Trigger
|
|
1645
|
+
|
|
1646
|
+
def initialize(h={})
|
|
1647
|
+
super(h)
|
|
1648
|
+
@group = 'device_events'
|
|
1649
|
+
end
|
|
1650
|
+
|
|
1651
|
+
end
|
|
1652
|
+
|
|
1148
1653
|
# Category: Device Events
|
|
1149
1654
|
#
|
|
1150
1655
|
# Airplane Mode Changed
|
|
@@ -1156,7 +1661,7 @@ end
|
|
|
1156
1661
|
# shorthand example:
|
|
1157
1662
|
# airplanemode: enabled
|
|
1158
1663
|
#
|
|
1159
|
-
class AirplaneModeTrigger <
|
|
1664
|
+
class AirplaneModeTrigger < DeviceEventsTrigger
|
|
1160
1665
|
|
|
1161
1666
|
def initialize(h={})
|
|
1162
1667
|
|
|
@@ -1172,7 +1677,7 @@ end
|
|
|
1172
1677
|
|
|
1173
1678
|
# Category: Device Events
|
|
1174
1679
|
#
|
|
1175
|
-
class AutoSyncChangeTrigger <
|
|
1680
|
+
class AutoSyncChangeTrigger < DeviceEventsTrigger
|
|
1176
1681
|
|
|
1177
1682
|
def initialize(h={})
|
|
1178
1683
|
|
|
@@ -1188,7 +1693,7 @@ end
|
|
|
1188
1693
|
|
|
1189
1694
|
# Category: Device Events
|
|
1190
1695
|
#
|
|
1191
|
-
class DayDreamTrigger <
|
|
1696
|
+
class DayDreamTrigger < DeviceEventsTrigger
|
|
1192
1697
|
|
|
1193
1698
|
def initialize(h={})
|
|
1194
1699
|
|
|
@@ -1204,7 +1709,7 @@ end
|
|
|
1204
1709
|
|
|
1205
1710
|
# Category: Device Events
|
|
1206
1711
|
#
|
|
1207
|
-
class DockTrigger <
|
|
1712
|
+
class DockTrigger < DeviceEventsTrigger
|
|
1208
1713
|
|
|
1209
1714
|
def initialize(h={})
|
|
1210
1715
|
|
|
@@ -1220,7 +1725,30 @@ end
|
|
|
1220
1725
|
|
|
1221
1726
|
# Category: Device Events
|
|
1222
1727
|
#
|
|
1223
|
-
class
|
|
1728
|
+
class FailedLoginTrigger < DeviceEventsTrigger
|
|
1729
|
+
|
|
1730
|
+
def initialize(h={})
|
|
1731
|
+
|
|
1732
|
+
options = {
|
|
1733
|
+
num_failures: 1
|
|
1734
|
+
}
|
|
1735
|
+
|
|
1736
|
+
super(options.merge h)
|
|
1737
|
+
|
|
1738
|
+
end
|
|
1739
|
+
|
|
1740
|
+
def to_pc()
|
|
1741
|
+
'failed_login?'
|
|
1742
|
+
end
|
|
1743
|
+
|
|
1744
|
+
def to_s()
|
|
1745
|
+
'Failed Login Attempt'
|
|
1746
|
+
end
|
|
1747
|
+
end
|
|
1748
|
+
|
|
1749
|
+
# Category: Device Events
|
|
1750
|
+
#
|
|
1751
|
+
class GPSEnabledTrigger < DeviceEventsTrigger
|
|
1224
1752
|
|
|
1225
1753
|
def initialize(h={})
|
|
1226
1754
|
|
|
@@ -1236,7 +1764,7 @@ end
|
|
|
1236
1764
|
|
|
1237
1765
|
# Category: Device Events
|
|
1238
1766
|
#
|
|
1239
|
-
class MusicPlayingTrigger <
|
|
1767
|
+
class MusicPlayingTrigger < DeviceEventsTrigger
|
|
1240
1768
|
|
|
1241
1769
|
def initialize(h={})
|
|
1242
1770
|
|
|
@@ -1253,7 +1781,7 @@ end
|
|
|
1253
1781
|
|
|
1254
1782
|
# Category: Device Events
|
|
1255
1783
|
#
|
|
1256
|
-
class DeviceUnlockedTrigger <
|
|
1784
|
+
class DeviceUnlockedTrigger < DeviceEventsTrigger
|
|
1257
1785
|
|
|
1258
1786
|
def initialize(h={})
|
|
1259
1787
|
|
|
@@ -1268,7 +1796,7 @@ end
|
|
|
1268
1796
|
|
|
1269
1797
|
# Category: Device Events
|
|
1270
1798
|
#
|
|
1271
|
-
class AutoRotateChangeTrigger <
|
|
1799
|
+
class AutoRotateChangeTrigger < DeviceEventsTrigger
|
|
1272
1800
|
|
|
1273
1801
|
def initialize(h={})
|
|
1274
1802
|
|
|
@@ -1284,7 +1812,7 @@ end
|
|
|
1284
1812
|
|
|
1285
1813
|
# Category: Device Events
|
|
1286
1814
|
#
|
|
1287
|
-
class ClipboardChangeTrigger <
|
|
1815
|
+
class ClipboardChangeTrigger < DeviceEventsTrigger
|
|
1288
1816
|
|
|
1289
1817
|
def initialize(h={})
|
|
1290
1818
|
|
|
@@ -1301,7 +1829,7 @@ end
|
|
|
1301
1829
|
|
|
1302
1830
|
# Category: Device Events
|
|
1303
1831
|
#
|
|
1304
|
-
class BootTrigger <
|
|
1832
|
+
class BootTrigger < DeviceEventsTrigger
|
|
1305
1833
|
|
|
1306
1834
|
def initialize(h={})
|
|
1307
1835
|
|
|
@@ -1316,7 +1844,7 @@ end
|
|
|
1316
1844
|
|
|
1317
1845
|
# Category: Device Events
|
|
1318
1846
|
#
|
|
1319
|
-
class IntentReceivedTrigger <
|
|
1847
|
+
class IntentReceivedTrigger < DeviceEventsTrigger
|
|
1320
1848
|
|
|
1321
1849
|
def initialize(h={})
|
|
1322
1850
|
|
|
@@ -1336,7 +1864,7 @@ end
|
|
|
1336
1864
|
|
|
1337
1865
|
# Category: Device Events
|
|
1338
1866
|
#
|
|
1339
|
-
class NotificationTrigger <
|
|
1867
|
+
class NotificationTrigger < DeviceEventsTrigger
|
|
1340
1868
|
|
|
1341
1869
|
def initialize(h={})
|
|
1342
1870
|
|
|
@@ -1362,7 +1890,7 @@ end
|
|
|
1362
1890
|
|
|
1363
1891
|
# Category: Device Events
|
|
1364
1892
|
#
|
|
1365
|
-
class ScreenOnOffTrigger <
|
|
1893
|
+
class ScreenOnOffTrigger < DeviceEventsTrigger
|
|
1366
1894
|
|
|
1367
1895
|
def initialize(h={})
|
|
1368
1896
|
|
|
@@ -1378,7 +1906,7 @@ end
|
|
|
1378
1906
|
|
|
1379
1907
|
# Category: Device Events
|
|
1380
1908
|
#
|
|
1381
|
-
class SilentModeTrigger <
|
|
1909
|
+
class SilentModeTrigger < DeviceEventsTrigger
|
|
1382
1910
|
|
|
1383
1911
|
def initialize(h={})
|
|
1384
1912
|
|
|
@@ -1421,8 +1949,15 @@ end
|
|
|
1421
1949
|
#
|
|
1422
1950
|
class GeofenceTrigger < Trigger
|
|
1423
1951
|
|
|
1424
|
-
def initialize(h={})
|
|
1952
|
+
def initialize( h={}, geofences: {})
|
|
1425
1953
|
|
|
1954
|
+
if h[:name] then
|
|
1955
|
+
puts ('geofences2: ' + geofences.inspect)
|
|
1956
|
+
found = geofences.find {|x| x.name.downcase == h[:name].downcase}
|
|
1957
|
+
h[:geofence_id] = found.id if found
|
|
1958
|
+
|
|
1959
|
+
end
|
|
1960
|
+
|
|
1426
1961
|
options = {
|
|
1427
1962
|
update_rate_text: '5 Minutes',
|
|
1428
1963
|
geofence_id: '',
|
|
@@ -1431,8 +1966,27 @@ class GeofenceTrigger < Trigger
|
|
|
1431
1966
|
enter_area: true
|
|
1432
1967
|
}
|
|
1433
1968
|
|
|
1434
|
-
super(options.merge h)
|
|
1969
|
+
super(options.merge filter(options, h))
|
|
1970
|
+
@geofences = geofences
|
|
1971
|
+
|
|
1972
|
+
end
|
|
1973
|
+
|
|
1974
|
+
def to_s()
|
|
1975
|
+
|
|
1976
|
+
if $debug then
|
|
1977
|
+
puts ' @geofences: ' + @geofences.inspect
|
|
1978
|
+
puts '@h: ' + @h.inspect
|
|
1979
|
+
puts '@h[:geofence_id]: ' + @h[:geofence_id].inspect
|
|
1980
|
+
end
|
|
1981
|
+
|
|
1982
|
+
direction = @h[:enter_area] ? 'Entry' : 'Exit'
|
|
1983
|
+
|
|
1984
|
+
found = @geofences.find {|x| x.id == @h[:geofence_id]}
|
|
1985
|
+
puts 'found: ' + found.inspect
|
|
1986
|
+
label = found ? found.name : 'error: name not found'
|
|
1435
1987
|
|
|
1988
|
+
"Geofence %s (%s)" % [direction, label]
|
|
1989
|
+
|
|
1436
1990
|
end
|
|
1437
1991
|
|
|
1438
1992
|
end
|
|
@@ -1454,9 +2008,19 @@ class SunriseSunsetTrigger < Trigger
|
|
|
1454
2008
|
|
|
1455
2009
|
end
|
|
1456
2010
|
|
|
2011
|
+
|
|
2012
|
+
class SensorsTrigger < Trigger
|
|
2013
|
+
|
|
2014
|
+
def initialize(h={})
|
|
2015
|
+
super(h)
|
|
2016
|
+
@group = 'sensors'
|
|
2017
|
+
end
|
|
2018
|
+
|
|
2019
|
+
end
|
|
2020
|
+
|
|
1457
2021
|
# Category: Sensors
|
|
1458
2022
|
#
|
|
1459
|
-
class ActivityRecognitionTrigger <
|
|
2023
|
+
class ActivityRecognitionTrigger < SensorsTrigger
|
|
1460
2024
|
|
|
1461
2025
|
def initialize(h={})
|
|
1462
2026
|
|
|
@@ -1473,7 +2037,7 @@ end
|
|
|
1473
2037
|
|
|
1474
2038
|
# Category: Sensors
|
|
1475
2039
|
#
|
|
1476
|
-
class ProximityTrigger <
|
|
2040
|
+
class ProximityTrigger < SensorsTrigger
|
|
1477
2041
|
|
|
1478
2042
|
def initialize(h={})
|
|
1479
2043
|
|
|
@@ -1490,7 +2054,7 @@ end
|
|
|
1490
2054
|
|
|
1491
2055
|
# Category: Sensors
|
|
1492
2056
|
#
|
|
1493
|
-
class ShakeDeviceTrigger <
|
|
2057
|
+
class ShakeDeviceTrigger < SensorsTrigger
|
|
1494
2058
|
|
|
1495
2059
|
def initialize(h={})
|
|
1496
2060
|
|
|
@@ -1500,12 +2064,25 @@ class ShakeDeviceTrigger < Trigger
|
|
|
1500
2064
|
super(options.merge h)
|
|
1501
2065
|
|
|
1502
2066
|
end
|
|
2067
|
+
|
|
2068
|
+
def to_pc()
|
|
2069
|
+
'shake_device?'
|
|
2070
|
+
end
|
|
2071
|
+
|
|
2072
|
+
def to_s()
|
|
2073
|
+
'Shake Device'
|
|
2074
|
+
end
|
|
1503
2075
|
|
|
1504
2076
|
end
|
|
1505
2077
|
|
|
1506
2078
|
# Category: Sensors
|
|
1507
2079
|
#
|
|
1508
|
-
|
|
2080
|
+
# options:
|
|
2081
|
+
# Face Up -> Face Down
|
|
2082
|
+
# Face Down -> Face Up
|
|
2083
|
+
# Any -> Face Down
|
|
2084
|
+
#
|
|
2085
|
+
class FlipDeviceTrigger < SensorsTrigger
|
|
1509
2086
|
|
|
1510
2087
|
def initialize(h={})
|
|
1511
2088
|
|
|
@@ -1517,13 +2094,23 @@ class FlipDeviceTrigger < Trigger
|
|
|
1517
2094
|
|
|
1518
2095
|
super(options.merge h)
|
|
1519
2096
|
|
|
2097
|
+
end
|
|
2098
|
+
|
|
2099
|
+
def to_pc()
|
|
2100
|
+
@h[:face_down] ? 'flip_device_down?' : 'flip_device_up?'
|
|
1520
2101
|
end
|
|
2102
|
+
|
|
2103
|
+
def to_s()
|
|
2104
|
+
|
|
2105
|
+
action = @h[:face_down] ? 'Face Up -> Face Down' : 'Face Down -> Face Up'
|
|
2106
|
+
'Flip Device ' + action
|
|
2107
|
+
end
|
|
1521
2108
|
|
|
1522
2109
|
end
|
|
1523
2110
|
|
|
1524
2111
|
# Category: Sensors
|
|
1525
2112
|
#
|
|
1526
|
-
class OrientationTrigger <
|
|
2113
|
+
class OrientationTrigger < SensorsTrigger
|
|
1527
2114
|
|
|
1528
2115
|
def initialize(h={})
|
|
1529
2116
|
|
|
@@ -1705,6 +2292,10 @@ class LaunchActivityAction < ApplicationAction
|
|
|
1705
2292
|
super(options.merge h)
|
|
1706
2293
|
|
|
1707
2294
|
end
|
|
2295
|
+
|
|
2296
|
+
def to_s()
|
|
2297
|
+
'Launch ' + @h[:application_name]
|
|
2298
|
+
end
|
|
1708
2299
|
|
|
1709
2300
|
end
|
|
1710
2301
|
|
|
@@ -1730,6 +2321,8 @@ end
|
|
|
1730
2321
|
class OpenWebPageAction < ApplicationAction
|
|
1731
2322
|
|
|
1732
2323
|
def initialize(h={})
|
|
2324
|
+
|
|
2325
|
+
h[:url_to_open] = h[:url] if h[:url]
|
|
1733
2326
|
|
|
1734
2327
|
options = {
|
|
1735
2328
|
variable_to_save_response: {:m_stringValue=>"", :m_name=>"", :m_decimalValue=>0.0, :isLocal=>true, :m_booleanValue=>false, :excludeFromLog=>false, :m_intValue=>0, :m_type=>2},
|
|
@@ -1739,9 +2332,13 @@ class OpenWebPageAction < ApplicationAction
|
|
|
1739
2332
|
block_next_action: false
|
|
1740
2333
|
}
|
|
1741
2334
|
|
|
1742
|
-
super(options.merge h)
|
|
2335
|
+
super(options.merge filter(options,h))
|
|
1743
2336
|
|
|
1744
2337
|
end
|
|
2338
|
+
|
|
2339
|
+
def to_s()
|
|
2340
|
+
"HTTP GET\n url: " + @h[:url_to_open]
|
|
2341
|
+
end
|
|
1745
2342
|
|
|
1746
2343
|
end
|
|
1747
2344
|
|
|
@@ -1789,6 +2386,15 @@ class TakePictureAction < CameraAction
|
|
|
1789
2386
|
super(options.merge h)
|
|
1790
2387
|
|
|
1791
2388
|
end
|
|
2389
|
+
|
|
2390
|
+
def to_pc()
|
|
2391
|
+
camera = @h[:use_front_camera] ? :front : :back
|
|
2392
|
+
'take_photo :' + camera.to_s
|
|
2393
|
+
end
|
|
2394
|
+
|
|
2395
|
+
def to_s()
|
|
2396
|
+
'Take Picture'
|
|
2397
|
+
end
|
|
1792
2398
|
|
|
1793
2399
|
end
|
|
1794
2400
|
|
|
@@ -1817,6 +2423,11 @@ class SetWifiAction < ConnectivityAction
|
|
|
1817
2423
|
super(options.merge h)
|
|
1818
2424
|
|
|
1819
2425
|
end
|
|
2426
|
+
|
|
2427
|
+
def to_s()
|
|
2428
|
+
action = @h[:state] == 0 ? 'Enable' : 'Disable'
|
|
2429
|
+
action + ' Wifi'
|
|
2430
|
+
end
|
|
1820
2431
|
|
|
1821
2432
|
end
|
|
1822
2433
|
|
|
@@ -1947,6 +2558,20 @@ class SayTimeAction < DateTimeAction
|
|
|
1947
2558
|
super(options.merge h)
|
|
1948
2559
|
|
|
1949
2560
|
end
|
|
2561
|
+
|
|
2562
|
+
def invoke()
|
|
2563
|
+
time = ($env and $env[:time]) ? $env[:time] : Time.now
|
|
2564
|
+
tformat = @h['12_hour'] ? "%-I:%M%P" : "%H:%M"
|
|
2565
|
+
super(time.strftime(tformat))
|
|
2566
|
+
end
|
|
2567
|
+
|
|
2568
|
+
def to_pc()
|
|
2569
|
+
'say current_time()'
|
|
2570
|
+
end
|
|
2571
|
+
|
|
2572
|
+
def to_s()
|
|
2573
|
+
'Say Current Time'
|
|
2574
|
+
end
|
|
1950
2575
|
|
|
1951
2576
|
end
|
|
1952
2577
|
|
|
@@ -2118,6 +2743,14 @@ class CameraFlashLightAction < DeviceSettingsAction
|
|
|
2118
2743
|
|
|
2119
2744
|
end
|
|
2120
2745
|
|
|
2746
|
+
def to_pc()
|
|
2747
|
+
'torch :on'
|
|
2748
|
+
end
|
|
2749
|
+
|
|
2750
|
+
def to_s()
|
|
2751
|
+
'Torch On'
|
|
2752
|
+
end
|
|
2753
|
+
|
|
2121
2754
|
end
|
|
2122
2755
|
|
|
2123
2756
|
# Category: Device Settings
|
|
@@ -2455,6 +3088,10 @@ class PlaySoundAction < MediaAction
|
|
|
2455
3088
|
super(options.merge h)
|
|
2456
3089
|
|
|
2457
3090
|
end
|
|
3091
|
+
|
|
3092
|
+
def to_s()
|
|
3093
|
+
'Play: ' + @h[:file_path]
|
|
3094
|
+
end
|
|
2458
3095
|
|
|
2459
3096
|
end
|
|
2460
3097
|
|
|
@@ -2659,6 +3296,9 @@ end
|
|
|
2659
3296
|
class NotificationAction < NotificationsAction
|
|
2660
3297
|
|
|
2661
3298
|
def initialize(h={})
|
|
3299
|
+
|
|
3300
|
+
h[:notification_subject] = h[:subject] if h[:subject]
|
|
3301
|
+
h[:notification_text] = h[:text] if h[:text]
|
|
2662
3302
|
|
|
2663
3303
|
options = {
|
|
2664
3304
|
ringtone_name: 'Default',
|
|
@@ -2674,9 +3314,13 @@ class NotificationAction < NotificationsAction
|
|
|
2674
3314
|
run_macro_when_pressed: false
|
|
2675
3315
|
}
|
|
2676
3316
|
|
|
2677
|
-
super(options.merge h)
|
|
3317
|
+
super(options.merge filter(options, h))
|
|
2678
3318
|
|
|
2679
3319
|
end
|
|
3320
|
+
|
|
3321
|
+
def to_s()
|
|
3322
|
+
'Display Notification: ' + "%s: %s" % [@h[:notification_subject], @h[:notification_text]]
|
|
3323
|
+
end
|
|
2680
3324
|
|
|
2681
3325
|
end
|
|
2682
3326
|
|
|
@@ -2709,6 +3353,14 @@ class ToastAction < NotificationsAction
|
|
|
2709
3353
|
def invoke()
|
|
2710
3354
|
super(@h[:message_text])
|
|
2711
3355
|
end
|
|
3356
|
+
|
|
3357
|
+
def to_pc()
|
|
3358
|
+
"popup_message '%s'" % @h[:message_text]
|
|
3359
|
+
end
|
|
3360
|
+
|
|
3361
|
+
def to_s()
|
|
3362
|
+
"Popup Message '%s'" % @h[:message_text]
|
|
3363
|
+
end
|
|
2712
3364
|
|
|
2713
3365
|
end
|
|
2714
3366
|
|
|
@@ -3037,6 +3689,30 @@ class Constraint < MacroObject
|
|
|
3037
3689
|
def initialize(h={})
|
|
3038
3690
|
super(h)
|
|
3039
3691
|
end
|
|
3692
|
+
|
|
3693
|
+
def match?(detail={}, model=nil)
|
|
3694
|
+
|
|
3695
|
+
detail.select {|k,v| @h.include? k }.all? {|key,value| @h[key] == value}
|
|
3696
|
+
|
|
3697
|
+
end
|
|
3698
|
+
|
|
3699
|
+
#def to_s()
|
|
3700
|
+
# ''
|
|
3701
|
+
#end
|
|
3702
|
+
|
|
3703
|
+
protected
|
|
3704
|
+
|
|
3705
|
+
def toggle_match?(key, val)
|
|
3706
|
+
|
|
3707
|
+
if @h[key] == true and val == key.to_s then
|
|
3708
|
+
true
|
|
3709
|
+
elsif @h[key] == false and val != key.to_s
|
|
3710
|
+
true
|
|
3711
|
+
else
|
|
3712
|
+
false
|
|
3713
|
+
end
|
|
3714
|
+
|
|
3715
|
+
end
|
|
3040
3716
|
|
|
3041
3717
|
end
|
|
3042
3718
|
|
|
@@ -3398,6 +4074,41 @@ class AirplaneModeConstraint < Constraint
|
|
|
3398
4074
|
super(options.merge h)
|
|
3399
4075
|
|
|
3400
4076
|
end
|
|
4077
|
+
|
|
4078
|
+
def match?(detail={}, model=nil)
|
|
4079
|
+
|
|
4080
|
+
puts 'inside airplaneModeConstraint#match?' if $debug
|
|
4081
|
+
|
|
4082
|
+
if detail.has_key? :enabled then
|
|
4083
|
+
|
|
4084
|
+
puts 'detail has the key' if $debug
|
|
4085
|
+
super(detail)
|
|
4086
|
+
|
|
4087
|
+
elsif model
|
|
4088
|
+
|
|
4089
|
+
if $debug then
|
|
4090
|
+
puts 'checking the model'
|
|
4091
|
+
switch = model.connectivity.airplane_mode.switch
|
|
4092
|
+
puts 'switch: ' + switch.inspect
|
|
4093
|
+
end
|
|
4094
|
+
|
|
4095
|
+
toggle_match?(:enabled, switch)
|
|
4096
|
+
|
|
4097
|
+
end
|
|
4098
|
+
|
|
4099
|
+
end
|
|
4100
|
+
|
|
4101
|
+
def to_pc()
|
|
4102
|
+
status = @h[:enabled] ? 'enabled?' : 'disabled?'
|
|
4103
|
+
'airplane_mode.' + status
|
|
4104
|
+
end
|
|
4105
|
+
|
|
4106
|
+
def to_s()
|
|
4107
|
+
|
|
4108
|
+
status = @h[:enabled] ? 'Enabled' : 'Disabled'
|
|
4109
|
+
'Airplane Mode ' + status
|
|
4110
|
+
|
|
4111
|
+
end
|
|
3401
4112
|
|
|
3402
4113
|
end
|
|
3403
4114
|
|