openhab-scripting 4.24.1 → 4.26.2

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 9aea0636a21d8c96b90fe0b904331e9980937c4c027bf9d246aacba450b28148
4
- data.tar.gz: 9cdb5a5c7625fc6d5fb3cc140a079f85622864aab6d9cf32442d38a1a4267d6d
3
+ metadata.gz: ee1cd79c7c26fcd970943290c98f1e4bc1eadba51143efb483e6fa3b91afef80
4
+ data.tar.gz: 438b03e4cae4fc3ba9fbb98be9c4d51154afda055666dc7cafa5bef82be06232
5
5
  SHA512:
6
- metadata.gz: d65920cefe3305a97d89bb4cb1caef9d6631738976b25e6f30ab7b2b02ba0abba6b09e8a2ae9ca810bd47c891b211d1f53dad443c547d3c934ecbe1880adfbaa
7
- data.tar.gz: 01f1a22c24ebad1ae80b125971a03d71ff4cd5785bd8c35a4ffa2a0e6299b24a27d7e8ceefda16e3b032314e42d26ceee13ab9c67363120a9d6b84a3a0f44596
6
+ metadata.gz: 37e389acddba81561af2e95ac053a81f0f6878ed49c2602cd1b604df94f65801278f3236f49aa1b54c17a88f230dee02055bce5af405e1ac3c71d125559cf7a2
7
+ data.tar.gz: fae20fd2c3635d1e0a0439ea59dc47f7100d8fd16124c3e7d5ac38baa676db62ebdf7ed0a909004db9f4d55137b98d5b6f39ca450fc0ae77a927f582a8775703
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'openhab/log/logger'
4
+ require 'openhab/core/services'
4
5
 
5
6
  module OpenHAB
6
7
  #
@@ -19,12 +20,10 @@ module OpenHAB
19
20
  #
20
21
  def self.wait_till_openhab_ready
21
22
  logger.trace('Checking readyness of OpenHAB')
22
- # rubocop: disable Style/GlobalVars
23
- until $scriptExtension.get('automationManager')
23
+ until OpenHAB::Core.automation_manager
24
24
  logger.trace("Automation manager not loaded, checking again in #{CHECK_DELAY} seconds.")
25
25
  sleep CHECK_DELAY
26
26
  end
27
- # rubocop: enable Style/GlobalVars
28
27
  logger.trace 'Automation manager instantiated, OpenHAB ready for rule processing.'
29
28
  end
30
29
  end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ module OpenHAB
4
+ #
5
+ # Core support for OpenHAB JRuby Library
6
+ #
7
+ module Core
8
+ #
9
+ # Access OpenHAB services
10
+ #
11
+
12
+ # Get the OpenHAB automation manager
13
+ # @return [AutomationManager] OpenHAB Automation manager
14
+ # rubocop:disable Style/GlobalVars
15
+ def self.automation_manager
16
+ $scriptExtension.get('automationManager')
17
+ end
18
+
19
+ # Get the OpenHAB rule registry
20
+ # @return [Registory] OpenHAB rule registry
21
+ def self.rule_registry
22
+ $scriptExtension.get('ruleRegistry')
23
+ end
24
+ # rubocop:enable Style/GlobalVars
25
+ end
26
+ end
@@ -11,17 +11,18 @@ require 'openhab/dsl/monkey_patch/events/events'
11
11
  require 'openhab/dsl/monkey_patch/actions/actions'
12
12
  require 'openhab/dsl/rules/rule'
13
13
  require 'openhab/dsl/rules/terse'
14
- require 'openhab/dsl/actions'
15
- require 'openhab/dsl/channel'
16
- require 'openhab/dsl/timers'
17
- require 'openhab/dsl/group'
18
- require 'openhab/dsl/things'
19
- require 'openhab/dsl/between'
20
- require 'openhab/dsl/gems'
21
- require 'openhab/dsl/persistence'
22
- require 'openhab/dsl/uid'
23
- require 'openhab/dsl/units'
24
- require 'openhab/dsl/states'
14
+ require_relative 'actions'
15
+ require_relative 'channel'
16
+ require_relative 'timers'
17
+ require_relative 'group'
18
+ require_relative 'things'
19
+ require_relative 'between'
20
+ require_relative 'gems'
21
+ require_relative 'persistence'
22
+ require_relative 'uid'
23
+ require_relative 'units'
24
+ require_relative 'states'
25
+ require_relative 'openhab'
25
26
 
26
27
  module OpenHAB
27
28
  #
@@ -34,6 +35,7 @@ module OpenHAB
34
35
  def self.extended(base)
35
36
  base.send :include, OpenHAB::DSL::Actions
36
37
  base.send :include, OpenHAB::DSL::Between
38
+ base.send :include, OpenHAB::DSL::Core
37
39
  base.send :include, OpenHAB::DSL::Groups
38
40
  base.send :include, OpenHAB::DSL::Items
39
41
  base.send :include, OpenHAB::DSL::Persistence
@@ -3,6 +3,7 @@
3
3
  require 'singleton'
4
4
 
5
5
  require 'openhab/dsl/lazy_array'
6
+ require 'openhab/core/entity_lookup'
6
7
 
7
8
  module OpenHAB
8
9
  module DSL
@@ -24,7 +25,7 @@ module OpenHAB
24
25
  # @return [Set] of OpenHAB Groups
25
26
  #
26
27
  def [](name)
27
- group = Core::EntityLookup.lookup_item(name)
28
+ group = OpenHAB::Core::EntityLookup.lookup_item(name)
28
29
  group.is_a?(Items::GroupItem) ? group : nil
29
30
  end
30
31
  alias include? []
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'pathname'
4
+
5
+ module OpenHAB
6
+ module DSL
7
+ #
8
+ # Provides access to OpenHAB attributes
9
+ #
10
+ module Core
11
+ include OpenHAB::Log
12
+
13
+ #
14
+ # Return the OpenHAB conf directory as a ruby pathname
15
+ #
16
+ # @return [Pathname] OpenHAB conf path
17
+ #
18
+ def __conf__
19
+ Pathname.new(ENV['OPENHAB_CONF'])
20
+ end
21
+ end
22
+ end
23
+ end
@@ -36,7 +36,8 @@ module OpenHAB
36
36
  # @param [Object] only_if Item or Proc to use as guard
37
37
  # @param [Object] not_if Item or Proc to use as guard
38
38
  #
39
- def initialize(only_if: nil, not_if: nil)
39
+ def initialize(run_context:, only_if: nil, not_if: nil)
40
+ @run_context = run_context
40
41
  @only_if = only_if
41
42
  @not_if = not_if
42
43
  end
@@ -106,27 +107,27 @@ module OpenHAB
106
107
  #
107
108
  # Check not_if guard
108
109
  #
109
- # @param [OpenHAB Event] event event to check if meets guard
110
+ # @param [OpenHAB Event] event to check if meets guard
110
111
  # @param [Array<Item>] items to check if satisfy criteria
111
112
  # @param [Array] procs to check if satisfy criteria
112
113
  #
113
114
  # @return [Boolean] True if criteria are satisfied, false otherwise
114
115
  #
115
116
  def process_not_if(event, items, procs)
116
- items.flatten.none?(&:truthy?) && procs.none? { |proc| proc.call(event) }
117
+ items.flatten.none?(&:truthy?) && procs.none? { |proc| @run_context.instance_exec(event, &proc) }
117
118
  end
118
119
 
119
120
  #
120
121
  # Check only_if guard
121
122
  #
122
- # @param [OpenHAB Event] event event to check if meets guard
123
+ # @param [OpenHAB Event] event to check if meets guard
123
124
  # @param [Array<Item>] items to check if satisfy criteria
124
125
  # @param [Array] procs to check if satisfy criteria
125
126
  #
126
127
  # @return [Boolean] True if criteria are satisfied, false otherwise
127
128
  #
128
129
  def process_only_if(event, items, procs)
129
- items.flatten.all?(&:truthy?) && procs.all? { |proc| proc.call(event) }
130
+ items.flatten.all?(&:truthy?) && procs.all? { |proc| @run_context.instance_exec(event, &proc) }
130
131
  end
131
132
  end
132
133
  end
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'openhab/core/thread_local'
4
+ require 'openhab/core/services'
4
5
  require 'openhab/log/logger'
5
6
  require_relative 'rule_config'
6
7
  require_relative 'automation_rule'
@@ -21,14 +22,11 @@ module OpenHAB
21
22
 
22
23
  @script_rules = []
23
24
 
24
- # rubocop: disable Style/GlobalVars
25
- @automation_manager = $scriptExtension.get('automationManager')
26
- @registry = $scriptExtension.get('ruleRegistry')
27
- # rubocop: enable Style/GlobalVars
28
-
25
+ @automation_manager = OpenHAB::Core.automation_manager
26
+ @registry = OpenHAB::Core.rule_registry
29
27
  class << self
30
28
  attr_reader :script_rules, :automation_manager, :registry
31
- end
29
+ end
32
30
 
33
31
  #
34
32
  # Create a new rule
@@ -37,13 +35,13 @@ module OpenHAB
37
35
  # @yield [] Block executed in context of a RuleConfig
38
36
  #
39
37
  #
40
- # rubocop: disable Metrics/MethodLength
38
+ # rubocop: disable Metrics
41
39
  def rule(rule_name, &block)
42
40
  thread_local(RULE_NAME: rule_name) do
43
41
  @rule_name = rule_name
44
42
  config = RuleConfig.new(rule_name, block.binding)
45
43
  config.instance_exec(config, &block)
46
- config.guard = Guard::Guard.new(only_if: config.only_if, not_if: config.not_if)
44
+ config.guard = Guard::Guard.new(run_context: config.caller, only_if: config.only_if, not_if: config.not_if)
47
45
  logger.trace { config.inspect }
48
46
  process_rule_config(config)
49
47
  nil # Must return something other than the rule object. See https://github.com/boc-tothefuture/openhab-jruby/issues/438
@@ -52,7 +50,7 @@ module OpenHAB
52
50
  puts "#{e.class}: #{e.message}"
53
51
  re_raise_with_backtrace(e)
54
52
  end
55
- # rubocop: enable Metrics/MethodLength
53
+ # rubocop: enable Metrics
56
54
 
57
55
  #
58
56
  # Cleanup rules in this script file
@@ -2,14 +2,15 @@
2
2
 
3
3
  require 'java'
4
4
  require 'pp'
5
- require 'openhab/dsl/rules/property'
6
- require 'openhab/dsl/rules/triggers/cron'
7
- require 'openhab/dsl/rules/triggers/changed'
8
- require 'openhab/dsl/rules/triggers/channel'
9
- require 'openhab/dsl/rules/triggers/command'
10
- require 'openhab/dsl/rules/triggers/updated'
11
- require 'openhab/dsl/rules/triggers/generic'
12
- require 'openhab/dsl/rules/guard'
5
+ require_relative 'property'
6
+ require_relative 'triggers/cron'
7
+ require_relative 'triggers/changed'
8
+ require_relative 'triggers/channel'
9
+ require_relative 'triggers/command'
10
+ require_relative 'triggers/updated'
11
+ require_relative 'triggers/generic'
12
+ require_relative 'triggers/watch'
13
+ require_relative 'guard'
13
14
  require 'openhab/core/entity_lookup'
14
15
  require 'openhab/dsl/between'
15
16
  require 'openhab/dsl/dsl'
@@ -0,0 +1,213 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'java'
4
+ require 'openhab/log/logger'
5
+ require 'openhab/core/services'
6
+
7
+ module OpenHAB
8
+ module DSL
9
+ module Rules
10
+ #
11
+ # Module holds rule triggers
12
+ #
13
+ module Triggers
14
+ #
15
+ # Module for watching directories/files
16
+ #
17
+ module Watch
18
+ include OpenHAB::Log
19
+
20
+ # Characters in an fnmatch compatible glob
21
+ GLOB_CHARS = ['**', '*', '?', '[', ']', '{', '}'].freeze
22
+
23
+ #
24
+ # Creates trigger types and trigger type factories for OpenHAB
25
+ #
26
+ def self.add_watch_handler
27
+ java_import org.openhab.core.automation.type.TriggerType
28
+ OpenHAB::Core.automation_manager.add_trigger_handler(
29
+ OpenHAB::DSL::Rules::Triggers::Watch::WATCH_TRIGGER_MODULE_ID,
30
+ OpenHAB::DSL::Rules::Triggers::Watch::WatchTriggerHandlerFactory.new
31
+ )
32
+
33
+ OpenHAB::Core.automation_manager.add_trigger_type(watch_trigger_type)
34
+ OpenHAB::Log.logger(self).trace('Added watch trigger handler')
35
+ end
36
+
37
+ #
38
+ # Creates trigger types and trigger type factories for OpenHAB
39
+ #
40
+ private_class_method def self.watch_trigger_type
41
+ TriggerType.new(
42
+ OpenHAB::DSL::Rules::Triggers::Watch::WATCH_TRIGGER_MODULE_ID,
43
+ nil,
44
+ 'A path change event is detected',
45
+ 'Triggers when a path change event is detected',
46
+ nil,
47
+ org.openhab.core.automation.Visibility::VISIBLE,
48
+ nil
49
+ )
50
+ end
51
+
52
+ # Struct for Watch Events
53
+ WatchEvent = Struct.new(:type, :path, :attachment)
54
+
55
+ # Trigger ID for Watch Triggers
56
+ WATCH_TRIGGER_MODULE_ID = 'jsr223.jruby.WatchTrigger'
57
+
58
+ # Extends the OpenHAB watch service to watch directories
59
+ #
60
+ # Must match java method name style
61
+ # rubocop:disable Naming/MethodName
62
+ class Watcher < org.openhab.core.service.AbstractWatchService
63
+ java_import java.nio.file.StandardWatchEventKinds
64
+
65
+ # Hash of event symbols as strings to map to NIO events
66
+ STRING_TO_EVENT = {
67
+ created: StandardWatchEventKinds::ENTRY_CREATE,
68
+ deleted: StandardWatchEventKinds::ENTRY_DELETE,
69
+ modified: StandardWatchEventKinds::ENTRY_MODIFY
70
+ }.transform_keys(&:to_s).freeze
71
+
72
+ # Hash of NIO event kinds to ruby symbols
73
+ EVENT_TO_SYMBOL = STRING_TO_EVENT.invert.transform_values(&:to_sym).freeze
74
+
75
+ # Creates a new Watch Service
76
+ def initialize(path, types, &block)
77
+ super(path)
78
+ @types = types.map { |type| STRING_TO_EVENT[type] }
79
+ @block = block
80
+ end
81
+
82
+ # Invoked by java super class to get type of events to watch for
83
+ # @param [String] _path ignored
84
+ #
85
+ # @return [Array] array of NIO event kinds
86
+ def getWatchEventKinds(_path)
87
+ @types
88
+ end
89
+
90
+ # Invoked by java super class to check if sub directories should be watched
91
+ # @return [false] false
92
+ def watchSubDirectories
93
+ false
94
+ end
95
+
96
+ # Invoked by java super class when an watch event occurs
97
+ # @param [String] _event ignored
98
+ # @param [StandardWatchEventKind] kind NIO watch event kind
99
+ # @param [java.nio.Path] Path that had an event
100
+ def processWatchEvent(_event, kind, path)
101
+ @block.call(WatchEvent.new(EVENT_TO_SYMBOL[kind], Pathname.new(path.to_s)))
102
+ end
103
+ end
104
+ # rubocop:enable Naming/MethodName
105
+
106
+ # Implements the OpenHAB TriggerHandler interface to process Watch Triggers
107
+ class WatchTriggerHandler
108
+ include OpenHAB::Log
109
+ include org.openhab.core.automation.handler.TriggerHandler
110
+
111
+ # Creates a new WatchTriggerHandler
112
+ # @param [Trigger] OpenHAB trigger associated with handler
113
+ #
114
+ def initialize(trigger)
115
+ @trigger = trigger
116
+ config = trigger.configuration.properties.to_hash.transform_keys(&:to_sym)
117
+ @path = config[:path]
118
+ @watcher = Watcher.new(@path, config[:types], &watch_event_handler(config[:glob]))
119
+ @watcher.activate
120
+ logger.trace("Created watcher for #{@path}")
121
+ end
122
+
123
+ # Create a lambda to use to invoke rule engine when file watch notification happens
124
+ # @param [String] glob to match for notification events
125
+ #
126
+ # @return [Lambda] lambda to execute on notification events
127
+ #
128
+ def watch_event_handler(glob)
129
+ lambda { |watch_event|
130
+ logger.trace("Received event(#{watch_event})")
131
+ if watch_event.path.fnmatch?(glob)
132
+ @rule_engine_callback&.triggered(@trigger, { 'event' => watch_event })
133
+ else
134
+ logger.trace("Event #{watch_event} did not match glob(#{glob})")
135
+ end
136
+ }
137
+ end
138
+
139
+ # Called by OpenHAB to set the rule engine to invoke when triggered
140
+ # Must match java method name style
141
+ # rubocop:disable Naming/MethodName
142
+ def setCallback(callback)
143
+ @rule_engine_callback = callback
144
+ end
145
+ # rubocop:enable Naming/MethodName
146
+
147
+ #
148
+ # Dispose of handler which deactivates watcher
149
+ #
150
+ def dispose
151
+ logger.trace("Deactivating watcher for #{@path}")
152
+ @watcher&.deactivate
153
+ end
154
+ end
155
+
156
+ # Implements the ScriptedTriggerHandlerFactory interface to create a new Trigger Handler
157
+ class WatchTriggerHandlerFactory
158
+ include org.openhab.core.automation.module.script.rulesupport.shared.factories.ScriptedTriggerHandlerFactory
159
+
160
+ # Invoked by the OpenHAB core to get a trigger handler for the supllied trigger
161
+ # @param [Trigger] OpenHAB trigger
162
+ #
163
+ # @return [WatchTriggerHandler] trigger handler for supplied trigger
164
+ def get(trigger)
165
+ WatchTriggerHandler.new(trigger)
166
+ end
167
+ end
168
+ end
169
+
170
+ #
171
+ # Create a trigger to watch a path
172
+ #
173
+ # @param [String] path to watch
174
+ #
175
+ # @return [Trigger] Trigger object
176
+ #
177
+ def watch(path, glob: '*', for: %i[created deleted modified], attach: nil)
178
+ glob, path = glob_for_path(Pathname.new(path), glob)
179
+ types = [binding.local_variable_get(:for)].flatten
180
+ conf = { path: path.to_s, types: types.map(&:to_s), glob: glob.to_s }
181
+
182
+ logger.trace("Creating a watch trigger for path(#{path}) with glob(#{glob}) for types(#{types})")
183
+ append_trigger(OpenHAB::DSL::Rules::Triggers::Watch::WATCH_TRIGGER_MODULE_ID,
184
+ conf,
185
+ attach: attach)
186
+ end
187
+
188
+ private
189
+
190
+ #
191
+ # Automatically creates globs for supplied paths if necessary
192
+ # @param [Pathname] path to check
193
+ # @param [String] specified glob
194
+ #
195
+ # @return [Pathname,String] Pathname to watch and glob to match
196
+ def glob_for_path(path, glob)
197
+ # Checks if the supplied pathname last element contains a glob char
198
+ if OpenHAB::DSL::Rules::Triggers::Watch::GLOB_CHARS.any? { |char| path.basename.to_s.include? char }
199
+ # Splits the supplied pathname into a glob string and parent path
200
+ [path.basename.to_s, path.parent]
201
+ elsif path.file? || !path.exist?
202
+ # glob string matching end of Pathname and parent path
203
+ ["*/#{path.basename}", path.parent]
204
+ else
205
+ [glob, path]
206
+ end
207
+ end
208
+ end
209
+ end
210
+ end
211
+ end
212
+ # Add the watch handler to OpenHAB
213
+ OpenHAB::DSL::Rules::Triggers::Watch.add_watch_handler
@@ -108,6 +108,21 @@ module OpenHAB
108
108
  class MonthDay
109
109
  include OpenHAB::Log
110
110
  java_import java.time.format.DateTimeFormatter
111
+
112
+ #
113
+ # Constructor
114
+ #
115
+ # @param [Integer] m month
116
+ # @param [Integer] d day of month
117
+ #
118
+ # @return [Object] MonthDay object
119
+ #
120
+ # rubocop: disable Naming/MethodParameterName
121
+ def self.new(m:, d:)
122
+ MonthDay.of(m, d)
123
+ end
124
+ # rubocop: enable Naming/MethodParameterName
125
+
111
126
  # Parse MonthDay string as defined with by Monthday class without leading double dash "--"
112
127
  def self.parse(string)
113
128
  logger.trace("#{self.class}.parse #{string} (#{string.class})")
@@ -2,11 +2,13 @@
2
2
 
3
3
  require 'forwardable'
4
4
  require 'time'
5
+ require 'java'
5
6
 
6
7
  module OpenHAB
7
8
  module DSL
8
9
  module Types
9
10
  java_import org.openhab.core.library.types.DateTimeType
11
+ java_import java.time.ZonedDateTime
10
12
 
11
13
  # global alias
12
14
  ::DateTimeType = DateTimeType
@@ -20,8 +20,8 @@ module OpenHAB
20
20
  # Invert the type
21
21
  # @return [OpenClosedType] +OPEN+ if +CLOSED+, +CLOSED+ if +OPEN+
22
22
  def !
23
- return OPEN if open?
24
- return CLOSED if closed?
23
+ return CLOSED if open?
24
+ return OPEN if closed?
25
25
  end
26
26
  end
27
27
  end
@@ -5,5 +5,5 @@
5
5
  #
6
6
  module OpenHAB
7
7
  # @return [String] Version of OpenHAB helper libraries
8
- VERSION = '4.24.1'
8
+ VERSION = '4.26.2'
9
9
  end
data/lib/openhab.rb CHANGED
@@ -1,5 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'java'
3
4
  require 'openhab/core/load_path'
4
5
  require 'openhab/core/entity_lookup'
5
6
  require 'openhab/core/script_handling'
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: openhab-scripting
3
3
  version: !ruby/object:Gem::Version
4
- version: 4.24.1
4
+ version: 4.26.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Brian O'Connell
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2021-12-14 00:00:00.000000000 Z
11
+ date: 2021-12-31 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -51,6 +51,7 @@ files:
51
51
  - lib/openhab/core/openhab_setup.rb
52
52
  - lib/openhab/core/osgi.rb
53
53
  - lib/openhab/core/script_handling.rb
54
+ - lib/openhab/core/services.rb
54
55
  - lib/openhab/core/thread_local.rb
55
56
  - lib/openhab/dsl/actions.rb
56
57
  - lib/openhab/dsl/between.rb
@@ -92,6 +93,7 @@ files:
92
93
  - lib/openhab/dsl/monkey_patch/ruby/number.rb
93
94
  - lib/openhab/dsl/monkey_patch/ruby/ruby.rb
94
95
  - lib/openhab/dsl/monkey_patch/ruby/string.rb
96
+ - lib/openhab/dsl/openhab.rb
95
97
  - lib/openhab/dsl/persistence.rb
96
98
  - lib/openhab/dsl/rules/automation_rule.rb
97
99
  - lib/openhab/dsl/rules/cron_trigger_rule.rb
@@ -108,6 +110,7 @@ files:
108
110
  - lib/openhab/dsl/rules/triggers/generic.rb
109
111
  - lib/openhab/dsl/rules/triggers/trigger.rb
110
112
  - lib/openhab/dsl/rules/triggers/updated.rb
113
+ - lib/openhab/dsl/rules/triggers/watch.rb
111
114
  - lib/openhab/dsl/states.rb
112
115
  - lib/openhab/dsl/things.rb
113
116
  - lib/openhab/dsl/time/month_day.rb