openhab-scripting 4.47.0 → 5.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (282) hide show
  1. checksums.yaml +4 -4
  2. data/lib/openhab/core/actions/audio.rb +54 -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/item_state_updated_event.rb +22 -0
  23. data/lib/openhab/core/events/thing_status_info_event.rb +55 -0
  24. data/lib/openhab/core/events.rb +10 -0
  25. data/lib/openhab/core/items/accepted_data_types.rb +29 -0
  26. data/lib/openhab/core/items/color_item.rb +52 -0
  27. data/lib/openhab/core/items/contact_item.rb +52 -0
  28. data/lib/openhab/core/items/date_time_item.rb +59 -0
  29. data/lib/openhab/core/items/dimmer_item.rb +148 -0
  30. data/lib/openhab/core/items/generic_item.rb +292 -0
  31. data/lib/openhab/core/items/group_item.rb +176 -0
  32. data/lib/openhab/{dsl → core}/items/image_item.rb +35 -29
  33. data/lib/openhab/core/items/item.rb +273 -0
  34. data/lib/openhab/core/items/location_item.rb +34 -0
  35. data/lib/openhab/core/items/metadata/hash.rb +433 -0
  36. data/lib/openhab/core/items/metadata/namespace_hash.rb +475 -0
  37. data/lib/openhab/core/items/metadata/provider.rb +48 -0
  38. data/lib/openhab/core/items/metadata.rb +11 -0
  39. data/lib/openhab/core/items/number_item.rb +62 -0
  40. data/lib/openhab/core/items/numeric_item.rb +22 -0
  41. data/lib/openhab/core/items/persistence.rb +416 -0
  42. data/lib/openhab/core/items/player_item.rb +66 -0
  43. data/lib/openhab/core/items/provider.rb +44 -0
  44. data/lib/openhab/core/items/proxy.rb +136 -0
  45. data/lib/openhab/core/items/registry.rb +86 -0
  46. data/lib/openhab/core/items/rollershutter_item.rb +68 -0
  47. data/lib/openhab/core/items/semantics/enumerable.rb +177 -0
  48. data/lib/openhab/core/items/semantics.rb +473 -0
  49. data/lib/openhab/core/items/state_storage.rb +53 -0
  50. data/lib/openhab/core/items/string_item.rb +28 -0
  51. data/lib/openhab/core/items/switch_item.rb +78 -0
  52. data/lib/openhab/core/items.rb +108 -0
  53. data/lib/openhab/{dsl → core}/lazy_array.rb +9 -3
  54. data/lib/openhab/core/profile_factory.rb +132 -0
  55. data/lib/openhab/core/provider.rb +230 -0
  56. data/lib/openhab/core/proxy.rb +130 -0
  57. data/lib/openhab/core/registry.rb +40 -0
  58. data/lib/openhab/core/rules/module.rb +26 -0
  59. data/lib/openhab/core/rules/provider.rb +25 -0
  60. data/lib/openhab/core/rules/registry.rb +94 -0
  61. data/lib/openhab/core/rules/rule.rb +174 -0
  62. data/lib/openhab/core/rules.rb +25 -0
  63. data/lib/openhab/core/script_handling.rb +78 -20
  64. data/lib/openhab/core/things/channel.rb +48 -0
  65. data/lib/openhab/core/things/channel_uid.rb +51 -0
  66. data/lib/openhab/core/things/item_channel_link.rb +33 -0
  67. data/lib/openhab/core/things/links/provider.rb +78 -0
  68. data/lib/openhab/core/things/profile_callback.rb +52 -0
  69. data/lib/openhab/core/things/provider.rb +29 -0
  70. data/lib/openhab/core/things/proxy.rb +87 -0
  71. data/lib/openhab/core/things/registry.rb +73 -0
  72. data/lib/openhab/core/things/thing.rb +194 -0
  73. data/lib/openhab/core/things.rb +22 -0
  74. data/lib/openhab/core/timer.rb +148 -0
  75. data/lib/openhab/{dsl → core}/types/comparable_type.rb +5 -3
  76. data/lib/openhab/{dsl → core}/types/date_time_type.rb +55 -127
  77. data/lib/openhab/{dsl → core}/types/decimal_type.rb +50 -48
  78. data/lib/openhab/{dsl → core}/types/hsb_type.rb +35 -83
  79. data/lib/openhab/core/types/increase_decrease_type.rb +34 -0
  80. data/lib/openhab/core/types/next_previous_type.rb +34 -0
  81. data/lib/openhab/{dsl → core}/types/numeric_type.rb +20 -7
  82. data/lib/openhab/core/types/on_off_type.rb +46 -0
  83. data/lib/openhab/core/types/open_closed_type.rb +41 -0
  84. data/lib/openhab/{dsl → core}/types/percent_type.rb +19 -20
  85. data/lib/openhab/core/types/play_pause_type.rb +38 -0
  86. data/lib/openhab/core/types/point_type.rb +117 -0
  87. data/lib/openhab/core/types/quantity_type.rb +325 -0
  88. data/lib/openhab/core/types/raw_type.rb +26 -0
  89. data/lib/openhab/core/types/refresh_type.rb +27 -0
  90. data/lib/openhab/core/types/rewind_fastforward_type.rb +38 -0
  91. data/lib/openhab/core/types/stop_move_type.rb +34 -0
  92. data/lib/openhab/{dsl → core}/types/string_type.rb +17 -28
  93. data/lib/openhab/{dsl → core}/types/type.rb +42 -40
  94. data/lib/openhab/core/types/un_def_type.rb +38 -0
  95. data/lib/openhab/core/types/up_down_type.rb +50 -0
  96. data/lib/openhab/core/types.rb +82 -0
  97. data/lib/openhab/{dsl → core}/uid.rb +4 -23
  98. data/lib/openhab/core/value_cache.rb +188 -0
  99. data/lib/openhab/core.rb +98 -0
  100. data/lib/openhab/core_ext/between.rb +32 -0
  101. data/lib/openhab/core_ext/ephemeris.rb +53 -0
  102. data/lib/openhab/core_ext/java/class.rb +34 -0
  103. data/lib/openhab/core_ext/java/duration.rb +142 -0
  104. data/lib/openhab/core_ext/java/list.rb +436 -0
  105. data/lib/openhab/core_ext/java/local_date.rb +104 -0
  106. data/lib/openhab/core_ext/java/local_time.rb +118 -0
  107. data/lib/openhab/core_ext/java/map.rb +66 -0
  108. data/lib/openhab/core_ext/java/month.rb +71 -0
  109. data/lib/openhab/core_ext/java/month_day.rb +119 -0
  110. data/lib/openhab/core_ext/java/period.rb +103 -0
  111. data/lib/openhab/core_ext/java/temporal_amount.rb +34 -0
  112. data/lib/openhab/core_ext/java/time.rb +62 -0
  113. data/lib/openhab/core_ext/java/unit.rb +15 -0
  114. data/lib/openhab/core_ext/java/zoned_date_time.rb +213 -0
  115. data/lib/openhab/core_ext/ruby/array.rb +21 -0
  116. data/lib/openhab/core_ext/ruby/date.rb +96 -0
  117. data/lib/openhab/core_ext/ruby/date_time.rb +55 -0
  118. data/lib/openhab/core_ext/ruby/module.rb +15 -0
  119. data/lib/openhab/core_ext/ruby/numeric.rb +195 -0
  120. data/lib/openhab/core_ext/ruby/range.rb +70 -0
  121. data/lib/openhab/core_ext/ruby/symbol.rb +7 -0
  122. data/lib/openhab/core_ext/ruby/time.rb +108 -0
  123. data/lib/openhab/core_ext.rb +18 -0
  124. data/lib/openhab/dsl/debouncer.rb +259 -0
  125. data/lib/openhab/dsl/events/watch_event.rb +18 -0
  126. data/lib/openhab/dsl/events.rb +9 -0
  127. data/lib/openhab/dsl/gems.rb +1 -1
  128. data/lib/openhab/dsl/items/builder.rb +578 -0
  129. data/lib/openhab/dsl/items/ensure.rb +73 -82
  130. data/lib/openhab/dsl/items/timed_command.rb +214 -159
  131. data/lib/openhab/dsl/rules/automation_rule.rb +129 -116
  132. data/lib/openhab/dsl/rules/builder.rb +1935 -0
  133. data/lib/openhab/dsl/rules/guard.rb +51 -114
  134. data/lib/openhab/dsl/rules/name_inference.rb +66 -25
  135. data/lib/openhab/dsl/rules/property.rb +48 -75
  136. data/lib/openhab/dsl/rules/rule_triggers.rb +22 -27
  137. data/lib/openhab/dsl/rules/terse.rb +58 -14
  138. data/lib/openhab/dsl/rules/triggers/changed.rb +48 -94
  139. data/lib/openhab/dsl/rules/triggers/channel.rb +9 -40
  140. data/lib/openhab/dsl/rules/triggers/command.rb +14 -63
  141. data/lib/openhab/dsl/rules/triggers/conditions/duration.rb +34 -69
  142. data/lib/openhab/dsl/rules/triggers/conditions/proc.rb +6 -14
  143. data/lib/openhab/dsl/rules/triggers/cron/cron.rb +48 -82
  144. data/lib/openhab/dsl/rules/triggers/cron/cron_handler.rb +30 -47
  145. data/lib/openhab/dsl/rules/triggers/trigger.rb +7 -28
  146. data/lib/openhab/dsl/rules/triggers/updated.rb +21 -45
  147. data/lib/openhab/dsl/rules/triggers/watch/watch_handler.rb +257 -102
  148. data/lib/openhab/dsl/rules/triggers.rb +12 -0
  149. data/lib/openhab/dsl/rules.rb +8 -0
  150. data/lib/openhab/dsl/things/builder.rb +299 -0
  151. data/lib/openhab/{core → dsl}/thread_local.rb +27 -17
  152. data/lib/openhab/dsl/timer_manager.rb +204 -0
  153. data/lib/openhab/dsl/version.rb +9 -0
  154. data/lib/openhab/dsl.rb +979 -0
  155. data/lib/openhab/log.rb +355 -0
  156. data/lib/openhab/osgi.rb +68 -0
  157. data/lib/openhab/rspec/configuration.rb +56 -0
  158. data/lib/openhab/rspec/example_group.rb +132 -0
  159. data/lib/openhab/rspec/helpers.rb +458 -0
  160. data/lib/openhab/rspec/hooks.rb +113 -0
  161. data/lib/openhab/rspec/jruby.rb +46 -0
  162. data/lib/openhab/rspec/karaf.rb +851 -0
  163. data/lib/openhab/rspec/mocks/bundle_install_support.rb +25 -0
  164. data/lib/openhab/rspec/mocks/bundle_resolver.rb +30 -0
  165. data/lib/openhab/rspec/mocks/event_admin.rb +146 -0
  166. data/lib/openhab/rspec/mocks/instance_method_stasher.rb +22 -0
  167. data/lib/openhab/rspec/mocks/persistence_service.rb +155 -0
  168. data/lib/openhab/rspec/mocks/safe_caller.rb +40 -0
  169. data/lib/openhab/rspec/mocks/space.rb +23 -0
  170. data/lib/openhab/rspec/mocks/synchronous_executor.rb +63 -0
  171. data/lib/openhab/rspec/mocks/thing_handler.rb +76 -0
  172. data/lib/openhab/rspec/mocks/timer.rb +134 -0
  173. data/lib/openhab/rspec/openhab/core/actions.rb +38 -0
  174. data/lib/openhab/rspec/openhab/core/items/proxy.rb +15 -0
  175. data/lib/openhab/rspec/openhab/core/things/proxy.rb +27 -0
  176. data/lib/openhab/rspec/shell.rb +31 -0
  177. data/lib/openhab/rspec/suspend_rules.rb +50 -0
  178. data/lib/openhab/rspec.rb +26 -0
  179. data/lib/openhab/yard/base_helper.rb +19 -0
  180. data/lib/openhab/yard/cli/stats.rb +23 -0
  181. data/lib/openhab/yard/code_objects/group_object.rb +23 -0
  182. data/lib/openhab/yard/code_objects/java/base.rb +31 -0
  183. data/lib/openhab/yard/code_objects/java/class_object.rb +11 -0
  184. data/lib/openhab/yard/code_objects/java/field_object.rb +15 -0
  185. data/lib/openhab/yard/code_objects/java/interface_object.rb +15 -0
  186. data/lib/openhab/yard/code_objects/java/package_object.rb +11 -0
  187. data/lib/openhab/yard/code_objects/java/proxy.rb +23 -0
  188. data/lib/openhab/yard/coderay.rb +17 -0
  189. data/lib/openhab/yard/handlers/jruby/base.rb +58 -0
  190. data/lib/openhab/yard/handlers/jruby/class_handler.rb +18 -0
  191. data/lib/openhab/yard/handlers/jruby/constant_handler.rb +18 -0
  192. data/lib/openhab/yard/handlers/jruby/java_import_handler.rb +30 -0
  193. data/lib/openhab/yard/handlers/jruby/mixin_handler.rb +23 -0
  194. data/lib/openhab/yard/html_helper.rb +78 -0
  195. data/lib/openhab/yard/markdown_helper.rb +148 -0
  196. data/lib/openhab/yard/tags/constant_directive.rb +20 -0
  197. data/lib/openhab/yard/tags/group_directive.rb +24 -0
  198. data/lib/openhab/yard/tags/library.rb +3 -0
  199. data/lib/openhab/yard.rb +38 -0
  200. metadata +476 -106
  201. data/lib/openhab/core/item_proxy.rb +0 -29
  202. data/lib/openhab/core/load_path.rb +0 -19
  203. data/lib/openhab/core/openhab_setup.rb +0 -29
  204. data/lib/openhab/core/osgi.rb +0 -58
  205. data/lib/openhab/core/services.rb +0 -24
  206. data/lib/openhab/dsl/actions.rb +0 -114
  207. data/lib/openhab/dsl/between.rb +0 -25
  208. data/lib/openhab/dsl/channel.rb +0 -43
  209. data/lib/openhab/dsl/dsl.rb +0 -59
  210. data/lib/openhab/dsl/group.rb +0 -54
  211. data/lib/openhab/dsl/imports.rb +0 -21
  212. data/lib/openhab/dsl/items/color_item.rb +0 -76
  213. data/lib/openhab/dsl/items/comparable_item.rb +0 -62
  214. data/lib/openhab/dsl/items/contact_item.rb +0 -41
  215. data/lib/openhab/dsl/items/date_time_item.rb +0 -65
  216. data/lib/openhab/dsl/items/dimmer_item.rb +0 -65
  217. data/lib/openhab/dsl/items/generic_item.rb +0 -229
  218. data/lib/openhab/dsl/items/group_item.rb +0 -127
  219. data/lib/openhab/dsl/items/item_equality.rb +0 -59
  220. data/lib/openhab/dsl/items/item_registry.rb +0 -54
  221. data/lib/openhab/dsl/items/items.rb +0 -109
  222. data/lib/openhab/dsl/items/location_item.rb +0 -59
  223. data/lib/openhab/dsl/items/metadata.rb +0 -326
  224. data/lib/openhab/dsl/items/number_item.rb +0 -17
  225. data/lib/openhab/dsl/items/numeric_item.rb +0 -87
  226. data/lib/openhab/dsl/items/persistence.rb +0 -307
  227. data/lib/openhab/dsl/items/player_item.rb +0 -58
  228. data/lib/openhab/dsl/items/rollershutter_item.rb +0 -51
  229. data/lib/openhab/dsl/items/semantics/enumerable.rb +0 -91
  230. data/lib/openhab/dsl/items/semantics.rb +0 -227
  231. data/lib/openhab/dsl/items/string_item.rb +0 -51
  232. data/lib/openhab/dsl/items/switch_item.rb +0 -70
  233. data/lib/openhab/dsl/monkey_patch/actions/actions.rb +0 -4
  234. data/lib/openhab/dsl/monkey_patch/actions/script_thing_actions.rb +0 -39
  235. data/lib/openhab/dsl/monkey_patch/events/events.rb +0 -7
  236. data/lib/openhab/dsl/monkey_patch/events/item_command.rb +0 -85
  237. data/lib/openhab/dsl/monkey_patch/events/item_event.rb +0 -28
  238. data/lib/openhab/dsl/monkey_patch/events/item_state.rb +0 -61
  239. data/lib/openhab/dsl/monkey_patch/events/item_state_changed.rb +0 -60
  240. data/lib/openhab/dsl/monkey_patch/events/thing_status_info.rb +0 -33
  241. data/lib/openhab/dsl/monkey_patch/java/java.rb +0 -4
  242. data/lib/openhab/dsl/monkey_patch/java/local_time.rb +0 -44
  243. data/lib/openhab/dsl/monkey_patch/java/time_extensions.rb +0 -50
  244. data/lib/openhab/dsl/monkey_patch/java/zoned_date_time.rb +0 -45
  245. data/lib/openhab/dsl/monkey_patch/ruby/number.rb +0 -104
  246. data/lib/openhab/dsl/monkey_patch/ruby/ruby.rb +0 -6
  247. data/lib/openhab/dsl/monkey_patch/ruby/string.rb +0 -47
  248. data/lib/openhab/dsl/monkey_patch/ruby/time.rb +0 -61
  249. data/lib/openhab/dsl/openhab.rb +0 -30
  250. data/lib/openhab/dsl/persistence.rb +0 -27
  251. data/lib/openhab/dsl/rules/item_event.rb +0 -19
  252. data/lib/openhab/dsl/rules/rule.rb +0 -160
  253. data/lib/openhab/dsl/rules/rule_config.rb +0 -147
  254. data/lib/openhab/dsl/rules/triggers/generic.rb +0 -31
  255. data/lib/openhab/dsl/rules/triggers/triggers.rb +0 -11
  256. data/lib/openhab/dsl/rules/triggers/watch/watch.rb +0 -81
  257. data/lib/openhab/dsl/states.rb +0 -89
  258. data/lib/openhab/dsl/things.rb +0 -147
  259. data/lib/openhab/dsl/time/month_day.rb +0 -180
  260. data/lib/openhab/dsl/time/time_of_day.rb +0 -235
  261. data/lib/openhab/dsl/timers/manager.rb +0 -119
  262. data/lib/openhab/dsl/timers/reentrant_timer.rb +0 -38
  263. data/lib/openhab/dsl/timers/timer.rb +0 -132
  264. data/lib/openhab/dsl/timers.rb +0 -77
  265. data/lib/openhab/dsl/types/increase_decrease_type.rb +0 -23
  266. data/lib/openhab/dsl/types/next_previous_type.rb +0 -23
  267. data/lib/openhab/dsl/types/on_off_type.rb +0 -28
  268. data/lib/openhab/dsl/types/open_closed_type.rb +0 -29
  269. data/lib/openhab/dsl/types/play_pause_type.rb +0 -27
  270. data/lib/openhab/dsl/types/point_type.rb +0 -180
  271. data/lib/openhab/dsl/types/quantity_type.rb +0 -265
  272. data/lib/openhab/dsl/types/refresh_type.rb +0 -18
  273. data/lib/openhab/dsl/types/rewind_fastforward_type.rb +0 -33
  274. data/lib/openhab/dsl/types/stop_move_type.rb +0 -23
  275. data/lib/openhab/dsl/types/types.rb +0 -83
  276. data/lib/openhab/dsl/types/un_def_type.rb +0 -22
  277. data/lib/openhab/dsl/types/up_down_type.rb +0 -32
  278. data/lib/openhab/dsl/units.rb +0 -45
  279. data/lib/openhab/log/configuration.rb +0 -21
  280. data/lib/openhab/log/logger.rb +0 -282
  281. data/lib/openhab/version.rb +0 -9
  282. data/lib/openhab.rb +0 -38
@@ -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