remotedroid 0.4.3 → 0.5.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/remotedroid.rb +238 -688
- data/lib/remotedroid/client.rb +201 -0
- data/lib/remotedroid/control.rb +207 -0
- data/lib/remotedroid/controller.rb +170 -0
- data/lib/remotedroid/model.rb +227 -0
- data/lib/remotedroid/query.rb +60 -0
- metadata +9 -4
- metadata.gz.sig +0 -0
@@ -0,0 +1,201 @@
|
|
1
|
+
module RemoteDroid
|
2
|
+
|
3
|
+
class Client
|
4
|
+
using ColouredText
|
5
|
+
|
6
|
+
def initialize(hostx='127.0.0.1', host: hostx, port: '5777', sps_host: 'sps.home', sps_port: '59000')
|
7
|
+
@drb = OneDrb::Client.new host: host, port: port
|
8
|
+
@sps = SPSPub.new host: sps_host, port: sps_port
|
9
|
+
end
|
10
|
+
|
11
|
+
def control
|
12
|
+
@drb.control
|
13
|
+
end
|
14
|
+
|
15
|
+
def export(s)
|
16
|
+
@drb.export(s)
|
17
|
+
end
|
18
|
+
|
19
|
+
def invoke(s, *args)
|
20
|
+
@drb.invoke(s, *args)
|
21
|
+
end
|
22
|
+
|
23
|
+
def macros()
|
24
|
+
@drb.macros
|
25
|
+
end
|
26
|
+
|
27
|
+
def query(id=nil)
|
28
|
+
|
29
|
+
return @drb.query unless id
|
30
|
+
t = Time.now
|
31
|
+
h = @drb.query(id)
|
32
|
+
h.merge({latency: (Time.now - t).round(3)})
|
33
|
+
|
34
|
+
end
|
35
|
+
|
36
|
+
def run_macro(name)
|
37
|
+
a = @drb.run_macro name
|
38
|
+
a.each {|msg| @sps.notice 'macrodroid/action: ' + msg }
|
39
|
+
end
|
40
|
+
|
41
|
+
def update(key, val)
|
42
|
+
@drb.update key.to_sym, val
|
43
|
+
end
|
44
|
+
|
45
|
+
def store()
|
46
|
+
@drb.store
|
47
|
+
end
|
48
|
+
|
49
|
+
def syslog()
|
50
|
+
@drb.syslog
|
51
|
+
end
|
52
|
+
|
53
|
+
# -- helpful methods -----------------
|
54
|
+
|
55
|
+
def battery()
|
56
|
+
query.battery
|
57
|
+
end
|
58
|
+
|
59
|
+
def cell_tower()
|
60
|
+
query.cell_tower
|
61
|
+
end
|
62
|
+
|
63
|
+
def control_media(option='Play/Pause')
|
64
|
+
control.control_media({option: option})
|
65
|
+
end
|
66
|
+
|
67
|
+
def disable(macro)
|
68
|
+
control.disable macro
|
69
|
+
end
|
70
|
+
|
71
|
+
def enable(macro)
|
72
|
+
control.enable macro
|
73
|
+
end
|
74
|
+
|
75
|
+
def hotspot(state=nil)
|
76
|
+
control.hotspot state
|
77
|
+
end
|
78
|
+
|
79
|
+
def launch_activity(app='')
|
80
|
+
control.launch_activity(app)
|
81
|
+
end
|
82
|
+
|
83
|
+
alias launch launch_activity
|
84
|
+
|
85
|
+
def location()
|
86
|
+
query.location
|
87
|
+
end
|
88
|
+
|
89
|
+
def location_watch(refresh: '1 minute', interval: refresh,
|
90
|
+
duration: '30 minutes')
|
91
|
+
|
92
|
+
|
93
|
+
d = ChronicDuration.parse(duration)
|
94
|
+
seconds = ChronicDuration.parse(interval)
|
95
|
+
puts ("monitoring location every %s for %s" % [interval, duration]).info
|
96
|
+
|
97
|
+
Thread.new do
|
98
|
+
|
99
|
+
t = Time.now + d
|
100
|
+
|
101
|
+
begin
|
102
|
+
|
103
|
+
query.location
|
104
|
+
sleep seconds
|
105
|
+
|
106
|
+
end until Time.now >= t
|
107
|
+
|
108
|
+
end
|
109
|
+
|
110
|
+
end
|
111
|
+
|
112
|
+
def next()
|
113
|
+
control_media(option='Next')
|
114
|
+
end
|
115
|
+
|
116
|
+
def pause()
|
117
|
+
control_media(option='Pause')
|
118
|
+
end
|
119
|
+
|
120
|
+
def play()
|
121
|
+
control_media(option='Play')
|
122
|
+
end
|
123
|
+
|
124
|
+
def play_pause()
|
125
|
+
control_media(option='Play/Pause')
|
126
|
+
end
|
127
|
+
|
128
|
+
def photo()
|
129
|
+
take_picture
|
130
|
+
end
|
131
|
+
|
132
|
+
def previous()
|
133
|
+
control_media(option='Previous')
|
134
|
+
end
|
135
|
+
|
136
|
+
def say(text)
|
137
|
+
control.speak_text text
|
138
|
+
end
|
139
|
+
|
140
|
+
def say_time()
|
141
|
+
control.say_time
|
142
|
+
end
|
143
|
+
|
144
|
+
alias saytime say_time
|
145
|
+
|
146
|
+
def screen(state=nil)
|
147
|
+
control.screen state
|
148
|
+
end
|
149
|
+
|
150
|
+
def screen_on()
|
151
|
+
screen :on
|
152
|
+
end
|
153
|
+
|
154
|
+
def screen_off()
|
155
|
+
screen :off
|
156
|
+
end
|
157
|
+
|
158
|
+
def stay_awake()
|
159
|
+
control.stay_awake
|
160
|
+
end
|
161
|
+
|
162
|
+
def stay_awake_off()
|
163
|
+
control.stay_awake_off
|
164
|
+
end
|
165
|
+
|
166
|
+
def stop()
|
167
|
+
control_media(option='Stop')
|
168
|
+
end
|
169
|
+
|
170
|
+
def take_picture(ftp_src: nil, fileout: '.')
|
171
|
+
|
172
|
+
#screen.on
|
173
|
+
r = query.take_picture
|
174
|
+
|
175
|
+
if ftp_src then
|
176
|
+
|
177
|
+
# give the device a second to write the image to file
|
178
|
+
sleep 1
|
179
|
+
|
180
|
+
credentials, dir = ftp_src.match(/(ftp:\/\/[^\/]+)\/([^$]+)/).captures
|
181
|
+
ftp = MyMediaFTP.new(credentials)
|
182
|
+
ftp.cd dir
|
183
|
+
filename = ftp.ls.sort_by {|x| x[:ctime]}.last[:name]
|
184
|
+
ftp.cp filename, fileout
|
185
|
+
|
186
|
+
end
|
187
|
+
|
188
|
+
end
|
189
|
+
|
190
|
+
alias take_photo take_picture
|
191
|
+
|
192
|
+
def torch()
|
193
|
+
control.torch
|
194
|
+
end
|
195
|
+
|
196
|
+
def vibrate
|
197
|
+
control.vibrate
|
198
|
+
end
|
199
|
+
|
200
|
+
end
|
201
|
+
end
|
@@ -0,0 +1,207 @@
|
|
1
|
+
module RemoteDroid
|
2
|
+
|
3
|
+
class Control
|
4
|
+
|
5
|
+
def initialize(dev=nil, deviceid: dev, remote_url: nil, debug: false)
|
6
|
+
|
7
|
+
@deviceid, @remote_url, @debug = deviceid, remote_url, debug
|
8
|
+
|
9
|
+
end
|
10
|
+
|
11
|
+
def bluetooth()
|
12
|
+
@bluetooth
|
13
|
+
end
|
14
|
+
|
15
|
+
def camera_flash_light(options={})
|
16
|
+
http_exec 'camera-flash-light', options
|
17
|
+
end
|
18
|
+
|
19
|
+
def control_media(options={})
|
20
|
+
http_exec 'media-' + options[:option].downcase.gsub(/\W/,'-')
|
21
|
+
end
|
22
|
+
|
23
|
+
def disable(macro)
|
24
|
+
http_exec 'disable-macro', {name: macro}
|
25
|
+
end
|
26
|
+
|
27
|
+
def enable(macro)
|
28
|
+
http_exec 'enable-macro', {name: macro}
|
29
|
+
end
|
30
|
+
|
31
|
+
def force_macro_run(options={})
|
32
|
+
http_exec option[:macro_name].downcase.gsub(/ /,'-')
|
33
|
+
end
|
34
|
+
|
35
|
+
def hotspot(state=nil)
|
36
|
+
|
37
|
+
if state then
|
38
|
+
http_exec 'hotspot', {enable: state == :enable}
|
39
|
+
else
|
40
|
+
|
41
|
+
def self.enable()
|
42
|
+
http_exec 'hotspot', {enable: true}
|
43
|
+
end
|
44
|
+
|
45
|
+
def self.on()
|
46
|
+
self.enable
|
47
|
+
end
|
48
|
+
|
49
|
+
def self.disable()
|
50
|
+
http_exec 'hotspot', {enable: false}
|
51
|
+
end
|
52
|
+
|
53
|
+
def self.off()
|
54
|
+
self.disable
|
55
|
+
end
|
56
|
+
|
57
|
+
self
|
58
|
+
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def http_exec(command, options={})
|
63
|
+
|
64
|
+
url = "https://trigger.macrodroid.com/%s/%s" % [@deviceid, command]
|
65
|
+
|
66
|
+
if options and options.any? then
|
67
|
+
h = options
|
68
|
+
url += '?' + \
|
69
|
+
URI.escape(h.map {|key,value| "%s=%s" % [key, value]}.join('&'))
|
70
|
+
end
|
71
|
+
|
72
|
+
s = open(url).read
|
73
|
+
|
74
|
+
end
|
75
|
+
|
76
|
+
def launch_activity(options={})
|
77
|
+
app = options[:app].downcase.gsub(/ /,'-')
|
78
|
+
http_exec 'launch-' + app
|
79
|
+
end
|
80
|
+
|
81
|
+
def location(options={})
|
82
|
+
http_exec 'location'
|
83
|
+
end
|
84
|
+
|
85
|
+
def say_current_time(options={})
|
86
|
+
http_exec 'say-current-time'
|
87
|
+
end
|
88
|
+
|
89
|
+
alias say_time say_current_time
|
90
|
+
|
91
|
+
def screen(state=nil)
|
92
|
+
|
93
|
+
if state then
|
94
|
+
http_exec 'screen', {on: state == :on}
|
95
|
+
else
|
96
|
+
|
97
|
+
def self.on()
|
98
|
+
http_exec 'screen', {on: true}
|
99
|
+
end
|
100
|
+
|
101
|
+
def self.off()
|
102
|
+
http_exec 'screen', {on: false}
|
103
|
+
end
|
104
|
+
|
105
|
+
self
|
106
|
+
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
def share_location(options={})
|
111
|
+
http_exec 'share-location'
|
112
|
+
end
|
113
|
+
|
114
|
+
def speak_text(obj)
|
115
|
+
|
116
|
+
options = case obj
|
117
|
+
when String
|
118
|
+
{text: obj}
|
119
|
+
when Hash
|
120
|
+
obj
|
121
|
+
end
|
122
|
+
|
123
|
+
http_exec 'speak-text', options
|
124
|
+
end
|
125
|
+
|
126
|
+
alias say speak_text
|
127
|
+
|
128
|
+
def stay_awake(options={})
|
129
|
+
http_exec 'stay-awake', options
|
130
|
+
end
|
131
|
+
|
132
|
+
def stay_awake_off(options={})
|
133
|
+
http_exec 'stay-awake-off', options
|
134
|
+
end
|
135
|
+
|
136
|
+
def take_picture(options={})
|
137
|
+
http_exec 'take-picture', options
|
138
|
+
end
|
139
|
+
|
140
|
+
alias take_photo take_picture
|
141
|
+
|
142
|
+
|
143
|
+
def toast(options={})
|
144
|
+
http_exec :toast, options
|
145
|
+
end
|
146
|
+
|
147
|
+
def torch(options={})
|
148
|
+
http_exec :torch
|
149
|
+
end
|
150
|
+
|
151
|
+
def vibrate(options={})
|
152
|
+
http_exec :vibrate
|
153
|
+
end
|
154
|
+
|
155
|
+
|
156
|
+
def write(s)
|
157
|
+
|
158
|
+
d = MacroDroid.new(RD_MACROS, deviceid: @deviceid,
|
159
|
+
remote_url: @remote_url, debug: false)
|
160
|
+
|
161
|
+
a = d.macros.select do |macro|
|
162
|
+
|
163
|
+
macro.triggers.find {|trigger| trigger.is_a? WebHookTrigger }.nil?
|
164
|
+
|
165
|
+
end
|
166
|
+
puts 'a: ' + a.length.inspect
|
167
|
+
|
168
|
+
aux_macros = %w(Disable Enable).map do |state|
|
169
|
+
|
170
|
+
rows = a[1..-1].map do |macro|
|
171
|
+
|
172
|
+
" Else If name = #{macro.title}
|
173
|
+
#{state} macro
|
174
|
+
#{macro.title}"
|
175
|
+
end
|
176
|
+
|
177
|
+
"
|
178
|
+
m: #{state} macro
|
179
|
+
v: name
|
180
|
+
t: webhook
|
181
|
+
a:
|
182
|
+
If name = #{a[0].title}
|
183
|
+
#{state} macro
|
184
|
+
#{a[0].title}
|
185
|
+
#{rows.join("\n")}
|
186
|
+
End If
|
187
|
+
" end
|
188
|
+
|
189
|
+
puts aux_macros.join
|
190
|
+
d.import aux_macros.join
|
191
|
+
|
192
|
+
# disable the non-webhook triggers by default
|
193
|
+
a.each(&:disable)
|
194
|
+
|
195
|
+
d.export s
|
196
|
+
puts 'exported to ' + s
|
197
|
+
|
198
|
+
end
|
199
|
+
|
200
|
+
alias export write
|
201
|
+
|
202
|
+
def method_missing2(method_name, *args)
|
203
|
+
http_exec(method_name, args.first)
|
204
|
+
end
|
205
|
+
|
206
|
+
end
|
207
|
+
end
|
@@ -0,0 +1,170 @@
|
|
1
|
+
module RemoteDroid
|
2
|
+
|
3
|
+
class Controller
|
4
|
+
|
5
|
+
attr_reader :model, :control, :syslog
|
6
|
+
attr_accessor :title, :macros, :store
|
7
|
+
|
8
|
+
def initialize(mcs, model=MODEL, deviceid: nil, debug: false)
|
9
|
+
|
10
|
+
@debug = debug
|
11
|
+
@syslog = []
|
12
|
+
|
13
|
+
@control = Control.new(deviceid)
|
14
|
+
@macros = mcs.macros
|
15
|
+
|
16
|
+
if model then
|
17
|
+
@model = Model.new(model)
|
18
|
+
end
|
19
|
+
|
20
|
+
@store = {}
|
21
|
+
@query = Query.new(self)
|
22
|
+
|
23
|
+
# enable the required triggers on the Android device
|
24
|
+
#
|
25
|
+
names = @macros.map {|x| x.triggers.first.type}.uniq
|
26
|
+
#@control.enable names.first.to_s.gsub('_',' ')
|
27
|
+
puts 'Enabling ' + names.join(',')
|
28
|
+
=begin
|
29
|
+
Thread.new do
|
30
|
+
names.each do |title|
|
31
|
+
@control.enable title.to_s.gsub('_',' ')
|
32
|
+
sleep 0.8
|
33
|
+
end
|
34
|
+
end
|
35
|
+
=end
|
36
|
+
end
|
37
|
+
|
38
|
+
def delete_all()
|
39
|
+
@macros = []
|
40
|
+
end
|
41
|
+
|
42
|
+
def export(s, replace: false)
|
43
|
+
|
44
|
+
macros = MacroDroid.new(s).macros
|
45
|
+
replace ? @macros = macros : @macros << macros
|
46
|
+
|
47
|
+
end
|
48
|
+
|
49
|
+
def invoke(name, options={})
|
50
|
+
|
51
|
+
if @control.respond_to? name.to_sym then
|
52
|
+
@control.method(name.to_sym).call(options)
|
53
|
+
else
|
54
|
+
@control.http_exec name.to_sym, options
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
# Object Property (op)
|
59
|
+
# Helpful for accessing properites in dot notation
|
60
|
+
# e.g. op.livingroom.light.switch = 'off'
|
61
|
+
#
|
62
|
+
def op()
|
63
|
+
@model.op
|
64
|
+
end
|
65
|
+
|
66
|
+
def query(id=nil)
|
67
|
+
|
68
|
+
return @query unless id
|
69
|
+
|
70
|
+
@store[id] = nil
|
71
|
+
|
72
|
+
sys = %i(accelerometer_rotation)
|
73
|
+
|
74
|
+
global = [:airplane_mode_on, :bluetooth_on, :cell_on, :device_name, \
|
75
|
+
:usb_mass_storage_enabled, :wifi_on]
|
76
|
+
|
77
|
+
secure = %i(bluetooth_name flashlight_enabled)
|
78
|
+
|
79
|
+
|
80
|
+
# send http request via macrodroid.com API
|
81
|
+
|
82
|
+
if id.downcase.to_sym == :location then
|
83
|
+
@control.http_exec id
|
84
|
+
elsif sys.include? id
|
85
|
+
@control.http_exec :'query-setting-system', {qvar: id}
|
86
|
+
elsif global.include? id
|
87
|
+
@control.http_exec :'query-setting-global', {qvar: id}
|
88
|
+
elsif secure.include? id
|
89
|
+
@control.http_exec :'query-setting-secure', {qvar: id}
|
90
|
+
elsif id.downcase.to_sym == :'take-picture'
|
91
|
+
@control.http_exec id
|
92
|
+
else
|
93
|
+
@control.http_exec :query, {qvar: id}
|
94
|
+
end
|
95
|
+
|
96
|
+
# wait for the local variable to be updated
|
97
|
+
# timeout after 5 seoncds
|
98
|
+
t = Time.now
|
99
|
+
|
100
|
+
begin
|
101
|
+
sleep 1
|
102
|
+
end until @store[id] or Time.now > t + 10
|
103
|
+
|
104
|
+
return {warning: 'HTTP response timeout'} if Time.now > t+5
|
105
|
+
|
106
|
+
return @store[id]
|
107
|
+
|
108
|
+
|
109
|
+
end
|
110
|
+
|
111
|
+
def request(s)
|
112
|
+
@model.request s
|
113
|
+
end
|
114
|
+
|
115
|
+
def run_macro(macro_name: '')
|
116
|
+
|
117
|
+
found = @macros.find do |macro|
|
118
|
+
macro.title.downcase == macro_name.downcase
|
119
|
+
end
|
120
|
+
|
121
|
+
found.run if found
|
122
|
+
|
123
|
+
end
|
124
|
+
|
125
|
+
def trigger(name, detail={})
|
126
|
+
|
127
|
+
macros = @macros.select do |macro|
|
128
|
+
|
129
|
+
puts 'macro: ' + macro.inspect if @debug
|
130
|
+
|
131
|
+
# fetch the associated properties from the model if possible and
|
132
|
+
# merge them into the detail.
|
133
|
+
#
|
134
|
+
valid_trigger = macro.match?(name, detail, @model.op)
|
135
|
+
|
136
|
+
#puts 'valid_trigger: ' + valid_trigger.inspect if @debug
|
137
|
+
|
138
|
+
#if valid_trigger then
|
139
|
+
# @syslog << [Time.now, :trigger, name]
|
140
|
+
# @syslog << [Time.now, :macro, macro.title]
|
141
|
+
#end
|
142
|
+
|
143
|
+
@syslog << [Time.now, name, detail]
|
144
|
+
|
145
|
+
valid_trigger
|
146
|
+
|
147
|
+
end
|
148
|
+
|
149
|
+
puts 'macros: ' + macros.inspect if @debug
|
150
|
+
|
151
|
+
macros.flat_map(&:run)
|
152
|
+
end
|
153
|
+
|
154
|
+
alias trigger_fired trigger
|
155
|
+
|
156
|
+
def update(id, val)
|
157
|
+
|
158
|
+
key = if %i(location take-picture).include? id
|
159
|
+
id
|
160
|
+
else
|
161
|
+
val.keys.first.to_sym
|
162
|
+
end
|
163
|
+
|
164
|
+
@syslog << [id, val]
|
165
|
+
@store[key] = val
|
166
|
+
|
167
|
+
end
|
168
|
+
|
169
|
+
end
|
170
|
+
end
|