openhab-scripting 4.47.0 → 5.0.0

Sign up to get free protection for your applications and to get access to all the features.
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 -38
@@ -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