mirror of
https://github.com/danieldemus/openhab-core.git
synced 2026-07-29 12:34:22 +02:00
[automation] Extend provider script extension for metadata & ItemChannelLinks (#4865)
* [automation] Move provider script extension to a new bundle * [automation] Add ProviderRegistryDelegate interface and improve ProviderScriptExtension code * [automation] Add ScriptedMetadataProvider & ProviderMetadataRegistryDelegate * Rename ProviderRegistryDelegate.java to ProviderRegistry.java * [automation] Add ScriptedItemChannelLinkProvider & ProviderItemChannelLinkRegistry * [automation] Implement ProviderItemChannelLinkRegistry::purge Signed-off-by: Florian Hotze <dev@florianhotze.com>
This commit is contained in:
@@ -412,6 +412,12 @@
|
|||||||
<version>${project.version}</version>
|
<version>${project.version}</version>
|
||||||
<scope>compile</scope>
|
<scope>compile</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.openhab.core.bundles</groupId>
|
||||||
|
<artifactId>org.openhab.core.automation.module.script.providersupport</artifactId>
|
||||||
|
<version>${project.version}</version>
|
||||||
|
<scope>compile</scope>
|
||||||
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.openhab.core.bundles</groupId>
|
<groupId>org.openhab.core.bundles</groupId>
|
||||||
<artifactId>org.openhab.core.automation.module.script.rulesupport</artifactId>
|
<artifactId>org.openhab.core.automation.module.script.rulesupport</artifactId>
|
||||||
|
|||||||
@@ -0,0 +1,42 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<classpath>
|
||||||
|
<classpathentry kind="src" output="target/classes" path="src/main/java">
|
||||||
|
<attributes>
|
||||||
|
<attribute name="optional" value="true"/>
|
||||||
|
<attribute name="maven.pomderived" value="true"/>
|
||||||
|
</attributes>
|
||||||
|
</classpathentry>
|
||||||
|
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-21">
|
||||||
|
<attributes>
|
||||||
|
<attribute name="maven.pomderived" value="true"/>
|
||||||
|
<attribute name="annotationpath" value="target/dependency"/>
|
||||||
|
</attributes>
|
||||||
|
</classpathentry>
|
||||||
|
<classpathentry kind="con" path="org.eclipse.m2e.MAVEN2_CLASSPATH_CONTAINER">
|
||||||
|
<attributes>
|
||||||
|
<attribute name="maven.pomderived" value="true"/>
|
||||||
|
<attribute name="annotationpath" value="target/dependency"/>
|
||||||
|
</attributes>
|
||||||
|
</classpathentry>
|
||||||
|
<classpathentry excluding="**" kind="src" output="target/classes" path="src/main/resources">
|
||||||
|
<attributes>
|
||||||
|
<attribute name="maven.pomderived" value="true"/>
|
||||||
|
<attribute name="optional" value="true"/>
|
||||||
|
</attributes>
|
||||||
|
</classpathentry>
|
||||||
|
<classpathentry kind="src" output="target/test-classes" path="src/test/java">
|
||||||
|
<attributes>
|
||||||
|
<attribute name="optional" value="true"/>
|
||||||
|
<attribute name="maven.pomderived" value="true"/>
|
||||||
|
<attribute name="test" value="true"/>
|
||||||
|
</attributes>
|
||||||
|
</classpathentry>
|
||||||
|
<classpathentry excluding="**" kind="src" output="target/test-classes" path="src/test/resources">
|
||||||
|
<attributes>
|
||||||
|
<attribute name="maven.pomderived" value="true"/>
|
||||||
|
<attribute name="test" value="true"/>
|
||||||
|
<attribute name="optional" value="true"/>
|
||||||
|
</attributes>
|
||||||
|
</classpathentry>
|
||||||
|
<classpathentry kind="output" path="target/classes"/>
|
||||||
|
</classpath>
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<projectDescription>
|
||||||
|
<name>org.openhab.core.automation.module.script.providersupport</name>
|
||||||
|
<comment></comment>
|
||||||
|
<projects>
|
||||||
|
</projects>
|
||||||
|
<buildSpec>
|
||||||
|
<buildCommand>
|
||||||
|
<name>org.eclipse.jdt.core.javabuilder</name>
|
||||||
|
<arguments>
|
||||||
|
</arguments>
|
||||||
|
</buildCommand>
|
||||||
|
<buildCommand>
|
||||||
|
<name>org.eclipse.m2e.core.maven2Builder</name>
|
||||||
|
<arguments>
|
||||||
|
</arguments>
|
||||||
|
</buildCommand>
|
||||||
|
</buildSpec>
|
||||||
|
<natures>
|
||||||
|
<nature>org.eclipse.jdt.core.javanature</nature>
|
||||||
|
<nature>org.eclipse.m2e.core.maven2Nature</nature>
|
||||||
|
</natures>
|
||||||
|
</projectDescription>
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
This content is produced and maintained by the openHAB project.
|
||||||
|
|
||||||
|
* Project home: https://www.openhab.org
|
||||||
|
|
||||||
|
== Declared Project Licenses
|
||||||
|
|
||||||
|
This program and the accompanying materials are made available under the terms
|
||||||
|
of the Eclipse Public License 2.0 which is available at
|
||||||
|
https://www.eclipse.org/legal/epl-2.0/.
|
||||||
|
|
||||||
|
== Source Code
|
||||||
|
|
||||||
|
https://github.com/openhab/openhab-core
|
||||||
|
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
|
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
|
<parent>
|
||||||
|
<groupId>org.openhab.core.bundles</groupId>
|
||||||
|
<artifactId>org.openhab.core.reactor.bundles</artifactId>
|
||||||
|
<version>5.0.0-SNAPSHOT</version>
|
||||||
|
</parent>
|
||||||
|
|
||||||
|
<artifactId>org.openhab.core.automation.module.script.providersupport</artifactId>
|
||||||
|
|
||||||
|
<name>openHAB Core :: Bundles :: Automation Script ProviderSupport</name>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.openhab.core.bundles</groupId>
|
||||||
|
<artifactId>org.openhab.core.automation.module.script</artifactId>
|
||||||
|
<version>${project.version}</version>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
|
||||||
|
</project>
|
||||||
+28
@@ -0,0 +1,28 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2010-2025 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.automation.module.script.providersupport.internal;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interface to be implemented by all {@link org.openhab.core.common.registry.Registry} (delegates) that are used to
|
||||||
|
* provide openHAB entities from scripts.
|
||||||
|
*
|
||||||
|
* @author Florian Hotze - Initial contribution
|
||||||
|
*/
|
||||||
|
public interface ProviderRegistry {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes all elements that are provided by the script the {@link ProviderRegistry} instance is bound to.
|
||||||
|
* To be called when the script is unloaded or reloaded.
|
||||||
|
*/
|
||||||
|
void removeAllAddedByScript();
|
||||||
|
}
|
||||||
+161
@@ -0,0 +1,161 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2010-2025 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.automation.module.script.providersupport.internal;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
|
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||||
|
import org.eclipse.jdt.annotation.Nullable;
|
||||||
|
import org.openhab.core.automation.module.script.ScriptExtensionProvider;
|
||||||
|
import org.openhab.core.automation.module.script.providersupport.shared.ProviderItemChannelLinkRegistry;
|
||||||
|
import org.openhab.core.automation.module.script.providersupport.shared.ProviderItemRegistryDelegate;
|
||||||
|
import org.openhab.core.automation.module.script.providersupport.shared.ProviderMetadataRegistryDelegate;
|
||||||
|
import org.openhab.core.automation.module.script.providersupport.shared.ProviderThingRegistryDelegate;
|
||||||
|
import org.openhab.core.automation.module.script.providersupport.shared.ScriptedItemChannelLinkProvider;
|
||||||
|
import org.openhab.core.automation.module.script.providersupport.shared.ScriptedItemProvider;
|
||||||
|
import org.openhab.core.automation.module.script.providersupport.shared.ScriptedMetadataProvider;
|
||||||
|
import org.openhab.core.automation.module.script.providersupport.shared.ScriptedThingProvider;
|
||||||
|
import org.openhab.core.items.ItemRegistry;
|
||||||
|
import org.openhab.core.items.MetadataRegistry;
|
||||||
|
import org.openhab.core.thing.ThingRegistry;
|
||||||
|
import org.openhab.core.thing.link.ItemChannelLinkRegistry;
|
||||||
|
import org.osgi.service.component.annotations.Activate;
|
||||||
|
import org.osgi.service.component.annotations.Component;
|
||||||
|
import org.osgi.service.component.annotations.Reference;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The {@link ProviderScriptExtension} extends scripts to provide openHAB entities like items.
|
||||||
|
* It handles the lifecycle of these entities, ensuring that they are removed when the script is unloaded.
|
||||||
|
*
|
||||||
|
* @author Florian Hotze - Initial contribution
|
||||||
|
*/
|
||||||
|
@Component(immediate = true)
|
||||||
|
@NonNullByDefault
|
||||||
|
public class ProviderScriptExtension implements ScriptExtensionProvider {
|
||||||
|
private static final String PRESET_NAME = "provider";
|
||||||
|
private static final String ITEM_CHANNEL_LINK_REGISTRY_NAME = "itemChannelLinkRegistry";
|
||||||
|
private static final String ITEM_REGISTRY_NAME = "itemRegistry";
|
||||||
|
private static final String METADATA_REGISTRY_NAME = "metadataRegistry";
|
||||||
|
private static final String THING_REGISTRY_NAME = "thingRegistry";
|
||||||
|
|
||||||
|
private final Map<String, Map<String, ProviderRegistry>> registryCache = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
|
private final ItemChannelLinkRegistry itemChannelLinkRegistry;
|
||||||
|
private final ScriptedItemChannelLinkProvider itemChannelLinkProvider;
|
||||||
|
private final ItemRegistry itemRegistry;
|
||||||
|
private final ScriptedItemProvider itemProvider;
|
||||||
|
private final MetadataRegistry metadataRegistry;
|
||||||
|
private final ScriptedMetadataProvider metadataProvider;
|
||||||
|
private final ThingRegistry thingRegistry;
|
||||||
|
private final ScriptedThingProvider thingProvider;
|
||||||
|
|
||||||
|
@Activate
|
||||||
|
public ProviderScriptExtension( //
|
||||||
|
final @Reference ItemChannelLinkRegistry itemChannelLinkRegistry,
|
||||||
|
final @Reference ScriptedItemChannelLinkProvider itemChannelLinkProvider,
|
||||||
|
final @Reference ItemRegistry itemRegistry, final @Reference ScriptedItemProvider itemProvider,
|
||||||
|
final @Reference MetadataRegistry metadataRegistry,
|
||||||
|
final @Reference ScriptedMetadataProvider metadataProvider, final @Reference ThingRegistry thingRegistry,
|
||||||
|
final @Reference ScriptedThingProvider thingProvider) {
|
||||||
|
this.itemChannelLinkRegistry = itemChannelLinkRegistry;
|
||||||
|
this.itemChannelLinkProvider = itemChannelLinkProvider;
|
||||||
|
this.itemRegistry = itemRegistry;
|
||||||
|
this.itemProvider = itemProvider;
|
||||||
|
this.metadataRegistry = metadataRegistry;
|
||||||
|
this.metadataProvider = metadataProvider;
|
||||||
|
this.thingRegistry = thingRegistry;
|
||||||
|
this.thingProvider = thingProvider;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Collection<String> getDefaultPresets() {
|
||||||
|
return Set.of();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Collection<String> getPresets() {
|
||||||
|
return Set.of(PRESET_NAME);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Collection<String> getTypes() {
|
||||||
|
return Set.of(ITEM_CHANNEL_LINK_REGISTRY_NAME, ITEM_REGISTRY_NAME, METADATA_REGISTRY_NAME, THING_REGISTRY_NAME);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @Nullable Object get(String scriptIdentifier, String type) throws IllegalArgumentException {
|
||||||
|
Map<String, ProviderRegistry> registries = Objects
|
||||||
|
.requireNonNull(registryCache.computeIfAbsent(scriptIdentifier, k -> new HashMap<>()));
|
||||||
|
|
||||||
|
ProviderRegistry registry = registries.get(type);
|
||||||
|
if (registry != null) {
|
||||||
|
return registry;
|
||||||
|
}
|
||||||
|
|
||||||
|
return switch (type) {
|
||||||
|
case ITEM_CHANNEL_LINK_REGISTRY_NAME -> {
|
||||||
|
ProviderItemChannelLinkRegistry providerItemChannelLinkRegistry = new ProviderItemChannelLinkRegistry(
|
||||||
|
itemChannelLinkRegistry, itemChannelLinkProvider);
|
||||||
|
registries.put(type, providerItemChannelLinkRegistry);
|
||||||
|
yield providerItemChannelLinkRegistry;
|
||||||
|
}
|
||||||
|
case ITEM_REGISTRY_NAME -> {
|
||||||
|
ProviderItemRegistryDelegate itemRegistryDelegate = new ProviderItemRegistryDelegate(itemRegistry,
|
||||||
|
itemProvider);
|
||||||
|
registries.put(ITEM_REGISTRY_NAME, itemRegistryDelegate);
|
||||||
|
yield itemRegistryDelegate;
|
||||||
|
}
|
||||||
|
case METADATA_REGISTRY_NAME -> {
|
||||||
|
ProviderMetadataRegistryDelegate metadataRegistryDelegate = new ProviderMetadataRegistryDelegate(
|
||||||
|
metadataRegistry, metadataProvider);
|
||||||
|
registries.put(METADATA_REGISTRY_NAME, metadataRegistryDelegate);
|
||||||
|
yield metadataRegistryDelegate;
|
||||||
|
}
|
||||||
|
case THING_REGISTRY_NAME -> {
|
||||||
|
ProviderThingRegistryDelegate thingRegistryDelegate = new ProviderThingRegistryDelegate(thingRegistry,
|
||||||
|
thingProvider);
|
||||||
|
registries.put(THING_REGISTRY_NAME, thingRegistryDelegate);
|
||||||
|
yield thingRegistryDelegate;
|
||||||
|
}
|
||||||
|
default -> null;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Map<String, Object> importPreset(String scriptIdentifier, String preset) {
|
||||||
|
if (PRESET_NAME.equals(preset)) {
|
||||||
|
return Map.of(ITEM_CHANNEL_LINK_REGISTRY_NAME,
|
||||||
|
Objects.requireNonNull(get(scriptIdentifier, ITEM_CHANNEL_LINK_REGISTRY_NAME)), ITEM_REGISTRY_NAME,
|
||||||
|
Objects.requireNonNull(get(scriptIdentifier, ITEM_REGISTRY_NAME)), METADATA_REGISTRY_NAME,
|
||||||
|
Objects.requireNonNull(get(scriptIdentifier, METADATA_REGISTRY_NAME)), THING_REGISTRY_NAME,
|
||||||
|
Objects.requireNonNull(get(scriptIdentifier, THING_REGISTRY_NAME)));
|
||||||
|
}
|
||||||
|
|
||||||
|
return Map.of();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void unload(String scriptIdentifier) {
|
||||||
|
Map<String, ProviderRegistry> registries = registryCache.remove(scriptIdentifier);
|
||||||
|
if (registries != null) {
|
||||||
|
for (ProviderRegistry registry : registries.values()) {
|
||||||
|
registry.removeAllAddedByScript();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+166
@@ -0,0 +1,166 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2010-2025 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.automation.module.script.providersupport.shared;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||||
|
import org.eclipse.jdt.annotation.Nullable;
|
||||||
|
import org.openhab.core.automation.module.script.providersupport.internal.ProviderRegistry;
|
||||||
|
import org.openhab.core.common.registry.Registry;
|
||||||
|
import org.openhab.core.common.registry.RegistryChangeListener;
|
||||||
|
import org.openhab.core.thing.ThingUID;
|
||||||
|
import org.openhab.core.thing.link.ItemChannelLink;
|
||||||
|
import org.openhab.core.thing.link.ItemChannelLinkRegistry;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The {@link ProviderItemChannelLinkRegistry} is implementing a {@link Registry} to provide a comfortable way to
|
||||||
|
* provide {@link ItemChannelLink}s from scripts without worrying about the need to remove them again when the script is
|
||||||
|
* unloaded.
|
||||||
|
* Nonetheless, using the {@link #addPermanent(ItemChannelLink)} method it is still possible to them permanently.
|
||||||
|
* <p>
|
||||||
|
* Use a new instance of this class for each {@link javax.script.ScriptEngine}.
|
||||||
|
* <p>
|
||||||
|
* ATTENTION: This class does not provide the same methods as {@link ItemChannelLinkRegistry}.
|
||||||
|
*
|
||||||
|
* @author Florian Hotze - Initial contribution
|
||||||
|
*/
|
||||||
|
@NonNullByDefault
|
||||||
|
public class ProviderItemChannelLinkRegistry implements Registry<ItemChannelLink, String>, ProviderRegistry {
|
||||||
|
private final ItemChannelLinkRegistry itemChannelLinkRegistry;
|
||||||
|
|
||||||
|
private final Set<String> uids = new HashSet<>();
|
||||||
|
|
||||||
|
private final ScriptedItemChannelLinkProvider scriptedProvider;
|
||||||
|
|
||||||
|
public ProviderItemChannelLinkRegistry(ItemChannelLinkRegistry itemChannelLinkRegistry,
|
||||||
|
ScriptedItemChannelLinkProvider scriptedProvider) {
|
||||||
|
this.itemChannelLinkRegistry = itemChannelLinkRegistry;
|
||||||
|
this.scriptedProvider = scriptedProvider;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addRegistryChangeListener(RegistryChangeListener<ItemChannelLink> listener) {
|
||||||
|
itemChannelLinkRegistry.addRegistryChangeListener(listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Collection<ItemChannelLink> getAll() {
|
||||||
|
return itemChannelLinkRegistry.getAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Stream<ItemChannelLink> stream() {
|
||||||
|
return itemChannelLinkRegistry.stream();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @Nullable ItemChannelLink get(String key) {
|
||||||
|
return itemChannelLinkRegistry.get(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void removeRegistryChangeListener(RegistryChangeListener<ItemChannelLink> listener) {
|
||||||
|
itemChannelLinkRegistry.removeRegistryChangeListener(listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ItemChannelLink add(ItemChannelLink element) {
|
||||||
|
String uid = element.getUID();
|
||||||
|
// Check for item->channel link already existing here because the item->channel link might exist in a different
|
||||||
|
// provider, so we need to
|
||||||
|
// check the registry and not only the provider itself
|
||||||
|
if (get(uid) != null) {
|
||||||
|
throw new IllegalArgumentException(
|
||||||
|
"Cannot add item->channel link, because an item->channel link with same UID (" + uid
|
||||||
|
+ ") already exists.");
|
||||||
|
}
|
||||||
|
|
||||||
|
scriptedProvider.add(element);
|
||||||
|
uids.add(uid);
|
||||||
|
|
||||||
|
return element;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds an {@link ItemChannelLink} permanently to the registry.
|
||||||
|
* This {@link ItemChannelLink} will be kept in the registry even if the script is unloaded
|
||||||
|
*
|
||||||
|
* @param element the {@link ItemChannelLink} to be added (must not be null)
|
||||||
|
* @return the added {@link ItemChannelLink}
|
||||||
|
*/
|
||||||
|
public ItemChannelLink addPermanent(ItemChannelLink element) {
|
||||||
|
return itemChannelLinkRegistry.add(element);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @Nullable ItemChannelLink update(ItemChannelLink element) {
|
||||||
|
if (uids.contains(element.getUID())) {
|
||||||
|
return scriptedProvider.update(element);
|
||||||
|
}
|
||||||
|
return itemChannelLinkRegistry.update(element);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @Nullable ItemChannelLink remove(String key) {
|
||||||
|
if (uids.contains(key)) {
|
||||||
|
return scriptedProvider.remove(key);
|
||||||
|
}
|
||||||
|
return itemChannelLinkRegistry.remove(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int removeLinksForThing(ThingUID thingUID) {
|
||||||
|
int removedLinks = 0;
|
||||||
|
Collection<ItemChannelLink> itemChannelLinks = getAll();
|
||||||
|
for (ItemChannelLink itemChannelLink : itemChannelLinks) {
|
||||||
|
if (itemChannelLink.getLinkedUID().getThingUID().equals(thingUID)) {
|
||||||
|
this.remove(itemChannelLink.getUID());
|
||||||
|
removedLinks++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return removedLinks;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int removeLinksForItem(String itemName) {
|
||||||
|
int removedLinks = 0;
|
||||||
|
Collection<ItemChannelLink> itemChannelLinks = getAll();
|
||||||
|
for (ItemChannelLink itemChannelLink : itemChannelLinks) {
|
||||||
|
if (itemChannelLink.getItemName().equals(itemName)) {
|
||||||
|
this.remove(itemChannelLink.getUID());
|
||||||
|
removedLinks++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return removedLinks;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int purge() {
|
||||||
|
List<String> toRemove = itemChannelLinkRegistry.getOrphanLinks().keySet().stream().map(ItemChannelLink::getUID)
|
||||||
|
.filter(i -> scriptedProvider.get(i) != null).toList();
|
||||||
|
|
||||||
|
toRemove.forEach(this::remove);
|
||||||
|
|
||||||
|
return toRemove.size() + itemChannelLinkRegistry.purge();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void removeAllAddedByScript() {
|
||||||
|
for (String uid : uids) {
|
||||||
|
scriptedProvider.remove(uid);
|
||||||
|
}
|
||||||
|
uids.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
+11
-13
@@ -10,7 +10,7 @@
|
|||||||
*
|
*
|
||||||
* SPDX-License-Identifier: EPL-2.0
|
* SPDX-License-Identifier: EPL-2.0
|
||||||
*/
|
*/
|
||||||
package org.openhab.core.automation.module.script.rulesupport.shared;
|
package org.openhab.core.automation.module.script.providersupport.shared;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
@@ -21,6 +21,7 @@ import java.util.stream.Stream;
|
|||||||
|
|
||||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||||
import org.eclipse.jdt.annotation.Nullable;
|
import org.eclipse.jdt.annotation.Nullable;
|
||||||
|
import org.openhab.core.automation.module.script.providersupport.internal.ProviderRegistry;
|
||||||
import org.openhab.core.common.registry.RegistryChangeListener;
|
import org.openhab.core.common.registry.RegistryChangeListener;
|
||||||
import org.openhab.core.items.GroupItem;
|
import org.openhab.core.items.GroupItem;
|
||||||
import org.openhab.core.items.Item;
|
import org.openhab.core.items.Item;
|
||||||
@@ -38,16 +39,16 @@ import org.openhab.core.items.ItemRegistry;
|
|||||||
* @author Florian Hotze - Initial contribution
|
* @author Florian Hotze - Initial contribution
|
||||||
*/
|
*/
|
||||||
@NonNullByDefault
|
@NonNullByDefault
|
||||||
public class ProviderItemRegistryDelegate implements ItemRegistry {
|
public class ProviderItemRegistryDelegate implements ItemRegistry, ProviderRegistry {
|
||||||
private final ItemRegistry itemRegistry;
|
private final ItemRegistry itemRegistry;
|
||||||
|
|
||||||
private final Set<String> items = new HashSet<>();
|
private final Set<String> items = new HashSet<>();
|
||||||
|
|
||||||
private final ScriptedItemProvider itemProvider;
|
private final ScriptedItemProvider scriptedProvider;
|
||||||
|
|
||||||
public ProviderItemRegistryDelegate(ItemRegistry itemRegistry, ScriptedItemProvider itemProvider) {
|
public ProviderItemRegistryDelegate(ItemRegistry itemRegistry, ScriptedItemProvider scriptedProvider) {
|
||||||
this.itemRegistry = itemRegistry;
|
this.itemRegistry = itemRegistry;
|
||||||
this.itemProvider = itemProvider;
|
this.scriptedProvider = scriptedProvider;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -85,7 +86,7 @@ public class ProviderItemRegistryDelegate implements ItemRegistry {
|
|||||||
"Cannot add item, because an item with same name (" + itemName + ") already exists.");
|
"Cannot add item, because an item with same name (" + itemName + ") already exists.");
|
||||||
}
|
}
|
||||||
|
|
||||||
itemProvider.add(element);
|
scriptedProvider.add(element);
|
||||||
items.add(itemName);
|
items.add(itemName);
|
||||||
|
|
||||||
return element;
|
return element;
|
||||||
@@ -105,7 +106,7 @@ public class ProviderItemRegistryDelegate implements ItemRegistry {
|
|||||||
@Override
|
@Override
|
||||||
public @Nullable Item update(Item element) {
|
public @Nullable Item update(Item element) {
|
||||||
if (items.contains(element.getName())) {
|
if (items.contains(element.getName())) {
|
||||||
return itemProvider.update(element);
|
return scriptedProvider.update(element);
|
||||||
}
|
}
|
||||||
return itemRegistry.update(element);
|
return itemRegistry.update(element);
|
||||||
}
|
}
|
||||||
@@ -113,7 +114,7 @@ public class ProviderItemRegistryDelegate implements ItemRegistry {
|
|||||||
@Override
|
@Override
|
||||||
public @Nullable Item remove(String key) {
|
public @Nullable Item remove(String key) {
|
||||||
if (items.remove(key)) {
|
if (items.remove(key)) {
|
||||||
return itemProvider.remove(key);
|
return scriptedProvider.remove(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
return itemRegistry.remove(key);
|
return itemRegistry.remove(key);
|
||||||
@@ -175,13 +176,10 @@ public class ProviderItemRegistryDelegate implements ItemRegistry {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
@Override
|
||||||
* Removes all items that are provided by this script.
|
|
||||||
* To be called when the script is unloaded or reloaded.
|
|
||||||
*/
|
|
||||||
public void removeAllAddedByScript() {
|
public void removeAllAddedByScript() {
|
||||||
for (String item : items) {
|
for (String item : items) {
|
||||||
itemProvider.remove(item);
|
scriptedProvider.remove(item);
|
||||||
}
|
}
|
||||||
items.clear();
|
items.clear();
|
||||||
}
|
}
|
||||||
+146
@@ -0,0 +1,146 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2010-2025 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.automation.module.script.providersupport.shared;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||||
|
import org.eclipse.jdt.annotation.Nullable;
|
||||||
|
import org.openhab.core.automation.module.script.providersupport.internal.ProviderRegistry;
|
||||||
|
import org.openhab.core.common.registry.RegistryChangeListener;
|
||||||
|
import org.openhab.core.items.Metadata;
|
||||||
|
import org.openhab.core.items.MetadataKey;
|
||||||
|
import org.openhab.core.items.MetadataPredicates;
|
||||||
|
import org.openhab.core.items.MetadataRegistry;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The {@link ProviderMetadataRegistryDelegate} is wrapping a {@link MetadataRegistry} to provide a comfortable way to
|
||||||
|
* provide items from scripts without worrying about the need to remove metadata again when the script is unloaded.
|
||||||
|
* Nonetheless, using the {@link #addPermanent(Metadata)} method it is still possible to add metadata permanently.
|
||||||
|
* <p>
|
||||||
|
* Use a new instance of this class for each {@link javax.script.ScriptEngine}.
|
||||||
|
*
|
||||||
|
* @author Florian Hotze - Initial contribution
|
||||||
|
*/
|
||||||
|
@NonNullByDefault
|
||||||
|
public class ProviderMetadataRegistryDelegate implements MetadataRegistry, ProviderRegistry {
|
||||||
|
private final MetadataRegistry metadataRegistry;
|
||||||
|
|
||||||
|
private final Set<MetadataKey> metadataKeys = new HashSet<>();
|
||||||
|
|
||||||
|
private final ScriptedMetadataProvider scriptedProvider;
|
||||||
|
|
||||||
|
public ProviderMetadataRegistryDelegate(MetadataRegistry metadataRegistry,
|
||||||
|
ScriptedMetadataProvider scriptedProvider) {
|
||||||
|
this.metadataRegistry = metadataRegistry;
|
||||||
|
this.scriptedProvider = scriptedProvider;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addRegistryChangeListener(RegistryChangeListener<Metadata> listener) {
|
||||||
|
metadataRegistry.addRegistryChangeListener(listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Collection<Metadata> getAll() {
|
||||||
|
return metadataRegistry.getAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Stream<Metadata> stream() {
|
||||||
|
return metadataRegistry.stream();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @Nullable Metadata get(MetadataKey key) {
|
||||||
|
return metadataRegistry.get(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void removeRegistryChangeListener(RegistryChangeListener<Metadata> listener) {
|
||||||
|
metadataRegistry.removeRegistryChangeListener(listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Metadata add(Metadata element) {
|
||||||
|
MetadataKey key = element.getUID();
|
||||||
|
// Check for metadata already existing here because the metadata might exist in a different provider, so we need
|
||||||
|
// to check the registry and not only the provider itself
|
||||||
|
if (get(key) != null) {
|
||||||
|
throw new IllegalArgumentException(
|
||||||
|
"Cannot add metadata, because metadata with same name (" + key + ") already exists.");
|
||||||
|
}
|
||||||
|
|
||||||
|
scriptedProvider.add(element);
|
||||||
|
metadataKeys.add(key);
|
||||||
|
|
||||||
|
return element;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds metadata permanently to the registry.
|
||||||
|
* This metadata will be kept in the registry even if the script is unloaded.
|
||||||
|
*
|
||||||
|
* @param element the metadata to be added (must not be null)
|
||||||
|
* @return the added metadata
|
||||||
|
*/
|
||||||
|
public Metadata addPermanent(Metadata element) {
|
||||||
|
return metadataRegistry.add(element);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @Nullable Metadata update(Metadata element) {
|
||||||
|
if (metadataKeys.contains(element.getUID())) {
|
||||||
|
return scriptedProvider.update(element);
|
||||||
|
}
|
||||||
|
return metadataRegistry.update(element);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @Nullable Metadata remove(MetadataKey key) {
|
||||||
|
if (metadataKeys.contains(key)) {
|
||||||
|
return scriptedProvider.remove(key);
|
||||||
|
}
|
||||||
|
return metadataRegistry.remove(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isInternalNamespace(String namespace) {
|
||||||
|
return metadataRegistry.isInternalNamespace(namespace);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Collection<String> getAllNamespaces(String itemname) {
|
||||||
|
return metadataRegistry.getAllNamespaces(itemname);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void removeItemMetadata(String itemname) {
|
||||||
|
if (scriptedProvider.getAll().stream().anyMatch(MetadataPredicates.ofItem(itemname))) {
|
||||||
|
scriptedProvider.removeItemMetadata(itemname);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
metadataRegistry.removeItemMetadata(itemname);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void removeAllAddedByScript() {
|
||||||
|
for (MetadataKey key : metadataKeys) {
|
||||||
|
scriptedProvider.remove(key);
|
||||||
|
}
|
||||||
|
metadataKeys.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
+11
-13
@@ -10,7 +10,7 @@
|
|||||||
*
|
*
|
||||||
* SPDX-License-Identifier: EPL-2.0
|
* SPDX-License-Identifier: EPL-2.0
|
||||||
*/
|
*/
|
||||||
package org.openhab.core.automation.module.script.rulesupport.shared;
|
package org.openhab.core.automation.module.script.providersupport.shared;
|
||||||
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
@@ -20,6 +20,7 @@ import java.util.stream.Stream;
|
|||||||
|
|
||||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||||
import org.eclipse.jdt.annotation.Nullable;
|
import org.eclipse.jdt.annotation.Nullable;
|
||||||
|
import org.openhab.core.automation.module.script.providersupport.internal.ProviderRegistry;
|
||||||
import org.openhab.core.common.registry.RegistryChangeListener;
|
import org.openhab.core.common.registry.RegistryChangeListener;
|
||||||
import org.openhab.core.config.core.Configuration;
|
import org.openhab.core.config.core.Configuration;
|
||||||
import org.openhab.core.thing.Channel;
|
import org.openhab.core.thing.Channel;
|
||||||
@@ -39,16 +40,16 @@ import org.openhab.core.thing.ThingUID;
|
|||||||
* @author Florian Hotze - Initial contribution
|
* @author Florian Hotze - Initial contribution
|
||||||
*/
|
*/
|
||||||
@NonNullByDefault
|
@NonNullByDefault
|
||||||
public class ProviderThingRegistryDelegate implements ThingRegistry {
|
public class ProviderThingRegistryDelegate implements ThingRegistry, ProviderRegistry {
|
||||||
private final ThingRegistry thingRegistry;
|
private final ThingRegistry thingRegistry;
|
||||||
|
|
||||||
private final Set<ThingUID> things = new HashSet<>();
|
private final Set<ThingUID> things = new HashSet<>();
|
||||||
|
|
||||||
private final ScriptedThingProvider thingProvider;
|
private final ScriptedThingProvider scriptedProvider;
|
||||||
|
|
||||||
public ProviderThingRegistryDelegate(ThingRegistry thingRegistry, ScriptedThingProvider thingProvider) {
|
public ProviderThingRegistryDelegate(ThingRegistry thingRegistry, ScriptedThingProvider scriptedProvider) {
|
||||||
this.thingRegistry = thingRegistry;
|
this.thingRegistry = thingRegistry;
|
||||||
this.thingProvider = thingProvider;
|
this.scriptedProvider = scriptedProvider;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -81,7 +82,7 @@ public class ProviderThingRegistryDelegate implements ThingRegistry {
|
|||||||
"Cannot add Thing, because a Thing with same UID (" + thingUID + ") already exists.");
|
"Cannot add Thing, because a Thing with same UID (" + thingUID + ") already exists.");
|
||||||
}
|
}
|
||||||
|
|
||||||
thingProvider.add(element);
|
scriptedProvider.add(element);
|
||||||
things.add(thingUID);
|
things.add(thingUID);
|
||||||
|
|
||||||
return element;
|
return element;
|
||||||
@@ -101,7 +102,7 @@ public class ProviderThingRegistryDelegate implements ThingRegistry {
|
|||||||
@Override
|
@Override
|
||||||
public @Nullable Thing update(Thing element) {
|
public @Nullable Thing update(Thing element) {
|
||||||
if (things.contains(element.getUID())) {
|
if (things.contains(element.getUID())) {
|
||||||
return thingProvider.update(element);
|
return scriptedProvider.update(element);
|
||||||
}
|
}
|
||||||
|
|
||||||
return thingRegistry.update(element);
|
return thingRegistry.update(element);
|
||||||
@@ -131,13 +132,10 @@ public class ProviderThingRegistryDelegate implements ThingRegistry {
|
|||||||
return thingRegistry.remove(thingUID);
|
return thingRegistry.remove(thingUID);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
@Override
|
||||||
* Removes all Things that are provided by this script.
|
|
||||||
* To be called when the script is unloaded or reloaded.
|
|
||||||
*/
|
|
||||||
public void removeAllAddedByScript() {
|
public void removeAllAddedByScript() {
|
||||||
for (ThingUID thing : things) {
|
for (ThingUID thing : things) {
|
||||||
thingProvider.remove(thing);
|
scriptedProvider.remove(thing);
|
||||||
}
|
}
|
||||||
things.clear();
|
things.clear();
|
||||||
}
|
}
|
||||||
@@ -145,7 +143,7 @@ public class ProviderThingRegistryDelegate implements ThingRegistry {
|
|||||||
@Override
|
@Override
|
||||||
public @Nullable Thing forceRemove(ThingUID thingUID) {
|
public @Nullable Thing forceRemove(ThingUID thingUID) {
|
||||||
if (things.remove(thingUID)) {
|
if (things.remove(thingUID)) {
|
||||||
return thingProvider.remove(thingUID);
|
return scriptedProvider.remove(thingUID);
|
||||||
}
|
}
|
||||||
|
|
||||||
return thingRegistry.forceRemove(thingUID);
|
return thingRegistry.forceRemove(thingUID);
|
||||||
+85
@@ -0,0 +1,85 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2010-2025 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.automation.module.script.providersupport.shared;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||||
|
import org.eclipse.jdt.annotation.Nullable;
|
||||||
|
import org.openhab.core.common.registry.AbstractProvider;
|
||||||
|
import org.openhab.core.common.registry.ManagedProvider;
|
||||||
|
import org.openhab.core.thing.link.ItemChannelLink;
|
||||||
|
import org.openhab.core.thing.link.ItemChannelLinkProvider;
|
||||||
|
import org.osgi.service.component.annotations.Component;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This {@link ItemChannelLinkProvider} keeps {@link ItemChannelLink}s provided by scripts during runtime.
|
||||||
|
* This ensures that {@link ItemChannelLink}s are not kept on reboot, but have to be provided by the scripts again.
|
||||||
|
*
|
||||||
|
* @author Florian Hotze - Initial contribution
|
||||||
|
*/
|
||||||
|
@NonNullByDefault
|
||||||
|
@Component(immediate = true, service = { ScriptedItemChannelLinkProvider.class, ItemChannelLinkProvider.class })
|
||||||
|
public class ScriptedItemChannelLinkProvider extends AbstractProvider<ItemChannelLink>
|
||||||
|
implements ItemChannelLinkProvider, ManagedProvider<ItemChannelLink, String> {
|
||||||
|
private final Logger logger = LoggerFactory.getLogger(ScriptedItemChannelLinkProvider.class);
|
||||||
|
private final Map<String, ItemChannelLink> itemChannelLinks = new HashMap<>();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Collection<ItemChannelLink> getAll() {
|
||||||
|
return itemChannelLinks.values();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @Nullable ItemChannelLink get(String key) {
|
||||||
|
return itemChannelLinks.get(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void add(ItemChannelLink itemChannelLink) {
|
||||||
|
if (get(itemChannelLink.getUID()) != null) {
|
||||||
|
throw new IllegalArgumentException(
|
||||||
|
"Cannot add item->channel link, because an item->channel link with same UID ("
|
||||||
|
+ itemChannelLink.getUID() + ") already exists.");
|
||||||
|
}
|
||||||
|
itemChannelLinks.put(itemChannelLink.getUID(), itemChannelLink);
|
||||||
|
|
||||||
|
notifyListenersAboutAddedElement(itemChannelLink);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @Nullable ItemChannelLink update(ItemChannelLink itemChannelLink) {
|
||||||
|
ItemChannelLink oldItemChannelLink = itemChannelLinks.get(itemChannelLink.getUID());
|
||||||
|
if (oldItemChannelLink != null) {
|
||||||
|
itemChannelLinks.put(itemChannelLink.getUID(), itemChannelLink);
|
||||||
|
notifyListenersAboutUpdatedElement(oldItemChannelLink, itemChannelLink);
|
||||||
|
} else {
|
||||||
|
logger.warn("Cannot update item->channel link with UID '{}', because it does not exist.",
|
||||||
|
itemChannelLink.getUID());
|
||||||
|
}
|
||||||
|
return oldItemChannelLink;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @Nullable ItemChannelLink remove(String key) {
|
||||||
|
ItemChannelLink itemChannelLink = itemChannelLinks.remove(key);
|
||||||
|
if (itemChannelLink != null) {
|
||||||
|
notifyListenersAboutRemovedElement(itemChannelLink);
|
||||||
|
}
|
||||||
|
return itemChannelLink;
|
||||||
|
}
|
||||||
|
}
|
||||||
+1
-1
@@ -10,7 +10,7 @@
|
|||||||
*
|
*
|
||||||
* SPDX-License-Identifier: EPL-2.0
|
* SPDX-License-Identifier: EPL-2.0
|
||||||
*/
|
*/
|
||||||
package org.openhab.core.automation.module.script.rulesupport.shared;
|
package org.openhab.core.automation.module.script.providersupport.shared;
|
||||||
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
+89
@@ -0,0 +1,89 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2010-2025 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.automation.module.script.providersupport.shared;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||||
|
import org.eclipse.jdt.annotation.Nullable;
|
||||||
|
import org.openhab.core.common.registry.AbstractProvider;
|
||||||
|
import org.openhab.core.items.ManagedMetadataProvider;
|
||||||
|
import org.openhab.core.items.Metadata;
|
||||||
|
import org.openhab.core.items.MetadataKey;
|
||||||
|
import org.openhab.core.items.MetadataPredicates;
|
||||||
|
import org.openhab.core.items.MetadataProvider;
|
||||||
|
import org.osgi.service.component.annotations.Component;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This {@link org.openhab.core.items.MetadataProvider} keeps metadata provided by scripts during runtime.
|
||||||
|
* This ensures that metadata is not kept on reboot, but has to be provided by the scripts again.
|
||||||
|
*
|
||||||
|
* @author Florian Hotze
|
||||||
|
*/
|
||||||
|
@NonNullByDefault
|
||||||
|
@Component(immediate = true, service = { ScriptedMetadataProvider.class, MetadataProvider.class })
|
||||||
|
public class ScriptedMetadataProvider extends AbstractProvider<Metadata> implements ManagedMetadataProvider {
|
||||||
|
private final Logger logger = LoggerFactory.getLogger(ScriptedMetadataProvider.class);
|
||||||
|
private final Map<MetadataKey, Metadata> metadataStorage = new HashMap<>();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void removeItemMetadata(String name) {
|
||||||
|
getAll().stream().filter(MetadataPredicates.ofItem(name)).map(Metadata::getUID).forEach(this::remove);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Collection<Metadata> getAll() {
|
||||||
|
return metadataStorage.values();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @Nullable Metadata get(MetadataKey metadataKey) {
|
||||||
|
return metadataStorage.get(metadataKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void add(Metadata metadata) {
|
||||||
|
if (metadataStorage.containsKey(metadata.getUID())) {
|
||||||
|
throw new IllegalArgumentException("Cannot add metadata, because metadata with the same UID ("
|
||||||
|
+ metadata.getUID() + ") already exists");
|
||||||
|
}
|
||||||
|
metadataStorage.put(metadata.getUID(), metadata);
|
||||||
|
|
||||||
|
notifyListenersAboutAddedElement(metadata);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @Nullable Metadata update(Metadata metadata) {
|
||||||
|
Metadata oldMetadata = metadataStorage.get(metadata.getUID());
|
||||||
|
if (oldMetadata != null) {
|
||||||
|
metadataStorage.put(metadata.getUID(), metadata);
|
||||||
|
notifyListenersAboutUpdatedElement(oldMetadata, metadata);
|
||||||
|
} else {
|
||||||
|
logger.warn("Could not update metadata with UID '{}', because it does not exist", metadata.getUID());
|
||||||
|
}
|
||||||
|
return oldMetadata;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @Nullable Metadata remove(MetadataKey metadataKey) {
|
||||||
|
Metadata metadata = metadataStorage.remove(metadataKey);
|
||||||
|
if (metadata != null) {
|
||||||
|
notifyListenersAboutRemovedElement(metadata);
|
||||||
|
}
|
||||||
|
return metadata;
|
||||||
|
}
|
||||||
|
}
|
||||||
+1
-1
@@ -10,7 +10,7 @@
|
|||||||
*
|
*
|
||||||
* SPDX-License-Identifier: EPL-2.0
|
* SPDX-License-Identifier: EPL-2.0
|
||||||
*/
|
*/
|
||||||
package org.openhab.core.automation.module.script.rulesupport.shared;
|
package org.openhab.core.automation.module.script.providersupport.shared;
|
||||||
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
-133
@@ -1,133 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2010-2025 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.automation.module.script.rulesupport.internal;
|
|
||||||
|
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Objects;
|
|
||||||
import java.util.Set;
|
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
|
||||||
|
|
||||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
|
||||||
import org.eclipse.jdt.annotation.Nullable;
|
|
||||||
import org.openhab.core.automation.module.script.ScriptExtensionProvider;
|
|
||||||
import org.openhab.core.automation.module.script.rulesupport.shared.ProviderItemRegistryDelegate;
|
|
||||||
import org.openhab.core.automation.module.script.rulesupport.shared.ProviderThingRegistryDelegate;
|
|
||||||
import org.openhab.core.automation.module.script.rulesupport.shared.ScriptedItemProvider;
|
|
||||||
import org.openhab.core.automation.module.script.rulesupport.shared.ScriptedThingProvider;
|
|
||||||
import org.openhab.core.items.ItemRegistry;
|
|
||||||
import org.openhab.core.thing.ThingRegistry;
|
|
||||||
import org.osgi.service.component.annotations.Activate;
|
|
||||||
import org.osgi.service.component.annotations.Component;
|
|
||||||
import org.osgi.service.component.annotations.Reference;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The {@link ProviderScriptExtension} extends scripts to provide openHAB entities like items.
|
|
||||||
* It handles the lifecycle of these entities, ensuring that they are removed when the script is unloaded.
|
|
||||||
*
|
|
||||||
* @author Florian Hotze - Initial contribution
|
|
||||||
*/
|
|
||||||
@Component(immediate = true)
|
|
||||||
@NonNullByDefault
|
|
||||||
public class ProviderScriptExtension implements ScriptExtensionProvider {
|
|
||||||
private static final String PRESET_NAME = "provider";
|
|
||||||
private static final String ITEM_REGISTRY_NAME = "itemRegistry";
|
|
||||||
private static final String THING_REGISTRY_NAME = "thingRegistry";
|
|
||||||
|
|
||||||
private final Map<String, Map<String, Object>> objectCache = new ConcurrentHashMap<>();
|
|
||||||
|
|
||||||
private final ItemRegistry itemRegistry;
|
|
||||||
private final ScriptedItemProvider itemProvider;
|
|
||||||
private final ThingRegistry thingRegistry;
|
|
||||||
private final ScriptedThingProvider thingProvider;
|
|
||||||
|
|
||||||
@Activate
|
|
||||||
public ProviderScriptExtension(final @Reference ItemRegistry itemRegistry,
|
|
||||||
final @Reference ScriptedItemProvider itemProvider, final @Reference ThingRegistry thingRegistry,
|
|
||||||
final @Reference ScriptedThingProvider thingProvider) {
|
|
||||||
this.itemRegistry = itemRegistry;
|
|
||||||
this.itemProvider = itemProvider;
|
|
||||||
this.thingRegistry = thingRegistry;
|
|
||||||
this.thingProvider = thingProvider;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Collection<String> getDefaultPresets() {
|
|
||||||
return Set.of();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Collection<String> getPresets() {
|
|
||||||
return Set.of(PRESET_NAME);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Collection<String> getTypes() {
|
|
||||||
return Set.of(ITEM_REGISTRY_NAME, THING_REGISTRY_NAME);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public @Nullable Object get(String scriptIdentifier, String type) throws IllegalArgumentException {
|
|
||||||
Map<String, Object> objects = Objects
|
|
||||||
.requireNonNull(objectCache.computeIfAbsent(scriptIdentifier, k -> new HashMap<>()));
|
|
||||||
|
|
||||||
Object obj = objects.get(type);
|
|
||||||
if (obj != null) {
|
|
||||||
return obj;
|
|
||||||
}
|
|
||||||
|
|
||||||
return switch (type) {
|
|
||||||
case ITEM_REGISTRY_NAME -> {
|
|
||||||
ProviderItemRegistryDelegate itemRegistryDelegate = new ProviderItemRegistryDelegate(itemRegistry,
|
|
||||||
itemProvider);
|
|
||||||
objects.put(ITEM_REGISTRY_NAME, itemRegistryDelegate);
|
|
||||||
yield itemRegistryDelegate;
|
|
||||||
}
|
|
||||||
case THING_REGISTRY_NAME -> {
|
|
||||||
ProviderThingRegistryDelegate thingRegistryDelegate = new ProviderThingRegistryDelegate(thingRegistry,
|
|
||||||
thingProvider);
|
|
||||||
objects.put(THING_REGISTRY_NAME, thingRegistryDelegate);
|
|
||||||
yield thingRegistryDelegate;
|
|
||||||
}
|
|
||||||
default -> null;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Map<String, Object> importPreset(String scriptIdentifier, String preset) {
|
|
||||||
if (PRESET_NAME.equals(preset)) {
|
|
||||||
return Map.of(ITEM_REGISTRY_NAME, Objects.requireNonNull(get(scriptIdentifier, ITEM_REGISTRY_NAME)),
|
|
||||||
THING_REGISTRY_NAME, Objects.requireNonNull(get(scriptIdentifier, THING_REGISTRY_NAME)));
|
|
||||||
}
|
|
||||||
|
|
||||||
return Map.of();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void unload(String scriptIdentifier) {
|
|
||||||
Map<String, Object> objects = objectCache.remove(scriptIdentifier);
|
|
||||||
if (objects != null) {
|
|
||||||
Object itemRegistry = objects.get(ITEM_REGISTRY_NAME);
|
|
||||||
if (itemRegistry != null) {
|
|
||||||
ProviderItemRegistryDelegate itemRegistryDelegate = (ProviderItemRegistryDelegate) itemRegistry;
|
|
||||||
itemRegistryDelegate.removeAllAddedByScript();
|
|
||||||
}
|
|
||||||
Object thingRegistry = objects.get(THING_REGISTRY_NAME);
|
|
||||||
if (thingRegistry != null) {
|
|
||||||
ProviderThingRegistryDelegate thingRegistryDelegate = (ProviderThingRegistryDelegate) thingRegistry;
|
|
||||||
thingRegistryDelegate.removeAllAddedByScript();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
+6
-2
@@ -952,7 +952,7 @@ public class ItemResource implements RESTResource {
|
|||||||
public Response getSemanticsHealth(@Context HttpHeaders headers) {
|
public Response getSemanticsHealth(@Context HttpHeaders headers) {
|
||||||
List<ItemSemanticsProblem> semanticsProblems = this.itemRegistry.stream().flatMap(item -> {
|
List<ItemSemanticsProblem> semanticsProblems = this.itemRegistry.stream().flatMap(item -> {
|
||||||
return semanticsService.getItemSemanticsProblems(item).stream()
|
return semanticsService.getItemSemanticsProblems(item).stream()
|
||||||
.map(p -> p.setEditable(isEditable(p.item())));
|
.map(p -> p.setEditable(isItemEditable(p.item())));
|
||||||
}).toList();
|
}).toList();
|
||||||
return JSONResponse.createResponse(Status.OK, semanticsProblems, null);
|
return JSONResponse.createResponse(Status.OK, semanticsProblems, null);
|
||||||
}
|
}
|
||||||
@@ -1061,7 +1061,11 @@ public class ItemResource implements RESTResource {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private boolean isEditable(EnrichedItemDTO item) {
|
private boolean isEditable(EnrichedItemDTO item) {
|
||||||
return managedItemProvider.get(item.name) != null;
|
return isItemEditable(item.name);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isItemEditable(String itemName) {
|
||||||
|
return managedItemProvider.get(itemName) != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean isEditable(MetadataKey metadataKey) {
|
private boolean isEditable(MetadataKey metadataKey) {
|
||||||
|
|||||||
@@ -26,6 +26,7 @@
|
|||||||
<module>org.openhab.core.automation</module>
|
<module>org.openhab.core.automation</module>
|
||||||
<module>org.openhab.core.automation.module.media</module>
|
<module>org.openhab.core.automation.module.media</module>
|
||||||
<module>org.openhab.core.automation.module.script</module>
|
<module>org.openhab.core.automation.module.script</module>
|
||||||
|
<module>org.openhab.core.automation.module.script.providersupport</module>
|
||||||
<module>org.openhab.core.automation.module.script.rulesupport</module>
|
<module>org.openhab.core.automation.module.script.rulesupport</module>
|
||||||
<module>org.openhab.core.automation.rest</module>
|
<module>org.openhab.core.automation.rest</module>
|
||||||
<module>org.openhab.core.config.core</module>
|
<module>org.openhab.core.config.core</module>
|
||||||
|
|||||||
@@ -142,6 +142,12 @@
|
|||||||
<bundle>mvn:org.openhab.core.bundles/org.openhab.core.automation.module.script/${project.version}</bundle>
|
<bundle>mvn:org.openhab.core.bundles/org.openhab.core.automation.module.script/${project.version}</bundle>
|
||||||
</feature>
|
</feature>
|
||||||
|
|
||||||
|
<feature name="openhab-core-automation-module-script-providersupport" version="${project.version}">
|
||||||
|
<feature>openhab-core-base</feature>
|
||||||
|
<feature dependency="true">openhab-core-automation-module-script</feature>
|
||||||
|
<bundle>mvn:org.openhab.core.bundles/org.openhab.core.automation.module.script.providersupport/${project.version}</bundle>
|
||||||
|
</feature>
|
||||||
|
|
||||||
<feature name="openhab-core-automation-module-script-rulesupport" version="${project.version}">
|
<feature name="openhab-core-automation-module-script-rulesupport" version="${project.version}">
|
||||||
<feature>openhab-core-base</feature>
|
<feature>openhab-core-base</feature>
|
||||||
<feature dependency="true">openhab-core-automation-module-script</feature>
|
<feature dependency="true">openhab-core-automation-module-script</feature>
|
||||||
@@ -448,6 +454,7 @@
|
|||||||
<feature>openhab-core-auth-jaas</feature>
|
<feature>openhab-core-auth-jaas</feature>
|
||||||
<feature>openhab-core-automation-rest</feature>
|
<feature>openhab-core-automation-rest</feature>
|
||||||
<feature>openhab-core-automation-module-script</feature>
|
<feature>openhab-core-automation-module-script</feature>
|
||||||
|
<feature>openhab-core-automation-module-script-providersupport</feature>
|
||||||
<feature>openhab-core-automation-module-script-rulesupport</feature>
|
<feature>openhab-core-automation-module-script-rulesupport</feature>
|
||||||
<feature>openhab-core-automation-module-media</feature>
|
<feature>openhab-core-automation-module-media</feature>
|
||||||
<feature>openhab-core-io-console-karaf</feature>
|
<feature>openhab-core-io-console-karaf</feature>
|
||||||
|
|||||||
Reference in New Issue
Block a user