openhab-scripting 4.37.0 → 4.39.0

Sign up to get free protection for your applications and to get access to all the features.
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