Thanks @pacive -
It is really based around the ‘zip’ install of OpenHAB which is what it gets tested against. For the docs, as you go, any PRs for improvement would be welcome.
Done!
A couple of releases:
2.12.0 (2021-02-02)
Bug Fixes
- return nil for items[‘nonexistent’] instead of raising an exception (4a412f8)
Features
- add Item.include? to check for item’s existence (1a8fd3a)
2.11.1 (2021-02-01)
Bug Fixes
- group: support for accessing triggering item in group updates (6204f0a)
2.11.0 (2021-02-01)
Features
- Add Duration.to_s (b5b9c81)
As usual, check changelog and latest documentation for details.
I liked it when the original post at the top included some sample scripts. It gave me a quick overview of the syntax and what the library is capable of, without having to delve into the documentation. It’s what piqued my interest in the first place.
There have been quite a few updates in the past two weeks with @jimtng and @pacive doing most of the new feature work while I have been focusing on removing technical debt.
The rules system is moving towards full feature compatibility with the addition of the DateTime item type from @pacive , which works very well in the ruby context. Usage details are here
@jimtng added persistence support, which works directly on the items, docs are here. It is easy to work with and you can do things like this:
Item1.average_since(12.hours, :influxdb)
Changelog is below containing other additional features, performance enhancements and bug fixes.
Thanks for the great contributions @jimtng and @pacive
2.18.0 (2021-02-14)
Features
- add DateTime Item type (a3cc139)
2.17.0 (2021-02-12)
Features
- units: import OpenHAB common units for UoM (351a776)
2.16.4 (2021-02-12)
Bug Fixes
- changed_duration: timer reschedule duration bug (6bc8862)
2.16.3 (2021-02-12)
2.16.2 (2021-02-11)
Bug Fixes
- decorate items[itemname], event.item, and triggered item (ce4ef03)
2.16.1 (2021-02-11)
Performance Improvements
- timeofdayrangeelement: subclass Numeric to make comparisons more efficient (c2482e8)
2.16.0 (2021-02-10)
Features
- support comparisons between various numeric item/state types (510d6db)
2.15.0 (2021-02-09)
Features
- add Persistence support (9cab1ff)
2.14.3 (2021-02-09)
Bug Fixes
- multiple delayed triggers overwrite the previous triggers (6f14429)
2.14.2 (2021-02-08)
2.14.1 (2021-02-05)
Bug Fixes
- number_item: make math operations and comparisons work with Floats (3b29aa9)
2.14.0 (2021-02-03)
Features
- logging: append rule name to logging class if logging within rule context (00c73a9)
2.13.1 (2021-02-02)
2.13.0 (2021-02-02)
Features
- dimmeritem: dimmeritems can now be compared (aa286dc)
I want to say thank you for all the work you guys have put into this. I just recently moved to OH3 and was really trying to do everything in the MainUI and ECMAScript but I was struggling with anything a bit more complicated than just a basic rule. I know rikoshk has put a lot of effort working on the documentation, but if I couldn’t find what I was looking for there I’d come back from stackoverflow with some code to try only to find out most of the time it wasn’t supported with the version of ECMAScript used.
Even in this early state this was easy to install and the rule structure just makes so much more sense to me. In fact I’d say it is easier than the Rule DSL.
Small things that probably should be noted in the installation documents. Currently it does not enable the info logging when installing the addon. And at least on linux the user needs to remember to change the permissions to the folders created to be owned by the openhab user. The permission one is pretty obvious as that will give you an error in the console, but it took me a while to figure out why I wasn’t seeing anything from the ‘Simple’ rule example.
Only coding that wasn’t immediately obvious was working with groups.
Sending a command to a group is:
GroupItem.group << YOUR_COMMAND
Whereas for an item it is just:
Item << YOUR_COMMAND
Also currently something like:
run { |event| logger.info(event.item.groups) } #Will list all groups not just the item's groups.
The rule I was struggling with in ECMAScript was trying to create a virtual color item that had the average color of a group of lights. Since groups can’t have a base type of HSBType. And even with HSBType and ColorItem not implemented in the library yet it was still way easier with Ruby’s map, sum, and string functions to accomplish it. While still having readable code.
Thank you for the feedback. Glad to hear that it is working out well for you so far!
Currently it does not enable the info logging when installing the addon.
Info logging should be the default, what did you need to do to enable it?
I will open documentation issues on the other install items.
As for groups, we realize there are some issues and are working through changes for them:
GroupItem.group << YOUR_COMMAND
That is a defect (well my poor design) and will be something we will fix.
run { |event| logger.info(event.item.groups) } #Will list all groups not just the item's groups.
That also seems like a defect and not sure what would cause that to happen.
The rule I was struggling with in ECMAScript was trying to create a virtual color item that had the average color of a group of lights. Since groups can’t have a base type of HSBType. And even with HSBType and ColorItem not implemented in the library yet it was still way easier with Ruby’s map, sum, and string functions to accomplish it. While still having readable code.
Can you share what you did? It may influence our design of the ruby implementation of those item types.
I think that info level logging is disabled for all scripting add-ons in OH3 for some reason. Jython doesn’t output info logging either.
Sure, here is my color item rules. Most of the actual code here is just trying to find the related group and virtual item that needs to be updated so I can use one rule to work with multiple rooms. Though that is where Ruby really shines. I’m still not quite happy with how I have it. Not a Ruby expert. Probably could just be a class instead of two methods and a struct. But it works!
require 'openhab'
rule 'Pass commands from virtual Lights Color' do
received_command virtual_Lights_Color.items
triggered { |item| logger.info("Group item #{item.name} received command")}
run { |event| groups[event.item.name.from(8)].group << event.command }
end
rule 'Update Virtual Lights Color item' do
changed All_Lights_Color.items
triggered { |item| logger.info("Group item #{item.name} updated")}
run do |event|
item = event.item
virtual_item = get_virtual_item(item)
group_item = get_group_item(item)
colors = group_item.map(&:color)
average = [:h,:s,:b].map{|k| colors.sum(&k)/colors.count}.join(',')
virtual_item.update(average)
logger.info("Updating virtual Item to #{average}")
end
end
class String
def from(position)
self[position, length]
end
end
NamingScheme = Struct.new(:room, :equipment, :number, :type) do
def virtual_name
["virtual", room, "#{equipment}s", type].join('_')
end
def group_name
[room, "#{equipment}s", type].join('_')
end
end
def get_virtual_item(item)
items[NamingScheme.new(*item.name.split('_')).virtual_name]
end
def get_group_item(item)
groups[NamingScheme.new(*item.name.split('_')).group_name]
end
class Java::OrgOpenhabCoreLibraryItems::ColorItem
def color
state.hsb
end
end
class Java::OrgOpenhabCoreLibraryTypes::HSBType
HSB = Struct.new(:h,:s,:b)
def hsb
HSB.new(constituents["h"].to_f, constituents["s"].to_i, constituents["b"].to_i)
end
end
Version 3.0.0 (Breaking Change) is released with many features, performance improvements and bug fixes. Thanks to @jimtng and @pacive they put into here.
Features are below, the big breaking change is the complete rewrite of groups by @pacive. Please see the updated docs.
@sovapatr This should address many of your issues that you noted
@pacive feel free to do another post going into detail on the changes and updates to groups.
Rollershutters and Players are also now supported.
BREAKING CHANGES
-
groups:
items
no longer acts as a indicator to rules to
trigger on member changes, it has been replaced withmembers
Features
-
groups: groups now act as items (210e507)
-
player: add support for player items (70418ab)
-
add stack trace to errors (572114e)
-
groups: support command and << (dd140aa)
-
groups: adds supports for item groups (127ab17)
-
event: add event.state for update trigger (d4eb4f7)
-
add conversion operator for DecimalType to Quantity (42bc5de)
-
persistence: automatically convert to quantity for dimensioned items (7e352d4)
-
add RollershutterItem (f5801d9)
Performance Improvements
-
logging: use block syntax to log method calls (9657e72)
-
datetime: delegate more methods directly to ZonedDateTime (ea31954)
Bug Fixes
-
persistence: selective conversion to Quantity (3187de7)
-
metadata: convert value to string before assignment (dba5db7)
-
changed: for parameter with thing chaged trigger (cd08922)
-
changed_duration: stringitem from/to comparison didn’t work (21721e7)
-
items: to_s did not include UNDEF and NULL (71f3de4)
-
add dig-method to top level metadata (2975cd5)
-
rule: otherwise blocks are always executed (dd5d5e5)
-
changed_duration: guards not evaluated for changed duration (48a63e8)
-
changed_duration: cancel ‘changed for’ timer correctly (1bf4aa3)
The updates for Groups makes them behave basically like any other item, and the state (as long as the group has one) can be accessed directly.
If the groups have a type (and an aggregation function, such as Group:Switch:OR(ON, OFF) Switches
) they also supports all methods that the base type does, e.g. Switches.on?
for checking if the state is ON, and Switches.on
to send an ON-command.
Even without a type, you can send a command using Group << ON
which then gets sent to all members of the group.
You can also work with the members of the group using Group.each { |item| ... }
or any of the methods provided by the ruby Enumerable module. These methods work on the direct members of the group.
To work with all descendants (i.e. members of sub-groups) you can use the all_members
method. This by default returns all non-group Items (as per default OpenHAB behaviour of the corresponding GroupItem method), but you can either pass :all
as an argument (all_members(:all)
) to include the groups as well, or :groups
to gen only sub-groups. You can also pass a block to create a custom filter.
Since this is a major version change the library will not upgrade automatically on restart of the bundle if you have followed the installation instructions and put
org.openhab.automation.jrubyscripting:gems=openhab-scripting=~>2.x
in your jruby.cfg
file. This means that if you have rules that depends on the old group syntax, nothing will break until you manually change that line.
To upgrade, just change to:
org.openhab.automation.jrubyscripting:gems=openhab-scripting=~>3.0
and restart the bundle or OpenHAB. You might then get some errors until you have updated your affected rules.
Required changes to rules
- If you have used
Group.items
in triggers, this needs to be changed toGroup.members
- If you access a groups state or properties using
Group.group[.method]
change this toGroup[.method]
Thank you for your effort here. I am not a ruby guy, but I like the looks of the rule examples shown here and your docs.
I have spent a couple hours trying to figure out between but the log only shows the else satisfied; what am I missing?
require 'openhab'
rule 'Log the rule name every minute' do
every :minute
run do
logger.info "Rule #{name} executed"
case Time.now
when between('00:00'..'05:59')
logger.info("Night")
when between('06:00'..'11:59')
logger.info("Morning")
when between('12:00'..'17:59')
logger.info("Afternoon")
when between('18:00'..'19:59')
logger.info("Evening")
when between('20:00'..'23:59')
logger.info("Night")
else
logger.info("Not in time range")
end
end
end
2021-03-07 16:20:01.073 [INFO ] [jruby.Log_the_rule_name_every_minute] - Not in time range
This seems to be a bug due to namespace conflicts. The “between” that you need is OpenHAB::DSL::TimeOfDay.between
but the between without specifying the full namespace resolved to the wrong one (from the rule_config).
I’ll raise an issue and get this fixed ASAP
Until a fix is in place, changing Time.now
to TimeOfDay.now
should work i think.
That won’t fix it, because the range created is (String…String). One way to get it working for now is to use fully qualified path i.e. OpenHAB::DSL::TimeOfDay.between
Version 3.1.2 is released which fixes the issue noted by @carlyler on accessing between within an execution block.
Your rule with some minor updates below, should now work:
require 'openhab'
rule 'Log the rule name every minute' do |rule|
every :minute
run do
logger.info "Rule #{rule.name} executed"
case Time.now
when between('00:00'..'05:59') then logger.info("Night")
when between('06:00'..'11:59') then logger.info("Morning")
when between('12:00'..'17:59') then logger.info("Afternoon")
when between('18:00'..'19:59') then logger.info("Evening")
when between('20:00'..'23:59') then logger.info("Night")
else logger.info("Not in time range")
end
end
end
Changelog is below. In addition we now support Image items.
3.1.2 (2021-03-09)
Bug Fixes
- scope: change execution block binding to be object that based a block to rule (b529684)
3.1.1 (2021-03-08)
Bug Fixes
- rollershutter_item: add safe navigation and nil checks (1e98464)
3.1.0 (2021-03-08)
Features
- image: support for image items (dacc7a8)
The case/when statements can be simplified to:
rule 'Log the rule name every minute' do |rule|
every :minute
run do
logger.info "Rule #{rule.name} executed"
case Time.now
when between('20:00'...'06:00') then logger.info('Night')
when between('06:00'...'12:00') then logger.info('Morning')
when between('12:00'...'18:00') then logger.info('Afternoon')
when between('18:00'...'20:00') then logger.info('Evening')
else logger.info("Not in time range")
end
end
end
Good use of the open range in Ruby. Thanks for sharing.
Just to highlight some ruby features, case statements return the value from the when statement, so you would not need to repeat the logging statement (if you didn’t want to).
rule 'Log the rule name every minute' do |rule|
every :minute
run do
logger.info "Rule #{rule.name} executed"
part_of_day = case Time.now
when between('20:00'...'06:00') then 'Night'
when between('06:00'...'12:00') then 'Morning'
when between('12:00'...'18:00') then 'Afternoon'
when between('18:00'...'20:00') then 'Evening'
else 'Not in time range'
end
logger.info(part_of_day)
end
end
If you aren’t going to need the result anywhere else, you can instead just ‘tap’ into the result.
rule 'Log the rule name every minute' do |rule|
every :minute
run do
logger.info "Rule #{rule.name} executed"
case Time.now
when between('20:00'...'06:00') then 'Night'
when between('06:00'...'12:00') then 'Morning'
when between('12:00'...'18:00') then 'Afternoon'
when between('18:00'...'20:00') then 'Evening'
else 'Not in time range'
end.tap { |part_of_day| logger.info(part_of_day) }
end
end
There have been a couple of fixes and features since the last post:
3.4.2 (2021-04-02)
Bug Fixes
- metadata: convert loaded metadata config into Ruby objects (aa8e2b7)
3.4.1 (2021-04-02)
Bug Fixes
- dependency: swapped mimemagic for marcel for mime type detection (b1ec891)
3.4.0 (2021-03-22)
Features
- thing: add boolean methods for checking thing’s status (58bda12)
3.3.0 (2021-03-16)
Features
- persistence: convert HistoricItem methods to directly return its state (942d7ea)
3.2.1 (2021-03-11)
Bug Fixes
- handle native java exceptions in clean_backtrace (a6f7be4)
3.2.0 (2021-03-10)
Features
- support more comparisons (3898d2d)
3.1.2 (2021-03-09)
Bug Fixes
- scope: change execution block binding to be object that based a block to rule (b529684)
3.1.1 (2021-03-08)
Bug Fixes
- rollershutter_item: add safe navigation and nil checks (1e98464)
Oh my gosh, this is EXACTLY what I need. I’m a ruby expert, and I write lots of stuff in ruby that interacts with OpenHAB (mostly via MQTT), but the Rules DSL is severely lacking, and every time I try to use javascript it’s just such a mess of documentation and differing versions (OH 2.x? 3.x? built in or Graal?). And I’m just not as familiar with javascript. And your approach is (mostly) exactly how I would have done - a gem with the ruby support code, that’s easy to install, and having support for gems, etc., a DSL that relatively closely matches the Rules DSL, etc.
But my question now, is the docs at Installation | OpenHAB JRuby Script Library link to an openhab2-addons repo, which then doesn’t seem to have a viable jar. https://github.com/boc-tothefuture/openhab-jruby/releases seems much more appropriate and up to date, but only seems to have a source archive. Do I need to compile it myself for OH 3.x?
Once I have it up and running, I’d be more than happy to help maintain the ruby side libraries.