Class: OpenHAB::DSL::Rules::Triggers::WatchHandler::WatchTriggerHandler

Inherits:
Object
  • Object
show all
Defined in:
lib/openhab/dsl/rules/triggers/watch/watch_handler.rb

Overview

Implements the openHAB TriggerHandler interface to process Watch Triggers

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(trigger) ⇒ WatchTriggerHandler

Creates a new WatchTriggerHandler

Parameters:

  • trigger (org.openhab.core.automation.Trigger)


242
243
244
245
246
247
248
249
250
251
252
253
254
# File 'lib/openhab/dsl/rules/triggers/watch/watch_handler.rb', line 242

def initialize(trigger)
  @trigger = trigger
  config = trigger.configuration.properties.to_hash.transform_keys(&:to_sym)
  @path, subdirs, glob = self.class.dir_subdir_glob(config[:path], config[:glob])
  logger.trace { "WatchTriggerHandler#initialize path: #{@path}, subdirs: #{subdirs}, glob: #{glob}" }
  unless @path
    logger.warn("Watch error: the given path doesn't exist: '#{@path}'")
    return
  end
  @watcher = Watcher.new(@path, subdirs, config[:types], &watch_event_handler(glob))
  @watcher.activate
  logger.trace { "Created watcher for #{@path} subdirs: #{subdirs}" }
end

Class Method Details

.dir_subdir_glob(path, glob) ⇒ Array<String,Boolean,String>?

Returns the directory to watch, subdir flag, and glob pattern to use

Parameters:

  • path (String)

    The path provided to the watch trigger which may include glob patterns

  • glob (String)

    The glob pattern provided by the user

Returns:

  • (Array<String,Boolean,String>, nil)

    An array of directory to watch, whether to watch in subdirectories, and the glob pattern to use. Returns nil if the given path doesn’t exist all the way to root, e.g. /nonexistent



173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
# File 'lib/openhab/dsl/rules/triggers/watch/watch_handler.rb', line 173

def dir_subdir_glob(path, glob)
  pathname = Pathname.new(path)
  return [pathname.dirname.to_s, false, path] if pathname.file?

  dir = find_parent(pathname)
  return unless dir

  # we were given the exact existing directory to watch
  if dir == pathname
    glob_pathname = Pathname.new(glob)
    subdirs = recursive_glob?(glob)
    unless glob_pathname.absolute? || glob.start_with?("**")
      glob = subdirs ? "**/#{glob}" : "#{path}/#{glob}"
    end
    return [path, subdirs, glob]
  end

  if glob != "*" # if it isn't the default glob
    logger.warn("The provided glob '#{glob}' is ignored because " \
                "the given path (#{path}) isn't an existing directory, " \
                "so it is used as the glob pattern")
  end

  relative_glob = pathname.relative_path_from(dir).to_s
  subdir_flag = dir != pathname.dirname || recursive_glob?(relative_glob)
  [dir.to_s, subdir_flag, path]
end

.find_parent(pathname) ⇒ Pathname?

Find the part of the path that exists on disk.

/a/b/c//d/.e -> /a/b/c if it exists /a/b/c/d/e/f -> /a/b/c if /a/b/c directory exists, but /a/b/c/d doesn’t exist /a/b/c -> nil if /a doesn’t exist / -> /

Parameters:

  • pathname (Pathname)

    The pathname to check

Returns:

  • (Pathname, nil)

    The leading part of the path name that corresponds to an existing directory. nil if none was found up until the root directory



232
233
234
235
236
# File 'lib/openhab/dsl/rules/triggers/watch/watch_handler.rb', line 232

def find_parent(pathname)
  return pathname if pathname.root?

  pathname.ascend { |part| return part if part.directory? && !part.root? }
end

.glob?(string) ⇒ Boolean

Returns true if string contains glob characters

Returns:

  • (Boolean)


202
203
204
205
206
207
208
209
210
211
212
213
# File 'lib/openhab/dsl/rules/triggers/watch/watch_handler.rb', line 202

def glob?(string)
  unless @regexp
    # (?<!X) is a negative lookbehind pattern: only match the pattern if it wasn't
    # preceded with X. In this case we want to match only non escaped glob chars
    glob_pattern = %w[** * ? [ ] { }].map { |char| Regexp.escape(char) }
                                     .join("|")
                                     .then { |pattern| "(?<!\\\\)(#{pattern})" }

    @regexp = Regexp.new(glob_pattern)
  end
  @regexp.match?(string)
end

.recursive_glob?(string) ⇒ Boolean

Returns true if string contains a recursive glob pattern (** or x/y)

Returns:

  • (Boolean)


216
217
218
# File 'lib/openhab/dsl/rules/triggers/watch/watch_handler.rb', line 216

def recursive_glob?(string)
  /(?<!\\\\)\*\*/.match?(string) || Pathname.new(string).each_filename.to_a.size > 1
end

Instance Method Details

#disposeObject

Dispose of handler which deactivates watcher



284
285
286
287
# File 'lib/openhab/dsl/rules/triggers/watch/watch_handler.rb', line 284

def dispose
  logger.trace { "Deactivating watcher for #{@path}" }
  @watcher.deactivate
end

#setCallback(callback) ⇒ Object

Called by openHAB to set the rule engine to invoke when triggered



277
278
279
# File 'lib/openhab/dsl/rules/triggers/watch/watch_handler.rb', line 277

def setCallback(callback) # rubocop:disable Naming/MethodName
  @rule_engine_callback = callback
end

#watch_event_handler(glob) ⇒ Proc

Create a lambda to use to invoke rule engine when file watch notification happens

Parameters:

  • glob (String)

    to match for notification events

Returns:

  • (Proc)

    lambda to execute on notification events



261
262
263
264
265
266
267
268
269
270
271
272
273
274
# File 'lib/openhab/dsl/rules/triggers/watch/watch_handler.rb', line 261

def watch_event_handler(glob)
  default_fs = java.nio.file.FileSystems.default
  path_matcher = default_fs.get_path_matcher("glob:#{glob}")
  lambda do |watch_event|
    if path_matcher.matches(default_fs.get_path(watch_event.path.to_s))
      logger.trace do
        "Received event(#{watch_event}) glob: #{glob}, rule_engine_callback = #{@rule_engine_callback}"
      end
      @rule_engine_callback&.triggered(@trigger, { "event" => watch_event })
    else
      logger.trace { "Event #{watch_event} did not match glob(#{glob})" }
    end
  end
end