diff --git a/ESH-INF/binding/binding.xml b/ESH-INF/binding/binding.xml index 679dad4324056e6279d821aa2aa67247047f9edd..1ef1a3eb1c5623c0c0943589eb1eb5fbb0648c7d 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-03-08 16:38).</description> + <description>This is the binding for OpenLicht (Last Update: 2019-04-08 16:03).</description> <author>René Schöne</author> </binding:binding> diff --git a/ESH-INF/config/config.xml b/ESH-INF/config/config.xml index baaa2e994044efe7611c03840bd198f9144c7f3b..8d0860d539ca4f485fa2afcf290029899584e4a9 100644 --- a/ESH-INF/config/config.xml +++ b/ESH-INF/config/config.xml @@ -7,15 +7,20 @@ <config-description uri="thing-type:openlicht:mqttdevice"> <parameter name="brokerName" type="text" required="true"> <label>Broker Name</label> - <description>Name of the broker as defined in the <broker>.url in services/mqtt.cfg. See the MQTT Binding for more information on how to configure MQTT broker connections.</description> + <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>mosquitto</default> + <default>embedded-mqtt-broker</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>sensors</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="unsupported-category-reset" type="integer" required="false"> <label>MQTT Unsupported Category Reset Timeout</label> <description>Timeout in seconds to log again unsupported MQTT categories. Set to zero to disable (default).</description> diff --git a/src/main/java/org/openhab/binding/openlicht/BindingConstants.java b/src/main/java/org/openhab/binding/openlicht/BindingConstants.java index dabf7128bd67b28baacf4167e9a447a396c2e408..5bdf6d8b13237893aad799d1da6f4a9d69f3db27 100644 --- a/src/main/java/org/openhab/binding/openlicht/BindingConstants.java +++ b/src/main/java/org/openhab/binding/openlicht/BindingConstants.java @@ -29,6 +29,7 @@ public class BindingConstants { // Configuration keys public static final String CONFIG_BROKER_NAME = "brokerName"; 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"; // List of all Thing Type UIDs 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 e2e8cbcdd38a0cd691b5a8a8f259bcb4a410ab64..d941bd479f6508a87d7c0bd65eaad940a07b87b8 100644 --- a/src/main/java/org/openhab/binding/openlicht/handler/AbstractMqttHandler.java +++ b/src/main/java/org/openhab/binding/openlicht/handler/AbstractMqttHandler.java @@ -39,6 +39,7 @@ public abstract class AbstractMqttHandler extends BaseThingHandler private Version version; private Lock configUpdateLock; private String myBrokerName; + protected boolean byteBaseMessages; public AbstractMqttHandler(Thing thing, ConfigurationHolder configurationHolder) { super(thing); @@ -93,6 +94,7 @@ public abstract class AbstractMqttHandler extends BaseThingHandler new Thread(() -> { try { configUpdateLock.lock(); + 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"); @@ -121,6 +123,10 @@ public abstract class AbstractMqttHandler extends BaseThingHandler return bd == null ? null : bd.intValue(); } + protected boolean getConfigValueAsBoolean(String key) { + return (boolean) getThing().getConfiguration().get(key); + } + public void myBrokerAdded(MqttBrokerConnection broker) { this.logger.info("Got correct broker, subscribing to topic {}", getTopic()); currentBrokerConnection = broker; @@ -216,7 +222,7 @@ public abstract class AbstractMqttHandler extends BaseThingHandler * @param input the buffer to read from * @param output the array to write in */ - protected void writeFloatArray(ByteBuffer input, float[] output) { + protected static void writeFloatArray(ByteBuffer input, float[] output) { for (int i = 0; i < output.length; i++) { output[i] = input.getFloat(); } diff --git a/src/main/java/org/openhab/binding/openlicht/handler/AbstractSmartWatchHandler.java b/src/main/java/org/openhab/binding/openlicht/handler/AbstractSmartWatchHandler.java index b84a57b79395a84fb56bf5426d6bcf635052bb93..3c827a859de7e937b75b2afaf5456f63731c00b2 100644 --- a/src/main/java/org/openhab/binding/openlicht/handler/AbstractSmartWatchHandler.java +++ b/src/main/java/org/openhab/binding/openlicht/handler/AbstractSmartWatchHandler.java @@ -15,6 +15,7 @@ package org.openhab.binding.openlicht.handler; import static org.openhab.binding.openlicht.BindingConstants.*; import java.nio.ByteBuffer; +import java.nio.FloatBuffer; import java.util.HashSet; import java.util.Set; import java.util.concurrent.ScheduledExecutorService; @@ -79,26 +80,41 @@ public abstract class AbstractSmartWatchHandler extends AbstractMqttHandler { public void processMessage(String topic, byte[] payload) { // switch category of message (last part of topic after last slash) String category = topic.substring(getTopicLength()); - ByteBuffer buffer = ByteBuffer.wrap(payload); - float[] values; - switch (category) { - case MQTT_CATEGORY_BRIGHTNESS: - updateState(CHANNEL_BRIGHTNESS, new DecimalType(buffer.getFloat())); - break; - case MQTT_CATEGORY_ACCELERATION: - values = new float[3]; - writeFloatArray(buffer, values); - updateState(CHANNEL_ACCELERATION_X, new DecimalType(values[0])); - updateState(CHANNEL_ACCELERATION_Y, new DecimalType(values[1])); - updateState(CHANNEL_ACCELERATION_Z, new DecimalType(values[2])); - case MQTT_CATEGORY_ROTATION: - values = new float[3]; - writeFloatArray(buffer, values); - updateState(CHANNEL_ROTATION_X, new DecimalType(values[0])); - updateState(CHANNEL_ROTATION_Y, new DecimalType(values[1])); - updateState(CHANNEL_ROTATION_Z, new DecimalType(values[2])); - default: - logUnsupportedCategory(category); + if (byteBaseMessages) { + ByteBuffer buffer = ByteBuffer.wrap(payload); + FloatBuffer floatBuffer; + switch (category) { + case MQTT_CATEGORY_BRIGHTNESS: + updateState(CHANNEL_BRIGHTNESS, new DecimalType(buffer.getFloat())); + break; + case MQTT_CATEGORY_ACCELERATION: + floatBuffer = buffer.asFloatBuffer(); + updateState(CHANNEL_ACCELERATION_X, new DecimalType(floatBuffer.get(0))); + updateState(CHANNEL_ACCELERATION_Y, new DecimalType(floatBuffer.get(1))); + updateState(CHANNEL_ACCELERATION_Z, new DecimalType(floatBuffer.get(2))); + case MQTT_CATEGORY_ROTATION: + floatBuffer = buffer.asFloatBuffer(); + updateState(CHANNEL_ROTATION_X, new DecimalType(floatBuffer.get(0))); + updateState(CHANNEL_ROTATION_Y, new DecimalType(floatBuffer.get(1))); + updateState(CHANNEL_ROTATION_Z, new DecimalType(floatBuffer.get(2))); + default: + logUnsupportedCategory(category); + } + } else { + String message = new String(payload); + switch (category) { + case MQTT_CATEGORY_BRIGHTNESS: + updateState(CHANNEL_BRIGHTNESS, new DecimalType(Float.parseFloat(message))); + break; + case MQTT_CATEGORY_ACCELERATION: + MqttUtils.handleAcceleration(message, this::updateState); + break; + case MQTT_CATEGORY_ROTATION: + MqttUtils.handleRotation(message, this::updateState); + break; + default: + logUnsupportedCategory(category); + } } } diff --git a/src/main/java/org/openhab/binding/openlicht/handler/AbstractSmartphoneHandler.java b/src/main/java/org/openhab/binding/openlicht/handler/AbstractSmartphoneHandler.java index 13e91b458bd33be6d0098e7cd27ff1dcc55e18ce..3840c60daade682242d604241f44fb03da0ed148 100644 --- a/src/main/java/org/openhab/binding/openlicht/handler/AbstractSmartphoneHandler.java +++ b/src/main/java/org/openhab/binding/openlicht/handler/AbstractSmartphoneHandler.java @@ -59,19 +59,33 @@ public abstract class AbstractSmartphoneHandler extends AbstractMqttHandler { public void processMessage(String topic, byte[] payload) { // switch category of message (last part of topic after last slash) String category = topic.substring(getTopicLength()); - ByteBuffer buffer = ByteBuffer.wrap(payload); - FloatBuffer floatBuffer; - switch (category) { - case MQTT_CATEGORY_BRIGHTNESS: - updateState(CHANNEL_BRIGHTNESS, new DecimalType(buffer.getFloat())); - break; - case MQTT_CATEGORY_ROTATION: - floatBuffer = buffer.asFloatBuffer(); - updateState(CHANNEL_ROTATION_X, new DecimalType(floatBuffer.get(0))); - updateState(CHANNEL_ROTATION_Y, new DecimalType(floatBuffer.get(1))); - updateState(CHANNEL_ROTATION_Z, new DecimalType(floatBuffer.get(2))); - default: - logUnsupportedCategory(category); + if (byteBaseMessages) { + ByteBuffer buffer = ByteBuffer.wrap(payload); + FloatBuffer floatBuffer; + switch (category) { + case MQTT_CATEGORY_BRIGHTNESS: + updateState(CHANNEL_BRIGHTNESS, new DecimalType(buffer.getFloat())); + break; + case MQTT_CATEGORY_ROTATION: + floatBuffer = buffer.asFloatBuffer(); + updateState(CHANNEL_ROTATION_X, new DecimalType(floatBuffer.get(0))); + updateState(CHANNEL_ROTATION_Y, new DecimalType(floatBuffer.get(1))); + updateState(CHANNEL_ROTATION_Z, new DecimalType(floatBuffer.get(2))); + default: + logUnsupportedCategory(category); + } + } else { + String message = new String(payload); + switch (category) { + case MQTT_CATEGORY_BRIGHTNESS: + updateState(CHANNEL_BRIGHTNESS, new DecimalType(Float.parseFloat(message))); + break; + case MQTT_CATEGORY_ROTATION: + MqttUtils.handleRotation(message, this::updateState); + break; + default: + logUnsupportedCategory(category); + } } } diff --git a/src/main/java/org/openhab/binding/openlicht/handler/MqttUtils.java b/src/main/java/org/openhab/binding/openlicht/handler/MqttUtils.java new file mode 100644 index 0000000000000000000000000000000000000000..1eec2e21e43746a5754704cac3514d7d2bff34e4 --- /dev/null +++ b/src/main/java/org/openhab/binding/openlicht/handler/MqttUtils.java @@ -0,0 +1,79 @@ +package org.openhab.binding.openlicht.handler; + +import static org.openhab.binding.openlicht.BindingConstants.*; + +import java.util.HashMap; +import java.util.Map; +import java.util.Map.Entry; +import java.util.function.BiConsumer; + +import org.eclipse.smarthome.core.library.types.DecimalType; +import org.eclipse.smarthome.core.types.State; + +public class MqttUtils { + + @FunctionalInterface + public interface UpdateState extends BiConsumer<String, State> { + + } + + public static Map<String, DecimalType> handleSimpleJsonMessage(final String message) { + Map<String, DecimalType> result = new HashMap<>(); + String content = message.substring(1, message.length() - 1); + String[] tokens = content.split(","); + for (String tok : tokens) { + String[] keyValue = tok.split("="); + String key = keyValue[0].trim(); + DecimalType value = new DecimalType(Float.parseFloat(keyValue[1].trim())); + result.put(key, value); + } + return result; + } + + public static void handleRotation(String message, UpdateState updateState) { + // state has the form "{z=$FLOAT, x=$FLOAT, y=$FLOAT}" + Map<String, DecimalType> json = MqttUtils.handleSimpleJsonMessage(message); + String channelId; + for (Entry<String, DecimalType> entry : json.entrySet()) { + switch (entry.getKey()) { + case "x": + channelId = CHANNEL_ROTATION_X; + break; + case "y": + channelId = CHANNEL_ROTATION_Y; + break; + case "z": + channelId = CHANNEL_ROTATION_Z; + break; + default: + System.err.print("Unknown JSON key: " + entry.getKey()); + continue; + } + updateState.accept(channelId, entry.getValue()); + } + } + + public static void handleAcceleration(String message, UpdateState updateState) { + // state has the form "{z=$FLOAT, x=$FLOAT, y=$FLOAT}" + Map<String, DecimalType> json = MqttUtils.handleSimpleJsonMessage(message); + String channelId; + for (Entry<String, DecimalType> entry : json.entrySet()) { + switch (entry.getKey()) { + case "x": + channelId = CHANNEL_ACCELERATION_X; + break; + case "y": + channelId = CHANNEL_ACCELERATION_Y; + break; + case "z": + channelId = CHANNEL_ACCELERATION_Z; + break; + default: + System.err.print("Unknown JSON key: " + entry.getKey()); + continue; + } + updateState.accept(channelId, entry.getValue()); + } + } + +} diff --git a/src/main/java/org/openhab/binding/openlicht/internal/OpenLichtConfiguration.java b/src/main/java/org/openhab/binding/openlicht/internal/OpenLichtConfiguration.java index 28824d9695ed41d9927f29f04222d2ca4c9f6c7d..ab149a85a5f0b5c3e6474a732e7bafab310972a0 100644 --- a/src/main/java/org/openhab/binding/openlicht/internal/OpenLichtConfiguration.java +++ b/src/main/java/org/openhab/binding/openlicht/internal/OpenLichtConfiguration.java @@ -20,7 +20,7 @@ package org.openhab.binding.openlicht.internal; public class OpenLichtConfiguration { /** - * Name of the broker as defined in the <broker>.url in services/mqtt.cfg. + * Name of the broker as defined in the name of the broker thing. */ public String brokerName; @@ -29,6 +29,12 @@ public class OpenLichtConfiguration { */ public String baseTopic; + /** + * Interpret MQTT messages as raw bytes. If <code>false</code>, interpret the messages as Strings containing + * numbers. + */ + public boolean byteBasedMessages; + /** * Timeout in seconds to log again unsupported MQTT categories. Set to zero to disable (default). */