Skip to content

Commit 3258510

Browse files
authored
Initial pass at adding some docs (#126)
1 parent f4c5b09 commit 3258510

4 files changed

Lines changed: 407 additions & 1 deletion

File tree

‎.github/copilot-instructions.md‎

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
# ZWave.NET — Copilot Agent Instructions
2+
3+
## Project Overview
4+
5+
ZWave.NET is a .NET library implementing the Z-Wave serial protocol for communicating with Z-Wave USB controllers (e.g. UZB-7, Aeotec Z-Stick). It uses **C# preview** features (static abstract interface members). Check `global.json` for the required SDK version and the `.csproj` files for target frameworks. The solution has four projects:
6+
7+
| Project | Path | Purpose |
8+
|---|---|---|
9+
| **ZWave** | `src/ZWave/` | Core library — serial protocol, command classes, driver |
10+
| **ZWave.Tests** | `src/ZWave.Tests/` | Unit tests (MSTest with `MSTest.Sdk`) |
11+
| **ZWave.Server** | `src/ZWave.Server/` | Blazor Server demo app |
12+
| **ZWave.BuildTools** | `src/ZWave.BuildTools/` | Roslyn source generators (targets `netstandard2.0`) |
13+
14+
## Build & Test Commands
15+
16+
Always run these commands from the repository root. The CI pipeline runs exactly these steps:
17+
18+
```shell
19+
dotnet restore
20+
dotnet build --configuration Release --no-restore
21+
dotnet test --configuration Release --no-build
22+
```
23+
24+
- `Directory.Build.rsp` enables `/Restore` by default, so a bare `dotnet build` also restores. But CI does explicit restore then `--no-restore` for build.
25+
- **Warnings are errors**: `TreatWarningsAsErrors` and `MSBuildTreatWarningsAsErrors` are both `true` in `Directory.Build.props`. Every warning will fail the build.
26+
- **Do NOT run `dotnet format --verify-no-changes`** — this is not part of CI, and it produces false compilation errors because it cannot run source generators. There are also many pre-existing formatting issues in the repo.
27+
28+
## CI / Validation
29+
30+
Two GitHub Actions workflows in `.github/workflows/`:
31+
- **`pr.yml`** — Runs on pull requests to `main`: restore → build (Release) → test
32+
- **`ci.yml`** — Runs on push to `main`: same steps plus test result artifact upload
33+
34+
Both require `fetch-depth: 0` for Nerdbank.GitVersioning. There is no separate lint or format check step.
35+
36+
## Architecture & Key Patterns
37+
38+
### Serial API Layer (`src/ZWave/Serial/`)
39+
40+
Implements the Z-Wave Serial API (INS12350) frame-level protocol:
41+
- **`Frame`** / **`DataFrame`** — Wire-level frame types (SOF/ACK/NAK/CAN). `DataFrame` handles the SOF-framed data with checksum.
42+
- **`FrameParser`** — Parses frames from a byte stream using `System.IO.Pipelines`.
43+
- **`ZWaveSerialPortCoordinator`** — Manages the serial port, frame send/receive channels, ACK handshake, and retransmission.
44+
- **`Commands/`** — Each Serial API command (e.g. `SendData`, `GetInitData`, `SoftReset`) is a struct implementing `ICommand<T>` (defined in `Serial/Commands/ICommand.cs`). Commands that expect a callback implement `IRequestWithCallback<T>`.
45+
- **`CommandId` enum** — All Serial API function IDs.
46+
47+
### Command Classes Layer (`src/ZWave/CommandClasses/`)
48+
49+
Implements Z-Wave Command Classes (SDS13781):
50+
- **`CommandClass`** / **`CommandClass<TCommand>`** — Abstract base classes. Each CC (e.g. `BinarySwitchCommandClass`) inherits from `CommandClass<TEnum>` where `TEnum` is a byte-backed enum of commands.
51+
- **`[CommandClass(CommandClassId.X)]` attribute** — Applied to each CC class. The source generator `CommandClassFactoryGenerator` scans for this attribute and generates `CommandClassFactory` with a mapping from `CommandClassId` → constructor.
52+
- **`ICommand` interface** (`CommandClasses/ICommand.cs`) — Different from the Serial API `ICommand`. Used for CC-level commands with `CommandClassId` and `CommandId`.
53+
- **`CommandClassFrame`** — Wraps CC payload bytes (CC ID + Command ID + parameters).
54+
- **`CommandClassId` enum** — All known Z-Wave command class IDs.
55+
56+
### High-Level Objects
57+
58+
- **`Driver`** — Entry point. Opens serial port, manages frame send/receive, processes unsolicited requests, coordinates request-response and callback flows.
59+
- **`Controller`** — Represents the Z-Wave USB controller. Runs identification sequence on startup.
60+
- **`Node`** — Represents a Z-Wave network node. Handles interviews and command class discovery.
61+
62+
### Source Generators (`src/ZWave.BuildTools/`)
63+
64+
- **`CommandClassFactoryGenerator`** — Generates `CommandClassFactory` from `[CommandClass]` attributes. If you add a new CC class, just apply the attribute — the factory is auto-generated.
65+
- **`MultilevelSensorTypeGenerator`** / **`MultilevelSensorScaleGenerator`** — Generate sensor type/scale enums and lookup tables from JSON config files in `src/ZWave/Config/`.
66+
- The BuildTools project is referenced as an analyzer in `ZWave.csproj` (`OutputItemType="Analyzer"`).
67+
68+
### Logging
69+
70+
Uses `Microsoft.Extensions.Logging` with source-generated `[LoggerMessage]` attributes in `Logging.cs`. Event IDs are grouped: 100-199 SerialApi, 200-299 Driver. Follow this pattern for new log messages.
71+
72+
## Coding Conventions
73+
74+
- **Nullable reference types** enabled. **Implicit usings** enabled.
75+
- **Naming** (from `.editorconfig`): private fields `_camelCase`, private static fields `s_camelCase`, constants `PascalCase`, interfaces `IPascalCase`.
76+
- **No `var`** — explicit types preferred (`csharp_style_var_*` = `false`).
77+
- Allman-style braces (`csharp_new_line_before_open_brace = all`).
78+
- NuGet package versions are centrally managed in `Directory.Packages.props`. When adding a package, add the version there and reference it without a version in the `.csproj`.
79+
- `InternalsVisibleTo` is set for `ZWave.Tests` so tests can access `internal` types.
80+
81+
## Testing Patterns
82+
83+
- Tests use **MSTest** with the `MSTest.Sdk` project SDK and the **Microsoft.Testing.Platform** runner.
84+
- Tests run in parallel at method level.
85+
- Serial API command tests inherit from `CommandTestBase` and use `TestSendableCommand` / `TestReceivableCommand` helper methods that verify frame structure round-tripping.
86+
- Test files mirror the source structure: `src/ZWave.Tests/Serial/Commands/` contains tests for each serial command.
87+
88+
## Adding New Functionality
89+
90+
**New Serial API Command**: Create a struct in `src/ZWave/Serial/Commands/` implementing `ICommand<T>` (and `IRequestWithCallback<T>` if it uses callbacks). Add the command ID to `CommandId` enum. Add tests in `src/ZWave.Tests/Serial/Commands/`.
91+
92+
**New Command Class**: Create a class in `src/ZWave/CommandClasses/` inheriting `CommandClass<TEnum>`. Apply `[CommandClass(CommandClassId.X)]`. Define private inner structs for each command (Set/Get/Report) implementing `ICommand`. The source generator auto-registers it.
93+
94+
## Protocol References
95+
96+
- Serial API: [INS12350 Programming Guide](https://www.silabs.com/documents/public/user-guides/INS12350-Serial-API-Host-Appl.-Prg.-Guide.pdf), [Silicon Labs Serial API Docs](https://docs.silabs.com/z-wave/latest/zwave-api/serial-api)
97+
- Command Classes: [SDS13781 Specification](https://www.zwavepublic.com/files/sds13781-z-wave-application-command-class-specificationpdf)
98+
- Full spec collection: [zwave-js/specs](https://github.com/zwave-js/specs)
99+
100+
---
101+
102+
Trust these instructions. Only search the codebase if information here is incomplete or found to be incorrect.

‎CONTRIBUTING.md‎

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
# Contributing to ZWave.NET
2+
3+
Thanks for your interest in contributing! Here's what you need to know.
4+
5+
## Getting Started
6+
7+
**Prerequisites:**
8+
- [.NET 10 SDK](https://dotnet.microsoft.com/download) or later (see `global.json`)
9+
- A code editor with C# support (Visual Studio, VS Code with C# Dev Kit, or Rider)
10+
- Familiarity with the Z-Wave protocol is helpful but not required - see [Protocol References](#protocol-references)
11+
12+
**Build and test:**
13+
14+
```shell
15+
dotnet build
16+
dotnet test
17+
```
18+
19+
Please make sure the build is clean before opening a PR.
20+
21+
## Project Layout
22+
23+
| Project | Description |
24+
|---|---|
25+
| `src/ZWave/` | Core library - serial protocol, command classes, driver |
26+
| `src/ZWave.Tests/` | Unit tests (MSTest) |
27+
| `src/ZWave.Server/` | Blazor Server demo app for manual testing with real hardware |
28+
| `src/ZWave.BuildTools/` | Roslyn source generators that run at compile time |
29+
30+
For a deeper dive into how these fit together, see [docs/architecture.md](docs/architecture.md).
31+
32+
## Code Style
33+
34+
The `.editorconfig` defines all formatting and naming rules. Your editor should pick these up automatically. The highlights:
35+
36+
- Use explicit types, not `var`
37+
- Allman-style braces (opening brace on its own line)
38+
- Private fields: `_camelCase`, private static fields: `s_camelCase`
39+
- XML doc comments on public APIs
40+
41+
NuGet package versions are centrally managed in `Directory.Packages.props` - add the version there and reference the package without a version in the `.csproj`.
42+
43+
## Common Contributions
44+
45+
### Adding a Command Class
46+
47+
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:
48+
49+
1. Add the CC ID to the `CommandClassId` enum if it's not already there
50+
2. Create a new file in `src/ZWave/CommandClasses/` with:
51+
- A byte-backed enum for the commands (Set, Get, Report, etc.)
52+
- A class inheriting `CommandClass<TEnum>` with a `[CommandClass(CommandClassId.X)]` attribute
53+
- Private inner structs for each command
54+
3. That's it - the source generator auto-registers your class in the factory
55+
56+
Refer to the [Command Class specification (SDS13781)](https://www.zwavepublic.com/files/sds13781-z-wave-application-command-class-specificationpdf) for message formats and field definitions.
57+
58+
### Adding a Serial API Command
59+
60+
Look at an existing command like `GetLibraryVersion.cs` (simple request/response) or `SendData.cs` (request with callback) as a template:
61+
62+
1. Add the command ID to the `CommandId` enum
63+
2. Create a new file in `src/ZWave/Serial/Commands/`
64+
3. Add tests in `src/ZWave.Tests/Serial/Commands/` - the `CommandTestBase` class has helpers for verifying frame round-tripping
65+
66+
Refer to the [Serial API programming guide (INS12350)](https://www.silabs.com/documents/public/user-guides/INS12350-Serial-API-Host-Appl.-Prg.-Guide.pdf) for command definitions.
67+
68+
### Logging
69+
70+
Log messages are defined in `Logging.cs` using source-generated `[LoggerMessage]` partial methods. Event IDs are grouped: 100–199 for Serial API, 200–299 for Driver. Pick the next available ID in the appropriate range.
71+
72+
## Submitting Changes
73+
74+
1. Fork the repository and create a branch from `main`
75+
2. Make your changes
76+
3. Make sure `dotnet build` and `dotnet test` pass
77+
4. Open a pull request against `main`
78+
79+
## Protocol References
80+
81+
- [INS12350 - Serial API Host Application Programming Guide](https://www.silabs.com/documents/public/user-guides/INS12350-Serial-API-Host-Appl.-Prg.-Guide.pdf)
82+
- [SDS13781 - Z-Wave Application Command Class Specification](https://www.zwavepublic.com/files/sds13781-z-wave-application-command-class-specificationpdf)
83+
- [Silicon Labs Z-Wave Serial API Reference](https://docs.silabs.com/z-wave/latest/zwave-api/serial-api)
84+
- [zwave-js/specs](https://github.com/zwave-js/specs) - Full specification collection

‎README.md‎

Lines changed: 67 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,67 @@
1-
# ZWave.NET
1+
# ZWave.NET
2+
3+
[![Build and Test](https://github.com/dfederm/ZWave.NET/actions/workflows/ci.yml/badge.svg)](https://github.com/dfederm/ZWave.NET/actions/workflows/ci.yml)
4+
[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)
5+
6+
A .NET library for communicating with Z-Wave devices through a Z-Wave USB controller (e.g. Aeotec Z-Stick, UZB-7) using the Z-Wave Serial API protocol.
7+
8+
> **Note:** This project is under active development and is not yet published as a NuGet package.
9+
10+
## Features
11+
12+
- Full implementation of the Z-Wave Serial API frame protocol (SOF/ACK/NAK/CAN handshake, retransmission, checksum validation)
13+
- Automatic controller identification and node discovery on startup
14+
- Node interviewing with topological command class dependency resolution
15+
- Async-first design using `System.IO.Pipelines`, `System.Threading.Channels`, and `CancellationToken` throughout
16+
17+
### Implemented Command Classes
18+
19+
| Command Class | ID | Get | Set | Report |
20+
|---|---|---|---|---|
21+
| Basic | 0x20 | ✅ | ✅ | ✅ |
22+
| Binary Switch | 0x25 | ✅ | ✅ | ✅ |
23+
| Multilevel Switch | 0x26 | ✅ | ✅ | ✅ |
24+
| Binary Sensor | 0x30 | ✅ | — | ✅ |
25+
| Multilevel Sensor | 0x31 | ✅ | — | ✅ |
26+
| Color Switch | 0x33 | ✅ | ✅ | ✅ |
27+
| Z-Wave Plus Info | 0x5E | ✅ | — | ✅ |
28+
| Notification | 0x71 | ✅ | ✅ | ✅ |
29+
| Manufacturer Specific | 0x72 | ✅ | — | ✅ |
30+
| Powerlevel | 0x73 | ✅ | ✅ | ✅ |
31+
| Battery | 0x80 | ✅ | — | ✅ |
32+
| Wake Up | 0x84 | ✅ | ✅ | ✅ |
33+
| Version | 0x86 | ✅ | — | ✅ |
34+
35+
Unimplemented command classes reported by a device are tracked as `NotImplementedCommandClass` and do not block node interviews.
36+
37+
## Quick Start
38+
39+
```csharp
40+
using Microsoft.Extensions.Logging;
41+
using ZWave;
42+
using ZWave.CommandClasses;
43+
44+
using ILoggerFactory loggerFactory = LoggerFactory.Create(builder => builder.AddConsole());
45+
ILogger logger = loggerFactory.CreateLogger<Driver>();
46+
47+
// Connect to the Z-Wave controller
48+
await using Driver driver = await Driver.CreateAsync(logger, "COM3", CancellationToken.None);
49+
50+
// Access discovered nodes
51+
foreach (var (nodeId, node) in driver.Controller.Nodes)
52+
{
53+
Console.WriteLine($"Node {nodeId}: Interview status = {node.InterviewStatus}");
54+
}
55+
56+
// Control a Binary Switch
57+
var binarySwitch = driver.Controller.Nodes[2].GetCommandClass<BinarySwitchCommandClass>();
58+
await binarySwitch.SetAsync(targetValue: true, duration: null, CancellationToken.None);
59+
```
60+
61+
## Contributing
62+
63+
See [CONTRIBUTING.md](CONTRIBUTING.md) for development details.
64+
65+
## License
66+
67+
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.

0 commit comments

Comments
 (0)