feat: improve MQTT reconnection handling + document arrival notifier

- Add keepalive (60s), reconnectPeriod (5s), connectTimeout (30s)
- Add offline/close event handlers with proper logging
- Document Tesla Arrival Notifier setup in README
- Document systemd service installation
This commit is contained in:
Clawd
2026-01-29 08:44:48 +00:00
parent 97f8eee507
commit 1c38e729f3
2 changed files with 93 additions and 1 deletions

View File

@@ -159,6 +159,78 @@ Messages are prefixed with the topic for context:
### Webhook returns 401 ### Webhook returns 401
- Token mismatch — check `hooks.token` in Clawdbot config matches `CLAWDBOT_TOKEN` - Token mismatch — check `hooks.token` in Clawdbot config matches `CLAWDBOT_TOKEN`
---
## Tesla Arrival Notifier
A specialized component that monitors TeslaMate MQTT for car state changes and notifies Clawdbot when you arrive somewhere.
### How It Works
1. Subscribes to TeslaMate MQTT topics (`teslamate/cars/<id>/state`, `geofence`, `latitude`, `longitude`)
2. Detects when car state transitions from `driving` → any stationary state (`online`, `parked`, `charging`, `suspended`, `asleep`, `offline`)
3. Sends arrival notification to Clawdbot with geofence name (or reverse-geocoded address)
4. Clawdbot checks `location-reminders.json` for location-specific reminders
### Installation
```bash
# Install to /opt
sudo mkdir -p /opt/mqtt-clawdbot-bridge
sudo cp arrival-notifier.js package*.json /opt/mqtt-clawdbot-bridge/
cd /opt/mqtt-clawdbot-bridge && npm install
# Install systemd service
sudo cp arrival-notifier.service /etc/systemd/system/
sudo systemctl daemon-reload
sudo systemctl enable arrival-notifier
sudo systemctl start arrival-notifier
```
### Configuration
Environment variables (configured in the systemd service file):
| Variable | Default | Description |
|----------|---------|-------------|
| `MQTT_URL` | `mqtts://mqtt.teslamate.olex.me:8883` | TeslaMate MQTT broker |
| `MQTT_USERNAME` | `olex` | MQTT username |
| `MQTT_PASSWORD` | — | MQTT password (**required**) |
| `CLAWDBOT_URL` | `http://127.0.0.1:18789` | Clawdbot gateway URL |
| `CLAWDBOT_TOKEN` | — | Webhook token (**required**) |
| `CAR_ID` | `14` | TeslaMate car ID |
| `LOG_LEVEL` | `info` | `debug` for verbose output |
### Managing the Service
```bash
# Check status
sudo systemctl status arrival-notifier
# View logs
journalctl -u arrival-notifier -f
# Restart after config changes
sudo systemctl restart arrival-notifier
```
### Location Reminders
Create `~/clawd/location-reminders.json`:
```json
{
"Zuhause (Geißberg 31)": [
"Reminder: Check the mailbox!"
],
"Arbeit (GUDE)": [
"Remember to submit timesheet on Fridays"
]
}
```
Geofence names must match exactly as shown in TeslaMate.
## License ## License
MIT MIT

View File

@@ -188,12 +188,17 @@ async function main() {
username: config.mqtt.username, username: config.mqtt.username,
password: config.mqtt.password, password: config.mqtt.password,
rejectUnauthorized: true, rejectUnauthorized: true,
keepalive: 60, // Send ping every 60s
reconnectPeriod: 5000, // Retry every 5s on disconnect
connectTimeout: 30000, // 30s connection timeout
}; };
const client = mqtt.connect(config.mqtt.url, mqttOptions); const client = mqtt.connect(config.mqtt.url, mqttOptions);
const prefix = `teslamate/cars/${config.carId}`; const prefix = `teslamate/cars/${config.carId}`;
let isConnected = false;
client.on('connect', () => { client.on('connect', () => {
isConnected = true;
log.info('Connected to MQTT'); log.info('Connected to MQTT');
// Subscribe to relevant topics // Subscribe to relevant topics
@@ -235,7 +240,22 @@ async function main() {
}); });
client.on('error', (err) => log.error('MQTT error:', err.message)); client.on('error', (err) => log.error('MQTT error:', err.message));
client.on('reconnect', () => log.info('Reconnecting...'));
client.on('reconnect', () => {
log.info('Reconnecting to MQTT...');
});
client.on('close', () => {
if (isConnected) {
log.info('MQTT connection closed');
isConnected = false;
}
});
client.on('offline', () => {
log.info('MQTT client offline');
isConnected = false;
});
// Graceful shutdown // Graceful shutdown
process.on('SIGINT', () => { process.on('SIGINT', () => {