You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

156 lines
5.6 KiB

3 years ago
  1. #pragma once
  2. #include "common.h"
  3. #include "color_instant_handler.h"
  4. #include "color_transition_handler.h"
  5. namespace esphome {
  6. namespace yeelight {
  7. namespace bs2 {
  8. /**
  9. * A LightOutput class for the Yeelight Bedside Lamp 2.
  10. *
  11. * The function of this class is to translate a required light state
  12. * into actual physicial GPIO output signals to drive the device's LED
  13. * circuitry. It forms the glue between the physical device and the
  14. * logical light color input.
  15. */
  16. class YeelightBS2LightOutput : public Component, public light::LightOutput {
  17. public:
  18. /** Sets the LEDC output for the red LED circuitry channel. */
  19. void set_red_output(ledc::LEDCOutput *red) { red_ = red; }
  20. /** Sets the LEDC output for the green LED circuitry channel. */
  21. void set_green_output(ledc::LEDCOutput *green) { green_ = green; }
  22. /** Sets the LEDC output for the blue LED circuitry channel. */
  23. void set_blue_output(ledc::LEDCOutput *blue) { blue_ = blue; }
  24. /** Sets the LEDC output for the white LED circuitry channel. */
  25. void set_white_output(ledc::LEDCOutput *white) { white_ = white; }
  26. /**
  27. * Sets the first GPIO binary output, used as internal master switch for
  28. * the LED light circuitry.
  29. */
  30. void set_master1_output(gpio::GPIOBinaryOutput *master1) { master1_ = master1; }
  31. /**
  32. * Set the second GPIO binary output, used as internal master switch for
  33. * the LED light circuitry.
  34. */
  35. void set_master2_output(gpio::GPIOBinaryOutput *master2) { master2_ = master2; }
  36. /**
  37. * Returns a LightTraits object, which is used to explain to the outside
  38. * world (e.g. Home Assistant) what features are supported by this device.
  39. */
  40. light::LightTraits get_traits() override
  41. {
  42. auto traits = light::LightTraits();
  43. traits.set_supports_rgb(true);
  44. traits.set_supports_color_temperature(true);
  45. traits.set_supports_brightness(true);
  46. traits.set_supports_rgb_white_value(false);
  47. traits.set_supports_color_interlock(true);
  48. traits.set_min_mireds(MIRED_MIN);
  49. traits.set_max_mireds(MIRED_MAX);
  50. return traits;
  51. }
  52. void add_on_state_callback(std::function<void(light::LightColorValues)> &&callback) {
  53. this->state_callback_.add(std::move(callback));
  54. }
  55. /**
  56. * Applies a requested light state to the physicial GPIO outputs.
  57. */
  58. void write_state(light::LightState *state)
  59. {
  60. auto values = state->current_values;
  61. // The color must either be set instantly, or the color is
  62. // transitioning to an end color. The transition handler will do its
  63. // own inspection to see if a transition is currently active or not.
  64. // Based on the outcome, use either the instant or transition handler.
  65. GPIOOutputs *delegate;
  66. if (transition_handler_->set_light_color_values(values)) {
  67. delegate = transition_handler_;
  68. } else {
  69. instant_handler_->set_light_color_values(values);
  70. delegate = instant_handler_;
  71. }
  72. // Note: one might think that it is more logical to turn on the LED
  73. // circuitry master switch after setting the individual channels,
  74. // but this is the order that was used by the original firmware. I
  75. // tried to stay as close as possible to the original behavior, so
  76. // that's why these GPIOs are turned on at this point.
  77. if (values.get_state() != 0)
  78. {
  79. master2_->turn_on();
  80. master1_->turn_on();
  81. }
  82. // Apply the current GPIO output levels from the selected handler.
  83. red_->set_level(delegate->red);
  84. green_->set_level(delegate->green);
  85. blue_->set_level(delegate->blue);
  86. white_->set_level(delegate->white);
  87. if (values.get_state() == 0)
  88. {
  89. master2_->turn_off();
  90. master1_->turn_off();
  91. }
  92. this->state_callback_.call(values);
  93. }
  94. protected:
  95. ledc::LEDCOutput *red_;
  96. ledc::LEDCOutput *green_;
  97. ledc::LEDCOutput *blue_;
  98. ledc::LEDCOutput *white_;
  99. esphome::gpio::GPIOBinaryOutput *master1_;
  100. esphome::gpio::GPIOBinaryOutput *master2_;
  101. GPIOOutputs *transition_handler_;
  102. GPIOOutputs *instant_handler_ = new ColorInstantHandler();
  103. CallbackManager<void(light::LightColorValues)> state_callback_{};
  104. friend class YeelightBS2LightState;
  105. /**
  106. * Called by the YeelightBS2LightState class, to set the object that can be
  107. * used to access the protected LightTransformer data from the LightState
  108. * object.
  109. */
  110. void set_transformer_inspector(LightStateTransformerInspector *exposer) {
  111. transition_handler_ = new ColorTransitionHandler(exposer);
  112. }
  113. };
  114. /**
  115. * This custom LightState class is used to provide access to the protected
  116. * LightTranformer information in the LightState class.
  117. *
  118. * This class is used by the ColorTransitionHandler class to inspect if
  119. * an ongoing light color transition is active in a LightState object.
  120. */
  121. class YeelightBS2LightState : public light::LightState, public LightStateTransformerInspector
  122. {
  123. public:
  124. YeelightBS2LightState(const std::string &name, YeelightBS2LightOutput *output) : light::LightState(name, output) {
  125. output->set_transformer_inspector(this);
  126. }
  127. bool is_active() { return this->transformer_ != nullptr; }
  128. bool is_transition() { return this->transformer_->is_transition(); }
  129. light::LightColorValues get_end_values() { return this->transformer_->get_end_values(); }
  130. float get_progress() { return this->transformer_->get_progress(); }
  131. };
  132. } // namespace bs2
  133. } // namespace yeelight
  134. } // namespace esphome