#pragma once #include "esphome/core/component.h" #include "esphome/core/helpers.h" #include "esphome/core/automation.h" #include "esphome/components/time/real_time_clock.h" namespace esphome { namespace sun { namespace internal { /* Usually, ESPHome uses single-precision floating point values * because those tend to be accurate enough and are more efficient. * * However, some of the data in this class has to be quite accurate, so double is * used everywhere. */ using num_t = double; struct GeoLocation { num_t latitude; num_t longitude; num_t latitude_rad() const; num_t longitude_rad() const; }; struct Moment { time::ESPTime dt; num_t jd() const; num_t jde() const; }; struct EquatorialCoordinate { num_t right_ascension; num_t declination; num_t right_ascension_rad() const; num_t declination_rad() const; }; struct HorizontalCoordinate { num_t elevation; num_t azimuth; num_t elevation_rad() const; num_t azimuth_rad() const; }; } // namespace internal class Sun { public: void set_time(time::RealTimeClock *time) { time_ = time; } time::RealTimeClock *get_time() const { return time_; } void set_latitude(double latitude) { location_.latitude = latitude; } void set_longitude(double longitude) { location_.longitude = longitude; } optional sunrise(double elevation); optional sunset(double elevation); double elevation(); double azimuth(); protected: internal::HorizontalCoordinate calc_coords_(); optional calc_event_(bool rising, double zenith); time::RealTimeClock *time_; internal::GeoLocation location_; }; class SunTrigger : public Trigger<>, public PollingComponent, public Parented { public: SunTrigger() : PollingComponent(60000) {} void set_sunrise(bool sunrise) { sunrise_ = sunrise; } void set_elevation(double elevation) { elevation_ = elevation; } void update() override { double current = this->parent_->elevation(); if (std::isnan(current)) return; bool crossed; if (this->sunrise_) { crossed = this->last_elevation_ <= this->elevation_ && this->elevation_ < current; } else { crossed = this->last_elevation_ >= this->elevation_ && this->elevation_ > current; } if (crossed && !std::isnan(this->last_elevation_)) { this->trigger(); } this->last_elevation_ = current; } protected: bool sunrise_; double last_elevation_{NAN}; double elevation_; }; template class SunCondition : public Condition, public Parented { public: TEMPLATABLE_VALUE(double, elevation); void set_above(bool above) { above_ = above; } bool check(Ts... x) override { double elevation = this->elevation_.value(x...); double current = this->parent_->elevation(); if (this->above_) { return current > elevation; } else { return current < elevation; } } protected: bool above_; }; } // namespace sun } // namespace esphome