Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
93 changes: 87 additions & 6 deletions lib/openhab/core/items/item.rb
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,90 @@ def display_state
alias_method :transformed_state, :display_state
alias_method :formatted_state, :display_state

#
# Check if the item's state has changed within a certain duration (inclusive).
#
# Note: This method uses the Item's {#last_state_change} timestamp, which is an internal
# timestamp maintained within the Item object, independent of any persistence data.
#
# @param duration [CoreExt::Java::TemporalAmount] the duration to check against
# @return [true, false] true if last_state_change >= duration.ago, false otherwise
# @raise [ArgumentError] if the duration is not a TemporalAmount
#
# @example
# # Check if the item's state has changed within the last 5 minutes
# MyLight.state_changed_within?(5.minutes)
#
def state_changed_within?(duration)
raise ArgumentError, "duration must be a TemporalAmount" unless duration.is_a?(CoreExt::Java::TemporalAmount)
Comment thread
jimtng marked this conversation as resolved.

changed_at = last_state_change
return false if changed_at.nil?

changed_at >= duration.ago
end

#
# Check if the item's state has changed since a certain time (inclusive).
#
# Note: This method uses the Item's {#last_state_change} timestamp, which is an internal
# timestamp maintained within the Item object, independent of any persistence data.
# This is different from {Persistence#changed_since?}, which is based on Persistence data.
#
# @param time [ZonedDateTime, Time, #to_zoned_date_time] the time to check against
# @return [true, false] true if last_state_change >= time, false otherwise
#
# @example
# # Check if the item's state has changed since 1 hour ago
# MyLight.state_changed_since?(1.hour.ago)
#
# @example
# # Check if the item's state has changed since sunset
# MyLight.state_changed_since?(Sunset_Time.state) # Sunset_Time is a DateTimeItem
#
def state_changed_since?(time)
changed_at = last_state_change
return false if changed_at.nil?

changed_at >= time
end

#
# Check if the item's state has been updated within a certain duration (inclusive).
#
# Note: This method uses the Item's {#last_state_update} timestamp, which is an internal
# timestamp maintained within the Item object, independent of any persistence data.
#
# @param duration [CoreExt::Java::TemporalAmount] the duration to check against
# @return [true, false] true if last_state_update >= duration.ago, false otherwise
# @raise [ArgumentError] if the duration is not a TemporalAmount
#
def state_updated_within?(duration)
raise ArgumentError, "duration must be a TemporalAmount" unless duration.is_a?(CoreExt::Java::TemporalAmount)

updated_at = last_state_update
return false if updated_at.nil?
Comment thread
jimtng marked this conversation as resolved.

updated_at >= duration.ago
end

#
# Check if the item's state has been updated since a certain time (inclusive).
#
# Note: This method uses the Item's {#last_state_update} timestamp, which is an internal
# timestamp maintained within the Item object, independent of any persistence data.
# This is different from {Persistence#updated_since?}, which is based on Persistence data.
#
# @param time [ZonedDateTime, Time, #to_zoned_date_time] the time to check against
# @return [true, false] true if last_state_update >= time, false otherwise
#
def state_updated_since?(time)
updated_at = last_state_update
return false if updated_at.nil?

updated_at >= time
end

#
# Send a command to this item
#
Expand Down Expand Up @@ -582,12 +666,9 @@ def unlink(channel)
# @return [String]
def inspect
s = "#<OpenHAB::Core::Items::#{type}Item#{type_details} #{name} #{label.inspect} state=#{raw_state.inspect}"
# @deprecated OH 4.3 Remove if guard when dropping support for OH 4.3
if respond_to?(:last_state)
s += " last_state=#{last_state.inspect}" if last_state
s += " last_state_update=#{last_state_update}" if last_state_update
s += " last_state_change=#{last_state_change}" if last_state_change
end
s += " last_state=#{last_state.inspect}" if last_state
s += " last_state_update=#{last_state_update}" if last_state_update
s += " last_state_change=#{last_state_change}" if last_state_change
s += " category=#{category.inspect}" if category
s += " tags=#{tags.to_a.inspect}" unless tags.empty?
s += " groups=#{group_names}" unless group_names.empty?
Expand Down
4 changes: 2 additions & 2 deletions lib/openhab/dsl.rb
Original file line number Diff line number Diff line change
Expand Up @@ -737,8 +737,8 @@ def ensure_states
# @example
# persistence(:influxdb) do
# Item1.persist
# Item1.changed_since(1.hour)
# Item1.average_since(12.hours)
# Item1.changed_since?(1.hour.ago)
# Item1.average_since(12.hours.ago)
# end
#
# @param [Object] service Persistence service either as a String or a Symbol
Expand Down
94 changes: 94 additions & 0 deletions spec/openhab/core/items/item_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -657,4 +657,98 @@
end.to raise_error(NoMethodError)
end
end

context "with time predicates" do
describe "#state_changed_within?" do
it "raises if duration isn't a TemporalAmount" do
expect do
LightSwitch.state_changed_within?(1)
end.to raise_error(ArgumentError, /duration must be a TemporalAmount/)
end

Comment thread
jimtng marked this conversation as resolved.
it "returns true when last_state_change is within the duration" do
allow(LightSwitch).to receive(:last_state_change).and_return(4.minutes.ago)
expect(LightSwitch.state_changed_within?(5.minutes)).to be true
end

it "returns false when last_state_change is older than the duration" do
allow(LightSwitch).to receive(:last_state_change).and_return(6.minutes.ago)
expect(LightSwitch.state_changed_within?(5.minutes)).to be false
end

it "returns false if last_state_change is nil" do
allow(LightSwitch).to receive(:last_state_change).and_return(nil)
expect(LightSwitch.state_changed_within?(5.minutes)).to be false
end
end

describe "#state_updated_within?" do
it "raises if duration isn't a TemporalAmount" do
expect do
LightSwitch.state_updated_within?(1)
end.to raise_error(ArgumentError, /duration must be a TemporalAmount/)
end

it "returns true when last_state_update is within the duration" do
allow(LightSwitch).to receive(:last_state_update).and_return(4.minutes.ago)
expect(LightSwitch.state_updated_within?(5.minutes)).to be true
end

it "returns false when last_state_update is older than the duration" do
allow(LightSwitch).to receive(:last_state_update).and_return(6.minutes.ago)
expect(LightSwitch.state_updated_within?(5.minutes)).to be false
end

it "returns false if last_state_update is nil" do
allow(LightSwitch).to receive(:last_state_update).and_return(nil)
expect(LightSwitch.state_updated_within?(5.minutes)).to be false
end
end

describe "#state_changed_since?" do
it "returns true when last_state_change is on or after the given time" do
allow(LightSwitch).to receive(:last_state_change).and_return(4.minutes.ago)
expect(LightSwitch.state_changed_since?(5.minutes.ago)).to be true
end

it "returns false when last_state_change is before the given time" do
allow(LightSwitch).to receive(:last_state_change).and_return(6.minutes.ago)
expect(LightSwitch.state_changed_since?(5.minutes.ago)).to be false
end

it "accepts objects that respond to #to_zoned_date_time" do
allow(LightSwitch).to receive(:last_state_change).and_return(4.minutes.ago)
time = 5.minutes.ago.to_time
expect(LightSwitch.state_changed_since?(time)).to be true
end

it "returns false if last_state_change is nil" do
allow(LightSwitch).to receive(:last_state_change).and_return(nil)
expect(LightSwitch.state_changed_since?(5.minutes.ago)).to be false
end
end

describe "#state_updated_since?" do
it "returns true when last_state_update is on or after the given time" do
allow(LightSwitch).to receive(:last_state_update).and_return(4.minutes.ago)
expect(LightSwitch.state_updated_since?(5.minutes.ago)).to be true
end

it "returns false when last_state_update is before the given time" do
allow(LightSwitch).to receive(:last_state_update).and_return(6.minutes.ago)
expect(LightSwitch.state_updated_since?(5.minutes.ago)).to be false
end

it "accepts objects that respond to #to_zoned_date_time" do
allow(LightSwitch).to receive(:last_state_update).and_return(4.minutes.ago)
time = 5.minutes.ago.to_time
expect(LightSwitch.state_updated_since?(time)).to be true
end

it "returns false if last_state_update is nil" do
allow(LightSwitch).to receive(:last_state_update).and_return(nil)
expect(LightSwitch.state_updated_since?(5.minutes.ago)).to be false
end
end
end
end