openhab-jrubyscripting 5.0.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (164) hide show
  1. checksums.yaml +7 -0
  2. data/lib/openhab/core/actions.rb +163 -0
  3. data/lib/openhab/core/entity_lookup.rb +144 -0
  4. data/lib/openhab/core/events/abstract_event.rb +17 -0
  5. data/lib/openhab/core/events/item_channel_link.rb +36 -0
  6. data/lib/openhab/core/events/item_command_event.rb +78 -0
  7. data/lib/openhab/core/events/item_event.rb +22 -0
  8. data/lib/openhab/core/events/item_state_changed_event.rb +52 -0
  9. data/lib/openhab/core/events/item_state_event.rb +51 -0
  10. data/lib/openhab/core/events/thing.rb +29 -0
  11. data/lib/openhab/core/events/thing_status_info_event.rb +53 -0
  12. data/lib/openhab/core/events.rb +10 -0
  13. data/lib/openhab/core/items/accepted_data_types.rb +29 -0
  14. data/lib/openhab/core/items/color_item.rb +52 -0
  15. data/lib/openhab/core/items/contact_item.rb +52 -0
  16. data/lib/openhab/core/items/date_time_item.rb +58 -0
  17. data/lib/openhab/core/items/dimmer_item.rb +148 -0
  18. data/lib/openhab/core/items/generic_item.rb +344 -0
  19. data/lib/openhab/core/items/group_item.rb +174 -0
  20. data/lib/openhab/core/items/image_item.rb +109 -0
  21. data/lib/openhab/core/items/location_item.rb +34 -0
  22. data/lib/openhab/core/items/metadata/hash.rb +390 -0
  23. data/lib/openhab/core/items/metadata/namespace_hash.rb +469 -0
  24. data/lib/openhab/core/items/metadata.rb +11 -0
  25. data/lib/openhab/core/items/number_item.rb +62 -0
  26. data/lib/openhab/core/items/numeric_item.rb +22 -0
  27. data/lib/openhab/core/items/persistence.rb +327 -0
  28. data/lib/openhab/core/items/player_item.rb +66 -0
  29. data/lib/openhab/core/items/proxy.rb +59 -0
  30. data/lib/openhab/core/items/registry.rb +66 -0
  31. data/lib/openhab/core/items/rollershutter_item.rb +68 -0
  32. data/lib/openhab/core/items/semantics/enumerable.rb +152 -0
  33. data/lib/openhab/core/items/semantics.rb +476 -0
  34. data/lib/openhab/core/items/state_storage.rb +53 -0
  35. data/lib/openhab/core/items/string_item.rb +28 -0
  36. data/lib/openhab/core/items/switch_item.rb +78 -0
  37. data/lib/openhab/core/items.rb +114 -0
  38. data/lib/openhab/core/lazy_array.rb +52 -0
  39. data/lib/openhab/core/profile_factory.rb +118 -0
  40. data/lib/openhab/core/script_handling.rb +55 -0
  41. data/lib/openhab/core/things/channel.rb +48 -0
  42. data/lib/openhab/core/things/channel_uid.rb +51 -0
  43. data/lib/openhab/core/things/item_channel_link.rb +33 -0
  44. data/lib/openhab/core/things/profile_callback.rb +52 -0
  45. data/lib/openhab/core/things/proxy.rb +69 -0
  46. data/lib/openhab/core/things/registry.rb +46 -0
  47. data/lib/openhab/core/things/thing.rb +194 -0
  48. data/lib/openhab/core/things.rb +22 -0
  49. data/lib/openhab/core/timer.rb +128 -0
  50. data/lib/openhab/core/types/comparable_type.rb +23 -0
  51. data/lib/openhab/core/types/date_time_type.rb +259 -0
  52. data/lib/openhab/core/types/decimal_type.rb +192 -0
  53. data/lib/openhab/core/types/hsb_type.rb +183 -0
  54. data/lib/openhab/core/types/increase_decrease_type.rb +34 -0
  55. data/lib/openhab/core/types/next_previous_type.rb +34 -0
  56. data/lib/openhab/core/types/numeric_type.rb +52 -0
  57. data/lib/openhab/core/types/on_off_type.rb +46 -0
  58. data/lib/openhab/core/types/open_closed_type.rb +41 -0
  59. data/lib/openhab/core/types/percent_type.rb +95 -0
  60. data/lib/openhab/core/types/play_pause_type.rb +38 -0
  61. data/lib/openhab/core/types/point_type.rb +117 -0
  62. data/lib/openhab/core/types/quantity_type.rb +327 -0
  63. data/lib/openhab/core/types/raw_type.rb +26 -0
  64. data/lib/openhab/core/types/refresh_type.rb +27 -0
  65. data/lib/openhab/core/types/rewind_fastforward_type.rb +38 -0
  66. data/lib/openhab/core/types/stop_move_type.rb +34 -0
  67. data/lib/openhab/core/types/string_type.rb +76 -0
  68. data/lib/openhab/core/types/type.rb +117 -0
  69. data/lib/openhab/core/types/un_def_type.rb +38 -0
  70. data/lib/openhab/core/types/up_down_type.rb +50 -0
  71. data/lib/openhab/core/types.rb +69 -0
  72. data/lib/openhab/core/uid.rb +36 -0
  73. data/lib/openhab/core.rb +85 -0
  74. data/lib/openhab/core_ext/java/duration.rb +115 -0
  75. data/lib/openhab/core_ext/java/local_date.rb +93 -0
  76. data/lib/openhab/core_ext/java/local_time.rb +106 -0
  77. data/lib/openhab/core_ext/java/month.rb +59 -0
  78. data/lib/openhab/core_ext/java/month_day.rb +105 -0
  79. data/lib/openhab/core_ext/java/period.rb +103 -0
  80. data/lib/openhab/core_ext/java/temporal_amount.rb +34 -0
  81. data/lib/openhab/core_ext/java/time.rb +58 -0
  82. data/lib/openhab/core_ext/java/unit.rb +15 -0
  83. data/lib/openhab/core_ext/java/zoned_date_time.rb +116 -0
  84. data/lib/openhab/core_ext/ruby/array.rb +21 -0
  85. data/lib/openhab/core_ext/ruby/class.rb +15 -0
  86. data/lib/openhab/core_ext/ruby/date.rb +89 -0
  87. data/lib/openhab/core_ext/ruby/numeric.rb +190 -0
  88. data/lib/openhab/core_ext/ruby/range.rb +70 -0
  89. data/lib/openhab/core_ext/ruby/time.rb +104 -0
  90. data/lib/openhab/core_ext.rb +18 -0
  91. data/lib/openhab/dsl/events/watch_event.rb +18 -0
  92. data/lib/openhab/dsl/events.rb +9 -0
  93. data/lib/openhab/dsl/gems.rb +3 -0
  94. data/lib/openhab/dsl/items/builder.rb +618 -0
  95. data/lib/openhab/dsl/items/ensure.rb +93 -0
  96. data/lib/openhab/dsl/items/timed_command.rb +236 -0
  97. data/lib/openhab/dsl/rules/automation_rule.rb +308 -0
  98. data/lib/openhab/dsl/rules/builder.rb +1373 -0
  99. data/lib/openhab/dsl/rules/guard.rb +115 -0
  100. data/lib/openhab/dsl/rules/name_inference.rb +160 -0
  101. data/lib/openhab/dsl/rules/property.rb +76 -0
  102. data/lib/openhab/dsl/rules/rule_triggers.rb +96 -0
  103. data/lib/openhab/dsl/rules/terse.rb +63 -0
  104. data/lib/openhab/dsl/rules/triggers/changed.rb +169 -0
  105. data/lib/openhab/dsl/rules/triggers/channel.rb +57 -0
  106. data/lib/openhab/dsl/rules/triggers/command.rb +107 -0
  107. data/lib/openhab/dsl/rules/triggers/conditions/duration.rb +161 -0
  108. data/lib/openhab/dsl/rules/triggers/conditions/proc.rb +164 -0
  109. data/lib/openhab/dsl/rules/triggers/cron/cron.rb +195 -0
  110. data/lib/openhab/dsl/rules/triggers/cron/cron_handler.rb +127 -0
  111. data/lib/openhab/dsl/rules/triggers/trigger.rb +56 -0
  112. data/lib/openhab/dsl/rules/triggers/updated.rb +130 -0
  113. data/lib/openhab/dsl/rules/triggers/watch/watch.rb +55 -0
  114. data/lib/openhab/dsl/rules/triggers/watch/watch_handler.rb +155 -0
  115. data/lib/openhab/dsl/rules/triggers.rb +12 -0
  116. data/lib/openhab/dsl/rules.rb +29 -0
  117. data/lib/openhab/dsl/script_handling.rb +55 -0
  118. data/lib/openhab/dsl/things/builder.rb +263 -0
  119. data/lib/openhab/dsl/thread_local.rb +48 -0
  120. data/lib/openhab/dsl/timer_manager.rb +191 -0
  121. data/lib/openhab/dsl/version.rb +9 -0
  122. data/lib/openhab/dsl.rb +686 -0
  123. data/lib/openhab/log.rb +348 -0
  124. data/lib/openhab/osgi.rb +70 -0
  125. data/lib/openhab/rspec/configuration.rb +56 -0
  126. data/lib/openhab/rspec/example_group.rb +90 -0
  127. data/lib/openhab/rspec/helpers.rb +439 -0
  128. data/lib/openhab/rspec/hooks.rb +93 -0
  129. data/lib/openhab/rspec/jruby.rb +46 -0
  130. data/lib/openhab/rspec/karaf.rb +811 -0
  131. data/lib/openhab/rspec/mocks/bundle_install_support.rb +25 -0
  132. data/lib/openhab/rspec/mocks/bundle_resolver.rb +30 -0
  133. data/lib/openhab/rspec/mocks/event_admin.rb +146 -0
  134. data/lib/openhab/rspec/mocks/metadata_provider.rb +75 -0
  135. data/lib/openhab/rspec/mocks/persistence_service.rb +140 -0
  136. data/lib/openhab/rspec/mocks/safe_caller.rb +40 -0
  137. data/lib/openhab/rspec/mocks/synchronous_executor.rb +56 -0
  138. data/lib/openhab/rspec/mocks/thing_handler.rb +76 -0
  139. data/lib/openhab/rspec/mocks/timer.rb +95 -0
  140. data/lib/openhab/rspec/openhab/core/actions.rb +26 -0
  141. data/lib/openhab/rspec/openhab/core/items/proxy.rb +27 -0
  142. data/lib/openhab/rspec/openhab/core/things/proxy.rb +27 -0
  143. data/lib/openhab/rspec/shell.rb +31 -0
  144. data/lib/openhab/rspec/suspend_rules.rb +60 -0
  145. data/lib/openhab/rspec.rb +17 -0
  146. data/lib/openhab/yard/cli/stats.rb +23 -0
  147. data/lib/openhab/yard/code_objects/group_object.rb +17 -0
  148. data/lib/openhab/yard/code_objects/java/base.rb +31 -0
  149. data/lib/openhab/yard/code_objects/java/class_object.rb +11 -0
  150. data/lib/openhab/yard/code_objects/java/field_object.rb +15 -0
  151. data/lib/openhab/yard/code_objects/java/interface_object.rb +15 -0
  152. data/lib/openhab/yard/code_objects/java/package_object.rb +11 -0
  153. data/lib/openhab/yard/code_objects/java/proxy.rb +23 -0
  154. data/lib/openhab/yard/handlers/jruby/base.rb +49 -0
  155. data/lib/openhab/yard/handlers/jruby/class_handler.rb +18 -0
  156. data/lib/openhab/yard/handlers/jruby/constant_handler.rb +18 -0
  157. data/lib/openhab/yard/handlers/jruby/java_import_handler.rb +27 -0
  158. data/lib/openhab/yard/handlers/jruby/mixin_handler.rb +23 -0
  159. data/lib/openhab/yard/html_helper.rb +44 -0
  160. data/lib/openhab/yard/tags/constant_directive.rb +20 -0
  161. data/lib/openhab/yard/tags/group_directive.rb +24 -0
  162. data/lib/openhab/yard/tags/library.rb +3 -0
  163. data/lib/openhab/yard.rb +32 -0
  164. metadata +504 -0
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "singleton"
4
+
5
+ module OpenHAB
6
+ module Core
7
+ module Things
8
+ #
9
+ # Provides access to all OpenHAB {Thing things}, and acts like an array.
10
+ #
11
+ class Registry
12
+ include LazyArray
13
+ include Singleton
14
+
15
+ #
16
+ # Gets a specific {Thing}
17
+ #
18
+ # @param [String, ThingUID] uid Thing UID in the format `binding_id:type_id:thing_id`
19
+ # or via the ThingUID
20
+ # @return [Thing, nil]
21
+ #
22
+ def [](uid)
23
+ EntityLookup.lookup_thing(uid)
24
+ end
25
+ alias_method :include?, :[]
26
+ alias_method :key?, :[]
27
+
28
+ #
29
+ # Explicit conversion to array
30
+ #
31
+ # @return [Array<Thing>]
32
+ #
33
+ def to_a
34
+ $things.all.map { |thing| Proxy.new(thing) }
35
+ end
36
+
37
+ # Enter the Thing Builder DSL.
38
+ # @yield Block executed in the context of a {DSL::Things::Builder}.
39
+ # @return [Object] The result of the block.
40
+ def build(&block)
41
+ DSL::Things::Builder.new.instance_eval(&block)
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,194 @@
1
+ # frozen_string_literal: true
2
+
3
+ module OpenHAB
4
+ module Core
5
+ module Things
6
+ # @interface
7
+ java_import org.openhab.core.thing.Thing
8
+
9
+ #
10
+ # A {Thing} is a representation of a connected part (e.g. physical device
11
+ # or cloud service) from the real world. It contains a list of
12
+ # {Channel Channels}, which can be bound to {GenericItem Items}.
13
+ #
14
+ # @see OpenHAB::DSL.things things[]
15
+ # @see EntityLookup
16
+ #
17
+ # @example
18
+ # thing = things["chromecast:audiogroup:dd9f8622-eee-4eaf-b33f-cdcdcdeee001121"]
19
+ # logger.info("Audiogroup Status: #{thing&.status}")
20
+ # logger.info("Audiogroup Online? #{thing&.online?}")
21
+ # logger.info("Channel ids: #{thing.channels.map(&:uid)}")
22
+ # logger.info("Items linked to volume channel: #{thing.channels['volume']&.items&.map(&:name)&.join(', ')}")
23
+ # logger.info("Item linked to volume channel: #{thing.channels['volume']&.item&.name}")
24
+ #
25
+ # @example Thing actions can be called directly through a Thing object
26
+ # things["mqtt:broker:mosquitto"].publish_mqtt("zigbee2mqttt/bridge/config/permit_join", "true")
27
+ # things["mail:smtp:local"].send_mail("me@example.com", "Subject", "Email body")
28
+ #
29
+ # @example Thing can be accessed directly through {EntityLookup entity lookup}
30
+ # # replace ':' with '_' in thing uid
31
+ # mqtt_broker_mosquitto.online? # is mqtt:broker:mosquitto thing online?
32
+ #
33
+ # @!attribute [r] status
34
+ # Return the {https://www.openhab.org/docs/concepts/things.html#thing-status thing status}
35
+ # @return [org.openhab.core.thing.ThingStatus]
36
+ #
37
+ # @!attribute [r] channels
38
+ # @return [ChannelArray]
39
+ #
40
+ module Thing
41
+ # Array wrapper class to allow searching a list of channels
42
+ # by channel id
43
+ class ChannelsArray < Array
44
+ # Allows indexing by both integer as an array or channel id acting like a hash.
45
+ # @param [Integer, String] index Numeric index or string channel id to search for.
46
+ def [](index)
47
+ if index.respond_to?(:to_str)
48
+ key = index.to_str
49
+ return find { |channel| channel.uid.id == key }
50
+ end
51
+
52
+ super
53
+ end
54
+ end
55
+
56
+ class << self
57
+ # @!visibility private
58
+ #
59
+ # Override to support Proxy
60
+ #
61
+ def ===(other)
62
+ other.is_a?(self)
63
+ end
64
+ end
65
+
66
+ #
67
+ # @!method uninitialized?
68
+ # Check if thing status == UNINITIALIZED
69
+ # @return [true,false]
70
+ #
71
+
72
+ #
73
+ # @!method initialized?
74
+ # Check if thing status == INITIALIZED
75
+ # @return [true,false]
76
+ #
77
+
78
+ #
79
+ # @!method unknown?
80
+ # Check if thing status == UNKNOWN
81
+ # @return [true,false]
82
+ #
83
+
84
+ #
85
+ # @!method online?
86
+ # Check if thing status == ONLINE
87
+ # @return [true,false]
88
+ #
89
+
90
+ #
91
+ # @!method offline?
92
+ # Check if thing status == OFFLINE
93
+ # @return [true,false]
94
+ #
95
+
96
+ #
97
+ # @!method removing?
98
+ # Check if thing status == REMOVING
99
+ # @return [true,false]
100
+ #
101
+
102
+ #
103
+ # @!method removed?
104
+ # Check if thing status == REMOVED
105
+ # @return [true,false]
106
+ #
107
+
108
+ ThingStatus.constants.each do |thingstatus|
109
+ define_method("#{thingstatus.to_s.downcase}?") { status == ThingStatus.value_of(thingstatus) }
110
+ end
111
+
112
+ #
113
+ # Enable the Thing
114
+ #
115
+ # @param [true, false] enabled
116
+ # @return [void]
117
+ #
118
+ def enable(enabled: true)
119
+ Things.manager.set_enabled(uid, enabled)
120
+ end
121
+
122
+ #
123
+ # Disable the Thing
124
+ #
125
+ # @return [void]
126
+ #
127
+ def disable
128
+ enable(enabled: false)
129
+ end
130
+
131
+ # @return [String]
132
+ def inspect
133
+ r = "#<OpenHAB::Core::Things::Thing #{uid}"
134
+ r += " #{label.inspect}" if label
135
+ r += " (#{location.inspect})" if location
136
+ r += " #{status}"
137
+ unless status_info.status_detail == org.openhab.core.thing.ThingStatusDetail::NONE
138
+ r += " (#{status_info.status_detail})"
139
+ end
140
+ r += " configuration=#{configuration.properties.to_h}" unless configuration.properties.empty?
141
+ r += " properties=#{properties.to_h}" unless properties.empty?
142
+ "#{r}>"
143
+ end
144
+
145
+ #
146
+ # Return Thing's uid as a string
147
+ #
148
+ # @return [String]
149
+ #
150
+ def to_s
151
+ uid.to_s
152
+ end
153
+
154
+ #
155
+ # Fetches the actions available for this thing.
156
+ #
157
+ # Default scope actions are available directly on the thing object, via
158
+ # {#method_missing}.
159
+ #
160
+ # @param [String, nil] scope The action scope. Default's to the thing's binding.
161
+ # @return [Object, nil]
162
+ #
163
+ # @example
164
+ # things["max:thermostat:mybridge:thermostat"].actions("max-devices").delete_from_cube
165
+ #
166
+ # @example (see #method_missing)
167
+ #
168
+ def actions(scope = nil)
169
+ $actions.get(scope || uid.binding_id, uid.to_s)
170
+ end
171
+
172
+ #
173
+ # Delegate missing methods to the thing's default actions scope.
174
+ #
175
+ # @example
176
+ # things['mail:smtp:local'].send_email('me@example.com', 'subject', 'message')
177
+ #
178
+ def method_missing(method, *args, &block)
179
+ return actions.public_send(method, *args, &block) if actions.respond_to?(method)
180
+
181
+ super
182
+ end
183
+
184
+ # @!visibility private
185
+ def respond_to_missing?(method_name, _include_private = false)
186
+ logger.trace("Checking if Thing #{uid} supports #{method_name} action")
187
+ return true if actions.respond_to?(method_name)
188
+
189
+ super
190
+ end
191
+ end
192
+ end
193
+ end
194
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ module OpenHAB
4
+ module Core
5
+ #
6
+ # Contains the core {Thing} that bindings use to represent connected devices,
7
+ # as well as related infrastructure.
8
+ #
9
+ module Things
10
+ java_import org.openhab.core.thing.ThingStatus,
11
+ org.openhab.core.thing.ThingUID,
12
+ org.openhab.core.thing.ThingTypeUID
13
+
14
+ class << self
15
+ # @!visibility private
16
+ def manager
17
+ @manager ||= OSGi.service("org.openhab.core.thing.ThingManager")
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,128 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "forwardable"
4
+
5
+ module OpenHAB
6
+ module Core
7
+ #
8
+ # Timer allows you to administer the block of code that
9
+ # has been scheduled to run later with {OpenHAB::DSL.after after}.
10
+ #
11
+ # @!attribute [r] execution_time
12
+ # @return [ZonedDateTime] the scheduled execution time, or null if the timer was cancelled
13
+ class Timer
14
+ extend Forwardable
15
+
16
+ # @!method active?
17
+ # Check if the timer will execute in the future.
18
+ # @return [true,false]
19
+
20
+ # @!method cancelled?
21
+ # Check if the timer has been cancelled.
22
+ # @return [true,false]
23
+
24
+ # @!method running?
25
+ # Check if the timer code is currently running.
26
+ # @return [true,false]
27
+
28
+ # @!method terminated?
29
+ # Check if the timer has terminated.
30
+ # @return [true,false]
31
+
32
+ def_delegator :@timer, :has_terminated, :terminated?
33
+ def_delegators :@timer, :execution_time, :active?, :cancelled?, :running?
34
+
35
+ # @return [Object, nil]
36
+ attr_accessor :id
37
+
38
+ # @!visibility private
39
+ # @!visibility private
40
+ attr_reader :block
41
+
42
+ #
43
+ # Create a new Timer Object
44
+ #
45
+ # @param [java.time.temporal.TemporalAmount, #to_zoned_date_time, Proc] time When to execute the block
46
+ # @yield Block to execute when timer fires
47
+ # @yieldparam [self]
48
+ #
49
+ # @!visibility private
50
+ def initialize(time, id:, thread_locals:, block:)
51
+ @time = time
52
+ @id = id
53
+ @thread_locals = thread_locals
54
+ @block = block
55
+ @timer = org.openhab.core.model.script.actions.ScriptExecution.create_timer(
56
+ # create it far enough in the future so it won't execute until we finish setting it up
57
+ 1.minute.from_now,
58
+ # when running in rspec, it may have troubles finding this class
59
+ # for auto-conversion of block to interface, so use .impl
60
+ org.eclipse.xtext.xbase.lib.Procedures::Procedure0.impl { execute }
61
+ )
62
+ reschedule(@time)
63
+ end
64
+
65
+ # @return [String]
66
+ def inspect
67
+ r = "#<#{self.class.name} #{"#{id.inspect} " if id}#{block.source_location.join(":")}"
68
+ if cancelled?
69
+ r += " (cancelled)"
70
+ else
71
+ r += " @ #{execution_time}"
72
+ r += " (executed)" if terminated?
73
+ end
74
+ "#{r}>"
75
+ end
76
+ alias_method :to_s, :inspect
77
+
78
+ #
79
+ # Reschedule timer
80
+ #
81
+ # @param [java.time.temporal.TemporalAmount, ZonedDateTime, Proc, nil] time When to reschedule the timer for.
82
+ # If unspecified, the original time is used.
83
+ #
84
+ # @return [self]
85
+ #
86
+ def reschedule(time = nil)
87
+ DSL.timers.add(self)
88
+ @timer.reschedule(new_execution_time(time || @time))
89
+ self
90
+ end
91
+
92
+ #
93
+ # Cancel timer
94
+ #
95
+ # @return [true,false] True if cancel was successful, false otherwise
96
+ #
97
+ def cancel
98
+ DSL.timers.delete(self)
99
+ @timer.cancel
100
+ end
101
+
102
+ private
103
+
104
+ #
105
+ # Calls the block with thread locals set up, and cleans up after itself
106
+ #
107
+ # @return [void]
108
+ #
109
+ def execute
110
+ last_execution_time = execution_time
111
+ DSL::ThreadLocal.thread_local(**@thread_locals) do
112
+ @block.call(self)
113
+ end
114
+ # don't remove ourselves if we were rescheduled in the block
115
+ DSL.timers.delete(self) if execution_time == last_execution_time
116
+ end
117
+
118
+ #
119
+ # @return [ZonedDateTime]
120
+ #
121
+ def new_execution_time(time)
122
+ time = time.call if time.is_a?(Proc)
123
+ time = time.from_now if time.is_a?(java.time.temporal.TemporalAmount)
124
+ time.to_zoned_date_time
125
+ end
126
+ end
127
+ end
128
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "type"
4
+
5
+ module OpenHAB
6
+ module Core
7
+ # `Comparable#==` is overwritten by Type, because {DecimalType} etc.
8
+ # inherit from `Comparable` on the Java side, so it's in the wrong place
9
+ # in the ancestor list
10
+ # @!visibility private
11
+ module ComparableType
12
+ # re-implement
13
+ # @!visibility private
14
+ def ==(other)
15
+ r = self <=> other
16
+
17
+ return false if r.nil?
18
+
19
+ r.zero?
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,259 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "forwardable"
4
+ require "time"
5
+
6
+ require_relative "type"
7
+
8
+ module OpenHAB
9
+ module Core
10
+ module Types
11
+ DateTimeType = org.openhab.core.library.types.DateTimeType
12
+
13
+ # {DateTimeType} uses a {ZonedDateTime} internally.
14
+ class DateTimeType
15
+ # @!parse include Command, State
16
+
17
+ # remove the JRuby default == so that we can inherit the Ruby method
18
+ remove_method :==
19
+
20
+ extend Forwardable
21
+ include Comparable
22
+
23
+ #
24
+ # Regex expression to identify strings defining a time in hours, minutes and optionally seconds
25
+ #
26
+ TIME_ONLY_REGEX = /\A(?<hours>\d\d):(?<minutes>\d\d)(?<seconds>:\d\d)?\Z/.freeze
27
+
28
+ #
29
+ # Regex expression to identify strings defining a time in year, month, and day
30
+ #
31
+ DATE_ONLY_REGEX = /\A\d{4}-\d\d-\d\d\Z/.freeze
32
+ private_constant :TIME_ONLY_REGEX, :DATE_ONLY_REGEX
33
+
34
+ class << self
35
+ #
36
+ # Parses a String representing a time into an OpenHAB DateTimeType. First tries to parse it
37
+ # using java's DateTimeType's parser, then falls back to the Ruby Time.parse
38
+ #
39
+ # @param [String] time_string
40
+ #
41
+ # @return [DateTimeType]
42
+ #
43
+ def parse(time_string)
44
+ time_string = "#{time_string}Z" if TIME_ONLY_REGEX.match?(time_string)
45
+ DateTimeType.new(time_string)
46
+ rescue java.lang.StringIndexOutOfBoundsException, java.lang.IllegalArgumentException => e
47
+ # Try Ruby's Time.parse if OpenHAB's DateTimeType parser fails
48
+ begin
49
+ DateTimeType.new(::Time.parse(time_string))
50
+ rescue ArgumentError
51
+ raise ArgumentError, e.message
52
+ end
53
+ end
54
+ end
55
+
56
+ # @param [ZonedDateTime, nil] context
57
+ # A {ZonedDateTime} used to fill in missing
58
+ # fields during conversion. Not used in this class.
59
+ # @return [ZonedTimeTime]
60
+ def to_zoned_date_time(context = nil) # rubocop:disable Lint/UnusedMethodArgument
61
+ zoned_date_time
62
+ end
63
+
64
+ # act like a Ruby Time
65
+ def_delegator :zoned_date_time, :month_value, :month
66
+ def_delegator :zoned_date_time, :day_of_month, :mday
67
+ def_delegator :zoned_date_time, :day_of_year, :yday
68
+ def_delegator :zoned_date_time, :minute, :min
69
+ def_delegator :zoned_date_time, :second, :sec
70
+ def_delegator :zoned_date_time, :nano, :nsec
71
+ def_delegator :zoned_date_time, :to_epoch_second, :to_i
72
+ def_delegator :zoned_date_time, :to_time
73
+
74
+ alias_method :day, :mday
75
+
76
+ #
77
+ # Create a new instance of DateTimeType
78
+ #
79
+ # @param value [#to_zoned_date_time, #to_time, #to_str, #to_d, nil]
80
+ #
81
+ def initialize(value = nil)
82
+ if value.respond_to?(:to_zoned_date_time)
83
+ super(value.to_zoned_date_time)
84
+ return
85
+ elsif value.respond_to?(:to_time)
86
+ super(value.to_time.to_zoned_date_time)
87
+ return
88
+ elsif value.respond_to?(:to_str)
89
+ # strings respond_do?(:to_d), but we want to avoid that conversion
90
+ super(value.to_str)
91
+ return
92
+ elsif value.respond_to?(:to_d)
93
+ super(Time.at(value.to_d).to_zoned_date_time)
94
+ return
95
+ end
96
+
97
+ super
98
+ end
99
+
100
+ #
101
+ # Check equality without type conversion
102
+ #
103
+ # @return [true,false] if the same value is represented, without type
104
+ # conversion
105
+ def eql?(other)
106
+ return false unless other.instance_of?(self.class)
107
+
108
+ zoned_date_time.compare_to(other.zoned_date_time).zero?
109
+ end
110
+
111
+ #
112
+ # Comparison
113
+ #
114
+ # @param [Object] other object to compare to
115
+ #
116
+ # @return [Integer, nil] -1, 0, +1 depending on whether `other` is
117
+ # less than, equal to, or greater than self
118
+ #
119
+ # `nil` is returned if the two values are incomparable.
120
+ #
121
+ def <=>(other)
122
+ logger.trace("(#{self.class}) #{self} <=> #{other} (#{other.class})")
123
+ if other.is_a?(self.class)
124
+ zoned_date_time <=> other.zoned_date_time
125
+ elsif other.respond_to?(:to_time)
126
+ to_time <=> other.to_time
127
+ elsif other.respond_to?(:coerce)
128
+ return nil unless (lhs, rhs = other.coerce(self))
129
+
130
+ lhs <=> rhs
131
+ end
132
+ end
133
+
134
+ #
135
+ # Type Coercion
136
+ #
137
+ # Coerce object to a DateTimeType
138
+ #
139
+ # @param [Time] other object to coerce to a DateTimeType
140
+ #
141
+ # @return [[DateTimeType, DateTimeType], nil]
142
+ #
143
+ def coerce(other)
144
+ logger.trace("Coercing #{self} as a request from #{other.class}")
145
+ return [other, zoned_date_time] if other.respond_to?(:to_zoned_date_time)
146
+ return [DateTimeType.new(other), self] if other.respond_to?(:to_time)
147
+ end
148
+
149
+ #
150
+ # Returns the value of time as a floating point number of seconds since the Epoch
151
+ #
152
+ # @return [Float] Number of seconds since the Epoch, with nanosecond presicion
153
+ #
154
+ def to_f
155
+ zoned_date_time.to_epoch_second + (zoned_date_time.nano / 1_000_000_000)
156
+ end
157
+
158
+ #
159
+ # The offset in seconds from UTC
160
+ #
161
+ # @return [Integer] The offset from UTC, in seconds
162
+ #
163
+ def utc_offset
164
+ zoned_date_time.offset.total_seconds
165
+ end
166
+
167
+ #
168
+ # Returns true if time represents a time in UTC (GMT)
169
+ #
170
+ # @return [true,false] true if utc_offset == 0, false otherwise
171
+ #
172
+ def utc?
173
+ utc_offset.zero?
174
+ end
175
+
176
+ #
177
+ # Returns an integer representing the day of the week, 0..6, with Sunday == 0.
178
+ #
179
+ # @return [Integer] The day of week
180
+ #
181
+ def wday
182
+ zoned_date_time.day_of_week.value % 7
183
+ end
184
+
185
+ #
186
+ # The timezone
187
+ #
188
+ # @return [String] The timezone in `[+-]hh:mm(:ss)` format (`Z` for UTC)
189
+ #
190
+ def zone
191
+ zoned_date_time.zone.id
192
+ end
193
+
194
+ # @!visibility private
195
+ def respond_to_missing?(method, _include_private = false)
196
+ return true if zoned_date_time.respond_to?(method)
197
+ return true if ::Time.instance_methods.include?(method.to_sym)
198
+
199
+ super
200
+ end
201
+
202
+ #
203
+ # Forward missing methods to the `ZonedDateTime` object or a ruby `Time`
204
+ # object representing the same instant
205
+ #
206
+ def method_missing(method, *args, &block)
207
+ return zoned_date_time.send(method, *args, &block) if zoned_date_time.respond_to?(method)
208
+ return to_time.send(method, *args, &block) if ::Time.instance_methods.include?(method.to_sym)
209
+
210
+ super
211
+ end
212
+
213
+ # Add other to self
214
+ #
215
+ # @param other [Duration, Numeric]
216
+ #
217
+ # @return [DateTimeType]
218
+ def +(other)
219
+ if other.is_a?(Duration)
220
+ DateTimeType.new(zoned_date_time.plus(other))
221
+ elsif other.respond_to?(:to_d)
222
+ DateTimeType.new(zoned_date_time.plus_nanos((other.to_d * 1_000_000_000).to_i))
223
+ elsif other.respond_to?(:coerce) && (lhs, rhs = other.coerce(to_d))
224
+ lhs + rhs
225
+ else
226
+ raise TypeError, "\#{other.class} can't be coerced into \#{self.class}"
227
+ end
228
+ end
229
+
230
+ # Subtract other from self
231
+ #
232
+ # if other is a Duration-like object, the result is a new
233
+ # {DateTimeType} of duration seconds earlier in time.
234
+ #
235
+ # if other is a DateTime-like object, the result is a Duration
236
+ # representing how long between the two instants in time.
237
+ #
238
+ # @param other [Duration, Time, Numeric]
239
+ #
240
+ # @return [DateTimeType, Duration]
241
+ def -(other)
242
+ if other.is_a?(Duration)
243
+ DateTimeType.new(zoned_date_time.minus(other))
244
+ elsif other.respond_to?(:to_time)
245
+ to_time - other.to_time
246
+ elsif other.respond_to?(:to_d)
247
+ DateTimeType.new(zoned_date_time.minus_nanos((other.to_d * 1_000_000_000).to_i))
248
+ elsif other.respond_to?(:coerce) && (lhs, rhs = other.coerce(to_d))
249
+ lhs - rhs
250
+ else
251
+ raise TypeError, "\#{other.class} can't be coerced into \#{self.class}"
252
+ end
253
+ end
254
+ end
255
+ end
256
+ end
257
+ end
258
+
259
+ # @!parse DateTimeType = OpenHAB::Core::Types::DateTimeType