openhab-scripting 4.46.2 → 5.0.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 (281) hide show
  1. checksums.yaml +4 -4
  2. data/lib/openhab/core/actions/audio.rb +47 -0
  3. data/lib/openhab/core/actions/ephemeris.rb +39 -0
  4. data/lib/openhab/core/actions/exec.rb +51 -0
  5. data/lib/openhab/core/actions/http.rb +80 -0
  6. data/lib/openhab/core/actions/ping.rb +30 -0
  7. data/lib/openhab/core/actions/transformation.rb +32 -0
  8. data/lib/openhab/core/actions/voice.rb +36 -0
  9. data/lib/openhab/core/actions.rb +82 -0
  10. data/lib/openhab/core/dependency_tracking.rb +34 -0
  11. data/lib/openhab/core/dto/item_channel_link.rb +33 -0
  12. data/lib/openhab/core/dto/thing.rb +27 -0
  13. data/lib/openhab/core/dto.rb +11 -0
  14. data/lib/openhab/core/entity_lookup.rb +152 -70
  15. data/lib/openhab/core/events/abstract_event.rb +18 -0
  16. data/lib/openhab/core/events/abstract_item_registry_event.rb +36 -0
  17. data/lib/openhab/core/events/abstract_thing_registry_event.rb +40 -0
  18. data/lib/openhab/core/events/item_command_event.rb +78 -0
  19. data/lib/openhab/core/events/item_event.rb +22 -0
  20. data/lib/openhab/core/events/item_state_changed_event.rb +75 -0
  21. data/lib/openhab/core/events/item_state_event.rb +79 -0
  22. data/lib/openhab/core/events/thing_status_info_event.rb +55 -0
  23. data/lib/openhab/core/events.rb +10 -0
  24. data/lib/openhab/core/items/accepted_data_types.rb +29 -0
  25. data/lib/openhab/core/items/color_item.rb +52 -0
  26. data/lib/openhab/core/items/contact_item.rb +52 -0
  27. data/lib/openhab/core/items/date_time_item.rb +59 -0
  28. data/lib/openhab/core/items/dimmer_item.rb +148 -0
  29. data/lib/openhab/core/items/generic_item.rb +292 -0
  30. data/lib/openhab/core/items/group_item.rb +176 -0
  31. data/lib/openhab/{dsl → core}/items/image_item.rb +35 -29
  32. data/lib/openhab/core/items/item.rb +273 -0
  33. data/lib/openhab/core/items/location_item.rb +34 -0
  34. data/lib/openhab/core/items/metadata/hash.rb +433 -0
  35. data/lib/openhab/core/items/metadata/namespace_hash.rb +475 -0
  36. data/lib/openhab/core/items/metadata/provider.rb +48 -0
  37. data/lib/openhab/core/items/metadata.rb +11 -0
  38. data/lib/openhab/core/items/number_item.rb +62 -0
  39. data/lib/openhab/core/items/numeric_item.rb +22 -0
  40. data/lib/openhab/core/items/persistence.rb +416 -0
  41. data/lib/openhab/core/items/player_item.rb +66 -0
  42. data/lib/openhab/core/items/provider.rb +44 -0
  43. data/lib/openhab/core/items/proxy.rb +136 -0
  44. data/lib/openhab/core/items/registry.rb +86 -0
  45. data/lib/openhab/core/items/rollershutter_item.rb +68 -0
  46. data/lib/openhab/core/items/semantics/enumerable.rb +177 -0
  47. data/lib/openhab/core/items/semantics.rb +473 -0
  48. data/lib/openhab/core/items/state_storage.rb +53 -0
  49. data/lib/openhab/core/items/string_item.rb +28 -0
  50. data/lib/openhab/core/items/switch_item.rb +78 -0
  51. data/lib/openhab/core/items.rb +108 -0
  52. data/lib/openhab/{dsl → core}/lazy_array.rb +9 -3
  53. data/lib/openhab/core/profile_factory.rb +132 -0
  54. data/lib/openhab/core/provider.rb +230 -0
  55. data/lib/openhab/core/proxy.rb +130 -0
  56. data/lib/openhab/core/registry.rb +40 -0
  57. data/lib/openhab/core/rules/module.rb +26 -0
  58. data/lib/openhab/core/rules/provider.rb +25 -0
  59. data/lib/openhab/core/rules/registry.rb +76 -0
  60. data/lib/openhab/core/rules/rule.rb +150 -0
  61. data/lib/openhab/core/rules.rb +25 -0
  62. data/lib/openhab/core/script_handling.rb +78 -20
  63. data/lib/openhab/core/things/channel.rb +48 -0
  64. data/lib/openhab/core/things/channel_uid.rb +51 -0
  65. data/lib/openhab/core/things/item_channel_link.rb +33 -0
  66. data/lib/openhab/core/things/links/provider.rb +78 -0
  67. data/lib/openhab/core/things/profile_callback.rb +52 -0
  68. data/lib/openhab/core/things/provider.rb +29 -0
  69. data/lib/openhab/core/things/proxy.rb +87 -0
  70. data/lib/openhab/core/things/registry.rb +73 -0
  71. data/lib/openhab/core/things/thing.rb +194 -0
  72. data/lib/openhab/core/things.rb +22 -0
  73. data/lib/openhab/core/timer.rb +148 -0
  74. data/lib/openhab/{dsl → core}/types/comparable_type.rb +5 -3
  75. data/lib/openhab/{dsl → core}/types/date_time_type.rb +55 -127
  76. data/lib/openhab/{dsl → core}/types/decimal_type.rb +50 -48
  77. data/lib/openhab/{dsl → core}/types/hsb_type.rb +35 -83
  78. data/lib/openhab/core/types/increase_decrease_type.rb +34 -0
  79. data/lib/openhab/core/types/next_previous_type.rb +34 -0
  80. data/lib/openhab/{dsl → core}/types/numeric_type.rb +20 -7
  81. data/lib/openhab/core/types/on_off_type.rb +46 -0
  82. data/lib/openhab/core/types/open_closed_type.rb +41 -0
  83. data/lib/openhab/{dsl → core}/types/percent_type.rb +19 -20
  84. data/lib/openhab/core/types/play_pause_type.rb +38 -0
  85. data/lib/openhab/core/types/point_type.rb +117 -0
  86. data/lib/openhab/core/types/quantity_type.rb +325 -0
  87. data/lib/openhab/core/types/raw_type.rb +26 -0
  88. data/lib/openhab/core/types/refresh_type.rb +27 -0
  89. data/lib/openhab/core/types/rewind_fastforward_type.rb +38 -0
  90. data/lib/openhab/core/types/stop_move_type.rb +34 -0
  91. data/lib/openhab/{dsl → core}/types/string_type.rb +17 -28
  92. data/lib/openhab/{dsl → core}/types/type.rb +42 -40
  93. data/lib/openhab/core/types/un_def_type.rb +38 -0
  94. data/lib/openhab/core/types/up_down_type.rb +50 -0
  95. data/lib/openhab/core/types.rb +82 -0
  96. data/lib/openhab/{dsl → core}/uid.rb +4 -23
  97. data/lib/openhab/core/value_cache.rb +188 -0
  98. data/lib/openhab/core.rb +98 -0
  99. data/lib/openhab/core_ext/between.rb +32 -0
  100. data/lib/openhab/core_ext/ephemeris.rb +53 -0
  101. data/lib/openhab/core_ext/java/class.rb +34 -0
  102. data/lib/openhab/core_ext/java/duration.rb +142 -0
  103. data/lib/openhab/core_ext/java/list.rb +436 -0
  104. data/lib/openhab/core_ext/java/local_date.rb +104 -0
  105. data/lib/openhab/core_ext/java/local_time.rb +118 -0
  106. data/lib/openhab/core_ext/java/map.rb +66 -0
  107. data/lib/openhab/core_ext/java/month.rb +71 -0
  108. data/lib/openhab/core_ext/java/month_day.rb +119 -0
  109. data/lib/openhab/core_ext/java/period.rb +103 -0
  110. data/lib/openhab/core_ext/java/temporal_amount.rb +34 -0
  111. data/lib/openhab/core_ext/java/time.rb +62 -0
  112. data/lib/openhab/core_ext/java/unit.rb +15 -0
  113. data/lib/openhab/core_ext/java/zoned_date_time.rb +213 -0
  114. data/lib/openhab/core_ext/ruby/array.rb +21 -0
  115. data/lib/openhab/core_ext/ruby/date.rb +96 -0
  116. data/lib/openhab/core_ext/ruby/date_time.rb +55 -0
  117. data/lib/openhab/core_ext/ruby/module.rb +15 -0
  118. data/lib/openhab/core_ext/ruby/numeric.rb +195 -0
  119. data/lib/openhab/core_ext/ruby/range.rb +70 -0
  120. data/lib/openhab/core_ext/ruby/symbol.rb +7 -0
  121. data/lib/openhab/core_ext/ruby/time.rb +108 -0
  122. data/lib/openhab/core_ext.rb +18 -0
  123. data/lib/openhab/dsl/debouncer.rb +259 -0
  124. data/lib/openhab/dsl/events/watch_event.rb +18 -0
  125. data/lib/openhab/dsl/events.rb +9 -0
  126. data/lib/openhab/dsl/gems.rb +1 -1
  127. data/lib/openhab/dsl/items/builder.rb +578 -0
  128. data/lib/openhab/dsl/items/ensure.rb +73 -82
  129. data/lib/openhab/dsl/items/timed_command.rb +214 -159
  130. data/lib/openhab/dsl/rules/automation_rule.rb +126 -115
  131. data/lib/openhab/dsl/rules/builder.rb +1935 -0
  132. data/lib/openhab/dsl/rules/guard.rb +51 -114
  133. data/lib/openhab/dsl/rules/name_inference.rb +66 -25
  134. data/lib/openhab/dsl/rules/property.rb +48 -75
  135. data/lib/openhab/dsl/rules/rule_triggers.rb +22 -27
  136. data/lib/openhab/dsl/rules/terse.rb +58 -14
  137. data/lib/openhab/dsl/rules/triggers/changed.rb +48 -94
  138. data/lib/openhab/dsl/rules/triggers/channel.rb +9 -40
  139. data/lib/openhab/dsl/rules/triggers/command.rb +14 -63
  140. data/lib/openhab/dsl/rules/triggers/conditions/duration.rb +34 -69
  141. data/lib/openhab/dsl/rules/triggers/conditions/proc.rb +6 -14
  142. data/lib/openhab/dsl/rules/triggers/cron/cron.rb +48 -82
  143. data/lib/openhab/dsl/rules/triggers/cron/cron_handler.rb +30 -47
  144. data/lib/openhab/dsl/rules/triggers/trigger.rb +7 -28
  145. data/lib/openhab/dsl/rules/triggers/updated.rb +21 -45
  146. data/lib/openhab/dsl/rules/triggers/watch/watch_handler.rb +257 -102
  147. data/lib/openhab/dsl/rules/triggers.rb +12 -0
  148. data/lib/openhab/dsl/rules.rb +8 -0
  149. data/lib/openhab/dsl/things/builder.rb +299 -0
  150. data/lib/openhab/{core → dsl}/thread_local.rb +27 -17
  151. data/lib/openhab/dsl/timer_manager.rb +204 -0
  152. data/lib/openhab/dsl/version.rb +9 -0
  153. data/lib/openhab/dsl.rb +979 -0
  154. data/lib/openhab/log.rb +355 -0
  155. data/lib/openhab/osgi.rb +68 -0
  156. data/lib/openhab/rspec/configuration.rb +56 -0
  157. data/lib/openhab/rspec/example_group.rb +132 -0
  158. data/lib/openhab/rspec/helpers.rb +458 -0
  159. data/lib/openhab/rspec/hooks.rb +113 -0
  160. data/lib/openhab/rspec/jruby.rb +46 -0
  161. data/lib/openhab/rspec/karaf.rb +851 -0
  162. data/lib/openhab/rspec/mocks/bundle_install_support.rb +25 -0
  163. data/lib/openhab/rspec/mocks/bundle_resolver.rb +30 -0
  164. data/lib/openhab/rspec/mocks/event_admin.rb +146 -0
  165. data/lib/openhab/rspec/mocks/instance_method_stasher.rb +22 -0
  166. data/lib/openhab/rspec/mocks/persistence_service.rb +155 -0
  167. data/lib/openhab/rspec/mocks/safe_caller.rb +40 -0
  168. data/lib/openhab/rspec/mocks/space.rb +23 -0
  169. data/lib/openhab/rspec/mocks/synchronous_executor.rb +63 -0
  170. data/lib/openhab/rspec/mocks/thing_handler.rb +76 -0
  171. data/lib/openhab/rspec/mocks/timer.rb +134 -0
  172. data/lib/openhab/rspec/openhab/core/actions.rb +38 -0
  173. data/lib/openhab/rspec/openhab/core/items/proxy.rb +15 -0
  174. data/lib/openhab/rspec/openhab/core/things/proxy.rb +27 -0
  175. data/lib/openhab/rspec/shell.rb +31 -0
  176. data/lib/openhab/rspec/suspend_rules.rb +50 -0
  177. data/lib/openhab/rspec.rb +26 -0
  178. data/lib/openhab/yard/base_helper.rb +19 -0
  179. data/lib/openhab/yard/cli/stats.rb +23 -0
  180. data/lib/openhab/yard/code_objects/group_object.rb +23 -0
  181. data/lib/openhab/yard/code_objects/java/base.rb +31 -0
  182. data/lib/openhab/yard/code_objects/java/class_object.rb +11 -0
  183. data/lib/openhab/yard/code_objects/java/field_object.rb +15 -0
  184. data/lib/openhab/yard/code_objects/java/interface_object.rb +15 -0
  185. data/lib/openhab/yard/code_objects/java/package_object.rb +11 -0
  186. data/lib/openhab/yard/code_objects/java/proxy.rb +23 -0
  187. data/lib/openhab/yard/coderay.rb +17 -0
  188. data/lib/openhab/yard/handlers/jruby/base.rb +58 -0
  189. data/lib/openhab/yard/handlers/jruby/class_handler.rb +18 -0
  190. data/lib/openhab/yard/handlers/jruby/constant_handler.rb +18 -0
  191. data/lib/openhab/yard/handlers/jruby/java_import_handler.rb +30 -0
  192. data/lib/openhab/yard/handlers/jruby/mixin_handler.rb +23 -0
  193. data/lib/openhab/yard/html_helper.rb +78 -0
  194. data/lib/openhab/yard/markdown_helper.rb +148 -0
  195. data/lib/openhab/yard/tags/constant_directive.rb +20 -0
  196. data/lib/openhab/yard/tags/group_directive.rb +24 -0
  197. data/lib/openhab/yard/tags/library.rb +3 -0
  198. data/lib/openhab/yard.rb +38 -0
  199. metadata +475 -106
  200. data/lib/openhab/core/item_proxy.rb +0 -29
  201. data/lib/openhab/core/load_path.rb +0 -19
  202. data/lib/openhab/core/openhab_setup.rb +0 -29
  203. data/lib/openhab/core/osgi.rb +0 -58
  204. data/lib/openhab/core/services.rb +0 -24
  205. data/lib/openhab/dsl/actions.rb +0 -114
  206. data/lib/openhab/dsl/between.rb +0 -25
  207. data/lib/openhab/dsl/channel.rb +0 -43
  208. data/lib/openhab/dsl/dsl.rb +0 -59
  209. data/lib/openhab/dsl/group.rb +0 -54
  210. data/lib/openhab/dsl/imports.rb +0 -21
  211. data/lib/openhab/dsl/items/color_item.rb +0 -76
  212. data/lib/openhab/dsl/items/comparable_item.rb +0 -62
  213. data/lib/openhab/dsl/items/contact_item.rb +0 -41
  214. data/lib/openhab/dsl/items/date_time_item.rb +0 -65
  215. data/lib/openhab/dsl/items/dimmer_item.rb +0 -65
  216. data/lib/openhab/dsl/items/generic_item.rb +0 -229
  217. data/lib/openhab/dsl/items/group_item.rb +0 -127
  218. data/lib/openhab/dsl/items/item_equality.rb +0 -59
  219. data/lib/openhab/dsl/items/item_registry.rb +0 -54
  220. data/lib/openhab/dsl/items/items.rb +0 -109
  221. data/lib/openhab/dsl/items/location_item.rb +0 -59
  222. data/lib/openhab/dsl/items/metadata.rb +0 -326
  223. data/lib/openhab/dsl/items/number_item.rb +0 -17
  224. data/lib/openhab/dsl/items/numeric_item.rb +0 -87
  225. data/lib/openhab/dsl/items/persistence.rb +0 -307
  226. data/lib/openhab/dsl/items/player_item.rb +0 -58
  227. data/lib/openhab/dsl/items/rollershutter_item.rb +0 -51
  228. data/lib/openhab/dsl/items/semantics/enumerable.rb +0 -91
  229. data/lib/openhab/dsl/items/semantics.rb +0 -227
  230. data/lib/openhab/dsl/items/string_item.rb +0 -51
  231. data/lib/openhab/dsl/items/switch_item.rb +0 -70
  232. data/lib/openhab/dsl/monkey_patch/actions/actions.rb +0 -4
  233. data/lib/openhab/dsl/monkey_patch/actions/script_thing_actions.rb +0 -39
  234. data/lib/openhab/dsl/monkey_patch/events/events.rb +0 -7
  235. data/lib/openhab/dsl/monkey_patch/events/item_command.rb +0 -85
  236. data/lib/openhab/dsl/monkey_patch/events/item_event.rb +0 -28
  237. data/lib/openhab/dsl/monkey_patch/events/item_state.rb +0 -61
  238. data/lib/openhab/dsl/monkey_patch/events/item_state_changed.rb +0 -60
  239. data/lib/openhab/dsl/monkey_patch/events/thing_status_info.rb +0 -33
  240. data/lib/openhab/dsl/monkey_patch/java/java.rb +0 -4
  241. data/lib/openhab/dsl/monkey_patch/java/local_time.rb +0 -44
  242. data/lib/openhab/dsl/monkey_patch/java/time_extensions.rb +0 -50
  243. data/lib/openhab/dsl/monkey_patch/java/zoned_date_time.rb +0 -45
  244. data/lib/openhab/dsl/monkey_patch/ruby/number.rb +0 -104
  245. data/lib/openhab/dsl/monkey_patch/ruby/ruby.rb +0 -6
  246. data/lib/openhab/dsl/monkey_patch/ruby/string.rb +0 -47
  247. data/lib/openhab/dsl/monkey_patch/ruby/time.rb +0 -61
  248. data/lib/openhab/dsl/openhab.rb +0 -30
  249. data/lib/openhab/dsl/persistence.rb +0 -27
  250. data/lib/openhab/dsl/rules/item_event.rb +0 -19
  251. data/lib/openhab/dsl/rules/rule.rb +0 -160
  252. data/lib/openhab/dsl/rules/rule_config.rb +0 -147
  253. data/lib/openhab/dsl/rules/triggers/generic.rb +0 -31
  254. data/lib/openhab/dsl/rules/triggers/triggers.rb +0 -11
  255. data/lib/openhab/dsl/rules/triggers/watch/watch.rb +0 -81
  256. data/lib/openhab/dsl/states.rb +0 -89
  257. data/lib/openhab/dsl/things.rb +0 -147
  258. data/lib/openhab/dsl/time/month_day.rb +0 -180
  259. data/lib/openhab/dsl/time/time_of_day.rb +0 -235
  260. data/lib/openhab/dsl/timers/manager.rb +0 -119
  261. data/lib/openhab/dsl/timers/reentrant_timer.rb +0 -38
  262. data/lib/openhab/dsl/timers/timer.rb +0 -132
  263. data/lib/openhab/dsl/timers.rb +0 -77
  264. data/lib/openhab/dsl/types/increase_decrease_type.rb +0 -23
  265. data/lib/openhab/dsl/types/next_previous_type.rb +0 -23
  266. data/lib/openhab/dsl/types/on_off_type.rb +0 -28
  267. data/lib/openhab/dsl/types/open_closed_type.rb +0 -29
  268. data/lib/openhab/dsl/types/play_pause_type.rb +0 -27
  269. data/lib/openhab/dsl/types/point_type.rb +0 -180
  270. data/lib/openhab/dsl/types/quantity_type.rb +0 -265
  271. data/lib/openhab/dsl/types/refresh_type.rb +0 -18
  272. data/lib/openhab/dsl/types/rewind_fastforward_type.rb +0 -33
  273. data/lib/openhab/dsl/types/stop_move_type.rb +0 -23
  274. data/lib/openhab/dsl/types/types.rb +0 -83
  275. data/lib/openhab/dsl/types/un_def_type.rb +0 -22
  276. data/lib/openhab/dsl/types/up_down_type.rb +0 -32
  277. data/lib/openhab/dsl/units.rb +0 -45
  278. data/lib/openhab/log/configuration.rb +0 -21
  279. data/lib/openhab/log/logger.rb +0 -282
  280. data/lib/openhab/version.rb +0 -9
  281. data/lib/openhab.rb +0 -36
@@ -0,0 +1,979 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "java"
4
+ require "method_source"
5
+
6
+ require "bundler/inline"
7
+
8
+ require_relative "log"
9
+ require_relative "osgi"
10
+ require_relative "core"
11
+
12
+ Dir[File.expand_path("dsl/**/*.rb", __dir__)].sort.each do |f|
13
+ require f
14
+ end
15
+
16
+ require_relative "core_ext"
17
+
18
+ #
19
+ # Main openHAB Module
20
+ #
21
+ module OpenHAB
22
+ #
23
+ # The main DSL available to rules.
24
+ #
25
+ # Methods on this module are extended onto `main`, the top level `self` in
26
+ # any file. You can also access them as class methods on the module for use
27
+ # inside of other classes, or include the module.
28
+ #
29
+ module DSL
30
+ # include this before Core::Actions so that Core::Action's method_missing
31
+ # takes priority
32
+ include Core::EntityLookup
33
+ #
34
+ # @!parse
35
+ # include Core::Actions
36
+ # include Core::ScriptHandling
37
+ # include Rules::Terse
38
+ #
39
+ [Core::Actions, Core::ScriptHandling, Rules::Terse].each do |mod|
40
+ # make these available both as regular and class methods
41
+ include mod
42
+ singleton_class.include mod
43
+ public_class_method(*mod.private_instance_methods)
44
+ end
45
+
46
+ class << self
47
+ # @!visibility private
48
+ attr_reader :debouncers
49
+ end
50
+
51
+ @debouncers = java.util.concurrent.ConcurrentHashMap.new
52
+
53
+ module_function
54
+
55
+ # @!group Rule Creation
56
+
57
+ # (see Rules::Builder#rule)
58
+ def rule(name = nil, **kwargs, &block)
59
+ rules.build { rule(name, **kwargs, &block) }
60
+ end
61
+
62
+ # (see Rules::Builder#script)
63
+ def script(name = nil, id: nil, **kwargs, &block)
64
+ rules.build { script(name, id: id, **kwargs, &block) }
65
+ end
66
+
67
+ # @!group Rule Support
68
+
69
+ # rubocop:disable Layout/LineLength
70
+
71
+ #
72
+ # Defines a new profile that can be applied to item channel links.
73
+ #
74
+ # @param [String, Symbol] id The id for the profile.
75
+ # @yield [event, command: nil, state: nil, link:, item:, channel_uid:, configuration:, context:]
76
+ # All keyword params are optional. Any that aren't defined won't be passed.
77
+ # @yieldparam [Core::Things::ProfileCallback] callback
78
+ # The callback to be used to customize the action taken.
79
+ # @yieldparam [:command_from_item, :state_from_item, :command_from_handler, :state_from_handler] event
80
+ # The event that needs to be processed.
81
+ # @yieldparam [Command, nil] command
82
+ # The command being sent for `:command_from_item` and `:command_from_handler` events.
83
+ # @yieldparam [State, nil] state
84
+ # The state being sent for `:state_from_item` and `:state_from_handler` events.
85
+ # @yieldparam [Core::Things::ItemChannelLink] link
86
+ # The link between the item and the channel, including its configuration.
87
+ # @yieldparam [Item] item The linked item.
88
+ # @yieldparam [Core::Things::ChannelUID] channel_uid The linked channel.
89
+ # @yieldparam [Hash] configuration The profile configuration.
90
+ # @yieldparam [org.openhab.core.thing.profiles.ProfileContext] context The profile context.
91
+ # @yieldreturn [Boolean] Return true from the block in order to have default processing.
92
+ # @return [void]
93
+ #
94
+ # @see org.openhab.thing.Profile
95
+ # @see org.openhab.thing.StateProfile
96
+ #
97
+ # @example Vetoing a command
98
+ # profile(:veto_closing_shades) do |event, item:, command:|
99
+ # next false if command&.down?
100
+ #
101
+ # true
102
+ # end
103
+ #
104
+ # items.build do
105
+ # rollershutter_item "MyShade" do
106
+ # channel "thing:rollershutter", profile: "ruby:veto_closing_shades"
107
+ # end
108
+ # end
109
+ # # can also be referenced from an `.items` file:
110
+ # # Rollershutter MyShade { channel="thing:rollershutter"[profile="ruby:veto_closing_shades"] }
111
+ #
112
+ # @example Overriding units from a binding
113
+ # profile(:set_uom) do |event, configuration:, state:, command:|
114
+ # unless configuration["unit"]
115
+ # logger.warn("Unit configuration not provided for set_uom profile")
116
+ # next true
117
+ # end
118
+ #
119
+ # case event
120
+ # when :state_from_handler
121
+ # next true unless state.is_a?(DecimalType) || state.is_a?(QuantityType) # what is it then?!
122
+ #
123
+ # state = state.to_d if state.is_a?(QuantityType) # ignore the units if QuantityType was given
124
+ # callback.send_update(state | configuration["unit"])
125
+ # false
126
+ # when :command_from_item
127
+ # # strip the unit from the command, as the binding likely can't handle it
128
+ # next true unless command.is_a?(QuantityType)
129
+ #
130
+ # callback.send_command(DecimalType.new(command.to_d))
131
+ # false
132
+ # else
133
+ # true # pass other events through as normal
134
+ # end
135
+ # end
136
+ # # can also be referenced from an `.items` file:
137
+ # # Number:Temperature MyTempWithNonUnitValueFromBinding "I prefer Celsius [%d °C]" { channel="something_that_returns_F"[profile="ruby:set_uom", unit="°F"] }
138
+ #
139
+ def profile(id, &block)
140
+ raise ArgumentError, "Block is required" unless block
141
+
142
+ id = id.to_s
143
+ uid = org.openhab.core.thing.profiles.ProfileTypeUID.new("ruby", id)
144
+
145
+ ThreadLocal.thread_local(openhab_rule_type: "profile", openhab_rule_uid: id) do
146
+ Core::ProfileFactory.instance.register(uid, block)
147
+ end
148
+ end
149
+
150
+ # rubocop:enable Layout/LineLength
151
+
152
+ # @!group Object Access
153
+
154
+ #
155
+ # (see Core::ValueCache)
156
+ #
157
+ # @return [Core::ValueCache] the cache shared among all scripts and UI rules in all languages.
158
+ #
159
+ # @see Core::ValueCache ValueCache
160
+ #
161
+ def shared_cache
162
+ $sharedCache
163
+ end
164
+
165
+ #
166
+ # Fetches all rules from the rule registry.
167
+ #
168
+ # @return [Core::Rules::Registry]
169
+ #
170
+ def rules
171
+ Core::Rules::Registry.instance
172
+ end
173
+
174
+ #
175
+ # Fetches all items from the item registry
176
+ #
177
+ # @return [Core::Items::Registry]
178
+ #
179
+ # The examples all assume the following items exist.
180
+ #
181
+ # ```xtend
182
+ # Dimmer DimmerTest "Test Dimmer"
183
+ # Switch SwitchTest "Test Switch"
184
+ # ```
185
+ #
186
+ # @example
187
+ # logger.info("Item Count: #{items.count}") # Item Count: 2
188
+ # logger.info("Items: #{items.map(&:label).sort.join(', ')}") # Items: Test Dimmer, Test Switch'
189
+ # logger.info("DimmerTest exists? #{items.key?('DimmerTest')}") # DimmerTest exists? true
190
+ # logger.info("StringTest exists? #{items.key?('StringTest')}") # StringTest exists? false
191
+ #
192
+ # @example
193
+ # rule 'Use dynamic item lookup to increase related dimmer brightness when switch is turned on' do
194
+ # changed SwitchTest, to: ON
195
+ # triggered { |item| items[item.name.gsub('Switch','Dimmer')].brighten(10) }
196
+ # end
197
+ #
198
+ # @example
199
+ # rule 'search for a suitable item' do
200
+ # on_load
201
+ # triggered do
202
+ # # Send ON to DimmerTest if it exists, otherwise send it to SwitchTest
203
+ # (items['DimmerTest'] || items['SwitchTest'])&.on
204
+ # end
205
+ # end
206
+ #
207
+ def items
208
+ Core::Items::Registry.instance
209
+ end
210
+
211
+ #
212
+ # Get all things known to openHAB
213
+ #
214
+ # @return [Core::Things::Registry] all Thing objects known to openHAB
215
+ #
216
+ # @example
217
+ # things.each { |thing| logger.info("Thing: #{thing.uid}")}
218
+ # logger.info("Thing: #{things['astro:sun:home'].uid}")
219
+ # homie_things = things.select { |t| t.thing_type_uid == "mqtt:homie300" }
220
+ # zwave_things = things.select { |t| t.binding_id == "zwave" }
221
+ # homeseer_dimmers = zwave_things.select { |t| t.thing_type_uid.id == "homeseer_hswd200_00_000" }
222
+ # things['zwave:device:512:node90'].uid.bridge_ids # => ["512"]
223
+ # things['mqtt:topic:4'].uid.bridge_ids # => []
224
+ #
225
+ def things
226
+ Core::Things::Registry.instance
227
+ end
228
+
229
+ #
230
+ # Provides access to timers created by {after after}
231
+ #
232
+ # @return [TimerManager]
233
+ def timers
234
+ TimerManager.instance
235
+ end
236
+
237
+ # @!group Utilities
238
+
239
+ #
240
+ # Create a timer and execute the supplied block after the specified duration
241
+ #
242
+ # ### Reentrant Timers
243
+ #
244
+ # Timers with an id are reentrant by id. Reentrant means that when the same id is encountered,
245
+ # the timer is rescheduled rather than creating a second new timer. Note that the timer will
246
+ # execute the block provided in the latest call.
247
+ #
248
+ # This removes the need for the usual boilerplate code to manually keep track of timer objects.
249
+ #
250
+ # Timers with `id` can be managed with the built-in {timers} object.
251
+ #
252
+ # When a timer is cancelled, it will be removed from the object.
253
+ #
254
+ # Be sure that your ids are unique. For example, if you're using {Item items} as your
255
+ # ids, you either need to be sure you don't use the same item for multiple logical contexts,
256
+ # or you need to make your id more specific, by doing something like embedding the item in
257
+ # array with a symbol of the timer's purpose, like `[:vacancy, item]`. But also note that
258
+ # assuming default settings, every Ruby file (for file-based rules) or UI rule gets its
259
+ # own instance of the timers object, so you don't need to worry about collisions among
260
+ # different files.
261
+ #
262
+ # @see timers
263
+ # @see Rules::BuilderDSL#changed
264
+ # @see Items::TimedCommand
265
+ #
266
+ # @param [java.time.temporal.TemporalAmount, #to_zoned_date_time, Proc] duration
267
+ # Duration after which to execute the block
268
+ # @param [Object] id ID to associate with timer. The timer can be managed via {timers}.
269
+ # @param [true,false] reschedule Reschedule the timer if it already exists.
270
+ # @yield Block to execute when the timer is elapsed.
271
+ # @yieldparam [Core::Timer] timer
272
+ #
273
+ # @return [Core::Timer] if `reschedule` is false, the existing timer.
274
+ # Otherwise the new timer.
275
+ #
276
+ # @example Create a simple timer
277
+ # after 5.seconds do
278
+ # logger.info("Timer Fired")
279
+ # end
280
+ #
281
+ # @example Timers delegate methods to openHAB timer objects
282
+ # after 1.second do |timer|
283
+ # logger.info("Timer is active? #{timer.active?}")
284
+ # end
285
+ #
286
+ # @example Timers can be rescheduled to run again, waiting the original duration
287
+ # after 3.seconds do |timer|
288
+ # logger.info("Timer Fired")
289
+ # timer.reschedule
290
+ # end
291
+ #
292
+ # @example Timers can be rescheduled for different durations
293
+ # after 3.seconds do |timer|
294
+ # logger.info("Timer Fired")
295
+ # timer.reschedule 5.seconds
296
+ # end
297
+ #
298
+ # @example Timers can be manipulated through the returned object
299
+ # mytimer = after 1.minute do
300
+ # logger.info("It has been 1 minute")
301
+ # end
302
+ #
303
+ # mytimer.cancel
304
+ #
305
+ # @example Reentrant timers will automatically reschedule if the same id is encountered again
306
+ # rule "Turn off closet light after 10 minutes" do
307
+ # changed ClosetLights.members, to: ON
308
+ # triggered do |item|
309
+ # after 10.minutes, id: item do
310
+ # item.ensure.off
311
+ # end
312
+ # end
313
+ # end
314
+ #
315
+ # @example Timers with id can be managed through the built-in `timers` object
316
+ # after 1.minute, id: :foo do
317
+ # logger.info("managed timer has fired")
318
+ # end
319
+ #
320
+ # timers.cancel(:foo)
321
+ #
322
+ # if timers.include?(:foo)
323
+ # logger.info("The timer :foo is not active")
324
+ # end
325
+ #
326
+ # @example Only create a new timer if it isn't already scheduled
327
+ # after(1.minute, id: :foo, reschedule: false) do
328
+ # logger.info("Timer fired")
329
+ # end
330
+ #
331
+ # @example Reentrant timers will execute the block from the most recent call
332
+ # # In the following example, if Item1 received a command, followed by Item2,
333
+ # # the timer will execute the block referring to Item2.
334
+ # rule "Execute The Most Recent Block" do
335
+ # received_command Item1, Item2
336
+ # run do |event|
337
+ # after(10.minutes, id: :common_timer) do
338
+ # logger.info "The latest command was received from #{event.item}"
339
+ # end
340
+ # end
341
+ # end
342
+ #
343
+ def after(duration, id: nil, reschedule: true, &block)
344
+ raise ArgumentError, "Block is required" unless block
345
+
346
+ # Carry rule name to timer
347
+ thread_locals = ThreadLocal.persist
348
+ timers.create(duration, id: id, reschedule: reschedule, thread_locals: thread_locals, block: block)
349
+ end
350
+
351
+ #
352
+ # Convert a string based range into a range of LocalTime, LocalDate, MonthDay, or ZonedDateTime
353
+ # depending on the format of the string.
354
+ #
355
+ # @return [Range] converted range object
356
+ #
357
+ # @example Range#cover?
358
+ # logger.info("Within month-day range") if between('02-20'..'06-01').cover?(MonthDay.now)
359
+ #
360
+ # @example Use in a Case
361
+ # case MonthDay.now
362
+ # when between('01-01'..'03-31')
363
+ # logger.info("First quarter")
364
+ # when between('04-01'..'06-30')
365
+ # logger.info("Second quarter")
366
+ # end
367
+ #
368
+ # @example Create a time range
369
+ # between('7am'..'12pm').cover?(LocalTime.now)
370
+ #
371
+ # @see CoreExt::Between#between? #between?
372
+ #
373
+ def between(range)
374
+ raise ArgumentError, "Supplied object must be a range" unless range.is_a?(Range)
375
+
376
+ start = try_parse_time_like(range.begin)
377
+ finish = try_parse_time_like(range.end)
378
+ Range.new(start, finish, range.exclude_end?)
379
+ end
380
+
381
+ #
382
+ # Limits the frequency of calls to the given block within a given amount of time.
383
+ #
384
+ # It can be useful to throttle certain actions even when a rule is triggered too often.
385
+ # This can be used to debounce triggers in a UI rule.
386
+ #
387
+ # @param (see Debouncer#initialize)
388
+ # @param [Object] id ID to associate with this debouncer.
389
+ # @param [Block] block The block to be debounced.
390
+ #
391
+ # @return [void]
392
+ #
393
+ # @example Run at most once per second even when being called more frequently
394
+ # (1..100).each do
395
+ # debounce for: 1.second do
396
+ # logger.info "This will not be logged more frequently than every 1 second"
397
+ # end
398
+ # sleep 0.1
399
+ # end
400
+ #
401
+ # @see Debouncer Debouncer class
402
+ # @see Rules::BuilderDSL#debounce debounce rule guard
403
+ #
404
+ # @!visibility private
405
+ def debounce(for:, leading: false, idle_time: nil, id: nil, &block)
406
+ interval = binding.local_variable_get(:for)
407
+ id ||= block.source_location
408
+ DSL.debouncers.compute(id) do |_key, debouncer|
409
+ debouncer ||= Debouncer.new(for: interval, leading: leading, idle_time: idle_time)
410
+ debouncer.call(&block)
411
+ debouncer
412
+ end
413
+ end
414
+
415
+ #
416
+ # Waits until calls to this method have stopped firing for a period of time
417
+ # before executing the block.
418
+ #
419
+ # This method acts as a guard for the given block to ensure that it doesn't get executed
420
+ # too frequently. The debounce_for method can be called as frequently as possible.
421
+ # The given block, however, will only be executed once the `debounce_time` has passed
422
+ # since the last call to debounce_for.
423
+ #
424
+ # This method can be used from within a UI rule as well as from a file-based rule.
425
+ #
426
+ # @param (see Rules::BuilderDSL#debounce_for)
427
+ # @param [Object] id ID to associate with this call.
428
+ # @param [Block] block The block to be debounced.
429
+ #
430
+ # @return [void]
431
+ #
432
+ # @example Run a block of code only after an item has stopped changing
433
+ # # This can be placed inside a UI rule with an Item Change trigger for
434
+ # # a Door contact sensor.
435
+ # # If the door state has stopped changing state for 10 minutes,
436
+ # # execute the block.
437
+ # debounce_for(10.minutes) do
438
+ # if DoorState.open?
439
+ # Voice.say("The door has been left open!")
440
+ # end
441
+ # end
442
+ #
443
+ # @see Rules::BuilderDSL#debounce_for Rule builder's debounce_for for a detailed description
444
+ #
445
+ def debounce_for(debounce_time, id: nil, &block)
446
+ idle_time = debounce_time.is_a?(Range) ? debounce_time.begin : debounce_time
447
+ debounce(for: debounce_time, idle_time: idle_time, id: id, &block)
448
+ end
449
+
450
+ #
451
+ # Rate-limits block executions by delaying calls and only executing the last
452
+ # call within the given duration.
453
+ #
454
+ # When throttle_for is called, it will hold from executing the block and start
455
+ # a fixed timer for the given duration. Should more calls occur during
456
+ # this time, keep holding and once the wait time is over, execute the block.
457
+ #
458
+ # {throttle_for} will execute the block after it had waited for the given duration,
459
+ # regardless of how frequently `throttle_for` was called.
460
+ # In contrast, {debounce_for} will wait until there is a minimum interval
461
+ # between two triggers.
462
+ #
463
+ # {throttle_for} is ideal in situations where regular status updates need to be made
464
+ # for frequently changing values. It is also useful when a rule responds to triggers
465
+ # from multiple related items that are updated at around the same time. Instead of
466
+ # executing the rule multiple times, {throttle_for} will wait for a pre-set amount
467
+ # of time since the first group of triggers occurred before executing the rule.
468
+ #
469
+ # @param (see Rules::BuilderDSL#throttle_for)
470
+ # @param [Object] id ID to associate with this call.
471
+ # @param [Block] block The block to be throttled.
472
+ #
473
+ # @return [void]
474
+ #
475
+ # @see Rules::BuilderDSL#debounce_for Rule builder's debounce_for for a detailed description
476
+ # @see Rules::BuilderDSL#throttle_for
477
+ #
478
+ def throttle_for(duration, id: nil, &block)
479
+ debounce(for: duration, id: id, &block)
480
+ end
481
+
482
+ # (see Core::Actions::Transformation.transform)
483
+ def transform(type, function, value)
484
+ Transformation.transform(type, function, value)
485
+ end
486
+
487
+ #
488
+ # Limit how often the given block executes to the specified interval.
489
+ #
490
+ # {only_every} will execute the given block but prevents further executions
491
+ # until the given interval has passed. In contrast, {throttle_for} will not
492
+ # execute the block immediately, and will wait until the end of the interval.
493
+ #
494
+ # @param (see Rules::BuilderDSL#only_every)
495
+ # @param [Object] id ID to associate with this call.
496
+ # @param [Block] block The block to be throttled.
497
+ #
498
+ # @return [void]
499
+ #
500
+ # @example Prevent door bell from ringing repeatedly
501
+ # # This can be called from a UI rule.
502
+ # # For file based rule, use the `only_every` rule guard
503
+ # only_every(30.seconds) do { Audio.play_sound("doorbell.mp3") }
504
+ #
505
+ # @see Rules::BuilderDSL#debounce_for Rule builder's debounce_for for a detailed description
506
+ # @see Rules::BuilderDSL#only_every
507
+ #
508
+ def only_every(interval, id: nil, &block)
509
+ interval = 1.send(interval) if %i[second minute hour day].include?(interval)
510
+ debounce(for: interval, leading: true, id: id, &block)
511
+ end
512
+
513
+ #
514
+ # Store states of supplied items
515
+ #
516
+ # Takes one or more items and returns a map `{Item => State}` with the
517
+ # current state of each item. It is implemented by calling openHAB's
518
+ # [events.storeStates()](https://www.openhab.org/docs/configuration/actions.html#event-bus-actions).
519
+ #
520
+ # @param [Item] items Items to store states of.
521
+ #
522
+ # @return [Core::Items::StateStorage] item states
523
+ #
524
+ # @example
525
+ # states = store_states Item1, Item2
526
+ # ...
527
+ # states.restore
528
+ #
529
+ # @example With a block
530
+ # store_states Item1, Item2 do
531
+ # ...
532
+ # end # the states will be restored here
533
+ #
534
+ def store_states(*items)
535
+ states = Core::Items::StateStorage.from_items(*items)
536
+ if block_given?
537
+ yield
538
+ states.restore
539
+ end
540
+ states
541
+ end
542
+
543
+ #
544
+ # @!group Block Modifiers
545
+ # These methods allow certain operations to be grouped inside the given block
546
+ # to reduce repetitions
547
+ #
548
+
549
+ #
550
+ # Global method that takes a block and for the duration of the block
551
+ # all commands sent will check if the item is in the command's state
552
+ # before sending the command.
553
+ #
554
+ # @yield
555
+ # @return [Object] The result of the block.
556
+ #
557
+ # @example Turn on several switches only if they're not already on
558
+ # ensure_states do
559
+ # Switch1.on
560
+ # Switch2.on
561
+ # end
562
+ #
563
+ # @example
564
+ # # VirtualSwitch is in state `ON`
565
+ # ensure_states do
566
+ # VirtualSwitch << ON # No command will be sent
567
+ # VirtualSwitch.update(ON) # No update will be posted
568
+ # VirtualSwitch << OFF # Off command will be sent
569
+ # VirtualSwitch.update(OFF) # No update will be posted
570
+ # end
571
+ #
572
+ # @example
573
+ # ensure_states do
574
+ # rule 'Items in an execution block will have ensure_states applied to them' do
575
+ # changed VirtualSwitch
576
+ # run do
577
+ # VirtualSwitch.on
578
+ # VirtualSwitch2.on
579
+ # end
580
+ # end
581
+ # end
582
+ #
583
+ # @example
584
+ # rule 'ensure_states must be in an execution block' do
585
+ # changed VirtualSwitch
586
+ # run do
587
+ # ensure_states do
588
+ # VirtualSwitch.on
589
+ # VirtualSwitch2.on
590
+ # end
591
+ # end
592
+ # end
593
+ #
594
+ def ensure_states
595
+ old = Thread.current[:openhab_ensure_states]
596
+ Thread.current[:openhab_ensure_states] = true
597
+ yield
598
+ ensure
599
+ Thread.current[:openhab_ensure_states] = old
600
+ end
601
+
602
+ #
603
+ # Sets a thread local variable to set the default persistence service
604
+ # for method calls inside the block
605
+ #
606
+ # @example
607
+ # persistence(:influxdb) do
608
+ # Item1.persist
609
+ # Item1.changed_since(1.hour)
610
+ # Item1.average_since(12.hours)
611
+ # end
612
+ #
613
+ # @param [Object] service Persistence service either as a String or a Symbol
614
+ # @yield [] Block executed in context of the supplied persistence service
615
+ # @return [Object] The return value from the block.
616
+ #
617
+ # @see persistence!
618
+ # @see OpenHAB::Core::Items::Persistence
619
+ #
620
+ def persistence(service)
621
+ old = persistence!(service)
622
+ yield
623
+ ensure
624
+ persistence!(old)
625
+ end
626
+
627
+ #
628
+ # Permanently sets the default persistence service for the current thread
629
+ #
630
+ # @note This method is only intended for use at the top level of rule
631
+ # scripts. If it's used within library methods, or hap-hazardly within
632
+ # rules, things can get very confusing because the prior state won't be
633
+ # properly restored.
634
+ #
635
+ # @param [Object] service Persistence service either as a String or a Symbol. When nil, use
636
+ # the system's default persistence service.
637
+ # @return [Object,nil] The previous persistence service settings, or nil when using the system's default.
638
+ #
639
+ # @see persistence
640
+ # @see OpenHAB::Core::Items::Persistence
641
+ #
642
+ def persistence!(service = nil)
643
+ old = Thread.current[:openhab_persistence_service]
644
+ Thread.current[:openhab_persistence_service] = service
645
+ old
646
+ end
647
+
648
+ #
649
+ # Sets the implicit unit(s) for operations inside the block.
650
+ #
651
+ # @yield
652
+ #
653
+ # @overload unit(*units)
654
+ # Sets the implicit unit(s) for this thread such that classes
655
+ # operating inside the block can perform automatic conversions to the
656
+ # supplied unit for {QuantityType}.
657
+ #
658
+ # To facilitate conversion of multiple dimensioned and dimensionless
659
+ # numbers the unit block may be used. The unit block attempts to do the
660
+ # _right thing_ based on the mix of dimensioned and dimensionless items
661
+ # within the block. Specifically all dimensionless items are converted to
662
+ # the supplied unit, except when they are used for multiplication or
663
+ # division.
664
+ #
665
+ # @param [String, javax.measure.Unit] units
666
+ # Unit or String representing unit
667
+ # @yield [] The block will be executed in the context of the specified unit(s).
668
+ # @return [Object] the result of the block
669
+ #
670
+ # @example Arithmetic Operations Between QuantityType and Numeric
671
+ # # Number:Temperature NumberC = 23 °C
672
+ # # Number:Temperature NumberF = 70 °F
673
+ # # Number Dimensionless = 2
674
+ # unit('°F') { NumberC.state - NumberF.state < 4 } # => true
675
+ # unit('°F') { NumberC.state - 24 | '°C' < 4 } # => true
676
+ # unit('°F') { (24 | '°C') - NumberC.state < 4 } # => true
677
+ # unit('°C') { NumberF.state - 20 < 2 } # => true
678
+ # unit('°C') { NumberF.state - Dimensionless.state } # => 19.11 °C
679
+ # unit('°C') { NumberF.state - Dimensionless.state < 20 } # => true
680
+ # unit('°C') { Dimensionless.state + NumberC.state == 25 } # => true
681
+ # unit('°C') { 2 + NumberC.state == 25 } # => true
682
+ # unit('°C') { Dimensionless.state * NumberC.state == 46 } # => true
683
+ # unit('°C') { 2 * NumberC.state == 46 } # => true
684
+ # unit('°C') { ( (2 * (NumberF.state + NumberC.state) ) / Dimensionless.state ) < 45 } # => true
685
+ # unit('°C') { [NumberC.state, NumberF.state, Dimensionless.state].min } # => 2
686
+ #
687
+ # @example Commands and Updates inside a unit block
688
+ # unit('°F') { NumberC << 32 }; NumberC.state # => 0 °C
689
+ # # Equivalent to
690
+ # NumberC << "32 °F"
691
+ # # or
692
+ # NumberC << 32 | "°F"
693
+ #
694
+ # @example Specifying Multiple Units
695
+ # unit("°C", "kW") do
696
+ # TemperatureItem.update("50 °F")
697
+ # TemperatureItem.state < 20 # => true. TemperatureItem.state < 20 °C
698
+ # PowerUsage.update("3000 W")
699
+ # PowerUsage.state < 10 # => true. PowerUsage.state < 10 kW
700
+ # end
701
+ #
702
+ # @overload unit(dimension)
703
+ # @param [javax.measure.Dimension] dimension The dimension to fetch the unit for.
704
+ # @return [javax.measure.Unit] The current unit for the thread of the specified dimensions
705
+ #
706
+ # @example
707
+ # unit(SIUnits::METRE.dimension) # => ImperialUnits::FOOT
708
+ #
709
+ def unit(*units)
710
+ if units.length == 1 && units.first.is_a?(javax.measure.Dimension)
711
+ return Thread.current[:openhab_units]&.[](units.first)
712
+ end
713
+
714
+ raise ArgumentError, "You must give a block to set the unit for the duration of" unless block_given?
715
+
716
+ begin
717
+ old_units = unit!(*units)
718
+ yield
719
+ ensure
720
+ Thread.current[:openhab_units] = old_units
721
+ end
722
+ end
723
+
724
+ #
725
+ # Permanently sets the implicit unit(s) for this thread
726
+ #
727
+ # @note This method is only intended for use at the top level of rule
728
+ # scripts. If it's used within library methods, or hap-hazardly within
729
+ # rules, things can get very confusing because the prior state won't be
730
+ # properly restored.
731
+ #
732
+ # {unit!} calls are cumulative - additional calls will not erase the effects
733
+ # of previous calls unless they are for the same dimension.
734
+ #
735
+ # @return [Hash<javax.measure.Dimension=>javax.measure.Unit>]
736
+ # the prior unit configuration
737
+ #
738
+ # @overload unit!(*units)
739
+ # @param [String, javax.measure.Unit] units
740
+ # Unit or String representing unit.
741
+ #
742
+ # @example Set several defaults at once
743
+ # unit!("°F", "ft", "lbs")
744
+ # (50 | "°F") == 50 # => true
745
+ #
746
+ # @example Calls are cumulative
747
+ # unit!("°F")
748
+ # unit!("ft")
749
+ # (50 | "°F") == 50 # => true
750
+ # (2 | "yd") == 6 # => true
751
+ #
752
+ # @example Subsequent calls override the same dimension from previous calls
753
+ # unit!("yd")
754
+ # unit!("ft")
755
+ # (2 | "yd") == 6 # => true
756
+ #
757
+ # @overload unit!
758
+ # Clear all unit settings
759
+ #
760
+ # @example Clear all unit settings
761
+ # unit!("ft")
762
+ # unit!
763
+ # (2 | "yd") == 6 # => false
764
+ #
765
+ def unit!(*units)
766
+ units = units.each_with_object({}) do |unit, r|
767
+ unit = org.openhab.core.types.util.UnitUtils.parse_unit(unit) if unit.is_a?(String)
768
+ r[unit.dimension] = unit
769
+ end
770
+
771
+ old_units = Thread.current[:openhab_units] || {}
772
+ Thread.current[:openhab_units] = units.empty? ? {} : old_units.merge(units)
773
+ old_units
774
+ end
775
+
776
+ #
777
+ # Sets the implicit provider(s) for operations inside the block.
778
+ #
779
+ # @param (see #provider!)
780
+ # @yield [] The block will be executed in the context of the specified unit(s).
781
+ # @return [Object] the result of the block
782
+ #
783
+ # @example
784
+ # provider(metadata: :persistent) do
785
+ # Switch1.metadata[:last_status_from_service] = status
786
+ # end
787
+ #
788
+ # provider!(metadata: { last_status_from_service: :persistent }, Switch2: :persistent)
789
+ # Switch1.metadata[:last_status_from_service] = status # this will persist in JSONDB
790
+ # Switch1.metadata[:homekit] = "Lightbulb" # this will be removed when the script is deleted
791
+ # Switch2.metadata[:homekit] = "Lightbulb" # this will persist in JSONDB
792
+ #
793
+ # @see provider!
794
+ # @see OpenHAB::Core::Provider.current Provider.current for how the current provider is calculated
795
+ #
796
+ def provider(*args, **kwargs)
797
+ raise ArgumentError, "You must give a block to set the provider for the duration of" unless block_given?
798
+
799
+ begin
800
+ old_providers = provider!(*args, **kwargs)
801
+ yield
802
+ ensure
803
+ Thread.current[:openhab_providers] = old_providers
804
+ end
805
+ end
806
+
807
+ #
808
+ # Permanently set the implicit provider(s) for this thread.
809
+ #
810
+ # @note This method is only intended for use at the top level of rule
811
+ # scripts. If it's used within library methods, or hap-hazardly within
812
+ # rules, things can get very confusing because the prior state won't be
813
+ # properly restored.
814
+ #
815
+ # {provider!} calls are cumulative - additional calls will not erase the effects
816
+ # of previous calls unless they are for the same provider type.
817
+ #
818
+ # @overload provider!(things: nil, items: nil, metadata: nil, links: nil, **metadata_items)
819
+ #
820
+ # @param [Core::Provider, org.openhab.core.common.registry.ManagedProvider, :persistent, :transient, Proc] providers
821
+ # An explicit provider to use. If it's a {Core::Provider}, the type will be inferred automatically.
822
+ # Otherwise it's applied to all types.
823
+ # @param [Hash] providers_by_type
824
+ # A list of providers by type. Type can be `:items`, `:metadata`, `:things`, `:links`,
825
+ # an {Item} applying the provider to all metadata on that item, or a String or Symbol
826
+ # applying the provider to all metadata of that namespace.
827
+ #
828
+ # The provider can be a {org.openhab.core.common.registry.Provider Provider}, `:persistent`,
829
+ # `:transient`, or a Proc returning one of those types. When the Proc is called for metadata
830
+ # elements, the {Core::Items::Metadata::Hash} will be passed as an argument. Therefore it's
831
+ # recommended that you use a Proc, not a Lambda, for permissive argument matching.
832
+ #
833
+ # @return [void]
834
+ #
835
+ # @see provider
836
+ # @see OpenHAB::Core::Provider.current Provider.current for how the current provider is calculated
837
+ #
838
+ def provider!(*providers, **providers_by_type)
839
+ thread_providers = Thread.current[:openhab_providers] ||= {}
840
+ old_providers = thread_providers.dup
841
+
842
+ providers.each do |provider|
843
+ case provider
844
+ when Core::Provider
845
+ thread_providers[provider.class.type] = provider
846
+ when org.openhab.core.common.registry.ManagedProvider
847
+ type = provider.type
848
+ unless type
849
+ raise ArgumentError, "#{provider.inspect} is for objects which are not supported by openhab-scripting"
850
+ end
851
+
852
+ thread_providers[type] = provider
853
+ when Proc,
854
+ :transient,
855
+ :persistent
856
+ Core::Provider::KNOWN_TYPES.each do |known_type|
857
+ thread_providers[known_type] = provider
858
+ end
859
+ when Hash
860
+ # non-symbols can't be used as kwargs, so Item keys show up as a separate hash here
861
+ # just merge it in, and allow it to be handled below
862
+ providers_by_type.merge!(provider)
863
+ else
864
+ raise ArgumentError, "#{provider.inspect} is not a valid provider"
865
+ end
866
+ end
867
+
868
+ providers_by_type.each do |type, provider|
869
+ case provider
870
+ when Proc,
871
+ org.openhab.core.common.registry.ManagedProvider,
872
+ :transient,
873
+ :persistent,
874
+ nil
875
+ nil
876
+ else
877
+ raise ArgumentError, "#{provider.inspect} is not a valid provider"
878
+ end
879
+
880
+ case type
881
+ when :items, :metadata, :things, :links
882
+ if provider.is_a?(org.openhab.core.common.registry.ManagedProvider) && provider.type != type
883
+ raise ArgumentError, "#{provider.inspect} is not a provider for #{type}"
884
+ end
885
+
886
+ thread_providers[type] = provider
887
+ when Symbol, String
888
+ (thread_providers[:metadata_namespaces] ||= {})[type.to_s] = provider
889
+ when Item
890
+ (thread_providers[:metadata_items] ||= {})[type.name] = provider
891
+ else
892
+ raise ArgumentError, "#{type.inspect} is not provider type"
893
+ end
894
+ end
895
+
896
+ old_providers
897
+ end
898
+
899
+ #
900
+ # @see CoreExt::Ephemeris
901
+ #
902
+ # @overload holiday_file(file)
903
+ #
904
+ # Sets a thread local variable to use a specific holiday file
905
+ # for {CoreExt::Ephemeris ephemeris calls} inside the block.
906
+ #
907
+ # @param [String, nil] file Path to a file defining holidays;
908
+ # `nil` to reset to default.
909
+ # @yield [] Block executed in context of the supplied holiday file
910
+ # @return [Object] The return value from the block.
911
+ #
912
+ # @example Set a specific holiday configuration file temporarily
913
+ # holiday_file("/home/cody/holidays.xml") do
914
+ # Time.now.next_holiday
915
+ # end
916
+ #
917
+ # @see holiday_file!
918
+ #
919
+ # @overload holiday_file
920
+ #
921
+ # Returns the current thread local value for the holiday file.
922
+ #
923
+ # @return [String, nil] the current holiday file
924
+ #
925
+ def holiday_file(*args)
926
+ raise ArgumentError, "wrong number of arguments (given #{args.length}, expected 0..1)" if args.length > 1
927
+
928
+ old = Thread.current[:openhab_holiday_file]
929
+ return old if args.empty?
930
+
931
+ holiday_file!(args.first)
932
+ yield
933
+ ensure
934
+ holiday_file!(old)
935
+ end
936
+
937
+ #
938
+ # Sets a thread local variable to set the default holiday file.
939
+ #
940
+ # @see https://github.com/svendiedrichsen/jollyday/tree/master/src/main/resources/holidays Example data files from the source
941
+ #
942
+ # @example
943
+ # holiday_file!("/home/cody/holidays.xml")
944
+ # Time.now.next_holiday
945
+ #
946
+ # @param [String, nil] file Path to a file defining holidays;
947
+ # `nil` to reset to default.
948
+ # @return [Symbol, nil] the new holiday file
949
+ #
950
+ def holiday_file!(file = nil)
951
+ Thread.current[:openhab_holiday_file] = file
952
+ end
953
+
954
+ # @!visibility private
955
+ def try_parse_time_like(string)
956
+ return string unless string.is_a?(String)
957
+
958
+ exception = nil
959
+ [java.time.LocalTime, java.time.LocalDate, java.time.MonthDay, java.time.ZonedDateTime].each do |klass|
960
+ return klass.parse(string)
961
+ rescue ArgumentError => e
962
+ exception ||= e
963
+ next
964
+ end
965
+
966
+ raise exception
967
+ end
968
+ end
969
+ end
970
+
971
+ OpenHAB::Core.wait_till_openhab_ready
972
+
973
+ # import Items classes into global namespace
974
+ OpenHAB::Core::Items.import_into_global_namespace
975
+
976
+ # Extend `main` with DSL methods
977
+ singleton_class.include(OpenHAB::DSL)
978
+
979
+ logger.debug "openHAB JRuby Scripting Library Version #{OpenHAB::DSL::VERSION} Loaded"