Compare commits

17 Commits

Author SHA256 Message Date
165c79e67b improve systemd unit file 2025-03-23 14:58:35 +01:00
c0ac71ebba fix: edit wrong .gitignore :D 2025-03-22 10:22:19 +01:00
10e8e14cd6 feat: add docker compose to gitignore 2025-03-22 10:20:42 +01:00
a9c8525e6e Merge pull request 'Add timestamps of locations providers' (#18) from feat/timestamps into main
Reviewed-on: #18
Reviewed-by: Philipp Schweizer <Philipp.schw@directbox.com>
2025-02-14 21:22:57 +01:00
39c07fcef0 refactor: only search once for wifiMessage 2025-02-14 21:06:47 +01:00
52d521a6ad rename var 2025-02-08 21:24:40 +01:00
d3848ac1aa Merge branch 'main' into feat/timestamps 2025-02-08 20:54:50 +01:00
93f0c71a6c set wifi timestamp 2025-02-08 20:54:06 +01:00
5e4fd59148 refactor to use either timestamp when one is undefined 2025-02-02 20:10:29 +01:00
62a2dc2c4a fix gnss timestamp 2025-01-29 23:03:48 +01:00
59dc57a618 set gnss timestamp in location 2025-01-28 22:12:48 +01:00
452589d11d add timestamps in DB model 2025-01-28 21:55:54 +01:00
d04bdb3ac1 Merge pull request 'Add Prometheus metrics endpoint' (#14) from feat/prometheus into main
Reviewed-on: #14
Reviewed-by: Philipp Schweizer <Philipp.schw@directbox.com>
2025-01-26 10:03:28 +01:00
a745aaf9d0 add timestamps to DB model 2025-01-25 21:51:42 +01:00
51112b5870 update readme 2025-01-25 19:14:18 +01:00
262b4718fc Merge branch 'main' into feat/prometheus 2025-01-25 19:10:09 +01:00
49fe1b4ce3 README.md aktualisiert 2025-01-19 13:35:53 +01:00
10 changed files with 78 additions and 32 deletions

1
.gitignore vendored Normal file
View File

@ -0,0 +1 @@
docker-compose.yml

View File

@ -11,10 +11,11 @@ We recommend using [The Things Network](https://www.thethingsnetwork.org/) (TTN)
- [Database](#database) - [Database](#database)
- [Configuration](#configuration) - [Configuration](#configuration)
2. [TTN Integration](#ttn-integration) 2. [TTN Integration](#ttn-integration)
3. [Add a Location Tracker](#add-a-location-tracker) 3. [Prometheus Metrics](#prometheus-metrics)
4. [Add a Location Tracker](#add-a-location-tracker)
- [Onboard SenseCAP T1000-B](#onboard-sensecap-t1000-b) - [Onboard SenseCAP T1000-B](#onboard-sensecap-t1000-b)
- [Register SenseCAP T1000-B](#register-sensecap-t1000-b) - [Register SenseCAP T1000-B](#register-sensecap-t1000-b)
4. [Testing](#testing) 5. [Testing](#testing)
- [Testing Webhook](#testing-webhook) - [Testing Webhook](#testing-webhook)
- [Emulating Wigle API](#emulating-wigle-api) - [Emulating Wigle API](#emulating-wigle-api)
@ -49,9 +50,15 @@ Add a addidtional header:
- Type: `authorization` - Type: `authorization`
- Value: `Bearer your-very-secure-token-from-the-env-file` - Value: `Bearer your-very-secure-token-from-the-env-file`
### Prometheus Metrics
Use `https://your.domain.tld/api/metrics` to retrieve useful insides for monitoring.
## Add a Location Tracker ## Add a Location Tracker
We use the [SenseCAP T1000-B](https://www.seeedstudio.com/SenseCAP-Card-Tracker-T1000-B-p-5698.html) from seeedstudio because of the fair price and multiple location providers. However, you can use any LoRaWAN-enabled tracker that is compatible with TTN and supports the required payload fields. We use the [SenseCAP T1000-B](https://www.seeedstudio.com/SenseCAP-Card-Tracker-T1000-B-p-5698.html) from seeedstudio because of the fair price and multiple location providers. However, you can use any LoRaWAN-enabled tracker that is compatible with TTN and supports the required payload fields.
## Troubleshooting
Run `journalctl -u locationhub.service -f` to see log output.
### Onboard SenseCAP T1000-B ### Onboard SenseCAP T1000-B
1. Download and install the App [SenseCraft](https://play.google.com/store/apps/details?id=cc.seeed.sensecapmate) 1. Download and install the App [SenseCraft](https://play.google.com/store/apps/details?id=cc.seeed.sensecapmate)
2. Skip the user account at startup with `Skip` in the upper right corner 2. Skip the user account at startup with `Skip` in the upper right corner
@ -60,7 +67,7 @@ We use the [SenseCAP T1000-B](https://www.seeedstudio.com/SenseCAP-Card-Tracker-
5. In the `Settings` tab select `The Things Network` as Platform under `LoRa` 5. In the `Settings` tab select `The Things Network` as Platform under `LoRa`
6. Copy `Device EUI`, `AppEUI` and `AppKey` 6. Copy `Device EUI`, `AppEUI` and `AppKey`
7. Save LoRa settings 7. Save LoRa settings
8. Under `Geolocation` select Geolocation Strategy as `Bluetooth+Wi-Fi+GNSS` 8. Under `Geolocation` select Geolocation Strategy as `GNSS+Wi-Fi`
9. Set `GNSS Max Scan Time (s)` to 120 9. Set `GNSS Max Scan Time (s)` to 120
10. Save Geolocation settings 10. Save Geolocation settings
11. Exit App with return arrow to trigger re-init of SenseCAP T1000-B 11. Exit App with return arrow to trigger re-init of SenseCAP T1000-B

3
server/.gitignore vendored
View File

@ -309,6 +309,3 @@ cython_debug/
*.vsix *.vsix
config.py config.py
#docker
docker-compose.yml

View File

@ -1,22 +1,29 @@
[Unit] [Unit]
Description=LocationHub Description=LocationHub Service
Documentation=https://git.mosad.xyz/localhorst/LocationHub
After=network.target systemd-networkd-wait-online.service mysqld.service After=network.target systemd-networkd-wait-online.service mysqld.service
[Service] [Service]
Type=simple Type=simple
User=locationhub User=locationhub
Group=locationhub
WorkingDirectory=/home/locationhub/git/LocationHub/server/ WorkingDirectory=/home/locationhub/git/LocationHub/server/
ExecStart=/usr/bin/npm run dev
# Combine commands for build and start
ExecStart=/bin/bash -c "/usr/bin/npm run build && /usr/bin/npm run start"
# Restart policies
Restart=on-failure Restart=on-failure
StandardOutput=append:/var/log/LocationHub.log RestartSec=5s
StandardError=append:/var/log/LocationHub.log
# Logging configuration
StandardOutput=journal
StandardError=journal
SyslogIdentifier=locationhub
# Resource control (optional but helps stability)
MemoryLimit=512M
CPUQuota=50%
[Install] [Install]
WantedBy=multi-user.target WantedBy=multi-user.target
```
Activate Systemd Job
```
systemctl daemon-reload
systemctl enable locationhub.service
systemctl start locationhub.service
systemctl status locationhub.service

View File

@ -5,7 +5,7 @@ CREATE TABLE IF NOT EXISTS lp_ttn_end_device_uplinks (
dev_eui VARCHAR(255), dev_eui VARCHAR(255),
join_eui VARCHAR(255), join_eui VARCHAR(255),
dev_addr VARCHAR(255), dev_addr VARCHAR(255),
received_at_utc DATE, received_at_utc DATE NOT NULL,
battery NUMERIC, battery NUMERIC,
created_at_utc TIMESTAMP DEFAULT CURRENT_TIMESTAMP, created_at_utc TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at_utc TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP updated_at_utc TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
@ -16,6 +16,7 @@ CREATE TABLE IF NOT EXISTS wifi_scan (
lp_ttn_end_device_uplinks_id UUID, lp_ttn_end_device_uplinks_id UUID,
mac VARCHAR(255), mac VARCHAR(255),
rssi NUMERIC, rssi NUMERIC,
scanned_at_utc TIMESTAMP NOT NULL,
created_at_utc TIMESTAMP DEFAULT CURRENT_TIMESTAMP, created_at_utc TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at_utc TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, updated_at_utc TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
FOREIGN KEY (lp_ttn_end_device_uplinks_id) REFERENCES lp_ttn_end_device_uplinks(lp_ttn_end_device_uplinks_id) FOREIGN KEY (lp_ttn_end_device_uplinks_id) REFERENCES lp_ttn_end_device_uplinks(lp_ttn_end_device_uplinks_id)
@ -63,6 +64,7 @@ CREATE TABLE IF NOT EXISTS location (
gnss_longitude DOUBLE, gnss_longitude DOUBLE,
ttn_gw_latitude DOUBLE, ttn_gw_latitude DOUBLE,
ttn_gw_longitude DOUBLE, ttn_gw_longitude DOUBLE,
gnss_location_at_utc TIMESTAMP,
created_at_utc TIMESTAMP DEFAULT CURRENT_TIMESTAMP, created_at_utc TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at_utc TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, updated_at_utc TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
FOREIGN KEY (lp_ttn_end_device_uplinks_id) REFERENCES lp_ttn_end_device_uplinks(lp_ttn_end_device_uplinks_id) FOREIGN KEY (lp_ttn_end_device_uplinks_id) REFERENCES lp_ttn_end_device_uplinks(lp_ttn_end_device_uplinks_id)

View File

@ -42,22 +42,37 @@ router.post(
)?.measurementValue, )?.measurementValue,
}); });
const messageData = message.uplink_message.decoded_payload?.messages[0];
const latitudeData = messageData?.find((e) => e.type === "Latitude");
const longitudeData = messageData?.find((e) => e.type === "Longitude");
const gnnsLocation = { const gnnsLocation = {
latitude: message.uplink_message.decoded_payload?.messages[0].find( latitude: latitudeData?.measurementValue,
(e) => e.type === "Latitude" longitude: longitudeData?.measurementValue,
)?.measurementValue,
longitude: message.uplink_message.decoded_payload?.messages[0].find(
(e) => e.type === "Longitude"
)?.measurementValue,
}; };
const gnssTimestamp = {
timestamp: latitudeData?.timestamp
? new Date(latitudeData.timestamp)
: longitudeData?.timestamp
? new Date(longitudeData.timestamp)
: undefined,
};
const wifiMessage =
message.uplink_message.decoded_payload?.messages[0].find(
(e) => e.type === "Wi-Fi Scan"
);
const wifiScans = const wifiScans =
message.uplink_message.decoded_payload?.messages[0] wifiMessage?.measurementValue?.map((w) => ({
.find((e) => e.type === "Wi-Fi Scan")
?.measurementValue?.map((w) => ({
lp_ttn_end_device_uplinks_id, lp_ttn_end_device_uplinks_id,
mac: w.mac, mac: w.mac,
rssi: w.rssi, rssi: w.rssi,
scanned_at_utc: wifiMessage?.timestamp
? new Date(wifiMessage.timestamp)
: undefined,
})) ?? []; })) ?? [];
const ttnGatewayReceptions = message.uplink_message.rx_metadata.map( const ttnGatewayReceptions = message.uplink_message.rx_metadata.map(
@ -98,6 +113,7 @@ router.post(
longitude: gnnsLocation.longitude, longitude: gnnsLocation.longitude,
} }
: undefined, : undefined,
gnss_timestamp: gnssTimestamp.timestamp,
}); });
}; };
createDatabaseEntries().then(); createDatabaseEntries().then();

View File

@ -10,6 +10,7 @@ export class Location extends Model {
public gnss_longitude!: number; public gnss_longitude!: number;
public ttn_gw_latitude!: number; public ttn_gw_latitude!: number;
public ttn_gw_longitude!: number; public ttn_gw_longitude!: number;
public gnss_location_at_utc!: Date;
public created_at_utc!: Date; public created_at_utc!: Date;
public updated_at_utc!: Date; public updated_at_utc!: Date;
} }
@ -42,6 +43,9 @@ Location.init(
ttn_gw_longitude: { ttn_gw_longitude: {
type: DataTypes.NUMBER, type: DataTypes.NUMBER,
}, },
gnss_location_at_utc: {
type: DataTypes.DATE,
},
created_at_utc: { created_at_utc: {
type: DataTypes.DATE, type: DataTypes.DATE,
defaultValue: DataTypes.NOW, defaultValue: DataTypes.NOW,

View File

@ -6,6 +6,7 @@ export class WifiScan extends Model {
public wifi_scan_id!: string; public wifi_scan_id!: string;
public mac!: string; public mac!: string;
public rssi!: number; public rssi!: number;
public scanned_at_utc!: Date;
public created_at_utc!: Date; public created_at_utc!: Date;
public updated_at_utc!: Date; public updated_at_utc!: Date;
} }
@ -30,6 +31,11 @@ WifiScan.init(
type: DataTypes.NUMBER, type: DataTypes.NUMBER,
allowNull: false, allowNull: false,
}, },
scanned_at_utc: {
type: DataTypes.DATE,
defaultValue: DataTypes.NOW,
allowNull: false,
},
created_at_utc: { created_at_utc: {
type: DataTypes.DATE, type: DataTypes.DATE,
defaultValue: DataTypes.NOW, defaultValue: DataTypes.NOW,

View File

@ -9,6 +9,7 @@ interface CreateLocationParams {
wifi?: Coordinates; wifi?: Coordinates;
gnss?: Coordinates; gnss?: Coordinates;
ttn_gw?: Coordinates; ttn_gw?: Coordinates;
gnss_timestamp?: Date;
} }
interface CreateLocationTriangulationParams { interface CreateLocationTriangulationParams {
@ -19,6 +20,7 @@ interface CreateLocationTriangulationParams {
}[]; }[];
ttn_gw: LocationSignal[]; ttn_gw: LocationSignal[];
gnss?: Coordinates; gnss?: Coordinates;
gnss_timestamp?: Date;
} }
interface LocationSignal extends Coordinates { interface LocationSignal extends Coordinates {
@ -75,6 +77,7 @@ export class LocationService {
ttn_gw_longitude: data.ttn_gw?.longitude, ttn_gw_longitude: data.ttn_gw?.longitude,
gnss_latitude: data.gnss?.latitude, gnss_latitude: data.gnss?.latitude,
gnss_longitude: data.gnss?.longitude, gnss_longitude: data.gnss?.longitude,
gnss_location_at_utc: data.gnss_timestamp,
}); });
} }
@ -91,6 +94,7 @@ export class LocationService {
wifi: wifi_location, wifi: wifi_location,
ttn_gw: gateway_location, ttn_gw: gateway_location,
gnss: data.gnss, gnss: data.gnss,
gnss_timestamp: data.gnss_timestamp,
}); });
} }

View File

@ -5,12 +5,14 @@ interface CreateWifiScanParams {
lp_ttn_end_device_uplinks_id: string; lp_ttn_end_device_uplinks_id: string;
mac: string; mac: string;
rssi: number; rssi: number;
scanned_at_utc?: Date;
} }
interface UpdateWifiScanParams { interface UpdateWifiScanParams {
wifi_scan_id: string; wifi_scan_id: string;
mac?: string; mac?: string;
rssi?: number; rssi?: number;
scanned_at_utc?: Date;
} }
@injectable() @injectable()