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,458 @@
1
+ # frozen_string_literal: true
2
+
3
+ module OpenHAB
4
+ #
5
+ # Contains loaded Ruby transformations as class methods
6
+ #
7
+ # Only during testing.
8
+ #
9
+ # @example Corresponds to `transform/compass.script`
10
+ # OpenHAB::Transform.compass("59 °")
11
+ #
12
+ # @example Corresponds to `transform/compass.script`
13
+ # OpenHAB::Transform.compass("30", param: "7")
14
+ #
15
+ module Transform
16
+ class << self
17
+ # @!visibility private
18
+ def add_script(modules, script)
19
+ full_name = modules.join("/")
20
+ name = modules.pop
21
+ (@scripts ||= {})[full_name] = engine_factory.script_engine.compile(script)
22
+
23
+ mod = modules.inject(self) { |m, n| m.const_get(n, false) }
24
+ mod.singleton_class.define_method(name) do |input, **kwargs|
25
+ Transform.send(:transform, full_name, input, kwargs)
26
+ end
27
+ end
28
+
29
+ private
30
+
31
+ def engine_factory
32
+ @engine_factory ||= org.jruby.embed.jsr223.JRubyEngineFactory.new
33
+ end
34
+
35
+ def transform(name, input, kwargs)
36
+ script = @scripts[name]
37
+ ctx = script.engine.context
38
+ ctx.set_attribute("input", input.to_s, javax.script.ScriptContext::ENGINE_SCOPE)
39
+ kwargs.each do |(k, v)|
40
+ ctx.set_attribute(k.to_s, v.to_s, javax.script.ScriptContext::ENGINE_SCOPE)
41
+ end
42
+ script.eval
43
+ end
44
+ end
45
+ end
46
+ end
47
+
48
+ module OpenHAB
49
+ module RSpec
50
+ #
51
+ # Provides helper methods for use in specs, to easily work with and adjust
52
+ # the openHAB environment.
53
+ #
54
+ # These methods are automatically available in RSpec spec blocks, as well
55
+ # as other per-spec hooks like `before` and `after`. You can also call them
56
+ # explicitly.
57
+ #
58
+ module Helpers
59
+ module BindingHelper
60
+ # @!visibility private
61
+ def add_kwargs_to_current_binding(binding, kwargs)
62
+ kwargs.each { |(k, v)| binding.local_variable_set(k, v) }
63
+ end
64
+ end
65
+ private_constant :BindingHelper
66
+
67
+ # Yard crashes on this; be tricky so it doesn't realize what's going on
68
+ s = singleton_class
69
+ s.include(Helpers)
70
+
71
+ module_function
72
+
73
+ #
74
+ # Reconfigure all items to autoupdate
75
+ #
76
+ # To bypass any items configured to not autoupdate, waiting for the binding to update them.
77
+ #
78
+ # @return [void]
79
+ #
80
+ def autoupdate_all_items
81
+ if instance_variable_defined?(:@autoupdated_items)
82
+ raise RuntimeError "You should only call `autoupdate_all_items` once per spec"
83
+ end
84
+
85
+ @autoupdated_items = []
86
+ @spec_metadata_provider = Core::Items::Metadata::Provider.current
87
+
88
+ $ir.for_each do |_provider, item|
89
+ if (hash = item.metadata["autoupdate"])
90
+ provider = Core::Items::Metadata::Provider.registry.provider_for(hash.uid)
91
+ provider.remove(hash.uid)
92
+ @autoupdated_items << [provider, hash]
93
+ provider(@spec_metadata_provider) do
94
+ item.metadata["autoupdate"] = "true"
95
+ end
96
+ end
97
+ end
98
+ end
99
+
100
+ #
101
+ # Execute all pending timers
102
+ #
103
+ # @return [void]
104
+ #
105
+ def execute_timers
106
+ raise "Cannot execute timers when timers aren't mocked" unless self.class.mock_timers?
107
+
108
+ now = ZonedDateTime.now
109
+ DSL::TimerManager.instance.instance_variable_get(:@timers).each_key do |t|
110
+ t.execute if t.active? && t.execution_time <= now
111
+ end
112
+ end
113
+
114
+ #
115
+ # Wait `duration` seconds, then execute any pending timers
116
+ #
117
+ # If timers are mocked, it will use Timecop. If they're not mocked, it
118
+ # will just sleep for `duration`
119
+ #
120
+ # @return [void]
121
+ #
122
+ def time_travel_and_execute_timers(duration)
123
+ if self.class.mock_timers?
124
+ Timecop.frozen? ? Timecop.freeze(duration) : Timecop.travel(duration)
125
+ execute_timers
126
+ else
127
+ sleep duration
128
+ end
129
+ end
130
+
131
+ #
132
+ # Suspend rules for the duration of the block
133
+ #
134
+ # @return [Object] The return value from the block.
135
+ #
136
+ def suspend_rules(&block)
137
+ SuspendRules.suspend_rules(&block)
138
+ end
139
+
140
+ #
141
+ # Calls the block repeatedly until the expectations inside pass.
142
+ #
143
+ # @param [Duration] how_long how long to keep trying before giving up
144
+ # @yield
145
+ # @return [void]
146
+ def wait(how_long = 2.seconds)
147
+ start = Process.clock_gettime(Process::CLOCK_MONOTONIC)
148
+
149
+ begin
150
+ yield
151
+ rescue ::RSpec::Expectations::ExpectationNotMetError,
152
+ ::RSpec::Mocks::MockExpectationError
153
+ raise if Process.clock_gettime(Process::CLOCK_MONOTONIC) > start + how_long.to_f
154
+
155
+ sleep 0.1
156
+ retry
157
+ end
158
+ end
159
+
160
+ #
161
+ # Manually send an event to a trigger channel
162
+ #
163
+ # @param [String, Core::Things::Channel, Core::Things::ChannelUID] channel The channel to trigger.
164
+ # @param [String] event The event data to send to the channel.
165
+ # @return [void]
166
+ #
167
+ def trigger_channel(channel, event = "")
168
+ channel = org.openhab.core.thing.ChannelUID.new(channel) if channel.is_a?(String)
169
+ channel = channel.uid if channel.is_a?(org.openhab.core.thing.Channel)
170
+ thing = channel.thing
171
+ thing.handler.callback.channel_triggered(nil, channel, event)
172
+ end
173
+
174
+ #
175
+ # Require all files configured to be autorequired with the jrubyscripting addon in openHAB.
176
+ #
177
+ # This method is normally called by RSpec hooks.
178
+ #
179
+ # @return [void]
180
+ #
181
+ def autorequires
182
+ ENV["RUBYLIB"] ||= ""
183
+ ENV["RUBYLIB"] += ":" unless ENV["RUBYLIB"].empty?
184
+ ENV["RUBYLIB"] += rubylib_dirs.join(":")
185
+
186
+ $LOAD_PATH.unshift(*ENV["RUBYLIB"]
187
+ .split(File::PATH_SEPARATOR)
188
+ .reject(&:empty?)
189
+ .reject do |path|
190
+ $LOAD_PATH.include?(path)
191
+ end)
192
+
193
+ requires = jrubyscripting_config&.get("require") || ""
194
+ requires.split(",").each do |f|
195
+ require f.strip
196
+ end
197
+ end
198
+
199
+ #
200
+ # Launch the karaf instance
201
+ #
202
+ # This method is normally called by RSpec hooks.
203
+ #
204
+ # @return [void]
205
+ # @see Configuration
206
+ #
207
+ def launch_karaf(include_bindings: true,
208
+ include_jsondb: true,
209
+ private_confdir: false,
210
+ use_root_instance: false)
211
+ karaf = Karaf.new("#{Dir.pwd}/.karaf")
212
+ karaf.include_bindings = include_bindings
213
+ karaf.include_jsondb = include_jsondb
214
+ karaf.private_confdir = private_confdir
215
+ karaf.use_root_instance = use_root_instance
216
+ main = karaf.launch
217
+
218
+ require "openhab/dsl"
219
+
220
+ require_relative "mocks/persistence_service"
221
+ require_relative "mocks/timer"
222
+
223
+ # override several DSL methods
224
+ require_relative "openhab/core/items/proxy"
225
+ require_relative "openhab/core/things/proxy"
226
+ require_relative "openhab/core/actions"
227
+
228
+ ps = Mocks::PersistenceService.instance
229
+ bundle = org.osgi.framework.FrameworkUtil.get_bundle(org.openhab.core.persistence.PersistenceService.java_class)
230
+ bundle.bundle_context.register_service(org.openhab.core.persistence.PersistenceService.java_class, ps, nil)
231
+
232
+ # wait for the rule engine
233
+ rs = OSGi.service("org.openhab.core.service.ReadyService")
234
+ filter = org.openhab.core.service.ReadyMarkerFilter.new
235
+ .with_type(org.openhab.core.service.StartLevelService::STARTLEVEL_MARKER_TYPE)
236
+ .with_identifier(org.openhab.core.service.StartLevelService::STARTLEVEL_RULEENGINE.to_s)
237
+
238
+ karaf.send(:wait) do |continue|
239
+ rs.register_tracker(org.openhab.core.service.ReadyService::ReadyTracker.impl { continue.call }, filter)
240
+ end
241
+
242
+ # RSpec additions
243
+ require_relative "suspend_rules"
244
+
245
+ if defined?(::RSpec)
246
+ ::RSpec.configure do |config|
247
+ config.include OpenHAB::DSL
248
+ end
249
+ end
250
+ main
251
+ rescue Exception => e
252
+ puts e.inspect
253
+ puts e.backtrace
254
+ raise
255
+ end
256
+
257
+ #
258
+ # Load all Ruby rules in the config/automation directory
259
+ #
260
+ # This method is normally called by RSpec hooks.
261
+ #
262
+ # @return [void]
263
+ #
264
+ def load_rules
265
+ automation_paths = Array(::RSpec.configuration.openhab_automation_search_paths)
266
+
267
+ lib_dirs = rubylib_dirs.map { |d| File.join(d, "") }
268
+ lib_dirs << File.join(gem_home, "")
269
+
270
+ SuspendRules.suspend_rules do
271
+ files = automation_paths.map { |p| Dir["#{p}/**/*.rb"] }.flatten
272
+ files.reject! do |f|
273
+ lib_dirs.any? { |l| f.start_with?(l) }
274
+ end
275
+ files.sort_by { |f| [get_start_level(f), f] }.each do |f|
276
+ load f
277
+ rescue Exception => e
278
+ warn "Failed loading #{f}: #{e.inspect}"
279
+ warn e.backtrace
280
+ end
281
+ end
282
+ end
283
+
284
+ #
285
+ # Load all Ruby transformations in the config/transform directory
286
+ #
287
+ # Since Ruby transformations must end with the .script extension, you must include
288
+ # an Emacs modeline comment (`# -*- mode: ruby -*-`) in your script for it to be
289
+ # recognized.
290
+ #
291
+ # This method is normally called by RSpec hooks.
292
+ #
293
+ # @return [void]
294
+ #
295
+ def load_transforms
296
+ transform_path = "#{org.openhab.core.OpenHAB.config_folder}/transform"
297
+ Dir["#{transform_path}/**/*.script"].each do |filename|
298
+ script = File.read(filename)
299
+ next unless ruby_file?(script)
300
+
301
+ filename.slice!(0..transform_path.length)
302
+ dir = File.dirname(filename)
303
+ modules = (dir == ".") ? [] : moduleize(dir)
304
+ basename = File.basename(filename)
305
+ method = basename[0...-7]
306
+ modules << method
307
+ Transform.add_script(modules, script)
308
+ end
309
+ end
310
+
311
+ #
312
+ # Install an openHAB addon
313
+ #
314
+ # @param [String] addon_id The addon id, such as "binding-mqtt"
315
+ # @param [true,false] wait Wait until OSGi has confirmed the bundle is installed and running before returning.
316
+ # @param [String,Array<String>] ready_markers Array of ready marker types to wait for.
317
+ # The addon's bundle id is used as the identifier.
318
+ # @return [void]
319
+ #
320
+ def install_addon(addon_id, wait: true, ready_markers: nil)
321
+ service_filter = "(component.name=org.openhab.core.karafaddons)"
322
+ addon_service = OSGi.service("org.openhab.core.addon.AddonService", filter: service_filter)
323
+ addon_service.install(addon_id)
324
+ return unless wait
325
+
326
+ addon = nil
327
+ loop do
328
+ addon = addon_service.get_addon(addon_id, nil)
329
+ break if addon.installed?
330
+
331
+ sleep 0.25
332
+ end
333
+
334
+ return unless ready_markers
335
+
336
+ package_id = addon.logger_packages.first
337
+
338
+ ready_markers = Array(ready_markers).map do |marker|
339
+ case marker
340
+ when String
341
+ org.openhab.core.service.ReadyMarker.new(marker, package_id)
342
+ else
343
+ marker
344
+ end
345
+ end
346
+
347
+ rs = OSGi.service("org.openhab.core.service.ReadyService")
348
+ loop do
349
+ break if ready_markers.all? { |rm| rs.ready?(rm) }
350
+
351
+ sleep 0.25
352
+ end
353
+ end
354
+
355
+ # @return [String] The filename of the openHAB log.
356
+ def log_file
357
+ "#{java.lang.System.get_property("openhab.logdir", nil)}/openhab.log"
358
+ end
359
+
360
+ #
361
+ # @return [Array<String>] The log lines since this spec started.
362
+ #
363
+ # @example
364
+ # it "logs" do
365
+ # logger.trace("log line")
366
+ # expect(spec_log_lines).to include(match(/TRACE.*log line/))
367
+ # end
368
+ #
369
+ def spec_log_lines
370
+ File.open(log_file, "rb") do |f|
371
+ f.seek(@log_index) if @log_index
372
+ f.read.split("\n")
373
+ end
374
+ end
375
+
376
+ private
377
+
378
+ def jrubyscripting_config
379
+ ca = OSGi.service("org.osgi.service.cm.ConfigurationAdmin")
380
+ ca.get_configuration("org.openhab.automation.jrubyscripting", nil)&.properties
381
+ end
382
+
383
+ def gem_home
384
+ gem_home = jrubyscripting_config&.get("gem_home")
385
+ return "#{org.openhab.core.OpenHAB.config_folder}/automation/ruby/.gem" unless gem_home
386
+
387
+ # strip everything after the first {
388
+ gem_home.split("{", 2).first
389
+ end
390
+
391
+ def rubylib_dirs
392
+ jrubyscripting_config&.get("rubylib")&.split(File::PATH_SEPARATOR) ||
393
+ ["#{org.openhab.core.OpenHAB.config_folder}/automation/ruby/lib"]
394
+ end
395
+
396
+ def get_start_level(file)
397
+ return ($1 || $2).to_i if file =~ %r{/sl(\d{2})/[^/]+$|\.sl(\d{2})\.[^/.]+$}
398
+
399
+ 50
400
+ end
401
+
402
+ EMACS_MODELINE_REGEXP = /# -\*-(.+)-\*-/.freeze
403
+ private_constant :EMACS_MODELINE_REGEXP
404
+
405
+ def parse_emacs_modeline(line)
406
+ line[EMACS_MODELINE_REGEXP, 1]
407
+ &.split(";")
408
+ &.map(&:strip)
409
+ &.map { |l| l.split(":", 2).map(&:strip).tap { |a| a[1] ||= nil } }
410
+ &.to_h
411
+ end
412
+
413
+ def ruby_file?(script)
414
+ # check the first 1KB for an emacs magic comment
415
+ script[0..1024].split("\n").any? { |line| parse_emacs_modeline(line)&.dig("mode") == "ruby" }
416
+ end
417
+
418
+ def moduleize(term)
419
+ term
420
+ .sub(/^[a-z\d]*/, &:capitalize)
421
+ .gsub(%r{(?:_|(/))([a-z\d]*)}) { "#{$1}#{$2.capitalize}" }
422
+ .split("/")
423
+ end
424
+
425
+ # need to transfer autoupdate metadata from GenericMetadataProvider to ManagedMetadataProvider
426
+ # so that we can mutate it in the future
427
+ def set_up_autoupdates
428
+ registry = Core::Items::Metadata::Provider.registry
429
+ registry.class.field_reader :identifierToElement
430
+
431
+ autoupdate_provider = Core::Items::Metadata::Provider.send(:new)
432
+ registry.all.each do |metadata|
433
+ next unless metadata.uid.namespace == "autoupdate"
434
+
435
+ # tweak the registry to allow us to overwrite this element
436
+ registry.identifierToElement.delete(metadata.uid)
437
+ autoupdate_provider.add(metadata)
438
+ end
439
+ end
440
+
441
+ def restore_autoupdate_items
442
+ return unless instance_variable_defined?(:@autoupdated_items)
443
+
444
+ @autoupdated_items.each do |(provider, hash)|
445
+ @spec_metadata_provider.remove(hash.uid)
446
+ provider.add(hash.instance_variable_get(:@metadata))
447
+ end
448
+ @autoupdated_items = nil
449
+ end
450
+ end
451
+
452
+ if defined?(::RSpec)
453
+ ::RSpec.configure do |config|
454
+ config.include Helpers
455
+ end
456
+ end
457
+ end
458
+ end
@@ -0,0 +1,113 @@
1
+ # frozen_string_literal: true
2
+
3
+ module OpenHAB
4
+ #
5
+ # This module contains helper methods, hooks, and infrastracture to
6
+ # boot openHAB inside of JRuby, and run RSpec (or other Ruby processes)
7
+ # in that context.
8
+ #
9
+ # @see file:testing.md Testing Your Rules
10
+ #
11
+ module RSpec
12
+ Object.include Helpers if defined?(IRB)
13
+
14
+ # @!visibility private
15
+ module Hooks
16
+ class << self
17
+ attr_accessor :cache_script_extension
18
+ end
19
+ self.cache_script_extension = nil
20
+ end
21
+
22
+ Helpers.launch_karaf(
23
+ include_bindings: Configuration.include_bindings,
24
+ include_jsondb: Configuration.include_jsondb,
25
+ private_confdir: Configuration.private_confdir,
26
+ use_root_instance: Configuration.use_root_instance
27
+ )
28
+
29
+ if defined?(::RSpec)
30
+ ::RSpec.configure do |config|
31
+ require_relative "example_group"
32
+ config.include ExampleGroup
33
+
34
+ config.before(:suite) do
35
+ if config.mock_framework.framework_name == :rspec
36
+ require_relative "mocks/instance_method_stasher"
37
+ require_relative "mocks/space"
38
+ end
39
+
40
+ Helpers.autorequires unless Configuration.private_confdir
41
+ Helpers.send(:set_up_autoupdates)
42
+ Helpers.load_transforms
43
+ Helpers.load_rules
44
+
45
+ if DSL.shared_cache
46
+ Hooks.cache_script_extension = OSGi.service(
47
+ "org.openhab.core.automation.module.script.ScriptExtensionProvider",
48
+ filter:
49
+ "(component.name=org.openhab.core.automation.module.script.rulesupport.internal.CacheScriptExtension)"
50
+ )
51
+ Hooks.cache_script_extension.class.field_reader :sharedCache
52
+ end
53
+ end
54
+
55
+ config.before do
56
+ suspend_rules do
57
+ $ir.for_each do |_provider, item|
58
+ next if item.is_a?(GroupItem) # groups only have calculated states
59
+
60
+ item.state = NULL unless item.raw_state == NULL
61
+ end
62
+ end
63
+ end
64
+
65
+ # Each spec gets temporary providers
66
+ [Core::Items::Provider,
67
+ Core::Items::Metadata::Provider,
68
+ Core::Rules::Provider,
69
+ Core::Things::Provider,
70
+ Core::Things::Links::Provider].each do |klass|
71
+ config.around do |example|
72
+ klass.new(&example)
73
+ end
74
+ end
75
+
76
+ config.before do |example|
77
+ # clear persisted thing status
78
+ tm = Core::Things.manager
79
+ begin
80
+ tm.class.field_reader :disabledStorage
81
+ tm.disabledStorage.keys.each { |k| tm.disabledStorage.remove(k) } # rubocop:disable Style/HashEachMethods not a hash
82
+ rescue NameError
83
+ # @deprecated OH3.4
84
+ tm.class.field_reader :storage
85
+ tm.storage.keys.each { |k| tm.storage.remove(k) } # rubocop:disable Style/HashEachMethods not a hash
86
+ end
87
+ @profile_factory = Core::ProfileFactory.send(:new)
88
+ allow(Core::ProfileFactory).to receive(:instance).and_return(@profile_factory)
89
+
90
+ stub_const("OpenHAB::Core::Timer", Mocks::Timer) if self.class.mock_timers?
91
+
92
+ log_line = "rspec #{example.location} # #{example.full_description}"
93
+ logger.info(log_line)
94
+ Logger.events.info(log_line)
95
+ @log_index = File.size(log_file)
96
+ end
97
+
98
+ config.after do
99
+ @profile_factory.unregister
100
+ timers.cancel_all
101
+ # timers and rules have already been canceled, so we can safely just
102
+ # wipe this
103
+ DSL::Items::TimedCommand.timed_commands.clear
104
+ Timecop.return
105
+ restore_autoupdate_items
106
+ Mocks::PersistenceService.instance.reset
107
+ Hooks.cache_script_extension.sharedCache.clear if DSL.shared_cache
108
+ DSL.persistence!(nil)
109
+ end
110
+ end
111
+ end
112
+ end
113
+ end
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ module OpenHAB
4
+ module RSpec
5
+ # @!visibility private
6
+ module JRuby
7
+ # Basically org.jruby.embed.osgi.OSGiIsolatedScriptingContainer$BundleGetResources,
8
+ # but implemented in Ruby so that it doesn't have a hard dependency on
9
+ # org.osgi.bundle.Bundle -- which we may need to load!
10
+ class OSGiBundleClassLoader
11
+ include org.jruby.util.Loader
12
+
13
+ def initialize(bundle)
14
+ @bundle = bundle
15
+ end
16
+
17
+ def get_resource(path)
18
+ @bundle.get_resource(path)
19
+ end
20
+
21
+ def get_resources(path)
22
+ @bundle.get_resources(path)
23
+ end
24
+
25
+ def load_class(name)
26
+ @bundle.load_class(name)
27
+ end
28
+
29
+ def get_class_loader # rubocop:disable Naming/AccessorMethodName
30
+ @bundle&.adapt(org.osgi.framework.wiring.BundleWiring.java_class)&.class_loader
31
+ end
32
+ end
33
+
34
+ module InstanceConfig
35
+ def add_loader(loader)
36
+ # have to use Ruby-style class reference for the defined? check
37
+ if defined?(Java::OrgOsgiFramework::Bundle) && loader.is_a?(org.osgi.framework.Bundle)
38
+ loader = OSGiBundleClassLoader.new(loader)
39
+ end
40
+ super(loader)
41
+ end
42
+ end
43
+ org.jruby.RubyInstanceConfig.prepend(InstanceConfig)
44
+ end
45
+ end
46
+ end