diff --git a/esphome/components/gps/__init__.py b/esphome/components/gps/__init__.py new file mode 100644 index 0000000000..3ecbc89f73 --- /dev/null +++ b/esphome/components/gps/__init__.py @@ -0,0 +1,23 @@ +import esphome.codegen as cg +import esphome.config_validation as cv +from esphome.components import uart +from esphome.const import CONF_ID + +DEPENDENCIES = ['uart'] + +gps_ns = cg.esphome_ns.namespace('gps') +GPS = gps_ns.class_('GPS', cg.Component, uart.UARTDevice) +GPSListener = gps_ns.class_('GPSListener') + +CONF_GPS_ID = 'gps_id' +MULTI_CONF = True +CONFIG_SCHEMA = cv.Schema({ + cv.GenerateID(): cv.declare_id(GPS), +}).extend(cv.COMPONENT_SCHEMA).extend(uart.UART_DEVICE_SCHEMA) + + +def to_code(config): + var = cg.new_Pvariable(config[CONF_ID]) + yield cg.register_component(var, config) + yield uart.register_uart_device(var, config) + cg.add_library('TinyGPSPlus', '1.0.2') diff --git a/esphome/components/gps/gps.cpp b/esphome/components/gps/gps.cpp new file mode 100644 index 0000000000..0391a9a955 --- /dev/null +++ b/esphome/components/gps/gps.cpp @@ -0,0 +1,12 @@ +#include "gps.h" +#include "esphome/core/log.h" + +namespace esphome { +namespace gps { + +static const char *TAG = "gps"; + +TinyGPSPlus &GPSListener::get_tiny_gps() { return this->parent_->get_tiny_gps(); } + +} // namespace gps +} // namespace esphome diff --git a/esphome/components/gps/gps.h b/esphome/components/gps/gps.h new file mode 100644 index 0000000000..7d845d1bed --- /dev/null +++ b/esphome/components/gps/gps.h @@ -0,0 +1,47 @@ +#pragma once + +#include "esphome/core/component.h" +#include "esphome/components/uart/uart.h" +#include + +namespace esphome { +namespace gps { + +class GPS; + +class GPSListener { + public: + virtual void on_update(TinyGPSPlus &tiny_gps) = 0; + TinyGPSPlus &get_tiny_gps(); + + protected: + friend GPS; + + GPS *parent_; +}; + +class GPS : public Component, public uart::UARTDevice { + public: + void register_listener(GPSListener *listener) { + listener->parent_ = this; + this->listeners_.push_back(listener); + } + float get_setup_priority() const override { return setup_priority::HARDWARE; } + void loop() override { + while (this->available() && !this->has_time_) { + if (this->tiny_gps_.encode(this->read())) { + for (auto *listener : this->listeners_) + listener->on_update(this->tiny_gps_); + } + } + } + TinyGPSPlus &get_tiny_gps() { return this->tiny_gps_; } + + protected: + bool has_time_{false}; + TinyGPSPlus tiny_gps_; + std::vector listeners_{}; +}; + +} // namespace gps +} // namespace esphome diff --git a/esphome/components/gps/time/__init__.py b/esphome/components/gps/time/__init__.py new file mode 100644 index 0000000000..bf746d19b2 --- /dev/null +++ b/esphome/components/gps/time/__init__.py @@ -0,0 +1,23 @@ +from esphome.components import time as time_ +import esphome.config_validation as cv +import esphome.codegen as cg +from esphome.const import CONF_ID +from .. import gps_ns, GPSListener, CONF_GPS_ID, GPS + +DEPENDENCIES = ['gps'] + +GPSTime = gps_ns.class_('GPSTime', time_.RealTimeClock, GPSListener) + +CONFIG_SCHEMA = time_.TIME_SCHEMA.extend({ + cv.GenerateID(): cv.declare_id(GPSTime), + cv.GenerateID(CONF_GPS_ID): cv.use_id(GPS), +}).extend(cv.COMPONENT_SCHEMA) + + +def to_code(config): + var = cg.new_Pvariable(config[CONF_ID]) + yield time_.register_time(var, config) + yield cg.register_component(var, config) + + paren = yield cg.get_variable(config[CONF_GPS_ID]) + cg.add(paren.register_listener(var)) diff --git a/esphome/components/gps/time/gps_time.cpp b/esphome/components/gps/time/gps_time.cpp new file mode 100644 index 0000000000..c6aa8adc67 --- /dev/null +++ b/esphome/components/gps/time/gps_time.cpp @@ -0,0 +1,10 @@ +#include "gps_time.h" +#include "esphome/core/log.h" + +namespace esphome { +namespace gps { + +static const char *TAG = "gps.time"; + +} // namespace gps +} // namespace esphome diff --git a/esphome/components/gps/time/gps_time.h b/esphome/components/gps/time/gps_time.h new file mode 100644 index 0000000000..b09aee364f --- /dev/null +++ b/esphome/components/gps/time/gps_time.h @@ -0,0 +1,39 @@ +#pragma once + +#include "esphome/core/component.h" +#include "esphome/components/time/real_time_clock.h" +#include "esphome/components/gps/gps.h" + +namespace esphome { +namespace gps { + +class GPSTime : public time::RealTimeClock, public GPSListener { + public: + void on_update(TinyGPSPlus &tiny_gps) override { + if (!this->has_time_) + this->from_tiny_gps_(tiny_gps); + } + void setup() override { + this->set_interval(5 * 60 * 1000, [this]() { this->from_tiny_gps_(this->get_tiny_gps()); }); + } + + protected: + void from_tiny_gps_(TinyGPSPlus &tiny_gps) { + if (!tiny_gps.time.isValid() || !tiny_gps.date.isValid()) + return; + time::ESPTime val{}; + val.year = tiny_gps.date.year(); + val.month = tiny_gps.date.month(); + val.day_of_month = tiny_gps.date.day(); + val.hour = tiny_gps.time.hour(); + val.minute = tiny_gps.time.minute(); + val.second = tiny_gps.time.second(); + val.recalc_timestamp_utc(false); + this->synchronize_epoch_(val.timestamp); + this->has_time_ = true; + } + bool has_time_{false}; +}; + +} // namespace gps +} // namespace esphome diff --git a/platformio.ini b/platformio.ini index 878176d874..c5a0d9a21b 100644 --- a/platformio.ini +++ b/platformio.ini @@ -17,6 +17,7 @@ lib_deps = FastLED@3.2.0 NeoPixelBus@2.4.1 ESPAsyncTCP@1.2.0 + TinyGPSPlus@1.0.2 build_flags = -Wno-reorder -DUSE_WEB_SERVER diff --git a/tests/test1.yaml b/tests/test1.yaml index 47b1c5ab67..b9177802fa 100644 --- a/tests/test1.yaml +++ b/tests/test1.yaml @@ -1146,6 +1146,8 @@ pn532: rdm6300: +gps: + time: - platform: sntp id: sntp_time @@ -1157,6 +1159,7 @@ time: cron: '/30 0-30,30/5 * ? JAN-DEC MON,SAT-SUN,TUE-FRI' then: - lambda: 'ESP_LOGD("main", "time");' +- platform: gps cover: - platform: template