openhab-scripting 4.37.0 → 4.39.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: 152174c3766a83b620a0ec7df6e692bb914e187c788d9c5eecfcedd42bfd641c
4
- data.tar.gz: 44084d3557abd293faf97858470414cf5f3dd65e07faa8f74e8f40e0d6ea8edb
3
+ metadata.gz: 1fb1294914e9c3e343bef8f0c1aaca327dbaa5875d446ec3e507edc1f390c9c9
4
+ data.tar.gz: be121541f849f733505c6180557f88fdd98869d89e0a593c6fc48db0e7e6daf5
5
5
  SHA512:
6
- metadata.gz: 617deb9b4f75a92a407cf8db6ff695f409ac5f006690751c10100919d414851dc83758ec0560badf3d40097cc896d86de132a215d22a3f505b8e97bff26a8ad2
7
- data.tar.gz: 5a2c037047ce9db79d57a153e5df94c631144d511ac716b1862a8a6c4fc567d01c6efa15d93583f2bc80a792c47c85e30dda65ec481a1f14f6a588765638d56f
6
+ metadata.gz: e6af3e7152dd02c9d98dea19f9bcaa4f474a20daac090941c1121705d177afec2498a2704462645d51b6e4e055efda1f0431702c0f7c6f126718b7994ef6baad
7
+ data.tar.gz: f9af8246710e44dc86719d10ba2577003293640e1c11885c054da9d23d0be8746e1103151c173672ddfab96ae218f5d717318ab72d628a194f7a243647ea885d
@@ -73,6 +73,13 @@ module OpenHAB
73
73
  @item = item
74
74
  end
75
75
 
76
+ # @!visibility private
77
+ # this is explicitly defined, instead of aliased, because #command
78
+ # doesn't actually exist as a method, and will go through method_missing
79
+ def <<(command)
80
+ command(command)
81
+ end
82
+
76
83
  # activate +ensure_states+ before forwarding to the wrapped object
77
84
  def method_missing(method, *args, &block)
78
85
  return super unless @item.respond_to?(method)
@@ -2,6 +2,7 @@
2
2
 
3
3
  require 'openhab/dsl/items/metadata'
4
4
  require 'openhab/dsl/items/persistence'
5
+ require 'openhab/dsl/items/semantics'
5
6
 
6
7
  require_relative 'item_equality'
7
8
 
@@ -10,14 +11,17 @@ module OpenHAB
10
11
  module Items
11
12
  java_import org.openhab.core.items.GenericItem
12
13
 
13
- # Adds methods to core OpenHAB DimmerItem type to make it more natural in
14
+ # Adds methods to core OpenHAB GenericItem type to make it more natural in
14
15
  # Ruby
16
+ #
17
+ # @see https://www.openhab.org/javadoc/latest/org/openhab/core/items/genericitem
15
18
  class GenericItem
16
19
  include Log
17
20
  include ItemEquality
18
21
 
19
22
  prepend Metadata
20
23
  prepend Persistence
24
+ include Semantics
21
25
 
22
26
  # rubocop:disable Naming/MethodName these mimic Java fields, which are
23
27
  # actually methods
@@ -39,73 +39,7 @@ module OpenHAB
39
39
  group.name
40
40
  end
41
41
 
42
- # Send a command to each member of the group
43
- #
44
- # @return [GroupMembers] +self+
45
- def command(command)
46
- each { |item| item << command }
47
- end
48
42
  alias << command
49
-
50
- # @!method refresh
51
- # Send the +REFRESH+ command to each member of the group
52
- # @return [GroupMembers] +self+
53
-
54
- # @!method on
55
- # Send the +ON+ command to each member of the group
56
- # @return [GroupMembers] +self+
57
-
58
- # @!method off
59
- # Send the +OFF+ command to each member of the group
60
- # @return [GroupMembers] +self+
61
-
62
- # @!method up
63
- # Send the +UP+ command to each member of the group
64
- # @return [GroupMembers] +self+
65
-
66
- # @!method down
67
- # Send the +DOWN+ command to each member of the group
68
- # @return [GroupMembers] +self+
69
-
70
- # @!method stop
71
- # Send the +STOP+ command to each member of the group
72
- # @return [GroupMembers] +self+
73
-
74
- # @!method move
75
- # Send the +MOVE+ command to each member of the group
76
- # @return [GroupMembers] +self+
77
-
78
- # @!method increase
79
- # Send the +INCREASE+ command to each member of the group
80
- # @return [GroupMembers] +self+
81
-
82
- # @!method desrease
83
- # Send the +DECREASE+ command to each member of the group
84
- # @return [GroupMembers] +self+
85
-
86
- # @!method play
87
- # Send the +PLAY+ command to each member of the group
88
- # @return [GroupMembers] +self+
89
-
90
- # @!method pause
91
- # Send the +PAUSE+ command to each member of the group
92
- # @return [GroupMembers] +self+
93
-
94
- # @!method rewind
95
- # Send the +REWIND+ command to each member of the group
96
- # @return [GroupMembers] +self+
97
-
98
- # @!method fast_forward
99
- # Send the +FASTFORWARD+ command to each member of the group
100
- # @return [GroupMembers] +self+
101
-
102
- # @!method next
103
- # Send the +NEXT+ command to each member of the group
104
- # @return [GroupMembers] +self+
105
-
106
- # @!method previous
107
- # Send the +PREVIOUS+ command to each member of the group
108
- # @return [GroupMembers] +self+
109
43
  end
110
44
 
111
45
  include Enumerable
@@ -113,6 +47,17 @@ module OpenHAB
113
47
 
114
48
  remove_method :==
115
49
 
50
+ # Override Enumerable because we want to send them to the base item if possible
51
+ #
52
+ # @return [GroupMembers] +self+
53
+ %i[command update].each do |method|
54
+ define_method(method) do |command|
55
+ return base_item.__send__(method, command) if base_item
56
+
57
+ super(command)
58
+ end
59
+ end
60
+
116
61
  #
117
62
  # Get an Array-like object representing the members of the group
118
63
  #
@@ -167,6 +112,7 @@ module OpenHAB
167
112
  super
168
113
  end
169
114
 
115
+ # Is this ever called?
170
116
  # give the base item type a chance to format commands
171
117
  # @!visibility private
172
118
  def format_type(command)
@@ -69,13 +69,20 @@ module OpenHAB
69
69
  end # end
70
70
  RUBY
71
71
 
72
- logger.trace("Defining GroupItem::GroupMembers##{command} for #{value}")
73
- GroupItem::GroupMembers.class_eval <<~RUBY, __FILE__, __LINE__ + 1
72
+ logger.trace("Defining Enumerable##{command} for #{value}")
73
+ Enumerable.class_eval <<~RUBY, __FILE__, __LINE__ + 1
74
74
  def #{command} # def on
75
75
  each(&:#{command}) # each(&:on)
76
76
  end # end
77
77
  RUBY
78
78
 
79
+ # Override the inherited methods from Enumerable and send it to the base_item
80
+ GroupItem.class_eval <<~RUBY, __FILE__, __LINE__ + 1
81
+ def #{command} # def on
82
+ method_missing(:#{command}) # method_missing(:on)
83
+ end # end
84
+ RUBY
85
+
79
86
  logger.trace("Defining ItemCommandEvent##{command}? for #{value}")
80
87
  MonkeyPatch::Events::ItemCommandEvent.class_eval <<~RUBY, __FILE__, __LINE__ + 1
81
88
  def #{command}? # def refresh?
@@ -19,11 +19,26 @@ module OpenHAB
19
19
  def self.included(klass)
20
20
  klass.prepend ItemEquality # make sure this is first
21
21
  klass.extend Forwardable
22
- klass.delegate %i[+ - * / % | to_d to_f to_i to_int] => :state
22
+ klass.delegate %i[+ - * / % to_d to_f to_i to_int] => :state
23
23
  # remove the JRuby default == so that we can inherit the Ruby method
24
24
  klass.remove_method :==
25
25
  end
26
26
 
27
+ #
28
+ # Convert state to a Quantity by calling state (DecimalType)#|
29
+ # Raise a NoMethodError if state is nil (NULL or UNDEF) instead of delegating to it.
30
+ # because nil#| would return true, causing an unexpected result
31
+ #
32
+ # @param [Unit, String] other the unit to convert to
33
+ #
34
+ # @return [QuantityType] the QuantityType in the given unit
35
+ #
36
+ def |(other)
37
+ raise NoMethodError, 'State is nil' unless state?
38
+
39
+ state.|(other)
40
+ end
41
+
27
42
  #
28
43
  # Check if NumericItem is truthy? as per defined by library
29
44
  #
@@ -0,0 +1,91 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Additions to Enumerable to allow easily filtering and commanding groups of items
4
+ module Enumerable
5
+ # Returns a new array of items that have at least one of the given tags
6
+ def tagged(*tags)
7
+ reject { |i| (tags & i.tags.to_a).empty? }
8
+ end
9
+
10
+ # Returns a new array of items that do not have any of the given tags
11
+ def not_tagged(*tags)
12
+ select { |i| (tags & i.tags.to_a).empty? }
13
+ end
14
+
15
+ # Returns a new array of items that are a member of at least one of the given groups
16
+ def member_of(*groups)
17
+ reject { |i| (groups.map(&:name) & i.group_names).empty? }
18
+ end
19
+
20
+ # Returns a new array of items that are not a member of any of the given groups
21
+ def not_member_of(*groups)
22
+ select { |i| (groups.map(&:name) & i.group_names).empty? }
23
+ end
24
+
25
+ # Send a command to every item in the collection
26
+ def command(command)
27
+ each { |i| i.command(command) }
28
+ end
29
+
30
+ # Update the state of every item in the collection
31
+ def update(state)
32
+ each { |i| i.update(state) }
33
+ end
34
+
35
+ # Returns the group members the elements
36
+ def members
37
+ select { |e| e.respond_to? :members }.flat_map(&:members)
38
+ end
39
+
40
+ # @!method refresh
41
+ # Send the +REFRESH+ command to every item in the collection
42
+
43
+ # @!method on
44
+ # Send the +ON+ command to every item in the collection
45
+
46
+ # @!method off
47
+ # Send the +OFF+ command to every item in the collection
48
+
49
+ # @!method up
50
+ # Send the +UP+ command to every item in the collection
51
+
52
+ # @!method down
53
+ # Send the +DOWN+ command to every item in the collection
54
+
55
+ # @!method stop
56
+ # Send the +STOP+ command to every item in the collection
57
+
58
+ # @!method move
59
+ # Send the +MOVE+ command to every item in the collection
60
+
61
+ # @!method increase
62
+ # Send the +INCREASE+ command to every item in the collection
63
+
64
+ # @!method decrease
65
+ # Send the +DECREASE+ command to every item in the collection
66
+
67
+ # @!method play
68
+ # Send the +PLAY+ command to every item in the collection
69
+
70
+ # @!method pause
71
+ # Send the +pause+ command to every item in the collection
72
+
73
+ # @!method rewind
74
+ # Send the +REWIND+ command to every item in the collection
75
+
76
+ # @!method fast_forward
77
+ # Send the +FAST_FORWARD+ command to every item in the collection
78
+
79
+ # @!method next
80
+ # Send the +NEXT+ command to every item in the collection
81
+
82
+ # @!method previous
83
+ # Send the +PREVIOUS+ command to every item in the collection
84
+
85
+ # @!visibility private
86
+ # can't use `include`, because Enumerable has already been included
87
+ # in other classes
88
+ def ensure
89
+ OpenHAB::DSL::Ensure::GenericItemDelegate.new(self)
90
+ end
91
+ end
@@ -0,0 +1,240 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'semantics/enumerable'
4
+
5
+ module OpenHAB
6
+ module DSL
7
+ # Module for implementing semantics helper methods on [GenericItem]
8
+ #
9
+ # Wraps https://www.openhab.org/javadoc/latest/org/openhab/core/model/script/actions/semantics,
10
+ # as well as adding a few additional convenience methods.
11
+ # Also includes Classes for each semantic tag.
12
+ #
13
+ # Be warned that the Semantic model is stricter than can actually be
14
+ # described by tags and groups on an Item. It makes assumptions that any
15
+ # given item only belongs to one semantic type (Location, Equipment, Point).
16
+ #
17
+ # See https://github.com/openhab/openhab-core/blob/main/bundles/org.openhab.core.semantics/model/SemanticTags.csv
18
+ module Semantics
19
+ # @!visibility private
20
+ # import the actual semantics action
21
+ SemanticsAction = org.openhab.core.model.script.actions.Semantics
22
+ private_constant :SemanticsAction
23
+
24
+ # import all the semantics constants
25
+ [org.openhab.core.semantics.model.point.Points,
26
+ org.openhab.core.semantics.model.property.Properties,
27
+ org.openhab.core.semantics.model.equipment.Equipments,
28
+ org.openhab.core.semantics.model.location.Locations].each do |parent_tag|
29
+ parent_tag.stream.for_each do |tag|
30
+ const_set(tag.simple_name.to_sym, tag.ruby_class)
31
+ end
32
+ end
33
+
34
+ # put ourself into the global namespace, replacing the action
35
+ ::Semantics = self # rubocop:disable Naming/ConstantName
36
+
37
+ # Checks if this Item is a Location
38
+ #
39
+ # This is implemented as checking if the item's semantic_type
40
+ # is a Location. I.e. an Item has a single semantic_type.
41
+ #
42
+ # @return [true, false]
43
+ def location?
44
+ SemanticsAction.location?(self)
45
+ end
46
+
47
+ # Checks if this Item is an Equipment
48
+ #
49
+ # This is implemented as checking if the item's semantic_type
50
+ # is an Equipment. I.e. an Item has a single semantic_type.
51
+ #
52
+ # @return [true, false]
53
+ def equipment?
54
+ SemanticsAction.equipment?(self)
55
+ end
56
+
57
+ # Checks if this Item is a Point
58
+ #
59
+ # This is implemented as checking if the item's semantic_type
60
+ # is a Point. I.e. an Item has a single semantic_type.
61
+ #
62
+ # @return [true, false]
63
+ def point?
64
+ SemanticsAction.point?(self)
65
+ end
66
+
67
+ # Checks if this Item has any semantic tags
68
+ # @return [true, false]
69
+ def semantic?
70
+ !!semantic_type
71
+ end
72
+
73
+ # Gets the related Location Item of this Item.
74
+ #
75
+ # Returns +self+ if this Item is a Location. Otherwise, checks ancestor
76
+ # groups one level at a time, returning the first Location Item found.
77
+ #
78
+ # @return [GenericItem, nil]
79
+ def location
80
+ SemanticsAction.get_location(self)
81
+ end
82
+
83
+ # Returns the sub-class of [Location] related to this Item.
84
+ #
85
+ # In other words, the semantic_type of this Item's Location.
86
+ #
87
+ # @return [Class]
88
+ def location_type
89
+ SemanticsAction.get_location_type(self)&.ruby_class
90
+ end
91
+
92
+ # Gets the related Equipment Item of this Item.
93
+ #
94
+ # Returns +self+ if this Item is an Equipment. Otherwise, checks ancestor
95
+ # groups one level at a time, returning the first Equipment Item found.
96
+ #
97
+ # @return [GenericItem, nil]
98
+ def equipment
99
+ SemanticsAction.get_equipment(self)
100
+ end
101
+
102
+ # Returns the sub-class of [Equipment] related to this Item.
103
+ #
104
+ # In other words, the semantic_type of this Item's Equipment.
105
+ #
106
+ # @return [Class]
107
+ def equipment_type
108
+ SemanticsAction.get_equipment_type(self)&.ruby_class
109
+ end
110
+
111
+ # Returns the sub-class of [Point] this Item is tagged with.
112
+ #
113
+ # @return [Class]
114
+ def point_type
115
+ SemanticsAction.get_point_type(self)&.ruby_class
116
+ end
117
+
118
+ # Returns the sub-class of [Property] this Item is tagged with.
119
+ # @return [Class]
120
+ def property_type
121
+ SemanticsAction.get_property_type(self)&.ruby_class
122
+ end
123
+
124
+ # Returns the sub-class of [Tag] this Item is tagged with.
125
+ #
126
+ # It will only return the first applicable Tag, preferring
127
+ # a sub-class of [Location], [Equipment], or [Point] first,
128
+ # and if none of those are found, looks for a [Property].
129
+ # @return [Class]
130
+ def semantic_type
131
+ SemanticsAction.get_semantic_type(self)&.ruby_class
132
+ end
133
+
134
+ # Return the related Point Items.
135
+ #
136
+ # Searches this Equipment Item for Points that are tagged appropriately.
137
+ #
138
+ # If called on a Point Item, it will automatically search for sibling Points
139
+ # (and remove itself if found).
140
+ #
141
+ # @example Get all points for a TV
142
+ # eGreatTV.points
143
+ # @example Search an Equipment item for its switch
144
+ # eGuestFan.points(Semantics::Switch) # => [GuestFan_Dimmer]
145
+ # @example Search a Thermostat item for its current temperature item
146
+ # eFamilyThermostat.points(Semantics::Status, Semantics::Temperature)
147
+ # # => [FamilyThermostat_AmbTemp]
148
+ # @example Search a Thermostat item for is setpoints
149
+ # eFamilyThermostat.points(Semantics::Control, Semantics::Temperature)
150
+ # # => [FamilyThermostat_HeatingSetpoint, FamilyThermostat_CoolingSetpoint]
151
+ # @example Given a A/V receiver's input item, search for its power item
152
+ # FamilyReceiver_Input.points(Semantics::Switch) # => [FamilyReceiver_Switch]
153
+ #
154
+ # @param [Class] point_or_property_types
155
+ # Pass 1 or 2 classes that are sub-classes of [Point] or [Property].
156
+ # Note that when comparing against semantic tags, it does a sub-class check.
157
+ # So if you search for [Control], you'll get items tagged with [Switch].
158
+ # @return [Array<GenericItem>]
159
+ def points(*point_or_property_types)
160
+ # automatically search the parent equipment (or location?!) for sibling points
161
+ unless equipment? || location?
162
+ result = (equipment || location)&.points(*point_or_property_types) || []
163
+ # remove self. but avoid state comparisons
164
+ result.delete_if { |item| item.eql?(self) }
165
+ return result
166
+ end
167
+
168
+ members.points(*point_or_property_types)
169
+ end
170
+ end
171
+ end
172
+ end
173
+
174
+ # Additions to Enumerable to allow easily filtering groups of items based on the semantic model
175
+ module Enumerable
176
+ # Returns a new array of items that are a semantics Location (optionally of the given type)
177
+ def sublocations(type = nil)
178
+ raise ArgumentError, 'type must be a subclass of Location' if type && !(type < OpenHAB::DSL::Semantics::Location)
179
+
180
+ result = select(&:location?)
181
+ result.select! { |i| i.location_type <= type } if type
182
+
183
+ result
184
+ end
185
+
186
+ # Returns a new array of items that are a semantics equipment (optionally of the given type)
187
+ #
188
+ # @example Get all TVs in a room
189
+ # lGreatRoom.equipments(Semantics::Screen)
190
+ def equipments(type = nil)
191
+ raise ArgumentError, 'type must be a subclass of Equipment' if type && !(type < OpenHAB::DSL::Semantics::Equipment)
192
+
193
+ result = select(&:equipment?)
194
+ result.select! { |i| i.equipment_type <= type } if type
195
+
196
+ result
197
+ end
198
+
199
+ # Returns a new array of items that are semantics points (optionally of a given type)
200
+ #
201
+ # @example Get all the power switch items for every equipment in a room
202
+ # lGreatRoom.equipments.points(Semantics::Switch)
203
+ def points(*point_or_property_types) # rubocop:disable Metrics
204
+ unless (0..2).cover?(point_or_property_types.length)
205
+ raise ArgumentError, "wrong number of arguments (given #{point_or_property_types.length}, expected 0..2)"
206
+ end
207
+ unless point_or_property_types.all? do |tag|
208
+ tag < OpenHAB::DSL::Semantics::Point || tag < OpenHAB::DSL::Semantics::Property
209
+ end
210
+ raise ArgumentError, 'point_or_property_types must all be a subclass of Point or Property'
211
+ end
212
+ if point_or_property_types.count { |tag| tag < OpenHAB::DSL::Semantics::Point } > 1 ||
213
+ point_or_property_types.count { |tag| tag < OpenHAB::DSL::Semantics::Property } > 1
214
+ raise ArgumentError, 'point_or_property_types cannot both be a subclass of Point or Property'
215
+ end
216
+
217
+ filter_with_members(&:point?)
218
+ .select do |point|
219
+ point_or_property_types.all? do |tag|
220
+ (tag < OpenHAB::DSL::Semantics::Point && point.point_type&.<=(tag)) ||
221
+ (tag < OpenHAB::DSL::Semantics::Property && point.property_type&.<=(tag))
222
+ end
223
+ end
224
+ end
225
+
226
+ #
227
+ # Select the elements where the given block returns true.
228
+ # If the block returns false but the element is a GroupItem with members,
229
+ # merge its members into the result and filter them using the same block
230
+ #
231
+ # @param [Block] &block A block that returns true for the elements and members to be included in the result
232
+ #
233
+ # @return [Array] The resulting array
234
+ #
235
+ # !@visibility private
236
+ def filter_with_members(&block)
237
+ selected, others = partition(&block)
238
+ others.members.select(&block).concat(selected)
239
+ end
240
+ end
@@ -5,5 +5,5 @@
5
5
  #
6
6
  module OpenHAB
7
7
  # @return [String] Version of OpenHAB helper libraries
8
- VERSION = '4.37.0'
8
+ VERSION = '4.39.0'
9
9
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: openhab-scripting
3
3
  version: !ruby/object:Gem::Version
4
- version: 4.37.0
4
+ version: 4.39.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Brian O'Connell
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2022-04-08 00:00:00.000000000 Z
11
+ date: 2022-04-20 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -80,6 +80,8 @@ files:
80
80
  - lib/openhab/dsl/items/persistence.rb
81
81
  - lib/openhab/dsl/items/player_item.rb
82
82
  - lib/openhab/dsl/items/rollershutter_item.rb
83
+ - lib/openhab/dsl/items/semantics.rb
84
+ - lib/openhab/dsl/items/semantics/enumerable.rb
83
85
  - lib/openhab/dsl/items/string_item.rb
84
86
  - lib/openhab/dsl/items/switch_item.rb
85
87
  - lib/openhab/dsl/items/timed_command.rb