Oh yes, that’s exactly the same “issue” I have.
Added BETA8 Build:
BETA8
- Added forName for items see example 27 Added forName and refactored ItemClass Generation · seaside1/jrule@0952ae4 · GitHub
- Added itemName and OldState by gerrieg Added itemName and old state to JRuleEvent by gerrieg · Pull Request #31 · seaside1/jrule · GitHub
BETA10
- Optimized items by gerrieg https://github.com/seaside1/jrule/pull/33
- Syntax change: event.getValue(), event.getValuesAsDouble() etc replaced with event.getState().getValue() and event.getState().getValueAsDouble()
- Syntax change JRuleSwitchItem.sendCommand(myItem, ON) replaced with JRuleSwitchItem.forName(myItem).sendCommand(ON)
I’ve got several Contact
items which are sometimes out of reach. In this case, JRule throws an IllegalArgumentException
when trying to access the state:
var ct = JRuleItemRegistry.get("GN_CT_DOOR", JRuleContactItem.class)
ct.getState();
This is the Exception:
2022-05-14 11:40:12.269 [ERROR] [on.jrule.internal.engine.JRuleEngine] - [UpdateDisplayHall] Error message: No enum constant org.openhab.core.library.types.OpenClosedType.NULL
2022-05-14 11:40:12.273 [ERROR] [on.jrule.internal.engine.JRuleEngine] - [UpdateDisplayHall] Error Stacktrace: java.lang.IllegalArgumentException: No enum constant org.openhab.core.library.types.OpenClosedType.NULL
at java.base/java.lang.Enum.valueOf(Enum.java:240)
at org.openhab.core.library.types.OpenClosedType.valueOf(OpenClosedType.java:1)
at org.openhab.automation.jrule.internal.handler.JRuleEventHandler.getOpenClosedValueFromState(JRuleEventHandler.java:276)
at org.openhab.automation.jrule.internal.handler.JRuleEventHandler.getOpenClosedValue(JRuleEventHandler.java:241)
at org.openhab.automation.jrule.items.JRuleContactItem.getState(JRuleContactItem.java:37)
at org.openhab.automation.jrule.rules.user.DisplayHallRule.lambda$updateDisplayHall$0(DisplayHallRule.java:90)
at java.base/java.util.ArrayList.forEach(ArrayList.java:1541)
at org.openhab.automation.jrule.rules.user.DisplayHallRule.updateDisplayHall(DisplayHallRule.java:83)
It would be nicer to get UNDEF
in this case.
trying to use transform
leads to an Exception
final var dayInfo = transform("MAP(DayOfWeek_de.map):%s", dayOfWeek.name());
Result:
2022-05-14 13:01:08.253 [INFO ] [rule.internal.compiler.JRuleCompiler] - [JRuleCompiler] Error on line 54 in file:///etc/openhab/automation/jrule/rules/org/openhab/automation/jrule/rules/user/DateRule.java: cannot access org.openhab.core.transform.TransformationException
class file for org.openhab.core.transform.TransformationException not found
I have created a fix for this. Have not tested it yet. Will build a new version soon.
See Fixed issued with undef item for state · seaside1/jrule@6827940 · GitHub
This is fixed by seime: See: Wrap TransformationException in JRuleExecutionException by seime · Pull Request #39 · seaside1/jrule · GitHub
This engine and the support/dedication it gets is awesome.
I’ve reimplemented my 250+ items/50+ rules openHAB 2.5 system with JRule on openHAB 3.2 within 6 hours. Some rules are really complex, but it was really easy to do a 1:1 migration from JSR223 groovy/jython and xtext rules.
Thanks to static typing, I could realize a long-postponed “rename items for more consistency” project without any problems.
Using IntelliJ and gradle instead of VSCode or the openHAB UI for editing really helps a lot.
For me, this is by far the best rule engine available in openHAB since openHAB 1.8.
It is fantastic to see that several people are contributing to the project.
Built a BETA11
BETA11
- Wrap TransformationException in JRuleExecutionException by seime Wrap TransformationException in JRuleExecutionException by seime · Pull Request #39 · seaside1/jrule · GitHub
- Add equivalent postUpdate logging as sendCommand by seime Add equivalent postUpdate logging as sendCommand by seime · Pull Request #38 · seaside1/jrule · GitHub
- Fix group sendCommand for UpDown by seime Fix group sendCommand for UpDown by seime · Pull Request #36 · seaside1/jrule · GitHub
- Added eq and neq to channel event by gerrieg Added eq and neq to channel event by gerrieg · Pull Request #35 · seaside1/jrule · GitHub
- Added support for ZonedDateTime in DateTimeItem by gerrieg Added support for ZonedDateTime in DateTimeItem by gerrieg · Pull Request #34 · seaside1/jrule · GitHub
- Fixed issued with undef item for state
- Added mocked eventbus for testing rules with junit
I have added a template project for building and deploying rules using maven.
In that example project there is also code for testing your rules with a fake eventbus.
see: GitHub - seaside1/jrule-user: JRule User Demo and Test Project
@querdenker2k
I recently came across a really weird memory leak. Since I added a rule which gets executed once per minute and updates some Items the openhab crashes with OutOfMemory after a few days.
This is the rule:
package org.openhab.automation.jrule.rules.user;
import org.json.JSONArray;
import org.json.JSONObject;
import org.openhab.automation.jrule.items.generated.*;
import org.openhab.automation.jrule.rules.JRule;
import org.openhab.automation.jrule.rules.JRuleName;
import org.openhab.automation.jrule.rules.JRuleWhen;
import org.openhab.automation.jrule.rules.user.dto.BrightSkyResponse;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.text.SimpleDateFormat;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Date;
import java.util.Optional;
/**
* Created by Finn on 29.04.2022.
*/
public class OHBrightSky extends JRule {
private static final SimpleDateFormat sdf1 = new SimpleDateFormat("yyyy-MM-dd");
private static final DateTimeFormatter df1 = DateTimeFormatter.ofPattern("yyyy-MM-dd");
public OHBrightSky() {
}
@JRuleName("Fetch BrightSkyData")
@JRuleWhen(item = _BrightSkyUpdate.ITEM, trigger = _BrightSkyUpdate.TRIGGER_RECEIVED_COMMAND_ON)
@JRuleWhen(cron = "0 * * * * *")
public void updateBrightSkyData() {
Date date = new Date();
LocalDateTime today = LocalDateTime.now();
LocalDateTime yesterday = LocalDateTime.now().minusDays(1);
Optional<BrightSkyResponse> brightSkyResponseToday = this.loadBrightSkyData("xx.xxxxxxxx", "yy.yyyyyyyy", today);
if (brightSkyResponseToday.isPresent()) {
_BrightSkyRainToday.sendCommand(brightSkyResponseToday.get().getRain());
_BrightSkyTempCurrent.sendCommand(brightSkyResponseToday.get().getTempCurrent());
_BrightSkyTempMaxToday.sendCommand(brightSkyResponseToday.get().getTempMax());
_BrightSkyWindCurrent.sendCommand(brightSkyResponseToday.get().getWindCurrent());
logInfo(String.format("tempMaxToday: %s; rainToday: %s; tempCurrent: %s; windCurrent: %s",
brightSkyResponseToday.get().getTempMax(),
brightSkyResponseToday.get().getRain(),
brightSkyResponseToday.get().getTempCurrent(),
brightSkyResponseToday.get().getWindCurrent()
));
}
Optional<BrightSkyResponse> brightSkyResponseYesterday = this.loadBrightSkyData("xx.xxxxxxxx", "yy.yyyyyyyy", yesterday);
if (brightSkyResponseYesterday.isPresent()) {
_BrightSkyTempMaxYesterday.sendCommand(brightSkyResponseYesterday.get().getTempMax());
_BrightSkyRainYesterday.sendCommand(brightSkyResponseYesterday.get().getRain());
logInfo(String.format("tempMaxYesterday: %s; rainYesterday: %s",
brightSkyResponseYesterday.get().getTempMax(),
brightSkyResponseYesterday.get().getRain()
));
}
}
private Optional<BrightSkyResponse> loadBrightSkyData(String lat, String lng, LocalDateTime date) {
String dateTimeFormatted = date.format(df1);
String url = String.format("https://api.brightsky.dev/weather?lat=%s&lon=%s&date=%s", lat, lng, dateTimeFormatted);
logInfo("Requesting: {}", url);
HttpClient client = HttpClient.newHttpClient();
HttpRequest request = HttpRequest.newBuilder().uri(URI.create(url)).build();
int currentHour = date.getHour();
BrightSkyResponse brightSkyResponse = new BrightSkyResponse();
try {
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
JSONObject myObject = new JSONObject(response.body());
JSONArray hours = myObject.getJSONArray("weather");
for (int i = 0; i < hours.length(); i++) {
JSONObject hour = hours.getJSONObject(i);
brightSkyResponse.setRain(brightSkyResponse.getRain() + hour.getDouble("precipitation"));
if (hour.getDouble("temperature") > brightSkyResponse.getTempMax()) {
brightSkyResponse.setTempMax(hour.getDouble("temperature"));
}
if (i == currentHour) {
brightSkyResponse.setTempCurrent(hour.getDouble("temperature"));
brightSkyResponse.setWindCurrent(hour.getDouble("wind_speed"));
}
}
} catch (Exception e) {
return Optional.empty();
}
return Optional.of(brightSkyResponse);
}
}
The issue is gone after removing the rule again.
Wild guess: Maybe reuse client
instead of creating 2 new ones every minute.
Hmm I think the Java GC should handle that fine but I’ll try it, thanks!
I would love to learn more about the lifecycle and limitations of JRules. Some questions are
- Are classes extending
JRule
singletons? - When are they instantiated?
- Are they discarded (besides when openHAB shuts down)?
- Is it ok to have multiple rule methods (= methods annotated with
@JRuleWhen
/@JRuleName
) in oneXyzRule extends JRule
class? - Is it ok to share state between multiple rule methods within one rule class?
- Does a rule method need to be designed for reentrancy? Or are there measures that ensure that a rule method is not called before its previous invocation is finished?
- Are there any known problems using certain Java features (i.e. regarding final classes, inner classes, Threads, …)?
It would be great to have documentation on these topics.
No they are not. The are however only instantiated once by the engine, and then reused.
Except on reload of the engine, then they are discarded and re instantiated.
When the automation addon is loaded in OpenHab, it will wait 5 seconds and then perform
an initialization:
- Create threads and executors (depending on config)
- Checking folder structure and read write permissions
- Extract the jrule.jar and write it to disk
- Read all items from the ItemRegistry and generate java files for the items if they don’t exist already.
- Compile items source files and store to the jrule-items.jar
- Compile the user defined rules (unless they are supplied with a jar-file)
- Create Rule instances, meaning this is where they are instantiated once.
- Starting to monitor folders for rules, items etc To perform a reload if needed.
- Start listening on the eventbus for events to be able to trigger rules
The rules are instantiated under step 7.
Yes if a reload of the engine is triggered, for instance when adding new items or rules.
Yes I tend to do this, I have typically one File for one area, like LightHandlingRules and under there several rules which handles the lights in the house.
Yes, not sure I would recommend it, but it is possible. You have to take care of thread safety etc. A lot of times, such states are easier to keep in items.
It can be triggered multiple times from different threads, you should add thread safety if you need it. Synchronize methods add locks etc. See example 2, 8, 9, 10 for examples of both locks and timers that are built into JRule.
Not that I know. Most bugs are fixed. There are some feature gaps still, like script actions, member of features, openhab gui support etc.
Yes I will try to update the documentation, and don’t be afraid to submit PRs on the documentation
Regards, S
Unfortunately it didn’t fix the issue, memory usage is still growing. What else could I try to fix this issue?
You could connect (your favourite) profiler to the JVM and take a heap dump. It might shed some light in what is going on.
Regards
Is it possible to postUpdate with UNDEF?
Is it planned to use all the other persistance functions? (not just lastUpdated)
I’ve done some work this weekend regarding persistence functions
[WIP] Generate Items file by seime · Pull Request #42 · seaside1/jrule · GitHub. Not complete nor reviewed by @Seaside yet