ruby-macrodroid 0.5.2 → 0.7.3
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 +792 -92
- metadata +84 -4
- 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: cd79e55449071a3fd0ed864f58fc606dedd69059413876c7466cb071f651fef4
|
4
|
+
data.tar.gz: 70dbc1fd844ff449d0ef1ca680e9b47e5233ff82148ab93063b65d9a87e5f70c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 1912e55be98c1236a2df6f17e5e19aa1de75e89854ca3be343821133e841656bb45c116e195237a313726b590c31bb7c898f8cf54f868f37c26f3dd9b11f4694
|
7
|
+
data.tar.gz: fdc3adcb802125e4346bbed8fc089e57a303d2f5624e20fe6b597b03a594ae50ca388b13c54bcb43da3bfb265e6fabc1b46f00119a48f199645ee5d8bdcf0c24
|
checksums.yaml.gz.sig
CHANGED
Binary file
|
data.tar.gz.sig
CHANGED
Binary file
|
data/lib/ruby-macrodroid.rb
CHANGED
@@ -2,9 +2,121 @@
|
|
2
2
|
|
3
3
|
# file: ruby-macrodroid.rb
|
4
4
|
|
5
|
-
|
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
|
+
|
6
110
|
require 'yaml'
|
7
|
-
require '
|
111
|
+
require 'rowx'
|
112
|
+
require 'uuid'
|
113
|
+
#require 'glw'
|
114
|
+
#require 'geozone'
|
115
|
+
require 'subunit'
|
116
|
+
#require 'rxfhelper'
|
117
|
+
require 'requestor'
|
118
|
+
eval Requestor.read('http://a0.jamesrobertson.eu/rorb/r/ruby'){|x| x.require 'rxfhelper' }
|
119
|
+
|
8
120
|
require 'chronic_cron'
|
9
121
|
|
10
122
|
|
@@ -60,7 +172,25 @@ class TriggersNlp
|
|
60
172
|
get /^failed_login?$/i do
|
61
173
|
[FailedLoginTrigger, {}]
|
62
174
|
end
|
63
|
-
|
175
|
+
|
176
|
+
get /^Geofence (Entry|Exit) \(([^\)]+)/i do |direction, name|
|
177
|
+
enter_area = direction.downcase.to_sym == :entry
|
178
|
+
[GeofenceTrigger, {name: name, enter_area: enter_area}]
|
179
|
+
end
|
180
|
+
|
181
|
+
get /^location (entered|exited) \(([^\)]+)/i do |direction, name|
|
182
|
+
enter_area = direction.downcase.to_sym == :entered
|
183
|
+
[GeofenceTrigger, {name: name, enter_area: enter_area}]
|
184
|
+
end
|
185
|
+
|
186
|
+
# eg. Proximity Sensor (Near)
|
187
|
+
#
|
188
|
+
get /^Proximity Sensor \(([^\)]+)\)/i do |distance|
|
189
|
+
|
190
|
+
[ProximityTrigger, {distance: distance}]
|
191
|
+
end
|
192
|
+
|
193
|
+
|
64
194
|
|
65
195
|
end
|
66
196
|
|
@@ -81,14 +211,17 @@ class ActionsNlp
|
|
81
211
|
|
82
212
|
def actions(params)
|
83
213
|
|
214
|
+
# e.g. message popup: hello world!
|
84
215
|
get /^message popup: (.*)/i do |msg|
|
85
216
|
[ToastAction, {msg: msg}]
|
86
217
|
end
|
87
218
|
|
219
|
+
# e.g. Popup Message 'hello world!'
|
88
220
|
get /^Popup[ _]Message ['"]([^'"]+)/i do |msg|
|
89
221
|
[ToastAction, {msg: msg}]
|
90
222
|
end
|
91
223
|
|
224
|
+
# e.g. say current time
|
92
225
|
get /^say current[ _]time/i do
|
93
226
|
[SayTimeAction, {}]
|
94
227
|
end
|
@@ -105,6 +238,78 @@ class ActionsNlp
|
|
105
238
|
get /^take_picture/i do
|
106
239
|
[TakePictureAction, {}]
|
107
240
|
end
|
241
|
+
|
242
|
+
# e.g. Display Notification: Hi there: This is the body of the message
|
243
|
+
get /^Display Notification: ([^:]+): [^$]+$/i do |subject, text|
|
244
|
+
[NotificationAction, {subject: subject, text: text}]
|
245
|
+
end
|
246
|
+
|
247
|
+
|
248
|
+
# e.g. Enable Wifi
|
249
|
+
get /^(Enable|Disable) Wifi$/i do |raw_state|
|
250
|
+
|
251
|
+
state = raw_state.downcase.to_sym == :enable ? 0 : 1
|
252
|
+
[SetWifiAction, {state: state}]
|
253
|
+
|
254
|
+
end
|
255
|
+
|
256
|
+
# e.g. Play: Altair
|
257
|
+
get /^Play: (.*)$/i do |name|
|
258
|
+
|
259
|
+
[PlaySoundAction, {file_path: name}]
|
260
|
+
|
261
|
+
end
|
262
|
+
|
263
|
+
# e.g. Launch Settings
|
264
|
+
get /^Launch (.*)$/i do |application|
|
265
|
+
|
266
|
+
h = {
|
267
|
+
application_name: application,
|
268
|
+
package_to_launch: 'com.android.' + application.downcase
|
269
|
+
}
|
270
|
+
[LaunchActivityAction, h]
|
271
|
+
|
272
|
+
end
|
273
|
+
|
274
|
+
# e.g. HTTP GET http://someurl.com/something
|
275
|
+
get /^HTTP GET ([^$]+)$/i do |url|
|
276
|
+
|
277
|
+
[OpenWebPageAction, url_to_open: url]
|
278
|
+
|
279
|
+
end
|
280
|
+
|
281
|
+
# e.g. webhook entered_kitchen
|
282
|
+
#
|
283
|
+
get /webhook|HTTP GET/i do
|
284
|
+
[OpenWebPageAction, {}]
|
285
|
+
end
|
286
|
+
|
287
|
+
#a: Keep Device Awake Screen On Until Disabled
|
288
|
+
#
|
289
|
+
get /Keep Device Awake Screen On Until Disabled/i do
|
290
|
+
[KeepAwakeAction, {enabled: true, permanent: true, screen_option: 0}]
|
291
|
+
end
|
292
|
+
|
293
|
+
|
294
|
+
#a: Keep Device Awake Screen On 1h 1m 1s
|
295
|
+
#
|
296
|
+
get /Keep Device Awake Screen On ([^$]+)/i do |duration|
|
297
|
+
|
298
|
+
a = duration.split.map(&:to_i)
|
299
|
+
secs = Subunit.new(units={minutes:60, hours:60, seconds: 60}, a).to_i
|
300
|
+
|
301
|
+
h = {
|
302
|
+
permanent: true, screen_option: 0, seconds_to_stay_awake_for: secs
|
303
|
+
}
|
304
|
+
[KeepAwakeAction, h]
|
305
|
+
end
|
306
|
+
|
307
|
+
#a: Disable Keep Awake
|
308
|
+
#
|
309
|
+
get /Disable Keep Awake/i do
|
310
|
+
[KeepAwakeAction, {enabled: false, screen_option: 0}]
|
311
|
+
end
|
312
|
+
|
108
313
|
|
109
314
|
end
|
110
315
|
|
@@ -195,9 +400,9 @@ class Macro
|
|
195
400
|
attr_reader :local_variables, :triggers, :actions, :constraints, :guid
|
196
401
|
attr_accessor :title, :description
|
197
402
|
|
198
|
-
def initialize(name=nil, debug: false)
|
403
|
+
def initialize(name=nil, geofences: geofences, debug: false)
|
199
404
|
|
200
|
-
@title, @debug = name, debug
|
405
|
+
@title, @geofences, @debug = name, geofences, debug
|
201
406
|
|
202
407
|
puts 'inside Macro#initialize' if @debug
|
203
408
|
|
@@ -288,19 +493,20 @@ class Macro
|
|
288
493
|
end
|
289
494
|
|
290
495
|
def import_xml(node)
|
291
|
-
|
496
|
+
|
292
497
|
if @debug then
|
293
498
|
puts 'inside Macro#import_xml'
|
294
499
|
puts 'node: ' + node.xml.inspect
|
295
500
|
end
|
296
501
|
|
297
|
-
@title = node.attributes[:name]
|
298
|
-
@description = node.attributes[:description]
|
299
|
-
|
300
502
|
if node.element('triggers') then
|
301
|
-
|
503
|
+
|
302
504
|
# level 2
|
303
505
|
|
506
|
+
@title = node.attributes[:name]
|
507
|
+
@description = node.attributes[:description]
|
508
|
+
|
509
|
+
|
304
510
|
# get all the triggers
|
305
511
|
@triggers = node.xpath('triggers/*').map do |e|
|
306
512
|
|
@@ -336,6 +542,12 @@ class Macro
|
|
336
542
|
|
337
543
|
# Level 1
|
338
544
|
|
545
|
+
puts 'import_xml: inside level 1' if @debug
|
546
|
+
|
547
|
+
@title = node.text('macro') || node.attributes[:name]
|
548
|
+
|
549
|
+
#@description = node.attributes[:description]
|
550
|
+
|
339
551
|
tp = TriggersNlp.new
|
340
552
|
|
341
553
|
@triggers = node.xpath('trigger').map do |e|
|
@@ -345,7 +557,11 @@ class Macro
|
|
345
557
|
puts 'found trigger ' + r.inspect if @debug
|
346
558
|
|
347
559
|
if r then
|
348
|
-
r[0]
|
560
|
+
if r[0] == GeofenceTrigger then
|
561
|
+
GeofenceTrigger.new(r[1], geofences: @geofences)
|
562
|
+
else
|
563
|
+
r[0].new(r[1])
|
564
|
+
end
|
349
565
|
end
|
350
566
|
|
351
567
|
end
|
@@ -354,11 +570,21 @@ class Macro
|
|
354
570
|
|
355
571
|
@actions = node.xpath('action').map do |e|
|
356
572
|
|
573
|
+
puts 'action e: ' + e.xml.inspect if @debug
|
357
574
|
r = ap.find_action e.text
|
358
575
|
puts 'found action ' + r.inspect if @debug
|
359
576
|
|
360
577
|
if r then
|
361
|
-
|
578
|
+
|
579
|
+
a = e.xpath('item/*')
|
580
|
+
|
581
|
+
h = if a.any? then
|
582
|
+
a.map {|node| [node.name.to_sym, node.text.to_s]}.to_h
|
583
|
+
else
|
584
|
+
{}
|
585
|
+
end
|
586
|
+
|
587
|
+
r[0].new(r[1].merge(h))
|
362
588
|
end
|
363
589
|
|
364
590
|
end
|
@@ -405,14 +631,23 @@ class Macro
|
|
405
631
|
|
406
632
|
end
|
407
633
|
|
634
|
+
# invokes the actions
|
635
|
+
#
|
408
636
|
def run()
|
409
637
|
@actions.map(&:invoke)
|
410
638
|
end
|
639
|
+
|
640
|
+
# prepares the environment in order for triggers to test fire successfully
|
641
|
+
# Used for testing
|
642
|
+
#
|
643
|
+
def set_env()
|
644
|
+
@triggers.each(&:set_env)
|
645
|
+
end
|
411
646
|
|
412
647
|
def to_pc()
|
413
648
|
|
414
|
-
heading = '# ' + @title
|
415
|
-
heading += '# ' + @description if @description
|
649
|
+
heading = '# ' + @title
|
650
|
+
heading += '\n# ' + @description if @description
|
416
651
|
condition = @triggers.first.to_pc
|
417
652
|
actions = @actions.map(&:to_pc).join("\n")
|
418
653
|
|
@@ -426,14 +661,71 @@ EOF
|
|
426
661
|
end
|
427
662
|
|
428
663
|
def to_s()
|
664
|
+
|
665
|
+
indent = 0
|
666
|
+
actions = @actions.map do |x|
|
667
|
+
|
668
|
+
s = x.to_s
|
669
|
+
|
670
|
+
r = if indent <= 0 then
|
671
|
+
|
672
|
+
"a: %s" % s
|
673
|
+
|
674
|
+
elsif indent > 0
|
675
|
+
|
676
|
+
if s =~ /^Else/ then
|
677
|
+
(' ' * (indent-1)) + "%s" % s
|
678
|
+
elsif s =~ /^End/
|
679
|
+
indent -= 1
|
680
|
+
(' ' * indent) + "%s" % s
|
681
|
+
else
|
682
|
+
(' ' * indent) + "%s" % s
|
683
|
+
end
|
684
|
+
|
685
|
+
end
|
686
|
+
|
687
|
+
if s =~ /^If/i then
|
688
|
+
if indent < 1 then
|
689
|
+
r = "a:\n %s" % s
|
690
|
+
indent += 1
|
691
|
+
else
|
692
|
+
r = (' ' * indent) + "%s" % s
|
693
|
+
end
|
694
|
+
|
695
|
+
indent += 1
|
696
|
+
end
|
697
|
+
|
698
|
+
r
|
699
|
+
|
700
|
+
end.join("\n")
|
701
|
+
|
429
702
|
a = [
|
430
703
|
'm: ' + @title,
|
431
704
|
@triggers.map {|x| "t: %s" % x}.join("\n"),
|
432
|
-
|
705
|
+
actions,
|
433
706
|
@constraints.map {|x| "a: %s" % x}.join("\n")
|
434
707
|
]
|
435
|
-
|
436
|
-
|
708
|
+
|
709
|
+
if @description and @description.length >= 1 then
|
710
|
+
a.insert(1, 'd: ' + @description.gsub(/\n/,"\n "))
|
711
|
+
end
|
712
|
+
|
713
|
+
a.join("\n") + "\n"
|
714
|
+
|
715
|
+
end
|
716
|
+
|
717
|
+
def to_summary()
|
718
|
+
|
719
|
+
a = [
|
720
|
+
'm: ' + @title,
|
721
|
+
't: ' + @triggers.map(&:to_summary).join(", "),
|
722
|
+
'a: ' + @actions.map(&:to_summary).join(", "),
|
723
|
+
]
|
724
|
+
|
725
|
+
a << 'c: ' + @constraints.map(&:to_summary).join(", ") if @constraints.any?
|
726
|
+
|
727
|
+
a.join("\n") + "\n"
|
728
|
+
|
437
729
|
end
|
438
730
|
|
439
731
|
private
|
@@ -446,7 +738,15 @@ EOF
|
|
446
738
|
|
447
739
|
puts ('inside object h:' + h.inspect).debug if @debug
|
448
740
|
klass = Object.const_get h[:class_type]
|
449
|
-
klass.
|
741
|
+
puts klass.inspect.highlight if $debug
|
742
|
+
|
743
|
+
if klass == GeofenceTrigger then
|
744
|
+
puts 'GeofenceTrigger found'.highlight if $debug
|
745
|
+
GeofenceTrigger.new(h, geofences: @geofences)
|
746
|
+
else
|
747
|
+
klass.new h
|
748
|
+
end
|
749
|
+
|
450
750
|
end
|
451
751
|
|
452
752
|
end
|
@@ -459,12 +759,14 @@ class MacroDroid
|
|
459
759
|
using ColouredText
|
460
760
|
using Params
|
461
761
|
|
462
|
-
attr_reader :macros
|
762
|
+
attr_reader :macros, :geofences, :yaml
|
463
763
|
|
464
764
|
def initialize(obj=nil, debug: false)
|
465
765
|
|
466
766
|
@debug = debug
|
467
767
|
|
768
|
+
@geofences = {}
|
769
|
+
|
468
770
|
if obj then
|
469
771
|
|
470
772
|
raw_s, _ = RXFHelper.read(obj)
|
@@ -481,18 +783,40 @@ class MacroDroid
|
|
481
783
|
@h = build_h
|
482
784
|
|
483
785
|
else
|
786
|
+
|
787
|
+
puts 's: ' + s.inspect if @debug
|
484
788
|
|
485
|
-
|
486
|
-
|
789
|
+
if s =~ /m(?:acro)?:\s/ then
|
790
|
+
|
791
|
+
puts 'before RowX.new' if @debug
|
792
|
+
|
793
|
+
s2 = s.gsub(/^g:/,'geofence:').gsub(/^m:/,'macro:')\
|
794
|
+
.gsub(/^t:/,'trigger:').gsub(/^a:/,'action:')\
|
795
|
+
.gsub(/^c:/,'constraint:').gsub(/^#.*/,'')
|
796
|
+
|
797
|
+
raw_macros, raw_geofences = s2.split(/(?=^macro:)/,2).reverse
|
798
|
+
|
799
|
+
if raw_geofences then
|
800
|
+
|
801
|
+
geoxml = RowX.new(raw_geofences).to_xml
|
802
|
+
|
803
|
+
geodoc = Rexle.new(geoxml)
|
804
|
+
geofences = geodoc.root.xpath('item/geofence')
|
805
|
+
@geofences = fetch_geofences(geofences) if geofences.any?
|
806
|
+
|
807
|
+
end
|
808
|
+
|
809
|
+
xml = RowX.new(raw_macros).to_xml
|
810
|
+
import_rowxml(xml)
|
811
|
+
|
487
812
|
elsif s =~ /^# /
|
488
|
-
pc_to_xml(s)
|
813
|
+
xml = pc_to_xml(s)
|
814
|
+
import_xml(xml)
|
489
815
|
else
|
490
816
|
raise MacroDroidError, 'invalid input'
|
491
817
|
end
|
492
|
-
import_xml(xml)
|
493
|
-
@h = build_h
|
494
818
|
|
495
|
-
|
819
|
+
@h = build_h
|
496
820
|
|
497
821
|
end
|
498
822
|
|
@@ -545,18 +869,95 @@ class MacroDroid
|
|
545
869
|
|
546
870
|
alias to_json export_json
|
547
871
|
|
872
|
+
|
873
|
+
def to_h()
|
874
|
+
|
875
|
+
h = {
|
876
|
+
geofence_data: {
|
877
|
+
geofence_map: @geofences.map {|key, value| [key, value.to_h] }.to_h
|
878
|
+
},
|
879
|
+
macro_list: @macros.map(&:to_h)
|
880
|
+
}
|
881
|
+
@h.merge(h).to_camel_case
|
882
|
+
|
883
|
+
end
|
884
|
+
|
885
|
+
# returns pseudocode
|
886
|
+
#
|
887
|
+
def to_pc()
|
888
|
+
@macros.map(&:to_pc).join("\n\n")
|
889
|
+
end
|
890
|
+
|
891
|
+
def to_s()
|
892
|
+
|
893
|
+
lines = []
|
894
|
+
|
895
|
+
if @geofences.any? then
|
896
|
+
lines << @geofences.map {|_, value| 'g: ' + value.to_s}.join("\n\n") + "\n"
|
897
|
+
end
|
898
|
+
|
899
|
+
lines << @macros.map(&:to_s).join("\n")
|
900
|
+
lines.join("\n")
|
901
|
+
|
902
|
+
end
|
903
|
+
|
904
|
+
def to_summary()
|
905
|
+
@macros.map(&:to_summary).join("\n")
|
906
|
+
end
|
907
|
+
|
908
|
+
private
|
909
|
+
|
910
|
+
def fetch_geofences(nodes)
|
911
|
+
|
912
|
+
nodes.map do |e|
|
913
|
+
|
914
|
+
name = e.text.to_s.strip
|
915
|
+
item = e.element('item')
|
916
|
+
coordinates = item.text('coordinates')
|
917
|
+
latitude, longitude = coordinates.split(/, */,2)
|
918
|
+
radius = item.text('radius')
|
919
|
+
|
920
|
+
id = UUID.new.generate
|
921
|
+
|
922
|
+
h = {
|
923
|
+
name: name,
|
924
|
+
longitude: longitude,
|
925
|
+
latitude: latitude,
|
926
|
+
radius: radius,
|
927
|
+
id: id
|
928
|
+
}
|
929
|
+
|
930
|
+
[id.to_sym, GeofenceMap.new(h)]
|
931
|
+
|
932
|
+
end.to_h
|
933
|
+
|
934
|
+
end
|
935
|
+
|
548
936
|
def import_json(s)
|
549
937
|
|
550
938
|
h = JSON.parse(s, symbolize_names: true)
|
551
939
|
puts 'json_to_yaml: ' + h.to_yaml if @debug
|
940
|
+
@yaml = h.to_yaml # helpful for debugging and testing
|
552
941
|
|
553
942
|
@h = h.to_snake_case
|
554
943
|
puts ('@h: ' + @h.inspect).debug if @debug
|
555
|
-
|
944
|
+
|
945
|
+
|
946
|
+
# fetch the geofence data
|
947
|
+
if @h[:geofence_data] then
|
948
|
+
|
949
|
+
@geofences = @h[:geofence_data][:geofence_map].map do |id, properties|
|
950
|
+
[id, GeofenceMap.new(properties)]
|
951
|
+
end.to_h
|
952
|
+
|
953
|
+
end
|
954
|
+
|
556
955
|
@macros = @h[:macro_list].map do |macro|
|
557
956
|
|
558
957
|
puts ('macro: ' + macro.inspect).debug if @debug
|
559
|
-
|
958
|
+
# puts '@geofences: ' + @geofences.inspect if @debug
|
959
|
+
|
960
|
+
m = Macro.new(geofences: @geofences.map(&:last), debug: @debug )
|
560
961
|
m.import_h(macro)
|
561
962
|
m
|
562
963
|
|
@@ -566,9 +967,30 @@ class MacroDroid
|
|
566
967
|
|
567
968
|
end
|
568
969
|
|
970
|
+
def import_rowxml(raws)
|
971
|
+
|
972
|
+
s = RXFHelper.read(raws).first
|
973
|
+
puts 's: ' + s.inspect if @debug
|
974
|
+
doc = Rexle.new(s)
|
975
|
+
puts 'after doc' if @debug
|
976
|
+
puts 'import_rowxml: @geofences: ' + @geofences.inspect if @debug
|
977
|
+
geofences = @geofences
|
978
|
+
|
979
|
+
@macros = doc.root.xpath('item').map do |node|
|
980
|
+
puts ('geofences: ' + geofences.inspect).highlight if @debug
|
981
|
+
Macro.new(geofences: geofences.map(&:last), debug: @debug).import_xml(node)
|
982
|
+
|
983
|
+
end
|
984
|
+
|
985
|
+
end
|
986
|
+
|
569
987
|
def import_xml(raws)
|
570
988
|
|
571
|
-
|
989
|
+
if @debug then
|
990
|
+
puts 'inside import_xml'
|
991
|
+
|
992
|
+
puts 'raws: ' + raws.inspect
|
993
|
+
end
|
572
994
|
s = RXFHelper.read(raws).first
|
573
995
|
puts 's: ' + s.inspect if @debug
|
574
996
|
doc = Rexle.new(s)
|
@@ -576,14 +998,10 @@ class MacroDroid
|
|
576
998
|
if @debug then
|
577
999
|
puts 'doc: ' + doc.root.xml
|
578
1000
|
end
|
579
|
-
|
580
|
-
debug = @debug
|
581
|
-
|
1001
|
+
|
582
1002
|
@macros = doc.root.xpath('macro').map do |node|
|
583
1003
|
|
584
|
-
|
585
|
-
macro.import_xml(node)
|
586
|
-
macro
|
1004
|
+
Macro.new(geofences: @geofences.map(&:last), debug: @debug).import_xml(node)
|
587
1005
|
|
588
1006
|
end
|
589
1007
|
end
|
@@ -608,51 +1026,40 @@ class MacroDroid
|
|
608
1026
|
doc = Rexle.new([:macros, {}, '', *macros])
|
609
1027
|
doc.root.xml pretty: true
|
610
1028
|
|
611
|
-
end
|
612
|
-
|
613
|
-
def text_to_xml(s)
|
614
|
-
|
615
|
-
a = s.split(/.*(?=^m:)/); a.shift
|
616
|
-
a.map!(&:chomp)
|
617
|
-
|
618
|
-
macros = a.map do |x|
|
619
|
-
|
620
|
-
lines = x.lines
|
621
|
-
puts 'lines: ' + lines.inspect if @debug
|
622
|
-
|
623
|
-
name = lines.shift[/^m: +(.*)/,1]
|
624
|
-
h = {t: [], a: [], c: []}
|
625
|
-
|
626
|
-
lines.each {|line| h[line[0].to_sym] << line[/^\w: +(.*)/,1] }
|
627
|
-
triggers = h[:t].map {|text| [:trigger, {}, text]}
|
628
|
-
actions = h[:a].map {|text| [:action, {}, text]}
|
629
|
-
constraints = h[:c].map {|text| [:constraint, {}, text]}
|
630
|
-
|
631
|
-
[:macro, {name: name},'', *triggers, *actions, *constraints]
|
1029
|
+
end
|
632
1030
|
|
633
|
-
|
1031
|
+
end
|
634
1032
|
|
635
|
-
|
636
|
-
|
1033
|
+
class GeofenceMap
|
1034
|
+
|
1035
|
+
attr_accessor :name, :longitude, :latitude, :radius, :id
|
1036
|
+
|
1037
|
+
def initialize(id: '', longitude: '', latitude: '', name: '', radius: '')
|
1038
|
+
|
1039
|
+
@id, @latitude, @longitude, @name, @radius = id, latitude, \
|
1040
|
+
longitude, name, radius
|
637
1041
|
|
638
1042
|
end
|
639
|
-
|
1043
|
+
|
640
1044
|
def to_h()
|
641
|
-
|
642
|
-
|
643
|
-
|
1045
|
+
|
1046
|
+
{
|
1047
|
+
id: @id,
|
1048
|
+
longitude: @longitude,
|
1049
|
+
latitude: @latitude,
|
1050
|
+
name: @name,
|
1051
|
+
radius: @radius
|
1052
|
+
}
|
1053
|
+
|
644
1054
|
end
|
645
1055
|
|
646
|
-
# returns pseudocode
|
647
|
-
#
|
648
|
-
def to_pc()
|
649
|
-
@macros.map(&:to_pc).join("\n\n")
|
650
|
-
end
|
651
|
-
|
652
1056
|
def to_s()
|
653
|
-
|
1057
|
+
|
1058
|
+
coordinates = "%s, %s" % [@longitude, @latitude]
|
1059
|
+
"%s\n coordinates: %s\n radius: %s" % [@name, coordinates, @radius]
|
1060
|
+
|
654
1061
|
end
|
655
|
-
|
1062
|
+
|
656
1063
|
end
|
657
1064
|
|
658
1065
|
class MacroObject
|
@@ -663,6 +1070,8 @@ class MacroObject
|
|
663
1070
|
|
664
1071
|
def initialize(h={})
|
665
1072
|
|
1073
|
+
$env ||= {}
|
1074
|
+
|
666
1075
|
@h = {constraint_list: [], is_or_condition: false,
|
667
1076
|
is_disabled: false}.merge(h)
|
668
1077
|
@list = []
|
@@ -691,6 +1100,12 @@ class MacroObject
|
|
691
1100
|
h2.merge('m_classType' => self.class.to_s)
|
692
1101
|
|
693
1102
|
end
|
1103
|
+
|
1104
|
+
def to_s()
|
1105
|
+
"#<%s %s>" % [self.class, @h.inspect]
|
1106
|
+
end
|
1107
|
+
|
1108
|
+
alias to_summary to_s
|
694
1109
|
|
695
1110
|
protected
|
696
1111
|
|
@@ -705,13 +1120,32 @@ class MacroObject
|
|
705
1120
|
UUID.new.generate
|
706
1121
|
end
|
707
1122
|
|
1123
|
+
def object(h={})
|
1124
|
+
|
1125
|
+
puts ('inside object h:' + h.inspect).debug if @debug
|
1126
|
+
klass = Object.const_get h[:class_type]
|
1127
|
+
puts klass.inspect.highlight if $debug
|
1128
|
+
|
1129
|
+
klass.new h
|
1130
|
+
|
1131
|
+
end
|
1132
|
+
|
708
1133
|
end
|
709
1134
|
|
710
1135
|
class Trigger < MacroObject
|
711
|
-
|
1136
|
+
using Params
|
1137
|
+
|
1138
|
+
attr_reader :constraints
|
1139
|
+
|
712
1140
|
def initialize(h={})
|
713
1141
|
super({fakeIcon: 0}.merge(h))
|
714
1142
|
@list << 'fakeIcon'
|
1143
|
+
|
1144
|
+
# fetch the constraints
|
1145
|
+
@constraints = h[:constraint_list].map do |constraint|
|
1146
|
+
object(constraint.to_snake_case)
|
1147
|
+
end
|
1148
|
+
|
715
1149
|
end
|
716
1150
|
|
717
1151
|
def match?(detail={}, model=nil)
|
@@ -824,6 +1258,11 @@ class BatteryLevelTrigger < Trigger
|
|
824
1258
|
super(options.merge h)
|
825
1259
|
|
826
1260
|
end
|
1261
|
+
|
1262
|
+
def to_s()
|
1263
|
+
operator = @h[:decreases_to] ? '<=' : '>='
|
1264
|
+
"Battery %s %s%%" % [operator, @h[:battery_level]]
|
1265
|
+
end
|
827
1266
|
|
828
1267
|
end
|
829
1268
|
|
@@ -1205,29 +1644,22 @@ class TimerTrigger < Trigger
|
|
1205
1644
|
use_alarm: false
|
1206
1645
|
}
|
1207
1646
|
|
1208
|
-
super(options.merge filter(options,h))
|
1647
|
+
super(options.merge filter(options, h))
|
1209
1648
|
|
1210
1649
|
end
|
1211
1650
|
|
1212
1651
|
def match?(detail={time: $env[:time]}, model=nil)
|
1213
|
-
|
1214
|
-
a = @h[:days_of_week]
|
1215
|
-
a.unshift a.pop
|
1216
|
-
|
1217
|
-
dow = a.map.with_index {|x, i| x ? i : nil }.compact.join(',')
|
1218
|
-
|
1219
|
-
s = "%s %s * * %s" % [@h[:minute], @h[:hour], dow]
|
1220
|
-
|
1221
|
-
if $debug then
|
1222
|
-
puts 's: ' + s.inspect
|
1223
|
-
puts 'detail: ' + detail.inspect
|
1224
|
-
puts '@h: ' + @h.inspect
|
1225
|
-
end
|
1226
1652
|
|
1227
|
-
|
1653
|
+
time() == detail[:time]
|
1228
1654
|
|
1229
1655
|
end
|
1230
1656
|
|
1657
|
+
# sets the environmental conditions for this trigger to fire
|
1658
|
+
#
|
1659
|
+
def set_env()
|
1660
|
+
$env[:time] = time()
|
1661
|
+
end
|
1662
|
+
|
1231
1663
|
def to_pc()
|
1232
1664
|
"time.is? '%s'" % self.to_s.gsub(',', ' or')
|
1233
1665
|
end
|
@@ -1243,6 +1675,20 @@ class TimerTrigger < Trigger
|
|
1243
1675
|
|
1244
1676
|
"at %s on %s" % [time, days.join(', ')]
|
1245
1677
|
end
|
1678
|
+
|
1679
|
+
private
|
1680
|
+
|
1681
|
+
def time()
|
1682
|
+
|
1683
|
+
a = @h[:days_of_week].clone
|
1684
|
+
a.unshift a.pop
|
1685
|
+
|
1686
|
+
dow = a.map.with_index {|x, i| x ? i : nil }.compact.join(',')
|
1687
|
+
s = "%s %s * * %s" % [@h[:minute], @h[:hour], dow]
|
1688
|
+
recent_time = ($env && $env[:time]) ? $env[:time] : Time.now
|
1689
|
+
ChronicCron.new(s, recent_time).to_time
|
1690
|
+
|
1691
|
+
end
|
1246
1692
|
|
1247
1693
|
end
|
1248
1694
|
|
@@ -1464,6 +1910,10 @@ class DeviceUnlockedTrigger < DeviceEventsTrigger
|
|
1464
1910
|
super(options.merge h)
|
1465
1911
|
|
1466
1912
|
end
|
1913
|
+
|
1914
|
+
def to_s()
|
1915
|
+
'Screen Unlocked'
|
1916
|
+
end
|
1467
1917
|
|
1468
1918
|
end
|
1469
1919
|
|
@@ -1622,8 +2072,15 @@ end
|
|
1622
2072
|
#
|
1623
2073
|
class GeofenceTrigger < Trigger
|
1624
2074
|
|
1625
|
-
def initialize(h={})
|
2075
|
+
def initialize( h={}, geofences: {})
|
1626
2076
|
|
2077
|
+
if h[:name] then
|
2078
|
+
puts ('geofences2: ' + geofences.inspect)
|
2079
|
+
found = geofences.find {|x| x.name.downcase == h[:name].downcase}
|
2080
|
+
h[:geofence_id] = found.id if found
|
2081
|
+
|
2082
|
+
end
|
2083
|
+
|
1627
2084
|
options = {
|
1628
2085
|
update_rate_text: '5 Minutes',
|
1629
2086
|
geofence_id: '',
|
@@ -1632,9 +2089,28 @@ class GeofenceTrigger < Trigger
|
|
1632
2089
|
enter_area: true
|
1633
2090
|
}
|
1634
2091
|
|
1635
|
-
super(options.merge h)
|
2092
|
+
super(options.merge filter(options, h))
|
2093
|
+
@geofences = geofences
|
1636
2094
|
|
1637
2095
|
end
|
2096
|
+
|
2097
|
+
def to_s()
|
2098
|
+
|
2099
|
+
if $debug then
|
2100
|
+
puts ' @geofences: ' + @geofences.inspect
|
2101
|
+
puts '@h: ' + @h.inspect
|
2102
|
+
puts '@h[:geofence_id]: ' + @h[:geofence_id].inspect
|
2103
|
+
end
|
2104
|
+
|
2105
|
+
direction = @h[:enter_area] ? 'Entry' : 'Exit'
|
2106
|
+
|
2107
|
+
found = @geofences.find {|x| x.id == @h[:geofence_id]}
|
2108
|
+
puts 'found: ' + found.inspect if @debug
|
2109
|
+
label = found ? found.name : 'error: name not found'
|
2110
|
+
|
2111
|
+
"Geofence %s (%s)" % [direction, label]
|
2112
|
+
|
2113
|
+
end
|
1638
2114
|
|
1639
2115
|
end
|
1640
2116
|
|
@@ -1677,8 +2153,28 @@ class ActivityRecognitionTrigger < SensorsTrigger
|
|
1677
2153
|
}
|
1678
2154
|
|
1679
2155
|
super(options.merge h)
|
2156
|
+
|
2157
|
+
@activity = ['In Vehicle', 'On Bicycle', 'Running', 'Walking', 'Still']
|
1680
2158
|
|
1681
2159
|
end
|
2160
|
+
|
2161
|
+
def to_s()
|
2162
|
+
activity = @activity[@h[:selected_index]]
|
2163
|
+
'Activity - ' + activity
|
2164
|
+
end
|
2165
|
+
|
2166
|
+
def to_summary
|
2167
|
+
|
2168
|
+
activity = @activity[@h[:selected_index]]
|
2169
|
+
s = if activity.length > 10 then
|
2170
|
+
activity[0..7] + '..'
|
2171
|
+
else
|
2172
|
+
activity
|
2173
|
+
end
|
2174
|
+
|
2175
|
+
'Activity - ' + s
|
2176
|
+
|
2177
|
+
end
|
1682
2178
|
|
1683
2179
|
end
|
1684
2180
|
|
@@ -1688,14 +2184,33 @@ class ProximityTrigger < SensorsTrigger
|
|
1688
2184
|
|
1689
2185
|
def initialize(h={})
|
1690
2186
|
|
2187
|
+
if h[:distance] then
|
2188
|
+
|
2189
|
+
case h[:distance].to_sym
|
2190
|
+
when :near
|
2191
|
+
options[:near] = true
|
2192
|
+
end
|
2193
|
+
end
|
2194
|
+
|
1691
2195
|
options = {
|
1692
2196
|
near: true,
|
1693
2197
|
selected_option: 0
|
1694
2198
|
}
|
1695
2199
|
|
1696
|
-
super(options.merge h)
|
2200
|
+
super(options.merge filter(options,h))
|
1697
2201
|
|
1698
2202
|
end
|
2203
|
+
|
2204
|
+
def to_s()
|
2205
|
+
|
2206
|
+
distance = if @h[:near] then
|
2207
|
+
'Near'
|
2208
|
+
else
|
2209
|
+
'Far'
|
2210
|
+
end
|
2211
|
+
|
2212
|
+
"Proximity Sensor (%s)" % distance
|
2213
|
+
end
|
1699
2214
|
|
1700
2215
|
end
|
1701
2216
|
|
@@ -1868,9 +2383,17 @@ end
|
|
1868
2383
|
|
1869
2384
|
|
1870
2385
|
class Action < MacroObject
|
2386
|
+
using Params
|
2387
|
+
|
2388
|
+
attr_reader :constraints
|
1871
2389
|
|
1872
2390
|
def initialize(h={})
|
1873
2391
|
super(h)
|
2392
|
+
|
2393
|
+
# fetch the constraints
|
2394
|
+
@constraints = h[:constraint_list].map do |constraint|
|
2395
|
+
object(constraint.to_snake_case)
|
2396
|
+
end
|
1874
2397
|
end
|
1875
2398
|
|
1876
2399
|
def invoke(s='')
|
@@ -1939,6 +2462,10 @@ class LaunchActivityAction < ApplicationAction
|
|
1939
2462
|
super(options.merge h)
|
1940
2463
|
|
1941
2464
|
end
|
2465
|
+
|
2466
|
+
def to_s()
|
2467
|
+
'Launch ' + @h[:application_name]
|
2468
|
+
end
|
1942
2469
|
|
1943
2470
|
end
|
1944
2471
|
|
@@ -1964,6 +2491,8 @@ end
|
|
1964
2491
|
class OpenWebPageAction < ApplicationAction
|
1965
2492
|
|
1966
2493
|
def initialize(h={})
|
2494
|
+
|
2495
|
+
h[:url_to_open] = h[:url] if h[:url]
|
1967
2496
|
|
1968
2497
|
options = {
|
1969
2498
|
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},
|
@@ -1973,9 +2502,13 @@ class OpenWebPageAction < ApplicationAction
|
|
1973
2502
|
block_next_action: false
|
1974
2503
|
}
|
1975
2504
|
|
1976
|
-
super(options.merge h)
|
2505
|
+
super(options.merge filter(options,h))
|
1977
2506
|
|
1978
2507
|
end
|
2508
|
+
|
2509
|
+
def to_s()
|
2510
|
+
"HTTP GET\n url: " + @h[:url_to_open]
|
2511
|
+
end
|
1979
2512
|
|
1980
2513
|
end
|
1981
2514
|
|
@@ -2035,6 +2568,64 @@ class TakePictureAction < CameraAction
|
|
2035
2568
|
|
2036
2569
|
end
|
2037
2570
|
|
2571
|
+
class IfConditionAction < Action
|
2572
|
+
|
2573
|
+
def initialize(h={})
|
2574
|
+
|
2575
|
+
options = {
|
2576
|
+
a: true,
|
2577
|
+
constraint_list: ''
|
2578
|
+
}
|
2579
|
+
|
2580
|
+
super(options.merge h)
|
2581
|
+
|
2582
|
+
end
|
2583
|
+
|
2584
|
+
def to_s()
|
2585
|
+
|
2586
|
+
operator = @h[:is_or_condition] ? 'OR' : 'AND'
|
2587
|
+
r = 'If ' + @constraints.map(&:to_s).join(" %s " % operator)
|
2588
|
+
puts 'if ... @h ' + @h.inspect
|
2589
|
+
r
|
2590
|
+
|
2591
|
+
end
|
2592
|
+
end
|
2593
|
+
|
2594
|
+
class ElseAction < Action
|
2595
|
+
|
2596
|
+
def initialize(h={})
|
2597
|
+
|
2598
|
+
options = {
|
2599
|
+
constraint_list: ''
|
2600
|
+
}
|
2601
|
+
|
2602
|
+
super(options.merge h)
|
2603
|
+
|
2604
|
+
end
|
2605
|
+
|
2606
|
+
def to_s()
|
2607
|
+
'Else'
|
2608
|
+
end
|
2609
|
+
|
2610
|
+
end
|
2611
|
+
|
2612
|
+
class EndIfAction < Action
|
2613
|
+
|
2614
|
+
def initialize(h={})
|
2615
|
+
|
2616
|
+
options = {
|
2617
|
+
constraint_list: ''
|
2618
|
+
}
|
2619
|
+
|
2620
|
+
super(options.merge h)
|
2621
|
+
|
2622
|
+
end
|
2623
|
+
|
2624
|
+
def to_s()
|
2625
|
+
'End If'
|
2626
|
+
end
|
2627
|
+
|
2628
|
+
end
|
2038
2629
|
|
2039
2630
|
class ConnectivityAction < Action
|
2040
2631
|
|
@@ -2060,6 +2651,11 @@ class SetWifiAction < ConnectivityAction
|
|
2060
2651
|
super(options.merge h)
|
2061
2652
|
|
2062
2653
|
end
|
2654
|
+
|
2655
|
+
def to_s()
|
2656
|
+
action = @h[:state] == 0 ? 'Enable' : 'Disable'
|
2657
|
+
action + ' Wifi'
|
2658
|
+
end
|
2063
2659
|
|
2064
2660
|
end
|
2065
2661
|
|
@@ -2284,6 +2880,10 @@ class SpeakTextAction < DeviceAction
|
|
2284
2880
|
super(options.merge h)
|
2285
2881
|
|
2286
2882
|
end
|
2883
|
+
|
2884
|
+
def to_s()
|
2885
|
+
"Speak Text (%s)" % @h[:text_to_say]
|
2886
|
+
end
|
2287
2887
|
|
2288
2888
|
end
|
2289
2889
|
|
@@ -2398,6 +2998,17 @@ class VibrateAction < DeviceSettingsAction
|
|
2398
2998
|
super(options.merge h)
|
2399
2999
|
|
2400
3000
|
end
|
3001
|
+
|
3002
|
+
def to_s()
|
3003
|
+
|
3004
|
+
pattern = [
|
3005
|
+
'Blip', 'Short Buzz', 'Long Buzz', 'Rapid', 'Slow', 'Increasing',
|
3006
|
+
'Constant', 'Decreasing', 'Final Fantasy', 'Game Over', 'Star Wars',
|
3007
|
+
'Mini Blip', 'Micro Blip'
|
3008
|
+
]
|
3009
|
+
|
3010
|
+
'Vibrate ' + "(%s)" % pattern[@h[:vibrate_pattern].to_i]
|
3011
|
+
end
|
2401
3012
|
|
2402
3013
|
end
|
2403
3014
|
|
@@ -2720,6 +3331,10 @@ class PlaySoundAction < MediaAction
|
|
2720
3331
|
super(options.merge h)
|
2721
3332
|
|
2722
3333
|
end
|
3334
|
+
|
3335
|
+
def to_s()
|
3336
|
+
'Play: ' + @h[:file_path]
|
3337
|
+
end
|
2723
3338
|
|
2724
3339
|
end
|
2725
3340
|
|
@@ -2924,6 +3539,9 @@ end
|
|
2924
3539
|
class NotificationAction < NotificationsAction
|
2925
3540
|
|
2926
3541
|
def initialize(h={})
|
3542
|
+
|
3543
|
+
h[:notification_subject] = h[:subject] if h[:subject]
|
3544
|
+
h[:notification_text] = h[:text] if h[:text]
|
2927
3545
|
|
2928
3546
|
options = {
|
2929
3547
|
ringtone_name: 'Default',
|
@@ -2939,9 +3557,13 @@ class NotificationAction < NotificationsAction
|
|
2939
3557
|
run_macro_when_pressed: false
|
2940
3558
|
}
|
2941
3559
|
|
2942
|
-
super(options.merge h)
|
3560
|
+
super(options.merge filter(options, h))
|
2943
3561
|
|
2944
3562
|
end
|
3563
|
+
|
3564
|
+
def to_s()
|
3565
|
+
'Display Notification: ' + "%s: %s" % [@h[:notification_subject], @h[:notification_text]]
|
3566
|
+
end
|
2945
3567
|
|
2946
3568
|
end
|
2947
3569
|
|
@@ -3174,6 +3796,10 @@ end
|
|
3174
3796
|
|
3175
3797
|
# Category: Screen
|
3176
3798
|
#
|
3799
|
+
# options:
|
3800
|
+
# keep awake, screen on => enabled: true
|
3801
|
+
# disable keep awake => enabled: false
|
3802
|
+
#
|
3177
3803
|
class KeepAwakeAction < ScreenAction
|
3178
3804
|
|
3179
3805
|
def initialize(h={})
|
@@ -3188,7 +3814,30 @@ class KeepAwakeAction < ScreenAction
|
|
3188
3814
|
super(options.merge h)
|
3189
3815
|
|
3190
3816
|
end
|
3191
|
-
|
3817
|
+
|
3818
|
+
def to_s()
|
3819
|
+
|
3820
|
+
screen = @h[:screen_option] == 0 ? 'Screen On' : 'Screen Off'
|
3821
|
+
|
3822
|
+
if @h[:enabled] then
|
3823
|
+
|
3824
|
+
whenx = if @h[:seconds_to_stay_awake_for] == 0 then
|
3825
|
+
|
3826
|
+
'Until Disabled'
|
3827
|
+
|
3828
|
+
else
|
3829
|
+
scnds = @h[:seconds_to_stay_awake_for]
|
3830
|
+
Subunit.new(units={minutes:60, hours:60}, seconds: scnds).strfunit("%x")
|
3831
|
+
end
|
3832
|
+
|
3833
|
+
'Keep Device Awake ' + screen + ' ' + whenx
|
3834
|
+
|
3835
|
+
else
|
3836
|
+
'Disable Keep Awake'
|
3837
|
+
end
|
3838
|
+
|
3839
|
+
|
3840
|
+
end
|
3192
3841
|
end
|
3193
3842
|
|
3194
3843
|
# Category: Screen
|
@@ -3369,6 +4018,21 @@ class BatteryLevelConstraint < Constraint
|
|
3369
4018
|
super(options.merge h)
|
3370
4019
|
|
3371
4020
|
end
|
4021
|
+
|
4022
|
+
def to_s()
|
4023
|
+
|
4024
|
+
operator = if @h[:greater_than] then
|
4025
|
+
'>'
|
4026
|
+
elsif @h[:equals]
|
4027
|
+
'='
|
4028
|
+
else
|
4029
|
+
'<'
|
4030
|
+
end
|
4031
|
+
|
4032
|
+
level = @h[:battery_level]
|
4033
|
+
|
4034
|
+
"Battery %s %s%%" % [operator, level]
|
4035
|
+
end
|
3372
4036
|
|
3373
4037
|
end
|
3374
4038
|
|
@@ -3420,6 +4084,11 @@ class ExternalPowerConstraint < Constraint
|
|
3420
4084
|
super(options.merge h)
|
3421
4085
|
|
3422
4086
|
end
|
4087
|
+
|
4088
|
+
def to_s()
|
4089
|
+
connection = @h[:external_power] ? 'Connected' : 'Disconnected'
|
4090
|
+
'Power ' + connection
|
4091
|
+
end
|
3423
4092
|
|
3424
4093
|
end
|
3425
4094
|
|
@@ -3438,6 +4107,12 @@ class BluetoothConstraint < Constraint
|
|
3438
4107
|
super(options.merge h)
|
3439
4108
|
|
3440
4109
|
end
|
4110
|
+
|
4111
|
+
def to_s()
|
4112
|
+
device = @h[:device_name] #== 'Any Device' ? 'Any' : @h[:device_name]
|
4113
|
+
"Device Connected (%s)" % device
|
4114
|
+
end
|
4115
|
+
|
3441
4116
|
|
3442
4117
|
end
|
3443
4118
|
|
@@ -3762,6 +4437,10 @@ class DeviceLockedConstraint < Constraint
|
|
3762
4437
|
super(options.merge h)
|
3763
4438
|
|
3764
4439
|
end
|
4440
|
+
|
4441
|
+
def to_s()
|
4442
|
+
'Device ' + (@h[:locked] ? 'Locked' : 'Unlocked')
|
4443
|
+
end
|
3765
4444
|
|
3766
4445
|
end
|
3767
4446
|
|
@@ -3948,6 +4627,11 @@ class HeadphonesConnectionConstraint < Constraint
|
|
3948
4627
|
super(options.merge h)
|
3949
4628
|
|
3950
4629
|
end
|
4630
|
+
|
4631
|
+
def to_s()
|
4632
|
+
connection = @h[:connected] ? 'Connected' : 'Disconnected'
|
4633
|
+
'Headphones ' + connection
|
4634
|
+
end
|
3951
4635
|
|
3952
4636
|
end
|
3953
4637
|
|
@@ -4137,6 +4821,10 @@ class ScreenOnOffConstraint < Constraint
|
|
4137
4821
|
super(options.merge h)
|
4138
4822
|
|
4139
4823
|
end
|
4824
|
+
|
4825
|
+
def to_s()
|
4826
|
+
'Screen ' + (@h[:screen_on] ? 'On' : 'Off')
|
4827
|
+
end
|
4140
4828
|
|
4141
4829
|
end
|
4142
4830
|
|
@@ -4190,6 +4878,14 @@ class LightLevelConstraint < Constraint
|
|
4190
4878
|
super(options.merge h)
|
4191
4879
|
|
4192
4880
|
end
|
4881
|
+
|
4882
|
+
def to_s()
|
4883
|
+
|
4884
|
+
operator = @h[:light_level] == -1 ? 'Less than' : 'Greater than'
|
4885
|
+
condition = operator + ' ' + @h[:light_level_float].to_s + 'lx'
|
4886
|
+
'Light Sensor ' + condition
|
4887
|
+
|
4888
|
+
end
|
4193
4889
|
|
4194
4890
|
end
|
4195
4891
|
|
@@ -4222,5 +4918,9 @@ class ProximitySensorConstraint < Constraint
|
|
4222
4918
|
super(options.merge h)
|
4223
4919
|
|
4224
4920
|
end
|
4921
|
+
|
4922
|
+
def to_s()
|
4923
|
+
'Proximity Sensor: ' + (@h[:near] ? 'Near' : 'Far')
|
4924
|
+
end
|
4225
4925
|
|
4226
4926
|
end
|