diff --git a/plugins/hal-mqtt/src/se/hal/plugin/mqtt/HalMqttController.java b/plugins/hal-mqtt/src/se/hal/plugin/mqtt/HalMqttController.java index c545e010..7d656c48 100644 --- a/plugins/hal-mqtt/src/se/hal/plugin/mqtt/HalMqttController.java +++ b/plugins/hal-mqtt/src/se/hal/plugin/mqtt/HalMqttController.java @@ -49,7 +49,7 @@ public class HalMqttController implements HalAutostartController, MqttSubscripti private MqttBroker mqttBroker; private List detectors = Collections.emptyList(); - private HashMap> topics = new HashMap<>(); + private Map> topics = new HashMap<>(); private List deviceListeners = new CopyOnWriteArrayList<>(); // -------------------------- @@ -95,10 +95,13 @@ public class HalMqttController implements HalAutostartController, MqttSubscripti // -------------------------- @Override - public void dataPublished(String topic, byte[] data) { - logger.finest("MQTT data published(topic: " + topic + "): " + new String(data, StandardCharsets.UTF_8)); + public void dataPublished(String topicName, byte[] data) { + if (data == null) + data = new byte[0]; + logger.finest("MQTT data published(topic: " + topicName + "): " + new String(data, StandardCharsets.UTF_8)); - List registeredDevices = topics.get(topic); + topicName = topicName.trim(); + List registeredDevices = topics.get(topicName); // Handle existing devices @@ -114,16 +117,16 @@ public class HalMqttController implements HalAutostartController, MqttSubscripti } } - // Handle new devices + // Handle detection of new devices for (HalMqttDetector detector : detectors) { - List detectedDevices = detector.parseTopic(topic, data); + List detectedDevices = detector.parseTopic(topicName, data); // Check if we already know the device if (!ObjectUtil.isEmpty(detectedDevices)) { for (HalMqttDeviceConfig detectedDeviceConfig : detectedDevices) { // Only handle unknown devices - if (!registeredDevices.contains(detectedDeviceConfig)) { + if (registeredDevices == null || !registeredDevices.contains(detectedDeviceConfig)) { HalDeviceData deviceData = detectedDeviceConfig.getDeviceData(data); if (deviceListeners != null) { @@ -150,9 +153,9 @@ public class HalMqttController implements HalAutostartController, MqttSubscripti if (deviceConfig instanceof HalMqttDeviceConfig) { HalMqttDeviceConfig mqttEvent = (HalMqttDeviceConfig) deviceConfig; - if (!topics.containsKey(mqttEvent.getTopic())) - topics.put(mqttEvent.getTopic(), new ArrayList<>()); - topics.get(mqttEvent.getTopic()).add(mqttEvent); + if (!topics.containsKey(mqttEvent.getTopicName())) + topics.put(mqttEvent.getTopicName(), new ArrayList<>()); + topics.get(mqttEvent.getTopicName()).add(mqttEvent); } else { throw new IllegalArgumentException( "Device config is not an instance of " + HalMqttDeviceConfig.class + ": " + deviceConfig.getClass()); @@ -163,8 +166,8 @@ public class HalMqttController implements HalAutostartController, MqttSubscripti public void deregister(HalDeviceConfig deviceConfig) { if (deviceConfig instanceof HalMqttDeviceConfig) { HalMqttDeviceConfig mqttEvent = (HalMqttDeviceConfig) deviceConfig; - if (topics.containsKey(mqttEvent.getTopic())) - topics.get(mqttEvent.getTopic()).remove(deviceConfig); + if (topics.containsKey(mqttEvent.getTopicName())) + topics.get(mqttEvent.getTopicName()).remove(deviceConfig); } } @@ -172,7 +175,7 @@ public class HalMqttController implements HalAutostartController, MqttSubscripti public void send(HalEventConfig eventConfig, HalEventData eventData) { if (eventConfig instanceof HalMqttDeviceConfig) { HalMqttDeviceConfig mqttEvent = (HalMqttDeviceConfig) eventConfig; - mqttBroker.publish(mqttEvent.getTopic(), Double.toString(eventData.getData()).getBytes()); + mqttBroker.publish(mqttEvent.getTopicName(), Double.toString(eventData.getData()).getBytes()); } else throw new IllegalArgumentException( "Device config is not an instance of " + HalMqttDeviceConfig.class + ": " + eventConfig.getClass()); diff --git a/plugins/hal-mqtt/src/se/hal/plugin/mqtt/detector/GenericMqttDetector.java b/plugins/hal-mqtt/src/se/hal/plugin/mqtt/detector/GenericMqttDetector.java index 229457af..6078bc75 100644 --- a/plugins/hal-mqtt/src/se/hal/plugin/mqtt/detector/GenericMqttDetector.java +++ b/plugins/hal-mqtt/src/se/hal/plugin/mqtt/detector/GenericMqttDetector.java @@ -51,7 +51,7 @@ public class GenericMqttDetector implements HalMqttDetector { if (config != null) { detectedDeviceConfigs.add(config); - return null; + return detectedDeviceConfigs; } } diff --git a/plugins/hal-mqtt/src/se/hal/plugin/mqtt/device/HalMqttDeviceConfig.java b/plugins/hal-mqtt/src/se/hal/plugin/mqtt/device/HalMqttDeviceConfig.java index f9c06d01..a166f71f 100644 --- a/plugins/hal-mqtt/src/se/hal/plugin/mqtt/device/HalMqttDeviceConfig.java +++ b/plugins/hal-mqtt/src/se/hal/plugin/mqtt/device/HalMqttDeviceConfig.java @@ -58,8 +58,8 @@ import java.util.Objects; public abstract class HalMqttDeviceConfig implements HalSensorConfig { - @Configurator.Configurable(value = "MQTT Topic") - private String topic; + @Configurator.Configurable(value = "MQTT Topic Name") + private String topicName; @Configurator.Configurable(value = "JSON Path", description = "If the value of the topic is a JSON then this parameter can be used to specify the path to the e.g. temperature value." + "
THe parameter uses the JSON-Path syntax where it always starts with $ and object fields can be accessed by . and a array element by [index]") private String jsonPath; @@ -68,27 +68,27 @@ public abstract class HalMqttDeviceConfig implements HalSensorConfig { public HalMqttDeviceConfig() {} /** - * @param topic is the topic associated to this device + * @param topicName is the topic associated to this device */ - public HalMqttDeviceConfig(String topic) { - this.topic = topic; + public HalMqttDeviceConfig(String topicName) { + this.topicName = topicName; } /** - * @param topic is the topic associated to this device. + * @param topicName is the topic associated to this device. * @param jsonPath indicates that the payload is of JSON format and the data should be extracted from this path. */ - public HalMqttDeviceConfig(String topic, String jsonPath) { - this.topic = topic; + public HalMqttDeviceConfig(String topicName, String jsonPath) { + this.topicName = topicName; this.jsonPath = jsonPath; } - public String getTopic() { - return topic; + public String getTopicName() { + return topicName; } - public void setTopic(String topic) { - this.topic = topic; + public void setTopicName(String topicName) { + this.topicName = topicName; } public String getJsonPath() { @@ -124,19 +124,19 @@ public abstract class HalMqttDeviceConfig implements HalSensorConfig { HalMqttDeviceConfig that = (HalMqttDeviceConfig) o; - if (!Objects.equals(topic, that.topic)) return false; + if (!Objects.equals(topicName, that.topicName)) return false; return Objects.equals(jsonPath, that.jsonPath); } @Override public int hashCode() { - int result = topic != null ? topic.hashCode() : 0; + int result = topicName != null ? topicName.hashCode() : 0; result = 31 * result + (jsonPath != null ? jsonPath.hashCode() : 0); return result; } @Override public String toString() { - return "Topic: " + topic + ", JSON Path: " + jsonPath; + return "Topic: " + topicName + ", JSON Path: " + jsonPath; } } diff --git a/plugins/hal-mqtt/test/se/hal/plugin/mqtt/detector/GenericMqttDetectorTest.java b/plugins/hal-mqtt/test/se/hal/plugin/mqtt/detector/GenericMqttDetectorTest.java index 7b1bfa38..a91570b3 100644 --- a/plugins/hal-mqtt/test/se/hal/plugin/mqtt/detector/GenericMqttDetectorTest.java +++ b/plugins/hal-mqtt/test/se/hal/plugin/mqtt/detector/GenericMqttDetectorTest.java @@ -1,6 +1,7 @@ package se.hal.plugin.mqtt.detector; import org.junit.Test; +import se.hal.plugin.mqtt.device.HalMqttDeviceConfig; import se.hal.plugin.mqtt.device.HalMqttHumidityDeviceConfig; import se.hal.plugin.mqtt.device.HalMqttParticularMatterDeviceConfig; import se.hal.plugin.mqtt.device.HalMqttTemperatureDeviceConfig; @@ -11,6 +12,7 @@ import se.hal.test.MockHalDeviceReportListener; import zutil.converter.Converter; import java.nio.charset.StandardCharsets; +import java.util.List; import static org.junit.Assert.*; @@ -19,123 +21,73 @@ public class GenericMqttDetectorTest { @Test public void ignoredTopics() { - MockHalDeviceReportListener listener = new MockHalDeviceReportListener(); GenericMqttDetector detector = new GenericMqttDetector(); - detector.addListener(listener); - - listener.reset(); - detector.parseTopic("", new byte[]{}); - assertEquals(0, listener.getNumberOfReports()); - - listener.reset(); - detector.parseTopic("invalid/topic", new byte[]{}); - assertEquals(0, listener.getNumberOfReports()); + assertEquals(0, detector.parseTopic("", new byte[]{}).size()); + assertEquals(0, detector.parseTopic("invalid/topic", new byte[]{}).size()); } @Test public void parseTemperature() { - MockHalDeviceReportListener listener = new MockHalDeviceReportListener(); GenericMqttDetector detector = new GenericMqttDetector(); - detector.addListener(listener); - - listener.reset(); - detector.parseTopic( + List devices = detector.parseTopic( "zigbee2mqtt/Kitchen air quality/temperature", Converter.toBytes(26)); - assertEquals(1, listener.getNumberOfReports()); + assertEquals(1, devices.size()); assertEquals( new HalMqttTemperatureDeviceConfig("zigbee2mqtt/Kitchen air quality/temperature"), - listener.getReport(0).config); - listener.getReport(0).data.setTimestamp(0); - assertEquals( - new TemperatureSensorData(26, 0), - listener.getReport(0).data); + devices.get(0)); - - listener.reset(); - detector.parseTopic( + devices = detector.parseTopic( "zigbee2mqtt/Kitchen air quality", "{\"temperature\": 26}".getBytes(StandardCharsets.UTF_8)); - assertEquals(1, listener.getNumberOfReports()); + assertEquals(1, devices.size()); assertEquals( new HalMqttTemperatureDeviceConfig("zigbee2mqtt/Kitchen air quality", "$.temperature"), - listener.getReport(0).config); - listener.getReport(0).data.setTimestamp(0); - assertEquals( - new TemperatureSensorData(26, 0), - listener.getReport(0).data); + devices.get(0)); } @Test public void parseHumidity() { - MockHalDeviceReportListener listener = new MockHalDeviceReportListener(); GenericMqttDetector detector = new GenericMqttDetector(); - detector.addListener(listener); - - listener.reset(); - detector.parseTopic( + List devices = detector.parseTopic( "zigbee2mqtt/Kitchen air quality/humidity", Converter.toBytes(51)); - assertEquals(1, listener.getNumberOfReports()); + assertEquals(1, devices.size()); assertEquals( new HalMqttHumidityDeviceConfig("zigbee2mqtt/Kitchen air quality/humidity"), - listener.getReport(0).config); - listener.getReport(0).data.setTimestamp(0); - assertEquals( - new HumiditySensorData(51, 0), - listener.getReport(0).data); + devices.get(0)); - - listener.reset(); - detector.parseTopic( + devices = detector.parseTopic( "zigbee2mqtt/Kitchen air quality", "{\"humidity\": 51}".getBytes(StandardCharsets.UTF_8)); - assertEquals(1, listener.getNumberOfReports()); + assertEquals(1, devices.size()); assertEquals( new HalMqttHumidityDeviceConfig("zigbee2mqtt/Kitchen air quality", "$.humidity"), - listener.getReport(0).config); - listener.getReport(0).data.setTimestamp(0); - assertEquals( - new HumiditySensorData(51, 0), - listener.getReport(0).data); + devices.get(0)); } @Test public void parseParticularMatter() { - MockHalDeviceReportListener listener = new MockHalDeviceReportListener(); GenericMqttDetector detector = new GenericMqttDetector(); - detector.addListener(listener); - - listener.reset(); - detector.parseTopic( + List devices = detector.parseTopic( "zigbee2mqtt/Kitchen air quality/pm25", Converter.toBytes(1)); - assertEquals(1, listener.getNumberOfReports()); + assertEquals(1, devices.size()); assertEquals( new HalMqttParticularMatterDeviceConfig("zigbee2mqtt/Kitchen air quality/pm25"), - listener.getReport(0).config); - listener.getReport(0).data.setTimestamp(0); - assertEquals( - new ParticulateMatterSensorData(1, 0), - listener.getReport(0).data); + devices.get(0)); - - listener.reset(); - detector.parseTopic( + devices = detector.parseTopic( "zigbee2mqtt/Kitchen air quality", "{\"pm25\": 1}".getBytes(StandardCharsets.UTF_8)); - assertEquals(1, listener.getNumberOfReports()); + assertEquals(1, devices.size()); assertEquals( new HalMqttParticularMatterDeviceConfig("zigbee2mqtt/Kitchen air quality", "$.pm25"), - listener.getReport(0).config); - listener.getReport(0).data.setTimestamp(0); - assertEquals( - new ParticulateMatterSensorData(1, 0), - listener.getReport(0).data); + devices.get(0)); } } \ No newline at end of file diff --git a/plugins/hal-mqtt/test/se/hal/plugin/mqtt/detector/Zigbee2mqttDetectorTest.java b/plugins/hal-mqtt/test/se/hal/plugin/mqtt/detector/Zigbee2mqttDetectorTest.java index dd6c6879..ab0064a5 100644 --- a/plugins/hal-mqtt/test/se/hal/plugin/mqtt/detector/Zigbee2mqttDetectorTest.java +++ b/plugins/hal-mqtt/test/se/hal/plugin/mqtt/detector/Zigbee2mqttDetectorTest.java @@ -1,10 +1,12 @@ package se.hal.plugin.mqtt.detector; import org.junit.Test; +import se.hal.plugin.mqtt.device.HalMqttDeviceConfig; import se.hal.plugin.mqtt.device.HalMqttParticularMatterDeviceConfig; import se.hal.test.MockHalDeviceReportListener; import java.nio.charset.StandardCharsets; +import java.util.List; import static org.junit.Assert.*; @@ -13,17 +15,13 @@ public class Zigbee2mqttDetectorTest { @Test public void ignoredTopics() { - MockHalDeviceReportListener listener = new MockHalDeviceReportListener(); Zigbee2mqttDetector detector = new Zigbee2mqttDetector(); - detector.addListener(listener); - listener.reset(); - detector.parseTopic("", new byte[]{}); - assertEquals(0, listener.getNumberOfReports()); + List devices = detector.parseTopic("", new byte[]{}); + assertEquals(0, devices.size()); - listener.reset(); - detector.parseTopic("invalid/topic", new byte[]{}); - assertEquals(0, listener.getNumberOfReports()); + devices = detector.parseTopic("invalid/topic", new byte[]{}); + assertEquals(0, devices.size()); } /* @Test