diff --git a/ESH-INF/binding/binding.xml b/ESH-INF/binding/binding.xml index 1ef1a3eb1c5623c0c0943589eb1eb5fbb0648c7d..c23ad97f979300f0b06561beec3f46b98aab9d8b 100644 --- a/ESH-INF/binding/binding.xml +++ b/ESH-INF/binding/binding.xml @@ -4,7 +4,7 @@ xsi:schemaLocation="http://eclipse.org/smarthome/schemas/binding/v1.0.0 http://eclipse.org/smarthome/schemas/binding-1.0.0.xsd"> <name>OpenLicht Binding</name> - <description>This is the binding for OpenLicht (Last Update: 2019-04-08 16:03).</description> + <description>This is the binding for OpenLicht (Last Update: 2019-04-09 15:21).</description> <author>René Schöne</author> </binding:binding> diff --git a/ESH-INF/thing/thing-types.xml b/ESH-INF/thing/thing-types.xml index c555472399c34fcb1eb923548cfb9271dcf017cc..d842b361b93291a87d789f453c7164f255e211d2 100644 --- a/ESH-INF/thing/thing-types.xml +++ b/ESH-INF/thing/thing-types.xml @@ -4,6 +4,35 @@ xmlns:thing="http://eclipse.org/smarthome/schemas/thing-description/v1.0.0" xsi:schemaLocation="http://eclipse.org/smarthome/schemas/thing-description/v1.0.0 http://eclipse.org/smarthome/schemas/thing-description-1.0.0.xsd"> + <!-- Eraser Thing Type --> + <thing-type id="eraser"> + <label>Eraser</label> + <description>Establish communication with a running eraser via MQTT</description> + <config-description> + <parameter name="brokerName" type="text" required="true"> + <label>Broker Name</label> + <description>Name of the broker as defined in the name of the broker thing. See the MQTT Binding for more information on how to create a MQTT broker thing.</description> + <context>service</context> + <default>embedded-mqtt-broker</default> + </parameter> + <parameter name="byte-based-messages" type="boolean" required="false"> + <label>Byte-based MQTT Messages</label> + <description>Interpret MQTT messages as raw bytes. If false, interpret the messages as Strings containing numbers.</description> + <default>false</default> + </parameter> + <parameter name="base-topic" type="text" required="true"> + <label>Base-Topic</label> + <description>Base topic for publishing updates. Do not include a trailing slash.</description> + <default>oh/in</default> + </parameter> + <parameter name="outTopic" type="text" required="true"> + <label>Outgoing topic</label> + <description>MQTT topic prefix for messages leaving openHAB to eraser.</description> + <default>oh/out/</default> + </parameter> + </config-description> + </thing-type> + <!-- Skywriter HAT Thing Type --> <thing-type id="skywriter-hat"> <label>SkyWriterHAT</label> diff --git a/META-INF/MANIFEST.MF b/META-INF/MANIFEST.MF index f6ff8e131037a500a8e89fc75bbf12841eae37d9..baaa6f952d248c6a8807ae6f3a2bc248eeae5c60 100644 --- a/META-INF/MANIFEST.MF +++ b/META-INF/MANIFEST.MF @@ -11,6 +11,8 @@ Import-Package: org.eclipse.jdt.annotation;resolution:=optional, org.eclipse.smarthome.config.core, org.eclipse.smarthome.core.common.registry, + org.eclipse.smarthome.core.events, + org.eclipse.smarthome.core.items.events, org.eclipse.smarthome.core.library.types, org.eclipse.smarthome.core.thing, org.eclipse.smarthome.core.thing.binding, diff --git a/src/main/java/org/openhab/binding/openlicht/BindingConstants.java b/src/main/java/org/openhab/binding/openlicht/BindingConstants.java index 5bdf6d8b13237893aad799d1da6f4a9d69f3db27..7673c99a530dbdc8c4f0a71ab715bcf149722350 100644 --- a/src/main/java/org/openhab/binding/openlicht/BindingConstants.java +++ b/src/main/java/org/openhab/binding/openlicht/BindingConstants.java @@ -31,12 +31,14 @@ public class BindingConstants { public static final String CONFIG_BASE_TOPIC = "base-topic"; public static final String CONFIG_BYTE_BASED_MESSAGES = "byte-based-messages"; public static final String CONFIG_TIMEOUT_MQTT_UNSUPPORTED_CATEGORIES = "unsupported-category-reset"; + public static final String CONFIG_ERASER_OUT_TOPIC = "outTopic"; // List of all Thing Type UIDs public static final ThingTypeUID THING_TYPE_SKYWRITER_HAT = new ThingTypeUID(BINDING_ID, "skywriter-hat"); public static final ThingTypeUID THING_TYPE_POLAR_M600 = new ThingTypeUID(BINDING_ID, "polar-m600"); public static final ThingTypeUID THING_TYPE_MOTO_360 = new ThingTypeUID(BINDING_ID, "moto-360"); public static final ThingTypeUID THING_TYPE_SAMSUNG_S6 = new ThingTypeUID(BINDING_ID, "samsung-s6"); + public static final ThingTypeUID THING_TYPE_ERASER = new ThingTypeUID(BINDING_ID, "eraser"); // List of all Channel ids public static final String CHANNEL_FLICK = "flick"; diff --git a/src/main/java/org/openhab/binding/openlicht/handler/AbstractMqttHandler.java b/src/main/java/org/openhab/binding/openlicht/handler/AbstractMqttHandler.java index 175124547382ce3fcc85318105bf5f5e011b03c2..a16ebb59f11e3206ea737196c6f377700a6d7b60 100644 --- a/src/main/java/org/openhab/binding/openlicht/handler/AbstractMqttHandler.java +++ b/src/main/java/org/openhab/binding/openlicht/handler/AbstractMqttHandler.java @@ -49,6 +49,8 @@ public abstract class AbstractMqttHandler extends BaseThingHandler this.mqttService = configurationHolder.getMqttService(); if (this.mqttService != null) { this.mqttService.addBrokersListener(this); + } else { + logger.warn("No mqtt service, so no broker listener added"); } this.executor = configurationHolder.getExecutor(); this.version = configurationHolder.getVersion(); @@ -114,14 +116,15 @@ public abstract class AbstractMqttHandler extends BaseThingHandler updateStatus(ThingStatus.UNKNOWN, ThingStatusDetail.CONFIGURATION_PENDING, "Searching broker"); new Thread(() -> { try { - configUpdateLock.lock(); + this.configUpdateLock.lock(); + this.logger.debug("Initializing"); this.byteBaseMessages = getConfigValueAsBoolean(CONFIG_BYTE_BASED_MESSAGES); String brokerName = getConfigValueAsString(CONFIG_BROKER_NAME); if (brokerName == null) { updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "BrokerName not set"); return; } - myBrokerName = brokerName; + this.myBrokerName = brokerName; logger.debug("Setting myBrokerName to '{}'", myBrokerName); // for (Thing thing : this.thingRegistry.getAll()) { @@ -138,8 +141,15 @@ public abstract class AbstractMqttHandler extends BaseThingHandler brokerAdded(entry.getKey(), entry.getValue()); } } + if (this.currentBrokerConnection == null) { + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_PENDING, + "Broker with name '" + this.myBrokerName + "' not found."); + } else { + updateStatus(ThingStatus.ONLINE); + } + moreInitialize(); } finally { - configUpdateLock.unlock(); + this.configUpdateLock.unlock(); } }).start(); } @@ -196,7 +206,7 @@ public abstract class AbstractMqttHandler extends BaseThingHandler currentBrokerConnection = null; } - protected void publish(String message) { + protected void publish(String topic, String message) { if (currentBrokerConnection == null) { if (warnNoBrokerConnection) { // just report once @@ -205,8 +215,8 @@ public abstract class AbstractMqttHandler extends BaseThingHandler } return; } - String topic = subscribeSubTopics() ? (getTopic().substring(0, usedTopicLength) + "out") - : (getTopic() + "/out"); + // String topic = subscribeSubTopics() ? (getTopic().substring(0, usedTopicLength) + "out") + // : (getTopic() + "/out"); byte[] payload = message.getBytes(); currentBrokerConnection.publish(topic, payload); } @@ -220,6 +230,13 @@ public abstract class AbstractMqttHandler extends BaseThingHandler return this.usedTopicLength; } + /** + * Subclasses may override this to do more asynchronous initialization. + */ + protected void moreInitialize() { + // empty by default + } + /** * Subclasses may override this to subscribe to a special subtopic. * Do not add a leading nor a trailing slash! @@ -248,7 +265,7 @@ public abstract class AbstractMqttHandler extends BaseThingHandler protected boolean subscribeSubTopics() { return true; } - // + // /** // * Writes a float array from the given input based on the length of the targeted output. // * diff --git a/src/main/java/org/openhab/binding/openlicht/handler/EraserHandler.java b/src/main/java/org/openhab/binding/openlicht/handler/EraserHandler.java new file mode 100644 index 0000000000000000000000000000000000000000..d2c97a7405ad51dd8c58845e517d2491684384ff --- /dev/null +++ b/src/main/java/org/openhab/binding/openlicht/handler/EraserHandler.java @@ -0,0 +1,78 @@ +package org.openhab.binding.openlicht.handler; + +import java.util.Collections; +import java.util.Set; + +import org.eclipse.jdt.annotation.NonNull; +import org.eclipse.jdt.annotation.Nullable; +import org.eclipse.smarthome.core.events.Event; +import org.eclipse.smarthome.core.events.EventFilter; +import org.eclipse.smarthome.core.events.EventPublisher; +import org.eclipse.smarthome.core.events.EventSubscriber; +import org.eclipse.smarthome.core.items.events.ItemEventFactory; +import org.eclipse.smarthome.core.items.events.ItemStateEvent; +import org.eclipse.smarthome.core.library.types.StringType; +import org.eclipse.smarthome.core.thing.ChannelUID; +import org.eclipse.smarthome.core.thing.Thing; +import org.eclipse.smarthome.core.types.Command; +import org.openhab.binding.openlicht.BindingConstants; +import org.openhab.binding.openlicht.internal.ConfigurationHolder; + +public class EraserHandler extends AbstractMqttHandler implements EventSubscriber { + + private @Nullable EventPublisher eventPublisher; + private String outTopic; + + public EraserHandler(Thing thing, ConfigurationHolder configurationHolder) { + super(thing, configurationHolder); + this.eventPublisher = configurationHolder.getEventPublisher(); + } + + @Override + protected void moreInitialize() { + this.outTopic = getConfigValueAsString(BindingConstants.CONFIG_ERASER_OUT_TOPIC); + if (this.outTopic.charAt(this.outTopic.length() - 1) != '/') { + this.outTopic += "/"; + } + } + + @Override + public void processMessage(String topic, byte[] payload) { + // check topic, and forward payload to respective item + int indexOfLastSlash = topic.lastIndexOf('/'); + String itemName = topic.substring(indexOfLastSlash); + ItemStateEvent eshEvent = ItemEventFactory.createStateEvent(itemName, new StringType(new String(payload))); + EventPublisher publisher = this.eventPublisher; + if (publisher == null) { + logger.debug("No event publisher to process message"); + } else { + publisher.post(eshEvent); + } + } + + @Override + public void handleCommand(ChannelUID channelUID, Command command) { + logger.debug("got a command " + command); + } + + @Override + public Set<@NonNull String> getSubscribedEventTypes() { + return Collections.singleton(ItemStateEvent.TYPE); + } + + @Override + public @Nullable EventFilter getEventFilter() { + return null; + } + + @Override + public void receive(Event event) { + // publish MQTT message + logger.debug("Recevied an event: " + event); + ItemStateEvent itemStateEvent = (ItemStateEvent) event; + String topic = outTopic + itemStateEvent.getItemName(); + String payload = itemStateEvent.getPayload(); + publish(topic, payload); + } + +} diff --git a/src/main/java/org/openhab/binding/openlicht/internal/ConfigurationHolder.java b/src/main/java/org/openhab/binding/openlicht/internal/ConfigurationHolder.java index e9b407ba3769110d9bf7cd20e8d39ccb75327828..94a32f73bde28e36d5801dad0dd9e7a12437a247 100644 --- a/src/main/java/org/openhab/binding/openlicht/internal/ConfigurationHolder.java +++ b/src/main/java/org/openhab/binding/openlicht/internal/ConfigurationHolder.java @@ -3,6 +3,7 @@ package org.openhab.binding.openlicht.internal; import java.util.concurrent.ScheduledExecutorService; import org.eclipse.jdt.annotation.Nullable; +import org.eclipse.smarthome.core.events.EventPublisher; import org.eclipse.smarthome.core.thing.ThingRegistry; import org.eclipse.smarthome.io.transport.mqtt.MqttService; import org.osgi.framework.Version; @@ -18,6 +19,9 @@ public interface ConfigurationHolder { @Nullable ThingRegistry getThingRegistry(); + @Nullable + EventPublisher getEventPublisher(); + Version getVersion(); } diff --git a/src/main/java/org/openhab/binding/openlicht/internal/OpenLichtHandlerFactory.java b/src/main/java/org/openhab/binding/openlicht/internal/OpenLichtHandlerFactory.java index 52587212757483aea03723b07643e1e1b6ed5599..7146bc7507c44a881a22f6ea7c75b0db7368aa78 100644 --- a/src/main/java/org/openhab/binding/openlicht/internal/OpenLichtHandlerFactory.java +++ b/src/main/java/org/openhab/binding/openlicht/internal/OpenLichtHandlerFactory.java @@ -23,6 +23,7 @@ import java.util.stream.Stream; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; +import org.eclipse.smarthome.core.events.EventPublisher; import org.eclipse.smarthome.core.thing.Thing; import org.eclipse.smarthome.core.thing.ThingRegistry; import org.eclipse.smarthome.core.thing.ThingTypeUID; @@ -30,6 +31,7 @@ import org.eclipse.smarthome.core.thing.binding.BaseThingHandlerFactory; import org.eclipse.smarthome.core.thing.binding.ThingHandler; import org.eclipse.smarthome.core.thing.binding.ThingHandlerFactory; import org.eclipse.smarthome.io.transport.mqtt.MqttService; +import org.openhab.binding.openlicht.handler.EraserHandler; import org.openhab.binding.openlicht.handler.Moto360Handler; import org.openhab.binding.openlicht.handler.PolarM600Handler; import org.openhab.binding.openlicht.handler.SamsungS6Handler; @@ -59,6 +61,7 @@ public class OpenLichtHandlerFactory extends BaseThingHandlerFactory implements private @Nullable MqttService service; private @Nullable ThingRegistry thingRegistry; private @Nullable ScheduledExecutorService executor; + private @Nullable EventPublisher eventPublisher; @Override public boolean supportsThingType(ThingTypeUID thingTypeUID) { @@ -81,6 +84,9 @@ public class OpenLichtHandlerFactory extends BaseThingHandlerFactory implements if (THING_TYPE_SAMSUNG_S6.equals(thingTypeUID)) { return new SamsungS6Handler(thing, this); } + if (THING_TYPE_ERASER.equals(thingTypeUID)) { + return new EraserHandler(thing, this); + } return null; } @@ -114,6 +120,11 @@ public class OpenLichtHandlerFactory extends BaseThingHandlerFactory implements return thingRegistry; } + @Override + public @Nullable EventPublisher getEventPublisher() { + return eventPublisher; + } + @Override public Version getVersion() { return FrameworkUtil.getBundle(getClass()).getVersion(); @@ -140,4 +151,15 @@ public class OpenLichtHandlerFactory extends BaseThingHandlerFactory implements LoggerFactory.getLogger(OpenLichtHandlerFactory.class).info("Deleting thing registry {}", service); this.thingRegistry = null; } + + @Reference(cardinality = ReferenceCardinality.MANDATORY, policy = ReferencePolicy.STATIC) + public void setEventPublisher(EventPublisher eventPublisher) { + LoggerFactory.getLogger(OpenLichtHandlerFactory.class).info("Setting event publisher to {}", thingRegistry); + this.eventPublisher = eventPublisher; + } + + public void unsetEventPublisher(EventPublisher eventPublisher) { + LoggerFactory.getLogger(OpenLichtHandlerFactory.class).info("Deleting event publisher {}", service); + this.eventPublisher = null; + } }