openhab-scripting 5.28.0 → 5.30.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.
Files changed (33) hide show
  1. checksums.yaml +4 -4
  2. data/lib/openhab/core/actions/notification.rb +5 -5
  3. data/lib/openhab/core/events/abstract_event.rb +9 -2
  4. data/lib/openhab/core/events/channel_triggered_event.rb +7 -0
  5. data/lib/openhab/core/events/item_command_event.rb +7 -0
  6. data/lib/openhab/core/events/item_state_changed_event.rb +8 -0
  7. data/lib/openhab/core/events/item_state_event.rb +7 -0
  8. data/lib/openhab/core/events/thing_status_info_event.rb +16 -0
  9. data/lib/openhab/core/events/timer_event.rb +23 -0
  10. data/lib/openhab/core/events.rb +8 -0
  11. data/lib/openhab/core/items/generic_item.rb +9 -4
  12. data/lib/openhab/core/items/group_item.rb +3 -3
  13. data/lib/openhab/core/types/date_time_type.rb +11 -0
  14. data/lib/openhab/core_ext/java/instant.rb +138 -2
  15. data/lib/openhab/core_ext/java/local_date.rb +9 -0
  16. data/lib/openhab/core_ext/java/local_time.rb +9 -0
  17. data/lib/openhab/core_ext/java/month.rb +10 -0
  18. data/lib/openhab/core_ext/java/month_day.rb +9 -0
  19. data/lib/openhab/core_ext/java/time.rb +2 -0
  20. data/lib/openhab/core_ext/java/zoned_date_time.rb +54 -6
  21. data/lib/openhab/core_ext/ruby/date.rb +21 -0
  22. data/lib/openhab/core_ext/ruby/date_time.rb +6 -0
  23. data/lib/openhab/core_ext/ruby/time.rb +11 -2
  24. data/lib/openhab/dsl/items/ensure.rb +11 -8
  25. data/lib/openhab/dsl/items/timed_command.rb +22 -13
  26. data/lib/openhab/dsl/rules/builder.rb +1 -1
  27. data/lib/openhab/dsl/rules/triggers/channel.rb +2 -2
  28. data/lib/openhab/dsl/sitemaps/builder.rb +1 -1
  29. data/lib/openhab/dsl/thread_local.rb +1 -1
  30. data/lib/openhab/dsl/version.rb +1 -1
  31. data/lib/openhab/dsl.rb +1 -1
  32. data/lib/openhab/osgi.rb +1 -1
  33. metadata +3 -3
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c99d5ea37c52514af7dfa8b5316c8b8d196f46dac4fb4ce557ac01963de8bb51
4
- data.tar.gz: d2ee0a5934babcd911d2584ba7e20f73164e568108657121870422fa247d6c6f
3
+ metadata.gz: ec8c705070eb9fa18e95281eb9247ec66e53b9656810f785cf1690ae7b9812f5
4
+ data.tar.gz: 32fbf23e31ab742a9799579d51a4fa3e5b59274a771321129af1f085ed6170ed
5
5
  SHA512:
6
- metadata.gz: c353e9b41fb5e11e95eaf2fe8d048caf7b3aa6873c7a6017a0d099f4738a67c8b2ae1015d2a2c2248a2089ebb42d7fafecafcc5613998d39739fcf44dd77ee92
7
- data.tar.gz: a54105ab52153231d2ee7871f74ae5d1743c23c18fa582a09114e15c754a0ccd86bd9772298352f7c9d4f191aa8f47b3807ca20919473f1795da4e0e3d643f2e
6
+ metadata.gz: 66643a7bf0b77e60d0277ece16fa71a8f8fe51f3a84d26c38b726e76dcfaf336a11c6688b26a5a426569f98ee1ffee97f53491084959c00f217bb3468866f579
7
+ data.tar.gz: 0a8d019c6de74b12183382680334606103be3878cd0b4ac2785df324fc82301e81d2f9c3e9b9d5ae747bb612439c2b25260106a5fd3bd092d83303015cde2e0d
@@ -4,14 +4,14 @@ module OpenHAB
4
4
  module Core
5
5
  module Actions
6
6
  #
7
- # Provides methods for {https://next.openhab.org/addons/integrations/openhabcloud/#cloud-notification-actions
7
+ # Provides methods for {https://www.openhab.org/addons/integrations/openhabcloud/#cloud-notification-actions
8
8
  # openHAB Cloud Notification Actions}.
9
9
  #
10
10
  class Notification
11
11
  class << self
12
12
  #
13
13
  # Send a notification using
14
- # {https://next.openhab.org/addons/integrations/openhabcloud/#cloud-notification-actions
14
+ # {https://www.openhab.org/addons/integrations/openhabcloud/#cloud-notification-actions
15
15
  # openHAB Cloud Notification Action}.
16
16
  #
17
17
  # @param msg [String] The message to send.
@@ -27,7 +27,7 @@ module OpenHAB
27
27
  # Subsequent notifications using the same reference ID will
28
28
  # update/overwrite the existing notification with the same ID.
29
29
  # @param on_click [String, nil] The action to be performed when the user clicks on the notification.
30
- # Specified using the {https://next.openhab.org/addons/integrations/openhabcloud/#action-syntax
30
+ # Specified using the {https://www.openhab.org/addons/integrations/openhabcloud/#action-syntax
31
31
  # action syntax}.
32
32
  # @param attachment [String, Item, nil] The URL of the media attachment to be displayed with the notification.
33
33
  # This can either be a fully qualified URL, prefixed with
@@ -36,7 +36,7 @@ module OpenHAB
36
36
  # or an image item.
37
37
  # @param buttons [Array<String>, Hash<String, String>, nil] Buttons to include in the notification.
38
38
  # - In array form, each element is specified as `Title=$action`, where `$action` follows the
39
- # {https://next.openhab.org/addons/integrations/openhabcloud/#action-syntax action syntax}.
39
+ # {https://www.openhab.org/addons/integrations/openhabcloud/#action-syntax action syntax}.
40
40
  # - In hash form, the keys are the button titles and the values are the actions.
41
41
  #
42
42
  # The maximum number of buttons is 3.
@@ -91,7 +91,7 @@ module OpenHAB
91
91
  tag ||= severity
92
92
  args.push(msg.to_s, icon&.to_s, tag&.to_s)
93
93
 
94
- # @!deprecated OH 4.1
94
+ # @deprecated OH 4.1
95
95
  if Core.version >= Core::V4_2
96
96
  buttons ||= []
97
97
  buttons = buttons.map { |title, action| "#{title}=#{action}" } if buttons.is_a?(Hash)
@@ -13,8 +13,8 @@ module OpenHAB
13
13
  # @return [Hash]
14
14
  attr_accessor :inputs
15
15
 
16
- # @return [String]
17
- alias_method :inspect, :to_s
16
+ # @!attribute [r] source
17
+ # @return [String] The component that sent the event.
18
18
 
19
19
  #
20
20
  # Returns the event payload as a Hash.
@@ -26,6 +26,13 @@ module OpenHAB
26
26
  require "json"
27
27
  @payload ||= JSON.parse(get_payload, symbolize_names: true) unless get_payload.empty?
28
28
  end
29
+
30
+ # @return [String]
31
+ def inspect
32
+ s = "#<OpenHAB::Core::Events::#{self.class.simple_name} topic=#{topic} payload=#{payload.inspect}"
33
+ s += " source=#{source.inspect}" if source
34
+ "#{s}>"
35
+ end
29
36
  end
30
37
  end
31
38
  end
@@ -24,6 +24,13 @@ module OpenHAB
24
24
 
25
25
  # @!attribute [r] event
26
26
  # @return [String] The event data
27
+
28
+ # @return [String]
29
+ def inspect
30
+ s = "#<OpenHAB::Core::Events::ChannelTriggeredEvent channel=#{channel} event=#{event.inspect}"
31
+ s += " source=#{source.inspect}" if source
32
+ "#{s}>"
33
+ end
27
34
  end
28
35
  end
29
36
  end
@@ -72,6 +72,13 @@ module OpenHAB
72
72
  # @!method previous?
73
73
  # Check if {#command} is {PREVIOUS}
74
74
  # @return [true, false]
75
+
76
+ # @return [String]
77
+ def inspect
78
+ s = "#<OpenHAB::Core::Events::ItemCommandEvent item=#{itemName} command=#{command.inspect}"
79
+ s += " source=#{source.inspect}" if source
80
+ "#{s}>"
81
+ end
75
82
  end
76
83
  end
77
84
  end
@@ -69,6 +69,14 @@ module OpenHAB
69
69
  def was
70
70
  old_item_state if was?
71
71
  end
72
+
73
+ # @return [String]
74
+ def inspect
75
+ s = "#<OpenHAB::Core::Events::ItemStateChangedEvent item=#{item_name} " \
76
+ "state=#{item_state.inspect} was=#{old_item_state.inspect}"
77
+ s += " source=#{source.inspect}" if source
78
+ "#{s}>"
79
+ end
72
80
  end
73
81
  end
74
82
  end
@@ -68,6 +68,13 @@ module OpenHAB
68
68
  def state
69
69
  item_state if state?
70
70
  end
71
+
72
+ # @return [String]
73
+ def inspect
74
+ s = "#<OpenHAB::Core::Events::#{self.class.simple_name} item=#{item_name} state=#{item_state.inspect}"
75
+ s += " source=#{source.inspect}" if source
76
+ "#{s}>"
77
+ end
71
78
  end
72
79
 
73
80
  # {AbstractEvent} sent when an item's state is updated (regardless of if it changed or not).
@@ -27,6 +27,14 @@ module OpenHAB
27
27
  def thing
28
28
  EntityLookup.lookup_thing(uid)
29
29
  end
30
+
31
+ # @return [String]
32
+ def inspect
33
+ s = "#<OpenHAB::Core::Events::ThingStatusInfoChangedEvent thing=#{uid} " \
34
+ "status=#{status.inspect} was=#{was.inspect}"
35
+ s += " source=#{source.inspect}" if source
36
+ "#{s}>"
37
+ end
30
38
  end
31
39
 
32
40
  # The {AbstractEvent} sent when a {Things::Thing}'s status is set.
@@ -49,6 +57,14 @@ module OpenHAB
49
57
  def thing
50
58
  EntityLookup.lookup_thing(uid)
51
59
  end
60
+
61
+ # @return [String]
62
+ def inspect
63
+ s = "#<OpenHAB::Core::Events::ThingStatusInfoEvent thing=#{uid} " \
64
+ "status=#{status.inspect}"
65
+ s += " source=#{source.inspect}" if source
66
+ "#{s}>"
67
+ end
52
68
  end
53
69
  end
54
70
  end
@@ -34,6 +34,29 @@ module OpenHAB
34
34
  payload&.[](:itemName)&.then { |item_name| EntityLookup.lookup_item(item_name) }
35
35
  end
36
36
 
37
+ #
38
+ # @!attribute [r] time_only?
39
+ # @return [Boolean]
40
+ # `true` when this event was triggered by a {Core::Items::DateTimeItem DateTimeItem} with `timeOnly` set.
41
+ # `false` when this event wasn't triggered by a DateTimeItem or the `timeOnly` flag is not set.
42
+ # @see DSL::Rules::BuilderDSL::every #every trigger
43
+ # @see DSL::Rules::BuilderDSL::at #at trigger
44
+ # @since openHAB 4.3
45
+ #
46
+ def time_only?
47
+ !!payload&.[](:timeOnly)
48
+ end
49
+
50
+ #
51
+ # @!attribute [r] offset
52
+ # @return [Duration, nil] The offset from the configured time for this DateTime trigger event.
53
+ # `nil` when this event wasn't triggered by a DateTime trigger.
54
+ # @since openHAB 4.3
55
+ #
56
+ def offset
57
+ payload&.[](:offset)&.seconds
58
+ end
59
+
37
60
  #
38
61
  # @!attribute [r] time
39
62
  # @return [LocalTime, nil] The configured time for this TimeOfDay trigger event.
@@ -5,6 +5,14 @@ module OpenHAB
5
5
  # Contains objects sent to event handlers containing context around the
6
6
  # triggered event.
7
7
  module Events
8
+ class << self
9
+ # @!visibility private
10
+ def publisher
11
+ @publisher ||= OSGi.service("org.openhab.core.events.EventPublisher")
12
+ end
13
+ end
14
+
15
+ java_import org.openhab.core.items.events.ItemEventFactory
8
16
  end
9
17
  end
10
18
  end
@@ -137,6 +137,7 @@ module OpenHAB
137
137
  # @param [Command, #to_s] command command to send to the item.
138
138
  # When given a {Command} argument, it will be passed directly.
139
139
  # Otherwise, the result of `#to_s` will be parsed into a {Command}.
140
+ # @param [String, nil] source Optional string to identify what sent the event.
140
141
  # @return [self, nil] nil when `ensure` is in effect and the item was already in the same state,
141
142
  # otherwise the item.
142
143
  #
@@ -156,10 +157,14 @@ module OpenHAB
156
157
  # @example Sending a string to a dimensioned {NumberItem}
157
158
  # SetTemperature.command("22.5 °C") # The string will be parsed and converted to a QuantityType
158
159
  #
159
- def command(command)
160
+ def command(command, source: nil)
160
161
  command = format_command(command)
161
- logger.trace "Sending Command #{command} to #{name}"
162
- $events.send_command(self, command)
162
+ logger.trace { "Sending Command #{command} to #{name}" }
163
+ if source
164
+ Events.publisher.post(Events::ItemEventFactory.create_command_event(name, command, source.to_s))
165
+ else
166
+ $events.send_command(self, command)
167
+ end
163
168
  Proxy.new(self)
164
169
  end
165
170
  alias_method :command!, :command
@@ -201,7 +206,7 @@ module OpenHAB
201
206
  #
202
207
  def update(state)
203
208
  state = format_update(state)
204
- logger.trace "Sending Update #{state} to #{name}"
209
+ logger.trace { "Sending Update #{state} to #{name}" }
205
210
  $events.post_update(self, state)
206
211
  Proxy.new(self)
207
212
  end
@@ -131,10 +131,10 @@ module OpenHAB
131
131
 
132
132
  # Override because we want to send them to the base item if possible
133
133
  %i[command update].each do |method|
134
- define_method(method) do |command|
135
- return base_item.__send__(method, command) if base_item
134
+ define_method(method) do |command, **kwargs|
135
+ return base_item.__send__(method, command, **kwargs) if base_item
136
136
 
137
- super(command)
137
+ super(command, **kwargs)
138
138
  end
139
139
  end
140
140
 
@@ -51,6 +51,17 @@ module OpenHAB
51
51
  zoned_date_time
52
52
  end
53
53
 
54
+ # @!visibility private
55
+ def to_instant(_context = nil)
56
+ # @deprecated OH 3.4 getInstant() was added in OH 4.0
57
+ return get_instant if respond_to?(:get_instant)
58
+
59
+ zoned_date_time.to_instant
60
+ end
61
+
62
+ # @!method to_instant
63
+ # @return [Instant]
64
+
54
65
  # act like a Ruby Time
55
66
  def_delegator :zoned_date_time, :month_value, :month
56
67
  def_delegator :zoned_date_time, :day_of_month, :mday
@@ -1,12 +1,148 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "forwardable"
4
+
5
+ require_relative "time"
6
+
3
7
  module OpenHAB
4
8
  module CoreExt
5
9
  module Java
6
- java_import java.time.Instant
10
+ Instant = java.time.Instant
7
11
 
8
12
  # Extensions to {java.time.Instant}
9
- class Instant < java.lang.Object; end
13
+ class Instant < java.lang.Object
14
+ extend Forwardable
15
+ include Time
16
+ include Between
17
+
18
+ class << self # rubocop:disable Lint/EmptyClass
19
+ # @!scope class
20
+
21
+ # @!attribute [r] now
22
+ # @return [Instant]
23
+
24
+ # @!method parse(text, formatter = nil)
25
+ # Parses a string into an Instant object.
26
+ #
27
+ # @param [String] text The text to parse.
28
+ # @param [java.time.format.DateTimeFormatter] formatter The formatter to use.
29
+ # @return [Instant]
30
+ end
31
+
32
+ # @!scope instance
33
+
34
+ # @!method to_local_time
35
+ # @return [LocalTime]
36
+ # @!method to_local_date
37
+ # @return [LocalDate]
38
+ # @!method to_month_day
39
+ # @return [MonthDay]
40
+ # @!method to_date
41
+ # @return [Date]
42
+ # @!method to_month
43
+ # @return [Month]
44
+ # @!method yesterday?
45
+ # (see OpenHAB::CoreExt::Java::ZonedDateTime#yesterday?)
46
+ # @!method today?
47
+ # (see OpenHAB::CoreExt::Java::ZonedDateTime#today?)
48
+ # @!method tomorrow?
49
+ # (see OpenHAB::CoreExt::Java::ZonedDateTime#tomorrow?)
50
+ def_delegators :to_zoned_date_time,
51
+ :to_local_time,
52
+ :to_local_date,
53
+ :to_date,
54
+ :to_month_day,
55
+ :to_month,
56
+ :yesterday?,
57
+ :today?,
58
+ :tomorrow?
59
+
60
+ # @param [TemporalAmount, #to_instant, #to_zoned_date_time, Numeric] other
61
+ # If other is a Numeric, it's interpreted as seconds.
62
+ # @return [Duration] If other responds to #to_zoned_date_time
63
+ # @return [Instant] If other is a TemporalAmount
64
+ def -(other)
65
+ if other.is_a?(Instant)
66
+ java.time.Duration.between(other, self)
67
+ elsif other.respond_to?(:to_instant)
68
+ java.time.Duration.between(other.to_instant, self)
69
+ elsif other.respond_to?(:to_zoned_date_time)
70
+ java.time.Duration.between(other.to_zoned_date_time.to_instant, self)
71
+ elsif other.is_a?(Numeric)
72
+ minus(other.seconds)
73
+ else
74
+ minus(other)
75
+ end
76
+ end
77
+
78
+ # @param [TemporalAmount, Numeric] other
79
+ # If other is a Numeric, it's interpreted as seconds.
80
+ # @return [Instant]
81
+ def +(other)
82
+ return plus(other.seconds) if other.is_a?(Numeric)
83
+
84
+ plus(other)
85
+ end
86
+
87
+ #
88
+ # The number of seconds since the Unix epoch.
89
+ # @return [Integer]
90
+ #
91
+ def to_i
92
+ epoch_second
93
+ end
94
+
95
+ #
96
+ # The number of seconds since the Unix epoch.
97
+ # @return [Float]
98
+ #
99
+ def to_f
100
+ ((epoch_second * 1_000_000_000) + nano).fdiv(1_000_000_000.0)
101
+ end
102
+
103
+ # This comes from JRuby
104
+
105
+ # @!method to_time
106
+ # @return [Time]
107
+
108
+ # @return [Integer, nil]
109
+ def <=>(other)
110
+ logger.trace { "(#{self.class}) #{self} <=> #{other} (#{other.class})" }
111
+ # compare instants, otherwise it will differ by timezone, which we don't want
112
+ # (use eql? if you care about that)
113
+ if other.respond_to?(:to_instant)
114
+ logger.trace { "Comparing #{self} to #{other.to_instant}" }
115
+ compare_to(other.to_instant(to_zoned_date_time))
116
+ elsif other.respond_to?(:coerce) && (lhs, rhs = other.coerce(self))
117
+ lhs <=> rhs
118
+ end
119
+ end
120
+
121
+ # @param [ZonedDateTime, nil] context A {ZonedDateTime} used to match the zone id. Defaults to UTC.
122
+ # @return [ZonedDateTime]
123
+ def to_zoned_date_time(context = nil)
124
+ zone = context&.zone || java.time.ZoneOffset::UTC
125
+ at_zone(zone)
126
+ end
127
+
128
+ # @!visibility private
129
+ def to_instant(_context = nil)
130
+ self
131
+ end
132
+
133
+ #
134
+ # Converts `other` to {Instant}, if possible
135
+ #
136
+ # @param [#to_instant] other
137
+ # @return [Array, nil]
138
+ #
139
+ def coerce(other)
140
+ logger.trace { "Coercing #{self} as a request from #{other.class}" }
141
+ return [other.to_instant(to_zoned_date_time), self] if other.respond_to?(:to_instant)
142
+
143
+ [other.to_zoned_date_time(zoned_date_time).to_instant, self] if other.respond_to?(:to_zoned_date_time)
144
+ end
145
+ end
10
146
  end
11
147
  end
12
148
  end
@@ -96,6 +96,15 @@ module OpenHAB
96
96
  zone = context&.zone || java.time.ZoneId.system_default
97
97
  at_start_of_day(zone)
98
98
  end
99
+
100
+ # @param [ZonedDateTime, nil] context
101
+ # A {ZonedDateTime} used to fill in missing fields
102
+ # during conversion. {ZonedDateTime.now} is assumed if not given.
103
+ # @return [Instant]
104
+ def to_instant(context = nil)
105
+ zone = context&.zone || java.time.ZoneOffset::UTC
106
+ at_start_of_day(zone).to_instant
107
+ end
99
108
  end
100
109
  end
101
110
  end
@@ -111,6 +111,15 @@ module OpenHAB
111
111
  context ||= ZonedDateTime.now
112
112
  context.with(self)
113
113
  end
114
+
115
+ # @param [ZonedDateTime, nil] context
116
+ # A {ZonedDateTime} used to fill in missing fields
117
+ # during conversion. {ZonedDateTime.now} is assumed if not given.
118
+ # @return [Instant]
119
+ def to_instant(context = nil)
120
+ context ||= Instant.now.to_zoned_date_time
121
+ to_zoned_date_time(context).to_instant
122
+ end
114
123
  end
115
124
  end
116
125
  end
@@ -62,6 +62,16 @@ module OpenHAB
62
62
  def to_zoned_date_time(context = nil)
63
63
  to_local_date(context).to_zoned_date_time(context)
64
64
  end
65
+
66
+ # @param [ZonedDateTime, nil] context
67
+ # A {ZonedDateTime} used to fill in the year during conversion,
68
+ # with the date set to the first day of the month.
69
+ # {Instant.now} is assumed if not given.
70
+ # @return [Instant]
71
+ def to_instant(context = nil)
72
+ context ||= Instant.now.to_zoned_date_time
73
+ to_local_date(context).to_instant
74
+ end
65
75
  end
66
76
  end
67
77
  end
@@ -112,6 +112,15 @@ module OpenHAB
112
112
  def to_zoned_date_time(context = nil)
113
113
  to_local_date(context).to_zoned_date_time(context)
114
114
  end
115
+
116
+ # @param [ZonedDateTime, nil] context
117
+ # A {ZonedDateTime} used to fill in missing year during conversion,
118
+ # {ZonedDateTime.now} is assumed if not given.
119
+ # @return [Instant]
120
+ def to_instant(context = nil)
121
+ context ||= Instant.now.to_zoned_date_time
122
+ to_local_date(context).to_instant
123
+ end
115
124
  end
116
125
  end
117
126
  end
@@ -38,6 +38,7 @@ module OpenHAB
38
38
  # less than, equal to, or greater than self
39
39
  #
40
40
  def <=>(other)
41
+ logger.trace { "(#{self.class}) #{self} <=> #{other} (#{other.class})" }
41
42
  if other.is_a?(self.class)
42
43
  compare_to(other)
43
44
  elsif other.respond_to?(:coerce)
@@ -50,6 +51,7 @@ module OpenHAB
50
51
  # Convert `other` to this class, if possible
51
52
  # @return [Array, nil]
52
53
  def coerce(other)
54
+ logger.trace { "Coercing #{self} as a request from #{other.class}" }
53
55
  coercion_method = self.class.coercion_method
54
56
  return unless other.respond_to?(coercion_method)
55
57
  return [other.send(coercion_method), self] if other.method(coercion_method).arity.zero?
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "forwardable"
4
+
3
5
  require_relative "time"
4
6
 
5
7
  module OpenHAB
@@ -9,6 +11,7 @@ module OpenHAB
9
11
 
10
12
  # Extensions to {java.time.ZonedDateTime}
11
13
  class ZonedDateTime
14
+ extend Forwardable
12
15
  include Time
13
16
  include Between
14
17
 
@@ -60,20 +63,20 @@ module OpenHAB
60
63
  end
61
64
 
62
65
  #
66
+ # @!method to_i
63
67
  # The number of seconds since the Unix epoch.
64
68
  #
65
69
  # @return [Integer]
66
- def to_i
67
- to_instant.epoch_second
68
- end
70
+ #
69
71
 
70
72
  #
73
+ # @!method to_f
71
74
  # The number of seconds since the Unix epoch.
72
75
  #
73
76
  # @return [Float]
74
- def to_f
75
- to_instant.to_epoch_milli / 1000.0
76
- end
77
+ #
78
+
79
+ delegate %i[to_i to_f] => :to_instant
77
80
 
78
81
  # @return [Date]
79
82
  def to_date
@@ -103,6 +106,36 @@ module OpenHAB
103
106
  self
104
107
  end
105
108
 
109
+ #
110
+ # Returns true if the date, converted to the system time zone, is yesterday.
111
+ #
112
+ # @return [true, false]
113
+ #
114
+ def yesterday?
115
+ with_zone_same_instant(ZoneId.system_default).to_local_date == LocalDate.now - 1
116
+ end
117
+
118
+ #
119
+ # Returns true if the date, converted to the system time zone, is today.
120
+ #
121
+ # This is the equivalent of checking if the current datetime is between midnight and end of the day
122
+ # of the system time zone.
123
+ #
124
+ # @return [true, false]
125
+ #
126
+ def today?
127
+ with_zone_same_instant(ZoneId.system_default).to_local_date == LocalDate.now
128
+ end
129
+
130
+ #
131
+ # Returns true if the date, converted to the system time zone, is tomorrow.
132
+ #
133
+ # @return [true, false]
134
+ #
135
+ def tomorrow?
136
+ with_zone_same_instant(ZoneId.system_default).to_local_date == LocalDate.now + 1
137
+ end
138
+
106
139
  # @group Ephemeris Methods
107
140
  # (see CoreExt::Ephemeris)
108
141
 
@@ -196,6 +229,20 @@ module OpenHAB
196
229
  end
197
230
  end
198
231
 
232
+ # @!visibility private
233
+ alias_method :raw_to_instant, :to_instant
234
+
235
+ # @!visibility private
236
+ def to_instant(_context = nil)
237
+ raw_to_instant
238
+ end
239
+
240
+ #
241
+ # @!method to_instant
242
+ # Converts this object to an {Instant}
243
+ # @return [Instant]
244
+ #
245
+
199
246
  #
200
247
  # Converts `other` to {ZonedDateTime}, if possible
201
248
  #
@@ -203,6 +250,7 @@ module OpenHAB
203
250
  # @return [Array, nil]
204
251
  #
205
252
  def coerce(other)
253
+ logger.trace { "Coercing #{self} as a request from #{other.class}" }
206
254
  [other.to_zoned_date_time(self), self] if other.respond_to?(:to_zoned_date_time)
207
255
  end
208
256
  end
@@ -1,9 +1,11 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "forwardable"
3
4
  require "date"
4
5
 
5
6
  # Extensions to Date
6
7
  class Date
8
+ extend Forwardable
7
9
  include OpenHAB::CoreExt::Between
8
10
  include OpenHAB::CoreExt::Ephemeris
9
11
 
@@ -53,6 +55,14 @@ class Date
53
55
  java.time.MonthDay.of(month, day)
54
56
  end
55
57
 
58
+ # @!method yesterday?
59
+ # (see OpenHAB::CoreExt::Java::ZonedDateTime#yesterday?)
60
+ # @!method today?
61
+ # (see OpenHAB::CoreExt::Java::ZonedDateTime#today?)
62
+ # @!method tomorrow?
63
+ # (see OpenHAB::CoreExt::Java::ZonedDateTime#tomorrow?)
64
+ def_delegators :to_zoned_date_time, :yesterday?, :today?, :tomorrow?
65
+
56
66
  # @param [ZonedDateTime, nil] context
57
67
  # A {ZonedDateTime} used to fill in missing fields during conversion.
58
68
  # {OpenHAB::CoreExt::Java::ZonedDateTime.now ZonedDateTime.now} is assumed
@@ -62,6 +72,16 @@ class Date
62
72
  to_local_date.to_zoned_date_time(context)
63
73
  end
64
74
 
75
+ # @param [ZonedDateTime, nil] context
76
+ # A {ZonedDateTime} used to fill in missing fields during conversion.
77
+ # {OpenHAB::CoreExt::Java::ZonedDateTime.now ZonedDateTime.now} is assumed
78
+ # if not given.
79
+ # @return [Instant]
80
+ def to_instant(context = nil)
81
+ context ||= Instant.now.to_zoned_date_time
82
+ to_zoned_date_time(context).to_instant
83
+ end
84
+
65
85
  # @return [Integer, nil]
66
86
  def compare_with_coercion(other)
67
87
  return compare_without_coercion(other) if other.is_a?(self.class)
@@ -84,6 +104,7 @@ class Date
84
104
  # @return [Array, nil]
85
105
  #
86
106
  def coerce(other)
107
+ logger.trace { "Coercing #{self} as a request from #{other.class}" }
87
108
  return nil unless other.respond_to?(:to_date)
88
109
  return [other.to_date, self] if other.method(:to_date).arity.zero?
89
110
 
@@ -45,8 +45,14 @@ class DateTime < Date
45
45
  to_java(ZonedDateTime)
46
46
  end
47
47
 
48
+ # @return [Instant]
49
+ def to_instant(_context = nil)
50
+ to_java(Instant)
51
+ end
52
+
48
53
  # (see Time#coerce)
49
54
  def coerce(other)
55
+ logger.trace { "Coercing #{self} as a request from #{other.class}" }
50
56
  return unless other.respond_to?(:to_zoned_date_time)
51
57
 
52
58
  zdt = to_zoned_date_time
@@ -84,6 +84,14 @@ class Time
84
84
  java.time.MonthDay.of(month, day)
85
85
  end
86
86
 
87
+ # @!method yesterday?
88
+ # (see OpenHAB::CoreExt::Java::ZonedDateTime#yesterday?)
89
+ # @!method today?
90
+ # (see OpenHAB::CoreExt::Java::ZonedDateTime#today?)
91
+ # @!method tomorrow?
92
+ # (see OpenHAB::CoreExt::Java::ZonedDateTime#tomorrow?)
93
+ def_delegators :to_zoned_date_time, :yesterday?, :today?, :tomorrow?
94
+
87
95
  # @param [ZonedDateTime, nil] context
88
96
  # A {ZonedDateTime} used to fill in missing fields
89
97
  # during conversion. Not used in this class.
@@ -92,8 +100,8 @@ class Time
92
100
  to_java(java.time.ZonedDateTime)
93
101
  end
94
102
 
95
- # @return [java.time.Instant]
96
- def to_instant
103
+ # @return [Instant]
104
+ def to_instant(_context = nil)
97
105
  to_java(java.time.Instant)
98
106
  end
99
107
 
@@ -105,6 +113,7 @@ class Time
105
113
  # @return [Array, nil]
106
114
  #
107
115
  def coerce(other)
116
+ logger.trace { "Coercing #{self} as a request from #{other.class}" }
108
117
  return unless other.respond_to?(:to_zoned_date_time)
109
118
 
110
119
  zdt = to_zoned_date_time
@@ -35,10 +35,12 @@ module OpenHAB
35
35
  # check if this item is in the command's state before actually
36
36
  # sending the command
37
37
  %i[command update].each do |ensured_method|
38
- # def command(state)
38
+ # rubocop:disable Style/IfUnlessModifier
39
+
40
+ # def command(state, **kwargs)
39
41
  # # 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)
41
- # return super(state) unless Thread.current[:openhab_ensure_states]
42
+ # return super(state, **kwargs) if state.is_a?(Command) && !state.is_a?(State)
43
+ # return super(state, **kwargs) unless Thread.current[:openhab_ensure_states]
42
44
  #
43
45
  # formatted_state = format_command(state)
44
46
  # logger.trace do
@@ -46,13 +48,13 @@ module OpenHAB
46
48
  # end
47
49
  # return if raw_state == formatted_state
48
50
  #
49
- # super(formatted_state)
51
+ # super(formatted_state, **kwargs)
50
52
  # end
51
53
  class_eval <<~RUBY, __FILE__, __LINE__ + 1 # rubocop:disable Style/DocumentDynamicEvalDefinition
52
- def #{ensured_method}(state)
54
+ def #{ensured_method}(state, **kwargs)
53
55
  # 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}
55
- return super(state) unless Thread.current[:openhab_ensure_states]
56
+ #{"return super(state, **kwargs) if state.is_a?(Command) && !state.is_a?(State)" if ensured_method == :command}
57
+ return super(state, **kwargs) unless Thread.current[:openhab_ensure_states]
56
58
 
57
59
  formatted_state = format_#{ensured_method}(state)
58
60
  logger.trace do
@@ -60,9 +62,10 @@ module OpenHAB
60
62
  end
61
63
  return if raw_state.as(formatted_state.class) == formatted_state
62
64
 
63
- super(formatted_state)
65
+ super(formatted_state, **kwargs)
64
66
  end
65
67
  RUBY
68
+ # rubocop:enable Style/IfUnlessModifier
66
69
  end
67
70
  end
68
71
 
@@ -85,7 +85,11 @@ module OpenHAB
85
85
  # @return [void]
86
86
  #
87
87
  def resume
88
- self.resolution = nil
88
+ if expired?
89
+ logger.warn "Cannot resume a timed command that has expired. Use reschedule instead."
90
+ else
91
+ self.resolution = nil
92
+ end
89
93
  end
90
94
  end
91
95
 
@@ -145,9 +149,9 @@ module OpenHAB
145
149
  # end
146
150
  # end
147
151
  #
148
- def command(command, for: nil, on_expire: nil, only_when_ensured: false, &block)
152
+ def command(command, for: nil, on_expire: nil, only_when_ensured: false, **kwargs, &block)
149
153
  duration = binding.local_variable_get(:for)
150
- return super(command) unless duration
154
+ return super(command, **kwargs) unless duration
151
155
 
152
156
  on_expire = block if block
153
157
 
@@ -155,10 +159,10 @@ module OpenHAB
155
159
  on_expire ||= default_on_expire(command)
156
160
  if only_when_ensured
157
161
  DSL.ensure_states do
158
- create_timed_command(command, duration: duration, on_expire: on_expire) if super(command)
162
+ create_timed_command(command, duration: duration, on_expire: on_expire) if super(command, **kwargs)
159
163
  end
160
164
  else
161
- super(command)
165
+ super(command, **kwargs)
162
166
  create_timed_command(command, duration: duration, on_expire: on_expire)
163
167
  end
164
168
  end
@@ -176,12 +180,12 @@ module OpenHAB
176
180
  create_ensured_timed_command.call
177
181
  else
178
182
  # timed command still pending; reset it
179
- logger.trace "Outstanding Timed Command #{timed_command_details} encountered - rescheduling"
183
+ logger.trace { "Outstanding Timed Command #{timed_command_details} encountered - rescheduling" }
180
184
  timed_command_details.on_expire = on_expire unless on_expire.nil?
181
185
  timed_command_details.timer.reschedule(duration)
182
186
  # disable the cancel rule while we send the new command
183
187
  DSL.rules[timed_command_details.rule_uid].disable
184
- super(command) # This returns nil when "ensured"
188
+ super(command, **kwargs) # This returns nil when "ensured"
185
189
  DSL.rules[timed_command_details.rule_uid].enable
186
190
  timed_command_details
187
191
  end
@@ -205,7 +209,7 @@ module OpenHAB
205
209
  unmanaged_rule = Core.automation_manager.add_unmanaged_rule(cancel_rule)
206
210
  timed_command_details.rule_uid = unmanaged_rule.uid
207
211
  Core::Rules::Provider.current.add(unmanaged_rule)
208
- logger.trace "Created Timed Command #{timed_command_details}"
212
+ logger.trace { "Created Timed Command #{timed_command_details}" }
209
213
  timed_command_details
210
214
  end
211
215
 
@@ -215,23 +219,28 @@ module OpenHAB
215
219
  def timed_command_timer(timed_command_details, duration)
216
220
  DSL.after(duration) do
217
221
  timed_command_details.mutex.synchronize do
218
- logger.trace "Timed command expired - #{timed_command_details}"
222
+ logger.trace { "Timed command expired - #{timed_command_details}" }
223
+ DSL.rules[timed_command_details.rule_uid].disable
219
224
  timed_command_details.resolution = :expired
220
225
  case timed_command_details.on_expire
221
226
  when Proc
222
- logger.trace "Invoking block #{timed_command_details.on_expire} after timed command for #{name} expired"
223
- timed_command_details.on_expire.call(timed_command_details)
224
- if timed_command_details.resolution.nil?
225
- logger.trace { "Block rescheduled the timer to #{timed_command_details.timer.execution_time}" }
227
+ logger.trace do
228
+ "Invoking block #{timed_command_details.on_expire} after timed command for #{name} expired"
226
229
  end
230
+ timed_command_details.on_expire.call(timed_command_details)
227
231
  when Core::Types::UnDefType
228
232
  update(timed_command_details.on_expire)
229
233
  else
230
234
  command(timed_command_details.on_expire)
231
235
  end
236
+ # The on_expire block can call timed_command_details.reschedule, which sets resolution to nil
237
+ # to prevent removal of the timed command
232
238
  if timed_command_details.resolution
233
239
  DSL.rules.remove(timed_command_details.rule_uid)
234
240
  TimedCommand.timed_commands.delete(timed_command_details.item)
241
+ else
242
+ DSL.rules[timed_command_details.rule_uid].enable
243
+ logger.trace { "Block rescheduled the timer to #{timed_command_details.timer.execution_time}" }
235
244
  end
236
245
  end
237
246
  end
@@ -1403,7 +1403,7 @@ module OpenHAB
1403
1403
  @ruby_triggers << [:every, value, { at: at }]
1404
1404
 
1405
1405
  if value == :day && at.is_a?(Item)
1406
- # @!deprecated OH 3.4 - attachments are supported in OH 4.0+
1406
+ # @deprecated OH 3.4 - attachments are supported in OH 4.0+
1407
1407
  if Core.version <= Core::V4_0 && !attach.nil?
1408
1408
  raise ArgumentError, "Attachments are not supported with dynamic datetime triggers in openHAB 3.x"
1409
1409
  end
@@ -20,7 +20,7 @@ module OpenHAB
20
20
  # @param [Object] thing to combine with channels and iterate over
21
21
  # @return [Enumerable] enumerable channel ids to trigger on
22
22
  def self.channels(channels:, thing:)
23
- logger.trace "Creating Channel/Thing Pairs for channels #{channels.inspect} and things #{thing.inspect}"
23
+ logger.trace { "Creating Channel/Thing Pairs for channels #{channels.inspect} and things #{thing.inspect}" }
24
24
  channels.flatten.product([thing].flatten)
25
25
  .map { |channel_thing| channel_id(*channel_thing) }
26
26
  end
@@ -47,7 +47,7 @@ module OpenHAB
47
47
  def trigger(channel:, trigger:, attach:)
48
48
  config = { "channelUID" => channel }
49
49
  config["event"] = trigger.to_s unless trigger.nil?
50
- logger.trace "Creating Channel Trigger for channels #{channel.inspect} and config #{config.inspect}"
50
+ logger.trace { "Creating Channel Trigger for channels #{channel.inspect} and config #{config.inspect}" }
51
51
  append_trigger(type: CHANNEL_EVENT, config: config, attach: attach)
52
52
  end
53
53
  end
@@ -1215,7 +1215,7 @@ module OpenHAB
1215
1215
  REQUIRED_BUTTON_ARGS = %i[row column click].freeze
1216
1216
  private_constant :REQUIRED_BUTTON_ARGS
1217
1217
 
1218
- # @!deprecated OH 4.1 in OH 4.1, Buttongrid is not a LinkableWidget.
1218
+ # @deprecated OH 4.1 in OH 4.1, Buttongrid is not a LinkableWidget.
1219
1219
  # Pretend that the buttons property is its children so we can add to it in LinkableWidgetBuilder#build
1220
1220
  if (Core::V4_1...Core::V4_2).cover?(Core.version)
1221
1221
  java_import org.openhab.core.model.sitemap.sitemap.Buttongrid
@@ -41,7 +41,7 @@ module OpenHAB
41
41
  def thread_local(**values)
42
42
  old_values = values.to_h { |key, _value| [key, Thread.current[key]] }
43
43
  values.each { |key, value| Thread.current[key] = value }
44
- logger.trace "Executing block with thread local context: #{values} - old context: #{old_values}"
44
+ logger.trace { "Executing block with thread local context: #{values} - old context: #{old_values}" }
45
45
  yield
46
46
  ensure
47
47
  old_values.each { |key, value| Thread.current[key] = value }
@@ -4,6 +4,6 @@ module OpenHAB
4
4
  module DSL
5
5
  # Version of openHAB helper libraries
6
6
  # @return [String]
7
- VERSION = "5.28.0"
7
+ VERSION = "5.30.0"
8
8
  end
9
9
  end
data/lib/openhab/dsl.rb CHANGED
@@ -1087,4 +1087,4 @@ singleton_class.include(OpenHAB::DSL)
1087
1087
  Object.extend(OpenHAB::CoreExt::Ruby::Object::ClassMethods)
1088
1088
  OpenHAB::CoreExt::Ruby::Object.instance_variable_set(:@top_self, self)
1089
1089
 
1090
- logger.debug "openHAB JRuby Scripting Library Version #{OpenHAB::DSL::VERSION} Loaded"
1090
+ logger.debug { "openHAB JRuby Scripting Library Version #{OpenHAB::DSL::VERSION} Loaded" }
data/lib/openhab/osgi.rb CHANGED
@@ -24,7 +24,7 @@ module OpenHAB
24
24
  #
25
25
  def services(name, filter: nil)
26
26
  (bundle_context.get_service_references(name, filter) || []).map do |reference|
27
- logger.trace "OSGi service found for '#{name}' using OSGi Service Reference #{reference}"
27
+ logger.trace { "OSGi service found for '#{name}' using OSGi Service Reference #{reference}" }
28
28
  bundle_context.get_service(reference)
29
29
  end
30
30
  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.28.0
4
+ version: 5.30.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: 2024-09-15 00:00:00.000000000 Z
13
+ date: 2024-10-02 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: bundler
@@ -490,7 +490,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
490
490
  - !ruby/object:Gem::Version
491
491
  version: '0'
492
492
  requirements: []
493
- rubygems_version: 3.5.18
493
+ rubygems_version: 3.5.20
494
494
  signing_key:
495
495
  specification_version: 4
496
496
  summary: JRuby Helper Libraries for openHAB Scripting