Colour flows and scenes
A colour flow is a program that a light runs by itself: a list of steps, each of which changes the colour or the colour temperature (and, with it, the brightness) over some time, or waits. The specification calls it the most powerful command of a light, and says that the recommended scenes of Yeelight, a sunrise and a sunset among them, are made with it. A scene sets a state directly, and works on a light that is off. Both are single commands, so they are the way to make an effect that lasts without sending a command for every change.
Steps
A ColorFlowStep is made with one of its static methods:
using System.Drawing;
var second = TimeSpan.FromSeconds(1);
var red = ColorFlowStep.Rgb(255, 0, 0, second); // red, over a second
var blue = ColorFlowStep.Rgb(Color.Blue, second, brightness: 40); // blue, and 40 percent
var warm = ColorFlowStep.ColorTemperature(2700, 2 * second, brightness: 100);
var pause = ColorFlowStep.Sleep(5 * second); // nothing, for five seconds
| Method | The step |
|---|---|
Rgb |
changes the colour, as three numbers or as a Color |
ColorTemperature |
changes the colour temperature, 1700 to 6500 kelvin |
Sleep |
does nothing for the duration |
The duration of a step is the time that the light takes to change gradually, or the time that it sleeps, and is at least
YeelightLimits.MinFlowStep (50 milliseconds). The brightness of a step, 1 to 100, is the
brightness that the light changes to along with the colour; leave it out to keep the brightness as it is. A step reads back as
Duration, Mode (a
ColorFlowMode), Value (the colour as a number, or the kelvin) and
Brightness.
A flow
var flow = new ColorFlow(
[
ColorFlowStep.Rgb(255, 0, 0, TimeSpan.FromSeconds(1), brightness: 100),
ColorFlowStep.Rgb(0, 0, 255, TimeSpan.FromSeconds(1), brightness: 100),
ColorFlowStep.Sleep(TimeSpan.FromSeconds(2)),
],
count: 6,
action: ColorFlowAction.RecoverState);
await light.StartColorFlowAsync(flow);
A ColorFlow has three things:
- The steps, at least one, as a read-only list (
Steps): a flow does not change once it is made. They run in a circle: after the last step the first one comes again. - The
Count: how many steps the light runs before the flow ends, and0for a flow that never ends. Every step that runs counts, a sleep too: the example in the specification has four steps (one of them a sleep) and a count of four, which makes one round. A flow of three steps and a count of six makes two rounds. - The
Action, what the light does when the flow has ended, aColorFlowAction:RecoverStategoes back to the state from before the flow,Staystays in the state in which the flow ended, andTurnOffswitches the light off.
StartColorFlowAsync starts it, and
StopColorFlowAsync stops the flow that is running. Starting needs a light that is on (see
Commands that need the light to be on); what the light does after you stop a flow is
not specified, so look at its state. The background light has its own flow: light.Background.StartColorFlowAsync(flow).
A flow is sent as one command, whatever its length: a start_cf with the count, the action and the steps as one text of four numbers per step
(duration in milliseconds, mode, value, brightness, where -1 is no brightness). The flow above is 6,0,1000,1,16711680,100,1000,1,255,100,2000,7,0,0.
ColorFlow.ToString gives that form.
Reading the flow that a light runs
A light reports whether it runs a flow, and which, whoever started it:
var parsed = ColorFlow.TryParse(light.FlowParameters, out var running);
if (light.IsFlowing && parsed && running is not null)
{
Console.WriteLine($"{running.Steps.Count} steps, then {running.Action}");
foreach (var step in running.Steps)
{
Console.WriteLine(step);
}
}
IsFlowing is whether a flow runs, and FlowParameters are
its parameters as the light reports them (empty when there is none, and only meaningful while a flow runs).
ColorFlow.TryParse reads them back into a ColorFlow, and returns false for a text that is not a flow.
Scenes
A YeelightScene is a state that a light is set to directly, switching it on first when it is off:
await light.SetSceneAsync(YeelightScene.ColorTemperature(2700, 40)); // warm white, 40 percent
await light.SetSceneAsync(YeelightScene.Rgb(255, 140, 0, 60)); // orange, 60 percent
// On at 30 percent, and off again after 20 minutes.
await light.SetSceneAsync(YeelightScene.AutoDelayOff(brightness: 30, minutes: 20));
| Method | The scene |
|---|---|
Rgb |
a colour (three numbers or a Color) at a brightness |
Hsv |
a colour as a hue and a saturation, at a brightness |
ColorTemperature |
a white at a brightness |
Flow |
starts a colour flow |
AutoDelayOff |
switches the light on at a brightness, and starts a sleep timer that switches it off after the minutes |
SetSceneAsync sends it. Unlike most commands, a scene is accepted by a light that is on and by one
that is off. That makes YeelightScene.Flow(flow) the way to start a flow on a light that may be off: it switches the light on and starts the
flow in one command, where StartColorFlowAsync needs a light that is on already.
Recipes
A sunrise. From the dimmest warm white to full daylight, in two steps of five minutes. Three steps and a count of three make one round, and the light stays at the last state:
var sunrise = new ColorFlow(
[
ColorFlowStep.ColorTemperature(1700, TimeSpan.FromSeconds(1), brightness: 1),
ColorFlowStep.ColorTemperature(2700, TimeSpan.FromMinutes(5), brightness: 60),
ColorFlowStep.ColorTemperature(5000, TimeSpan.FromMinutes(5), brightness: 100),
],
count: 3,
action: ColorFlowAction.Stay);
await light.SetSceneAsync(YeelightScene.Flow(sunrise));
Police lights. Red and blue, 300 milliseconds each, twenty changes, and then back to what the light showed. This needs a light that can show
colours (light.SupportsColor):
var police = new ColorFlow(
[
ColorFlowStep.Rgb(255, 0, 0, TimeSpan.FromMilliseconds(300), brightness: 100),
ColorFlowStep.Rgb(0, 0, 255, TimeSpan.FromMilliseconds(300), brightness: 100),
],
count: 20,
action: ColorFlowAction.RecoverState);
await light.SetSceneAsync(YeelightScene.Flow(police));
A candle. Warm whites that vary in brightness, without an end (a count of 0). Stop it with StopColorFlowAsync:
var candle = new ColorFlow(
[
ColorFlowStep.ColorTemperature(1800, TimeSpan.FromMilliseconds(400), brightness: 45),
ColorFlowStep.ColorTemperature(1900, TimeSpan.FromMilliseconds(250), brightness: 62),
ColorFlowStep.ColorTemperature(1800, TimeSpan.FromMilliseconds(500), brightness: 38),
ColorFlowStep.ColorTemperature(2000, TimeSpan.FromMilliseconds(300), brightness: 55),
ColorFlowStep.ColorTemperature(1700, TimeSpan.FromMilliseconds(600), brightness: 30),
ColorFlowStep.ColorTemperature(1900, TimeSpan.FromMilliseconds(350), brightness: 58),
]);
await light.SetSceneAsync(YeelightScene.Flow(candle));
// ...
await light.StopColorFlowAsync();
A night light that fades out. One step that takes twenty minutes to reach the lowest brightness, and then the light switches off:
var fadeOut = new ColorFlow(
[ColorFlowStep.ColorTemperature(2200, TimeSpan.FromMinutes(20), brightness: 1)],
count: 1,
action: ColorFlowAction.TurnOff);
await light.SetSceneAsync(YeelightScene.Flow(fadeOut));
The range of colour temperatures of a light depends on its model: a light that does not go down to 1700 kelvin may refuse the steps of these recipes that ask for it, so use the lowest value that your light has.
Limits
- A step lasts at least 50 milliseconds, its colour is 0 to
0xFFFFFF, its colour temperature 1700 to 6500, its brightness 1 to 100 or left out, and a flow has at least one step and a count that is not negative. Anything else is anArgumentOutOfRangeExceptionor anArgumentExceptionwhen the step or the flow is made, before anything is sent. - The specification gives no longest duration, no largest number of steps and no longest flow. The flow is one text in one command, and what a
light accepts was not measured here. A light that refuses a flow answers with an error, which is thrown as a
YeelightExceptionof the kindDeviceRejected. - A flow made of steps that the light cannot show (colours for a light that has none) is not checked by the client: look at
SupportsColorandSupportsColorTemperaturefirst.