BACnet to Brick: Turning a Raw Point Scrape into a Clean Model

A step-by-step walkthrough of converting a messy BACnet point list into a validated Brick Schema model — classification, the two relationship graphs, SHACL validation, and export.

A BACnet scrape is where most building-data projects begin and where most of them get stuck. You point a discovery tool at the network, and out comes a list of thousands of objects with names like AHU3_SAT, VAV_212_DPR, CHW_RET_TEMP and ZN_T_SP. It's complete, it's machine-readable, and it's almost unusable. Before any analytics rule, fault-detection routine or digital twin can touch it, that list has to become a Brick Schema model — typed, related, and validated. This is the walkthrough for getting there.

Why the scrape isn't a model

A BACnet point list is a flat inventory. It tells you what objects exist and, if you're lucky, a present value and a unit. It does not tell you what kind of thing each point is in any standardised sense, what equipment it belongs to, where that equipment sits, or what feeds what. Two identical buildings from two contractors will produce two completely different naming schemes for the same physical reality.

Brick fixes that by giving every point, piece of equipment and location a standard type and a set of typed relationships. The job of conversion is to add all the structure the scrape is missing — without inventing anything that isn't true on site.

Step 1 — Inventory every source, not just the scrape

The scrape is one input. It is rarely the most accurate one, and it is never the only one. Before mapping anything, pull together:

  • the BACnet point list (the raw objects and their addresses)
  • the controls contractor's equipment schedule (what plant actually exists)
  • the BMS / BAS export (graphics, trends, and any tagging already applied)
  • any existing Project Haystack tags (don't discard these — they're a head start)
  • the as-built drawings (for the connectivity nobody captured digitally)

Expect them to disagree. The scrape will list points for equipment the schedule doesn't mention; the drawings will show an AHU feeding zones the BMS never tagged. Reconciling those conflicts is the work. This is exactly why staging building data in a dedicated layer beats editing a live model — you need somewhere to hold the contradictions while you resolve them.

Step 2 — Classify each point and asset to a Brick type

This is the bulk of the effort. Every object has to be mapped to the right Brick class:

  • CHW_RET_TEMP → a Chilled_Water_Return_Temperature_Sensor
  • ZN_T_SP → a Zone_Air_Temperature_Setpoint
  • VAV_212_DPR → a Damper_Position_Command on a VAV box
  • AHU3 → an Air_Handling_Unit

Done by hand in a spreadsheet, this is slow and inconsistent — the same abbreviation gets classified three different ways by three different people. The leverage is in pattern-matching: once you've established that _SAT means supply air temperature on one AHU, the same rule classifies it across all of them. A visual, reviewable workflow that lets you classify in bulk and see what you've done beats per-row guesswork.

Step 3 — Build both relationship graphs

Here's the step first-timers skip, and the one that quietly breaks the model. Brick describes a building with two different kinds of relationship at once, and you have to build both:

Containmentwhere things live and what they're part of. hasPoint / isPointOf ties a sensor to its equipment. hasPart / isPartOf makes a VAV box part of an air-handling system. hasLocation / isLocatedIn places equipment in rooms, floors and zones.

Connectivitywhat feeds what. feeds / isFedBy captures flow: an AHU feeds the VAVs downstream of it; a chiller feeds the chilled-water loop.

The same chiller participates in both graphs simultaneously — it sits inside a spatial containment tree and a separate flow network. A tool that allows only one parent per asset forces you to pick one and throw the other away, and a Brick model with half its relationships missing won't answer the questions analytics platforms ask of it. You need multiple, typed parent relationships per node to represent both at once.

Step 4 — Validate against SHACL before you export

Brick ships SHACL shapes — machine-readable rules that say what a valid model looks like. A point of a given type must attach to a compatible equipment type; certain equipment must have certain points; relationships have to connect the right kinds of thing.

Run the model against those shapes and you get a list of structural errors: a sensor floating with no equipment, an AHU with no supply path, a zone with no location. Fix these in bulk, before export — not one at a time after the model has already loaded into your analytics platform and started generating wrong answers. A model that passes SHACL isn't just well-spelled; it's structurally correct.

Step 5 — Export Turtle or JSON-LD and hand it over

The output of a clean conversion is a serialised Brick model — Turtle (.ttl) or JSON-LD — that any Brick-aware platform can ingest. At that point the portability payoff kicks in: the same fault-detection rule or energy query that runs against this building runs against the next one you model the same way, with no per-site rewriting.

The shape of a good conversion

  1. Inventory every source and expect conflict.
  2. Classify each point and asset to its Brick type — in bulk, reviewably.
  3. Build both graphs — containment and connectivity, kept separate.
  4. Validate against SHACL and fix errors before export.
  5. Export Turtle or JSON-LD and load a model you trust.

The order is the point. The expensive mistakes all come from compressing these steps — classifying straight into a single tree, or loading before validating. Keep them distinct in a staging layer and a BACnet scrape becomes a Brick model you can build analytics on.


Want to see a BACnet export become a validated Brick model? Explore Brick Schema staging, see how it fits smart buildings, or get in touch.