projectsimulator 0.2.2 → 0.4.0

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 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