Skip to content

Changing Fan States

Parker Hawke edited this page Sep 10, 2022 · 4 revisions

This guide follows the code snippets from previous guides. Please refer to these first.

Understanding State

Dyson fans all have states which may be both updated and queried. Dyson4J defines these states in the FanState class which has a list of public constants and a generic value type. Every value type can be found under the .setting package, but the generic type of the FanState instance will force its use in the setState() method (see below).

Using #setState()

While you have a successful connection to a Dyson fan, you are able to make use of DysonFan's state methods methods. There are two methods which accomplish the same goal but should be used in two different situations. These methods are setState(FanState<T>, T), and setState(Consumer<MultiStateChange>). Each method will update the state of the connected fan to its provided values.

Not all fans support every state defined by Dyson4J and whether or not your fan supports it can be determined by FanModel#supportsFeature(FanState) (CURRENTLY NON-FUNCTIONAL and returns true for all states for every model). Setting an unsupported state will not throw any exceptions. The state will still be sent to the fan, it will just not update a feature that does not exist. If sent as part of a multi state change, all other supported changes will be made, but the unsupported state change will be ignored.

#setState(FanState<T>, T)

This method can be used to update a single state on a Dyson fan. It should be used when one, and only one, state needs updating at any given time. This method, like most others, requires a connection to the fan and is performed asynchronously with a returned CompletableFuture. The following snippet will turn the fan on.

(We're also going to clean up the code and make better use of the CompletableFuture API)

public final class Main {

    private static final String DEVICE_ADDRESS = "192.168.0.180";
    private static final String USERNAME = "DYSON-XXX-XX-XXXXXXXX";
    private static final String PASSWORD = "xxxxxxxxxx";

    public static String main(String[] args) {
        DysonFan dysonFan = new StandardDysonFan(
            FanModel.DYSON_PURE_HOT_COOL_LINK_TOWER,
            InetAddress.getByName(deviceAddress),
            new DysonFanCredentials(username, password)
        );

        AtomicBoolean terminate = new AtomicBoolean(false);
        System.out.println("Connecting!");

        dysonFan.connect()
            .thenApply(fan -> { // If successful, then print the message! Be sure to forward the "fan" instance though
                System.out.println("Connected!");
                return fan;
            })
            .thenCompose(fan -> fan.setState(FanState.MODE, FanMode.ON)) // Now that we're connected, we set the state
            .thenAccept(fan -> System.out.println("Fan turned on!")) // If successful, then print the message!
            .whenComplete((ignore, e) -> {
                // If some error occurred along the way, we're going to have to print it
                if (e != null) {
                    e.printStackTrace();
                }

                // Then after EVERYTHING, inform the program to terminate
                terminate.set(true);
            });

        while (!terminate.get());

        dysonFan.disconnect().get();
        System.out.println("Disconnected!");
    }

}

After running the above snippet, you should see your fan turn on (or do nothing if it was already on). You'll notice we used FanState.MODE and FanMode.ON. The generic for FanState.MODE is a FanMode enum value and we can choose from either ON, OFF, or AUTO (again, support depends on the fan model). Every FanState has a different required value type.

You'll also notice that we've taken better advantage of Java's CompletableFuture API. If you want to learn more about this API, you can read about it here on Baeldung which always has amazing in-depth guides. It is strongly advised to get a better understanding of the CF API when working with Dyson4J as they are used heavily.

#setState(Consumer<MultiStateChange>)

This setState() method differs from the above slightly in that it takes a Consumer of a MultiStateChange, a type provided by Dyson4J to allow you to change multiple states at once using a method identical to the above detailed setState() method. Rather than creating a long chain of setState(FanState<T>, T>) calls and sending more messages than is necessary, this will hold all state changes locally and then dispatched in a single MQTT message to the fan. This is far more efficient and compact, and improves code execution time rather than waiting on each state change individually before updating the next. The snippet below will turn on the fan, set its power to 5, and enable night mode.

public final class Main {

    private static final String DEVICE_ADDRESS = "192.168.0.180";
    private static final String USERNAME = "DYSON-XXX-XX-XXXXXXXX";
    private static final String PASSWORD = "xxxxxxxxxx";

    public static String main(String[] args) {
        DysonFan dysonFan = new StandardDysonFan(
            FanModel.DYSON_PURE_HOT_COOL_LINK_TOWER,
            InetAddress.getByName(deviceAddress),
            new DysonFanCredentials(username, password)
        );

        AtomicBoolean terminate = new AtomicBoolean(false);
        System.out.println("Connecting!");

        dysonFan.connect()
            .thenApply(fan -> {
                System.out.println("Connected!");
                return fan;
            })
            .thenCompose(fan -> fan.setState(change -> change // Notice we're using a Consumer here
                    /*
                     * (don't be fooled by the formatting; this just looks easiest to read)
                     *
                     * The MultiStateChange#setState() method is identical to the DysonFan#setState() method.
                     * We can call it as many times as we'd like so long as we're in the scope of Consumer.
                     */
                    .setState(FanState.MODE, FanMode.ON)
                    .setState(FanState.SPEED, FanSpeed.POWER_5)
                    .setState(FanState.NIGHT_MODE, NightMode.ON)
            ))
            .thenAccept(fan -> System.out.println("Fan states changed!"))
            .whenComplete((ignore, e) -> {
                if (e != null) {
                    e.printStackTrace();
                }

                terminate.set(true);
            });

        while (!terminate.get());

        dysonFan.disconnect().get();
        System.out.println("Disconnected!");
    }

}

After running the above snippet, you should see your fan turn on, be set to speed 5, and enable night mode. Much like how FanState.MODE required its own type, FanMode, the FanState.SPEED and FanState.NIGHT_MODE each required their own types, FanSpeed and NightMode. See below for a list of all required types per state.

Fan State Value Types

FanState ValueType Possible Values
MODE FanMode ON, OFF, AUTO
SPEED FanSpeed POWER_1, POWER_2, ..., POWER_10, AUTO. See below
OSCILLATION Oscillation ON, OFF
SLEEP_TIMER SleepTimer See below
MONITOR_AIR_QUALITY AirQualityMonitor ON, OFF
RESET_FILTER_STATE ResetFilterState RESET, DO_NOTHING
AIR_QUALITY_TARGET AirQualityTarget BETTER, HIGH, NORMAL
NIGHT_MODE NightMode ON, OFF
HEAT_MODE HeatMode ON, OFF
FOCUS_MODE FocusMode FOCUS, DIFFUSE
MAXIMUM_TEMPERATURE TemperatureLimit See below

Special Fan States

Some state value types have types that either are not convenient to identify as constants, or just outright cannot be. There are three such types, these types are FanSpeed, SleepTimer, and TemperatureLimit. These are considered special types because there are utility methods in these classes to create (or more conveniently get) instances of these values.

FanSpeed

Dyson fans support speeds from 1 to 10. FanSpeed does have constants for each of these speeds and one for AUTO, but this can be cumbersome for user input. Therefore, FanSpeed provides a static power(int) method which will return the appropriate POWER_X constant based on the integer provided.

FanSpeed one = FanSpeed.power(1);
FanSpeed six = FanSpeed.power(6);
FanSpeed ten = FanSpeed.power(10);

SleepTimer

Dyson fans can be turned off automatically with a sleep timer and this value is calculated internally in minutes. SleepTimer.OFF is available as a constant to disable the sleep timer, but to create a sleep timer, one of the of() methods may be used to create them in either minutes, hours, or any other time unit or java.time.Duration.

SleepTimer off = SleepTimer.OFF;
SleepTimer tenMinutes = SleepTimer.ofMinutes(10);
SleepTimer thirtySeconds = SleepTimer.ofSeconds(30);
SleepTimer fiveSeconds = SleepTimer.of(5000, TimeUnit.MILLISECONDS);
SleepTimer eighteenHours = SleepTimer.of(Duration.between(Instant.now(), Instant.now().plus(18, TimeUnit.HOURS)));

TemperatureLimit

Dyson heaters can specify a temperature limit so that the environment will not exceed the provided temperature. This value is calculated internally in Kelvin * 10, but the TemperatureLimit class provides a static method to create instances of it in various TemperatureUnits.

TemperatureLimit celsius = TemperatureLimit.of(23, TemperatureUnit.CELSIUS);
TemperatureLimit fahrenheit = TemperatureLimit.of(73, TemperatureUnit.FAHRENHEIT);
TemperatureLimit kelvin = TemperatureLimit.of(296, TemperatureUnit.KELVIN);

(internal documentation is not API. You may use this if you wish to create your own library).

Clone this wiki locally