Pacem.Domotics.OpenWebNet
0.10.17
See the version list below for details.
dotnet add package Pacem.Domotics.OpenWebNet --version 0.10.17
NuGet\Install-Package Pacem.Domotics.OpenWebNet -Version 0.10.17
<PackageReference Include="Pacem.Domotics.OpenWebNet" Version="0.10.17" />
<PackageVersion Include="Pacem.Domotics.OpenWebNet" Version="0.10.17" />
<PackageReference Include="Pacem.Domotics.OpenWebNet" />
paket add Pacem.Domotics.OpenWebNet --version 0.10.17
#r "nuget: Pacem.Domotics.OpenWebNet, 0.10.17"
#:package Pacem.Domotics.OpenWebNet@0.10.17
#addin nuget:?package=Pacem.Domotics.OpenWebNet&version=0.10.17
#tool nuget:?package=Pacem.Domotics.OpenWebNet&version=0.10.17
Pacem.Domotics.OpenWebNet
An OpenWebNet (BTicino / Legrand MyHOME) client: connect to a gateway, drive lights, dimmers, door locks and energy meters, and receive bus events as they happen.
Built on Pacem.Extensions.Sockets, so the link to the gateway survives reboots, cable pulls
and gateways that vanish for an afternoon.
Install
dotnet add package Pacem.Domotics.OpenWebNet
Targets net10.0.
Quick start
services.AddPacemOpenWebNet();
var gateways = provider.GetRequiredService<IOpenWebNetGatewayFactory>();
var devices = provider.GetRequiredService<IOpenWebNetDeviceFactory>();
using var gateway = gateways.CreateGateway("172.24.0.100", Gateway.DefaultBTicinoPort);
gateway.Events.OnOpenWebNetResponseAsync = ctx =>
{
if (ctx.Response is LightingResponse light)
{
Console.WriteLine($"light {light.Where} -> {light.LightStatus} ({light.Intensity:P0})");
}
return Task.CompletedTask;
};
await gateway.ConnectAsync();
var kitchen = (Light)devices.CreateOpenWebNetDevice(gateway, KnownDeviceType.Light, where: 11);
await kitchen.RequestStatusAsync();
await kitchen.SwitchOnAsync();
How it is wired
A Gateway holds two sockets to the same endpoint, as the protocol expects: a command
channel for requests and a listen channel for spontaneous bus events. Both keep themselves
alive and reconnect independently.
Devices are created against a gateway and register themselves for the messages addressed to
them. Incoming frames are routed by (WHO, WHERE) — so a light and an energy meter may
share an address without stealing each other's messages.
your code ──► IOpenWebNetDeviceFactory ──► Light / ConsumptionTracker / DoorEntry
│
▼
Gateway ──► command socket ──► BTicino gateway
◄── listen socket ◄──
Connecting is non-blocking
ConnectAsync() does not throw or hang when the gateway is unreachable — the underlying
connections retry in the background. Check IsSenderConnected / IsReceiverConnected:
await gateway.ConnectAsync();
if (!gateway.IsSenderConnected)
{
// still coming up; commands will be dropped with a logged error until it is
}
Devices
KnownDeviceType |
Type | Capabilities |
|---|---|---|
Light |
Light |
on / off / toggle / timed on / blink |
Dimmer |
Light (IsDimmable) |
the above, plus DimToAsync |
ConsumptionTracker |
ConsumptionTracker |
instantaneous, total, daily, monthly |
Door |
DoorEntry |
UnlockAsync |
var lamp = (Light)devices.CreateOpenWebNetDevice(gateway, KnownDeviceType.Dimmer, 12);
await lamp.DimToAsync(0.4); // 40%
await lamp.BlinkAsync(1.5); // 1.5s
var meter = (ConsumptionTracker)devices.CreateOpenWebNetDevice(gateway, KnownDeviceType.ConsumptionTracker, 7);
await meter.RequestCurrentAsync();
await meter.RequestMonthlyAsync(2026, 9);
var door = (DoorEntry)devices.CreateOpenWebNetDevice(gateway, KnownDeviceType.Door, 0);
await door.UnlockAsync();
Gateways and devices are cached per endpoint and per (gateway, type, where), so asking twice
gives you the same instance.
Device state (Light.Status, Light.Intensity, …) reflects the last message received, not
a live query — call RequestStatusAsync() to refresh it. Note that SwitchOnAsync /
SwitchOffAsync short-circuit when the cached state already matches.
Events
Set handlers on gateway.Events:
| Handler | Fires on |
|---|---|
OnOpenWebNetResponseAsync |
Every parsed frame from either channel |
OnOpenWebNetRequestAsync |
Every request sent |
OnConnectedAsync / OnDisconnectedAsync / OnCloseAsync |
Channel lifecycle |
OnErrorAsync |
Transport failures |
Each carries the CommunicationMode (Listen or Command) of the channel it came from.
Handlers run on the receive pump — keep them quick.
Parsing frames yourself
OpenWebNetResponse? response = OpenWebNetResponse.Parse("*1*1*11##");
IEnumerable<OpenWebNetResponse> many = OpenWebNetResponse.Extract("*1*1*11##*1*0*12##");
Parse takes one complete frame and returns null for frames whose WHO this library does not
model. Extract splits a buffer and discards a trailing incomplete frame — the Gateway
itself uses a stateful reader instead, so messages split across TCP reads are re-assembled
rather than lost.
Modelled today: lighting (WHO 1), energy (WHO 18) and acknowledgements. Other WHOs
reach OnOpenWebNetResponseAsync as null and are logged at trace level.
A note on WHO values
OpenWebNetWhoTable gathers the WHO constants. Two caveats worth carrying:
- Devices answer on the WHO given by their
KnownDeviceType, which is not always the WHO their commands go out on — door entry is the live example (DoorEntry= 6 vsVideoDoorEntry= 7). Gateway generations differ here. - Routing is by
(WHO, WHERE), so an incorrect WHO does not produce an error — it silently starves the device of its own messages. Confirm against your hardware rather than against the published tables.
License
Apache 2.0.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net10.0
- Microsoft.Extensions.DependencyInjection.Abstractions (>= 10.0.12)
- Microsoft.Extensions.Logging (>= 10.0.12)
- Pacem.Domotics.Abstractions (>= 0.10.17)
- Pacem.Extensions.Sockets (>= 0.10.17)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 0.10.18-pascal | 0 | 9/21/2026 |
| 0.10.17 | 37 | 9/18/2026 |
| 0.10.17-foucault | 45 | 9/18/2026 |
| 0.10.16 | 46 | 9/16/2026 |
| 0.10.16-hamilton | 66 | 9/14/2026 |
| 0.10.16-gauss | 67 | 9/14/2026 |
| 0.10.16-faraday | 87 | 9/9/2026 |
| 0.10.15 | 86 | 9/7/2026 |