openhab-scripting 5.11.0 → 5.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 9e7d078cc73659d0f435950d36438b67759b2e3b5c0abf80f258a36101792a0d
4
- data.tar.gz: 2a76c20015d6320b64a2a9ea79557c7133b1101a8514fabba9241f91b455615d
3
+ metadata.gz: 35d0d28ba4dd272f12cd989761c2d615836d25dbafbc2af2b60692f8a535d5c1
4
+ data.tar.gz: 4d51dd07a43f5ff7a7ad43bc66cbb1d63b320486a82036d1a8cbfb22412a061b
5
5
  SHA512:
6
- metadata.gz: bc54b1fae7be3bee15b1af2c27526cf942a13741404dd1c0e61034b82d2d472703bfb48ef921d25ed608dbe3d9c964abbded85ab1d3adfd855847229c6bf2447
7
- data.tar.gz: c454623c911c90be03464391f885b8ccebfd2789b5e9dbbe32a6f8309fdf7e1f948e2c0730373d383bf7892271ab752bf9d8c2197ed541cff7f33a692ea296ec
6
+ metadata.gz: 76c075f22df43c0ecfe37775e297f496d2f55fe60e6da95bbece311d92fe3e37e50d867a23f6f52c3ebeb690771e7eb5512c357ca3b0745d83d03a8df892cad7
7
+ data.tar.gz: 93eb532732729b91437a669fbbe9b0b024397332faa435c82ebe17d50c4ea4a29576108c819010c0a497a5912d74eaa95e87fc0d98b01f9fa0987fbb52b5fa7b
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ module OpenHAB
4
+ module Core
5
+ module Events
6
+ java_import org.openhab.core.thing.events.ChannelTriggeredEvent
7
+
8
+ #
9
+ # {AbstractEvent} sent when a channel triggers.
10
+ #
11
+ class ChannelTriggeredEvent < AbstractEvent
12
+ extend Forwardable
13
+
14
+ # @!attribute [r] channel_uid
15
+ # @return [Things::ChannelUID] The UID of the {Things::Channel Channel} that triggered this event.
16
+ alias_method :channel_uid, :get_channel
17
+
18
+ # @!attribute [r] channel
19
+ # @return [Things::Channel, nil] The channel that triggered this event.
20
+
21
+ # @!attribute [r] thing
22
+ # @return [Things::Thing, nil] The thing that triggered this event.
23
+ def_delegators :channel_uid, :thing, :channel
24
+
25
+ # @!attribute [r] event
26
+ # @return [String] The event data
27
+ end
28
+ end
29
+ end
30
+ end
@@ -5,17 +5,18 @@ require_relative "item_state_event"
5
5
  module OpenHAB
6
6
  module Core
7
7
  module Events
8
- begin
8
+ # @deprecated OH3.4 if guard only needed in OH 3.4
9
+ if Gem::Version.new(OpenHAB::Core::VERSION) >= Gem::Version.new("4.0.0")
9
10
  java_import org.openhab.core.items.events.ItemStateUpdatedEvent
10
11
 
11
12
  #
12
13
  # {AbstractEvent} sent when an item's state has updated.
13
14
  #
15
+ # @since openHAB 4.0
16
+ #
14
17
  class ItemStateUpdatedEvent < ItemEvent
15
18
  include ItemState
16
19
  end
17
- rescue NameError
18
- # @deprecated OH3.4 OH3 will raise an error ItemStateUpdatedEvent is only in OH4
19
20
  end
20
21
  end
21
22
  end
@@ -102,7 +102,7 @@ module OpenHAB
102
102
  #
103
103
  def dim(amount = 1)
104
104
  target = [state&.-(amount), 0].compact.max
105
- command(target)
105
+ command!(target)
106
106
  target
107
107
  end
108
108
 
@@ -121,7 +121,7 @@ module OpenHAB
121
121
  #
122
122
  def brighten(amount = 1)
123
123
  target = [state&.+(amount), 100].compact.min
124
- command(target)
124
+ command!(target)
125
125
  target
126
126
  end
127
127
 
@@ -133,16 +133,6 @@ module OpenHAB
133
133
  # Send the {DECREASE} command to the item
134
134
  # @return [DimmerItem] `self`
135
135
 
136
- # @!method increase!
137
- # Send the {INCREASE} command to the item, even when
138
- # {OpenHAB::DSL.ensure_states! ensure_states!} is in effect.
139
- # @return [DimmerItem] `self`
140
-
141
- # @!method decrease!
142
- # Send the {DECREASE} command to the item, even when
143
- # {OpenHAB::DSL.ensure_states! ensure_states!} is in effect.
144
- # @return [DimmerItem] `self`
145
-
146
136
  # raw numbers translate directly to PercentType, not a DecimalType
147
137
  # @!visibility private
148
138
  def format_type(command)
@@ -55,7 +55,7 @@ module OpenHAB
55
55
  # Send the {REWIND} command to the item
56
56
  # @return [PlayerItem] `self`
57
57
 
58
- # @!method rewind
58
+ # @!method rewind!
59
59
  # Send the {REWIND} command to the item, even when {OpenHAB::DSL.ensure_states! ensure_states!} is in effect.
60
60
  # @return [PlayerItem] `self`
61
61
 
@@ -72,18 +72,9 @@ module OpenHAB
72
72
  # Send the {NEXT} command to the item
73
73
  # @return [PlayerItem] `self`
74
74
 
75
- # @!method next!
76
- # Send the {NEXT} command to the item, even when {OpenHAB::DSL.ensure_states! ensure_states!} is in effect.
77
- # @return [PlayerItem] `self`
78
-
79
75
  # @!method previous
80
76
  # Send the {PREVIOUS} command to the item
81
77
  # @return [PlayerItem] `self`
82
-
83
- # @!method previous!
84
- # Send the {PREVIOUS} command to the item, even when
85
- # {OpenHAB::DSL.ensure_states! ensure_states!} is in effect.
86
- # @return [PlayerItem] `self`
87
78
  end
88
79
  end
89
80
  end
@@ -57,18 +57,10 @@ module OpenHAB
57
57
  # Send the {STOP} command to the item
58
58
  # @return [RollershutterItem] `self`
59
59
 
60
- # @!method stop!
61
- # Send the {STOP} command to the item, even when {OpenHAB::DSL.ensure_states! ensure_states!} is in effect.
62
- # @return [RollershutterItem] `self`
63
-
64
60
  # @!method move
65
61
  # Send the {MOVE} command to the item
66
62
  # @return [RollershutterItem] `self`
67
63
 
68
- # @!method move!
69
- # Send the {MOVE} command to the item, even when {OpenHAB::DSL.ensure_states! ensure_states!} is in effect.
70
- # @return [RollershutterItem] `self`
71
-
72
64
  # raw numbers translate directly to PercentType, not a DecimalType
73
65
  # @!visibility private
74
66
  def format_type(command)
@@ -50,9 +50,9 @@ module OpenHAB
50
50
  # @return [self]
51
51
  #
52
52
  def toggle
53
- return on unless state?
53
+ return on! unless state?
54
54
 
55
- command(!state)
55
+ command!(!state)
56
56
  end
57
57
 
58
58
  # @!method on?
@@ -60,25 +60,43 @@ module OpenHAB
60
60
  command = Types::COMMAND_ALIASES[value.to_s]
61
61
  next if klass.instance_methods.include?(command)
62
62
 
63
- logger.trace("Defining #{klass}##{command} for #{value}")
64
- klass.class_eval <<~RUBY, __FILE__, __LINE__ + 1
65
- ruby2_keywords def #{command}(*args, &block) # ruby2_keywords def on(*args, &block)
66
- command(#{value}, *args, &block) # command(ON, *args, &block)
67
- end # end
68
- ruby2_keywords def #{command}!(*args, &block) # ruby2_keywords def on!(*args, &block)
69
- command!(#{value}, *args, &block) # command!(ON, *args, &block)
70
- end # end
71
- RUBY
63
+ if value.is_a?(Types::State)
64
+ logger.trace("Defining #{klass}/Enumerable##{command}/#{command}! for #{value}")
72
65
 
73
- logger.trace("Defining Enumerable##{command} for #{value}")
74
- Enumerable.class_eval <<~RUBY, __FILE__, __LINE__ + 1
75
- def #{command} # def on
76
- each(&:#{command}) # each(&:on)
77
- end # end
78
- def #{command}! # def on!
79
- each(&:#{command}!) # each(&:on!)
80
- end # end
81
- RUBY
66
+ klass.class_eval <<~RUBY, __FILE__, __LINE__ + 1
67
+ ruby2_keywords def #{command}(*args, &block) # ruby2_keywords def on(*args, &block)
68
+ command(#{value}, *args, &block) # command(ON, *args, &block)
69
+ end # end
70
+ #
71
+ ruby2_keywords def #{command}!(*args, &block) # ruby2_keywords def on!(*args, &block)
72
+ command!(#{value}, *args, &block) # command!(ON, *args, &block)
73
+ end # end
74
+ RUBY
75
+
76
+ Enumerable.class_eval <<~RUBY, __FILE__, __LINE__ + 1
77
+ def #{command} # def on
78
+ each(&:#{command}) # each(&:on)
79
+ end # end
80
+ #
81
+ def #{command}! # def on!
82
+ each(&:#{command}!) # each(&:on!)
83
+ end # end
84
+ RUBY
85
+ else
86
+ logger.trace("Defining #{klass}/Enumerable##{command} for #{value}")
87
+
88
+ klass.class_eval <<~RUBY, __FILE__, __LINE__ + 1
89
+ ruby2_keywords def #{command}(*args, &block) # ruby2_keywords def refresh(*args, &block)
90
+ command!(#{value}, *args, &block) # command!(REFRESH, *args, &block)
91
+ end # end
92
+ RUBY
93
+
94
+ Enumerable.class_eval <<~RUBY, __FILE__, __LINE__ + 1
95
+ def #{command} # def refresh
96
+ each(&:#{command}) # each(&:refresh)
97
+ end # end
98
+ RUBY
99
+ end
82
100
 
83
101
  logger.trace("Defining ItemCommandEvent##{command}? for #{value}")
84
102
  Events::ItemCommandEvent.class_eval <<~RUBY, __FILE__, __LINE__ + 1
@@ -6,12 +6,14 @@ require_relative "script_handling"
6
6
 
7
7
  module OpenHAB
8
8
  module Core
9
+ # rubocop:disable Naming/MethodName
9
10
  # @!visibility private
10
11
  class ProfileFactory
12
+ include org.openhab.core.config.core.ConfigDescriptionProvider # This needs to be included first
11
13
  include org.openhab.core.thing.profiles.ProfileFactory
14
+ include org.openhab.core.thing.profiles.ProfileTypeProvider
12
15
  include Singleton
13
16
 
14
- # rubocop:disable Naming/MethodName
15
17
  class Profile
16
18
  include org.openhab.core.thing.profiles.StateProfile
17
19
 
@@ -96,10 +98,10 @@ module OpenHAB
96
98
  end
97
99
  end
98
100
  private_constant :Profile
99
- # rubocop:enable Naming/MethodName
100
101
 
101
102
  def initialize
102
103
  @profiles = {}
104
+ @uri_to_uid = {}
103
105
 
104
106
  @registration = OSGi.register_service(self)
105
107
  ScriptHandling.script_unloaded { unregister }
@@ -116,17 +118,56 @@ module OpenHAB
116
118
  end
117
119
 
118
120
  # @!visibility private
119
- def register(uid, block)
120
- @profiles[uid] = [DSL::ThreadLocal.persist, block]
121
+ def register(id, block, label: nil, config_description: nil)
122
+ uid = org.openhab.core.thing.profiles.ProfileTypeUID.new("ruby", id)
123
+ uri = java.net.URI.new("profile", uid.to_s, nil)
124
+ if config_description && config_description.uid != uri
125
+ config_description = org.openhab.core.config.core.ConfigDescriptionBuilder.create(uri)
126
+ .with_parameters(config_description.parameters)
127
+ .with_parameter_groups(config_description.parameter_groups)
128
+ .build
129
+ end
130
+
131
+ @profiles[uid] = {
132
+ thread_locals: DSL::ThreadLocal.persist,
133
+ label: label,
134
+ config_description: config_description,
135
+ block: block
136
+ }
137
+ @uri_to_uid[uri] = uid
121
138
  end
122
139
 
123
- def createProfile(type, callback, context) # rubocop:disable Naming/MethodName
124
- @profiles[type].then { |(thread_locals, block)| Profile.new(callback, context, type, thread_locals, block) }
140
+ # @!visibility private
141
+ def createProfile(uid, callback, context)
142
+ profile = @profiles[uid]
143
+ Profile.new(callback, context, uid, profile[:thread_locals], profile[:block])
125
144
  end
126
145
 
127
- def getSupportedProfileTypeUIDs # rubocop:disable Naming/MethodName
146
+ # @!visibility private
147
+ def getSupportedProfileTypeUIDs
128
148
  @profiles.keys
129
149
  end
150
+
151
+ # @!visibility private
152
+ def getProfileTypes(_locale)
153
+ @profiles.map do |uid, profile|
154
+ next if profile[:label].nil?
155
+
156
+ org.openhab.core.thing.profiles.ProfileTypeBuilder.new_state(uid, "RUBY #{profile[:label]}").build
157
+ end.compact
158
+ end
159
+
160
+ # @!visibility private
161
+ def getConfigDescriptions(_locale)
162
+ @profiles.values.map { |profile| profile[:config_description] if profile[:label] }.compact
163
+ end
164
+
165
+ # @!visibility private
166
+ def getConfigDescription(uri, _locale)
167
+ uid = @uri_to_uid[uri]
168
+ @profiles.dig(uid, :config_description)
169
+ end
130
170
  end
171
+ # rubocop:enable Naming/MethodName
131
172
  end
132
173
  end
@@ -70,13 +70,17 @@ module OpenHAB
70
70
  # text label: "Climate", icon: "if:mdi:home-thermometer-outline" do
71
71
  # frame label: "Main Floor" do
72
72
  # text item: MainFloor_AmbTemp
73
- # switch item: MainFloorThermostat_TargetMode, label: "Mode", mappings: %w[off auto cool heat]
73
+ # # colors are set with a hash, with key being condition, and value being the color
74
+ # switch item: MainFloorThermostat_TargetMode, label: "Mode", mappings: %w[off auto cool heat], label_color: { "==heat" => "red", "" => "black" }
75
+ # # an array of conditions are OR'd together
76
+ # switch item: MainFloorThermostat_TargetMode, label: "Mode", mappings: %w[off auto cool heat], label_color: { ["==heat", "==cool"], => "green" }
74
77
  # setpoint item: MainFloorThermostat_SetPoint, label: "Set Point", visibility: "MainFloorThermostat_TargetMode!=off"
75
78
  # end
76
79
  # frame label: "Basement" do
77
80
  # text item: Basement_AmbTemp
78
81
  # switch item: BasementThermostat_TargetMode, label: "Mode", mappings: { OFF: "off", COOL: "cool", HEAT: "heat" }
79
- # setpoint item: BasementThermostat_SetPoint, label: "Set Point", visibility: "BasementThermostat_TargetMode!=off"
82
+ # # nested arrays are conditions that are AND'd together, instead of OR'd (requires openHAB 4.1)
83
+ # setpoint item: BasementThermostat_SetPoint, label: "Set Point", visibility: [["BasementThermostat_TargetMode!=off", "Vacation_Switch!=OFF"]]
80
84
  # end
81
85
  # end
82
86
  # end
@@ -22,6 +22,16 @@ module OpenHAB
22
22
  EntityLookup.lookup_thing(thing_uid)
23
23
  end
24
24
 
25
+ # @attribute [r] channel
26
+ #
27
+ # Return the channel object for this channel
28
+ #
29
+ # @return [Channel, nil]
30
+ #
31
+ def channel
32
+ thing.channels[self]
33
+ end
34
+
25
35
  #
26
36
  # @attribute [r] item
27
37
  #
@@ -72,6 +72,7 @@ module OpenHAB
72
72
  # Allows indexing by both integer as an array or channel id acting like a hash.
73
73
  # @param [Integer, String, ChannelUID] index
74
74
  # Numeric index, string channel id, or a {ChannelUID} to search for.
75
+ # @return [Channel, nil]
75
76
  def [](index)
76
77
  return @thing.get_channel(index) if index.is_a?(ChannelUID)
77
78
  return @thing.get_channel(index.to_str) if index.respond_to?(:to_str)
@@ -70,7 +70,7 @@ module OpenHAB
70
70
  #
71
71
  # Convert DecimalType to a QuantityType
72
72
  #
73
- # @param [String, javax.measure.units.Unit] other
73
+ # @param [String, javax.measure.Unit] other
74
74
  #
75
75
  # @return [QuantityType] `self` as a {QuantityType} of the supplied Unit
76
76
  #
@@ -106,7 +106,7 @@ module OpenHAB
106
106
  # #
107
107
  # # Convert this {QuantityType} into another unit.
108
108
  # #
109
- # # @param [String, javax.measure.units.Unit] unit
109
+ # # @param [String, javax.measure.Unit] unit
110
110
  # # @return [QuantityType]
111
111
  # #
112
112
  # # @example
@@ -287,7 +287,7 @@ module OpenHAB
287
287
  end
288
288
  end
289
289
 
290
- # if unit is {org.openhab.core.library.unit.Units.ONE}, return a plain
290
+ # if unit is {org.openhab.core.library.unit.Units::ONE}, return a plain
291
291
  # Java BigDecimal
292
292
  # @!visibility private
293
293
  def deunitize
@@ -0,0 +1,157 @@
1
+ # frozen_string_literal: true
2
+
3
+ module OpenHAB
4
+ module DSL
5
+ #
6
+ # Contains the DSL for creating {org.openhab.core.config.core.ConfigDescription} instances.
7
+ #
8
+ module ConfigDescription
9
+ #
10
+ # A ConfigDescriptionBuilder is used to create a {org.openhab.core.config.core.ConfigDescription}
11
+ # instance.
12
+ #
13
+ # @see DSL.config_description config_description
14
+ #
15
+ class Builder
16
+ def initialize
17
+ @parameters = []
18
+ @parameter_groups = []
19
+ @current_group = nil
20
+ end
21
+
22
+ #
23
+ # Create a parameter group.
24
+ #
25
+ # @param [String, Symbol] name The group name. This name will be referred to by {parameter}.
26
+ # @param [String, nil] label The group label
27
+ # @param [String, nil] description The group description
28
+ # @param [Boolean] advanced Whether the group is advanced
29
+ # @param [<Type>] context Context for the group
30
+ #
31
+ # @yield Block executed in the context of this group. Any {parameter} calls within the block will
32
+ # automatically be added to this group, unless it specifies a different group name.
33
+ #
34
+ # @return [void]
35
+ #
36
+ def group(name, label: nil, description: nil, advanced: false, context: nil, &block)
37
+ raise ArgumentError, "Groups cannot be nested" if @current_group
38
+
39
+ name = name.to_s
40
+ @parameter_groups << org.openhab.core.config.core.ConfigDescriptionParameterGroupBuilder
41
+ .create(name)
42
+ .with_label(label)
43
+ .with_description(description)
44
+ .with_advanced(advanced)
45
+ .with_context(context)
46
+ .build
47
+
48
+ @current_group = name
49
+ instance_eval(&block) if block
50
+ ensure
51
+ @current_group = nil
52
+ end
53
+
54
+ #
55
+ # Adds a parameter to the config description.
56
+ #
57
+ # @param [String, Symbol] name Parameter name
58
+ # @param [:text, :integer, :decimal, :boolean] type
59
+ # Parameter type. See {org.openhab.core.config.core.ConfigDescriptionParameter.Type}
60
+ # @param [String, nil] label Parameter label
61
+ # @param [String, nil] description Parameter description
62
+ # @param [Numeric, nil] min Minimum value for numeric types
63
+ # @param [Numeric, nil] max Maximum value for numeric types
64
+ # @param [Numeric, nil] step Step size for numeric types
65
+ # @param [String, nil] pattern Regular expression pattern for string types
66
+ # @param [true, false] required Whether the parameter is required
67
+ # @param [true, false] read_only Whether the parameter is read only
68
+ # @param [true, false] multiple Whether the parameter is a list of values
69
+ # @param [String, nil] context Context for the parameter
70
+ # @param [Object, nil] default Default value for the parameter
71
+ # @param [Hash] options Options for the parameter
72
+ # @param [Hash] filter_criteria Filter criteria for the parameter
73
+ # @param [String, nil] group_name Parameter group name.
74
+ # When nil, it will be inferred when this method is called inside a {group} block.
75
+ # @param [true, false] advanced Whether the parameter is advanced
76
+ # @param [true, false] limit_to_options Whether the parameter is limited to the given options
77
+ # @param [Integer, nil] multiple_limit Maximum number of values for a multiple parameter
78
+ # @param [String, nil] unit Parameter unit
79
+ # @param [String, nil] unit_label Parameter unit label
80
+ # @param [true, false] verify Whether the parameter value should be verified
81
+ #
82
+ # @return [void]
83
+ #
84
+ # @see org.openhab.core.config.core.ConfigDescriptionParameter
85
+ #
86
+ def parameter(name,
87
+ type,
88
+ label: nil,
89
+ description: nil,
90
+ min: nil,
91
+ max: nil,
92
+ step: nil,
93
+ pattern: nil,
94
+ required: false,
95
+ read_only: false,
96
+ multiple: false,
97
+ context: nil,
98
+ default: nil,
99
+ options: {},
100
+ filter_criteria: {},
101
+ group_name: nil,
102
+ advanced: false,
103
+ limit_to_options: false,
104
+ multiple_limit: nil,
105
+ unit: nil,
106
+ unit_label: nil,
107
+ verify: false)
108
+ # Extract the named arguments into a hash
109
+ @parameters << method(__method__).parameters
110
+ .select { |param_type, _| param_type == :key }
111
+ .to_h { |_, key| [key, binding.local_variable_get(key)] }
112
+ .then do |p|
113
+ p[:options] = p[:options].map do |opt_value, opt_label|
114
+ org.openhab.core.config.core.ParameterOption.new(opt_value, opt_label)
115
+ end
116
+ p[:filter_criteria] = p[:filter_criteria].map do |filter_name, filter_value|
117
+ org.openhab.core.config.core.FilterCriteria.new(filter_name, filter_value)
118
+ end
119
+ p[:minimum] = p.delete(:min)&.to_d&.to_java
120
+ p[:maximum] = p.delete(:max)&.to_d&.to_java
121
+ p[:step] = p.delete(:step)&.to_d&.to_java
122
+ p[:group_name] ||= @current_group
123
+ type = org.openhab.core.config.core.ConfigDescriptionParameter::Type.value_of(type.to_s.upcase)
124
+
125
+ parameter = org.openhab.core.config.core.ConfigDescriptionParameterBuilder.create(name.to_s, type)
126
+
127
+ p.each do |key, value|
128
+ parameter.send("with_#{key}", value) unless value.nil?
129
+ end
130
+ parameter.build
131
+ end
132
+ end
133
+
134
+ #
135
+ # Build the config description
136
+ #
137
+ # @param [String, java.net.URI] uri The URI for the config description. When nil, it will default to `dummy:uri`
138
+ # @yield Block executed in the context of this builder. Inside the block, you can call {parameter} and {group}.
139
+ #
140
+ # @return [org.openhab.core.config.core.ConfigDescription] The created ConfigDescription object
141
+ #
142
+ def build(uri = nil, &block)
143
+ instance_eval(&block) if block
144
+ raise ArgumentError, "No parameters defined" if @parameters.empty?
145
+
146
+ uri ||= "dummy:uri"
147
+ uri = java.net.URI.new(uri.to_s) unless uri.is_a?(java.net.URI)
148
+ org.openhab.core.config.core.ConfigDescriptionBuilder
149
+ .create(uri)
150
+ .with_parameters(@parameters)
151
+ .with_parameter_groups(@parameter_groups)
152
+ .build
153
+ end
154
+ end
155
+ end
156
+ end
157
+ end
@@ -36,6 +36,8 @@ module OpenHAB
36
36
  # sending the command
37
37
  %i[command update].each do |ensured_method|
38
38
  # def command(state)
39
+ # # immediately send the command if it's a command, but not a state (like REFRESH)
40
+ # return super(state) if state.is_a?(Command) && !state.is_a?(State)
39
41
  # return super(state) unless Thread.current[:openhab_ensure_states]
40
42
  #
41
43
  # formatted_state = format_command(state)
@@ -48,6 +50,8 @@ module OpenHAB
48
50
  # end
49
51
  class_eval <<~RUBY, __FILE__, __LINE__ + 1 # rubocop:disable Style/DocumentDynamicEvalDefinition
50
52
  def #{ensured_method}(state)
53
+ # immediately send the command if it's a command, but not a state (like REFRESH)
54
+ #{"return super(state) if state.is_a?(Command) && !state.is_a?(State)" if ensured_method == :command}
51
55
  return super(state) unless Thread.current[:openhab_ensure_states]
52
56
 
53
57
  formatted_state = format_#{ensured_method}(state)
@@ -36,12 +36,9 @@ module OpenHAB
36
36
  # Base class for all widgets
37
37
  # @see org.openhab.core.model.sitemap.sitemap.Widget
38
38
  class WidgetBuilder
39
- # rubocop:disable Layout/LineLength, Lint/MixedRegexpCaptureTypes
40
- # These are copied directly out of UIComponentSitemapProvider.java
41
- VISIBILITY_PATTERN = /(?<item>[A-Za-z]\w*)\s*(?<condition>==|!=|<=|>=|<|>)\s*(?<sign>\+|-)?(?<state>\S+)/.freeze
42
- COLOR_PATTERN = /((?<item>[A-Za-z]\w*)?\s*((?<condition>==|!=|<=|>=|<|>)\s*(?<sign>\+|-)?(?<state>\S+))?\s*=)?\s*(?<arg>\S+)/.freeze
43
- # rubocop:enable Layout/LineLength, Lint/MixedRegexpCaptureTypes
44
- private_constant :VISIBILITY_PATTERN, :COLOR_PATTERN
39
+ # This is copied directly out of UIComponentSitemapProvider.java
40
+ CONDITION_PATTERN = /(?<item>[A-Za-z]\w*)?\s*(?<condition>==|!=|<=|>=|<|>)?\s*(?<sign>\+|-)?(?<state>.+)/.freeze
41
+ private_constant :CONDITION_PATTERN
45
42
 
46
43
  # @return [String, nil]
47
44
  attr_accessor :label
@@ -52,15 +49,15 @@ module OpenHAB
52
49
  # @see https://www.openhab.org/docs/ui/sitemaps.html#icons
53
50
  attr_accessor :icon
54
51
  # Label color rules
55
- # @return [Array<String>]
52
+ # @return [Hash<String, String>]
56
53
  # @see https://www.openhab.org/docs/ui/sitemaps.html#label-value-and-icon-colors
57
54
  attr_reader :label_colors
58
55
  # Value color rules
59
- # @return [Array<String>]
56
+ # @return [Hash<String, String>]
60
57
  # @see https://www.openhab.org/docs/ui/sitemaps.html#label-value-and-icon-colors
61
58
  attr_reader :value_colors
62
59
  # Icon color rules
63
- # @return [Array<String>]
60
+ # @return [Hash<String, String>]
64
61
  # @see https://www.openhab.org/docs/ui/sitemaps.html#label-value-and-icon-colors
65
62
  attr_reader :icon_colors
66
63
  # Visibility rules
@@ -94,32 +91,32 @@ module OpenHAB
94
91
  @label = label
95
92
  @icon = icon
96
93
  @visibilities = []
97
- @label_colors = []
98
- @value_colors = []
99
- @icon_colors = []
94
+ @label_colors = {}
95
+ @value_colors = {}
96
+ @icon_colors = {}
100
97
 
101
- self.label_color(*label_color) if label_color
102
- self.value_color(*value_color) if value_color
103
- self.icon_color(*icon_color) if icon_color
98
+ self.label_color(label_color) if label_color
99
+ self.value_color(value_color) if value_color
100
+ self.icon_color(icon_color) if icon_color
104
101
  self.visibility(*visibility) if visibility
105
102
  end
106
103
 
107
104
  # Adds one or more new rules for setting the label color
108
- # @return [Array<String>] the current rules
109
- def label_color(*rules)
110
- @label_colors.concat(rules)
105
+ # @return [Hash<String, String>] the current rules
106
+ def label_color(rules)
107
+ @label_colors.merge!(rules)
111
108
  end
112
109
 
113
110
  # Adds one or more new rules for setting the value color
114
- # @return [Array<String>] the current rules
115
- def value_color(*rules)
116
- @value_colors.concat(rules)
111
+ # @return [Hash<String, String>] the current rules
112
+ def value_color(rules)
113
+ @value_colors.merge!(rules)
117
114
  end
118
115
 
119
116
  # Adds one or more new rules for setting the icon color
120
- # @return [Array<String>] the current rules
121
- def icon_color(*rules)
122
- @icon_colors.concat(rules)
117
+ # @return [Hash<String, String>] the current rules
118
+ def icon_color(rules)
119
+ @icon_colors.merge!(rules)
123
120
  end
124
121
 
125
122
  # Adds one or more new visibility rules
@@ -137,21 +134,28 @@ module OpenHAB
137
134
  widget.label = @label
138
135
  widget.icon = @icon
139
136
 
140
- add_color(widget.label_color, label_colors) unless label_colors.empty?
141
- add_color(widget.value_color, value_colors) unless value_colors.empty?
142
- add_color(widget.icon_color, icon_colors) unless icon_colors.empty?
137
+ add_colors(widget, :label_color, label_colors)
138
+ add_colors(widget, :value_color, value_colors)
139
+ add_colors(widget, :icon_color, icon_colors)
143
140
 
144
- visibilities.each do |v|
145
- unless (match = VISIBILITY_PATTERN.match(v))
146
- raise ArgumentError, "Syntax error in visibility rule #{v.inspect}"
141
+ # @deprecated OH 4.1
142
+ if SitemapBuilder.factory.respond_to?(:create_condition)
143
+ add_conditions(widget, :visibility, visibilities, :create_visibility_rule)
144
+ else
145
+ visibilities.each do |v|
146
+ raise ArgumentError, "AND conditions not supported prior to openHAB 4.1" if v.is_a?(Array)
147
+
148
+ unless (match = CONDITION_PATTERN.match(v))
149
+ raise ArgumentError, "Syntax error in visibility rule #{v.inspect}"
150
+ end
151
+
152
+ rule = SitemapBuilder.factory.create_visibility_rule
153
+ rule.item = match["item"]
154
+ rule.condition = match["condition"]
155
+ rule.sign = match["sign"]
156
+ rule.state = match["state"]
157
+ widget.visibility.add(rule)
147
158
  end
148
-
149
- rule = SitemapBuilder.factory.create_visibility_rule
150
- rule.item = match["item"]
151
- rule.condition = match["condition"]
152
- rule.sign = match["sign"]
153
- rule.state = match["state"]
154
- widget.visibility.add(rule)
155
159
  end
156
160
 
157
161
  widget
@@ -169,19 +173,51 @@ module OpenHAB
169
173
 
170
174
  private
171
175
 
172
- def add_color(widget_color, colors)
173
- colors.each do |c|
174
- unless (match = COLOR_PATTERN.match(c))
175
- raise ArgumentError, "Syntax error in color rule #{c.inspect}"
176
+ def add_colors(widget, method, conditions)
177
+ conditions.each do |condition, color|
178
+ condition = [condition] unless condition.is_a?(Array)
179
+ add_conditions(widget, method, condition, :create_color_array) do |color_array|
180
+ color_array.arg = color
181
+ end
182
+ end
183
+ end
184
+
185
+ def add_conditions(widget, method, conditions, container_method)
186
+ return if conditions.empty?
187
+
188
+ object = widget.send(method)
189
+ has_and_conditions = conditions.any?(Array)
190
+ # @deprecated OH 4.1
191
+ if !SitemapBuilder.factory.respond_to?(:create_condition) && has_and_conditions
192
+ raise ArgumentError, "AND conditions not supported prior to openHAB 4.1"
193
+ end
194
+
195
+ conditions = [conditions] unless has_and_conditions
196
+
197
+ conditions.each do |sub_conditions|
198
+ container = SitemapBuilder.factory.send(container_method)
199
+
200
+ add_conditions_to_container(container, sub_conditions)
201
+ yield container if block_given?
202
+ object.add(container)
203
+ end
204
+ end
205
+
206
+ def add_conditions_to_container(container, conditions)
207
+ # @deprecated OH 4.1
208
+ supports_and_conditions = SitemapBuilder.factory.respond_to?(:create_condition)
209
+
210
+ Array.wrap(conditions).each do |c|
211
+ unless (match = CONDITION_PATTERN.match(c))
212
+ raise ArgumentError, "Syntax error in condition #{c.inspect}"
176
213
  end
177
214
 
178
- rule = SitemapBuilder.factory.create_color_array
179
- rule.item = match["item"]
180
- rule.condition = match["condition"]
181
- rule.sign = match["sign"]
182
- rule.state = match["state"]
183
- rule.arg = match["arg"]
184
- widget_color.add(color)
215
+ condition = supports_and_conditions ? SitemapBuilder.factory.create_condition : container
216
+ condition.item = match["item"]
217
+ condition.condition = match["condition"]
218
+ condition.sign = match["sign"]
219
+ condition.state = match["state"]
220
+ container.conditions.add(condition) if supports_and_conditions
185
221
  end
186
222
  end
187
223
  end
@@ -284,6 +320,7 @@ module OpenHAB
284
320
 
285
321
  @switch = switch
286
322
  @frequency = frequency
323
+ @switch_enabled = nil
287
324
  end
288
325
 
289
326
  # (see #switch=)
@@ -4,6 +4,6 @@ module OpenHAB
4
4
  module DSL
5
5
  # Version of openHAB helper libraries
6
6
  # @return [String]
7
- VERSION = "5.11.0"
7
+ VERSION = "5.12.0"
8
8
  end
9
9
  end
data/lib/openhab/dsl.rb CHANGED
@@ -77,7 +77,13 @@ module OpenHAB
77
77
  #
78
78
  # Defines a new profile that can be applied to item channel links.
79
79
  #
80
+ # To create a profile that can be used in the UI, provide a label and optionally a {config_description},
81
+ # otherwise the profile will not be visible in the UI.
82
+ #
80
83
  # @param [String, Symbol] id The id for the profile.
84
+ # @param [String, nil] label The label for the profile. When nil, the profile will not be visible in the UI.
85
+ # @param [org.openhab.core.config.core.ConfigDescription, nil] config_description
86
+ # The configuration description for the profile so that it can be configured in the UI.
81
87
  # @yield [event, command: nil, state: nil, callback:, link:, item:, channel_uid:, configuration:, context:]
82
88
  # All keyword params are optional. Any that aren't defined won't be passed.
83
89
  # @yieldparam [:command_from_item, :state_from_item, :command_from_handler, :state_from_handler] event
@@ -97,8 +103,8 @@ module OpenHAB
97
103
  # @yieldreturn [Boolean] Return true from the block in order to have default processing.
98
104
  # @return [void]
99
105
  #
100
- # @see org.openhab.thing.Profile
101
- # @see org.openhab.thing.StateProfile
106
+ # @see org.openhab.core.thing.profiles.Profile
107
+ # @see org.openhab.core.thing.profiles.StateProfile
102
108
  #
103
109
  # @example Vetoing a command
104
110
  # profile(:veto_closing_shades) do |event, item:, command:|
@@ -142,17 +148,54 @@ module OpenHAB
142
148
  # # can also be referenced from an `.items` file:
143
149
  # # Number:Temperature MyTempWithNonUnitValueFromBinding "I prefer Celsius [%d °C]" { channel="something_that_returns_F"[profile="ruby:set_uom", unit="°F"] }
144
150
  #
145
- def profile(id, &block)
151
+ # @example Create a profile that is usable in the UI
152
+ # config_description = config_description do
153
+ # parameter :min, :decimal, label: "Minimum", description: "Minimum value"
154
+ # parameter :max, :decimal, label: "Maximum", description: "Maximum value"
155
+ # end
156
+ #
157
+ # profile(:range_filter, label: "Range Filter", config_description: config_description) do |event, state:, configuration:|
158
+ # return true unless event == :state_from_handler
159
+ #
160
+ # (configuration["min"]..configuration["max"]).cover?(state)
161
+ # end
162
+ #
163
+ def profile(id, label: nil, config_description: nil, &block)
146
164
  raise ArgumentError, "Block is required" unless block
147
165
 
148
166
  id = id.to_s
149
- uid = org.openhab.core.thing.profiles.ProfileTypeUID.new("ruby", id)
150
167
 
151
168
  ThreadLocal.thread_local(openhab_rule_type: "profile", openhab_rule_uid: id) do
152
- Core::ProfileFactory.instance.register(uid, block)
169
+ Core::ProfileFactory.instance.register(id, block, label: label, config_description: config_description)
153
170
  end
154
171
  end
155
172
 
173
+ #
174
+ # Create a {org.openhab.core.config.core.ConfigDescription ConfigDescription} object.
175
+ #
176
+ # @param [String, java.net.URI] uri The URI for the ConfigDescription. When nil, a dummy URI is used which will
177
+ # be replaced by the profile with the correct URI for that profile.
178
+ # @yield Block that consists of {ConfigDescription::Builder#parameter} and {ConfigDescription::Builder#group} calls.
179
+ #
180
+ # @return [org.openhab.core.config.core.ConfigDescription]
181
+ # The created {org.openhab.core.config.core.ConfigDescription ConfigDescription} object
182
+ #
183
+ # @example
184
+ # config_description = config_description do
185
+ # parameter :ungrouped_parameter, :decimal, label: "Ungrouped Parameter", min: 1, max: 5
186
+ #
187
+ # group "Config Group", label: "Grouped parameters", advanced: true do
188
+ # parameter :my_parameter, :string, label: "My Parameter", description: "My Parameter Description"
189
+ # parameter :other_parameter, :integer, label: "Other Parameter", description: "Other Parameter Description"
190
+ # end
191
+ # end
192
+ #
193
+ def config_description(uri = nil, &block)
194
+ raise ArgumentError, "Block is required" unless block
195
+
196
+ ConfigDescription::Builder.new.build(uri, &block)
197
+ end
198
+
156
199
  # rubocop:enable Layout/LineLength
157
200
 
158
201
  # @!group Object Access
@@ -9,7 +9,9 @@ module YARD
9
9
  # Which might be a class, an enum, or an interface
10
10
  module Base
11
11
  module ClassMethods
12
- def new(name)
12
+ def new(name, _suffix = nil)
13
+ # _suffix is given when it encounters a class with ::, e.g. org.openhab.core.OpenHAB::DEFAULT_CONFIG_FOLDER
14
+
13
15
  super(:root, name)
14
16
  end
15
17
  end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module YARD
4
+ module CodeObjects
5
+ module Java
6
+ class MethodObject < CodeObjects::MethodObject
7
+ include Base
8
+
9
+ def type
10
+ :method
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -5,27 +5,76 @@ module YARD
5
5
  module JRuby
6
6
  module Base
7
7
  class << self
8
+ #
9
+ # Creates an external link to java documentation for a java class.
10
+ #
11
+ # The supported classes are defined in the `javadocs` configuration option.
12
+ #
13
+ # Supported syntaxes:
14
+ # Package:
15
+ # - org.openhab.core => href_base/org/openhab/core/package-summary.html
16
+ #
17
+ # Class:
18
+ # - org.openhab.core.OpenHAB => href_base/org/openhab/core/OpenHAB.html
19
+ # This doesn't get mistaken as a constant:
20
+ # - java.net.URI => href_base/java/net/URI.html
21
+ #
22
+ # Constant: (To specify a constant, use Ruby's `::` syntax)
23
+ # - org.openhab.core.OpenHAB::DEFAULT_CONFIG_FOLDER =>
24
+ # href_base/org/openhab/core/OpenHAB.html#DEFAULT_CONFIG_FOLDER
25
+ #
26
+ # Method:
27
+ # - org.openhab.core.OpenHAB#getVersion() => href_base/org/openhab/core/OpenHAB.html#getVersion()
28
+ # But can also work with constants, albeit not a valid Ruby syntax:
29
+ # - org.openhab.core.OpenHAB#version => href_base/org/openhab/core/OpenHAB.html#version
30
+ # - org.openhab.core.OpenHAB#DEFAULT_CONFIG_FOLDER =>
31
+ # href_base/org/openhab/core/OpenHAB.html#DEFAULT_CONFIG_FOLDER
32
+ #
33
+ # Inner class:
34
+ # - org.openhab.core.config.core.ConfigDescriptionParameter::Type =>
35
+ # href_base/org/openhab/core/config/core/ConfigDescriptionParameter.Type.html
36
+ # - org.openhab.core.config.core.ConfigDescriptionParameter.Type =>
37
+ # href_base/org/openhab/core/config/core/ConfigDescriptionParameter.Type.html
38
+ #
39
+ # Constant in inner class:
40
+ # - org.openhab.core.config.core.ConfigDescriptionParameter::Type::TEXT =>
41
+ # href_base/org/openhab/core/config/core/ConfigDescriptionParameter.Type.html#TEXT
42
+ # - org.openhab.core.config.core.ConfigDescriptionParameter.Type::TEXT =>
43
+ # href_base/org/openhab/core/config/core/ConfigDescriptionParameter.Type.html#TEXT
44
+ #
8
45
  def infer_java_class(klass, inferred_type = nil, comments = nil, statement = nil)
9
- components = klass.split(".")
10
- components.pop if components.last == "freeze"
11
-
12
- class_first_char = components.last[0]
13
- is_field = components.last == components.last.upcase
14
- container_first_char = components[-2]&.[](0)
15
- is_method = container_first_char &&
16
- class_first_char != class_first_char.upcase &&
17
- container_first_char == container_first_char.upcase
18
- is_package = !is_method && !is_field && class_first_char != class_first_char.upcase
19
-
20
- # methods aren't supported right now
21
- return if is_method
22
-
23
46
  javadocs = YARD::Config.options.dig(:jruby, "javadocs") || {}
24
47
 
25
48
  href_base = javadocs.find { |package, _href| klass == package || klass.start_with?("#{package}.") }&.last
26
49
  return unless href_base
27
50
 
51
+ components = klass.split(/\.(?=[A-Z])/, 2)
52
+ components.unshift(*components.shift.split("."))
53
+ components.push(components.pop.delete_suffix(".freeze"))
54
+
55
+ class_first_char = components.last[0]
56
+ if /#|::/.match?(components.last)
57
+ parts = components.pop.rpartition(/#|::/)
58
+ is_field = parts.last == parts.last.upcase
59
+ # explicit method is fine, e.g. `org.openhab.core.OpenHAB#getVersion()`
60
+ is_method = !is_field
61
+ components.push(parts.first.gsub("::", "."), parts.last)
62
+ else
63
+ is_field = is_method = false
64
+ if components.last.include?(".")
65
+ parts = components.last.split(".")
66
+ if (is_method = parts.last[0] == parts.last[0].downcase)
67
+ # implicit method is not supported, e.g. `org.openhab.core.OpenHAB.version`
68
+ # because we're not sure whether it should be #version() or #getVersion()
69
+ return
70
+ end
71
+ end
72
+ end
73
+
74
+ is_package = !is_method && !is_field && class_first_char != class_first_char.upcase
75
+
28
76
  inferred_type = CodeObjects::Java::FieldObject if is_field
77
+ inferred_type = CodeObjects::Java::MethodObject if is_method
29
78
  inferred_type = CodeObjects::Java::PackageObject if is_package
30
79
  if inferred_type.nil?
31
80
  docstring = Docstring.parser.parse(comments || statement&.comments).to_docstring
@@ -36,12 +85,18 @@ module YARD
36
85
  end
37
86
  end
38
87
 
88
+ orig_klass = klass.dup
89
+
90
+ # purposely calling gsub! to modify the caller's string
91
+ # YARD doesn't handle java inner classes well, so we convert them to ruby
92
+ klass.gsub!("::", ".")
93
+
39
94
  inferred_type.new(klass) do |o|
40
95
  o.source = statement if statement
41
96
  suffix = "/package-summary" if is_package
42
- field = "##{components.pop}" if is_field
97
+ field = "##{components.pop}" if is_field || is_method
43
98
  link = "#{href_base}#{components.join("/")}#{suffix}.html#{field}"
44
- o.docstring.add_tag(Tags::Tag.new(:see, klass, nil, link)) unless o.docstring.has_tag?(:see)
99
+ o.docstring.add_tag(Tags::Tag.new(:see, orig_klass, nil, link)) unless o.docstring.has_tag?(:see)
45
100
  end
46
101
  end
47
102
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: openhab-scripting
3
3
  version: !ruby/object:Gem::Version
4
- version: 5.11.0
4
+ version: 5.12.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Brian O'Connell
@@ -10,7 +10,7 @@ authors:
10
10
  autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2023-10-29 00:00:00.000000000 Z
13
+ date: 2023-12-19 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: bundler
@@ -124,20 +124,6 @@ dependencies:
124
124
  - - "~>"
125
125
  - !ruby/object:Gem::Version
126
126
  version: '2.2'
127
- - !ruby/object:Gem::Dependency
128
- name: github_changelog_generator
129
- requirement: !ruby/object:Gem::Requirement
130
- requirements:
131
- - - "~>"
132
- - !ruby/object:Gem::Version
133
- version: '1.16'
134
- type: :development
135
- prerelease: false
136
- version_requirements: !ruby/object:Gem::Requirement
137
- requirements:
138
- - - "~>"
139
- - !ruby/object:Gem::Version
140
- version: '1.16'
141
127
  - !ruby/object:Gem::Dependency
142
128
  name: guard-rubocop
143
129
  requirement: !ruby/object:Gem::Requirement
@@ -391,6 +377,7 @@ files:
391
377
  - lib/openhab/core/events/abstract_event.rb
392
378
  - lib/openhab/core/events/abstract_item_registry_event.rb
393
379
  - lib/openhab/core/events/abstract_thing_registry_event.rb
380
+ - lib/openhab/core/events/channel_triggered_event.rb
394
381
  - lib/openhab/core/events/item_command_event.rb
395
382
  - lib/openhab/core/events/item_event.rb
396
383
  - lib/openhab/core/events/item_state_changed_event.rb
@@ -505,6 +492,7 @@ files:
505
492
  - lib/openhab/core_ext/ruby/symbol.rb
506
493
  - lib/openhab/core_ext/ruby/time.rb
507
494
  - lib/openhab/dsl.rb
495
+ - lib/openhab/dsl/config_description/builder.rb
508
496
  - lib/openhab/dsl/debouncer.rb
509
497
  - lib/openhab/dsl/events.rb
510
498
  - lib/openhab/dsl/events/watch_event.rb
@@ -569,6 +557,7 @@ files:
569
557
  - lib/openhab/yard/code_objects/java/class_object.rb
570
558
  - lib/openhab/yard/code_objects/java/field_object.rb
571
559
  - lib/openhab/yard/code_objects/java/interface_object.rb
560
+ - lib/openhab/yard/code_objects/java/method_object.rb
572
561
  - lib/openhab/yard/code_objects/java/package_object.rb
573
562
  - lib/openhab/yard/code_objects/java/proxy.rb
574
563
  - lib/openhab/yard/coderay.rb