diff --git a/pom.xml b/pom.xml
index 86c8139..07889be 100644
--- a/pom.xml
+++ b/pom.xml
@@ -30,6 +30,11 @@
4.13.2
test
+
+ org.java-websocket
+ Java-WebSocket
+ 1.5.6
+
diff --git a/src/main/java/eu/m724/wtapi/thunder/ThunderProvider.java b/src/main/java/eu/m724/wtapi/thunder/ThunderProvider.java
new file mode 100644
index 0000000..cdf50be
--- /dev/null
+++ b/src/main/java/eu/m724/wtapi/thunder/ThunderProvider.java
@@ -0,0 +1,36 @@
+package eu.m724.wtapi.thunder;
+
+import java.util.concurrent.Callable;
+import java.util.function.Consumer;
+
+import eu.m724.wtapi.object.Coordinates;
+import eu.m724.wtapi.provider.exception.ProviderException;
+
+public abstract class ThunderProvider {
+
+ public abstract void init() throws ProviderException;
+
+ /**
+ * connects to remote server and starts processing data
+ * @throws ProviderException
+ */
+ public abstract void start() throws ProviderException;
+
+ /**
+ * disconnects from remote server
+ */
+ public abstract void stop();
+
+ /**
+ * check for new data and call callback and stuff
+ */
+ public abstract void tick();
+
+ public abstract void registerStrikeHandler(Consumer runnable);
+
+ /**
+ * delay between irl strike and receiving of data
+ * @return delay in ms
+ */
+ public abstract int getDelay();
+}
diff --git a/src/main/java/eu/m724/wtapi/thunder/impl/lightningmaps/LMWebsocketClient.java b/src/main/java/eu/m724/wtapi/thunder/impl/lightningmaps/LMWebsocketClient.java
new file mode 100644
index 0000000..702afd9
--- /dev/null
+++ b/src/main/java/eu/m724/wtapi/thunder/impl/lightningmaps/LMWebsocketClient.java
@@ -0,0 +1,71 @@
+package eu.m724.wtapi.thunder.impl.lightningmaps;
+
+import java.net.URI;
+
+import org.java_websocket.client.WebSocketClient;
+import org.java_websocket.handshake.ServerHandshake;
+
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
+import com.google.gson.JsonParser;
+import com.google.gson.JsonSyntaxException;
+
+import eu.m724.wtapi.object.Coordinates;
+
+class LMWebsocketClient extends WebSocketClient {
+ LightningMapsProvider lightningMapsProvider;
+
+ private static URI[] uris = new URI[] {
+ URI.create("wss://live.lightningmaps.org/"),
+ URI.create("wss://live2.lightningmaps.org/")
+ };
+ private int currentUri = 0;
+
+ public LMWebsocketClient(LightningMapsProvider lightningMapsProvider) {
+ super(uris[0]);
+ this.lightningMapsProvider = lightningMapsProvider;
+ }
+
+ @Override
+ public void onOpen(ServerHandshake handshakedata) {
+ this.send("{\"v\":24,\"i\":{},\"s\":false,\"x\":0,\"w\":4,\"tx\":0,\"tw\":3,\"a\":4,\"z\":2,\"b\":true,\"h\":\"#m=oss;t=3;s=0;o=0;b=;ts=0;z=2;y=45.4601;x=17.5814;d=2;dl=2;dc=0;\",\"l\":5,\"t\":5,\"from_lightningmaps_org\":true,\"p\":[87,309,-72.5,-273.9],\"r\":\"re\"}");
+ }
+
+ @Override
+ public void onMessage(String message) {
+ try {
+ JsonObject json =
+ JsonParser.parseString(message)
+ .getAsJsonObject();
+
+ if (!json.has("strokes")) return;
+
+ json.getAsJsonArray("strokes").forEach(ele -> {
+ long time = ele.getAsJsonObject().getAsJsonPrimitive("time").getAsLong();
+ double lat = ele.getAsJsonObject().getAsJsonPrimitive("lat").getAsDouble();
+ double lon = ele.getAsJsonObject().getAsJsonPrimitive("lon").getAsDouble();
+
+ Coordinates coordinates = new Coordinates(lat, lon);
+ lightningMapsProvider.submitStrike(coordinates, time);
+ });
+ } catch (JsonSyntaxException e) {
+ // ignore invalid json
+ }
+
+ }
+
+ @Override
+ public void onClose(int code, String reason, boolean remote) {
+ System.out.printf("%s Closed: %d %s\n", this.uri, code, reason);
+ this.uri = uris[++currentUri % 2];
+ lightningMapsProvider.reconnect();
+
+ }
+
+ @Override
+ public void onError(Exception ex) {
+ this.uri = uris[++currentUri % 2];
+ lightningMapsProvider.reconnect();
+ }
+
+}
diff --git a/src/main/java/eu/m724/wtapi/thunder/impl/lightningmaps/LightningMapsProvider.java b/src/main/java/eu/m724/wtapi/thunder/impl/lightningmaps/LightningMapsProvider.java
new file mode 100644
index 0000000..d4d57fc
--- /dev/null
+++ b/src/main/java/eu/m724/wtapi/thunder/impl/lightningmaps/LightningMapsProvider.java
@@ -0,0 +1,85 @@
+package eu.m724.wtapi.thunder.impl.lightningmaps;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Queue;
+import java.util.concurrent.CopyOnWriteArrayList;
+import java.util.concurrent.DelayQueue;
+import java.util.function.Consumer;
+
+import eu.m724.wtapi.object.Coordinates;
+import eu.m724.wtapi.object.Weather;
+import eu.m724.wtapi.provider.exception.ProviderException;
+import eu.m724.wtapi.thunder.ThunderProvider;
+
+public class LightningMapsProvider extends ThunderProvider {
+ LMWebsocketClient websocketClient = new LMWebsocketClient(this);
+ ArrayList> strikeHandlers = new ArrayList<>();
+ CopyOnWriteArrayList strikes = new CopyOnWriteArrayList<>(); // TODO optimize?
+
+ private long reconnectPending;
+ private final int delay = 10000;
+
+ @Override
+ public void init() throws ProviderException {
+ // TODO Auto-generated method stub
+
+ }
+
+ @Override
+ public void start() throws ProviderException {
+ try {
+ websocketClient.connectBlocking();
+ } catch (InterruptedException e) {
+ throw new ProviderException("unexpected interruptedexception");
+ }
+ }
+
+ @Override
+ public void stop() {
+ try {
+ websocketClient.closeBlocking();
+ } catch (InterruptedException e) {
+ websocketClient.close();
+ }
+ }
+
+ @Override
+ public void registerStrikeHandler(Consumer runnable) {
+ strikeHandlers.add(runnable);
+ }
+
+ @Override
+ public int getDelay() {
+ return this.delay;
+ }
+
+ @Override
+ public void tick() {
+ long now = System.currentTimeMillis();
+
+ if (reconnectPending > 0) {
+ if (now > reconnectPending) {
+ websocketClient.reconnect();
+ reconnectPending = 0;
+ }
+ }
+
+ for (TimedStrike strike : strikes) {
+ if (strike.timestamp > now)
+ break;
+ strikeHandlers.forEach(con -> con.accept(strike.coordinates));
+ strikes.remove(0);
+ }
+ }
+
+ void reconnect() {
+ System.out.println("reconnecting in 5 seconds");
+ reconnectPending = System.currentTimeMillis() + 3000;
+ }
+
+ void submitStrike(Coordinates coordinates, long timestamp) {
+ strikes.add(new TimedStrike(timestamp, coordinates));
+ }
+
+}
diff --git a/src/main/java/eu/m724/wtapi/thunder/impl/lightningmaps/TimedStrike.java b/src/main/java/eu/m724/wtapi/thunder/impl/lightningmaps/TimedStrike.java
new file mode 100644
index 0000000..240d092
--- /dev/null
+++ b/src/main/java/eu/m724/wtapi/thunder/impl/lightningmaps/TimedStrike.java
@@ -0,0 +1,13 @@
+package eu.m724.wtapi.thunder.impl.lightningmaps;
+
+import eu.m724.wtapi.object.Coordinates;
+
+public class TimedStrike {
+ public long timestamp;
+ public Coordinates coordinates;
+
+ public TimedStrike(long timestamp, Coordinates coordinates) {
+ this.timestamp = timestamp;
+ this.coordinates = coordinates;
+ }
+}
diff --git a/src/test/java/eu/m724/wtapi/TestLM.java b/src/test/java/eu/m724/wtapi/TestLM.java
new file mode 100644
index 0000000..050f23c
--- /dev/null
+++ b/src/test/java/eu/m724/wtapi/TestLM.java
@@ -0,0 +1,34 @@
+package eu.m724.wtapi;
+
+import java.util.ArrayList;
+
+import org.junit.Test;
+
+import eu.m724.wtapi.object.Coordinates;
+import eu.m724.wtapi.thunder.ThunderProvider;
+import eu.m724.wtapi.thunder.impl.lightningmaps.LightningMapsProvider;
+
+public class TestLM {
+ @Test
+ public void lightningMapsTest() throws InterruptedException {
+ ArrayList coordinatesList = new ArrayList<>();
+
+ ThunderProvider provider = new LightningMapsProvider();
+
+ provider.registerStrikeHandler(coordinates ->
+ coordinatesList.add(coordinates));
+
+ provider.init();
+
+ provider.start();
+
+ for (int i=0; i < 100; i++) {
+ provider.tick();
+ Thread.sleep(100);
+ }
+
+ System.out.printf("Strikes in the last 10s: %d\n", coordinatesList.size());
+ System.out.printf("%f %f", coordinatesList.get(0).latitude, coordinatesList.get(0).longitude);
+
+ }
+}