From 7538913014d8bee6e961ff49f779e66d6aab5210 Mon Sep 17 00:00:00 2001 From: Ravi Nadahar Date: Thu, 9 Apr 2026 17:59:14 +0200 Subject: [PATCH 01/16] Add access to various system registries, OSGi instances and the ability to run and enable/disable rules to DSL scripts Signed-off-by: Ravi Nadahar --- bundles/org.openhab.core.model.script/pom.xml | 5 + .../core/model/script/ScriptServiceUtil.java | 40 +++- .../core/model/script/actions/System.java | 182 ++++++++++++++++++ .../ScriptImplicitlyImportedTypes.java | 3 + 4 files changed, 229 insertions(+), 1 deletion(-) create mode 100644 bundles/org.openhab.core.model.script/src/org/openhab/core/model/script/actions/System.java diff --git a/bundles/org.openhab.core.model.script/pom.xml b/bundles/org.openhab.core.model.script/pom.xml index b666248ae38..c63df47a5cc 100644 --- a/bundles/org.openhab.core.model.script/pom.xml +++ b/bundles/org.openhab.core.model.script/pom.xml @@ -50,6 +50,11 @@ org.openhab.core.io.net ${project.version} + + org.openhab.core.bundles + org.openhab.core.automation + ${project.version} + org.openhab.core.bundles org.openhab.core.automation.module.script diff --git a/bundles/org.openhab.core.model.script/src/org/openhab/core/model/script/ScriptServiceUtil.java b/bundles/org.openhab.core.model.script/src/org/openhab/core/model/script/ScriptServiceUtil.java index 0ba56d99ec0..42c1b12ab94 100644 --- a/bundles/org.openhab.core.model.script/src/org/openhab/core/model/script/ScriptServiceUtil.java +++ b/bundles/org.openhab.core.model.script/src/org/openhab/core/model/script/ScriptServiceUtil.java @@ -16,8 +16,11 @@ import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.atomic.AtomicReference; +import org.openhab.core.automation.RuleManager; +import org.openhab.core.automation.RuleRegistry; import org.openhab.core.events.EventPublisher; import org.openhab.core.items.ItemRegistry; +import org.openhab.core.items.MetadataRegistry; import org.openhab.core.model.core.ModelRepository; import org.openhab.core.model.script.engine.ScriptEngine; import org.openhab.core.model.script.engine.action.ActionService; @@ -50,6 +53,9 @@ public class ScriptServiceUtil { private final ThingRegistry thingRegistry; private final EventPublisher eventPublisher; private final ModelRepository modelRepository; + private final MetadataRegistry metadataRegistry; + private final RuleRegistry ruleRegistry; + private final RuleManager ruleManager; private final Scheduler scheduler; private final AtomicReference scriptEngine = new AtomicReference<>(); @@ -59,11 +65,15 @@ public class ScriptServiceUtil { @Activate public ScriptServiceUtil(final @Reference ItemRegistry itemRegistry, final @Reference ThingRegistry thingRegistry, final @Reference EventPublisher eventPublisher, final @Reference ModelRepository modelRepository, - final @Reference Scheduler scheduler) { + final @Reference MetadataRegistry metadataRegistry, final @Reference RuleRegistry ruleRegistry, + final @Reference RuleManager ruleManager, final @Reference Scheduler scheduler) { this.itemRegistry = itemRegistry; this.thingRegistry = thingRegistry; this.eventPublisher = eventPublisher; this.modelRepository = modelRepository; + this.metadataRegistry = metadataRegistry; + this.ruleRegistry = ruleRegistry; + this.ruleManager = ruleManager; this.scheduler = scheduler; if (instance != null) { @@ -91,6 +101,10 @@ public ItemRegistry getItemRegistryInstance() { return itemRegistry; } + public static ThingRegistry getThingRegistry() { + return getInstance().thingRegistry; + } + public ThingRegistry getThingRegistryInstance() { return thingRegistry; } @@ -107,6 +121,30 @@ public ModelRepository getModelRepositoryInstance() { return modelRepository; } + public static MetadataRegistry getMetadataRegistry() { + return getInstance().metadataRegistry; + } + + public MetadataRegistry getMetadataRegistryInstance() { + return metadataRegistry; + } + + public static RuleRegistry getRuleRegistry() { + return getInstance().ruleRegistry; + } + + public RuleRegistry getRuleRegistryInstance() { + return ruleRegistry; + } + + public static RuleManager getRuleManager() { + return getInstance().ruleManager; + } + + public RuleManager getRuleManagerInstance() { + return ruleManager; + } + public static Scheduler getScheduler() { return getInstance().scheduler; } diff --git a/bundles/org.openhab.core.model.script/src/org/openhab/core/model/script/actions/System.java b/bundles/org.openhab.core.model.script/src/org/openhab/core/model/script/actions/System.java new file mode 100644 index 00000000000..cbd1deead5a --- /dev/null +++ b/bundles/org.openhab.core.model.script/src/org/openhab/core/model/script/actions/System.java @@ -0,0 +1,182 @@ +/* + * Copyright (c) 2010-2026 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.core.model.script.actions; + +import java.util.Map; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.openhab.core.automation.RuleManager; +import org.openhab.core.automation.RuleRegistry; +import org.openhab.core.items.ItemRegistry; +import org.openhab.core.items.MetadataRegistry; +import org.openhab.core.model.script.ScriptServiceUtil; +import org.openhab.core.model.script.engine.action.ActionDoc; +import org.openhab.core.thing.ThingRegistry; +import org.osgi.framework.Bundle; +import org.osgi.framework.BundleContext; +import org.osgi.framework.FrameworkUtil; +import org.osgi.framework.ServiceReference; + +/** + * {@link System} provides DSL access to things like OSGi instances, system registries and the ability to run other + * rules. + * + * @author Ravi Nadahar - Initial contribution + */ +@NonNullByDefault +public class System { + + /** + * Retrieve an OSGi instance of the specified {@link Class}, if it exists. + * + * @param the class type. + * @param clazz the class of the instance to get. + * @return The instance or {@code null} if the instance wasn't found. + */ + @ActionDoc(text = "acquire an OSGi instance") + public static @Nullable T getInstance(Class clazz) { + Bundle bundle = FrameworkUtil.getBundle(clazz); + if (bundle != null) { + BundleContext bc = bundle.getBundleContext(); + if (bc != null) { + ServiceReference ref = bc.getServiceReference(clazz); + if (ref != null) { + return bc.getService(ref); + } + } + } + return null; + } + + /** + * Run the rule with the specified UID. + * + * @param ruleUID the UID of the rule to run. + * @return A copy of the rule context, including possible return values. + * @throws IllegalArgumentException If a rule with the specified UID doesn't exist. + */ + @ActionDoc(text = "run the rule with the specified UID") + public static Map runRule(String ruleUID) { + RuleManager ruleManager = ScriptServiceUtil.getRuleManager(); + if (ruleManager.getStatus(ruleUID) == null) { + throw new IllegalArgumentException("Rule with UID '" + ruleUID + "' doesn't exist"); + } + return ruleManager.runNow(ruleUID); + } + + /** + * Run the rule with the specified UID with the specified context. + * + * @param ruleUID the UID of the rule to run. + * @param context the {@link Map} of {@link String} and {@link Object} pairs that constitutes the context. + * @return A copy of the rule context, including possible return values. + * @throws IllegalArgumentException If a rule with the specified UID doesn't exist. + */ + @ActionDoc(text = "run the rule with the specified UID and context") + public static Map runRule(String ruleUID, Map context) { + RuleManager ruleManager = ScriptServiceUtil.getRuleManager(); + if (ruleManager.getStatus(ruleUID) == null) { + throw new IllegalArgumentException("Rule with UID '" + ruleUID + "' doesn't exist"); + } + return ruleManager.runNow(ruleUID, false, context); + } + + /** + * Run the rule with the specified UID with the specified context, while optionally taking conditions into + * account. + * + * @param ruleUID the UID of the rule to run. + * @param considerConditions {@code true} to not run the rule if its conditions don't qualify. + * @param context the {@link Map} of {@link String} and {@link Object} pairs that constitutes the context. + * @return A copy of the rule context, including possible return values. + * @throws IllegalArgumentException If a rule with the specified UID doesn't exist. + */ + @ActionDoc(text = "run the rule with the specified UID, condition evaluation setting and context") + public static Map runRule(String ruleUID, boolean considerConditions, Map context) { + RuleManager ruleManager = ScriptServiceUtil.getRuleManager(); + if (ruleManager.getStatus(ruleUID) == null) { + throw new IllegalArgumentException("Rule with UID '" + ruleUID + "' doesn't exist"); + } + return ruleManager.runNow(ruleUID, considerConditions, context); + } + + /** + * Check whether the specified rule is enabled. + * + * @param ruleUID the UID of the rule to check. + * @return {@code true} if the rule is enabled, {@code false} otherwise. + * @throws IllegalArgumentException If a rule with the specified UID doesn't exist. + */ + @ActionDoc(text = "check whether the specified rule rule is enabled") + public static boolean isRuleEnabled(String ruleUID) { + RuleManager ruleManager = ScriptServiceUtil.getRuleManager(); + Boolean result = ruleManager.isEnabled(ruleUID); + if (result == null) { + throw new IllegalArgumentException("Rule with UID '" + ruleUID + "' doesn't exist"); + } + return result.booleanValue(); + } + + /** + * Set whether the specified rule is enabled. + * + * @param ruleUID the UID of the rule to enable or disable. + * @param enabled {@code true} to enable the rule, {@code false} to disable the rule. + * @throws IllegalArgumentException If a rule with the specified UID doesn't exist. + */ + @ActionDoc(text = "set whether the specified rule is enabled") + public static void setRuleEnabled(String ruleUID, boolean enabled) { + ScriptServiceUtil.getRuleManager().setEnabled(ruleUID, enabled); + } + + /** + * @return The {@link ThingRegistry}. + */ + @ActionDoc(text = "get the thing registry") + public static ThingRegistry getThingRegistry() { + return ScriptServiceUtil.getThingRegistry(); + } + + /** + * @return The {@link MetadataRegistry}. + */ + @ActionDoc(text = "get the metadata registry") + public static MetadataRegistry getMetadataRegistry() { + return ScriptServiceUtil.getMetadataRegistry(); + } + + /** + * @return The {@link ItemRegistry}. + */ + @ActionDoc(text = "get the item registry") + public static ItemRegistry getItemRegistry() { + return ScriptServiceUtil.getItemRegistry(); + } + + /** + * @return The {@link RuleRegistry}. + */ + @ActionDoc(text = "get the rule registry") + public static RuleRegistry getRuleRegistry() { + return ScriptServiceUtil.getRuleRegistry(); + } + + /** + * @return The {@link RuleManager}. + */ + @ActionDoc(text = "get the rule manager") + public static RuleManager getRuleManager() { + return ScriptServiceUtil.getRuleManager(); + } +} diff --git a/bundles/org.openhab.core.model.script/src/org/openhab/core/model/script/scoping/ScriptImplicitlyImportedTypes.java b/bundles/org.openhab.core.model.script/src/org/openhab/core/model/script/scoping/ScriptImplicitlyImportedTypes.java index d8c68693d23..55719c8b45c 100644 --- a/bundles/org.openhab.core.model.script/src/org/openhab/core/model/script/scoping/ScriptImplicitlyImportedTypes.java +++ b/bundles/org.openhab.core.model.script/src/org/openhab/core/model/script/scoping/ScriptImplicitlyImportedTypes.java @@ -35,6 +35,7 @@ import org.openhab.core.model.script.actions.Log; import org.openhab.core.model.script.actions.Ping; import org.openhab.core.model.script.actions.ScriptExecution; +import org.openhab.core.model.script.actions.System; import org.openhab.core.model.script.actions.Transformation; import org.openhab.core.model.script.engine.IActionServiceProvider; import org.openhab.core.model.script.engine.IThingActionsProvider; @@ -76,6 +77,7 @@ protected List> getExtensionClasses() { result.add(Ping.class); result.add(Transformation.class); result.add(ScriptExecution.class); + result.add(System.class); result.add(URLEncoder.class); result.addAll(getActionClasses()); @@ -92,6 +94,7 @@ protected List> getStaticImportClasses() { result.add(Ping.class); result.add(Transformation.class); result.add(ScriptExecution.class); + result.add(System.class); result.add(URLEncoder.class); result.add(CoreUtil.class); From 831ac4ecd26cef9f61ec623df8c16c835b3d575e Mon Sep 17 00:00:00 2001 From: Ravi Nadahar Date: Thu, 9 Apr 2026 20:13:56 +0200 Subject: [PATCH 02/16] Set required startlevel in ScriptEngineOSGiTest Signed-off-by: Ravi Nadahar --- .../core/model/script/engine/ScriptEngineOSGiTest.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/itests/org.openhab.core.model.script.tests/src/main/java/org/openhab/core/model/script/engine/ScriptEngineOSGiTest.java b/itests/org.openhab.core.model.script.tests/src/main/java/org/openhab/core/model/script/engine/ScriptEngineOSGiTest.java index ffd5639b102..9c749a89884 100644 --- a/itests/org.openhab.core.model.script.tests/src/main/java/org/openhab/core/model/script/engine/ScriptEngineOSGiTest.java +++ b/itests/org.openhab.core.model.script.tests/src/main/java/org/openhab/core/model/script/engine/ScriptEngineOSGiTest.java @@ -47,6 +47,7 @@ import org.openhab.core.library.unit.SIUnits; import org.openhab.core.library.unit.Units; import org.openhab.core.model.script.ScriptServiceUtil; +import org.openhab.core.service.StartLevelService; import org.openhab.core.test.java.JavaOSGiTest; import org.openhab.core.types.State; @@ -68,6 +69,7 @@ public class ScriptEngineOSGiTest extends JavaOSGiTest { private @NonNullByDefault({}) ItemRegistry itemRegistry; private @NonNullByDefault({}) ScriptEngine scriptEngine; private @Mock @NonNullByDefault({}) UnitProvider unitProviderMock; + private @Mock @NonNullByDefault({}) StartLevelService startLevelService; @BeforeEach public void setup() { @@ -81,6 +83,9 @@ public void setup() { registerService(eventPublisher); + when(startLevelService.getStartLevel()).thenReturn(40); + registerService(startLevelService, StartLevelService.class.getName()); + itemRegistry = getService(ItemRegistry.class); assertNotNull(itemRegistry); From 6839f309c3ff9f6d5bf731d71f864e58319b642a Mon Sep 17 00:00:00 2001 From: Ravi Nadahar Date: Fri, 10 Apr 2026 03:34:23 +0200 Subject: [PATCH 03/16] Make RuleManager a dynamic reference Signed-off-by: Ravi Nadahar --- .../core/model/script/ScriptServiceUtil.java | 19 +++++++++---- .../core/model/script/actions/System.java | 27 ++++++++++++++++--- 2 files changed, 38 insertions(+), 8 deletions(-) diff --git a/bundles/org.openhab.core.model.script/src/org/openhab/core/model/script/ScriptServiceUtil.java b/bundles/org.openhab.core.model.script/src/org/openhab/core/model/script/ScriptServiceUtil.java index 42c1b12ab94..fd0348bd8d8 100644 --- a/bundles/org.openhab.core.model.script/src/org/openhab/core/model/script/ScriptServiceUtil.java +++ b/bundles/org.openhab.core.model.script/src/org/openhab/core/model/script/ScriptServiceUtil.java @@ -16,6 +16,7 @@ import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.atomic.AtomicReference; +import org.eclipse.jdt.annotation.Nullable; import org.openhab.core.automation.RuleManager; import org.openhab.core.automation.RuleRegistry; import org.openhab.core.events.EventPublisher; @@ -55,7 +56,7 @@ public class ScriptServiceUtil { private final ModelRepository modelRepository; private final MetadataRegistry metadataRegistry; private final RuleRegistry ruleRegistry; - private final RuleManager ruleManager; + private volatile @Nullable RuleManager ruleManager; private final Scheduler scheduler; private final AtomicReference scriptEngine = new AtomicReference<>(); @@ -66,14 +67,13 @@ public class ScriptServiceUtil { public ScriptServiceUtil(final @Reference ItemRegistry itemRegistry, final @Reference ThingRegistry thingRegistry, final @Reference EventPublisher eventPublisher, final @Reference ModelRepository modelRepository, final @Reference MetadataRegistry metadataRegistry, final @Reference RuleRegistry ruleRegistry, - final @Reference RuleManager ruleManager, final @Reference Scheduler scheduler) { + final @Reference Scheduler scheduler) { this.itemRegistry = itemRegistry; this.thingRegistry = thingRegistry; this.eventPublisher = eventPublisher; this.modelRepository = modelRepository; this.metadataRegistry = metadataRegistry; this.ruleRegistry = ruleRegistry; - this.ruleManager = ruleManager; this.scheduler = scheduler; if (instance != null) { @@ -83,6 +83,15 @@ public ScriptServiceUtil(final @Reference ItemRegistry itemRegistry, final @Refe logger.debug("ScriptServiceUtil started"); } + @Reference(cardinality = ReferenceCardinality.OPTIONAL, policy = ReferencePolicy.DYNAMIC) + void setRuleManager(RuleManager ruleManager) { + this.ruleManager = ruleManager; + } + + void unsetRuleManager(RuleManager ruleManager) { + this.ruleManager = null; + } + @Deactivate public void deactivate() { logger.debug("ScriptServiceUtil stopped"); @@ -137,11 +146,11 @@ public RuleRegistry getRuleRegistryInstance() { return ruleRegistry; } - public static RuleManager getRuleManager() { + public @Nullable static RuleManager getRuleManager() { return getInstance().ruleManager; } - public RuleManager getRuleManagerInstance() { + public @Nullable RuleManager getRuleManagerInstance() { return ruleManager; } diff --git a/bundles/org.openhab.core.model.script/src/org/openhab/core/model/script/actions/System.java b/bundles/org.openhab.core.model.script/src/org/openhab/core/model/script/actions/System.java index cbd1deead5a..51cee942928 100644 --- a/bundles/org.openhab.core.model.script/src/org/openhab/core/model/script/actions/System.java +++ b/bundles/org.openhab.core.model.script/src/org/openhab/core/model/script/actions/System.java @@ -65,10 +65,14 @@ public class System { * @param ruleUID the UID of the rule to run. * @return A copy of the rule context, including possible return values. * @throws IllegalArgumentException If a rule with the specified UID doesn't exist. + * @throws IllegalStateException If no {@link RuleManager} instance exists. */ @ActionDoc(text = "run the rule with the specified UID") public static Map runRule(String ruleUID) { RuleManager ruleManager = ScriptServiceUtil.getRuleManager(); + if (ruleManager == null) { + throw new IllegalStateException("RuleManager doesn't exist"); + } if (ruleManager.getStatus(ruleUID) == null) { throw new IllegalArgumentException("Rule with UID '" + ruleUID + "' doesn't exist"); } @@ -82,10 +86,14 @@ public static Map runRule(String ruleUID) { * @param context the {@link Map} of {@link String} and {@link Object} pairs that constitutes the context. * @return A copy of the rule context, including possible return values. * @throws IllegalArgumentException If a rule with the specified UID doesn't exist. + * @throws IllegalStateException If no {@link RuleManager} instance exists. */ @ActionDoc(text = "run the rule with the specified UID and context") public static Map runRule(String ruleUID, Map context) { RuleManager ruleManager = ScriptServiceUtil.getRuleManager(); + if (ruleManager == null) { + throw new IllegalStateException("RuleManager doesn't exist"); + } if (ruleManager.getStatus(ruleUID) == null) { throw new IllegalArgumentException("Rule with UID '" + ruleUID + "' doesn't exist"); } @@ -101,10 +109,14 @@ public static Map runRule(String ruleUID, Map co * @param context the {@link Map} of {@link String} and {@link Object} pairs that constitutes the context. * @return A copy of the rule context, including possible return values. * @throws IllegalArgumentException If a rule with the specified UID doesn't exist. + * @throws IllegalStateException If no {@link RuleManager} instance exists. */ @ActionDoc(text = "run the rule with the specified UID, condition evaluation setting and context") public static Map runRule(String ruleUID, boolean considerConditions, Map context) { RuleManager ruleManager = ScriptServiceUtil.getRuleManager(); + if (ruleManager == null) { + throw new IllegalStateException("RuleManager doesn't exist"); + } if (ruleManager.getStatus(ruleUID) == null) { throw new IllegalArgumentException("Rule with UID '" + ruleUID + "' doesn't exist"); } @@ -117,10 +129,14 @@ public static Map runRule(String ruleUID, boolean considerCondit * @param ruleUID the UID of the rule to check. * @return {@code true} if the rule is enabled, {@code false} otherwise. * @throws IllegalArgumentException If a rule with the specified UID doesn't exist. + * @throws IllegalStateException If no {@link RuleManager} instance exists. */ @ActionDoc(text = "check whether the specified rule rule is enabled") public static boolean isRuleEnabled(String ruleUID) { RuleManager ruleManager = ScriptServiceUtil.getRuleManager(); + if (ruleManager == null) { + throw new IllegalStateException("RuleManager doesn't exist"); + } Boolean result = ruleManager.isEnabled(ruleUID); if (result == null) { throw new IllegalArgumentException("Rule with UID '" + ruleUID + "' doesn't exist"); @@ -134,10 +150,15 @@ public static boolean isRuleEnabled(String ruleUID) { * @param ruleUID the UID of the rule to enable or disable. * @param enabled {@code true} to enable the rule, {@code false} to disable the rule. * @throws IllegalArgumentException If a rule with the specified UID doesn't exist. + * @throws IllegalStateException If no {@link RuleManager} instance exists. */ @ActionDoc(text = "set whether the specified rule is enabled") public static void setRuleEnabled(String ruleUID, boolean enabled) { - ScriptServiceUtil.getRuleManager().setEnabled(ruleUID, enabled); + RuleManager ruleManager = ScriptServiceUtil.getRuleManager(); + if (ruleManager == null) { + throw new IllegalStateException("RuleManager doesn't exist"); + } + ruleManager.setEnabled(ruleUID, enabled); } /** @@ -173,10 +194,10 @@ public static RuleRegistry getRuleRegistry() { } /** - * @return The {@link RuleManager}. + * @return The {@link RuleManager} or {@code null}. */ @ActionDoc(text = "get the rule manager") - public static RuleManager getRuleManager() { + public static @Nullable RuleManager getRuleManager() { return ScriptServiceUtil.getRuleManager(); } } From 9921c742c001f262226bba58d7942519202d425a Mon Sep 17 00:00:00 2001 From: Ravi Nadahar Date: Fri, 10 Apr 2026 03:34:49 +0200 Subject: [PATCH 04/16] Revert "Set required startlevel in ScriptEngineOSGiTest" This reverts commit ee8ae1ed198b267c8e2311d9bc025a49be5c8ca9. Signed-off-by: Ravi Nadahar --- .../core/model/script/engine/ScriptEngineOSGiTest.java | 5 ----- 1 file changed, 5 deletions(-) diff --git a/itests/org.openhab.core.model.script.tests/src/main/java/org/openhab/core/model/script/engine/ScriptEngineOSGiTest.java b/itests/org.openhab.core.model.script.tests/src/main/java/org/openhab/core/model/script/engine/ScriptEngineOSGiTest.java index 9c749a89884..ffd5639b102 100644 --- a/itests/org.openhab.core.model.script.tests/src/main/java/org/openhab/core/model/script/engine/ScriptEngineOSGiTest.java +++ b/itests/org.openhab.core.model.script.tests/src/main/java/org/openhab/core/model/script/engine/ScriptEngineOSGiTest.java @@ -47,7 +47,6 @@ import org.openhab.core.library.unit.SIUnits; import org.openhab.core.library.unit.Units; import org.openhab.core.model.script.ScriptServiceUtil; -import org.openhab.core.service.StartLevelService; import org.openhab.core.test.java.JavaOSGiTest; import org.openhab.core.types.State; @@ -69,7 +68,6 @@ public class ScriptEngineOSGiTest extends JavaOSGiTest { private @NonNullByDefault({}) ItemRegistry itemRegistry; private @NonNullByDefault({}) ScriptEngine scriptEngine; private @Mock @NonNullByDefault({}) UnitProvider unitProviderMock; - private @Mock @NonNullByDefault({}) StartLevelService startLevelService; @BeforeEach public void setup() { @@ -83,9 +81,6 @@ public void setup() { registerService(eventPublisher); - when(startLevelService.getStartLevel()).thenReturn(40); - registerService(startLevelService, StartLevelService.class.getName()); - itemRegistry = getService(ItemRegistry.class); assertNotNull(itemRegistry); From 135e265b17bd299ea8385880a449e61651df8f1b Mon Sep 17 00:00:00 2001 From: Ravi Nadahar Date: Fri, 10 Apr 2026 15:40:26 +0200 Subject: [PATCH 05/16] Add additional convenience methods Signed-off-by: Ravi Nadahar --- .../core/model/script/actions/System.java | 161 ++++++++++++++++++ 1 file changed, 161 insertions(+) diff --git a/bundles/org.openhab.core.model.script/src/org/openhab/core/model/script/actions/System.java b/bundles/org.openhab.core.model.script/src/org/openhab/core/model/script/actions/System.java index 51cee942928..ede7b433e72 100644 --- a/bundles/org.openhab.core.model.script/src/org/openhab/core/model/script/actions/System.java +++ b/bundles/org.openhab.core.model.script/src/org/openhab/core/model/script/actions/System.java @@ -12,13 +12,19 @@ */ package org.openhab.core.model.script.actions; +import java.util.LinkedHashMap; import java.util.Map; +import org.eclipse.jdt.annotation.NonNull; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; import org.openhab.core.automation.RuleManager; import org.openhab.core.automation.RuleRegistry; +import org.openhab.core.common.registry.ManagedProvider; import org.openhab.core.items.ItemRegistry; +import org.openhab.core.items.Metadata; +import org.openhab.core.items.MetadataKey; +import org.openhab.core.items.MetadataProvider; import org.openhab.core.items.MetadataRegistry; import org.openhab.core.model.script.ScriptServiceUtil; import org.openhab.core.model.script.engine.action.ActionDoc; @@ -100,6 +106,30 @@ public static Map runRule(String ruleUID, Map co return ruleManager.runNow(ruleUID, false, context); } + /** + * Run the rule with the specified UID with the specified context, while optionally taking conditions into + * account. + * + * @param ruleUID the UID of the rule to run. + * @param considerConditions {@code true} to not run the rule if its conditions don't qualify. + * @param context the pairs of {@link String}s and {@link Object}s that constitutes the context. Must be in pairs, + * the first is the key, the second is the value. + * @return A copy of the rule context, including possible return values. + * @throws IllegalArgumentException If a rule with the specified UID doesn't exist. + * @throws IllegalStateException If no {@link RuleManager} instance exists. + */ + @ActionDoc(text = "run the rule with the specified UID, condition evaluation setting and context") + public static Map runRule(String ruleUID, boolean considerConditions, Object... context) { + RuleManager ruleManager = ScriptServiceUtil.getRuleManager(); + if (ruleManager == null) { + throw new IllegalStateException("RuleManager doesn't exist"); + } + if (ruleManager.getStatus(ruleUID) == null) { + throw new IllegalArgumentException("Rule with UID '" + ruleUID + "' doesn't exist"); + } + return ruleManager.runNow(ruleUID, considerConditions, parseObjectArray(context)); + } + /** * Run the rule with the specified UID with the specified context, while optionally taking conditions into * account. @@ -161,6 +191,119 @@ public static void setRuleEnabled(String ruleUID, boolean enabled) { ruleManager.setEnabled(ruleUID, enabled); } + /** + * + * @param namespace + * @param itemName + * @param value + * @throws IllegalArgumentException If either value is {@code null} or {@code namespace} or + * {@code itemName} is invalid. + * @throws UnsupportedOperationException If the metadata namespace has a reserved {@link MetadataProvider} that is + * not a {@link ManagedProvider}. + * @throws IllegalStateException If no ManagedProvider is available. + */ + @NonNullByDefault({}) + public static void addMetadata(String namespace, String itemName, String value) { + addMetadata(namespace, itemName, value, (String) null); + } + + @NonNullByDefault({}) + public static void addMetadata(String namespace, String itemName, String value, Object... configuration) { + addMetadata(namespace, itemName, value, parseObjectArray(configuration)); + } + + /** + * + * @param namespace + * @param itemName + * @param value + * @param configuration + * @throws IllegalArgumentException If {@code namespace}, {@code itemName} or {@code value} is {@code null} or if + * {@code namespace} or {@code itemName} is invalid. + * @throws UnsupportedOperationException If the metadata namespace has a reserved {@link MetadataProvider} that is + * not a {@link ManagedProvider}. + * @throws IllegalStateException If no ManagedProvider is available. + */ + @NonNullByDefault({}) + public static void addMetadata(String namespace, String itemName, String value, + @Nullable Map<@NonNull String, @NonNull Object> configuration) { + if (namespace == null) { + throw new IllegalArgumentException("namespace cannot be null"); + } + if (itemName == null) { + throw new IllegalArgumentException("itemName cannot be null"); + } + if (value == null) { + throw new IllegalArgumentException("value cannot be null"); + } + getMetadataRegistry().add(new Metadata(new MetadataKey(namespace, itemName), value, configuration)); + } + + @NonNullByDefault({}) + public static @Nullable Metadata getMetadata(String namespace, String itemName) { + if (namespace == null) { + throw new IllegalArgumentException("namespace cannot be null"); + } + if (itemName == null) { + throw new IllegalArgumentException("itemName cannot be null"); + } + return getMetadataRegistry().get(new MetadataKey(namespace, itemName)); + } + + @NonNullByDefault({}) + public static @Nullable Metadata removeMetadata(String namespace, String itemName) { + if (namespace == null) { + throw new IllegalArgumentException("namespace cannot be null"); + } + if (itemName == null) { + throw new IllegalArgumentException("itemName cannot be null"); + } + return getMetadataRegistry().remove(new MetadataKey(namespace, itemName)); + } + + /** + * + * @param namespace + * @param itemName + * @param value + * @throws IllegalArgumentException If either value is {@code null} or {@code namespace} or + * {@code itemName} is invalid. + * @throws UnsupportedOperationException If the metadata namespace has a reserved {@link MetadataProvider} that is + * not a {@link ManagedProvider}. + * @throws IllegalStateException If no ManagedProvider is available. + */ + @NonNullByDefault({}) + public static @Nullable Metadata updateMetadata(String namespace, String itemName, String value) { + return updateMetadata(namespace, itemName, value, null); + } + + /** + * + * @param namespace + * @param itemName + * @param value + * @param configuration + * @throws IllegalArgumentException If {@code namespace}, {@code itemName} or {@code value} is {@code null} or if + * {@code namespace} or {@code itemName} is invalid. + * @throws UnsupportedOperationException If the metadata namespace has a reserved {@link MetadataProvider} that is + * not a {@link ManagedProvider}. + * @throws IllegalStateException If no ManagedProvider is available. + */ + @NonNullByDefault({}) + public static @Nullable Metadata updateMetadata(String namespace, String itemName, String value, + @Nullable Map<@NonNull String, @NonNull Object> configuration) { + if (namespace == null) { + throw new IllegalArgumentException("namespace cannot be null"); + } + if (itemName == null) { + throw new IllegalArgumentException("itemName cannot be null"); + } + if (value == null) { + throw new IllegalArgumentException("value cannot be null"); + } + return getMetadataRegistry().update(new Metadata(new MetadataKey(namespace, itemName), value, configuration)); + } + /** * @return The {@link ThingRegistry}. */ @@ -200,4 +343,22 @@ public static RuleRegistry getRuleRegistry() { public static @Nullable RuleManager getRuleManager() { return ScriptServiceUtil.getRuleManager(); } + + private static Map parseObjectArray(Object @Nullable [] objects) throws IllegalArgumentException { + if (objects == null || objects.length == 0) { + return Map.of(); + } + if ((objects.length % 2) != 0) { + throw new IllegalArgumentException("There must be an even number of objects (" + objects.length + ')'); + } + Map result = new LinkedHashMap<>(); + for (int i = 0; i < objects.length; i += 2) { + if (objects[i] instanceof String key) { + result.put(key, objects[i + 1]); + } else { + throw new IllegalArgumentException("Keys must be strings: " + objects[i]); + } + } + return result; + } } From 24f7e63d8311c3908e3a1272f3107e91e50aec7b Mon Sep 17 00:00:00 2001 From: Ravi Nadahar Date: Fri, 10 Apr 2026 20:04:06 +0200 Subject: [PATCH 06/16] Add more overloads Signed-off-by: Ravi Nadahar --- .../core/model/script/actions/System.java | 129 ++++++++++++++---- 1 file changed, 106 insertions(+), 23 deletions(-) diff --git a/bundles/org.openhab.core.model.script/src/org/openhab/core/model/script/actions/System.java b/bundles/org.openhab.core.model.script/src/org/openhab/core/model/script/actions/System.java index ede7b433e72..95c66979407 100644 --- a/bundles/org.openhab.core.model.script/src/org/openhab/core/model/script/actions/System.java +++ b/bundles/org.openhab.core.model.script/src/org/openhab/core/model/script/actions/System.java @@ -21,6 +21,7 @@ import org.openhab.core.automation.RuleManager; import org.openhab.core.automation.RuleRegistry; import org.openhab.core.common.registry.ManagedProvider; +import org.openhab.core.items.Item; import org.openhab.core.items.ItemRegistry; import org.openhab.core.items.Metadata; import org.openhab.core.items.MetadataKey; @@ -85,6 +86,18 @@ public static Map runRule(String ruleUID) { return ruleManager.runNow(ruleUID); } + @ActionDoc(text = "run the rule with the specified UID and condition evaluation setting") + public static Map runRule(String ruleUID, boolean considerConditions) { + RuleManager ruleManager = ScriptServiceUtil.getRuleManager(); + if (ruleManager == null) { + throw new IllegalStateException("RuleManager doesn't exist"); + } + if (ruleManager.getStatus(ruleUID) == null) { + throw new IllegalArgumentException("Rule with UID '" + ruleUID + "' doesn't exist"); + } + return ruleManager.runNow(ruleUID, considerConditions, null); + } + /** * Run the rule with the specified UID with the specified context. * @@ -191,10 +204,18 @@ public static void setRuleEnabled(String ruleUID, boolean enabled) { ruleManager.setEnabled(ruleUID, enabled); } + @NonNullByDefault({}) + public static void addMetadata(Item item, String namespace, String value) { + if (item == null) { + throw new IllegalArgumentException("item cannot be null"); + } + addMetadata(item.getName(), namespace, value, (String) null); + } + /** * - * @param namespace * @param itemName + * @param namespace * @param value * @throws IllegalArgumentException If either value is {@code null} or {@code namespace} or * {@code itemName} is invalid. @@ -203,19 +224,36 @@ public static void setRuleEnabled(String ruleUID, boolean enabled) { * @throws IllegalStateException If no ManagedProvider is available. */ @NonNullByDefault({}) - public static void addMetadata(String namespace, String itemName, String value) { - addMetadata(namespace, itemName, value, (String) null); + public static void addMetadata(String itemName, String namespace, String value) { + addMetadata(itemName, namespace, value, (String) null); + } + + @NonNullByDefault({}) + public static void addMetadata(Item item, String namespace, String value, Object... configuration) { + if (item == null) { + throw new IllegalArgumentException("item cannot be null"); + } + addMetadata(item.getName(), namespace, value, parseObjectArray(configuration)); } @NonNullByDefault({}) - public static void addMetadata(String namespace, String itemName, String value, Object... configuration) { - addMetadata(namespace, itemName, value, parseObjectArray(configuration)); + public static void addMetadata(String itemName, String namespace, String value, Object... configuration) { + addMetadata(itemName, namespace, value, parseObjectArray(configuration)); + } + + @NonNullByDefault({}) + public static void addMetadata(Item item, String namespace, String value, + @Nullable Map<@NonNull String, @NonNull Object> configuration) { + if (item == null) { + throw new IllegalArgumentException("item cannot be null"); + } + addMetadata(item.getName(), namespace, value, configuration); } /** * - * @param namespace * @param itemName + * @param namespace * @param value * @param configuration * @throws IllegalArgumentException If {@code namespace}, {@code itemName} or {@code value} is {@code null} or if @@ -225,14 +263,14 @@ public static void addMetadata(String namespace, String itemName, String value, * @throws IllegalStateException If no ManagedProvider is available. */ @NonNullByDefault({}) - public static void addMetadata(String namespace, String itemName, String value, + public static void addMetadata(String itemName, String namespace, String value, @Nullable Map<@NonNull String, @NonNull Object> configuration) { - if (namespace == null) { - throw new IllegalArgumentException("namespace cannot be null"); - } if (itemName == null) { throw new IllegalArgumentException("itemName cannot be null"); } + if (namespace == null) { + throw new IllegalArgumentException("namespace cannot be null"); + } if (value == null) { throw new IllegalArgumentException("value cannot be null"); } @@ -240,27 +278,51 @@ public static void addMetadata(String namespace, String itemName, String value, } @NonNullByDefault({}) - public static @Nullable Metadata getMetadata(String namespace, String itemName) { - if (namespace == null) { - throw new IllegalArgumentException("namespace cannot be null"); + public static @Nullable Metadata getMetadata(Item item, String namespace) { + if (item == null) { + throw new IllegalArgumentException("item cannot be null"); } + return getMetadata(item.getName(), namespace); + } + + @NonNullByDefault({}) + public static @Nullable Metadata getMetadata(String itemName, String namespace) { if (itemName == null) { throw new IllegalArgumentException("itemName cannot be null"); } + if (namespace == null) { + throw new IllegalArgumentException("namespace cannot be null"); + } return getMetadataRegistry().get(new MetadataKey(namespace, itemName)); } @NonNullByDefault({}) - public static @Nullable Metadata removeMetadata(String namespace, String itemName) { - if (namespace == null) { - throw new IllegalArgumentException("namespace cannot be null"); + public static @Nullable Metadata removeMetadata(Item item, String namespace) { + if (item == null) { + throw new IllegalArgumentException("item cannot be null"); } + return removeMetadata(item.getName(), namespace); + } + + @NonNullByDefault({}) + public static @Nullable Metadata removeMetadata(String itemName, String namespace) { if (itemName == null) { throw new IllegalArgumentException("itemName cannot be null"); } + if (namespace == null) { + throw new IllegalArgumentException("namespace cannot be null"); + } return getMetadataRegistry().remove(new MetadataKey(namespace, itemName)); } + @NonNullByDefault({}) + public static @Nullable Metadata updateMetadata(Item item, String namespace, String value) { + if (item == null) { + throw new IllegalArgumentException("item cannot be null"); + } + return updateMetadata(item.getName(), namespace, value); + } + /** * * @param namespace @@ -273,14 +335,35 @@ public static void addMetadata(String namespace, String itemName, String value, * @throws IllegalStateException If no ManagedProvider is available. */ @NonNullByDefault({}) - public static @Nullable Metadata updateMetadata(String namespace, String itemName, String value) { - return updateMetadata(namespace, itemName, value, null); + public static @Nullable Metadata updateMetadata(String itemName, String namespace, String value) { + return updateMetadata(itemName, namespace, value, (Map) null); + } + + @NonNullByDefault({}) + public static @Nullable Metadata updateMetadata(Item item, String namespace, String value, + Object... configuration) { + return updateMetadata(item.getName(), namespace, value, parseObjectArray(configuration)); + } + + @NonNullByDefault({}) + public static @Nullable Metadata updateMetadata(String itemName, String namespace, String value, + Object... configuration) { + return updateMetadata(itemName, namespace, value, parseObjectArray(configuration)); + } + + @NonNullByDefault({}) + public static @Nullable Metadata updateMetadata(Item item, String namespace, String value, + @Nullable Map<@NonNull String, @NonNull Object> configuration) { + if (item == null) { + throw new IllegalArgumentException("item cannot be null"); + } + return updateMetadata(item.getName(), namespace, value); } /** * - * @param namespace * @param itemName + * @param namespace * @param value * @param configuration * @throws IllegalArgumentException If {@code namespace}, {@code itemName} or {@code value} is {@code null} or if @@ -290,14 +373,14 @@ public static void addMetadata(String namespace, String itemName, String value, * @throws IllegalStateException If no ManagedProvider is available. */ @NonNullByDefault({}) - public static @Nullable Metadata updateMetadata(String namespace, String itemName, String value, + public static @Nullable Metadata updateMetadata(String itemName, String namespace, String value, @Nullable Map<@NonNull String, @NonNull Object> configuration) { - if (namespace == null) { - throw new IllegalArgumentException("namespace cannot be null"); - } if (itemName == null) { throw new IllegalArgumentException("itemName cannot be null"); } + if (namespace == null) { + throw new IllegalArgumentException("namespace cannot be null"); + } if (value == null) { throw new IllegalArgumentException("value cannot be null"); } From 252c1f242f9a5c2470d15845ee8414b286bab75a Mon Sep 17 00:00:00 2001 From: Ravi Nadahar Date: Sat, 11 Apr 2026 03:57:46 +0200 Subject: [PATCH 07/16] Fix socket leak Signed-off-by: Ravi Nadahar --- .../openhab/core/model/script/actions/Ping.java | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/bundles/org.openhab.core.model.script/src/org/openhab/core/model/script/actions/Ping.java b/bundles/org.openhab.core.model.script/src/org/openhab/core/model/script/actions/Ping.java index e252bf1c0fb..ed55132aa40 100644 --- a/bundles/org.openhab.core.model.script/src/org/openhab/core/model/script/actions/Ping.java +++ b/bundles/org.openhab.core.model.script/src/org/openhab/core/model/script/actions/Ping.java @@ -32,15 +32,15 @@ public class Ping { * is specified (which is the default when configuring just the host), a * regular ping is issued. If other ports are specified we try open a new * Socket with the given timeout. - * + * * @param host * @param port * @param timeout - * + * * @return true when host is reachable on port within the given * timeout and false in all other * cases. - * + * * @throws IOException * @throws SocketTimeoutException */ @@ -53,10 +53,10 @@ public static boolean checkVitality(String host, int port, int timeout) throws I } else { SocketAddress socketAddress = new InetSocketAddress(host, port); - Socket socket = new Socket(); - socket.connect(socketAddress, timeout); - success = true; - socket.close(); + try (Socket socket = new Socket()) { + socket.connect(socketAddress, timeout); + success = true; + } } } From 321ef8986981bd6876d93b93c8b11cea277afcc3 Mon Sep 17 00:00:00 2001 From: Ravi Nadahar Date: Sat, 11 Apr 2026 04:39:58 +0200 Subject: [PATCH 08/16] Reorganize classes and implement extensions Signed-off-by: Ravi Nadahar --- .../model/script/actions/ItemExtensions.java | 115 +++++ .../core/model/script/actions/Items.java | 179 +++++++ .../core/model/script/actions/Registries.java | 90 ++++ .../model/script/actions/RuleExtensions.java | 189 ++++++++ .../core/model/script/actions/Rules.java | 201 ++++++++ .../core/model/script/actions/System.java | 447 ------------------ .../ScriptImplicitlyImportedTypes.java | 13 +- 7 files changed, 784 insertions(+), 450 deletions(-) create mode 100644 bundles/org.openhab.core.model.script/src/org/openhab/core/model/script/actions/ItemExtensions.java create mode 100644 bundles/org.openhab.core.model.script/src/org/openhab/core/model/script/actions/Items.java create mode 100644 bundles/org.openhab.core.model.script/src/org/openhab/core/model/script/actions/Registries.java create mode 100644 bundles/org.openhab.core.model.script/src/org/openhab/core/model/script/actions/RuleExtensions.java create mode 100644 bundles/org.openhab.core.model.script/src/org/openhab/core/model/script/actions/Rules.java delete mode 100644 bundles/org.openhab.core.model.script/src/org/openhab/core/model/script/actions/System.java diff --git a/bundles/org.openhab.core.model.script/src/org/openhab/core/model/script/actions/ItemExtensions.java b/bundles/org.openhab.core.model.script/src/org/openhab/core/model/script/actions/ItemExtensions.java new file mode 100644 index 00000000000..390a26496b1 --- /dev/null +++ b/bundles/org.openhab.core.model.script/src/org/openhab/core/model/script/actions/ItemExtensions.java @@ -0,0 +1,115 @@ +/* + * Copyright (c) 2010-2026 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.core.model.script.actions; + +import java.util.LinkedHashMap; +import java.util.Map; + +import org.eclipse.jdt.annotation.NonNull; +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.openhab.core.items.Item; +import org.openhab.core.items.Metadata; + +/** + * {@link ItemExtensions} provides DSL access to things like OSGi instances, system registries and the ability to run + * other + * rules. + * + * @author Ravi Nadahar - Initial contribution + */ +@NonNullByDefault +public class ItemExtensions { + + @NonNullByDefault({}) + public static void addMetadata(Item item, String namespace, String value) { + if (item == null) { + throw new IllegalArgumentException("item cannot be null"); + } + Items.addMetadata(item.getName(), namespace, value, (String) null); + } + + @NonNullByDefault({}) + public static void addMetadata(Item item, String namespace, String value, Object... configuration) { + if (item == null) { + throw new IllegalArgumentException("item cannot be null"); + } + Items.addMetadata(item.getName(), namespace, value, parseObjectArray(configuration)); + } + + @NonNullByDefault({}) + public static void addMetadata(Item item, String namespace, String value, + @Nullable Map<@NonNull String, @NonNull Object> configuration) { + if (item == null) { + throw new IllegalArgumentException("item cannot be null"); + } + Items.addMetadata(item.getName(), namespace, value, configuration); + } + + @NonNullByDefault({}) + public static @Nullable Metadata getMetadata(Item item, String namespace) { + if (item == null) { + throw new IllegalArgumentException("item cannot be null"); + } + return Items.getMetadata(item.getName(), namespace); + } + + @NonNullByDefault({}) + public static @Nullable Metadata removeMetadata(Item item, String namespace) { + if (item == null) { + throw new IllegalArgumentException("item cannot be null"); + } + return Items.removeMetadata(item.getName(), namespace); + } + + @NonNullByDefault({}) + public static @Nullable Metadata updateMetadata(Item item, String namespace, String value) { + if (item == null) { + throw new IllegalArgumentException("item cannot be null"); + } + return Items.updateMetadata(item.getName(), namespace, value); + } + + @NonNullByDefault({}) + public static @Nullable Metadata updateMetadata(Item item, String namespace, String value, + Object... configuration) { + return Items.updateMetadata(item.getName(), namespace, value, parseObjectArray(configuration)); + } + + @NonNullByDefault({}) + public static @Nullable Metadata updateMetadata(Item item, String namespace, String value, + @Nullable Map<@NonNull String, @NonNull Object> configuration) { + if (item == null) { + throw new IllegalArgumentException("item cannot be null"); + } + return Items.updateMetadata(item.getName(), namespace, value); + } + + private static Map parseObjectArray(Object @Nullable [] objects) throws IllegalArgumentException { + if (objects == null || objects.length == 0) { + return Map.of(); + } + if ((objects.length % 2) != 0) { + throw new IllegalArgumentException("There must be an even number of objects (" + objects.length + ')'); + } + Map result = new LinkedHashMap<>(); + for (int i = 0; i < objects.length; i += 2) { + if (objects[i] instanceof String key) { + result.put(key, objects[i + 1]); + } else { + throw new IllegalArgumentException("Keys must be strings: " + objects[i]); + } + } + return result; + } +} diff --git a/bundles/org.openhab.core.model.script/src/org/openhab/core/model/script/actions/Items.java b/bundles/org.openhab.core.model.script/src/org/openhab/core/model/script/actions/Items.java new file mode 100644 index 00000000000..d54e2766e0d --- /dev/null +++ b/bundles/org.openhab.core.model.script/src/org/openhab/core/model/script/actions/Items.java @@ -0,0 +1,179 @@ +/* + * Copyright (c) 2010-2026 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.core.model.script.actions; + +import java.util.LinkedHashMap; +import java.util.Map; + +import org.eclipse.jdt.annotation.NonNull; +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.openhab.core.common.registry.ManagedProvider; +import org.openhab.core.items.Item; +import org.openhab.core.items.Metadata; +import org.openhab.core.items.MetadataKey; +import org.openhab.core.items.MetadataProvider; +import org.openhab.core.model.script.ScriptServiceUtil; + +/** + * {@link Items} provides DSL access to things like OSGi instances, system registries and the ability to run other + * rules. + * + * @author Ravi Nadahar - Initial contribution + */ +@NonNullByDefault +public class Items { + + public static @Nullable Item getItem(String itemName) { + return ScriptServiceUtil.getItemRegistry().get(itemName); + } + + /** + * + * @param itemName + * @param namespace + * @param value + * @throws IllegalArgumentException If either value is {@code null} or {@code namespace} or + * {@code itemName} is invalid. + * @throws UnsupportedOperationException If the metadata namespace has a reserved {@link MetadataProvider} that is + * not a {@link ManagedProvider}. + * @throws IllegalStateException If no ManagedProvider is available. + */ + @NonNullByDefault({}) + public static void addMetadata(String itemName, String namespace, String value) { + addMetadata(itemName, namespace, value, (String) null); + } + + @NonNullByDefault({}) + public static void addMetadata(String itemName, String namespace, String value, Object... configuration) { + addMetadata(itemName, namespace, value, parseObjectArray(configuration)); + } + + /** + * + * @param itemName + * @param namespace + * @param value + * @param configuration + * @throws IllegalArgumentException If {@code namespace}, {@code itemName} or {@code value} is {@code null} or if + * {@code namespace} or {@code itemName} is invalid. + * @throws UnsupportedOperationException If the metadata namespace has a reserved {@link MetadataProvider} that is + * not a {@link ManagedProvider}. + * @throws IllegalStateException If no ManagedProvider is available. + */ + @NonNullByDefault({}) + public static void addMetadata(String itemName, String namespace, String value, + @Nullable Map<@NonNull String, @NonNull Object> configuration) { + if (itemName == null) { + throw new IllegalArgumentException("itemName cannot be null"); + } + if (namespace == null) { + throw new IllegalArgumentException("namespace cannot be null"); + } + if (value == null) { + throw new IllegalArgumentException("value cannot be null"); + } + ScriptServiceUtil.getMetadataRegistry() + .add(new Metadata(new MetadataKey(namespace, itemName), value, configuration)); + } + + @NonNullByDefault({}) + public static @Nullable Metadata getMetadata(String itemName, String namespace) { + if (itemName == null) { + throw new IllegalArgumentException("itemName cannot be null"); + } + if (namespace == null) { + throw new IllegalArgumentException("namespace cannot be null"); + } + return ScriptServiceUtil.getMetadataRegistry().get(new MetadataKey(namespace, itemName)); + } + + @NonNullByDefault({}) + public static @Nullable Metadata removeMetadata(String itemName, String namespace) { + if (itemName == null) { + throw new IllegalArgumentException("itemName cannot be null"); + } + if (namespace == null) { + throw new IllegalArgumentException("namespace cannot be null"); + } + return ScriptServiceUtil.getMetadataRegistry().remove(new MetadataKey(namespace, itemName)); + } + + /** + * + * @param namespace + * @param itemName + * @param value + * @throws IllegalArgumentException If either value is {@code null} or {@code namespace} or + * {@code itemName} is invalid. + * @throws UnsupportedOperationException If the metadata namespace has a reserved {@link MetadataProvider} that is + * not a {@link ManagedProvider}. + * @throws IllegalStateException If no ManagedProvider is available. + */ + @NonNullByDefault({}) + public static @Nullable Metadata updateMetadata(String itemName, String namespace, String value) { + return updateMetadata(itemName, namespace, value, (Map) null); + } + + @NonNullByDefault({}) + public static @Nullable Metadata updateMetadata(String itemName, String namespace, String value, + Object... configuration) { + return updateMetadata(itemName, namespace, value, parseObjectArray(configuration)); + } + + /** + * + * @param itemName + * @param namespace + * @param value + * @param configuration + * @throws IllegalArgumentException If {@code namespace}, {@code itemName} or {@code value} is {@code null} or if + * {@code namespace} or {@code itemName} is invalid. + * @throws UnsupportedOperationException If the metadata namespace has a reserved {@link MetadataProvider} that is + * not a {@link ManagedProvider}. + * @throws IllegalStateException If no ManagedProvider is available. + */ + @NonNullByDefault({}) + public static @Nullable Metadata updateMetadata(String itemName, String namespace, String value, + @Nullable Map<@NonNull String, @NonNull Object> configuration) { + if (itemName == null) { + throw new IllegalArgumentException("itemName cannot be null"); + } + if (namespace == null) { + throw new IllegalArgumentException("namespace cannot be null"); + } + if (value == null) { + throw new IllegalArgumentException("value cannot be null"); + } + return ScriptServiceUtil.getMetadataRegistry() + .update(new Metadata(new MetadataKey(namespace, itemName), value, configuration)); + } + + private static Map parseObjectArray(Object @Nullable [] objects) throws IllegalArgumentException { + if (objects == null || objects.length == 0) { + return Map.of(); + } + if ((objects.length % 2) != 0) { + throw new IllegalArgumentException("There must be an even number of objects (" + objects.length + ')'); + } + Map result = new LinkedHashMap<>(); + for (int i = 0; i < objects.length; i += 2) { + if (objects[i] instanceof String key) { + result.put(key, objects[i + 1]); + } else { + throw new IllegalArgumentException("Keys must be strings: " + objects[i]); + } + } + return result; + } +} diff --git a/bundles/org.openhab.core.model.script/src/org/openhab/core/model/script/actions/Registries.java b/bundles/org.openhab.core.model.script/src/org/openhab/core/model/script/actions/Registries.java new file mode 100644 index 00000000000..7a7e53c1f2e --- /dev/null +++ b/bundles/org.openhab.core.model.script/src/org/openhab/core/model/script/actions/Registries.java @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2010-2026 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.core.model.script.actions; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.openhab.core.automation.RuleRegistry; +import org.openhab.core.items.ItemRegistry; +import org.openhab.core.items.MetadataRegistry; +import org.openhab.core.model.script.ScriptServiceUtil; +import org.openhab.core.model.script.engine.action.ActionDoc; +import org.openhab.core.thing.ThingRegistry; +import org.osgi.framework.Bundle; +import org.osgi.framework.BundleContext; +import org.osgi.framework.FrameworkUtil; +import org.osgi.framework.ServiceReference; + +/** + * {@link Registries} provides DSL access to things like OSGi instances, system registries and the ability to run other + * rules. + * + * @author Ravi Nadahar - Initial contribution + */ +@NonNullByDefault +public class Registries { + + /** + * Retrieve an OSGi instance of the specified {@link Class}, if it exists. + * + * @param the class type. + * @param clazz the class of the instance to get. + * @return The instance or {@code null} if the instance wasn't found. + */ + @ActionDoc(text = "acquire an OSGi instance") + public static @Nullable T getInstance(Class clazz) { + Bundle bundle = FrameworkUtil.getBundle(clazz); + if (bundle != null) { + BundleContext bc = bundle.getBundleContext(); + if (bc != null) { + ServiceReference ref = bc.getServiceReference(clazz); + if (ref != null) { + return bc.getService(ref); + } + } + } + return null; + } + + /** + * @return The {@link ThingRegistry}. + */ + @ActionDoc(text = "get the thing registry") + public static ThingRegistry getThingRegistry() { + return ScriptServiceUtil.getThingRegistry(); + } + + /** + * @return The {@link MetadataRegistry}. + */ + @ActionDoc(text = "get the metadata registry") + public static MetadataRegistry getMetadataRegistry() { + return ScriptServiceUtil.getMetadataRegistry(); + } + + /** + * @return The {@link ItemRegistry}. + */ + @ActionDoc(text = "get the item registry") + public static ItemRegistry getItemRegistry() { + return ScriptServiceUtil.getItemRegistry(); + } + + /** + * @return The {@link RuleRegistry}. + */ + @ActionDoc(text = "get the rule registry") + public static RuleRegistry getRuleRegistry() { + return ScriptServiceUtil.getRuleRegistry(); + } +} diff --git a/bundles/org.openhab.core.model.script/src/org/openhab/core/model/script/actions/RuleExtensions.java b/bundles/org.openhab.core.model.script/src/org/openhab/core/model/script/actions/RuleExtensions.java new file mode 100644 index 00000000000..d0efe331b83 --- /dev/null +++ b/bundles/org.openhab.core.model.script/src/org/openhab/core/model/script/actions/RuleExtensions.java @@ -0,0 +1,189 @@ +/* + * Copyright (c) 2010-2026 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.core.model.script.actions; + +import java.util.LinkedHashMap; +import java.util.Map; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.openhab.core.automation.Rule; +import org.openhab.core.automation.RuleManager; +import org.openhab.core.model.script.ScriptServiceUtil; + +/** + * {@link RuleExtensions} provides DSL access to things like OSGi instances, system registries and the ability to run + * other + * rules. + * + * @author Ravi Nadahar - Initial contribution + */ +@NonNullByDefault +public class RuleExtensions { + + /** + * Run the rule with the specified UID. + * + * @param ruleUID the UID of the rule to run. + * @return A copy of the rule context, including possible return values. + * @throws IllegalArgumentException If a rule with the specified UID doesn't exist. + * @throws IllegalStateException If no {@link RuleManager} instance exists. + */ + public static Map run(Rule rule) { + RuleManager ruleManager = ScriptServiceUtil.getRuleManager(); + if (ruleManager == null) { + throw new IllegalStateException("RuleManager doesn't exist"); + } + String ruleUID = rule.getUID(); + if (ruleManager.getStatus(ruleUID) == null) { + throw new IllegalArgumentException("Rule with UID '" + ruleUID + "' doesn't exist"); + } + return ruleManager.runNow(ruleUID); + } + + public static Map run(Rule rule, boolean considerConditions) { + RuleManager ruleManager = ScriptServiceUtil.getRuleManager(); + if (ruleManager == null) { + throw new IllegalStateException("RuleManager doesn't exist"); + } + String ruleUID = rule.getUID(); + if (ruleManager.getStatus(ruleUID) == null) { + throw new IllegalArgumentException("Rule with UID '" + ruleUID + "' doesn't exist"); + } + return ruleManager.runNow(ruleUID, considerConditions, null); + } + + /** + * Run the rule with the specified UID with the specified context. + * + * @param ruleUID the UID of the rule to run. + * @param context the {@link Map} of {@link String} and {@link Object} pairs that constitutes the context. + * @return A copy of the rule context, including possible return values. + * @throws IllegalArgumentException If a rule with the specified UID doesn't exist. + * @throws IllegalStateException If no {@link RuleManager} instance exists. + */ + public static Map run(Rule rule, Map context) { + RuleManager ruleManager = ScriptServiceUtil.getRuleManager(); + if (ruleManager == null) { + throw new IllegalStateException("RuleManager doesn't exist"); + } + String ruleUID = rule.getUID(); + if (ruleManager.getStatus(ruleUID) == null) { + throw new IllegalArgumentException("Rule with UID '" + ruleUID + "' doesn't exist"); + } + return ruleManager.runNow(ruleUID, false, context); + } + + /** + * Run the rule with the specified UID with the specified context, while optionally taking conditions into + * account. + * + * @param ruleUID the UID of the rule to run. + * @param considerConditions {@code true} to not run the rule if its conditions don't qualify. + * @param context the pairs of {@link String}s and {@link Object}s that constitutes the context. Must be in pairs, + * the first is the key, the second is the value. + * @return A copy of the rule context, including possible return values. + * @throws IllegalArgumentException If a rule with the specified UID doesn't exist. + * @throws IllegalStateException If no {@link RuleManager} instance exists. + */ + public static Map run(Rule rule, boolean considerConditions, Object... context) { + RuleManager ruleManager = ScriptServiceUtil.getRuleManager(); + if (ruleManager == null) { + throw new IllegalStateException("RuleManager doesn't exist"); + } + String ruleUID = rule.getUID(); + if (ruleManager.getStatus(ruleUID) == null) { + throw new IllegalArgumentException("Rule with UID '" + ruleUID + "' doesn't exist"); + } + return ruleManager.runNow(ruleUID, considerConditions, parseObjectArray(context)); + } + + /** + * Run the rule with the specified UID with the specified context, while optionally taking conditions into + * account. + * + * @param ruleUID the UID of the rule to run. + * @param considerConditions {@code true} to not run the rule if its conditions don't qualify. + * @param context the {@link Map} of {@link String} and {@link Object} pairs that constitutes the context. + * @return A copy of the rule context, including possible return values. + * @throws IllegalArgumentException If a rule with the specified UID doesn't exist. + * @throws IllegalStateException If no {@link RuleManager} instance exists. + */ + public static Map run(Rule rule, boolean considerConditions, + @Nullable Map context) { + RuleManager ruleManager = ScriptServiceUtil.getRuleManager(); + if (ruleManager == null) { + throw new IllegalStateException("RuleManager doesn't exist"); + } + String ruleUID = rule.getUID(); + if (ruleManager.getStatus(ruleUID) == null) { + throw new IllegalArgumentException("Rule with UID '" + ruleUID + "' doesn't exist"); + } + return ruleManager.runNow(ruleUID, considerConditions, context); + } + + /** + * Check whether the specified rule is enabled. + * + * @param ruleUID the UID of the rule to check. + * @return {@code true} if the rule is enabled, {@code false} otherwise. + * @throws IllegalArgumentException If a rule with the specified UID doesn't exist. + * @throws IllegalStateException If no {@link RuleManager} instance exists. + */ + public static boolean isEnabled(Rule rule) { + RuleManager ruleManager = ScriptServiceUtil.getRuleManager(); + if (ruleManager == null) { + throw new IllegalStateException("RuleManager doesn't exist"); + } + String ruleUID = rule.getUID(); + Boolean result = ruleManager.isEnabled(ruleUID); + if (result == null) { + throw new IllegalArgumentException("Rule with UID '" + ruleUID + "' doesn't exist"); + } + return result.booleanValue(); + } + + /** + * Set whether the specified rule is enabled. + * + * @param ruleUID the UID of the rule to enable or disable. + * @param enabled {@code true} to enable the rule, {@code false} to disable the rule. + * @throws IllegalArgumentException If a rule with the specified UID doesn't exist. + * @throws IllegalStateException If no {@link RuleManager} instance exists. + */ + public static void setEnabled(Rule rule, boolean enabled) { + RuleManager ruleManager = ScriptServiceUtil.getRuleManager(); + if (ruleManager == null) { + throw new IllegalStateException("RuleManager doesn't exist"); + } + ruleManager.setEnabled(rule.getUID(), enabled); + } + + private static Map parseObjectArray(Object @Nullable [] objects) throws IllegalArgumentException { + if (objects == null || objects.length == 0) { + return Map.of(); + } + if ((objects.length % 2) != 0) { + throw new IllegalArgumentException("There must be an even number of objects (" + objects.length + ')'); + } + Map result = new LinkedHashMap<>(); + for (int i = 0; i < objects.length; i += 2) { + if (objects[i] instanceof String key) { + result.put(key, objects[i + 1]); + } else { + throw new IllegalArgumentException("Keys must be strings: " + objects[i]); + } + } + return result; + } +} diff --git a/bundles/org.openhab.core.model.script/src/org/openhab/core/model/script/actions/Rules.java b/bundles/org.openhab.core.model.script/src/org/openhab/core/model/script/actions/Rules.java new file mode 100644 index 00000000000..4d98c70f2a0 --- /dev/null +++ b/bundles/org.openhab.core.model.script/src/org/openhab/core/model/script/actions/Rules.java @@ -0,0 +1,201 @@ +/* + * Copyright (c) 2010-2026 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.core.model.script.actions; + +import java.util.LinkedHashMap; +import java.util.Map; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.openhab.core.automation.Rule; +import org.openhab.core.automation.RuleManager; +import org.openhab.core.model.script.ScriptServiceUtil; +import org.openhab.core.model.script.engine.action.ActionDoc; + +/** + * {@link Rules} provides DSL access to things like OSGi instances, system registries and the ability to run other + * rules. + * + * @author Ravi Nadahar - Initial contribution + */ +@NonNullByDefault +public class Rules { + + public static @Nullable Rule getRule(String ruleUID) { + return ScriptServiceUtil.getRuleRegistry().get(ruleUID); + } + + /** + * Run the rule with the specified UID. + * + * @param ruleUID the UID of the rule to run. + * @return A copy of the rule context, including possible return values. + * @throws IllegalArgumentException If a rule with the specified UID doesn't exist. + * @throws IllegalStateException If no {@link RuleManager} instance exists. + */ + @ActionDoc(text = "run the rule with the specified UID") + public static Map runRule(String ruleUID) { + RuleManager ruleManager = ScriptServiceUtil.getRuleManager(); + if (ruleManager == null) { + throw new IllegalStateException("RuleManager doesn't exist"); + } + if (ruleManager.getStatus(ruleUID) == null) { + throw new IllegalArgumentException("Rule with UID '" + ruleUID + "' doesn't exist"); + } + return ruleManager.runNow(ruleUID); + } + + @ActionDoc(text = "run the rule with the specified UID and condition evaluation setting") + public static Map runRule(String ruleUID, boolean considerConditions) { + RuleManager ruleManager = ScriptServiceUtil.getRuleManager(); + if (ruleManager == null) { + throw new IllegalStateException("RuleManager doesn't exist"); + } + if (ruleManager.getStatus(ruleUID) == null) { + throw new IllegalArgumentException("Rule with UID '" + ruleUID + "' doesn't exist"); + } + return ruleManager.runNow(ruleUID, considerConditions, null); + } + + /** + * Run the rule with the specified UID with the specified context. + * + * @param ruleUID the UID of the rule to run. + * @param context the {@link Map} of {@link String} and {@link Object} pairs that constitutes the context. + * @return A copy of the rule context, including possible return values. + * @throws IllegalArgumentException If a rule with the specified UID doesn't exist. + * @throws IllegalStateException If no {@link RuleManager} instance exists. + */ + @ActionDoc(text = "run the rule with the specified UID and context") + public static Map runRule(String ruleUID, Map context) { + RuleManager ruleManager = ScriptServiceUtil.getRuleManager(); + if (ruleManager == null) { + throw new IllegalStateException("RuleManager doesn't exist"); + } + if (ruleManager.getStatus(ruleUID) == null) { + throw new IllegalArgumentException("Rule with UID '" + ruleUID + "' doesn't exist"); + } + return ruleManager.runNow(ruleUID, false, context); + } + + /** + * Run the rule with the specified UID with the specified context, while optionally taking conditions into + * account. + * + * @param ruleUID the UID of the rule to run. + * @param considerConditions {@code true} to not run the rule if its conditions don't qualify. + * @param context the pairs of {@link String}s and {@link Object}s that constitutes the context. Must be in pairs, + * the first is the key, the second is the value. + * @return A copy of the rule context, including possible return values. + * @throws IllegalArgumentException If a rule with the specified UID doesn't exist. + * @throws IllegalStateException If no {@link RuleManager} instance exists. + */ + @ActionDoc(text = "run the rule with the specified UID, condition evaluation setting and context") + public static Map runRule(String ruleUID, boolean considerConditions, Object... context) { + RuleManager ruleManager = ScriptServiceUtil.getRuleManager(); + if (ruleManager == null) { + throw new IllegalStateException("RuleManager doesn't exist"); + } + if (ruleManager.getStatus(ruleUID) == null) { + throw new IllegalArgumentException("Rule with UID '" + ruleUID + "' doesn't exist"); + } + return ruleManager.runNow(ruleUID, considerConditions, parseObjectArray(context)); + } + + /** + * Run the rule with the specified UID with the specified context, while optionally taking conditions into + * account. + * + * @param ruleUID the UID of the rule to run. + * @param considerConditions {@code true} to not run the rule if its conditions don't qualify. + * @param context the {@link Map} of {@link String} and {@link Object} pairs that constitutes the context. + * @return A copy of the rule context, including possible return values. + * @throws IllegalArgumentException If a rule with the specified UID doesn't exist. + * @throws IllegalStateException If no {@link RuleManager} instance exists. + */ + @ActionDoc(text = "run the rule with the specified UID, condition evaluation setting and context") + public static Map runRule(String ruleUID, boolean considerConditions, Map context) { + RuleManager ruleManager = ScriptServiceUtil.getRuleManager(); + if (ruleManager == null) { + throw new IllegalStateException("RuleManager doesn't exist"); + } + if (ruleManager.getStatus(ruleUID) == null) { + throw new IllegalArgumentException("Rule with UID '" + ruleUID + "' doesn't exist"); + } + return ruleManager.runNow(ruleUID, considerConditions, context); + } + + /** + * Check whether the specified rule is enabled. + * + * @param ruleUID the UID of the rule to check. + * @return {@code true} if the rule is enabled, {@code false} otherwise. + * @throws IllegalArgumentException If a rule with the specified UID doesn't exist. + * @throws IllegalStateException If no {@link RuleManager} instance exists. + */ + @ActionDoc(text = "check whether the specified rule rule is enabled") + public static boolean isRuleEnabled(String ruleUID) { + RuleManager ruleManager = ScriptServiceUtil.getRuleManager(); + if (ruleManager == null) { + throw new IllegalStateException("RuleManager doesn't exist"); + } + Boolean result = ruleManager.isEnabled(ruleUID); + if (result == null) { + throw new IllegalArgumentException("Rule with UID '" + ruleUID + "' doesn't exist"); + } + return result.booleanValue(); + } + + /** + * Set whether the specified rule is enabled. + * + * @param ruleUID the UID of the rule to enable or disable. + * @param enabled {@code true} to enable the rule, {@code false} to disable the rule. + * @throws IllegalArgumentException If a rule with the specified UID doesn't exist. + * @throws IllegalStateException If no {@link RuleManager} instance exists. + */ + @ActionDoc(text = "set whether the specified rule is enabled") + public static void setRuleEnabled(String ruleUID, boolean enabled) { + RuleManager ruleManager = ScriptServiceUtil.getRuleManager(); + if (ruleManager == null) { + throw new IllegalStateException("RuleManager doesn't exist"); + } + ruleManager.setEnabled(ruleUID, enabled); + } + + /** + * @return The {@link RuleManager} or {@code null}. + */ + @ActionDoc(text = "get the rule manager") + public static @Nullable RuleManager getRuleManager() { + return ScriptServiceUtil.getRuleManager(); + } + + private static Map parseObjectArray(Object @Nullable [] objects) throws IllegalArgumentException { + if (objects == null || objects.length == 0) { + return Map.of(); + } + if ((objects.length % 2) != 0) { + throw new IllegalArgumentException("There must be an even number of objects (" + objects.length + ')'); + } + Map result = new LinkedHashMap<>(); + for (int i = 0; i < objects.length; i += 2) { + if (objects[i] instanceof String key) { + result.put(key, objects[i + 1]); + } else { + throw new IllegalArgumentException("Keys must be strings: " + objects[i]); + } + } + return result; + } +} diff --git a/bundles/org.openhab.core.model.script/src/org/openhab/core/model/script/actions/System.java b/bundles/org.openhab.core.model.script/src/org/openhab/core/model/script/actions/System.java deleted file mode 100644 index 95c66979407..00000000000 --- a/bundles/org.openhab.core.model.script/src/org/openhab/core/model/script/actions/System.java +++ /dev/null @@ -1,447 +0,0 @@ -/* - * Copyright (c) 2010-2026 Contributors to the openHAB project - * - * See the NOTICE file(s) distributed with this work for additional - * information. - * - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License 2.0 which is available at - * http://www.eclipse.org/legal/epl-2.0 - * - * SPDX-License-Identifier: EPL-2.0 - */ -package org.openhab.core.model.script.actions; - -import java.util.LinkedHashMap; -import java.util.Map; - -import org.eclipse.jdt.annotation.NonNull; -import org.eclipse.jdt.annotation.NonNullByDefault; -import org.eclipse.jdt.annotation.Nullable; -import org.openhab.core.automation.RuleManager; -import org.openhab.core.automation.RuleRegistry; -import org.openhab.core.common.registry.ManagedProvider; -import org.openhab.core.items.Item; -import org.openhab.core.items.ItemRegistry; -import org.openhab.core.items.Metadata; -import org.openhab.core.items.MetadataKey; -import org.openhab.core.items.MetadataProvider; -import org.openhab.core.items.MetadataRegistry; -import org.openhab.core.model.script.ScriptServiceUtil; -import org.openhab.core.model.script.engine.action.ActionDoc; -import org.openhab.core.thing.ThingRegistry; -import org.osgi.framework.Bundle; -import org.osgi.framework.BundleContext; -import org.osgi.framework.FrameworkUtil; -import org.osgi.framework.ServiceReference; - -/** - * {@link System} provides DSL access to things like OSGi instances, system registries and the ability to run other - * rules. - * - * @author Ravi Nadahar - Initial contribution - */ -@NonNullByDefault -public class System { - - /** - * Retrieve an OSGi instance of the specified {@link Class}, if it exists. - * - * @param the class type. - * @param clazz the class of the instance to get. - * @return The instance or {@code null} if the instance wasn't found. - */ - @ActionDoc(text = "acquire an OSGi instance") - public static @Nullable T getInstance(Class clazz) { - Bundle bundle = FrameworkUtil.getBundle(clazz); - if (bundle != null) { - BundleContext bc = bundle.getBundleContext(); - if (bc != null) { - ServiceReference ref = bc.getServiceReference(clazz); - if (ref != null) { - return bc.getService(ref); - } - } - } - return null; - } - - /** - * Run the rule with the specified UID. - * - * @param ruleUID the UID of the rule to run. - * @return A copy of the rule context, including possible return values. - * @throws IllegalArgumentException If a rule with the specified UID doesn't exist. - * @throws IllegalStateException If no {@link RuleManager} instance exists. - */ - @ActionDoc(text = "run the rule with the specified UID") - public static Map runRule(String ruleUID) { - RuleManager ruleManager = ScriptServiceUtil.getRuleManager(); - if (ruleManager == null) { - throw new IllegalStateException("RuleManager doesn't exist"); - } - if (ruleManager.getStatus(ruleUID) == null) { - throw new IllegalArgumentException("Rule with UID '" + ruleUID + "' doesn't exist"); - } - return ruleManager.runNow(ruleUID); - } - - @ActionDoc(text = "run the rule with the specified UID and condition evaluation setting") - public static Map runRule(String ruleUID, boolean considerConditions) { - RuleManager ruleManager = ScriptServiceUtil.getRuleManager(); - if (ruleManager == null) { - throw new IllegalStateException("RuleManager doesn't exist"); - } - if (ruleManager.getStatus(ruleUID) == null) { - throw new IllegalArgumentException("Rule with UID '" + ruleUID + "' doesn't exist"); - } - return ruleManager.runNow(ruleUID, considerConditions, null); - } - - /** - * Run the rule with the specified UID with the specified context. - * - * @param ruleUID the UID of the rule to run. - * @param context the {@link Map} of {@link String} and {@link Object} pairs that constitutes the context. - * @return A copy of the rule context, including possible return values. - * @throws IllegalArgumentException If a rule with the specified UID doesn't exist. - * @throws IllegalStateException If no {@link RuleManager} instance exists. - */ - @ActionDoc(text = "run the rule with the specified UID and context") - public static Map runRule(String ruleUID, Map context) { - RuleManager ruleManager = ScriptServiceUtil.getRuleManager(); - if (ruleManager == null) { - throw new IllegalStateException("RuleManager doesn't exist"); - } - if (ruleManager.getStatus(ruleUID) == null) { - throw new IllegalArgumentException("Rule with UID '" + ruleUID + "' doesn't exist"); - } - return ruleManager.runNow(ruleUID, false, context); - } - - /** - * Run the rule with the specified UID with the specified context, while optionally taking conditions into - * account. - * - * @param ruleUID the UID of the rule to run. - * @param considerConditions {@code true} to not run the rule if its conditions don't qualify. - * @param context the pairs of {@link String}s and {@link Object}s that constitutes the context. Must be in pairs, - * the first is the key, the second is the value. - * @return A copy of the rule context, including possible return values. - * @throws IllegalArgumentException If a rule with the specified UID doesn't exist. - * @throws IllegalStateException If no {@link RuleManager} instance exists. - */ - @ActionDoc(text = "run the rule with the specified UID, condition evaluation setting and context") - public static Map runRule(String ruleUID, boolean considerConditions, Object... context) { - RuleManager ruleManager = ScriptServiceUtil.getRuleManager(); - if (ruleManager == null) { - throw new IllegalStateException("RuleManager doesn't exist"); - } - if (ruleManager.getStatus(ruleUID) == null) { - throw new IllegalArgumentException("Rule with UID '" + ruleUID + "' doesn't exist"); - } - return ruleManager.runNow(ruleUID, considerConditions, parseObjectArray(context)); - } - - /** - * Run the rule with the specified UID with the specified context, while optionally taking conditions into - * account. - * - * @param ruleUID the UID of the rule to run. - * @param considerConditions {@code true} to not run the rule if its conditions don't qualify. - * @param context the {@link Map} of {@link String} and {@link Object} pairs that constitutes the context. - * @return A copy of the rule context, including possible return values. - * @throws IllegalArgumentException If a rule with the specified UID doesn't exist. - * @throws IllegalStateException If no {@link RuleManager} instance exists. - */ - @ActionDoc(text = "run the rule with the specified UID, condition evaluation setting and context") - public static Map runRule(String ruleUID, boolean considerConditions, Map context) { - RuleManager ruleManager = ScriptServiceUtil.getRuleManager(); - if (ruleManager == null) { - throw new IllegalStateException("RuleManager doesn't exist"); - } - if (ruleManager.getStatus(ruleUID) == null) { - throw new IllegalArgumentException("Rule with UID '" + ruleUID + "' doesn't exist"); - } - return ruleManager.runNow(ruleUID, considerConditions, context); - } - - /** - * Check whether the specified rule is enabled. - * - * @param ruleUID the UID of the rule to check. - * @return {@code true} if the rule is enabled, {@code false} otherwise. - * @throws IllegalArgumentException If a rule with the specified UID doesn't exist. - * @throws IllegalStateException If no {@link RuleManager} instance exists. - */ - @ActionDoc(text = "check whether the specified rule rule is enabled") - public static boolean isRuleEnabled(String ruleUID) { - RuleManager ruleManager = ScriptServiceUtil.getRuleManager(); - if (ruleManager == null) { - throw new IllegalStateException("RuleManager doesn't exist"); - } - Boolean result = ruleManager.isEnabled(ruleUID); - if (result == null) { - throw new IllegalArgumentException("Rule with UID '" + ruleUID + "' doesn't exist"); - } - return result.booleanValue(); - } - - /** - * Set whether the specified rule is enabled. - * - * @param ruleUID the UID of the rule to enable or disable. - * @param enabled {@code true} to enable the rule, {@code false} to disable the rule. - * @throws IllegalArgumentException If a rule with the specified UID doesn't exist. - * @throws IllegalStateException If no {@link RuleManager} instance exists. - */ - @ActionDoc(text = "set whether the specified rule is enabled") - public static void setRuleEnabled(String ruleUID, boolean enabled) { - RuleManager ruleManager = ScriptServiceUtil.getRuleManager(); - if (ruleManager == null) { - throw new IllegalStateException("RuleManager doesn't exist"); - } - ruleManager.setEnabled(ruleUID, enabled); - } - - @NonNullByDefault({}) - public static void addMetadata(Item item, String namespace, String value) { - if (item == null) { - throw new IllegalArgumentException("item cannot be null"); - } - addMetadata(item.getName(), namespace, value, (String) null); - } - - /** - * - * @param itemName - * @param namespace - * @param value - * @throws IllegalArgumentException If either value is {@code null} or {@code namespace} or - * {@code itemName} is invalid. - * @throws UnsupportedOperationException If the metadata namespace has a reserved {@link MetadataProvider} that is - * not a {@link ManagedProvider}. - * @throws IllegalStateException If no ManagedProvider is available. - */ - @NonNullByDefault({}) - public static void addMetadata(String itemName, String namespace, String value) { - addMetadata(itemName, namespace, value, (String) null); - } - - @NonNullByDefault({}) - public static void addMetadata(Item item, String namespace, String value, Object... configuration) { - if (item == null) { - throw new IllegalArgumentException("item cannot be null"); - } - addMetadata(item.getName(), namespace, value, parseObjectArray(configuration)); - } - - @NonNullByDefault({}) - public static void addMetadata(String itemName, String namespace, String value, Object... configuration) { - addMetadata(itemName, namespace, value, parseObjectArray(configuration)); - } - - @NonNullByDefault({}) - public static void addMetadata(Item item, String namespace, String value, - @Nullable Map<@NonNull String, @NonNull Object> configuration) { - if (item == null) { - throw new IllegalArgumentException("item cannot be null"); - } - addMetadata(item.getName(), namespace, value, configuration); - } - - /** - * - * @param itemName - * @param namespace - * @param value - * @param configuration - * @throws IllegalArgumentException If {@code namespace}, {@code itemName} or {@code value} is {@code null} or if - * {@code namespace} or {@code itemName} is invalid. - * @throws UnsupportedOperationException If the metadata namespace has a reserved {@link MetadataProvider} that is - * not a {@link ManagedProvider}. - * @throws IllegalStateException If no ManagedProvider is available. - */ - @NonNullByDefault({}) - public static void addMetadata(String itemName, String namespace, String value, - @Nullable Map<@NonNull String, @NonNull Object> configuration) { - if (itemName == null) { - throw new IllegalArgumentException("itemName cannot be null"); - } - if (namespace == null) { - throw new IllegalArgumentException("namespace cannot be null"); - } - if (value == null) { - throw new IllegalArgumentException("value cannot be null"); - } - getMetadataRegistry().add(new Metadata(new MetadataKey(namespace, itemName), value, configuration)); - } - - @NonNullByDefault({}) - public static @Nullable Metadata getMetadata(Item item, String namespace) { - if (item == null) { - throw new IllegalArgumentException("item cannot be null"); - } - return getMetadata(item.getName(), namespace); - } - - @NonNullByDefault({}) - public static @Nullable Metadata getMetadata(String itemName, String namespace) { - if (itemName == null) { - throw new IllegalArgumentException("itemName cannot be null"); - } - if (namespace == null) { - throw new IllegalArgumentException("namespace cannot be null"); - } - return getMetadataRegistry().get(new MetadataKey(namespace, itemName)); - } - - @NonNullByDefault({}) - public static @Nullable Metadata removeMetadata(Item item, String namespace) { - if (item == null) { - throw new IllegalArgumentException("item cannot be null"); - } - return removeMetadata(item.getName(), namespace); - } - - @NonNullByDefault({}) - public static @Nullable Metadata removeMetadata(String itemName, String namespace) { - if (itemName == null) { - throw new IllegalArgumentException("itemName cannot be null"); - } - if (namespace == null) { - throw new IllegalArgumentException("namespace cannot be null"); - } - return getMetadataRegistry().remove(new MetadataKey(namespace, itemName)); - } - - @NonNullByDefault({}) - public static @Nullable Metadata updateMetadata(Item item, String namespace, String value) { - if (item == null) { - throw new IllegalArgumentException("item cannot be null"); - } - return updateMetadata(item.getName(), namespace, value); - } - - /** - * - * @param namespace - * @param itemName - * @param value - * @throws IllegalArgumentException If either value is {@code null} or {@code namespace} or - * {@code itemName} is invalid. - * @throws UnsupportedOperationException If the metadata namespace has a reserved {@link MetadataProvider} that is - * not a {@link ManagedProvider}. - * @throws IllegalStateException If no ManagedProvider is available. - */ - @NonNullByDefault({}) - public static @Nullable Metadata updateMetadata(String itemName, String namespace, String value) { - return updateMetadata(itemName, namespace, value, (Map) null); - } - - @NonNullByDefault({}) - public static @Nullable Metadata updateMetadata(Item item, String namespace, String value, - Object... configuration) { - return updateMetadata(item.getName(), namespace, value, parseObjectArray(configuration)); - } - - @NonNullByDefault({}) - public static @Nullable Metadata updateMetadata(String itemName, String namespace, String value, - Object... configuration) { - return updateMetadata(itemName, namespace, value, parseObjectArray(configuration)); - } - - @NonNullByDefault({}) - public static @Nullable Metadata updateMetadata(Item item, String namespace, String value, - @Nullable Map<@NonNull String, @NonNull Object> configuration) { - if (item == null) { - throw new IllegalArgumentException("item cannot be null"); - } - return updateMetadata(item.getName(), namespace, value); - } - - /** - * - * @param itemName - * @param namespace - * @param value - * @param configuration - * @throws IllegalArgumentException If {@code namespace}, {@code itemName} or {@code value} is {@code null} or if - * {@code namespace} or {@code itemName} is invalid. - * @throws UnsupportedOperationException If the metadata namespace has a reserved {@link MetadataProvider} that is - * not a {@link ManagedProvider}. - * @throws IllegalStateException If no ManagedProvider is available. - */ - @NonNullByDefault({}) - public static @Nullable Metadata updateMetadata(String itemName, String namespace, String value, - @Nullable Map<@NonNull String, @NonNull Object> configuration) { - if (itemName == null) { - throw new IllegalArgumentException("itemName cannot be null"); - } - if (namespace == null) { - throw new IllegalArgumentException("namespace cannot be null"); - } - if (value == null) { - throw new IllegalArgumentException("value cannot be null"); - } - return getMetadataRegistry().update(new Metadata(new MetadataKey(namespace, itemName), value, configuration)); - } - - /** - * @return The {@link ThingRegistry}. - */ - @ActionDoc(text = "get the thing registry") - public static ThingRegistry getThingRegistry() { - return ScriptServiceUtil.getThingRegistry(); - } - - /** - * @return The {@link MetadataRegistry}. - */ - @ActionDoc(text = "get the metadata registry") - public static MetadataRegistry getMetadataRegistry() { - return ScriptServiceUtil.getMetadataRegistry(); - } - - /** - * @return The {@link ItemRegistry}. - */ - @ActionDoc(text = "get the item registry") - public static ItemRegistry getItemRegistry() { - return ScriptServiceUtil.getItemRegistry(); - } - - /** - * @return The {@link RuleRegistry}. - */ - @ActionDoc(text = "get the rule registry") - public static RuleRegistry getRuleRegistry() { - return ScriptServiceUtil.getRuleRegistry(); - } - - /** - * @return The {@link RuleManager} or {@code null}. - */ - @ActionDoc(text = "get the rule manager") - public static @Nullable RuleManager getRuleManager() { - return ScriptServiceUtil.getRuleManager(); - } - - private static Map parseObjectArray(Object @Nullable [] objects) throws IllegalArgumentException { - if (objects == null || objects.length == 0) { - return Map.of(); - } - if ((objects.length % 2) != 0) { - throw new IllegalArgumentException("There must be an even number of objects (" + objects.length + ')'); - } - Map result = new LinkedHashMap<>(); - for (int i = 0; i < objects.length; i += 2) { - if (objects[i] instanceof String key) { - result.put(key, objects[i + 1]); - } else { - throw new IllegalArgumentException("Keys must be strings: " + objects[i]); - } - } - return result; - } -} diff --git a/bundles/org.openhab.core.model.script/src/org/openhab/core/model/script/scoping/ScriptImplicitlyImportedTypes.java b/bundles/org.openhab.core.model.script/src/org/openhab/core/model/script/scoping/ScriptImplicitlyImportedTypes.java index 55719c8b45c..93405f09f1b 100644 --- a/bundles/org.openhab.core.model.script/src/org/openhab/core/model/script/scoping/ScriptImplicitlyImportedTypes.java +++ b/bundles/org.openhab.core.model.script/src/org/openhab/core/model/script/scoping/ScriptImplicitlyImportedTypes.java @@ -32,10 +32,14 @@ import org.openhab.core.model.script.actions.CoreUtil; import org.openhab.core.model.script.actions.Exec; import org.openhab.core.model.script.actions.HTTP; +import org.openhab.core.model.script.actions.ItemExtensions; +import org.openhab.core.model.script.actions.Items; import org.openhab.core.model.script.actions.Log; import org.openhab.core.model.script.actions.Ping; +import org.openhab.core.model.script.actions.Registries; +import org.openhab.core.model.script.actions.RuleExtensions; +import org.openhab.core.model.script.actions.Rules; import org.openhab.core.model.script.actions.ScriptExecution; -import org.openhab.core.model.script.actions.System; import org.openhab.core.model.script.actions.Transformation; import org.openhab.core.model.script.engine.IActionServiceProvider; import org.openhab.core.model.script.engine.IThingActionsProvider; @@ -77,7 +81,8 @@ protected List> getExtensionClasses() { result.add(Ping.class); result.add(Transformation.class); result.add(ScriptExecution.class); - result.add(System.class); + result.add(ItemExtensions.class); + result.add(RuleExtensions.class); result.add(URLEncoder.class); result.addAll(getActionClasses()); @@ -94,7 +99,9 @@ protected List> getStaticImportClasses() { result.add(Ping.class); result.add(Transformation.class); result.add(ScriptExecution.class); - result.add(System.class); + result.add(Items.class); + result.add(Registries.class); + result.add(Rules.class); result.add(URLEncoder.class); result.add(CoreUtil.class); From f926dbd66e17e491719f44d16a868bafef828ea2 Mon Sep 17 00:00:00 2001 From: Ravi Nadahar Date: Sun, 17 May 2026 23:02:13 +0200 Subject: [PATCH 09/16] Complete reorganization Signed-off-by: Ravi Nadahar --- bundles/org.openhab.core.model.rule/bnd.bnd | 1 + bundles/org.openhab.core.model.script/bnd.bnd | 2 + .../core/model/script/ScriptServiceUtil.java | 89 ++- .../model/script/actions/ItemExtensions.java | 115 ---- .../core/model/script/actions/Items.java | 179 ------ .../core/model/script/actions/Log.java | 104 +++- .../core/model/script/actions/Registries.java | 90 --- .../core/model/script/actions/Things.java | 29 + .../core/model/script/helper/Channels.java | 576 ++++++++++++++++++ .../model/script/helper/ItemExtensions.java | 316 ++++++++++ .../core/model/script/helper/Items.java | 305 ++++++++++ .../{actions => helper}/RuleExtensions.java | 71 +-- .../script/{actions => helper}/Rules.java | 95 +-- .../engine/action/ThingActionService.java | 30 +- .../script/scoping/ActionClassLoader.java | 8 +- .../ScriptImplicitlyImportedTypes.java | 12 +- ...ptImportSectionNamespaceScopeProvider.java | 26 + 17 files changed, 1538 insertions(+), 510 deletions(-) delete mode 100644 bundles/org.openhab.core.model.script/src/org/openhab/core/model/script/actions/ItemExtensions.java delete mode 100644 bundles/org.openhab.core.model.script/src/org/openhab/core/model/script/actions/Items.java delete mode 100644 bundles/org.openhab.core.model.script/src/org/openhab/core/model/script/actions/Registries.java create mode 100644 bundles/org.openhab.core.model.script/src/org/openhab/core/model/script/helper/Channels.java create mode 100644 bundles/org.openhab.core.model.script/src/org/openhab/core/model/script/helper/ItemExtensions.java create mode 100644 bundles/org.openhab.core.model.script/src/org/openhab/core/model/script/helper/Items.java rename bundles/org.openhab.core.model.script/src/org/openhab/core/model/script/{actions => helper}/RuleExtensions.java (68%) rename bundles/org.openhab.core.model.script/src/org/openhab/core/model/script/{actions => helper}/Rules.java (71%) diff --git a/bundles/org.openhab.core.model.rule/bnd.bnd b/bundles/org.openhab.core.model.rule/bnd.bnd index f43eae7b46c..0d7459ca75c 100644 --- a/bundles/org.openhab.core.model.rule/bnd.bnd +++ b/bundles/org.openhab.core.model.rule/bnd.bnd @@ -27,6 +27,7 @@ Import-Package: \ org.openhab.core.model.items,\ org.openhab.core.model.script,\ org.openhab.core.model.script.engine.action,\ + org.openhab.core.model.script.helper,\ org.openhab.core.persistence,\ org.openhab.core.persistence.extensions,\ org.openhab.core.service,\ diff --git a/bundles/org.openhab.core.model.script/bnd.bnd b/bundles/org.openhab.core.model.script/bnd.bnd index 4d49f171a14..1f4eebb0d1e 100644 --- a/bundles/org.openhab.core.model.script/bnd.bnd +++ b/bundles/org.openhab.core.model.script/bnd.bnd @@ -5,6 +5,7 @@ Export-Package: org.openhab.core.model.script,\ org.openhab.core.model.script.engine.action,\ org.openhab.core.model.script.extension,\ org.openhab.core.model.script.formatting,\ + org.openhab.core.model.script.helper,\ org.openhab.core.model.script.interpreter,\ org.openhab.core.model.script.jvmmodel,\ org.openhab.core.model.script.lib,\ @@ -42,6 +43,7 @@ Import-Package: \ org.openhab.core.thing,\ org.openhab.core.thing.binding,\ org.openhab.core.thing.events,\ + org.openhab.core.thing.link,\ org.openhab.core.transform,\ org.openhab.core.transform.actions,\ org.openhab.core.types,\ diff --git a/bundles/org.openhab.core.model.script/src/org/openhab/core/model/script/ScriptServiceUtil.java b/bundles/org.openhab.core.model.script/src/org/openhab/core/model/script/ScriptServiceUtil.java index fd0348bd8d8..48dfc1a4481 100644 --- a/bundles/org.openhab.core.model.script/src/org/openhab/core/model/script/ScriptServiceUtil.java +++ b/bundles/org.openhab.core.model.script/src/org/openhab/core/model/script/ScriptServiceUtil.java @@ -28,6 +28,11 @@ import org.openhab.core.scheduler.Scheduler; import org.openhab.core.thing.ThingRegistry; import org.openhab.core.thing.binding.ThingActions; +import org.openhab.core.thing.link.ItemChannelLinkRegistry; +import org.osgi.framework.Bundle; +import org.osgi.framework.BundleContext; +import org.osgi.framework.FrameworkUtil; +import org.osgi.framework.ServiceReference; import org.osgi.service.component.annotations.Activate; import org.osgi.service.component.annotations.Component; import org.osgi.service.component.annotations.Deactivate; @@ -42,6 +47,7 @@ * * @author Davy Vanherbergen - Initial contribution * @author Kai Kreuzer - renamed and removed interface + * @author Ravi Nadahar - added additional registries for retrieval */ @Component(immediate = true, service = ScriptServiceUtil.class) public class ScriptServiceUtil { @@ -56,6 +62,7 @@ public class ScriptServiceUtil { private final ModelRepository modelRepository; private final MetadataRegistry metadataRegistry; private final RuleRegistry ruleRegistry; + private final ItemChannelLinkRegistry itemChannelLinkRegistry; private volatile @Nullable RuleManager ruleManager; private final Scheduler scheduler; @@ -67,13 +74,14 @@ public class ScriptServiceUtil { public ScriptServiceUtil(final @Reference ItemRegistry itemRegistry, final @Reference ThingRegistry thingRegistry, final @Reference EventPublisher eventPublisher, final @Reference ModelRepository modelRepository, final @Reference MetadataRegistry metadataRegistry, final @Reference RuleRegistry ruleRegistry, - final @Reference Scheduler scheduler) { + final @Reference ItemChannelLinkRegistry itemChannelLinkRegistry, final @Reference Scheduler scheduler) { this.itemRegistry = itemRegistry; this.thingRegistry = thingRegistry; this.eventPublisher = eventPublisher; this.modelRepository = modelRepository; this.metadataRegistry = metadataRegistry; this.ruleRegistry = ruleRegistry; + this.itemChannelLinkRegistry = itemChannelLinkRegistry; this.scheduler = scheduler; if (instance != null) { @@ -102,6 +110,9 @@ private static ScriptServiceUtil getInstance() { return instance; } + /** + * @return The {@link ItemRegistry}. + */ public static ItemRegistry getItemRegistry() { return getInstance().itemRegistry; } @@ -110,6 +121,9 @@ public ItemRegistry getItemRegistryInstance() { return itemRegistry; } + /** + * @return The {@link ThingRegistry} instance. + */ public static ThingRegistry getThingRegistry() { return getInstance().thingRegistry; } @@ -118,10 +132,16 @@ public ThingRegistry getThingRegistryInstance() { return thingRegistry; } + /** + * @return The {@link EventPublisher} instance. + */ public static EventPublisher getEventPublisher() { return getInstance().eventPublisher; } + /** + * @return The {@link ModelRepository} instance. + */ public static ModelRepository getModelRepository() { return getInstance().modelRepository; } @@ -130,6 +150,9 @@ public ModelRepository getModelRepositoryInstance() { return modelRepository; } + /** + * @return The {@link MetadataRegistry} instance. + */ public static MetadataRegistry getMetadataRegistry() { return getInstance().metadataRegistry; } @@ -138,6 +161,9 @@ public MetadataRegistry getMetadataRegistryInstance() { return metadataRegistry; } + /** + * @return The {@link RuleRegistry} instance. + */ public static RuleRegistry getRuleRegistry() { return getInstance().ruleRegistry; } @@ -146,6 +172,20 @@ public RuleRegistry getRuleRegistryInstance() { return ruleRegistry; } + /** + * @return The {@link ItemChannelLinkRegistry} instance. + */ + public static ItemChannelLinkRegistry getItemChannelLinkRegistry() { + return getInstance().itemChannelLinkRegistry; + } + + public ItemChannelLinkRegistry getItemChannelLinkRegistryInstance() { + return itemChannelLinkRegistry; + } + + /** + * @return The {@link RuleManager} / rule engine instance or {@code null} if it doesn't exist. + */ public @Nullable static RuleManager getRuleManager() { return getInstance().ruleManager; } @@ -154,6 +194,9 @@ public RuleRegistry getRuleRegistryInstance() { return ruleManager; } + /** + * @return The {@link Scheduler} instance. + */ public static Scheduler getScheduler() { return getInstance().scheduler; } @@ -166,12 +209,18 @@ public static ScriptEngine getScriptEngine() { return getInstance().scriptEngine.get(); } + /** + * @return A {@link List} of currently registered {@link ActionService} instances. + */ public static List getActionServices() { - return getInstance().actionServices; + return List.copyOf(getInstance().actionServices); } + /** + * @return A {@link List} of currently registered {@link ThingActions} instances. + */ public static List getThingActions() { - return getInstance().thingActions; + return List.copyOf(getInstance().thingActions); } public List getActionServiceInstances() { @@ -209,4 +258,38 @@ public void unsetScriptEngine(ScriptEngine scriptEngine) { // uninjected as a callback from the script engine, not via DS as it is a circular dependency... this.scriptEngine.compareAndSet(scriptEngine, null); } + + /** + * Retrieve an OSGi instance of the specified {@link Class}, if it exists. The reference to the instance is + * unreserved, which means that the instance might stop being valid at any time, for example if the service + * or the containing bundle is stopped. + *

+ * Returning an unreserved service isn't kosher in the world of OSGi, but the only alternative is that the scripts + * that retrieve the instance are responsible for unregistering the reservation after use. That isn't a reasonable + * thing to expect from user scripts. The chance that a service is stopped while OH is running is quite small, so + * on balance, returning an unreserved service instance seems like the best way to do it. It isn't much different + * from returning an instance to a registry that is reserved by {@link ScriptServiceUtil} - if the + * {@link ScriptServiceUtil} itself is stopped, the instance might become invalid while the script is using it. + * + * @param the class type. + * @param clazz the class of the instance to get. + * @return The instance or {@code null} if the instance wasn't found. + */ + public static @Nullable T getInstance(Class clazz) { + Bundle bundle = FrameworkUtil.getBundle(clazz); + if (bundle != null) { + BundleContext bc = bundle.getBundleContext(); + if (bc != null) { + ServiceReference ref = bc.getServiceReference(clazz); + if (ref != null) { + T result = bc.getService(ref); + if (result != null) { + bc.ungetService(ref); + } + return result; + } + } + } + return null; + } } diff --git a/bundles/org.openhab.core.model.script/src/org/openhab/core/model/script/actions/ItemExtensions.java b/bundles/org.openhab.core.model.script/src/org/openhab/core/model/script/actions/ItemExtensions.java deleted file mode 100644 index 390a26496b1..00000000000 --- a/bundles/org.openhab.core.model.script/src/org/openhab/core/model/script/actions/ItemExtensions.java +++ /dev/null @@ -1,115 +0,0 @@ -/* - * Copyright (c) 2010-2026 Contributors to the openHAB project - * - * See the NOTICE file(s) distributed with this work for additional - * information. - * - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License 2.0 which is available at - * http://www.eclipse.org/legal/epl-2.0 - * - * SPDX-License-Identifier: EPL-2.0 - */ -package org.openhab.core.model.script.actions; - -import java.util.LinkedHashMap; -import java.util.Map; - -import org.eclipse.jdt.annotation.NonNull; -import org.eclipse.jdt.annotation.NonNullByDefault; -import org.eclipse.jdt.annotation.Nullable; -import org.openhab.core.items.Item; -import org.openhab.core.items.Metadata; - -/** - * {@link ItemExtensions} provides DSL access to things like OSGi instances, system registries and the ability to run - * other - * rules. - * - * @author Ravi Nadahar - Initial contribution - */ -@NonNullByDefault -public class ItemExtensions { - - @NonNullByDefault({}) - public static void addMetadata(Item item, String namespace, String value) { - if (item == null) { - throw new IllegalArgumentException("item cannot be null"); - } - Items.addMetadata(item.getName(), namespace, value, (String) null); - } - - @NonNullByDefault({}) - public static void addMetadata(Item item, String namespace, String value, Object... configuration) { - if (item == null) { - throw new IllegalArgumentException("item cannot be null"); - } - Items.addMetadata(item.getName(), namespace, value, parseObjectArray(configuration)); - } - - @NonNullByDefault({}) - public static void addMetadata(Item item, String namespace, String value, - @Nullable Map<@NonNull String, @NonNull Object> configuration) { - if (item == null) { - throw new IllegalArgumentException("item cannot be null"); - } - Items.addMetadata(item.getName(), namespace, value, configuration); - } - - @NonNullByDefault({}) - public static @Nullable Metadata getMetadata(Item item, String namespace) { - if (item == null) { - throw new IllegalArgumentException("item cannot be null"); - } - return Items.getMetadata(item.getName(), namespace); - } - - @NonNullByDefault({}) - public static @Nullable Metadata removeMetadata(Item item, String namespace) { - if (item == null) { - throw new IllegalArgumentException("item cannot be null"); - } - return Items.removeMetadata(item.getName(), namespace); - } - - @NonNullByDefault({}) - public static @Nullable Metadata updateMetadata(Item item, String namespace, String value) { - if (item == null) { - throw new IllegalArgumentException("item cannot be null"); - } - return Items.updateMetadata(item.getName(), namespace, value); - } - - @NonNullByDefault({}) - public static @Nullable Metadata updateMetadata(Item item, String namespace, String value, - Object... configuration) { - return Items.updateMetadata(item.getName(), namespace, value, parseObjectArray(configuration)); - } - - @NonNullByDefault({}) - public static @Nullable Metadata updateMetadata(Item item, String namespace, String value, - @Nullable Map<@NonNull String, @NonNull Object> configuration) { - if (item == null) { - throw new IllegalArgumentException("item cannot be null"); - } - return Items.updateMetadata(item.getName(), namespace, value); - } - - private static Map parseObjectArray(Object @Nullable [] objects) throws IllegalArgumentException { - if (objects == null || objects.length == 0) { - return Map.of(); - } - if ((objects.length % 2) != 0) { - throw new IllegalArgumentException("There must be an even number of objects (" + objects.length + ')'); - } - Map result = new LinkedHashMap<>(); - for (int i = 0; i < objects.length; i += 2) { - if (objects[i] instanceof String key) { - result.put(key, objects[i + 1]); - } else { - throw new IllegalArgumentException("Keys must be strings: " + objects[i]); - } - } - return result; - } -} diff --git a/bundles/org.openhab.core.model.script/src/org/openhab/core/model/script/actions/Items.java b/bundles/org.openhab.core.model.script/src/org/openhab/core/model/script/actions/Items.java deleted file mode 100644 index d54e2766e0d..00000000000 --- a/bundles/org.openhab.core.model.script/src/org/openhab/core/model/script/actions/Items.java +++ /dev/null @@ -1,179 +0,0 @@ -/* - * Copyright (c) 2010-2026 Contributors to the openHAB project - * - * See the NOTICE file(s) distributed with this work for additional - * information. - * - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License 2.0 which is available at - * http://www.eclipse.org/legal/epl-2.0 - * - * SPDX-License-Identifier: EPL-2.0 - */ -package org.openhab.core.model.script.actions; - -import java.util.LinkedHashMap; -import java.util.Map; - -import org.eclipse.jdt.annotation.NonNull; -import org.eclipse.jdt.annotation.NonNullByDefault; -import org.eclipse.jdt.annotation.Nullable; -import org.openhab.core.common.registry.ManagedProvider; -import org.openhab.core.items.Item; -import org.openhab.core.items.Metadata; -import org.openhab.core.items.MetadataKey; -import org.openhab.core.items.MetadataProvider; -import org.openhab.core.model.script.ScriptServiceUtil; - -/** - * {@link Items} provides DSL access to things like OSGi instances, system registries and the ability to run other - * rules. - * - * @author Ravi Nadahar - Initial contribution - */ -@NonNullByDefault -public class Items { - - public static @Nullable Item getItem(String itemName) { - return ScriptServiceUtil.getItemRegistry().get(itemName); - } - - /** - * - * @param itemName - * @param namespace - * @param value - * @throws IllegalArgumentException If either value is {@code null} or {@code namespace} or - * {@code itemName} is invalid. - * @throws UnsupportedOperationException If the metadata namespace has a reserved {@link MetadataProvider} that is - * not a {@link ManagedProvider}. - * @throws IllegalStateException If no ManagedProvider is available. - */ - @NonNullByDefault({}) - public static void addMetadata(String itemName, String namespace, String value) { - addMetadata(itemName, namespace, value, (String) null); - } - - @NonNullByDefault({}) - public static void addMetadata(String itemName, String namespace, String value, Object... configuration) { - addMetadata(itemName, namespace, value, parseObjectArray(configuration)); - } - - /** - * - * @param itemName - * @param namespace - * @param value - * @param configuration - * @throws IllegalArgumentException If {@code namespace}, {@code itemName} or {@code value} is {@code null} or if - * {@code namespace} or {@code itemName} is invalid. - * @throws UnsupportedOperationException If the metadata namespace has a reserved {@link MetadataProvider} that is - * not a {@link ManagedProvider}. - * @throws IllegalStateException If no ManagedProvider is available. - */ - @NonNullByDefault({}) - public static void addMetadata(String itemName, String namespace, String value, - @Nullable Map<@NonNull String, @NonNull Object> configuration) { - if (itemName == null) { - throw new IllegalArgumentException("itemName cannot be null"); - } - if (namespace == null) { - throw new IllegalArgumentException("namespace cannot be null"); - } - if (value == null) { - throw new IllegalArgumentException("value cannot be null"); - } - ScriptServiceUtil.getMetadataRegistry() - .add(new Metadata(new MetadataKey(namespace, itemName), value, configuration)); - } - - @NonNullByDefault({}) - public static @Nullable Metadata getMetadata(String itemName, String namespace) { - if (itemName == null) { - throw new IllegalArgumentException("itemName cannot be null"); - } - if (namespace == null) { - throw new IllegalArgumentException("namespace cannot be null"); - } - return ScriptServiceUtil.getMetadataRegistry().get(new MetadataKey(namespace, itemName)); - } - - @NonNullByDefault({}) - public static @Nullable Metadata removeMetadata(String itemName, String namespace) { - if (itemName == null) { - throw new IllegalArgumentException("itemName cannot be null"); - } - if (namespace == null) { - throw new IllegalArgumentException("namespace cannot be null"); - } - return ScriptServiceUtil.getMetadataRegistry().remove(new MetadataKey(namespace, itemName)); - } - - /** - * - * @param namespace - * @param itemName - * @param value - * @throws IllegalArgumentException If either value is {@code null} or {@code namespace} or - * {@code itemName} is invalid. - * @throws UnsupportedOperationException If the metadata namespace has a reserved {@link MetadataProvider} that is - * not a {@link ManagedProvider}. - * @throws IllegalStateException If no ManagedProvider is available. - */ - @NonNullByDefault({}) - public static @Nullable Metadata updateMetadata(String itemName, String namespace, String value) { - return updateMetadata(itemName, namespace, value, (Map) null); - } - - @NonNullByDefault({}) - public static @Nullable Metadata updateMetadata(String itemName, String namespace, String value, - Object... configuration) { - return updateMetadata(itemName, namespace, value, parseObjectArray(configuration)); - } - - /** - * - * @param itemName - * @param namespace - * @param value - * @param configuration - * @throws IllegalArgumentException If {@code namespace}, {@code itemName} or {@code value} is {@code null} or if - * {@code namespace} or {@code itemName} is invalid. - * @throws UnsupportedOperationException If the metadata namespace has a reserved {@link MetadataProvider} that is - * not a {@link ManagedProvider}. - * @throws IllegalStateException If no ManagedProvider is available. - */ - @NonNullByDefault({}) - public static @Nullable Metadata updateMetadata(String itemName, String namespace, String value, - @Nullable Map<@NonNull String, @NonNull Object> configuration) { - if (itemName == null) { - throw new IllegalArgumentException("itemName cannot be null"); - } - if (namespace == null) { - throw new IllegalArgumentException("namespace cannot be null"); - } - if (value == null) { - throw new IllegalArgumentException("value cannot be null"); - } - return ScriptServiceUtil.getMetadataRegistry() - .update(new Metadata(new MetadataKey(namespace, itemName), value, configuration)); - } - - private static Map parseObjectArray(Object @Nullable [] objects) throws IllegalArgumentException { - if (objects == null || objects.length == 0) { - return Map.of(); - } - if ((objects.length % 2) != 0) { - throw new IllegalArgumentException("There must be an even number of objects (" + objects.length + ')'); - } - Map result = new LinkedHashMap<>(); - for (int i = 0; i < objects.length; i += 2) { - if (objects[i] instanceof String key) { - result.put(key, objects[i + 1]); - } else { - throw new IllegalArgumentException("Keys must be strings: " + objects[i]); - } - } - return result; - } -} diff --git a/bundles/org.openhab.core.model.script/src/org/openhab/core/model/script/actions/Log.java b/bundles/org.openhab.core.model.script/src/org/openhab/core/model/script/actions/Log.java index 505b64cee36..922dc39a61a 100644 --- a/bundles/org.openhab.core.model.script/src/org/openhab/core/model/script/actions/Log.java +++ b/bundles/org.openhab.core.model.script/src/org/openhab/core/model/script/actions/Log.java @@ -28,56 +28,128 @@ public class Log { /** * Creates the Log-Entry format with level DEBUG and logs under the loggers name * org.openhab.core.model.script.<loggerName> - * + * + * @param loggerName the name of the Logger which is prefixed with org.openhab.core.model.script. + * @param format the Log-Statement which can contain a placeholder '{}' + * @param arg the argument to replace the placeholder contained in format + * + * @see Logger + * + * @implNote This overload exists to avoid confusion when mapping a single argument to Varargs. + */ + public static void logDebug(String loggerName, Object format, Object arg) { + LoggerFactory.getLogger(LOGGER_NAME_PREFIX.concat(loggerName)) + .debug(format instanceof String formatStr ? formatStr : format == null ? null : format.toString(), arg); + } + + /** + * Creates the Log-Entry format with level DEBUG and logs under the loggers name + * org.openhab.core.model.script.<loggerName> + * * @param loggerName the name of the Logger which is prefixed with org.openhab.core.model.script. * @param format the Log-Statement which can contain placeholders '{}' * @param args the arguments to replace the placeholders contained in format - * + * + * @see Logger + */ + public static void logDebug(String loggerName, Object format, Object... args) { + LoggerFactory.getLogger(LOGGER_NAME_PREFIX.concat(loggerName)).debug( + format instanceof String formatStr ? formatStr : format == null ? null : format.toString(), args); + } + + /** + * Creates the Log-Entry format with level INFO and logs under the loggers name + * org.openhab.core.model.script.<loggerName> + * + * @param loggerName the name of the Logger which is prefixed with org.openhab.core.model.script. + * @param format the Log-Statement which can contain a placeholder '{}' + * @param arg the argument to replace the placeholder contained in format + * * @see Logger + * + * @implNote This overload exists to avoid confusion when mapping a single argument to Varargs. */ - public static void logDebug(String loggerName, String format, Object... args) { - LoggerFactory.getLogger(LOGGER_NAME_PREFIX.concat(loggerName)).debug(format, args); + public static void logInfo(String loggerName, Object format, Object arg) { + LoggerFactory.getLogger(LOGGER_NAME_PREFIX.concat(loggerName)) + .info(format instanceof String formatStr ? formatStr : format == null ? null : format.toString(), arg); } /** * Creates the Log-Entry format with level INFO and logs under the loggers name * org.openhab.core.model.script.<loggerName> - * + * * @param loggerName the name of the Logger which is prefixed with org.openhab.core.model.script. * @param format the Log-Statement which can contain placeholders '{}' * @param args the arguments to replace the placeholders contained in format - * + * * @see Logger */ - public static void logInfo(String loggerName, String format, Object... args) { - LoggerFactory.getLogger(LOGGER_NAME_PREFIX.concat(loggerName)).info(format, args); + public static void logInfo(String loggerName, Object format, Object... args) { + LoggerFactory.getLogger(LOGGER_NAME_PREFIX.concat(loggerName)) + .info(format instanceof String formatStr ? formatStr : format == null ? null : format.toString(), args); } /** * Creates the Log-Entry format with level WARN and logs under the loggers name * org.openhab.core.model.script.<loggerName> - * + * + * @param loggerName the name of the Logger which is prefixed with org.openhab.core.model.script. + * @param format the Log-Statement which can contain a placeholder '{}' + * @param arg the argument to replace the placeholder contained in format + * + * @see Logger + * + * @implNote This overload exists to avoid confusion when mapping a single argument to Varargs. + */ + public static void logWarn(String loggerName, Object format, Object arg) { + LoggerFactory.getLogger(LOGGER_NAME_PREFIX.concat(loggerName)) + .warn(format instanceof String formatStr ? formatStr : format == null ? null : format.toString(), arg); + } + + /** + * Creates the Log-Entry format with level WARN and logs under the loggers name + * org.openhab.core.model.script.<loggerName> + * * @param loggerName the name of the Logger which is prefixed with org.openhab.core.model.script. * @param format the Log-Statement which can contain placeholders '{}' * @param args the arguments to replace the placeholders contained in format - * + * + * @see Logger + */ + public static void logWarn(String loggerName, Object format, Object... args) { + LoggerFactory.getLogger(LOGGER_NAME_PREFIX.concat(loggerName)) + .warn(format instanceof String formatStr ? formatStr : format == null ? null : format.toString(), args); + } + + /** + * Creates the Log-Entry format with level ERROR and logs under the loggers name + * org.openhab.core.model.script.<loggerName> + * + * @param loggerName the name of the Logger which is prefixed with org.openhab.core.model.script. + * @param format the Log-Statement which can contain a placeholder '{}' + * @param arg the argument to replace the placeholder contained in format + * * @see Logger + * + * @implNote This overload exists to avoid confusion when mapping a single argument to Varargs. */ - public static void logWarn(String loggerName, String format, Object... args) { - LoggerFactory.getLogger(LOGGER_NAME_PREFIX.concat(loggerName)).warn(format, args); + public static void logError(String loggerName, Object format, Object arg) { + LoggerFactory.getLogger(LOGGER_NAME_PREFIX.concat(loggerName)) + .error(format instanceof String formatStr ? formatStr : format == null ? null : format.toString(), arg); } /** * Creates the Log-Entry format with level ERROR and logs under the loggers name * org.openhab.core.model.script.<loggerName> - * + * * @param loggerName the name of the Logger which is prefixed with org.openhab.core.model.script. * @param format the Log-Statement which can contain placeholders '{}' * @param args the arguments to replace the placeholders contained in format - * + * * @see Logger */ - public static void logError(String loggerName, String format, Object... args) { - LoggerFactory.getLogger(LOGGER_NAME_PREFIX.concat(loggerName)).error(format, args); + public static void logError(String loggerName, Object format, Object... args) { + LoggerFactory.getLogger(LOGGER_NAME_PREFIX.concat(loggerName)).error( + format instanceof String formatStr ? formatStr : format == null ? null : format.toString(), args); } } diff --git a/bundles/org.openhab.core.model.script/src/org/openhab/core/model/script/actions/Registries.java b/bundles/org.openhab.core.model.script/src/org/openhab/core/model/script/actions/Registries.java deleted file mode 100644 index 7a7e53c1f2e..00000000000 --- a/bundles/org.openhab.core.model.script/src/org/openhab/core/model/script/actions/Registries.java +++ /dev/null @@ -1,90 +0,0 @@ -/* - * Copyright (c) 2010-2026 Contributors to the openHAB project - * - * See the NOTICE file(s) distributed with this work for additional - * information. - * - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License 2.0 which is available at - * http://www.eclipse.org/legal/epl-2.0 - * - * SPDX-License-Identifier: EPL-2.0 - */ -package org.openhab.core.model.script.actions; - -import org.eclipse.jdt.annotation.NonNullByDefault; -import org.eclipse.jdt.annotation.Nullable; -import org.openhab.core.automation.RuleRegistry; -import org.openhab.core.items.ItemRegistry; -import org.openhab.core.items.MetadataRegistry; -import org.openhab.core.model.script.ScriptServiceUtil; -import org.openhab.core.model.script.engine.action.ActionDoc; -import org.openhab.core.thing.ThingRegistry; -import org.osgi.framework.Bundle; -import org.osgi.framework.BundleContext; -import org.osgi.framework.FrameworkUtil; -import org.osgi.framework.ServiceReference; - -/** - * {@link Registries} provides DSL access to things like OSGi instances, system registries and the ability to run other - * rules. - * - * @author Ravi Nadahar - Initial contribution - */ -@NonNullByDefault -public class Registries { - - /** - * Retrieve an OSGi instance of the specified {@link Class}, if it exists. - * - * @param the class type. - * @param clazz the class of the instance to get. - * @return The instance or {@code null} if the instance wasn't found. - */ - @ActionDoc(text = "acquire an OSGi instance") - public static @Nullable T getInstance(Class clazz) { - Bundle bundle = FrameworkUtil.getBundle(clazz); - if (bundle != null) { - BundleContext bc = bundle.getBundleContext(); - if (bc != null) { - ServiceReference ref = bc.getServiceReference(clazz); - if (ref != null) { - return bc.getService(ref); - } - } - } - return null; - } - - /** - * @return The {@link ThingRegistry}. - */ - @ActionDoc(text = "get the thing registry") - public static ThingRegistry getThingRegistry() { - return ScriptServiceUtil.getThingRegistry(); - } - - /** - * @return The {@link MetadataRegistry}. - */ - @ActionDoc(text = "get the metadata registry") - public static MetadataRegistry getMetadataRegistry() { - return ScriptServiceUtil.getMetadataRegistry(); - } - - /** - * @return The {@link ItemRegistry}. - */ - @ActionDoc(text = "get the item registry") - public static ItemRegistry getItemRegistry() { - return ScriptServiceUtil.getItemRegistry(); - } - - /** - * @return The {@link RuleRegistry}. - */ - @ActionDoc(text = "get the rule registry") - public static RuleRegistry getRuleRegistry() { - return ScriptServiceUtil.getRuleRegistry(); - } -} diff --git a/bundles/org.openhab.core.model.script/src/org/openhab/core/model/script/actions/Things.java b/bundles/org.openhab.core.model.script/src/org/openhab/core/model/script/actions/Things.java index 7d9afcc0258..4bf0617c579 100644 --- a/bundles/org.openhab.core.model.script/src/org/openhab/core/model/script/actions/Things.java +++ b/bundles/org.openhab.core.model.script/src/org/openhab/core/model/script/actions/Things.java @@ -13,7 +13,10 @@ package org.openhab.core.model.script.actions; import org.openhab.core.model.script.internal.engine.action.ThingActionService; +import org.openhab.core.thing.Thing; +import org.openhab.core.thing.ThingRegistry; import org.openhab.core.thing.ThingStatusInfo; +import org.openhab.core.thing.ThingUID; import org.openhab.core.thing.binding.ThingActions; /** @@ -45,4 +48,30 @@ public static ThingStatusInfo getThingStatusInfo(String thingUid) { public static ThingActions getActions(String scope, String thingUid) { return ThingActionService.getActions(scope, thingUid); } + + /** + * Gets the actions instance of a certain scope for a given {@link Thing}. + * + * @param thing the {@link Thing}. + * @param scope the action scope. + * + * @return The {@link ThingActions} instance or {@code null}. + */ + public static ThingActions getActions(Thing thing, String scope) { + return ThingActionService.getActions(thing, scope); + } + + /** + * Get a {@link Thing} from the {@link ThingRegistry}. + * + * @param registry the {@link ThingRegistry}. + * @param thingUid the Thing UID string. + * @return The {@link Thing} instance of {@code null}. + */ + public static Thing get(ThingRegistry registry, String thingUid) { + if (registry == null || thingUid == null) { + return null; + } + return registry.get(new ThingUID(thingUid)); + } } diff --git a/bundles/org.openhab.core.model.script/src/org/openhab/core/model/script/helper/Channels.java b/bundles/org.openhab.core.model.script/src/org/openhab/core/model/script/helper/Channels.java new file mode 100644 index 00000000000..b019f789d9c --- /dev/null +++ b/bundles/org.openhab.core.model.script/src/org/openhab/core/model/script/helper/Channels.java @@ -0,0 +1,576 @@ +/* + * Copyright (c) 2010-2026 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.core.model.script.helper; + +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.Set; + +import org.eclipse.jdt.annotation.NonNull; +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.openhab.core.common.registry.ManagedProvider; +import org.openhab.core.config.core.Configuration; +import org.openhab.core.items.Item; +import org.openhab.core.model.script.ScriptServiceUtil; +import org.openhab.core.thing.Channel; +import org.openhab.core.thing.ChannelUID; +import org.openhab.core.thing.Thing; +import org.openhab.core.thing.link.ItemChannelLink; + +/** + * {@link Channels} provides DSL access to channel manipulation. + * + * @author Ravi Nadahar - Initial contribution + */ +@NonNullByDefault +public class Channels { + + /** + * Get the {@link ItemChannelLink}s that are linked to the specified item. + * + * @param itemName the name of the item. + * @return The {@link Set} of {@link ItemChannelLink}s. + */ + public static Set getLinks(@Nullable String itemName) { + if (itemName == null || itemName.isBlank()) { + return Set.of(); + } + return ScriptServiceUtil.getItemChannelLinkRegistry().getLinks(itemName); + } + + /** + * Get the {@link ItemChannelLink}s that are linked to the specified item. + * + * @param item the {@link Item}. + * @return The {@link Set} of {@link ItemChannelLink}s. + */ + public static Set getLinks(@Nullable Item item) { + if (item == null) { + return Set.of(); + } + return ScriptServiceUtil.getItemChannelLinkRegistry().getLinks(item.getName()); + } + + /** + * Get the {@link ItemChannelLink}s that are linked to the specified channel. + * + * @param channelUid the UID of the channel. + * @return The {@link Set} of {@link ItemChannelLink}s. + */ + public static Set getChannelLinks(@Nullable String channelUid) { + if (channelUid == null || channelUid.isBlank()) { + return Set.of(); + } + return ScriptServiceUtil.getItemChannelLinkRegistry().getLinks(new ChannelUID(channelUid)); + } + + /** + * Get the {@link ItemChannelLink}s that are linked to the specified channel. + * + * @param channelUid the {@link ChannelUID} of the channel. + * @return The {@link Set} of {@link ItemChannelLink}s. + */ + public static Set getChannelLinks(@Nullable ChannelUID channelUid) { + if (channelUid == null) { + return Set.of(); + } + return ScriptServiceUtil.getItemChannelLinkRegistry().getLinks(channelUid); + } + + /** + * Get the {@link ChannelUID}s of the channels that are bound to the specified {@link Item}. + * + * @param itemName the name of the item. + * @return The {@link Set} of {@link ChannelUID}s of the bound channels. + */ + public static Set getBoundChannels(@Nullable String itemName) { + if (itemName == null) { + return Set.of(); + } + return ScriptServiceUtil.getItemChannelLinkRegistry().getBoundChannels(itemName); + } + + /** + * Get the {@link ChannelUID}s of the channels that are bound to the specified {@link Item}. + * + * @param item the {@link Item}. + * @return The {@link Set} of {@link ChannelUID}s of the bound channels. + * @throws IllegalArgumentException If {@code item} is {@code null}. + */ + @NonNullByDefault({}) + public static @NonNull Set<@NonNull ChannelUID> getBoundChannels(Item item) { + if (item == null) { + throw new IllegalArgumentException("item cannot be null"); + } + return ScriptServiceUtil.getItemChannelLinkRegistry().getBoundChannels(item.getName()); + } + + /** + * Get the {@link Thing}s that are bound to the specified {@link Item}. + * + * @param itemName the name of the item. + * @return The {@link Set} of bound {@link Thing}s. + */ + public static Set getBoundThings(@Nullable String itemName) { + if (itemName == null) { + return Set.of(); + } + return ScriptServiceUtil.getItemChannelLinkRegistry().getBoundThings(itemName); + } + + /** + * Get the {@link Thing}s that are bound to the specified {@link Item}. + * + * @param item the {@link Item}. + * @return The {@link Set} of bound {@link Thing}s. + * @throws IllegalArgumentException If {@code item} is {@code null}. + */ + @NonNullByDefault({}) + public static @NonNull Set<@NonNull Thing> getBoundThings(Item item) { + if (item == null) { + throw new IllegalArgumentException("item cannot be null"); + } + return ScriptServiceUtil.getItemChannelLinkRegistry().getBoundThings(item.getName()); + } + + /** + * Get the names of all the items that are linked to a specific channel. + * + * @param channelUid the UID of the channel. + * @return The {@link Set} of names of the linked items. + */ + public static Set getLinkedItemNames(@Nullable String channelUid) { + if (channelUid == null || channelUid.isBlank()) { + return Set.of(); + } + return ScriptServiceUtil.getItemChannelLinkRegistry().getLinkedItemNames(new ChannelUID(channelUid)); + } + + /** + * Get the names of all the items that are linked to a specific channel. + * + * @param channelUid the {@link ChannelUID} of the channel. + * @return The {@link Set} of names of the linked items. + * @throws IllegalArgumentException If {@code channelUid} is {@code null}. + */ + @NonNullByDefault({}) + public static @NonNull Set<@NonNull String> getLinkedItemNames(ChannelUID channelUid) { + if (channelUid == null) { + throw new IllegalArgumentException("channelUid cannot be null"); + } + return ScriptServiceUtil.getItemChannelLinkRegistry().getLinkedItemNames(channelUid); + } + + /** + * Get all {@link Item}s that are linked to a specific channel. + * + * @param channelUid the UID of the channel. + * @return The {@link Set} of linked {@link Item}s. + */ + public static Set getLinkedItems(@Nullable String channelUid) { + if (channelUid == null || channelUid.isBlank()) { + return Set.of(); + } + return ScriptServiceUtil.getItemChannelLinkRegistry().getLinkedItems(new ChannelUID(channelUid)); + } + + /** + * Get all {@link Item}s that are linked to a specific channel. + * + * @param channelUid the {@link ChannelUID} of the channel. + * @return The {@link Set} of linked {@link Item}s. + * @throws IllegalArgumentException If {@code channelUid} is {@code null}. + */ + @NonNullByDefault({}) + public static @NonNull Set<@NonNull Item> getLinkedItems(ChannelUID channelUid) { + if (channelUid == null) { + throw new IllegalArgumentException("channelUid cannot be null"); + } + return ScriptServiceUtil.getItemChannelLinkRegistry().getLinkedItems(channelUid); + } + + /** + * Check if the specified item has at least one link. + * + * @param itemName the name of the item to check. + * @return {@code true} if a link exists, {@code false} otherwise. + */ + public static boolean isLinked(@Nullable String itemName) { + if (itemName == null || itemName.isBlank()) { + return false; + } + return ScriptServiceUtil.getItemChannelLinkRegistry().isLinked(itemName); + } + + /** + * Check if the specified item has at least one link. + * + * @param item the {@link Item} to check. + * @return {@code true} if a link exists, {@code false} otherwise. + */ + public static boolean isLinked(@Nullable Item item) { + if (item == null) { + return false; + } + return ScriptServiceUtil.getItemChannelLinkRegistry().isLinked(item.getName()); + } + + /** + * Check if the specified item and channel are linked. + * + * @param itemName the name of the item to check. + * @param channelUid the UID of the channel to check. + * @return {@code true} if a link exists, {@code false} otherwise. + */ + public static boolean isLinked(@Nullable String itemName, @Nullable String channelUid) { + if (itemName == null || itemName.isBlank() || channelUid == null || channelUid.isBlank()) { + return false; + } + return ScriptServiceUtil.getItemChannelLinkRegistry().isLinked(itemName, new ChannelUID(channelUid)); + } + + /** + * Check if the specified item and channel are linked. + * + * @param item the {@link Item} to check. + * @param channelUid the UID of the channel to check. + * @return {@code true} if a link exists, {@code false} otherwise. + */ + public static boolean isLinked(@Nullable Item item, @Nullable String channelUid) { + if (item == null || channelUid == null || channelUid.isBlank()) { + return false; + } + return ScriptServiceUtil.getItemChannelLinkRegistry().isLinked(item.getName(), new ChannelUID(channelUid)); + } + + /** + * Check if the specified item and channel are linked. + * + * @param item the {@link Item} to check. + * @param channelUid the {@link ChannelUID} of the channel to check. + * @return {@code true} if a link exists, {@code false} otherwise. + */ + public static boolean isLinked(@Nullable Item item, @Nullable ChannelUID channelUid) { + if (item == null || channelUid == null) { + return false; + } + return ScriptServiceUtil.getItemChannelLinkRegistry().isLinked(item.getName(), channelUid); + } + + /** + * Check if the specified channel has at least one link. + * + * @param channelUid the UID of the channel to check. + * @return {@code true} if a link exists, {@code false} otherwise. + */ + public static boolean isChannelLinked(@Nullable String channelUid) { + if (channelUid == null || channelUid.isBlank()) { + return false; + } + return ScriptServiceUtil.getItemChannelLinkRegistry().isLinked(new ChannelUID(channelUid)); + } + + /** + * Check if the specified channel has at least one link. + * + * @param channelUid the {@link ChannelUID} of the channel to check. + * @return {@code true} if a link exists, {@code false} otherwise. + */ + public static boolean isChannelLinked(@Nullable ChannelUID channelUid) { + if (channelUid == null) { + return false; + } + return ScriptServiceUtil.getItemChannelLinkRegistry().isLinked(channelUid); + } + + /** + * Get an existing {@link ItemChannelLink} for the specified item and channel. + * + * @param item the {@link Item}. + * @param channelUid the {@link ChannelUID} of the channel. + * @return The {@link ItemChannelLink} or {@code null} if none were found. + * @throws IllegalArgumentException If {@code item} or {@code channelUid} is {@code null}. + */ + @NonNullByDefault({}) + public static @Nullable ItemChannelLink getLink(Item item, ChannelUID channelUid) { + if (item == null) { + throw new IllegalArgumentException("item cannot be null"); + } + if (channelUid == null) { + throw new IllegalArgumentException("channelUid cannot be null"); + } + return getLink(item.getName(), channelUid.getAsString()); + } + + /** + * Get an existing {@link ItemChannelLink} for the specified item and channel. + * + * @param item the {@link Item}. + * @param channelUid the UID of the channel. + * @return The {@link ItemChannelLink} or {@code null} if none were found. + * @throws IllegalArgumentException If {@code item} is {@code null}. + */ + @NonNullByDefault({}) + public static @Nullable ItemChannelLink getLink(Item item, @Nullable String channelUid) { + if (item == null) { + throw new IllegalArgumentException("item cannot be null"); + } + return getLink(item.getName(), channelUid); + } + + /** + * Get an existing {@link ItemChannelLink} for the specified item and channel. + * + * @param itemName the name of the item. + * @param channelUid the UID of the channel. + * @return The {@link ItemChannelLink} or {@code null} if none were found. + */ + public static @Nullable ItemChannelLink getLink(@Nullable String itemName, @Nullable String channelUid) { + if (itemName == null || channelUid == null) { + return null; + } + return ScriptServiceUtil.getItemChannelLinkRegistry().get(itemName + " -> " + channelUid); + } + + /** + * Add a new {@link ItemChannelLink} between an existing {@link Item} and a {@link Channel}. + * + * @param item the {@link Item} to link. + * @param channelUid the UID of the channel to link. + * @return The newly created {@link ItemChannelLink}. + * @throws IllegalArgumentException If {@code item} is {@code null}, the {@link ItemChannelLink} already exists, + * or {@code channelUid} is invalid. + * @throws IllegalStateException If a {@link ManagedProvider} isn't available. + */ + @NonNullByDefault({}) + public static ItemChannelLink addItemChannelLink(Item item, String channelUid) { + if (item == null) { + throw new IllegalArgumentException("item cannot be null"); + } + return ScriptServiceUtil.getItemChannelLinkRegistry() + .add(new ItemChannelLink(item.getName(), new ChannelUID(channelUid))); + } + + /** + * Add a new {@link ItemChannelLink} between an existing {@link Item} and a {@link Channel}. + * + * @param item the {@link Item} to link. + * @param channelUid the UID of the channel to link. + * @param configProperties the pairs of {@link String}s and {@link Object}s that constitutes the configuration. + * properties for the link. Must be in pairs, the first is the key, the second is the value. + * @return The newly created {@link ItemChannelLink}. + * @throws IllegalArgumentException If {@code item} is {@code null}, {@code channelUid} is invalid, the + * {@link ItemChannelLink} already exists, or if there is an odd number of {@code configProperties}, or + * if any of the keys aren't {@link String}s. + * @throws IllegalStateException If a {@link ManagedProvider} isn't available. + */ + public static ItemChannelLink addItemChannelLink(Item item, String channelUid, Object... configProperties) { + Map<@Nullable String, @Nullable Object> props = Map.copyOf(parseObjectArray(configProperties)); + return addItemChannelLink(item, channelUid, props); + } + + /** + * Add a new {@link ItemChannelLink} between an existing {@link Item} and a {@link Channel}. + * + * @param item the {@link Item} to link. + * @param channelUid the UID of the channel to link. + * @param configProperties the map of configuration properties for the link. + * @return The newly created {@link ItemChannelLink}. + * @throws IllegalArgumentException If {@code item} is {@code null}, the {@link ItemChannelLink} already exists, + * or {@code channelUid} is invalid. + * @throws IllegalStateException If a {@link ManagedProvider} isn't available. + */ + @NonNullByDefault({}) + public static ItemChannelLink addItemChannelLink(Item item, String channelUid, + @Nullable Map<@Nullable String, @Nullable Object> configProperties) { + if (item == null) { + throw new IllegalArgumentException("item cannot be null"); + } + return ScriptServiceUtil.getItemChannelLinkRegistry().add( + new ItemChannelLink(item.getName(), new ChannelUID(channelUid), new Configuration(configProperties))); + } + + /** + * Add or replace a {@link ItemChannelLink} between an existing {@link Item} and a {@link Channel}. + * + * @param item the {@link Item} to link. + * @param channelUid the UID of the channel to link. + * @return The old {@link ItemChannelLink} if one existed, or {@code null}. + * @throws IllegalArgumentException If {@code item} is {@code null} or {@code channelUid} is invalid. + * @throws IllegalStateException If a {@link ManagedProvider} isn't available. + */ + @NonNullByDefault({}) + public static @Nullable ItemChannelLink replaceItemChannelLink(Item item, String channelUid) { + if (item == null) { + throw new IllegalArgumentException("item cannot be null"); + } + return ScriptServiceUtil.getItemChannelLinkRegistry() + .update(new ItemChannelLink(item.getName(), new ChannelUID(channelUid))); + } + + /** + * Add or replace a {@link ItemChannelLink} between an existing {@link Item} and a {@link Channel}. + * + * @param item the {@link Item} to link. + * @param channelUid the UID of the channel to link. + * @param configProperties the pairs of {@link String}s and {@link Object}s that constitutes the configuration. + * properties for the link. Must be in pairs, the first is the key, the second is the value. + * @return The old {@link ItemChannelLink} if one existed, or {@code null}. + * @throws IllegalArgumentException If {@code item} is {@code null}, {@code channelUid} is invalid, or if there is + * an odd number of {@code configProperties}, or if any of the keys aren't {@link String}s. + * @throws IllegalStateException If a {@link ManagedProvider} isn't available. + */ + public static @Nullable ItemChannelLink replaceItemChannelLink(Item item, String channelUid, + Object... configProperties) { + Map<@Nullable String, @Nullable Object> props = Map.copyOf(parseObjectArray(configProperties)); + return replaceItemChannelLink(item, channelUid, props); + } + + /** + * Add or replace a {@link ItemChannelLink} between an existing {@link Item} and a {@link Channel}. + * + * @param item the {@link Item} to link. + * @param channelUid the UID of the channel to link. + * @param configProperties the map of configuration properties for the link. + * @return The old {@link ItemChannelLink} if one existed, or {@code null}. + * @throws IllegalArgumentException If {@code item} is {@code null}, or {@code channelUid} is invalid. + * @throws IllegalStateException If a {@link ManagedProvider} isn't available. + */ + @NonNullByDefault({}) + public static @Nullable ItemChannelLink replaceItemChannelLink(Item item, String channelUid, + @Nullable Map<@Nullable String, @Nullable Object> configProperties) { + if (item == null) { + throw new IllegalArgumentException("item cannot be null"); + } + return ScriptServiceUtil.getItemChannelLinkRegistry().update( + new ItemChannelLink(item.getName(), new ChannelUID(channelUid), new Configuration(configProperties))); + } + + /** + * Remove a {@link ItemChannelLink} between an existing {@link Item} and a {@link Channel}. + * + * @param item the {@link Item} of the link to remove. + * @param channelUid the UID of the channel of the link to remove. + * @return The removed {@link ItemChannelLink} or {@code null} if none existed. + * @throws IllegalArgumentException If {@code item} or {@code channelUid} is {@code null}. + * @throws IllegalStateException If a {@link ManagedProvider} isn't available. + */ + @NonNullByDefault({}) + public static @Nullable ItemChannelLink removeItemChannelLink(Item item, ChannelUID channelUid) { + if (item == null) { + throw new IllegalArgumentException("item cannot be null"); + } + if (channelUid == null) { + throw new IllegalArgumentException("channelUid cannot be null"); + } + return removeItemChannelLink(item.getName(), channelUid.getAsString()); + } + + /** + * Remove a {@link ItemChannelLink} between an existing {@link Item} and a {@link Channel}. + * + * @param item the {@link Item} of the link to remove. + * @param channelUid the UID of the channel of the link to remove. + * @return The removed {@link ItemChannelLink} or {@code null} if none existed. + * @throws IllegalArgumentException If {@code item} is {@code null}. + * @throws IllegalStateException If a {@link ManagedProvider} isn't available. + */ + @NonNullByDefault({}) + public static @Nullable ItemChannelLink removeItemChannelLink(Item item, @Nullable String channelUid) { + if (item == null) { + throw new IllegalArgumentException("item cannot be null"); + } + return removeItemChannelLink(item.getName(), channelUid); + } + + /** + * Remove a {@link ItemChannelLink} between an existing {@link Item} and a {@link Channel}. + * + * @param itemName the name of the item of the link to remove. + * @param channelUid the UID of the channel of the link to remove. + * @return The removed {@link ItemChannelLink} or {@code null} if none existed. + * @throws IllegalStateException If a {@link ManagedProvider} isn't available. + */ + public static @Nullable ItemChannelLink removeItemChannelLink(@Nullable String itemName, + @Nullable String channelUid) { + if (itemName == null || channelUid == null) { + return null; + } + return ScriptServiceUtil.getItemChannelLinkRegistry().remove(itemName + " -> " + channelUid); + } + + /** + * Remove all managed links related to the specified item. + * + * @param itemName the name of the item. + * @return The number of removed links. + */ + public static int removeLinksForItem(@Nullable String itemName) { + if (itemName == null || itemName.isBlank()) { + return 0; + } + + return ScriptServiceUtil.getItemChannelLinkRegistry().removeLinksForItem(itemName); + } + + /** + * Remove all managed links related to the specified item. + * + * @param item the {@link Item}. + * @return The number of removed links. + */ + public static int removeLinksForItem(@Nullable Item item) { + if (item == null) { + return 0; + } + + return ScriptServiceUtil.getItemChannelLinkRegistry().removeLinksForItem(item.getName()); + } + + /** + * Remove all orphaned (item or channel missing) managed links. + * + * @return The number of removed links. + */ + public static int removeOrphanedItemChannelLinks() { + return ScriptServiceUtil.getItemChannelLinkRegistry().purge(); + } + + /** + * Transforms pairs of {@link Object}s into a {@link Map}. The former of each pair (the key) must be a + * {@link String}. + * + * @param objects the array of {@link Object}s to transform. + * @return The resulting {@link Map}. + * @throws IllegalArgumentException If there is an odd number of objects, or if any of the keys aren't + * {@link String}s. + */ + private static Map parseObjectArray(Object @Nullable [] objects) throws IllegalArgumentException { + if (objects == null || objects.length == 0) { + return Map.of(); + } + if ((objects.length % 2) != 0) { + throw new IllegalArgumentException("There must be an even number of objects (" + objects.length + ')'); + } + Map result = new LinkedHashMap<>(); + for (int i = 0; i < objects.length; i += 2) { + if (objects[i] instanceof String key) { + result.put(key, objects[i + 1]); + } else { + throw new IllegalArgumentException("Keys must be strings: " + objects[i]); + } + } + return result; + } +} diff --git a/bundles/org.openhab.core.model.script/src/org/openhab/core/model/script/helper/ItemExtensions.java b/bundles/org.openhab.core.model.script/src/org/openhab/core/model/script/helper/ItemExtensions.java new file mode 100644 index 00000000000..e6c9237d9e1 --- /dev/null +++ b/bundles/org.openhab.core.model.script/src/org/openhab/core/model/script/helper/ItemExtensions.java @@ -0,0 +1,316 @@ +/* + * Copyright (c) 2010-2026 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.core.model.script.helper; + +import java.util.Map; +import java.util.Set; + +import org.eclipse.jdt.annotation.NonNull; +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.openhab.core.common.registry.ManagedProvider; +import org.openhab.core.items.Item; +import org.openhab.core.items.Metadata; +import org.openhab.core.thing.Channel; +import org.openhab.core.thing.ChannelUID; +import org.openhab.core.thing.Thing; +import org.openhab.core.thing.link.ItemChannelLink; + +/** + * {@link ItemExtensions} provides DSL {@link Item} extensions. + * + * @author Ravi Nadahar - Initial contribution + */ +@NonNullByDefault +public class ItemExtensions { + + @NonNullByDefault({}) + public static void addMetadata(Item item, String namespace, String value) { + if (item == null) { + throw new IllegalArgumentException("item cannot be null"); + } + Items.addMetadata(item.getName(), namespace, value, (String) null); + } + + @NonNullByDefault({}) + public static void addMetadata(Item item, String namespace, String value, Object... configuration) { + if (item == null) { + throw new IllegalArgumentException("item cannot be null"); + } + Items.addMetadata(item.getName(), namespace, value, configuration); + } + + @NonNullByDefault({}) + public static void addMetadata(Item item, String namespace, String value, + @Nullable Map<@NonNull String, @NonNull Object> configuration) { + if (item == null) { + throw new IllegalArgumentException("item cannot be null"); + } + Items.addMetadata(item.getName(), namespace, value, configuration); + } + + @NonNullByDefault({}) + public static @Nullable Metadata getMetadata(Item item, String namespace) { + if (item == null) { + throw new IllegalArgumentException("item cannot be null"); + } + return Items.getMetadata(item.getName(), namespace); + } + + @NonNullByDefault({}) + public static @Nullable Metadata removeMetadata(Item item, String namespace) { + if (item == null) { + throw new IllegalArgumentException("item cannot be null"); + } + return Items.removeMetadata(item.getName(), namespace); + } + + @NonNullByDefault({}) + public static @Nullable Metadata updateMetadata(Item item, String namespace, String value) { + if (item == null) { + throw new IllegalArgumentException("item cannot be null"); + } + return Items.updateMetadata(item.getName(), namespace, value); + } + + @NonNullByDefault({}) + public static @Nullable Metadata updateMetadata(Item item, String namespace, String value, + Object... configuration) { + return Items.updateMetadata(item.getName(), namespace, value, configuration); + } + + @NonNullByDefault({}) + public static @Nullable Metadata updateMetadata(Item item, String namespace, String value, + @Nullable Map<@NonNull String, @NonNull Object> configuration) { + if (item == null) { + throw new IllegalArgumentException("item cannot be null"); + } + return Items.updateMetadata(item.getName(), namespace, value, configuration); + } + + /** + * Get the {@link ItemChannelLink}s that are linked to the specified item. + * + * @param item the {@link Item}. + * @return The {@link Set} of {@link ItemChannelLink}s. + */ + public static Set getChannelLinks(@Nullable Item item) { + return Channels.getLinks(item); + } + + /** + * Get the {@link ChannelUID}s of the channels that are bound to the specified {@link Item}. + * + * @param item the {@link Item}. + * @return The {@link Set} of {@link ChannelUID}s of the bound channels. + */ + public static Set getBoundChannels(Item item) { + return Channels.getBoundChannels(item); + } + + /** + * Get the {@link Thing}s that are bound to the specified {@link Item}. + * + * @param item the {@link Item}. + * @return The {@link Set} of bound {@link Thing}s. + * @throws IllegalArgumentException If {@code item} is {@code null}. + */ + public static Set getBoundThings(Item item) { + return Channels.getBoundThings(item); + } + + /** + * Check if the specified item has at least one link. + * + * @param item the {@link Item} to check. + * @return {@code true} if a link exists, {@code false} otherwise. + */ + public static boolean isLinked(@Nullable Item item) { + return Channels.isLinked(item); + } + + /** + * Check if the specified item and channel are linked. + * + * @param item the {@link Item} to check. + * @param channelUid the UID of the channel to check. + * @return {@code true} if a link exists, {@code false} otherwise. + */ + public static boolean isLinked(@Nullable Item item, @Nullable String channelUid) { + return Channels.isLinked(item, channelUid); + } + + /** + * Check if the specified item and channel are linked. + * + * @param item the {@link Item} to check. + * @param channelUid the {@link ChannelUID} of the channel to check. + * @return {@code true} if a link exists, {@code false} otherwise. + */ + public static boolean isLinked(@Nullable Item item, @Nullable ChannelUID channelUid) { + return Channels.isLinked(item, channelUid); + } + + /** + * Get an existing {@link ItemChannelLink} for the specified item and channel. + * + * @param item the {@link Item}. + * @param channelUid the {@link ChannelUID} of the channel. + * @return The {@link ItemChannelLink} or {@code null} if none were found. + * @throws IllegalArgumentException If {@code item} or {@code channelUid} is {@code null}. + */ + public static @Nullable ItemChannelLink getChannelLink(Item item, ChannelUID channelUid) { + return Channels.getLink(item, channelUid); + } + + /** + * Get an existing {@link ItemChannelLink} for the specified item and channel. + * + * @param item the {@link Item}. + * @param channelUid the UID of the channel. + * @return The {@link ItemChannelLink} or {@code null} if none were found. + * @throws IllegalArgumentException If {@code item} is {@code null}. + */ + public static @Nullable ItemChannelLink getChannelLink(Item item, @Nullable String channelUid) { + return Channels.getLink(item, channelUid); + } + + /** + * Add a new {@link ItemChannelLink} between an existing {@link Item} and a {@link Channel}. + * + * @param item the {@link Item} to link. + * @param channelUid the UID of the channel to link. + * @return The newly created {@link ItemChannelLink}. + * @throws IllegalArgumentException If {@code item} is {@code null}, the {@link ItemChannelLink} already exists, + * or {@code channelUid} is invalid. + * @throws IllegalStateException If a {@link ManagedProvider} isn't available. + */ + @NonNullByDefault({}) + public static ItemChannelLink addChannelLink(Item item, String channelUid) { + return Channels.addItemChannelLink(item, channelUid); + } + + /** + * Add a new {@link ItemChannelLink} between an existing {@link Item} and a {@link Channel}. + * + * @param item the {@link Item} to link. + * @param channelUid the UID of the channel to link. + * @param configProperties the pairs of {@link String}s and {@link Object}s that constitutes the configuration + * properties for the link. Must be in pairs, the first is the key, the second is the value. + * @return The newly created {@link ItemChannelLink}. + * @throws IllegalArgumentException If {@code item} is {@code null}, {@code channelUid} is invalid, the + * {@link ItemChannelLink} already exists, or if there is an odd number of {@code configProperties}, or + * if any of the keys aren't {@link String}s. + * @throws IllegalStateException If a {@link ManagedProvider} isn't available. + */ + public static ItemChannelLink addChannelLink(Item item, String channelUid, Object... configProperties) { + return Channels.addItemChannelLink(item, channelUid, configProperties); + } + + /** + * Add a new {@link ItemChannelLink} between an existing {@link Item} and a {@link Channel}. + * + * @param item the {@link Item} to link. + * @param channelUid the UID of the channel to link. + * @param configProperties the map of configuration properties for the link. + * @return The newly created {@link ItemChannelLink}. + * @throws IllegalArgumentException If {@code item} is {@code null}, the {@link ItemChannelLink} already exists, + * or {@code channelUid} is invalid. + * @throws IllegalStateException If a {@link ManagedProvider} isn't available. + */ + public static ItemChannelLink addChannelLink(Item item, String channelUid, + @Nullable Map<@Nullable String, @Nullable Object> configProperties) { + return Channels.addItemChannelLink(item, channelUid, configProperties); + } + + /** + * Add or replace a {@link ItemChannelLink} between an existing {@link Item} and a {@link Channel}. + * + * @param item the {@link Item} to link. + * @param channelUid the UID of the channel to link. + * @return The old {@link ItemChannelLink} if one existed, or {@code null}. + * @throws IllegalArgumentException If {@code item} is {@code null} or {@code channelUid} is invalid. + * @throws IllegalStateException If a {@link ManagedProvider} isn't available. + */ + public static @Nullable ItemChannelLink replaceChannelLink(Item item, String channelUid) { + return Channels.replaceItemChannelLink(item, channelUid); + } + + /** + * Add or replace a {@link ItemChannelLink} between an existing {@link Item} and a {@link Channel}. + * + * @param item the {@link Item} to link. + * @param channelUid the UID of the channel to link. + * @param configProperties the pairs of {@link String}s and {@link Object}s that constitutes the configuration + * properties for the link. Must be in pairs, the first is the key, the second is the value. + * @return The old {@link ItemChannelLink} if one existed, or {@code null}. + * @throws IllegalArgumentException If {@code item} is {@code null}, {@code channelUid} is invalid, or if there is + * an odd number of {@code configProperties}, or if any of the keys aren't {@link String}s. + * @throws IllegalStateException If a {@link ManagedProvider} isn't available. + */ + public static @Nullable ItemChannelLink replaceChannelLink(Item item, String channelUid, + Object... configProperties) { + return Channels.replaceItemChannelLink(item, channelUid, configProperties); + } + + /** + * Add or replace a {@link ItemChannelLink} between an existing {@link Item} and a {@link Channel}. + * + * @param item the {@link Item} to link. + * @param channelUid the UID of the channel to link. + * @param configProperties the map of configuration properties for the link. + * @return The old {@link ItemChannelLink} if one existed, or {@code null}. + * @throws IllegalArgumentException If {@code item} is {@code null}, or {@code channelUid} is invalid. + * @throws IllegalStateException If a {@link ManagedProvider} isn't available. + */ + public static @Nullable ItemChannelLink replaceChannelLink(Item item, String channelUid, + @Nullable Map<@Nullable String, @Nullable Object> configProperties) { + return Channels.replaceItemChannelLink(item, channelUid, configProperties); + } + + /** + * Remove a {@link ItemChannelLink} between an existing {@link Item} and a {@link Channel}. + * + * @param item the {@link Item} of the link to remove. + * @param channelUid the UID of the channel of the link to remove. + * @return The removed {@link ItemChannelLink} or {@code null} if none existed. + * @throws IllegalArgumentException If {@code item} or {@code channelUid} is {@code null}. + * @throws IllegalStateException If a {@link ManagedProvider} isn't available. + */ + public static @Nullable ItemChannelLink removeChannelLink(Item item, ChannelUID channelUid) { + return Channels.removeItemChannelLink(item, channelUid); + } + + /** + * Remove a {@link ItemChannelLink} between an existing {@link Item} and a {@link Channel}. + * + * @param item the {@link Item} of the link to remove. + * @param channelUid the UID of the channel of the link to remove. + * @return The removed {@link ItemChannelLink} or {@code null} if none existed. + * @throws IllegalArgumentException If {@code item} is {@code null}. + * @throws IllegalStateException If a {@link ManagedProvider} isn't available. + */ + public static @Nullable ItemChannelLink removeChannelLink(Item item, @Nullable String channelUid) { + return Channels.removeItemChannelLink(item, channelUid); + } + + /** + * Remove all managed links related to the specified item. + * + * @param item the {@link Item}. + * @return The number of removed links. + */ + public static int removeChannelLinks(@Nullable Item item) { + return Channels.removeLinksForItem(item); + } +} diff --git a/bundles/org.openhab.core.model.script/src/org/openhab/core/model/script/helper/Items.java b/bundles/org.openhab.core.model.script/src/org/openhab/core/model/script/helper/Items.java new file mode 100644 index 00000000000..8d6aa6de1d9 --- /dev/null +++ b/bundles/org.openhab.core.model.script/src/org/openhab/core/model/script/helper/Items.java @@ -0,0 +1,305 @@ +/* + * Copyright (c) 2010-2026 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.core.model.script.helper; + +import java.util.Collection; +import java.util.LinkedHashMap; +import java.util.Map; + +import org.eclipse.jdt.annotation.NonNull; +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.openhab.core.common.registry.ManagedProvider; +import org.openhab.core.items.Item; +import org.openhab.core.items.Metadata; +import org.openhab.core.items.MetadataKey; +import org.openhab.core.items.MetadataProvider; +import org.openhab.core.model.script.ScriptServiceUtil; + +/** + * {@link Items} provides DSL access to item and metadata manipulation. + * + * @author Ravi Nadahar - Initial contribution + */ +@NonNullByDefault +public class Items { + + /** + * Check whether a named item exists. + * + * @param itemName the item name. + * @return {@code true} if the item exists, {@code false} if it doesn't. + */ + public static boolean exists(String itemName) { + return ScriptServiceUtil.getItemRegistry().get(itemName) != null; + } + + /** + * Get an item by name. + * + * @param itemName the item name. + * @return The {@link Item} or {@code null} if it doesn't exist. + */ + public static @Nullable Item get(String itemName) { + return ScriptServiceUtil.getItemRegistry().get(itemName); + } + + /** + * Get all items. + * + * @return The {@link Collection} of {@link Item}s. + */ + public static Collection getAll() { + return ScriptServiceUtil.getItemRegistry().getAll(); + } + + /** + * Get all items that match a pattern using {@code ?} and {@code *}. + * + * @param pattern the pattern. + * @return The {@link Collection} of matching {@link Item}s. + */ + public static Collection getByPattern(String pattern) { + return ScriptServiceUtil.getItemRegistry().getItems(pattern); + } + + /** + * Get all items that have all the specified tags. + * + * @param tags the tags. + * @return The {@link Collection} of matching {@link Item}s. + */ + public static Collection getByTag(String... tags) { + return ScriptServiceUtil.getItemRegistry().getItemsByTag(tags); + } + + /** + * Get all items of the specified type. + *

+ * Types includes: {@code Call}, {@code Color}, {@code Contact}, {@code DateTime}, {@code Dimmer}, {@code Image}, + * {@code Location}, {@code Number}, {@code Player}, {@code Rollershutter}, {@code String} and {@code Switch}. + * + * @param type the type. + * @return The {@link Collection} of matching {@link Item}s. + */ + public static Collection getOfType(String type) { + return ScriptServiceUtil.getItemRegistry().getItemsOfType(type); + } + + /** + * Get all items of the specified type that also have all the specified tags. + *

+ * Types includes: {@code Call}, {@code Color}, {@code Contact}, {@code DateTime}, {@code Dimmer}, {@code Image}, + * {@code Location}, {@code Number}, {@code Player}, {@code Rollershutter}, {@code String} and {@code Switch}. + * + * @param type the type. + * @param tags the tags. + * @return The {@link Collection} of matching {@link Item}s. + */ + public static Collection getByTagAndType(String type, String... tags) { + return ScriptServiceUtil.getItemRegistry().getItemsByTagAndType(type, tags); + } + + /** + * Get item metadata for the specified namespace. + * + * @param itemName the item name. + * @param namespace the metadata namespace. + * @return The matching {@link Metadata} or {@code null}. + */ + @NonNullByDefault({}) + public static @Nullable Metadata getMetadata(String itemName, String namespace) { + if (itemName == null) { + throw new IllegalArgumentException("itemName cannot be null"); + } + if (namespace == null) { + throw new IllegalArgumentException("namespace cannot be null"); + } + return ScriptServiceUtil.getMetadataRegistry().get(new MetadataKey(namespace, itemName)); + } + + /** + * Add metadata to an item. + * + * @param itemName the item name. + * @param namespace the metadata namespace. + * @param value the metadata value. + * @throws IllegalArgumentException If {@code value} is {@code null}, {@code namespace} is {@code null}, or + * {@code itemName} is invalid. + * @throws UnsupportedOperationException If the metadata namespace has a reserved {@link MetadataProvider} that is + * not a {@link ManagedProvider}. + * @throws IllegalStateException If no {@code ManagedProvider} is available. + */ + public static void addMetadata(String itemName, String namespace, String value) { + addMetadata(itemName, namespace, value, (String) null); + } + + /** + * Add metadata to an item. + * + * @param itemName the item name. + * @param namespace the metadata namespace. + * @param value the metadata value. + * @param configProperties the pairs of {@link String}s and {@link Object}s that constitutes the configuration. + * @throws IllegalArgumentException If {@code namespace}, {@code itemName} or {@code value} is {@code null}, if + * {@code namespace} or {@code itemName} is invalid, or if there is an odd number of + * {@code configProperties}, or if any of the keys aren't {@link String}s. + * @throws UnsupportedOperationException If the metadata namespace has a reserved {@link MetadataProvider} that is + * not a {@link ManagedProvider}. + * @throws IllegalStateException If no {@code ManagedProvider} is available. + */ + public static void addMetadata(String itemName, String namespace, String value, Object... configProperties) { + addMetadata(itemName, namespace, value, parseObjectArray(configProperties)); + } + + /** + * Add metadata to an item. + * + * @param itemName the item name. + * @param namespace the metadata namespace. + * @param value the metadata value. + * @param configuration the {@link Map} of configuration properties that make up the configuration. + * @throws IllegalArgumentException If {@code namespace}, {@code itemName} or {@code value} is {@code null}, or if + * {@code namespace} or {@code itemName} is invalid. + * @throws UnsupportedOperationException If the metadata namespace has a reserved {@link MetadataProvider} that is + * not a {@link ManagedProvider}. + * @throws IllegalStateException If no {@code ManagedProvider} is available. + */ + @NonNullByDefault({}) + public static void addMetadata(String itemName, String namespace, String value, + @Nullable Map<@NonNull String, @NonNull Object> configuration) { + if (itemName == null) { + throw new IllegalArgumentException("itemName cannot be null"); + } + if (namespace == null) { + throw new IllegalArgumentException("namespace cannot be null"); + } + if (value == null) { + throw new IllegalArgumentException("value cannot be null"); + } + ScriptServiceUtil.getMetadataRegistry() + .add(new Metadata(new MetadataKey(namespace, itemName), value, configuration)); + } + + /** + * Remove metadata from an item for the specified namespace. + * + * @param itemName the item name. + * @param namespace the metadata namespace. + * @return The removed {@link Metadata} or {@code null} if no such metadata existed. + */ + @NonNullByDefault({}) + public static @Nullable Metadata removeMetadata(String itemName, String namespace) { + if (itemName == null) { + throw new IllegalArgumentException("itemName cannot be null"); + } + if (namespace == null) { + throw new IllegalArgumentException("namespace cannot be null"); + } + return ScriptServiceUtil.getMetadataRegistry().remove(new MetadataKey(namespace, itemName)); + } + + /** + * Update item metadata for the specified namespace. + * + * @param itemName the item name. + * @param namespace the metadata namespace. + * @param value the new metadata value. + * @return The old {@link Metadata} or {@code null} if no previous metadata existed. + * @throws IllegalArgumentException If {@code namespace}, {@code itemName} or {@code value} is {@code null}, or if + * {@code namespace} or {@code itemName} is invalid. + * @throws UnsupportedOperationException If the metadata namespace has a reserved {@link MetadataProvider} that is + * not a {@link ManagedProvider}. + * @throws IllegalStateException If no {@code ManagedProvider} is available. + */ + public static @Nullable Metadata updateMetadata(String itemName, String namespace, String value) { + return updateMetadata(itemName, namespace, value, (Map) null); + } + + /** + * Update item metadata for the specified namespace. + * + * @param itemName the item name. + * @param namespace the metadata namespace. + * @param value the new metadata value. + * @param configProperties the pairs of {@link String}s and {@link Object}s that constitutes the configuration. + * @return The old {@link Metadata} or {@code null} if no previous metadata existed. + * @throws IllegalArgumentException If {@code namespace}, {@code itemName} or {@code value} is {@code null}, if + * {@code namespace} or {@code itemName} is invalid, or if there is an odd number of + * {@code configProperties}, or if any of the keys aren't {@link String}s. + * @throws UnsupportedOperationException If the metadata namespace has a reserved {@link MetadataProvider} that is + * not a {@link ManagedProvider}. + * @throws IllegalStateException If no {@code ManagedProvider} is available. + */ + public static @Nullable Metadata updateMetadata(String itemName, String namespace, String value, + Object... configProperties) { + return updateMetadata(itemName, namespace, value, parseObjectArray(configProperties)); + } + + /** + * Update item metadata for the specified namespace. + * + * @param itemName the item name. + * @param namespace the metadata namespace. + * @param value the new metadata value. + * @param configuration the {@link Map} of configuration properties that make up the configuration. + * @return The old {@link Metadata} or {@code null} if no previous metadata existed. + * @throws IllegalArgumentException If {@code namespace}, {@code itemName} or {@code value} is {@code null}, or if + * {@code namespace} or {@code itemName} is invalid. + * @throws UnsupportedOperationException If the metadata namespace has a reserved {@link MetadataProvider} that is + * not a {@link ManagedProvider}. + * @throws IllegalStateException If no {@code ManagedProvider} is available. + */ + @NonNullByDefault({}) + public static @Nullable Metadata updateMetadata(String itemName, String namespace, String value, + @Nullable Map<@NonNull String, @NonNull Object> configuration) { + if (itemName == null) { + throw new IllegalArgumentException("itemName cannot be null"); + } + if (namespace == null) { + throw new IllegalArgumentException("namespace cannot be null"); + } + if (value == null) { + throw new IllegalArgumentException("value cannot be null"); + } + return ScriptServiceUtil.getMetadataRegistry() + .update(new Metadata(new MetadataKey(namespace, itemName), value, configuration)); + } + + /** + * Transforms pairs of {@link Object}s into a {@link Map}. The former of each pair (the key) must be a + * {@link String}. + * + * @param objects the array of {@link Object}s to transform. + * @return The resulting {@link Map}. + * @throws IllegalArgumentException If there is an odd number of objects, or if any of the keys aren't + * {@link String}s. + */ + private static Map parseObjectArray(Object @Nullable [] objects) throws IllegalArgumentException { + if (objects == null || objects.length == 0) { + return Map.of(); + } + if ((objects.length % 2) != 0) { + throw new IllegalArgumentException("There must be an even number of objects (" + objects.length + ')'); + } + Map result = new LinkedHashMap<>(); + for (int i = 0; i < objects.length; i += 2) { + if (objects[i] instanceof String key) { + result.put(key, objects[i + 1]); + } else { + throw new IllegalArgumentException("Keys must be strings: " + objects[i]); + } + } + return result; + } +} diff --git a/bundles/org.openhab.core.model.script/src/org/openhab/core/model/script/actions/RuleExtensions.java b/bundles/org.openhab.core.model.script/src/org/openhab/core/model/script/helper/RuleExtensions.java similarity index 68% rename from bundles/org.openhab.core.model.script/src/org/openhab/core/model/script/actions/RuleExtensions.java rename to bundles/org.openhab.core.model.script/src/org/openhab/core/model/script/helper/RuleExtensions.java index d0efe331b83..beed92ca57d 100644 --- a/bundles/org.openhab.core.model.script/src/org/openhab/core/model/script/actions/RuleExtensions.java +++ b/bundles/org.openhab.core.model.script/src/org/openhab/core/model/script/helper/RuleExtensions.java @@ -10,9 +10,8 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.core.model.script.actions; +package org.openhab.core.model.script.helper; -import java.util.LinkedHashMap; import java.util.Map; import org.eclipse.jdt.annotation.NonNullByDefault; @@ -22,9 +21,7 @@ import org.openhab.core.model.script.ScriptServiceUtil; /** - * {@link RuleExtensions} provides DSL access to things like OSGi instances, system registries and the ability to run - * other - * rules. + * {@link ItemExtensions} provides DSL {@link Rule} extensions. * * @author Ravi Nadahar - Initial contribution */ @@ -32,11 +29,10 @@ public class RuleExtensions { /** - * Run the rule with the specified UID. + * Run the specified rule. * - * @param ruleUID the UID of the rule to run. + * @param rule the {@link Rule} to run. * @return A copy of the rule context, including possible return values. - * @throws IllegalArgumentException If a rule with the specified UID doesn't exist. * @throws IllegalStateException If no {@link RuleManager} instance exists. */ public static Map run(Rule rule) { @@ -51,6 +47,14 @@ public static Map run(Rule rule) { return ruleManager.runNow(ruleUID); } + /** + * Run the specified rule while optionally taking conditions into account. + * + * @param rule the {@link Rule} to run. + * @param considerConditions {@code true} to not run the rule if its conditions don't qualify. + * @return A copy of the rule context, including possible return values. + * @throws IllegalStateException If no {@link RuleManager} instance exists. + */ public static Map run(Rule rule, boolean considerConditions) { RuleManager ruleManager = ScriptServiceUtil.getRuleManager(); if (ruleManager == null) { @@ -64,12 +68,11 @@ public static Map run(Rule rule, boolean considerConditions) { } /** - * Run the rule with the specified UID with the specified context. + * Run the specified rule with the specified context. * - * @param ruleUID the UID of the rule to run. + * @param rule the {@link Rule} to run. * @param context the {@link Map} of {@link String} and {@link Object} pairs that constitutes the context. * @return A copy of the rule context, including possible return values. - * @throws IllegalArgumentException If a rule with the specified UID doesn't exist. * @throws IllegalStateException If no {@link RuleManager} instance exists. */ public static Map run(Rule rule, Map context) { @@ -85,38 +88,26 @@ public static Map run(Rule rule, Map context) { } /** - * Run the rule with the specified UID with the specified context, while optionally taking conditions into - * account. + * Run the specified rule with the specified context, while optionally taking conditions into account. * - * @param ruleUID the UID of the rule to run. + * @param rule the {@link Rule} to run. * @param considerConditions {@code true} to not run the rule if its conditions don't qualify. * @param context the pairs of {@link String}s and {@link Object}s that constitutes the context. Must be in pairs, * the first is the key, the second is the value. * @return A copy of the rule context, including possible return values. - * @throws IllegalArgumentException If a rule with the specified UID doesn't exist. * @throws IllegalStateException If no {@link RuleManager} instance exists. */ public static Map run(Rule rule, boolean considerConditions, Object... context) { - RuleManager ruleManager = ScriptServiceUtil.getRuleManager(); - if (ruleManager == null) { - throw new IllegalStateException("RuleManager doesn't exist"); - } - String ruleUID = rule.getUID(); - if (ruleManager.getStatus(ruleUID) == null) { - throw new IllegalArgumentException("Rule with UID '" + ruleUID + "' doesn't exist"); - } - return ruleManager.runNow(ruleUID, considerConditions, parseObjectArray(context)); + return Rules.runRule(rule.getUID(), considerConditions, context); } /** - * Run the rule with the specified UID with the specified context, while optionally taking conditions into - * account. + * Run the specified rule with the specified context, while optionally taking conditions into account. * - * @param ruleUID the UID of the rule to run. + * @param rule the {@link Rule} to run. * @param considerConditions {@code true} to not run the rule if its conditions don't qualify. * @param context the {@link Map} of {@link String} and {@link Object} pairs that constitutes the context. * @return A copy of the rule context, including possible return values. - * @throws IllegalArgumentException If a rule with the specified UID doesn't exist. * @throws IllegalStateException If no {@link RuleManager} instance exists. */ public static Map run(Rule rule, boolean considerConditions, @@ -135,9 +126,8 @@ public static Map run(Rule rule, boolean considerConditions, /** * Check whether the specified rule is enabled. * - * @param ruleUID the UID of the rule to check. + * @param rule the {@link Rule} to check. * @return {@code true} if the rule is enabled, {@code false} otherwise. - * @throws IllegalArgumentException If a rule with the specified UID doesn't exist. * @throws IllegalStateException If no {@link RuleManager} instance exists. */ public static boolean isEnabled(Rule rule) { @@ -156,9 +146,8 @@ public static boolean isEnabled(Rule rule) { /** * Set whether the specified rule is enabled. * - * @param ruleUID the UID of the rule to enable or disable. + * @param rule the {@link Rule} to enable or disable. * @param enabled {@code true} to enable the rule, {@code false} to disable the rule. - * @throws IllegalArgumentException If a rule with the specified UID doesn't exist. * @throws IllegalStateException If no {@link RuleManager} instance exists. */ public static void setEnabled(Rule rule, boolean enabled) { @@ -168,22 +157,4 @@ public static void setEnabled(Rule rule, boolean enabled) { } ruleManager.setEnabled(rule.getUID(), enabled); } - - private static Map parseObjectArray(Object @Nullable [] objects) throws IllegalArgumentException { - if (objects == null || objects.length == 0) { - return Map.of(); - } - if ((objects.length % 2) != 0) { - throw new IllegalArgumentException("There must be an even number of objects (" + objects.length + ')'); - } - Map result = new LinkedHashMap<>(); - for (int i = 0; i < objects.length; i += 2) { - if (objects[i] instanceof String key) { - result.put(key, objects[i + 1]); - } else { - throw new IllegalArgumentException("Keys must be strings: " + objects[i]); - } - } - return result; - } } diff --git a/bundles/org.openhab.core.model.script/src/org/openhab/core/model/script/actions/Rules.java b/bundles/org.openhab.core.model.script/src/org/openhab/core/model/script/helper/Rules.java similarity index 71% rename from bundles/org.openhab.core.model.script/src/org/openhab/core/model/script/actions/Rules.java rename to bundles/org.openhab.core.model.script/src/org/openhab/core/model/script/helper/Rules.java index 4d98c70f2a0..6e05b2765b2 100644 --- a/bundles/org.openhab.core.model.script/src/org/openhab/core/model/script/actions/Rules.java +++ b/bundles/org.openhab.core.model.script/src/org/openhab/core/model/script/helper/Rules.java @@ -10,7 +10,7 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.core.model.script.actions; +package org.openhab.core.model.script.helper; import java.util.LinkedHashMap; import java.util.Map; @@ -20,79 +20,80 @@ import org.openhab.core.automation.Rule; import org.openhab.core.automation.RuleManager; import org.openhab.core.model.script.ScriptServiceUtil; -import org.openhab.core.model.script.engine.action.ActionDoc; /** - * {@link Rules} provides DSL access to things like OSGi instances, system registries and the ability to run other - * rules. + * {@link Rules} provides DSL access to rule operations. * * @author Ravi Nadahar - Initial contribution */ @NonNullByDefault public class Rules { - public static @Nullable Rule getRule(String ruleUID) { - return ScriptServiceUtil.getRuleRegistry().get(ruleUID); + /** + * Get a rule by UID. + * + * @param ruleUid + * @return The {@link Rule} or {@code null} if no matching rule exists. + */ + public static @Nullable Rule getRule(String ruleUid) { + return ScriptServiceUtil.getRuleRegistry().get(ruleUid); } /** * Run the rule with the specified UID. * - * @param ruleUID the UID of the rule to run. + * @param ruleUid the UID of the rule to run. * @return A copy of the rule context, including possible return values. * @throws IllegalArgumentException If a rule with the specified UID doesn't exist. * @throws IllegalStateException If no {@link RuleManager} instance exists. */ - @ActionDoc(text = "run the rule with the specified UID") - public static Map runRule(String ruleUID) { + public static Map runRule(String ruleUid) { RuleManager ruleManager = ScriptServiceUtil.getRuleManager(); if (ruleManager == null) { throw new IllegalStateException("RuleManager doesn't exist"); } - if (ruleManager.getStatus(ruleUID) == null) { - throw new IllegalArgumentException("Rule with UID '" + ruleUID + "' doesn't exist"); + if (ruleManager.getStatus(ruleUid) == null) { + throw new IllegalArgumentException("Rule with UID '" + ruleUid + "' doesn't exist"); } - return ruleManager.runNow(ruleUID); + return ruleManager.runNow(ruleUid); } - @ActionDoc(text = "run the rule with the specified UID and condition evaluation setting") - public static Map runRule(String ruleUID, boolean considerConditions) { + public static Map runRule(String ruleUid, boolean considerConditions) { RuleManager ruleManager = ScriptServiceUtil.getRuleManager(); if (ruleManager == null) { throw new IllegalStateException("RuleManager doesn't exist"); } - if (ruleManager.getStatus(ruleUID) == null) { - throw new IllegalArgumentException("Rule with UID '" + ruleUID + "' doesn't exist"); + if (ruleManager.getStatus(ruleUid) == null) { + throw new IllegalArgumentException("Rule with UID '" + ruleUid + "' doesn't exist"); } - return ruleManager.runNow(ruleUID, considerConditions, null); + return ruleManager.runNow(ruleUid, considerConditions, null); } /** * Run the rule with the specified UID with the specified context. * - * @param ruleUID the UID of the rule to run. + * @param ruleUid the UID of the rule to run. * @param context the {@link Map} of {@link String} and {@link Object} pairs that constitutes the context. * @return A copy of the rule context, including possible return values. * @throws IllegalArgumentException If a rule with the specified UID doesn't exist. * @throws IllegalStateException If no {@link RuleManager} instance exists. */ - @ActionDoc(text = "run the rule with the specified UID and context") - public static Map runRule(String ruleUID, Map context) { + public static Map runRule(String ruleUid, Map context) { RuleManager ruleManager = ScriptServiceUtil.getRuleManager(); if (ruleManager == null) { throw new IllegalStateException("RuleManager doesn't exist"); } - if (ruleManager.getStatus(ruleUID) == null) { - throw new IllegalArgumentException("Rule with UID '" + ruleUID + "' doesn't exist"); + if (ruleManager.getStatus(ruleUid) == null) { + throw new IllegalArgumentException("Rule with UID '" + ruleUid + "' doesn't exist"); } - return ruleManager.runNow(ruleUID, false, context); + return ruleManager.runNow(ruleUid, false, context); } /** * Run the rule with the specified UID with the specified context, while optionally taking conditions into * account. * - * @param ruleUID the UID of the rule to run. + * @param ruleUid the UID of the rule to run. * @param considerConditions {@code true} to not run the rule if its conditions don't qualify. * @param context the pairs of {@link String}s and {@link Object}s that constitutes the context. Must be in pairs, * the first is the key, the second is the value. @@ -100,58 +101,55 @@ public static Map runRule(String ruleUID, Map co * @throws IllegalArgumentException If a rule with the specified UID doesn't exist. * @throws IllegalStateException If no {@link RuleManager} instance exists. */ - @ActionDoc(text = "run the rule with the specified UID, condition evaluation setting and context") - public static Map runRule(String ruleUID, boolean considerConditions, Object... context) { + public static Map runRule(String ruleUid, boolean considerConditions, Object... context) { RuleManager ruleManager = ScriptServiceUtil.getRuleManager(); if (ruleManager == null) { throw new IllegalStateException("RuleManager doesn't exist"); } - if (ruleManager.getStatus(ruleUID) == null) { - throw new IllegalArgumentException("Rule with UID '" + ruleUID + "' doesn't exist"); + if (ruleManager.getStatus(ruleUid) == null) { + throw new IllegalArgumentException("Rule with UID '" + ruleUid + "' doesn't exist"); } - return ruleManager.runNow(ruleUID, considerConditions, parseObjectArray(context)); + return ruleManager.runNow(ruleUid, considerConditions, parseObjectArray(context)); } /** * Run the rule with the specified UID with the specified context, while optionally taking conditions into * account. * - * @param ruleUID the UID of the rule to run. + * @param ruleUid the UID of the rule to run. * @param considerConditions {@code true} to not run the rule if its conditions don't qualify. * @param context the {@link Map} of {@link String} and {@link Object} pairs that constitutes the context. * @return A copy of the rule context, including possible return values. * @throws IllegalArgumentException If a rule with the specified UID doesn't exist. * @throws IllegalStateException If no {@link RuleManager} instance exists. */ - @ActionDoc(text = "run the rule with the specified UID, condition evaluation setting and context") - public static Map runRule(String ruleUID, boolean considerConditions, Map context) { + public static Map runRule(String ruleUid, boolean considerConditions, Map context) { RuleManager ruleManager = ScriptServiceUtil.getRuleManager(); if (ruleManager == null) { throw new IllegalStateException("RuleManager doesn't exist"); } - if (ruleManager.getStatus(ruleUID) == null) { - throw new IllegalArgumentException("Rule with UID '" + ruleUID + "' doesn't exist"); + if (ruleManager.getStatus(ruleUid) == null) { + throw new IllegalArgumentException("Rule with UID '" + ruleUid + "' doesn't exist"); } - return ruleManager.runNow(ruleUID, considerConditions, context); + return ruleManager.runNow(ruleUid, considerConditions, context); } /** * Check whether the specified rule is enabled. * - * @param ruleUID the UID of the rule to check. + * @param ruleUid the UID of the rule to check. * @return {@code true} if the rule is enabled, {@code false} otherwise. * @throws IllegalArgumentException If a rule with the specified UID doesn't exist. * @throws IllegalStateException If no {@link RuleManager} instance exists. */ - @ActionDoc(text = "check whether the specified rule rule is enabled") - public static boolean isRuleEnabled(String ruleUID) { + public static boolean isRuleEnabled(String ruleUid) { RuleManager ruleManager = ScriptServiceUtil.getRuleManager(); if (ruleManager == null) { throw new IllegalStateException("RuleManager doesn't exist"); } - Boolean result = ruleManager.isEnabled(ruleUID); + Boolean result = ruleManager.isEnabled(ruleUid); if (result == null) { - throw new IllegalArgumentException("Rule with UID '" + ruleUID + "' doesn't exist"); + throw new IllegalArgumentException("Rule with UID '" + ruleUid + "' doesn't exist"); } return result.booleanValue(); } @@ -159,28 +157,35 @@ public static boolean isRuleEnabled(String ruleUID) { /** * Set whether the specified rule is enabled. * - * @param ruleUID the UID of the rule to enable or disable. + * @param ruleUid the UID of the rule to enable or disable. * @param enabled {@code true} to enable the rule, {@code false} to disable the rule. * @throws IllegalArgumentException If a rule with the specified UID doesn't exist. * @throws IllegalStateException If no {@link RuleManager} instance exists. */ - @ActionDoc(text = "set whether the specified rule is enabled") - public static void setRuleEnabled(String ruleUID, boolean enabled) { + public static void setRuleEnabled(String ruleUid, boolean enabled) { RuleManager ruleManager = ScriptServiceUtil.getRuleManager(); if (ruleManager == null) { throw new IllegalStateException("RuleManager doesn't exist"); } - ruleManager.setEnabled(ruleUID, enabled); + ruleManager.setEnabled(ruleUid, enabled); } /** * @return The {@link RuleManager} or {@code null}. */ - @ActionDoc(text = "get the rule manager") public static @Nullable RuleManager getRuleManager() { return ScriptServiceUtil.getRuleManager(); } + /** + * Transforms pairs of {@link Object}s into a {@link Map}. The former of each pair (the key) must be a + * {@link String}. + * + * @param objects the array of {@link Object}s to transform. + * @return The resulting {@link Map}. + * @throws IllegalArgumentException If there is an odd number of objects, or if any of the keys aren't + * {@link String}s. + */ private static Map parseObjectArray(Object @Nullable [] objects) throws IllegalArgumentException { if (objects == null || objects.length == 0) { return Map.of(); diff --git a/bundles/org.openhab.core.model.script/src/org/openhab/core/model/script/internal/engine/action/ThingActionService.java b/bundles/org.openhab.core.model.script/src/org/openhab/core/model/script/internal/engine/action/ThingActionService.java index 90f75a3a402..57e8098e4e3 100644 --- a/bundles/org.openhab.core.model.script/src/org/openhab/core/model/script/internal/engine/action/ThingActionService.java +++ b/bundles/org.openhab.core.model.script/src/org/openhab/core/model/script/internal/engine/action/ThingActionService.java @@ -56,8 +56,12 @@ public Class getActionClass() { } public static @Nullable ThingStatusInfo getThingStatusInfo(String thingUid) { + ThingRegistry registry = thingRegistry; + if (registry == null) { + return null; + } ThingUID uid = new ThingUID(thingUid); - Thing thing = thingRegistry.get(uid); + Thing thing = registry.get(uid); if (thing != null) { return thing.getStatusInfo(); @@ -75,8 +79,12 @@ public Class getActionClass() { * @return actions the actions instance or null, if not available */ public static @Nullable ThingActions getActions(String scope, String thingUid) { + ThingRegistry registry = thingRegistry; + if (registry == null) { + return null; + } ThingUID uid = new ThingUID(thingUid); - Thing thing = thingRegistry.get(uid); + Thing thing = registry.get(uid); if (thing != null) { ThingHandler handler = thing.getHandler(); if (handler != null) { @@ -86,6 +94,24 @@ public Class getActionClass() { return null; } + /** + * Gets an actions instance of a certain scope for a given {@link Thing}. + * + * @param thing the {@link Thing}. + * @param scope the action scope. + * + * @return actions the actions instance or null, if not available. + */ + public static @Nullable ThingActions getActions(@Nullable Thing thing, String scope) { + if (thing != null) { + ThingHandler handler = thing.getHandler(); + if (handler != null) { + return THING_ACTIONS_MAP.get(getKey(scope, thing.getUID().getAsString())); + } + } + return null; + } + @Reference(policy = ReferencePolicy.DYNAMIC, cardinality = ReferenceCardinality.MULTIPLE) public void addThingActions(ThingActions thingActions) { String key = getKey(thingActions); diff --git a/bundles/org.openhab.core.model.script/src/org/openhab/core/model/script/scoping/ActionClassLoader.java b/bundles/org.openhab.core.model.script/src/org/openhab/core/model/script/scoping/ActionClassLoader.java index bf9bb596c1a..5ac7bf9d014 100644 --- a/bundles/org.openhab.core.model.script/src/org/openhab/core/model/script/scoping/ActionClassLoader.java +++ b/bundles/org.openhab.core.model.script/src/org/openhab/core/model/script/scoping/ActionClassLoader.java @@ -31,7 +31,11 @@ public ActionClassLoader(ClassLoader cl) { @Override public Class loadClass(String name) throws ClassNotFoundException { try { - return getParent().loadClass(name); + ClassLoader parent = getParent(); + if (parent == null) { + throw new ClassNotFoundException("Unable to load '" + name + "' because parent is null"); + } + return parent.loadClass(name); } catch (ClassNotFoundException e) { for (ActionService actionService : ScriptServiceUtil.getActionServices()) { if (actionService.getActionClassName().equals(name)) { @@ -43,7 +47,7 @@ public Class loadClass(String name) throws ClassNotFoundException { return actions.getClass(); } } + throw e; } - throw new ClassNotFoundException(); } } diff --git a/bundles/org.openhab.core.model.script/src/org/openhab/core/model/script/scoping/ScriptImplicitlyImportedTypes.java b/bundles/org.openhab.core.model.script/src/org/openhab/core/model/script/scoping/ScriptImplicitlyImportedTypes.java index 93405f09f1b..2c1ba5d39a9 100644 --- a/bundles/org.openhab.core.model.script/src/org/openhab/core/model/script/scoping/ScriptImplicitlyImportedTypes.java +++ b/bundles/org.openhab.core.model.script/src/org/openhab/core/model/script/scoping/ScriptImplicitlyImportedTypes.java @@ -28,22 +28,20 @@ import org.openhab.core.library.unit.MetricPrefix; import org.openhab.core.library.unit.SIUnits; import org.openhab.core.library.unit.Units; +import org.openhab.core.model.script.ScriptServiceUtil; import org.openhab.core.model.script.actions.BusEvent; import org.openhab.core.model.script.actions.CoreUtil; import org.openhab.core.model.script.actions.Exec; import org.openhab.core.model.script.actions.HTTP; -import org.openhab.core.model.script.actions.ItemExtensions; -import org.openhab.core.model.script.actions.Items; import org.openhab.core.model.script.actions.Log; import org.openhab.core.model.script.actions.Ping; -import org.openhab.core.model.script.actions.Registries; -import org.openhab.core.model.script.actions.RuleExtensions; -import org.openhab.core.model.script.actions.Rules; import org.openhab.core.model.script.actions.ScriptExecution; import org.openhab.core.model.script.actions.Transformation; import org.openhab.core.model.script.engine.IActionServiceProvider; import org.openhab.core.model.script.engine.IThingActionsProvider; import org.openhab.core.model.script.engine.action.ActionService; +import org.openhab.core.model.script.helper.ItemExtensions; +import org.openhab.core.model.script.helper.RuleExtensions; import org.openhab.core.model.script.lib.NumberExtensions; import org.openhab.core.thing.binding.ThingActions; @@ -99,9 +97,7 @@ protected List> getStaticImportClasses() { result.add(Ping.class); result.add(Transformation.class); result.add(ScriptExecution.class); - result.add(Items.class); - result.add(Registries.class); - result.add(Rules.class); + result.add(ScriptServiceUtil.class); result.add(URLEncoder.class); result.add(CoreUtil.class); diff --git a/bundles/org.openhab.core.model.script/src/org/openhab/core/model/script/scoping/ScriptImportSectionNamespaceScopeProvider.java b/bundles/org.openhab.core.model.script/src/org/openhab/core/model/script/scoping/ScriptImportSectionNamespaceScopeProvider.java index c0a78b64ab4..427f7206af4 100644 --- a/bundles/org.openhab.core.model.script/src/org/openhab/core/model/script/scoping/ScriptImportSectionNamespaceScopeProvider.java +++ b/bundles/org.openhab.core.model.script/src/org/openhab/core/model/script/scoping/ScriptImportSectionNamespaceScopeProvider.java @@ -30,8 +30,19 @@ public class ScriptImportSectionNamespaceScopeProvider extends XImportSectionNam public static final QualifiedName CORE_LIBRARY_ITEMS_PACKAGE = QualifiedName.create("org", "openhab", "core", "library", "items"); public static final QualifiedName CORE_ITEMS_PACKAGE = QualifiedName.create("org", "openhab", "core", "items"); + public static final QualifiedName CORE_THING_PACKAGE = QualifiedName.create("org", "openhab", "core", "thing"); + public static final QualifiedName CORE_THING_LINK_ITEMCHANNELLINK_CLASS = QualifiedName.create("org", "openhab", + "core", "thing", "link", "ItemChannelLink"); + public static final QualifiedName CORE_THING_LINK_ITEMCHANNELLINKREGISTRY_CLASS = QualifiedName.create("org", + "openhab", "core", "thing", "link", "ItemChannelLinkRegistry"); public static final QualifiedName CORE_PERSISTENCE_PACKAGE = QualifiedName.create("org", "openhab", "core", "persistence"); + public static final QualifiedName CORE_PERSISTENCE_HISTORICITEM_CLASS = QualifiedName.create("org", "openhab", + "core", "persistence", "HistoricItem"); + public static final QualifiedName CORE_PERSISTENCE_PERSISTENCESERVICE_INTERFACE = QualifiedName.create("org", + "openhab", "core", "persistence", "PersistenceService"); + public static final QualifiedName CORE_PERSISTENCE_PERSISTENCEEXTENSIONS_CLASS = QualifiedName.create("org", + "openhab", "core", "persistence", "extensions", "PersistenceExtensions"); public static final QualifiedName CORE_PERSISTENCE_RIEMANNTYPE_CLASS = QualifiedName.create("org", "openhab", "core", "persistence", "extensions", "PersistenceExtensions", "RiemannType"); public static final QualifiedName MODEL_SCRIPT_ACTIONS_PACKAGE = QualifiedName.create("org", "openhab", "core", @@ -39,6 +50,12 @@ public class ScriptImportSectionNamespaceScopeProvider extends XImportSectionNam public static final QualifiedName TIME_PACKAGE = QualifiedName.create("java", "time"); public static final QualifiedName CHRONOUNIT_CLASS = QualifiedName.create("java", "time", "temporal", "ChronoUnit"); public static final QualifiedName QUANTITY_PACKAGE = QualifiedName.create("javax", "measure", "quantity"); + public static final QualifiedName CHANNELS_CLASS = QualifiedName.create("org", "openhab", "core", "model", "script", + "helper", "Channels"); + public static final QualifiedName ITEMS_CLASS = QualifiedName.create("org", "openhab", "core", "model", "script", + "helper", "Items"); + public static final QualifiedName RULES_CLASS = QualifiedName.create("org", "openhab", "core", "model", "script", + "helper", "Rules"); @Override protected List getImplicitImports(boolean ignoreCase) { @@ -47,12 +64,21 @@ protected List getImplicitImports(boolean ignoreCase) { implicitImports.add(doCreateImportNormalizer(CORE_LIBRARY_TYPES_PACKAGE, true, false)); implicitImports.add(doCreateImportNormalizer(CORE_LIBRARY_ITEMS_PACKAGE, true, false)); implicitImports.add(doCreateImportNormalizer(CORE_ITEMS_PACKAGE, true, false)); + implicitImports.add(doCreateImportNormalizer(CORE_THING_PACKAGE, true, false)); + implicitImports.add(doCreateImportNormalizer(CORE_THING_LINK_ITEMCHANNELLINK_CLASS, false, false)); + implicitImports.add(doCreateImportNormalizer(CORE_THING_LINK_ITEMCHANNELLINKREGISTRY_CLASS, false, false)); implicitImports.add(doCreateImportNormalizer(CORE_PERSISTENCE_PACKAGE, true, false)); + implicitImports.add(doCreateImportNormalizer(CORE_PERSISTENCE_HISTORICITEM_CLASS, false, false)); + implicitImports.add(doCreateImportNormalizer(CORE_PERSISTENCE_PERSISTENCESERVICE_INTERFACE, false, false)); + implicitImports.add(doCreateImportNormalizer(CORE_PERSISTENCE_PERSISTENCEEXTENSIONS_CLASS, false, false)); implicitImports.add(doCreateImportNormalizer(CORE_PERSISTENCE_RIEMANNTYPE_CLASS, false, false)); implicitImports.add(doCreateImportNormalizer(MODEL_SCRIPT_ACTIONS_PACKAGE, true, false)); implicitImports.add(doCreateImportNormalizer(TIME_PACKAGE, true, false)); implicitImports.add(doCreateImportNormalizer(CHRONOUNIT_CLASS, false, false)); implicitImports.add(doCreateImportNormalizer(QUANTITY_PACKAGE, true, false)); + implicitImports.add(doCreateImportNormalizer(CHANNELS_CLASS, false, false)); + implicitImports.add(doCreateImportNormalizer(ITEMS_CLASS, false, false)); + implicitImports.add(doCreateImportNormalizer(RULES_CLASS, false, false)); return implicitImports; } } From 96886d62c55501cdeb997d5c7118c5539064c8c2 Mon Sep 17 00:00:00 2001 From: Ravi Nadahar Date: Wed, 20 May 2026 19:32:46 +0200 Subject: [PATCH 10/16] Add missed JavaDocs Signed-off-by: Ravi Nadahar --- .../model/script/helper/ItemExtensions.java | 130 +++++++++++++----- 1 file changed, 98 insertions(+), 32 deletions(-) diff --git a/bundles/org.openhab.core.model.script/src/org/openhab/core/model/script/helper/ItemExtensions.java b/bundles/org.openhab.core.model.script/src/org/openhab/core/model/script/helper/ItemExtensions.java index e6c9237d9e1..4da64b2e748 100644 --- a/bundles/org.openhab.core.model.script/src/org/openhab/core/model/script/helper/ItemExtensions.java +++ b/bundles/org.openhab.core.model.script/src/org/openhab/core/model/script/helper/ItemExtensions.java @@ -15,12 +15,12 @@ import java.util.Map; import java.util.Set; -import org.eclipse.jdt.annotation.NonNull; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; import org.openhab.core.common.registry.ManagedProvider; import org.openhab.core.items.Item; import org.openhab.core.items.Metadata; +import org.openhab.core.items.MetadataProvider; import org.openhab.core.thing.Channel; import org.openhab.core.thing.ChannelUID; import org.openhab.core.thing.Thing; @@ -34,67 +34,133 @@ @NonNullByDefault public class ItemExtensions { - @NonNullByDefault({}) + /** + * Add metadata to an item. + * + * @param item the {@link Item}. + * @param namespace the metadata namespace. + * @param value the metadata value. + * @param configProperties the pairs of {@link String}s and {@link Object}s that constitutes the configuration. + * @throws IllegalArgumentException If {@code namespace} or {@code value} is {@code null}, if {@code namespace} is + * invalid, or if there is an odd number of {@code configProperties}, or if any of the keys aren't + * {@link String}s. + * @throws UnsupportedOperationException If the metadata namespace has a reserved {@link MetadataProvider} that is + * not a {@link ManagedProvider}. + * @throws IllegalStateException If no {@code ManagedProvider} is available. + */ public static void addMetadata(Item item, String namespace, String value) { - if (item == null) { - throw new IllegalArgumentException("item cannot be null"); - } Items.addMetadata(item.getName(), namespace, value, (String) null); } - @NonNullByDefault({}) + /** + * Add metadata to an item. + * + * @param item the {@link Item}. + * @param namespace the metadata namespace. + * @param value the metadata value. + * @param configProperties the pairs of {@link String}s and {@link Object}s that constitutes the configuration. + * @throws IllegalArgumentException If {@code namespace} or {@code value} is {@code null}, if {@code namespace} is + * invalid, or if there is an odd number of {@code configProperties}, or if any of the keys aren't + * {@link String}s. + * @throws UnsupportedOperationException If the metadata namespace has a reserved {@link MetadataProvider} that is + * not a {@link ManagedProvider}. + * @throws IllegalStateException If no {@code ManagedProvider} is available. + */ public static void addMetadata(Item item, String namespace, String value, Object... configuration) { - if (item == null) { - throw new IllegalArgumentException("item cannot be null"); - } Items.addMetadata(item.getName(), namespace, value, configuration); } - @NonNullByDefault({}) + /** + * Add metadata to an item. + * + * @param item the {@link Item}. + * @param namespace the metadata namespace. + * @param value the metadata value. + * @param configuration the {@link Map} of configuration properties that make up the configuration. + * @throws IllegalArgumentException If {@code namespace} or {@code value} is {@code null}, or if {@code namespace} + * is invalid. + * @throws UnsupportedOperationException If the metadata namespace has a reserved {@link MetadataProvider} that is + * not a {@link ManagedProvider}. + * @throws IllegalStateException If no {@code ManagedProvider} is available. + */ public static void addMetadata(Item item, String namespace, String value, - @Nullable Map<@NonNull String, @NonNull Object> configuration) { - if (item == null) { - throw new IllegalArgumentException("item cannot be null"); - } + @Nullable Map configuration) { Items.addMetadata(item.getName(), namespace, value, configuration); } - @NonNullByDefault({}) + /** + * Get item metadata for the specified namespace. + * + * @param item the {@link Item}. + * @param namespace the metadata namespace. + * @return The matching {@link Metadata} or {@code null}. + */ public static @Nullable Metadata getMetadata(Item item, String namespace) { - if (item == null) { - throw new IllegalArgumentException("item cannot be null"); - } return Items.getMetadata(item.getName(), namespace); } - @NonNullByDefault({}) + /** + * Remove metadata from an item for the specified namespace. + * + * @param item the {@link Item}. + * @param namespace the metadata namespace. + * @return The removed {@link Metadata} or {@code null} if no such metadata existed. + */ public static @Nullable Metadata removeMetadata(Item item, String namespace) { - if (item == null) { - throw new IllegalArgumentException("item cannot be null"); - } return Items.removeMetadata(item.getName(), namespace); } - @NonNullByDefault({}) + /** + * Update item metadata for the specified namespace. + * + * @param item the {@link Item}. + * @param namespace the metadata namespace. + * @param value the new metadata value. + * @return The old {@link Metadata} or {@code null} if no previous metadata existed. + * @throws IllegalArgumentException If {@code namespace} or {@code value} is {@code null}, or if {@code namespace} + * is invalid. + * @throws UnsupportedOperationException If the metadata namespace has a reserved {@link MetadataProvider} that is + * not a {@link ManagedProvider}. + * @throws IllegalStateException If no {@code ManagedProvider} is available. + */ public static @Nullable Metadata updateMetadata(Item item, String namespace, String value) { - if (item == null) { - throw new IllegalArgumentException("item cannot be null"); - } return Items.updateMetadata(item.getName(), namespace, value); } - @NonNullByDefault({}) + /** + * Update item metadata for the specified namespace. + * + * @param item the {@link Item}. + * @param namespace the metadata namespace. + * @param value the new metadata value. + * @return The old {@link Metadata} or {@code null} if no previous metadata existed. + * @throws IllegalArgumentException If {@code namespace} or {@code value} is {@code null}, or is {@code namespace} + * is invalid. + * @throws UnsupportedOperationException If the metadata namespace has a reserved {@link MetadataProvider} that is + * not a {@link ManagedProvider}. + * @throws IllegalStateException If no {@code ManagedProvider} is available. + */ public static @Nullable Metadata updateMetadata(Item item, String namespace, String value, Object... configuration) { return Items.updateMetadata(item.getName(), namespace, value, configuration); } - @NonNullByDefault({}) + /** + * Update item metadata for the specified namespace. + * + * @param item the {@link Item}. + * @param namespace the metadata namespace. + * @param value the new metadata value. + * @param configuration the {@link Map} of configuration properties that make up the configuration. + * @return The old {@link Metadata} or {@code null} if no previous metadata existed. + * @throws IllegalArgumentException If {@code namespace} or {@code value} is {@code null}, or if {@code namespace} + * is invalid. + * @throws UnsupportedOperationException If the metadata namespace has a reserved {@link MetadataProvider} that is + * not a {@link ManagedProvider}. + * @throws IllegalStateException If no {@code ManagedProvider} is available. + */ public static @Nullable Metadata updateMetadata(Item item, String namespace, String value, - @Nullable Map<@NonNull String, @NonNull Object> configuration) { - if (item == null) { - throw new IllegalArgumentException("item cannot be null"); - } + @Nullable Map configuration) { return Items.updateMetadata(item.getName(), namespace, value, configuration); } From 9372b3e3624a7814780989e13ea2d3ba8ccfb102 Mon Sep 17 00:00:00 2001 From: Ravi Nadahar Date: Wed, 20 May 2026 20:12:45 +0200 Subject: [PATCH 11/16] Fix missed JavaDocs Signed-off-by: Ravi Nadahar --- .../openhab/core/model/script/helper/ItemExtensions.java | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/bundles/org.openhab.core.model.script/src/org/openhab/core/model/script/helper/ItemExtensions.java b/bundles/org.openhab.core.model.script/src/org/openhab/core/model/script/helper/ItemExtensions.java index 4da64b2e748..8322e1864b4 100644 --- a/bundles/org.openhab.core.model.script/src/org/openhab/core/model/script/helper/ItemExtensions.java +++ b/bundles/org.openhab.core.model.script/src/org/openhab/core/model/script/helper/ItemExtensions.java @@ -40,10 +40,8 @@ public class ItemExtensions { * @param item the {@link Item}. * @param namespace the metadata namespace. * @param value the metadata value. - * @param configProperties the pairs of {@link String}s and {@link Object}s that constitutes the configuration. * @throws IllegalArgumentException If {@code namespace} or {@code value} is {@code null}, if {@code namespace} is - * invalid, or if there is an odd number of {@code configProperties}, or if any of the keys aren't - * {@link String}s. + * invalid. * @throws UnsupportedOperationException If the metadata namespace has a reserved {@link MetadataProvider} that is * not a {@link ManagedProvider}. * @throws IllegalStateException If no {@code ManagedProvider} is available. @@ -66,8 +64,8 @@ public static void addMetadata(Item item, String namespace, String value) { * not a {@link ManagedProvider}. * @throws IllegalStateException If no {@code ManagedProvider} is available. */ - public static void addMetadata(Item item, String namespace, String value, Object... configuration) { - Items.addMetadata(item.getName(), namespace, value, configuration); + public static void addMetadata(Item item, String namespace, String value, Object... configProperties) { + Items.addMetadata(item.getName(), namespace, value, configProperties); } /** From 46eba839f05057fba4c24043aa25d99e7525f17f Mon Sep 17 00:00:00 2001 From: Ravi Nadahar Date: Fri, 22 May 2026 23:10:42 +0200 Subject: [PATCH 12/16] Modify implicit imports: - Remove unnecessary, left-over persistence imports - Add potentially useful import considering the new stricter class resolution mechanism in the recently bumped Xtext version Signed-off-by: Ravi Nadahar --- ...ptImportSectionNamespaceScopeProvider.java | 20 +++++++++---------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/bundles/org.openhab.core.model.script/src/org/openhab/core/model/script/scoping/ScriptImportSectionNamespaceScopeProvider.java b/bundles/org.openhab.core.model.script/src/org/openhab/core/model/script/scoping/ScriptImportSectionNamespaceScopeProvider.java index 427f7206af4..2c89e217d91 100644 --- a/bundles/org.openhab.core.model.script/src/org/openhab/core/model/script/scoping/ScriptImportSectionNamespaceScopeProvider.java +++ b/bundles/org.openhab.core.model.script/src/org/openhab/core/model/script/scoping/ScriptImportSectionNamespaceScopeProvider.java @@ -29,6 +29,9 @@ public class ScriptImportSectionNamespaceScopeProvider extends XImportSectionNam "library", "types"); public static final QualifiedName CORE_LIBRARY_ITEMS_PACKAGE = QualifiedName.create("org", "openhab", "core", "library", "items"); + public static final QualifiedName CORE_TYPES_PACKAGE = QualifiedName.create("org", "openhab", "core", "types"); + public static final QualifiedName CORE_TYPES_UTIL_PACKAGE = QualifiedName.create("org", "openhab", "core", "types", + "util"); public static final QualifiedName CORE_ITEMS_PACKAGE = QualifiedName.create("org", "openhab", "core", "items"); public static final QualifiedName CORE_THING_PACKAGE = QualifiedName.create("org", "openhab", "core", "thing"); public static final QualifiedName CORE_THING_LINK_ITEMCHANNELLINK_CLASS = QualifiedName.create("org", "openhab", @@ -37,18 +40,13 @@ public class ScriptImportSectionNamespaceScopeProvider extends XImportSectionNam "openhab", "core", "thing", "link", "ItemChannelLinkRegistry"); public static final QualifiedName CORE_PERSISTENCE_PACKAGE = QualifiedName.create("org", "openhab", "core", "persistence"); - public static final QualifiedName CORE_PERSISTENCE_HISTORICITEM_CLASS = QualifiedName.create("org", "openhab", - "core", "persistence", "HistoricItem"); - public static final QualifiedName CORE_PERSISTENCE_PERSISTENCESERVICE_INTERFACE = QualifiedName.create("org", - "openhab", "core", "persistence", "PersistenceService"); - public static final QualifiedName CORE_PERSISTENCE_PERSISTENCEEXTENSIONS_CLASS = QualifiedName.create("org", - "openhab", "core", "persistence", "extensions", "PersistenceExtensions"); public static final QualifiedName CORE_PERSISTENCE_RIEMANNTYPE_CLASS = QualifiedName.create("org", "openhab", "core", "persistence", "extensions", "PersistenceExtensions", "RiemannType"); public static final QualifiedName MODEL_SCRIPT_ACTIONS_PACKAGE = QualifiedName.create("org", "openhab", "core", "model", "script", "actions"); public static final QualifiedName TIME_PACKAGE = QualifiedName.create("java", "time"); - public static final QualifiedName CHRONOUNIT_CLASS = QualifiedName.create("java", "time", "temporal", "ChronoUnit"); + public static final QualifiedName TIME_FORMAT_PACKAGE = QualifiedName.create("java", "time", "format"); + public static final QualifiedName TIME_TEMPORAL_PACKAGE = QualifiedName.create("java", "time", "temporal"); public static final QualifiedName QUANTITY_PACKAGE = QualifiedName.create("javax", "measure", "quantity"); public static final QualifiedName CHANNELS_CLASS = QualifiedName.create("org", "openhab", "core", "model", "script", "helper", "Channels"); @@ -63,18 +61,18 @@ protected List getImplicitImports(boolean ignoreCase) { implicitImports.add(doCreateImportNormalizer(CORE_LIBRARY_UNITS_PACKAGE, true, false)); implicitImports.add(doCreateImportNormalizer(CORE_LIBRARY_TYPES_PACKAGE, true, false)); implicitImports.add(doCreateImportNormalizer(CORE_LIBRARY_ITEMS_PACKAGE, true, false)); + implicitImports.add(doCreateImportNormalizer(CORE_TYPES_PACKAGE, true, false)); + implicitImports.add(doCreateImportNormalizer(CORE_TYPES_UTIL_PACKAGE, true, false)); implicitImports.add(doCreateImportNormalizer(CORE_ITEMS_PACKAGE, true, false)); implicitImports.add(doCreateImportNormalizer(CORE_THING_PACKAGE, true, false)); implicitImports.add(doCreateImportNormalizer(CORE_THING_LINK_ITEMCHANNELLINK_CLASS, false, false)); implicitImports.add(doCreateImportNormalizer(CORE_THING_LINK_ITEMCHANNELLINKREGISTRY_CLASS, false, false)); implicitImports.add(doCreateImportNormalizer(CORE_PERSISTENCE_PACKAGE, true, false)); - implicitImports.add(doCreateImportNormalizer(CORE_PERSISTENCE_HISTORICITEM_CLASS, false, false)); - implicitImports.add(doCreateImportNormalizer(CORE_PERSISTENCE_PERSISTENCESERVICE_INTERFACE, false, false)); - implicitImports.add(doCreateImportNormalizer(CORE_PERSISTENCE_PERSISTENCEEXTENSIONS_CLASS, false, false)); implicitImports.add(doCreateImportNormalizer(CORE_PERSISTENCE_RIEMANNTYPE_CLASS, false, false)); implicitImports.add(doCreateImportNormalizer(MODEL_SCRIPT_ACTIONS_PACKAGE, true, false)); implicitImports.add(doCreateImportNormalizer(TIME_PACKAGE, true, false)); - implicitImports.add(doCreateImportNormalizer(CHRONOUNIT_CLASS, false, false)); + implicitImports.add(doCreateImportNormalizer(TIME_FORMAT_PACKAGE, true, false)); + implicitImports.add(doCreateImportNormalizer(TIME_TEMPORAL_PACKAGE, true, false)); implicitImports.add(doCreateImportNormalizer(QUANTITY_PACKAGE, true, false)); implicitImports.add(doCreateImportNormalizer(CHANNELS_CLASS, false, false)); implicitImports.add(doCreateImportNormalizer(ITEMS_CLASS, false, false)); From 06fc762f5b92af02a8a569840cf91526028557ae Mon Sep 17 00:00:00 2001 From: Ravi Nadahar Date: Fri, 22 May 2026 23:11:47 +0200 Subject: [PATCH 13/16] Call existing static methods from RuleExtensions, some further JavaDoc cleanup Signed-off-by: Ravi Nadahar --- .../model/script/helper/RuleExtensions.java | 65 ++++--------------- .../core/model/script/helper/Rules.java | 12 +++- 2 files changed, 24 insertions(+), 53 deletions(-) diff --git a/bundles/org.openhab.core.model.script/src/org/openhab/core/model/script/helper/RuleExtensions.java b/bundles/org.openhab.core.model.script/src/org/openhab/core/model/script/helper/RuleExtensions.java index beed92ca57d..1a0f967ea9e 100644 --- a/bundles/org.openhab.core.model.script/src/org/openhab/core/model/script/helper/RuleExtensions.java +++ b/bundles/org.openhab.core.model.script/src/org/openhab/core/model/script/helper/RuleExtensions.java @@ -18,7 +18,6 @@ import org.eclipse.jdt.annotation.Nullable; import org.openhab.core.automation.Rule; import org.openhab.core.automation.RuleManager; -import org.openhab.core.model.script.ScriptServiceUtil; /** * {@link ItemExtensions} provides DSL {@link Rule} extensions. @@ -33,18 +32,11 @@ public class RuleExtensions { * * @param rule the {@link Rule} to run. * @return A copy of the rule context, including possible return values. + * @throws IllegalArgumentException If the specified rule isn't registered. * @throws IllegalStateException If no {@link RuleManager} instance exists. */ public static Map run(Rule rule) { - RuleManager ruleManager = ScriptServiceUtil.getRuleManager(); - if (ruleManager == null) { - throw new IllegalStateException("RuleManager doesn't exist"); - } - String ruleUID = rule.getUID(); - if (ruleManager.getStatus(ruleUID) == null) { - throw new IllegalArgumentException("Rule with UID '" + ruleUID + "' doesn't exist"); - } - return ruleManager.runNow(ruleUID); + return Rules.runRule(rule.getUID()); } /** @@ -53,18 +45,11 @@ public static Map run(Rule rule) { * @param rule the {@link Rule} to run. * @param considerConditions {@code true} to not run the rule if its conditions don't qualify. * @return A copy of the rule context, including possible return values. + * @throws IllegalArgumentException If the specified rule isn't registered. * @throws IllegalStateException If no {@link RuleManager} instance exists. */ public static Map run(Rule rule, boolean considerConditions) { - RuleManager ruleManager = ScriptServiceUtil.getRuleManager(); - if (ruleManager == null) { - throw new IllegalStateException("RuleManager doesn't exist"); - } - String ruleUID = rule.getUID(); - if (ruleManager.getStatus(ruleUID) == null) { - throw new IllegalArgumentException("Rule with UID '" + ruleUID + "' doesn't exist"); - } - return ruleManager.runNow(ruleUID, considerConditions, null); + return Rules.runRule(rule.getUID(), considerConditions); } /** @@ -73,18 +58,11 @@ public static Map run(Rule rule, boolean considerConditions) { * @param rule the {@link Rule} to run. * @param context the {@link Map} of {@link String} and {@link Object} pairs that constitutes the context. * @return A copy of the rule context, including possible return values. + * @throws IllegalArgumentException If the specified rule isn't registered. * @throws IllegalStateException If no {@link RuleManager} instance exists. */ public static Map run(Rule rule, Map context) { - RuleManager ruleManager = ScriptServiceUtil.getRuleManager(); - if (ruleManager == null) { - throw new IllegalStateException("RuleManager doesn't exist"); - } - String ruleUID = rule.getUID(); - if (ruleManager.getStatus(ruleUID) == null) { - throw new IllegalArgumentException("Rule with UID '" + ruleUID + "' doesn't exist"); - } - return ruleManager.runNow(ruleUID, false, context); + return Rules.runRule(rule.getUID(), context); } /** @@ -95,6 +73,7 @@ public static Map run(Rule rule, Map context) { * @param context the pairs of {@link String}s and {@link Object}s that constitutes the context. Must be in pairs, * the first is the key, the second is the value. * @return A copy of the rule context, including possible return values. + * @throws IllegalArgumentException If the specified rule isn't registered. * @throws IllegalStateException If no {@link RuleManager} instance exists. */ public static Map run(Rule rule, boolean considerConditions, Object... context) { @@ -108,19 +87,12 @@ public static Map run(Rule rule, boolean considerConditions, Obj * @param considerConditions {@code true} to not run the rule if its conditions don't qualify. * @param context the {@link Map} of {@link String} and {@link Object} pairs that constitutes the context. * @return A copy of the rule context, including possible return values. + * @throws IllegalArgumentException If the specified rule isn't registered. * @throws IllegalStateException If no {@link RuleManager} instance exists. */ public static Map run(Rule rule, boolean considerConditions, @Nullable Map context) { - RuleManager ruleManager = ScriptServiceUtil.getRuleManager(); - if (ruleManager == null) { - throw new IllegalStateException("RuleManager doesn't exist"); - } - String ruleUID = rule.getUID(); - if (ruleManager.getStatus(ruleUID) == null) { - throw new IllegalArgumentException("Rule with UID '" + ruleUID + "' doesn't exist"); - } - return ruleManager.runNow(ruleUID, considerConditions, context); + return Rules.runRule(rule.getUID(), considerConditions, context); } /** @@ -128,19 +100,11 @@ public static Map run(Rule rule, boolean considerConditions, * * @param rule the {@link Rule} to check. * @return {@code true} if the rule is enabled, {@code false} otherwise. + * @throws IllegalArgumentException If the specified rule isn't registered. * @throws IllegalStateException If no {@link RuleManager} instance exists. */ public static boolean isEnabled(Rule rule) { - RuleManager ruleManager = ScriptServiceUtil.getRuleManager(); - if (ruleManager == null) { - throw new IllegalStateException("RuleManager doesn't exist"); - } - String ruleUID = rule.getUID(); - Boolean result = ruleManager.isEnabled(ruleUID); - if (result == null) { - throw new IllegalArgumentException("Rule with UID '" + ruleUID + "' doesn't exist"); - } - return result.booleanValue(); + return Rules.isRuleEnabled(rule.getUID()); } /** @@ -148,13 +112,10 @@ public static boolean isEnabled(Rule rule) { * * @param rule the {@link Rule} to enable or disable. * @param enabled {@code true} to enable the rule, {@code false} to disable the rule. + * @throws IllegalArgumentException If the specified rule isn't registered. * @throws IllegalStateException If no {@link RuleManager} instance exists. */ public static void setEnabled(Rule rule, boolean enabled) { - RuleManager ruleManager = ScriptServiceUtil.getRuleManager(); - if (ruleManager == null) { - throw new IllegalStateException("RuleManager doesn't exist"); - } - ruleManager.setEnabled(rule.getUID(), enabled); + Rules.setRuleEnabled(rule.getUID(), enabled); } } diff --git a/bundles/org.openhab.core.model.script/src/org/openhab/core/model/script/helper/Rules.java b/bundles/org.openhab.core.model.script/src/org/openhab/core/model/script/helper/Rules.java index 6e05b2765b2..d94e77f816e 100644 --- a/bundles/org.openhab.core.model.script/src/org/openhab/core/model/script/helper/Rules.java +++ b/bundles/org.openhab.core.model.script/src/org/openhab/core/model/script/helper/Rules.java @@ -58,6 +58,15 @@ public static Map runRule(String ruleUid) { return ruleManager.runNow(ruleUid); } + /** + * Run the rule with the specified UID while optionally taking conditions into account. + * + * @param ruleUid the UID of the rule to run. + * @param considerConditions {@code true} to not run the rule if its conditions don't qualify. + * @return A copy of the rule context, including possible return values. + * @throws IllegalArgumentException If a rule with the specified UID doesn't exist. + * @throws IllegalStateException If no {@link RuleManager} instance exists. + */ public static Map runRule(String ruleUid, boolean considerConditions) { RuleManager ruleManager = ScriptServiceUtil.getRuleManager(); if (ruleManager == null) { @@ -123,7 +132,8 @@ public static Map runRule(String ruleUid, boolean considerCondit * @throws IllegalArgumentException If a rule with the specified UID doesn't exist. * @throws IllegalStateException If no {@link RuleManager} instance exists. */ - public static Map runRule(String ruleUid, boolean considerConditions, Map context) { + public static Map runRule(String ruleUid, boolean considerConditions, + @Nullable Map context) { RuleManager ruleManager = ScriptServiceUtil.getRuleManager(); if (ruleManager == null) { throw new IllegalStateException("RuleManager doesn't exist"); From bffd0a95c38373374843dc9a41acc22f1411e13d Mon Sep 17 00:00:00 2001 From: Ravi Nadahar Date: Thu, 28 May 2026 14:33:29 +0200 Subject: [PATCH 14/16] Revise implicit imports Signed-off-by: Ravi Nadahar --- .../ScriptImportSectionNamespaceScopeProvider.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/bundles/org.openhab.core.model.script/src/org/openhab/core/model/script/scoping/ScriptImportSectionNamespaceScopeProvider.java b/bundles/org.openhab.core.model.script/src/org/openhab/core/model/script/scoping/ScriptImportSectionNamespaceScopeProvider.java index 2c89e217d91..013704c9080 100644 --- a/bundles/org.openhab.core.model.script/src/org/openhab/core/model/script/scoping/ScriptImportSectionNamespaceScopeProvider.java +++ b/bundles/org.openhab.core.model.script/src/org/openhab/core/model/script/scoping/ScriptImportSectionNamespaceScopeProvider.java @@ -29,9 +29,8 @@ public class ScriptImportSectionNamespaceScopeProvider extends XImportSectionNam "library", "types"); public static final QualifiedName CORE_LIBRARY_ITEMS_PACKAGE = QualifiedName.create("org", "openhab", "core", "library", "items"); - public static final QualifiedName CORE_TYPES_PACKAGE = QualifiedName.create("org", "openhab", "core", "types"); - public static final QualifiedName CORE_TYPES_UTIL_PACKAGE = QualifiedName.create("org", "openhab", "core", "types", - "util"); + public static final QualifiedName CORE_TYPES_TIMESERIES_CLASS = QualifiedName.create("org", "openhab", "core", + "types", "TimeSeries"); public static final QualifiedName CORE_ITEMS_PACKAGE = QualifiedName.create("org", "openhab", "core", "items"); public static final QualifiedName CORE_THING_PACKAGE = QualifiedName.create("org", "openhab", "core", "thing"); public static final QualifiedName CORE_THING_LINK_ITEMCHANNELLINK_CLASS = QualifiedName.create("org", "openhab", @@ -47,6 +46,7 @@ public class ScriptImportSectionNamespaceScopeProvider extends XImportSectionNam public static final QualifiedName TIME_PACKAGE = QualifiedName.create("java", "time"); public static final QualifiedName TIME_FORMAT_PACKAGE = QualifiedName.create("java", "time", "format"); public static final QualifiedName TIME_TEMPORAL_PACKAGE = QualifiedName.create("java", "time", "temporal"); + public static final QualifiedName UTIL_REGEX_PACKAGE = QualifiedName.create("java", "util", "regex"); public static final QualifiedName QUANTITY_PACKAGE = QualifiedName.create("javax", "measure", "quantity"); public static final QualifiedName CHANNELS_CLASS = QualifiedName.create("org", "openhab", "core", "model", "script", "helper", "Channels"); @@ -61,8 +61,7 @@ protected List getImplicitImports(boolean ignoreCase) { implicitImports.add(doCreateImportNormalizer(CORE_LIBRARY_UNITS_PACKAGE, true, false)); implicitImports.add(doCreateImportNormalizer(CORE_LIBRARY_TYPES_PACKAGE, true, false)); implicitImports.add(doCreateImportNormalizer(CORE_LIBRARY_ITEMS_PACKAGE, true, false)); - implicitImports.add(doCreateImportNormalizer(CORE_TYPES_PACKAGE, true, false)); - implicitImports.add(doCreateImportNormalizer(CORE_TYPES_UTIL_PACKAGE, true, false)); + implicitImports.add(doCreateImportNormalizer(CORE_TYPES_TIMESERIES_CLASS, false, false)); implicitImports.add(doCreateImportNormalizer(CORE_ITEMS_PACKAGE, true, false)); implicitImports.add(doCreateImportNormalizer(CORE_THING_PACKAGE, true, false)); implicitImports.add(doCreateImportNormalizer(CORE_THING_LINK_ITEMCHANNELLINK_CLASS, false, false)); @@ -73,6 +72,7 @@ protected List getImplicitImports(boolean ignoreCase) { implicitImports.add(doCreateImportNormalizer(TIME_PACKAGE, true, false)); implicitImports.add(doCreateImportNormalizer(TIME_FORMAT_PACKAGE, true, false)); implicitImports.add(doCreateImportNormalizer(TIME_TEMPORAL_PACKAGE, true, false)); + implicitImports.add(doCreateImportNormalizer(UTIL_REGEX_PACKAGE, true, false)); implicitImports.add(doCreateImportNormalizer(QUANTITY_PACKAGE, true, false)); implicitImports.add(doCreateImportNormalizer(CHANNELS_CLASS, false, false)); implicitImports.add(doCreateImportNormalizer(ITEMS_CLASS, false, false)); From c1dbebdbe5f5779aabe13a400e3fbb679b4c5c3b Mon Sep 17 00:00:00 2001 From: Ravi Nadahar Date: Sat, 30 May 2026 21:23:39 +0200 Subject: [PATCH 15/16] Add TimeZone and Locale to available providers and methods Signed-off-by: Ravi Nadahar --- bundles/org.openhab.core.model.script/bnd.bnd | 1 + .../core/model/script/ScriptServiceUtil.java | 56 ++++++++++++++++++- ...ptImportSectionNamespaceScopeProvider.java | 4 ++ 3 files changed, 60 insertions(+), 1 deletion(-) diff --git a/bundles/org.openhab.core.model.script/bnd.bnd b/bundles/org.openhab.core.model.script/bnd.bnd index 1f4eebb0d1e..d8826f1d533 100644 --- a/bundles/org.openhab.core.model.script/bnd.bnd +++ b/bundles/org.openhab.core.model.script/bnd.bnd @@ -26,6 +26,7 @@ Import-Package: \ org.openhab.core.common.registry,\ org.openhab.core.ephemeris,\ org.openhab.core.events,\ + org.openhab.core.i18n,\ org.openhab.core.io.console,\ org.openhab.core.io.console.extensions,\ org.openhab.core.io.net.exec,\ diff --git a/bundles/org.openhab.core.model.script/src/org/openhab/core/model/script/ScriptServiceUtil.java b/bundles/org.openhab.core.model.script/src/org/openhab/core/model/script/ScriptServiceUtil.java index 48dfc1a4481..9173f1f5842 100644 --- a/bundles/org.openhab.core.model.script/src/org/openhab/core/model/script/ScriptServiceUtil.java +++ b/bundles/org.openhab.core.model.script/src/org/openhab/core/model/script/ScriptServiceUtil.java @@ -12,7 +12,10 @@ */ package org.openhab.core.model.script; +import java.time.ZoneId; import java.util.List; +import java.util.Locale; +import java.util.TimeZone; import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.atomic.AtomicReference; @@ -20,6 +23,8 @@ import org.openhab.core.automation.RuleManager; import org.openhab.core.automation.RuleRegistry; import org.openhab.core.events.EventPublisher; +import org.openhab.core.i18n.LocaleProvider; +import org.openhab.core.i18n.TimeZoneProvider; import org.openhab.core.items.ItemRegistry; import org.openhab.core.items.MetadataRegistry; import org.openhab.core.model.core.ModelRepository; @@ -63,6 +68,8 @@ public class ScriptServiceUtil { private final MetadataRegistry metadataRegistry; private final RuleRegistry ruleRegistry; private final ItemChannelLinkRegistry itemChannelLinkRegistry; + private final TimeZoneProvider timeZoneProvider; + private final LocaleProvider localeProvider; private volatile @Nullable RuleManager ruleManager; private final Scheduler scheduler; @@ -74,7 +81,9 @@ public class ScriptServiceUtil { public ScriptServiceUtil(final @Reference ItemRegistry itemRegistry, final @Reference ThingRegistry thingRegistry, final @Reference EventPublisher eventPublisher, final @Reference ModelRepository modelRepository, final @Reference MetadataRegistry metadataRegistry, final @Reference RuleRegistry ruleRegistry, - final @Reference ItemChannelLinkRegistry itemChannelLinkRegistry, final @Reference Scheduler scheduler) { + final @Reference ItemChannelLinkRegistry itemChannelLinkRegistry, + final @Reference TimeZoneProvider timeZoneProvider, final @Reference LocaleProvider localeProvider, + final @Reference Scheduler scheduler) { this.itemRegistry = itemRegistry; this.thingRegistry = thingRegistry; this.eventPublisher = eventPublisher; @@ -82,6 +91,8 @@ public ScriptServiceUtil(final @Reference ItemRegistry itemRegistry, final @Refe this.metadataRegistry = metadataRegistry; this.ruleRegistry = ruleRegistry; this.itemChannelLinkRegistry = itemChannelLinkRegistry; + this.timeZoneProvider = timeZoneProvider; + this.localeProvider = localeProvider; this.scheduler = scheduler; if (instance != null) { @@ -183,6 +194,49 @@ public ItemChannelLinkRegistry getItemChannelLinkRegistryInstance() { return itemChannelLinkRegistry; } + /** + * @return The currently openHAB configured {@link TimeZone}. + */ + public static TimeZone getTimeZone() { + return TimeZone.getTimeZone(getInstance().timeZoneProvider.getTimeZone()); + } + + /** + * @return The currently openHAB configured {@link ZoneId}. + */ + public static ZoneId getZoneId() { + return getInstance().timeZoneProvider.getTimeZone(); + } + + /** + * @return The {@link TimeZoneProvider} instance. + */ + public static TimeZoneProvider getTimeZoneProvider() { + return getInstance().timeZoneProvider; + } + + public TimeZoneProvider getTimeZoneProviderInstance() { + return timeZoneProvider; + } + + /** + * @return The currently openHAB configured {@link Locale}. + */ + public static Locale getLocale() { + return getInstance().localeProvider.getLocale(); + } + + /** + * @return The {@link LocaleProvider} instance. + */ + public static LocaleProvider getLocaleProvide() { + return getInstance().localeProvider; + } + + public LocaleProvider getLocaleProviderInstance() { + return localeProvider; + } + /** * @return The {@link RuleManager} / rule engine instance or {@code null} if it doesn't exist. */ diff --git a/bundles/org.openhab.core.model.script/src/org/openhab/core/model/script/scoping/ScriptImportSectionNamespaceScopeProvider.java b/bundles/org.openhab.core.model.script/src/org/openhab/core/model/script/scoping/ScriptImportSectionNamespaceScopeProvider.java index 013704c9080..457accf810e 100644 --- a/bundles/org.openhab.core.model.script/src/org/openhab/core/model/script/scoping/ScriptImportSectionNamespaceScopeProvider.java +++ b/bundles/org.openhab.core.model.script/src/org/openhab/core/model/script/scoping/ScriptImportSectionNamespaceScopeProvider.java @@ -47,6 +47,8 @@ public class ScriptImportSectionNamespaceScopeProvider extends XImportSectionNam public static final QualifiedName TIME_FORMAT_PACKAGE = QualifiedName.create("java", "time", "format"); public static final QualifiedName TIME_TEMPORAL_PACKAGE = QualifiedName.create("java", "time", "temporal"); public static final QualifiedName UTIL_REGEX_PACKAGE = QualifiedName.create("java", "util", "regex"); + public static final QualifiedName UTIL_LOCALE_CLASS = QualifiedName.create("java", "util", "Locale"); + public static final QualifiedName UTIL_TIMEZONE_CLASS = QualifiedName.create("java", "util", "TimeZone"); public static final QualifiedName QUANTITY_PACKAGE = QualifiedName.create("javax", "measure", "quantity"); public static final QualifiedName CHANNELS_CLASS = QualifiedName.create("org", "openhab", "core", "model", "script", "helper", "Channels"); @@ -73,6 +75,8 @@ protected List getImplicitImports(boolean ignoreCase) { implicitImports.add(doCreateImportNormalizer(TIME_FORMAT_PACKAGE, true, false)); implicitImports.add(doCreateImportNormalizer(TIME_TEMPORAL_PACKAGE, true, false)); implicitImports.add(doCreateImportNormalizer(UTIL_REGEX_PACKAGE, true, false)); + implicitImports.add(doCreateImportNormalizer(UTIL_LOCALE_CLASS, false, false)); + implicitImports.add(doCreateImportNormalizer(UTIL_TIMEZONE_CLASS, false, false)); implicitImports.add(doCreateImportNormalizer(QUANTITY_PACKAGE, true, false)); implicitImports.add(doCreateImportNormalizer(CHANNELS_CLASS, false, false)); implicitImports.add(doCreateImportNormalizer(ITEMS_CLASS, false, false)); From 09efd8cc6de34a4e00f78db1112c472a83fe78ab Mon Sep 17 00:00:00 2001 From: Ravi Nadahar Date: Sun, 31 May 2026 05:53:18 +0200 Subject: [PATCH 16/16] Move "helper" classes to existing package with similar content Signed-off-by: Ravi Nadahar --- .../openhab/core/model/script/{helper => lib}/Channels.java | 2 +- .../core/model/script/{helper => lib}/ItemExtensions.java | 2 +- .../openhab/core/model/script/{helper => lib}/Items.java | 2 +- .../core/model/script/{helper => lib}/RuleExtensions.java | 2 +- .../openhab/core/model/script/{helper => lib}/Rules.java | 2 +- .../model/script/scoping/ScriptImplicitlyImportedTypes.java | 4 ++-- .../scoping/ScriptImportSectionNamespaceScopeProvider.java | 6 +++--- 7 files changed, 10 insertions(+), 10 deletions(-) rename bundles/org.openhab.core.model.script/src/org/openhab/core/model/script/{helper => lib}/Channels.java (99%) rename bundles/org.openhab.core.model.script/src/org/openhab/core/model/script/{helper => lib}/ItemExtensions.java (99%) rename bundles/org.openhab.core.model.script/src/org/openhab/core/model/script/{helper => lib}/Items.java (99%) rename bundles/org.openhab.core.model.script/src/org/openhab/core/model/script/{helper => lib}/RuleExtensions.java (99%) rename bundles/org.openhab.core.model.script/src/org/openhab/core/model/script/{helper => lib}/Rules.java (99%) diff --git a/bundles/org.openhab.core.model.script/src/org/openhab/core/model/script/helper/Channels.java b/bundles/org.openhab.core.model.script/src/org/openhab/core/model/script/lib/Channels.java similarity index 99% rename from bundles/org.openhab.core.model.script/src/org/openhab/core/model/script/helper/Channels.java rename to bundles/org.openhab.core.model.script/src/org/openhab/core/model/script/lib/Channels.java index b019f789d9c..e3ff5c7f189 100644 --- a/bundles/org.openhab.core.model.script/src/org/openhab/core/model/script/helper/Channels.java +++ b/bundles/org.openhab.core.model.script/src/org/openhab/core/model/script/lib/Channels.java @@ -10,7 +10,7 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.core.model.script.helper; +package org.openhab.core.model.script.lib; import java.util.LinkedHashMap; import java.util.Map; diff --git a/bundles/org.openhab.core.model.script/src/org/openhab/core/model/script/helper/ItemExtensions.java b/bundles/org.openhab.core.model.script/src/org/openhab/core/model/script/lib/ItemExtensions.java similarity index 99% rename from bundles/org.openhab.core.model.script/src/org/openhab/core/model/script/helper/ItemExtensions.java rename to bundles/org.openhab.core.model.script/src/org/openhab/core/model/script/lib/ItemExtensions.java index 8322e1864b4..561b66c62a5 100644 --- a/bundles/org.openhab.core.model.script/src/org/openhab/core/model/script/helper/ItemExtensions.java +++ b/bundles/org.openhab.core.model.script/src/org/openhab/core/model/script/lib/ItemExtensions.java @@ -10,7 +10,7 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.core.model.script.helper; +package org.openhab.core.model.script.lib; import java.util.Map; import java.util.Set; diff --git a/bundles/org.openhab.core.model.script/src/org/openhab/core/model/script/helper/Items.java b/bundles/org.openhab.core.model.script/src/org/openhab/core/model/script/lib/Items.java similarity index 99% rename from bundles/org.openhab.core.model.script/src/org/openhab/core/model/script/helper/Items.java rename to bundles/org.openhab.core.model.script/src/org/openhab/core/model/script/lib/Items.java index 8d6aa6de1d9..6a6078dc59f 100644 --- a/bundles/org.openhab.core.model.script/src/org/openhab/core/model/script/helper/Items.java +++ b/bundles/org.openhab.core.model.script/src/org/openhab/core/model/script/lib/Items.java @@ -10,7 +10,7 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.core.model.script.helper; +package org.openhab.core.model.script.lib; import java.util.Collection; import java.util.LinkedHashMap; diff --git a/bundles/org.openhab.core.model.script/src/org/openhab/core/model/script/helper/RuleExtensions.java b/bundles/org.openhab.core.model.script/src/org/openhab/core/model/script/lib/RuleExtensions.java similarity index 99% rename from bundles/org.openhab.core.model.script/src/org/openhab/core/model/script/helper/RuleExtensions.java rename to bundles/org.openhab.core.model.script/src/org/openhab/core/model/script/lib/RuleExtensions.java index 1a0f967ea9e..778fe02a1c9 100644 --- a/bundles/org.openhab.core.model.script/src/org/openhab/core/model/script/helper/RuleExtensions.java +++ b/bundles/org.openhab.core.model.script/src/org/openhab/core/model/script/lib/RuleExtensions.java @@ -10,7 +10,7 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.core.model.script.helper; +package org.openhab.core.model.script.lib; import java.util.Map; diff --git a/bundles/org.openhab.core.model.script/src/org/openhab/core/model/script/helper/Rules.java b/bundles/org.openhab.core.model.script/src/org/openhab/core/model/script/lib/Rules.java similarity index 99% rename from bundles/org.openhab.core.model.script/src/org/openhab/core/model/script/helper/Rules.java rename to bundles/org.openhab.core.model.script/src/org/openhab/core/model/script/lib/Rules.java index d94e77f816e..bc22e540d94 100644 --- a/bundles/org.openhab.core.model.script/src/org/openhab/core/model/script/helper/Rules.java +++ b/bundles/org.openhab.core.model.script/src/org/openhab/core/model/script/lib/Rules.java @@ -10,7 +10,7 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.core.model.script.helper; +package org.openhab.core.model.script.lib; import java.util.LinkedHashMap; import java.util.Map; diff --git a/bundles/org.openhab.core.model.script/src/org/openhab/core/model/script/scoping/ScriptImplicitlyImportedTypes.java b/bundles/org.openhab.core.model.script/src/org/openhab/core/model/script/scoping/ScriptImplicitlyImportedTypes.java index 2c1ba5d39a9..262b5d5df6e 100644 --- a/bundles/org.openhab.core.model.script/src/org/openhab/core/model/script/scoping/ScriptImplicitlyImportedTypes.java +++ b/bundles/org.openhab.core.model.script/src/org/openhab/core/model/script/scoping/ScriptImplicitlyImportedTypes.java @@ -40,9 +40,9 @@ import org.openhab.core.model.script.engine.IActionServiceProvider; import org.openhab.core.model.script.engine.IThingActionsProvider; import org.openhab.core.model.script.engine.action.ActionService; -import org.openhab.core.model.script.helper.ItemExtensions; -import org.openhab.core.model.script.helper.RuleExtensions; +import org.openhab.core.model.script.lib.ItemExtensions; import org.openhab.core.model.script.lib.NumberExtensions; +import org.openhab.core.model.script.lib.RuleExtensions; import org.openhab.core.thing.binding.ThingActions; import com.google.inject.Inject; diff --git a/bundles/org.openhab.core.model.script/src/org/openhab/core/model/script/scoping/ScriptImportSectionNamespaceScopeProvider.java b/bundles/org.openhab.core.model.script/src/org/openhab/core/model/script/scoping/ScriptImportSectionNamespaceScopeProvider.java index 457accf810e..815411660ed 100644 --- a/bundles/org.openhab.core.model.script/src/org/openhab/core/model/script/scoping/ScriptImportSectionNamespaceScopeProvider.java +++ b/bundles/org.openhab.core.model.script/src/org/openhab/core/model/script/scoping/ScriptImportSectionNamespaceScopeProvider.java @@ -51,11 +51,11 @@ public class ScriptImportSectionNamespaceScopeProvider extends XImportSectionNam public static final QualifiedName UTIL_TIMEZONE_CLASS = QualifiedName.create("java", "util", "TimeZone"); public static final QualifiedName QUANTITY_PACKAGE = QualifiedName.create("javax", "measure", "quantity"); public static final QualifiedName CHANNELS_CLASS = QualifiedName.create("org", "openhab", "core", "model", "script", - "helper", "Channels"); + "lib", "Channels"); public static final QualifiedName ITEMS_CLASS = QualifiedName.create("org", "openhab", "core", "model", "script", - "helper", "Items"); + "lib", "Items"); public static final QualifiedName RULES_CLASS = QualifiedName.create("org", "openhab", "core", "model", "script", - "helper", "Rules"); + "lib", "Rules"); @Override protected List getImplicitImports(boolean ignoreCase) {