macrohub 0.1.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 +7 -0
- checksums.yaml.gz.sig +0 -0
- data.tar.gz.sig +0 -0
- data/lib/macrohub.rb +572 -0
- metadata +150 -0
- metadata.gz.sig +0 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: ba95c07689c92cdb6df0081466aea3961da3eee30e7217f4d2077bca43984084
|
4
|
+
data.tar.gz: 3f860c4ba1baa27d5711b37a1443619d3c0418d27f0f3f64f7c8b204a5031ac1
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 6181da5d0d83f8a519ae29836a606376590a797fb97d8d3e0ac90cea8eec3bc9132703c6c39e09a2f9be2f675360115555c8be14b3bba3049323050e87fcd41c
|
7
|
+
data.tar.gz: 83b5c8505b963d25f55096fb59f9c5aaaad58cd3aea47f9ac46dbaea50baf05f278c526e157b600641b2f8c3acbb05f233e13e73aefd63b45f7f942ee3249edd
|
checksums.yaml.gz.sig
ADDED
Binary file
|
data.tar.gz.sig
ADDED
Binary file
|
data/lib/macrohub.rb
ADDED
@@ -0,0 +1,572 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
# file: macrohub.rb
|
4
|
+
|
5
|
+
require 'rowx'
|
6
|
+
require 'app-routes'
|
7
|
+
require 'rxfhelper'
|
8
|
+
require 'chronic_between'
|
9
|
+
|
10
|
+
# This file includes the following classes:
|
11
|
+
#
|
12
|
+
# * SayAction
|
13
|
+
# * WebhookAction
|
14
|
+
# * MotionTrigger
|
15
|
+
# * FrequencyConstraint
|
16
|
+
# * TimeConstraint
|
17
|
+
# * TriggersNlp - selects the trigger to use
|
18
|
+
# * ActionsNlp - selects the action to use
|
19
|
+
# * ConstraintsNlp - selects the constraint to use
|
20
|
+
# * Macro - contains the triggers, actions, and constraints
|
21
|
+
# * MacroHub - contains the macros
|
22
|
+
|
23
|
+
class SayAction
|
24
|
+
|
25
|
+
def initialize(s=nil, msg: s)
|
26
|
+
@s = msg
|
27
|
+
end
|
28
|
+
|
29
|
+
def invoke()
|
30
|
+
"say: %s" % @s
|
31
|
+
end
|
32
|
+
|
33
|
+
def to_node()
|
34
|
+
Rexle::Element.new(:action, attributes: {type: :say, text: @s})
|
35
|
+
end
|
36
|
+
|
37
|
+
def to_rowx()
|
38
|
+
"action: say '%s'" % @s
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
42
|
+
|
43
|
+
class WebhookAction
|
44
|
+
|
45
|
+
attr_accessor :url
|
46
|
+
|
47
|
+
def initialize(idx=nil, id: idx, url: '127.0.0.1', env: {debug: false})
|
48
|
+
|
49
|
+
@name, @url = id, url
|
50
|
+
|
51
|
+
end
|
52
|
+
|
53
|
+
def invoke()
|
54
|
+
"webhook: %s" % @url
|
55
|
+
end
|
56
|
+
|
57
|
+
def to_node()
|
58
|
+
Rexle::Element.new(:action, \
|
59
|
+
attributes: {type: :webhook, name: @name, url: @url})
|
60
|
+
end
|
61
|
+
|
62
|
+
def to_rowx()
|
63
|
+
s = "action: webhook %s" % @name
|
64
|
+
s += "\n url: %s" % @url
|
65
|
+
end
|
66
|
+
|
67
|
+
end
|
68
|
+
|
69
|
+
class MotionTrigger
|
70
|
+
|
71
|
+
attr_reader :location, :type
|
72
|
+
|
73
|
+
def initialize(location: nil)
|
74
|
+
|
75
|
+
@location = location
|
76
|
+
@type = :motion
|
77
|
+
|
78
|
+
end
|
79
|
+
|
80
|
+
def match?(detail)
|
81
|
+
|
82
|
+
puts 'inside MotionTrigger#match' if $debug
|
83
|
+
location = detail[:location]
|
84
|
+
|
85
|
+
if location then
|
86
|
+
|
87
|
+
@location.downcase == location.downcase
|
88
|
+
|
89
|
+
else
|
90
|
+
return false
|
91
|
+
end
|
92
|
+
|
93
|
+
end
|
94
|
+
|
95
|
+
def to_node()
|
96
|
+
Rexle::Element.new(:trigger, attributes: {type: :motion,
|
97
|
+
location: @location})
|
98
|
+
end
|
99
|
+
|
100
|
+
def to_rowx()
|
101
|
+
"trigger: Motion detected in the %s" % @location
|
102
|
+
end
|
103
|
+
|
104
|
+
end
|
105
|
+
|
106
|
+
class FrequencyConstraint
|
107
|
+
|
108
|
+
def initialize(freqx, freq: freqx)
|
109
|
+
|
110
|
+
@freq = freq
|
111
|
+
@counter = 0
|
112
|
+
@interval = 60
|
113
|
+
end
|
114
|
+
|
115
|
+
def counter()
|
116
|
+
@counter
|
117
|
+
end
|
118
|
+
|
119
|
+
def increment()
|
120
|
+
@counter += 1
|
121
|
+
end
|
122
|
+
|
123
|
+
def match?()
|
124
|
+
@counter < @freq
|
125
|
+
end
|
126
|
+
|
127
|
+
def reset()
|
128
|
+
puts 'resetting' if $debug
|
129
|
+
@counter = 0
|
130
|
+
end
|
131
|
+
|
132
|
+
def to_node()
|
133
|
+
Rexle::Element.new(:constraint, \
|
134
|
+
attributes: {type: :frequency, freq: @freq})
|
135
|
+
end
|
136
|
+
|
137
|
+
def to_rowx()
|
138
|
+
|
139
|
+
freq = case @freq
|
140
|
+
when 1
|
141
|
+
'Once'
|
142
|
+
when 2
|
143
|
+
'Twice'
|
144
|
+
else
|
145
|
+
"Maximum %s times" % @freq
|
146
|
+
end
|
147
|
+
|
148
|
+
"constraint: %s" % freq
|
149
|
+
|
150
|
+
end
|
151
|
+
|
152
|
+
end
|
153
|
+
|
154
|
+
class TimeConstraint
|
155
|
+
|
156
|
+
attr_accessor :time
|
157
|
+
|
158
|
+
def initialize(timex=nil, times: timex, time: timex)
|
159
|
+
|
160
|
+
@time = times || time
|
161
|
+
|
162
|
+
end
|
163
|
+
|
164
|
+
def match?(detail)
|
165
|
+
|
166
|
+
if $debug then
|
167
|
+
puts 'inside TimeConstraint#match?'
|
168
|
+
puts 'detail: ' + detail.inspect
|
169
|
+
puts '@time: ' + @time.inspect
|
170
|
+
end
|
171
|
+
|
172
|
+
ChronicBetween.new(@time).within?(detail[:time])
|
173
|
+
|
174
|
+
end
|
175
|
+
|
176
|
+
def to_node()
|
177
|
+
Rexle::Element.new(:constraint, \
|
178
|
+
attributes: {type: :time, time: @time})
|
179
|
+
end
|
180
|
+
|
181
|
+
def to_rowx()
|
182
|
+
"constraint: %s" % @time
|
183
|
+
end
|
184
|
+
|
185
|
+
end
|
186
|
+
|
187
|
+
|
188
|
+
|
189
|
+
|
190
|
+
class TriggersNlp
|
191
|
+
include AppRoutes
|
192
|
+
|
193
|
+
attr_reader :to_type
|
194
|
+
|
195
|
+
def initialize()
|
196
|
+
|
197
|
+
super()
|
198
|
+
|
199
|
+
params = {}
|
200
|
+
puts 'inside Trigger'
|
201
|
+
puts 'params: ' + params.inspect
|
202
|
+
triggers(params)
|
203
|
+
|
204
|
+
end
|
205
|
+
|
206
|
+
protected
|
207
|
+
|
208
|
+
def triggers(params)
|
209
|
+
|
210
|
+
puts 'inside triggers'
|
211
|
+
|
212
|
+
# e.g. Motion detected in the kitchen
|
213
|
+
#
|
214
|
+
get /motion detected in the (.*)/i do |location|
|
215
|
+
puts 'motion detection trigger' if $debug
|
216
|
+
[MotionTrigger, {location: location}]
|
217
|
+
end
|
218
|
+
|
219
|
+
end
|
220
|
+
|
221
|
+
private
|
222
|
+
|
223
|
+
alias find_trigger run_route
|
224
|
+
|
225
|
+
end
|
226
|
+
|
227
|
+
class ActionsNlp
|
228
|
+
include AppRoutes
|
229
|
+
|
230
|
+
def initialize()
|
231
|
+
|
232
|
+
super()
|
233
|
+
|
234
|
+
params = {}
|
235
|
+
actions(params)
|
236
|
+
|
237
|
+
end
|
238
|
+
|
239
|
+
protected
|
240
|
+
|
241
|
+
def actions(params)
|
242
|
+
|
243
|
+
puts 'inside actions'
|
244
|
+
# e.g. Say 'Good morning'
|
245
|
+
#
|
246
|
+
get /say ['"]([^'"]+)/i do |s|
|
247
|
+
puts 's: ' + s.inspect if $debug
|
248
|
+
[SayAction, {msg: s} ]
|
249
|
+
end
|
250
|
+
|
251
|
+
# e.g. webhook entered_kitchen
|
252
|
+
#
|
253
|
+
get /webhook (.*)/i do |name|
|
254
|
+
[WebhookAction, {id: name }]
|
255
|
+
end
|
256
|
+
|
257
|
+
get /.*/ do
|
258
|
+
puts 'action unknown' if $debug
|
259
|
+
[]
|
260
|
+
end
|
261
|
+
|
262
|
+
end
|
263
|
+
|
264
|
+
private
|
265
|
+
|
266
|
+
alias find_action run_route
|
267
|
+
|
268
|
+
end
|
269
|
+
|
270
|
+
class ConstraintsNlp
|
271
|
+
include AppRoutes
|
272
|
+
|
273
|
+
|
274
|
+
def initialize()
|
275
|
+
|
276
|
+
super()
|
277
|
+
|
278
|
+
params = {}
|
279
|
+
constraints(params)
|
280
|
+
|
281
|
+
end
|
282
|
+
|
283
|
+
protected
|
284
|
+
|
285
|
+
def constraints(params)
|
286
|
+
|
287
|
+
puts 'inside constraints' if $debug
|
288
|
+
# e.g. Between 8am and 10am
|
289
|
+
#
|
290
|
+
get /^between (.*)/i do |s|
|
291
|
+
[TimeConstraint, {time: s}]
|
292
|
+
end
|
293
|
+
|
294
|
+
get /^on a (.*)/i do |s|
|
295
|
+
[TimeConstraint, {time: s}]
|
296
|
+
end
|
297
|
+
|
298
|
+
get /^(after .*)/i do |s|
|
299
|
+
[TimeConstraint, {time: s}]
|
300
|
+
end
|
301
|
+
|
302
|
+
get /^(#{(Date::DAYNAMES + Date::ABBR_DAYNAMES).join('|')}$)/i do |s|
|
303
|
+
[TimeConstraint, {time: s}]
|
304
|
+
end
|
305
|
+
|
306
|
+
get /^once only|only once|once|one time|1 time$/i do |s|
|
307
|
+
[FrequencyConstraint, {freq: 1}]
|
308
|
+
end
|
309
|
+
|
310
|
+
get /^twice only|only twice|twice|two times|2 times$/i do |s|
|
311
|
+
[FrequencyConstraint, {freq: 2}]
|
312
|
+
end
|
313
|
+
|
314
|
+
get /^(Maximum|Max|Up to) ?three times|3 times$/i do |s|
|
315
|
+
[FrequencyConstraint, {freq: 3}]
|
316
|
+
end
|
317
|
+
|
318
|
+
get /^(Maximum|Max|Up to) ?four times|4 times$/i do |s|
|
319
|
+
[FrequencyConstraint, {freq: 4}]
|
320
|
+
end
|
321
|
+
|
322
|
+
end
|
323
|
+
|
324
|
+
private
|
325
|
+
|
326
|
+
alias find_constraint run_route
|
327
|
+
end
|
328
|
+
|
329
|
+
class Macro
|
330
|
+
|
331
|
+
attr_accessor :title, :env
|
332
|
+
attr_reader :triggers, :actions, :constraints
|
333
|
+
|
334
|
+
def initialize(node, title: '')
|
335
|
+
|
336
|
+
@title = title
|
337
|
+
|
338
|
+
@actions = []
|
339
|
+
@triggers = []
|
340
|
+
@constraints = []
|
341
|
+
|
342
|
+
end
|
343
|
+
|
344
|
+
def import_xml(node)
|
345
|
+
|
346
|
+
@title = node.text('macro')
|
347
|
+
|
348
|
+
if node.element('triggers') then
|
349
|
+
|
350
|
+
triggers = {motion: MotionTrigger, timer: TimerTrigger}
|
351
|
+
|
352
|
+
# level 2
|
353
|
+
@triggers = node.xpath('triggers/*').map do |e|
|
354
|
+
|
355
|
+
puts 'e.name: ' + e.name.inspect if $debug
|
356
|
+
triggers[e.name.to_sym].new(e.attributes.to_h)
|
357
|
+
|
358
|
+
end
|
359
|
+
|
360
|
+
actions = {say: SayAction, webhook: WebhookAction}
|
361
|
+
|
362
|
+
@actions = node.xpath('actions/*').map do |e|
|
363
|
+
|
364
|
+
actions[e.name.to_sym].new(e.attributes.to_h)
|
365
|
+
|
366
|
+
end
|
367
|
+
|
368
|
+
constraints = {time: TimeConstraint, frequency: FrequencyConstraint}
|
369
|
+
|
370
|
+
@constraints = node.xpath('constraints/*').map do |e|
|
371
|
+
|
372
|
+
puts 'before Constraints.new' if $debug
|
373
|
+
constraints[e.name.to_sym].new(e.attributes.to_h)
|
374
|
+
|
375
|
+
end
|
376
|
+
|
377
|
+
else
|
378
|
+
|
379
|
+
# Level 1
|
380
|
+
|
381
|
+
tp = TriggersNlp.new
|
382
|
+
|
383
|
+
@triggers = node.xpath('trigger').map do |e|
|
384
|
+
|
385
|
+
r = tp.find_trigger e.text
|
386
|
+
|
387
|
+
if r then
|
388
|
+
r[0].new(r[1])
|
389
|
+
end
|
390
|
+
|
391
|
+
end
|
392
|
+
|
393
|
+
ap = ActionsNlp.new
|
394
|
+
|
395
|
+
@actions = node.xpath('action').map do |e|
|
396
|
+
|
397
|
+
r = ap.find_action e.text
|
398
|
+
|
399
|
+
if r then
|
400
|
+
|
401
|
+
a = e.xpath('item/*')
|
402
|
+
|
403
|
+
h = if a.any? then
|
404
|
+
a.map {|node| [node.name.to_sym, node.text.to_s]}.to_h
|
405
|
+
else
|
406
|
+
{}
|
407
|
+
end
|
408
|
+
|
409
|
+
r[0].new(r[1].merge(h))
|
410
|
+
end
|
411
|
+
|
412
|
+
end
|
413
|
+
|
414
|
+
cn = ConstraintsNlp.new
|
415
|
+
|
416
|
+
@constraints = node.xpath('constraint').map do |e|
|
417
|
+
|
418
|
+
puts 'constraint e: ' + e.xml.inspect
|
419
|
+
r = cn.find_constraint e.text
|
420
|
+
|
421
|
+
puts 'r: ' + r.inspect if $debug
|
422
|
+
|
423
|
+
if r then
|
424
|
+
r[0].new(r[1])
|
425
|
+
end
|
426
|
+
|
427
|
+
end
|
428
|
+
|
429
|
+
end
|
430
|
+
end
|
431
|
+
|
432
|
+
def match?(triggerx, detail={} )
|
433
|
+
|
434
|
+
if @triggers.any? {|x| x.type == triggerx and x.match?(detail) } then
|
435
|
+
|
436
|
+
if $debug then
|
437
|
+
puts 'checking constraints ...'
|
438
|
+
puts '@constraints: ' + @constraints.inspect
|
439
|
+
end
|
440
|
+
|
441
|
+
if @constraints.all? {|x| x.match?($env.merge(detail)) } then
|
442
|
+
|
443
|
+
true
|
444
|
+
|
445
|
+
else
|
446
|
+
|
447
|
+
return false
|
448
|
+
|
449
|
+
end
|
450
|
+
|
451
|
+
end
|
452
|
+
|
453
|
+
end
|
454
|
+
|
455
|
+
def run()
|
456
|
+
@actions.map(&:invoke)
|
457
|
+
end
|
458
|
+
|
459
|
+
def to_node()
|
460
|
+
|
461
|
+
if $debug then
|
462
|
+
puts 'inside to_node'
|
463
|
+
puts '@title: ' + @title.inspect
|
464
|
+
end
|
465
|
+
|
466
|
+
e = Rexle::Element.new(:macro, attributes: {title: @title})
|
467
|
+
|
468
|
+
e.add node_collection(:triggers, @triggers)
|
469
|
+
e.add node_collection(:actions, @actions)
|
470
|
+
e.add node_collection(:constraints, @constraints)
|
471
|
+
|
472
|
+
return e
|
473
|
+
end
|
474
|
+
|
475
|
+
def to_rowx()
|
476
|
+
|
477
|
+
s = "macro: %s\n\n" % @title
|
478
|
+
s + [@triggers, @actions, @constraints]\
|
479
|
+
.map {|x| x.collect(&:to_rowx).join("\n")}.join("\n")
|
480
|
+
end
|
481
|
+
|
482
|
+
private
|
483
|
+
|
484
|
+
def node_collection(name, a)
|
485
|
+
|
486
|
+
if $debug then
|
487
|
+
puts 'inside node_collection name: ' + name.inspect
|
488
|
+
puts 'a: ' + a.inspect
|
489
|
+
end
|
490
|
+
|
491
|
+
e = Rexle::Element.new(name)
|
492
|
+
a.each do |x|
|
493
|
+
|
494
|
+
puts 'x: ' + x.inspect if $debug
|
495
|
+
e.add x.to_node
|
496
|
+
|
497
|
+
end
|
498
|
+
|
499
|
+
return e
|
500
|
+
|
501
|
+
end
|
502
|
+
|
503
|
+
end
|
504
|
+
|
505
|
+
class MacroHub
|
506
|
+
|
507
|
+
attr_reader :macros
|
508
|
+
|
509
|
+
def initialize(obj=nil)
|
510
|
+
|
511
|
+
if obj then
|
512
|
+
|
513
|
+
s, _ = RXFHelper.read(obj)
|
514
|
+
|
515
|
+
if s[0] == '<'
|
516
|
+
import_xml(s)
|
517
|
+
else
|
518
|
+
import_xml(RowX.new(s.gsub(/^#.*/,'')).to_xml)
|
519
|
+
end
|
520
|
+
|
521
|
+
else
|
522
|
+
|
523
|
+
@macros = []
|
524
|
+
|
525
|
+
end
|
526
|
+
end
|
527
|
+
|
528
|
+
def import_xml(raws)
|
529
|
+
|
530
|
+
s = RXFHelper.read(raws).first
|
531
|
+
puts 's: ' + s.inspect if $debug
|
532
|
+
doc = Rexle.new(s)
|
533
|
+
puts 'after doc' if $debug
|
534
|
+
|
535
|
+
@macros = doc.root.xpath('item').map do |node|
|
536
|
+
|
537
|
+
macro = Macro.new node.text('macro')
|
538
|
+
macro.import_xml(node)
|
539
|
+
macro
|
540
|
+
|
541
|
+
end
|
542
|
+
|
543
|
+
end
|
544
|
+
|
545
|
+
def to_doc()
|
546
|
+
|
547
|
+
doc = Rexle.new('<macros/>')
|
548
|
+
|
549
|
+
@macros.each do |macro|
|
550
|
+
puts 'macro: ' + macro.inspect if $debug
|
551
|
+
doc.root.add macro.to_node
|
552
|
+
end
|
553
|
+
|
554
|
+
return doc
|
555
|
+
|
556
|
+
end
|
557
|
+
|
558
|
+
def to_rowx()
|
559
|
+
|
560
|
+
s = ''
|
561
|
+
s += "title: %s\n%s\n\n" % [@title, '-' * 50] if @title
|
562
|
+
s += @macros.collect(&:to_rowx).join("\n\n#{'-'*50}\n\n")
|
563
|
+
|
564
|
+
end
|
565
|
+
|
566
|
+
alias to_s to_rowx
|
567
|
+
|
568
|
+
def to_xml()
|
569
|
+
to_doc.xml pretty: true
|
570
|
+
end
|
571
|
+
|
572
|
+
end
|
metadata
ADDED
@@ -0,0 +1,150 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: macrohub
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- James Robertson
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain:
|
11
|
+
- |
|
12
|
+
-----BEGIN CERTIFICATE-----
|
13
|
+
MIIEXjCCAsagAwIBAgIBATANBgkqhkiG9w0BAQsFADAsMSowKAYDVQQDDCFnZW1t
|
14
|
+
YXN0ZXIvREM9amFtZXNyb2JlcnRzb24vREM9ZXUwHhcNMjAwODE4MjAzMzEwWhcN
|
15
|
+
MjEwODE4MjAzMzEwWjAsMSowKAYDVQQDDCFnZW1tYXN0ZXIvREM9amFtZXNyb2Jl
|
16
|
+
cnRzb24vREM9ZXUwggGiMA0GCSqGSIb3DQEBAQUAA4IBjwAwggGKAoIBgQCpxMPq
|
17
|
+
78/MXnTnNIR4r4w8nLvnmiI4+FQdKXM3B+4DtwOD9IYSoJvA0HvEAWaVRpCTNVqf
|
18
|
+
PofbiC3gtiN7fxr8TnuAoY45nm30E7iqOFZvRuHK3Ss9Lrw706yA0532QT3dctzU
|
19
|
+
/QpwjYTOxa1CPH2K0mudFJmgzthY22z3BSi8mgOCEcfXGkh2xmTXzxg8yIp62I5T
|
20
|
+
PzdH43Jpfh7goclqaMZqpg7C06vXpFfQJcy+AMdiSPgrowuJ0WIMK3aQof7lfFPa
|
21
|
+
f+l1rP2eJE/DGPwFDYNqFp5hgYk2N7WiSlp7C7rL/d7KclvXy7lVwOj0uO3MUQPe
|
22
|
+
a5Tyx+9KnXwFFGbi1q3IdrECfGnNeo70jd0mMjbSfeFPlRiALXKI4Sy3Gp1oNhsI
|
23
|
+
sw3epDAap/6TMaqaJbCHHxqk232BX56brcq71v/tJ1QLtK4kss5zir2S7WtT8EfF
|
24
|
+
Csr2KXHBn4yVQlTTvmTQohB2kTkuCQkvYLEtNhH3eJAGPufH83jz2kaW70cCAwEA
|
25
|
+
AaOBijCBhzAJBgNVHRMEAjAAMAsGA1UdDwQEAwIEsDAdBgNVHQ4EFgQUVfxui/u3
|
26
|
+
RFnJbdwUpdKC7VGDNXgwJgYDVR0RBB8wHYEbZ2VtbWFzdGVyQGphbWVzcm9iZXJ0
|
27
|
+
c29uLmV1MCYGA1UdEgQfMB2BG2dlbW1hc3RlckBqYW1lc3JvYmVydHNvbi5ldTAN
|
28
|
+
BgkqhkiG9w0BAQsFAAOCAYEAphrYf3LYTmBQtbFJs5w4GgcBNQWSsLrUdeQKP+zy
|
29
|
+
KIVH03WEZm3A0Z5bFBYcLUu7ovp18w6fRIf3PaLk11vVrj2TzuwosT0VoNpeLKQk
|
30
|
+
HITIJkITbee6UFOmg7bvXiwQRNEbiaFhvNid+hIBg93TgbcaiVaXTssRb7diSU29
|
31
|
+
CNgBgY3DuwiS4YtcKim7T5OMqgw6IxvHR86MiE2S9DC36WTxb6bztJs6UCfboecM
|
32
|
+
1GPnqPHri4Gbe9Nx1n0Y/pnQeXUAeetKisk1ry/TeQAbIxSGm7i0xqGFNWw3n6I5
|
33
|
+
KDpDkFCLQnCiyY8s11MYeaU6IsEJtnS7gth+tswkSXo83vwlFzsd2qrGyvFTtdoD
|
34
|
+
v98JCJNEFoLYGkFaAE+AOUJLcRANqZGgsBF5pRg91h0/U92CIMqAP3lAJcudEm7v
|
35
|
+
3fO1S1r6TwHl52rWjJPDVGgq1Xy+wZgNc33Y5Mps7yaLvVH+GmCtOwmn2WNJ9sxH
|
36
|
+
K6cWFZZoUVgD1xtNkydgCYhq
|
37
|
+
-----END CERTIFICATE-----
|
38
|
+
date: 2020-08-18 00:00:00.000000000 Z
|
39
|
+
dependencies:
|
40
|
+
- !ruby/object:Gem::Dependency
|
41
|
+
name: rowx
|
42
|
+
requirement: !ruby/object:Gem::Requirement
|
43
|
+
requirements:
|
44
|
+
- - ">="
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
version: 0.7.0
|
47
|
+
- - "~>"
|
48
|
+
- !ruby/object:Gem::Version
|
49
|
+
version: '0.7'
|
50
|
+
type: :runtime
|
51
|
+
prerelease: false
|
52
|
+
version_requirements: !ruby/object:Gem::Requirement
|
53
|
+
requirements:
|
54
|
+
- - ">="
|
55
|
+
- !ruby/object:Gem::Version
|
56
|
+
version: 0.7.0
|
57
|
+
- - "~>"
|
58
|
+
- !ruby/object:Gem::Version
|
59
|
+
version: '0.7'
|
60
|
+
- !ruby/object:Gem::Dependency
|
61
|
+
name: app-routes
|
62
|
+
requirement: !ruby/object:Gem::Requirement
|
63
|
+
requirements:
|
64
|
+
- - "~>"
|
65
|
+
- !ruby/object:Gem::Version
|
66
|
+
version: '0.1'
|
67
|
+
- - ">="
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: 0.1.19
|
70
|
+
type: :runtime
|
71
|
+
prerelease: false
|
72
|
+
version_requirements: !ruby/object:Gem::Requirement
|
73
|
+
requirements:
|
74
|
+
- - "~>"
|
75
|
+
- !ruby/object:Gem::Version
|
76
|
+
version: '0.1'
|
77
|
+
- - ">="
|
78
|
+
- !ruby/object:Gem::Version
|
79
|
+
version: 0.1.19
|
80
|
+
- !ruby/object:Gem::Dependency
|
81
|
+
name: rxfhelper
|
82
|
+
requirement: !ruby/object:Gem::Requirement
|
83
|
+
requirements:
|
84
|
+
- - ">="
|
85
|
+
- !ruby/object:Gem::Version
|
86
|
+
version: 1.0.0
|
87
|
+
- - "~>"
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '1.0'
|
90
|
+
type: :runtime
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - ">="
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: 1.0.0
|
97
|
+
- - "~>"
|
98
|
+
- !ruby/object:Gem::Version
|
99
|
+
version: '1.0'
|
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.4.0
|
107
|
+
- - "~>"
|
108
|
+
- !ruby/object:Gem::Version
|
109
|
+
version: '0.4'
|
110
|
+
type: :runtime
|
111
|
+
prerelease: false
|
112
|
+
version_requirements: !ruby/object:Gem::Requirement
|
113
|
+
requirements:
|
114
|
+
- - ">="
|
115
|
+
- !ruby/object:Gem::Version
|
116
|
+
version: 0.4.0
|
117
|
+
- - "~>"
|
118
|
+
- !ruby/object:Gem::Version
|
119
|
+
version: '0.4'
|
120
|
+
description:
|
121
|
+
email: james@jamesrobertson.eu
|
122
|
+
executables: []
|
123
|
+
extensions: []
|
124
|
+
extra_rdoc_files: []
|
125
|
+
files:
|
126
|
+
- lib/macrohub.rb
|
127
|
+
homepage: https://github.com/jrobertson/macrohub
|
128
|
+
licenses:
|
129
|
+
- MIT
|
130
|
+
metadata: {}
|
131
|
+
post_install_message:
|
132
|
+
rdoc_options: []
|
133
|
+
require_paths:
|
134
|
+
- lib
|
135
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
136
|
+
requirements:
|
137
|
+
- - ">="
|
138
|
+
- !ruby/object:Gem::Version
|
139
|
+
version: '0'
|
140
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
141
|
+
requirements:
|
142
|
+
- - ">="
|
143
|
+
- !ruby/object:Gem::Version
|
144
|
+
version: '0'
|
145
|
+
requirements: []
|
146
|
+
rubygems_version: 3.0.3
|
147
|
+
signing_key:
|
148
|
+
specification_version: 4
|
149
|
+
summary: Experimental gem to create macros with the aim to simulate home automation.
|
150
|
+
test_files: []
|
metadata.gz.sig
ADDED
Binary file
|