Skip to content

v5.0.0

Compare
Choose a tag to compare
@jimtng jimtng released this 06 Apr 23:36
· 371 commits to main since this release

5.0 is the first release as the officially supported gem of the openHAB organization.
Many thanks to @boc-tothefuture, @ccutrer, @jimtng, and @pacive for their work on the previous versions.
This major release contains significant restructuring by @ccutrer, with additional contributions by @jimtng.
Here is a non-exhaustive list of significant departures from the original gem:

  • Logging has been reworked. There's generally no need to
    include OpenHAB::Log in your classes. {OpenHAB::Log.logger} method now
    accepts a String to explicitly find whichever logger you would like, and
    {OpenHAB::Logger#level=} can be used to dynamically change the log level.
    Issues around the logger name while a rule is executing have also been
    resolved: the top-level logger will be named after the file, and the
    logger within a rule or execution block will be named after the rule.
    Loggers within class or instance-of-a-class context will be named after
    the class. These loggers will not have their name changed simply because
    their methods happened to be called while a rule is executing. Logging
    also defaults to #to_s now, instead of #inspect.
  • The documentation philosophy has changed. Instead of relying on a large
    set of markdown files to give both commentary and to document the details
    of objects, YARD is now the primary generator
    of the documentation site. Details of individual objects and methods are
    now documented inline with the code, reducing duplication, aiding in
    keeping them up-to-date and accurate, and being more rigorous in ensuring
    the documentation has every available method listed, and in a consistent
    manner. Some commentary and high level examples (such as this file) are
    still maintained as dedicated markdown files, but included in the YARD
    docs, instead of being a separate site that then links to the YARD docs.
  • The testing philosophy has also changed. The
    rspec-openhab-scripting gem,
    previously written as an independent project by
    @ccutrer, has now been merged into this gem.
    There is a tight interdependence between the two, and especially during the
    large refactoring it's much easier to have them in the same repository. This
    means that that gem is now the endorsed method to write tests for end-user
    rules, as well as the preferred way to write tests for this gem itself, when
    possible.
  • Major re-organization of class structures. {OpenHAB::Core} now contains any
    classes that are mostly wrappers or extensions of org.openhab.core Java
    classes, while {OpenHAB::DSL} contains novel Ruby-only classes that implement
    a Ruby-first manner of creating rules, items, and things.
  • As part of the re-organization from above, the definition of a "DSL method"
    that is publicly exposed and available for use has been greatly refined.
    Top-level DSL methods are only available on main (the top-level {Object}
    instance when you're writing code in a rules file and not in any other
    classes), and inside of other select additional DSL constructs. If you've
    written your own classes that need access to DSL methods, you either need
    to explicitly call them on {OpenHAB::DSL}, or mix that module into your
    class yourself. Additional internal and Java constants and methods should
    no longer be leaking out of the gem's public API.

Breaking Changes

  • Dropping support for openHAB < 3.4.
  • The main require is now require "openhab/dsl" instead of just
    require "openhab". The reason being to avoid conflicts if a gem gets
    written to access openHAB via REST API. It's probably preferred that you
    configure automatic requires for this file anyway.
  • {GenericItem} and descendants can no longer be treated as the item's state.
    While convenient at times, it introduces many ambiguities on if the intention
    is to interact with the item or its state, and contortionist code attempting
    to support both use cases.
  • {OpenHAB::Core::Types::Type Enum types} no longer have implicit conversions for comparisons.
    This means you can no longer do DimmerItem.state == ON.
    Predicate methods retain the implicit conversion semantics, so you can do DimmerItem.on?.
    ensure.on, etc. also still retain their internal implicit comparisons, so you can also still do DimmerItem.ensure.on and it will not send {ON} if the item is anything but 0.
    {OpenHAB::Core::Events::ItemStateEvent} and {OpenHAB::Core::Events::ItemStateChangedEvent} both now have a full set of predicate methods to ease use from within rule execution blocks.
  • Semi-related to the above two points, the #truthy? method has been removed from any items the previously implemented it.
    Instead, be more explicit on what you mean - for example Item.on?.
    If you would like to use a similar structure with {StringItem StringItems}, just include the ActiveSupport gem in your rules to get #blank? and #present? methods, and then you can use Item.state.present?.
  • Semi-related to the above, the
    {OpenHAB::DSL::Rules::BuilderDSL#only_if only_if} and
    {OpenHAB::DSL::Rules::BuilderDSL#not_if not_if} guards now only take blocks.
    This just means where you previously had only_if Item you now write
    only_if { Item.on? }.
  • Related to the above, {OpenHAB::DSL::Rules::BuilderDSL#changed changed for:}
    guards no longer take items. This just means if you previously had
    changed Item, for: OtherItem you now write
    changed Item, for: -> { OtherItem.state }.
  • {QuantityType} is no longer implicitly
    convertible and comparable against Strings. Use the | operator for easy
    construction of {QuantityType}s: 10 | "°F".
  • {HSBType} is no longer convertible and comparable against Strings, Hashes,
    and Arrays. Just construct an HSBType. Sending a HTML hex color as a string
    as a command is still supported.
  • {PointType} is no longer convertible and comparable against Strings and
    Hashes. Just construct a PointType. Send strings as a command is still supported.
  • {QuantityType} can no longer be compared against Numeric or {DecimalType} outside
    a {OpenHAB::DSL.unit unit} block. Either compare it against another QuantityType, or
    convert it with to_f first, perform the comparison inside a
    {OpenHAB::DSL.unit unit} block, or {OpenHAB::DSL.unit! permanently set} your
    preferred units.
  • The top-level groups method providing access to only {GroupItem}s has been
    removed. Use items.grep(GroupItem) if you would like to filter to only
    groups.
  • GenericItem#id no longer exists; just use
    {Item#to_s Item#to_s} which does what #id used to do.
  • states?(*items) helper is gone. Just use items.all?(:state?), or in
    the rare cased you used states?(*items, things: true), use
    items.all? { |i| i.state? && i.things.all?(&:online?) }.
  • {GroupItem} is no longer {Enumerable}, and you must use
    {GroupItem#members GroupItem#members}.
  • {GroupItem#all_members GroupItem#all_members} no
    longer has a filter parameter; use grep if you want just {GroupItem}s.
  • create_timer no longer exists as an alias for {after}.
  • Item#meta is no longer a supported alias for
    {Item#metadata Item#metadata}.
  • Triggers (such as {OpenHAB::DSL::Rules::BuilderDSL#changed changed},
    {OpenHAB::DSL::Rules::BuilderDSL#updated updated}, and
    {OpenHAB::DSL::Rules::BuilderDSL#received_command received_command} that
    previously took a splat or an Array of Items now only take a splat.
    This just means instead of changed [Item1, Item2] you write
    changed Item1, Item2, or if you have an actual array you write
    changed(*item_array). This greatly simplifies the internal code that has to
    distinguish between {GroupItem::Members GroupItem::Members} and other
    types of collections of items.
  • Date and time objects have been reworked:
    • TimeOfDay has been replaced with {LocalTime}
    • Date/time objects are no longer comparable to strings.
      Please use the correct type.
    • Comparisons among the varying date/time classes all work.
    • See also Working With Time
    • Persistence methods no longer accept a {Duration}. Please use Duration#ago
      instead.
  • Thing actions are no longer available as a top level method. You must use the
    {OpenHAB::Core::Things::Thing#actions Thing} object.
  • Thing actions whose scope does not match the thing's binding are no longer
    directly available on {OpenHAB::Core::Things::Thing Thing}; you must
    explicitly access them via
    {OpenHAB::Core::Things::Thing#actions Thing#actions}.
  • {OpenHAB::Core::Items::Persistence Persistence} predicates are no longer
    aliased without the ? (i.e. you must call #changed_since?, not
    #changed_since).
  • Timers with IDs are no longer uniqueified by where they were created. Make
    sure you're using a completely unique timer ID if you're using them in
    multiple locations. For example,
    after(1.minute, id: [:this_logical_usage, event.item]). This makes it
    possible to schedule the same logically re-entrant timer from multiple rules.
  • OpenHAB.conf_root was renamed to {OpenHAB::Core.config_folder}.
  • {OpenHAB::Core::Items::GenericItem#metadata Metadata} now defaults to using transient backing provider.
    This means that if you add metadata to an item from Ruby, it will disappear when the script is unloaded.
    See {OpenHAB::DSL.provider} for how to revert to the old behavior within a single block, or for your entire script.
  • {OpenHAB::Core::Items::GenericItem#metadata Metadata} will now be serialized before being set.
    This fixes a complicated issue where types would changed unexpectedly, or even worse, reference Ruby classes that are not even available in the current JRuby instance.
    See openhab/openhab-core#3169 for more details.
  • #on_start trigger was renamed to {OpenHAB::DSL::Rules::BuilderDSL#on_load #on_load} and its 'run_on_start' parameter removed.
    {OpenHAB::DSL::Rules::BuilderDSL#on_start #on_start} is now a trigger for core.SystemStartlevelTrigger.
  • say, play_sound, and play_stream are no longer available at the top level.
    You must access them from their Action class: {OpenHAB::Core::Actions::Voice.say Voice.say},
    {OpenHAB::Core::Actions::Audio.play_sound Audio.play_sound}, and {OpenHAB::Core::Actions::Audio.play_stream Audio.play_stream}

Features

  • {OpenHAB::DSL::Items::Builder}
  • {OpenHAB::DSL::Things::Builder}
  • {group::OpenHAB::DSL::Rules::BuilderDSL::Triggers Several new triggers}
  • {OpenHAB::DSL.profile}
  • {OpenHAB::DSL.script}
  • {OpenHAB::DSL.shared_cache}
  • {OpenHAB::Core::Rules::Registry}, specifically {OpenHAB::Core::Rules::Registry#remove #remove} and {OpenHAB::Core::Rules::Rule#trigger #trigger} are new functionality.
  • {OpenHAB::DSL.unit} can now handle units for multiple dimensions.
  • Support Ruby's method name convention for thing actions, e.g. things["mqtt:broker:mosquitto"].publish_mqtt
  • {OpenHAB::DSL.timers timers} now returns {OpenHAB::DSL::TimerManager an object}
    that can be used to thread-safely schedule/reschedule/cancel timers by ID.
  • #inspect on several classes has been improved to be useful, instead of just returning the class name.
  • {OpenHAB::DSL.after after} (and anything else that ultimately relies on timers) support Proc for durations.
  • Add #ago and #from_now methods to {Duration}.
  • The ability to designate how metadata should be persisted or not, via {OpenHAB::DSL.provider}.
  • {java.util.Map} and {java.util.List} now more fully implement the expected interfaces of Hash and Array, so you don't need to explicitly convert (as much) anymore.
  • If you reference an item that does not currently exist in a rule trigger, instead of raising MethodMissing or NameError, the trigger will be created anyway.
    openHAB will log a warning that the item is missing, and the trigger will not work. When the item is eventually created, the trigger will begin to work.
    This matches the behavior of DSL rules.
    Note that this only works for {OpenHAB::DSL::Rules::Terse terse rules} if they're created within a {OpenHAB::DSL::Rules::Builder rules.build} block.
  • {OpenHAB::DSL::Rules::BuilderDSL#on_start #on_start} supports creating a core.SystemStartlevelTrigger.
    Also see {OpenHAB::DSL::Rules::BuilderDSL#on_load #on_load}.
  • {OpenHAB::DSL::Rules::BuilderDSL#on_load #on_load} supports delay
  • Various Ephemeris methods on {ZonedDateTime}.
  • {OpenHAB::DSL::Rules::BuilderDSL#dependencies} Rule dependencies
  • A set of debounce/throttle guards for file-based rules: {OpenHAB::DSL::Rules::BuilderDSL#debounce_for debounce_for}, {OpenHAB::DSL::Rules::BuilderDSL#throttle_for throttle_for}, and {OpenHAB::DSL::Rules::BuilderDSL#only_every only_every}
  • And for UI rules: {OpenHAB::DSL.debounce_for debounce_for}, {OpenHAB::DSL.throttle_for throttle_for}, {OpenHAB::DSL.only_every only_every}
  • Explicitly document modifying item tags, labels and categories (where possible), and notify openHAB of the change
  • {OpenHAB::Core::Events::ItemStateEvent} and {OpenHAB::Core::Events::ItemStateChangedEvent} now have full sets of predicate methods.
  • {OpenHAB::DSL::Rules::Terse terse rules} now have an on_load parameter.
  • {Item#all_groups Item#all_groups}, {Enumerable#all_members}, {Enumerable#groups}, {Enumerable#all_groups}.
  • {GenericItem#formatted_state GenericItem#formatted_state}.
  • {OpenHAB::DSL.transform} now available at top-level, like Rules DSL.
  • {Item#member_of? Item#member_of?}, {Item#tagged? Item#tagged?}.
  • {OpenHAB::DSL::Rules::BuilderDSL.watch watch} can now be used to monitor subdirectories

Bug Fixes

  • Fix thing {OpenHAB::Core::EntityLookup#method_missing entity lookup}
  • Fix {OpenHAB::DSL::Items::Ensure ensure} to work with {QuantityType}
  • Fix scoping of {OpenHAB::DSL::Rules::Terse terse rule} blocks
  • {OpenHAB::DSL.persistence persistence block} now restores the previous setting
  • {OpenHAB::DSL.unit unit} block applies to sending commands to {NumberItem NumberItems}.
  • All thread locals are carried over to rule executions and timers. This includes {OpenHAB::DSL.unit unit}, {OpenHAB::DSL.persistence persistence}, and {OpenHAB::DSL.ensure_states ensure_states}.
  • Fix thread safety issue that could cause timers to not be canceled when the script unloads.
  • {OpenHAB::DSL::Items::TimedCommand#command Timed command} thread safety issues resolved
  • {OpenHAB::DSL::Items::TimedCommand#command Timed command} now resets the on_expire setting when called again
  • {OpenHAB::DSL::Items::TimedCommand#command Timed command} still sends the command even the previous timed command is still pending
  • {OpenHAB::DSL::Items::TimedCommand#command Timed command} works with resetting to {NULL}/{UNDEF}
  • {OpenHAB::DSL::Items::TimedCommand#command Timed command} works on items with autoupdate=false
  • {OpenHAB::Core::Items::Metadata} hashes are indifferent (converts symbol keys to string keys).
  • Fix {OpenHAB::DSL::Items::Ensure ensure} to work with conversions-from-string that are handled by openhab-core.
  • Avoid stack overflow issues when all of ActiveSupport is required.
  • Don't swallow exceptions inside of rule blocks - just let them propagate up.
  • Fix changed duration when only the from state is given
  • Updating an item with nil sets it to NULL, not an empty string (which will be ignored by other item types).