diff --git a/lib/openhab/dsl/items/builder.rb b/lib/openhab/dsl/items/builder.rb index c50703ff4..d487e5c86 100644 --- a/lib/openhab/dsl/items/builder.rb +++ b/lib/openhab/dsl/items/builder.rb @@ -222,6 +222,24 @@ class ItemBuilder # # @return [String, nil] attr_accessor :format + # The valid range for a number item + # @return [Range, nil] + attr_accessor :range + # The step size for a number item + # @return [Number, nil] + attr_accessor :step + # If the item is read-only, and does not accept commands + # @return [true, false, nil] + attr_accessor :read_only + alias_method :read_only?, :read_only + # A list of valid commands + # If a hash, keys are commands, and values are labels + # @return [Hash, Array, nil] + attr_accessor :command_options + # A list of valid states + # If a hash, keys are commands, and values are labels + # @return [Hash, Array, nil] + attr_accessor :state_options # The icon to be associated with the item # @return [Symbol, String, nil] attr_accessor :icon @@ -321,6 +339,11 @@ def initialize(type, dimension: nil, unit: nil, format: nil, + range: nil, + step: nil, + read_only: nil, + command_options: nil, + state_options: nil, icon: nil, group: nil, groups: nil, @@ -358,6 +381,11 @@ def initialize(type, @label = label @dimension = dimension @format = format + @range = range + @step = step + @read_only = read_only + @command_options = command_options + @state_options = state_options self.unit = unit @icon = icon @groups = [] @@ -591,7 +619,35 @@ def build end metadata["autoupdate"] = autoupdate.to_s unless autoupdate.nil? metadata["expire"] = expire if expire - metadata["stateDescription"] = { "pattern" => format } if format + if format || range || step || !read_only.nil? || state_options + sd = {} + sd["pattern"] = format if format + sd["min"] = range.begin&.to_d if range&.begin + sd["max"] = range.end&.to_d if range&.end + sd["step"] = step if step + sd["readOnly"] = read_only unless read_only.nil? + if state_options + sd["options"] = if state_options.respond_to?(:to_hash) + state_options.to_hash.map { |k, v| "#{k}=#{v}" }.join(",") + elsif state_options.respond_to?(:to_ary) + state_options.to_ary.join(",") + else + state_options.to_s + end + end + + metadata["stateDescription"] = sd + end + if command_options + options = if command_options.respond_to?(:to_hash) + command_options.to_hash.map { |k, v| "#{k}=#{v}" }.join(",") + elsif command_options.respond_to?(:to_ary) + command_options.to_ary.join(",") + else + command_options.to_s + end + metadata["commandDescription"] = { "options" => options } + end metadata["unit"] = unit if unit item end diff --git a/spec/openhab/dsl/items/builder_spec.rb b/spec/openhab/dsl/items/builder_spec.rb index 7e8e653d3..375fba312 100644 --- a/spec/openhab/dsl/items/builder_spec.rb +++ b/spec/openhab/dsl/items/builder_spec.rb @@ -366,6 +366,72 @@ def build_and_update(org_config, new_config, item_to_keep: :new_item, &block) expect(MyNumberItem.state_description.pattern).to eql "something %d else" end + it "can set a range on a number item" do + items.build do + number_item "Number1", range: 5..10 + number_item "Number2", range: 2..10, step: 2 + number_item "Number3", range: Range.new(nil, 50) if RUBY_VERSION >= "2.7" + number_item "Number4", range: 50.. + end + + expect(Number1.state_description.minimum.to_i).to be 5 + expect(Number1.state_description.maximum.to_i).to be 10 + expect(Number1.state_description.step).to be_nil + expect(Number2.state_description.minimum.to_i).to be 2 + expect(Number2.state_description.maximum.to_i).to be 10 + expect(Number2.state_description.step.to_i).to be 2 + if RUBY_VERSION >= "2.7" + expect(Number3.state_description.minimum).to be_nil + expect(Number3.state_description.maximum.to_i).to be 50 + end + expect(Number4.state_description.minimum.to_i).to be 50 + expect(Number4.state_description.maximum).to be_nil + end + + it "can set read only" do + items.build do + switch_item "Switch1", read_only: true + switch_item "Switch2", read_only: false + switch_item "Switch3" + end + + expect(Switch1.state_description).to be_read_only + expect(Switch2.state_description).not_to be_read_only + expect(Switch3.state_description).to be_nil + end + + it "can set state options" do + items.build do + string_item "Text1", state_options: %w[LOCKED UNLOCKED] + switch_item "Lock1", state_options: { ON => "LOCKED", OFF => "UNLOCKED" } + end + + expect(Text1.state_description.options.to_h { |o| [o.value, o.label] }).to eql({ + "LOCKED" => nil, + "UNLOCKED" => nil + }) + expect(Lock1.state_description.options.to_h { |o| [o.value, o.label] }).to eql({ + "ON" => "LOCKED", + "OFF" => "UNLOCKED" + }) + end + + it "can set command options" do + items.build do + string_item "Text1", command_options: %w[LOCKED UNLOCKED] + switch_item "Lock1", command_options: { ON => "LOCKED", OFF => "UNLOCKED" } + end + + expect(Text1.command_description.command_options.to_h { |o| [o.command, o.label] }).to eql({ + "LOCKED" => nil, + "UNLOCKED" => nil + }) + expect(Lock1.command_description.command_options.to_h { |o| [o.command, o.label] }).to eql({ + "ON" => "LOCKED", + "OFF" => "UNLOCKED" + }) + end + it "does not overwrite an explicit format with the unit" do items.build do number_item "MyNumberItem", format: "something %d else", unit: "W"