From 1f5597fe2576db049a9bf3161dc982a6ce68de58 Mon Sep 17 00:00:00 2001 From: Cody Cutrer Date: Thu, 10 Oct 2024 13:50:16 -0600 Subject: [PATCH] Add NumberItem#range Returns a Range from and stated description, and applies units if possible Signed-off-by: Cody Cutrer --- lib/openhab/core/items/number_item.rb | 24 +++++++++++++++ spec/openhab/core/items/number_item_spec.rb | 33 +++++++++++++++++++-- 2 files changed, 55 insertions(+), 2 deletions(-) diff --git a/lib/openhab/core/items/number_item.rb b/lib/openhab/core/items/number_item.rb index 2013a77c5a..358830274f 100644 --- a/lib/openhab/core/items/number_item.rb +++ b/lib/openhab/core/items/number_item.rb @@ -53,6 +53,30 @@ def config_eql?(other) super && dimension == other.dimension end + # @!attribute [r] range + # Returns the range of values allowed for this item, as defined by its + # state description. + # + # If this item has a {#unit}, it will be applied to the result, returning + # a range of {QuantityType} instead of BigDecimal. + # @return [Range] + # @note State descriptions can be provided by bindings, defined in + # metadata, or theoretically come from other sources. + def range + return unless (sd = state_description) + + # check if we have a unit, even if the item's metadata doesn't declare + # it properly + unit = self.unit || ((s = state) && s.is_a?(QuantityType) && s.unit) + min = sd.minimum&.to_d + max = sd.maximum&.to_d + return nil unless min || max + + min |= unit if min && unit + max |= unit if max && unit + min..max + end + protected # Adds the unit dimension diff --git a/spec/openhab/core/items/number_item_spec.rb b/spec/openhab/core/items/number_item_spec.rb index 17b14fd032..f010237438 100644 --- a/spec/openhab/core/items/number_item_spec.rb +++ b/spec/openhab/core/items/number_item_spec.rb @@ -7,7 +7,10 @@ items.build do group_item "Numbers" do number_item "NumberOne", state: 0 - number_item "NumberTwo", state: 70 + number_item "NumberTwo", state: 70, format: "%s" + number_item "RangedNumber", range: 50..100 + number_item "UnittedRangedNumber", range: 2700..6000, unit: "K" + number_item "OpenEndedNumber", range: 50.., unit: "°C" end number_item "NumberNull" end @@ -23,7 +26,12 @@ it "works with grep" do items.build { switch_item "Switch1" } - expect(items.grep(NumberItem)).to match_array [NumberOne, NumberTwo, NumberNull] + expect(items.grep(NumberItem)).to match_array [NumberOne, + NumberTwo, + NumberNull, + RangedNumber, + UnittedRangedNumber, + OpenEndedNumber] end describe "#positive?" do @@ -41,4 +49,25 @@ end expect(Feet.state).to eq(6 | "ft") end + + describe "#range?" do + it "returns nil without a meaningful state description" do + expect(NumberOne.range).to be_nil + expect(NumberTwo.range).to be_nil + end + + it "returns a value with a state description" do + expect(RangedNumber.range).to eql 50..100 + end + + it "returns a QuantityType as necessary" do + UnittedRangedNumber.update(3000 | "K") + expect(UnittedRangedNumber.range).to eql(2700 | "K")..(6000 | "K") + end + + it "supports open ended ranges" do + OpenEndedNumber.update(75 | "°C") + expect(OpenEndedNumber.range).to eql(50 | "°C").. + end + end end