Class YeelightClient
- Namespace
- Yeelight
- Assembly
- YeelightClient.dll
Finds Yeelight devices on the local network, keeps the list of them, and keeps a connection to each.
public sealed class YeelightClient : IDisposable, IAsyncDisposable
- Inheritance
-
YeelightClient
- Implements
- Inherited Members
Remarks
This is where you start. Make a client, tell it to look for lights, and control the ones that it finds:
using var client = new YeelightClient();
client.DeviceDiscovered += (_, e) => Console.WriteLine($"Found {e.Device}");
client.StartDiscovery();
var lights = await client.DiscoverAsync(TimeSpan.FromSeconds(3));
foreach (var light in lights)
{
await light.TurnOnAsync();
await light.SetBrightnessAsync(60, TimeSpan.FromSeconds(1));
}
Lights are looked for in two ways, and the client does both: it searches (a message to the multicast group of the lights and as a broadcast, from every network that this computer is on, to which every light answers) and it listens to what the lights say by themselves, which they do now and then (StartDiscovery(TimeSpan?) and StartListening()). A light that is found is a YeelightDevice in Devices, and DeviceDiscovered is raised once for it. The client keeps a connection to each (see AutoConnect), through which it learns what the lights do, whoever changes them. A light that is found again, at another address, keeps its object and its connection follows it.
Events (DeviceDiscovered, DeviceStateChanged and the PropertyChanged of the lights) are raised
by a dispatcher of the client: one at a time, in the order in which they happened, on a thread of the thread pool that is not the
thread that reads from a light. A handler can therefore wait for the answer to a command, but the events behind it wait while it runs.
The event of a change of a property says which property changed and not what it changed to, so a change that is still waiting
when the same property changes again is raised once: read the property in the handler. Once the client is disposed nothing new is
started: what was waiting is dropped. A handler that is running is neither interrupted nor waited for.
Trust. The Yeelight protocol has no authentication and no encryption: any computer on the network can control a light, and any computer can announce one. The client takes only announcements that name the address that they were sent from and that is on the local network (a private, link-local or loopback address), never moves a light that it is connected to to another address, and keeps at most MaxDevices lights. That is as far as a client can go: use it on a network that you trust, and give a light that must not be found by anyone else a network of its own.
The client writes to the ILogger<TCategoryName> that it is given: every message that it sends to a light and every message that it gets are logged at the debug level, and the connections and the searches at the information level.
Constructors
- YeelightClient()
Initializes a new instance of the YeelightClient class with the default options and no logging.
- YeelightClient(YeelightClientOptions?, ILogger<YeelightClient>?)
Initializes a new instance of the YeelightClient class.
Properties
- Devices
Gets the lights that were found. This is a copy that never changes: loop over it freely, and ask again (or use DeviceDiscovered) to learn about the lights that are found later.
- IsListening
Gets whether the client listens to the announcements of the lights (see StartListening()).
- IsSearching
Gets whether the client searches for lights (see StartDiscovery(TimeSpan?)).
- Options
Gets the settings of the client.
Methods
- DiscoverAsync(TimeSpan, CancellationToken)
Searches for lights for a while, and returns the lights that the client knows then.
- Dispose()
Stops looking for lights, forgets them, and closes their connections.
- DisposeAsync()
Stops looking for lights, forgets them, and closes their connections. What the event dispatcher still had to raise is dropped.
- FindDevice(string)
Finds a light by its id.
- ProbeAsync(IPAddress, int, CancellationToken)
Asks one address whether a Yeelight light is there, on a UDP port other than the usual one (DiscoveryPort). For a simulator, or a light behind a forwarded port. See ProbeAsync(IPAddress, CancellationToken).
- ProbeAsync(IPAddress, CancellationToken)
Asks one address whether a Yeelight light is there, with a search that is sent straight to it. For networks that do not pass the multicast or the broadcast of a search (a VPN, a network with its lights on another subnet): a light answers a search that is sent to it, wherever it is, as long as the address can be reached.
- ProbeAsync(string, CancellationToken)
Asks one address whether a Yeelight light is there. See ProbeAsync(IPAddress, CancellationToken).
- RemoveDeviceAsync(YeelightDevice)
Forgets a light and closes its connection. It is found again (as a new object) when it announces itself or answers a search again.
- SearchNow()
Searches for lights now instead of when the interval ends. Does nothing when StartDiscovery(TimeSpan?) was not called.
- StartDiscovery(TimeSpan?)
Searches for lights now and every
interval, on every network that this computer is on, and takes over the answers. A search goes out as a multicast and as a broadcast, and is sent three times (see SearchBurst), because UDP over WiFi loses packets. Calling it again starts anew (to change the interval, for example).
- StartListening()
Listens to the announcements that the lights send by themselves, which they do when something changed and now and then. This finds lights without asking, but it can take a while: StartDiscovery(TimeSpan?) is quicker. It joins the multicast group of the lights on every network of this computer, and holds UDP port 1982 (shared with other programs that listen to it). Calling it again starts the listening anew.
- StopDiscovery()
Stops searching for lights.
- StopListening()
Stops listening to the announcements of the lights.
- WaitForDeviceAsync(string, TimeSpan, CancellationToken)
Waits until a light is found, when it is not yet: look for lights first (StartDiscovery(TimeSpan?), StartListening() or ProbeAsync(IPAddress, CancellationToken)).
Events
- DeviceDiscovered
Raised when a light is found for the first time. The light is in Devices by then, and is connecting.
- DeviceStateChanged
Raised when a property of a light changed (its brightness, whether it is connected, its name, ...), for every light of the client. The event says which property changed: a change that is still waiting when the same property changes again is raised once, so read the property.