From 63cc7ce533d5efcd9c72edc04a4934fcb88141cf Mon Sep 17 00:00:00 2001 From: Cody Cutrer Date: Wed, 2 Oct 2024 12:52:03 -0600 Subject: [PATCH] Allow sending source with commands And document the source field on events, and add several inspect methods to more easily debug events with sources. Signed-off-by: Cody Cutrer --- lib/openhab/core/events.rb | 8 ++++++++ lib/openhab/core/events/abstract_event.rb | 11 +++++++++-- .../core/events/channel_triggered_event.rb | 7 +++++++ lib/openhab/core/events/item_command_event.rb | 7 +++++++ .../core/events/item_state_changed_event.rb | 8 ++++++++ lib/openhab/core/events/item_state_event.rb | 7 +++++++ .../core/events/thing_status_info_event.rb | 16 ++++++++++++++++ lib/openhab/core/items/generic_item.rb | 9 +++++++-- lib/openhab/core/items/group_item.rb | 6 +++--- lib/openhab/dsl/items/ensure.rb | 19 +++++++++++-------- lib/openhab/dsl/items/timed_command.rb | 10 +++++----- .../core/events/item_command_event_spec.rb | 10 +++++++++- .../events/item_state_changed_event_spec.rb | 13 +++++++++---- .../core/events/item_state_event_spec.rb | 14 +++++++++++--- .../events/item_state_updated_event_spec.rb | 16 +++++++++++++--- spec/openhab/core/items/item_spec.rb | 18 ++++++++++++++++++ 16 files changed, 148 insertions(+), 31 deletions(-) diff --git a/lib/openhab/core/events.rb b/lib/openhab/core/events.rb index 85bffbc5f7..f81970cb3a 100644 --- a/lib/openhab/core/events.rb +++ b/lib/openhab/core/events.rb @@ -5,6 +5,14 @@ module Core # Contains objects sent to event handlers containing context around the # triggered event. module Events + class << self + # @!visibility private + def publisher + @publisher ||= OSGi.service("org.openhab.core.events.EventPublisher") + end + end + + java_import org.openhab.core.items.events.ItemEventFactory end end end diff --git a/lib/openhab/core/events/abstract_event.rb b/lib/openhab/core/events/abstract_event.rb index 45f87a58eb..baa60466ff 100644 --- a/lib/openhab/core/events/abstract_event.rb +++ b/lib/openhab/core/events/abstract_event.rb @@ -13,8 +13,8 @@ class AbstractEvent # @return [Hash] attr_accessor :inputs - # @return [String] - alias_method :inspect, :to_s + # @!attribute [r] source + # @return [String] The component that sent the event. # # Returns the event payload as a Hash. @@ -26,6 +26,13 @@ def payload require "json" @payload ||= JSON.parse(get_payload, symbolize_names: true) unless get_payload.empty? end + + # @return [String] + def inspect + s = "#" + end end end end diff --git a/lib/openhab/core/events/channel_triggered_event.rb b/lib/openhab/core/events/channel_triggered_event.rb index 40f5265bc4..64a4be203b 100644 --- a/lib/openhab/core/events/channel_triggered_event.rb +++ b/lib/openhab/core/events/channel_triggered_event.rb @@ -24,6 +24,13 @@ class ChannelTriggeredEvent < AbstractEvent # @!attribute [r] event # @return [String] The event data + + # @return [String] + def inspect + s = "#" + end end end end diff --git a/lib/openhab/core/events/item_command_event.rb b/lib/openhab/core/events/item_command_event.rb index 31d3bc810e..dbb3d2976f 100644 --- a/lib/openhab/core/events/item_command_event.rb +++ b/lib/openhab/core/events/item_command_event.rb @@ -72,6 +72,13 @@ class ItemCommandEvent < ItemEvent # @!method previous? # Check if {#command} is {PREVIOUS} # @return [true, false] + + # @return [String] + def inspect + s = "#" + end end end end diff --git a/lib/openhab/core/events/item_state_changed_event.rb b/lib/openhab/core/events/item_state_changed_event.rb index c2978f607b..e42c62304c 100644 --- a/lib/openhab/core/events/item_state_changed_event.rb +++ b/lib/openhab/core/events/item_state_changed_event.rb @@ -69,6 +69,14 @@ def was? def was old_item_state if was? end + + # @return [String] + def inspect + s = "#" + end end end end diff --git a/lib/openhab/core/events/item_state_event.rb b/lib/openhab/core/events/item_state_event.rb index d42eba3007..f0a7211dca 100644 --- a/lib/openhab/core/events/item_state_event.rb +++ b/lib/openhab/core/events/item_state_event.rb @@ -68,6 +68,13 @@ def state? def state item_state if state? end + + # @return [String] + def inspect + s = "#" + end end # {AbstractEvent} sent when an item's state is updated (regardless of if it changed or not). diff --git a/lib/openhab/core/events/thing_status_info_event.rb b/lib/openhab/core/events/thing_status_info_event.rb index 805babf488..0db692c12b 100644 --- a/lib/openhab/core/events/thing_status_info_event.rb +++ b/lib/openhab/core/events/thing_status_info_event.rb @@ -27,6 +27,14 @@ class ThingStatusInfoChangedEvent < AbstractEvent def thing EntityLookup.lookup_thing(uid) end + + # @return [String] + def inspect + s = "#" + end end # The {AbstractEvent} sent when a {Things::Thing}'s status is set. @@ -49,6 +57,14 @@ class ThingStatusInfoEvent < AbstractEvent def thing EntityLookup.lookup_thing(uid) end + + # @return [String] + def inspect + s = "#" + end end end end diff --git a/lib/openhab/core/items/generic_item.rb b/lib/openhab/core/items/generic_item.rb index 520df4fdb5..c60fa87627 100644 --- a/lib/openhab/core/items/generic_item.rb +++ b/lib/openhab/core/items/generic_item.rb @@ -137,6 +137,7 @@ def state # @param [Command, #to_s] command command to send to the item. # When given a {Command} argument, it will be passed directly. # Otherwise, the result of `#to_s` will be parsed into a {Command}. + # @param [String, nil] source Optional string to identify what sent the event. # @return [self, nil] nil when `ensure` is in effect and the item was already in the same state, # otherwise the item. # @@ -156,10 +157,14 @@ def state # @example Sending a string to a dimensioned {NumberItem} # SetTemperature.command("22.5 °C") # The string will be parsed and converted to a QuantityType # - def command(command) + def command(command, source: nil) command = format_command(command) logger.trace { "Sending Command #{command} to #{name}" } - $events.send_command(self, command) + if source + Events.publisher.post(Events::ItemEventFactory.create_command_event(name, command, source.to_s)) + else + $events.send_command(self, command) + end Proxy.new(self) end alias_method :command!, :command diff --git a/lib/openhab/core/items/group_item.rb b/lib/openhab/core/items/group_item.rb index b9bdc07672..71372df9ef 100644 --- a/lib/openhab/core/items/group_item.rb +++ b/lib/openhab/core/items/group_item.rb @@ -131,10 +131,10 @@ def inspect # Override because we want to send them to the base item if possible %i[command update].each do |method| - define_method(method) do |command| - return base_item.__send__(method, command) if base_item + define_method(method) do |command, **kwargs| + return base_item.__send__(method, command, **kwargs) if base_item - super(command) + super(command, **kwargs) end end diff --git a/lib/openhab/dsl/items/ensure.rb b/lib/openhab/dsl/items/ensure.rb index b28eded007..41668050af 100644 --- a/lib/openhab/dsl/items/ensure.rb +++ b/lib/openhab/dsl/items/ensure.rb @@ -35,10 +35,12 @@ module Item # check if this item is in the command's state before actually # sending the command %i[command update].each do |ensured_method| - # def command(state) + # rubocop:disable Style/IfUnlessModifier + + # def command(state, **kwargs) # # immediately send the command if it's a command, but not a state (like REFRESH) - # return super(state) if state.is_a?(Command) && !state.is_a?(State) - # return super(state) unless Thread.current[:openhab_ensure_states] + # return super(state, **kwargs) if state.is_a?(Command) && !state.is_a?(State) + # return super(state, **kwargs) unless Thread.current[:openhab_ensure_states] # # formatted_state = format_command(state) # logger.trace do @@ -46,13 +48,13 @@ module Item # end # return if raw_state == formatted_state # - # super(formatted_state) + # super(formatted_state, **kwargs) # end class_eval <<~RUBY, __FILE__, __LINE__ + 1 # rubocop:disable Style/DocumentDynamicEvalDefinition - def #{ensured_method}(state) + def #{ensured_method}(state, **kwargs) # immediately send the command if it's a command, but not a state (like REFRESH) - #{"return super(state) if state.is_a?(Command) && !state.is_a?(State)" if ensured_method == :command} - return super(state) unless Thread.current[:openhab_ensure_states] + #{"return super(state, *args) if state.is_a?(Command) && !state.is_a?(State)" if ensured_method == :command} + return super(state, **kwargs) unless Thread.current[:openhab_ensure_states] formatted_state = format_#{ensured_method}(state) logger.trace do @@ -60,9 +62,10 @@ def #{ensured_method}(state) end return if raw_state.as(formatted_state.class) == formatted_state - super(formatted_state) + super(formatted_state, **kwargs) end RUBY + # rubocop:enable Style/IfUnlessModifier end end diff --git a/lib/openhab/dsl/items/timed_command.rb b/lib/openhab/dsl/items/timed_command.rb index 4c034e569c..20c7b0f3dd 100644 --- a/lib/openhab/dsl/items/timed_command.rb +++ b/lib/openhab/dsl/items/timed_command.rb @@ -149,9 +149,9 @@ class << self # end # end # - def command(command, for: nil, on_expire: nil, only_when_ensured: false, &block) + def command(command, for: nil, on_expire: nil, only_when_ensured: false, **kwargs, &block) duration = binding.local_variable_get(:for) - return super(command) unless duration + return super(command, **kwargs) unless duration on_expire = block if block @@ -159,10 +159,10 @@ def command(command, for: nil, on_expire: nil, only_when_ensured: false, &block) on_expire ||= default_on_expire(command) if only_when_ensured DSL.ensure_states do - create_timed_command(command, duration: duration, on_expire: on_expire) if super(command) + create_timed_command(command, duration: duration, on_expire: on_expire) if super(command, **kwargs) end else - super(command) + super(command, **kwargs) create_timed_command(command, duration: duration, on_expire: on_expire) end end @@ -185,7 +185,7 @@ def command(command, for: nil, on_expire: nil, only_when_ensured: false, &block) timed_command_details.timer.reschedule(duration) # disable the cancel rule while we send the new command DSL.rules[timed_command_details.rule_uid].disable - super(command) # This returns nil when "ensured" + super(command, **kwargs) # This returns nil when "ensured" DSL.rules[timed_command_details.rule_uid].enable timed_command_details end diff --git a/spec/openhab/core/events/item_command_event_spec.rb b/spec/openhab/core/events/item_command_event_spec.rb index b8fa908325..cedf1f6e79 100644 --- a/spec/openhab/core/events/item_command_event_spec.rb +++ b/spec/openhab/core/events/item_command_event_spec.rb @@ -1,6 +1,14 @@ # frozen_string_literal: true RSpec.describe OpenHAB::Core::Events::ItemCommandEvent do + it "is inspectable" do + event = OpenHAB::Core::Events::ItemEventFactory.create_command_event("item", REFRESH, nil) + expect(event.inspect).to eql "#" + + event = OpenHAB::Core::Events::ItemEventFactory.create_command_event("item", REFRESH, "source") + expect(event.inspect).to eql '#' + end + describe "predicates" do # rubocop:disable RSpec/EmptyExampleGroup examples are dynamically generated before do stub_const("PREDICATES", @@ -24,7 +32,7 @@ def self.test_command_predicate(command) it "has a predicate for #{command}" do predicate_method = PREDICATES.find { |pr| pr.to_s[0..3].delete("?").upcase == command.to_s[0..3] } - event = org.openhab.core.items.events.ItemEventFactory.create_command_event("item", command, nil) + event = OpenHAB::Core::Events::ItemEventFactory.create_command_event("item", command, nil) expect(event.send(predicate_method)).to be true other_predicates = PREDICATES - [predicate_method] diff --git a/spec/openhab/core/events/item_state_changed_event_spec.rb b/spec/openhab/core/events/item_state_changed_event_spec.rb index 9c560d5a01..d90e3a3686 100644 --- a/spec/openhab/core/events/item_state_changed_event_spec.rb +++ b/spec/openhab/core/events/item_state_changed_event_spec.rb @@ -1,8 +1,13 @@ # frozen_string_literal: true RSpec.describe OpenHAB::Core::Events::ItemStateChangedEvent do + it "is inspectable" do + event = OpenHAB::Core::Events::ItemEventFactory.create_state_changed_event("item", NULL, ON) + expect(event.inspect).to eql "#" + end + it "has proper predicates for an ON => NULL event" do - event = org.openhab.core.items.events.ItemEventFactory.create_state_changed_event("item", NULL, ON) + event = OpenHAB::Core::Events::ItemEventFactory.create_state_changed_event("item", NULL, ON) expect(event).to be_null expect(event).not_to be_undef @@ -15,7 +20,7 @@ end it "has proper predicates for an ON => UNDEF event" do - event = org.openhab.core.items.events.ItemEventFactory.create_state_changed_event("item", UNDEF, ON) + event = OpenHAB::Core::Events::ItemEventFactory.create_state_changed_event("item", UNDEF, ON) expect(event).not_to be_null expect(event).to be_undef @@ -28,7 +33,7 @@ end it "has proper predicates for a NULL => ON event" do - event = org.openhab.core.items.events.ItemEventFactory.create_state_changed_event("item", ON, NULL) + event = OpenHAB::Core::Events::ItemEventFactory.create_state_changed_event("item", ON, NULL) expect(event).not_to be_null expect(event).not_to be_undef @@ -41,7 +46,7 @@ end it "has proper predicates for an UNDEF => ON event" do - event = org.openhab.core.items.events.ItemEventFactory.create_state_changed_event("item", ON, UNDEF) + event = OpenHAB::Core::Events::ItemEventFactory.create_state_changed_event("item", ON, UNDEF) expect(event).not_to be_null expect(event).not_to be_undef diff --git a/spec/openhab/core/events/item_state_event_spec.rb b/spec/openhab/core/events/item_state_event_spec.rb index 6e1f5a1614..a2bfede786 100644 --- a/spec/openhab/core/events/item_state_event_spec.rb +++ b/spec/openhab/core/events/item_state_event_spec.rb @@ -1,8 +1,16 @@ # frozen_string_literal: true RSpec.describe OpenHAB::Core::Events::ItemStateEvent do + it "is inspectable" do + event = OpenHAB::Core::Events::ItemEventFactory.create_state_event("item", NULL, nil) + expect(event.inspect).to eql "#" + + event = OpenHAB::Core::Events::ItemEventFactory.create_state_event("item", NULL, "source") + expect(event.inspect).to eql '#' + end + it "has proper predicates for a NULL event" do - event = org.openhab.core.items.events.ItemEventFactory.create_state_event("item", NULL) + event = OpenHAB::Core::Events::ItemEventFactory.create_state_event("item", NULL) expect(event).to be_null expect(event).not_to be_undef @@ -11,7 +19,7 @@ end it "has proper predicates for an UNDEF event" do - event = org.openhab.core.items.events.ItemEventFactory.create_state_event("item", UNDEF) + event = OpenHAB::Core::Events::ItemEventFactory.create_state_event("item", UNDEF) expect(event).not_to be_null expect(event).to be_undef @@ -20,7 +28,7 @@ end it "has proper predicates for a ON event" do - event = org.openhab.core.items.events.ItemEventFactory.create_state_event("item", ON) + event = OpenHAB::Core::Events::ItemEventFactory.create_state_event("item", ON) expect(event).not_to be_null expect(event).not_to be_undef diff --git a/spec/openhab/core/events/item_state_updated_event_spec.rb b/spec/openhab/core/events/item_state_updated_event_spec.rb index b184836697..3a0a5807b4 100644 --- a/spec/openhab/core/events/item_state_updated_event_spec.rb +++ b/spec/openhab/core/events/item_state_updated_event_spec.rb @@ -2,8 +2,18 @@ if OpenHAB::Core::Events.const_defined?(:ItemStateUpdatedEvent) # @deprecated OH3.4 - remove if RSpec.describe OpenHAB::Core::Events::ItemStateUpdatedEvent do + it "is inspectable" do + event = OpenHAB::Core::Events::ItemEventFactory.create_state_updated_event("item", NULL, nil) + expect(event.inspect).to eql "#" + + event = OpenHAB::Core::Events::ItemEventFactory.create_state_updated_event("item", NULL, "source") + expect(event.inspect).to eql( + '#' + ) + end + it "has proper predicates for a NULL event" do - event = org.openhab.core.items.events.ItemEventFactory.create_state_updated_event("item", NULL) + event = OpenHAB::Core::Events::ItemEventFactory.create_state_updated_event("item", NULL) expect(event).to be_null expect(event).not_to be_undef @@ -12,7 +22,7 @@ end it "has proper predicates for an UNDEF event" do - event = org.openhab.core.items.events.ItemEventFactory.create_state_updated_event("item", UNDEF) + event = OpenHAB::Core::Events::ItemEventFactory.create_state_updated_event("item", UNDEF) expect(event).not_to be_null expect(event).to be_undef @@ -21,7 +31,7 @@ end it "has proper predicates for an ON event" do - event = org.openhab.core.items.events.ItemEventFactory.create_state_updated_event("item", ON) + event = OpenHAB::Core::Events::ItemEventFactory.create_state_updated_event("item", ON) expect(event).not_to be_null expect(event).not_to be_undef diff --git a/spec/openhab/core/items/item_spec.rb b/spec/openhab/core/items/item_spec.rb index 1ae912e019..94ebd4f5d4 100644 --- a/spec/openhab/core/items/item_spec.rb +++ b/spec/openhab/core/items/item_spec.rb @@ -131,6 +131,24 @@ it "returns `self` (wrapped in a proxy)" do expect(LightSwitch.on).to be LightSwitch end + + it "accepts a source" do + items.build { switch_item LightSwitch2 } + source = nil + rule do + received_command LightSwitch2 + run do |event| + source = event.source + end + end + LightSwitch2.command(ON, source: "one") + expect(source).to eql "one" + # command aliases work + LightSwitch2.on(source: "two") + expect(source).to eql "two" + LightSwitch2.refresh(source: "three") + expect(source).to eql "three" + end end describe "#update" do