diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index ef9da07..27ee442 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -18,11 +18,6 @@ jobs:
with:
fetch-depth: 0
- # This enables task distribution via Nx Cloud
- # Run this command as early as possible, before dependencies are installed
- # Learn more at https://nx.dev/ci/reference/nx-cloud-cli#npx-nxcloud-startcirun
- - run: npx nx-cloud start-ci-run --distribute-on="3 linux-medium-js" --stop-agents-after="build"
-
# Cache node_modules
- uses: actions/setup-node@v4
with:
@@ -33,6 +28,6 @@ jobs:
- uses: nrwl/nx-set-shas@v4
# Prepend any command with "nx-cloud record --" to record its logs to Nx Cloud
- # - run: npx nx-cloud record -- echo Hello World
+ # - run: npx nx record -- echo Hello World
# Nx Affected runs only tasks affected by the changes in this PR/commit. Learn more: https://nx.dev/ci/features/affected
- run: npx nx affected -t lint test build
diff --git a/.gitignore b/.gitignore
index 4f4d87b..01d8498 100644
--- a/.gitignore
+++ b/.gitignore
@@ -40,3 +40,4 @@ Thumbs.db
.nx/cache
.nx/workspace-data
+.env
diff --git a/.prettierrc b/.prettierrc
index e87976a..602f24d 100644
--- a/.prettierrc
+++ b/.prettierrc
@@ -1,4 +1,5 @@
{
+ "printWidth": 100,
"semi": false,
"singleQuote": true,
"tabWidth": 4,
diff --git a/apps/mqtt-gateway/.env.example b/apps/mqtt-gateway/.env.example
new file mode 100644
index 0000000..f22e77e
--- /dev/null
+++ b/apps/mqtt-gateway/.env.example
@@ -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
diff --git a/apps/mqtt-gateway/Dockerfile b/apps/mqtt-gateway/Dockerfile
index 57e9d7f..d4de5a0 100644
--- a/apps/mqtt-gateway/Dockerfile
+++ b/apps/mqtt-gateway/Dockerfile
@@ -12,7 +12,7 @@ ENV PORT=3000
WORKDIR /app
RUN addgroup --system mqtt-gateway && \
- adduser --system -G mqtt-gateway mqtt-gateway
+ adduser --system -G mqtt-gateway mqtt-gateway
COPY dist/apps/mqtt-gateway mqtt-gateway/
RUN chown -R mqtt-gateway:mqtt-gateway .
diff --git a/apps/mqtt-gateway/compose.yml b/apps/mqtt-gateway/compose.yml
new file mode 100644
index 0000000..0493be0
--- /dev/null
+++ b/apps/mqtt-gateway/compose.yml
@@ -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
diff --git a/apps/mqtt-gateway/src/chatgpt.ts b/apps/mqtt-gateway/src/chatgpt.ts
deleted file mode 100644
index 15e3cd9..0000000
--- a/apps/mqtt-gateway/src/chatgpt.ts
+++ /dev/null
@@ -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
usage
Required fields missing: ID, PASSWORD, action, and dateutc'
- )
- }
-
- const { ID, PASSWORD, action, dateutc } = params as Record
-
- // 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}`)
-})
diff --git a/apps/mqtt-gateway/src/main.ts b/apps/mqtt-gateway/src/main.ts
index 5cdd3a5..3624239 100644
--- a/apps/mqtt-gateway/src/main.ts
+++ b/apps/mqtt-gateway/src/main.ts
@@ -1,43 +1,57 @@
-import express from 'express';
-import { checkSchema, Schema } from 'express-validator'
+import dotenv from 'dotenv'
+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';
-const port = process.env.PORT ? Number(process.env.PORT) : 3000;
+dotenv.config()
-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 inHgTohpa: (inHg: number) => number = (inHg) => inHg * 33.86389
-const mphToKmh: (mph: number) => number = (mph) => mph * 1.609344
-const mphToMs: (mph: number) => number = (mph) => mph * 0.44704
+const app = express()
+const port = 3000
-type UpdateweatherstationQuery = Record & {
- action: 'updateraw'
- ID: string
- KEY: string
-}
+// MQTT Client setup
+const mqttClient = mqtt.connect(MQTT_BROKER_URL) // Replace with your MQTT server URL and port
-app.get('/weatherstation/updateweatherstation.php', (req, res) => {
- const query = req.query as Record
+mqttClient.on('connect', () => {
+ console.log('Connected to MQTT broker')
+})
- // Validate only the required fields for now
- const { action, ID, KEY } = query
+mqttClient.on('error', (err) => {
+ console.error('MQTT connection error:', err)
+})
- 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))
+app.use(morgan(':method :url :status :res[content-length] - :response-time ms'))
- res.send('OK')
-});
+// Routes
+app.get('/', (req: Request, res: Response) => {
+ res.send('AWEKAS Data Server URL')
+})
-app.listen(port, host, () => {
- console.log(`[ ready ] http://${host}:${port}`);
-});
+// 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)
+ }
+)
+
+// Start the server
+app.listen(port, () => {
+ console.log(`Server is running at http://localhost:${port}`)
+})
diff --git a/apps/mqtt-gateway/src/middleware/checkRequiredFields.ts b/apps/mqtt-gateway/src/middleware/checkRequiredFields.ts
new file mode 100644
index 0000000..3d608b5
--- /dev/null
+++ b/apps/mqtt-gateway/src/middleware/checkRequiredFields.ts
@@ -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
Required fields missing: ID, PASSWORD, action, and dateutc`
+ )
+ } else {
+ next()
+ }
+}
+
+export default checkRequiredFieldsMiddleware
diff --git a/apps/mqtt-gateway/src/middleware/forwardData.ts b/apps/mqtt-gateway/src/middleware/forwardData.ts
new file mode 100644
index 0000000..8bfe8a3
--- /dev/null
+++ b/apps/mqtt-gateway/src/middleware/forwardData.ts
@@ -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
diff --git a/apps/mqtt-gateway/src/middleware/sentToMqtt.ts b/apps/mqtt-gateway/src/middleware/sentToMqtt.ts
new file mode 100644
index 0000000..6d37b35
--- /dev/null
+++ b/apps/mqtt-gateway/src/middleware/sentToMqtt.ts
@@ -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
diff --git a/apps/mqtt-gateway/src/middleware/validateParams.ts b/apps/mqtt-gateway/src/middleware/validateParams.ts
new file mode 100644
index 0000000..1e6adc3
--- /dev/null
+++ b/apps/mqtt-gateway/src/middleware/validateParams.ts
@@ -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
diff --git a/apps/mqtt-gateway/src/types.ts b/apps/mqtt-gateway/src/types.ts
new file mode 100644
index 0000000..9e3e462
--- /dev/null
+++ b/apps/mqtt-gateway/src/types.ts
@@ -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
diff --git a/apps/mqtt-gateway/src/utils/object.ts b/apps/mqtt-gateway/src/utils/object.ts
new file mode 100644
index 0000000..a4458da
--- /dev/null
+++ b/apps/mqtt-gateway/src/utils/object.ts
@@ -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(obj: T, ...props: K[]): Omit {
+ return (Object.keys(obj) as K[]).reduce((acc, key) => {
+ if (!props.includes(key)) {
+ acc[key] = obj[key]
+ }
+ return acc
+ }, {} as T)
+}
diff --git a/apps/mqtt-gateway/src/utils/transformData.ts b/apps/mqtt-gateway/src/utils/transformData.ts
new file mode 100644
index 0000000..6c7e1a5
--- /dev/null
+++ b/apps/mqtt-gateway/src/utils/transformData.ts
@@ -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
+) => ({
+ ...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
diff --git a/package-lock.json b/package-lock.json
index 0636d20..784c9ae 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -11,9 +11,11 @@
"dependencies": {
"@types/morgan": "^1.9.9",
"axios": "^1.6.0",
+ "dotenv": "^16.4.5",
"express": "~4.18.1",
"express-validator": "^7.2.0",
"morgan": "^1.10.0",
+ "mqtt": "^5.10.1",
"tslib": "^2.3.0"
},
"devDependencies": {
@@ -1917,7 +1919,6 @@
"version": "7.25.6",
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.25.6.tgz",
"integrity": "sha512-VBj9MYyDb9tuLq7yzqjgzt6Q+IBQLrGZfdjOekyEirZPHxXWoTSGUTMrpsfi58Up73d13NfYLv8HT9vmznjzhQ==",
- "dev": true,
"dependencies": {
"regenerator-runtime": "^0.14.0"
},
@@ -3943,6 +3944,20 @@
"integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==",
"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": {
"version": "0.17.4",
"resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.4.tgz",
@@ -3970,6 +3985,14 @@
"integrity": "sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==",
"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": {
"version": "17.0.33",
"resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.33.tgz",
@@ -4228,6 +4251,17 @@
"integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
"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": {
"version": "1.3.8",
"resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz",
@@ -4620,7 +4654,6 @@
"version": "1.5.1",
"resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
"integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==",
- "dev": true,
"funding": [
{
"type": "github",
@@ -4800,8 +4833,7 @@
"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==",
- "dev": true
+ "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ=="
},
"node_modules/bytes": {
"version": "3.1.2",
@@ -5024,12 +5056,31 @@
"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": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
"integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==",
"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": {
"version": "1.0.11",
"resolved": "https://registry.npmjs.org/confusing-browser-globals/-/confusing-browser-globals-1.0.11.tgz",
@@ -5148,7 +5199,6 @@
"version": "4.3.7",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz",
"integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==",
- "dev": true,
"dependencies": {
"ms": "^2.1.3"
},
@@ -5310,7 +5360,6 @@
"version": "16.4.5",
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.5.tgz",
"integrity": "sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==",
- "dev": true,
"engines": {
"node": ">=12"
},
@@ -5792,6 +5841,22 @@
"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": {
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz",
@@ -5952,6 +6017,18 @@
"integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==",
"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": {
"version": "1.17.1",
"resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz",
@@ -6467,6 +6544,11 @@
"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": {
"version": "7.0.2",
"resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-7.0.2.tgz",
@@ -6542,7 +6624,6 @@
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz",
"integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==",
- "dev": true,
"funding": [
{
"type": "github",
@@ -7517,6 +7598,15 @@
"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": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
@@ -7866,7 +7956,6 @@
"version": "1.2.8",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz",
"integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==",
- "dev": true,
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
@@ -7910,6 +7999,139 @@
"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": {
"version": "2.1.3",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
@@ -7983,6 +8205,15 @@
"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": {
"version": "19.7.3",
"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_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": {
"version": "2.4.2",
"resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz",
@@ -8526,7 +8770,6 @@
"version": "3.6.2",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz",
"integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==",
- "dev": true,
"dependencies": {
"inherits": "^2.0.3",
"string_decoder": "^1.1.1",
@@ -8557,8 +8800,7 @@
"node_modules/regenerator-runtime": {
"version": "0.14.1",
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz",
- "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==",
- "dev": true
+ "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw=="
},
"node_modules/regenerator-transform": {
"version": "0.15.2",
@@ -8607,6 +8849,11 @@
"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": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
@@ -8686,6 +8933,11 @@
"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": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz",
@@ -8910,6 +9162,14 @@
"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": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
@@ -8949,7 +9209,6 @@
"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==",
- "dev": true,
"dependencies": {
"safe-buffer": "~5.2.0"
}
@@ -9348,6 +9607,11 @@
"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": {
"version": "5.5.4",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.5.4.tgz",
@@ -9460,8 +9724,7 @@
"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==",
- "dev": true
+ "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="
},
"node_modules/utils-merge": {
"version": "1.0.1",
@@ -9558,6 +9821,37 @@
"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": {
"version": "7.0.0",
"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_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": {
"version": "5.0.8",
"resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz",
diff --git a/package.json b/package.json
index 224f832..4f5b511 100644
--- a/package.json
+++ b/package.json
@@ -7,9 +7,11 @@
"dependencies": {
"@types/morgan": "^1.9.9",
"axios": "^1.6.0",
+ "dotenv": "^16.4.5",
"express": "~4.18.1",
"express-validator": "^7.2.0",
"morgan": "^1.10.0",
+ "mqtt": "^5.10.1",
"tslib": "^2.3.0"
},
"devDependencies": {