openhab-jrubyscripting 5.0.0.rc10 → 5.0.0.rc12
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/openhab/core/actions/audio.rb +47 -0
- data/lib/openhab/core/actions/ephemeris.rb +39 -0
- data/lib/openhab/core/actions/exec.rb +51 -0
- data/lib/openhab/core/actions/http.rb +80 -0
- data/lib/openhab/core/actions/ping.rb +30 -0
- data/lib/openhab/core/actions/transformation.rb +32 -0
- data/lib/openhab/core/actions/voice.rb +36 -0
- data/lib/openhab/core/actions.rb +23 -120
- data/lib/openhab/core/{events → dto}/item_channel_link.rb +1 -4
- data/lib/openhab/core/{events → dto}/thing.rb +10 -12
- data/lib/openhab/core/dto.rb +11 -0
- data/lib/openhab/core/entity_lookup.rb +1 -1
- data/lib/openhab/core/events/abstract_event.rb +1 -0
- data/lib/openhab/core/events/abstract_item_registry_event.rb +36 -0
- data/lib/openhab/core/events/abstract_thing_registry_event.rb +40 -0
- data/lib/openhab/core/events/item_command_event.rb +1 -1
- data/lib/openhab/core/events/item_state_changed_event.rb +6 -6
- data/lib/openhab/core/events/item_state_event.rb +6 -6
- data/lib/openhab/core/events/thing_status_info_event.rb +8 -6
- data/lib/openhab/core/items/date_time_item.rb +3 -2
- data/lib/openhab/core/items/generic_item.rb +92 -1
- data/lib/openhab/core/items/item.rb +9 -8
- data/lib/openhab/core/items/metadata/hash.rb +1 -1
- data/lib/openhab/core/items/metadata/namespace_hash.rb +10 -2
- data/lib/openhab/core/items/metadata/provider.rb +2 -2
- data/lib/openhab/core/items/persistence.rb +99 -21
- data/lib/openhab/core/items/player_item.rb +1 -1
- data/lib/openhab/core/items/proxy.rb +20 -14
- data/lib/openhab/core/items/registry.rb +12 -1
- data/lib/openhab/core/items/state_storage.rb +2 -2
- data/lib/openhab/core/items.rb +3 -3
- data/lib/openhab/core/profile_factory.rb +3 -1
- data/lib/openhab/core/proxy.rb +130 -0
- data/lib/openhab/core/registry.rb +12 -2
- data/lib/openhab/core/rules.rb +1 -1
- data/lib/openhab/core/things/links/provider.rb +39 -1
- data/lib/openhab/core/things/proxy.rb +8 -0
- data/lib/openhab/core/things/registry.rb +4 -0
- data/lib/openhab/core/timer.rb +3 -19
- data/lib/openhab/core/types/date_time_type.rb +3 -2
- data/lib/openhab/core/types/decimal_type.rb +1 -1
- data/lib/openhab/core/types/un_def_type.rb +2 -2
- data/lib/openhab/core/value_cache.rb +1 -1
- data/lib/openhab/core.rb +3 -3
- data/lib/openhab/core_ext/ephemeris.rb +53 -0
- data/lib/openhab/core_ext/java/class.rb +1 -1
- data/lib/openhab/core_ext/java/duration.rb +27 -1
- data/lib/openhab/core_ext/java/local_date.rb +17 -7
- data/lib/openhab/core_ext/java/local_time.rb +13 -3
- data/lib/openhab/core_ext/java/month.rb +1 -1
- data/lib/openhab/core_ext/java/month_day.rb +15 -3
- data/lib/openhab/core_ext/java/period.rb +1 -1
- data/lib/openhab/core_ext/java/temporal_amount.rb +1 -1
- data/lib/openhab/core_ext/java/time.rb +5 -1
- data/lib/openhab/core_ext/java/zoned_date_time.rb +100 -2
- data/lib/openhab/core_ext/ruby/date.rb +4 -2
- data/lib/openhab/core_ext/ruby/date_time.rb +1 -0
- data/lib/openhab/core_ext/ruby/numeric.rb +6 -1
- data/lib/openhab/core_ext/ruby/time.rb +1 -0
- data/lib/openhab/dsl/debouncer.rb +259 -0
- data/lib/openhab/dsl/items/builder.rb +29 -14
- data/lib/openhab/dsl/items/timed_command.rb +31 -13
- data/lib/openhab/dsl/rules/automation_rule.rb +30 -44
- data/lib/openhab/dsl/rules/builder.rb +404 -39
- data/lib/openhab/dsl/rules/guard.rb +12 -54
- data/lib/openhab/dsl/rules/name_inference.rb +11 -0
- data/lib/openhab/dsl/rules/property.rb +3 -4
- data/lib/openhab/dsl/rules/terse.rb +4 -1
- data/lib/openhab/dsl/rules/triggers/conditions/duration.rb +5 -6
- data/lib/openhab/dsl/rules/triggers/cron/cron.rb +1 -0
- data/lib/openhab/dsl/rules/triggers/cron/cron_handler.rb +19 -31
- data/lib/openhab/dsl/rules/triggers/watch/watch.rb +1 -0
- data/lib/openhab/dsl/rules/triggers/watch/watch_handler.rb +22 -30
- data/lib/openhab/dsl/things/builder.rb +1 -1
- data/lib/openhab/dsl/thread_local.rb +1 -0
- data/lib/openhab/dsl/version.rb +1 -1
- data/lib/openhab/dsl.rb +251 -14
- data/lib/openhab/rspec/helpers.rb +3 -2
- data/lib/openhab/rspec/hooks.rb +6 -2
- data/lib/openhab/rspec/karaf.rb +7 -0
- data/lib/openhab/rspec/mocks/instance_method_stasher.rb +22 -0
- data/lib/openhab/rspec/mocks/space.rb +23 -0
- data/lib/openhab/rspec/mocks/timer.rb +33 -0
- data/lib/openhab/rspec/openhab/core/actions.rb +16 -4
- data/lib/openhab/rspec/openhab/core/items/proxy.rb +1 -13
- data/lib/openhab/rspec/suspend_rules.rb +1 -14
- data/lib/openhab/rspec.rb +9 -0
- data/lib/openhab/yard/base_helper.rb +19 -0
- data/lib/openhab/yard/code_objects/group_object.rb +9 -3
- data/lib/openhab/yard/coderay.rb +17 -0
- data/lib/openhab/yard/handlers/jruby/base.rb +10 -1
- data/lib/openhab/yard/handlers/jruby/java_import_handler.rb +3 -0
- data/lib/openhab/yard/html_helper.rb +49 -15
- data/lib/openhab/yard/markdown_helper.rb +135 -0
- data/lib/openhab/yard.rb +6 -0
- metadata +36 -4
data/lib/openhab/dsl.rb
CHANGED
@@ -37,6 +37,13 @@ module OpenHAB
|
|
37
37
|
public_class_method(*mod.private_instance_methods)
|
38
38
|
end
|
39
39
|
|
40
|
+
class << self
|
41
|
+
# @!visibility private
|
42
|
+
attr_reader :debouncers
|
43
|
+
end
|
44
|
+
|
45
|
+
@debouncers = java.util.concurrent.ConcurrentHashMap.new
|
46
|
+
|
40
47
|
module_function
|
41
48
|
|
42
49
|
# @!group Rule Creation
|
@@ -53,6 +60,8 @@ module OpenHAB
|
|
53
60
|
|
54
61
|
# @!group Rule Support
|
55
62
|
|
63
|
+
# rubocop:disable Layout/LineLength
|
64
|
+
|
56
65
|
#
|
57
66
|
# Defines a new profile that can be applied to item channel links.
|
58
67
|
#
|
@@ -79,8 +88,8 @@ module OpenHAB
|
|
79
88
|
# @see org.openhab.thing.Profile
|
80
89
|
# @see org.openhab.thing.StateProfile
|
81
90
|
#
|
82
|
-
# @example
|
83
|
-
# profile(:veto_closing_shades) do |event, item:, command
|
91
|
+
# @example Vetoing a command
|
92
|
+
# profile(:veto_closing_shades) do |event, item:, command:|
|
84
93
|
# next false if command&.down?
|
85
94
|
#
|
86
95
|
# true
|
@@ -94,9 +103,37 @@ module OpenHAB
|
|
94
103
|
# # can also be referenced from an `.items` file:
|
95
104
|
# # Rollershutter MyShade { channel="thing:rollershutter"[profile="ruby:veto_closing_shades"] }
|
96
105
|
#
|
106
|
+
# @example Overriding units from a binding
|
107
|
+
# profile(:set_uom) do |event, configuration:, state:, command:|
|
108
|
+
# unless configuration["unit"]
|
109
|
+
# logger.warn("Unit configuration not provided for set_uom profile")
|
110
|
+
# next true
|
111
|
+
# end
|
112
|
+
#
|
113
|
+
# case event
|
114
|
+
# when :state_from_handler
|
115
|
+
# next true unless state.is_a?(DecimalType) || state.is_a?(QuantityType) # what is it then?!
|
116
|
+
#
|
117
|
+
# state = state.to_d if state.is_a?(QuantityType) # ignore the units if QuantityType was given
|
118
|
+
# callback.send_update(state | configuration["unit"])
|
119
|
+
# false
|
120
|
+
# when :command_from_item
|
121
|
+
# # strip the unit from the command, as the binding likely can't handle it
|
122
|
+
# next true unless command.is_a?(QuantityType)
|
123
|
+
#
|
124
|
+
# callback.send_command(DecimalType.new(command.to_d))
|
125
|
+
# false
|
126
|
+
# else
|
127
|
+
# true # pass other events through as normal
|
128
|
+
# end
|
129
|
+
# end
|
130
|
+
# # can also be referenced from an `.items` file:
|
131
|
+
# # Number:Temperature MyTempWithNonUnitValueFromBinding "I prefer Celsius [%d °C]" { channel="something_that_returns_F"[profile="ruby:set_uom", unit="°F"] }
|
132
|
+
#
|
97
133
|
def profile(id, &block)
|
98
134
|
raise ArgumentError, "Block is required" unless block
|
99
135
|
|
136
|
+
id = id.to_s
|
100
137
|
uid = org.openhab.core.thing.profiles.ProfileTypeUID.new("ruby", id)
|
101
138
|
|
102
139
|
ThreadLocal.thread_local(openhab_rule_type: "profile", openhab_rule_uid: id) do
|
@@ -104,6 +141,8 @@ module OpenHAB
|
|
104
141
|
end
|
105
142
|
end
|
106
143
|
|
144
|
+
# rubocop:enable Layout/LineLength
|
145
|
+
|
107
146
|
# @!group Object Access
|
108
147
|
|
109
148
|
#
|
@@ -113,8 +152,6 @@ module OpenHAB
|
|
113
152
|
#
|
114
153
|
# @see Core::ValueCache ValueCache
|
115
154
|
#
|
116
|
-
# @since openHAB 3.4.0
|
117
|
-
#
|
118
155
|
def shared_cache
|
119
156
|
$sharedCache
|
120
157
|
end
|
@@ -335,6 +372,133 @@ module OpenHAB
|
|
335
372
|
Range.new(start, finish, range.exclude_end?)
|
336
373
|
end
|
337
374
|
|
375
|
+
#
|
376
|
+
# Limits the frequency of calls to the given block within a given amount of time.
|
377
|
+
#
|
378
|
+
# It can be useful to throttle certain actions even when a rule is triggered too often.
|
379
|
+
# This can be used to debounce triggers in a UI rule.
|
380
|
+
#
|
381
|
+
# @param (see Debouncer#initialize)
|
382
|
+
# @param [Object] id ID to associate with this debouncer.
|
383
|
+
# @param [Block] block The block to be debounced.
|
384
|
+
#
|
385
|
+
# @return [void]
|
386
|
+
#
|
387
|
+
# @example Run at most once per second even when being called more frequently
|
388
|
+
# (1..100).each do
|
389
|
+
# debounce for: 1.second do
|
390
|
+
# logger.info "This will not be logged more frequently than every 1 second"
|
391
|
+
# end
|
392
|
+
# sleep 0.1
|
393
|
+
# end
|
394
|
+
#
|
395
|
+
# @see Debouncer Debouncer class
|
396
|
+
# @see Rules::BuilderDSL#debounce debounce rule guard
|
397
|
+
#
|
398
|
+
# @!visibility private
|
399
|
+
def debounce(for:, leading: false, idle_time: nil, id: nil, &block)
|
400
|
+
interval = binding.local_variable_get(:for)
|
401
|
+
id ||= block.source_location
|
402
|
+
DSL.debouncers.compute(id) do |_key, debouncer|
|
403
|
+
debouncer ||= Debouncer.new(for: interval, leading: leading, idle_time: idle_time)
|
404
|
+
debouncer.call(&block)
|
405
|
+
debouncer
|
406
|
+
end
|
407
|
+
end
|
408
|
+
|
409
|
+
#
|
410
|
+
# Waits until calls to this method have stopped firing for a period of time
|
411
|
+
# before executing the block.
|
412
|
+
#
|
413
|
+
# This method acts as a guard for the given block to ensure that it doesn't get executed
|
414
|
+
# too frequently. The debounce_for method can be called as frequently as possible.
|
415
|
+
# The given block, however, will only be executed once the `debounce_time` has passed
|
416
|
+
# since the last call to debounce_for.
|
417
|
+
#
|
418
|
+
# This method can be used from within a UI rule as well as from a file-based rule.
|
419
|
+
#
|
420
|
+
# @param (see Rules::BuilderDSL#debounce_for)
|
421
|
+
# @param [Object] id ID to associate with this call.
|
422
|
+
# @param [Block] block The block to be debounced.
|
423
|
+
#
|
424
|
+
# @return [void]
|
425
|
+
#
|
426
|
+
# @example Run a block of code only after an item has stopped changing
|
427
|
+
# # This can be placed inside a UI rule with an Item Change trigger for
|
428
|
+
# # a Door contact sensor.
|
429
|
+
# # If the door state has stopped changing state for 10 minutes,
|
430
|
+
# # execute the block.
|
431
|
+
# debounce_for(10.minutes) do
|
432
|
+
# if DoorState.open?
|
433
|
+
# Voice.say("The door has been left open!")
|
434
|
+
# end
|
435
|
+
# end
|
436
|
+
#
|
437
|
+
# @see Rules::BuilderDSL#debounce_for Rule builder's debounce_for for a detailed description
|
438
|
+
#
|
439
|
+
def debounce_for(debounce_time, id: nil, &block)
|
440
|
+
idle_time = debounce_time.is_a?(Range) ? debounce_time.begin : debounce_time
|
441
|
+
debounce(for: debounce_time, idle_time: idle_time, id: id, &block)
|
442
|
+
end
|
443
|
+
|
444
|
+
#
|
445
|
+
# Rate-limits block executions by delaying calls and only executing the last
|
446
|
+
# call within the given duration.
|
447
|
+
#
|
448
|
+
# When throttle_for is called, it will hold from executing the block and start
|
449
|
+
# a fixed timer for the given duration. Should more calls occur during
|
450
|
+
# this time, keep holding and once the wait time is over, execute the block.
|
451
|
+
#
|
452
|
+
# {throttle_for} will execute the block after it had waited for the given duration,
|
453
|
+
# regardless of how frequently `throttle_for` was called.
|
454
|
+
# In contrast, {debounce_for} will wait until there is a minimum interval
|
455
|
+
# between two triggers.
|
456
|
+
#
|
457
|
+
# {throttle_for} is ideal in situations where regular status updates need to be made
|
458
|
+
# for frequently changing values. It is also useful when a rule responds to triggers
|
459
|
+
# from multiple related items that are updated at around the same time. Instead of
|
460
|
+
# executing the rule multiple times, {throttle_for} will wait for a pre-set amount
|
461
|
+
# of time since the first group of triggers occurred before executing the rule.
|
462
|
+
#
|
463
|
+
# @param (see Rules::BuilderDSL#throttle_for)
|
464
|
+
# @param [Object] id ID to associate with this call.
|
465
|
+
# @param [Block] block The block to be throttled.
|
466
|
+
#
|
467
|
+
# @return [void]
|
468
|
+
#
|
469
|
+
# @see Rules::BuilderDSL#debounce_for Rule builder's debounce_for for a detailed description
|
470
|
+
# @see Rules::BuilderDSL#throttle_for
|
471
|
+
#
|
472
|
+
def throttle_for(duration, id: nil, &block)
|
473
|
+
debounce(for: duration, id: id, &block)
|
474
|
+
end
|
475
|
+
|
476
|
+
#
|
477
|
+
# Limit how often the given block executes to the specified interval.
|
478
|
+
#
|
479
|
+
# {only_every} will execute the given block but prevents further executions
|
480
|
+
# until the given interval has passed. In contrast, {throttle_for} will not
|
481
|
+
# execute the block immediately, and will wait until the end of the interval.
|
482
|
+
#
|
483
|
+
# @param (see Rules::BuilderDSL#only_every)
|
484
|
+
# @param [Object] id ID to associate with this call.
|
485
|
+
# @param [Block] block The block to be throttled.
|
486
|
+
#
|
487
|
+
# @return [void]
|
488
|
+
#
|
489
|
+
# @example Prevent door bell from ringing repeatedly
|
490
|
+
# # This can be called from a UI rule.
|
491
|
+
# # For file based rule, use the `only_every` rule guard
|
492
|
+
# only_every(30.seconds) do { Audio.play_sound("doorbell.mp3") }
|
493
|
+
#
|
494
|
+
# @see Rules::BuilderDSL#debounce_for Rule builder's debounce_for for a detailed description
|
495
|
+
# @see Rules::BuilderDSL#only_every
|
496
|
+
#
|
497
|
+
def only_every(interval, id: nil, &block)
|
498
|
+
interval = 1.send(interval) if %i[second minute hour day].include?(interval)
|
499
|
+
debounce(for: interval, leading: true, id: id, &block)
|
500
|
+
end
|
501
|
+
|
338
502
|
#
|
339
503
|
# Store states of supplied items
|
340
504
|
#
|
@@ -357,9 +521,6 @@ module OpenHAB
|
|
357
521
|
# end # the states will be restored here
|
358
522
|
#
|
359
523
|
def store_states(*items)
|
360
|
-
items = items.flatten.map do |item|
|
361
|
-
item.respond_to?(:__getobj__) ? item.__getobj__ : item
|
362
|
-
end
|
363
524
|
states = Core::Items::StateStorage.from_items(*items)
|
364
525
|
if block_given?
|
365
526
|
yield
|
@@ -438,18 +599,39 @@ module OpenHAB
|
|
438
599
|
# Item1.average_since(12.hours)
|
439
600
|
# end
|
440
601
|
#
|
441
|
-
# @
|
442
|
-
#
|
443
|
-
# @param [Object] service service either as a String or a Symbol
|
602
|
+
# @param [Object] service Persistence service either as a String or a Symbol
|
444
603
|
# @yield [] Block executed in context of the supplied persistence service
|
445
604
|
# @return [Object] The return value from the block.
|
446
605
|
#
|
606
|
+
# @see persistence!
|
607
|
+
# @see OpenHAB::Core::Items::Persistence
|
608
|
+
#
|
447
609
|
def persistence(service)
|
448
|
-
old =
|
449
|
-
Thread.current[:openhab_persistence_service] = service
|
610
|
+
old = persistence!(service)
|
450
611
|
yield
|
451
612
|
ensure
|
452
|
-
|
613
|
+
persistence!(old)
|
614
|
+
end
|
615
|
+
|
616
|
+
#
|
617
|
+
# Permanently sets the default persistence service for the current thread
|
618
|
+
#
|
619
|
+
# @note This method is only intended for use at the top level of rule
|
620
|
+
# scripts. If it's used within library methods, or hap-hazardly within
|
621
|
+
# rules, things can get very confusing because the prior state won't be
|
622
|
+
# properly restored.
|
623
|
+
#
|
624
|
+
# @param [Object] service Persistence service either as a String or a Symbol. When nil, use
|
625
|
+
# the system's default persistence service.
|
626
|
+
# @return [Object,nil] The previous persistence service settings, or nil when using the system's default.
|
627
|
+
#
|
628
|
+
# @see persistence
|
629
|
+
# @see OpenHAB::Core::Items::Persistence
|
630
|
+
#
|
631
|
+
def persistence!(service = nil)
|
632
|
+
old = Thread.current[:openhab_persistence_service]
|
633
|
+
Thread.current[:openhab_persistence_service] = service
|
634
|
+
old
|
453
635
|
end
|
454
636
|
|
455
637
|
#
|
@@ -624,7 +806,7 @@ module OpenHAB
|
|
624
806
|
#
|
625
807
|
# @overload provider!(things: nil, items: nil, metadata: nil, links: nil, **metadata_items)
|
626
808
|
#
|
627
|
-
# @param [Core::Provider, org.openhab.core.registry.
|
809
|
+
# @param [Core::Provider, org.openhab.core.common.registry.ManagedProvider, :persistent, :transient, Proc] providers
|
628
810
|
# An explicit provider to use. If it's a {Core::Provider}, the type will be inferred automatically.
|
629
811
|
# Otherwise it's applied to all types.
|
630
812
|
# @param [Hash] providers_by_type
|
@@ -703,6 +885,61 @@ module OpenHAB
|
|
703
885
|
old_providers
|
704
886
|
end
|
705
887
|
|
888
|
+
#
|
889
|
+
# @see CoreExt::Ephemeris
|
890
|
+
#
|
891
|
+
# @overload holiday_file(file)
|
892
|
+
#
|
893
|
+
# Sets a thread local variable to use a specific holiday file
|
894
|
+
# for {CoreExt::Ephemeris ephemeris calls} inside the block.
|
895
|
+
#
|
896
|
+
# @param [String, nil] file Path to a file defining holidays;
|
897
|
+
# `nil` to reset to default.
|
898
|
+
# @yield [] Block executed in context of the supplied holiday file
|
899
|
+
# @return [Object] The return value from the block.
|
900
|
+
#
|
901
|
+
# @example Set a specific holiday configuration file temporarily
|
902
|
+
# holiday_file("/home/cody/holidays.xml") do
|
903
|
+
# Time.now.next_holiday
|
904
|
+
# end
|
905
|
+
#
|
906
|
+
# @see holiday_file!
|
907
|
+
#
|
908
|
+
# @overload holiday_file
|
909
|
+
#
|
910
|
+
# Returns the current thread local value for the holiday file.
|
911
|
+
#
|
912
|
+
# @return [String, nil] the current holiday file
|
913
|
+
#
|
914
|
+
def holiday_file(*args)
|
915
|
+
raise ArgumentError, "wrong number of arguments (given #{args.length}, expected 0..1)" if args.length > 1
|
916
|
+
|
917
|
+
old = Thread.current[:openhab_holiday_file]
|
918
|
+
return old if args.empty?
|
919
|
+
|
920
|
+
holiday_file!(args.first)
|
921
|
+
yield
|
922
|
+
ensure
|
923
|
+
holiday_file!(old)
|
924
|
+
end
|
925
|
+
|
926
|
+
#
|
927
|
+
# Sets a thread local variable to set the default holiday file.
|
928
|
+
#
|
929
|
+
# @see https://github.com/svendiedrichsen/jollyday/tree/master/src/main/resources/holidays Example data files from the source
|
930
|
+
#
|
931
|
+
# @example
|
932
|
+
# holiday_file!("/home/cody/holidays.xml")
|
933
|
+
# Time.now.next_holiday
|
934
|
+
#
|
935
|
+
# @param [String, nil] file Path to a file defining holidays;
|
936
|
+
# `nil` to reset to default.
|
937
|
+
# @return [Symbol, nil] the new holiday file
|
938
|
+
#
|
939
|
+
def holiday_file!(file = nil)
|
940
|
+
Thread.current[:openhab_holiday_file] = file
|
941
|
+
end
|
942
|
+
|
706
943
|
# @!visibility private
|
707
944
|
def try_parse_time_like(string)
|
708
945
|
return string unless string.is_a?(String)
|
@@ -265,12 +265,13 @@ module OpenHAB
|
|
265
265
|
# @return [void]
|
266
266
|
#
|
267
267
|
def load_rules
|
268
|
-
|
268
|
+
automation_paths = Array(::RSpec.configuration.openhab_automation_search_paths)
|
269
|
+
|
269
270
|
lib_dirs = rubylib_dirs.map { |d| File.join(d, "") }
|
270
271
|
lib_dirs << File.join(gem_home, "")
|
271
272
|
|
272
273
|
SuspendRules.suspend_rules do
|
273
|
-
files = Dir["#{
|
274
|
+
files = automation_paths.map { |p| Dir["#{p}/**/*.rb"] }.flatten
|
274
275
|
files.reject! do |f|
|
275
276
|
lib_dirs.any? { |l| f.start_with?(l) }
|
276
277
|
end
|
data/lib/openhab/rspec/hooks.rb
CHANGED
@@ -32,6 +32,11 @@ module OpenHAB
|
|
32
32
|
config.include ExampleGroup
|
33
33
|
|
34
34
|
config.before(:suite) do
|
35
|
+
if config.mock_framework.framework_name == :rspec
|
36
|
+
require_relative "mocks/instance_method_stasher"
|
37
|
+
require_relative "mocks/space"
|
38
|
+
end
|
39
|
+
|
35
40
|
Helpers.autorequires unless Configuration.private_confdir
|
36
41
|
Helpers.send(:set_up_autoupdates)
|
37
42
|
Helpers.load_transforms
|
@@ -86,8 +91,6 @@ module OpenHAB
|
|
86
91
|
end
|
87
92
|
|
88
93
|
config.after do
|
89
|
-
Core::Items::Proxy.reset_cache
|
90
|
-
Core::Things::Proxy.reset_cache
|
91
94
|
@profile_factory.unregister
|
92
95
|
timers.cancel_all
|
93
96
|
# timers and rules have already been canceled, so we can safely just
|
@@ -97,6 +100,7 @@ module OpenHAB
|
|
97
100
|
restore_autoupdate_items
|
98
101
|
Mocks::PersistenceService.instance.reset
|
99
102
|
Hooks.cache_script_extension.sharedCache.clear if DSL.shared_cache
|
103
|
+
DSL.persistence!(nil)
|
100
104
|
end
|
101
105
|
end
|
102
106
|
end
|
data/lib/openhab/rspec/karaf.rb
CHANGED
@@ -280,6 +280,13 @@ module OpenHAB
|
|
280
280
|
props = cfg.properties || java.util.Hashtable.new
|
281
281
|
props.put("default", "default")
|
282
282
|
cfg.update(props)
|
283
|
+
|
284
|
+
# configure ephemeris with basic values
|
285
|
+
cfg = ca.get_configuration("org.openhab.ephemeris", nil)
|
286
|
+
props = cfg.properties || java.util.Hashtable.new
|
287
|
+
props.put("country", "us")
|
288
|
+
props.put("dayset-weekend", %w[SATURDAY SUNDAY])
|
289
|
+
cfg.update(props)
|
283
290
|
end
|
284
291
|
wait_for_service("org.openhab.core.automation.RuleManager") do |re|
|
285
292
|
require_relative "mocks/synchronous_executor"
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module OpenHAB
|
4
|
+
module RSpec
|
5
|
+
module Mocks
|
6
|
+
module InstanceMethodStasher
|
7
|
+
::RSpec::Mocks::InstanceMethodStasher.prepend(self)
|
8
|
+
|
9
|
+
# Disable "singleton on non-persistent Java type"
|
10
|
+
# it doesn't matter during specs
|
11
|
+
def initialize(*)
|
12
|
+
original_verbose = $VERBOSE
|
13
|
+
$VERBOSE = nil
|
14
|
+
|
15
|
+
super
|
16
|
+
ensure
|
17
|
+
$VERBOSE = original_verbose
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module OpenHAB
|
4
|
+
module RSpec
|
5
|
+
module Mocks
|
6
|
+
module Space
|
7
|
+
::RSpec::Mocks::Space.prepend(self)
|
8
|
+
|
9
|
+
#
|
10
|
+
# When setting expectations on {Items::Proxy proxies}, set them against the item
|
11
|
+
# themselves, so that it will catch calls against `self` from within the instance.
|
12
|
+
#
|
13
|
+
def proxy_for(object)
|
14
|
+
return super unless ::RSpec.current_example&.example_group&.consistent_proxies?
|
15
|
+
|
16
|
+
object = object.__getobj__ if object.is_a?(Core::Items::Proxy)
|
17
|
+
|
18
|
+
super
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -17,6 +17,39 @@ module OpenHAB
|
|
17
17
|
end
|
18
18
|
ZonedDateTime.singleton_class.prepend(MockedZonedDateTime)
|
19
19
|
|
20
|
+
# @!visibility private
|
21
|
+
module MockedLocalDate
|
22
|
+
def now
|
23
|
+
mocked_time_stack_item = Timecop.top_stack_item
|
24
|
+
return super unless mocked_time_stack_item
|
25
|
+
|
26
|
+
mocked_time_stack_item.time.to_zoned_date_time.to_local_date
|
27
|
+
end
|
28
|
+
end
|
29
|
+
LocalDate.singleton_class.prepend(MockedLocalDate)
|
30
|
+
|
31
|
+
# @!visibility private
|
32
|
+
module MockedLocalTime
|
33
|
+
def now
|
34
|
+
mocked_time_stack_item = Timecop.top_stack_item
|
35
|
+
return super unless mocked_time_stack_item
|
36
|
+
|
37
|
+
mocked_time_stack_item.time.to_zoned_date_time.to_local_time
|
38
|
+
end
|
39
|
+
end
|
40
|
+
LocalTime.singleton_class.prepend(MockedLocalTime)
|
41
|
+
|
42
|
+
# @!visibility private
|
43
|
+
module MockedMonthDay
|
44
|
+
def now
|
45
|
+
mocked_time_stack_item = Timecop.top_stack_item
|
46
|
+
return super unless mocked_time_stack_item
|
47
|
+
|
48
|
+
mocked_time_stack_item.time.to_zoned_date_time.to_month_day
|
49
|
+
end
|
50
|
+
end
|
51
|
+
MonthDay.singleton_class.prepend(MockedMonthDay)
|
52
|
+
|
20
53
|
# extend Timecop to support Java time classes
|
21
54
|
# @!visibility private
|
22
55
|
module TimeCopStackItem
|
@@ -12,12 +12,24 @@ module OpenHAB
|
|
12
12
|
logger.debug("notify: #{msg}")
|
13
13
|
end
|
14
14
|
|
15
|
-
|
16
|
-
|
15
|
+
class Voice
|
16
|
+
class << self
|
17
|
+
def say(text, voice: nil, sink: nil, volume: nil)
|
18
|
+
logger.debug("say: #{text}")
|
19
|
+
end
|
20
|
+
end
|
17
21
|
end
|
18
22
|
|
19
|
-
|
20
|
-
|
23
|
+
class Audio
|
24
|
+
class << self
|
25
|
+
def play_sound(filename, sink: nil, volume: nil)
|
26
|
+
logger.debug("play_sound: #{filename}")
|
27
|
+
end
|
28
|
+
|
29
|
+
def play_stream(url, sink: nil)
|
30
|
+
logger.debug("play_stream: #{url}")
|
31
|
+
end
|
32
|
+
end
|
21
33
|
end
|
22
34
|
|
23
35
|
# rubocop:enable Lint/UnusedMethodArgument
|
@@ -4,21 +4,9 @@ module OpenHAB
|
|
4
4
|
module Core
|
5
5
|
module Items
|
6
6
|
class Proxy
|
7
|
-
@proxies = {}
|
8
|
-
|
9
7
|
class << self
|
10
|
-
# ensure each item only has a single proxy, so that
|
11
|
-
# expect(item).to receive(:method) works
|
12
|
-
def new(item)
|
13
|
-
return super unless defined?(::RSpec) && ::RSpec.current_example&.example_group&.consistent_proxies?
|
14
|
-
|
15
|
-
@proxies.fetch(item.name) do
|
16
|
-
@proxies[item.name] = super
|
17
|
-
end
|
18
|
-
end
|
19
|
-
|
20
8
|
def reset_cache
|
21
|
-
@proxies
|
9
|
+
@proxies.clear
|
22
10
|
end
|
23
11
|
end
|
24
12
|
end
|
@@ -12,20 +12,7 @@ module OpenHAB
|
|
12
12
|
logger.trace("Skipping execution of #{uid} because rules are suspended.")
|
13
13
|
return
|
14
14
|
end
|
15
|
-
|
16
|
-
# super
|
17
|
-
::OpenHAB::DSL::ThreadLocal.thread_local(**@thread_locals) do
|
18
|
-
logger.trace { "Execute called with mod (#{mod&.to_string}) and inputs (#{inputs.inspect})" }
|
19
|
-
logger.trace { "Event details #{inputs["event"].inspect}" } if inputs&.key?("event")
|
20
|
-
trigger_conditions(inputs).process(mod: mod, inputs: inputs) do
|
21
|
-
event = extract_event(inputs)
|
22
|
-
process_queue(create_queue(event), mod, event)
|
23
|
-
end
|
24
|
-
rescue Exception => e
|
25
|
-
raise if defined?(::RSpec) && ::RSpec.current_example.example_group.propagate_exceptions?
|
26
|
-
|
27
|
-
@run_context.send(:logger).log_exception(e)
|
28
|
-
end
|
15
|
+
execute!(mod, inputs)
|
29
16
|
end
|
30
17
|
end
|
31
18
|
# private_constant :AutomationRule
|
data/lib/openhab/rspec.rb
CHANGED
@@ -15,3 +15,12 @@ require_relative "rspec/configuration"
|
|
15
15
|
require_relative "rspec/helpers"
|
16
16
|
require_relative "rspec/karaf"
|
17
17
|
require_relative "rspec/hooks"
|
18
|
+
|
19
|
+
return unless defined?(RSpec)
|
20
|
+
|
21
|
+
RSpec.configure do |c|
|
22
|
+
c.add_setting :openhab_automation_search_paths, default: [
|
23
|
+
"#{org.openhab.core.OpenHAB.config_folder}/automation/ruby",
|
24
|
+
"#{org.openhab.core.OpenHAB.config_folder}/automation/jsr223"
|
25
|
+
]
|
26
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module OpenHAB
|
4
|
+
module YARD
|
5
|
+
# @!visibility private
|
6
|
+
module BaseHelper
|
7
|
+
def link_object(obj, title = nil, *)
|
8
|
+
::YARD::Handlers::JRuby::Base.infer_java_class(obj) if obj.is_a?(String)
|
9
|
+
obj = ::YARD::Registry.resolve(object, obj, true, true) if obj.is_a?(String)
|
10
|
+
if obj.is_a?(::YARD::CodeObjects::Java::Base) && (see = obj.docstring.tag(:see))
|
11
|
+
# link to the first see tag
|
12
|
+
return linkify(see.name, title&.to_s || see.text)
|
13
|
+
end
|
14
|
+
|
15
|
+
super
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -4,13 +4,19 @@ module OpenHAB
|
|
4
4
|
module YARD
|
5
5
|
module CodeObjects
|
6
6
|
class GroupObject < ::YARD::CodeObjects::Base
|
7
|
+
attr_reader :full_name
|
8
|
+
|
9
|
+
def initialize(namespace, name)
|
10
|
+
@full_name = name
|
11
|
+
name = name.delete(%(.?"')).tr(" ", "-")
|
12
|
+
super
|
13
|
+
end
|
14
|
+
|
7
15
|
def path
|
8
16
|
"group#{sep}#{super}"
|
9
17
|
end
|
10
18
|
|
11
|
-
|
12
|
-
name
|
13
|
-
end
|
19
|
+
alias_method :to_s, :full_name
|
14
20
|
end
|
15
21
|
end
|
16
22
|
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "coderay"
|
4
|
+
|
5
|
+
module OpenHAB
|
6
|
+
module YARD
|
7
|
+
module CodeRay
|
8
|
+
module HtmlHelper
|
9
|
+
::CodeRay::Scanners.list.each do |scanner|
|
10
|
+
define_method("html_syntax_highlight_#{scanner}") do |source|
|
11
|
+
::CodeRay.scan(source, scanner).html
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -7,9 +7,18 @@ module YARD
|
|
7
7
|
class << self
|
8
8
|
def infer_java_class(klass, inferred_type = nil, comments = nil, statement = nil)
|
9
9
|
components = klass.split(".")
|
10
|
+
components.pop if components.last == "freeze"
|
11
|
+
|
10
12
|
class_first_char = components.last[0]
|
11
13
|
is_field = components.last == components.last.upcase
|
12
|
-
|
14
|
+
container_first_char = components[-2]&.[](0)
|
15
|
+
is_method = container_first_char &&
|
16
|
+
class_first_char != class_first_char.upcase &&
|
17
|
+
container_first_char == container_first_char.upcase
|
18
|
+
is_package = !is_method && !is_field && class_first_char != class_first_char.upcase
|
19
|
+
|
20
|
+
# methods aren't supported right now
|
21
|
+
return if is_method
|
13
22
|
|
14
23
|
javadocs = YARD::Config.options.dig(:jruby, "javadocs") || {}
|
15
24
|
|
@@ -14,6 +14,9 @@ module YARD
|
|
14
14
|
obj = infer_java_class(klass)
|
15
15
|
next unless obj
|
16
16
|
|
17
|
+
# don't overwrite an already-extant object with the same name
|
18
|
+
next if ::YARD::Registry.at("#{namespace.path}::#{obj.simple_name}")
|
19
|
+
|
17
20
|
# then we create a new constant in the current namespace
|
18
21
|
register CodeObjects::ConstantObject.new(namespace, obj.simple_name) { |o|
|
19
22
|
o.source = statement
|