{"_id":"@emitucom/bacnet-connector","name":"@emitucom/bacnet-connector","dist-tags":{"latest":"0.1.0"},"versions":{"0.1.0":{"name":"@emitucom/bacnet-connector","version":"0.1.0","description":"Native TypeScript BACnet client library for reading and writing BACnet objects over IP and MSTP","main":"dist/index.js","types":"dist/index.d.ts","scripts":{"prepublishOnly":"npm run build","build":"tsc -p tsconfig.build.json","lint":"tsc --noEmit","test":"jest","test:watch":"jest --watch","test:coverage":"jest --coverage"},"dependencies":{"serialport":"^8.0.2","raw-socket":"^1.8.1"},"devDependencies":{"typescript":"^4.9.5","@types/node":"~10.17.60","@types/serialport":"^8.0.5","jest":"^24.9.0","ts-jest":"^24.3.0","@types/jest":"^24.9.1"},"engines":{"node":">=8.17.0"},"author":{"name":"Emitu","email":"dev@emitu.com","url":"https://emitu.com"},"license":"BSD-3-Clause","homepage":"https://github.com/EmituCom/BACnetConnector#readme","repository":{"type":"git","url":"git+https://github.com/EmituCom/BACnetConnector.git"},"bugs":{"url":"https://github.com/EmituCom/BACnetConnector/issues"},"keywords":["bacnet","bacnet-ip","bacnet-mstp","building-automation","hvac","iot","industrial","typescript","plc"],"gitHead":"258e2cec97f86fc5416c281ee17ecefada9c1f76","_id":"@emitucom/bacnet-connector@0.1.0","_nodeVersion":"8.17.0","_npmVersion":"6.13.4","dist":{"integrity":"sha512-qjYJABzOm8HH22dI+pfAlUzmfMDSQ8h1PW1HArIHQTQEAemcqf/AHfgXItEk4tlv8owmteHAIpcOcAhRoI0dKA==","shasum":"64408bbc5256b6a0064c3abeadf01ffb9546a194","tarball":"https://registry.npmjs.org/@emitucom/bacnet-connector/-/bacnet-connector-0.1.0.tgz","fileCount":173,"unpackedSize":307024,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEQCIFWih3l2GGoltlaLjOJY+2GkuaolO0ngn900E8aB1Z2aAiBdMnrn12h7WUHYK5+BBpx5jTu46+ish2N0KC9vz0vrpw=="}]},"_npmUser":{"name":"emitucom","email":"dev@emitu.com"},"directories":{},"maintainers":[{"name":"emitucom","email":"dev@emitu.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/bacnet-connector_0.1.0_1772032713967_0.6586149641273233"},"_hasShrinkwrap":false}},"time":{"created":"2026-02-25T15:18:33.861Z","0.1.0":"2026-02-25T15:18:34.112Z","modified":"2026-02-25T15:18:34.319Z"},"maintainers":[{"name":"emitucom","email":"dev@emitu.com"}],"description":"Native TypeScript BACnet client library for reading and writing BACnet objects over IP and MSTP","homepage":"https://github.com/EmituCom/BACnetConnector#readme","keywords":["bacnet","bacnet-ip","bacnet-mstp","building-automation","hvac","iot","industrial","typescript","plc"],"repository":{"type":"git","url":"git+https://github.com/EmituCom/BACnetConnector.git"},"author":{"name":"Emitu","email":"dev@emitu.com","url":"https://emitu.com"},"bugs":{"url":"https://github.com/EmituCom/BACnetConnector/issues"},"license":"BSD-3-Clause","readme":"# @emitucom/bacnet-connector\n\nNative TypeScript BACnet client library with no external BACnet dependencies.\nSupports BACnet/IP (UDP), MS/TP (serial), and Ethernet (ISO 8802-3) transports.\n\n## Requirements\n\n- **Node.js** >= 8.17.0\n- For MS/TP: a USB-to-RS485 adapter and the `serialport` npm package\n- For Ethernet: `raw-socket` npm package and `CAP_NET_RAW` capability (Linux) or root (macOS/Windows)\n\n## Installation\n\n```bash\nnpm install @emitucom/bacnet-connector\n```\n\nBuild from source:\n\n```bash\ngit clone https://github.com/EmituCom/BACnetConnector.git\ncd BACnetConnector\nnpm install\nnpm run build\n```\n\n## Quick Start\n\n```js\nconst { BACnetClient, BACnetIPTransport } = require('@emitucom/bacnet-connector');\n\nconst client = new BACnetClient({\n  transport: new BACnetIPTransport({ broadcastAddress: '192.168.1.255' }),\n});\n\nasync function main() {\n  await client.open();\n\n  // Discover all devices on the local network\n  const devices = await client.whoIs();\n  console.log(`Found ${devices.length} device(s)`);\n  devices.forEach(d => console.log(d.deviceIdentifier.instance, d.vendorId));\n\n  await client.close();\n}\n\nmain().catch(console.error);\n```\n\n---\n\n## Transports\n\n### BACnet/IP (UDP)\n\nThe most common transport. Uses UDP port 47808 (0xBAC0).\n\n```js\nconst { BACnetIPTransport } = require('@emitucom/bacnet-connector');\n\nconst transport = new BACnetIPTransport({\n  localAddress:     '0.0.0.0',     // bind address (default: 0.0.0.0)\n  localPort:        47808,          // bind port     (default: 47808)\n  broadcastAddress: '192.168.1.255',// subnet broadcast (default: 255.255.255.255)\n  broadcastPort:    47808,          // broadcast port   (default: 47808)\n});\n```\n\n> **Tip:** Always use your subnet broadcast address (e.g. `192.168.1.255`) rather than\n> `255.255.255.255`. On Linux the latter may not route to the correct interface.\n\n### MS/TP (Serial RS-485)\n\nFor BACnet MS/TP field buses. Requires the `serialport` package.\n\n```js\nconst { MstpTransport } = require('@emitucom/bacnet-connector');\n\nconst transport = new MstpTransport({\n  port:          '/dev/ttyUSB0', // serial port path (required)\n  baudRate:      76800,          // baud rate        (default: 76800)\n  macAddress:    0,              // this node's MAC  (default: 0, range 0–127)\n  maxMaster:     127,            // highest master MAC to poll (default: 127)\n  maxInfoFrames: 1,              // frames per token pass      (default: 1)\n});\n```\n\n### Ethernet (ISO 8802-3)\n\nRaw Ethernet frames using EtherType `0x82DC`. Requires `raw-socket` and elevated privileges.\n\n```js\nconst { EthernetTransport } = require('@emitucom/bacnet-connector');\n\nconst transport = new EthernetTransport({\n  interface: 'eth0',                           // network interface (required)\n  sourceMac: Buffer.from([0x00,0x11,0x22,0x33,0x44,0x55]), // optional\n});\n```\n\nOn Linux, grant raw socket access without running as root:\n\n```bash\nsudo setcap cap_net_raw+eip $(which node)\n```\n\n---\n\n## API Reference\n\n### `new BACnetClient(options)`\n\n| Option | Type | Default | Description |\n|---|---|---|---|\n| `transport` | `ITransport` | required | Transport instance to use |\n| `timeoutMs` | `number` | `3000` | Confirmed request timeout (ms) |\n| `maxRetries` | `number` | `2` | Retransmissions before rejection |\n| `whoIsTimeoutMs` | `number` | `3000` | Who-Is collection window (ms) |\n\n#### `client.open()` → `Promise<void>`\n\nOpens the underlying transport (binds socket / opens serial port).\n\n#### `client.close()` → `Promise<void>`\n\nCloses the transport and rejects all pending requests.\n\n---\n\n### `client.whoIs(lowLimit?, highLimit?, opts?)` → `Promise<IAmResult[]>`\n\nBroadcasts a Who-Is and collects I-Am replies for `whoIsTimeoutMs` milliseconds.\n\n| Parameter | Type | Description |\n|---|---|---|\n| `lowLimit` | `number` | Filter: only collect devices with instance ≥ lowLimit |\n| `highLimit` | `number` | Filter: only collect devices with instance ≤ highLimit |\n| `opts.remoteNetwork` | `number` | NPDU destination network. Use `0xFFFF` for global broadcast (reaches devices behind BACnet routers) |\n| `opts.routerAddress` | `BACnetAddress` | Send the Who-Is to a specific router instead of broadcasting |\n\n```js\n// Local network only\nconst devices = await client.whoIs();\n\n// Global broadcast — reaches devices on all routed networks\nconst allDevices = await client.whoIs(undefined, undefined, { remoteNetwork: 0xFFFF });\n\n// Specific instance range\nconst subset = await client.whoIs(1000, 2000);\n```\n\n**`IAmResult`**\n\n```ts\n{\n  deviceIdentifier:     { objectType: number; instance: number };\n  maxApduLength:        number;\n  segmentationSupported: number;\n  vendorId:             number;\n  source?:              BACnetAddress; // transport-layer source (IP/port of sender or router)\n  networkSource?:       BACnetAddress; // NPDU-layer source (net + MAC for routed devices)\n}\n```\n\n---\n\n### `client.readProperty(dest, request)` → `Promise<ReadPropertyResult>`\n\nReads a single property from a BACnet object.\n\n```js\nconst { ObjectType, PropertyIdentifier } = require('@emitucom/bacnet-connector');\n\nconst result = await client.readProperty(\n  { ip: '192.168.1.11', port: 47808 },\n  {\n    objectType: ObjectType.Device,\n    instance:   1973,\n    propertyId: PropertyIdentifier.ObjectName,\n  }\n);\nconsole.log(result.value[0].value); // e.g. \"enteliWEB\"\n```\n\nFor devices behind a BACnet router, combine the router IP and the device's network address:\n\n```js\n// Device on BACnet network 2, MAC 0x214E000000, reachable via router at 192.168.1.8\nconst result = await client.readProperty(\n  { ip: '192.168.1.8', port: 47808, net: 2, adr: Buffer.from([0x21, 0x4e, 0x00, 0x00, 0x00, 0x00]) },\n  { objectType: ObjectType.Device, instance: 20001, propertyId: PropertyIdentifier.ObjectName }\n);\n```\n\n---\n\n### `client.writeProperty(dest, request)` → `Promise<void>`\n\nWrites a value to a BACnet property.\n\n```js\nconst { BACnetValue } = require('@emitucom/bacnet-connector');\n\nawait client.writeProperty(\n  { ip: '192.168.1.11', port: 47808 },\n  {\n    objectType: ObjectType.AnalogValue,\n    instance:   1,\n    propertyId: PropertyIdentifier.PresentValue,\n    value:      [{ type: 'Real', value: 21.5 }],\n    priority:   8, // optional, 1–16\n  }\n);\n```\n\n---\n\n### `client.readPropertyMultiple(dest, request)` → `Promise<ReadPropertyMultipleResult>`\n\nReads multiple properties from multiple objects in one request.\n\n```js\nconst results = await client.readPropertyMultiple(\n  { ip: '192.168.1.11', port: 47808 },\n  [\n    {\n      objectType: ObjectType.Device,\n      instance:   1973,\n      properties: [\n        { propertyId: PropertyIdentifier.ObjectName },\n        { propertyId: PropertyIdentifier.VendorName },\n        { propertyId: PropertyIdentifier.SystemStatus },\n      ],\n    },\n    {\n      objectType: ObjectType.AnalogInput,\n      instance:   1,\n      properties: [\n        { propertyId: PropertyIdentifier.PresentValue },\n        { propertyId: PropertyIdentifier.Units },\n      ],\n    },\n  ]\n);\n```\n\n---\n\n### `client.subscribeCOV(dest, request)` → `Promise<void>`\n\nSubscribes to Change-of-Value notifications for an object.\n\n```js\nawait client.subscribeCOV(\n  { ip: '192.168.1.11', port: 47808 },\n  {\n    subscriberProcessId:          42,\n    monitoredObjectType:          ObjectType.BinaryInput,\n    monitoredObjectInstance:      1,\n    issueConfirmedNotifications:  false,\n    lifetime:                     300, // seconds; 0 = cancel\n  }\n);\n\nclient.on('covNotification', notification => {\n  console.log('COV from device', notification.initiatingDeviceIdentifier.instance);\n  notification.listOfValues.forEach(v => {\n    console.log(`  property ${v.propertyId}:`, v.value);\n  });\n});\n```\n\n---\n\n### Events\n\n```js\nclient.on('iAm',             result => { /* IAmResult       */ });\nclient.on('covNotification', result => { /* COVNotificationResult */ });\nclient.on('error',           err    => { /* Error           */ });\n```\n\n---\n\n## Types\n\n### `BACnetAddress`\n\n```ts\n{\n  ip?:          string;  // IPv4 dotted-decimal, e.g. \"192.168.1.10\"\n  port?:        number;  // UDP port, default 47808\n  mac?:         number;  // MS/TP MAC (0–127)\n  ethernetMac?: Buffer;  // Ethernet MAC (6 bytes)\n  net?:         number;  // BACnet network number\n  adr?:         Buffer;  // BACnet device address bytes\n}\n```\n\n### `BACnetValue`\n\nUnion of all BACnet application-layer data types:\n\n```ts\n{ type: 'Null' }\n{ type: 'Boolean';    value: boolean }\n{ type: 'Unsigned';   value: number }\n{ type: 'Signed';     value: number }\n{ type: 'Real';       value: number }\n{ type: 'Double';     value: number }\n{ type: 'OctetString'; value: Buffer }\n{ type: 'CharString'; value: string; encoding: number }\n{ type: 'BitString';  unusedBits: number; bits: Buffer }\n{ type: 'Enumerated'; value: number }\n{ type: 'Date';       year: number; month: number; day: number; dayOfWeek: number }\n{ type: 'Time';       hour: number; minute: number; second: number; hundredths: number }\n{ type: 'ObjectIdentifier'; objectType: number; instance: number }\n```\n\n---\n\n## Constants\n\n```js\nconst { ObjectType, PropertyIdentifier } = require('@emitucom/bacnet-connector');\n\nObjectType.Device       // 8\nObjectType.AnalogInput  // 0\nObjectType.BinaryInput  // 3\n// ... full ASHRAE 135 table\n\nPropertyIdentifier.ObjectName     // 77\nPropertyIdentifier.PresentValue   // 85\nPropertyIdentifier.VendorName     // 121\n// ... full ASHRAE 135 table\n```\n\n---\n\n## Examples\n\n```\nexamples/\n  scan.js    Discover all BACnet devices on the network (local + routed)\n```\n\n### Running the scanner\n\n```bash\nnpm run build\n\n# Auto-detect broadcast address\nnode examples/scan.js\n\n# Specify subnet broadcast explicitly (recommended)\nnode examples/scan.js --broadcast 192.168.1.255\n\n# Wider collection window, skip property reads\nnode examples/scan.js --timeout 5000 --no-props\n\n# Filter by device instance range\nnode examples/scan.js --low 1000 --high 9999\n\n# Verbose mode — prints each I-Am as it arrives\nnode examples/scan.js --verbose\n```\n\n---\n\n## Project Structure\n\n```\nsrc/\n  client/\n    BACnetClient.ts          Main client class\n    BACnetClientOptions.ts   Client configuration\n    InvokeIdManager.ts       Invoke ID allocation (0–255, rolling)\n    PendingRequestManager.ts Timeout / retry logic for confirmed requests\n  transport/\n    ip/\n      BACnetIPTransport.ts   BACnet/IP UDP transport\n      BACnetIPOptions.ts\n    mstp/\n      MstpTransport.ts       MS/TP serial transport\n      MstpFramer.ts          MS/TP frame encoder/decoder\n      MstpCrc.ts             CRC-8 / CRC-16 for MS/TP\n      MstpOptions.ts\n    ethernet/\n      EthernetTransport.ts   Raw Ethernet transport\n      EthernetOptions.ts\n  codec/\n    bvlc/                    BACnet Virtual Link Control (BACnet/IP header)\n    npdu/                    Network PDU (routing header)\n    apdu/                    Application PDU (service type + invoke ID)\n    tag/                     BACnet application tag encoder/decoder\n    services/                Individual service encoders and decoders\n  constants/\n    ObjectType.ts            BACnet object type enumeration\n    PropertyIdentifier.ts    BACnet property identifier enumeration\n    ConfirmedService.ts      Confirmed service choice codes\n    UnconfirmedService.ts    Unconfirmed service choice codes\n    ErrorCodes.ts            BACnet error class and error code enumerations\n  types/\n    BACnetDataTypes.ts       BACnetAddress, BACnetValue, TransportMessage\n    ServiceShapes.ts         Request/result interfaces for all services\n  index.ts                   Public API barrel export\ntests/\n  unit/                      Unit tests (Jest)\n  integration/               Integration tests\nexamples/\n  scan.js                    Network scanner\n```\n\n## Building\n\n```bash\nnpm run build          # compile TypeScript → dist/\nnpm test               # run unit tests\nnpm run test:coverage  # run tests with coverage report\nnpm run lint           # type-check without emitting\n```\n\n---\n\n## Contributing\n\nContributions are welcome. Please open an issue or pull request on [GitHub](https://github.com/EmituCom/BACnetConnector).\n\n---\n\n## License\n\n[BSD 3-Clause](./LICENSE) © 2026 [Emitu](https://emitu.com)\n","readmeFilename":"README.md","_rev":"1-a765cbb9daf57f7a72ef56ac50df2407"}