diff --git a/lib/openhab/core/items.rb b/lib/openhab/core/items.rb index 09e3fbf70..5482daeff 100644 --- a/lib/openhab/core/items.rb +++ b/lib/openhab/core/items.rb @@ -74,13 +74,17 @@ def def_command_methods(klass) RUBY Enumerable.class_eval <<~RUBY, __FILE__, __LINE__ + 1 - def #{command} # def on - each(&:#{command}) # each(&:on) - end # end - # - def #{command}! # def on! - each(&:#{command}!) # each(&:on!) - end # end + ruby2_keywords def #{command}(*args, &block) # ruby2_keywords def on(*args, &block) + each do |member| # each do |member| + member.#{command}(*args, &block) # member.on(*args, &block) + end # end + end # end + # + ruby2_keywords def #{command}!(*args, &block) # ruby2_keywords def on!(*args, &block) + each do |member| # each do |member| + member.#{command}!(*args, &block) # member.on!(*args, &block) + end # end + end # end RUBY else logger.trace { "Defining #{klass}/Enumerable##{command} for #{value}" } diff --git a/lib/openhab/core/items/group_item.rb b/lib/openhab/core/items/group_item.rb index 71372df9e..ce4a5b4cc 100644 --- a/lib/openhab/core/items/group_item.rb +++ b/lib/openhab/core/items/group_item.rb @@ -199,7 +199,10 @@ def type_details end # Delegate missing methods to {base_item} if possible - def method_missing(method, *args, &block) + # Command methods and predicate methods for GroupItem are performed here + # instead of being statically defined in items.rb + # because base_item is needed to determine the command/data types but is not available in the static context + ruby2_keywords def method_missing(method, *args, &block) return base_item.__send__(method, *args, &block) if base_item&.respond_to?(method) # rubocop:disable Lint/RedundantSafeNavigation nil responds to :to_a super diff --git a/spec/openhab/core/items/group_item_spec.rb b/spec/openhab/core/items/group_item_spec.rb index e18fb6f23..aef7fb11f 100644 --- a/spec/openhab/core/items/group_item_spec.rb +++ b/spec/openhab/core/items/group_item_spec.rb @@ -122,6 +122,16 @@ end end + it "supports command methods" do + items.build do + group_item "Switches", type: :switch do + switch_item "SwitchOne" + end + end + + Switches.on + end + describe "#method_missing" do it "has command methods for the group type" do items.build do diff --git a/spec/openhab/dsl/items/builder_spec.rb b/spec/openhab/dsl/items/builder_spec.rb index 142d41198..5ac7010e6 100644 --- a/spec/openhab/dsl/items/builder_spec.rb +++ b/spec/openhab/dsl/items/builder_spec.rb @@ -604,7 +604,7 @@ def build_and_update(org_config, new_config, item_to_keep: :new_item, &block) end it "sets initial state on a group item" do - items.build { group_item "GroupItem1", type: "Switch", state: ON } + items.build { group_item "GroupItem1", type: :switch, state: ON } expect(GroupItem1.state).to be ON end diff --git a/spec/openhab/dsl/items/timed_command_spec.rb b/spec/openhab/dsl/items/timed_command_spec.rb index 772826dc2..7ad6b681d 100644 --- a/spec/openhab/dsl/items/timed_command_spec.rb +++ b/spec/openhab/dsl/items/timed_command_spec.rb @@ -291,13 +291,49 @@ def self.test_it(initial_state, command) expect(manualitem.state).to eq OFF end - it "works with GroupItem" do - items.build { group_item "Group1", type: "Switch", autoupdate: true } - Group1.update(OFF) - Group1.command(ON, for: 1.second) - expect(Group1).to be_on - time_travel_and_execute_timers(2.seconds) - expect(Group1).to be_off + context "with GroupItem" do + it "works" do + items.build do + group_item "Group1", type: :switch do + switch_item "Switch1" + end + end + Group1.command(ON, for: 1.second) + expect(Group1).to be_on + time_travel_and_execute_timers(2.seconds) + expect(Group1).to be_off + end + + it "works in command methods" do + items.build do + group_item "Group1", type: :switch do + switch_item "Switch1" + end + end + Group1.on for: 1.second + expect(Group1).to be_on + time_travel_and_execute_timers(2.seconds) + expect(Group1).to be_off + end + + it "cancels implicit timer when its group member received a command" do + items.build do + group_item "Group1", type: :number, function: "AVG" do + number_item "Number1", state: 0 + end + end + Group1.update(0) + Group1.command(1, for: 1.second) + expect(Group1.state).to eq 1 + time_travel_and_execute_timers(2.seconds) + expect(Group1.state).to eq 0 + + Group1.command(1, for: 1.second) + Number1.command(2) + expect(Group1.state).to eq 2 + time_travel_and_execute_timers(2.seconds) + expect(Group1.state).to eq 2 + end end context "with Enumerable" do @@ -317,6 +353,15 @@ def self.test_it(initial_state, command) expect(Switch2).to be_off end + it "works with command methods" do + [Switch1, Switch2].on for: 1.second + expect(Switch1).to be_on + expect(Switch2).to be_on + time_travel_and_execute_timers(2.seconds) + expect(Switch1).to be_off + expect(Switch2).to be_off + end + it "each member has its own timer" do [Switch1, Switch2].command(ON, for: 1.second) Switch1.on