remotedroid 0.4.2 → 0.5.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -0
- data.tar.gz.sig +0 -0
- data/lib/remotedroid.rb +214 -648
- data/lib/remotedroid/client.rb +167 -0
- data/lib/remotedroid/control.rb +194 -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,167 @@
|
|
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 disable(macro)
|
64
|
+
control.disable macro
|
65
|
+
end
|
66
|
+
|
67
|
+
def enable(macro)
|
68
|
+
control.enable macro
|
69
|
+
end
|
70
|
+
|
71
|
+
def hotspot(state=nil)
|
72
|
+
control.hotspot state
|
73
|
+
end
|
74
|
+
|
75
|
+
def location()
|
76
|
+
query.location
|
77
|
+
end
|
78
|
+
|
79
|
+
def location_watch(refresh: '1 minute', interval: refresh,
|
80
|
+
duration: '30 minutes')
|
81
|
+
|
82
|
+
|
83
|
+
d = ChronicDuration.parse(duration)
|
84
|
+
seconds = ChronicDuration.parse(interval)
|
85
|
+
puts ("monitoring location every %s for %s" % [interval, duration]).info
|
86
|
+
|
87
|
+
Thread.new do
|
88
|
+
|
89
|
+
t = Time.now + d
|
90
|
+
|
91
|
+
begin
|
92
|
+
|
93
|
+
query.location
|
94
|
+
sleep seconds
|
95
|
+
|
96
|
+
end until Time.now >= t
|
97
|
+
|
98
|
+
end
|
99
|
+
|
100
|
+
end
|
101
|
+
|
102
|
+
def photo()
|
103
|
+
take_picture
|
104
|
+
end
|
105
|
+
|
106
|
+
def say(text)
|
107
|
+
control.speak_text text
|
108
|
+
end
|
109
|
+
|
110
|
+
def say_time()
|
111
|
+
control.say_time
|
112
|
+
end
|
113
|
+
|
114
|
+
alias saytime say_time
|
115
|
+
|
116
|
+
def screen(state=nil)
|
117
|
+
control.screen state
|
118
|
+
end
|
119
|
+
|
120
|
+
def screen_on()
|
121
|
+
screen :on
|
122
|
+
end
|
123
|
+
|
124
|
+
def screen_off()
|
125
|
+
screen :off
|
126
|
+
end
|
127
|
+
|
128
|
+
def stay_awake()
|
129
|
+
control.stay_awake
|
130
|
+
end
|
131
|
+
|
132
|
+
def stay_awake_off()
|
133
|
+
control.stay_awake_off
|
134
|
+
end
|
135
|
+
|
136
|
+
def take_picture(ftp_src: nil, fileout: '.')
|
137
|
+
|
138
|
+
#screen.on
|
139
|
+
r = query.take_picture
|
140
|
+
|
141
|
+
if ftp_src then
|
142
|
+
|
143
|
+
# give the device a second to write the image to file
|
144
|
+
sleep 1
|
145
|
+
|
146
|
+
credentials, dir = ftp_src.match(/(ftp:\/\/[^\/]+)\/([^$]+)/).captures
|
147
|
+
ftp = MyMediaFTP.new(credentials)
|
148
|
+
ftp.cd dir
|
149
|
+
filename = ftp.ls.sort_by {|x| x[:ctime]}.last[:name]
|
150
|
+
ftp.cp filename, fileout
|
151
|
+
|
152
|
+
end
|
153
|
+
|
154
|
+
end
|
155
|
+
|
156
|
+
alias take_photo take_picture
|
157
|
+
|
158
|
+
def torch()
|
159
|
+
control.torch
|
160
|
+
end
|
161
|
+
|
162
|
+
def vibrate
|
163
|
+
control.vibrate
|
164
|
+
end
|
165
|
+
|
166
|
+
end
|
167
|
+
end
|
@@ -0,0 +1,194 @@
|
|
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 disable(macro)
|
20
|
+
http_exec 'disable-macro', {name: macro}
|
21
|
+
end
|
22
|
+
|
23
|
+
def enable(macro)
|
24
|
+
http_exec 'enable-macro', {name: macro}
|
25
|
+
end
|
26
|
+
|
27
|
+
def hotspot(state=nil)
|
28
|
+
|
29
|
+
if state then
|
30
|
+
http_exec 'hotspot', {enable: state == :enable}
|
31
|
+
else
|
32
|
+
|
33
|
+
def self.enable()
|
34
|
+
http_exec 'hotspot', {enable: true}
|
35
|
+
end
|
36
|
+
|
37
|
+
def self.on()
|
38
|
+
self.enable
|
39
|
+
end
|
40
|
+
|
41
|
+
def self.disable()
|
42
|
+
http_exec 'hotspot', {enable: false}
|
43
|
+
end
|
44
|
+
|
45
|
+
def self.off()
|
46
|
+
self.disable
|
47
|
+
end
|
48
|
+
|
49
|
+
self
|
50
|
+
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def http_exec(command, options={})
|
55
|
+
|
56
|
+
url = "https://trigger.macrodroid.com/%s/%s" % [@deviceid, command]
|
57
|
+
|
58
|
+
if options and options.any? then
|
59
|
+
h = options
|
60
|
+
url += '?' + \
|
61
|
+
URI.escape(h.map {|key,value| "%s=%s" % [key, value]}.join('&'))
|
62
|
+
end
|
63
|
+
|
64
|
+
s = open(url).read
|
65
|
+
|
66
|
+
end
|
67
|
+
|
68
|
+
def location(options={})
|
69
|
+
http_exec 'location'
|
70
|
+
end
|
71
|
+
|
72
|
+
def say_current_time(options={})
|
73
|
+
http_exec 'say-current-time'
|
74
|
+
end
|
75
|
+
|
76
|
+
alias say_time say_current_time
|
77
|
+
|
78
|
+
def screen(state=nil)
|
79
|
+
|
80
|
+
if state then
|
81
|
+
http_exec 'screen', {on: state == :on}
|
82
|
+
else
|
83
|
+
|
84
|
+
def self.on()
|
85
|
+
http_exec 'screen', {on: true}
|
86
|
+
end
|
87
|
+
|
88
|
+
def self.off()
|
89
|
+
http_exec 'screen', {on: false}
|
90
|
+
end
|
91
|
+
|
92
|
+
self
|
93
|
+
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
def share_location(options={})
|
98
|
+
http_exec 'share-location'
|
99
|
+
end
|
100
|
+
|
101
|
+
def speak_text(obj)
|
102
|
+
|
103
|
+
options = case obj
|
104
|
+
when String
|
105
|
+
{text: obj}
|
106
|
+
when Hash
|
107
|
+
obj
|
108
|
+
end
|
109
|
+
|
110
|
+
http_exec 'speak-text', options
|
111
|
+
end
|
112
|
+
|
113
|
+
alias say speak_text
|
114
|
+
|
115
|
+
def stay_awake(options={})
|
116
|
+
http_exec 'stay-awake', options
|
117
|
+
end
|
118
|
+
|
119
|
+
def stay_awake_off(options={})
|
120
|
+
http_exec 'stay-awake-off', options
|
121
|
+
end
|
122
|
+
|
123
|
+
def take_picture(options={})
|
124
|
+
http_exec 'take-picture', options
|
125
|
+
end
|
126
|
+
|
127
|
+
alias take_photo take_picture
|
128
|
+
|
129
|
+
|
130
|
+
def toast(options={})
|
131
|
+
http_exec :toast, options
|
132
|
+
end
|
133
|
+
|
134
|
+
def torch(options={})
|
135
|
+
http_exec :torch
|
136
|
+
end
|
137
|
+
|
138
|
+
def vibrate(options={})
|
139
|
+
http_exec :vibrate
|
140
|
+
end
|
141
|
+
|
142
|
+
|
143
|
+
def write(s)
|
144
|
+
|
145
|
+
d = MacroDroid.new(RD_MACROS, deviceid: @deviceid,
|
146
|
+
remote_url: @remote_url, debug: false)
|
147
|
+
|
148
|
+
a = d.macros.select do |macro|
|
149
|
+
|
150
|
+
macro.triggers.find {|trigger| trigger.is_a? WebHookTrigger }.nil?
|
151
|
+
|
152
|
+
end
|
153
|
+
puts 'a: ' + a.length.inspect
|
154
|
+
|
155
|
+
aux_macros = %w(Disable Enable).map do |state|
|
156
|
+
|
157
|
+
rows = a[1..-1].map do |macro|
|
158
|
+
|
159
|
+
" Else If name = #{macro.title}
|
160
|
+
#{state} macro
|
161
|
+
#{macro.title}"
|
162
|
+
end
|
163
|
+
|
164
|
+
"
|
165
|
+
m: #{state} macro
|
166
|
+
v: name
|
167
|
+
t: webhook
|
168
|
+
a:
|
169
|
+
If name = #{a[0].title}
|
170
|
+
#{state} macro
|
171
|
+
#{a[0].title}
|
172
|
+
#{rows.join("\n")}
|
173
|
+
End If
|
174
|
+
" end
|
175
|
+
|
176
|
+
puts aux_macros.join
|
177
|
+
d.import aux_macros.join
|
178
|
+
|
179
|
+
# disable the non-webhook triggers by default
|
180
|
+
a.each(&:disable)
|
181
|
+
|
182
|
+
d.export s
|
183
|
+
puts 'exported to ' + s
|
184
|
+
|
185
|
+
end
|
186
|
+
|
187
|
+
alias export write
|
188
|
+
|
189
|
+
def method_missing2(method_name, *args)
|
190
|
+
http_exec(method_name, args.first)
|
191
|
+
end
|
192
|
+
|
193
|
+
end
|
194
|
+
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
|