├── .dockerignore ├── .github ├── FUNDING.yml ├── dependabot.yml └── workflows │ └── publish.yml ├── .gitignore ├── Dockerfile ├── LICENSE ├── README.md ├── package.json ├── src ├── config.ts ├── config_schema.ts ├── home_assistant.ts ├── index.ts ├── log.ts ├── mqtt.ts ├── net-snmp.d.ts ├── safe-eval.d.ts ├── snmp.ts ├── types.ts └── util.ts ├── tsconfig.json └── yarn.lock /.dockerignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | npm-debug.log 3 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | custom: https://www.buymeacoffee.com/dchesterton 2 | 3 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: "npm" 4 | directory: "/" 5 | schedule: 6 | interval: "daily" 7 | ignore: 8 | - dependency-name: "@types/node" 9 | commit-message: 10 | prefix: "" 11 | prefix-development: "[ci skip]" 12 | - package-ecosystem: "github-actions" 13 | directory: "/" 14 | schedule: 15 | interval: "daily" 16 | commit-message: 17 | prefix: "[ci skip]" 18 | -------------------------------------------------------------------------------- /.github/workflows/publish.yml: -------------------------------------------------------------------------------- 1 | name: Publish 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | 8 | jobs: 9 | docker-publish: 10 | name: Publish to Docker Hub 11 | if: "!contains(github.event.head_commit.message, '[ci skip]')" 12 | runs-on: ubuntu-20.04 13 | steps: 14 | - name: Checkout code 15 | uses: actions/checkout@v2.4.0 16 | - name: Automated version bump 17 | uses: phips28/gh-action-bump-version@master 18 | id: bump 19 | env: 20 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 21 | - name: Extract version 22 | run: | 23 | RE='[^0-9]*\([0-9]*\)[.]\([0-9]*\)[.]\([0-9]*\)\([0-9A-Za-z-]*\)' 24 | MAJOR=`echo $version | sed -e "s#$RE#\1#"` 25 | MINOR=`echo $version | sed -e "s#$RE#\2#"` 26 | PATCH=`echo $version | sed -e "s#$RE#\3#"` 27 | echo "::set-output name=version::$MAJOR.$MINOR.$PATCH" 28 | echo "::set-output name=major::$MAJOR" 29 | echo "::set-output name=minor::$MAJOR.$MINOR" 30 | id: version 31 | env: 32 | version: ${{ steps.bump.outputs.newTag }} 33 | - name: Set up QEMU 34 | uses: docker/setup-qemu-action@v1.2.0 35 | - name: Set up Docker Buildx 36 | uses: docker/setup-buildx-action@v1.6.0 37 | - name: Login to DockerHub 38 | uses: docker/login-action@v1.12.0 39 | with: 40 | username: ${{ secrets.DOCKER_USERNAME }} 41 | password: ${{ secrets.DOCKER_PASSWORD }} 42 | - name: Build and push 43 | uses: docker/build-push-action@v2.7.0 44 | with: 45 | push: true 46 | platforms: linux/amd64,linux/arm/v6,linux/arm/v7,linux/arm64/v8,linux/ppc64le,linux/s390x 47 | tags: | 48 | dchesterton/snmp2mqtt:latest 49 | dchesterton/snmp2mqtt:${{ steps.version.outputs.version }} 50 | labels: "version=${{ steps.version.outputs.version }}" 51 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | coverage 3 | dist 4 | node_modules 5 | config.yml 6 | yarn-error.log 7 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:16-alpine AS base 2 | FROM base as builder 3 | 4 | WORKDIR /app 5 | 6 | COPY package.json /app 7 | COPY yarn.lock /app 8 | COPY tsconfig.json /app 9 | COPY src /app/src 10 | 11 | RUN yarn install --frozen-lockfile 12 | RUN yarn build 13 | 14 | RUN mv /app/node_modules /app/node_modules_dev 15 | RUN yarn install --frozen-lockfile --production 16 | 17 | FROM base 18 | STOPSIGNAL SIGINT 19 | WORKDIR /app 20 | 21 | COPY --from=builder /app/node_modules /app/node_modules 22 | COPY --from=builder /app/dist /app/dist 23 | COPY --from=builder /app/package.json /app/package.json 24 | 25 | CMD [ "node", "/app/dist/index.js" ] 26 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Daniel Chesterton 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # snmp2mqtt 2 | 3 | Expose SNMP sensors to MQTT. 4 | 5 | ## config.yml 6 | 7 | ```yaml 8 | log: debug # Optional: debug, info, warning or error (default: info) 9 | 10 | mqtt: 11 | host: 192.168.1.5 # Optional: broker URL or IP address (default: localhost) 12 | port: 1884 # Optional: broker port (default: 1883 or 8883 for TLS connections) 13 | username: my_user # Optional: broker user (default: none) 14 | password: my_password # Optional: broker password (default: none) 15 | client_id: snmp2mqtt # Optional: client ID (default: snmp2mqtt) 16 | keepalive: 30 # Optional: keepalive in seconds (default: 10) 17 | clean: true # Optional: clean session (default: true) 18 | retain: true # Optional: retain (default: true) 19 | qos: 2 # Optional: QoS (default: 0) 20 | ca: /cert/ca.pem # Optional: CA for TLS connection (default: none) 21 | cert: /cert/cert.pem # Optional: certificate for TLS connection (default: none) 22 | key: /cert/key.pem # Optional: private key for TLS connection (default: none) 23 | reject_unauthorized: true # Optional: if not false, the server certificate is verified against the list of supplied CAs. Override with caution (default: true when using TLS) 24 | 25 | homeassistant: 26 | discovery: true # Optional: enable Home Assistant discovery (default: false) 27 | prefix: "home-assistant" # Optional: Home Assistant MQTT topic prefix (default: homeassistant) 28 | 29 | targets: 30 | - host: 192.168.0.2 # Required: target IP address 31 | name: Raspberry Pi # Optional: target name 32 | scan_interval: 30 # Optional: fetch interval in seconds (default: 10) 33 | device_manufacturer: Raspberry Pi # Optional: set the device manufacturer in Home Assistant 34 | device_model: 3 Model B # Optional: set the device model in Home Assistant 35 | suggested_area: Bedroom # Optional: set the area in Home Assistant 36 | auth_key: password # Optional: set the auth password for SNMPv3 37 | auth_protocol: sha # Optional: set the auth protocol for SNMPv3, one of sha or md5 38 | priv_key: password # Optional: set the privilege password for SNMPv3 39 | priv_protocol: des # Optional: set the privilege protocol for SNMPv3, one of des, aes, aes256b or aes256r 40 | version: "3" # Optional: 1, 2c or 3 (default: 1) 41 | sensors: 42 | - oid: 1.3.6.1.2.1.25.1.1.0 # Required: SNMP oid 43 | name: Raspberry Pi Uptime # Required: sensor name 44 | unit_of_measurement: days # Optional: set the unit of measurement in Home Assistant 45 | transform: "value / 6000" # Optional: a transform function written in JavaScript 46 | icon: mdi:calendar-clock # Optional: set an icon in Home Assistant 47 | binary_sensor: false # Optional: whether to expose the sensor as a binary sensor in Home Assistant 48 | 49 | - host: 192.168.0.3 50 | name: Raspberry Pi 2 51 | version: 2c 52 | sensors: 53 | - oid: 1.3.6.1.2.1.25.1.1.0 54 | name: Raspberry Pi 2 Uptime 55 | unit_of_measurement: days 56 | transform: "Math.floor(value / 6000 / 60 / 24)" 57 | icon: mdi:calendar-clock 58 | 59 | - oid: 1.3.6.1.4.1.2021.11.11.0 60 | name: Raspberry Pi 2 CPU 61 | unit_of_measurement: "%" 62 | transform: "100 - value" 63 | icon: mdi:cpu-64-bit 64 | ``` 65 | 66 | ## Running the app 67 | 68 | The easiest way to run the app is via Docker Compose, e.g. 69 | 70 | ```yaml 71 | version: "3" 72 | services: 73 | snmp2mqtt: 74 | container_name: snmp2mqtt 75 | image: dchesterton/snmp2mqtt:latest 76 | restart: unless-stopped 77 | volumes: 78 | - ./config.yml:/app/config.yml 79 | ``` 80 | 81 | ## Buy Me A ~~Coffee~~ Beer 🍻 82 | 83 | A few people have kindly requested a way to donate a small amount of money. If you feel so inclined I've set up a "Buy Me A Coffee" 84 | page where you can donate a small sum. Please do not feel obligated to donate in any way - I work on the app because it's 85 | useful to myself and others, not for any financial gain - but any token of appreciation is much appreciated 🙂 86 | 87 | 88 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "snmp2mqtt", 3 | "version": "0.2.4", 4 | "license": "MIT", 5 | "bin": { 6 | "snmp2mqtt": "dist/index.js" 7 | }, 8 | "author": "Daniel Chesterton", 9 | "dependencies": { 10 | "ajv": "^8.8.2", 11 | "async-mqtt": "^2.6.1", 12 | "better-ajv-errors": "^1.1.2", 13 | "bigint-buffer": "^1.1.5", 14 | "js-yaml": "^4.1.0", 15 | "luxon": "^2.3.0", 16 | "net-snmp": "^3.5.8", 17 | "slugify": "^1.6.5" 18 | }, 19 | "devDependencies": { 20 | "@tsconfig/node16": "^1.0.2", 21 | "@types/js-yaml": "^4.0.5", 22 | "@types/luxon": "^2.0.8", 23 | "@types/node": "^14.14.27", 24 | "prettier": "^2.5.1", 25 | "ts-node": "^10.4.0", 26 | "typescript": "^4.5.4" 27 | }, 28 | "scripts": { 29 | "build": "rm -rf dist && tsc", 30 | "start": "ts-node --files src/index.ts", 31 | "prettier:write": "prettier src --write" 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/config.ts: -------------------------------------------------------------------------------- 1 | import Ajv from "ajv"; 2 | import betterAjvErrors from "better-ajv-errors"; 3 | import * as fs from "fs"; 4 | import { JSON_SCHEMA, load } from "js-yaml"; 5 | 6 | import { schema } from "./config_schema"; 7 | import { createLogger } from "./log"; 8 | import { Config } from "./types"; 9 | 10 | export function loadConfig(): Config { 11 | const yamlConfig = loadYamlConfig(); 12 | 13 | if (yamlConfig) { 14 | return validate(yamlConfig); 15 | } 16 | 17 | throw new Error("Could not find config file"); 18 | } 19 | 20 | function validate(userConfig: unknown) { 21 | const ajv = new Ajv({ 22 | allowUnionTypes: true, 23 | useDefaults: true, 24 | allErrors: true, 25 | }); 26 | 27 | const validator = ajv.compile(schema); 28 | 29 | if (!validator(userConfig)) { 30 | const errors = betterAjvErrors(schema, userConfig, validator.errors!, { 31 | format: "js", 32 | }); 33 | 34 | if (errors && errors.length) { 35 | const log = createLogger("ERROR"); 36 | 37 | log.error( 38 | `${errors.length} error${ 39 | errors.length > 1 ? "s" : "" 40 | } found in config...` 41 | ); 42 | for (const error of errors) { 43 | log.error(error.error); 44 | } 45 | 46 | process.exit(1); 47 | } 48 | } 49 | 50 | return userConfig as Config; 51 | } 52 | 53 | function loadYamlConfig() { 54 | const fileName = `${process.cwd()}/config.yml`; 55 | 56 | if (!fs.existsSync(fileName)) { 57 | return false; 58 | } 59 | 60 | try { 61 | return load(fs.readFileSync(fileName, "utf8"), { 62 | schema: JSON_SCHEMA, 63 | }); 64 | } catch (e) { 65 | throw new Error(`Error loading config file: ${e}`); 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/config_schema.ts: -------------------------------------------------------------------------------- 1 | export const schema = { 2 | $schema: "http://json-schema.org/draft-07/schema", 3 | $id: "https://github.com/dchesterton/snmp2mqtt/tree/master/src/config_schema.json", 4 | type: "object", 5 | definitions: { 6 | target: { 7 | type: "object", 8 | required: ["host", "sensors"], 9 | properties: { 10 | host: { 11 | type: "string", 12 | }, 13 | name: { 14 | type: "string", 15 | }, 16 | device_manufacturer: { 17 | type: "string", 18 | }, 19 | device_model: { 20 | type: "string", 21 | }, 22 | suggested_area: { 23 | type: "string", 24 | }, 25 | port: { 26 | type: "number", 27 | }, 28 | community: { 29 | type: "string", 30 | }, 31 | version: { 32 | type: ["string", "number"], 33 | }, 34 | scan_interval: { 35 | type: "number", 36 | }, 37 | username: { 38 | type: "string", 39 | }, 40 | auth_key: { 41 | type: "string", 42 | }, 43 | auth_protocol: { 44 | type: "string", 45 | enum: ["sha", "md5"], 46 | }, 47 | priv_key: { 48 | type: "string", 49 | }, 50 | priv_protocol: { 51 | type: "string", 52 | enum: ["des", "aes", "aes256b", "aes256r"], 53 | }, 54 | sensors: { 55 | type: "array", 56 | default: [], 57 | items: { $ref: "#/definitions/sensor" }, 58 | }, 59 | user: { 60 | type: "string", 61 | }, 62 | level: { 63 | type: "string", 64 | enum: ["noAuthNoPriv", "authNoPriv", "authPriv"], 65 | }, 66 | }, 67 | additionalProperties: false, 68 | }, 69 | sensor: { 70 | type: "object", 71 | required: ["oid", "name"], 72 | properties: { 73 | oid: { 74 | type: "string", 75 | }, 76 | name: { 77 | type: "string", 78 | }, 79 | transform: { 80 | type: "string", 81 | }, 82 | unit_of_measurement: { 83 | type: "string", 84 | }, 85 | device_class: { 86 | type: "string", 87 | }, 88 | icon: { 89 | type: "string", 90 | }, 91 | binary_sensor: { 92 | type: "boolean", 93 | }, 94 | }, 95 | additionalProperties: false, 96 | }, 97 | }, 98 | properties: { 99 | mqtt: { 100 | type: "object", 101 | default: {}, 102 | properties: { 103 | client_id: { 104 | type: "string", 105 | default: "snmp2mqtt", 106 | }, 107 | host: { 108 | type: "string", 109 | default: "localhost", 110 | }, 111 | port: { 112 | type: "number", 113 | }, 114 | keepalive: { 115 | type: "number", 116 | default: 10, 117 | }, 118 | password: { 119 | type: "string", 120 | default: "", 121 | }, 122 | qos: { 123 | enum: [0, 1, 2], 124 | type: "number", 125 | default: 0, 126 | }, 127 | retain: { 128 | type: "boolean", 129 | default: true, 130 | }, 131 | username: { 132 | type: "string", 133 | default: "", 134 | }, 135 | ca: { 136 | type: "string", 137 | }, 138 | cert: { 139 | type: "string", 140 | }, 141 | key: { 142 | type: "string", 143 | }, 144 | reject_unauthorized: { 145 | type: "boolean", 146 | }, 147 | clean: { 148 | type: "boolean", 149 | default: true, 150 | }, 151 | }, 152 | additionalProperties: false, 153 | }, 154 | homeassistant: { 155 | type: "object", 156 | default: {}, 157 | properties: { 158 | discovery: { 159 | type: "boolean", 160 | default: false, 161 | }, 162 | prefix: { 163 | type: "string", 164 | default: "homeassistant", 165 | }, 166 | }, 167 | additionalProperties: false, 168 | }, 169 | targets: { 170 | type: "array", 171 | default: [], 172 | items: { $ref: "#/definitions/target" }, 173 | }, 174 | log: { 175 | type: "string", 176 | enum: ["debug", "error", "info", "warning"], 177 | default: "info", 178 | }, 179 | }, 180 | additionalProperties: false, 181 | }; 182 | -------------------------------------------------------------------------------- /src/home_assistant.ts: -------------------------------------------------------------------------------- 1 | import { Client } from "./mqtt"; 2 | import { TargetConfig } from "./types"; 3 | import { md5, slugify } from "./util"; 4 | 5 | export const createHomeAssistantTopics = async ( 6 | mqtt: Client, 7 | targets: Array, 8 | prefix: string 9 | ) => { 10 | const promises = []; 11 | 12 | for (const target of targets) { 13 | const device: any = { 14 | name: target.name ?? target.host, 15 | identifiers: target.host, 16 | via_device: "snmp2mqtt", 17 | }; 18 | 19 | if (target.suggested_area) { 20 | device.suggested_area = target.suggested_area; 21 | } 22 | if (target.device_manufacturer) { 23 | device.manufacturer = target.device_manufacturer; 24 | } 25 | if (target.device_model) { 26 | device.model = target.device_model; 27 | } 28 | 29 | for (const sensor of target.sensors) { 30 | const sensorType = sensor.binary_sensor 31 | ? "binary_sensor" 32 | : "sensor"; 33 | const sensorName = slugify(sensor.name); 34 | const topic = `${prefix}/${sensorType}/snmp2mqtt/${sensorName}/config`; 35 | 36 | const discovery: any = { 37 | availability: [ 38 | { 39 | topic: mqtt.STATUS_TOPIC, 40 | }, 41 | { 42 | topic: mqtt.sensorStatusTopic(sensor, target), 43 | }, 44 | ], 45 | availability_mode: "all", 46 | device, 47 | name: sensor.name, 48 | unique_id: `snmp2mqtt.${md5(`${target.host}-${sensor.oid}`)}`, 49 | state_topic: mqtt.sensorValueTopic(sensor, target), 50 | qos: mqtt.qos, 51 | entity_category: "diagnostic", 52 | }; 53 | 54 | if (sensor.unit_of_measurement) { 55 | discovery.unit_of_measurement = sensor.unit_of_measurement; 56 | } 57 | if (sensor.device_class) { 58 | discovery.device_class = sensor.device_class; 59 | } 60 | if (sensor.icon) { 61 | discovery.icon = sensor.icon; 62 | } 63 | 64 | promises.push(mqtt.publish(topic, discovery)); 65 | } 66 | } 67 | 68 | await Promise.all(promises); 69 | }; 70 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | import { createLogger } from "./log"; 2 | import { createClient } from "./mqtt"; 3 | import { Target } from "./snmp"; 4 | import { loadConfig } from "./config"; 5 | import { TargetConfig } from "./types"; 6 | import { createHomeAssistantTopics } from "./home_assistant"; 7 | import { readFileSync } from "fs"; 8 | 9 | const config = loadConfig(); 10 | const log = createLogger(config.log); 11 | 12 | (async () => { 13 | const version: string = JSON.parse( 14 | readFileSync(`${__dirname}/../package.json`).toString() 15 | ).version; 16 | 17 | log.info(`Starting snmp2mqtt v${version}...`); 18 | 19 | const mqtt = await createClient(config.mqtt, log, version); 20 | 21 | if (config.homeassistant.discovery) { 22 | await createHomeAssistantTopics( 23 | mqtt, 24 | config.targets, 25 | config.homeassistant.prefix 26 | ); 27 | } 28 | 29 | const publishSensors = async ( 30 | values: Array, 31 | target: TargetConfig 32 | ) => { 33 | const promises: Array> = []; 34 | 35 | for (const i in values) { 36 | const value = values[i]; 37 | const sensor = target.sensors[i]; 38 | 39 | if (value instanceof Error) { 40 | log.warning( 41 | `Error ${value.message} fetching sensor ${JSON.stringify( 42 | sensor 43 | )} from ${target.host}` 44 | ); 45 | 46 | promises.push( 47 | mqtt.publish( 48 | mqtt.sensorStatusTopic(sensor, target), 49 | mqtt.OFFLINE 50 | ) 51 | ); 52 | } else { 53 | log.info( 54 | `[${target.host}] ${sensor.name}: ${value}${ 55 | sensor.unit_of_measurement 56 | ? `${sensor.unit_of_measurement}` 57 | : "" 58 | }` 59 | ); 60 | 61 | promises.push( 62 | mqtt.publish(mqtt.sensorValueTopic(sensor, target), value), 63 | mqtt.publish( 64 | mqtt.sensorStatusTopic(sensor, target), 65 | mqtt.ONLINE 66 | ) 67 | ); 68 | } 69 | } 70 | 71 | await Promise.all(promises); 72 | }; 73 | 74 | const clients: Target[] = []; 75 | 76 | for (const target of config.targets) { 77 | const client = new Target(target, log); 78 | client.on("response", publishSensors); 79 | client.connect(); 80 | 81 | clients.push(client); 82 | } 83 | 84 | const pauseClients = () => { 85 | log.warning("MQTT client disconnected"); 86 | clients.forEach((client) => client.pause()); 87 | }; 88 | 89 | mqtt.on("close", pauseClients); 90 | 91 | mqtt.on("connect", () => { 92 | clients.forEach((client) => client.resume()); 93 | }); 94 | 95 | const exit = async (code: number = 0) => { 96 | log.info("Exiting program..."); 97 | mqtt.off("close", pauseClients); 98 | 99 | await mqtt.end(); 100 | 101 | for (const client of clients) { 102 | await client.end(); 103 | } 104 | 105 | process.exit(code); 106 | }; 107 | 108 | process.on("SIGINT", async () => { 109 | log.info("Caught interrupt signal, exiting gracefully..."); 110 | await exit(0); 111 | }); 112 | 113 | process.on("unhandledRejection", async (error) => { 114 | log.error(`Unhandled rejection - ${error}`); 115 | await exit(1); 116 | }); 117 | })(); 118 | -------------------------------------------------------------------------------- /src/log.ts: -------------------------------------------------------------------------------- 1 | import { DateTime } from "luxon"; 2 | 3 | enum LogLevel { 4 | DEBUG = 0, 5 | INFO = 1, 6 | WARNING = 2, 7 | ERROR = 3, 8 | } 9 | 10 | export type LogLevelStrings = keyof typeof LogLevel; 11 | export type Logger = ReturnType; 12 | 13 | export const createLogger = (minLevel: LogLevelStrings) => { 14 | const n = LogLevel[minLevel.toUpperCase() as LogLevelStrings]; 15 | 16 | const createLevelFn = (level: LogLevel, min: LogLevel) => (msg: string) => { 17 | if (level < min) { 18 | return; 19 | } 20 | 21 | const timestampStr = DateTime.utc().toFormat("yyyy-LL-dd HH:mm:ss"); 22 | console.log(`${timestampStr} - ${LogLevel[level]}: ${msg}`); 23 | }; 24 | 25 | const debug = createLevelFn(LogLevel.DEBUG, n); 26 | const info = createLevelFn(LogLevel.INFO, n); 27 | const warning = createLevelFn(LogLevel.WARNING, n); 28 | const error = createLevelFn(LogLevel.ERROR, n); 29 | 30 | return { debug, info, warning, error }; 31 | }; 32 | -------------------------------------------------------------------------------- /src/mqtt.ts: -------------------------------------------------------------------------------- 1 | import { IClientOptions, connectAsync, AsyncMqttClient } from "async-mqtt"; 2 | import { MQTTConfig, SensorConfig, TargetConfig } from "./types"; 3 | import { readFileSync } from "fs"; 4 | import { slugify } from "./util"; 5 | import { Logger } from "./log"; 6 | import { EventEmitter } from "events"; 7 | 8 | const OFFLINE = "offline"; 9 | const ONLINE = "online"; 10 | 11 | const STATUS_TOPIC = "snmp2mqtt/status"; 12 | const CONFIG_TOPIC = "snmp2mqtt/config"; 13 | 14 | const connect = (config: MQTTConfig) => { 15 | const port = config.port ? config.port : config.ca ? 8883 : 1883; 16 | 17 | const options: IClientOptions = { 18 | hostname: config.host, 19 | protocol: "mqtt", 20 | port, 21 | will: { 22 | topic: STATUS_TOPIC, 23 | payload: OFFLINE, 24 | retain: config.retain, 25 | qos: config.qos, 26 | }, 27 | clientId: config.client_id, 28 | clean: config.clean, 29 | keepalive: config.keepalive, 30 | reconnectPeriod: 5000, 31 | rejectUnauthorized: config.reject_unauthorized, 32 | }; 33 | 34 | console.log(config); 35 | 36 | if (config.username) { 37 | options.username = config.username; 38 | } 39 | 40 | if (config.password) { 41 | options.password = config.password; 42 | } 43 | 44 | if (config.ca) { 45 | options.ca = readFileSync(config.ca); 46 | options.protocol = "mqtts"; 47 | } 48 | 49 | if (config.cert) { 50 | options.cert = readFileSync(config.cert); 51 | options.protocol = "mqtts"; 52 | } 53 | 54 | if (config.key) { 55 | options.key = readFileSync(config.key); 56 | options.protocol = "mqtts"; 57 | } 58 | 59 | return connectAsync(options); 60 | }; 61 | 62 | export const createClient = async ( 63 | config: MQTTConfig, 64 | log: Logger, 65 | version: string 66 | ) => { 67 | const emitter = new EventEmitter(); 68 | 69 | let client: AsyncMqttClient = await connect(config); 70 | 71 | const publish = ( 72 | topic: string, 73 | message: string | Record | number | bigint 74 | ) => { 75 | if (!client.connected) { 76 | log.warning(`Skipping publish to ${topic}, MQTT connection closed`); 77 | return Promise.resolve(null); 78 | } 79 | const payload = 80 | typeof message === "object" 81 | ? JSON.stringify(message) 82 | : String(message); 83 | 84 | log.debug( 85 | `Writing to ${topic} (QOS: ${config.qos}, retain: ${ 86 | config.retain ? "true" : "false" 87 | })` 88 | ); 89 | 90 | return client.publish(topic, payload, { 91 | qos: config.qos, 92 | retain: config.retain, 93 | }); 94 | }; 95 | 96 | const onConnect = async () => { 97 | await publish(STATUS_TOPIC, ONLINE); 98 | await publish(CONFIG_TOPIC, { version }); 99 | emitter.emit("connect"); 100 | }; 101 | 102 | client.on("close", async () => { 103 | emitter.emit("close"); 104 | }); 105 | 106 | client.on("connect", onConnect); 107 | 108 | await onConnect(); 109 | 110 | return { 111 | publish, 112 | sensorStatusTopic: (sensor: SensorConfig, target: TargetConfig) => 113 | `snmp2mqtt/${target.host}/${slugify(sensor.name)}/status`, 114 | sensorValueTopic: (sensor: SensorConfig, target: TargetConfig) => 115 | `snmp2mqtt/${target.host}/${slugify(sensor.name)}/value`, 116 | STATUS_TOPIC, 117 | ONLINE, 118 | OFFLINE, 119 | on: (event: "close" | "connect", cb: () => void) => 120 | emitter.on(event, cb), 121 | off: (event: "close" | "connect", cb: () => void) => 122 | emitter.off(event, cb), 123 | end: async () => { 124 | await client.publish(STATUS_TOPIC, OFFLINE); 125 | await client.end(); 126 | }, 127 | qos: config.qos, 128 | }; 129 | }; 130 | 131 | type ThenArg = T extends PromiseLike ? U : T; 132 | export type Client = ThenArg>; 133 | -------------------------------------------------------------------------------- /src/net-snmp.d.ts: -------------------------------------------------------------------------------- 1 | declare module "net-snmp"; 2 | -------------------------------------------------------------------------------- /src/safe-eval.d.ts: -------------------------------------------------------------------------------- 1 | declare module "safe-eval"; 2 | -------------------------------------------------------------------------------- /src/snmp.ts: -------------------------------------------------------------------------------- 1 | import * as snmp from "net-snmp"; 2 | 3 | import { TargetConfig, VersionConfig } from "./types"; 4 | import { EventEmitter } from "events"; 5 | import { Logger } from "./log"; 6 | import { toBigIntBE } from "bigint-buffer"; 7 | 8 | const versionToNetSnmp = (version?: VersionConfig) => { 9 | switch (version) { 10 | case "2c": 11 | return snmp.Version2c as number; 12 | case 3: 13 | case "3": 14 | return snmp.Version3 as number; 15 | default: 16 | return snmp.Version1 as number; 17 | } 18 | }; 19 | 20 | export declare interface Target { 21 | on( 22 | event: "response", 23 | listener: ( 24 | values: Array, 25 | target: TargetConfig 26 | ) => void 27 | ): this; 28 | } 29 | 30 | export class Target extends EventEmitter { 31 | private session: any; 32 | private interval?: NodeJS.Timer; 33 | private ending: boolean = false; 34 | 35 | public constructor(private options: TargetConfig, private log: Logger) { 36 | super(); 37 | } 38 | 39 | public pause() { 40 | if (this.interval) { 41 | clearInterval(this.interval); 42 | } 43 | } 44 | 45 | public resume() { 46 | this.interval = setInterval(() => { 47 | this.fetch(); 48 | }, this.getScanInterval()); 49 | 50 | this.fetch(); 51 | } 52 | 53 | public end() { 54 | this.ending = true; 55 | 56 | return new Promise((res) => { 57 | this.session.on("close", () => { 58 | res(); 59 | }); 60 | this.session.close(); 61 | }); 62 | } 63 | 64 | private getScanInterval() { 65 | return (this.options.scan_interval ?? 10) * 1000; 66 | } 67 | 68 | public connect() { 69 | const scanIntervalMs = this.getScanInterval(); 70 | 71 | const options: any = { 72 | port: this.options.port ?? 161, 73 | retries: 3, 74 | timeout: scanIntervalMs > 5000 ? 5000 : scanIntervalMs / 2, 75 | backoff: 1.0, 76 | version: versionToNetSnmp(this.options.version), 77 | }; 78 | 79 | if (options.version === snmp.Version3) { 80 | const user: any = { 81 | name: this.options.username, 82 | }; 83 | 84 | if (this.options.auth_key && this.options.priv_key) { 85 | user.level = snmp.SecurityLevel.authPriv; 86 | } else if (this.options.auth_key && !this.options.priv_key) { 87 | user.level = snmp.SecurityLevel.authNoPriv; 88 | } else { 89 | user.level = snmp.SecurityLevel.noAuthNoPriv; 90 | } 91 | 92 | if (this.options.auth_protocol) { 93 | user.authProtocol = 94 | snmp.AuthProtocols[this.options.auth_protocol]; 95 | } 96 | if (this.options.auth_key) { 97 | user.authKey = this.options.auth_key; 98 | } 99 | 100 | if (this.options.priv_protocol) { 101 | user.privProtocol = 102 | snmp.PrivProtocols[this.options.priv_protocol]; 103 | } 104 | if (this.options.priv_key) { 105 | user.privKey = this.options.priv_key; 106 | } 107 | 108 | this.session = snmp.createV3Session( 109 | this.options.host, 110 | user, 111 | options 112 | ); 113 | } else { 114 | const community = this.options.community ?? "public"; 115 | this.session = snmp.createSession( 116 | this.options.host, 117 | community, 118 | options 119 | ); 120 | } 121 | 122 | this.session.on("close", () => { 123 | if (this.ending) { 124 | return; 125 | } 126 | 127 | this.log.warning(`Target ${this.options.host} disconnected`); 128 | this.pause(); 129 | 130 | setTimeout(() => { 131 | this.connect(); 132 | }, 2000); 133 | }); 134 | 135 | this.resume(); 136 | } 137 | 138 | public close() { 139 | if (this.interval) { 140 | clearInterval(this.interval); 141 | } 142 | } 143 | 144 | private fetch() { 145 | const oids = this.options.sensors.map((sensor) => sensor.oid); 146 | 147 | this.log.debug( 148 | `Fetching ${oids.length} sensors from ${this.options.host}...` 149 | ); 150 | 151 | this.session.get( 152 | oids, 153 | (error: Error, varbinds: Array<{ value: string | number }>) => { 154 | if (error) { 155 | const errors = []; 156 | 157 | for (let i = 0; i < oids.length; i++) { 158 | errors.push(error); 159 | } 160 | 161 | this.emit("response", errors, this.options); 162 | } else { 163 | const values = []; 164 | 165 | for (const i in this.options.sensors) { 166 | const sensor = this.options.sensors[i]; 167 | const result = varbinds[i]; 168 | 169 | if (snmp.isVarbindError(result)) { 170 | values.push(new Error(snmp.varbindError(result))); 171 | } else { 172 | let { value, type } = result as { 173 | value: string | number | Buffer | bigint; 174 | type: any; 175 | }; 176 | 177 | switch (type) { 178 | case snmp.ObjectType.Counter64: 179 | value = toBigIntBE(value as Buffer); 180 | break; 181 | case snmp.ObjectType.OctetString: 182 | value = value.toString(); 183 | break; 184 | } 185 | 186 | if (sensor.transform) { 187 | value = eval(sensor.transform); 188 | } 189 | 190 | values.push(value); 191 | } 192 | } 193 | 194 | this.emit("response", values, this.options); 195 | } 196 | } 197 | ); 198 | } 199 | } 200 | -------------------------------------------------------------------------------- /src/types.ts: -------------------------------------------------------------------------------- 1 | import { LogLevelStrings } from "./log"; 2 | 3 | export interface MQTTConfig { 4 | host: string; 5 | port?: number; 6 | username?: string; 7 | password?: string; 8 | retain: boolean; 9 | qos: 0 | 1 | 2; 10 | client_id?: string; 11 | keepalive?: number; 12 | ca?: string; 13 | cert?: string; 14 | key?: string; 15 | clean: boolean; 16 | reject_unauthorized?: boolean; 17 | } 18 | 19 | export interface TargetConfig { 20 | host: string; 21 | sensors: SensorConfig[]; 22 | name?: string; 23 | device_manufacturer?: string; 24 | device_model?: string; 25 | suggested_area?: string; 26 | community?: string; 27 | version?: VersionConfig; 28 | port?: number; 29 | scan_interval?: number; 30 | username?: string; 31 | auth_protocol?: "md5" | "sha"; 32 | auth_key?: string; 33 | priv_protocol?: "des" | "aes" | "aes256b" | "aes256r"; 34 | priv_key?: string; 35 | } 36 | 37 | export type VersionConfig = "1" | 1 | "2c" | 3 | "3"; 38 | 39 | export interface SensorConfig { 40 | oid: string; 41 | name: string; 42 | transform?: string; 43 | unit_of_measurement?: string; 44 | device_class?: string; 45 | icon?: string; 46 | binary_sensor?: boolean; 47 | } 48 | 49 | export interface Config { 50 | log: LogLevelStrings; 51 | mqtt: MQTTConfig; 52 | homeassistant: HomeAssistantConfig; 53 | targets: Array; 54 | } 55 | 56 | export interface HomeAssistantConfig { 57 | discovery: boolean; 58 | prefix: string; 59 | } 60 | -------------------------------------------------------------------------------- /src/util.ts: -------------------------------------------------------------------------------- 1 | import slugifyFn from "slugify"; 2 | import { createHash } from "crypto"; 3 | 4 | export function slugify(str: string) { 5 | return slugifyFn( 6 | str.toLowerCase().replaceAll("-", "_").replaceAll("~", "_"), 7 | { 8 | replacement: "_", 9 | strict: true, 10 | } 11 | ).replace(/^_+|_+$/g, ""); 12 | } 13 | 14 | export const md5 = (str: string) => createHash("md5").update(str).digest("hex"); 15 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@tsconfig/node16/tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "./dist" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /yarn.lock: -------------------------------------------------------------------------------- 1 | # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. 2 | # yarn lockfile v1 3 | 4 | 5 | "@babel/code-frame@^7.16.0": 6 | version "7.16.0" 7 | resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.16.0.tgz#0dfc80309beec8411e65e706461c408b0bb9b431" 8 | integrity sha512-IF4EOMEV+bfYwOmNxGzSnjR2EmQod7f1UXOpZM3l4i4o4QNwzjtJAu/HxdjHq0aYBvdqMuQEY1eg0nqW9ZPORA== 9 | dependencies: 10 | "@babel/highlight" "^7.16.0" 11 | 12 | "@babel/helper-validator-identifier@^7.15.7": 13 | version "7.15.7" 14 | resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.15.7.tgz#220df993bfe904a4a6b02ab4f3385a5ebf6e2389" 15 | integrity sha512-K4JvCtQqad9OY2+yTU8w+E82ywk/fe+ELNlt1G8z3bVGlZfn/hOcQQsUhGhW/N+tb3fxK800wLtKOE/aM0m72w== 16 | 17 | "@babel/highlight@^7.16.0": 18 | version "7.16.0" 19 | resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.16.0.tgz#6ceb32b2ca4b8f5f361fb7fd821e3fddf4a1725a" 20 | integrity sha512-t8MH41kUQylBtu2+4IQA3atqevA2lRgqA2wyVB/YiWmsDSuylZZuXOUy9ric30hfzauEFfdsuk/eXTRrGrfd0g== 21 | dependencies: 22 | "@babel/helper-validator-identifier" "^7.15.7" 23 | chalk "^2.0.0" 24 | js-tokens "^4.0.0" 25 | 26 | "@cspotcode/source-map-consumer@0.8.0": 27 | version "0.8.0" 28 | resolved "https://registry.yarnpkg.com/@cspotcode/source-map-consumer/-/source-map-consumer-0.8.0.tgz#33bf4b7b39c178821606f669bbc447a6a629786b" 29 | integrity sha512-41qniHzTU8yAGbCp04ohlmSrZf8bkf/iJsl3V0dRGsQN/5GFfx+LbCSsCpp2gqrqjTVg/K6O8ycoV35JIwAzAg== 30 | 31 | "@cspotcode/source-map-support@0.7.0": 32 | version "0.7.0" 33 | resolved "https://registry.yarnpkg.com/@cspotcode/source-map-support/-/source-map-support-0.7.0.tgz#4789840aa859e46d2f3173727ab707c66bf344f5" 34 | integrity sha512-X4xqRHqN8ACt2aHVe51OxeA2HjbcL4MqFqXkrmQszJ1NOUuUu5u6Vqx/0lZSVNku7velL5FC/s5uEAj1lsBMhA== 35 | dependencies: 36 | "@cspotcode/source-map-consumer" "0.8.0" 37 | 38 | "@humanwhocodes/momoa@^2.0.2": 39 | version "2.0.2" 40 | resolved "https://registry.yarnpkg.com/@humanwhocodes/momoa/-/momoa-2.0.2.tgz#127d18c22abf4dd988797c898b4356db8d99b662" 41 | integrity sha512-mkMcsshJ7L17AyntqpyjLiGqhbG62w93B0StW+HSNVJ1WUeVFA2uPssV/GufEfDqN6lRKI1I+uDzBUw83C0VuA== 42 | 43 | "@tsconfig/node10@^1.0.7": 44 | version "1.0.7" 45 | resolved "https://registry.yarnpkg.com/@tsconfig/node10/-/node10-1.0.7.tgz#1eb1de36c73478a2479cc661ef5af1c16d86d606" 46 | integrity sha512-aBvUmXLQbayM4w3A8TrjwrXs4DZ8iduJnuJLLRGdkWlyakCf1q6uHZJBzXoRA/huAEknG5tcUyQxN3A+In5euQ== 47 | 48 | "@tsconfig/node12@^1.0.7": 49 | version "1.0.7" 50 | resolved "https://registry.yarnpkg.com/@tsconfig/node12/-/node12-1.0.7.tgz#677bd9117e8164dc319987dd6ff5fc1ba6fbf18b" 51 | integrity sha512-dgasobK/Y0wVMswcipr3k0HpevxFJLijN03A8mYfEPvWvOs14v0ZlYTR4kIgMx8g4+fTyTFv8/jLCIfRqLDJ4A== 52 | 53 | "@tsconfig/node14@^1.0.0": 54 | version "1.0.0" 55 | resolved "https://registry.yarnpkg.com/@tsconfig/node14/-/node14-1.0.0.tgz#5bd046e508b1ee90bc091766758838741fdefd6e" 56 | integrity sha512-RKkL8eTdPv6t5EHgFKIVQgsDapugbuOptNd9OOunN/HAkzmmTnZELx1kNCK0rSdUYGmiFMM3rRQMAWiyp023LQ== 57 | 58 | "@tsconfig/node16@^1.0.2": 59 | version "1.0.2" 60 | resolved "https://registry.yarnpkg.com/@tsconfig/node16/-/node16-1.0.2.tgz#423c77877d0569db20e1fc80885ac4118314010e" 61 | integrity sha512-eZxlbI8GZscaGS7kkc/trHTT5xgrjH3/1n2JDwusC9iahPKWMRvRjJSAN5mCXviuTGQ/lHnhvv8Q1YTpnfz9gA== 62 | 63 | "@types/js-yaml@^4.0.5": 64 | version "4.0.5" 65 | resolved "https://registry.yarnpkg.com/@types/js-yaml/-/js-yaml-4.0.5.tgz#738dd390a6ecc5442f35e7f03fa1431353f7e138" 66 | integrity sha512-FhpRzf927MNQdRZP0J5DLIdTXhjLYzeUTmLAu69mnVksLH9CJY3IuSeEgbKUki7GQZm0WqDkGzyxju2EZGD2wA== 67 | 68 | "@types/luxon@^2.0.8": 69 | version "2.0.8" 70 | resolved "https://registry.yarnpkg.com/@types/luxon/-/luxon-2.0.8.tgz#a0fdd7ab0b67e08bf1d301232a7fef79b74ded69" 71 | integrity sha512-lGmxL6hMEVqXr8w9bL52RUWXVu90o7vH8WQSutQssr2e+w0TNttXx2Zfw2V2lHHHWfW6OGqB8bXDvtKocv19qQ== 72 | 73 | "@types/node@^14.14.27": 74 | version "14.14.27" 75 | resolved "https://registry.yarnpkg.com/@types/node/-/node-14.14.27.tgz#c7127f8da0498993e13b1a42faf1303d3110d2f2" 76 | integrity sha512-Ecfmo4YDQPwuqTCl1yBxLV5ihKfRlkBmzUEDcfIRvDxOTGQEeikr317Ln7Gcv0tjA8dVgKI3rniqW2G1OyKDng== 77 | 78 | acorn-walk@^8.1.1: 79 | version "8.2.0" 80 | resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-8.2.0.tgz#741210f2e2426454508853a2f44d0ab83b7f69c1" 81 | integrity sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA== 82 | 83 | acorn@^8.4.1: 84 | version "8.5.0" 85 | resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.5.0.tgz#4512ccb99b3698c752591e9bb4472e38ad43cee2" 86 | integrity sha512-yXbYeFy+jUuYd3/CDcg2NkIYE991XYX/bje7LmjJigUciaeO1JR4XxXgCIV1/Zc/dRuFEyw1L0pbA+qynJkW5Q== 87 | 88 | ajv@^8.8.2: 89 | version "8.8.2" 90 | resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.8.2.tgz#01b4fef2007a28bf75f0b7fc009f62679de4abbb" 91 | integrity sha512-x9VuX+R/jcFj1DHo/fCp99esgGDWiHENrKxaCENuCxpoMCmAt/COCGVDwA7kleEpEzJjDnvh3yGoOuLu0Dtllw== 92 | dependencies: 93 | fast-deep-equal "^3.1.1" 94 | json-schema-traverse "^1.0.0" 95 | require-from-string "^2.0.2" 96 | uri-js "^4.2.2" 97 | 98 | ansi-styles@^3.2.1: 99 | version "3.2.1" 100 | resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" 101 | integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA== 102 | dependencies: 103 | color-convert "^1.9.0" 104 | 105 | ansi-styles@^4.1.0: 106 | version "4.3.0" 107 | resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937" 108 | integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg== 109 | dependencies: 110 | color-convert "^2.0.1" 111 | 112 | arg@^4.1.0: 113 | version "4.1.3" 114 | resolved "https://registry.yarnpkg.com/arg/-/arg-4.1.3.tgz#269fc7ad5b8e42cb63c896d5666017261c144089" 115 | integrity sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA== 116 | 117 | argparse@^2.0.1: 118 | version "2.0.1" 119 | resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38" 120 | integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q== 121 | 122 | asn1-ber@*: 123 | version "1.1.0" 124 | resolved "https://registry.yarnpkg.com/asn1-ber/-/asn1-ber-1.1.0.tgz#b69f91578c08afa65511124908a7a82f62c6571c" 125 | integrity sha512-t/pm9zz3CC7jE2ItzMPi4rYUXp4d3JkLAUQY8E/5u5Lkaq7KMvMalRWrW9WWARm5gOB/HE/mkorImJwSL7fi9w== 126 | 127 | async-mqtt@^2.6.1: 128 | version "2.6.1" 129 | resolved "https://registry.yarnpkg.com/async-mqtt/-/async-mqtt-2.6.1.tgz#7cca37b0c766e00d7b0b33c9eb236e216ed06248" 130 | integrity sha512-EkXAwRzwMaPC6ji0EvNeM5OMe6VjMhEKVJJUN7gu/hGzkcDpZtaI34nUwdwCMbjQB3pnuSOHqQMFKsUpg+D8kA== 131 | dependencies: 132 | mqtt "^4.1.0" 133 | 134 | balanced-match@^1.0.0: 135 | version "1.0.0" 136 | resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767" 137 | integrity sha1-ibTRmasr7kneFk6gK4nORi1xt2c= 138 | 139 | base64-js@^1.3.1: 140 | version "1.5.1" 141 | resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" 142 | integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== 143 | 144 | better-ajv-errors@^1.1.2: 145 | version "1.1.2" 146 | resolved "https://registry.yarnpkg.com/better-ajv-errors/-/better-ajv-errors-1.1.2.tgz#6accafad01e1ae93121cf915c8c99377f609c010" 147 | integrity sha512-xpFTC7JqkSGkvchJlH4IFtmwZ5SXomh0FqbEVEHRcXl/aiHh9nM/dnNnGTlxjrFCjWOVLLWpcNW1Hcrzs55/lg== 148 | dependencies: 149 | "@babel/code-frame" "^7.16.0" 150 | "@humanwhocodes/momoa" "^2.0.2" 151 | chalk "^4.1.2" 152 | jsonpointer "^5.0.0" 153 | leven "^3.1.0 < 4" 154 | 155 | bigint-buffer@^1.1.5: 156 | version "1.1.5" 157 | resolved "https://registry.yarnpkg.com/bigint-buffer/-/bigint-buffer-1.1.5.tgz#d038f31c8e4534c1f8d0015209bf34b4fa6dd442" 158 | integrity sha512-trfYco6AoZ+rKhKnxA0hgX0HAbVP/s808/EuDSe2JDzUnCp/xAsli35Orvk67UrTEcwuxZqYZDmfA2RXJgxVvA== 159 | dependencies: 160 | bindings "^1.3.0" 161 | 162 | bindings@^1.3.0: 163 | version "1.5.0" 164 | resolved "https://registry.yarnpkg.com/bindings/-/bindings-1.5.0.tgz#10353c9e945334bc0511a6d90b38fbc7c9c504df" 165 | integrity sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ== 166 | dependencies: 167 | file-uri-to-path "1.0.0" 168 | 169 | bl@^4.0.2: 170 | version "4.1.0" 171 | resolved "https://registry.yarnpkg.com/bl/-/bl-4.1.0.tgz#451535264182bec2fbbc83a62ab98cf11d9f7b3a" 172 | integrity sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w== 173 | dependencies: 174 | buffer "^5.5.0" 175 | inherits "^2.0.4" 176 | readable-stream "^3.4.0" 177 | 178 | brace-expansion@^1.1.7: 179 | version "1.1.11" 180 | resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" 181 | integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA== 182 | dependencies: 183 | balanced-match "^1.0.0" 184 | concat-map "0.0.1" 185 | 186 | buffer-from@^1.0.0: 187 | version "1.1.1" 188 | resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.1.tgz#32713bc028f75c02fdb710d7c7bcec1f2c6070ef" 189 | integrity sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A== 190 | 191 | buffer@^5.5.0: 192 | version "5.7.1" 193 | resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.7.1.tgz#ba62e7c13133053582197160851a8f648e99eed0" 194 | integrity sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ== 195 | dependencies: 196 | base64-js "^1.3.1" 197 | ieee754 "^1.1.13" 198 | 199 | callback-stream@^1.0.2: 200 | version "1.1.0" 201 | resolved "https://registry.yarnpkg.com/callback-stream/-/callback-stream-1.1.0.tgz#4701a51266f06e06eaa71fc17233822d875f4908" 202 | integrity sha1-RwGlEmbwbgbqpx/BcjOCLYdfSQg= 203 | dependencies: 204 | inherits "^2.0.1" 205 | readable-stream "> 1.0.0 < 3.0.0" 206 | 207 | chalk@^2.0.0: 208 | version "2.4.2" 209 | resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" 210 | integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== 211 | dependencies: 212 | ansi-styles "^3.2.1" 213 | escape-string-regexp "^1.0.5" 214 | supports-color "^5.3.0" 215 | 216 | chalk@^4.1.2: 217 | version "4.1.2" 218 | resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01" 219 | integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== 220 | dependencies: 221 | ansi-styles "^4.1.0" 222 | supports-color "^7.1.0" 223 | 224 | color-convert@^1.9.0: 225 | version "1.9.3" 226 | resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8" 227 | integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg== 228 | dependencies: 229 | color-name "1.1.3" 230 | 231 | color-convert@^2.0.1: 232 | version "2.0.1" 233 | resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3" 234 | integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ== 235 | dependencies: 236 | color-name "~1.1.4" 237 | 238 | color-name@1.1.3: 239 | version "1.1.3" 240 | resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" 241 | integrity sha1-p9BVi9icQveV3UIyj3QIMcpTvCU= 242 | 243 | color-name@~1.1.4: 244 | version "1.1.4" 245 | resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" 246 | integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== 247 | 248 | commist@^1.0.0: 249 | version "1.1.0" 250 | resolved "https://registry.yarnpkg.com/commist/-/commist-1.1.0.tgz#17811ec6978f6c15ee4de80c45c9beb77cee35d5" 251 | integrity sha512-rraC8NXWOEjhADbZe9QBNzLAN5Q3fsTPQtBV+fEVj6xKIgDgNiEVE6ZNfHpZOqfQ21YUzfVNUXLOEZquYvQPPg== 252 | dependencies: 253 | leven "^2.1.0" 254 | minimist "^1.1.0" 255 | 256 | concat-map@0.0.1: 257 | version "0.0.1" 258 | resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" 259 | integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s= 260 | 261 | concat-stream@^2.0.0: 262 | version "2.0.0" 263 | resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-2.0.0.tgz#414cf5af790a48c60ab9be4527d56d5e41133cb1" 264 | integrity sha512-MWufYdFw53ccGjCA+Ol7XJYpAlW6/prSMzuPOTRnJGcGzuhLn4Scrz7qf6o8bROZ514ltazcIFJZevcfbo0x7A== 265 | dependencies: 266 | buffer-from "^1.0.0" 267 | inherits "^2.0.3" 268 | readable-stream "^3.0.2" 269 | typedarray "^0.0.6" 270 | 271 | core-util-is@~1.0.0: 272 | version "1.0.2" 273 | resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" 274 | integrity sha1-tf1UIgqivFq1eqtxQMlAdUUDwac= 275 | 276 | create-require@^1.1.0: 277 | version "1.1.1" 278 | resolved "https://registry.yarnpkg.com/create-require/-/create-require-1.1.1.tgz#c1d7e8f1e5f6cfc9ff65f9cd352d37348756c333" 279 | integrity sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ== 280 | 281 | debug@^4.1.1: 282 | version "4.3.1" 283 | resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.1.tgz#f0d229c505e0c6d8c49ac553d1b13dc183f6b2ee" 284 | integrity sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ== 285 | dependencies: 286 | ms "2.1.2" 287 | 288 | diff@^4.0.1: 289 | version "4.0.2" 290 | resolved "https://registry.yarnpkg.com/diff/-/diff-4.0.2.tgz#60f3aecb89d5fae520c11aa19efc2bb982aade7d" 291 | integrity sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A== 292 | 293 | duplexify@^3.6.0: 294 | version "3.7.1" 295 | resolved "https://registry.yarnpkg.com/duplexify/-/duplexify-3.7.1.tgz#2a4df5317f6ccfd91f86d6fd25d8d8a103b88309" 296 | integrity sha512-07z8uv2wMyS51kKhD1KsdXJg5WQ6t93RneqRxUHnskXVtlYYkLqM0gqStQZ3pj073g687jPCHrqNfCzawLYh5g== 297 | dependencies: 298 | end-of-stream "^1.0.0" 299 | inherits "^2.0.1" 300 | readable-stream "^2.0.0" 301 | stream-shift "^1.0.0" 302 | 303 | end-of-stream@^1.0.0, end-of-stream@^1.1.0: 304 | version "1.4.4" 305 | resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.4.tgz#5ae64a5f45057baf3626ec14da0ca5e4b2431eb0" 306 | integrity sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q== 307 | dependencies: 308 | once "^1.4.0" 309 | 310 | escape-string-regexp@^1.0.5: 311 | version "1.0.5" 312 | resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" 313 | integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ= 314 | 315 | extend@^3.0.0: 316 | version "3.0.2" 317 | resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa" 318 | integrity sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g== 319 | 320 | fast-deep-equal@^3.1.1: 321 | version "3.1.3" 322 | resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" 323 | integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== 324 | 325 | file-uri-to-path@1.0.0: 326 | version "1.0.0" 327 | resolved "https://registry.yarnpkg.com/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz#553a7b8446ff6f684359c445f1e37a05dacc33dd" 328 | integrity sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw== 329 | 330 | fs.realpath@^1.0.0: 331 | version "1.0.0" 332 | resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" 333 | integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8= 334 | 335 | glob-parent@^3.1.0: 336 | version "3.1.0" 337 | resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-3.1.0.tgz#9e6af6299d8d3bd2bd40430832bd113df906c5ae" 338 | integrity sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4= 339 | dependencies: 340 | is-glob "^3.1.0" 341 | path-dirname "^1.0.0" 342 | 343 | glob-stream@^6.1.0: 344 | version "6.1.0" 345 | resolved "https://registry.yarnpkg.com/glob-stream/-/glob-stream-6.1.0.tgz#7045c99413b3eb94888d83ab46d0b404cc7bdde4" 346 | integrity sha1-cEXJlBOz65SIjYOrRtC0BMx73eQ= 347 | dependencies: 348 | extend "^3.0.0" 349 | glob "^7.1.1" 350 | glob-parent "^3.1.0" 351 | is-negated-glob "^1.0.0" 352 | ordered-read-streams "^1.0.0" 353 | pumpify "^1.3.5" 354 | readable-stream "^2.1.5" 355 | remove-trailing-separator "^1.0.1" 356 | to-absolute-glob "^2.0.0" 357 | unique-stream "^2.0.2" 358 | 359 | glob@^7.1.1: 360 | version "7.1.6" 361 | resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.6.tgz#141f33b81a7c2492e125594307480c46679278a6" 362 | integrity sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA== 363 | dependencies: 364 | fs.realpath "^1.0.0" 365 | inflight "^1.0.4" 366 | inherits "2" 367 | minimatch "^3.0.4" 368 | once "^1.3.0" 369 | path-is-absolute "^1.0.0" 370 | 371 | has-flag@^3.0.0: 372 | version "3.0.0" 373 | resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" 374 | integrity sha1-tdRU3CGZriJWmfNGfloH87lVuv0= 375 | 376 | has-flag@^4.0.0: 377 | version "4.0.0" 378 | resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" 379 | integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== 380 | 381 | help-me@^1.0.1: 382 | version "1.1.0" 383 | resolved "https://registry.yarnpkg.com/help-me/-/help-me-1.1.0.tgz#8f2d508d0600b4a456da2f086556e7e5c056a3c6" 384 | integrity sha1-jy1QjQYAtKRW2i8IZVbn5cBWo8Y= 385 | dependencies: 386 | callback-stream "^1.0.2" 387 | glob-stream "^6.1.0" 388 | through2 "^2.0.1" 389 | xtend "^4.0.0" 390 | 391 | ieee754@^1.1.13: 392 | version "1.2.1" 393 | resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352" 394 | integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA== 395 | 396 | inflight@^1.0.4: 397 | version "1.0.6" 398 | resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" 399 | integrity sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk= 400 | dependencies: 401 | once "^1.3.0" 402 | wrappy "1" 403 | 404 | inherits@2, inherits@^2.0.1, inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.3: 405 | version "2.0.4" 406 | resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" 407 | integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== 408 | 409 | is-absolute@^1.0.0: 410 | version "1.0.0" 411 | resolved "https://registry.yarnpkg.com/is-absolute/-/is-absolute-1.0.0.tgz#395e1ae84b11f26ad1795e73c17378e48a301576" 412 | integrity sha512-dOWoqflvcydARa360Gvv18DZ/gRuHKi2NU/wU5X1ZFzdYfH29nkiNZsF3mp4OJ3H4yo9Mx8A/uAGNzpzPN3yBA== 413 | dependencies: 414 | is-relative "^1.0.0" 415 | is-windows "^1.0.1" 416 | 417 | is-extglob@^2.1.0: 418 | version "2.1.1" 419 | resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" 420 | integrity sha1-qIwCU1eR8C7TfHahueqXc8gz+MI= 421 | 422 | is-glob@^3.1.0: 423 | version "3.1.0" 424 | resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-3.1.0.tgz#7ba5ae24217804ac70707b96922567486cc3e84a" 425 | integrity sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo= 426 | dependencies: 427 | is-extglob "^2.1.0" 428 | 429 | is-negated-glob@^1.0.0: 430 | version "1.0.0" 431 | resolved "https://registry.yarnpkg.com/is-negated-glob/-/is-negated-glob-1.0.0.tgz#6910bca5da8c95e784b5751b976cf5a10fee36d2" 432 | integrity sha1-aRC8pdqMleeEtXUbl2z1oQ/uNtI= 433 | 434 | is-relative@^1.0.0: 435 | version "1.0.0" 436 | resolved "https://registry.yarnpkg.com/is-relative/-/is-relative-1.0.0.tgz#a1bb6935ce8c5dba1e8b9754b9b2dcc020e2260d" 437 | integrity sha512-Kw/ReK0iqwKeu0MITLFuj0jbPAmEiOsIwyIXvvbfa6QfmN9pkD1M+8pdk7Rl/dTKbH34/XBFMbgD4iMJhLQbGA== 438 | dependencies: 439 | is-unc-path "^1.0.0" 440 | 441 | is-unc-path@^1.0.0: 442 | version "1.0.0" 443 | resolved "https://registry.yarnpkg.com/is-unc-path/-/is-unc-path-1.0.0.tgz#d731e8898ed090a12c352ad2eaed5095ad322c9d" 444 | integrity sha512-mrGpVd0fs7WWLfVsStvgF6iEJnbjDFZh9/emhRDcGWTduTfNHd9CHeUwH3gYIjdbwo4On6hunkztwOaAw0yllQ== 445 | dependencies: 446 | unc-path-regex "^0.1.2" 447 | 448 | is-windows@^1.0.1: 449 | version "1.0.2" 450 | resolved "https://registry.yarnpkg.com/is-windows/-/is-windows-1.0.2.tgz#d1850eb9791ecd18e6182ce12a30f396634bb19d" 451 | integrity sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA== 452 | 453 | isarray@~1.0.0: 454 | version "1.0.0" 455 | resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" 456 | integrity sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE= 457 | 458 | js-tokens@^4.0.0: 459 | version "4.0.0" 460 | resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" 461 | integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== 462 | 463 | js-yaml@^4.1.0: 464 | version "4.1.0" 465 | resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602" 466 | integrity sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA== 467 | dependencies: 468 | argparse "^2.0.1" 469 | 470 | json-schema-traverse@^1.0.0: 471 | version "1.0.0" 472 | resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz#ae7bcb3656ab77a73ba5c49bf654f38e6b6860e2" 473 | integrity sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug== 474 | 475 | json-stable-stringify-without-jsonify@^1.0.1: 476 | version "1.0.1" 477 | resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651" 478 | integrity sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE= 479 | 480 | jsonpointer@^5.0.0: 481 | version "5.0.0" 482 | resolved "https://registry.yarnpkg.com/jsonpointer/-/jsonpointer-5.0.0.tgz#f802669a524ec4805fa7389eadbc9921d5dc8072" 483 | integrity sha512-PNYZIdMjVIvVgDSYKTT63Y+KZ6IZvGRNNWcxwD+GNnUz1MKPfv30J8ueCjdwcN0nDx2SlshgyB7Oy0epAzVRRg== 484 | 485 | leven@^2.1.0: 486 | version "2.1.0" 487 | resolved "https://registry.yarnpkg.com/leven/-/leven-2.1.0.tgz#c2e7a9f772094dee9d34202ae8acce4687875580" 488 | integrity sha1-wuep93IJTe6dNCAq6KzORoeHVYA= 489 | 490 | "leven@^3.1.0 < 4": 491 | version "3.1.0" 492 | resolved "https://registry.yarnpkg.com/leven/-/leven-3.1.0.tgz#77891de834064cccba82ae7842bb6b14a13ed7f2" 493 | integrity sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A== 494 | 495 | luxon@^2.3.0: 496 | version "2.3.0" 497 | resolved "https://registry.yarnpkg.com/luxon/-/luxon-2.3.0.tgz#bf16a7e642513c2a20a6230a6a41b0ab446d0045" 498 | integrity sha512-gv6jZCV+gGIrVKhO90yrsn8qXPKD8HYZJtrUDSfEbow8Tkw84T9OnCyJhWvnJIaIF/tBuiAjZuQHUt1LddX2mg== 499 | 500 | make-error@^1.1.1: 501 | version "1.3.6" 502 | resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.3.6.tgz#2eb2e37ea9b67c4891f684a1394799af484cf7a2" 503 | integrity sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw== 504 | 505 | minimatch@^3.0.4: 506 | version "3.0.4" 507 | resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" 508 | integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA== 509 | dependencies: 510 | brace-expansion "^1.1.7" 511 | 512 | minimist@^1.1.0, minimist@^1.2.5: 513 | version "1.2.5" 514 | resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602" 515 | integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw== 516 | 517 | mqtt-packet@^6.6.0: 518 | version "6.7.0" 519 | resolved "https://registry.yarnpkg.com/mqtt-packet/-/mqtt-packet-6.7.0.tgz#0098f7f5d02f34b8dce35507befd6394e1109575" 520 | integrity sha512-GzgeeCirQpB59FyhHvf8BLiIYgxctPSxuSyaF2vWnkt7paX7jtuQ8Gpl+DkHCxZmYuv7GQE6zcUAegpafd0MqQ== 521 | dependencies: 522 | bl "^4.0.2" 523 | debug "^4.1.1" 524 | process-nextick-args "^2.0.1" 525 | 526 | mqtt@^4.1.0: 527 | version "4.2.6" 528 | resolved "https://registry.yarnpkg.com/mqtt/-/mqtt-4.2.6.tgz#b655547a9cfb3d86bfb398948b8dbb37e2e3bfd0" 529 | integrity sha512-GpxVObyOzL0CGPBqo6B04GinN8JLk12NRYAIkYvARd9ZCoJKevvOyCaWK6bdK/kFSDj3LPDnCsJbezzNlsi87Q== 530 | dependencies: 531 | commist "^1.0.0" 532 | concat-stream "^2.0.0" 533 | debug "^4.1.1" 534 | help-me "^1.0.1" 535 | inherits "^2.0.3" 536 | minimist "^1.2.5" 537 | mqtt-packet "^6.6.0" 538 | pump "^3.0.0" 539 | readable-stream "^3.6.0" 540 | reinterval "^1.1.0" 541 | split2 "^3.1.0" 542 | ws "^7.3.1" 543 | xtend "^4.0.2" 544 | 545 | ms@2.1.2: 546 | version "2.1.2" 547 | resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" 548 | integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== 549 | 550 | net-snmp@^3.5.8: 551 | version "3.5.8" 552 | resolved "https://registry.yarnpkg.com/net-snmp/-/net-snmp-3.5.8.tgz#90fd37b2ef28bf30a96fc176540bec0fe2172d5e" 553 | integrity sha512-SPMn8jE9QXD9QUMOzEeEEXAYZtRIX3zUyPDewZmRRZ0Qgx9t42H90VQG165tG32/MglU8gCoRWoBsGapyN75Jg== 554 | dependencies: 555 | asn1-ber "*" 556 | smart-buffer "^4.1.0" 557 | 558 | once@^1.3.0, once@^1.3.1, once@^1.4.0: 559 | version "1.4.0" 560 | resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" 561 | integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E= 562 | dependencies: 563 | wrappy "1" 564 | 565 | ordered-read-streams@^1.0.0: 566 | version "1.0.1" 567 | resolved "https://registry.yarnpkg.com/ordered-read-streams/-/ordered-read-streams-1.0.1.tgz#77c0cb37c41525d64166d990ffad7ec6a0e1363e" 568 | integrity sha1-d8DLN8QVJdZBZtmQ/61+xqDhNj4= 569 | dependencies: 570 | readable-stream "^2.0.1" 571 | 572 | path-dirname@^1.0.0: 573 | version "1.0.2" 574 | resolved "https://registry.yarnpkg.com/path-dirname/-/path-dirname-1.0.2.tgz#cc33d24d525e099a5388c0336c6e32b9160609e0" 575 | integrity sha1-zDPSTVJeCZpTiMAzbG4yuRYGCeA= 576 | 577 | path-is-absolute@^1.0.0: 578 | version "1.0.1" 579 | resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" 580 | integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18= 581 | 582 | prettier@^2.5.1: 583 | version "2.5.1" 584 | resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.5.1.tgz#fff75fa9d519c54cf0fce328c1017d94546bc56a" 585 | integrity sha512-vBZcPRUR5MZJwoyi3ZoyQlc1rXeEck8KgeC9AwwOn+exuxLxq5toTRDTSaVrXHxelDMHy9zlicw8u66yxoSUFg== 586 | 587 | process-nextick-args@^2.0.1, process-nextick-args@~2.0.0: 588 | version "2.0.1" 589 | resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2" 590 | integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag== 591 | 592 | pump@^2.0.0: 593 | version "2.0.1" 594 | resolved "https://registry.yarnpkg.com/pump/-/pump-2.0.1.tgz#12399add6e4cf7526d973cbc8b5ce2e2908b3909" 595 | integrity sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA== 596 | dependencies: 597 | end-of-stream "^1.1.0" 598 | once "^1.3.1" 599 | 600 | pump@^3.0.0: 601 | version "3.0.0" 602 | resolved "https://registry.yarnpkg.com/pump/-/pump-3.0.0.tgz#b4a2116815bde2f4e1ea602354e8c75565107a64" 603 | integrity sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww== 604 | dependencies: 605 | end-of-stream "^1.1.0" 606 | once "^1.3.1" 607 | 608 | pumpify@^1.3.5: 609 | version "1.5.1" 610 | resolved "https://registry.yarnpkg.com/pumpify/-/pumpify-1.5.1.tgz#36513be246ab27570b1a374a5ce278bfd74370ce" 611 | integrity sha512-oClZI37HvuUJJxSKKrC17bZ9Cu0ZYhEAGPsPUy9KlMUmv9dKX2o77RUmq7f3XjIxbwyGwYzbzQ1L2Ks8sIradQ== 612 | dependencies: 613 | duplexify "^3.6.0" 614 | inherits "^2.0.3" 615 | pump "^2.0.0" 616 | 617 | punycode@^2.1.0: 618 | version "2.1.1" 619 | resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec" 620 | integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A== 621 | 622 | "readable-stream@> 1.0.0 < 3.0.0", readable-stream@^2.0.0, readable-stream@^2.0.1, readable-stream@^2.1.5, readable-stream@~2.3.6: 623 | version "2.3.7" 624 | resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.7.tgz#1eca1cf711aef814c04f62252a36a62f6cb23b57" 625 | integrity sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw== 626 | dependencies: 627 | core-util-is "~1.0.0" 628 | inherits "~2.0.3" 629 | isarray "~1.0.0" 630 | process-nextick-args "~2.0.0" 631 | safe-buffer "~5.1.1" 632 | string_decoder "~1.1.1" 633 | util-deprecate "~1.0.1" 634 | 635 | readable-stream@^3.0.0, readable-stream@^3.0.2, readable-stream@^3.4.0, readable-stream@^3.6.0: 636 | version "3.6.0" 637 | resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.0.tgz#337bbda3adc0706bd3e024426a286d4b4b2c9198" 638 | integrity sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA== 639 | dependencies: 640 | inherits "^2.0.3" 641 | string_decoder "^1.1.1" 642 | util-deprecate "^1.0.1" 643 | 644 | reinterval@^1.1.0: 645 | version "1.1.0" 646 | resolved "https://registry.yarnpkg.com/reinterval/-/reinterval-1.1.0.tgz#3361ecfa3ca6c18283380dd0bb9546f390f5ece7" 647 | integrity sha1-M2Hs+jymwYKDOA3Qu5VG85D17Oc= 648 | 649 | remove-trailing-separator@^1.0.1: 650 | version "1.1.0" 651 | resolved "https://registry.yarnpkg.com/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz#c24bce2a283adad5bc3f58e0d48249b92379d8ef" 652 | integrity sha1-wkvOKig62tW8P1jg1IJJuSN52O8= 653 | 654 | require-from-string@^2.0.2: 655 | version "2.0.2" 656 | resolved "https://registry.yarnpkg.com/require-from-string/-/require-from-string-2.0.2.tgz#89a7fdd938261267318eafe14f9c32e598c36909" 657 | integrity sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw== 658 | 659 | safe-buffer@~5.1.0, safe-buffer@~5.1.1: 660 | version "5.1.2" 661 | resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" 662 | integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== 663 | 664 | safe-buffer@~5.2.0: 665 | version "5.2.1" 666 | resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" 667 | integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== 668 | 669 | slugify@^1.6.5: 670 | version "1.6.5" 671 | resolved "https://registry.yarnpkg.com/slugify/-/slugify-1.6.5.tgz#c8f5c072bf2135b80703589b39a3d41451fbe8c8" 672 | integrity sha512-8mo9bslnBO3tr5PEVFzMPIWwWnipGS0xVbYf65zxDqfNwmzYn1LpiKNrR6DlClusuvo+hDHd1zKpmfAe83NQSQ== 673 | 674 | smart-buffer@^4.1.0: 675 | version "4.1.0" 676 | resolved "https://registry.yarnpkg.com/smart-buffer/-/smart-buffer-4.1.0.tgz#91605c25d91652f4661ea69ccf45f1b331ca21ba" 677 | integrity sha512-iVICrxOzCynf/SNaBQCw34eM9jROU/s5rzIhpOvzhzuYHfJR/DhZfDkXiZSgKXfgv26HT3Yni3AV/DGw0cGnnw== 678 | 679 | split2@^3.1.0: 680 | version "3.2.2" 681 | resolved "https://registry.yarnpkg.com/split2/-/split2-3.2.2.tgz#bf2cf2a37d838312c249c89206fd7a17dd12365f" 682 | integrity sha512-9NThjpgZnifTkJpzTZ7Eue85S49QwpNhZTq6GRJwObb6jnLFNGB7Qm73V5HewTROPyxD0C29xqmaI68bQtV+hg== 683 | dependencies: 684 | readable-stream "^3.0.0" 685 | 686 | stream-shift@^1.0.0: 687 | version "1.0.1" 688 | resolved "https://registry.yarnpkg.com/stream-shift/-/stream-shift-1.0.1.tgz#d7088281559ab2778424279b0877da3c392d5a3d" 689 | integrity sha512-AiisoFqQ0vbGcZgQPY1cdP2I76glaVA/RauYR4G4thNFgkTqr90yXTo4LYX60Jl+sIlPNHHdGSwo01AvbKUSVQ== 690 | 691 | string_decoder@^1.1.1: 692 | version "1.3.0" 693 | resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e" 694 | integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA== 695 | dependencies: 696 | safe-buffer "~5.2.0" 697 | 698 | string_decoder@~1.1.1: 699 | version "1.1.1" 700 | resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8" 701 | integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg== 702 | dependencies: 703 | safe-buffer "~5.1.0" 704 | 705 | supports-color@^5.3.0: 706 | version "5.5.0" 707 | resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" 708 | integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow== 709 | dependencies: 710 | has-flag "^3.0.0" 711 | 712 | supports-color@^7.1.0: 713 | version "7.2.0" 714 | resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da" 715 | integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw== 716 | dependencies: 717 | has-flag "^4.0.0" 718 | 719 | through2-filter@^3.0.0: 720 | version "3.0.0" 721 | resolved "https://registry.yarnpkg.com/through2-filter/-/through2-filter-3.0.0.tgz#700e786df2367c2c88cd8aa5be4cf9c1e7831254" 722 | integrity sha512-jaRjI2WxN3W1V8/FMZ9HKIBXixtiqs3SQSX4/YGIiP3gL6djW48VoZq9tDqeCWs3MT8YY5wb/zli8VW8snY1CA== 723 | dependencies: 724 | through2 "~2.0.0" 725 | xtend "~4.0.0" 726 | 727 | through2@^2.0.1, through2@~2.0.0: 728 | version "2.0.5" 729 | resolved "https://registry.yarnpkg.com/through2/-/through2-2.0.5.tgz#01c1e39eb31d07cb7d03a96a70823260b23132cd" 730 | integrity sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ== 731 | dependencies: 732 | readable-stream "~2.3.6" 733 | xtend "~4.0.1" 734 | 735 | to-absolute-glob@^2.0.0: 736 | version "2.0.2" 737 | resolved "https://registry.yarnpkg.com/to-absolute-glob/-/to-absolute-glob-2.0.2.tgz#1865f43d9e74b0822db9f145b78cff7d0f7c849b" 738 | integrity sha1-GGX0PZ50sIItufFFt4z/fQ98hJs= 739 | dependencies: 740 | is-absolute "^1.0.0" 741 | is-negated-glob "^1.0.0" 742 | 743 | ts-node@^10.4.0: 744 | version "10.4.0" 745 | resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-10.4.0.tgz#680f88945885f4e6cf450e7f0d6223dd404895f7" 746 | integrity sha512-g0FlPvvCXSIO1JDF6S232P5jPYqBkRL9qly81ZgAOSU7rwI0stphCgd2kLiCrU9DjQCrJMWEqcNSjQL02s6d8A== 747 | dependencies: 748 | "@cspotcode/source-map-support" "0.7.0" 749 | "@tsconfig/node10" "^1.0.7" 750 | "@tsconfig/node12" "^1.0.7" 751 | "@tsconfig/node14" "^1.0.0" 752 | "@tsconfig/node16" "^1.0.2" 753 | acorn "^8.4.1" 754 | acorn-walk "^8.1.1" 755 | arg "^4.1.0" 756 | create-require "^1.1.0" 757 | diff "^4.0.1" 758 | make-error "^1.1.1" 759 | yn "3.1.1" 760 | 761 | typedarray@^0.0.6: 762 | version "0.0.6" 763 | resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" 764 | integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c= 765 | 766 | typescript@^4.5.4: 767 | version "4.5.4" 768 | resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.5.4.tgz#a17d3a0263bf5c8723b9c52f43c5084edf13c2e8" 769 | integrity sha512-VgYs2A2QIRuGphtzFV7aQJduJ2gyfTljngLzjpfW9FoYZF6xuw1W0vW9ghCKLfcWrCFxK81CSGRAvS1pn4fIUg== 770 | 771 | unc-path-regex@^0.1.2: 772 | version "0.1.2" 773 | resolved "https://registry.yarnpkg.com/unc-path-regex/-/unc-path-regex-0.1.2.tgz#e73dd3d7b0d7c5ed86fbac6b0ae7d8c6a69d50fa" 774 | integrity sha1-5z3T17DXxe2G+6xrCufYxqadUPo= 775 | 776 | unique-stream@^2.0.2: 777 | version "2.3.1" 778 | resolved "https://registry.yarnpkg.com/unique-stream/-/unique-stream-2.3.1.tgz#c65d110e9a4adf9a6c5948b28053d9a8d04cbeac" 779 | integrity sha512-2nY4TnBE70yoxHkDli7DMazpWiP7xMdCYqU2nBRO0UB+ZpEkGsSija7MvmvnZFUeC+mrgiUfcHSr3LmRFIg4+A== 780 | dependencies: 781 | json-stable-stringify-without-jsonify "^1.0.1" 782 | through2-filter "^3.0.0" 783 | 784 | uri-js@^4.2.2: 785 | version "4.4.1" 786 | resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.4.1.tgz#9b1a52595225859e55f669d928f88c6c57f2a77e" 787 | integrity sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg== 788 | dependencies: 789 | punycode "^2.1.0" 790 | 791 | util-deprecate@^1.0.1, util-deprecate@~1.0.1: 792 | version "1.0.2" 793 | resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" 794 | integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8= 795 | 796 | wrappy@1: 797 | version "1.0.2" 798 | resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" 799 | integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= 800 | 801 | ws@^7.3.1: 802 | version "7.4.3" 803 | resolved "https://registry.yarnpkg.com/ws/-/ws-7.4.3.tgz#1f9643de34a543b8edb124bdcbc457ae55a6e5cd" 804 | integrity sha512-hr6vCR76GsossIRsr8OLR9acVVm1jyfEWvhbNjtgPOrfvAlKzvyeg/P6r8RuDjRyrcQoPQT7K0DGEPc7Ae6jzA== 805 | 806 | xtend@^4.0.0, xtend@^4.0.2, xtend@~4.0.0, xtend@~4.0.1: 807 | version "4.0.2" 808 | resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54" 809 | integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ== 810 | 811 | yn@3.1.1: 812 | version "3.1.1" 813 | resolved "https://registry.yarnpkg.com/yn/-/yn-3.1.1.tgz#1e87401a09d767c1d5eab26a6e4c185182d2eb50" 814 | integrity sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q== 815 | --------------------------------------------------------------------------------