diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..2f7896d1d1365eafb0da03d9fe456fac81408487 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +target/ diff --git a/ESH-INF/config/config.xml b/ESH-INF/config/config.xml index 3e5e1e722b72c3a35688ce3e08402c094f526f9b..9a3bff11cbc4a5bf50389ecc0e2ea184b4c5e5e7 100644 --- a/ESH-INF/config/config.xml +++ b/ESH-INF/config/config.xml @@ -13,8 +13,8 @@ </parameter> <parameter name="base-topic" type="text" required="true"> <label>Base-Topic</label> - <description>Base topic for publishing updates.</description> - <default>sensors/</default> + <description>Base topic for publishing updates. Do not include a trailing slash.</description> + <default>sensors</default> </parameter> </config-description> diff --git a/ESH-INF/thing/thing-types.xml b/ESH-INF/thing/thing-types.xml index 95628bea857b0792a1cb64305500d31a6ec308ba..ae709002d0da722f09c91159128688de2b96402b 100644 --- a/ESH-INF/thing/thing-types.xml +++ b/ESH-INF/thing/thing-types.xml @@ -31,9 +31,39 @@ <description>Provides sensor information from a Polar M600 smart watch</description> <channels> - <channel typeId="polar-accelerator-type" id="polar-accelerator-x" /> - <channel typeId="polar-accelerator-type" id="polar-accelerator-y" /> - <channel typeId="polar-accelerator-type" id="polar-accelerator-z" /> + <channel typeId="polar-acceleration-type" id="polar-acceleration-x"> + <label>Acceleration X</label> + <description>Acceleration on the x-axis.</description> + </channel> + <channel typeId="polar-acceleration-type" id="polar-acceleration-y" /> + <channel typeId="polar-acceleration-type" id="polar-acceleration-z" /> + <channel typeId="polar-rotation-type" id="polar-rotation-x" /> + <channel typeId="polar-rotation-type" id="polar-rotation-y" /> + <channel typeId="polar-rotation-type" id="polar-rotation-z" /> + <channel typeId="polar-activity-type" id="polar-activity" /> + <channel typeId="polar-heart-rate-type" id="polar-heart-rate" /> + <channel typeId="polar-steps-type" id="polar-steps" /> + <channel typeId="openlicht-brightness-type" id="polar-brightness" /> + </channels> + + <config-description-ref uri="thing-type:openlicht:mqttdevice" /> + </thing-type> + + <!-- Polar M600 Thing Type --> + <thing-type id="moto-360"> + <label>Moto 360</label> + <description>Provides sensor information from a Moto 360 smart watch</description> + + <channels> + <channel typeId="polar-acceleration-type" id="polar-acceleration-x"> + <label>Acceleration X</label> + <description>Acceleration on the x-axis.</description> + </channel> + <channel typeId="polar-acceleration-type" id="polar-acceleration-y" /> + <channel typeId="polar-acceleration-type" id="polar-acceleration-z" /> + <channel typeId="polar-rotation-type" id="polar-rotation-x" /> + <channel typeId="polar-rotation-type" id="polar-rotation-y" /> + <channel typeId="polar-rotation-type" id="polar-rotation-z" /> <channel typeId="polar-activity-type" id="polar-activity" /> <channel typeId="polar-heart-rate-type" id="polar-heart-rate" /> <channel typeId="polar-steps-type" id="polar-steps" /> @@ -44,7 +74,7 @@ </thing-type> <!-- Accelerator Type --> - <channel-type id="polar-accelerator-type"> + <channel-type id="polar-acceleration-type"> <item-type>Number</item-type> <label>Acceleration</label> <description>Acceleration in one direction in meters per second.</description> @@ -52,6 +82,15 @@ <state readOnly="true" /> </channel-type> + <!-- Rotation Type --> + <channel-type id="polar-rotation-type"> + <item-type>Number</item-type> + <label>Rotation</label> + <description>Rotation around one axis (unitless).</description> + <category>Motion</category> + <state readOnly="true" /> + </channel-type> + <!-- Activity Type --> <channel-type id="polar-activity-type"> <item-type>String</item-type> diff --git a/META-INF/MANIFEST.MF b/META-INF/MANIFEST.MF index e809621f72ff1fe5bee2e22ac7afd9a6c5456040..e81e710bd3ce43ab8b4d126763d4f3a4628ae293 100644 --- a/META-INF/MANIFEST.MF +++ b/META-INF/MANIFEST.MF @@ -21,4 +21,5 @@ Import-Package: org.osgi.service.component, org.slf4j Service-Component: OSGI-INF/*.xml -Export-Package: org.openhab.binding.openlicht.handler +Export-Package: org.openhab.binding.openlicht, + org.openhab.binding.openlicht.handler diff --git a/README.md b/README.md index efbfe951511f9f5d733cdb4ef7238721a9fb87c3..a9c21f3aa11ade40d50910dcd515c7203da1315f 100644 --- a/README.md +++ b/README.md @@ -1,18 +1,25 @@ # <bindingName> Binding +Currently, the additional package `org.eclipse.smarthome.io.transport.mqtt` is needed for this binding to work. +It can be exported as a plugin from the OpenHAB Eclipse. + _Give some details about what this binding is meant for - a protocol, system, specific device._ _If possible, provide some resources like pictures, a YouTube video, etc. to give an impression of what can be done with this binding. You can place such resources into a `doc` folder next to this README.md._ ## Supported Things +- SkyWriterHAT +- Polar M600 +- Samsung S6 (or actually any smart phone) + _Please describe the different supported things / devices within this section._ _Which different types are supported, which models were tested etc.?_ _Note that it is planned to generate some part of this based on the XML files within ```ESH-INF/thing``` of your binding._ ## Discovery -_Describe the available auto-discovery features here. Mention for what it works and what needs to be kept in mind when using it._ +Not implemented yet. ## Binding Configuration diff --git a/src/main/java/org/openhab/binding/openlicht/internal/BindingConstants.java b/src/main/java/org/openhab/binding/openlicht/BindingConstants.java similarity index 71% rename from src/main/java/org/openhab/binding/openlicht/internal/BindingConstants.java rename to src/main/java/org/openhab/binding/openlicht/BindingConstants.java index 6cb656516c7c34bca010513b554e6ed988cec2d9..974b723370fc1e69d6ae78c0190f8a4658e37a31 100644 --- a/src/main/java/org/openhab/binding/openlicht/internal/BindingConstants.java +++ b/src/main/java/org/openhab/binding/openlicht/BindingConstants.java @@ -10,7 +10,7 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.openlicht.internal; +package org.openhab.binding.openlicht; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.smarthome.core.thing.ThingTypeUID; @@ -26,6 +26,10 @@ public class BindingConstants { private static final String BINDING_ID = "openlicht"; + // Configuration keys + public static final String CONFIG_BROKER_NAME = "brokerName"; + public static final String CONFIG_BASE_TOPIC = "base-topic"; + // 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 = new ThingTypeUID(BINDING_ID, "polar-m600"); @@ -33,9 +37,12 @@ public class BindingConstants { // List of all Channel ids public static final String CHANNEL_FLICK = "flick"; - public static final String CHANNEL_ACCELERATOR_X = "polar-accelerator-x"; - public static final String CHANNEL_ACCELERATOR_Y = "polar-accelerator-y"; - public static final String CHANNEL_ACCELERATOR_Z = "polar-accelerator-z"; + public static final String CHANNEL_ACCELERATION_X = "polar-acceleration-x"; + public static final String CHANNEL_ACCELERATION_Y = "polar-acceleration-y"; + public static final String CHANNEL_ACCELERATION_Z = "polar-acceleration-z"; + public static final String CHANNEL_ROTATION_X = "polar-rotation-x"; + public static final String CHANNEL_ROTATION_Y = "polar-rotation-y"; + public static final String CHANNEL_ROTATION_Z = "polar-rotation-z"; public static final String CHANNEL_ACTIVITY = "polar-activity"; public static final String CHANNEL_HEART_RATE = "polar-heart-rate"; public static final String CHANNEL_STEPS = "polar-steps"; @@ -44,7 +51,8 @@ public class BindingConstants { // List of MQTT categories public static final String MQTT_CATEGORY_FLICK = "flick"; - public static final String MQTT_CATEGORY_ACCELERATOR = "accelerator"; + public static final String MQTT_CATEGORY_ACCELERATION = "acceleration"; + public static final String MQTT_CATEGORY_ROTATION = "rotation"; public static final String MQTT_CATEGORY_ACTIVITY = "activity"; public static final String MQTT_CATEGORY_HEART_RATE = "heart-rate"; public static final String MQTT_CATEGORY_STEPS = "steps"; 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 7ccbb6c97dcc6eea7568026b3f4fbca012f90ee8..2ee0ac7a4388c69fad9928ab48546792d6dbbd26 100644 --- a/src/main/java/org/openhab/binding/openlicht/handler/AbstractMqttHandler.java +++ b/src/main/java/org/openhab/binding/openlicht/handler/AbstractMqttHandler.java @@ -1,5 +1,7 @@ package org.openhab.binding.openlicht.handler; +import static org.openhab.binding.openlicht.BindingConstants.*; + import org.eclipse.jdt.annotation.NonNull; import org.eclipse.smarthome.core.thing.Thing; import org.eclipse.smarthome.core.thing.ThingStatus; @@ -10,7 +12,6 @@ import org.eclipse.smarthome.io.transport.mqtt.MqttBrokersObserver; import org.eclipse.smarthome.io.transport.mqtt.MqttException; import org.eclipse.smarthome.io.transport.mqtt.MqttMessageSubscriber; import org.eclipse.smarthome.io.transport.mqtt.MqttService; -import org.openhab.binding.openlicht.internal.OpenLichtConfiguration; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -20,7 +21,7 @@ public abstract class AbstractMqttHandler extends BaseThingHandler @NonNull protected final Logger logger = LoggerFactory.getLogger(AbstractMqttHandler.class); - protected OpenLichtConfiguration config; + // protected OpenLichtConfiguration config; private MqttService mqttService; @@ -28,6 +29,8 @@ public abstract class AbstractMqttHandler extends BaseThingHandler private MqttBrokerConnection currentBrokerConnection = null; + private boolean warnNoBrokerConnection = true; + public AbstractMqttHandler(Thing thing, MqttService mqttService) { super(thing); this.mqttService = mqttService; @@ -35,14 +38,19 @@ public abstract class AbstractMqttHandler extends BaseThingHandler @Override public void initialize() { - config = getConfigAs(OpenLichtConfiguration.class); + // config = getConfigAs(OpenLichtConfiguration.class); + String brokerName = getConfigValue(CONFIG_BROKER_NAME); + + // remove this handler from mqttService (in case initialize is called because of a config update) + this.mqttService.removeBrokersListener(this); this.mqttService.addBrokersListener(this); + updateStatus(ThingStatus.UNKNOWN, ThingStatusDetail.CONFIGURATION_PENDING, "Searching broker"); for (MqttBrokerConnection broker : this.mqttService.getAllBrokerConnections()) { brokerAdded(broker); } if (currentBrokerConnection == null) { - MqttBrokerConnection myBroker = this.mqttService.getBrokerConnection(config.brokerName); + MqttBrokerConnection myBroker = this.mqttService.getBrokerConnection(brokerName); if (myBroker != null) { brokerAdded(myBroker); } @@ -50,11 +58,17 @@ public abstract class AbstractMqttHandler extends BaseThingHandler publish("init"); } + private String getConfigValue(String key) { + return (String) getThing().getConfiguration().get(key); + } + @Override public void brokerAdded(MqttBrokerConnection broker) { - if (broker.getName().equals(config.brokerName)) { + String brokerName = getConfigValue(CONFIG_BROKER_NAME); + if (broker.getName().equals(brokerName)) { // this is our broker! currentBrokerConnection = broker; + warnNoBrokerConnection = true; updateTopicLength(); try { if (subscribeTopic()) { @@ -63,9 +77,9 @@ public abstract class AbstractMqttHandler extends BaseThingHandler updateStatus(ThingStatus.ONLINE); this.logger.info("Broker found, thing is online"); } catch (MqttException e) { - this.logger.error("Could not add subscriber to broker {}", config.brokerName); + this.logger.error("Could not add subscriber to broker {}", brokerName); updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, - "Could not add subscriber to broker " + config.brokerName); + "Could not add subscriber to broker " + brokerName); } } else { this.logger.debug("Got another broker, not interessted!"); @@ -80,7 +94,8 @@ public abstract class AbstractMqttHandler extends BaseThingHandler @Override public void brokerRemoved(MqttBrokerConnection broker) { - if (broker.getName().equals(config.brokerName)) { + String brokerName = getConfigValue(CONFIG_BROKER_NAME); + if (broker.getName().equals(brokerName)) { // this was our broker! updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_PENDING, "Broker is offline"); currentBrokerConnection = null; @@ -89,7 +104,11 @@ public abstract class AbstractMqttHandler extends BaseThingHandler protected void publish(String message) { if (currentBrokerConnection == null) { - logger.warn("Can't publish message, no connection"); + if (warnNoBrokerConnection) { + // just report once + logger.warn("Can't publish message, no connection"); + warnNoBrokerConnection = false; + } return; } String topic = subscribeSubTopics() ? (getTopic().substring(0, usedTopicLength) + "out") @@ -104,7 +123,8 @@ public abstract class AbstractMqttHandler extends BaseThingHandler @Override public final String getTopic() { - return config.topic + (getSubTopic() == null ? "" : "/" + getSubTopic()) + (subscribeSubTopics() ? "/#" : ""); + return getConfigValue(CONFIG_BASE_TOPIC) + (getSubTopic() == null ? "" : "/" + getSubTopic()) + + (subscribeSubTopics() ? "/#" : ""); } protected final int getTopicLength() { diff --git a/src/main/java/org/openhab/binding/openlicht/handler/PolarHandler.java b/src/main/java/org/openhab/binding/openlicht/handler/PolarHandler.java index c6b138d0255e021a9cb94e9076aaa42912b33a18..e7cd7ad4fc56f9cd49d32e0e77c98e10a55026be 100644 --- a/src/main/java/org/openhab/binding/openlicht/handler/PolarHandler.java +++ b/src/main/java/org/openhab/binding/openlicht/handler/PolarHandler.java @@ -12,18 +12,17 @@ */ package org.openhab.binding.openlicht.handler; -import static org.openhab.binding.openlicht.internal.BindingConstants.*; +import static org.openhab.binding.openlicht.BindingConstants.*; +import java.nio.ByteBuffer; import java.util.HashSet; import java.util.Set; -import org.eclipse.jdt.annotation.Nullable; import org.eclipse.smarthome.core.library.types.DecimalType; import org.eclipse.smarthome.core.thing.ChannelUID; import org.eclipse.smarthome.core.thing.Thing; import org.eclipse.smarthome.core.types.Command; import org.eclipse.smarthome.io.transport.mqtt.MqttService; -import org.openhab.binding.openlicht.internal.OpenLichtConfiguration; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -37,9 +36,6 @@ public class PolarHandler extends AbstractMqttHandler { private final Logger logger = LoggerFactory.getLogger(PolarHandler.class); - @Nullable - private OpenLichtConfiguration config; - private Set<String> seenUnsupportedCategories; public PolarHandler(Thing thing, MqttService mqttService) { @@ -50,30 +46,50 @@ public class PolarHandler extends AbstractMqttHandler { @Override public void handleCommand(ChannelUID channelUID, Command command) { logger.debug("Got command for read-only thing: {} {}", channelUID.getAsString(), command.toFullString()); - if (channelUID.getId().equals(CHANNEL_ACCELERATOR_X)) { - // TODO: handle command + // TODO: handle command - // Note: if communication with thing fails for some reason, - // indicate that by setting the status with detail information - // updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, - // "Could not control device at IP address x.x.x.x"); - } + // Note: if communication with thing fails for some reason, + // indicate that by setting the status with detail information + // updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, + // "Could not control device at IP address x.x.x.x"); } @Override public void processMessage(String topic, byte[] payload) { // switch category of message (last part of topic after last slash) String category = topic.substring(getTopicLength()); - String state = new String(payload); + // String state = new String(payload); + ByteBuffer buffer = ByteBuffer.wrap(payload); + float[] values; switch (category) { case MQTT_CATEGORY_BRIGHTNESS: - updateState(CHANNEL_BRIGHTNESS_POLAR, DecimalType.valueOf(state)); + updateState(CHANNEL_BRIGHTNESS_POLAR, new DecimalType(buffer.getInt())); break; + case MQTT_CATEGORY_ACCELERATION: + // expected form: [0.12345678, 0.12345678, 0.12345678] + 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: + // expected form: [0.12345678, 0.12345678, 0.12345678] + 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); } } + private void writeFloatArray(ByteBuffer input, float[] output) { + for (int i = 0; i < output.length; i++) { + output[i] = input.getFloat(); + } + } + private void logUnsupportedCategory(String category) { if (seenUnsupportedCategories.add(category)) { logger.warn("Category not supported: {}", category); diff --git a/src/main/java/org/openhab/binding/openlicht/handler/SkyWriterHATHandler.java b/src/main/java/org/openhab/binding/openlicht/handler/SkyWriterHATHandler.java index 6214559ba9f6a9a95954c5cd17f00655e0e8c92a..bdfae51465d11859254a267e9ec681cb2b3ef6c3 100644 --- a/src/main/java/org/openhab/binding/openlicht/handler/SkyWriterHATHandler.java +++ b/src/main/java/org/openhab/binding/openlicht/handler/SkyWriterHATHandler.java @@ -8,7 +8,7 @@ */ package org.openhab.binding.openlicht.handler; -import static org.openhab.binding.openlicht.internal.BindingConstants.CHANNEL_FLICK; +import static org.openhab.binding.openlicht.BindingConstants.CHANNEL_FLICK; import org.eclipse.smarthome.core.library.types.StringType; import org.eclipse.smarthome.core.thing.ChannelUID; @@ -30,14 +30,13 @@ public class SkyWriterHATHandler extends AbstractMqttHandler { @Override public void handleCommand(ChannelUID channelUID, Command command) { - if (channelUID.getId().equals(CHANNEL_FLICK)) { - // TODO: handle command - - // Note: if communication with thing fails for some reason, - // indicate that by setting the status with detail information - // updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, - // "Could not control device at IP address x.x.x.x"); - } + logger.debug("Got command for read-only thing: {} {}", channelUID.getAsString(), command.toFullString()); + // TODO: handle command + + // Note: if communication with thing fails for some reason, + // indicate that by setting the status with detail information + // updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, + // "Could not control device at IP address x.x.x.x"); } @Override diff --git a/src/main/java/org/openhab/binding/openlicht/handler/SmartphoneHandler.java b/src/main/java/org/openhab/binding/openlicht/handler/SmartphoneHandler.java index 4d0dde4276bf8063d85a34f8098f1ab47cbb1181..e5b3155a95dca962980bd5328f9d070f2b311257 100644 --- a/src/main/java/org/openhab/binding/openlicht/handler/SmartphoneHandler.java +++ b/src/main/java/org/openhab/binding/openlicht/handler/SmartphoneHandler.java @@ -12,18 +12,16 @@ */ package org.openhab.binding.openlicht.handler; -import static org.openhab.binding.openlicht.internal.BindingConstants.*; +import static org.openhab.binding.openlicht.BindingConstants.*; import java.util.HashSet; import java.util.Set; -import org.eclipse.jdt.annotation.Nullable; import org.eclipse.smarthome.core.library.types.DecimalType; import org.eclipse.smarthome.core.thing.ChannelUID; import org.eclipse.smarthome.core.thing.Thing; import org.eclipse.smarthome.core.types.Command; import org.eclipse.smarthome.io.transport.mqtt.MqttService; -import org.openhab.binding.openlicht.internal.OpenLichtConfiguration; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -37,9 +35,6 @@ public class SmartphoneHandler extends AbstractMqttHandler { private final Logger logger = LoggerFactory.getLogger(SmartphoneHandler.class); - @Nullable - private OpenLichtConfiguration config; - private Set<String> seenUnsupportedCategories; public SmartphoneHandler(Thing thing, MqttService mqttService) { 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 39197749f2f83863043d4826680e0f8188515d4c..315b3ec33fac8c867c46f259d829cb113976e201 100644 --- a/src/main/java/org/openhab/binding/openlicht/internal/OpenLichtHandlerFactory.java +++ b/src/main/java/org/openhab/binding/openlicht/internal/OpenLichtHandlerFactory.java @@ -12,7 +12,7 @@ */ package org.openhab.binding.openlicht.internal; -import static org.openhab.binding.openlicht.internal.BindingConstants.*; +import static org.openhab.binding.openlicht.BindingConstants.*; import java.util.Collections; import java.util.Set;