Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Thing support in rules #59

Merged
merged 8 commits into from
Oct 18, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 50 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ The following jar files can be found under the jrule/jar-folder:

| Jar File | Description |
| -------------------------------------- | --------------------------------------------------------------------------------------------- |
| jrule-items.jar | Contains all generated items, which will be used when developing rules |
| jrule-generated.jar | Contains all generated items, which will be used when developing rules |
| jrule.jar | JRule Addon classes neeed as dependency when doing development |


Expand All @@ -63,7 +63,7 @@ The following jar files can be found under the jrule/jar-folder:
2. Compile java source files and create a resulting jrule.jar file under /etc/openhab/automation/jrule/jar
3. Compile any java rules file under /etc/openhab/automation/jrule/rules/org/openhab/automation/jrule/rules/user/
It is possible to use package structure with subdirectories in this folder, or the can be place in a flat structure right under this folder
4. Create jar files with dependencies to be used when creating your java-rules (jrule-items.jar).
4. Create jar files with dependencies to be used when creating your java-rules (jrule-generated.jar).
The two jar files needed for Java rules development can be found under /etc/openhab/automation/jrule/jar

Once the JAVA rule engine has started and compiled items successfully you can either copy the jar files
Expand Down Expand Up @@ -642,7 +642,55 @@ Use case: Use generated JRuleItems.java to get hold of items. For instance get s

```

## Example 31

Use case: Restart thing every night due to binding flakyness

```java
@JRuleName("Restart thing every night")
@JRuleWhen(hours = 3)
public void restartThing() {
JRuleThings.my_flaky_thing.restart();
}
```

## Example 32

Use case: Detect if a specific thing goes offline, wait for it to come online again within a given time

```java
@JRuleName("Notify if thing stays offline")
@JRuleWhen(thing = remoteopenhab_thing.ID, trigger = remoteopenhab_thing.TRIGGER_CHANGED, from = "ONLINE")
public void warnIfThingStaysOffline() {
createOrReplaceTimer("MY_TIMER", 3 * 60, unused -> {
if (JRuleThings.remoteopenhab_thing.getStatus() != JRuleThingStatus.ONLINE) {
logWarn("Thing {} is still offline, restarting",remoteopenhab_thing.ID);
JRuleThings.remoteopenhab_thing.restart();
}
});
}
```

## Example 33

Use case: Listen for thing status events on _all_ things

```java
@JRuleName("Log every thing that goes offline")
@JRuleWhen(thing = "*", trigger = JRuleThingStatusTrigger.TRIGGER_CHANGED, from = "ONLINE")
public void startTrackingNonOnlineThing(JRuleEvent event) {
String offlineThingUID = event.getThing();
// ...
}
```

# Changelog

## NEXT

- Added thing rule support
- BREAKING: jrule-items.jar has been renamed to jrule-generated.jar

## BETA13
- Fixed a bug with naming of JRuleItems.java
- Fixed issues with post and sendCommand to groups
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,11 +42,13 @@ public class JRuleConfig {
private static final String WORKING_DIR_PROPERTY = "org.openhab.automation.jrule.directory";
private static final String GENERATED_ITEM_PREFIX_PROPERTY = "org.openhab.automation.jrule.itemprefix";
private static final String GENERATED_ITEM_PACKAGE_PROPERTY = "org.openhab.automation.jrule.itempackage";
private static final String GENERATED_THING_PACKAGE_PROPERTY = "org.openhab.automation.jrule.thingpackage";
private static final String EXECUTORS_MIN_PROPERTY = "org.openhab.automation.jrule.engine.executors.min";
private static final String EXECUTORS_MAX_PROPERTY = "org.openhab.automation.jrule.engine.executors.max";
private static final String EXECUTORS_ENABLE_PROPERTY = "org.openhab.automation.jrule.engine.executors.enable";
private static final String EXECUTORS_THREAD_KEEPALIVE_PROPERTY = "org.openhab.automation.jrule.engine.executors.keepalive";
public static final String ITEMS_DIR_START = "items";
public static final String THINGS_DIR_START = "things";

private static final int DEFAULT_MIN_EXECUTORS = 2;
private static final int DEFAULT_MAX_EXECUTORS = 10;
Expand All @@ -62,6 +64,7 @@ public class JRuleConfig {
+ File.separator + "automation" + File.separator + "jrule";
private static final String DEFAULT_GENERATED_ITEM_PREFIX = "_";
private static final String DEFAULT_GENERATED_ITEM_PACKAGE = "org.openhab.automation.jrule.items.generated";
private static final String DEFAULT_GENERATED_THING_PACKAGE = "org.openhab.automation.jrule.things.generated";

private static final String CLASS_DIR = "class";

Expand Down Expand Up @@ -134,6 +137,14 @@ public String getItemsDirectory() {
return sb.toString();
}

public String getThingsDirectory() {
final StringBuilder sb = new StringBuilder(getWorkingDirectory());
sb.append(File.separator).append(ITEMS_DIR_START).append(File.separator);
final String p = JRuleUtil.packageNameToPath(getGeneratedThingPackage());
sb.append(p);
return sb.toString();
}

public String getJarDirectory() {
return new StringBuilder().append(getWorkingDirectory()).append(File.separator).append(JAR_DIR).toString();
}
Expand Down Expand Up @@ -220,6 +231,10 @@ public String getGeneratedItemPackage() {
return getConfigPropertyOrDefaultValue(GENERATED_ITEM_PACKAGE_PROPERTY, DEFAULT_GENERATED_ITEM_PACKAGE);
}

public String getGeneratedThingPackage() {
return getConfigPropertyOrDefaultValue(GENERATED_THING_PACKAGE_PROPERTY, DEFAULT_GENERATED_THING_PACKAGE);
}

public int getRulesInitDelaySeconds() {
return getIntConfigProperty(INIT_RULES_DELAY_PROPERTY, DEFAULT_RULES_INIT_DELAY);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@
import org.openhab.automation.jrule.internal.handler.JRuleHandler;
import org.openhab.core.events.EventPublisher;
import org.openhab.core.items.ItemRegistry;
import org.openhab.core.thing.ThingManager;
import org.openhab.core.thing.ThingRegistry;
import org.openhab.core.voice.VoiceManager;
import org.osgi.framework.FrameworkUtil;
import org.osgi.service.component.ComponentContext;
Expand Down Expand Up @@ -53,15 +55,16 @@ public class JRuleFactory {

@Activate
public JRuleFactory(Map<String, Object> properties, final @Reference JRuleEventSubscriber eventSubscriber,
final @Reference ItemRegistry itemRegistry, final @Reference EventPublisher eventPublisher,
final @Reference ItemRegistry itemRegistry, final @Reference ThingRegistry thingRegistry,
final @Reference ThingManager thingManager, final @Reference EventPublisher eventPublisher,
final @Reference VoiceManager voiceManager, final ComponentContext componentContext) {
JRuleConfig config = new JRuleConfig(properties);
config.initConfig();
jRuleEngine = JRuleEngine.get();
jRuleEngine.setConfig(config);
jRuleEngine.setItemRegistry(itemRegistry);
jRuleHandler = new JRuleHandler(config, itemRegistry, eventPublisher, eventSubscriber, voiceManager,
componentContext.getBundleContext());
jRuleHandler = new JRuleHandler(config, itemRegistry, thingRegistry, thingManager, eventPublisher,
eventSubscriber, voiceManager, componentContext.getBundleContext());
delayedInit.call(this::init);
}

Expand Down
12 changes: 12 additions & 0 deletions src/main/java/org/openhab/automation/jrule/internal/JRuleUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -233,6 +233,18 @@ public static String getItemNameFromTopic(@NonNull String topic) {
return end > 0 && end > start ? topic.substring(start, end) : JRuleConstants.EMPTY;
}

public static String getThingFromTopic(@NonNull String topic) {
if (topic.isEmpty()) {
return JRuleConstants.EMPTY;
}
final int start = topic.indexOf("things/") + "things/".length(); // TODO make constant
int end = topic.indexOf(SEPARATOR, start);
if (start > end) {
end = topic.length();
}
return end > 0 && end > start ? topic.substring(start, end) : JRuleConstants.EMPTY;
}

public static String packageNameToPath(String packageName) {
return packageName.replace('.', File.separatorChar);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ public class JRuleCompiler {
private static final String JAVA_CLASS_PATH_PROPERTY = "java.class.path";
private static final String CLASSPATH_OPTION = "-classpath";
public static final String JAR_JRULE_NAME = "jrule.jar";
public static final String JAR_JRULE_ITEMS_NAME = "jrule-items.jar";
public static final String JAR_JRULE_GENERATED_JAR_NAME = "jrule-generated.jar";
private static final String LOG_NAME_COMPILER = "JRuleCompiler";
private static final String FRONT_SLASH = "/";

Expand Down Expand Up @@ -180,35 +180,47 @@ public void loadClassesFromFolder(ClassLoader classLoader, File rootFolder, Stri
}
}

public boolean compileItems() {
return compileItems(new File(jRuleConfig.getItemsDirectory()));
public boolean compileItemsAndThings() {
return compileItemsAndThings(new File(jRuleConfig.getItemsRootDirectory()));
}

private boolean compileItems(File sourceFolder) {
private boolean compileItemsAndThings(File sourceFolder) {
final String itemsClassPath = System.getProperty(JAVA_CLASS_PATH_PROPERTY) + File.pathSeparator
+ getJarPath(JAR_JRULE_NAME) + ":" + jRuleConfig.getItemsRootDirectory();
logDebug("Compiling items in folder: {}", sourceFolder.getAbsolutePath());

final File[] javaSourceFiles = sourceFolder.listFiles(JRuleFileNameFilter.JAVA_FILTER);
final File[] javaClassFiles = sourceFolder.listFiles(JRuleFileNameFilter.CLASS_FILTER);
List<File> javaSourceFiles = new ArrayList<>();
List<File> javaClassFiles = new ArrayList<>();

try (Stream<Path> paths = Files.walk(Paths.get(sourceFolder.toURI()))) {
javaSourceFiles = paths.filter(Files::isRegularFile) // is a file
.filter(f -> f.getFileName().toString().endsWith(JRuleConstants.JAVA_FILE_TYPE)).map(Path::toFile)
.collect(Collectors.toList());
} catch (IOException e) {
logError("Error listing source files in folder: {}", sourceFolder.getAbsolutePath(), e);
}
try (Stream<Path> paths = Files.walk(Paths.get(sourceFolder.toURI()))) {
javaClassFiles = paths.filter(Files::isRegularFile) // is a file
.filter(f -> f.getFileName().toString().endsWith(JRuleConstants.CLASS_FILE_TYPE)).map(Path::toFile)
.collect(Collectors.toList());
} catch (IOException e) {
logError("Error listing class files in folder: {}", sourceFolder.getAbsolutePath(), e);
}

Map<String, File> classFiles = new HashMap<>();
Arrays.stream(javaClassFiles).forEach(classFile -> classFiles
javaClassFiles.forEach(classFile -> classFiles
.put(JRuleUtil.removeExtension(classFile.getName(), JRuleConstants.CLASS_FILE_TYPE), classFile));

Map<String, File> sourceFiles = new HashMap<>();
Arrays.stream(javaSourceFiles).forEach(sourceFile -> sourceFiles
javaSourceFiles.forEach(sourceFile -> sourceFiles
.put(JRuleUtil.removeExtension(sourceFile.getName(), JRuleConstants.JAVA_FILE_TYPE), sourceFile));

// First delete any class files with no corresponding source file
classFiles.keySet().stream().filter(className -> !sourceFiles.containsKey(className))
.forEach(className -> classFiles.get(className).delete());
// Will trigger compilation of any missing or old item java files
return compile(new File(sourceFolder, "JRuleItems.java"), itemsClassPath);
}

public boolean compile(File javaSourceFile, String classPath) {
return compile(List.of(javaSourceFile), classPath);
return compile(List.of(new File(jRuleConfig.getItemsDirectory(), "JRuleItems.java"),
new File(jRuleConfig.getThingsDirectory(), "JRuleThings.java")), itemsClassPath);
}

public boolean compile(List<File> javaSourceFiles, String classPath) {
Expand Down Expand Up @@ -258,7 +270,7 @@ public String getJarPath(String jarName) {
public boolean compileRules() {
String rulesClassPath = //
System.getProperty(JAVA_CLASS_PATH_PROPERTY) + File.pathSeparator //
+ getJarPath(JAR_JRULE_ITEMS_NAME) + File.pathSeparator //
+ getJarPath(JAR_JRULE_GENERATED_JAR_NAME) + File.pathSeparator //
+ getJarPath(JAR_JRULE_NAME) + File.pathSeparator; //
String extLibPath = getExtLibPaths();
logDebug("extLibPath: {}", extLibPath);
Expand Down
Loading