Thanks for your interest in contributing! Here's what you need to know.
Prerequisites:
- .NET 10 SDK or later (see
global.json) - A code editor with C# support (Visual Studio, VS Code with C# Dev Kit, or Rider)
- Familiarity with the Z-Wave protocol is helpful but not required - see Protocol References
Build and test:
dotnet build
dotnet testPlease make sure the build is clean before opening a PR.
| Project | Description |
|---|---|
src/ZWave.Protocol/ |
Shared Z-Wave domain types (CommandClassId, ZWaveException, etc.) |
src/ZWave.Serial/ |
Serial frame protocol and Serial API command structs |
src/ZWave.CommandClasses/ |
Command class implementations |
src/ZWave/ |
Driver - orchestration layer (Driver, Controller, Node) |
src/ZWave.Serial.Tests/ |
Unit tests for the serial layer (MSTest) |
src/ZWave.CommandClasses.Tests/ |
Unit tests for command classes (MSTest) |
src/ZWave.Server/ |
Blazor Server demo app for manual testing with real hardware |
src/ZWave.BuildTools/ |
Roslyn source generators that run at compile time |
For a deeper dive into how these fit together, see docs/architecture.md.
The .editorconfig defines all formatting and naming rules. Your editor should pick these up automatically. The highlights:
- Use explicit types, not
var - Allman-style braces (opening brace on its own line)
- Private fields:
_camelCase, private static fields:s_camelCase - XML doc comments on public APIs
NuGet package versions are centrally managed in Directory.Packages.props - add the version there and reference the package without a version in the .csproj.
This is the most common type of contribution. Look at an existing command class like BinarySwitchCommandClass.cs as a template - it shows the full pattern:
- Add the CC ID to the
CommandClassIdenum insrc/ZWave.Protocol/if it's not already there - Create a new file in
src/ZWave.CommandClasses/with:- A byte-backed enum for the commands (Set, Get, Report, etc.)
- A class inheriting
CommandClass<TEnum>with a[CommandClass(CommandClassId.X)]attribute - Private inner structs for each command
- That's it - the source generator auto-registers your class in the factory
Refer to the Z-Wave Application Specification for message formats and field definitions.
Look at an existing command like GetLibraryVersion.cs (simple request/response) or SendData.cs (request with callback) as a template:
- Add the command ID to the
CommandIdenum - Create a new file in
src/ZWave.Serial/Commands/ - Add tests in
src/ZWave.Serial.Tests/Commands/- theCommandTestBaseclass has helpers for verifying frame round-tripping
Refer to the Z-Wave Host API Specification (from the Z-Wave Alliance specification package) for command definitions.
Log messages are defined in Logging.cs files using source-generated [LoggerMessage] partial methods. Event IDs are grouped: 100–199 for Serial API (in src/ZWave.Serial/Logging.cs), 200–299 for Driver (in src/ZWave/Logging.cs). Pick the next available ID in the appropriate range.
- Fork the repository and create a branch from
main - Make your changes
- Make sure
dotnet buildanddotnet testpass - Open a pull request against
main
The official Z-Wave specification package can be downloaded from the Z-Wave Alliance. The two most relevant specs are:
- Z-Wave Host API Specification — Serial API frame format, handshake, initialization, command definitions
- Z-Wave Application Specification — Command Class message formats, versioning, required fields
Additional resources:
- Silicon Labs Z-Wave Serial API Reference
- zwave-js/specs — Community-maintained specification collection