From ad26b51da625c8c908b9e35bf79273138a7a41eb Mon Sep 17 00:00:00 2001 From: Bertrik Sikken Date: Sun, 7 Mar 2021 14:59:14 +0100 Subject: [PATCH] Add possiblity to specify TTN stack version (V2 or V3) --- .../sikken/bertrik/ITtnHabBridgeConfig.java | 7 +++++ .../java/nl/sikken/bertrik/TtnHabBridge.java | 30 ++++++++++--------- .../nl/sikken/bertrik/TtnHabBridgeConfig.java | 8 +++++ .../bertrik/hab/ttn/ETtnStackVersion.java | 19 ++++++++++++ .../sikken/bertrik/hab/ttn/TtnListener.java | 7 +++-- 5 files changed, 54 insertions(+), 17 deletions(-) create mode 100644 ttnhabbridge/src/main/java/nl/sikken/bertrik/hab/ttn/ETtnStackVersion.java diff --git a/ttnhabbridge/src/main/java/nl/sikken/bertrik/ITtnHabBridgeConfig.java b/ttnhabbridge/src/main/java/nl/sikken/bertrik/ITtnHabBridgeConfig.java index d6c9a18..1ac7e7e 100644 --- a/ttnhabbridge/src/main/java/nl/sikken/bertrik/ITtnHabBridgeConfig.java +++ b/ttnhabbridge/src/main/java/nl/sikken/bertrik/ITtnHabBridgeConfig.java @@ -2,6 +2,8 @@ package nl.sikken.bertrik; import java.time.Duration; +import nl.sikken.bertrik.hab.ttn.ETtnStackVersion; + /** * Configuration interface for the application. */ @@ -22,6 +24,11 @@ public interface ITtnHabBridgeConfig { */ String getTtnMqttUrl(); + /** + * @return the version of the TTN stack, either v2 or v3 + */ + ETtnStackVersion getTtnStackVersion(); + /** * @return the application id of the TTN application */ diff --git a/ttnhabbridge/src/main/java/nl/sikken/bertrik/TtnHabBridge.java b/ttnhabbridge/src/main/java/nl/sikken/bertrik/TtnHabBridge.java index 4a8a64a..ccae10d 100644 --- a/ttnhabbridge/src/main/java/nl/sikken/bertrik/TtnHabBridge.java +++ b/ttnhabbridge/src/main/java/nl/sikken/bertrik/TtnHabBridge.java @@ -44,7 +44,7 @@ public final class TtnHabBridge { * Main application entry point. * * @param arguments application arguments (none taken) - * @throws IOException in case of a problem reading a config file + * @throws IOException in case of a problem reading a config file * @throws MqttException in case of a problem starting MQTT client */ public static void main(String[] arguments) throws IOException, MqttException { @@ -65,10 +65,9 @@ public final class TtnHabBridge { * @param config the application configuration */ private TtnHabBridge(ITtnHabBridgeConfig config) { - this.ttnListener = new TtnListener(this::handleTTNMessage, - config.getTtnMqttUrl(), config.getTtnAppId(), config.getTtnAppKey()); - IHabitatRestApi restApi = - HabitatUploader.newRestClient(config.getHabitatUrl(), config.getHabitatTimeout()); + this.ttnListener = new TtnListener(this::handleTTNMessage, config.getTtnMqttUrl(), config.getTtnStackVersion(), + config.getTtnAppId(), config.getTtnAppKey()); + IHabitatRestApi restApi = HabitatUploader.newRestClient(config.getHabitatUrl(), config.getHabitatTimeout()); this.habUploader = new HabitatUploader(restApi); this.decoder = new PayloadDecoder(EPayloadEncoding.parse(config.getTtnPayloadEncoding())); this.gwCache = new ExpiringCache(config.getTtnGwCacheExpiry()); @@ -91,22 +90,24 @@ public final class TtnHabBridge { /** * Handles an incoming TTN message + * * @param textMessage the message contents - * @param now message arrival time + * @param now message arrival time */ private void handleTTNMessage(TtnUplinkMessage message) { Instant now = Instant.now(); try { // decode from JSON if (message.isRetry()) { - // skip "retry" messages, they contain duplicate data with a misleading time stamp + // skip "retry" messages, they contain duplicate data with a misleading time + // stamp LOG.warn("Ignoring 'retry' message"); return; } Sentence sentence = decoder.decode(message); String line = sentence.format(); - - // collect list of listeners + + // collect list of listeners List receivers = new ArrayList<>(); for (GatewayInfo gw : message.getGateways()) { String gwName = gw.getId(); @@ -114,7 +115,8 @@ public final class TtnHabBridge { HabReceiver receiver = new HabReceiver(gwName, gwLocation); receivers.add(receiver); - // send listener data only if it has a valid location and hasn't been sent recently + // send listener data only if it has a valid location and hasn't been sent + // recently if (gwLocation.isValid() && gwCache.add(gwName, now)) { habUploader.scheduleListenerDataUpload(receiver, now); } @@ -125,8 +127,8 @@ public final class TtnHabBridge { } catch (DecodeException e) { LOG.warn("Payload decoding exception: {}", e.getMessage()); } catch (Exception e) { - LOG.trace("Caught unhandled exception", e); - LOG.error("Caught unhandled exception:" + e.getMessage()); + LOG.trace("Caught unhandled exception", e); + LOG.error("Caught unhandled exception:" + e.getMessage()); } } @@ -141,7 +143,7 @@ public final class TtnHabBridge { habUploader.stop(); LOG.info("Stopped TTN HAB bridge application"); } - + /** * Handles uncaught exceptions: log it and stop the application. * @@ -152,7 +154,7 @@ public final class TtnHabBridge { LOG.error("Caught unhandled exception, application will be stopped ...", e); stop(); } - + private static ITtnHabBridgeConfig readConfig(File file) throws IOException { TtnHabBridgeConfig config = new TtnHabBridgeConfig(); try (FileInputStream fis = new FileInputStream(file)) { diff --git a/ttnhabbridge/src/main/java/nl/sikken/bertrik/TtnHabBridgeConfig.java b/ttnhabbridge/src/main/java/nl/sikken/bertrik/TtnHabBridgeConfig.java index 7aa1f6c..70abae1 100644 --- a/ttnhabbridge/src/main/java/nl/sikken/bertrik/TtnHabBridgeConfig.java +++ b/ttnhabbridge/src/main/java/nl/sikken/bertrik/TtnHabBridgeConfig.java @@ -2,6 +2,8 @@ package nl.sikken.bertrik; import java.time.Duration; +import nl.sikken.bertrik.hab.ttn.ETtnStackVersion; + /** * Configuration class. */ @@ -15,6 +17,7 @@ final class TtnHabBridgeConfig extends BaseConfig implements ITtnHabBridgeConfig HABITAT_TIMEOUT_MS("habitat.timeout", "5000", "Timeout in milliseconds"), TTN_MQTT_URL("ttn.mqtt.url", "tcp://eu.thethings.network", "URL of the TTN MQTT server"), + TTN_VERSION("ttn.version", "V2", "TTN stack version, V2 or V3"), TTN_APP_ID("ttn.app.id", "habhub", "TTN Application Id (e.g. habhub, ttnmapper, etc.)"), TTN_APP_KEY("ttn.app.key", "ttn-account-v2.Sh49WL90oQz-ZuxoDrS6yKuACL_jtAA0agdDfO_eVj4", "TTN Application key"), TTN_GW_CACHE_EXPIRY_SEC("ttn.gwcache.expiry", "600", "Gateway cache expiration time (seconds)"), @@ -59,6 +62,11 @@ final class TtnHabBridgeConfig extends BaseConfig implements ITtnHabBridgeConfig return get(EConfigItem.TTN_MQTT_URL.key); } + @Override + public ETtnStackVersion getTtnStackVersion() { + return ETtnStackVersion.valueOf(get(EConfigItem.TTN_VERSION.key)); + } + @Override public String getTtnAppId() { return get(EConfigItem.TTN_APP_ID.key); diff --git a/ttnhabbridge/src/main/java/nl/sikken/bertrik/hab/ttn/ETtnStackVersion.java b/ttnhabbridge/src/main/java/nl/sikken/bertrik/hab/ttn/ETtnStackVersion.java new file mode 100644 index 0000000..126ba96 --- /dev/null +++ b/ttnhabbridge/src/main/java/nl/sikken/bertrik/hab/ttn/ETtnStackVersion.java @@ -0,0 +1,19 @@ +package nl.sikken.bertrik.hab.ttn; + +/** + * The TTN stack version, used for example to differentiate between MQTT conventions. + */ +public enum ETtnStackVersion { + V2(""), + V3("v3/"); + + private final String prefix; + + ETtnStackVersion(String prefix) { + this.prefix = prefix; + } + + String getPrefix() { + return prefix; + } +} diff --git a/ttnhabbridge/src/main/java/nl/sikken/bertrik/hab/ttn/TtnListener.java b/ttnhabbridge/src/main/java/nl/sikken/bertrik/hab/ttn/TtnListener.java index b7fc38b..5af0231 100644 --- a/ttnhabbridge/src/main/java/nl/sikken/bertrik/hab/ttn/TtnListener.java +++ b/ttnhabbridge/src/main/java/nl/sikken/bertrik/hab/ttn/TtnListener.java @@ -36,7 +36,7 @@ public final class TtnListener { * @param appId the user name * @param appKey the password */ - public TtnListener(IMessageReceived callback, String url, String appId, String appKey) { + public TtnListener(IMessageReceived callback, String url, ETtnStackVersion version, String appId, String appKey) { LOG.info("Creating client for MQTT server '{}' for app '{}'", url, appId); try { this.mqttClient = new MqttClient(url, MqttClient.generateClientId(), new MemoryPersistence()); @@ -44,7 +44,8 @@ public final class TtnListener { throw new IllegalArgumentException(e); } this.callback = callback; - mqttClient.setCallback(new MqttCallbackHandler(mqttClient, "+/devices/+/up", this::handleMessage)); + mqttClient.setCallback( + new MqttCallbackHandler(mqttClient, version.getPrefix() + "+/devices/+/up", this::handleMessage)); // create connect options options = new MqttConnectOptions(); @@ -145,7 +146,7 @@ public final class TtnListener { try { client.subscribe(topic); } catch (MqttException e) { - LOG.error("Caught exception while subscribing!"); + LOG.error("Caught exception while subscribing!", e); } } }