feat: Implement mqtt-gateway
Some checks failed
CI / main (push) Failing after 11s

This commit is contained in:
Felix Dürrwald 2024-09-29 08:46:40 +02:00
parent f11c3d80f6
commit 94a95876dd
Signed by: mplabs
GPG Key ID: 6E7D7F464AC5179D
15 changed files with 622 additions and 115 deletions

View File

@ -1,4 +1,5 @@
{ {
"printWidth": 100,
"semi": false, "semi": false,
"singleQuote": true, "singleQuote": true,
"tabWidth": 4, "tabWidth": 4,

View File

@ -0,0 +1,6 @@
AWEKAS_ID=your-awkas-id
AWEKAS_PASSWORD=your-awekas-password
MPLABS_ID=your-local-id
MPLABS_PASSWORD=your-local-password
MQTT_BROKER_URL=mqtt://127.0.0.1:1883
MQTT_TOPIC=weather

View File

@ -0,0 +1,30 @@
services:
watchtower:
image: containrrr/watchtower
command:
- "--label-enable"
- "--interval"
- "30"
- "--rolling-restart"
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- /etc/localtime:/etc/localtime:ro
mqtt-gateway:
image: fduerrwald/awekas:latest
labels:
- "traefik.enable=true"
- "traefik.http.routers.mqtt-gateway.rule=Host(`ws.mplabshome.net`)"
- "traefik.http.routers.mqtt-gateway.entrypoints=web"
- "com.centurylinklabs.watchtower.enable=true"
environment:
- AWEKAS_ID=${AWEKAS_ID}
- AWEKAS_PASSWORD=${AWEKAS_PASSWORD}
- MPLABS_ID=${MPLABS_ID}
- MPLABS_PASSWORD=${MPLABS_PASSWORD}
- MQTT_BROKER_URL=${MQTT_BROKER_URL:-mqtt://127.0.0.1:1883}
- MQTT_TOPIC=${MQTT_TOPIC:-weather}
deploy:
mode: replicated
replicas: 3
restart: always

View File

@ -1,66 +0,0 @@
import type { ParsedQs } from 'qs'
import express, { Request, Response } from 'express'
import morgan from 'morgan'
const app = express()
const port = 3000
// Utility function to check required fields
const checkRequiredFields = (params: ParsedQs): boolean => {
const requiredFields = ['ID', 'PASSWORD', 'dateutc', 'action']
for (const field of requiredFields) {
if (!params[field]) {
return false
}
}
return true
}
app.use(morgan(':method :url :status :res[content-length] - :response-time ms'))
// Route to handle weather station updates
app.get(
'/weatherstation/updateweatherstation.php',
(req: Request, res: Response) => {
const params = req.query
// Check if required fields are present
if (!checkRequiredFields(params)) {
return res
.status(400)
.send(
'RapidFire Server<br><br><b>usage</b><br>Required fields missing: ID, PASSWORD, action, and dateutc'
)
}
const { ID, PASSWORD, action, dateutc } = params as Record<string, string>
// Validate action and dateutc
if (action !== 'updateraw') {
return res.status(400).send('Invalid action')
}
if (dateutc !== 'now' && isNaN(Date.parse(dateutc))) {
return res.status(400).send('Invalid dateutc format')
}
// Validate ID and PASSWORD (this is where you would verify against your database)
// For now, we'll mock this validation
if (ID !== 'KCASANFR5' || PASSWORD !== 'XXXXXX') {
return res
.status(401)
.send('INVALIDPASSWORDID|Password and/or id are incorrect')
}
// Success response
res.status(200).send('success')
// Log the incoming data
console.log('Received weather data:', params)
}
)
// Start the server
app.listen(port, () => {
console.log(`Server is running at http://localhost:${port}`)
})

View File

@ -1,43 +1,57 @@
import express from 'express'; import dotenv from 'dotenv'
import { checkSchema, Schema } from 'express-validator' import express, { NextFunction, Request, Response } from 'express'
import morgan from 'morgan'
import mqtt from 'mqtt'
import checkRequiredFieldsMiddleware from './middleware/checkRequiredFields'
import validateParamsMiddleware from './middleware/validateParams'
import forwardDataMiddleware from './middleware/forwardData'
import { WSQuery } from './types'
import sendToMqtt from './middleware/sentToMqtt'
const host = process.env.HOST ?? 'localhost'; dotenv.config()
const port = process.env.PORT ? Number(process.env.PORT) : 3000;
const app = express(); const { AWEKAS_ID, AWEKAS_PASSWORD, MPLABS_ID, MPLABS_PASSWORD, MQTT_BROKER_URL, MQTT_TOPIC } = process.env
const FtoC: (f: number) => number = (f) => (f - 32) * 5 / 9 const app = express()
const inHgTohpa: (inHg: number) => number = (inHg) => inHg * 33.86389 const port = 3000
const mphToKmh: (mph: number) => number = (mph) => mph * 1.609344
const mphToMs: (mph: number) => number = (mph) => mph * 0.44704
type UpdateweatherstationQuery = Record<string, unknown> & { // MQTT Client setup
action: 'updateraw' const mqttClient = mqtt.connect(MQTT_BROKER_URL) // Replace with your MQTT server URL and port
ID: string
KEY: string mqttClient.on('connect', () => {
console.log('Connected to MQTT broker')
})
mqttClient.on('error', (err) => {
console.error('MQTT connection error:', err)
})
app.use(morgan(':method :url :status :res[content-length] - :response-time ms'))
// Routes
app.get('/', (req: Request, res: Response) => {
res.send('AWEKAS Data Server URL')
})
// Route to handle weather station updates
app.get(
'/weatherstation/updateweatherstation.php',
checkRequiredFieldsMiddleware,
validateParamsMiddleware(MPLABS_ID, MPLABS_PASSWORD),
forwardDataMiddleware(AWEKAS_ID, AWEKAS_PASSWORD),
sendToMqtt(mqttClient, MQTT_TOPIC),
(req: Request, res: Response) => {
const params = req.query as WSQuery
// Success response
res.status(200).send(' - airp locked - Transfer OK')
// Log the incoming data
console.log('Received weather data:', params)
} }
)
app.get('/weatherstation/updateweatherstation.php', (req, res) => { // Start the server
const query = req.query as Record<string, string> app.listen(port, () => {
console.log(`Server is running at http://localhost:${port}`)
// Validate only the required fields for now })
const { action, ID, KEY } = query
const timestamp = new Date().getTime()
const pressure_inhg = parseFloat(query.baromin)
const pressure_hpa = inHgTohpa(parseFloat(query.baromin))
const temperature_f = parseFloat(query.tempf)
const temperature_c = FtoC(parseFloat(query.tempf))
const dew_point_f = parseFloat(query.dewptf)
const dew_point_c = FtoC(parseFloat(query.dewptf))
const humidity = parseFloat(query.humidity)
const wind_speed_mph = parseFloat(query.windspeedmph)
const wind_speed_mph = mphToKmh(parseFloat(query.windspeedmph))
const wind_speed_ms = mphToMs(parseFloat(query.windspeedmph))
res.send('OK')
});
app.listen(port, host, () => {
console.log(`[ ready ] http://${host}:${port}`);
});

View File

@ -0,0 +1,17 @@
import { NextFunction, Request, Response } from "express"
import { WSQuery } from "../types"
// Utility middleware to check required fields
const checkRequiredFieldsMiddleware = (req: Request, res: Response, next: NextFunction) => {
const params = req.query as WSQuery
const requiredFields = ['ID', 'PASSWORD', 'dateutc', 'action']
if (!requiredFields.every((field) => Boolean(params[field]))) {
res.status(400).send(
`AWEKAS Data Server URL<br><strong>Required fields missing:</strong> ID, PASSWORD, action, and dateutc`
)
} else {
next()
}
}
export default checkRequiredFieldsMiddleware

View File

@ -0,0 +1,35 @@
import { NextFunction, Request, Response } from "express"
import { WSQuery } from "../types"
// Forward received data to AWEKAS
const forwardDataMiddleware = (AWEKAS_ID: string, AWEKAS_PASSWORD: string) => async (req: Request, res: Response, next: NextFunction) => {
const awekasServerUrl = 'http://ws.awekas.at/weatherstation/updateweatherstation.php'
const queryParams = new URLSearchParams(req.query as WSQuery)
queryParams.set('ID', AWEKAS_ID)
queryParams.set('PASSWORD', AWEKAS_PASSWORD)
const awekasUrlWithParams = `${awekasServerUrl}?${queryParams.toString()}`
try {
// Forward the request using the native fetch API
const awekasResponse = await fetch(awekasUrlWithParams, {
method: 'GET',
headers: {
Host: 'ws.awekas.at', // Override the host header to match the expected hostname
},
})
// If the forward request is successful, log and continue to the next handler
const awekasData = await awekasResponse.text()
console.log('Response from Awekas:', awekasData)
// Move to the next middleware or route handler
next()
} catch (error) {
console.error('Error forwarding request to Awekas:', error)
// If there's an error, respond with a 502 Bad Gateway
res.status(502).send('Error forwarding request to Awekas server')
}
}
export default forwardDataMiddleware

View File

@ -0,0 +1,36 @@
import { NextFunction, Request, Response } from 'express'
import { WSQuery } from '../types'
import { MqttClient } from 'mqtt/*'
import transformData from '../utils/transformData'
import { omit } from '../utils/object'
// Middleware to send data to MQTT server
const sendToMqtt =
(mqttClient: MqttClient, MQTT_TOPIC: string) =>
(req: Request, res: Response, next: NextFunction) => {
const { ID, ...params } = omit(
req.query as WSQuery,
'PASSWORD',
'action',
'realtime',
'rtfreq'
)
// Format the data to be sent to MQTT
const data = transformData(params)
// Send the data to the MQTT server
for (const [key, value] of Object.entries(data)) {
const topic = `${MQTT_TOPIC}/${ID}/${key}`
mqttClient.publish(topic, value.toString(), (err) => {
if (err) {
console.error('Failed to publish to MQTT:', err)
}
})
}
// Proceed to the next middleware
next()
}
export default sendToMqtt

View File

@ -0,0 +1,24 @@
import { NextFunction, Request, Response } from "express"
import { WSQuery } from "../types"
// Validate input parameters
const validateParamsMiddleware = (MPLABS_ID: string, MPLABS_PASSWORD: string) => (req: Request, res: Response, next: NextFunction) => {
const { ID, PASSWORD, action, dateutc } = req.query as WSQuery
// Validate action and dateutc
if (action !== 'updateraww') {
res.status(400).send('Invalid action')
} else if (dateutc !== 'now' && isNaN(Date.parse(dateutc))) {
res.status(400).send('Invalid dateutc format')
}
// Validate ID and PASSWORD (this is where you would verify against your database)
// For now, we'll mock this validation
else if (ID !== MPLABS_ID || PASSWORD !== MPLABS_PASSWORD) {
return res.status(401).send('INVALIDPASSWORDID|Password and/or id are incorrect')
} else {
next()
}
}
export default validateParamsMiddleware

View File

@ -0,0 +1,21 @@
export type WSKeys =
| 'ID'
| 'PASSWORD'
| 'action'
| 'realtime'
| 'rtfreq'
| 'dateutc'
| 'baromin'
| 'tempf'
| 'dewptf'
| 'humidity'
| 'windspeedmph'
| 'windgustmph'
| 'winddir'
| 'rainin'
| 'dailyrainin'
| 'solarradiation'
| 'UV'
| 'indoortempf'
| 'indoorhumidity'
export type WSQuery = Record<WSKeys, string>

View File

@ -0,0 +1,11 @@
// This function, omit, takes an object and an array of property keys, returning a new object
// that excludes the specified properties. It ensures type safety by allowing only valid keys
// from the original object to be omitted, without mutating the original data.
export function omit<T extends object, K extends keyof T>(obj: T, ...props: K[]): Omit<T, K> {
return (Object.keys(obj) as K[]).reduce((acc, key) => {
if (!props.includes(key)) {
acc[key] = obj[key]
}
return acc
}, {} as T)
}

View File

@ -0,0 +1,62 @@
import { WSQuery } from '../types'
// Returns the current UTC time in the format
// YYYY-MM-DD HH:MM:SS
const getCurrentTimeString = () => {
const now = new Date()
const formatter = new Intl.DateTimeFormat('en-US', {
year: 'numeric',
month: '2-digit',
day: '2-digit',
hour: '2-digit',
minute: '2-digit',
second: '2-digit',
hour12: false,
timeZone: 'UTC',
})
return formatter.format(now).replace(',', '')
}
// Convert miles per hour to kilometers per hour
const mphToKph = (mph: string) => (parseFloat(mph) * 1.60934).toFixed(1)
// Convert Fahreneheit to Celsius
const fToC = (f: string) => (((parseFloat(f) - 32) * 5) / 9).toFixed(1)
// Convert inches Hg to hectopascal
const inHgTohPa = (inHg: string) => (parseFloat(inHg) * 33.8639).toFixed(1)
// Convert inches to mm
const inchToCm = (inch: string) => (parseFloat(inch) * 25.4).toFixed(1)
// Calculate the wet bulb temperature
//
const wetbulbtemperature = (tempf: string, humidity: string) => {
const T = ((parseFloat(tempf) - 32) * 5) / 9
const RH = parseFloat(humidity)
return (
T * Math.atan(0.151977 * Math.sqrt(RH + 8.313659)) +
Math.atan(T + RH) -
Math.atan(RH - 1.676331) +
0.00391838 * Math.pow(RH, 1.5) * Math.atan(0.023101 * RH) -
4.686035
).toFixed(2)
}
const transformData = (
params: Omit<WSQuery, 'action' | 'ID' | 'PASSWORD' | 'realtime' | 'rtfreq'>
) => ({
...params,
dateutc: params.dateutc === 'now' ? getCurrentTimeString() : params.dateutc,
windspeedkph: mphToKph(params.windspeedmph),
windgustkph: mphToKph(params.windgustmph),
tempc: fToC(params.tempf),
dewptc: fToC(params.dewptf),
baromhpa: inHgTohPa(params.baromin),
rainmm: inchToCm(params.rainin),
dailyrainmm: inchToCm(params.dailyrainin),
indoortempc: fToC(params.indoortempf),
wetbulbtemperature: wetbulbtemperature(params.tempf, params.humidity),
})
export default transformData

342
package-lock.json generated
View File

@ -11,9 +11,11 @@
"dependencies": { "dependencies": {
"@types/morgan": "^1.9.9", "@types/morgan": "^1.9.9",
"axios": "^1.6.0", "axios": "^1.6.0",
"dotenv": "^16.4.5",
"express": "~4.18.1", "express": "~4.18.1",
"express-validator": "^7.2.0", "express-validator": "^7.2.0",
"morgan": "^1.10.0", "morgan": "^1.10.0",
"mqtt": "^5.10.1",
"tslib": "^2.3.0" "tslib": "^2.3.0"
}, },
"devDependencies": { "devDependencies": {
@ -1917,7 +1919,6 @@
"version": "7.25.6", "version": "7.25.6",
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.25.6.tgz", "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.25.6.tgz",
"integrity": "sha512-VBj9MYyDb9tuLq7yzqjgzt6Q+IBQLrGZfdjOekyEirZPHxXWoTSGUTMrpsfi58Up73d13NfYLv8HT9vmznjzhQ==", "integrity": "sha512-VBj9MYyDb9tuLq7yzqjgzt6Q+IBQLrGZfdjOekyEirZPHxXWoTSGUTMrpsfi58Up73d13NfYLv8HT9vmznjzhQ==",
"dev": true,
"dependencies": { "dependencies": {
"regenerator-runtime": "^0.14.0" "regenerator-runtime": "^0.14.0"
}, },
@ -3943,6 +3944,20 @@
"integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==", "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==",
"dev": true "dev": true
}, },
"node_modules/@types/readable-stream": {
"version": "4.0.15",
"resolved": "https://registry.npmjs.org/@types/readable-stream/-/readable-stream-4.0.15.tgz",
"integrity": "sha512-oAZ3kw+kJFkEqyh7xORZOku1YAKvsFTogRY8kVl4vHpEKiDkfnSA/My8haRE7fvmix5Zyy+1pwzOi7yycGLBJw==",
"dependencies": {
"@types/node": "*",
"safe-buffer": "~5.1.1"
}
},
"node_modules/@types/readable-stream/node_modules/safe-buffer": {
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
},
"node_modules/@types/send": { "node_modules/@types/send": {
"version": "0.17.4", "version": "0.17.4",
"resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.4.tgz", "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.4.tgz",
@ -3970,6 +3985,14 @@
"integrity": "sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==", "integrity": "sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==",
"dev": true "dev": true
}, },
"node_modules/@types/ws": {
"version": "8.5.12",
"resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.12.tgz",
"integrity": "sha512-3tPRkv1EtkDpzlgyKyI8pGsGZAGPEaXeu0DOj5DI25Ja91bdAYddYHbADRYVrZMRbfW+1l5YwXVDKohDJNQxkQ==",
"dependencies": {
"@types/node": "*"
}
},
"node_modules/@types/yargs": { "node_modules/@types/yargs": {
"version": "17.0.33", "version": "17.0.33",
"resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.33.tgz", "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.33.tgz",
@ -4228,6 +4251,17 @@
"integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
"dev": true "dev": true
}, },
"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==",
"dependencies": {
"event-target-shim": "^5.0.0"
},
"engines": {
"node": ">=6.5"
}
},
"node_modules/accepts": { "node_modules/accepts": {
"version": "1.3.8", "version": "1.3.8",
"resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz",
@ -4620,7 +4654,6 @@
"version": "1.5.1", "version": "1.5.1",
"resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
"integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==",
"dev": true,
"funding": [ "funding": [
{ {
"type": "github", "type": "github",
@ -4800,8 +4833,7 @@
"node_modules/buffer-from": { "node_modules/buffer-from": {
"version": "1.1.2", "version": "1.1.2",
"resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz",
"integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ=="
"dev": true
}, },
"node_modules/bytes": { "node_modules/bytes": {
"version": "3.1.2", "version": "3.1.2",
@ -5024,12 +5056,31 @@
"node": ">= 0.8" "node": ">= 0.8"
} }
}, },
"node_modules/commist": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/commist/-/commist-3.2.0.tgz",
"integrity": "sha512-4PIMoPniho+LqXmpS5d3NuGYncG6XWlkBSVGiWycL22dd42OYdUGil2CWuzklaJoNxyxUSpO4MKIBU94viWNAw=="
},
"node_modules/concat-map": { "node_modules/concat-map": {
"version": "0.0.1", "version": "0.0.1",
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
"integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==",
"dev": true "dev": true
}, },
"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"
],
"dependencies": {
"buffer-from": "^1.0.0",
"inherits": "^2.0.3",
"readable-stream": "^3.0.2",
"typedarray": "^0.0.6"
}
},
"node_modules/confusing-browser-globals": { "node_modules/confusing-browser-globals": {
"version": "1.0.11", "version": "1.0.11",
"resolved": "https://registry.npmjs.org/confusing-browser-globals/-/confusing-browser-globals-1.0.11.tgz", "resolved": "https://registry.npmjs.org/confusing-browser-globals/-/confusing-browser-globals-1.0.11.tgz",
@ -5148,7 +5199,6 @@
"version": "4.3.7", "version": "4.3.7",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz",
"integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==",
"dev": true,
"dependencies": { "dependencies": {
"ms": "^2.1.3" "ms": "^2.1.3"
}, },
@ -5310,7 +5360,6 @@
"version": "16.4.5", "version": "16.4.5",
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.5.tgz", "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.5.tgz",
"integrity": "sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==", "integrity": "sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==",
"dev": true,
"engines": { "engines": {
"node": ">=12" "node": ">=12"
}, },
@ -5792,6 +5841,22 @@
"node": ">= 0.6" "node": ">= 0.6"
} }
}, },
"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==",
"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==",
"engines": {
"node": ">=0.8.x"
}
},
"node_modules/execa": { "node_modules/execa": {
"version": "5.1.1", "version": "5.1.1",
"resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz",
@ -5952,6 +6017,18 @@
"integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==",
"dev": true "dev": true
}, },
"node_modules/fast-unique-numbers": {
"version": "8.0.13",
"resolved": "https://registry.npmjs.org/fast-unique-numbers/-/fast-unique-numbers-8.0.13.tgz",
"integrity": "sha512-7OnTFAVPefgw2eBJ1xj2PGGR9FwYzSUso9decayHgCDX4sJkHLdcsYTytTg+tYv+wKF3U8gJuSBz2jJpQV4u/g==",
"dependencies": {
"@babel/runtime": "^7.23.8",
"tslib": "^2.6.2"
},
"engines": {
"node": ">=16.1.0"
}
},
"node_modules/fastq": { "node_modules/fastq": {
"version": "1.17.1", "version": "1.17.1",
"resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz", "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz",
@ -6467,6 +6544,11 @@
"node": ">= 0.4" "node": ">= 0.4"
} }
}, },
"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=="
},
"node_modules/hosted-git-info": { "node_modules/hosted-git-info": {
"version": "7.0.2", "version": "7.0.2",
"resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-7.0.2.tgz", "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-7.0.2.tgz",
@ -6542,7 +6624,6 @@
"version": "1.2.1", "version": "1.2.1",
"resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz",
"integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==",
"dev": true,
"funding": [ "funding": [
{ {
"type": "github", "type": "github",
@ -7517,6 +7598,15 @@
"url": "https://github.com/chalk/supports-color?sponsor=1" "url": "https://github.com/chalk/supports-color?sponsor=1"
} }
}, },
"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==",
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/js-sdsl"
}
},
"node_modules/js-tokens": { "node_modules/js-tokens": {
"version": "4.0.0", "version": "4.0.0",
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
@ -7866,7 +7956,6 @@
"version": "1.2.8", "version": "1.2.8",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz",
"integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==",
"dev": true,
"funding": { "funding": {
"url": "https://github.com/sponsors/ljharb" "url": "https://github.com/sponsors/ljharb"
} }
@ -7910,6 +7999,139 @@
"node": ">= 0.8" "node": ">= 0.8"
} }
}, },
"node_modules/mqtt": {
"version": "5.10.1",
"resolved": "https://registry.npmjs.org/mqtt/-/mqtt-5.10.1.tgz",
"integrity": "sha512-hXCOki8sANoQ7w+2OzJzg6qMBxTtrH9RlnVNV8panLZgnl+Gh0J/t4k6r8Az8+C7y3KAcyXtn0mmLixyUom8Sw==",
"dependencies": {
"@types/readable-stream": "^4.0.5",
"@types/ws": "^8.5.9",
"commist": "^3.2.0",
"concat-stream": "^2.0.0",
"debug": "^4.3.4",
"help-me": "^5.0.0",
"lru-cache": "^10.0.1",
"minimist": "^1.2.8",
"mqtt-packet": "^9.0.0",
"number-allocator": "^1.0.14",
"readable-stream": "^4.4.2",
"reinterval": "^1.1.0",
"rfdc": "^1.3.0",
"split2": "^4.2.0",
"worker-timers": "^7.1.4",
"ws": "^8.17.1"
},
"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.0",
"resolved": "https://registry.npmjs.org/mqtt-packet/-/mqtt-packet-9.0.0.tgz",
"integrity": "sha512-8v+HkX+fwbodsWAZIZTI074XIoxVBOmPeggQuDFCGg1SqNcC+uoRMWu7J6QlJPqIUIJXmjNYYHxBBLr1Y/Df4w==",
"dependencies": {
"bl": "^6.0.8",
"debug": "^4.3.4",
"process-nextick-args": "^2.0.1"
}
},
"node_modules/mqtt-packet/node_modules/bl": {
"version": "6.0.16",
"resolved": "https://registry.npmjs.org/bl/-/bl-6.0.16.tgz",
"integrity": "sha512-V/kz+z2Mx5/6qDfRCilmrukUXcXuCoXKg3/3hDvzKKoSUx8CJKudfIoT29XZc3UE9xBvxs5qictiHdprwtteEg==",
"dependencies": {
"@types/readable-stream": "^4.0.0",
"buffer": "^6.0.3",
"inherits": "^2.0.4",
"readable-stream": "^4.2.0"
}
},
"node_modules/mqtt-packet/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"
}
],
"dependencies": {
"base64-js": "^1.3.1",
"ieee754": "^1.2.1"
}
},
"node_modules/mqtt-packet/node_modules/readable-stream": {
"version": "4.5.2",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.5.2.tgz",
"integrity": "sha512-yjavECdqeZ3GLXNgRXgeQEdz9fvDDkNKyHnbHRFtOr7/LcfgBcmct7t/ET+HaCTqfh06OzoAxrkN/IfjJBVe+g==",
"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/mqtt/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"
}
],
"dependencies": {
"base64-js": "^1.3.1",
"ieee754": "^1.2.1"
}
},
"node_modules/mqtt/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=="
},
"node_modules/mqtt/node_modules/readable-stream": {
"version": "4.5.2",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.5.2.tgz",
"integrity": "sha512-yjavECdqeZ3GLXNgRXgeQEdz9fvDDkNKyHnbHRFtOr7/LcfgBcmct7t/ET+HaCTqfh06OzoAxrkN/IfjJBVe+g==",
"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/ms": { "node_modules/ms": {
"version": "2.1.3", "version": "2.1.3",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
@ -7983,6 +8205,15 @@
"node": ">=8" "node": ">=8"
} }
}, },
"node_modules/number-allocator": {
"version": "1.0.14",
"resolved": "https://registry.npmjs.org/number-allocator/-/number-allocator-1.0.14.tgz",
"integrity": "sha512-OrL44UTVAvkKdOdRQZIJpLkAdjXGTRda052sN4sO77bKEzYYqWKMBjQvrJFzqygI99gL6Z4u2xctPW1tB8ErvA==",
"dependencies": {
"debug": "^4.3.1",
"js-sdsl": "4.3.0"
}
},
"node_modules/nx": { "node_modules/nx": {
"version": "19.7.3", "version": "19.7.3",
"resolved": "https://registry.npmjs.org/nx/-/nx-19.7.3.tgz", "resolved": "https://registry.npmjs.org/nx/-/nx-19.7.3.tgz",
@ -8405,6 +8636,19 @@
"node": "^14.17.0 || ^16.13.0 || >=18.0.0" "node": "^14.17.0 || ^16.13.0 || >=18.0.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==",
"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=="
},
"node_modules/prompts": { "node_modules/prompts": {
"version": "2.4.2", "version": "2.4.2",
"resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz",
@ -8526,7 +8770,6 @@
"version": "3.6.2", "version": "3.6.2",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz",
"integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==",
"dev": true,
"dependencies": { "dependencies": {
"inherits": "^2.0.3", "inherits": "^2.0.3",
"string_decoder": "^1.1.1", "string_decoder": "^1.1.1",
@ -8557,8 +8800,7 @@
"node_modules/regenerator-runtime": { "node_modules/regenerator-runtime": {
"version": "0.14.1", "version": "0.14.1",
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz",
"integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==", "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw=="
"dev": true
}, },
"node_modules/regenerator-transform": { "node_modules/regenerator-transform": {
"version": "0.15.2", "version": "0.15.2",
@ -8607,6 +8849,11 @@
"jsesc": "bin/jsesc" "jsesc": "bin/jsesc"
} }
}, },
"node_modules/reinterval": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/reinterval/-/reinterval-1.1.0.tgz",
"integrity": "sha512-QIRet3SYrGp0HUHO88jVskiG6seqUGC5iAG7AwI/BV4ypGcuqk9Du6YQBUOUqm9c8pw1eyLoIaONifRua1lsEQ=="
},
"node_modules/require-directory": { "node_modules/require-directory": {
"version": "2.1.1", "version": "2.1.1",
"resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
@ -8686,6 +8933,11 @@
"node": ">=0.10.0" "node": ">=0.10.0"
} }
}, },
"node_modules/rfdc": {
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.4.1.tgz",
"integrity": "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA=="
},
"node_modules/rimraf": { "node_modules/rimraf": {
"version": "3.0.2", "version": "3.0.2",
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz",
@ -8910,6 +9162,14 @@
"source-map": "^0.6.0" "source-map": "^0.6.0"
} }
}, },
"node_modules/split2": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz",
"integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==",
"engines": {
"node": ">= 10.x"
}
},
"node_modules/sprintf-js": { "node_modules/sprintf-js": {
"version": "1.0.3", "version": "1.0.3",
"resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
@ -8949,7 +9209,6 @@
"version": "1.3.0", "version": "1.3.0",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz",
"integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==",
"dev": true,
"dependencies": { "dependencies": {
"safe-buffer": "~5.2.0" "safe-buffer": "~5.2.0"
} }
@ -9348,6 +9607,11 @@
"node": ">= 0.6" "node": ">= 0.6"
} }
}, },
"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=="
},
"node_modules/typescript": { "node_modules/typescript": {
"version": "5.5.4", "version": "5.5.4",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.5.4.tgz", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.5.4.tgz",
@ -9460,8 +9724,7 @@
"node_modules/util-deprecate": { "node_modules/util-deprecate": {
"version": "1.0.2", "version": "1.0.2",
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
"integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="
"dev": true
}, },
"node_modules/utils-merge": { "node_modules/utils-merge": {
"version": "1.0.1", "version": "1.0.1",
@ -9558,6 +9821,37 @@
"node": ">=0.10.0" "node": ">=0.10.0"
} }
}, },
"node_modules/worker-timers": {
"version": "7.1.8",
"resolved": "https://registry.npmjs.org/worker-timers/-/worker-timers-7.1.8.tgz",
"integrity": "sha512-R54psRKYVLuzff7c1OTFcq/4Hue5Vlz4bFtNEIarpSiCYhpifHU3aIQI29S84o1j87ePCYqbmEJPqwBTf+3sfw==",
"dependencies": {
"@babel/runtime": "^7.24.5",
"tslib": "^2.6.2",
"worker-timers-broker": "^6.1.8",
"worker-timers-worker": "^7.0.71"
}
},
"node_modules/worker-timers-broker": {
"version": "6.1.8",
"resolved": "https://registry.npmjs.org/worker-timers-broker/-/worker-timers-broker-6.1.8.tgz",
"integrity": "sha512-FUCJu9jlK3A8WqLTKXM9E6kAmI/dR1vAJ8dHYLMisLNB/n3GuaFIjJ7pn16ZcD1zCOf7P6H62lWIEBi+yz/zQQ==",
"dependencies": {
"@babel/runtime": "^7.24.5",
"fast-unique-numbers": "^8.0.13",
"tslib": "^2.6.2",
"worker-timers-worker": "^7.0.71"
}
},
"node_modules/worker-timers-worker": {
"version": "7.0.71",
"resolved": "https://registry.npmjs.org/worker-timers-worker/-/worker-timers-worker-7.0.71.tgz",
"integrity": "sha512-ks/5YKwZsto1c2vmljroppOKCivB/ma97g9y77MAAz2TBBjPPgpoOiS1qYQKIgvGTr2QYPT3XhJWIB6Rj2MVPQ==",
"dependencies": {
"@babel/runtime": "^7.24.5",
"tslib": "^2.6.2"
}
},
"node_modules/wrap-ansi": { "node_modules/wrap-ansi": {
"version": "7.0.0", "version": "7.0.0",
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
@ -9594,6 +9888,26 @@
"node": "^12.13.0 || ^14.15.0 || >=16.0.0" "node": "^12.13.0 || ^14.15.0 || >=16.0.0"
} }
}, },
"node_modules/ws": {
"version": "8.18.0",
"resolved": "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz",
"integrity": "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==",
"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
}
}
},
"node_modules/y18n": { "node_modules/y18n": {
"version": "5.0.8", "version": "5.0.8",
"resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz",

View File

@ -7,9 +7,11 @@
"dependencies": { "dependencies": {
"@types/morgan": "^1.9.9", "@types/morgan": "^1.9.9",
"axios": "^1.6.0", "axios": "^1.6.0",
"dotenv": "^16.4.5",
"express": "~4.18.1", "express": "~4.18.1",
"express-validator": "^7.2.0", "express-validator": "^7.2.0",
"morgan": "^1.10.0", "morgan": "^1.10.0",
"mqtt": "^5.10.1",
"tslib": "^2.3.0" "tslib": "^2.3.0"
}, },
"devDependencies": { "devDependencies": {