Niagara SDK (Software Development Kit)

The Niagara SDK is the toolkit behind every custom module and driver. Working in it well takes more than the API – it takes a build environment, module packaging, signing, and versioning discipline that most integrators do not want to maintain in-house. Software Pile does this daily.

SDK Work We Handle

  • Module development against the SDK for your Niagara version line, using Baja components, services, and drivers
  • Build tooling with Gradle and the Workbench SDK, reproducible across engineers and CI
  • Module packaging and third-party-certificate signing so modules install cleanly on secured stations
  • Version compatibility management and recompilation when you move Niagara versions

Delivered as Real Software

We treat Niagara modules like the production software they are: source-controlled, documented, signed, and versioned – not one-off JARs with no history. When you upgrade Niagara or hand the work to another engineer, the trail is there.

Describe the module or driver you need and your Niagara version. We will scope the build and the ongoing compatibility path.

What Is the Niagara SDK?

The Niagara SDK is the toolchain for building Niagara modules: the framework libraries, the build system, and the tooling needed to compile, package and sign a module so a station will load it. It is what turns Java source into a deployable .jar module that Niagara recognises.

N4 module builds use Gradle. Getting a working, repeatable build — correct dependencies, correct target version, signing configured, output that installs cleanly — is usually the first real task in any Niagara development project, and it is more involved than a standard Java build.

Why Do Niagara Modules Have to Be Signed?

Modern Niagara stations will not load unsigned modules. Signing establishes that a module came from an identified source and has not been altered since. It is a security control and it is not optional in current deployments.

The commercial implication matters more than the technical one: signing requires a certificate, and that certificate belongs to somebody. Establish before development starts who signs, who holds the key, what happens if the relationship with the developer ends, and whether you can rebuild and re-sign the module yourself later. Source code you cannot sign is source code you cannot deploy.

What Does a Niagara Development Environment Need?

  • A matching Niagara installation for the target version — the SDK is tied to a Niagara release.
  • A compatible JDK, as required by that release.
  • Gradle and a working module build configuration.
  • A signing certificate configured in the build.
  • A test station — ideally real target hardware, not only a local simulation, since resource behaviour differs.
  • Version control, which is startlingly often absent in Niagara projects and is the root of most “which version is deployed?” incidents.

What About Supporting Multiple Niagara Versions?

Most integrators run an estate spanning several Niagara releases, and a module built for one will not automatically load on another. That means either maintaining separate builds per target version or restricting a module to a defined version range and being explicit about it.

This is a specification question, not an implementation detail. Decide up front which versions must be supported over the expected life of the station, because it drives build configuration, testing effort and long-term maintenance cost more than any feature in the module.

Related services: custom Niagara module and driver development · engineering tools.

What a Niagara Module Is Made Of

A module is not one body of code. It is a set of parts, each declared with a runtime profile that states where that code is allowed to run, and in Niagara 4 each part builds into its own jar, so a module with station-side and Workbench-side code produces myModule-rt.jar and myModule-wb.jar rather than a single file. The module manifest carries the name, vendor, version, declared dependencies and the parts list.

Putting code in the wrong part is a deployment problem rather than a compile problem, which is what makes it slow to find. On a development workstation the full Workbench classpath is present, so everything compiles and everything runs. The controller loads only the parts it is entitled to load, so the same code goes missing there, on hardware, at the end of the project.

  • rt: station-side runtime code. Components, services and drivers live here, and this part carries the tightest constraints on what it may depend on, because it has to run on the smallest target you support.
  • wb: Workbench-side code. Views, editors and manager tables that only exist while an engineer has the tool open. Nothing in rt may reference it.
  • ux: browser-facing HTML, JavaScript and CSS served to a web client, used for presentation rather than by station logic.
  • se: code that needs full Java SE. A controller running a restricted JVM profile will not load an se part, so anything placed there quietly does not exist on embedded hardware even though the same module works on a Supervisor.
  • doc: documentation shipped inside the module. It is not loaded at runtime.

Slots Are a Persistence Decision, Not Just an API

Component slots in N4 are declared with annotations on the class (@NiagaraType, @NiagaraProperty, @NiagaraAction, @NiagaraTopic), and an annotation processor generates the slot code into a delimited block in the source at build time. That block is build output that happens to live in your source file: hand edits to it are overwritten on the next build, and the annotation is the thing to change.

The part that costs you later is that frozen slots are part of the component's persisted form. A station database holds instances of your type together with their values. Add a slot and existing instances take the declared default at the next start; rename or remove one and the stored value has nowhere to land. Once a type is deployed in a live station a slot rename is a data migration rather than a refactor, and links drawn on a wire sheet to a removed action or topic do not survive it.

Dynamic slots avoid the recompile because they are added at runtime, and they persist the same way, but nothing checks them when the module is built. A name typed in one place and read in another is a runtime fault, not a build error.

Driver Work Is Structure Before Protocol

Niagara's driver model is a three level tree, and a custom driver has to implement that shape whatever the wire protocol is. A network component sits under Drivers, device components sit under the network, and each device carries extensions for points, alarms, histories and schedules. Every proxy point under the points extension has a proxy extension that owns the read and write path to one value in the field device.

Poll and write behavior is configuration rather than code, and it is where most complaints about a driver actually live. The network's poll scheduler runs fast, normal and slow buckets with a rate for each, and points are assigned to one of them. Tuning policies control minimum and maximum write time, write on start, write on up and stale time, which is what decides whether a value gets written twice, written late, or shown as stale rather than shown as wrong.

Devices, points and histories count against the station's licensed capacity, so what a driver chooses to model as a point is a licensing decision as well as a design one. A driver that instantiates a proxy point for every register a device exposes will run out of licensed points on a controller long before it runs out of processor.

  • Blocking calls belong on a worker thread. Anything that waits on a socket inside a component callback or an action holds the engine, and the symptom is a station that appears frozen to everything, not only to your driver.
  • Retry and timeout handling belongs at the device level, so one unreachable device does not stall polling for the rest of the network.
  • Set status correctly. Device down, point fault and point stale are distinct states in Niagara, and a driver that collapses them into one leaves the person troubleshooting no way to tell an unreachable device from a wrong point address.

When the Module Does Not Show Up

A module can build cleanly, be installed, and still not be there when the station comes up. The station's start output, visible in Application Director, names the module it skipped and the reason at the point in startup where it tried to load it. Workbench's own interface just shows an absence.

  • The signing certificate is not trusted on the target. A signed module still needs its signer in the target platform's User Trust Store, and a development certificate that works on your own machine means nothing to a controller that has never seen it.
  • A declared dependency is not satisfied. The manifest names each dependency by module and vendor version, and a target carrying an older version than the one declared skips the module rather than loading it against what is present.
  • The station is running the code it loaded at start. Modules are read at station start, so replacing a jar under a running station changes nothing until it restarts, and a fix that is definitely in the build can look like it did nothing.
  • The module loaded but the palette is empty. Workbench reads the palette file shipped inside the module, so a type that was never added to it exists on the station and still cannot be dragged in. That is a packaging problem, not a code problem.

When a Custom Module Is Not the Right Answer

A custom module earns its place when you need a new component type, a driver for a protocol nothing in the estate already speaks, or runtime behavior a station cannot express with what it has. It is the wrong tool for a range of requirements that get described as module work, and settling that during scoping avoids standing up a build environment for work that does not need one.

The test is whether the station itself needs a new type or new runtime behavior of its own. If it does not, one of the following usually does the same job with ordinary engineering, with no signing certificate and no rebuild for every Niagara version in the estate.

  • Standard protocols. BACnet/IP, Modbus TCP, oBIX and SNMP drivers already ship with Niagara, and a device that speaks one of them needs a point map and a device template rather than code.
  • Getting data out. oBIX and the station's web services already expose points and histories, and a BQL query returns a filtered set without any module at all.
  • Calculation and sequence logic. A Program object compiles Java inside the station rather than shipping a module; the tradeoff is that the code lives in the station database instead of a repository, so it has to be exported deliberately to be kept.
  • Presentation only. If the data is already in the station and what is wanted is a different view of it, a px view built from existing widgets, or a ux part on its own, is a smaller scope of work than a station-side module: no new types and no slots to migrate later.
  • The same thing many times. Templates reproduce a configured branch of the tree across stations, which is where repetitive engineering usually belongs rather than in code.

Frequently Asked Questions

Is the Niagara SDK the same thing as Workbench?

No. Workbench is the engineering tool used to connect to platforms and stations, configure them, install software and open views. The SDK is what a developer builds against, and its output is a jar that Workbench then installs onto a platform. Most Niagara work never touches the SDK at all, while development work needs both: one produces the module, the other puts it on a station and lets you watch it run.

How does a finished module actually get onto a controller?

It goes into the modules directory of the Niagara installation on the Workbench host first, and from there to the target through the platform's Software Manager over a platform connection, which by default is TLS on port 5011 and is a different connection from the Fox station connection on 4911 used for engineering. The controller restarts the station to pick up the change. On a Supervisor or a local development station the same jar can be placed in that host's modules directory and is read at the next station start.

Does every code change require the module to be signed again?

Yes. A jar signature covers the contents of the jar, so any rebuild, including a one line change, invalidates the signature the previous build carried. Where a module produces more than one part jar, each is signed in its own right, so every part you rebuilt needs signing and not only the one whose source you edited.

Can we build with a newer JDK than the target Niagara release runs?

No. Class files carry the version of the compiler that produced them and a JVM refuses to load anything newer than itself, so the module compiles without complaint and then fails at load on the station. The older source and target flags set the class file version but still compile against the newer runtime library, so the code can reference API that is not present on the target; the release flag constrains both.

What do you need from us before development can start?

The target Niagara version line, the hardware it will run on, and a point list rather than a device description. A point list with data types, scaling, units, ranges and write behavior can be built against; a description of the device cannot. For anything that is not a standard protocol, add the register map or message specification, and state explicitly which points the module is allowed to write, since write permission is an owner decision and cannot be inferred from the device.

Can a custom module be tested without the field devices present?

Partly. Component logic, slot behavior, views and alarm and history handling can all be exercised on a station with simulated inputs. The protocol layer cannot be trusted until it has talked to the real device or a faithful simulator, because timing, malformed or unexpected responses and vendor deviations from the specification are where drivers break, not the happy path. A device on a bench, or a scheduled window against one on site, is worth arranging before the driver is called finished.