-
-
Notifications
You must be signed in to change notification settings - Fork 428
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add-on suggestion finder for USB devices (#3922)
Also-by: Holger Friedrich <[email protected]> Signed-off-by: Andrew Fiddian-Green <[email protected]>
- Loading branch information
Showing
17 changed files
with
451 additions
and
12 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
29 changes: 29 additions & 0 deletions
29
bundles/org.openhab.core.config.discovery.addon.usb/.classpath
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
<?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="src" output="target/test-classes" path="src/test/java"> | ||
<attributes> | ||
<attribute name="test" value="true"/> | ||
<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-17"> | ||
<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 kind="output" path="target/classes"/> | ||
</classpath> |
23 changes: 23 additions & 0 deletions
23
bundles/org.openhab.core.config.discovery.addon.usb/.project
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
<?xml version="1.0" encoding="UTF-8"?> | ||
<projectDescription> | ||
<name>org.openhab.core.config.discovery.addon.usb</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.m2e.core.maven2Nature</nature> | ||
<nature>org.eclipse.jdt.core.javanature</nature> | ||
</natures> | ||
</projectDescription> |
14 changes: 14 additions & 0 deletions
14
bundles/org.openhab.core.config.discovery.addon.usb/NOTICE
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 | ||
|
35 changes: 35 additions & 0 deletions
35
bundles/org.openhab.core.config.discovery.addon.usb/pom.xml
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
<?xml version="1.0" encoding="UTF-8" standalone="no"?> | ||
<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>4.2.0-SNAPSHOT</version> | ||
</parent> | ||
|
||
<artifactId>org.openhab.core.config.discovery.addon.usb</artifactId> | ||
|
||
<name>openHAB Core :: Bundles :: USB Suggested Add-on Finder</name> | ||
|
||
<dependencies> | ||
<dependency> | ||
<groupId>org.openhab.core.bundles</groupId> | ||
<artifactId>org.openhab.core.config.discovery.addon</artifactId> | ||
<version>${project.version}</version> | ||
</dependency> | ||
<dependency> | ||
<groupId>org.openhab.core.bundles</groupId> | ||
<artifactId>org.openhab.core.addon</artifactId> | ||
<version>${project.version}</version> | ||
</dependency> | ||
<dependency> | ||
<groupId>org.openhab.core.bundles</groupId> | ||
<artifactId>org.openhab.core.config.discovery.usbserial</artifactId> | ||
<version>${project.version}</version> | ||
</dependency> | ||
</dependencies> | ||
|
||
</project> |
186 changes: 186 additions & 0 deletions
186
...y.addon.usb/src/main/java/org/openhab/core/config/discovery/addon/usb/UsbAddonFinder.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,186 @@ | ||
/** | ||
* Copyright (c) 2010-2023 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.config.discovery.addon.usb; | ||
|
||
import static org.openhab.core.config.discovery.addon.AddonFinderConstants.SERVICE_NAME_USB; | ||
import static org.openhab.core.config.discovery.addon.AddonFinderConstants.SERVICE_TYPE_USB; | ||
|
||
import java.util.HashSet; | ||
import java.util.Map; | ||
import java.util.Set; | ||
import java.util.concurrent.ConcurrentHashMap; | ||
import java.util.concurrent.CopyOnWriteArraySet; | ||
import java.util.regex.Pattern; | ||
import java.util.stream.Collectors; | ||
|
||
import org.eclipse.jdt.annotation.NonNullByDefault; | ||
import org.openhab.core.addon.AddonDiscoveryMethod; | ||
import org.openhab.core.addon.AddonInfo; | ||
import org.openhab.core.config.discovery.addon.AddonFinder; | ||
import org.openhab.core.config.discovery.addon.BaseAddonFinder; | ||
import org.openhab.core.config.discovery.usbserial.UsbSerialDeviceInformation; | ||
import org.openhab.core.config.discovery.usbserial.UsbSerialDiscovery; | ||
import org.openhab.core.config.discovery.usbserial.UsbSerialDiscoveryListener; | ||
import org.osgi.service.component.annotations.Component; | ||
import org.osgi.service.component.annotations.Reference; | ||
import org.osgi.service.component.annotations.ReferenceCardinality; | ||
import org.osgi.service.component.annotations.ReferencePolicy; | ||
import org.slf4j.Logger; | ||
import org.slf4j.LoggerFactory; | ||
|
||
/** | ||
* This is a {@link USBAddonFinder} for finding suggested add-ons related to USB devices. | ||
* <p> | ||
* It supports the following values for the 'match-property' 'name' element: | ||
* <li>product - match on the product description text | ||
* <li>manufacturer - match on the device manufacturer text | ||
* <li>chipId - match on the chip vendor id plus product id | ||
* <li>remote - match on whether the device is connected remotely or locally | ||
* | ||
* @author Andrew Fiddian-Green - Initial contribution | ||
*/ | ||
@NonNullByDefault | ||
@Component(service = AddonFinder.class, name = UsbAddonFinder.SERVICE_NAME) | ||
public class UsbAddonFinder extends BaseAddonFinder implements UsbSerialDiscoveryListener { | ||
|
||
public static final String SERVICE_TYPE = SERVICE_TYPE_USB; | ||
public static final String SERVICE_NAME = SERVICE_NAME_USB; | ||
|
||
/* | ||
* Supported 'match-property' names | ||
*/ | ||
public static final String PRODUCT = "product"; | ||
public static final String MANUFACTURER = "manufacturer"; | ||
public static final String CHIP_ID = "chipId"; | ||
public static final String REMOTE = "remote"; | ||
|
||
public static final Set<String> SUPPORTED_PROPERTIES = Set.of(PRODUCT, MANUFACTURER, CHIP_ID, REMOTE); | ||
|
||
private final Logger logger = LoggerFactory.getLogger(UsbAddonFinder.class); | ||
private final Set<UsbSerialDiscovery> usbSerialDiscoveries = new CopyOnWriteArraySet<>(); | ||
private final Map<Long, UsbSerialDeviceInformation> usbDeviceInformations = new ConcurrentHashMap<>(); | ||
|
||
@Reference(cardinality = ReferenceCardinality.MULTIPLE, policy = ReferencePolicy.DYNAMIC) | ||
protected void addUsbSerialDiscovery(UsbSerialDiscovery usbSerialDiscovery) { | ||
usbSerialDiscoveries.add(usbSerialDiscovery); | ||
usbSerialDiscovery.registerDiscoveryListener(this); | ||
usbSerialDiscovery.doSingleScan(); | ||
} | ||
|
||
protected synchronized void removeUsbSerialDiscovery(UsbSerialDiscovery usbSerialDiscovery) { | ||
usbSerialDiscovery.unregisterDiscoveryListener(this); | ||
usbSerialDiscoveries.remove(usbSerialDiscovery); | ||
} | ||
|
||
@Override | ||
public Set<AddonInfo> getSuggestedAddons() { | ||
Set<AddonInfo> result = new HashSet<>(); | ||
for (AddonInfo candidate : addonCandidates) { | ||
for (AddonDiscoveryMethod method : candidate.getDiscoveryMethods().stream() | ||
.filter(method -> SERVICE_TYPE.equals(method.getServiceType())).toList()) { | ||
Map<String, Pattern> matchProperties = method.getMatchProperties().stream() | ||
.collect(Collectors.toMap(property -> property.getName(), property -> property.getPattern())); | ||
|
||
Set<String> propertyNames = new HashSet<>(matchProperties.keySet()); | ||
propertyNames.removeAll(SUPPORTED_PROPERTIES); | ||
|
||
if (!propertyNames.isEmpty()) { | ||
logger.warn("Add-on '{}' addon.xml file contains unsupported 'match-property' [{}]", | ||
candidate.getUID(), String.join(",", propertyNames)); | ||
break; | ||
} | ||
|
||
logger.trace("Checking candidate: {}", candidate.getUID()); | ||
for (UsbSerialDeviceInformation device : usbDeviceInformations.values()) { | ||
logger.trace("Checking device: {}", device); | ||
|
||
if (propertyMatches(matchProperties, PRODUCT, device.getProduct()) | ||
&& propertyMatches(matchProperties, MANUFACTURER, device.getManufacturer()) | ||
&& propertyMatches(matchProperties, CHIP_ID, | ||
getChipId(device.getVendorId(), device.getProductId())) | ||
&& propertyMatches(matchProperties, REMOTE, String.valueOf(device.getRemote()))) { | ||
result.add(candidate); | ||
logger.debug("Suggested add-on found: {}", candidate.getUID()); | ||
break; | ||
} | ||
} | ||
} | ||
} | ||
return result; | ||
} | ||
|
||
private String getChipId(int vendorId, int productId) { | ||
return String.format("%04x:%04x", vendorId, productId); | ||
} | ||
|
||
@Override | ||
public String getServiceName() { | ||
return SERVICE_NAME; | ||
} | ||
|
||
/** | ||
* Create a unique 33 bit integer map hash key comprising the remote flag in the upper bit, the vendorId in the | ||
* middle 16 bits, and the productId in the lower 16 bits. | ||
*/ | ||
private long keyOf(UsbSerialDeviceInformation deviceInfo) { | ||
return (deviceInfo.getRemote() ? 0x1_0000_0000L : 0) + (deviceInfo.getVendorId() * 0x1_0000L) | ||
+ deviceInfo.getProductId(); | ||
} | ||
|
||
/** | ||
* Add the discovered USB device information record to our internal map. If there is already an entry in the map | ||
* then merge the two sets of data. | ||
* | ||
* @param discoveredInfo the newly discovered USB device information. | ||
*/ | ||
@Override | ||
public void usbSerialDeviceDiscovered(UsbSerialDeviceInformation discoveredInfo) { | ||
UsbSerialDeviceInformation targetInfo = discoveredInfo; | ||
UsbSerialDeviceInformation existingInfo = usbDeviceInformations.get(keyOf(targetInfo)); | ||
|
||
if (existingInfo != null) { | ||
boolean isMerging = false; | ||
String product = existingInfo.getProduct(); | ||
if (product != null) { | ||
product = discoveredInfo.getProduct(); | ||
isMerging = true; | ||
} | ||
String manufacturer = existingInfo.getManufacturer(); | ||
if (manufacturer != null) { | ||
manufacturer = discoveredInfo.getManufacturer(); | ||
isMerging = true; | ||
} | ||
String serialNumber = existingInfo.getSerialNumber(); | ||
if (serialNumber != null) { | ||
serialNumber = discoveredInfo.getSerialNumber(); | ||
isMerging = true; | ||
} | ||
boolean remote = existingInfo.getRemote(); | ||
if (remote == discoveredInfo.getRemote()) { | ||
isMerging = true; | ||
} | ||
if (isMerging) { | ||
targetInfo = new UsbSerialDeviceInformation(discoveredInfo.getVendorId(), discoveredInfo.getProductId(), | ||
serialNumber, manufacturer, product, discoveredInfo.getInterfaceNumber(), | ||
discoveredInfo.getInterfaceDescription(), discoveredInfo.getSerialPort()).setRemote(remote); | ||
} | ||
} | ||
|
||
usbDeviceInformations.put(keyOf(targetInfo), targetInfo); | ||
} | ||
|
||
@Override | ||
public void usbSerialDeviceRemoved(UsbSerialDeviceInformation removedInfo) { | ||
usbDeviceInformations.remove(keyOf(removedInfo)); | ||
} | ||
} |
Oops, something went wrong.