openhab-scripting 2.9.1
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
- data/.github/workflows/workflow.yml +327 -0
- data/.gitignore +17 -0
- data/.java-version +1 -0
- data/.rspec +1 -0
- data/.yardopts +1 -0
- data/CHANGELOG.md +113 -0
- data/Gemfile +28 -0
- data/Gemfile.lock +245 -0
- data/Guardfile +35 -0
- data/LICENSE +277 -0
- data/README.md +23 -0
- data/Rakefile +406 -0
- data/bin/console +15 -0
- data/bin/setup +8 -0
- data/config/userdata/config/org/openhab/restauth.config +3 -0
- data/cucumber.yml +1 -0
- data/docs/_config.yml +135 -0
- data/docs/contributing/index.md +47 -0
- data/docs/examples/conversions.md +123 -0
- data/docs/examples/index.md +61 -0
- data/docs/index.md +19 -0
- data/docs/installation/index.md +26 -0
- data/docs/motivation/index.md +27 -0
- data/docs/usage/execution.md +9 -0
- data/docs/usage/execution/delay.md +48 -0
- data/docs/usage/execution/otherwise.md +30 -0
- data/docs/usage/execution/run.md +70 -0
- data/docs/usage/execution/triggered.md +48 -0
- data/docs/usage/guards.md +51 -0
- data/docs/usage/guards/between.md +30 -0
- data/docs/usage/guards/not_if.md +41 -0
- data/docs/usage/guards/only_if.md +40 -0
- data/docs/usage/index.md +11 -0
- data/docs/usage/items.md +66 -0
- data/docs/usage/items/contact.md +84 -0
- data/docs/usage/items/dimmer.md +147 -0
- data/docs/usage/items/groups.md +76 -0
- data/docs/usage/items/number.md +225 -0
- data/docs/usage/items/string.md +49 -0
- data/docs/usage/items/switch.md +85 -0
- data/docs/usage/misc.md +7 -0
- data/docs/usage/misc/actions.md +108 -0
- data/docs/usage/misc/duration.md +21 -0
- data/docs/usage/misc/gems.md +25 -0
- data/docs/usage/misc/logging.md +21 -0
- data/docs/usage/misc/metadata.md +128 -0
- data/docs/usage/misc/store_states.md +42 -0
- data/docs/usage/misc/time_of_day.md +69 -0
- data/docs/usage/misc/timers.md +67 -0
- data/docs/usage/rule.md +43 -0
- data/docs/usage/things.md +29 -0
- data/docs/usage/triggers.md +8 -0
- data/docs/usage/triggers/changed.md +57 -0
- data/docs/usage/triggers/channel.md +54 -0
- data/docs/usage/triggers/command.md +69 -0
- data/docs/usage/triggers/cron.md +19 -0
- data/docs/usage/triggers/every.md +76 -0
- data/docs/usage/triggers/updated.md +78 -0
- data/lib/openhab.rb +39 -0
- data/lib/openhab/configuration.rb +16 -0
- data/lib/openhab/core/cron.rb +27 -0
- data/lib/openhab/core/debug.rb +34 -0
- data/lib/openhab/core/dsl.rb +47 -0
- data/lib/openhab/core/dsl/actions.rb +107 -0
- data/lib/openhab/core/dsl/entities.rb +103 -0
- data/lib/openhab/core/dsl/gems.rb +29 -0
- data/lib/openhab/core/dsl/group.rb +91 -0
- data/lib/openhab/core/dsl/items/items.rb +39 -0
- data/lib/openhab/core/dsl/items/number_item.rb +217 -0
- data/lib/openhab/core/dsl/items/string_item.rb +102 -0
- data/lib/openhab/core/dsl/monkey_patch/actions/actions.rb +4 -0
- data/lib/openhab/core/dsl/monkey_patch/actions/script_thing_actions.rb +22 -0
- data/lib/openhab/core/dsl/monkey_patch/events.rb +5 -0
- data/lib/openhab/core/dsl/monkey_patch/events/item_command.rb +13 -0
- data/lib/openhab/core/dsl/monkey_patch/events/item_state_changed.rb +25 -0
- data/lib/openhab/core/dsl/monkey_patch/events/thing_status_info.rb +26 -0
- data/lib/openhab/core/dsl/monkey_patch/items/contact_item.rb +54 -0
- data/lib/openhab/core/dsl/monkey_patch/items/dimmer_item.rb +125 -0
- data/lib/openhab/core/dsl/monkey_patch/items/group_item.rb +27 -0
- data/lib/openhab/core/dsl/monkey_patch/items/items.rb +130 -0
- data/lib/openhab/core/dsl/monkey_patch/items/metadata.rb +259 -0
- data/lib/openhab/core/dsl/monkey_patch/items/switch_item.rb +86 -0
- data/lib/openhab/core/dsl/monkey_patch/ruby/number.rb +69 -0
- data/lib/openhab/core/dsl/monkey_patch/ruby/range.rb +46 -0
- data/lib/openhab/core/dsl/monkey_patch/ruby/ruby.rb +5 -0
- data/lib/openhab/core/dsl/monkey_patch/types/decimal_type.rb +24 -0
- data/lib/openhab/core/dsl/monkey_patch/types/on_off_type.rb +41 -0
- data/lib/openhab/core/dsl/monkey_patch/types/open_closed_type.rb +25 -0
- data/lib/openhab/core/dsl/monkey_patch/types/percent_type.rb +23 -0
- data/lib/openhab/core/dsl/monkey_patch/types/types.rb +7 -0
- data/lib/openhab/core/dsl/property.rb +85 -0
- data/lib/openhab/core/dsl/rule/channel.rb +41 -0
- data/lib/openhab/core/dsl/rule/cron.rb +115 -0
- data/lib/openhab/core/dsl/rule/guard.rb +99 -0
- data/lib/openhab/core/dsl/rule/item.rb +207 -0
- data/lib/openhab/core/dsl/rule/rule.rb +374 -0
- data/lib/openhab/core/dsl/rule/triggers.rb +77 -0
- data/lib/openhab/core/dsl/states.rb +63 -0
- data/lib/openhab/core/dsl/things.rb +93 -0
- data/lib/openhab/core/dsl/time_of_day.rb +203 -0
- data/lib/openhab/core/dsl/timers.rb +85 -0
- data/lib/openhab/core/dsl/types/quantity.rb +255 -0
- data/lib/openhab/core/dsl/units.rb +41 -0
- data/lib/openhab/core/duration.rb +69 -0
- data/lib/openhab/core/log.rb +175 -0
- data/lib/openhab/core/patch_load_path.rb +7 -0
- data/lib/openhab/core/startup_delay.rb +22 -0
- data/lib/openhab/osgi.rb +52 -0
- data/lib/openhab/version.rb +9 -0
- data/openhab-scripting.gemspec +30 -0
- data/openhab_rules/warmup.rb +5 -0
- metadata +157 -0
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'java'
|
|
4
|
+
|
|
5
|
+
#
|
|
6
|
+
# MonkeyPatching Decimal Type
|
|
7
|
+
#
|
|
8
|
+
# rubocop:disable Style/ClassAndModuleChildren
|
|
9
|
+
class Java::OrgOpenhabCoreLibraryTypes::DecimalType
|
|
10
|
+
# rubocop:enable Style/ClassAndModuleChildren
|
|
11
|
+
|
|
12
|
+
#
|
|
13
|
+
# Compare self to other using Java BigDecimal compare method
|
|
14
|
+
#
|
|
15
|
+
# @param [Object] other object to compare to
|
|
16
|
+
#
|
|
17
|
+
# @return [Boolean] True if have the same BigDecimal representation, false otherwise
|
|
18
|
+
#
|
|
19
|
+
def ==(other)
|
|
20
|
+
return equals(other) unless other.is_a? Integer
|
|
21
|
+
|
|
22
|
+
to_big_decimal.compare_to(Java::JavaMath::BigDecimal.new(other)).zero?
|
|
23
|
+
end
|
|
24
|
+
end
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'java'
|
|
4
|
+
|
|
5
|
+
#
|
|
6
|
+
# Monkey patching OnOffType
|
|
7
|
+
#
|
|
8
|
+
# rubocop:disable Style/ClassAndModuleChildren
|
|
9
|
+
class Java::OrgOpenhabCoreLibraryTypes::OnOffType
|
|
10
|
+
# rubocop:enable Style/ClassAndModuleChildren
|
|
11
|
+
|
|
12
|
+
#
|
|
13
|
+
# Invert the type
|
|
14
|
+
#
|
|
15
|
+
# @return [Java::OrgOpenhabCoreLibraryTypes::OnOffType] OFF if ON, ON if OFF
|
|
16
|
+
#
|
|
17
|
+
def !
|
|
18
|
+
return OFF if self == ON
|
|
19
|
+
return ON if self == OFF
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
# Check if the supplied object is case equals to self
|
|
23
|
+
#
|
|
24
|
+
# @param [Object] other object to compare
|
|
25
|
+
#
|
|
26
|
+
# @return [Boolean] True if the other object responds to on?/off? and is in the same state as this object,
|
|
27
|
+
# nil if object cannot be compared
|
|
28
|
+
#
|
|
29
|
+
def ===(other)
|
|
30
|
+
# A case statement here causes and infinite loop
|
|
31
|
+
# rubocop:disable Style/CaseLikeIf
|
|
32
|
+
if self == ON
|
|
33
|
+
other.on? if other.respond_to? :on?
|
|
34
|
+
elsif self == OFF
|
|
35
|
+
other.off? if other.respond_to?('off?')
|
|
36
|
+
else
|
|
37
|
+
false
|
|
38
|
+
end
|
|
39
|
+
# rubocop:enable Style/CaseLikeIf
|
|
40
|
+
end
|
|
41
|
+
end
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'java'
|
|
4
|
+
|
|
5
|
+
#
|
|
6
|
+
# Monkey patch for DSL use
|
|
7
|
+
#
|
|
8
|
+
# rubocop:disable Style/ClassAndModuleChildren
|
|
9
|
+
class Java::OrgOpenhabCoreLibraryTypes::OpenClosedType
|
|
10
|
+
# rubocop:enable Style/ClassAndModuleChildren
|
|
11
|
+
java_import org.openhab.core.library.items.ContactItem
|
|
12
|
+
|
|
13
|
+
#
|
|
14
|
+
# Check if the supplied object is case equals to self
|
|
15
|
+
#
|
|
16
|
+
# @param [Object] other object to compare
|
|
17
|
+
#
|
|
18
|
+
# @return [Boolean] True if the other object is a ContactItem and has the same state
|
|
19
|
+
#
|
|
20
|
+
def ===(other)
|
|
21
|
+
super unless other.is_a? ContactItem
|
|
22
|
+
|
|
23
|
+
self == other.state
|
|
24
|
+
end
|
|
25
|
+
end
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'java'
|
|
4
|
+
|
|
5
|
+
#
|
|
6
|
+
# MonkeyPatching PercentType
|
|
7
|
+
#
|
|
8
|
+
# rubocop:disable Style/ClassAndModuleChildren
|
|
9
|
+
class Java::OrgOpenhabCoreLibraryTypes::PercentType
|
|
10
|
+
# rubocop:enable Style/ClassAndModuleChildren
|
|
11
|
+
|
|
12
|
+
#
|
|
13
|
+
# Need to override and point to super because default JRuby implementation doesn't point to == of parent class
|
|
14
|
+
#
|
|
15
|
+
# @param [Object] other object to check equality for
|
|
16
|
+
# @return [Boolean] True if other equals self, false otherwise
|
|
17
|
+
#
|
|
18
|
+
# rubocop:disable Lint/UselessMethodDefinition
|
|
19
|
+
def ==(other)
|
|
20
|
+
super
|
|
21
|
+
end
|
|
22
|
+
# rubocop:enable Lint/UselessMethodDefinition
|
|
23
|
+
end
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Monkey patch types
|
|
4
|
+
require 'core/dsl/monkey_patch/types/open_closed_type'
|
|
5
|
+
require 'core/dsl/monkey_patch/types/on_off_type'
|
|
6
|
+
require 'core/dsl/monkey_patch/types/decimal_type'
|
|
7
|
+
require 'core/dsl/monkey_patch/types/percent_type'
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'core/log'
|
|
4
|
+
|
|
5
|
+
#
|
|
6
|
+
# Provides methods to support DSL properties
|
|
7
|
+
#
|
|
8
|
+
module DSLProperty
|
|
9
|
+
include Logging
|
|
10
|
+
|
|
11
|
+
#
|
|
12
|
+
# Extend the calling object with the property methods
|
|
13
|
+
#
|
|
14
|
+
# @param [Object] base object to extend
|
|
15
|
+
#
|
|
16
|
+
#
|
|
17
|
+
def self.included(base)
|
|
18
|
+
base.extend PropertyMethods
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
#
|
|
22
|
+
# Methods that support creating properties in the DSL
|
|
23
|
+
#
|
|
24
|
+
module PropertyMethods
|
|
25
|
+
#
|
|
26
|
+
# Dynamically creates a property that acts and an accessor with no arguments
|
|
27
|
+
# and a setter with any number of arguments or a block.
|
|
28
|
+
#
|
|
29
|
+
# @param [String] name of the property
|
|
30
|
+
#
|
|
31
|
+
#
|
|
32
|
+
def prop(name)
|
|
33
|
+
define_method(name) do |*args, &block|
|
|
34
|
+
if args.length.zero? && block.nil? == true
|
|
35
|
+
instance_variable_get("@#{name}")
|
|
36
|
+
else
|
|
37
|
+
logger.trace("Property '#{name}' called with args(#{args}) and block(#{block})")
|
|
38
|
+
if args.length == 1
|
|
39
|
+
instance_variable_set("@#{name}", args.first)
|
|
40
|
+
elsif args.length > 1
|
|
41
|
+
instance_variable_set("@#{name}", args)
|
|
42
|
+
elsif block
|
|
43
|
+
instance_variable_set("@#{name}", block)
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
#
|
|
50
|
+
# Dynamically creates a property array acts and an accessor with no arguments
|
|
51
|
+
# and a pushes any number of arguments or a block onto they property array
|
|
52
|
+
# You can provide a block to this method which can be used to check if the provided value is acceptable.
|
|
53
|
+
#
|
|
54
|
+
# @param [String] name of the property
|
|
55
|
+
# @param [String] array_name name of the array to use, defaults to name of property
|
|
56
|
+
# @param [Class] wrapper object to put around elements added to the array
|
|
57
|
+
#
|
|
58
|
+
def prop_array(name, array_name: nil, wrapper: nil)
|
|
59
|
+
define_method(name) do |*args, &block|
|
|
60
|
+
array_name ||= name
|
|
61
|
+
if args.length.zero? && block.nil? == true
|
|
62
|
+
instance_variable_get("@#{array_name}")
|
|
63
|
+
else
|
|
64
|
+
logger.trace("Property '#{name}' called with args(#{args}) and block(#{block})")
|
|
65
|
+
if args.length == 1
|
|
66
|
+
insert = args.first
|
|
67
|
+
elsif args.length > 1
|
|
68
|
+
insert = args
|
|
69
|
+
elsif block
|
|
70
|
+
insert = block
|
|
71
|
+
end
|
|
72
|
+
yield insert if block_given?
|
|
73
|
+
insert = wrapper.new(insert) if wrapper
|
|
74
|
+
instance_variable_set("@#{array_name}", (instance_variable_get("@#{array_name}") || []) << insert)
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
if array_name
|
|
79
|
+
define_method(array_name) do
|
|
80
|
+
instance_variable_get("@#{array_name}")
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
end
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'core/log'
|
|
4
|
+
require 'openhab/core/dsl/rule/triggers'
|
|
5
|
+
|
|
6
|
+
module OpenHAB
|
|
7
|
+
module Core
|
|
8
|
+
module DSL
|
|
9
|
+
module Rule
|
|
10
|
+
#
|
|
11
|
+
# Channel triggers
|
|
12
|
+
#
|
|
13
|
+
module Channel
|
|
14
|
+
include Logging
|
|
15
|
+
|
|
16
|
+
#
|
|
17
|
+
# Creates a channel trigger
|
|
18
|
+
#
|
|
19
|
+
# @param [Array] channels array to create triggers for on form of 'binding_id:type_id:thing_id#channel_id' or 'channel_id' if thing is provided
|
|
20
|
+
# @param [thing] thing to create trigger for if not specified with the channel
|
|
21
|
+
# @param [String] triggered specific triggering condition to match for trigger
|
|
22
|
+
#
|
|
23
|
+
#
|
|
24
|
+
def channel(*channels, thing: nil, triggered: nil)
|
|
25
|
+
channels.flatten.each do |channel|
|
|
26
|
+
channel = [thing, channel].join(':') if thing
|
|
27
|
+
logger.trace("Creating channel trigger for channel(#{channel}), thing(#{thing}), trigger(#{triggered})")
|
|
28
|
+
[triggered].flatten.each do |trigger|
|
|
29
|
+
config = { 'channelUID' => channel }
|
|
30
|
+
config['event'] = trigger.to_s unless trigger.nil?
|
|
31
|
+
config['channelUID'] = channel
|
|
32
|
+
logger.trace("Creating Change Trigger for #{config}")
|
|
33
|
+
@triggers << Trigger.trigger(type: Trigger::CHANNEL_EVENT, config: config)
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'java'
|
|
4
|
+
require 'core/duration'
|
|
5
|
+
require 'core/dsl/time_of_day'
|
|
6
|
+
require 'core/cron'
|
|
7
|
+
|
|
8
|
+
module OpenHAB
|
|
9
|
+
module Core
|
|
10
|
+
module DSL
|
|
11
|
+
module Rule
|
|
12
|
+
#
|
|
13
|
+
# Cron type rules
|
|
14
|
+
#
|
|
15
|
+
module Cron
|
|
16
|
+
java_import org.openhab.core.automation.util.TriggerBuilder
|
|
17
|
+
java_import org.openhab.core.config.core.Configuration
|
|
18
|
+
|
|
19
|
+
include OpenHAB::Core::DSL::Rule
|
|
20
|
+
extend OpenHAB::Core::Cron
|
|
21
|
+
|
|
22
|
+
# @return [Map] Map of days of the week from symbols to to OpenHAB cron strings
|
|
23
|
+
DAY_OF_WEEK_MAP = {
|
|
24
|
+
monday: 'MON',
|
|
25
|
+
tuesday: 'TUE',
|
|
26
|
+
wednesday: 'WED',
|
|
27
|
+
thursday: 'THU',
|
|
28
|
+
friday: 'FRI',
|
|
29
|
+
saturday: 'SAT',
|
|
30
|
+
sunday: 'SUN'
|
|
31
|
+
}.freeze
|
|
32
|
+
|
|
33
|
+
private_constant :DAY_OF_WEEK_MAP
|
|
34
|
+
|
|
35
|
+
# @return [MAP] Converts the DAY_OF_WEEK_MAP to map used by Cron Expression
|
|
36
|
+
DAY_OF_WEEK_EXPRESSION_MAP = DAY_OF_WEEK_MAP.transform_values { |v| cron_expression_map.merge(dow: v) }
|
|
37
|
+
|
|
38
|
+
private_constant :DAY_OF_WEEK_EXPRESSION_MAP
|
|
39
|
+
|
|
40
|
+
# @return [Map] Create a set of cron expressions based on different time intervals
|
|
41
|
+
EXPRESSION_MAP = {
|
|
42
|
+
second: cron_expression_map,
|
|
43
|
+
minute: cron_expression_map.merge(second: '0'),
|
|
44
|
+
hour: cron_expression_map.merge(second: '0', minute: '0'),
|
|
45
|
+
day: cron_expression_map.merge(second: '0', minute: '0', hour: '0'),
|
|
46
|
+
week: cron_expression_map.merge(second: '0', minute: '0', hour: '0', dow: 'MON'),
|
|
47
|
+
month: cron_expression_map.merge(second: '0', minute: '0', hour: '0', dom: '1'),
|
|
48
|
+
year: cron_expression_map.merge(second: '0', minute: '0', hour: '0', dom: '1', month: '1')
|
|
49
|
+
}.merge(DAY_OF_WEEK_EXPRESSION_MAP)
|
|
50
|
+
.freeze
|
|
51
|
+
|
|
52
|
+
private_constant :EXPRESSION_MAP
|
|
53
|
+
|
|
54
|
+
#
|
|
55
|
+
# Create a rule that executes at the specified interval
|
|
56
|
+
#
|
|
57
|
+
# @param [Object] value Symbol or Duration to execute this rule
|
|
58
|
+
# @param [Object] at TimeOfDay or String representing TimeOfDay in which to execute rule
|
|
59
|
+
#
|
|
60
|
+
#
|
|
61
|
+
def every(value, at: nil)
|
|
62
|
+
case value
|
|
63
|
+
when Symbol
|
|
64
|
+
expression_map = EXPRESSION_MAP[value]
|
|
65
|
+
expression_map = at_condition(expression_map, at) if at
|
|
66
|
+
cron(map_to_cron(expression_map))
|
|
67
|
+
when Duration
|
|
68
|
+
cron(map_to_cron(value.cron_map))
|
|
69
|
+
else
|
|
70
|
+
raise ArgumentExpression, 'Unknown interval' unless expression_map
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
#
|
|
75
|
+
# Create a OpenHAB Cron trigger
|
|
76
|
+
#
|
|
77
|
+
# @param [String] expression OpenHAB style cron expression
|
|
78
|
+
#
|
|
79
|
+
def cron(expression)
|
|
80
|
+
@triggers << Trigger.trigger(type: Trigger::CRON, config: { 'cronExpression' => expression })
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
private
|
|
84
|
+
|
|
85
|
+
#
|
|
86
|
+
# Map cron expression to to cron string
|
|
87
|
+
#
|
|
88
|
+
# @param [Map] map of cron expression
|
|
89
|
+
#
|
|
90
|
+
# @return [String] OpenHAB cron string
|
|
91
|
+
#
|
|
92
|
+
def map_to_cron(map)
|
|
93
|
+
%i[second minute hour dom month dow].map { |field| map.fetch(field) }.join(' ')
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
#
|
|
97
|
+
# If an at time is provided, parse that and merge the new fields into the expression.
|
|
98
|
+
#
|
|
99
|
+
# @param [<Type>] expression_map <description>
|
|
100
|
+
# @param [<Type>] at_time <description>
|
|
101
|
+
#
|
|
102
|
+
# @return [<Type>] <description>
|
|
103
|
+
#
|
|
104
|
+
def at_condition(expression_map, at_time)
|
|
105
|
+
if at_time
|
|
106
|
+
tod = (at_time.is_a? TimeOfDay) ? at_time : TimeOfDay.parse(at_time)
|
|
107
|
+
expression_map = expression_map.merge(hour: tod.hour, minute: tod.minute, second: tod.second)
|
|
108
|
+
end
|
|
109
|
+
expression_map
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
end
|
|
115
|
+
end
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'core/dsl/property'
|
|
4
|
+
require 'core/log'
|
|
5
|
+
|
|
6
|
+
module OpenHAB
|
|
7
|
+
module Core
|
|
8
|
+
module DSL
|
|
9
|
+
module Rule
|
|
10
|
+
#
|
|
11
|
+
# Guards for rules
|
|
12
|
+
#
|
|
13
|
+
module Guard
|
|
14
|
+
include DSLProperty
|
|
15
|
+
|
|
16
|
+
prop_array(:only_if) do |item|
|
|
17
|
+
unless item.is_a?(Proc) || item.respond_to?(:truthy?)
|
|
18
|
+
raise ArgumentError, "Object passed to only_if must respond_to 'truthy?'"
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
prop_array(:not_if) do |item|
|
|
23
|
+
unless item.is_a?(Proc) || item.respond_to?(:truthy?)
|
|
24
|
+
raise ArgumentError, "Object passed to not_if must respond_to 'truthy?'"
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
#
|
|
29
|
+
# Guard that can prevent execute of a rule if not satisfied
|
|
30
|
+
#
|
|
31
|
+
class Guard
|
|
32
|
+
include Logging
|
|
33
|
+
|
|
34
|
+
#
|
|
35
|
+
# Create a new Guard
|
|
36
|
+
#
|
|
37
|
+
# @param [Object] only_if Item or Proc to use as guard
|
|
38
|
+
# @param [Object] not_if Item or Proc to use as guard
|
|
39
|
+
#
|
|
40
|
+
def initialize(only_if: nil, not_if: nil)
|
|
41
|
+
@only_if = only_if
|
|
42
|
+
@not_if = not_if
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
#
|
|
46
|
+
# Convert the guard into a string
|
|
47
|
+
#
|
|
48
|
+
# @return [String] describing the only_of and not_if guards
|
|
49
|
+
#
|
|
50
|
+
def to_s
|
|
51
|
+
"only_if: #{@only_if}, not_if: #{@not_if}"
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
#
|
|
55
|
+
# Checks if a guard should run
|
|
56
|
+
#
|
|
57
|
+
# @param [OpenHAB Trigger Event] event OpenHAB Trigger Event
|
|
58
|
+
#
|
|
59
|
+
# @return [Boolean] True if guard is satisfied, false otherwise
|
|
60
|
+
#
|
|
61
|
+
def should_run?(event)
|
|
62
|
+
logger.trace("Checking guards #{self}")
|
|
63
|
+
check(@only_if, check_type: :only_if, event: event) && check(@not_if, check_type: :not_if, event: event)
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
private
|
|
67
|
+
|
|
68
|
+
#
|
|
69
|
+
# Check if guard is satisfied
|
|
70
|
+
#
|
|
71
|
+
# @param [Array] conditions to check
|
|
72
|
+
# @param [Symbol] check_type type of check to perform (:only_if or :not_if)
|
|
73
|
+
# @param [Event] event OpenHAB event to see if it satisfies the guard
|
|
74
|
+
#
|
|
75
|
+
# @return [Boolean] True if guard is satisfied, false otherwise
|
|
76
|
+
#
|
|
77
|
+
def check(conditions, check_type:, event:)
|
|
78
|
+
return true if conditions.nil? || conditions.empty?
|
|
79
|
+
|
|
80
|
+
procs, items = conditions.flatten.partition { |condition| condition.is_a? Proc }
|
|
81
|
+
logger.trace("Procs: #{procs} Items: #{items}")
|
|
82
|
+
|
|
83
|
+
items.each { |item| logger.trace("#{item} truthy? #{item.truthy?}") }
|
|
84
|
+
|
|
85
|
+
case check_type
|
|
86
|
+
when :only_if
|
|
87
|
+
items.all?(&:truthy?) && procs.all? { |proc| proc.call(event) }
|
|
88
|
+
when :not_if
|
|
89
|
+
items.none?(&:truthy?) && procs.none? { |proc| proc.call(event) }
|
|
90
|
+
else
|
|
91
|
+
raise ArgumentError, "Unexpected check type: #{check_type}"
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
end
|