diff --git a/lib/openhab/dsl/rules/builder.rb b/lib/openhab/dsl/rules/builder.rb index 538469f14e..21b0a45138 100644 --- a/lib/openhab/dsl/rules/builder.rb +++ b/lib/openhab/dsl/rules/builder.rb @@ -1087,7 +1087,8 @@ def changed(*items, to: nil, from: nil, for: nil, attach: nil) # The same rules for the standard # [cron expression](https://www.quartz-scheduler.org/documentation/quartz-2.2.2/tutorials/tutorial-lesson-06.html) # apply for each field. For example, multiple values can be separated - # with a comma within a string. + # with a comma within a string, and ranges can be specified with a dash or with + # a Ruby Range. # # @param [Integer, String, nil] second # @param [Integer, String, nil] minute @@ -1097,7 +1098,8 @@ def changed(*items, to: nil, from: nil, for: nil, attach: nil) # @param [Integer, String, nil] dow # @param [Integer, String, nil] year # @param [Object] attach object to be attached to the trigger - # @example + # + # @example Using String values # # Run every 3 minutes on Monday to Friday # # equivalent to the cron expression "0 */3 * ? * MON-FRI *" # rule "Using cron fields" do @@ -1105,7 +1107,7 @@ def changed(*items, to: nil, from: nil, for: nil, attach: nil) # run { logger.info "Cron rule executed" } # end # - # @example + # @example Defaults for unspecified fields # # Run at midnight on the first day of January, February, and March # # equivalent to the cron expression "0 0 0 1 JAN-MAR ? *" # rule "Using cron fields" do @@ -1113,6 +1115,14 @@ def changed(*items, to: nil, from: nil, for: nil, attach: nil) # run { logger.info "Cron rule executed" } # end # + # @example Using Ruby Range values + # # Run on the hour, every hour between 1pm and 5pm + # # equivalent to the cron expression "0 0 13-17 ? * ? *" + # rule "Using cron fields" do + # cron hour: 13..17 + # run { logger.info "Cron rule executed" } + # end + # # @return [void] # def cron(expression = nil, attach: nil, **fields) diff --git a/lib/openhab/dsl/rules/triggers/cron/cron.rb b/lib/openhab/dsl/rules/triggers/cron/cron.rb index 4213754c02..d4f8f56fb2 100644 --- a/lib/openhab/dsl/rules/triggers/cron/cron.rb +++ b/lib/openhab/dsl/rules/triggers/cron/cron.rb @@ -125,7 +125,23 @@ def self.from_fields(fields) "unknown keyword#{"s" if extra_fields.size > 1}: #{extra_fields.map(&:inspect).join(", ")}" end - fields = fields.transform_values { |value| value.to_s.delete(" ") } + fields = fields.to_h do |key, value| + if value.is_a?(Range) + if value.exclude_end? + raise ArgumentError, + "Range must be inclusive for '#{key}'. Try '#{value.begin}..#{value.end}' instead" + end + + unless value.begin && value.end + raise ArgumentError, + "Range must have a beginning and ending for '#{key}'" + end + + [key, "#{value.begin}-#{value.end}".delete(" ")] + else + [key, value.to_s.delete(" ")] + end + end # convert fields' key dow->week, dom->day to look into EXPRESSION_MAP fields_expression = fields.transform_keys { |key| FIELD_TO_EXPRESSION_KEY[key] } # find the first expression map that has a field from fields. diff --git a/spec/openhab/dsl/rules/builder_spec.rb b/spec/openhab/dsl/rules/builder_spec.rb index 8e9740a083..a3637264dc 100644 --- a/spec/openhab/dsl/rules/builder_spec.rb +++ b/spec/openhab/dsl/rules/builder_spec.rb @@ -1060,6 +1060,22 @@ def self.test_cron_fields(expected, description: nil, caller: Kernel.caller, **k test_cron_fields("0 0 0 1 2 ? *", month: 2) test_cron_fields("0 0 0 1 1 ? 2023-2025", year: "2023-2025") end + + context "with ranges" do # examples are dynamically generated + test_cron_fields("12-14 * * ? * ? *", second: 12..14) + test_cron_fields("0 12-14 * ? * ? *", minute: 12..14) + test_cron_fields("0 0 12-14 ? * ? *", hour: 12..14) + test_cron_fields("0 0 0 ? * TUE-WED *", dow: :TUE..:WED) + test_cron_fields("0 0 0 12-14 * ? *", dom: 12..14) + test_cron_fields("0 0 0 1 2-5 ? *", month: 2..5) + test_cron_fields("0 0 0 1 FEB-MAY ? *", month: :FEB..:MAY) + test_cron_fields("0 0 0 1 1 ? 2023-2025", year: 2023..2025) + + it "raises ArgumentError about unsupported ranges" do + expect { cron second: 12.. }.to raise_error(ArgumentError) + expect { cron second: 12...14 }.to raise_error(ArgumentError) + end + end end end