|
| 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. |
0 commit comments