Ah, there you are! That makes sense. With this I can split the rule files again and it works nicely.
Another noobie-question:
Is timers.cancel(someItem) if timers.include?(someItem) necessary or can it be reduced to timers.cancel(someItem) in a typical use case? Typical means: Usually I am not interested in whether the timer could be canceled or not. I assume it will get canceled if there exists one for the item.
Alright then. I think Iām done with the refactoring from RuleDSL to JRuby.
I will make some cosmetic changes here and there and will post examples afterwards in the examples section of this forum to show how I solved certain use cases to ultimately inspire more people to use this wonderful scripting language.
I am trying to open/close my blinds based on the astro binding but want to consider differences in weekends or holidays.
The trigger condition would be:
Mo-Fr 10 minutes after sunrise but never before 06:30 and not when a switch item āholidayā is ON
Sa-Su 10 minutes after sunrise but never before 07:00
The ā10 minutes afterā and ānever beforeā can be handled by the astro binding.
But how can I combine both triggers in one rule? My current approach would be one rule for each trigger condition but can this also be put into one rule?
rule "Open blinds in the morning" do
channel "astro:sun:home:rise#event"
not_if { Time.now.holiday? } # or check your holiday item here instead
run do
earliest = Time.now.weekend? ? "07:00" : "06:30"
earliest = Time.parse(earliest)
after([earliest, Time.now].max) do
# Open blinds
end
end
end
Reason this works: after can accept not just duration (from now) but also an absolute time, thus saving you from having to do any calculations / conversions.
This will not open the blinds on holiday. Do you still want to open it on holiday but never before 7am like itās the weekend?
If you still want to open the blinds during holidays, but after 7am, it would look like this:
rule "Open blinds in the morning" do
channel "astro:sun:home:rise#event"
run do
now = Time.now
earliest = (now.weekend? || now.holiday?) ? "07:00" : "06:30"
earliest = Time.parse(earliest)
after([earliest, now].max) do
# Open blinds
end
end
end
Yes, the blinds should open on holidays as well (or when the āholidayā switch is set to ON, as ephemeris does not know when I took days off ^^) but at 07:00 then as your second rule suggests.
Hi, I just found something very odd which I currently can only nail down to a race condition issue and hope you can guide me to the right direction.
I have a rule which reacts on opening/closing a window and changes the thermostat temperature:
rule "Window opened/closed" do
changed Fensterkontakte.members, to:[OPEN, CLOSED], for: 5.seconds
run do |event|
window = event.item
temperatureSetpoint = window.location.equipments(Semantics::HVAC).members.points(Semantics::Setpoint, Semantics::Temperature).first
currentTemperature = window.location.equipments(Semantics::HVAC).members.points(Semantics::Measurement, Semantics::Temperature).first
thermostate = window.location.equipments(Semantics::HVAC).members.points(Semantics::Switch).first
if window.open?
logger.info("#{window} was opened. Outside temperature: #{Aussentemperatur.state}")
setMetadata(temperatureSetpoint, "temperatureSetpoint_before_opening", temperatureSetpoint.state.to_f) unless timers.include?(temperatureSetpoint)
timers.cancel(temperatureSetpoint) # In case the window was opened again while a timer was running
# some more code ...
else
temperatureSetpoint_value = temperatureSetpoint.metadata.dig("dynamicMetadata", "temperatureSetpoint_before_opening").to_f
logger.info("#{fenster} was closed. In 15 minutes #{temperatureSetpoint} will be set to #{temperatureSetpoint_value} Ā°C.")
after (15.minutes), id: temperatureSetpoint do
temperatureSetpoint.ensure << temperatureSetpoint_value if temperatureSetpoint.state.to_f == 16.0 # In case the user changed the temperature within these 15 minutes, the system does not change it back
end
end
end
end
Usually this works as intended but I found an edge case where problems occur. When closing and openenig a window very quickly within 1-2 seconds the line
gets executed. So the runtime system assumes that there is no active timer but there is definitely one as I closed and opened the window. It looks like the system did not have the time yet to create the timer after having closed the window.
I increased the for: guard to higher values like 10 seconds but this does not have any effect.
What do you suggest? Actively waiting a few seconds before executing this posted line above?
I am running OH 3.4.5 on a Raspberry Pi 4. So hardware is quite limited for sure but I hope there are ways around here.
If you closed the window then reopened it again within 1-2 seconds, you would not receive the CLOSED event because it wouldnāt have been closed for 5 seconds. Youāll only receive the OPEN event at the end after 5 seconds.
Also, change this: after (15.minutes), id: temperatureSetpoint do to: after 15.minutes, id: temperatureSetpoint do
Or alternatively `after(15.minutes, id: temperatureSetpoint) do
The original version, while it works, seems a bit odd to me.
That is correct. In my case the system behaves as if the window was opened and then it was opened again. The closing was not registered. That means that I need to execute the if window open? only when the state was closed before. So:
How about simplifying it like this, so you donāt actually need to create another timer:
rule "Window opened/closed" do
changed Fensterkontakte.members, to: CLOSED, for: 20.seconds
changed Fensterkontakte.members, to: OPEN, for: 5.seconds
run do |event|
window = event.item
hvac = window.location.equipments(Semantics::HVAC).members
setpoint_temp = hvac.points(Semantics::Setpoint, Semantics::Temperature).first
current_temp = hvac.points(Semantics::Measurement, Semantics::Temperature).first
thermostate = hvac.points(Semantics::Switch).first
setpoint_before_opening = setpoint_temp.metadata.dig("dynamicMetadata", "temperatureSetpoint_before_opening")&.to_f
if window.open?
logger.info("#{window} was opened. Outside temperature: #{Aussentemperatur.state}")
unless setpoint_before_opening
setMetadata(setpoint_temp, "temperatureSetpoint_before_opening", setpoint_temp.state.to_f)
end
# some more code ...
else
# In case the user changed the temperature within these 15 minutes, the system does not change it back
setpoint_temp.ensure << setpoint_before_opening if setpoint_before_opening && setpoint_temp.state.to_f == 16.0
setpoint_temp.metadata["dynamicMetadata"].delete("temperatureSetpoint_before_opening")
end
end
end
Iāve also changed your variables into snake_case. Itās the Ruby convention and my syntax checker in vscode keeps warning me about it. In case youāre using vscode, Iād recommend installing ruby-lsp extension + rubocop gem to do your auto formatting.
Good hint! I do use VSC, so I will install those extensions.
OK, your adjustments do make sense. My idea for this rule was to change the set point temperature to 16 Ā°C when the window was opened and once the window was closed, OH waits another 15 minutes and then sets it back to its original value.
This assures that the heating does not get swtiched on due to (by Germans beloved) āStoĆlueftenā (rapid air exchange). The room has 15 minutes time to adjust itself. It mostly goes back to its original temperature which is why heating is not necessary. Only if the temperature is still below the original threshold before opening the window then of course the heat should get switched on.
In the āsome more codeā section I also initiated a timer which sends a WhatsApp message to our phones if the room temperature fell below a certain value. It gets rescheduled as long as the window is opened and canceled once closed.
I think with your modifications (I guess the 20.seconds should be changed to 15.minutes) this should still be possible.
But you are right, your simplification are much more convenient as there is 1 timer less I have to worry about.
Hey guys, having a general question regarding OH3.4.5. Are there any known issues regarding memory leaks with JRuby?
I sometimes use the āScriptsā section on the OH homepage to test simple JRuby scripts and to learn/understand the syntax. However, when executing these simple 4-liners the CPU usage of the OH Docker container increases and increases. At some point it takes ages until OH executes the rule and from that point on I need to restart the container.
In case you need more details, let me know but for now I just wanted to understand whether there are generally known issues.
There isnāt a known issue with the jrubys add-on/library on 3.x afaik. @Andrew_Rowe experienced something similar which ended up to do with an openhab core issue but I donāt remember whether it was on 4.x or 3.x
While writing rules and sending commands/updates to items I always wondered: Why not using ensure everywhere? The method makes sure that an item only gets updated if its state is different than the command about to be sent. In which scenario would this not make sense?
I mean, there can be edge cases where one reacts to received command instead of changed. But besides that I actually always would use ensure as it does no harm but reduces communication load on the bus.
Iād be interested in hearing what others think about this.
For me, it depends on the specific circumstances and items. I use received_command a lot. Often on virtual items where I donāt even bother setting it to off afterwards, so it could stay on forever, or off, I pay no attention to its state. received_command ON just means ātrigger this routineā. In that situation, I canāt use ensure, unless I also made sure it switches back to OFF afterwards.
In other cases when sending a command would cause things to actually publish an mqtt message out or perform an http post request, then I would use ensure.
There was an idea in the past to āsetā ensure per rule, but that can be done using an ensure block.
ensure. Itās one of my favorite features of JRuby rules. But it canāt always be used. Besides some virtual or proxy items that you may not care to keep state up to date, some devices canāt be trusted to accurately portray state (or simply cannot - for example I have a motorized drapery that I can send open, close, or stop, but have no feedback on the current state), so itās important that the command is always sent regardless.
That said, as long as I can have a decent way to force a command to be sent, Iād be very much in favor of an ensure! top level method to perma-enable it. Iām thinking on! vs on, command! vs command, etc. Thereās not an easy way to do that with the << convenience alias for command, but I think Iād rather not have to type .ensure all the time.