added migrated 2.x add-ons

Signed-off-by: Kai Kreuzer <kai@openhab.org>
This commit is contained in:
Kai Kreuzer
2020-09-21 01:58:32 +02:00
parent bbf1a7fd29
commit 6df6783b60
11662 changed files with 1302875 additions and 11 deletions

View File

@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<features name="org.openhab.binding.lirc-${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-lirc" description="LIRC Binding" version="${project.version}">
<feature>openhab-runtime-base</feature>
<bundle start-level="80">mvn:org.openhab.addons.bundles/org.openhab.binding.lirc/${project.version}</bundle>
</feature>
</features>

View File

@@ -0,0 +1,49 @@
/**
* 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.lirc.internal;
import java.util.Collections;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.openhab.core.thing.ThingTypeUID;
/**
* The {@link LIRCBinding} class defines common constants, which are
* used across the whole binding.
*
* @author Andrew Nagle - Initial contribution
*/
public class LIRCBindingConstants {
public static final String BINDING_ID = "lirc";
public static final int DISCOVERY_TIMOUT = 5;
// List of all Thing Type UIDs
public static final ThingTypeUID THING_TYPE_BRIDGE = new ThingTypeUID(BINDING_ID, "bridge");
public static final ThingTypeUID THING_TYPE_REMOTE = new ThingTypeUID(BINDING_ID, "remote");
// List of all channel ids
public static final String CHANNEL_EVENT = "event";
public static final String CHANNEL_TRANSMIT = "transmit";
// List of all supported thing types
public static final Set<ThingTypeUID> SUPPORTED_DEVICE_TYPES = Collections.singleton(THING_TYPE_REMOTE);
public static final Set<ThingTypeUID> SUPPORTED_BRIDGE_TYPES = Collections.singleton(THING_TYPE_BRIDGE);
public static final Set<ThingTypeUID> SUPPORTED_THING_TYPES = Stream.of(THING_TYPE_REMOTE, THING_TYPE_BRIDGE)
.collect(Collectors.toSet());
// List of all properties
public static final String PROPERTY_REMOTE = "remote";
}

View File

@@ -0,0 +1,79 @@
/**
* 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.lirc.internal;
import static org.openhab.binding.lirc.internal.LIRCBindingConstants.THING_TYPE_REMOTE;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Map;
import org.openhab.binding.lirc.internal.discovery.LIRCRemoteDiscoveryService;
import org.openhab.binding.lirc.internal.handler.LIRCBridgeHandler;
import org.openhab.binding.lirc.internal.handler.LIRCRemoteHandler;
import org.openhab.core.config.discovery.DiscoveryService;
import org.openhab.core.thing.Bridge;
import org.openhab.core.thing.Thing;
import org.openhab.core.thing.ThingTypeUID;
import org.openhab.core.thing.ThingUID;
import org.openhab.core.thing.binding.BaseThingHandlerFactory;
import org.openhab.core.thing.binding.ThingHandler;
import org.openhab.core.thing.binding.ThingHandlerFactory;
import org.osgi.framework.ServiceRegistration;
import org.osgi.service.component.annotations.Component;
/**
* The {@link LIRCHandlerFactory} is responsible for creating things and thing
* handlers.
*
* @author Andrew Nagle - Initial contribution
*/
@Component(service = ThingHandlerFactory.class, configurationPid = "binding.lirc")
public class LIRCHandlerFactory extends BaseThingHandlerFactory {
private final Map<ThingUID, ServiceRegistration<?>> discoveryServiceRegs = new HashMap<>();
@Override
public boolean supportsThingType(ThingTypeUID thingTypeUID) {
return LIRCBindingConstants.SUPPORTED_THING_TYPES.contains(thingTypeUID);
}
@Override
protected ThingHandler createHandler(Thing thing) {
ThingTypeUID thingTypeUID = thing.getThingTypeUID();
if (LIRCBindingConstants.SUPPORTED_BRIDGE_TYPES.contains(thingTypeUID)) {
LIRCBridgeHandler handler = new LIRCBridgeHandler((Bridge) thing);
registerDeviceDiscoveryService(handler);
return handler;
} else if (thingTypeUID.equals(THING_TYPE_REMOTE)) {
return new LIRCRemoteHandler(thing);
}
return null;
}
@Override
protected synchronized void removeHandler(ThingHandler thingHandler) {
if (this.discoveryServiceRegs != null) {
ServiceRegistration<?> serviceReg = this.discoveryServiceRegs.remove(thingHandler.getThing().getUID());
if (serviceReg != null) {
serviceReg.unregister();
}
}
}
private synchronized void registerDeviceDiscoveryService(LIRCBridgeHandler handler) {
LIRCRemoteDiscoveryService discoveryService = new LIRCRemoteDiscoveryService(handler);
this.discoveryServiceRegs.put(handler.getThing().getUID(),
bundleContext.registerService(DiscoveryService.class.getName(), discoveryService, new Hashtable<>()));
}
}

View File

@@ -0,0 +1,45 @@
/**
* 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.lirc.internal;
import org.openhab.binding.lirc.internal.messages.LIRCButtonEvent;
import org.openhab.binding.lirc.internal.messages.LIRCResponse;
import org.openhab.core.thing.ThingUID;
/**
* Interface for listeners to receive messages from LIRC server
*
* @author Andrew Nagle
*/
public interface LIRCMessageListener {
/**
* This method is called whenever the message is received from the bridge.
*
* @param bridge
* The LIRC bridge where message is received.
* @param message
* The message which received.
*/
void onMessageReceived(ThingUID bridge, LIRCResponse message);
/**
* This method is called whenever a button is pressed on a remote.
*
* @param bridge
* The LIRC bridge where message is received.
* @param buttonEvent
* Button event details
*/
void onButtonPressed(ThingUID bridge, LIRCButtonEvent buttonEvent);
}

View File

@@ -0,0 +1,58 @@
/**
* 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.lirc.internal;
/**
* Exceptions thrown from the serial interface.
*
* @author Andrew Nagle - Initial contributor
*/
public class LIRCResponseException extends Exception {
private static final long serialVersionUID = 6214176461907613559L;
/**
* Constructor. Creates new instance of LIRCResponseException
*/
public LIRCResponseException() {
super();
}
/**
* Constructor. Creates new instance of LIRCResponseException
*
* @param message the detail message.
*/
public LIRCResponseException(String message) {
super(message);
}
/**
* Constructor. Creates new instance of LIRCResponseException
*
* @param cause the cause. (A null value is permitted, and indicates that the cause is nonexistent or unknown.)
*/
public LIRCResponseException(Throwable cause) {
super(cause);
}
/**
* Constructor. Creates new instance of LIRCResponseException
*
* @param message the detail message.
* @param cause the cause. (A null value is permitted, and indicates that the cause is nonexistent or unknown.)
*/
public LIRCResponseException(String message, Throwable cause) {
super(message, cause);
}
}

View File

@@ -0,0 +1,53 @@
/**
* 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.lirc.internal.config;
/**
* Configuration class for {@link LIRCBridge} device.
*
* @author Andrew Nagle - Initial contribution
*/
public class LIRCBridgeConfiguration {
private String host;
private int portNumber;
/**
* @return the host
*/
public String getHost() {
return host;
}
/**
* @param host the host to set
*/
public void setHost(String host) {
this.host = host;
}
/**
* @return the portNumber
*/
public int getPortNumber() {
return portNumber;
}
/**
* @param portNumber the portNumber to set
*/
public void setPortNumber(int portNumber) {
this.portNumber = portNumber;
}
}

View File

@@ -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.lirc.internal.config;
/**
* Configuration class for {@link LIRCRemote} device.
*
* @author Andrew Nagle - Initial contribution
*/
public class LIRCRemoteConfiguration {
private String remote;
/**
* @return the remote
*/
public String getRemote() {
return remote;
}
/**
* @param remote the name of the remote
*/
public void setRemote(String remote) {
this.remote = remote;
}
}

View File

@@ -0,0 +1,191 @@
/**
* 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.lirc.internal.connector;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArraySet;
import org.openhab.binding.lirc.internal.config.LIRCBridgeConfiguration;
import org.openhab.binding.lirc.internal.messages.LIRCButtonEvent;
import org.openhab.binding.lirc.internal.messages.LIRCResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Connector for communication with the LIRC server
*
* @author Andrew Nagle - Initial contributor
*/
public class LIRCConnector {
private final Logger logger = LoggerFactory.getLogger(LIRCConnector.class);
private Set<LIRCEventListener> listeners = new CopyOnWriteArraySet<>();
private Socket socket;
private InputStream in;
private OutputStream out;
private PrintWriter outWriter;
private Thread readerThread;
public void addEventListener(LIRCEventListener listener) {
listeners.add(listener);
}
public void removeEventListener(LIRCEventListener listener) {
listeners.remove(listener);
}
public void connect(LIRCBridgeConfiguration config) throws UnknownHostException, IOException {
logger.debug("Connecting");
// Consider adding support for Unix Domain sockets as well.
// This would allow us to autodiscover the local LIRC server.
// The junixsocket library should work nicely
socket = new Socket(config.getHost(), config.getPortNumber());
out = socket.getOutputStream();
in = socket.getInputStream();
outWriter = new PrintWriter(out, true);
readerThread = new LIRCStreamReader(this, in);
readerThread.start();
}
public void disconnect() {
logger.debug("Disconnecting");
if (readerThread != null) {
logger.debug("Interrupt stream listener");
readerThread.interrupt();
readerThread = null;
}
if (outWriter != null) {
logger.debug("Close print writer stream");
outWriter.close();
outWriter = null;
}
if (socket != null) {
logger.debug("Close socket");
try {
socket.close();
} catch (IOException e) {
logger.debug("Error while closing the socket: {}", e.getMessage());
}
socket = null;
out = null;
in = null;
}
logger.debug("Disconnected");
}
/**
* Begins discovery of all remotes the LIRC server knows about.
*/
public void startRemoteDiscovery() {
sendCommand("LIST");
}
/**
* Transmits the button press for the specified remote.
*
* @param remote
* Name of the remote
* @param button
* Button to press
*/
public void transmit(String remote, String button) {
int timesToSend = 1;
String buttonName;
String[] parts = button.split(" ");
if (parts.length > 1) {
buttonName = parts[0];
timesToSend = Integer.parseInt(parts[1]);
} else {
buttonName = button;
}
transmit(remote, buttonName, timesToSend);
}
/**
* Transmits the button press for the specified remote
*
* @param remote
* Name of the remote
* @param button
* Button to press
* @param timesToSend
* Number of times to transmit the button
*/
public void transmit(String remote, String button, int timesToSend) {
// The last parameter is the number of times the command should be repeated.
// For example, the command "SEND_ONCE TV KEY_VOLUMEUP 4" will transmit
// the volume up code 5 times.
sendCommand(String.format("SEND_ONCE %s %s %s", remote, button, timesToSend - 1));
}
private synchronized void sendCommand(String command) {
outWriter.println(command);
outWriter.flush();
}
/**
* Sends a button pressed event to all listeners
*
* @param buttonEvent
* the button pressed
*/
public synchronized void sendButtonToListeners(LIRCButtonEvent buttonEvent) {
try {
for (LIRCEventListener listener : listeners) {
listener.buttonPressed(buttonEvent);
}
} catch (Exception e) {
logger.error("Error invoking event listener", e);
}
}
/**
* Sends an error message to all listeners
*
* @param error
* error message to send
*/
public synchronized void sendErrorToListeners(String error) {
try {
for (LIRCEventListener listener : listeners) {
listener.errorOccured(error);
}
} catch (Exception e) {
logger.error("Error invoking event listener", e);
}
}
/**
* Sends a LIRC message to all listeners
*
* @param message
* message to send
*/
public synchronized void sendMessageToListeners(LIRCResponse message) {
try {
for (LIRCEventListener listener : listeners) {
listener.messageReceived(message);
}
} catch (Exception e) {
logger.error("Error invoking event listener", e);
}
}
}

View File

@@ -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.lirc.internal.connector;
import org.openhab.binding.lirc.internal.messages.LIRCButtonEvent;
import org.openhab.binding.lirc.internal.messages.LIRCResponse;
/**
* Defines an interface to receive messages from the LIRC server
*
* @author Andrew Nagle
*/
public interface LIRCEventListener {
/**
* Procedure to receive messages from the LIRC server
*
* @param reponse
* Message received
*/
void messageReceived(LIRCResponse message);
/**
* Procedure for receiving notification of button presses
*
* @param buttonEvent
* Button press event details
*/
void buttonPressed(LIRCButtonEvent buttonEvent);
/**
* Procedure for receiving information about fatal errors.
*
* @param error
* Error occured.
*/
void errorOccured(String error);
}

View File

@@ -0,0 +1,143 @@
/**
* 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.lirc.internal.connector;
import java.io.BufferedReader;
import java.io.EOFException;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.InterruptedIOException;
import java.util.Arrays;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.openhab.binding.lirc.internal.LIRCResponseException;
import org.openhab.binding.lirc.internal.messages.LIRCButtonEvent;
import org.openhab.binding.lirc.internal.messages.LIRCResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Stream reader to parse LIRC output into messages
*
* @author Andrew Nagle
*/
public class LIRCStreamReader extends Thread {
private final Logger logger = LoggerFactory.getLogger(LIRCStreamReader.class);
private static final Pattern EVENT_PATTERN = Pattern.compile("^([a-f0-9]+)\\s([a-f0-9]+)\\s(.+)\\s(.+)$");
private InputStream in;
private boolean interrupted = false;
private BufferedReader reader;
private LIRCConnector connector;
public LIRCStreamReader(LIRCConnector connector, InputStream in) {
this.connector = connector;
this.in = in;
}
@Override
public void interrupt() {
interrupted = true;
super.interrupt();
}
@Override
public void run() {
reader = new BufferedReader(new InputStreamReader(in));
String line;
String responseText = "";
while (!interrupted) {
try {
line = reader.readLine();
if (line == null) {
throw new EOFException("lost connection");
} else {
logger.trace("Received message: {}", line);
Matcher m = EVENT_PATTERN.matcher(line);
if (m.matches()) {
String code = m.group(1);
String repeatsHex = m.group(2);
String button = m.group(3);
String remote = m.group(4);
int repeats = Integer.parseInt(repeatsHex, 16);
LIRCButtonEvent buttonMessage = new LIRCButtonEvent(remote, button, repeats, code);
connector.sendButtonToListeners(buttonMessage);
} else {
if ("BEGIN".equals(line)) {
responseText = "";
} else if ("END".equals(line)) {
processResponse(responseText);
responseText = null;
} else {
responseText += line + "\n";
}
}
}
} catch (InterruptedIOException e) {
Thread.currentThread().interrupt();
logger.error("Interrupted via InterruptedIOException");
} catch (EOFException e) {
logger.error("Lost connection to LIRC server", e);
connector.sendErrorToListeners(e.getMessage());
this.interrupt();
} catch (IOException e) {
if (!interrupted) {
logger.error("Reading from socket failed", e);
connector.sendErrorToListeners(e.getMessage());
}
} catch (LIRCResponseException e) {
logger.error("Invalid message received", e);
}
}
try {
reader.close();
} catch (IOException e) {
logger.debug("Error while closing the input stream: {}", e.getMessage());
}
}
private void processResponse(String responseText) throws LIRCResponseException {
String[] parts = responseText.split("\n");
String command = parts[0];
boolean success = true;
int dataLength = 0;
String[] data = null;
if (parts.length > 1) {
if ("SUCCESS".equals(parts[1]) || "ERROR".equals(parts[1])) {
success = "SUCCESS".equals(parts[1]);
} else {
throw new LIRCResponseException("Malformed response");
}
}
if (parts.length > 2) {
if ("DATA".equals(parts[2]) && parts.length > 3) {
dataLength = Integer.parseInt(parts[3]);
} else {
throw new LIRCResponseException("Malformed response");
}
}
if (parts.length > 4) {
data = Arrays.copyOfRange(parts, 4, parts.length);
if (data.length != dataLength) {
throw new LIRCResponseException(String
.format("Data does not match expected length. Expected: %s, Got: %s", dataLength, data.length));
}
}
LIRCResponse response = new LIRCResponse(command, success, data);
connector.sendMessageToListeners(response);
}
}

View File

@@ -0,0 +1,80 @@
/**
* 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.lirc.internal.discovery;
import java.util.HashMap;
import java.util.Map;
import org.openhab.binding.lirc.internal.LIRCBindingConstants;
import org.openhab.binding.lirc.internal.LIRCMessageListener;
import org.openhab.binding.lirc.internal.handler.LIRCBridgeHandler;
import org.openhab.binding.lirc.internal.messages.LIRCButtonEvent;
import org.openhab.binding.lirc.internal.messages.LIRCResponse;
import org.openhab.core.config.discovery.AbstractDiscoveryService;
import org.openhab.core.config.discovery.DiscoveryResult;
import org.openhab.core.config.discovery.DiscoveryResultBuilder;
import org.openhab.core.thing.ThingTypeUID;
import org.openhab.core.thing.ThingUID;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
*
* @author Andrew Nagle - Initial contribution
*/
public class LIRCRemoteDiscoveryService extends AbstractDiscoveryService implements LIRCMessageListener {
private final Logger logger = LoggerFactory.getLogger(LIRCRemoteDiscoveryService.class);
private LIRCBridgeHandler bridgeHandler;
public LIRCRemoteDiscoveryService(LIRCBridgeHandler lircBridgeHandler) {
super(LIRCBindingConstants.SUPPORTED_DEVICE_TYPES, LIRCBindingConstants.DISCOVERY_TIMOUT, true);
this.bridgeHandler = lircBridgeHandler;
bridgeHandler.registerMessageListener(this);
}
@Override
protected void startScan() {
logger.debug("Discovery service scan started");
bridgeHandler.startDeviceDiscovery();
}
@Override
public void onButtonPressed(ThingUID bridge, LIRCButtonEvent buttonEvent) {
addRemote(bridge, buttonEvent.getRemote());
}
@Override
public void onMessageReceived(ThingUID bridge, LIRCResponse message) {
LIRCResponse response = message;
String command = response.getCommand();
if ("LIST".equals(command) && response.isSuccess()) {
for (String remoteID : response.getData()) {
addRemote(bridge, remoteID);
}
}
}
private void addRemote(ThingUID bridge, String remote) {
ThingTypeUID uid = LIRCBindingConstants.THING_TYPE_REMOTE;
ThingUID thingUID = new ThingUID(uid, bridge, remote);
logger.trace("Remote {}: Discovered new remote.", remote);
Map<String, Object> properties = new HashMap<>(1);
properties.put(LIRCBindingConstants.PROPERTY_REMOTE, remote);
DiscoveryResult discoveryResult = DiscoveryResultBuilder.create(thingUID).withLabel(remote).withBridge(bridge)
.withProperties(properties).build();
thingDiscovered(discoveryResult);
}
}

View File

@@ -0,0 +1,199 @@
/**
* 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.lirc.internal.handler;
import java.io.IOException;
import java.net.UnknownHostException;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArraySet;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import org.openhab.binding.lirc.internal.LIRCMessageListener;
import org.openhab.binding.lirc.internal.config.LIRCBridgeConfiguration;
import org.openhab.binding.lirc.internal.connector.LIRCConnector;
import org.openhab.binding.lirc.internal.connector.LIRCEventListener;
import org.openhab.binding.lirc.internal.messages.LIRCButtonEvent;
import org.openhab.binding.lirc.internal.messages.LIRCResponse;
import org.openhab.core.thing.Bridge;
import org.openhab.core.thing.ChannelUID;
import org.openhab.core.thing.ThingStatus;
import org.openhab.core.thing.ThingStatusDetail;
import org.openhab.core.thing.binding.BaseBridgeHandler;
import org.openhab.core.types.Command;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* The {@link LIRCBridgeHandler} is responsible for handling commands, which are
* sent to one of the channels.
*
* @author Andrew Nagle - Initial contribution
*/
public class LIRCBridgeHandler extends BaseBridgeHandler {
private final Logger logger = LoggerFactory.getLogger(LIRCBridgeHandler.class);
private LIRCBridgeConfiguration configuration;
private ScheduledFuture<?> connectorTask;
private LIRCConnector connector;
private EventListener eventListener = new EventListener();
private Set<LIRCMessageListener> deviceStatusListeners = new CopyOnWriteArraySet<>();
public LIRCBridgeHandler(Bridge bridge) {
super(bridge);
}
@Override
public void handleCommand(ChannelUID channelUID, Command command) {
logger.debug("Bridge commands not supported.");
}
@Override
public void initialize() {
logger.debug("Initializing the LIRC Bridge handler");
configuration = getConfigAs(LIRCBridgeConfiguration.class);
if (connectorTask == null || connectorTask.isCancelled()) {
connectorTask = scheduler.scheduleWithFixedDelay(new Runnable() {
@Override
public void run() {
logger.debug("Checking LIRC connection, thing status = {}", thing.getStatus());
if (thing.getStatus() != ThingStatus.ONLINE) {
connect();
}
}
}, 0, 60, TimeUnit.SECONDS);
}
}
@Override
public void dispose() {
logger.debug("Disposing bridge handler.");
if (connectorTask != null && !connectorTask.isCancelled()) {
logger.debug("Cancelling task.");
connectorTask.cancel(true);
connectorTask = null;
}
if (connector != null) {
logger.debug("Stopping connector");
connector.removeEventListener(eventListener);
connector.disconnect();
}
for (LIRCMessageListener deviceStatusListener : deviceStatusListeners) {
unregisterMessageListener(deviceStatusListener);
}
super.dispose();
logger.debug("Bridge handler disposed.");
}
private void connect() {
logger.debug("Connecting to LIRC");
try {
if (connector != null) {
connector.disconnect();
}
if (configuration.getHost() != null && connector == null) {
connector = new LIRCConnector();
}
if (connector != null) {
connector.connect(configuration);
connector.addEventListener(eventListener);
updateStatus(ThingStatus.ONLINE);
startDeviceDiscovery();
}
} catch (UnknownHostException e) {
logger.error("Connection to LIRC failed: unknown host");
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "Unknown Host");
} catch (IOException e) {
logger.error("Connection to LIRC failed", e);
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.getMessage());
}
}
/**
* Initiates discovery of remotes
*/
public void startDeviceDiscovery() {
if (connector != null) {
connector.startRemoteDiscovery();
}
}
/**
* Registers a message listener
*
* @param listener message listener to add
* @return true if listener as added successfully; false otherwise
*/
public boolean registerMessageListener(LIRCMessageListener listener) {
if (listener == null) {
throw new IllegalArgumentException("The listener parameter may not be null.");
}
return deviceStatusListeners.add(listener);
}
/**
* Unregisters a message listener
*
* @param listener message listener to remove
* @return true if listener as removed successfully; false otherwise
*/
public boolean unregisterMessageListener(LIRCMessageListener listener) {
if (listener == null) {
throw new IllegalArgumentException("The listener parameter may not be null.");
}
return deviceStatusListeners.remove(listener);
}
/**
* Transmits the button press for the specified remote.
*
* @param remote Name of the remote
* @param button Button to press
*/
public void transmit(String remote, String button) {
connector.transmit(remote, button);
}
private class EventListener implements LIRCEventListener {
@Override
public void messageReceived(LIRCResponse response) {
for (LIRCMessageListener deviceStatusListener : deviceStatusListeners) {
try {
deviceStatusListener.onMessageReceived(getThing().getUID(), response);
} catch (Exception e) {
logger.error("An exception occurred while calling the DeviceStatusListener", e);
}
}
}
@Override
public void buttonPressed(LIRCButtonEvent buttonEvent) {
for (LIRCMessageListener deviceStatusListener : deviceStatusListeners) {
try {
deviceStatusListener.onButtonPressed(getThing().getUID(), buttonEvent);
} catch (Exception e) {
logger.error("An exception occurred while calling the DeviceStatusListener", e);
}
}
}
@Override
public void errorOccured(String error) {
logger.error("Error occured: {}", error);
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, error);
}
}
}

View File

@@ -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.lirc.internal.handler;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.openhab.binding.lirc.internal.LIRCBindingConstants;
import org.openhab.binding.lirc.internal.LIRCMessageListener;
import org.openhab.binding.lirc.internal.config.LIRCRemoteConfiguration;
import org.openhab.binding.lirc.internal.messages.LIRCButtonEvent;
import org.openhab.binding.lirc.internal.messages.LIRCResponse;
import org.openhab.core.library.types.StringType;
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.ThingUID;
import org.openhab.core.thing.binding.BaseThingHandler;
import org.openhab.core.types.Command;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* The {@link LIRCRemoteHandler} is responsible for handling commands, which are
* sent to one of the channels.
*
* @author Andrew Nagle - Initial contribution
*/
public class LIRCRemoteHandler extends BaseThingHandler implements LIRCMessageListener {
private final Logger logger = LoggerFactory.getLogger(LIRCRemoteHandler.class);
private static final Pattern UNKNOWN_REMOTE_PATTERN = Pattern.compile("^unknown remote: \"(.+)\"$");
private LIRCBridgeHandler bridgeHandler;
private LIRCRemoteConfiguration config;
private String remoteName = null;
public LIRCRemoteHandler(Thing thing) {
super(thing);
}
@Override
public void handleCommand(ChannelUID channelUID, Command command) {
logger.debug("Received channel: {}, command: {}", channelUID, command);
if (remoteName == null) {
logger.error("Remote name is not set in {}", getThing().getUID());
return;
}
if (channelUID.getId().equals(LIRCBindingConstants.CHANNEL_TRANSMIT)) {
// command instanceof RefreshType is not supported
if (command instanceof StringType) {
bridgeHandler.transmit(remoteName, command.toString());
}
}
}
@Override
public void initialize() {
logger.debug("Initializing thing {}", getThing().getUID());
config = getConfigAs(LIRCRemoteConfiguration.class);
remoteName = config.getRemote();
if (remoteName == null) {
logger.error("Remote name is not set in {}", getThing().getUID());
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "Remote name is not set");
} else {
bridgeHandler = (LIRCBridgeHandler) getBridge().getHandler();
bridgeHandler.registerMessageListener(this);
if (getBridge().getStatus() == ThingStatus.ONLINE) {
updateStatus(ThingStatus.ONLINE);
} else {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_OFFLINE);
}
}
}
@Override
public void dispose() {
logger.debug("Thing {} disposed.", getThing().getUID());
if (bridgeHandler != null) {
bridgeHandler.unregisterMessageListener(this);
}
bridgeHandler = null;
super.dispose();
}
@Override
public void onButtonPressed(ThingUID bridge, LIRCButtonEvent buttonEvent) {
if (remoteName.equals(buttonEvent.getRemote())) {
logger.debug("Remote {}: Button {} pressed {} times.", remoteName, buttonEvent.getButton(),
buttonEvent.getRepeats() + 1);
updateStatus(ThingStatus.ONLINE);
triggerChannel(LIRCBindingConstants.CHANNEL_EVENT, buttonEvent.getButton());
}
}
@Override
public void onMessageReceived(ThingUID bridge, LIRCResponse response) {
String command = response.getCommand();
if ("LIST".equals(command) && response.isSuccess()) {
boolean found = false;
for (String remote : response.getData()) {
if (remoteName.equals(remote)) {
found = true;
}
}
if (found) {
updateStatus(ThingStatus.ONLINE);
} else {
logger.error("Remote {}: Remote was removed from LIRC server.", remoteName);
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR);
}
} else if (!response.isSuccess()) {
String error = response.getData()[0];
Matcher m = UNKNOWN_REMOTE_PATTERN.matcher(error);
if (m.matches() && remoteName.equals(m.group(1))) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, "Unknown remote");
}
}
}
}

View File

@@ -0,0 +1,69 @@
/**
* 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.lirc.internal.messages;
/**
* Represents a button event that was received from the LIRC server
*
* @author Andrew Nagle - Initial contribution
*/
public class LIRCButtonEvent {
private final String code;
private final int repeats;
private final String button;
private final String remote;
public LIRCButtonEvent(String remote, String button, int repeats, String code) {
this.code = code;
this.repeats = repeats;
this.button = button;
this.remote = remote;
}
/**
* Gets the number of times this event was repeated.
*
* @return number of repeats
*/
public int getRepeats() {
return repeats;
}
/**
* Gets the name of the button that was pressed
*
* @return the name of the button
*/
public String getButton() {
return button;
}
/**
* Gets the name of the remote that generated this event
*
* @return the name of the remote
*/
public String getRemote() {
return remote;
}
/**
* Gets the raw hex code of the button pressed
*
* @return the hex code
*/
public String getCode() {
return code;
}
}

View File

@@ -0,0 +1,44 @@
/**
* 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.lirc.internal.messages;
/**
* Represents a response received from the LIRC server
*
* @author Andrew Nagle - Initial contribution
*/
public class LIRCResponse {
private final String command;
private final boolean success;
private final String[] data;
public LIRCResponse(String command, boolean success, String[] data) {
super();
this.command = command;
this.success = success;
this.data = data;
}
public String getCommand() {
return command;
}
public boolean isSuccess() {
return success;
}
public String[] getData() {
return data;
}
}

View File

@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<binding:binding id="lirc" 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>LIRC Binding</name>
<description>The LIRC binding allows transmission and receipt of standard infrared remote control signals via a LIRC
server.</description>
<author>Andrew Nagle</author>
</binding:binding>

View File

@@ -0,0 +1,27 @@
<?xml version="1.0" encoding="UTF-8"?>
<thing:thing-descriptions bindingId="lirc"
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">
<!-- LIRC Server Bridge -->
<bridge-type id="bridge">
<label>LIRC Server</label>
<description>The LIRC Server Bridge.</description>
<config-description>
<parameter name="host" type="text">
<context>network-address</context>
<label>Hostname</label>
<description>The host of the LIRC server</description>
<default>localhost</default>
</parameter>
<parameter name="portNumber" type="integer" min="1" max="65535">
<label>Port</label>
<description>The port number the LIRC server is listening to.</description>
<default>8765</default>
</parameter>
</config-description>
</bridge-type>
</thing:thing-descriptions>

View File

@@ -0,0 +1,40 @@
<?xml version="1.0" encoding="UTF-8"?>
<thing:thing-descriptions bindingId="lirc"
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">
<!-- Remote Thing Type -->
<thing-type id="remote" listed="false">
<label>Remote Control</label>
<description>An IR remote control</description>
<channels>
<channel id="event" typeId="event"/>
<channel id="transmit" typeId="transmit"/>
</channels>
<config-description>
<parameter name="remote" type="text" required="true">
<label>Remote Name</label>
<description>The name of the remote as configured in LIRC</description>
</parameter>
</config-description>
</thing-type>
<!-- Button Event Channel Type -->
<channel-type id="event">
<kind>trigger</kind>
<label>Button Event</label>
<description>Triggers whenever a button is pressed</description>
</channel-type>
<!-- Transmit Channel Type -->
<channel-type id="transmit">
<item-type>String</item-type>
<label>Transmit Command</label>
<description>Used to transmit IR commands</description>
</channel-type>
</thing:thing-descriptions>