projectsimulator 0.2.2 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 004c088ecb944c24b758301d5cd41fe5a9135b6401b5c489bbac794a951ad17e
4
- data.tar.gz: d5ce967f78e745769888e57b0f3342f1c2669280923bbd699f55a066f84fc5de
3
+ metadata.gz: 7e70e7f4a42e34c20b395b0fab1113c1ed59bbb00c2e0b0b117b201708d8578c
4
+ data.tar.gz: 3c9431a5a680b62c1070023a2b32f4b540530145d0eb896af6bc629105643d2f
5
5
  SHA512:
6
- metadata.gz: 35fbc194fa72f2fa1b8081259504e62abe03b47336b8bbf952957b172f2d19276296278406a0db710a8dca39e9d798bb4514de397d429cabcf981085a7272c53
7
- data.tar.gz: 5de28c8c32f433c3efe1cad7b90cd0111ac69025fa40ed4d28e75cd14ae35c52a755feb3ce899ccd27836bc6c61218292cf0789def2baa6812c644c0d219a0c3
6
+ metadata.gz: '0683c9527e84ffa413551d16b9d9fff3805fe9042a7068b3bdbf8b7c6ede26894aa6d2ce26a7cef07144abfa7501c577f0db9aa13418fe40f5283c283563be46'
7
+ data.tar.gz: a64eb0b3ba73be9163927edcdb8f202f955fc29474a9c65539a456a3f4e70f3612cbe97c9892a996b62d529436485319183cce26c80f37883f6b162fd204b443
Binary file
data.tar.gz.sig CHANGED
Binary file
@@ -2,556 +2,35 @@
2
2
 
3
3
  # file: projectsimulator.rb
4
4
 
5
- require 'rowx'
5
+
6
6
  require 'easydom'
7
+ require 'unichron'
7
8
  require 'app-routes'
8
- require 'chronic_between'
9
9
 
10
10
 
11
11
  module ProjectSimulator
12
12
 
13
- class Model
14
- include AppRoutes
15
-
16
- def initialize(obj=nil, root: 'building1')
17
-
18
- super()
19
- @root = root
20
- @location = nil
21
- build(obj, root: root) if obj
22
-
23
- end
24
-
25
- def build(raw_requests, root: @root)
26
-
27
- @ed = EasyDom.new(debug: false, root: root)
28
- raw_requests.lines.each {|line| request(line) }
29
-
30
- end
31
-
32
- def get_device(h)
33
-
34
- a = h[:location].split(/ /)
35
- a << h[:device]
36
- status = a.inject(@ed) {|r,x| r.send(x)}.send(h[:action])
37
- "The %s %s is %s." % [h[:location], h[:device], status]
38
-
39
- end
40
-
41
- def op()
42
- @ed
43
- end
44
-
45
- def query(s)
46
- @ed.e.element(s)
47
- end
48
-
49
- def request(s)
50
-
51
- params = {request: s}
52
- requests(params)
53
- h = find_request(s)
54
-
55
- method(h.first[-1]).call(h)
56
-
57
- end
13
+ class Server
58
14
 
59
- def set_device(h)
60
-
61
- a = h[:location].split(/ /)
62
- a << h[:device]
63
- a.inject(@ed) {|r,x| r.send(x)}.send(h[:action], h[:value])
64
-
65
- end
66
-
67
- def to_sliml()
68
- @ed.to_sliml
69
- end
70
-
71
- def xml(options=nil)
72
- @ed.xml(pretty: true).gsub(' style=\'\'','')
73
- end
74
-
75
- protected
76
-
77
- def requests(params)
78
-
79
- # e.g. switch the livingroom gas_fire off
80
- #
81
- get /(?:switch|turn) the ([^ ]+) +([^ ]+) +(on|off)$/ do |location, device, onoff|
82
- {type: :set_device, action: 'switch=', location: location, device: device, value: onoff}
83
- end
15
+ def initialize(macros_package, drb_host: '127.0.0.1', devices: nil,
16
+ debug: false)
84
17
 
85
- # e.g. switch the gas _fire off
86
- #
87
- get /(?:switch|turn) the ([^ ]+) +(on|off)$/ do |device, onoff|
88
- location = dev_location(device)
89
- {type: :set_device, action: 'switch=', location: location, device: device, value: onoff}
90
- end
18
+ rdc = ProjectSimulator::Controller.new(macros_package, devices: devices,
19
+ debug: debug)
20
+ @drb = OneDrb::Server.new host: drb_host, port: '5777', obj: rdc
91
21
 
92
- # e.g. is the livingroom gas_fire on?
93
- #
94
- get /is the ([^ ]+) +([^ ]+) +(?:on|off)\??$/ do |location, device|
95
- {type: :get_device, action: 'switch', location: location, device: device}
96
- end
97
-
98
- # e.g. is the gas_fire on?
99
- #
100
- get /is the ([^ ]+) +(?:on|off)\??$/ do |device|
101
- location = dev_location(device)
102
- {type: :get_device, action: 'switch', location: location, device: device}
103
- end
104
-
105
- # e.g. fetch the livingroom temperature reading
106
- #
107
- get /fetch the ([^ ]+) +([^ ]+) +(?:reading)$/ do |location, device|
108
- {type: :get_device, action: 'reading', location: location, device: device}
109
- end
110
-
111
- # e.g. fetch the temperature reading
112
- #
113
- get /fetch the ([^ ]+) +(?:reading)$/ do |device|
114
- location = dev_location(device)
115
- {type: :get_device, action: 'reading', location: location, device: device}
116
- end
117
-
118
22
  end
119
23
 
120
- private
121
-
122
- def dev_location(device)
123
- a = query('//'+ device).backtrack.to_xpath.split('/')
124
- a[1..-2].join(' ')
24
+ def start
25
+ @drb.start
125
26
  end
126
-
127
- alias find_request run_route
128
27
 
129
28
  end
130
29
 
131
30
 
132
- class Event
133
-
134
- attr_accessor :title
135
- attr_reader :triggers, :actions, :constraints, :messages
136
-
137
- def initialize(e, time: nil, title: '', debug: false)
138
-
139
- @time, @debug = time, debug
140
- @title = e.text('event')
141
- @actions = []
142
- @triggers = []
143
- @constraints = []
144
-
145
- e.xpath('trigger').each do |x|
146
- @triggers << Trigger.new(x.text().strip, time: time, debug: debug)\
147
- .to_type
148
- end
149
-
150
- e.xpath('action').each do |x|
151
- @actions << Action.new(x.text().strip, debug: debug).to_type
152
- end
153
-
154
- e.xpath('constraint').each do |x|
155
- puts 'before Constraints.new'
156
- @constraints << Constraint.new(x.text().strip, \
157
- time: time, debug: debug).to_type
158
- end
159
-
160
- end
161
-
162
- def match(trigger: nil, location: '')
163
-
164
- @messages = []
165
-
166
- h = {motion: MotionTrigger}
167
-
168
- if @triggers.any? {|x| x.is_a? h[trigger.to_sym] and x.match } then
169
-
170
- if @constraints.all?(&:match) then
171
-
172
- @messages = @actions.map(&:call)
173
-
174
- else
175
-
176
- puts 'else reset?' if @debug
177
- a = @constraints.select {|x| x.is_a? FrequencyConstraint }
178
- puts 'a:' + a.inspect if @debug
179
- a.each {|x| x.reset if x.counter > 0 }
180
- return false
181
- end
182
-
183
- end
184
-
185
- end
186
-
187
- def time=(val)
188
- @time = val
189
- @constraints.each {|x| x.time = val if x.is_a? TimeConstraint }
190
- end
191
-
192
- def to_node()
193
-
194
- e = Rexle::Element.new(:event, attributes: {title: @title})
195
-
196
- e.add node_collection(:triggers, @triggers)
197
- e.add node_collection(:actions, @actions)
198
- e.add node_collection(:constraints, @constraints)
199
-
200
- return e
201
- end
202
-
203
- def to_rowx()
204
-
205
- s = "event: %s\n\n" % @title
206
- s + [@triggers, @actions, @constraints]\
207
- .map {|x| x.collect(&:to_rowx).join("\n")}.join("\n")
208
- end
209
-
210
- private
211
-
212
- def node_collection(name, a)
213
-
214
- e = Rexle::Element.new(name)
215
- a.each {|x| e.add x.to_node}
216
- return e
217
-
218
- end
219
-
220
- end
221
-
222
- class Action
223
- include AppRoutes
224
-
225
- attr_reader :to_type
226
-
227
- def initialize(s, event: '', debug: false)
228
-
229
- super()
230
- @debug = debug
231
- params = {s: s, event: event}
232
- actions(params)
233
- @to_type = find_action(s) || {}
234
-
235
- end
236
-
237
- protected
238
-
239
- def actions(params)
240
-
241
- puts 'inside actions'
242
- # e.g. Say 'Good morning'
243
- #
244
- get /say ['"]([^'"]+)/i do |s|
245
- puts 's: ' + s.inspect if @debug
246
- SayAction.new(s)
247
- end
248
-
249
- # e.g. webhook entered_kitchen
250
- #
251
- get /webhook (.*)/i do |name|
252
- WebhookAction.new(name)
253
- end
254
-
255
- get /.*/ do
256
- puts 'action unknown' if @debug
257
- {}
258
- end
259
-
260
- end
261
-
262
- private
263
-
264
- alias find_action run_route
265
-
266
- end
267
-
268
- class Trigger
269
- include AppRoutes
270
-
271
- attr_reader :to_type
272
-
273
- def initialize(s, time: nil, debug: false)
274
-
275
- super()
276
- @time, @debug = time, debug
277
- params = {s: s}
278
- puts 'inside Trigger'
279
- puts 'params: ' + params.inspect
280
- triggers(params)
281
- @to_type = find_trigger(s)
282
-
283
- end
284
-
285
- protected
286
-
287
- def triggers(params)
288
-
289
- puts 'inside triggers'
290
-
291
- # e.g. Motion detected in the kitchen
292
- #
293
- get /motion detected in the (.*)/i do |location|
294
- puts 'motion detection trigger'
295
- MotionTrigger.new(location)
296
- end
297
-
298
- end
299
-
300
- private
301
-
302
- alias find_trigger run_route
303
-
304
- end
305
-
306
- class Constraint
307
- include AppRoutes
308
-
309
- attr_reader :to_type
310
-
311
- def initialize(s, time: nil, debug: false)
312
-
313
- super()
314
- @time, @debug = time, debug
315
-
316
- params = {s: s }
317
- constraints(params)
318
- @to_type = find_constraint(s)
319
-
320
- end
321
-
322
- protected
323
-
324
- def constraints(params)
325
-
326
- puts 'inside constraints' if @debug
327
- # e.g. Between 8am and 10am
328
- #
329
- get /^between (.*)/i do |s|
330
- TimeConstraint.new(s, time: @time)
331
- end
332
-
333
- get /^on a (.*)/i do |s|
334
- TimeConstraint.new(s, time: @time)
335
- end
336
-
337
- get /^(after .*)/i do |s|
338
- TimeConstraint.new(s, time: @time)
339
- end
340
-
341
- get /^once only|only once|once|one time|1 time$/i do |s|
342
- FrequencyConstraint.new(1, debug: @debug)
343
- end
344
-
345
- get /^twice only|only twice|twice|two times|2 times$/i do |s|
346
- FrequencyConstraint.new(2, debug: @debug)
347
- end
348
-
349
- get /^(Maximum|Max|Up to) ?three times|3 times$/i do |s|
350
- FrequencyConstraint.new(3, debug: @debug)
351
- end
352
-
353
- get /^(Maximum|Max|Up to) ?four times|4 times$/i do |s|
354
- FrequencyConstraint.new(4, debug: @debug)
355
- end
356
-
357
- end
358
-
359
- private
360
-
361
- alias find_constraint run_route
362
- end
363
-
364
- class MotionTrigger
365
-
366
- attr_reader :location
367
-
368
- def initialize(locationx, location: locationx)
369
- @location = location
370
- end
371
-
372
- def match()
373
- @location.downcase == location.downcase
374
- end
375
-
376
- def to_node()
377
- Rexle::Element.new(:trigger, \
378
- attributes: {type: :motion, location: @location})
379
- end
380
-
381
- def to_rowx()
382
- "trigger: Motion detected in the %s" % @location
383
- end
384
-
385
- end
386
-
387
- class SayAction
388
-
389
- def initialize(s, text: s)
390
- @s = s
391
- end
392
-
393
- def call()
394
- "say: %s" % @s
395
- end
396
-
397
- def to_node()
398
- Rexle::Element.new(:action, attributes: {type: :say, text: @s})
399
- end
400
-
401
- def to_rowx()
402
- "action: say %s" % @s
403
- end
404
-
405
- end
406
-
407
- class WebhookAction
408
-
409
- attr_accessor :url
410
-
411
- def initialize(namex, name: namex, url: '127.0.0.1')
412
- @name = name
413
- @url = url
414
- end
415
-
416
- def call()
417
- "webhook: %s" % @url
418
- end
419
-
420
- def to_node()
421
- Rexle::Element.new(:action, \
422
- attributes: {type: :webhook, name: @name, url: @url})
423
- end
424
-
425
- def to_rowx()
426
- s = "action: webhook %s" % @name
427
- s += "\n url: %s" % @url
428
- end
429
-
430
- end
431
-
432
- class TimeConstraint
433
-
434
- attr_accessor :time
435
-
436
- def initialize(timesx, times: timesx, time: nil)
437
- @times, @time = times, time
438
- end
439
-
440
- def match()
441
- ChronicBetween.new(@times).within?(@time)
442
- end
443
-
444
- def to_node()
445
- Rexle::Element.new(:constraint, \
446
- attributes: {type: :time, times: @times})
447
- end
448
-
449
- def to_rowx()
450
- "constraint: %s" % @times
451
- end
452
-
453
- end
454
-
455
- class FrequencyConstraint
456
-
457
- def initialize(freqx, freq: freqx, debug: false)
458
- @freq, @debug = freq, debug
459
- @counter = 0
460
- end
461
-
462
- def counter()
463
- @counter
464
- end
465
-
466
- def increment()
467
- @counter += 1
468
- end
469
-
470
- def match()
471
- @counter < @freq
472
- end
473
-
474
- def reset()
475
- puts 'resetting' if @debug
476
- @counter = 0
477
- end
478
-
479
- def to_node()
480
- Rexle::Element.new(:constraint, \
481
- attributes: {type: :frequency, freq: @freq})
482
- end
483
-
484
- def to_rowx()
485
-
486
- freq = case @freq
487
- when 1
488
- 'Once'
489
- when 2
490
- 'Twice'
491
- else
492
- "Maximum %s times" % @freq
493
- end
494
-
495
- "constraint: %s" % freq
496
-
497
- end
498
-
499
- end
500
-
501
- class Controller
502
-
503
- attr_reader :events
504
- attr_accessor :time
505
-
506
- def initialize(s, time: Time.now, debug: false)
507
-
508
- @time, @debug = time, debug
509
-
510
- doc = Rexle.new(RowX.new(s).to_xml)
511
-
512
- @events = doc.root.xpath('item')\
513
- .map {|e| Event.new(e, time: @time, debug: debug) }
514
-
515
- end
516
-
517
- def time=(val)
518
- @time = val
519
- @events.each {|event| event.time = val }
520
- end
521
-
522
- def to_doc()
523
-
524
- doc = Rexle.new('<events/>')
525
- @events.each {|event| doc.root.add event.to_node }
526
- return doc
527
-
528
- end
529
-
530
- def to_rowx()
531
- @events.collect(&:to_rowx).join("\n\n#{'-'*50}\n\n")
532
- end
533
-
534
- def trigger(name, location: '')
535
-
536
- events = @events.select do |event|
537
-
538
- puts 'event: ' + event.inspect if @debug
31
+ end
539
32
 
540
- event.match(trigger: 'motion', location: location)
541
-
542
- end
543
-
544
- puts 'events: ' + events.inspect if @debug
545
-
546
- events.each do |event|
547
- c = event.constraints.select {|x| x.is_a? FrequencyConstraint }
548
- puts 'c:' + c.inspect
549
- c.each(&:increment)
550
- end
551
-
552
- events.flat_map(&:messages)
553
-
554
- end
555
33
 
556
- end
557
- end
34
+ require 'projectsimulator/model'
35
+ require 'projectsimulator/controller'
36
+ require 'projectsimulator/client'
@@ -0,0 +1,116 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ # file: projectsimulator/controller.rb
4
+
5
+
6
+ module ProjectSimulator
7
+
8
+ class Controller
9
+
10
+ attr_reader :macros, :model
11
+ attr_accessor :title, :speed
12
+
13
+ def initialize(mcs, model=nil, time: Time.now, speed: 1, debug: false)
14
+
15
+ @debug = debug
16
+ @syslog = []
17
+
18
+ @macros = mcs.macros
19
+
20
+ if model then
21
+ @model = Model.new(model)
22
+ end
23
+
24
+ @state = :stop
25
+ @speed = speed
26
+
27
+ @qt = UnichronUtils::Quicktime.new time: time, speed: @speed
28
+ @qt.start
29
+ @qt.pause
30
+
31
+ end
32
+
33
+ # Object Property (op)
34
+ # Helpful for accessing properites in dot notation
35
+ # e.g. op.livingroom.light.switch = 'off'
36
+ #
37
+ def op()
38
+ @model.op if @model
39
+ end
40
+
41
+ def pause()
42
+ @state = :pause
43
+ @qt.pause
44
+ end
45
+
46
+ def play()
47
+ @state = :play
48
+ @qt.play
49
+ end
50
+
51
+ def request(s)
52
+ @model.request s
53
+ end
54
+
55
+ def start()
56
+
57
+
58
+ Thread.new do
59
+
60
+ old_time = @qt.time - 1
61
+
62
+ loop do
63
+
64
+ interval = (1 / (2.0 * @speed))
65
+ (sleep interval; next) if @state != :play or old_time == @qt.time
66
+ #puts Time.now.inspect if @debug
67
+ r = self.trigger :timer, {time: @qt.time}
68
+
69
+ yield(r) if r.any?
70
+
71
+ puts 'r: ' + r.inspect if @debug and r.any?
72
+ sleep interval
73
+ old_time = @qt.time
74
+ end
75
+
76
+ end
77
+ end
78
+
79
+ def stop()
80
+ @state = :stop
81
+ @qt.pause
82
+ end
83
+
84
+ def time()
85
+ @qt.time
86
+ end
87
+
88
+ def trigger(name, detail={})
89
+
90
+ macros = @macros.select do |macro|
91
+
92
+ #puts 'macro: ' + macro.inspect if @debug
93
+
94
+ # fetch the associated properties from the model if possible and
95
+ # merge them into the detail.
96
+ #
97
+ valid_trigger = macro.match?(name, detail, op())
98
+
99
+ #puts 'valid_trigger: ' + valid_trigger.inspect if @debug
100
+
101
+ if valid_trigger then
102
+ @syslog << [Time.now, :trigger, name]
103
+ @syslog << [Time.now, :macro, macro.title]
104
+ end
105
+
106
+ valid_trigger
107
+
108
+ end
109
+
110
+ #puts 'macros: ' + macros.inspect if @debug
111
+
112
+ macros.flat_map(&:run)
113
+ end
114
+
115
+ end
116
+ end
@@ -0,0 +1,232 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ # file: projectsimulator/controller.rb
4
+
5
+
6
+ module ProjectSimulator
7
+
8
+ class Model
9
+ include AppRoutes
10
+
11
+ def initialize(obj=nil, root: 'building1', debug: false)
12
+
13
+ super()
14
+ @root, @debug = root, debug
15
+ @location = nil
16
+
17
+ if obj then
18
+
19
+ s = obj.strip
20
+
21
+ puts 's: ' + s.inspect if @debug
22
+
23
+ if s[0] == '<' or s.lines[1][0..1] == ' ' then
24
+
25
+ puts 'before easydom' if @debug
26
+
27
+ s2 = if s.lines[1][0..1] == ' ' then
28
+
29
+ lines = s.lines.map do |line|
30
+ line.sub(/(\w+) +is +(\w+)$/) {|x| "#{$1} {switch: #{$2}}" }
31
+ end
32
+
33
+ lines.join
34
+
35
+ else
36
+ s
37
+ end
38
+
39
+ @ed = EasyDom.new(s2)
40
+ else
41
+ build(s, root: root)
42
+ end
43
+
44
+ end
45
+
46
+ end
47
+
48
+ def build(raw_requests, root: @root)
49
+
50
+ @ed = EasyDom.new(debug: false, root: root)
51
+ raw_requests.lines.each {|line| request(line) }
52
+
53
+ end
54
+
55
+
56
+ def get_thing(h)
57
+
58
+ h[:thing].gsub!(/ /,'_')
59
+
60
+ if not h.has_key? :location then
61
+ location = false
62
+ h[:location] = find_path(h[:thing])
63
+ else
64
+ location = true
65
+ end
66
+
67
+ puts 'h: ' + h.inspect if @debug
68
+
69
+ a = []
70
+ a += h[:location].split(/ /)
71
+ a << h[:thing]
72
+ status = a.inject(@ed) {|r,x| r.send(x)}.send(h[:action])
73
+
74
+ if location then
75
+ "The %s %s is %s." % [h[:location], h[:thing], status]
76
+ else
77
+ "%s is %s." % [h[:thing].capitalize, status]
78
+ end
79
+
80
+ end
81
+
82
+ # Object Property (op)
83
+ # Helpful for accessing properites in dot notation
84
+ # e.g. op.livingroom.light.switch = 'off'
85
+ #
86
+ def op()
87
+ @ed
88
+ end
89
+
90
+ def query(s)
91
+ @ed.e.element(s)
92
+ end
93
+
94
+ # request accepts a string in plain english
95
+ # e.g. request 'switch the livingroom light on'
96
+ #
97
+ def request(s)
98
+
99
+ params = {request: s}
100
+ requests(params)
101
+ h = find_request(s)
102
+
103
+ method(h.first[-1]).call(h).gsub(/_/,' ')
104
+
105
+ end
106
+
107
+ def set_thing(h)
108
+
109
+ h[:thing].gsub!(/ /,'_')
110
+ h[:location] = find_path(h[:thing]) unless h.has_key? :location
111
+
112
+ a = []
113
+ a += h[:location].split(/ /)
114
+ a << h[:thing]
115
+
116
+ a.inject(@ed) {|r,x| r.send(x)}.send(h[:action], h[:value])
117
+
118
+ end
119
+
120
+ def to_sliml(level: 0)
121
+
122
+ s = @ed.to_sliml
123
+
124
+ return s if level.to_i > 0
125
+
126
+ lines = s.lines.map do |line|
127
+
128
+ line.sub(/\{[^\}]+\}/) do |x|
129
+
130
+ a = x.scan(/\w+: +[^ ]+/)
131
+ if a.length == 1 and x[/switch:/] then
132
+
133
+ val = x[/(?<=switch: ) *["']([^"']+)/,1]
134
+ 'is ' + val
135
+ else
136
+ x
137
+ end
138
+
139
+ end
140
+ end
141
+
142
+ lines.join
143
+
144
+ end
145
+
146
+ def to_xml(options=nil)
147
+ @ed.xml(pretty: true).gsub(' style=\'\'','')
148
+ end
149
+
150
+ alias xml to_xml
151
+
152
+ # to_xml() is the preferred method
153
+
154
+ protected
155
+
156
+ def requests(params)
157
+
158
+ # e.g. switch the livingroom gas_fire off
159
+ #
160
+ get /(?:switch|turn) the ([^ ]+) +([^ ]+) +(on|off)$/ do |location, device, onoff|
161
+ {type: :set_thing, action: 'switch=', location: location, thing: device, value: onoff}
162
+ end
163
+
164
+ # e.g. switch the gas _fire off
165
+ #
166
+ get /(?:switch|turn) the ([^ ]+) +(on|off)$/ do |device, onoff|
167
+ {type: :set_thing, action: 'switch=', thing: device, value: onoff}
168
+ end
169
+
170
+ # e.g. is the livingroom gas_fire on?
171
+ #
172
+ get /is the ([^ ]+) +([^ ]+) +(?:on|off)\??$/ do |location, device|
173
+ {type: :get_thing, action: 'switch', location: location, thing: device}
174
+ end
175
+
176
+ # e.g. enable airplane mode
177
+ #
178
+ get /((?:dis|en)able) ([^$]+)$/ do |state, service|
179
+ {type: :set_thing, action: 'switch=', thing: service, value: state + 'd'}
180
+ end
181
+
182
+ # e.g. switch airplane mode off
183
+ #
184
+ get /switch (.*) (on|off)/ do |service, rawstate|
185
+
186
+ state = rawstate == 'on' ? 'enabled' : 'disabled'
187
+ {type: :set_thing, action: 'switch=', thing: service, value: state}
188
+
189
+ end
190
+
191
+ # e.g. is airplane mode enabed?
192
+ #
193
+ get /is (.*) +(?:(?:dis|en)abled)\??$/ do |service|
194
+ {type: :get_thing, action: 'switch', thing: service.gsub(/ /,'_')}
195
+ end
196
+
197
+ # e.g. is the gas_fire on?
198
+ #
199
+ get /is the ([^ ]+) +(?:on|off)\??$/ do |device|
200
+ location = find_path(device)
201
+ {type: :get_thing, action: 'switch', location: location, thing: device}
202
+ end
203
+
204
+ # e.g. fetch the livingroom temperature reading
205
+ #
206
+ get /fetch the ([^ ]+) +([^ ]+) +(?:reading)$/ do |location, device|
207
+ {type: :get_thing, action: 'reading', location: location, thing: device}
208
+ end
209
+
210
+ # e.g. fetch the temperature reading
211
+ #
212
+ get /fetch the ([^ ]+) +(?:reading)$/ do |device|
213
+ location = find_path(device)
214
+ {type: :get_thing, action: 'reading', location: location, thing: device}
215
+ end
216
+
217
+ end
218
+
219
+ private
220
+
221
+ def find_path(s)
222
+ puts 'find_path s: ' + s.inspect if @debug
223
+ found = query('//'+ s)
224
+ return unless found
225
+ a = found.backtrack.to_xpath.split('/')
226
+ a[1..-2].join(' ')
227
+ end
228
+
229
+ alias find_request run_route
230
+
231
+ end
232
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: projectsimulator
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.2
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - James Robertson
@@ -35,28 +35,28 @@ cert_chain:
35
35
  2GrtfOXGVOS+2jvmCQC6vU+ew9WBxiDUbebI95TeTwMs2o0cs3IASXX5rIn4TPcz
36
36
  SCccB3fVg2yfsy5DivaWaZwg
37
37
  -----END CERTIFICATE-----
38
- date: 2020-05-07 00:00:00.000000000 Z
38
+ date: 2020-11-18 00:00:00.000000000 Z
39
39
  dependencies:
40
40
  - !ruby/object:Gem::Dependency
41
- name: rowx
41
+ name: unichron
42
42
  requirement: !ruby/object:Gem::Requirement
43
43
  requirements:
44
44
  - - ">="
45
45
  - !ruby/object:Gem::Version
46
- version: 0.7.0
46
+ version: 0.2.0
47
47
  - - "~>"
48
48
  - !ruby/object:Gem::Version
49
- version: '0.7'
49
+ version: '0.2'
50
50
  type: :runtime
51
51
  prerelease: false
52
52
  version_requirements: !ruby/object:Gem::Requirement
53
53
  requirements:
54
54
  - - ">="
55
55
  - !ruby/object:Gem::Version
56
- version: 0.7.0
56
+ version: 0.2.0
57
57
  - - "~>"
58
58
  - !ruby/object:Gem::Version
59
- version: '0.7'
59
+ version: '0.2'
60
60
  - !ruby/object:Gem::Dependency
61
61
  name: easydom
62
62
  requirement: !ruby/object:Gem::Requirement
@@ -97,26 +97,6 @@ dependencies:
97
97
  - - ">="
98
98
  - !ruby/object:Gem::Version
99
99
  version: 0.1.19
100
- - !ruby/object:Gem::Dependency
101
- name: chronic_between
102
- requirement: !ruby/object:Gem::Requirement
103
- requirements:
104
- - - "~>"
105
- - !ruby/object:Gem::Version
106
- version: '0.3'
107
- - - ">="
108
- - !ruby/object:Gem::Version
109
- version: 0.3.1
110
- type: :runtime
111
- prerelease: false
112
- version_requirements: !ruby/object:Gem::Requirement
113
- requirements:
114
- - - "~>"
115
- - !ruby/object:Gem::Version
116
- version: '0.3'
117
- - - ">="
118
- - !ruby/object:Gem::Version
119
- version: 0.3.1
120
100
  description:
121
101
  email: james@jamesrobertson.eu
122
102
  executables: []
@@ -124,6 +104,8 @@ extensions: []
124
104
  extra_rdoc_files: []
125
105
  files:
126
106
  - lib/projectsimulator.rb
107
+ - lib/projectsimulator/controller.rb
108
+ - lib/projectsimulator/model.rb
127
109
  homepage: https://github.com/jrobertson/projectsimulator
128
110
  licenses:
129
111
  - MIT
metadata.gz.sig CHANGED
Binary file