[sonyprojector] Add discovery through SDDP (#16849)

Only applicable to projector models having a network connector

Signed-off-by: Laurent Garnier <lg.hc@free.fr>
This commit is contained in:
lolodomo 2024-06-14 17:48:02 +02:00 committed by GitHub
parent 67e6f8bf30
commit cc71808b78
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
10 changed files with 149 additions and 27 deletions

View File

@ -94,8 +94,8 @@ This binding supports the following thing types:
## Discovery
Discovery is not supported at the moment.
You have to add all things manually.
If the projector is connected via Ethernet and the 'Start SDDP Service' option is present and enabled in the projector Advanced Settings->Service page, the Thing using Ethernet connection and PJ Talk will be discovered automatically.
Serial or Serial over IP connections must be configured manually.
## Binding Configuration

View File

@ -5,6 +5,7 @@
<feature name="openhab-binding-sonyprojector" description="Sony Projector Binding" version="${project.version}">
<feature>openhab-runtime-base</feature>
<feature>openhab-transport-serial</feature>
<feature>openhab-core-config-discovery-sddp</feature>
<bundle start-level="80">mvn:org.openhab.addons.bundles/org.openhab.binding.sonyprojector/${project.version}</bundle>
</feature>
</features>

View File

@ -143,6 +143,9 @@ public enum SonyProjectorItem {
SERIAL_NUMBER("Serial Number", new byte[] { (byte) 0x80, 0x02 }),
INSTALLATION_LOCATION("Installation Location", new byte[] { (byte) 0x80, 0x03 }),
MAC_ADDRESS("MAC Address", new byte[] { (byte) 0x90, 0x00 }),
IP_ADDRESS("IP Address", new byte[] { (byte) 0x90, 0x01 }),
MENU("Menu", null, new byte[] { 0x17, 0x29 }),
UP("Cursor UP", null, new byte[] { 0x17, 0x35 }),
DOWN("Cursor DOWN", null, new byte[] { 0x17, 0x36 }),

View File

@ -311,4 +311,15 @@ public class SonyProjectorSdcpConnector extends SonyProjectorConnector {
public String getModelName() throws SonyProjectorException {
return new String(getSetting(SonyProjectorItem.MODEL_NAME), StandardCharsets.UTF_8);
}
/**
* Request the MAC address
*
* @return the MAC address
*
* @throws SonyProjectorException in case of any problem
*/
public String getMacAddress() throws SonyProjectorException {
return new String(getSetting(SonyProjectorItem.MAC_ADDRESS), StandardCharsets.UTF_8);
}
}

View File

@ -13,7 +13,6 @@
package org.openhab.binding.sonyprojector.internal.configuration;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
/**
* The {@link SonyProjectorEthernetConfiguration} class contains fields mapping thing configuration parameters.
@ -23,9 +22,12 @@ import org.eclipse.jdt.annotation.Nullable;
*/
@NonNullByDefault
public class SonyProjectorEthernetConfiguration {
public static final int DEFAULT_PORT = 53484;
private static final String DEFAULT_COMMUNITY = "SONY";
public static final String MODEL_AUTO = "AUTO";
public @NonNullByDefault({}) String host;
public @Nullable Integer port;
public @Nullable String community;
public @Nullable String model;
public String host = "";
public int port = DEFAULT_PORT;
public String community = DEFAULT_COMMUNITY;
public String model = MODEL_AUTO;
}

View File

@ -13,6 +13,7 @@
package org.openhab.binding.sonyprojector.internal.configuration;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.binding.sonyprojector.internal.handler.SonyProjectorHandler;
/**
* The {@link SonyProjectorSerialConfiguration} class contains fields mapping thing configuration parameters.
@ -21,7 +22,6 @@ import org.eclipse.jdt.annotation.NonNullByDefault;
*/
@NonNullByDefault
public class SonyProjectorSerialConfiguration {
public @NonNullByDefault({}) String port;
public @NonNullByDefault({}) String model;
public String port = "";
public String model = SonyProjectorHandler.DEFAULT_MODEL.getName();
}

View File

@ -13,6 +13,7 @@
package org.openhab.binding.sonyprojector.internal.configuration;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.binding.sonyprojector.internal.handler.SonyProjectorHandler;
/**
* The {@link SonyProjectorSerialOverIpConfiguration} class contains fields mapping thing configuration parameters.
@ -21,8 +22,7 @@ import org.eclipse.jdt.annotation.NonNullByDefault;
*/
@NonNullByDefault
public class SonyProjectorSerialOverIpConfiguration {
public @NonNullByDefault({}) String host;
public @NonNullByDefault({}) Integer port;
public @NonNullByDefault({}) String model;
public String host = "";
public int port;
public String model = SonyProjectorHandler.DEFAULT_MODEL.getName();
}

View File

@ -0,0 +1,82 @@
/**
* Copyright (c) 2010-2024 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.binding.sonyprojector.internal.discovery;
import static org.openhab.binding.sonyprojector.internal.SonyProjectorBindingConstants.THING_TYPE_ETHERNET;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.sonyprojector.internal.configuration.SonyProjectorEthernetConfiguration;
import org.openhab.core.config.discovery.DiscoveryResult;
import org.openhab.core.config.discovery.DiscoveryResultBuilder;
import org.openhab.core.config.discovery.sddp.SddpDevice;
import org.openhab.core.config.discovery.sddp.SddpDiscoveryParticipant;
import org.openhab.core.thing.Thing;
import org.openhab.core.thing.ThingTypeUID;
import org.openhab.core.thing.ThingUID;
import org.osgi.service.component.annotations.Component;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Discovery Service for Sony Projectors that support SDDP.
*
* @author Laurent Garnier - Initial contribution
*
*/
@NonNullByDefault
@Component(immediate = true)
public class SonyProjectorDiscoveryParticipant implements SddpDiscoveryParticipant {
private final Logger logger = LoggerFactory.getLogger(SonyProjectorDiscoveryParticipant.class);
private static final String SONY = "SONY";
private static final String TYPE_PROJECTOR = "PROJECTOR";
@Override
public Set<ThingTypeUID> getSupportedThingTypeUIDs() {
return Set.of(THING_TYPE_ETHERNET);
}
@Override
public @Nullable DiscoveryResult createResult(SddpDevice device) {
final ThingUID uid = getThingUID(device);
if (uid != null) {
final String label = device.manufacturer + " " + device.model;
final Map<String, Object> properties = Map.of("host", device.ipAddress, //
"port", SonyProjectorEthernetConfiguration.DEFAULT_PORT, //
"model", SonyProjectorEthernetConfiguration.MODEL_AUTO, //
Thing.PROPERTY_MAC_ADDRESS, device.macAddress);
logger.debug("Created a DiscoveryResult for device '{}' with UID '{}'", label, uid.getId());
return DiscoveryResultBuilder.create(uid).withProperties(properties)
.withRepresentationProperty(Thing.PROPERTY_MAC_ADDRESS).withLabel(label).build();
} else {
return null;
}
}
@Override
public @Nullable ThingUID getThingUID(SddpDevice device) {
if (device.manufacturer.toUpperCase(Locale.ENGLISH).contains(SONY)
&& device.type.toUpperCase(Locale.ENGLISH).contains(TYPE_PROJECTOR) && !device.macAddress.isBlank()
&& !device.ipAddress.isBlank()) {
logger.debug("Sony projector with mac {} found at {}", device.macAddress, device.ipAddress);
return new ThingUID(THING_TYPE_ETHERNET, device.macAddress);
}
return null;
}
}

View File

@ -13,6 +13,7 @@
package org.openhab.binding.sonyprojector.internal.handler;
import static org.openhab.binding.sonyprojector.internal.SonyProjectorBindingConstants.*;
import static org.openhab.binding.sonyprojector.internal.configuration.SonyProjectorEthernetConfiguration.MODEL_AUTO;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
@ -67,7 +68,7 @@ import org.slf4j.LoggerFactory;
@NonNullByDefault
public class SonyProjectorHandler extends BaseThingHandler {
private static final SonyProjectorModel DEFAULT_MODEL = SonyProjectorModel.VW520;
public static final SonyProjectorModel DEFAULT_MODEL = SonyProjectorModel.VW528;
private static final long POLLING_INTERVAL = TimeUnit.SECONDS.toSeconds(15);
private final Logger logger = LoggerFactory.getLogger(SonyProjectorHandler.class);
@ -83,6 +84,7 @@ public class SonyProjectorHandler extends BaseThingHandler {
private @Nullable ScheduledFuture<?> refreshJob;
private boolean identifyMac;
private boolean identifyProjector;
private SonyProjectorModel projectorModel = DEFAULT_MODEL;
private SonyProjectorConnector connector = new SonyProjectorSdcpSimuConnector(DEFAULT_MODEL);
@ -321,10 +323,13 @@ public class SonyProjectorHandler extends BaseThingHandler {
logger.debug("Ethernet config port {}", config.port);
logger.debug("Ethernet config model {}", configModel);
logger.debug("Ethernet config community {}", config.community);
if (config.host == null || config.host.isEmpty()) {
if (config.host.isBlank()) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
"@text/offline.config-error-unknown-host");
} else if (configModel == null || configModel.isEmpty()) {
} else if (config.port <= 0) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
"@text/offline.config-error-invalid-port");
} else if (configModel.isBlank()) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
"@text/offline.config-error-unknown-model");
} else {
@ -332,8 +337,9 @@ public class SonyProjectorHandler extends BaseThingHandler {
connector = simu ? new SonyProjectorSdcpSimuConnector(DEFAULT_MODEL)
: new SonyProjectorSdcpConnector(config.host, config.port, config.community, DEFAULT_MODEL);
identifyProjector = "AUTO".equals(configModel);
projectorModel = switchToModel("AUTO".equals(configModel) ? null : configModel, true);
identifyMac = getThing().getProperties().get(Thing.PROPERTY_MAC_ADDRESS) == null;
identifyProjector = MODEL_AUTO.equals(configModel);
projectorModel = switchToModel(identifyProjector ? null : configModel, true);
updateStatus(ThingStatus.UNKNOWN);
}
@ -342,13 +348,13 @@ public class SonyProjectorHandler extends BaseThingHandler {
String configModel = config.model;
logger.debug("Serial config port {}", config.port);
logger.debug("Serial config model {}", configModel);
if (config.port == null || config.port.isEmpty()) {
if (config.port.isBlank()) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
"@text/offline.config-error-unknown-port");
} else if (config.port.toLowerCase().startsWith("rfc2217")) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
"@text/offline.config-error-invalid-thing-type");
} else if (configModel == null || configModel.isEmpty()) {
} else if (configModel.isBlank()) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
"@text/offline.config-error-unknown-model");
} else {
@ -356,6 +362,7 @@ public class SonyProjectorHandler extends BaseThingHandler {
connector = simu ? new SonyProjectorSerialSimuConnector(serialPortManager, DEFAULT_MODEL)
: new SonyProjectorSerialConnector(serialPortManager, config.port, DEFAULT_MODEL);
identifyMac = false;
identifyProjector = false;
projectorModel = switchToModel(configModel, true);
@ -367,16 +374,13 @@ public class SonyProjectorHandler extends BaseThingHandler {
logger.debug("Serial over IP config host {}", config.host);
logger.debug("Serial over IP config port {}", config.port);
logger.debug("Serial over IP config model {}", configModel);
if (config.host == null || config.host.isEmpty()) {
if (config.host.isBlank()) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
"@text/offline.config-error-unknown-host");
} else if (config.port == null) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
"@text/offline.config-error-unknown-port");
} else if (config.port <= 0) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
"@text/offline.config-error-invalid-port");
} else if (configModel == null || configModel.isEmpty()) {
} else if (configModel.isBlank()) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
"@text/offline.config-error-unknown-model");
} else {
@ -385,6 +389,7 @@ public class SonyProjectorHandler extends BaseThingHandler {
connector = simu ? new SonyProjectorSerialSimuConnector(serialPortManager, DEFAULT_MODEL)
: new SonyProjectorSerialOverIpConnector(serialPortManager, config.host, config.port,
DEFAULT_MODEL);
identifyMac = false;
identifyProjector = false;
projectorModel = switchToModel(configModel, true);
@ -432,6 +437,7 @@ public class SonyProjectorHandler extends BaseThingHandler {
boolean isOn = refreshPowerState();
refreshModel();
refreshMacAddress();
refreshChannel(CHANNEL_INPUT, isOn);
refreshChannel(CHANNEL_CALIBRATION_PRESET, isOn);
refreshChannel(CHANNEL_CONTRAST, isOn);
@ -537,6 +543,19 @@ public class SonyProjectorHandler extends BaseThingHandler {
return model;
}
private void refreshMacAddress() {
if (identifyMac && getThing().getThingTypeUID().equals(THING_TYPE_ETHERNET)) {
try {
String mac = ((SonyProjectorSdcpConnector) connector).getMacAddress();
logger.debug("getMacAddress returned {}", mac);
getThing().setProperty(Thing.PROPERTY_MAC_ADDRESS, mac);
identifyMac = false;
} catch (SonyProjectorException e) {
logger.debug("getMacAddress failed: {}", e.getMessage());
}
}
}
private boolean refreshPowerState() {
boolean on = false;
State state = UnDefType.UNDEF;

View File

@ -12,9 +12,13 @@
<discovery-method>
<service-type>sddp</service-type>
<match-properties>
<match-property>
<name>manufacturer</name>
<regex>(?i).*sony.*</regex>
</match-property>
<match-property>
<name>type</name>
<regex>(?i)sony:projector.*</regex>
<regex>(?i).*projector.*</regex>
</match-property>
</match-properties>
</discovery-method>