openhab-scripting 5.36.2 → 5.38.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.
@@ -10,71 +10,92 @@ module OpenHAB
10
10
  #
11
11
  # A {PlayerItem} allows control of a player, e.g. an audio player.
12
12
  #
13
- # @!attribute [r] state
14
- # @return [PlayPauseType, RewindFastforwardType, nil]
15
- #
16
13
  # @example Start playing on a player item
17
14
  # Chromecast.play
18
15
  # @example Check if a player is paused
19
16
  # logger.warn("#{item.name} is paused") if Chromecast.paused?
20
17
  #
18
+ # @!attribute [r] state
19
+ # @return [PlayPauseType, RewindFastforwardType, nil]
20
+ #
21
+ # @!attribute [r] was
22
+ # @return [PlayPauseType, RewindFastforwardType, nil]
23
+ # @since openHAB 5.0
24
+ #
25
+ # @!method play?
26
+ # Check if the item state == {PLAY}
27
+ # @return [true,false]
28
+ #
29
+ # @!method paused?
30
+ # Check if the item state == {PAUSE}
31
+ # @return [true,false]
32
+ #
33
+ # @!method rewinding?
34
+ # Check if the item state == {REWIND}
35
+ # @return [true,false]
36
+ #
37
+ # @!method fast_forwarding?
38
+ # Check if the item state == {FASTFORWARD}
39
+ # @return [true,false]
40
+ #
41
+ # @!method was_playing?
42
+ # Check if {#was} is {PLAY}
43
+ # @return [true, false]
44
+ #
45
+ # @!method was_paused?
46
+ # Check if {#was} is {PAUSE}
47
+ # @return [true, false]
48
+ #
49
+ # @!method was_rewinding?
50
+ # Check if {#was} is {REWIND}
51
+ # @return [true, false]
52
+ #
53
+ # @!method was_fast_forwarding?
54
+ # Check if {#was} is {FASTFORWARD}
55
+ # @return [true, false]
56
+ #
57
+ # @!method play
58
+ # Send the {PLAY} command to the item
59
+ # @return [PlayerItem] `self`
60
+ #
61
+ # @!method play!
62
+ # Send the {PLAY} command to the item, even when {OpenHAB::DSL.ensure_states! ensure_states!} is in effect.
63
+ # @return [PlayerItem] `self`
64
+ #
65
+ # @!method pause
66
+ # Send the {PAUSE} command to the item
67
+ # @return [PlayerItem] `self`
68
+ #
69
+ # @!method pause!
70
+ # Send the {PAUSE} command to the item, even when {OpenHAB::DSL.ensure_states! ensure_states!} is in effect.
71
+ # @return [PlayerItem] `self`
72
+ #
73
+ # @!method rewind
74
+ # Send the {REWIND} command to the item
75
+ # @return [PlayerItem] `self`
76
+ #
77
+ # @!method rewind!
78
+ # Send the {REWIND} command to the item, even when {OpenHAB::DSL.ensure_states! ensure_states!} is in effect.
79
+ # @return [PlayerItem] `self`
80
+ #
81
+ # @!method fast_forward
82
+ # Send the {FASTFORWARD} command to the item
83
+ # @return [PlayerItem] `self`
84
+ #
85
+ # @!method fast_forward!
86
+ # Send the {FASTFORWARD} command to the item, even when
87
+ # {OpenHAB::DSL.ensure_states! ensure_states!} is in effect.
88
+ # @return [PlayerItem] `self`
89
+ #
90
+ # @!method next
91
+ # Send the {NEXT} command to the item
92
+ # @return [PlayerItem] `self`
93
+ #
94
+ # @!method previous
95
+ # Send the {PREVIOUS} command to the item
96
+ # @return [PlayerItem] `self`
97
+ #
21
98
  class PlayerItem < GenericItem
22
- # @!method play?
23
- # Check if the item state == {PLAY}
24
- # @return [true,false]
25
-
26
- # @!method paused?
27
- # Check if the item state == {PAUSE}
28
- # @return [true,false]
29
-
30
- # @!method rewinding?
31
- # Check if the item state == {REWIND}
32
- # @return [true,false]
33
-
34
- # @!method fast_forwarding?
35
- # Check if the item state == {FASTFORWARD}
36
- # @return [true,false]
37
-
38
- # @!method play
39
- # Send the {PLAY} command to the item
40
- # @return [PlayerItem] `self`
41
-
42
- # @!method play!
43
- # Send the {PLAY} command to the item, even when {OpenHAB::DSL.ensure_states! ensure_states!} is in effect.
44
- # @return [PlayerItem] `self`
45
-
46
- # @!method pause
47
- # Send the {PAUSE} command to the item
48
- # @return [PlayerItem] `self`
49
-
50
- # @!method pause!
51
- # Send the {PAUSE} command to the item, even when {OpenHAB::DSL.ensure_states! ensure_states!} is in effect.
52
- # @return [PlayerItem] `self`
53
-
54
- # @!method rewind
55
- # Send the {REWIND} command to the item
56
- # @return [PlayerItem] `self`
57
-
58
- # @!method rewind!
59
- # Send the {REWIND} command to the item, even when {OpenHAB::DSL.ensure_states! ensure_states!} is in effect.
60
- # @return [PlayerItem] `self`
61
-
62
- # @!method fast_forward
63
- # Send the {FASTFORWARD} command to the item
64
- # @return [PlayerItem] `self`
65
-
66
- # @!method fast_forward!
67
- # Send the {FASTFORWARD} command to the item, even when
68
- # {OpenHAB::DSL.ensure_states! ensure_states!} is in effect.
69
- # @return [PlayerItem] `self`
70
-
71
- # @!method next
72
- # Send the {NEXT} command to the item
73
- # @return [PlayerItem] `self`
74
-
75
- # @!method previous
76
- # Send the {PREVIOUS} command to the item
77
- # @return [PlayerItem] `self`
78
99
  end
79
100
  end
80
101
  end
@@ -16,6 +16,10 @@ module OpenHAB
16
16
  # @!attribute [r] state
17
17
  # @return [PercentType, UpDownType, nil]
18
18
  #
19
+ # @!attribute [r] was
20
+ # @return [PercentType, UpDownType, nil]
21
+ # @since openHAB 5.0
22
+ #
19
23
  # @example Roll up all Rollershutters in a group
20
24
  # Shutters.up
21
25
  #
@@ -37,6 +41,14 @@ module OpenHAB
37
41
  # Check if the item state == {DOWN}
38
42
  # @return [true,false]
39
43
 
44
+ # @!method was_up?
45
+ # Check if {#was} is {UP}
46
+ # @return [true, false]
47
+
48
+ # @!method was_down?
49
+ # Check if {#was} is {DOWN}
50
+ # @return [true, false]
51
+
40
52
  # @!method up
41
53
  # Send the {UP} command to the item
42
54
  # @return [RollershutterItem] `self`
@@ -543,17 +543,14 @@ module OpenHAB
543
543
  # @example Given a A/V receiver's input item, search for its power item
544
544
  # FamilyReceiver_Input.points(Semantics::Switch) # => [FamilyReceiver_Switch]
545
545
  #
546
- # @param [SemanticTag] point_or_property_types
547
- # Pass 1 or 2 classes that are sub-classes of {Point} or {Property}.
548
- # Note that when comparing against semantic tags, it does a sub-class check.
549
- # So if you search for [Control], you'll get items tagged with [Switch].
546
+ # @param (see Enumerable#points)
550
547
  # @return [Array<Item>]
551
548
  #
552
- def points(*point_or_property_types)
553
- return members.points(*point_or_property_types) if equipment? || location?
549
+ def points(...)
550
+ return members.points(...) if equipment? || location?
554
551
 
555
552
  # automatically search the parent equipment (or location?!) for sibling points
556
- result = (equipment || location)&.points(*point_or_property_types) || []
553
+ result = (equipment || location)&.points(...) || []
557
554
  result.delete(self)
558
555
  result
559
556
  end
@@ -572,6 +569,14 @@ module Enumerable
572
569
 
573
570
  #
574
571
  # Returns a new array of items that are a semantics Location (optionally of one of the given types)
572
+ #
573
+ # @param [SemanticTag] types
574
+ # Pass 1 or more classes that are sub-classes of {Semantics::Location Semantics::Location}.
575
+ # Note that when comparing against semantic tags, it does a sub-class check by default.
576
+ # So if you search for [Room], you'll get items tagged with [LivingRoom], [Kitchen], etc.
577
+ # @param [true, false] subclasses
578
+ # If true, match all subclasses of the given types.
579
+ # If false, only match the exact type.
575
580
  # @return [Array<Item>]
576
581
  #
577
582
  # @example Get all rooms
@@ -580,7 +585,7 @@ module Enumerable
580
585
  # @example Get all bedrooms and bathrooms
581
586
  # items.locations(Semantics::Bedroom, Semantics::Bathroom)
582
587
  #
583
- def locations(*types)
588
+ def locations(*types, subclasses: true)
584
589
  begin
585
590
  raise ArgumentError unless types.all? { |type| type < Semantics::Location }
586
591
  rescue ArgumentError, TypeError
@@ -588,7 +593,13 @@ module Enumerable
588
593
  end
589
594
 
590
595
  result = select(&:location?)
591
- result.select! { |i| types.any? { |type| i.location_type <= type } } unless types.empty?
596
+ unless types.empty?
597
+ if subclasses
598
+ result.select! { |i| types.any? { |type| i.location_type <= type } }
599
+ else
600
+ result.select! { |i| types.include?(i.location_type) }
601
+ end
602
+ end
592
603
 
593
604
  result
594
605
  end
@@ -602,6 +613,13 @@ module Enumerable
602
613
  # that belong to the {Semantics::Equipment equipments}, use {#members}
603
614
  # before calling {#points}. See the example with {#points}.
604
615
  #
616
+ # @param [SemanticTag] types
617
+ # Pass 1 or more classes that are sub-classes of {Semantics::Equipment Semantics::Equipment}.
618
+ # Note that when comparing against semantic tags, it does a sub-class check by default.
619
+ # So if you search for [Fan], you'll get items tagged with [CeilingFan], [KitchenHood], etc.
620
+ # @param [true, false] subclasses
621
+ # If true, match all subclasses of the given types.
622
+ # If false, only match the exact type.
605
623
  # @return [Array<Item>]
606
624
  #
607
625
  # @example Get all TVs in a room
@@ -610,7 +628,7 @@ module Enumerable
610
628
  # @example Get all TVs and Speakers in a room
611
629
  # lGreatRoom.equipments(Semantics::Television, Semantics::Speaker)
612
630
  #
613
- def equipments(*types)
631
+ def equipments(*types, subclasses: true)
614
632
  begin
615
633
  raise ArgumentError unless types.all? { |type| type < Semantics::Equipment }
616
634
  rescue ArgumentError, TypeError
@@ -618,13 +636,27 @@ module Enumerable
618
636
  end
619
637
 
620
638
  result = select(&:equipment?)
621
- result.select! { |i| types.any? { |type| i.equipment_type <= type } } unless types.empty?
639
+ unless types.empty?
640
+ if subclasses
641
+ result.select! { |i| types.any? { |type| i.equipment_type <= type } }
642
+ else
643
+ result.select! { |i| types.include?(i.equipment_type) }
644
+ end
645
+ end
622
646
 
623
647
  result
624
648
  end
625
649
 
626
650
  # Returns a new array of items that are semantics points (optionally of a given type)
627
651
  #
652
+ # @param [SemanticTag] point_or_property_types
653
+ # Pass 1 or 2 classes that are sub-classes of {Semantics::Point Semantics::Point} or
654
+ # {Semantics::Property Semantics::Property}.
655
+ # Note that when comparing against semantic tags, it does a sub-class check by default.
656
+ # So if you search for [Control], you'll get items tagged with [Switch].
657
+ # @param [true, false] subclasses
658
+ # If true, match all subclasses of the given types.
659
+ # If false, only match the exact type.
628
660
  # @return [Array<Item>]
629
661
  #
630
662
  # @example Get all the power switch items for every equipment in a room
@@ -632,7 +664,7 @@ module Enumerable
632
664
  #
633
665
  # @see #members
634
666
  #
635
- def points(*point_or_property_types)
667
+ def points(*point_or_property_types, subclasses: true)
636
668
  unless (0..2).cover?(point_or_property_types.length)
637
669
  raise ArgumentError, "wrong number of arguments (given #{point_or_property_types.length}, expected 0..2)"
638
670
  end
@@ -652,8 +684,13 @@ module Enumerable
652
684
 
653
685
  select do |point|
654
686
  point.point? && point_or_property_types.all? do |tag|
655
- (tag < Semantics::Point && point.point_type&.<=(tag)) ||
656
- (tag < Semantics::Property && point.property_type&.<=(tag))
687
+ if subclasses
688
+ (tag < Semantics::Point && point.point_type&.<=(tag)) ||
689
+ (tag < Semantics::Property && point.property_type&.<=(tag))
690
+ else
691
+ (tag < Semantics::Point && point.point_type == tag) ||
692
+ (tag < Semantics::Property && point.property_type == tag)
693
+ end
657
694
  end
658
695
  end
659
696
  end
@@ -14,6 +14,10 @@ module OpenHAB
14
14
  # @!attribute [r] state
15
15
  # @return [StringType, nil]
16
16
  #
17
+ # @!attribute [r] was
18
+ # @return [StringType, nil]
19
+ # @since openHAB 5.0
20
+ #
17
21
  # @example
18
22
  # # StringOne has a current state of "Hello"
19
23
  # StringOne << StringOne + " World!"
@@ -14,6 +14,9 @@ module OpenHAB
14
14
  # @!attribute [r] state
15
15
  # @return [OnOffType, nil]
16
16
  #
17
+ # @!attribute [r] was
18
+ # @return [OnOffType, nil]
19
+ # @since openHAB 5.0
17
20
  #
18
21
  # @example Turn on all switches in a `Group:Switch` called Switches
19
22
  # Switches.on
@@ -76,6 +79,14 @@ module OpenHAB
76
79
  # @!method off!
77
80
  # Send the {OFF} command to the item, even when {OpenHAB::DSL.ensure_states! ensure_states!} is in effect.
78
81
  # @return [SwitchItem] `self`
82
+
83
+ # @!method was_on?
84
+ # Check if {#was} is (implicitly convertible to) {ON}
85
+ # @return [true, false]
86
+
87
+ # @!method was_off?
88
+ # Check if {#was} is (implicitly convertible to) {OFF}
89
+ # @return [true, false]
79
90
  end
80
91
  end
81
92
  end
@@ -49,6 +49,10 @@ module OpenHAB
49
49
  def #{state_predicate} # def on?
50
50
  raw_state.as(#{state.class.java_class.simple_name}).equal?(#{state}) # raw_state.as(OnOffType) == ON
51
51
  end # end
52
+
53
+ def was_#{state_predicate} # def was_on?
54
+ last_state.as(#{state.class.java_class.simple_name}).equal?(#{state}) # last_state.as(OnOffType) == ON
55
+ end # end
52
56
  RUBY
53
57
  end
54
58
  end
@@ -45,6 +45,7 @@ module OpenHAB
45
45
  #
46
46
  # @!visibility private
47
47
  def initialize(time, id:, thread_locals:, block:)
48
+ @managed = true
48
49
  @time = time
49
50
  @id = id
50
51
  @thread_locals = thread_locals
@@ -95,7 +96,7 @@ module OpenHAB
95
96
  # @!visibility private
96
97
  def reschedule!(time = nil)
97
98
  Thread.current[:openhab_rescheduled_timer] = true if Thread.current[:openhab_rescheduled_timer] == self
98
- DSL.timers.add(self)
99
+ DSL.timers.add(self) if managed?
99
100
  @timer.reschedule(new_execution_time(time || @time))
100
101
  self
101
102
  end
@@ -122,6 +123,79 @@ module OpenHAB
122
123
  @timer.cancel
123
124
  end
124
125
 
126
+ #
127
+ # Returns the openHAB Timer object.
128
+ #
129
+ # This can be used to share the timer with other scripts via {OpenHAB::DSL.shared_cache shared_cache}.
130
+ # The other scripts can be other JRuby scripts or scripts written in a different language
131
+ # such as JSScripting, Python, etc. which can either be file-based or UI based scripts.
132
+ #
133
+ # Timers are normally managed by TimerManager, and are normally automatically cancelled
134
+ # when the script unloads/reloads.
135
+ #
136
+ # To disable this automatic timer cancellation at script unload, call {unmanage}.
137
+ #
138
+ # openHAB will cancel the timer stored in the {OpenHAB::DSL.shared_cache shared_cache}
139
+ # and remove the cache entry when all the scripts that _had accessed_ it have been unloaded.
140
+ #
141
+ # @return [org.openhab.core.automation.module.script.action.Timer]
142
+ #
143
+ # @see unmanage
144
+ #
145
+ # @example
146
+ # # script1.rb:
147
+ # timer = after(10.hours) { logger.warn "Timer created in script1.rb fired" }
148
+ # shared_cache[:script1_timer] = timer.to_java
149
+ #
150
+ # # script2.js: (JavaScript!)
151
+ # rules.when().item("TestSwitch1").receivedCommand().then(event => {
152
+ # cache.shared.get("script1_timer")?.cancel()
153
+ # })
154
+ #
155
+ # # or in Ruby script2.rb
156
+ # received_command(TestSwitch2, command: ON) do
157
+ # # This is an openHAB timer object, not a JRuby timer object
158
+ # # the reschedule method expects a ZonedDateTime
159
+ # shared_cache[:script1_timer]&.reschedule(3.seconds.from_now)
160
+ # end
161
+ #
162
+ # @!visibility private
163
+ def to_java
164
+ @timer
165
+ end
166
+
167
+ #
168
+ # Removes the timer from the {TimerManager TimerManager}
169
+ #
170
+ # The effects of calling this method are:
171
+ # - The timer will no longer be automatically cancelled when the script unloads.
172
+ # It will still execute as scheduled even if the script is unloaded/removed.
173
+ # - It can no longer be referenced by its `id`.
174
+ # - Subsequent calls to {OpenHAB::DSL.after after} with the same `id` will create a separate new timer.
175
+ # - Normal timer operations such as {reschedule}, {cancel}, etc. will still work.
176
+ #
177
+ # @return [org.openhab.core.automation.module.script.action.Timer] The openHAB Timer object
178
+ #
179
+ # @example
180
+ # timer = after(10.hours) { logger.warn "Timer created in script1.rb fired" }
181
+ # shared_cache[:script1_timer] = timer
182
+ # # Don't cancel the timer when this script unloads,
183
+ # # but openHAB will do it if all scripts that had referenced the shared cache are unloaded.
184
+ # timer.unmanage
185
+ #
186
+ def unmanage
187
+ @managed = false
188
+ DSL.timers.delete(self)
189
+ @id = nil
190
+ @timer
191
+ end
192
+
193
+ # @return [true,false] True if the timer is managed by TimerManager, false otherwise.
194
+ # A timer is managed by default, and becomes un-managed when {unmanage} is called.
195
+ def managed?
196
+ @managed
197
+ end
198
+
125
199
  private
126
200
 
127
201
  #
@@ -47,7 +47,7 @@ module OpenHAB
47
47
  module ValueCache
48
48
  # @see https://docs.ruby-lang.org/en/master/Hash.html#method-i-5B-5D Hash#[]
49
49
  def [](key)
50
- get(key.to_s)
50
+ get(key)
51
51
  end
52
52
 
53
53
  #
@@ -58,18 +58,18 @@ module OpenHAB
58
58
  # @return [Object] new value or current value
59
59
  #
60
60
  def compute_if_absent(key, &)
61
- get(key.to_s, &)
61
+ get(key, &)
62
62
  end
63
63
 
64
64
  # @see https://docs.ruby-lang.org/en/master/Hash.html#method-i-5B-5D-3D Hash#[]=
65
65
  def []=(key, value)
66
- put(key.to_s, value)
66
+ put(key, value)
67
67
  end
68
68
  alias_method :store, :[]
69
69
 
70
70
  # @see https://docs.ruby-lang.org/en/master/Hash.html#method-i-delete Hash#delete
71
71
  def delete(key)
72
- key = key.to_s
72
+ key = key.to_s # needed for remove below
73
73
  if block_given?
74
74
  fetch(key) do
75
75
  return yield(key)
@@ -89,8 +89,8 @@ module OpenHAB
89
89
  "wrong number of arguments (given #{default_value.length + 1}, expected 0..1)"
90
90
  end
91
91
 
92
- key = key.to_s
93
92
  if default_value.empty?
93
+ key = key.to_s
94
94
  if block_given?
95
95
  get(key) do
96
96
  return yield(key)
@@ -184,6 +184,44 @@ module OpenHAB
184
184
  self[k]
185
185
  end
186
186
  end
187
+
188
+ #
189
+ # Converts values before storing them in the cache.
190
+ #
191
+ # This is used to convert JRuby timers created with {OpenHAB::DSL.after} to openHAB timers.
192
+ #
193
+ # Because we generally can't store JRuby objects in the shared cache,
194
+ # we can convert other things to Java objects here too as necessary.
195
+ #
196
+ # @!visibility private
197
+ module ValueConverter
198
+ # @!visibility private
199
+ def get(key, &)
200
+ key = key.to_s
201
+ return super(key) unless block_given?
202
+
203
+ super do
204
+ convert(yield(key))
205
+ end
206
+ end
207
+
208
+ # @!visibility private
209
+ def put(key, value)
210
+ key = key.to_s
211
+ value = convert(value)
212
+ super
213
+ end
214
+
215
+ private
216
+
217
+ def convert(value)
218
+ value.respond_to?(:to_java) ? value.to_java : value
219
+ end
220
+ end
221
+
222
+ # We use prepend here instead of overriding the methods inside ValueCache module/interface
223
+ # because the methods are defined in the implementation class
224
+ $sharedCache.class.prepend(ValueConverter)
187
225
  end
188
226
  end
189
227
  end
data/lib/openhab/core.rb CHANGED
@@ -13,6 +13,8 @@ module OpenHAB
13
13
  V4_2 = Gem::Version.new("4.2.0").freeze
14
14
  # @!visibility private
15
15
  V4_3 = Gem::Version.new("4.3.0").freeze
16
+ # @!visibility private
17
+ V5_0 = Gem::Version.new("5.0.0").freeze
16
18
 
17
19
  # @return [Gem::Version] Returns the current openHAB version as a Gem::Version object
18
20
  # Note, this strips off snapshots, milestones and RC versions and returns the release version.
@@ -21,6 +23,21 @@ module OpenHAB
21
23
  @version ||= Gem::Version.new(VERSION).release.freeze
22
24
  end
23
25
 
26
+ #
27
+ # Returns the full version of openHAB
28
+ #
29
+ # The {version} method returns the release version, stripping off any
30
+ # additional qualifiers such as M1, or snapshots.
31
+ # This method returns the full version string, including the qualifiers.
32
+ #
33
+ # @return [Gem::Version] Returns the full version of openHAB
34
+ #
35
+ # @!visibility private
36
+ #
37
+ def self.full_version
38
+ @full_version ||= Gem::Version.new(VERSION).freeze
39
+ end
40
+
24
41
  raise "`openhab-scripting` requires openHAB >= 4.1.0" unless version >= V4_1
25
42
 
26
43
  # @return [Integer] Number of seconds to wait between checks for automation manager