diff --git a/CHANGELOG.md b/CHANGELOG.md index a3abea32f..47d578f25 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -181,11 +181,9 @@ here is a non-exhaustive list of significant departures from the original gem: * {OpenHAB::DSL::Rules::BuilderDSL#on_load #on_load} supports delay * Various Ephemeris methods on {ZonedDateTime}. * {OpenHAB::DSL::Rules::BuilderDSL#dependencies} Rule dependencies -* A set of debounce/throttle guards for file-based rules: {OpenHAB::DSL::Rules::BuilderDSL#debounce_for debounce_for}, - {OpenHAB::DSL::Rules::BuilderDSL#throttle_for throttle_for}, and - {OpenHAB::DSL::Rules::BuilderDSL#only_every only_every} -* And for UI rules: {OpenHAB::DSL.debounce_for debounce_for}, {OpenHAB::DSL.throttle_for throttle_for}, - {OpenHAB::DSL.only_every only_every} +* A set of debounce/throttle guards for file-based rules: {OpenHAB::DSL::Rules::BuilderDSL#debounce_for debounce_for}, {OpenHAB::DSL::Rules::BuilderDSL#throttle_for throttle_for}, and {OpenHAB::DSL::Rules::BuilderDSL#only_every only_every} +* And for UI rules: {OpenHAB::DSL.debounce_for debounce_for}, {OpenHAB::DSL.throttle_for throttle_for}, {OpenHAB::DSL.only_every only_every} +* Explicitly document modifying item labels and categories (where possible), and notify openHAB of the change ### Bug Fixes diff --git a/lib/openhab/core/items/generic_item.rb b/lib/openhab/core/items/generic_item.rb index 3863eefa9..4add06f3e 100644 --- a/lib/openhab/core/items/generic_item.rb +++ b/lib/openhab/core/items/generic_item.rb @@ -166,6 +166,72 @@ def format_type(type) type.to_s end + + # + # Defers notifying openHAB of modifications to multiple attributes until the block is complete. + # + # @param force [true, false] force allowing modifications to file-based items. + # Normally a FrozenError is raised when attempting to modify file-based items, since + # they will then be out-of-sync with the definition on disk. Advanced users may do this + # knowingly and intentionally though, so an escape hatch is provided to allow runtime + # modifications. + # @yield + # @return [Object] the block's return value + # + # @example Modify label and tags for an item + # MySwitch.modify do + # MySwitch.label = "New Label" + # MySwitch.tag = [:labeled] + # end + # + def modify(force: false) + raise ArgumentError, "you must pass a block to modify" unless block_given? + return yield if instance_variable_defined?(:@modifying) && @modifying + + begin + provider = self.provider + if provider && !provider.is_a?(org.openhab.core.common.registry.ManagedProvider) + provider = nil + raise FrozenError, "Cannot modify item #{name} from provider #{provider.inspect}." unless force + + logger.debug("Forcing modifications to non-managed item #{name}") + end + @modified = false + @modifying = true + + r = yield + + provider&.update(self) if @modified + r + ensure + @modifying = false + end + end + + # @!attribute [rw] label + # The item's descriptive label. + # @return [String] + def label=(value) + modify do + next if label == value + + @modified = true + set_label(value) + end + end + + # @!attribute [rw] category + # The item's category. + # @return [String] + def category=(value) + modify do + value = value&.to_s + next if category == value + + @modified = true + set_category(value) + end + end end end end diff --git a/lib/openhab/core/items/item.rb b/lib/openhab/core/items/item.rb index 9803eb2d2..0c1971e59 100644 --- a/lib/openhab/core/items/item.rb +++ b/lib/openhab/core/items/item.rb @@ -24,10 +24,6 @@ def ===(other) # The item's name. # @return [String] - # @!attribute [r] label - # The item's descriptive label. - # @return [String, nil] - # @!attribute [r] accepted_command_types # @return [Array] An array of {Command}s that can be sent as commands to this item @@ -35,7 +31,7 @@ def ===(other) # @return [Array] An array of {State}s that can be sent as commands to this item # - # The item's {#label} if one is defined, otherwise it's {#name}. + # The item's {GenericItem#label label} if one is defined, otherwise its {#name}. # # @return [String] # @@ -206,6 +202,11 @@ def inspect "#{s}>" end + # @return [org.openhab.core.common.registry.Provider] + def provider + Provider.registry.provider_for(self) + end + private # Allows sub-classes to append additional details to the type in an inspect string diff --git a/lib/openhab/core/registry.rb b/lib/openhab/core/registry.rb index b6cb835fb..bc8778a4b 100644 --- a/lib/openhab/core/registry.rb +++ b/lib/openhab/core/registry.rb @@ -19,7 +19,11 @@ class Registry # def provider_for(key) elementReadLock.lock - return nil unless (element = identifierToElement[key]) + if key.is_a?(org.openhab.core.common.registry.Identifiable) + element = key + else + return nil unless (element = identifierToElement[key]) + end elementToProvider[element] ensure diff --git a/spec/openhab/core/items/item_spec.rb b/spec/openhab/core/items/item_spec.rb index de396ec43..9ec1f8b1b 100644 --- a/spec/openhab/core/items/item_spec.rb +++ b/spec/openhab/core/items/item_spec.rb @@ -164,6 +164,50 @@ expect(Switch1).not_to eq Switch3 end + describe "#label=" do + it "calls update on the provider" do + expect(LightSwitch.provider).to receive(:update) + LightSwitch.label = "Light Switch" + end + + it "doesn't call update if no change was made" do + LightSwitch.label = "Light Switch" + expect(LightSwitch.provider).not_to receive(:update) + LightSwitch.label = "Light Switch" + end + + it "raises an error if the item's provider doesn't support update" do + expect(LightSwitch.provider).not_to receive(:update) + allow(LightSwitch).to receive(:provider).and_return(Object.new) + expect { LightSwitch.label = "Light Switch" }.to raise_error(FrozenError) + end + + it "ignores provider check if the item doesn't yet have a provider" do + expect(LightSwitch.provider).not_to receive(:update) + allow(LightSwitch).to receive(:provider).and_return(nil) + LightSwitch.label = "Light Switch" + expect(LightSwitch.label).to eql "Light Switch" + end + end + + describe "#modify" do + it "batches multiple provider update calls" do + expect(LightSwitch.provider).to receive(:update).once + LightSwitch.modify do + LightSwitch.label = "Light Switch 1" + LightSwitch.label = "Light Switch 2" + end + end + + it "doesn't call update if the item's provider doesn't support it and we're forced" do + expect(LightSwitch.provider).not_to receive(:update) + allow(LightSwitch).to receive(:provider).and_return(nil) + LightSwitch.modify(force: true) do + LightSwitch.label = "Light Switch" + end + end + end + describe "entity lookup" do it "doesn't confuse a method call with an item" do items.build { group_item "gGroup" } diff --git a/spec/openhab/core/items/registry_spec.rb b/spec/openhab/core/items/registry_spec.rb index 73ad39d0c..0d30c8f73 100644 --- a/spec/openhab/core/items/registry_spec.rb +++ b/spec/openhab/core/items/registry_spec.rb @@ -23,4 +23,10 @@ expect(items).not_to include("SimmerTest") expect(items["SimmerTest"]).to be_nil end + + describe "#provider" do + it "works" do + expect(SwitchTwo.provider).to be_a OpenHAB::Core::Items::Provider + end + end end