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,32 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="src" output="target/classes" path="src/main/java">
<attributes>
<attribute name="optional" value="true"/>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry excluding="**" kind="src" output="target/classes" path="src/main/resources">
<attributes>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-11">
<attributes>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="con" path="org.eclipse.m2e.MAVEN2_CLASSPATH_CONTAINER">
<attributes>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="src" output="target/test-classes" path="src/test/java">
<attributes>
<attribute name="optional" value="true"/>
<attribute name="maven.pomderived" value="true"/>
<attribute name="test" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="output" path="target/classes"/>
</classpath>

View File

@@ -0,0 +1,23 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>org.openhab.binding.lirc</name>
<comment></comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>org.eclipse.jdt.core.javabuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.m2e.core.maven2Builder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>org.eclipse.jdt.core.javanature</nature>
<nature>org.eclipse.m2e.core.maven2Nature</nature>
</natures>
</projectDescription>

View File

@@ -0,0 +1,13 @@
This content is produced and maintained by the openHAB project.
* Project home: https://www.openhab.org
== Declared Project Licenses
This program and the accompanying materials are made available under the terms
of the Eclipse Public License 2.0 which is available at
https://www.eclipse.org/legal/epl-2.0/.
== Source Code
https://github.com/openhab/openhab-addons

View File

@@ -0,0 +1,87 @@
# LIRC Binding
This binding integrates infrared transceivers through [LIRC](http://www.lirc.org) or [WinLIRC](http://winlirc.sourceforge.net).
A list of remote configuration files for LIRC is available [here](http://lirc-remotes.sourceforge.net/remotes-table.html).
## Supported Things
This binding supports LIRC and WinLIRC as bridges for accessing the configured remotes.
LIRC must be started with TCP enabled. On systemd based systems, TCP can be enabled by editing the file
`/usr/lib/systemd/system/lirc.service` and adding `--listen` to the end of the `ExecStart` line. An example
systemd service file for LIRC is shown below.
```ini
[Unit]
Description=Linux Infrared Remote Control
After=network.target
[Service]
RuntimeDirectory=lirc
ExecStart=/usr/sbin/lircd --nodaemon --driver=default --device=/dev/lirc0 --listen
[Install]
WantedBy=multi-user.target
```
By default, LIRC will listen on IP address 0.0.0.0 (any available IP address) and port 8765. If you would
rather run LIRC on a specific port or IP address, you can use `--listen=192.168.1.100:9001` instead.
## Discovery
Discovery of the LIRC bridge is not supported. However, remotes will be automatically discovered once
a bridge is configured.
## Example Configuration
### Things
```xtend
Bridge lirc:bridge:local [ host="192.168.1.120", portNumber="9001" ] {
Thing remote Onkyo_RC_799M [ remote="Onkyo_RC-799M" ]
Thing remote Samsung [ remote="Samsung" ]
}
```
Bridge:
* **host**: IP address or hostname of the LIRC server. Defaults to localhost
* **port**: The port number the LIRC server is listening on. Defaults to 8765
Remote:
* **remote**: The name of the remote as known by LIRC
### Items
```xtend
String Remote_AVReceiver { channel="lirc:remote:local:Onkyo_RC_799M:transmit" }
String Remote_TV { channel="lirc:remote:local:Samsung:transmit" }
```
### Rules
```xtend
rule "LIRC Test"
when
Channel 'lirc:remote:local:Samsung:event' triggered KEY_DVD
then
// Toggle base boost on the AV Receiver
Remote_AVReceiver.sendCommand("KEY_BASEBOOST")
// Increase the volume by 5.
Remote_AVReceiver.sendCommand("KEY_VOLUMEUP 5")
end
```
## Channels
This binding currently supports following channels:
| Channel Type ID | Item Type | Description |
|-----------------|--------------|---------------------------------------|
| event | Trigger | Triggers when a button is pressed. |
| transmit | String | Used to transmit IR commands to LIRC. |

View File

@@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.addons.reactor.bundles</artifactId>
<version>3.0.0-SNAPSHOT</version>
</parent>
<artifactId>org.openhab.binding.lirc</artifactId>
<name>openHAB Add-ons :: Bundles :: LIRC Binding</name>
</project>

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>