Compare commits
13 Commits
d04bdb3ac1
...
feat/syste
Author | SHA256 | Date | |
---|---|---|---|
165c79e67b | |||
c0ac71ebba | |||
10e8e14cd6 | |||
a9c8525e6e | |||
39c07fcef0 | |||
52d521a6ad | |||
d3848ac1aa | |||
93f0c71a6c | |||
5e4fd59148 | |||
62a2dc2c4a | |||
59dc57a618 | |||
452589d11d | |||
a745aaf9d0 |
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
docker-compose.yml
|
@ -56,6 +56,9 @@ Use `https://your.domain.tld/api/metrics` to retrieve useful insides for monitor
|
|||||||
## 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
|
||||||
|
5
server/.gitignore
vendored
5
server/.gitignore
vendored
@ -308,7 +308,4 @@ cython_debug/
|
|||||||
# Built Visual Studio Code Extensions
|
# Built Visual Studio Code Extensions
|
||||||
*.vsix
|
*.vsix
|
||||||
|
|
||||||
config.py
|
config.py
|
||||||
|
|
||||||
#docker
|
|
||||||
docker-compose.yml
|
|
@ -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
|
|
@ -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)
|
||||||
|
@ -42,23 +42,38 @@ 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")
|
lp_ttn_end_device_uplinks_id,
|
||||||
?.measurementValue?.map((w) => ({
|
mac: w.mac,
|
||||||
lp_ttn_end_device_uplinks_id,
|
rssi: w.rssi,
|
||||||
mac: w.mac,
|
scanned_at_utc: wifiMessage?.timestamp
|
||||||
rssi: w.rssi,
|
? new Date(wifiMessage.timestamp)
|
||||||
})) ?? [];
|
: undefined,
|
||||||
|
})) ?? [];
|
||||||
|
|
||||||
const ttnGatewayReceptions = message.uplink_message.rx_metadata.map(
|
const ttnGatewayReceptions = message.uplink_message.rx_metadata.map(
|
||||||
(g) => ({
|
(g) => ({
|
||||||
@ -98,6 +113,7 @@ router.post(
|
|||||||
longitude: gnnsLocation.longitude,
|
longitude: gnnsLocation.longitude,
|
||||||
}
|
}
|
||||||
: undefined,
|
: undefined,
|
||||||
|
gnss_timestamp: gnssTimestamp.timestamp,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
createDatabaseEntries().then();
|
createDatabaseEntries().then();
|
||||||
|
@ -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,
|
||||||
|
@ -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,
|
||||||
|
@ -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,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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()
|
||||||
|
Reference in New Issue
Block a user