diff --git a/.env b/.env
new file mode 100644
index 0000000..83ab5b9
--- /dev/null
+++ b/.env
@@ -0,0 +1,7 @@
+APIHOST=www.example.com
+APIURL=/track/api.php
+# The APIKEY from the things network
+APIKEY=xxx
+DBNAME=track
+DBUSER=track
+DBPASS=track
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..d962d21
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,2 @@
+.idea
+.env.*
diff --git a/api.php b/api.php
new file mode 100644
index 0000000..1276545
--- /dev/null
+++ b/api.php
@@ -0,0 +1,34 @@
+write();
+ Logging::info('uplink', $headers, $raw);
+ } else {
+ db_logging($_GET['type'] ?? 'error', $raw, $headers);
+ Logging::info($_GET['type'] || 'error', $headers);
+ }
+ } catch (Throwable $e) {
+ error_log($e->getMessage() . ' - see also /tmp/track-error.log');
+ Logging::info('error', $e->getMessage(), $headers, $_GET, $_POST, json_decode($raw));
+ }
+} else {
+ Logging::info('error', "Not allowed", $headers, $_GET, $_POST, json_decode($raw));
+}
+
+header("Content-Type: application/json", true, 200);
+echo "true";
+exit(0);
diff --git a/bootstrap.php b/bootstrap.php
new file mode 100644
index 0000000..54054ef
--- /dev/null
+++ b/bootstrap.php
@@ -0,0 +1,51 @@
+ PDO::ERRMODE_EXCEPTION,
+ ]
+ );
+ }
+ return self::$_pdo;
+ }
+}
+$pdo = Config::dbConnection();
diff --git a/gpx.php b/gpx.php
new file mode 100644
index 0000000..9f09c21
--- /dev/null
+++ b/gpx.php
@@ -0,0 +1,13 @@
+exec("CREATE TABLE IF NOT EXISTS logging(
+ id SERIAL PRIMARY KEY,
+ request_type VARCHAR,
+ inserted_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL,
+ raw JSON,
+ headers JSON,
+ _get JSON,
+ _post JSON
+ )");
+ $stmt = $pdo->prepare("INSERT INTO logging(raw, request_type, headers, _get, _post) VALUES(?, ?, ?, ?, ?)");
+ $stmt->execute([$raw_data, $_GET['type'] ?? 'NN', json_encode($headers), json_encode($_GET), json_encode($_POST)]);
+}
+
+class UplinkData
+{
+
+ public $raw;
+
+ public $altitude;
+ public $longitude;
+ public $latitude;
+ public $uplink_token;
+ public $request_type = 'uplink';
+ public $_get;
+ public $_post;
+ public $_headers;
+ public $time;
+ public $device_id;
+
+ public function __construct(string $raw, array $headers)
+ {
+ $this->raw = $raw;
+ $data = json_decode($raw);
+
+ $this->device_id = $data->end_device_ids->device_id;
+ $this->time = $data->received_at;
+ $this->_headers = json_encode($headers);
+ $this->_post = json_encode($_POST);
+ $this->_get = json_encode($_GET);
+ $this->uplink_token = $data->uplink_message->rx_metadata[0]->uplink_token;
+ $this->latitude = $data->uplink_message->rx_metadata[0]->location->latitude;
+ $this->longitude = $data->uplink_message->rx_metadata[0]->location->longitude;
+ $this->altitude = $data->uplink_message->rx_metadata[0]->location->altitude;
+ #$this->latitude = $data->uplink_message->decoded_payload->latitude;
+ #$this->altitude = $data->uplink_message->decoded_payload->altitude;
+ #$this->longitude = $data->uplink_message->decoded_payload->longitude;
+ }
+
+ public function write()
+ {
+ $pdo = Config::dbConnection();
+ $stmt = $pdo->prepare("INSERT INTO tracks
+ (
+ raw, device_id, _get, _post, _headers, latitude,
+ longitude, altitude, time, request_type, uplink_token
+ )
+ VALUES(
+ ?, ?, ?, ?, ?, ?,
+ ?, ?, ?, ?, ?
+ )");
+ $args = [
+ $this->raw,
+ $this->device_id,
+ $this->_get,
+ $this->_post,
+ $this->_headers,
+ $this->latitude,
+ $this->longitude,
+ $this->altitude,
+ $this->time,
+ $this->request_type,
+ $this->uplink_token,
+ ];
+ $stmt->execute($args);
+ }
+
+ public static function get(string $device_id = '', string $from = '', string $to = ''): array
+ {
+ $pdo = Config::dbConnection();
+ $from = $from ?: $_GET['from'] ?: date('Y-m-d H:i:s', time() - 3600 * 24);
+ $to = $to ?: date('Y-m-d H:i:s');
+ $sql = "SELECT * FROM tracks WHERE request_type = 'uplink' AND time BETWEEN ? AND ?";
+ $args = [$from, $to];
+ if ($device_id) {
+ $sql .= " AND device_id = ?";
+ $args[] = $device_id;
+ }
+ $stmt = $pdo->prepare($sql);
+ $res = $stmt->execute($args);
+ $ret = [];
+ if ($res) {
+ $ret = [];
+ foreach ($stmt->fetchAll(PDO::FETCH_OBJ) as $_) {
+ if(!$_->device_id){
+ continue;
+ }
+ if ( ! isset($ret[$_->device_id])) {
+ $ret[$_->device_id] = [];
+ }
+ $ret[$_->device_id][] = $_;
+ }
+ }
+
+ return $ret;
+ }
+
+ public static function makePositions(string $device_id = '',string $from = '', string $to='')
+ {
+ $data = self::get($device_id, $from, $to);
+ $tracks = [];
+ if ($data) {
+ foreach ($data as $device_id => $resultset) {
+ if(!isset($tracks[$device_id])){$tracks[$device_id] = [];}
+
+ foreach ($resultset as $res) {
+ $raw = json_decode($res->raw);
+ list($lat,$lon) = static::decodePayload($raw, $human);
+ $tracks[$device_id][] = ['received_at' => $raw->received_at, 'lat' => $lat, 'lon' => $lon,];
+ }
+ }
+ }
+
+ return [$tracks, $human];
+
+ }
+
+ public static function makeGPX(string $device_id = '', string $from = '', string $to = ''): string
+ {
+ $root = ''
+ . ''
+ . 'Track - %s bis %sTrack haltHolguin'
+ . '%s'
+ . '';
+ $track = 'Trackname %sTrackbeschreibung %s'
+ . ''
+ . '%s'
+ . ''
+ . '';
+ $data = self::get($device_id, $from, $to);
+ $tracks = [];
+ if ($data) {
+ foreach ($data as $device_id => $resultset) {
+
+ $inner = [];
+ foreach ($resultset as $res) {
+ $raw = json_decode($res->raw);
+ list($lat,$lon) = static::decodePayload($raw);
+ # $inner[] = sprintf('%s', $res->latitude, $res->longitude, $res->altitude, $res->time);
+ $inner[] = sprintf('%s', $lat, $lon, 0, $raw->received_at);
+ }
+ $tracks[] = sprintf($track, $device_id, $device_id, join("\n", $inner));
+ }
+
+ return sprintf($root,$from, $to, join("\n", $tracks));
+ }
+ return '';
+ }
+ public static function decodePayload($raw, &$human=[])
+ {
+ $payload=$raw->uplink_message->frm_payload;
+ $human = file_get_contents('https://1m2m.eu/services/GETPAYLOAD?Human=0&PL='.bin2hex(base64_decode($payload)));
+ $human = json_decode($human);
+ if(isset($human->Lat) && isset($human->Lon)){
+ return [str_replace(',','.',$human->Lat),str_replace(',','.',$human->Lon)];
+ }
+ return [0,0];
+
+ }
+
+}
+
+
+class Logging
+{
+ public static function info($type, ...$_)
+ {
+ global $verbose;
+ if ($verbose || $type == 'error') {
+ file_put_contents("/tmp/track-$type.log", json_encode($_) . "\n", FILE_APPEND);
+ }
+ }
+}
+
+if ( ! function_exists('getallheaders')) {
+ function getallheaders(): array
+ {
+ $headers = [
+ 'Content-type' => 'application/json',
+ "X-Tts-Domain" => '',
+ ];
+ foreach ($_SERVER as $name => $value) {
+ if ($name != 'HTTP_MOD_REWRITE' && (substr($name, 0, 5) == 'HTTP_' || $name == 'CONTENT_LENGTH' || $name == 'CONTENT_TYPE')) {
+ $name = str_replace(' ', '-', ucwords(strtolower(str_replace('_', ' ', str_replace('HTTP_', '', $name)))));
+ if ($name == 'Content-Type') {
+ $name = 'Content-type';
+ }
+ $headers[$name] = $value;
+ }
+ }
+
+ return $headers;
+ }
+}
diff --git a/utils b/utils
new file mode 100755
index 0000000..7622784
--- /dev/null
+++ b/utils
@@ -0,0 +1,2 @@
+psq="sudo -upostgres psql track -c"
+$psq "select count(*) from tracks WHERE request_type = 'uplink'"