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
@@ -1,108 +1,99 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'ruby2_keywords'
4
- require 'openhab/log/logger'
5
- require_relative 'generic_item'
3
+ require "ruby2_keywords"
6
4
 
7
5
  module OpenHAB
8
6
  module DSL
9
- # Functionality to implement +ensure+/+ensure_states+
10
- module Ensure
11
- # Contains the global +ensure_states+ method
12
- module EnsureStates
13
- # Global method that takes a block and for the duration of the block
14
- # all commands sent will check if the item is in the command's state
15
- # before sending the command.
16
- #
17
- # @example Turn on several switches only if they're not already on
18
- # ensure_states do
19
- # Switch1.on
20
- # Switch2.on
21
- # end
22
- def ensure_states
23
- old = Thread.current[:ensure_states]
24
- Thread.current[:ensure_states] = true
25
- yield
26
- ensure
27
- Thread.current[:ensure_states] = old
7
+ module Items
8
+ # Functionality to implement `ensure`/`ensure_states`
9
+ module Ensure
10
+ # Contains the `ensure` method mixed into {Item} and {GroupItem::Members}
11
+ module Ensurable
12
+ # Fluent method call that you can chain commands on to, that will
13
+ # then automatically ensure that the item is not in the command's
14
+ # state before sending the command.
15
+ #
16
+ # @example Turn switch on only if it's not on
17
+ # MySwitch.ensure.on
18
+ # @example Turn on all switches in a group that aren't already on
19
+ # MySwitchGroup.members.ensure.on
20
+ def ensure
21
+ ItemDelegate.new(self)
22
+ end
28
23
  end
29
- module_function :ensure_states
30
- end
31
24
 
32
- # Contains the +ensure+ method mixed into {GenericItem} and {GroupItem::GroupMembers}
33
- module Ensurable
34
- # Fluent method call that you can chain commands on to, that will
35
- # then automatically ensure that the item is not in the command's
36
- # state before sending the command.
25
+ # Extensions for {::Item} to implement {Ensure}'s functionality
37
26
  #
38
- # @example Turn switch on only if it's not on
39
- # MySwitch.ensure.on
40
- # @example Turn on all switches in a group that aren't already on
41
- # MySwitchGroup.members.ensure.on
42
- def ensure
43
- GenericItemDelegate.new(self)
44
- end
45
- end
27
+ # @see OpenHAB::DSL.ensure ensure
28
+ # @see OpenHAB::DSL.ensure_states ensure_states
29
+ module Item
30
+ include Ensurable
46
31
 
47
- # Extensions for {Items::GenericItem} to implement {Ensure}'s
48
- # functionality
49
- module GenericItem
50
- include Ensurable
32
+ Core::Items::GenericItem.prepend(self)
51
33
 
52
- # If +ensure_states+ is active (by block or chained method), then
53
- # check if this item is in the command's state before actually
54
- # sending the command
55
- %i[command update].each do |ensured_method|
56
- define_method(ensured_method) do |command|
57
- return super(command) unless Thread.current[:ensure_states]
34
+ # If `ensure_states` is active (by block or chained method), then
35
+ # check if this item is in the command's state before actually
36
+ # sending the command
37
+ %i[command update].each do |ensured_method|
38
+ # def command(state)
39
+ # return super(state) unless Thread.current[:openhab_ensure_states]
40
+ #
41
+ # formatted_state = format_command(state)
42
+ # logger.trace do
43
+ # "#{name} ensure #{state}, format_command: #{formatted_state}, current state: #{raw_state}"
44
+ # end
45
+ # return if raw_state == formatted_state
46
+ #
47
+ # super(formatted_state)
48
+ # end
49
+ class_eval <<~RUBY, __FILE__, __LINE__ + 1 # rubocop:disable Style/DocumentDynamicEvalDefinition
50
+ def #{ensured_method}(state)
51
+ return super(state) unless Thread.current[:openhab_ensure_states]
58
52
 
59
- logger.trace do
60
- "#{name} ensure #{command}, format_type_pre: #{format_type_pre(command)}, current state: #{state}"
61
- end
62
- return if state == format_type_pre(command)
53
+ formatted_state = format_#{ensured_method}(state)
54
+ logger.trace do
55
+ "\#{name} ensure \#{state}, format_#{ensured_method}: \#{formatted_state}, current state: \#{raw_state}"
56
+ end
57
+ return if raw_state.as(formatted_state.class) == formatted_state
63
58
 
64
- super(command)
59
+ super(formatted_state)
60
+ end
61
+ RUBY
65
62
  end
66
63
  end
67
- alias << command
68
- end
69
-
70
- # "anonymous" class that wraps any method call in +ensure_states+
71
- # before forwarding to the wrapped object
72
- # @!visibility private
73
- class GenericItemDelegate
74
- def initialize(item)
75
- @item = item
76
- end
77
64
 
65
+ # "anonymous" class that wraps any method call in `ensure_states`
66
+ # before forwarding to the wrapped object
78
67
  # @!visibility private
79
- # this is explicitly defined, instead of aliased, because #command
80
- # doesn't actually exist as a method, and will go through method_missing
81
- def <<(command)
82
- command(command)
83
- end
68
+ class ItemDelegate
69
+ def initialize(item)
70
+ @item = item
71
+ end
84
72
 
85
- # activate +ensure_states+ before forwarding to the wrapped object
86
- ruby2_keywords def method_missing(method, *args, &block)
87
- return super unless @item.respond_to?(method)
73
+ # @!visibility private
74
+ # this is explicitly defined, instead of aliased, because #command
75
+ # doesn't actually exist as a method, and will go through method_missing
76
+ def <<(command)
77
+ command(command)
78
+ end
88
79
 
89
- ensure_states do
90
- @item.__send__(method, *args, &block)
80
+ # activate `ensure_states` before forwarding to the wrapped object
81
+ ruby2_keywords def method_missing(method, *args, &block)
82
+ return super unless @item.respond_to?(method)
83
+
84
+ DSL.ensure_states do
85
+ @item.__send__(method, *args, &block)
86
+ end
91
87
  end
92
- end
93
88
 
94
- # .
95
- def respond_to_missing?(method, include_private = false)
96
- @item.respond_to?(method, include_private) || super
89
+ # .
90
+ def respond_to_missing?(method, include_private = false)
91
+ @item.respond_to?(method, include_private) || super
92
+ end
97
93
  end
98
94
  end
99
- end
100
95
 
101
- module Items
102
- GenericItem.prepend(OpenHAB::DSL::Ensure::GenericItem)
103
- GroupItem::GroupMembers.include(OpenHAB::DSL::Ensure::Ensurable)
96
+ Core::Items::GroupItem::Members.include(Ensure::Ensurable)
104
97
  end
105
98
  end
106
99
  end
107
-
108
- Object.include OpenHAB::DSL::Ensure::EnsureStates
@@ -1,198 +1,253 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'openhab/dsl/timers'
4
- require 'openhab/dsl/rules/rule_triggers'
5
- require 'openhab/dsl/rules/triggers/triggers'
6
- require 'openhab/log/logger'
7
- require 'java'
8
-
9
- require_relative 'generic_item'
10
-
11
3
  module OpenHAB
12
4
  module DSL
13
5
  module Items
14
- # Module enables timed commands e.g. Switch.on for: 3.minutes
6
+ # Extensions for {Item} to implement timed commands
7
+ #
8
+ # All items have an implicit timer associated with them, enabling to
9
+ # easily set an item into a specific state for a specified duration and
10
+ # then at the expiration of that duration have the item automatically
11
+ # change to another state. These timed commands are reentrant, meaning
12
+ # if the same timed command is triggered while an outstanding timed
13
+ # command exist, that timed command will be rescheduled rather than
14
+ # creating a distinct timed command.
15
+ #
16
+ # Timed commands are initiated by using the 'for:' argument with the
17
+ # command. This is available on both the 'command' method and any
18
+ # command-specific methods, e.g. {SwitchItem#on}.
19
+ #
20
+ # The timer will be cancelled, and the item's state will not be changed
21
+ # to the on_expire state if:
22
+ # - The item receives any command within the timed command duration.
23
+ # - The item is updated to a different state, even if it is then updated
24
+ # back to the same state.
25
+ #
26
+ # For example, if you have a Switch on a timer and another rule sends
27
+ # a command to that item, even when it's commanded to the same state,
28
+ # the timer will be automatically canceled.
29
+ #
30
+ # Sending a different duration (for:) value for the timed
31
+ # command will reschedule the timed command for that new duration.
32
+ #
15
33
  module TimedCommand
16
- # Stores information about timed commands
17
- TimedCommandDetails = Struct.new(:item, :command, :was, :duration, :on_expire, :timer, :expired, :cancel_rule,
18
- :rule_uid, keyword_init: true) do
34
+ #
35
+ # Provides information about why the expiration block of a
36
+ # {TimedCommand#command timed command} is being called.
37
+ #
38
+ # @attr [Item] item
39
+ # @!visibility private
40
+ # @attr [Types::Type, Proc] on_expire
41
+ # @!visibility private
42
+ # @attr [Core::Timer] timer
43
+ # @!visibility private
44
+ # @attr [Symbol] resolution
45
+ # @!visibility private
46
+ # @attr [String] rule_uid
47
+ # @!visibility private
48
+ # @attr [Mutex] mutex
49
+ # @!visibility private
50
+ #
51
+ TimedCommandDetails = Struct.new(:item,
52
+ :on_expire,
53
+ :timer,
54
+ :resolution,
55
+ :rule_uid,
56
+ :mutex,
57
+ keyword_init: true) do
58
+ # @return [true, false]
19
59
  def expired?
20
- expired
60
+ resolution == :expired
21
61
  end
22
62
 
23
- def canceled?
24
- !expired?
63
+ # @return [true, false]
64
+ def cancelled?
65
+ resolution == :cancelled
25
66
  end
26
67
  end
27
68
 
28
- @timed_commands = {}
69
+ @timed_commands = java.util.concurrent.ConcurrentHashMap.new
29
70
 
30
71
  class << self
72
+ # @!visibility private
31
73
  attr_reader :timed_commands
32
74
  end
33
75
 
34
- # Extensions for {Items::GenericItem} to implement timed commands
35
- module GenericItem
36
- #
37
- # Sends command to an item for specified duration, then on timer expiration sends
38
- # the expiration command to the item
39
- #
40
- # @param [Types::Type] command to send to object
41
- # @param [Duration] for duration for item to be in command state
42
- # @param [Types::Type] on_expire Command to send when duration expires
43
- #
44
- #
45
- # rubocop: disable Metrics/MethodLength
46
- # The mutex makes this over 10 lines, but there is usable way to break this method up
47
- def command(command, for: nil, on_expire: nil, &block)
48
- duration = binding.local_variable_get(:for)
49
- return super(command) unless duration
50
-
51
- # Timer needs access to rule to disable, rule needs access to timer to cancel.
52
- # Using a mutex to ensure neither fires before the other is constructed
53
- semaphore = Mutex.new
54
-
55
- semaphore.synchronize do
56
- timed_command_details = TimedCommand.timed_commands[self]
57
- if timed_command_details.nil?
58
- create_timed_command(command: command, duration: duration,
59
- semaphore: semaphore, on_expire: on_expire, &block)
60
- else
61
- logger.trace "Outstanding Timed Command #{timed_command_details} encountered - rescheduling"
62
- timed_command_details.duration = duration # Capture updated duration
63
- timed_command_details.timer.reschedule duration
76
+ Core::Items::GenericItem.prepend(self)
77
+
78
+ #
79
+ # Sends command to an item for specified duration, then on timer expiration sends
80
+ # the expiration command to the item
81
+ #
82
+ # @note If a block is provided, and the timer is canceled because the
83
+ # item changed state while it was waiting, the block will still be
84
+ # executed. Be sure to check {TimedCommandDetails#expired? #expired?}
85
+ # and/or {TimedCommandDetails#cancelled? #cancelled?} to determine why
86
+ # the block was called.
87
+ #
88
+ # @param [Command] command to send to object
89
+ # @param [Duration] for duration for item to be in command state
90
+ # @param [Command] on_expire Command to send when duration expires
91
+ # @yield If a block is provided, `on_expire` is ignored and the block
92
+ # is expected to set the item to the desired state or carry out some
93
+ # other action.
94
+ # @yieldparam [TimedCommandDetails] timed_command
95
+ # @return [self]
96
+ #
97
+ # @example
98
+ # Switch.command(ON, for: 5.minutes)
99
+ # @example
100
+ # Switch.on for: 5.minutes
101
+ # @example
102
+ # Dimmer.on for: 5.minutes, on_expire: 50
103
+ # @example
104
+ # Dimmer.on(for: 5.minutes) { |event| Dimmer.off if Light.on? }
105
+ #
106
+ def command(command, for: nil, on_expire: nil, &block)
107
+ duration = binding.local_variable_get(:for)
108
+ return super(command) unless duration
109
+
110
+ on_expire = block if block
111
+
112
+ TimedCommand.timed_commands.compute(self) do |_key, timed_command_details|
113
+ if timed_command_details.nil?
114
+ # no prior timed command
115
+ on_expire ||= default_on_expire(command)
116
+ super(command)
117
+ create_timed_command(command, duration: duration, on_expire: on_expire)
118
+ else
119
+ timed_command_details.mutex.synchronize do
120
+ if timed_command_details.resolution
121
+ # timed command that finished, but hadn't removed itself from the map yet
122
+ # (it doesn't do so under the mutex to prevent a deadlock).
123
+ # just create a new one
124
+ on_expire ||= default_on_expire(command)
125
+ super(command)
126
+ create_timed_command(command, duration: duration, on_expire: on_expire)
127
+ else
128
+ # timed command still pending; reset it
129
+ logger.trace "Outstanding Timed Command #{timed_command_details} encountered - rescheduling"
130
+ timed_command_details.on_expire = on_expire unless on_expire.nil?
131
+ timed_command_details.timer.reschedule(duration)
132
+ # disable the cancel rule while we send the new command
133
+ DSL.rules[timed_command_details.rule_uid].disable
134
+ super(command)
135
+ DSL.rules[timed_command_details.rule_uid].enable
136
+ timed_command_details
137
+ end
64
138
  end
65
139
  end
66
-
67
- self
68
140
  end
69
- # rubocop: enable Metrics/MethodLength
70
- alias << command
71
-
72
- private
73
-
74
- # Creates a new timed command and places it in the TimedCommand hash
75
- # rubocop: disable Metrics/AbcSize
76
- # rubocop: disable Metrics/MethodLength
77
- # There is no feasible way to break this method into smaller components
78
- def create_timed_command(command:, duration:, semaphore:, on_expire:, &block)
79
- on_expire ||= default_on_expire(command)
80
- timed_command_details = TimedCommandDetails.new(item: self, command: command, was: state,
81
- on_expire: on_expire, duration: duration)
82
-
83
- # Send specified command after capturing current state
84
- command(command)
85
-
86
- timed_command_details.timer = timed_command_timer(timed_command_details, semaphore, &block)
87
- timed_command_details.cancel_rule = TimedCommandCancelRule.new(timed_command_details, semaphore,
88
- &block)
89
- timed_command_details.rule_uid = OpenHAB::DSL::Rules::Rule.automation_manager
90
- .addRule(timed_command_details.cancel_rule)
91
- .getUID
92
- logger.trace "Created Timed Command #{timed_command_details}"
93
- TimedCommand.timed_commands[self] = timed_command_details
94
- end
95
- # rubocop: enable Metrics/AbcSize
96
- # rubocop: enable Metrics/MethodLength
97
-
98
- # Creates the timer to handle changing the item state when timer expires or invoking user supplied block
99
- # @param [TimedCommandDetailes] timed_command_details details about the timed command
100
- # @param [Mutex] semaphore Semaphore to lock on to prevent race condition between rule and timer
101
- # @return [Timer] Timer
102
- # rubocop: disable Metrics/MethodLength
103
- # There is no feasible way to break this method into smaller components
104
- def timed_command_timer(timed_command_details, semaphore, &block)
105
- after(timed_command_details.duration, id: self) do
106
- semaphore.synchronize do
107
- logger.trace "Timed command expired - #{timed_command_details}"
108
- cancel_timed_command_rule(timed_command_details)
109
- timed_command_details.expired = true
110
- if block
111
- logger.trace "Invoking block #{block} after timed command for #{id} expired"
112
- yield(timed_command_details)
113
- else
114
- command(timed_command_details.on_expire)
115
- end
116
141
 
117
- TimedCommand.timed_commands.delete(timed_command_details.item)
142
+ self
143
+ end
144
+
145
+ private
146
+
147
+ # Creates a new timed command and places it in the TimedCommand hash
148
+ def create_timed_command(command, duration:, on_expire:)
149
+ timed_command_details = TimedCommandDetails.new(item: self,
150
+ on_expire: on_expire,
151
+ mutex: Mutex.new)
152
+
153
+ timed_command_details.timer = timed_command_timer(timed_command_details, duration)
154
+ cancel_rule = TimedCommandCancelRule.new(command, timed_command_details)
155
+ unmanaged_rule = Core.automation_manager.add_unmanaged_rule(cancel_rule)
156
+ timed_command_details.rule_uid = unmanaged_rule.uid
157
+ Core::Rules::Provider.current.add(unmanaged_rule)
158
+ logger.trace "Created Timed Command #{timed_command_details}"
159
+ timed_command_details
160
+ end
161
+
162
+ # Creates the timer to handle changing the item state when timer expires or invoking user supplied block
163
+ # @param [TimedCommandDetailes] timed_command_details details about the timed command
164
+ # @return [Timer] Timer
165
+ def timed_command_timer(timed_command_details, duration)
166
+ DSL.after(duration) do
167
+ timed_command_details.mutex.synchronize do
168
+ logger.trace "Timed command expired - #{timed_command_details}"
169
+ DSL.rules.remove(timed_command_details.rule_uid)
170
+ timed_command_details.resolution = :expired
171
+ case timed_command_details.on_expire
172
+ when Proc
173
+ logger.trace "Invoking block #{timed_command_details.on_expire} after timed command for #{name} expired"
174
+ timed_command_details.on_expire.call(timed_command_details)
175
+ when Core::Types::UnDefType
176
+ update(timed_command_details.on_expire)
177
+ else
178
+ command(timed_command_details.on_expire)
118
179
  end
119
180
  end
181
+ TimedCommand.timed_commands.delete(timed_command_details.item)
120
182
  end
121
- # rubocop: enable Metrics/MethodLength
183
+ end
122
184
 
123
- # Cancels timed command rule
124
- # @param [TimedCommandDetailed] timed_command_details details about the timed command
125
- def cancel_timed_command_rule(timed_command_details)
126
- logger.trace "Removing rule: #{timed_command_details.rule_uid}"
127
- OpenHAB::DSL::Rules::Rule.registry.remove(timed_command_details.rule_uid)
128
- end
185
+ #
186
+ # The default expire for ON/OFF is their inverse
187
+ #
188
+ def default_on_expire(command)
189
+ return !command if command.is_a?(Core::Types::OnOffType)
129
190
 
130
- #
131
- # The default expire for ON/OFF is their inverse
132
- #
133
- def default_on_expire(command)
134
- case format_type_pre(command)
135
- when ON then OFF
136
- when OFF then ON
137
- else state
138
- end
191
+ raw_state
192
+ end
193
+
194
+ #
195
+ # Rule to cancel timed commands
196
+ #
197
+ # @!visibility private
198
+ class TimedCommandCancelRule < org.openhab.core.automation.module.script.rulesupport.shared.simple.SimpleRule
199
+ def initialize(command, timed_command_details)
200
+ super()
201
+ @timed_command_details = timed_command_details
202
+ # Capture rule name if known
203
+ @thread_locals = ThreadLocal.persist
204
+ self.name = "Cancel implicit timer for #{timed_command_details.item.name}"
205
+ self.triggers = [
206
+ Rules::RuleTriggers.trigger(
207
+ type: Rules::Triggers::Changed::ITEM_STATE_CHANGE,
208
+ config: {
209
+ "itemName" => timed_command_details.item.name,
210
+ "previousState" => timed_command_details.item.format_command(command).to_s
211
+ }
212
+ ),
213
+ Rules::RuleTriggers.trigger(
214
+ type: Rules::Triggers::Command::ITEM_COMMAND,
215
+ config: {
216
+ "itemName" => timed_command_details.item.name
217
+ }
218
+ )
219
+ ]
220
+ self.visibility = Core::Rules::Visibility::HIDDEN
139
221
  end
140
222
 
141
223
  #
142
- # Rule to cancel timed commands
224
+ # Execute the rule
143
225
  #
144
- class TimedCommandCancelRule < Java::OrgOpenhabCoreAutomationModuleScriptRulesupportSharedSimple::SimpleRule
145
- include OpenHAB::Log
146
- include OpenHAB::Core::ThreadLocal
147
-
148
- def initialize(timed_command_details, semaphore, &block) # rubocop:disable Metrics/MethodLength
149
- super()
150
- @semaphore = semaphore
151
- @timed_command_details = timed_command_details
152
- @block = block
153
- # Capture rule name if known
154
- @thread_locals = Thread.current[:RULE_NAME] ? { RULE_NAME: Thread.current[:RULE_NAME] } : {}
155
- set_name("Cancels implicit timer for #{timed_command_details.item.id}")
156
- set_triggers([OpenHAB::DSL::Rules::RuleTriggers.trigger(
157
- type: OpenHAB::DSL::Rules::Triggers::Changed::ITEM_STATE_CHANGE,
158
- config: { 'itemName' => timed_command_details.item.name,
159
- 'previousState' => timed_command_details.command.to_s }
160
- )])
161
- end
162
-
163
- #
164
- # Execute the rule
165
- #
166
- # @param [Map] _mod map provided by OpenHAB rules engine
167
- # @param [Map] inputs map provided by OpenHAB rules engine containing event and other information
168
- #
169
- #
170
- # rubocop: disable Metrics/MethodLength
171
- # rubocop: disable Metrics/AbcSize
172
- # There is no feasible way to break this method into smaller components
173
- def execute(_mod = nil, inputs = nil)
174
- OpenHAB::DSL.import_presets
175
- @semaphore.synchronize do
176
- thread_local(**@thread_locals) do
177
- logger.trace "Canceling implicit timer #{@timed_command_details.timer} for " \
178
- "#{@timed_command_details.item.id} because received event #{inputs}"
179
- @timed_command_details.timer.cancel
180
- # Disabled due to OpenHAB design
181
- $scriptExtension.get('ruleRegistry').remove(@timed_command_details.rule_uid)
182
- TimedCommand.timed_commands.delete(@timed_command_details.item)
183
- if @block
184
- logger.trace 'Executing user supplied block on timed command cancelation'
185
- @block&.call(@timed_command_details)
186
- end
226
+ # @param [java.util.Map] _mod map provided by openHAB rules engine
227
+ # @param [java.util.Map] inputs map provided by openHAB rules engine containing event and other information
228
+ #
229
+ def execute(_mod = nil, inputs = nil)
230
+ ThreadLocal.thread_local(**@thread_locals) do
231
+ @timed_command_details.mutex.synchronize do
232
+ logger.trace "Canceling implicit timer #{@timed_command_details.timer} for " \
233
+ "#{@timed_command_details.item.name} because received event #{inputs}"
234
+ @timed_command_details.timer.cancel
235
+ DSL.rules.remove(@timed_command_details.rule_uid)
236
+ @timed_command_details.resolution = :cancelled
237
+ if @timed_command_details.on_expire.is_a?(Proc)
238
+ logger.trace "Executing user supplied block on timed command cancelation"
239
+ @timed_command_details.on_expire.call(@timed_command_details)
187
240
  end
188
241
  end
242
+ TimedCommand.timed_commands.delete(@timed_command_details.item)
243
+ rescue Exception => e
244
+ raise if defined?(::RSpec)
245
+
246
+ logger.log_exception(e)
189
247
  end
190
- # rubocop: enable Metrics/MethodLength
191
- # rubocop: enable Metrics/AbcSize
192
248
  end
193
249
  end
194
250
  end
195
- GenericItem.prepend(TimedCommand::GenericItem)
196
251
  end
197
252
  end
198
253
  end