All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.github.zeroone3010.yahueapi.State Maven / Gradle / Ivy

There is a newer version: 3.0.0-rc
Show newest version
package io.github.zeroone3010.yahueapi;

import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonInclude.Include;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.github.zeroone3010.yahueapi.StateBuilderSteps.AlertStep;
import io.github.zeroone3010.yahueapi.StateBuilderSteps.BrightnessStep;
import io.github.zeroone3010.yahueapi.StateBuilderSteps.BuildStep;
import io.github.zeroone3010.yahueapi.StateBuilderSteps.ColorStep;
import io.github.zeroone3010.yahueapi.StateBuilderSteps.ColorTemperatureStep;
import io.github.zeroone3010.yahueapi.StateBuilderSteps.HueStep;
import io.github.zeroone3010.yahueapi.StateBuilderSteps.InitialStep;
import io.github.zeroone3010.yahueapi.StateBuilderSteps.OnOffStep;
import io.github.zeroone3010.yahueapi.StateBuilderSteps.SaturationStep;
import io.github.zeroone3010.yahueapi.StateBuilderSteps.TransitionTimeStep;
import io.github.zeroone3010.yahueapi.StateBuilderSteps.XyStep;
import io.github.zeroone3010.yahueapi.domain.LightState;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.logging.Logger;

import static io.github.zeroone3010.yahueapi.MathUtil.isInRange;

@JsonInclude(Include.NON_NULL)
public final class State {
  private static final Logger logger = Logger.getLogger("State");

  private static final int DIMMABLE_LIGHT_COLOR_TEMPERATURE = 370;

  /**
   * A state that causes a short blink to occur.
   */
  public static final State SHORT_ALERT = new State(AlertType.SHORT_ALERT);

  /**
   * A state that causes light(s) to blink for 15 seconds.
   */
  public static final State LONG_ALERT = new State(AlertType.LONG_ALERT);

  /**
   * A state that stops lights from blinking.
   */
  public static final State NO_ALERT = new State(AlertType.NONE);

  /**
   * A state that turns on the {@link EffectType#COLOR_LOOP} effect.
   */
  public static final State COLOR_LOOP_EFFECT = new State(EffectType.COLOR_LOOP);

  /**
   * A state that stops effects.
   */
  public static final State NO_EFFECTS = new State(EffectType.NONE);

  private final Boolean on;
  private final Integer hue;
  private final Integer sat;
  private final Integer bri;
  private final Integer ct;
  private final Integer transitiontime;
  private final List xy;
  private final String scene;
  private final AlertType alert;
  private final EffectType effect;

  private State(final Builder builder) {
    this.on = builder.on;
    this.bri = builder.bri;
    this.xy = builder.xy;
    this.hue = builder.hue;
    this.sat = builder.sat;
    this.ct = builder.ct;
    this.transitiontime = builder.transitionTime;
    this.scene = builder.scene;
    this.alert = builder.alert;
    this.effect = builder.effect;
  }

  State(final AlertType alertType) {
    this.on = null;
    this.bri = null;
    this.xy = null;
    this.hue = null;
    this.sat = null;
    this.ct = null;
    this.transitiontime = null;
    this.scene = null;
    this.alert = alertType;
    this.effect = null;
  }

  State(final EffectType effect) {
    this.on = null;
    this.bri = null;
    this.xy = null;
    this.hue = null;
    this.sat = null;
    this.ct = null;
    this.transitiontime = null;
    this.scene = null;
    this.alert = null;
    this.effect = effect;
  }

  public Boolean getOn() {
    return on;
  }

  public Integer getBri() {
    return bri;
  }

  public List getXy() {
    return xy;
  }

  public Integer getHue() {
    return hue;
  }

  public Integer getSat() {
    return sat;
  }

  public Integer getCt() {
    return ct;
  }

  public Integer getTransitiontime() {
    return transitiontime;
  }

  /**
   * The id of the scene that this state will activate. Note that the API does not populate
   * this value when you get the state of a light or a room. This means that unless you created this State object
   * manually, this value will always be {@code null}.
   *
   * @return Id of a scene, or {@code null} if this object was received from an actual {@code Light} object.
   * @since 1.3.0
   */
  public String getScene() {
    return scene;
  }

  /**
   * The latest alert command issued. Does not automatically reset to {@link AlertType#NONE} when the alert ends.
   *
   * @return Latest alert command issued.
   * @since 2.1.0
   */
  public AlertType getAlert() {
    return alert;
  }

  /**
   * The latest effect command issued. Does not automatically reset to {@link EffectType#NONE} when the effect ends.
   *
   * @return Latest effect command issued.
   * @since 2.3.0
   */
  public EffectType getEffect() {
    return effect;
  }

  public static InitialStep builder() {
    return new Builder();
  }

  static State build(final LightState state) {
    logger.fine(state.toString());
    final InitialStep builder = new State.Builder(AlertType.parseTypeString(state.getAlert()));
    if (state.getColorMode() == null) {
      return builder.colorTemperatureInMireks(DIMMABLE_LIGHT_COLOR_TEMPERATURE).brightness(state.getBrightness()).on(state.isOn());
    }
    switch (state.getColorMode()) {
      case "xy":
        return builder.xy(state.getXy()).brightness(state.getBrightness()).on(state.isOn());
      case "ct":
        return builder.colorTemperatureInMireks(state.getCt()).brightness(state.getBrightness()).on(state.isOn());
      case "hs":
        return builder.hue(state.getHue()).saturation(state.getSaturation()).brightness(state.getBrightness()).on(state.isOn());
    }
    throw new HueApiException("Unknown color mode '" + state.getColorMode() + "'.");
  }

  @Override
  public boolean equals(final Object o) {
    if (this == o) return true;
    if (o == null || getClass() != o.getClass()) return false;
    final State state = (State) o;
    return Objects.equals(on, state.on) &&
        Objects.equals(hue, state.hue) &&
        Objects.equals(sat, state.sat) &&
        Objects.equals(bri, state.bri) &&
        Objects.equals(ct, state.ct) &&
        Objects.equals(xy, state.xy) &&
        Objects.equals(alert, state.alert) &&
        Objects.equals(effect, state.effect);
  }

  @Override
  public int hashCode() {
    return Objects.hash(on, hue, sat, bri, ct, xy, alert, effect);
  }


  public static final class Builder implements InitialStep, HueStep, SaturationStep, BrightnessStep, XyStep, ColorStep, ColorTemperatureStep, TransitionTimeStep, BuildStep, OnOffStep, AlertStep {
    private Boolean on;
    private Integer hue;
    private Integer sat;
    private Integer bri;
    private Integer ct;
    private Integer transitionTime;
    private List xy;
    private String scene;
    private AlertType alert;
    private EffectType effect;

    public Builder() {
    }

    Builder(AlertType alert) {
      this.alert = alert;
    }

    Builder(EffectType effect) {
      this.effect = effect;
    }

    @Override
    public SaturationStep hue(int hue) {
      this.hue = hue;
      return this;
    }

    @Override
    public BrightnessStep saturation(int saturation) {
      this.sat = saturation;
      return this;
    }

    @Override
    public BuildStep brightness(int brightness) {
      this.bri = brightness;
      return this;
    }

    @Override
    public BrightnessStep xy(List xy) {
      if (xy == null || xy.size() != 2 || !isInRange(xy.get(0), 0, 1) || !isInRange(xy.get(1), 0, 1)) {
        throw new IllegalArgumentException("The xy list must contain exactly 2 values, between 0 and 1.");
      }
      final List xyValues = new ArrayList<>();
      xyValues.addAll(xy);
      this.xy = Collections.unmodifiableList(xyValues);
      return this;
    }

    @Override
    public BuildStep color(Color color) {
      if (color == null) {
        throw new IllegalArgumentException("Color must not be null");
      }
      final XAndYAndBrightness xAndYAndBrightness = rgbToXy(color);
      this.xy = xAndYAndBrightness.getXY();
      this.bri = xAndYAndBrightness.getBrightness();
      return this;
    }

    @Override
    public BrightnessStep colorTemperatureInMireks(int colorTemperature) {
      this.ct = colorTemperature;
      return this;
    }

    @Override
    public OnOffStep transitionTime(int tenths) {
      this.transitionTime = tenths;
      return this;
    }

    @Override
    public State on(Boolean on) {
      this.on = on;
      return build();
    }

    @Override
    public BuildStep scene(String scene) {
      this.scene = scene;
      return this;
    }

    @Override
    public State alert(AlertType alert) {
      return new State(alert);
    }

    @Override
    public State effect(EffectType effect) {
      return new State(effect);
    }

    private State build() {
      return new State(this);
    }

    private static XAndYAndBrightness rgbToXy(final Color color) {
      final float red = color.getRed();
      final float green = color.getGreen();
      final float blue = color.getBlue();
      final double r = gammaCorrection(red);
      final double g = gammaCorrection(green);
      final double b = gammaCorrection(blue);
      final double rgbX = r * 0.664511f + g * 0.154324f + b * 0.162028f;
      final double rgbY = r * 0.283881f + g * 0.668433f + b * 0.047685f;
      final double rgbZ = r * 0.000088f + g * 0.072310f + b * 0.986039f;
      final float x = (float) (rgbX / (rgbX + rgbY + rgbZ));
      final float y = (float) (rgbY / (rgbX + rgbY + rgbZ));
      return new XAndYAndBrightness(x, y, (int) (rgbY * 255f));
    }

    private static double gammaCorrection(float component) {
      return (component > 0.04045f) ? Math.pow((component + 0.055f) / (1.0f + 0.055f), 2.4f) : (component / 12.92f);
    }
  }

  @Override
  public String toString() {
    return "State{" +
        "on=" + on +
        ", hue=" + hue +
        ", sat=" + sat +
        ", bri=" + bri +
        ", ct=" + ct +
        ", transitiontime=" + transitiontime +
        ", xy=" + xy +
        ", scene='" + scene + '\'' +
        ", alert=" + alert +
        ", effect=" + effect +
        '}';
  }

  private static final class XAndYAndBrightness {
    final float x;
    final float y;
    final int brightness;

    XAndYAndBrightness(final float x, final float y, final int brightness) {
      this.x = x;
      this.y = y;
      this.brightness = brightness;
    }

    List getXY() {
      final List xyColor = new ArrayList<>();
      xyColor.add(this.x);
      xyColor.add(this.y);
      return xyColor;
    }

    int getBrightness() {
      return brightness;
    }

    @Override
    public String toString() {
      try {
        return new ObjectMapper().writeValueAsString(this);
      } catch (final JsonProcessingException e) {
        throw new RuntimeException(e);
      }
    }
  }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy