v5.0.0
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-levellogger
will be named after the file, and the
logger
within arule
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 onmain
(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 doDimmerItem.state == ON
.
Predicate methods retain the implicit conversion semantics, so you can doDimmerItem.on?
.
ensure.on
, etc. also still retain their internal implicit comparisons, so you can also still doDimmerItem.ensure.on
and it will not send {ON} if the item is anything but0
.
{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 exampleItem.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 useItem.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 hadonly_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. Useitems.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 useitems.all?(:state?)
, or in
the rare cased you usedstates?(*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 afilter
parameter; usegrep
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 ofchanged [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 forcore.SystemStartlevelTrigger
.say
,play_sound
, andplay_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
andArray
, 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
orNameError
, 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 toNULL
, not an empty string (which will be ignored by other item types).