Skip to content

Commit

Permalink
Allow sending source with commands (#344)
Browse files Browse the repository at this point in the history
And document the source field on events, and add several inspect methods
to more easily debug events with sources.

Signed-off-by: Cody Cutrer <[email protected]>
  • Loading branch information
ccutrer authored Oct 2, 2024
1 parent 91e705b commit 945d1ad
Show file tree
Hide file tree
Showing 16 changed files with 148 additions and 31 deletions.
8 changes: 8 additions & 0 deletions lib/openhab/core/events.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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
11 changes: 9 additions & 2 deletions lib/openhab/core/events/abstract_event.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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 = "#<OpenHAB::Core::Events::#{self.class.simple_name} topic=#{topic} payload=#{payload.inspect}"
s += " source=#{source.inspect}" if source
"#{s}>"
end
end
end
end
Expand Down
7 changes: 7 additions & 0 deletions lib/openhab/core/events/channel_triggered_event.rb
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,13 @@ class ChannelTriggeredEvent < AbstractEvent

# @!attribute [r] event
# @return [String] The event data

# @return [String]
def inspect
s = "#<OpenHAB::Core::Events::ChannelTriggeredEvent channel=#{channel} event=#{event.inspect}"
s += " source=#{source.inspect}" if source
"#{s}>"
end
end
end
end
Expand Down
7 changes: 7 additions & 0 deletions lib/openhab/core/events/item_command_event.rb
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,13 @@ class ItemCommandEvent < ItemEvent
# @!method previous?
# Check if {#command} is {PREVIOUS}
# @return [true, false]

# @return [String]
def inspect
s = "#<OpenHAB::Core::Events::ItemCommandEvent item=#{itemName} command=#{command.inspect}"
s += " source=#{source.inspect}" if source
"#{s}>"
end
end
end
end
Expand Down
8 changes: 8 additions & 0 deletions lib/openhab/core/events/item_state_changed_event.rb
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,14 @@ def was?
def was
old_item_state if was?
end

# @return [String]
def inspect
s = "#<OpenHAB::Core::Events::ItemStateChangedEvent item=#{item_name} " \
"state=#{item_state.inspect} was=#{old_item_state.inspect}"
s += " source=#{source.inspect}" if source
"#{s}>"
end
end
end
end
Expand Down
7 changes: 7 additions & 0 deletions lib/openhab/core/events/item_state_event.rb
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,13 @@ def state?
def state
item_state if state?
end

# @return [String]
def inspect
s = "#<OpenHAB::Core::Events::#{self.class.simple_name} item=#{item_name} state=#{item_state.inspect}"
s += " source=#{source.inspect}" if source
"#{s}>"
end
end

# {AbstractEvent} sent when an item's state is updated (regardless of if it changed or not).
Expand Down
16 changes: 16 additions & 0 deletions lib/openhab/core/events/thing_status_info_event.rb
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,14 @@ class ThingStatusInfoChangedEvent < AbstractEvent
def thing
EntityLookup.lookup_thing(uid)
end

# @return [String]
def inspect
s = "#<OpenHAB::Core::Events::ThingStatusInfoChangedEvent thing=#{uid} " \
"status=#{status.inspect} was=#{was.inspect}"
s += " source=#{source.inspect}" if source
"#{s}>"
end
end

# The {AbstractEvent} sent when a {Things::Thing}'s status is set.
Expand All @@ -49,6 +57,14 @@ class ThingStatusInfoEvent < AbstractEvent
def thing
EntityLookup.lookup_thing(uid)
end

# @return [String]
def inspect
s = "#<OpenHAB::Core::Events::ThingStatusInfoEvent thing=#{uid} " \
"status=#{status.inspect}"
s += " source=#{source.inspect}" if source
"#{s}>"
end
end
end
end
Expand Down
9 changes: 7 additions & 2 deletions lib/openhab/core/items/generic_item.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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.
#
Expand All @@ -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
Expand Down
6 changes: 3 additions & 3 deletions lib/openhab/core/items/group_item.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
19 changes: 11 additions & 8 deletions lib/openhab/dsl/items/ensure.rb
Original file line number Diff line number Diff line change
Expand Up @@ -35,34 +35,37 @@ 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
# "#{name} ensure #{state}, format_command: #{formatted_state}, current state: #{raw_state}"
# 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, **kwargs) 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
"\#{name} ensure \#{state}, format_#{ensured_method}: \#{formatted_state}, current state: \#{raw_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

Expand Down
10 changes: 5 additions & 5 deletions lib/openhab/dsl/items/timed_command.rb
Original file line number Diff line number Diff line change
Expand Up @@ -149,20 +149,20 @@ 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

create_ensured_timed_command = proc do
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
Expand All @@ -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
Expand Down
10 changes: 9 additions & 1 deletion spec/openhab/core/events/item_command_event_spec.rb
Original file line number Diff line number Diff line change
@@ -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 "#<OpenHAB::Core::Events::ItemCommandEvent item=item command=REFRESH>"

event = OpenHAB::Core::Events::ItemEventFactory.create_command_event("item", REFRESH, "source")
expect(event.inspect).to eql '#<OpenHAB::Core::Events::ItemCommandEvent item=item command=REFRESH source="source">'
end

describe "predicates" do # rubocop:disable RSpec/EmptyExampleGroup examples are dynamically generated
before do
stub_const("PREDICATES",
Expand All @@ -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]
Expand Down
13 changes: 9 additions & 4 deletions spec/openhab/core/events/item_state_changed_event_spec.rb
Original file line number Diff line number Diff line change
@@ -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 "#<OpenHAB::Core::Events::ItemStateChangedEvent item=item state=NULL was=ON>"
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
Expand All @@ -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
Expand All @@ -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
Expand All @@ -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
Expand Down
14 changes: 11 additions & 3 deletions spec/openhab/core/events/item_state_event_spec.rb
Original file line number Diff line number Diff line change
@@ -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 "#<OpenHAB::Core::Events::ItemStateEvent item=item state=NULL>"

event = OpenHAB::Core::Events::ItemEventFactory.create_state_event("item", NULL, "source")
expect(event.inspect).to eql '#<OpenHAB::Core::Events::ItemStateEvent item=item state=NULL source="source">'
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
Expand All @@ -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
Expand All @@ -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
Expand Down
16 changes: 13 additions & 3 deletions spec/openhab/core/events/item_state_updated_event_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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 "#<OpenHAB::Core::Events::ItemStateUpdatedEvent item=item state=NULL>"

event = OpenHAB::Core::Events::ItemEventFactory.create_state_updated_event("item", NULL, "source")
expect(event.inspect).to eql(
'#<OpenHAB::Core::Events::ItemStateUpdatedEvent item=item state=NULL source="source">'
)
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
Expand All @@ -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
Expand All @@ -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
Expand Down
Loading

0 comments on commit 945d1ad

Please sign in to comment.