added migrated 2.x add-ons
Signed-off-by: Kai Kreuzer <kai@openhab.org>
This commit is contained in:
@@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<features name="org.openhab.binding.pjlinkdevice-${project.version}" xmlns="http://karaf.apache.org/xmlns/features/v1.4.0">
|
||||
<repository>mvn:org.openhab.core.features.karaf/org.openhab.core.features.karaf.openhab-core/${ohc.version}/xml/features</repository>
|
||||
|
||||
<feature name="openhab-binding-pjlinkdevice" description="PJLinkDevice Binding" version="${project.version}">
|
||||
<feature>openhab-runtime-base</feature>
|
||||
<bundle start-level="80">mvn:org.openhab.addons.bundles/org.openhab.binding.pjlinkdevice/${project.version}</bundle>
|
||||
</feature>
|
||||
</features>
|
||||
@@ -0,0 +1,33 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 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.pjlinkdevice.internal;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
|
||||
/**
|
||||
* Exception thrown whenever the thing configuration is invalid
|
||||
*
|
||||
* @author Nils Schnabel - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class ConfigurationException extends Exception {
|
||||
private static final long serialVersionUID = -3319800607314286998L;
|
||||
|
||||
public ConfigurationException(String string) {
|
||||
super(string);
|
||||
}
|
||||
|
||||
public ConfigurationException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 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.pjlinkdevice.internal;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.core.thing.binding.BaseDynamicStateDescriptionProvider;
|
||||
import org.openhab.core.thing.i18n.ChannelTypeI18nLocalizationService;
|
||||
import org.openhab.core.thing.type.DynamicStateDescriptionProvider;
|
||||
import org.osgi.service.component.annotations.Component;
|
||||
import org.osgi.service.component.annotations.Reference;
|
||||
|
||||
/**
|
||||
* Dynamic provider of state options for the input selection of the PJLink device.
|
||||
*
|
||||
* @author Nils Schnabel - Initial contribution
|
||||
*/
|
||||
@Component(service = { DynamicStateDescriptionProvider.class, InputChannelStateDescriptionProvider.class })
|
||||
@NonNullByDefault
|
||||
public class InputChannelStateDescriptionProvider extends BaseDynamicStateDescriptionProvider {
|
||||
@Reference
|
||||
protected void setChannelTypeI18nLocalizationService(
|
||||
final ChannelTypeI18nLocalizationService channelTypeI18nLocalizationService) {
|
||||
this.channelTypeI18nLocalizationService = channelTypeI18nLocalizationService;
|
||||
}
|
||||
|
||||
protected void unsetChannelTypeI18nLocalizationService(
|
||||
final ChannelTypeI18nLocalizationService channelTypeI18nLocalizationService) {
|
||||
this.channelTypeI18nLocalizationService = null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 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.pjlinkdevice.internal;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.core.thing.ThingTypeUID;
|
||||
|
||||
/**
|
||||
* The {@link PJLinkDeviceBindingConstants} class defines common constants, which are
|
||||
* used across the whole binding.
|
||||
*
|
||||
* @author Nils Schnabel - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class PJLinkDeviceBindingConstants {
|
||||
|
||||
private static final String BINDING_ID = "pjLinkDevice";
|
||||
|
||||
// List of all thing type UIDs
|
||||
public static final ThingTypeUID THING_TYPE_PJLINK = new ThingTypeUID(BINDING_ID, "pjLinkDevice");
|
||||
|
||||
// List of all channel type IDs
|
||||
public static final String CHANNEL_TYPE_POWER = "power";
|
||||
public static final String CHANNEL_TYPE_INPUT = "input";
|
||||
public static final String CHANNEL_TYPE_AUDIO_MUTE = "audioMute";
|
||||
public static final String CHANNEL_TYPE_VIDEO_MUTE = "videoMute";
|
||||
public static final String CHANNEL_TYPE_LAMP_HOURS = "lampHours";
|
||||
public static final String CHANNEL_TYPE_LAMP_ACTIVE = "lampActive";
|
||||
|
||||
// List of all channel IDs
|
||||
public static final String CHANNEL_POWER = "power";
|
||||
public static final String CHANNEL_INPUT = "input";
|
||||
public static final String CHANNEL_AUDIO_MUTE = "audioMute";
|
||||
public static final String CHANNEL_VIDEO_MUTE = "videoMute";
|
||||
public static final String CHANNEL_LAMP_1_HOURS = "lamp1Hours";
|
||||
public static final String CHANNEL_LAMP_1_ACTIVE = "lamp1Active";
|
||||
|
||||
// List of all channel parameter names
|
||||
public static final String CHANNEL_PARAMETER_LAMP_NUMBER = "lampNumber";
|
||||
|
||||
public static final int DEFAULT_PORT = 4352;
|
||||
public static final int DEFAULT_SCAN_TIMEOUT_SECONDS = 60;
|
||||
|
||||
// configuration
|
||||
public static final String PARAMETER_HOSTNAME = "ipAddress";
|
||||
public static final String PARAMETER_PORT = "tcpPort";
|
||||
public static final long DISCOVERY_RESULT_TTL_SECONDS = TimeUnit.MINUTES.toSeconds(10);
|
||||
|
||||
// information disclosed by device
|
||||
public static final String PROPERTY_CLASS = "disclosedPjLinkClass";
|
||||
public static final String PROPERTY_NAME = "disclosedName";
|
||||
public static final String PROPERTY_ERROR_STATUS = "disclosedErrorStatus";
|
||||
public static final String PROPERTY_LAMP_HOURS = "disclosedLampHours";
|
||||
public static final String PROPERTY_OTHER_INFORMATION = "disclosedOtherInformation";
|
||||
|
||||
// calculated properties
|
||||
public static final String PROPERTY_AUTHENTICATION_REQUIRED = "authenticationRequired";
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 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.pjlinkdevice.internal;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* The {@link PJLinkDeviceConfiguration} class contains fields mapping thing configuration parameters.
|
||||
*
|
||||
* @author Nils Schnabel - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class PJLinkDeviceConfiguration {
|
||||
public @Nullable String ipAddress;
|
||||
public int tcpPort;
|
||||
|
||||
public @Nullable String adminPassword;
|
||||
|
||||
public int refreshInterval;
|
||||
public boolean refreshPower;
|
||||
public boolean refreshMute;
|
||||
public boolean refreshInputChannel;
|
||||
public boolean refreshLampState;
|
||||
|
||||
public int autoReconnectInterval;
|
||||
}
|
||||
@@ -0,0 +1,447 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 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.pjlinkdevice.internal;
|
||||
|
||||
import static org.openhab.binding.pjlinkdevice.internal.PJLinkDeviceBindingConstants.*;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.math.BigDecimal;
|
||||
import java.net.InetAddress;
|
||||
import java.net.UnknownHostException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.binding.pjlinkdevice.internal.device.PJLinkDevice;
|
||||
import org.openhab.binding.pjlinkdevice.internal.device.command.AuthenticationException;
|
||||
import org.openhab.binding.pjlinkdevice.internal.device.command.ResponseException;
|
||||
import org.openhab.binding.pjlinkdevice.internal.device.command.input.Input;
|
||||
import org.openhab.binding.pjlinkdevice.internal.device.command.lampstatus.LampStatesResponse.LampState;
|
||||
import org.openhab.binding.pjlinkdevice.internal.device.command.mute.MuteInstructionCommand.MuteInstructionChannel;
|
||||
import org.openhab.binding.pjlinkdevice.internal.device.command.mute.MuteQueryResponse.MuteQueryResponseValue;
|
||||
import org.openhab.binding.pjlinkdevice.internal.device.command.power.PowerQueryResponse.PowerQueryResponseValue;
|
||||
import org.openhab.core.config.core.validation.ConfigValidationException;
|
||||
import org.openhab.core.library.types.DecimalType;
|
||||
import org.openhab.core.library.types.OnOffType;
|
||||
import org.openhab.core.library.types.StringType;
|
||||
import org.openhab.core.thing.Channel;
|
||||
import org.openhab.core.thing.ChannelUID;
|
||||
import org.openhab.core.thing.Thing;
|
||||
import org.openhab.core.thing.ThingStatus;
|
||||
import org.openhab.core.thing.ThingStatusDetail;
|
||||
import org.openhab.core.thing.binding.BaseThingHandler;
|
||||
import org.openhab.core.thing.type.ChannelTypeUID;
|
||||
import org.openhab.core.types.Command;
|
||||
import org.openhab.core.types.RefreshType;
|
||||
import org.openhab.core.types.StateOption;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* The {@link PJLinkDeviceHandler} is responsible for handling commands, which are
|
||||
* sent to one of the channels.
|
||||
*
|
||||
* @author Nils Schnabel - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class PJLinkDeviceHandler extends BaseThingHandler {
|
||||
|
||||
private @Nullable PJLinkDeviceConfiguration config;
|
||||
|
||||
private @Nullable PJLinkDevice device;
|
||||
|
||||
private InputChannelStateDescriptionProvider stateDescriptionProvider;
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(PJLinkDeviceHandler.class);
|
||||
|
||||
private @Nullable ScheduledFuture<?> refreshJob;
|
||||
|
||||
private @Nullable ScheduledFuture<?> setupJob;
|
||||
|
||||
public PJLinkDeviceHandler(Thing thing, InputChannelStateDescriptionProvider stateDescriptionProvider) {
|
||||
super(thing);
|
||||
this.stateDescriptionProvider = stateDescriptionProvider;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
clearSetupJob();
|
||||
|
||||
clearRefreshInterval();
|
||||
|
||||
final PJLinkDevice device = this.device;
|
||||
if (device != null) {
|
||||
device.dispose();
|
||||
}
|
||||
|
||||
this.config = null;
|
||||
this.device = null;
|
||||
}
|
||||
|
||||
public void refresh(PJLinkDeviceConfiguration config) {
|
||||
PJLinkDeviceHandler.this.logger.debug("Polling device status...");
|
||||
|
||||
// build list of channels to be refreshed
|
||||
List<String> channelNames = new ArrayList<>();
|
||||
if (config.refreshPower) {
|
||||
channelNames.add(CHANNEL_POWER);
|
||||
}
|
||||
if (config.refreshMute) {
|
||||
// this updates both CHANNEL_AUDIO_MUTE and CHANNEL_VIDEO_MUTE
|
||||
channelNames.add(CHANNEL_AUDIO_MUTE);
|
||||
}
|
||||
if (config.refreshInputChannel) {
|
||||
channelNames.add(CHANNEL_INPUT);
|
||||
}
|
||||
if (config.refreshLampState) {
|
||||
// this updates both CHANNEL_LAMP_ACTIVE and CHANNEL_LAMP_HOURS for all lamps
|
||||
channelNames.add(CHANNEL_LAMP_1_HOURS);
|
||||
}
|
||||
|
||||
// refresh all channels enabled for refreshing
|
||||
for (String channelName : channelNames) {
|
||||
// Do not poll if device is offline
|
||||
if (PJLinkDeviceHandler.this.getThing().getStatus() != ThingStatus.ONLINE) {
|
||||
PJLinkDeviceHandler.this.logger.debug("Not polling device status because device is offline");
|
||||
// setup() will schedule a new refresh interval after successful reconnection, cancel this one
|
||||
this.clearRefreshInterval();
|
||||
return;
|
||||
}
|
||||
|
||||
PJLinkDeviceHandler.this.handleCommand(new ChannelUID(getThing().getUID(), channelName),
|
||||
RefreshType.REFRESH);
|
||||
}
|
||||
}
|
||||
|
||||
public PJLinkDevice getDevice() throws UnknownHostException, ConfigurationException {
|
||||
PJLinkDevice device = this.device;
|
||||
if (device == null) {
|
||||
PJLinkDeviceConfiguration config = getConfiguration();
|
||||
this.device = device = new PJLinkDevice(config.tcpPort, InetAddress.getByName(config.ipAddress),
|
||||
config.adminPassword);
|
||||
}
|
||||
return device;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleCommand(ChannelUID channelUID, Command command) {
|
||||
logger.trace("Received command {} on channel {}", command, channelUID.getId());
|
||||
try {
|
||||
PJLinkDevice device = getDevice();
|
||||
String channelTypeId = getChannelTypeId(channelUID);
|
||||
if (channelTypeId == null) {
|
||||
logger.debug("unknown channel {}", channelUID);
|
||||
return;
|
||||
}
|
||||
switch (channelTypeId) {
|
||||
case CHANNEL_TYPE_POWER:
|
||||
logger.trace("Received power command {}", command);
|
||||
if (command == OnOffType.ON) {
|
||||
device.powerOn();
|
||||
} else if (command == OnOffType.OFF) {
|
||||
device.powerOff();
|
||||
} else if (command == RefreshType.REFRESH) {
|
||||
updateState(PJLinkDeviceBindingConstants.CHANNEL_POWER,
|
||||
PowerQueryResponseValue.POWER_ON.equals(device.getPowerStatus().getResult())
|
||||
? OnOffType.ON
|
||||
: OnOffType.OFF);
|
||||
} else {
|
||||
logger.debug("Received unknown power command {}", command);
|
||||
}
|
||||
break;
|
||||
case CHANNEL_TYPE_INPUT:
|
||||
if (command == RefreshType.REFRESH) {
|
||||
StringType input = new StringType(device.getInputStatus().getResult().getValue());
|
||||
updateState(PJLinkDeviceBindingConstants.CHANNEL_INPUT, input);
|
||||
} else if (command instanceof StringType) {
|
||||
logger.trace("Received input command {}", command);
|
||||
Input input = new Input(((StringType) command).toString());
|
||||
device.setInput(input);
|
||||
} else {
|
||||
logger.debug("Received unknown channel command {}", command);
|
||||
}
|
||||
break;
|
||||
case CHANNEL_TYPE_AUDIO_MUTE:
|
||||
case CHANNEL_TYPE_VIDEO_MUTE:
|
||||
boolean isAudioMute = channelTypeId.equals(PJLinkDeviceBindingConstants.CHANNEL_TYPE_AUDIO_MUTE);
|
||||
boolean isVideoMute = channelTypeId.equals(PJLinkDeviceBindingConstants.CHANNEL_TYPE_VIDEO_MUTE);
|
||||
if (isVideoMute || isAudioMute) {
|
||||
if (command == RefreshType.REFRESH) {
|
||||
// refresh both video and audio mute, as it's one request
|
||||
MuteQueryResponseValue muteStatus = device.getMuteStatus();
|
||||
updateState(PJLinkDeviceBindingConstants.CHANNEL_AUDIO_MUTE,
|
||||
muteStatus.isAudioMuted() ? OnOffType.ON : OnOffType.OFF);
|
||||
updateState(PJLinkDeviceBindingConstants.CHANNEL_VIDEO_MUTE,
|
||||
muteStatus.isVideoMuted() ? OnOffType.ON : OnOffType.OFF);
|
||||
} else {
|
||||
if (isAudioMute) {
|
||||
logger.trace("Received audio mute command {}", command);
|
||||
boolean muteOn = command == OnOffType.ON;
|
||||
device.setMute(MuteInstructionChannel.AUDIO, muteOn);
|
||||
}
|
||||
if (isVideoMute) {
|
||||
logger.trace("Received video mute command {}", command);
|
||||
boolean muteOn = command == OnOffType.ON;
|
||||
device.setMute(MuteInstructionChannel.VIDEO, muteOn);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
logger.debug("Received unknown audio/video mute command {}", command);
|
||||
}
|
||||
break;
|
||||
case CHANNEL_TYPE_LAMP_ACTIVE:
|
||||
case CHANNEL_TYPE_LAMP_HOURS:
|
||||
if (command == RefreshType.REFRESH) {
|
||||
List<LampState> lampStates = device.getLampStatesCached();
|
||||
// update all lamp related channels, as the response contains information about all of them
|
||||
for (Channel lampChannel : thing.getChannels()) {
|
||||
String lampChannelTypeId = getChannelTypeId(lampChannel.getUID());
|
||||
if (lampChannelTypeId == null) {
|
||||
continue;
|
||||
}
|
||||
boolean isLampActiveChannel = CHANNEL_TYPE_LAMP_ACTIVE.equals(lampChannelTypeId);
|
||||
boolean isLampHoursChannel = CHANNEL_TYPE_LAMP_HOURS.equals(lampChannelTypeId);
|
||||
|
||||
if (isLampActiveChannel || isLampHoursChannel) {
|
||||
int lampNumber = ((BigDecimal) lampChannel.getConfiguration()
|
||||
.get(CHANNEL_PARAMETER_LAMP_NUMBER)).intValue();
|
||||
try {
|
||||
LampState lampState = lampStates.get(lampNumber - 1);
|
||||
if (isLampActiveChannel) {
|
||||
updateState(lampChannel.getUID(),
|
||||
lampState.isActive() ? OnOffType.ON : OnOffType.OFF);
|
||||
}
|
||||
if (isLampHoursChannel) {
|
||||
updateState(lampChannel.getUID(), new DecimalType(lampState.getLampHours()));
|
||||
}
|
||||
} catch (IndexOutOfBoundsException e) {
|
||||
logger.debug("Status information for lamp {} is not available", lampNumber);
|
||||
throw new ConfigurationException(
|
||||
"Status information for lamp " + lampNumber + " is not available");
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
logger.debug("Received unknown lamp state command {}", command);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
logger.debug("unknown channel {}", channelUID);
|
||||
break;
|
||||
}
|
||||
|
||||
logger.trace("Successfully handled command {} on channel {}", command, channelUID.getId());
|
||||
handleCommunicationEstablished();
|
||||
} catch (IOException | ResponseException e) {
|
||||
handleCommunicationException(e);
|
||||
} catch (ConfigurationException e) {
|
||||
handleConfigurationException(e);
|
||||
} catch (AuthenticationException e) {
|
||||
handleAuthenticationException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private @Nullable String getChannelTypeId(ChannelUID channelUID) {
|
||||
Channel channel = thing.getChannel(channelUID);
|
||||
if (channel == null) {
|
||||
logger.debug("channel is null");
|
||||
return null;
|
||||
}
|
||||
ChannelTypeUID channelTypeUID = channel.getChannelTypeUID();
|
||||
if (channelTypeUID == null) {
|
||||
logger.debug("channelTypeUID for channel {} is null", channel);
|
||||
return null;
|
||||
}
|
||||
String channelTypeId = channelTypeUID.getId();
|
||||
if (channelTypeId == null) {
|
||||
logger.debug("channelTypeId for channelTypeUID {} is null", channelTypeUID);
|
||||
return null;
|
||||
}
|
||||
return channelTypeId;
|
||||
}
|
||||
|
||||
private void handleCommunicationEstablished() {
|
||||
updateStatus(ThingStatus.ONLINE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize() {
|
||||
this.setup(0);
|
||||
}
|
||||
|
||||
public void setup(int delay) {
|
||||
this.clearSetupJob();
|
||||
this.setupJob = scheduler.schedule(() -> {
|
||||
try {
|
||||
setupDevice();
|
||||
handleCommunicationEstablished();
|
||||
|
||||
setupRefreshInterval();
|
||||
|
||||
logger.trace("device {} setup up successfully", this.getThing().getUID());
|
||||
} catch (ResponseException | IOException e) {
|
||||
handleCommunicationException(e);
|
||||
} catch (ConfigurationException e) {
|
||||
handleConfigurationException(e);
|
||||
} catch (AuthenticationException e) {
|
||||
handleAuthenticationException(e);
|
||||
}
|
||||
}, delay, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
protected PJLinkDeviceConfiguration getConfiguration() throws ConfigurationException {
|
||||
PJLinkDeviceConfiguration config = this.config;
|
||||
if (config != null) {
|
||||
return config;
|
||||
}
|
||||
|
||||
Map<String, String> validationMessages = new HashMap<>();
|
||||
try {
|
||||
validateConfigurationParameters(getThing().getConfiguration().getProperties());
|
||||
} catch (ConfigValidationException e) {
|
||||
validationMessages.putAll(e.getValidationMessages());
|
||||
}
|
||||
|
||||
this.config = config = getConfigAs(PJLinkDeviceConfiguration.class);
|
||||
|
||||
int autoReconnectInterval = config.autoReconnectInterval;
|
||||
if (autoReconnectInterval != 0 && autoReconnectInterval < 30) {
|
||||
validationMessages.put("autoReconnectInterval", "allowed values are 0 (never) or >30");
|
||||
}
|
||||
|
||||
if (!validationMessages.isEmpty()) {
|
||||
String message = validationMessages.entrySet().stream()
|
||||
.map((Map.Entry<String, String> a) -> (a.getKey() + ": " + a.getValue()))
|
||||
.collect(Collectors.joining("; "));
|
||||
throw new ConfigurationException(message);
|
||||
}
|
||||
|
||||
return config;
|
||||
}
|
||||
|
||||
private void clearSetupJob() {
|
||||
final ScheduledFuture<?> setupJob = this.setupJob;
|
||||
if (setupJob != null) {
|
||||
setupJob.cancel(true);
|
||||
this.setupJob = null;
|
||||
}
|
||||
}
|
||||
|
||||
private void clearRefreshInterval() {
|
||||
final ScheduledFuture<?> refreshJob = this.refreshJob;
|
||||
if (refreshJob != null) {
|
||||
refreshJob.cancel(true);
|
||||
this.refreshJob = null;
|
||||
}
|
||||
}
|
||||
|
||||
private void handleAuthenticationException(AuthenticationException e) {
|
||||
this.clearRefreshInterval();
|
||||
updateProperty(PJLinkDeviceBindingConstants.PROPERTY_AUTHENTICATION_REQUIRED, Boolean.TRUE.toString());
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, e.getMessage());
|
||||
}
|
||||
|
||||
private void handleCommunicationException(Exception e) {
|
||||
this.clearRefreshInterval();
|
||||
PJLinkDeviceConfiguration config = this.config;
|
||||
if (config != null && config.autoReconnectInterval > 0) {
|
||||
this.setup(config.autoReconnectInterval);
|
||||
}
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.getMessage());
|
||||
}
|
||||
|
||||
private void handleConfigurationException(ConfigurationException e) {
|
||||
this.clearRefreshInterval();
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, e.getMessage());
|
||||
}
|
||||
|
||||
private void setupDevice() throws ConfigurationException, IOException, AuthenticationException, ResponseException {
|
||||
PJLinkDevice device = getDevice();
|
||||
device.checkAvailability();
|
||||
|
||||
updateDeviceProperties(device);
|
||||
updateInputChannelStates(device);
|
||||
}
|
||||
|
||||
private void setupRefreshInterval() throws ConfigurationException {
|
||||
clearRefreshInterval();
|
||||
PJLinkDeviceConfiguration config = PJLinkDeviceHandler.this.getConfiguration();
|
||||
boolean atLeastOneChannelToBeRefreshed = config.refreshPower || config.refreshMute || config.refreshInputChannel
|
||||
|| config.refreshLampState;
|
||||
if (config.refreshInterval > 0 && atLeastOneChannelToBeRefreshed) {
|
||||
refreshJob = scheduler.scheduleWithFixedDelay(() -> refresh(config), 0, config.refreshInterval,
|
||||
TimeUnit.SECONDS);
|
||||
}
|
||||
}
|
||||
|
||||
private void updateDeviceProperties(PJLinkDevice device) throws IOException, AuthenticationException {
|
||||
Map<String, String> properties = editProperties();
|
||||
|
||||
properties.put(PJLinkDeviceBindingConstants.PROPERTY_AUTHENTICATION_REQUIRED,
|
||||
device.getAuthenticationRequired().toString());
|
||||
|
||||
try {
|
||||
properties.put(PJLinkDeviceBindingConstants.PROPERTY_NAME, device.getName());
|
||||
} catch (ResponseException e) {
|
||||
logger.debug("Error retrieving property {}", PJLinkDeviceBindingConstants.PROPERTY_NAME, e);
|
||||
}
|
||||
try {
|
||||
properties.put(Thing.PROPERTY_VENDOR, device.getManufacturer());
|
||||
} catch (ResponseException e) {
|
||||
logger.debug("Error retrieving property {}", Thing.PROPERTY_VENDOR, e);
|
||||
}
|
||||
try {
|
||||
properties.put(Thing.PROPERTY_MODEL_ID, device.getModel());
|
||||
} catch (ResponseException e) {
|
||||
logger.debug("Error retrieving property {}", Thing.PROPERTY_MODEL_ID, e);
|
||||
}
|
||||
try {
|
||||
properties.put(PJLinkDeviceBindingConstants.PROPERTY_CLASS, device.getPJLinkClass());
|
||||
} catch (ResponseException e) {
|
||||
logger.debug("Error retrieving property {}", PJLinkDeviceBindingConstants.PROPERTY_CLASS, e);
|
||||
}
|
||||
try {
|
||||
device.getErrorStatus().forEach((k, v) -> properties
|
||||
.put(PJLinkDeviceBindingConstants.PROPERTY_ERROR_STATUS + k.getCamelCaseText(), v.getText()));
|
||||
} catch (ResponseException e) {
|
||||
logger.debug("Error retrieving property {}", PJLinkDeviceBindingConstants.PROPERTY_ERROR_STATUS, e);
|
||||
}
|
||||
try {
|
||||
properties.put(PJLinkDeviceBindingConstants.PROPERTY_OTHER_INFORMATION, device.getOtherInformation());
|
||||
} catch (ResponseException e) {
|
||||
logger.debug("Error retrieving property {}", PJLinkDeviceBindingConstants.PROPERTY_OTHER_INFORMATION, e);
|
||||
}
|
||||
|
||||
updateProperties(properties);
|
||||
}
|
||||
|
||||
private void updateInputChannelStates(PJLinkDevice device)
|
||||
throws ResponseException, IOException, AuthenticationException {
|
||||
Set<Input> inputs = device.getAvailableInputs();
|
||||
List<StateOption> states = new LinkedList<>();
|
||||
for (Input input : inputs) {
|
||||
states.add(new StateOption(input.getPJLinkRepresentation(), input.getText()));
|
||||
}
|
||||
|
||||
ChannelUID channelUid = new ChannelUID(getThing().getUID(), PJLinkDeviceBindingConstants.CHANNEL_INPUT);
|
||||
this.stateDescriptionProvider.setStateOptions(channelUid, states);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 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.pjlinkdevice.internal;
|
||||
|
||||
import static org.openhab.binding.pjlinkdevice.internal.PJLinkDeviceBindingConstants.THING_TYPE_PJLINK;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Set;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.core.thing.Thing;
|
||||
import org.openhab.core.thing.ThingTypeUID;
|
||||
import org.openhab.core.thing.binding.BaseThingHandlerFactory;
|
||||
import org.openhab.core.thing.binding.ThingHandler;
|
||||
import org.openhab.core.thing.binding.ThingHandlerFactory;
|
||||
import org.osgi.service.component.annotations.Activate;
|
||||
import org.osgi.service.component.annotations.Component;
|
||||
import org.osgi.service.component.annotations.Reference;
|
||||
|
||||
/**
|
||||
* The {@link PJLinkDeviceHandlerFactory} is responsible for creating things and thing
|
||||
* handlers.
|
||||
*
|
||||
* @author Nils Schnabel - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
@Component(configurationPid = "binding.pjlinkdevice", service = { ThingHandlerFactory.class })
|
||||
public class PJLinkDeviceHandlerFactory extends BaseThingHandlerFactory {
|
||||
private InputChannelStateDescriptionProvider stateDescriptionProvider;
|
||||
private static final Set<ThingTypeUID> SUPPORTED_THING_TYPES_UIDS = Collections.singleton(THING_TYPE_PJLINK);
|
||||
|
||||
@Activate
|
||||
public PJLinkDeviceHandlerFactory(@Reference InputChannelStateDescriptionProvider provider) {
|
||||
this.stateDescriptionProvider = provider;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsThingType(ThingTypeUID thingTypeUID) {
|
||||
return SUPPORTED_THING_TYPES_UIDS.contains(thingTypeUID);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected @Nullable ThingHandler createHandler(Thing thing) {
|
||||
ThingTypeUID thingTypeUID = thing.getThingTypeUID();
|
||||
if (THING_TYPE_PJLINK.equals(thingTypeUID)) {
|
||||
return new PJLinkDeviceHandler(thing, this.stateDescriptionProvider);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,369 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 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.pjlinkdevice.internal.device;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.net.ConnectException;
|
||||
import java.net.InetAddress;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.NoRouteToHostException;
|
||||
import java.net.Socket;
|
||||
import java.net.SocketAddress;
|
||||
import java.net.SocketTimeoutException;
|
||||
import java.text.MessageFormat;
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.binding.pjlinkdevice.internal.device.command.AuthenticationException;
|
||||
import org.openhab.binding.pjlinkdevice.internal.device.command.CachedCommand;
|
||||
import org.openhab.binding.pjlinkdevice.internal.device.command.ResponseException;
|
||||
import org.openhab.binding.pjlinkdevice.internal.device.command.authentication.AuthenticationCommand;
|
||||
import org.openhab.binding.pjlinkdevice.internal.device.command.errorstatus.ErrorStatusQueryCommand;
|
||||
import org.openhab.binding.pjlinkdevice.internal.device.command.errorstatus.ErrorStatusQueryResponse.ErrorStatusDevicePart;
|
||||
import org.openhab.binding.pjlinkdevice.internal.device.command.errorstatus.ErrorStatusQueryResponse.ErrorStatusQueryResponseState;
|
||||
import org.openhab.binding.pjlinkdevice.internal.device.command.identification.IdentificationCommand;
|
||||
import org.openhab.binding.pjlinkdevice.internal.device.command.input.Input;
|
||||
import org.openhab.binding.pjlinkdevice.internal.device.command.input.InputInstructionCommand;
|
||||
import org.openhab.binding.pjlinkdevice.internal.device.command.input.InputListQueryCommand;
|
||||
import org.openhab.binding.pjlinkdevice.internal.device.command.input.InputQueryCommand;
|
||||
import org.openhab.binding.pjlinkdevice.internal.device.command.input.InputQueryResponse;
|
||||
import org.openhab.binding.pjlinkdevice.internal.device.command.lampstatus.LampStatesCommand;
|
||||
import org.openhab.binding.pjlinkdevice.internal.device.command.lampstatus.LampStatesResponse;
|
||||
import org.openhab.binding.pjlinkdevice.internal.device.command.lampstatus.LampStatesResponse.LampState;
|
||||
import org.openhab.binding.pjlinkdevice.internal.device.command.mute.MuteInstructionCommand;
|
||||
import org.openhab.binding.pjlinkdevice.internal.device.command.mute.MuteInstructionCommand.MuteInstructionChannel;
|
||||
import org.openhab.binding.pjlinkdevice.internal.device.command.mute.MuteInstructionCommand.MuteInstructionState;
|
||||
import org.openhab.binding.pjlinkdevice.internal.device.command.mute.MuteQueryCommand;
|
||||
import org.openhab.binding.pjlinkdevice.internal.device.command.mute.MuteQueryResponse.MuteQueryResponseValue;
|
||||
import org.openhab.binding.pjlinkdevice.internal.device.command.power.PowerInstructionCommand;
|
||||
import org.openhab.binding.pjlinkdevice.internal.device.command.power.PowerQueryCommand;
|
||||
import org.openhab.binding.pjlinkdevice.internal.device.command.power.PowerQueryResponse;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* Represents a PJLink device and takes care of managing the TCP connection, executing commands, and authentication.
|
||||
*
|
||||
* The central interface to get information about and set status on the device.
|
||||
*
|
||||
* @author Nils Schnabel - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class PJLinkDevice {
|
||||
private static final int TIMEOUT = 30000;
|
||||
protected int tcpPort;
|
||||
protected InetAddress ipAddress;
|
||||
protected @Nullable String adminPassword;
|
||||
protected boolean authenticationRequired;
|
||||
protected @Nullable BufferedReader reader;
|
||||
protected @Nullable Socket socket;
|
||||
protected int timeout = TIMEOUT;
|
||||
private final Logger logger = LoggerFactory.getLogger(PJLinkDevice.class);
|
||||
private String prefixForNextCommand = "";
|
||||
private @Nullable Instant socketCreatedOn;
|
||||
private CachedCommand<LampStatesResponse> cachedLampHoursCommand = new CachedCommand<>(new LampStatesCommand(this));
|
||||
|
||||
public PJLinkDevice(int tcpPort, InetAddress ipAddress, @Nullable String adminPassword, int timeout) {
|
||||
this.tcpPort = tcpPort;
|
||||
this.ipAddress = ipAddress;
|
||||
this.adminPassword = adminPassword;
|
||||
this.timeout = timeout;
|
||||
}
|
||||
|
||||
public PJLinkDevice(int tcpPort, InetAddress ipAddress, @Nullable String adminPassword) {
|
||||
this(tcpPort, ipAddress, adminPassword, TIMEOUT);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "PJLink " + this.ipAddress + ":" + this.tcpPort;
|
||||
}
|
||||
|
||||
protected Socket connect() throws IOException, ResponseException, AuthenticationException {
|
||||
return connect(false);
|
||||
}
|
||||
|
||||
protected BufferedReader getReader() throws IOException, ResponseException, AuthenticationException {
|
||||
BufferedReader reader = this.reader;
|
||||
if (reader == null) {
|
||||
this.reader = reader = new BufferedReader(new InputStreamReader(connect().getInputStream()));
|
||||
}
|
||||
return reader;
|
||||
}
|
||||
|
||||
protected void closeSocket(@Nullable Socket socket) {
|
||||
if (socket != null) {
|
||||
try {
|
||||
socket.close();
|
||||
} catch (IOException e) {
|
||||
// okay then, at least we tried
|
||||
logger.trace("closing of socket failed", e);
|
||||
}
|
||||
}
|
||||
this.socket = null;
|
||||
this.reader = null;
|
||||
}
|
||||
|
||||
protected Socket connect(boolean forceReconnect) throws IOException, ResponseException, AuthenticationException {
|
||||
Instant now = Instant.now();
|
||||
Socket socket = this.socket;
|
||||
boolean connectionTooOld = false;
|
||||
if (this.socketCreatedOn != null) {
|
||||
long millisecondsSinceLastConnect = Duration.between(this.socketCreatedOn, now).toMillis();
|
||||
// according to the PJLink specification, the device closes the connection after 30s idle (without notice),
|
||||
// so to be on the safe side we do not reuse sockets older than 20s
|
||||
connectionTooOld = millisecondsSinceLastConnect > 20 * 1000;
|
||||
}
|
||||
|
||||
if (forceReconnect || connectionTooOld) {
|
||||
if (socket != null) {
|
||||
closeSocket(socket);
|
||||
}
|
||||
}
|
||||
|
||||
this.socketCreatedOn = now;
|
||||
if (socket != null && socket.isConnected() && !socket.isClosed()) {
|
||||
return socket;
|
||||
}
|
||||
|
||||
SocketAddress socketAddress = new InetSocketAddress(ipAddress, tcpPort);
|
||||
|
||||
try {
|
||||
this.socket = socket = new Socket();
|
||||
socket.connect(socketAddress, timeout);
|
||||
socket.setSoTimeout(timeout);
|
||||
BufferedReader reader = getReader();
|
||||
String header = reader.readLine();
|
||||
if (header == null) {
|
||||
throw new ResponseException("No PJLink header received from the device");
|
||||
}
|
||||
header = header.toUpperCase();
|
||||
switch (header.substring(0, "PJLINK x".length())) {
|
||||
case "PJLINK 0":
|
||||
logger.debug("Authentication not needed");
|
||||
this.authenticationRequired = false;
|
||||
break;
|
||||
case "PJLINK 1":
|
||||
logger.debug("Authentication needed");
|
||||
this.authenticationRequired = true;
|
||||
if (this.adminPassword == null) {
|
||||
closeSocket(socket);
|
||||
throw new AuthenticationException("No password provided, but device requires authentication");
|
||||
} else {
|
||||
try {
|
||||
authenticate(header.substring("PJLINK 1 ".length()));
|
||||
} catch (AuthenticationException e) {
|
||||
// propagate AuthenticationException
|
||||
throw e;
|
||||
} catch (ResponseException e) {
|
||||
// maybe only the test command is broken on the device
|
||||
// as long as we don't get an AuthenticationException, we'll just ignore it for now
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
logger.debug("Cannot handle introduction response {}", header);
|
||||
throw new ResponseException("Invalid header: " + header);
|
||||
}
|
||||
return socket;
|
||||
} catch (ConnectException | SocketTimeoutException | NoRouteToHostException e) {
|
||||
// these exceptions indicate that there's no device at this address, just throw without logging
|
||||
throw e;
|
||||
} catch (IOException | ResponseException e) {
|
||||
// these exceptions seem to be more interesting in the log during a scan
|
||||
// This should not happen and might be a user configuration issue, we log a warning message therefore.
|
||||
logger.debug("Could not create a socket connection", e);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
private void authenticate(String challenge) throws ResponseException, IOException, AuthenticationException {
|
||||
new AuthenticationCommand<>(this, challenge, new PowerQueryCommand(this)).execute();
|
||||
}
|
||||
|
||||
public PowerQueryResponse getPowerStatus() throws ResponseException, IOException, AuthenticationException {
|
||||
return new PowerQueryCommand(this).execute();
|
||||
}
|
||||
|
||||
public void addPrefixToNextCommand(String cmd) throws IOException, AuthenticationException {
|
||||
this.prefixForNextCommand = cmd;
|
||||
}
|
||||
|
||||
public static String preprocessResponse(String response) {
|
||||
// some devices send leading zero bytes, see https://github.com/openhab/openhab-addons/issues/6725
|
||||
return response.replaceAll("^\0*|\0*$", "");
|
||||
}
|
||||
|
||||
public synchronized String execute(String command) throws IOException, AuthenticationException, ResponseException {
|
||||
String fullCommand = this.prefixForNextCommand + command;
|
||||
this.prefixForNextCommand = "";
|
||||
for (int numberOfTries = 0; true; numberOfTries++) {
|
||||
try {
|
||||
Socket socket = connect();
|
||||
socket.getOutputStream().write((fullCommand).getBytes());
|
||||
socket.getOutputStream().flush();
|
||||
|
||||
// success, no further tries needed
|
||||
break;
|
||||
} catch (java.net.SocketException e) {
|
||||
closeSocket(socket);
|
||||
if (numberOfTries >= 2) {
|
||||
// do not retry endlessly
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
String response = null;
|
||||
while ((response = getReader().readLine()) != null && preprocessResponse(response).isEmpty()) {
|
||||
logger.debug("Got empty string response for request '{}' from {}, waiting for another line", response,
|
||||
fullCommand.replaceAll("\r", "\\\\r"));
|
||||
}
|
||||
if (response == null) {
|
||||
throw new ResponseException(MessageFormat.format("Response to request ''{0}'' was null",
|
||||
fullCommand.replaceAll("\r", "\\\\r")));
|
||||
}
|
||||
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Got response '{}' ({}) for request '{}' from {}", response,
|
||||
Arrays.toString(response.getBytes()), fullCommand.replaceAll("\r", "\\\\r"), ipAddress);
|
||||
}
|
||||
return preprocessResponse(response);
|
||||
}
|
||||
|
||||
public void checkAvailability() throws IOException, AuthenticationException, ResponseException {
|
||||
connect();
|
||||
}
|
||||
|
||||
public String getName() throws IOException, ResponseException, AuthenticationException {
|
||||
return new IdentificationCommand(this, IdentificationCommand.IdentificationProperty.NAME).execute().getResult();
|
||||
}
|
||||
|
||||
public String getManufacturer() throws IOException, ResponseException, AuthenticationException {
|
||||
return new IdentificationCommand(this, IdentificationCommand.IdentificationProperty.MANUFACTURER).execute()
|
||||
.getResult();
|
||||
}
|
||||
|
||||
public String getModel() throws IOException, ResponseException, AuthenticationException {
|
||||
return new IdentificationCommand(this, IdentificationCommand.IdentificationProperty.MODEL).execute()
|
||||
.getResult();
|
||||
}
|
||||
|
||||
public String getFullDescription() throws AuthenticationException, ResponseException {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
try {
|
||||
sb.append(getManufacturer());
|
||||
sb.append(" ");
|
||||
} catch (ResponseException | IOException e) {
|
||||
// okay, we'll try the other identification commands
|
||||
}
|
||||
|
||||
try {
|
||||
sb.append(getModel());
|
||||
} catch (ResponseException | IOException e1) {
|
||||
// okay, we'll try the other identification commands
|
||||
}
|
||||
|
||||
try {
|
||||
String name = getName();
|
||||
if (!name.isEmpty()) {
|
||||
sb.append(": ");
|
||||
sb.append(name);
|
||||
}
|
||||
} catch (ResponseException | IOException e2) {
|
||||
// okay, we'll try the other identification commands
|
||||
}
|
||||
|
||||
if (sb.length() == 0) {
|
||||
throw new ResponseException("None of the identification commands worked");
|
||||
}
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
public String getPJLinkClass() throws IOException, AuthenticationException, ResponseException {
|
||||
return new IdentificationCommand(this, IdentificationCommand.IdentificationProperty.CLASS).execute()
|
||||
.getResult();
|
||||
}
|
||||
|
||||
public void powerOn() throws ResponseException, IOException, AuthenticationException {
|
||||
new PowerInstructionCommand(this, PowerInstructionCommand.PowerInstructionState.ON).execute();
|
||||
}
|
||||
|
||||
public void powerOff() throws IOException, ResponseException, AuthenticationException {
|
||||
new PowerInstructionCommand(this, PowerInstructionCommand.PowerInstructionState.OFF).execute();
|
||||
}
|
||||
|
||||
public @Nullable String getAdminPassword() {
|
||||
return this.adminPassword;
|
||||
}
|
||||
|
||||
public Boolean getAuthenticationRequired() {
|
||||
return this.authenticationRequired;
|
||||
}
|
||||
|
||||
public InputQueryResponse getInputStatus() throws ResponseException, IOException, AuthenticationException {
|
||||
return new InputQueryCommand(this).execute();
|
||||
}
|
||||
|
||||
public void setInput(Input input) throws ResponseException, IOException, AuthenticationException {
|
||||
new InputInstructionCommand(this, input).execute();
|
||||
}
|
||||
|
||||
public MuteQueryResponseValue getMuteStatus() throws ResponseException, IOException, AuthenticationException {
|
||||
return new MuteQueryCommand(this).execute().getResult();
|
||||
}
|
||||
|
||||
public void setMute(MuteInstructionChannel channel, boolean muteOn)
|
||||
throws ResponseException, IOException, AuthenticationException {
|
||||
new MuteInstructionCommand(this, muteOn ? MuteInstructionState.ON : MuteInstructionState.OFF, channel)
|
||||
.execute();
|
||||
}
|
||||
|
||||
public Map<ErrorStatusDevicePart, ErrorStatusQueryResponseState> getErrorStatus()
|
||||
throws ResponseException, IOException, AuthenticationException {
|
||||
return new ErrorStatusQueryCommand(this).execute().getResult();
|
||||
}
|
||||
|
||||
public List<LampState> getLampStates() throws ResponseException, IOException, AuthenticationException {
|
||||
return new LampStatesCommand(this).execute().getResult();
|
||||
}
|
||||
|
||||
public List<LampState> getLampStatesCached() throws ResponseException, IOException, AuthenticationException {
|
||||
return cachedLampHoursCommand.execute().getResult();
|
||||
}
|
||||
|
||||
public String getOtherInformation() throws ResponseException, IOException, AuthenticationException {
|
||||
return new IdentificationCommand(this, IdentificationCommand.IdentificationProperty.OTHER_INFORMATION).execute()
|
||||
.getResult();
|
||||
}
|
||||
|
||||
public Set<Input> getAvailableInputs() throws ResponseException, IOException, AuthenticationException {
|
||||
return new InputListQueryCommand(this).execute().getResult();
|
||||
}
|
||||
|
||||
public void dispose() {
|
||||
final Socket socket = this.socket;
|
||||
if (socket != null) {
|
||||
closeSocket(socket);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 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.pjlinkdevice.internal.device.command;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.binding.pjlinkdevice.internal.device.PJLinkDevice;
|
||||
|
||||
/**
|
||||
* Common base class for most PJLink commands.
|
||||
*
|
||||
* Takes care of generating the request string, sending it to the device, authentication error checking and response
|
||||
* parsing.
|
||||
*
|
||||
* @author Nils Schnabel - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public abstract class AbstractCommand<RequestType extends Request, ResponseType extends Response<?>>
|
||||
implements Command<ResponseType> {
|
||||
private PJLinkDevice pjLinkDevice;
|
||||
|
||||
public AbstractCommand(PJLinkDevice pjLinkDevice) {
|
||||
this.pjLinkDevice = pjLinkDevice;
|
||||
}
|
||||
|
||||
public PJLinkDevice getDevice() {
|
||||
return this.pjLinkDevice;
|
||||
}
|
||||
|
||||
protected abstract RequestType createRequest();
|
||||
|
||||
protected abstract ResponseType parseResponse(String response) throws ResponseException;
|
||||
|
||||
@Override
|
||||
public ResponseType execute() throws ResponseException, IOException, AuthenticationException {
|
||||
RequestType request = createRequest();
|
||||
String responseString = this.pjLinkDevice.execute(request.getRequestString() + "\r");
|
||||
if ("PJLINK ERRA".equalsIgnoreCase(responseString)) {
|
||||
throw new AuthenticationException("Authentication error, wrong password provided?");
|
||||
}
|
||||
return parseResponse(responseString);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 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.pjlinkdevice.internal.device.command;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
|
||||
/**
|
||||
* The possible outcomes of a command which can only be acknowledged or fail.
|
||||
*
|
||||
* @author Nils Schnabel - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public enum AcknowledgeResponseValue {
|
||||
OK("Success", "OK");
|
||||
|
||||
private String text;
|
||||
private String code;
|
||||
|
||||
private AcknowledgeResponseValue(String text, String code) {
|
||||
this.text = text;
|
||||
this.code = code;
|
||||
}
|
||||
|
||||
public String getText() {
|
||||
return this.text;
|
||||
}
|
||||
|
||||
public static AcknowledgeResponseValue getValueForCode(String code) throws ResponseException {
|
||||
for (AcknowledgeResponseValue result : AcknowledgeResponseValue.values()) {
|
||||
if (result.code.equalsIgnoreCase(code)) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
throw new ResponseException("Cannot understand acknowledgement status: " + code);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 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.pjlinkdevice.internal.device.command;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
|
||||
/**
|
||||
* Exception thrown whenever authentication with the device fails.
|
||||
*
|
||||
* @author Nils Schnabel - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class AuthenticationException extends Exception {
|
||||
private static final long serialVersionUID = -3319800607314286998L;
|
||||
|
||||
public AuthenticationException(String string) {
|
||||
super(string);
|
||||
}
|
||||
|
||||
public AuthenticationException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 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.pjlinkdevice.internal.device.command;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
|
||||
/**
|
||||
* Exception thrown whenever an error code or unexpected response is retrieved from the device.
|
||||
*
|
||||
* @author Nils Schnabel - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class CacheException extends RuntimeException {
|
||||
private static final long serialVersionUID = -3319800607314286998L;
|
||||
|
||||
public CacheException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 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.pjlinkdevice.internal.device.command;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.core.cache.ExpiringCache;
|
||||
|
||||
/**
|
||||
* CachedCommand wraps any command and caches its response for a configurable period of time.
|
||||
*
|
||||
* @author Nils Schnabel - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class CachedCommand<ResponseType extends Response<?>> implements Command<ResponseType> {
|
||||
|
||||
private Command<ResponseType> cachedCommand;
|
||||
private ExpiringCache<ResponseType> cache;
|
||||
|
||||
public CachedCommand(Command<ResponseType> cachedCommand) {
|
||||
this(cachedCommand, 1000);
|
||||
}
|
||||
|
||||
public CachedCommand(Command<ResponseType> cachedCommand, int expiry) {
|
||||
this.cachedCommand = cachedCommand;
|
||||
this.cache = new ExpiringCache<>(expiry, () -> {
|
||||
try {
|
||||
return this.cachedCommand.execute();
|
||||
} catch (ResponseException | IOException | AuthenticationException e) {
|
||||
// wrap exception into RuntimeException to unwrap again later in CachedCommand.execute()
|
||||
throw new CacheException(e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResponseType execute() throws ResponseException, IOException, AuthenticationException {
|
||||
ExpiringCache<ResponseType> cache = this.cache;
|
||||
try {
|
||||
@Nullable
|
||||
ResponseType result = cache.getValue();
|
||||
if (result == null) {
|
||||
// this can not happen in reality, limitation of ExpiringCache
|
||||
throw new ResponseException("Cached value is null");
|
||||
}
|
||||
return result;
|
||||
} catch (CacheException e) {
|
||||
// try to unwrap RuntimeException thrown in ExpiringCache
|
||||
Throwable cause = e.getCause();
|
||||
if (cause instanceof ResponseException) {
|
||||
throw (ResponseException) cause;
|
||||
}
|
||||
if (cause instanceof IOException) {
|
||||
throw (IOException) cause;
|
||||
}
|
||||
if (cause instanceof AuthenticationException) {
|
||||
throw (AuthenticationException) cause;
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 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.pjlinkdevice.internal.device.command;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
|
||||
/**
|
||||
* Basic command interface allowing to execute the command.
|
||||
*
|
||||
* @author Nils Schnabel - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public interface Command<ResponseType extends Response<?>> {
|
||||
public ResponseType execute() throws ResponseException, IOException, AuthenticationException;
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 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.pjlinkdevice.internal.device.command;
|
||||
|
||||
import java.text.MessageFormat;
|
||||
import java.util.Set;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* Error codes as specified in <a href="https://pjlink.jbmia.or.jp/english/data_cl2/PJLink_5-1.pdf">[PJLinkSpec]</a>
|
||||
* chapters
|
||||
* 2.3. Set commands
|
||||
* 2.4. Get commands
|
||||
*
|
||||
* @author Nils Schnabel - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public enum ErrorCode {
|
||||
UNDEFINED_COMMAND("Undefined command", "ERR1"),
|
||||
OUT_OF_PARAMETER("Out of parameter", "ERR2"),
|
||||
UNAVAILABLE_TIME("Unavailable time", "ERR3"),
|
||||
DEVICE_FAILURE("Projector/Display failure", "ERR4");
|
||||
|
||||
private String text;
|
||||
private String code;
|
||||
|
||||
private ErrorCode(String text, String code) {
|
||||
this.text = text;
|
||||
this.code = code;
|
||||
}
|
||||
|
||||
public static @Nullable ErrorCode getValueForCode(String code) {
|
||||
for (ErrorCode result : ErrorCode.values()) {
|
||||
if (result.code.equalsIgnoreCase(code)) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public String getText() {
|
||||
return this.text;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a the given code is an error code. Optionally, restrictCodesTo can restrict the allowed error codes
|
||||
* (based on PJLink specification).
|
||||
*
|
||||
* @param code string to be checked for error codes
|
||||
* @param restrictCodesTo list of expected error codes according to PJLink specification, can be null if all error
|
||||
* codes (ERR1-ERR4) can be expected
|
||||
* @throws ResponseException
|
||||
*/
|
||||
public static void checkForErrorStatus(String code, @Nullable Set<ErrorCode> restrictCodesTo)
|
||||
throws ResponseException {
|
||||
ErrorCode parsed = getValueForCode(code);
|
||||
if (parsed != null && (restrictCodesTo == null || restrictCodesTo.contains(parsed))) {
|
||||
throw new ResponseException(MessageFormat.format("Got error status {0} ({1})", parsed.getText(), code));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 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.pjlinkdevice.internal.device.command;
|
||||
|
||||
import java.text.MessageFormat;
|
||||
import java.util.Arrays;
|
||||
import java.util.Set;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* Base class for most responses that can be retrieved from the device.
|
||||
*
|
||||
* A prefix has to be passed in the constructor for which is checked.
|
||||
*
|
||||
* Subclasses have to implement parseResponseWithoutPrefix, which allows parsing without having to remove the prefix
|
||||
* first.
|
||||
*
|
||||
* @author Nils Schnabel - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public abstract class PrefixedResponse<ResponseType> implements Response<ResponseType> {
|
||||
private String prefix;
|
||||
private @Nullable Set<ErrorCode> specifiedErrors;
|
||||
private ResponseType result;
|
||||
|
||||
public PrefixedResponse(String prefix, String response) throws ResponseException {
|
||||
this(prefix, null, response);
|
||||
}
|
||||
|
||||
public PrefixedResponse(String prefix, @Nullable Set<ErrorCode> specifiedErrors, String response)
|
||||
throws ResponseException {
|
||||
this.prefix = prefix;
|
||||
this.specifiedErrors = specifiedErrors;
|
||||
this.result = parse(response);
|
||||
}
|
||||
|
||||
public ResponseType getResult() {
|
||||
return this.result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResponseType parse(String response) throws ResponseException {
|
||||
String fullPrefix = "%1" + this.prefix;
|
||||
if (!response.toUpperCase().startsWith(fullPrefix)) {
|
||||
throw new ResponseException(
|
||||
MessageFormat.format("Expected prefix ''{0}'' ({1}), instead got ''{2}'' ({3})", fullPrefix,
|
||||
Arrays.toString(fullPrefix.getBytes()), response, Arrays.toString(response.getBytes())));
|
||||
}
|
||||
String result = response.substring(fullPrefix.length());
|
||||
ErrorCode.checkForErrorStatus(result, this.specifiedErrors);
|
||||
return parseResponseWithoutPrefix(result);
|
||||
}
|
||||
|
||||
protected abstract ResponseType parseResponseWithoutPrefix(String responseWithoutPrefix) throws ResponseException;
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 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.pjlinkdevice.internal.device.command;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
|
||||
/**
|
||||
* Basic request interface allowing to create the request string.
|
||||
*
|
||||
* @author Nils Schnabel - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public interface Request {
|
||||
public String getRequestString() throws AuthenticationException;
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 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.pjlinkdevice.internal.device.command;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
|
||||
/**
|
||||
* Basic response interface allowing to parse the response string.
|
||||
*
|
||||
* @author Nils Schnabel - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public interface Response<ResponseType> {
|
||||
public ResponseType parse(String response) throws ResponseException;
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 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.pjlinkdevice.internal.device.command;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
|
||||
/**
|
||||
* Exception thrown whenever an error code or unexpected response is retrieved from the device.
|
||||
*
|
||||
* @author Nils Schnabel - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class ResponseException extends Exception {
|
||||
private static final long serialVersionUID = -3319800607314286998L;
|
||||
|
||||
public ResponseException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
|
||||
super(message, cause, enableSuppression, writableStackTrace);
|
||||
}
|
||||
|
||||
public ResponseException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
public ResponseException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public ResponseException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 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.pjlinkdevice.internal.device.command.authentication;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.binding.pjlinkdevice.internal.device.PJLinkDevice;
|
||||
import org.openhab.binding.pjlinkdevice.internal.device.command.AuthenticationException;
|
||||
import org.openhab.binding.pjlinkdevice.internal.device.command.Command;
|
||||
import org.openhab.binding.pjlinkdevice.internal.device.command.Response;
|
||||
import org.openhab.binding.pjlinkdevice.internal.device.command.ResponseException;
|
||||
|
||||
/**
|
||||
* This command is used to authenticate to the device after the connection established.
|
||||
* As authentication can only be done in conjunction with a real command, a testCommand must be passed to authenticate.
|
||||
*
|
||||
* The authentication procedure is described in
|
||||
* <a href="https://pjlink.jbmia.or.jp/english/data_cl2/PJLink_5-1.pdf">[PJLinkSpec]</a> chapter 5.1. Authentication
|
||||
* procedure
|
||||
*
|
||||
* @author Nils Schnabel - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class AuthenticationCommand<ResponseType extends Response<?>> implements Command<ResponseType> {
|
||||
|
||||
private String challenge;
|
||||
private Command<ResponseType> testCommand;
|
||||
private PJLinkDevice device;
|
||||
|
||||
public AuthenticationCommand(PJLinkDevice pjLinkDevice, String challenge, Command<ResponseType> testCommand) {
|
||||
this.device = pjLinkDevice;
|
||||
this.challenge = challenge;
|
||||
this.testCommand = testCommand;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResponseType execute() throws ResponseException, IOException, AuthenticationException {
|
||||
this.device.addPrefixToNextCommand(createRequest().getRequestString());
|
||||
return this.testCommand.execute();
|
||||
}
|
||||
|
||||
protected AuthenticationRequest<ResponseType> createRequest() {
|
||||
return new AuthenticationRequest<>(this);
|
||||
}
|
||||
|
||||
public String getChallenge() {
|
||||
return this.challenge;
|
||||
}
|
||||
|
||||
public PJLinkDevice getDevice() {
|
||||
return this.device;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 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.pjlinkdevice.internal.device.command.authentication;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.binding.pjlinkdevice.internal.device.command.AuthenticationException;
|
||||
import org.openhab.binding.pjlinkdevice.internal.device.command.Request;
|
||||
import org.openhab.binding.pjlinkdevice.internal.device.command.Response;
|
||||
|
||||
/**
|
||||
* Calculates the response matching the callenge received from the PJLink device.
|
||||
*
|
||||
* @author Nils Schnabel - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class AuthenticationRequest<ResponseType extends Response<?>> implements Request {
|
||||
|
||||
private AuthenticationCommand<ResponseType> command;
|
||||
|
||||
public AuthenticationRequest(AuthenticationCommand<ResponseType> command) {
|
||||
this.command = command;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getRequestString() throws AuthenticationException {
|
||||
try {
|
||||
MessageDigest md = MessageDigest.getInstance("MD5");
|
||||
String toBeDigested = (this.command.getChallenge() + this.command.getDevice().getAdminPassword());
|
||||
byte[] digest = md.digest(toBeDigested.getBytes());
|
||||
BigInteger bigInt = new BigInteger(1, digest);
|
||||
return bigInt.toString(16);
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
throw new AuthenticationException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 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.pjlinkdevice.internal.device.command.errorstatus;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.binding.pjlinkdevice.internal.device.PJLinkDevice;
|
||||
import org.openhab.binding.pjlinkdevice.internal.device.command.AbstractCommand;
|
||||
import org.openhab.binding.pjlinkdevice.internal.device.command.ResponseException;
|
||||
|
||||
/**
|
||||
* This command is used for retrieving error information as described in
|
||||
* <a href="https://pjlink.jbmia.or.jp/english/data_cl2/PJLink_5-1.pdf">[PJLinkSpec]</a> chapter 4.7. Error status query
|
||||
*
|
||||
* @author Nils Schnabel - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class ErrorStatusQueryCommand extends AbstractCommand<ErrorStatusQueryRequest, ErrorStatusQueryResponse> {
|
||||
|
||||
public ErrorStatusQueryCommand(PJLinkDevice pjLinkDevice) {
|
||||
super(pjLinkDevice);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ErrorStatusQueryRequest createRequest() {
|
||||
return new ErrorStatusQueryRequest();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ErrorStatusQueryResponse parseResponse(String response) throws ResponseException {
|
||||
return new ErrorStatusQueryResponse(response);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 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.pjlinkdevice.internal.device.command.errorstatus;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.binding.pjlinkdevice.internal.device.command.Request;
|
||||
|
||||
/**
|
||||
* The request part of {@link ErrorStatusQueryCommand}
|
||||
*
|
||||
* @author Nils Schnabel - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class ErrorStatusQueryRequest implements Request {
|
||||
|
||||
@Override
|
||||
public String getRequestString() {
|
||||
return "%1ERST ?";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,116 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 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.pjlinkdevice.internal.device.command.errorstatus;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.binding.pjlinkdevice.internal.device.command.ErrorCode;
|
||||
import org.openhab.binding.pjlinkdevice.internal.device.command.PrefixedResponse;
|
||||
import org.openhab.binding.pjlinkdevice.internal.device.command.ResponseException;
|
||||
|
||||
/**
|
||||
* The response part of {@link ErrorStatusQueryCommand}
|
||||
*
|
||||
* @author Nils Schnabel - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class ErrorStatusQueryResponse extends
|
||||
PrefixedResponse<Map<ErrorStatusQueryResponse.ErrorStatusDevicePart, ErrorStatusQueryResponse.ErrorStatusQueryResponseState>> {
|
||||
|
||||
public enum ErrorStatusQueryResponseState {
|
||||
OK_UNKOWN("OK/no failure detection", "0"),
|
||||
WARNING("Warning", "1"),
|
||||
ERROR("Error", "2");
|
||||
|
||||
private String text;
|
||||
private String code;
|
||||
|
||||
private ErrorStatusQueryResponseState(String text, String code) {
|
||||
this.text = text;
|
||||
this.code = code;
|
||||
}
|
||||
|
||||
public String getText() {
|
||||
return this.text;
|
||||
}
|
||||
|
||||
public static ErrorStatusQueryResponseState parseString(String code) throws ResponseException {
|
||||
for (ErrorStatusQueryResponseState result : ErrorStatusQueryResponseState.values()) {
|
||||
if (result.code.equals(code)) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
throw new ResponseException("Cannot understand error status: " + code);
|
||||
}
|
||||
}
|
||||
|
||||
public enum ErrorStatusDevicePart {
|
||||
FAN("Fan error", "FanError", 0),
|
||||
LAMP("Lamp error", "LampError", 1),
|
||||
TEMPERATURE("Temperature error", "TemperatureError", 2),
|
||||
COVER_OPEN("Cover open error", "CoverOpenError", 3),
|
||||
FILTER("Filter error", "FilterError", 4),
|
||||
OTHER("Other errors", "OtherErrors", 5);
|
||||
|
||||
private String text;
|
||||
private String camelCaseText;
|
||||
private int positionInResponse;
|
||||
|
||||
private ErrorStatusDevicePart(String text, String camelCaseText, int positionInResponse) {
|
||||
this.text = text;
|
||||
this.camelCaseText = camelCaseText;
|
||||
this.positionInResponse = positionInResponse;
|
||||
}
|
||||
|
||||
public String getText() {
|
||||
return this.text;
|
||||
}
|
||||
|
||||
public String getCamelCaseText() {
|
||||
return this.camelCaseText;
|
||||
}
|
||||
|
||||
public static ErrorStatusDevicePart getDevicePartByResponsePosition(int pos) {
|
||||
for (ErrorStatusDevicePart result : ErrorStatusDevicePart.values()) {
|
||||
if (result.positionInResponse == pos) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
return OTHER;
|
||||
}
|
||||
}
|
||||
|
||||
private static final HashSet<ErrorCode> SPECIFIED_ERRORCODES = new HashSet<>(
|
||||
Arrays.asList(ErrorCode.UNAVAILABLE_TIME, ErrorCode.DEVICE_FAILURE));
|
||||
|
||||
public ErrorStatusQueryResponse(String response) throws ResponseException {
|
||||
super("ERST=", SPECIFIED_ERRORCODES, response);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Map<ErrorStatusDevicePart, ErrorStatusQueryResponseState> parseResponseWithoutPrefix(
|
||||
String responseWithoutPrefix) throws ResponseException {
|
||||
Map<ErrorStatusDevicePart, ErrorStatusQueryResponseState> result = new HashMap<>();
|
||||
for (int i = 0; i < ErrorStatusDevicePart.values().length; i++) {
|
||||
result.put(ErrorStatusDevicePart.getDevicePartByResponsePosition(i),
|
||||
ErrorStatusQueryResponseState.parseString(responseWithoutPrefix.substring(i, i + 1)));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 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.pjlinkdevice.internal.device.command.identification;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.binding.pjlinkdevice.internal.device.PJLinkDevice;
|
||||
import org.openhab.binding.pjlinkdevice.internal.device.command.AbstractCommand;
|
||||
import org.openhab.binding.pjlinkdevice.internal.device.command.ResponseException;
|
||||
|
||||
/**
|
||||
* This command is used for retrieving device information as described in
|
||||
* <a href="https://pjlink.jbmia.or.jp/english/data_cl2/PJLink_5-1.pdf">[PJLinkSpec]</a> chapters:
|
||||
* 4.8. Lamp number/ lighting hour query
|
||||
* 4.10. Projector/Display name query
|
||||
* 4.11. Manufacture name information query
|
||||
* 4.12. Product name information query
|
||||
* 4.13. Other information query
|
||||
* 4.14. Class information query
|
||||
*
|
||||
* @author Nils Schnabel - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class IdentificationCommand extends AbstractCommand<IdentificationRequest, IdentificationResponse> {
|
||||
|
||||
public enum IdentificationProperty {
|
||||
NAME("NAME"),
|
||||
MANUFACTURER("INF1"),
|
||||
MODEL("INF2"),
|
||||
CLASS("CLSS"),
|
||||
OTHER_INFORMATION("INFO"),
|
||||
LAMP_HOURS("LAMP");
|
||||
|
||||
private String prefix;
|
||||
|
||||
private IdentificationProperty(String prefix) {
|
||||
this.prefix = prefix;
|
||||
}
|
||||
|
||||
public String getPJLinkCommandPrefix() {
|
||||
return this.prefix;
|
||||
}
|
||||
}
|
||||
|
||||
private IdentificationProperty identificationProperty;
|
||||
|
||||
public IdentificationCommand(PJLinkDevice pjLinkDevice, IdentificationProperty identificationProperty) {
|
||||
super(pjLinkDevice);
|
||||
this.identificationProperty = identificationProperty;
|
||||
}
|
||||
|
||||
public IdentificationProperty getIdentificationProperty() {
|
||||
return identificationProperty;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected IdentificationRequest createRequest() {
|
||||
return new IdentificationRequest(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected IdentificationResponse parseResponse(String response) throws ResponseException {
|
||||
return new IdentificationResponse(this, response);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 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.pjlinkdevice.internal.device.command.identification;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.binding.pjlinkdevice.internal.device.command.Request;
|
||||
|
||||
/**
|
||||
* The request part of {@link IdentificationCommand}
|
||||
*
|
||||
* @author Nils Schnabel - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class IdentificationRequest implements Request {
|
||||
|
||||
private IdentificationCommand command;
|
||||
|
||||
public IdentificationRequest(IdentificationCommand command) {
|
||||
this.command = command;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getRequestString() {
|
||||
return "%1" + this.command.getIdentificationProperty().getPJLinkCommandPrefix() + " ?";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 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.pjlinkdevice.internal.device.command.identification;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.binding.pjlinkdevice.internal.device.command.PrefixedResponse;
|
||||
import org.openhab.binding.pjlinkdevice.internal.device.command.ResponseException;
|
||||
|
||||
/**
|
||||
* The response part of {@link IdentificationCommand}
|
||||
*
|
||||
* @author Nils Schnabel - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class IdentificationResponse extends PrefixedResponse<String> {
|
||||
public IdentificationResponse(IdentificationCommand command, String response) throws ResponseException {
|
||||
super(command.getIdentificationProperty().getPJLinkCommandPrefix() + "=", response);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String parseResponseWithoutPrefix(String responseWithoutPrefix) throws ResponseException {
|
||||
return responseWithoutPrefix;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,127 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 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.pjlinkdevice.internal.device.command.input;
|
||||
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.binding.pjlinkdevice.internal.device.command.ResponseException;
|
||||
|
||||
/**
|
||||
* Describes an A/V source that can be selected on the PJLink device.
|
||||
*
|
||||
* @author Nils Schnabel - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class Input {
|
||||
|
||||
private static final Pattern INPUT_NUMBER_PATTERN = Pattern.compile("[0-9A-Z]");
|
||||
|
||||
enum InputType {
|
||||
RGB("RGB", '1'),
|
||||
VIDEO("Video", '2'),
|
||||
DIGITAL("Digital", '3'),
|
||||
STORAGE("Storage", '4'),
|
||||
NETWORK("Network", '5');
|
||||
|
||||
private String text;
|
||||
private char code;
|
||||
|
||||
private InputType(String text, char code) {
|
||||
this.text = text;
|
||||
this.code = code;
|
||||
}
|
||||
|
||||
public String getText() {
|
||||
return this.text;
|
||||
}
|
||||
|
||||
public static InputType parseString(String value) throws ResponseException {
|
||||
for (InputType result : InputType.values()) {
|
||||
if (result.code == value.charAt(0)) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
throw new ResponseException("Unknown input channel type: " + value);
|
||||
}
|
||||
}
|
||||
|
||||
private String value;
|
||||
|
||||
public Input(String value) throws ResponseException {
|
||||
this.value = value;
|
||||
validate();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
final int prime = 31;
|
||||
int result = 1;
|
||||
result = prime * result + value.hashCode();
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(@Nullable Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (obj == null) {
|
||||
return false;
|
||||
}
|
||||
if (getClass() != obj.getClass()) {
|
||||
return false;
|
||||
}
|
||||
Input other = (Input) obj;
|
||||
if (!value.equals(other.value)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public InputType getInputType() throws ResponseException {
|
||||
return InputType.parseString(this.value);
|
||||
}
|
||||
|
||||
public String getInputNumber() throws ResponseException {
|
||||
String inputNumber = this.value.substring(1, 2);
|
||||
if (!INPUT_NUMBER_PATTERN.matcher(inputNumber).matches()) {
|
||||
throw new ResponseException("Illegal channel number: " + inputNumber);
|
||||
}
|
||||
|
||||
return inputNumber;
|
||||
}
|
||||
|
||||
public void validate() throws ResponseException {
|
||||
if (this.value.length() != 2) {
|
||||
throw new ResponseException("Illegal input description: " + value);
|
||||
}
|
||||
// these method also validate
|
||||
getInputType();
|
||||
getInputNumber();
|
||||
}
|
||||
|
||||
public String getValue() {
|
||||
return this.value;
|
||||
}
|
||||
|
||||
public String getPJLinkRepresentation() {
|
||||
return this.value;
|
||||
}
|
||||
|
||||
public String getText() throws ResponseException {
|
||||
return getInputType().getText() + " " + getInputNumber();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 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.pjlinkdevice.internal.device.command.input;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.binding.pjlinkdevice.internal.device.PJLinkDevice;
|
||||
import org.openhab.binding.pjlinkdevice.internal.device.command.AbstractCommand;
|
||||
import org.openhab.binding.pjlinkdevice.internal.device.command.ResponseException;
|
||||
|
||||
/**
|
||||
* This command is used for setting the current input of the device as described in
|
||||
* <a href="https://pjlink.jbmia.or.jp/english/data_cl2/PJLink_5-1.pdf">[PJLinkSpec]</a> chapter 4.3. Input switch
|
||||
* instruction
|
||||
*
|
||||
* @author Nils Schnabel - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class InputInstructionCommand extends AbstractCommand<InputInstructionRequest, InputInstructionResponse> {
|
||||
|
||||
private Input target;
|
||||
|
||||
public InputInstructionCommand(PJLinkDevice pjLinkDevice, Input target) {
|
||||
super(pjLinkDevice);
|
||||
this.target = target;
|
||||
}
|
||||
|
||||
public Input getTarget() {
|
||||
return target;
|
||||
}
|
||||
|
||||
@Override
|
||||
public InputInstructionRequest createRequest() {
|
||||
return new InputInstructionRequest(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public InputInstructionResponse parseResponse(String response) throws ResponseException {
|
||||
return new InputInstructionResponse(response);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 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.pjlinkdevice.internal.device.command.input;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.binding.pjlinkdevice.internal.device.command.Request;
|
||||
|
||||
/**
|
||||
* The request part of {@link InputInstructionCommand}
|
||||
*
|
||||
* @author Nils Schnabel - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class InputInstructionRequest implements Request {
|
||||
|
||||
private InputInstructionCommand command;
|
||||
|
||||
public InputInstructionRequest(InputInstructionCommand command) {
|
||||
this.command = command;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getRequestString() {
|
||||
return "%1INPT " + this.command.getTarget().getPJLinkRepresentation();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 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.pjlinkdevice.internal.device.command.input;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.binding.pjlinkdevice.internal.device.command.AcknowledgeResponseValue;
|
||||
import org.openhab.binding.pjlinkdevice.internal.device.command.ErrorCode;
|
||||
import org.openhab.binding.pjlinkdevice.internal.device.command.PrefixedResponse;
|
||||
import org.openhab.binding.pjlinkdevice.internal.device.command.ResponseException;
|
||||
|
||||
/**
|
||||
* The response part of {@link InputInstructionCommand}
|
||||
*
|
||||
* @author Nils Schnabel - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class InputInstructionResponse extends PrefixedResponse<AcknowledgeResponseValue> {
|
||||
private static final HashSet<ErrorCode> SPECIFIED_ERRORCODES = new HashSet<>(
|
||||
Arrays.asList(ErrorCode.OUT_OF_PARAMETER, ErrorCode.UNAVAILABLE_TIME, ErrorCode.DEVICE_FAILURE));
|
||||
|
||||
public InputInstructionResponse(String response) throws ResponseException {
|
||||
super("INPT=", SPECIFIED_ERRORCODES, response);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected AcknowledgeResponseValue parseResponseWithoutPrefix(String responseWithoutPrefix)
|
||||
throws ResponseException {
|
||||
return AcknowledgeResponseValue.getValueForCode(responseWithoutPrefix);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 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.pjlinkdevice.internal.device.command.input;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.binding.pjlinkdevice.internal.device.PJLinkDevice;
|
||||
import org.openhab.binding.pjlinkdevice.internal.device.command.AbstractCommand;
|
||||
import org.openhab.binding.pjlinkdevice.internal.device.command.ResponseException;
|
||||
|
||||
/**
|
||||
* This command is used for retrieving the list of available inputs of the device as described in
|
||||
* <a href="https://pjlink.jbmia.or.jp/english/data_cl2/PJLink_5-1.pdf">[PJLinkSpec]</a> chapter 4.9. Input toggling
|
||||
* list query
|
||||
*
|
||||
* @author Nils Schnabel - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class InputListQueryCommand extends AbstractCommand<InputListQueryRequest, InputListQueryResponse> {
|
||||
|
||||
public InputListQueryCommand(PJLinkDevice pjLinkDevice) {
|
||||
super(pjLinkDevice);
|
||||
}
|
||||
|
||||
@Override
|
||||
public InputListQueryRequest createRequest() {
|
||||
return new InputListQueryRequest();
|
||||
}
|
||||
|
||||
@Override
|
||||
public InputListQueryResponse parseResponse(String response) throws ResponseException {
|
||||
return new InputListQueryResponse(response);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 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.pjlinkdevice.internal.device.command.input;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.binding.pjlinkdevice.internal.device.command.Request;
|
||||
|
||||
/**
|
||||
* The request part of {@link InputListQueryCommand}
|
||||
*
|
||||
* @author Nils Schnabel - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class InputListQueryRequest implements Request {
|
||||
|
||||
@Override
|
||||
public String getRequestString() {
|
||||
return "%1INST ?";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 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.pjlinkdevice.internal.device.command.input;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.binding.pjlinkdevice.internal.device.command.ErrorCode;
|
||||
import org.openhab.binding.pjlinkdevice.internal.device.command.PrefixedResponse;
|
||||
import org.openhab.binding.pjlinkdevice.internal.device.command.ResponseException;
|
||||
|
||||
/**
|
||||
* The response part of {@link InputListQueryCommand}
|
||||
*
|
||||
* @author Nils Schnabel - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class InputListQueryResponse extends PrefixedResponse<Set<Input>> {
|
||||
private static final HashSet<ErrorCode> SPECIFIED_ERRORCODES = new HashSet<>(
|
||||
Arrays.asList(ErrorCode.UNAVAILABLE_TIME, ErrorCode.DEVICE_FAILURE));
|
||||
|
||||
public InputListQueryResponse(String response) throws ResponseException {
|
||||
super("INST=", SPECIFIED_ERRORCODES, response);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Set<Input> parseResponseWithoutPrefix(String responseWithoutPrefix) throws ResponseException {
|
||||
Set<Input> result = new HashSet<>();
|
||||
int pos = 0;
|
||||
while (pos < responseWithoutPrefix.length()) {
|
||||
result.add(new Input(responseWithoutPrefix.substring(pos, pos + 2)));
|
||||
pos += 3;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 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.pjlinkdevice.internal.device.command.input;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.binding.pjlinkdevice.internal.device.PJLinkDevice;
|
||||
import org.openhab.binding.pjlinkdevice.internal.device.command.AbstractCommand;
|
||||
import org.openhab.binding.pjlinkdevice.internal.device.command.ResponseException;
|
||||
|
||||
/**
|
||||
* This command is used for retrieving the currently selected input of the device as described in
|
||||
* <a href="https://pjlink.jbmia.or.jp/english/data_cl2/PJLink_5-1.pdf">[PJLinkSpec]</a> chapter 4.4. Input switch query
|
||||
*
|
||||
* @author Nils Schnabel - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class InputQueryCommand extends AbstractCommand<InputQueryRequest, InputQueryResponse> {
|
||||
|
||||
public InputQueryCommand(PJLinkDevice pjLinkDevice) {
|
||||
super(pjLinkDevice);
|
||||
}
|
||||
|
||||
@Override
|
||||
public InputQueryRequest createRequest() {
|
||||
return new InputQueryRequest();
|
||||
}
|
||||
|
||||
@Override
|
||||
public InputQueryResponse parseResponse(String response) throws ResponseException {
|
||||
return new InputQueryResponse(response);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 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.pjlinkdevice.internal.device.command.input;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.binding.pjlinkdevice.internal.device.command.Request;
|
||||
|
||||
/**
|
||||
* The request part of {@link InputQueryCommand}
|
||||
*
|
||||
* @author Nils Schnabel - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class InputQueryRequest implements Request {
|
||||
|
||||
@Override
|
||||
public String getRequestString() {
|
||||
return "%1INPT ?";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 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.pjlinkdevice.internal.device.command.input;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.binding.pjlinkdevice.internal.device.command.PrefixedResponse;
|
||||
import org.openhab.binding.pjlinkdevice.internal.device.command.ResponseException;
|
||||
|
||||
/**
|
||||
* The response part of {@link InputQueryCommand}
|
||||
*
|
||||
* @author Nils Schnabel - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class InputQueryResponse extends PrefixedResponse<Input> {
|
||||
public InputQueryResponse(String response) throws ResponseException {
|
||||
super("INPT=", response);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Input parseResponseWithoutPrefix(String responseWithoutPrefix) throws ResponseException {
|
||||
return new Input(responseWithoutPrefix);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 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.pjlinkdevice.internal.device.command.lampstatus;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.binding.pjlinkdevice.internal.device.PJLinkDevice;
|
||||
import org.openhab.binding.pjlinkdevice.internal.device.command.AbstractCommand;
|
||||
import org.openhab.binding.pjlinkdevice.internal.device.command.ResponseException;
|
||||
|
||||
/**
|
||||
* This command is used for retrieving device information as described in
|
||||
* <a href="https://pjlink.jbmia.or.jp/english/data_cl2/PJLink_5-1.pdf">[PJLinkSpec]</a> chapters:
|
||||
* 4.8. Lamp number/ lighting hour query
|
||||
*
|
||||
* @author Nils Schnabel - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class LampStatesCommand extends AbstractCommand<LampStatesRequest, LampStatesResponse> {
|
||||
public LampStatesCommand(PJLinkDevice pjLinkDevice) {
|
||||
super(pjLinkDevice);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected LampStatesRequest createRequest() {
|
||||
return new LampStatesRequest();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected LampStatesResponse parseResponse(String response) throws ResponseException {
|
||||
return new LampStatesResponse(response);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 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.pjlinkdevice.internal.device.command.lampstatus;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.binding.pjlinkdevice.internal.device.command.Request;
|
||||
|
||||
/**
|
||||
* The request part of {@link LampStatesCommand}
|
||||
*
|
||||
* @author Nils Schnabel - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class LampStatesRequest implements Request {
|
||||
@Override
|
||||
public String getRequestString() {
|
||||
return "%1LAMP ?";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 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.pjlinkdevice.internal.device.command.lampstatus;
|
||||
|
||||
import java.text.MessageFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.binding.pjlinkdevice.internal.device.command.PrefixedResponse;
|
||||
import org.openhab.binding.pjlinkdevice.internal.device.command.ResponseException;
|
||||
|
||||
/**
|
||||
* The response part of {@link LampStatesCommand}
|
||||
*
|
||||
* @author Nils Schnabel - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class LampStatesResponse extends PrefixedResponse<List<LampStatesResponse.LampState>> {
|
||||
static final Pattern RESPONSE_VALIDATION_PATTERN = Pattern.compile("^((\\d+) ([01]))( (\\d+) ([01]))*$");
|
||||
static final Pattern RESPONSE_PARSING_PATTERN = Pattern.compile("(?<hours>\\d+) (?<active>[01])");
|
||||
|
||||
@NonNullByDefault
|
||||
public class LampState {
|
||||
private boolean active;
|
||||
private int lampHours;
|
||||
|
||||
public LampState(boolean active, int lampHours) {
|
||||
this.active = active;
|
||||
this.lampHours = lampHours;
|
||||
}
|
||||
|
||||
public int getLampHours() {
|
||||
return lampHours;
|
||||
}
|
||||
|
||||
public boolean isActive() {
|
||||
return active;
|
||||
}
|
||||
}
|
||||
|
||||
public LampStatesResponse(String response) throws ResponseException {
|
||||
super("LAMP=", response);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<LampStatesResponse.LampState> parseResponseWithoutPrefix(String responseWithoutPrefix)
|
||||
throws ResponseException {
|
||||
// validate if response fully matches specification
|
||||
if (!RESPONSE_VALIDATION_PATTERN.matcher(responseWithoutPrefix).matches()) {
|
||||
throw new ResponseException(
|
||||
MessageFormat.format("Lamp status response could not be parsed: ''{0}''", responseWithoutPrefix));
|
||||
}
|
||||
|
||||
// go through individual matches for each lamp
|
||||
List<LampStatesResponse.LampState> result = new ArrayList<>();
|
||||
Matcher matcher = RESPONSE_PARSING_PATTERN.matcher(responseWithoutPrefix);
|
||||
while (matcher.find()) {
|
||||
int lampHours = Integer.parseInt(matcher.group("hours"));
|
||||
boolean active = matcher.group("active").equals("1");
|
||||
result.add(new LampState(active, lampHours));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 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.pjlinkdevice.internal.device.command.mute;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.binding.pjlinkdevice.internal.device.PJLinkDevice;
|
||||
import org.openhab.binding.pjlinkdevice.internal.device.command.AbstractCommand;
|
||||
import org.openhab.binding.pjlinkdevice.internal.device.command.ResponseException;
|
||||
|
||||
/**
|
||||
* This command is used for selecting audio/video mute of the device as described in
|
||||
* <a href="https://pjlink.jbmia.or.jp/english/data_cl2/PJLink_5-1.pdf">[PJLinkSpec]</a> 4.5. Mute instruction
|
||||
*
|
||||
* @author Nils Schnabel - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class MuteInstructionCommand extends AbstractCommand<MuteInstructionRequest, MuteInstructionResponse> {
|
||||
|
||||
public enum MuteInstructionState {
|
||||
ON("1"),
|
||||
OFF("0");
|
||||
|
||||
private String pjLinkRepresentation;
|
||||
|
||||
private MuteInstructionState(String pjLinkRepresentation) {
|
||||
this.pjLinkRepresentation = pjLinkRepresentation;
|
||||
}
|
||||
|
||||
public String getPJLinkRepresentation() {
|
||||
return this.pjLinkRepresentation;
|
||||
}
|
||||
}
|
||||
|
||||
public enum MuteInstructionChannel {
|
||||
VIDEO("1"),
|
||||
AUDIO("2"),
|
||||
AUDIO_AND_VIDEO("3");
|
||||
|
||||
private String pjLinkRepresentation;
|
||||
|
||||
private MuteInstructionChannel(String pjLinkRepresentation) {
|
||||
this.pjLinkRepresentation = pjLinkRepresentation;
|
||||
}
|
||||
|
||||
public String getPJLinkRepresentation() {
|
||||
return this.pjLinkRepresentation;
|
||||
}
|
||||
}
|
||||
|
||||
private MuteInstructionState targetState;
|
||||
private MuteInstructionChannel targetChannel;
|
||||
|
||||
public MuteInstructionCommand(PJLinkDevice pjLinkDevice, MuteInstructionState targetState,
|
||||
MuteInstructionChannel targetChannel) {
|
||||
super(pjLinkDevice);
|
||||
this.targetState = targetState;
|
||||
this.targetChannel = targetChannel;
|
||||
}
|
||||
|
||||
public MuteInstructionState getTargetState() {
|
||||
return this.targetState;
|
||||
}
|
||||
|
||||
public MuteInstructionChannel getTargetChannel() {
|
||||
return this.targetChannel;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MuteInstructionRequest createRequest() {
|
||||
return new MuteInstructionRequest(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MuteInstructionResponse parseResponse(String response) throws ResponseException {
|
||||
return new MuteInstructionResponse(response);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 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.pjlinkdevice.internal.device.command.mute;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.binding.pjlinkdevice.internal.device.command.Request;
|
||||
|
||||
/**
|
||||
* The request part of {@link MuteInstructionCommand}
|
||||
*
|
||||
* @author Nils Schnabel - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class MuteInstructionRequest implements Request {
|
||||
|
||||
private MuteInstructionCommand command;
|
||||
|
||||
public MuteInstructionRequest(MuteInstructionCommand command) {
|
||||
this.command = command;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getRequestString() {
|
||||
return "%1AVMT " + this.command.getTargetChannel().getPJLinkRepresentation()
|
||||
+ this.command.getTargetState().getPJLinkRepresentation();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 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.pjlinkdevice.internal.device.command.mute;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.binding.pjlinkdevice.internal.device.command.AcknowledgeResponseValue;
|
||||
import org.openhab.binding.pjlinkdevice.internal.device.command.ErrorCode;
|
||||
import org.openhab.binding.pjlinkdevice.internal.device.command.PrefixedResponse;
|
||||
import org.openhab.binding.pjlinkdevice.internal.device.command.ResponseException;
|
||||
|
||||
/**
|
||||
* The response part of {@link MuteInstructionCommand}
|
||||
*
|
||||
* @author Nils Schnabel - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class MuteInstructionResponse extends PrefixedResponse<AcknowledgeResponseValue> {
|
||||
private static final HashSet<ErrorCode> SPECIFIED_ERRORCODES = new HashSet<>(
|
||||
Arrays.asList(ErrorCode.OUT_OF_PARAMETER, ErrorCode.UNAVAILABLE_TIME, ErrorCode.DEVICE_FAILURE));
|
||||
|
||||
public MuteInstructionResponse(String response) throws ResponseException {
|
||||
super("AVMT=", SPECIFIED_ERRORCODES, response);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected AcknowledgeResponseValue parseResponseWithoutPrefix(String responseWithoutPrefix)
|
||||
throws ResponseException {
|
||||
return AcknowledgeResponseValue.getValueForCode(responseWithoutPrefix);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 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.pjlinkdevice.internal.device.command.mute;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.binding.pjlinkdevice.internal.device.PJLinkDevice;
|
||||
import org.openhab.binding.pjlinkdevice.internal.device.command.AbstractCommand;
|
||||
import org.openhab.binding.pjlinkdevice.internal.device.command.ResponseException;
|
||||
|
||||
/**
|
||||
* This command is used for retrieving the current audio/video mute status of the device as described in
|
||||
* <a href="https://pjlink.jbmia.or.jp/english/data_cl2/PJLink_5-1.pdf">[PJLinkSpec]</a> 4.6. Mute status query
|
||||
*
|
||||
* @author Nils Schnabel - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class MuteQueryCommand extends AbstractCommand<MuteQueryRequest, MuteQueryResponse> {
|
||||
|
||||
public MuteQueryCommand(PJLinkDevice pjLinkDevice) {
|
||||
super(pjLinkDevice);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MuteQueryRequest createRequest() {
|
||||
return new MuteQueryRequest();
|
||||
}
|
||||
|
||||
@Override
|
||||
public MuteQueryResponse parseResponse(String response) throws ResponseException {
|
||||
return new MuteQueryResponse(response);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 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.pjlinkdevice.internal.device.command.mute;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.binding.pjlinkdevice.internal.device.command.Request;
|
||||
|
||||
/**
|
||||
* The request part of {@link MuteQueryCommand}
|
||||
*
|
||||
* @author Nils Schnabel - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class MuteQueryRequest implements Request {
|
||||
|
||||
@Override
|
||||
public String getRequestString() {
|
||||
return "%1AVMT ?";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 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.pjlinkdevice.internal.device.command.mute;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.binding.pjlinkdevice.internal.device.command.ErrorCode;
|
||||
import org.openhab.binding.pjlinkdevice.internal.device.command.PrefixedResponse;
|
||||
import org.openhab.binding.pjlinkdevice.internal.device.command.ResponseException;
|
||||
|
||||
/**
|
||||
* The response part of {@link MuteQueryCommand}
|
||||
*
|
||||
* @author Nils Schnabel - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class MuteQueryResponse extends PrefixedResponse<MuteQueryResponse.MuteQueryResponseValue> {
|
||||
|
||||
public enum MuteQueryResponseValue {
|
||||
OFF("Mute off", "30", false, false),
|
||||
VIDEO_MUTE_ON("Video muted", "11", false, true),
|
||||
AUDIO_MUTE_ON("Audio muted", "21", true, false),
|
||||
AUDIO_AND_VIDEO_MUTE_ON("Audio and video muted", "31", true, true);
|
||||
|
||||
private String text;
|
||||
private String code;
|
||||
private boolean audioMuted;
|
||||
private boolean videoMuted;
|
||||
|
||||
private MuteQueryResponseValue(String text, String code, boolean audioMuted, boolean videoMuted) {
|
||||
this.text = text;
|
||||
this.code = code;
|
||||
this.audioMuted = audioMuted;
|
||||
this.videoMuted = videoMuted;
|
||||
}
|
||||
|
||||
public String getText() {
|
||||
return this.text;
|
||||
}
|
||||
|
||||
public static MuteQueryResponseValue parseString(String code) throws ResponseException {
|
||||
for (MuteQueryResponseValue result : MuteQueryResponseValue.values()) {
|
||||
if (result.code.equals(code)) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
throw new ResponseException("Cannot understand mute status: " + code);
|
||||
}
|
||||
|
||||
public boolean isAudioMuted() {
|
||||
return this.audioMuted;
|
||||
}
|
||||
|
||||
public boolean isVideoMuted() {
|
||||
return this.videoMuted;
|
||||
}
|
||||
}
|
||||
|
||||
private static final HashSet<ErrorCode> SPECIFIED_ERRORCODES = new HashSet<>(
|
||||
Arrays.asList(ErrorCode.UNAVAILABLE_TIME, ErrorCode.DEVICE_FAILURE));
|
||||
|
||||
public MuteQueryResponse(String response) throws ResponseException {
|
||||
super("AVMT=", SPECIFIED_ERRORCODES, response);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected MuteQueryResponseValue parseResponseWithoutPrefix(String responseWithoutPrefix) throws ResponseException {
|
||||
return MuteQueryResponseValue.parseString(responseWithoutPrefix);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 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.pjlinkdevice.internal.device.command.power;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.binding.pjlinkdevice.internal.device.PJLinkDevice;
|
||||
import org.openhab.binding.pjlinkdevice.internal.device.command.AbstractCommand;
|
||||
import org.openhab.binding.pjlinkdevice.internal.device.command.ResponseException;
|
||||
|
||||
/**
|
||||
* This command is used for switching the device on/off as described in
|
||||
* <a href="https://pjlink.jbmia.or.jp/english/data_cl2/PJLink_5-1.pdf">[PJLinkSpec]</a> 4.1. Power control instruction
|
||||
*
|
||||
* @author Nils Schnabel - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class PowerInstructionCommand extends AbstractCommand<PowerInstructionRequest, PowerInstructionResponse> {
|
||||
|
||||
public enum PowerInstructionState {
|
||||
ON("1"),
|
||||
OFF("0");
|
||||
|
||||
private String pjLinkRepresentation;
|
||||
|
||||
private PowerInstructionState(String pjLinkRepresentation) {
|
||||
this.pjLinkRepresentation = pjLinkRepresentation;
|
||||
}
|
||||
|
||||
public String getPJLinkRepresentation() {
|
||||
return this.pjLinkRepresentation;
|
||||
}
|
||||
}
|
||||
|
||||
private PowerInstructionState target;
|
||||
|
||||
public PowerInstructionCommand(PJLinkDevice pjLinkDevice, PowerInstructionState target) {
|
||||
super(pjLinkDevice);
|
||||
this.target = target;
|
||||
}
|
||||
|
||||
public PowerInstructionState getTarget() {
|
||||
return this.target;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PowerInstructionRequest createRequest() {
|
||||
return new PowerInstructionRequest(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PowerInstructionResponse parseResponse(String response) throws ResponseException {
|
||||
return new PowerInstructionResponse(response);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 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.pjlinkdevice.internal.device.command.power;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.binding.pjlinkdevice.internal.device.command.Request;
|
||||
|
||||
/**
|
||||
* The request part of {@link PowerInstructionCommand}
|
||||
*
|
||||
* @author Nils Schnabel - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class PowerInstructionRequest implements Request {
|
||||
|
||||
private PowerInstructionCommand command;
|
||||
|
||||
public PowerInstructionRequest(PowerInstructionCommand command) {
|
||||
this.command = command;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getRequestString() {
|
||||
return "%1POWR " + this.command.getTarget().getPJLinkRepresentation();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 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.pjlinkdevice.internal.device.command.power;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.binding.pjlinkdevice.internal.device.command.AcknowledgeResponseValue;
|
||||
import org.openhab.binding.pjlinkdevice.internal.device.command.PrefixedResponse;
|
||||
import org.openhab.binding.pjlinkdevice.internal.device.command.ResponseException;
|
||||
|
||||
/**
|
||||
* The response part of {@link PowerInstructionCommand}
|
||||
*
|
||||
* @author Nils Schnabel - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class PowerInstructionResponse extends PrefixedResponse<AcknowledgeResponseValue> {
|
||||
public PowerInstructionResponse(String response) throws ResponseException {
|
||||
super("POWR=", response);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected AcknowledgeResponseValue parseResponseWithoutPrefix(String responseWithoutPrefix)
|
||||
throws ResponseException {
|
||||
return AcknowledgeResponseValue.getValueForCode(responseWithoutPrefix);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 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.pjlinkdevice.internal.device.command.power;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.binding.pjlinkdevice.internal.device.PJLinkDevice;
|
||||
import org.openhab.binding.pjlinkdevice.internal.device.command.AbstractCommand;
|
||||
import org.openhab.binding.pjlinkdevice.internal.device.command.ResponseException;
|
||||
|
||||
/**
|
||||
* This command is used for retrieving the devices power status as described in
|
||||
* <a href="https://pjlink.jbmia.or.jp/english/data_cl2/PJLink_5-1.pdf">[PJLinkSpec]</a> 4.2. Power status query
|
||||
*
|
||||
* @author Nils Schnabel - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class PowerQueryCommand extends AbstractCommand<PowerQueryRequest, PowerQueryResponse> {
|
||||
|
||||
public PowerQueryCommand(PJLinkDevice pjLinkDevice) {
|
||||
super(pjLinkDevice);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PowerQueryRequest createRequest() {
|
||||
return new PowerQueryRequest();
|
||||
}
|
||||
|
||||
@Override
|
||||
public PowerQueryResponse parseResponse(String response) throws ResponseException {
|
||||
return new PowerQueryResponse(response);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 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.pjlinkdevice.internal.device.command.power;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.binding.pjlinkdevice.internal.device.command.Request;
|
||||
|
||||
/**
|
||||
* The request part of {@link PowerQueryCommand}
|
||||
*
|
||||
* @author Nils Schnabel - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class PowerQueryRequest implements Request {
|
||||
|
||||
@Override
|
||||
public String getRequestString() {
|
||||
return "%1POWR ?";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 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.pjlinkdevice.internal.device.command.power;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.binding.pjlinkdevice.internal.device.command.PrefixedResponse;
|
||||
import org.openhab.binding.pjlinkdevice.internal.device.command.ResponseException;
|
||||
|
||||
/**
|
||||
* The response part of {@link PowerQueryCommand}
|
||||
*
|
||||
* @author Nils Schnabel - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class PowerQueryResponse extends PrefixedResponse<PowerQueryResponse.PowerQueryResponseValue> {
|
||||
public enum PowerQueryResponseValue {
|
||||
STAND_BY("Stand-by", "0"),
|
||||
POWER_ON("Power on", "1"),
|
||||
COOLING_DOWN("Cooling down", "2"),
|
||||
WARMING_UP("Warming up", "3");
|
||||
|
||||
private String text;
|
||||
private String code;
|
||||
|
||||
private PowerQueryResponseValue(String text, String code) {
|
||||
this.text = text;
|
||||
this.code = code;
|
||||
}
|
||||
|
||||
public String getText() {
|
||||
return this.text;
|
||||
}
|
||||
|
||||
public static PowerQueryResponseValue parseString(String code) throws ResponseException {
|
||||
for (PowerQueryResponseValue result : PowerQueryResponseValue.values()) {
|
||||
if (result.code.equals(code)) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
throw new ResponseException("Cannot understand power status: " + code);
|
||||
}
|
||||
}
|
||||
|
||||
public PowerQueryResponse(String response) throws ResponseException {
|
||||
super("POWR=", response);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected PowerQueryResponseValue parseResponseWithoutPrefix(String responseWithoutPrefix)
|
||||
throws ResponseException {
|
||||
return PowerQueryResponseValue.parseString(responseWithoutPrefix);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,131 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 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.pjlinkdevice.internal.discovery;
|
||||
|
||||
import java.net.InetAddress;
|
||||
import java.net.InterfaceAddress;
|
||||
import java.net.NetworkInterface;
|
||||
import java.net.SocketException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.binding.pjlinkdevice.internal.PJLinkDeviceBindingConstants;
|
||||
import org.openhab.core.config.discovery.AbstractDiscoveryService;
|
||||
import org.openhab.core.thing.ThingTypeUID;
|
||||
import org.openhab.core.thing.ThingUID;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* Discovery of PJLink devices. Checks IP addresses in parallel processing.
|
||||
*
|
||||
* Generating IP addresses and checking them is done by the subclasses implementing
|
||||
* {@link AbstractDiscoveryParticipant#generateAddressesToScan} and {@link AbstractDiscoveryParticipant#checkAddress}
|
||||
*
|
||||
* @author Nils Schnabel - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public abstract class AbstractDiscoveryParticipant extends AbstractDiscoveryService {
|
||||
protected final Logger logger = LoggerFactory.getLogger(AbstractDiscoveryParticipant.class);
|
||||
private Integer scannedIPcount = 0;
|
||||
private @Nullable ExecutorService executorService = null;
|
||||
|
||||
public AbstractDiscoveryParticipant(Set<ThingTypeUID> supportedThingTypes, int timeout,
|
||||
boolean backgroundDiscoveryEnabledByDefault) throws IllegalArgumentException {
|
||||
super(supportedThingTypes, timeout, backgroundDiscoveryEnabledByDefault);
|
||||
}
|
||||
|
||||
protected ExecutorService getExecutorService() {
|
||||
ExecutorService executorService = this.executorService;
|
||||
if (executorService == null) {
|
||||
this.executorService = executorService = Executors
|
||||
.newFixedThreadPool(Runtime.getRuntime().availableProcessors() * 2);
|
||||
}
|
||||
return executorService;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void startScan() {
|
||||
logger.trace("PJLinkProjectorDiscoveryParticipant startScan");
|
||||
Set<InetAddress> addressesToScan = generateAddressesToScan();
|
||||
scannedIPcount = 0;
|
||||
for (InetAddress ip : addressesToScan) {
|
||||
getExecutorService().execute(() -> {
|
||||
Thread.currentThread().setName("Discovery thread " + ip);
|
||||
checkAddress(ip, PJLinkDeviceBindingConstants.DEFAULT_PORT,
|
||||
PJLinkDeviceBindingConstants.DEFAULT_SCAN_TIMEOUT_SECONDS);
|
||||
|
||||
synchronized (scannedIPcount) {
|
||||
scannedIPcount += 1;
|
||||
logger.debug("Scanned {} of {} IPs", scannedIPcount, addressesToScan.size());
|
||||
if (scannedIPcount == addressesToScan.size()) {
|
||||
logger.debug("Scan of {} IPs successful", scannedIPcount);
|
||||
stopScan();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected synchronized void stopScan() {
|
||||
super.stopScan();
|
||||
ExecutorService executorService = this.executorService;
|
||||
if (executorService == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
executorService.awaitTermination(10000, TimeUnit.MILLISECONDS);
|
||||
} catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt(); // Reset interrupt flag
|
||||
}
|
||||
executorService.shutdown();
|
||||
}
|
||||
|
||||
public static ThingUID createServiceUID(String ip, int tcpPort) {
|
||||
// uid must not contains dots
|
||||
return new ThingUID(PJLinkDeviceBindingConstants.THING_TYPE_PJLINK,
|
||||
ip.replace('.', '_') + "_" + String.valueOf(tcpPort));
|
||||
}
|
||||
|
||||
protected abstract void checkAddress(InetAddress ip, int tcpPort, int timeout);
|
||||
|
||||
private Set<InetAddress> generateAddressesToScan() {
|
||||
try {
|
||||
Set<InetAddress> addressesToScan = new HashSet<>();
|
||||
ArrayList<NetworkInterface> interfaces = java.util.Collections
|
||||
.list(NetworkInterface.getNetworkInterfaces());
|
||||
for (NetworkInterface networkInterface : interfaces) {
|
||||
if (networkInterface.isLoopback() || !networkInterface.isUp()) {
|
||||
continue;
|
||||
}
|
||||
for (InterfaceAddress i : networkInterface.getInterfaceAddresses()) {
|
||||
collectAddressesToScan(addressesToScan, i);
|
||||
}
|
||||
}
|
||||
return addressesToScan;
|
||||
} catch (SocketException e) {
|
||||
logger.debug("Could not enumerate network interfaces", e);
|
||||
}
|
||||
return new HashSet<>();
|
||||
}
|
||||
|
||||
protected abstract void collectAddressesToScan(Set<InetAddress> addressesToScan, InterfaceAddress i);
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 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.pjlinkdevice.internal.discovery;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.Inet4Address;
|
||||
import java.net.InetAddress;
|
||||
import java.net.InterfaceAddress;
|
||||
import java.net.UnknownHostException;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.apache.commons.net.util.SubnetUtils;
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.binding.pjlinkdevice.internal.PJLinkDeviceBindingConstants;
|
||||
import org.openhab.binding.pjlinkdevice.internal.device.PJLinkDevice;
|
||||
import org.openhab.binding.pjlinkdevice.internal.device.command.AuthenticationException;
|
||||
import org.openhab.binding.pjlinkdevice.internal.device.command.ResponseException;
|
||||
import org.openhab.core.config.discovery.DiscoveryResultBuilder;
|
||||
import org.openhab.core.config.discovery.DiscoveryService;
|
||||
import org.osgi.service.component.annotations.Component;
|
||||
|
||||
/**
|
||||
* Implementation of {@link AbstractDiscoveryParticipant} for IPv4 address ranges and PJLink Class 1 devices.
|
||||
*
|
||||
* @author Nils Schnabel - Initial contribution
|
||||
*/
|
||||
|
||||
@Component(service = DiscoveryService.class, configurationPid = "org.openhab.binding.pjlinkdevice.internal.discovery.DiscoveryParticipantClass1")
|
||||
@NonNullByDefault
|
||||
public class DiscoveryParticipantClass1 extends AbstractDiscoveryParticipant {
|
||||
public DiscoveryParticipantClass1() throws IllegalArgumentException {
|
||||
super(Collections.singleton(PJLinkDeviceBindingConstants.THING_TYPE_PJLINK), 60, true);
|
||||
|
||||
logger.trace("PJLinkProjectorDiscoveryParticipant constructor");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void collectAddressesToScan(Set<InetAddress> addressesToScan, InterfaceAddress i) {
|
||||
// only scan IPv4
|
||||
if (!(i.getAddress() instanceof Inet4Address)) {
|
||||
return;
|
||||
}
|
||||
// only scan Class C networks
|
||||
if (i.getNetworkPrefixLength() < 24) {
|
||||
return;
|
||||
}
|
||||
|
||||
SubnetUtils utils = new SubnetUtils(i.getAddress().getHostAddress() + "/" + i.getNetworkPrefixLength());
|
||||
for (String addressToScan : utils.getInfo().getAllAddresses()) {
|
||||
try {
|
||||
logger.debug("Add address to scan: {}", addressToScan);
|
||||
addressesToScan.add(InetAddress.getByName(addressToScan));
|
||||
} catch (UnknownHostException e) {
|
||||
logger.debug("Unknown Host", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void checkAddress(InetAddress ip, int tcpPort, int timeout) {
|
||||
PJLinkDevice device = new PJLinkDevice(tcpPort, ip, null, timeout);
|
||||
try {
|
||||
Map<String, Object> properties = new HashMap<>();
|
||||
properties.put(PJLinkDeviceBindingConstants.PARAMETER_HOSTNAME, ip.getHostAddress());
|
||||
properties.put(PJLinkDeviceBindingConstants.PARAMETER_PORT, tcpPort);
|
||||
String description = "Unknown PJLink Device";
|
||||
try {
|
||||
device.checkAvailability();
|
||||
|
||||
try {
|
||||
description = device.getFullDescription();
|
||||
logger.debug("got name {}", description);
|
||||
} catch (ResponseException e) {
|
||||
logger.debug("Could not find a name for PJLink device", e);
|
||||
// okay, no name
|
||||
}
|
||||
} catch (AuthenticationException e) {
|
||||
properties.put(PJLinkDeviceBindingConstants.PROPERTY_AUTHENTICATION_REQUIRED, true);
|
||||
}
|
||||
logger.debug("Adding thing");
|
||||
thingDiscovered(DiscoveryResultBuilder.create(createServiceUID(ip.getHostAddress(), tcpPort))
|
||||
.withTTL(PJLinkDeviceBindingConstants.DISCOVERY_RESULT_TTL_SECONDS).withProperties(properties)
|
||||
.withLabel(description).build());
|
||||
logger.debug("Added thing");
|
||||
} catch (ResponseException | IOException e) {
|
||||
logger.debug("No PJLinkDevice here {} {}", ip, e.getStackTrace());
|
||||
// no device here
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<binding:binding id="pjLinkDevice" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:binding="https://openhab.org/schemas/binding/v1.0.0"
|
||||
xsi:schemaLocation="https://openhab.org/schemas/binding/v1.0.0 https://openhab.org/schemas/binding-1.0.0.xsd">
|
||||
|
||||
<name>PJLinkDevice Binding</name>
|
||||
<description>This binding can control PJLink compatible devices.
|
||||
|
||||
The PJLink protocol was mainly standardized for digital
|
||||
projectors, but some other types of devices also use it, in
|
||||
particular TV screens.</description>
|
||||
<author>Nils Schnabel</author>
|
||||
|
||||
</binding:binding>
|
||||
@@ -0,0 +1,133 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<thing:thing-descriptions bindingId="pjLinkDevice"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:thing="https://openhab.org/schemas/thing-description/v1.0.0"
|
||||
xsi:schemaLocation="https://openhab.org/schemas/thing-description/v1.0.0 https://openhab.org/schemas/thing-description-1.0.0.xsd">
|
||||
|
||||
<thing-type id="pjLinkDevice">
|
||||
<label>PJLink Device</label>
|
||||
<description>A PJLink compatible device, e.g. a digital projector</description>
|
||||
|
||||
<channels>
|
||||
<channel id="power" typeId="power"/>
|
||||
<channel id="input" typeId="input"/>
|
||||
<channel id="audioMute" typeId="audioMute"/>
|
||||
<channel id="videoMute" typeId="videoMute"/>
|
||||
<channel id="lamp1Hours" typeId="lampHours"/>
|
||||
<channel id="lamp1Active" typeId="lampActive"/>
|
||||
</channels>
|
||||
|
||||
<config-description>
|
||||
<parameter-group name="basic">
|
||||
<context>basic</context>
|
||||
<label>Basic Settings</label>
|
||||
</parameter-group>
|
||||
<parameter-group name="refresh">
|
||||
<label>Polling Settings</label>
|
||||
<description>Use these settings to configure how the status of the PJLink device will be refreshed</description>
|
||||
</parameter-group>
|
||||
<parameter name="ipAddress" type="text" required="true" min="1" groupName="basic">
|
||||
<context>network-address</context>
|
||||
<label>IP Address</label>
|
||||
<description>The address of the PJLink device to control.</description>
|
||||
</parameter>
|
||||
<parameter name="tcpPort" type="integer" min="1" max="65535" groupName="basic">
|
||||
<default>4352</default>
|
||||
<label>TCP Port</label>
|
||||
<description>The TCP port of the PJLink device to control.</description>
|
||||
</parameter>
|
||||
<parameter name="adminPassword" type="text" groupName="basic">
|
||||
<context>password</context>
|
||||
<label>Password</label>
|
||||
<description>The password of the PJLink device.</description>
|
||||
</parameter>
|
||||
<parameter name="autoReconnectInterval" type="integer" min="0" groupName="basic">
|
||||
<label>Auto Reconnect Interval</label>
|
||||
<description>Seconds between connection retries when connection to the PJLink device has been lost, 0 means never
|
||||
retry, minimum 30s</description>
|
||||
<default>60</default>
|
||||
</parameter>
|
||||
<parameter name="refreshInterval" type="integer" min="0" groupName="refresh">
|
||||
<default>5</default>
|
||||
<label>Refresh Interval (s)</label>
|
||||
<description>How often to poll the device state (in seconds). A value of zero will disable polling.</description>
|
||||
</parameter>
|
||||
<parameter name="refreshPower" type="boolean" groupName="refresh">
|
||||
<default>false</default>
|
||||
<label>Poll for Power State</label>
|
||||
<description>Enable polling for the power state. Only considered if the refreshInterval interval is greater than
|
||||
zero.</description>
|
||||
</parameter>
|
||||
<parameter name="refreshMute" type="boolean" groupName="refresh">
|
||||
<default>false</default>
|
||||
<label>Poll for Mute State</label>
|
||||
<description>Enable polling for the mute state. Only considered if the refreshInterval interval is greater than
|
||||
zero.</description>
|
||||
</parameter>
|
||||
<parameter name="refreshInputChannel" type="boolean" groupName="refresh">
|
||||
<default>false</default>
|
||||
<label>Poll for Selected Input Channel</label>
|
||||
<description>Enable polling for the selected input channel. Only considered if the refreshInterval interval is
|
||||
greater than zero.</description>
|
||||
</parameter>
|
||||
<parameter name="refreshLampState" type="boolean" groupName="refresh">
|
||||
<default>false</default>
|
||||
<label>Poll for Lamp State</label>
|
||||
<description>Enable polling for the lamp state. Only considered if the refresh interval is greater than zero.</description>
|
||||
</parameter>
|
||||
</config-description>
|
||||
|
||||
</thing-type>
|
||||
|
||||
<channel-type id="power">
|
||||
<item-type>Switch</item-type>
|
||||
<label>Power</label>
|
||||
<description>Power ON/OFF the projector</description>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="input">
|
||||
<item-type>String</item-type>
|
||||
<label>Input</label>
|
||||
<description>Select the input signal to use</description>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="audioMute">
|
||||
<item-type>Switch</item-type>
|
||||
<label>Audio Mute</label>
|
||||
<description>Select the audio mute status</description>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="videoMute">
|
||||
<item-type>Switch</item-type>
|
||||
<label>Video Mute</label>
|
||||
<description>Select the video mute status</description>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="lampHours">
|
||||
<item-type>Number</item-type>
|
||||
<label>Lamp Hours</label>
|
||||
<description>How long the lamp has been in use (in hours)</description>
|
||||
<state readOnly="true" pattern="%d h"/>
|
||||
<config-description>
|
||||
<parameter name="lampNumber" type="integer" min="1" max="8">
|
||||
<label>Lamp Number</label>
|
||||
<description>Show used hours for this lamp</description>
|
||||
<default>1</default>
|
||||
</parameter>
|
||||
</config-description>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="lampActive">
|
||||
<item-type>Switch</item-type>
|
||||
<label>Lamp Active</label>
|
||||
<description>Is the lamp in use?</description>
|
||||
<state readOnly="true"/>
|
||||
<config-description>
|
||||
<parameter name="lampNumber" type="integer" min="1" max="8">
|
||||
<label>Lamp Number</label>
|
||||
<description>Show activity for this lamp</description>
|
||||
<default>1</default>
|
||||
</parameter>
|
||||
</config-description>
|
||||
</channel-type>
|
||||
</thing:thing-descriptions>
|
||||
Reference in New Issue
Block a user