Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Add-on suggestion finder for USB devices #3922

Merged
merged 49 commits into from
Dec 27, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
49 commits
Select commit Hold shift + click to select a range
14e3349
Supress log output on shutdown
andrewfg Dec 10, 2023
0790943
Revert "Supress log output on shutdown"
andrewfg Dec 10, 2023
68569d3
Merge branch 'openhab:main' into main
andrewfg Dec 12, 2023
69bda66
Merge branch 'main' of https://github.com/andrewfg/openhab-core
andrewfg Dec 18, 2023
2f41474
Merge branch 'openhab:main' into main
andrewfg Dec 21, 2023
4e1f44b
Merge branch 'main' of https://github.com/andrewfg/openhab-core
andrewfg Dec 22, 2023
d66a7f5
Addon finder for USB related device things
andrewfg Dec 13, 2023
d69c589
Various fixes
andrewfg Dec 13, 2023
d7711e7
Work in progress
andrewfg Dec 14, 2023
f11246d
Create separate scanner and finder
andrewfg Dec 15, 2023
0e21c64
Fix karaf (again)
andrewfg Dec 15, 2023
4527f75
Tweaks
andrewfg Dec 15, 2023
d3e47dd
Refactoring and add test unit
andrewfg Dec 16, 2023
c94966d
Fix java doc
andrewfg Dec 16, 2023
f0a148b
Add Zooz sticks
andrewfg Dec 17, 2023
4724499
Improve javadoc; update database
andrewfg Dec 17, 2023
1a64f69
Addon finder for USB related device things
andrewfg Dec 13, 2023
d2c8749
Various fixes
andrewfg Dec 13, 2023
d74a963
Work in progress
andrewfg Dec 14, 2023
197bdf5
Create separate scanner and finder
andrewfg Dec 15, 2023
f6709bf
Tweaks
andrewfg Dec 15, 2023
8cfed18
Refactoring and add test unit
andrewfg Dec 16, 2023
0f455df
Refactoring and add test unit
andrewfg Dec 16, 2023
4474297
Fix java doc
andrewfg Dec 16, 2023
022b5b6
Addon finder for USB related device things
andrewfg Dec 13, 2023
95852e9
Various fixes
andrewfg Dec 13, 2023
f339e3d
Work in progress
andrewfg Dec 14, 2023
143fcf5
Create separate scanner and finder
andrewfg Dec 15, 2023
38b710f
Tweaks
andrewfg Dec 15, 2023
2e2b2b4
Refactoring and add test unit
andrewfg Dec 16, 2023
7de9eb2
Refactoring and add test unit
andrewfg Dec 16, 2023
13d8aab
Class name case conflict part 1
andrewfg Dec 17, 2023
be292a6
Class name case conflict part 2
andrewfg Dec 17, 2023
e4ebfda
Move otheros UsbSerialDiscovery to separate PR
andrewfg Dec 18, 2023
c971377
Merging of discovery results
andrewfg Dec 18, 2023
a996ee1
Fix pom
andrewfg Dec 18, 2023
60ed6ab
Fix build
andrewfg Dec 18, 2023
da4b488
Performance optimisations
andrewfg Dec 20, 2023
bd2e8f0
Adopt reviewer suggestions
andrewfg Dec 21, 2023
d154743
Fix pom
andrewfg Dec 22, 2023
d5adc9a
Apply spotless after release, resolve bundles (#3953)
holgerfriedrich Dec 22, 2023
987ef94
Merge branch 'openhab:main' into discovery-usb
andrewfg Dec 22, 2023
8b9f97c
Fix constant error
andrewfg Dec 22, 2023
3b8c5a5
Adopt reviwer suggestions
andrewfg Dec 23, 2023
ae4c801
Merge branch 'openhab:main' into discovery-usb
andrewfg Dec 23, 2023
28db618
Fix unit tests
andrewfg Dec 23, 2023
e815e9e
Add chipId property
andrewfg Dec 25, 2023
752e100
Harmonize logging across finders
andrewfg Dec 27, 2023
2574f88
Adopt reviewer suggestions
andrewfg Dec 27, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
6 changes: 6 additions & 0 deletions bom/openhab-core/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -334,6 +334,12 @@
<version>${project.version}</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.openhab.core.bundles</groupId>
<artifactId>org.openhab.core.config.discovery.addon.usb</artifactId>
<version>${project.version}</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.openhab.core.bundles</groupId>
<artifactId>org.openhab.core.config.discovery.mdns</artifactId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ public Set<AddonInfo> getSuggestedAddons() {
}

// now check if a process matches the pattern defined in addon.xml
logger.debug("Checking candidate: {}", candidate.getUID());
logger.trace("Checking candidate: {}", candidate.getUID());

for (AddonMatchProperty command : commands) {
logger.trace("Candidate {}, pattern \"{}\"", candidate.getUID(), command.getRegex());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,7 @@ && propertyMatches(matchProperties, MODEL_URI, modelURI)
&& propertyMatches(matchProperties, SERIAL_NUMBER, serialNumber)
&& propertyMatches(matchProperties, FRIENDLY_NAME, friendlyName)) {
result.add(candidate);
logger.debug("Suggested addon found: {}", candidate.getUID());
logger.debug("Suggested add-on found: {}", candidate.getUID());
break;
}
}
Expand Down
29 changes: 29 additions & 0 deletions bundles/org.openhab.core.config.discovery.addon.usb/.classpath
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 bundles/org.openhab.core.config.discovery.addon.usb/.project
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 bundles/org.openhab.core.config.discovery.addon.usb/NOTICE
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 bundles/org.openhab.core.config.discovery.addon.usb/pom.xml
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>
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));
}
}