Add arrival notifier for Tesla/TeslaMate
- Monitors state changes (driving → parked/charging) - Uses geofences when available - Falls back to reverse geocoding via Nominatim - Triggers Clawdbot agent to check location-based reminders - Includes systemd service template - 30s debounce to prevent duplicate notifications
This commit is contained in:
252
arrival-notifier.js
Normal file
252
arrival-notifier.js
Normal file
@@ -0,0 +1,252 @@
|
|||||||
|
#!/usr/bin/env node
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tesla Arrival Notifier
|
||||||
|
*
|
||||||
|
* Monitors TeslaMate MQTT topics and notifies Clawdbot when you arrive somewhere.
|
||||||
|
* Uses geofences when available, reverse geocoding for unknown locations.
|
||||||
|
*
|
||||||
|
* Environment variables:
|
||||||
|
* MQTT_URL, MQTT_USERNAME, MQTT_PASSWORD - MQTT connection
|
||||||
|
* CLAWDBOT_URL, CLAWDBOT_TOKEN - Clawdbot webhook
|
||||||
|
* NOMINATIM_URL - Reverse geocoding (default: OpenStreetMap)
|
||||||
|
* CAR_ID - TeslaMate car ID (default: 14)
|
||||||
|
*/
|
||||||
|
|
||||||
|
import mqtt from 'mqtt';
|
||||||
|
|
||||||
|
const config = {
|
||||||
|
mqtt: {
|
||||||
|
url: process.env.MQTT_URL || 'mqtts://mqtt.teslamate.olex.me:8883',
|
||||||
|
username: process.env.MQTT_USERNAME || 'olex',
|
||||||
|
password: process.env.MQTT_PASSWORD,
|
||||||
|
clientId: `arrival-notifier-${Math.random().toString(16).slice(2, 8)}`,
|
||||||
|
},
|
||||||
|
clawdbot: {
|
||||||
|
url: process.env.CLAWDBOT_URL || 'http://127.0.0.1:18789',
|
||||||
|
token: process.env.CLAWDBOT_TOKEN,
|
||||||
|
},
|
||||||
|
nominatim: {
|
||||||
|
url: process.env.NOMINATIM_URL || 'https://nominatim.openstreetmap.org',
|
||||||
|
},
|
||||||
|
carId: process.env.CAR_ID || '14',
|
||||||
|
debounceMs: 30000, // Ignore arrivals within 30s of each other
|
||||||
|
};
|
||||||
|
|
||||||
|
// State tracking
|
||||||
|
const state = {
|
||||||
|
currentState: null, // driving, parked, charging, etc.
|
||||||
|
previousState: null,
|
||||||
|
geofence: null,
|
||||||
|
latitude: null,
|
||||||
|
longitude: null,
|
||||||
|
lastArrivalTime: 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
const log = {
|
||||||
|
info: (...args) => console.log('[INFO]', new Date().toISOString(), ...args),
|
||||||
|
debug: (...args) => process.env.LOG_LEVEL === 'debug' && console.log('[DEBUG]', new Date().toISOString(), ...args),
|
||||||
|
error: (...args) => console.error('[ERROR]', new Date().toISOString(), ...args),
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse geocode coordinates to address/POI
|
||||||
|
*/
|
||||||
|
async function reverseGeocode(lat, lon) {
|
||||||
|
try {
|
||||||
|
const url = `${config.nominatim.url}/reverse?lat=${lat}&lon=${lon}&format=json&addressdetails=1&zoom=18`;
|
||||||
|
const response = await fetch(url, {
|
||||||
|
headers: { 'User-Agent': 'TeslaArrivalNotifier/1.0' }
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!response.ok) throw new Error(`HTTP ${response.status}`);
|
||||||
|
|
||||||
|
const data = await response.json();
|
||||||
|
|
||||||
|
// Build a human-readable location description
|
||||||
|
const addr = data.address || {};
|
||||||
|
const parts = [];
|
||||||
|
|
||||||
|
// POI name if available
|
||||||
|
if (data.name && data.name !== addr.road) {
|
||||||
|
parts.push(data.name);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Street address
|
||||||
|
if (addr.road) {
|
||||||
|
parts.push(addr.house_number ? `${addr.road} ${addr.house_number}` : addr.road);
|
||||||
|
}
|
||||||
|
|
||||||
|
// City/town
|
||||||
|
const city = addr.city || addr.town || addr.village || addr.municipality;
|
||||||
|
if (city) parts.push(city);
|
||||||
|
|
||||||
|
return {
|
||||||
|
display: parts.join(', ') || data.display_name,
|
||||||
|
type: data.type,
|
||||||
|
category: data.category,
|
||||||
|
address: addr,
|
||||||
|
};
|
||||||
|
} catch (err) {
|
||||||
|
log.error('Reverse geocode failed:', err.message);
|
||||||
|
return { display: `${lat.toFixed(5)}, ${lon.toFixed(5)}`, type: 'coordinates' };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send arrival notification to Clawdbot
|
||||||
|
*/
|
||||||
|
async function notifyArrival(location, isGeofence) {
|
||||||
|
const now = Date.now();
|
||||||
|
|
||||||
|
// Debounce
|
||||||
|
if (now - state.lastArrivalTime < config.debounceMs) {
|
||||||
|
log.debug('Debounced arrival notification');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
state.lastArrivalTime = now;
|
||||||
|
|
||||||
|
const locationType = isGeofence ? 'geofence' : 'geocoded';
|
||||||
|
const message = `[Arrival] Olex arrived at: ${location.display || location}
|
||||||
|
Location type: ${locationType}
|
||||||
|
Coordinates: ${state.latitude}, ${state.longitude}
|
||||||
|
Time: ${new Date().toLocaleString('de-DE', { timeZone: 'Europe/Berlin' })}
|
||||||
|
|
||||||
|
Check location-reminders.json and your notes for any reminders relevant to this location. If there are relevant reminders, send them to Olex. If not, stay silent (HEARTBEAT_OK).`;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await fetch(`${config.clawdbot.url}/hooks/agent`, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
'Authorization': `Bearer ${config.clawdbot.token}`,
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
message,
|
||||||
|
name: 'Arrival',
|
||||||
|
deliver: true,
|
||||||
|
channel: 'telegram',
|
||||||
|
to: '157917114',
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error(`HTTP ${response.status}: ${await response.text()}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
log.info(`Notified arrival at: ${location.display || location}`);
|
||||||
|
} catch (err) {
|
||||||
|
log.error('Failed to notify Clawdbot:', err.message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle state change - detect arrivals
|
||||||
|
*/
|
||||||
|
async function handleStateChange(newState) {
|
||||||
|
state.previousState = state.currentState;
|
||||||
|
state.currentState = newState;
|
||||||
|
|
||||||
|
log.debug(`State: ${state.previousState} → ${newState}`);
|
||||||
|
|
||||||
|
// Detect arrival: was driving/moving, now parked or charging
|
||||||
|
const wasMoving = ['driving', 'online'].includes(state.previousState);
|
||||||
|
const nowStopped = ['parked', 'charging'].includes(newState);
|
||||||
|
|
||||||
|
if (wasMoving && nowStopped) {
|
||||||
|
log.info('Arrival detected!');
|
||||||
|
|
||||||
|
// Check if we have a geofence
|
||||||
|
if (state.geofence && state.geofence !== 'nil' && state.geofence !== '') {
|
||||||
|
await notifyArrival({ display: state.geofence }, true);
|
||||||
|
} else if (state.latitude && state.longitude) {
|
||||||
|
// Reverse geocode
|
||||||
|
const location = await reverseGeocode(state.latitude, state.longitude);
|
||||||
|
await notifyArrival(location, false);
|
||||||
|
} else {
|
||||||
|
log.error('No location data available for arrival');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Main
|
||||||
|
*/
|
||||||
|
async function main() {
|
||||||
|
if (!config.clawdbot.token) {
|
||||||
|
log.error('CLAWDBOT_TOKEN required');
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
log.info('Starting Tesla Arrival Notifier');
|
||||||
|
log.info(`MQTT: ${config.mqtt.url}`);
|
||||||
|
log.info(`Car ID: ${config.carId}`);
|
||||||
|
|
||||||
|
const mqttOptions = {
|
||||||
|
clientId: config.mqtt.clientId,
|
||||||
|
username: config.mqtt.username,
|
||||||
|
password: config.mqtt.password,
|
||||||
|
rejectUnauthorized: true,
|
||||||
|
};
|
||||||
|
|
||||||
|
const client = mqtt.connect(config.mqtt.url, mqttOptions);
|
||||||
|
const prefix = `teslamate/cars/${config.carId}`;
|
||||||
|
|
||||||
|
client.on('connect', () => {
|
||||||
|
log.info('Connected to MQTT');
|
||||||
|
|
||||||
|
// Subscribe to relevant topics
|
||||||
|
const topics = [
|
||||||
|
`${prefix}/state`,
|
||||||
|
`${prefix}/geofence`,
|
||||||
|
`${prefix}/latitude`,
|
||||||
|
`${prefix}/longitude`,
|
||||||
|
];
|
||||||
|
|
||||||
|
topics.forEach(topic => {
|
||||||
|
client.subscribe(topic, (err) => {
|
||||||
|
if (err) log.error(`Subscribe failed: ${topic}`);
|
||||||
|
else log.debug(`Subscribed: ${topic}`);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
client.on('message', async (topic, payload) => {
|
||||||
|
const value = payload.toString();
|
||||||
|
const field = topic.split('/').pop();
|
||||||
|
|
||||||
|
log.debug(`${field}: ${value}`);
|
||||||
|
|
||||||
|
switch (field) {
|
||||||
|
case 'state':
|
||||||
|
await handleStateChange(value);
|
||||||
|
break;
|
||||||
|
case 'geofence':
|
||||||
|
state.geofence = value;
|
||||||
|
break;
|
||||||
|
case 'latitude':
|
||||||
|
state.latitude = parseFloat(value);
|
||||||
|
break;
|
||||||
|
case 'longitude':
|
||||||
|
state.longitude = parseFloat(value);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
client.on('error', (err) => log.error('MQTT error:', err.message));
|
||||||
|
client.on('reconnect', () => log.info('Reconnecting...'));
|
||||||
|
|
||||||
|
// Graceful shutdown
|
||||||
|
process.on('SIGINT', () => {
|
||||||
|
log.info('Shutting down...');
|
||||||
|
client.end(true, () => process.exit(0));
|
||||||
|
});
|
||||||
|
process.on('SIGTERM', () => {
|
||||||
|
client.end(true, () => process.exit(0));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
main().catch(err => {
|
||||||
|
log.error('Fatal:', err.message);
|
||||||
|
process.exit(1);
|
||||||
|
});
|
||||||
26
arrival-notifier.service
Normal file
26
arrival-notifier.service
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
[Unit]
|
||||||
|
Description=Tesla Arrival Notifier for Clawdbot
|
||||||
|
After=network.target
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Type=simple
|
||||||
|
WorkingDirectory=/opt/mqtt-clawdbot-bridge
|
||||||
|
ExecStart=/usr/bin/node arrival-notifier.js
|
||||||
|
Restart=always
|
||||||
|
RestartSec=10
|
||||||
|
|
||||||
|
Environment=MQTT_URL=mqtts://mqtt.teslamate.olex.me:8883
|
||||||
|
Environment=MQTT_USERNAME=olex
|
||||||
|
Environment=MQTT_PASSWORD=wnGrhm6t
|
||||||
|
Environment=CLAWDBOT_URL=http://127.0.0.1:18789
|
||||||
|
Environment=CLAWDBOT_TOKEN=c92e6b350068e07ff71ab77ae4bd0610da898a2f38d394dd
|
||||||
|
Environment=CAR_ID=14
|
||||||
|
Environment=LOG_LEVEL=info
|
||||||
|
|
||||||
|
NoNewPrivileges=true
|
||||||
|
ProtectSystem=strict
|
||||||
|
ProtectHome=read-only
|
||||||
|
PrivateTmp=true
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
||||||
537
package-lock.json
generated
Normal file
537
package-lock.json
generated
Normal file
@@ -0,0 +1,537 @@
|
|||||||
|
{
|
||||||
|
"name": "mqtt-clawdbot-bridge",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"lockfileVersion": 3,
|
||||||
|
"requires": true,
|
||||||
|
"packages": {
|
||||||
|
"": {
|
||||||
|
"name": "mqtt-clawdbot-bridge",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"mqtt": "^5.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@babel/runtime": {
|
||||||
|
"version": "7.28.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.28.6.tgz",
|
||||||
|
"integrity": "sha512-05WQkdpL9COIMz4LjTxGpPNCdlpyimKppYNoJ5Di5EUObifl8t4tuLuUBBZEpoLYOmfvIWrsp9fCl0HoPRVTdA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6.9.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@types/node": {
|
||||||
|
"version": "25.0.10",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/node/-/node-25.0.10.tgz",
|
||||||
|
"integrity": "sha512-zWW5KPngR/yvakJgGOmZ5vTBemDoSqF3AcV/LrO5u5wTWyEAVVh+IT39G4gtyAkh3CtTZs8aX/yRM82OfzHJRg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"undici-types": "~7.16.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@types/readable-stream": {
|
||||||
|
"version": "4.0.23",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/readable-stream/-/readable-stream-4.0.23.tgz",
|
||||||
|
"integrity": "sha512-wwXrtQvbMHxCbBgjHaMGEmImFTQxxpfMOR/ZoQnXxB1woqkUbdLGFDgauo00Py9IudiaqSeiBiulSV9i6XIPig==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@types/node": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@types/ws": {
|
||||||
|
"version": "8.18.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.18.1.tgz",
|
||||||
|
"integrity": "sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@types/node": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/abort-controller": {
|
||||||
|
"version": "3.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz",
|
||||||
|
"integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"event-target-shim": "^5.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6.5"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/base64-js": {
|
||||||
|
"version": "1.5.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
|
||||||
|
"integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==",
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://github.com/sponsors/feross"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "patreon",
|
||||||
|
"url": "https://www.patreon.com/feross"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "consulting",
|
||||||
|
"url": "https://feross.org/support"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/bl": {
|
||||||
|
"version": "6.1.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/bl/-/bl-6.1.6.tgz",
|
||||||
|
"integrity": "sha512-jLsPgN/YSvPUg9UX0Kd73CXpm2Psg9FxMeCSXnk3WBO3CMT10JMwijubhGfHCnFu6TPn1ei3b975dxv7K2pWVg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@types/readable-stream": "^4.0.0",
|
||||||
|
"buffer": "^6.0.3",
|
||||||
|
"inherits": "^2.0.4",
|
||||||
|
"readable-stream": "^4.2.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/broker-factory": {
|
||||||
|
"version": "3.1.13",
|
||||||
|
"resolved": "https://registry.npmjs.org/broker-factory/-/broker-factory-3.1.13.tgz",
|
||||||
|
"integrity": "sha512-H2VALe31mEtO/SRcNp4cUU5BAm1biwhc/JaF77AigUuni/1YT0FLCJfbUxwIEs9y6Kssjk2fmXgf+Y9ALvmKlw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@babel/runtime": "^7.28.6",
|
||||||
|
"fast-unique-numbers": "^9.0.26",
|
||||||
|
"tslib": "^2.8.1",
|
||||||
|
"worker-factory": "^7.0.48"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/buffer": {
|
||||||
|
"version": "6.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz",
|
||||||
|
"integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==",
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://github.com/sponsors/feross"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "patreon",
|
||||||
|
"url": "https://www.patreon.com/feross"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "consulting",
|
||||||
|
"url": "https://feross.org/support"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"base64-js": "^1.3.1",
|
||||||
|
"ieee754": "^1.2.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/buffer-from": {
|
||||||
|
"version": "1.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz",
|
||||||
|
"integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/commist": {
|
||||||
|
"version": "3.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/commist/-/commist-3.2.0.tgz",
|
||||||
|
"integrity": "sha512-4PIMoPniho+LqXmpS5d3NuGYncG6XWlkBSVGiWycL22dd42OYdUGil2CWuzklaJoNxyxUSpO4MKIBU94viWNAw==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/concat-stream": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-2.0.0.tgz",
|
||||||
|
"integrity": "sha512-MWufYdFw53ccGjCA+Ol7XJYpAlW6/prSMzuPOTRnJGcGzuhLn4Scrz7qf6o8bROZ514ltazcIFJZevcfbo0x7A==",
|
||||||
|
"engines": [
|
||||||
|
"node >= 6.0"
|
||||||
|
],
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"buffer-from": "^1.0.0",
|
||||||
|
"inherits": "^2.0.3",
|
||||||
|
"readable-stream": "^3.0.2",
|
||||||
|
"typedarray": "^0.0.6"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/concat-stream/node_modules/readable-stream": {
|
||||||
|
"version": "3.6.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz",
|
||||||
|
"integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"inherits": "^2.0.3",
|
||||||
|
"string_decoder": "^1.1.1",
|
||||||
|
"util-deprecate": "^1.0.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 6"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/debug": {
|
||||||
|
"version": "4.4.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
|
||||||
|
"integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"ms": "^2.1.3"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6.0"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"supports-color": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/event-target-shim": {
|
||||||
|
"version": "5.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz",
|
||||||
|
"integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/events": {
|
||||||
|
"version": "3.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz",
|
||||||
|
"integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.8.x"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/fast-unique-numbers": {
|
||||||
|
"version": "9.0.26",
|
||||||
|
"resolved": "https://registry.npmjs.org/fast-unique-numbers/-/fast-unique-numbers-9.0.26.tgz",
|
||||||
|
"integrity": "sha512-3Mtq8p1zQinjGyWfKeuBunbuFoixG72AUkk4VvzbX4ykCW9Q4FzRaNyIlfQhUjnKw2ARVP+/CKnoyr6wfHftig==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@babel/runtime": "^7.28.6",
|
||||||
|
"tslib": "^2.8.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18.2.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/help-me": {
|
||||||
|
"version": "5.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/help-me/-/help-me-5.0.0.tgz",
|
||||||
|
"integrity": "sha512-7xgomUX6ADmcYzFik0HzAxh/73YlKR9bmFzf51CZwR+b6YtzU2m0u49hQCqV6SvlqIqsaxovfwdvbnsw3b/zpg==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/ieee754": {
|
||||||
|
"version": "1.2.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz",
|
||||||
|
"integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==",
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://github.com/sponsors/feross"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "patreon",
|
||||||
|
"url": "https://www.patreon.com/feross"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "consulting",
|
||||||
|
"url": "https://feross.org/support"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"license": "BSD-3-Clause"
|
||||||
|
},
|
||||||
|
"node_modules/inherits": {
|
||||||
|
"version": "2.0.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
|
||||||
|
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
|
||||||
|
"license": "ISC"
|
||||||
|
},
|
||||||
|
"node_modules/ip-address": {
|
||||||
|
"version": "10.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.1.0.tgz",
|
||||||
|
"integrity": "sha512-XXADHxXmvT9+CRxhXg56LJovE+bmWnEWB78LB83VZTprKTmaC5QfruXocxzTZ2Kl0DNwKuBdlIhjL8LeY8Sf8Q==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 12"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/js-sdsl": {
|
||||||
|
"version": "4.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/js-sdsl/-/js-sdsl-4.3.0.tgz",
|
||||||
|
"integrity": "sha512-mifzlm2+5nZ+lEcLJMoBK0/IH/bDg8XnJfd/Wq6IP+xoCjLZsTOnV2QpxlVbX9bMnkl5PdEjNtBJ9Cj1NjifhQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"funding": {
|
||||||
|
"type": "opencollective",
|
||||||
|
"url": "https://opencollective.com/js-sdsl"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/lru-cache": {
|
||||||
|
"version": "10.4.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz",
|
||||||
|
"integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==",
|
||||||
|
"license": "ISC"
|
||||||
|
},
|
||||||
|
"node_modules/minimist": {
|
||||||
|
"version": "1.2.8",
|
||||||
|
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz",
|
||||||
|
"integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/mqtt": {
|
||||||
|
"version": "5.14.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/mqtt/-/mqtt-5.14.1.tgz",
|
||||||
|
"integrity": "sha512-NxkPxE70Uq3Ph7goefQa7ggSsVzHrayCD0OyxlJgITN/EbzlZN+JEPmaAZdxP1LsIT5FamDyILoQTF72W7Nnbw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@types/readable-stream": "^4.0.21",
|
||||||
|
"@types/ws": "^8.18.1",
|
||||||
|
"commist": "^3.2.0",
|
||||||
|
"concat-stream": "^2.0.0",
|
||||||
|
"debug": "^4.4.1",
|
||||||
|
"help-me": "^5.0.0",
|
||||||
|
"lru-cache": "^10.4.3",
|
||||||
|
"minimist": "^1.2.8",
|
||||||
|
"mqtt-packet": "^9.0.2",
|
||||||
|
"number-allocator": "^1.0.14",
|
||||||
|
"readable-stream": "^4.7.0",
|
||||||
|
"rfdc": "^1.4.1",
|
||||||
|
"socks": "^2.8.6",
|
||||||
|
"split2": "^4.2.0",
|
||||||
|
"worker-timers": "^8.0.23",
|
||||||
|
"ws": "^8.18.3"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"mqtt": "build/bin/mqtt.js",
|
||||||
|
"mqtt_pub": "build/bin/pub.js",
|
||||||
|
"mqtt_sub": "build/bin/sub.js"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=16.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/mqtt-packet": {
|
||||||
|
"version": "9.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/mqtt-packet/-/mqtt-packet-9.0.2.tgz",
|
||||||
|
"integrity": "sha512-MvIY0B8/qjq7bKxdN1eD+nrljoeaai+qjLJgfRn3TiMuz0pamsIWY2bFODPZMSNmabsLANXsLl4EMoWvlaTZWA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"bl": "^6.0.8",
|
||||||
|
"debug": "^4.3.4",
|
||||||
|
"process-nextick-args": "^2.0.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/ms": {
|
||||||
|
"version": "2.1.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
|
||||||
|
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/number-allocator": {
|
||||||
|
"version": "1.0.14",
|
||||||
|
"resolved": "https://registry.npmjs.org/number-allocator/-/number-allocator-1.0.14.tgz",
|
||||||
|
"integrity": "sha512-OrL44UTVAvkKdOdRQZIJpLkAdjXGTRda052sN4sO77bKEzYYqWKMBjQvrJFzqygI99gL6Z4u2xctPW1tB8ErvA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"debug": "^4.3.1",
|
||||||
|
"js-sdsl": "4.3.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/process": {
|
||||||
|
"version": "0.11.10",
|
||||||
|
"resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz",
|
||||||
|
"integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.6.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/process-nextick-args": {
|
||||||
|
"version": "2.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
|
||||||
|
"integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/readable-stream": {
|
||||||
|
"version": "4.7.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.7.0.tgz",
|
||||||
|
"integrity": "sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"abort-controller": "^3.0.0",
|
||||||
|
"buffer": "^6.0.3",
|
||||||
|
"events": "^3.3.0",
|
||||||
|
"process": "^0.11.10",
|
||||||
|
"string_decoder": "^1.3.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/rfdc": {
|
||||||
|
"version": "1.4.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.4.1.tgz",
|
||||||
|
"integrity": "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/safe-buffer": {
|
||||||
|
"version": "5.2.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
|
||||||
|
"integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://github.com/sponsors/feross"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "patreon",
|
||||||
|
"url": "https://www.patreon.com/feross"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "consulting",
|
||||||
|
"url": "https://feross.org/support"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/smart-buffer": {
|
||||||
|
"version": "4.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz",
|
||||||
|
"integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 6.0.0",
|
||||||
|
"npm": ">= 3.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/socks": {
|
||||||
|
"version": "2.8.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/socks/-/socks-2.8.7.tgz",
|
||||||
|
"integrity": "sha512-HLpt+uLy/pxB+bum/9DzAgiKS8CX1EvbWxI4zlmgGCExImLdiad2iCwXT5Z4c9c3Eq8rP2318mPW2c+QbtjK8A==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"ip-address": "^10.0.1",
|
||||||
|
"smart-buffer": "^4.2.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 10.0.0",
|
||||||
|
"npm": ">= 3.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/split2": {
|
||||||
|
"version": "4.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz",
|
||||||
|
"integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==",
|
||||||
|
"license": "ISC",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 10.x"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/string_decoder": {
|
||||||
|
"version": "1.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz",
|
||||||
|
"integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"safe-buffer": "~5.2.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/tslib": {
|
||||||
|
"version": "2.8.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
|
||||||
|
"integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
|
||||||
|
"license": "0BSD"
|
||||||
|
},
|
||||||
|
"node_modules/typedarray": {
|
||||||
|
"version": "0.0.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz",
|
||||||
|
"integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/undici-types": {
|
||||||
|
"version": "7.16.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz",
|
||||||
|
"integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/util-deprecate": {
|
||||||
|
"version": "1.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
|
||||||
|
"integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/worker-factory": {
|
||||||
|
"version": "7.0.48",
|
||||||
|
"resolved": "https://registry.npmjs.org/worker-factory/-/worker-factory-7.0.48.tgz",
|
||||||
|
"integrity": "sha512-CGmBy3tJvpBPjUvb0t4PrpKubUsfkI1Ohg0/GGFU2RvA9j/tiVYwKU8O7yu7gH06YtzbeJLzdUR29lmZKn5pag==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@babel/runtime": "^7.28.6",
|
||||||
|
"fast-unique-numbers": "^9.0.26",
|
||||||
|
"tslib": "^2.8.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/worker-timers": {
|
||||||
|
"version": "8.0.29",
|
||||||
|
"resolved": "https://registry.npmjs.org/worker-timers/-/worker-timers-8.0.29.tgz",
|
||||||
|
"integrity": "sha512-9jk0MWHhWAZ2xlJPXr45oe5UF/opdpfZrY0HtyPizWuJ+ce1M3IYk/4IIdGct3kn9Ncfs+tkZt3w1tU6KW2Fsg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@babel/runtime": "^7.28.6",
|
||||||
|
"tslib": "^2.8.1",
|
||||||
|
"worker-timers-broker": "^8.0.15",
|
||||||
|
"worker-timers-worker": "^9.0.13"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/worker-timers-broker": {
|
||||||
|
"version": "8.0.15",
|
||||||
|
"resolved": "https://registry.npmjs.org/worker-timers-broker/-/worker-timers-broker-8.0.15.tgz",
|
||||||
|
"integrity": "sha512-Te+EiVUMzG5TtHdmaBZvBrZSFNauym6ImDaCAnzQUxvjnw+oGjMT2idmAOgDy30vOZMLejd0bcsc90Axu6XPWA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@babel/runtime": "^7.28.6",
|
||||||
|
"broker-factory": "^3.1.13",
|
||||||
|
"fast-unique-numbers": "^9.0.26",
|
||||||
|
"tslib": "^2.8.1",
|
||||||
|
"worker-timers-worker": "^9.0.13"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/worker-timers-worker": {
|
||||||
|
"version": "9.0.13",
|
||||||
|
"resolved": "https://registry.npmjs.org/worker-timers-worker/-/worker-timers-worker-9.0.13.tgz",
|
||||||
|
"integrity": "sha512-qjn18szGb1kjcmh2traAdki1eiIS5ikFo+L90nfMOvSRpuDw1hAcR1nzkP2+Hkdqz5thIRnfuWx7QSpsEUsA6Q==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@babel/runtime": "^7.28.6",
|
||||||
|
"tslib": "^2.8.1",
|
||||||
|
"worker-factory": "^7.0.48"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/ws": {
|
||||||
|
"version": "8.19.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/ws/-/ws-8.19.0.tgz",
|
||||||
|
"integrity": "sha512-blAT2mjOEIi0ZzruJfIhb3nps74PRWTCz1IjglWEEpQl5XS/UNama6u2/rjFkDDouqr4L67ry+1aGIALViWjDg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10.0.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"bufferutil": "^4.0.1",
|
||||||
|
"utf-8-validate": ">=5.0.2"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"bufferutil": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"utf-8-validate": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user