diff options
| author | Michele Bini <michele.bini@gmail.com> | 2022-03-24 04:51:16 (GMT) |
|---|---|---|
| committer | Michele Bini <michele.bini@gmail.com> | 2022-03-24 10:48:25 (GMT) |
| commit | 3c29183dd22d33928ac62c65a378922d49fa06e9 (patch) | |
| tree | c05c85bb5b2cf079e1547a572cd75c72e0d42d15 | |
| parent | b729db093ddb903fc55b5010fa1fe17da5217ae4 (diff) | |
Remove weather service
| -rw-r--r-- | src/CMakeLists.txt | 1 | ||||
| -rw-r--r-- | src/components/ble/NimbleController.h | 4 | ||||
| -rw-r--r-- | src/components/ble/weather/WeatherData.h | 385 | ||||
| -rw-r--r-- | src/components/ble/weather/WeatherService.cpp | 604 | ||||
| -rw-r--r-- | src/components/ble/weather/WeatherService.h | 172 | ||||
| -rw-r--r-- | src/displayapp/screens/Weather.cpp | 222 | ||||
| -rw-r--r-- | src/displayapp/screens/Weather.h | 45 |
7 files changed, 0 insertions, 1433 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 7032f0d..8e505d6 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -654,7 +654,6 @@ set(INCLUDE_FILES components/ble/BleClient.h components/ble/HeartRateService.h components/ble/MotionService.h - components/ble/weather/WeatherService.h components/settings/Settings.h components/timer/TimerController.h components/alarm/AlarmController.h diff --git a/src/components/ble/NimbleController.h b/src/components/ble/NimbleController.h index 87d44aa..fecc665 100644 --- a/src/components/ble/NimbleController.h +++ b/src/components/ble/NimbleController.h @@ -19,7 +19,6 @@ #include "components/ble/ImmediateAlertService.h" #include "components/ble/ServiceDiscovery.h" #include "components/ble/MotionService.h" -#include "components/ble/weather/WeatherService.h" #include "components/fs/FS.h" namespace Pinetime { @@ -56,9 +55,6 @@ namespace Pinetime { Pinetime::Controllers::AlertNotificationService& alertService() { return anService; }; - Pinetime::Controllers::WeatherService& weather() { - return weatherService; - }; uint16_t connHandle(); void NotifyBatteryLevel(uint8_t level); diff --git a/src/components/ble/weather/WeatherData.h b/src/components/ble/weather/WeatherData.h deleted file mode 100644 index 613d5ac..0000000 --- a/src/components/ble/weather/WeatherData.h +++ /dev/null @@ -1,385 +0,0 @@ -/* Copyright (C) 2021 Avamander - - This file is part of InfiniTime. - - InfiniTime is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published - by the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - InfiniTime is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see <https://www.gnu.org/licenses/>. -*/ -#pragma once - -/** - * Different weather events, weather data structures used by {@link WeatherService.h} - * - * How to upload events to the timeline? - * - * All timeline write payloads are simply CBOR-encoded payloads of the structs described below. - * - * All payloads have a mandatory header part and the dynamic part that - * depends on the event type specified in the header. If you don't, - * you'll get an error returned. Data is relatively well-validated, - * so keep in the bounds of the data types given. - * - * Write all struct members (CamelCase keys) into a single finite-sized map, and write it to the characteristic. - * Mind the MTU. - * - * How to debug? - * - * There's a Screen that you can compile into your firmware that shows currently valid events. - * You can adapt that to display something else. That part right now is very much work in progress - * because the exact requirements are not yet known. - * - * - * Implemented based on and other material: - * https://en.wikipedia.org/wiki/METAR - * https://www.weather.gov/jetstream/obscurationtypes - * http://www.faraim.org/aim/aim-4-03-14-493.html - */ - -namespace Pinetime { - namespace Controllers { - class WeatherData { - public: - /** - * Visibility obscuration types - */ - enum class obscurationtype { - /** No obscuration */ - None = 0, - /** Water particles suspended in the air; low visibility; does not fall */ - Fog = 1, - /** Tiny, dry particles in the air; invisible to the eye; opalescent */ - Haze = 2, - /** Small fire-created particles suspended in the air */ - Smoke = 3, - /** Fine rock powder, from for example volcanoes */ - Ash = 4, - /** Fine particles of earth suspended in the air by the wind */ - Dust = 5, - /** Fine particles of sand suspended in the air by the wind */ - Sand = 6, - /** Water particles suspended in the air; low-ish visibility; temperature is near dewpoint */ - Mist = 7, - /** This is SPECIAL in the sense that the thing raining down is doing the obscuration */ - Precipitation = 8, - Length - }; - - /** - * Types of precipitation - */ - enum class precipitationtype { - /** - * No precipitation - * - * Theoretically we could just _not_ send the event, but then - * how do we differentiate between no precipitation and - * no information about precipitation - */ - None = 0, - /** Drops larger than a drizzle; also widely separated drizzle */ - Rain = 1, - /** Fairly uniform rain consisting of fine drops */ - Drizzle = 2, - /** Rain that freezes upon contact with objects and ground */ - FreezingRain = 3, - /** Rain + hail; ice pellets; small translucent frozen raindrops */ - Sleet = 4, - /** Larger ice pellets; falling separately or in irregular clumps */ - Hail = 5, - /** Hail with smaller grains of ice; mini-snowballs */ - SmallHail = 6, - /** Snow... */ - Snow = 7, - /** Frozen drizzle; very small snow crystals */ - SnowGrains = 8, - /** Needles; columns or plates of ice. Sometimes described as "diamond dust". In very cold regions */ - IceCrystals = 9, - /** It's raining down ash, e.g. from a volcano */ - Ash = 10, - Length - }; - - /** - * These are special events that can "enhance" the "experience" of existing weather events - */ - enum class specialtype { - /** Strong wind with a sudden onset that lasts at least a minute */ - Squall = 0, - /** Series of waves in a water body caused by the displacement of a large volume of water */ - Tsunami = 1, - /** Violent; rotating column of air */ - Tornado = 2, - /** Unplanned; unwanted; uncontrolled fire in an area */ - Fire = 3, - /** Thunder and/or lightning */ - Thunder = 4, - Length - }; - - /** - * These are used for weather timeline manipulation - * that isn't just adding to the stack of weather events - */ - enum class controlcodes { - /** How much is stored already */ - GetLength = 0, - /** This wipes the entire timeline */ - DelTimeline = 1, - /** There's a currently valid timeline event with the given type */ - HasValidEvent = 3, - Length - }; - - /** - * Events have types - * then they're easier to parse after sending them over the air - */ - enum class eventtype : uint8_t { - /** @see obscuration */ - Obscuration = 0, - /** @see precipitation */ - Precipitation = 1, - /** @see wind */ - Wind = 2, - /** @see temperature */ - Temperature = 3, - /** @see airquality */ - AirQuality = 4, - /** @see special */ - Special = 5, - /** @see pressure */ - Pressure = 6, - /** @see location */ - Location = 7, - /** @see cloud */ - Clouds = 8, - /** @see humidity */ - Humidity = 9, - Length - }; - - /** - * Valid event query - * - * NOTE: Not currently available, until needs are better known - */ - class ValidEventQuery { - public: - static constexpr controlcodes code = controlcodes::HasValidEvent; - eventtype eventType; - }; - - /** The header used for further parsing */ - class TimelineHeader { - public: - /** - * UNIX timestamp - * TODO: This is currently WITH A TIMEZONE OFFSET! - * Please send events with the timestamp offset by the timezone. - **/ - uint64_t timestamp; - /** - * Time in seconds until the event expires - * - * 32 bits ought to be enough for everyone - * - * If there's a newer event of the same type then it overrides this one, even if it hasn't expired - */ - uint32_t expires; - /** - * What type of weather-related event - */ - eventtype eventType; - }; - - /** Specifies how cloudiness is stored */ - class Clouds : public TimelineHeader { - public: - /** Cloud coverage in percentage, 0-100% */ - uint8_t amount; - }; - - /** Specifies how obscuration is stored */ - class Obscuration : public TimelineHeader { - public: - /** Type of precipitation */ - obscurationtype type; - /** - * Visibility distance in meters - * 65535 is reserved for unspecified - */ - uint16_t amount; - }; - - /** Specifies how precipitation is stored */ - class Precipitation : public TimelineHeader { - public: - /** Type of precipitation */ - precipitationtype type; - /** - * How much is it going to rain? In millimeters - * 255 is reserved for unspecified - **/ - uint8_t amount; - }; - - /** - * How wind speed is stored - * - * In order to represent bursts of wind instead of constant wind, - * you have minimum and maximum speeds. - * - * As direction can fluctuate wildly and some watchfaces might wish to display it nicely, - * we're following the aerospace industry weather report option of specifying a range. - */ - class Wind : public TimelineHeader { - public: - /** Meters per second */ - uint8_t speedMin; - /** Meters per second */ - uint8_t speedMax; - /** Unitless direction between 0-255; approximately 1 unit per 0.71 degrees */ - uint8_t directionMin; - /** Unitless direction between 0-255; approximately 1 unit per 0.71 degrees */ - uint8_t directionMax; - }; - - /** - * How temperature is stored - * - * As it's annoying to figure out the dewpoint on the watch, - * please send it from the companion - * - * We don't do floats, picodegrees are not useful. Make sure to multiply. - */ - class Temperature : public TimelineHeader { - public: - /** - * Temperature °C but multiplied by 100 (e.g. -12.50°C becomes -1250) - * -32768 is reserved for "no data" - */ - int16_t temperature; - /** - * Dewpoint °C but multiplied by 100 (e.g. -12.50°C becomes -1250) - * -32768 is reserved for "no data" - */ - int16_t dewPoint; - }; - - /** - * How location info is stored - * - * This can be mostly static with long expiration, - * as it usually is, but it could change during a trip for ex. - * so we allow changing it dynamically. - * - * Location info can be for some kind of map watchface - * or daylight calculations, should those be required. - * - */ - class Location : public TimelineHeader { - public: - /** Location name */ - std::string location; - /** Altitude relative to sea level in meters */ - int16_t altitude; - /** Latitude, EPSG:3857 (Google Maps, Openstreetmaps datum) */ - int32_t latitude; - /** Longitude, EPSG:3857 (Google Maps, Openstreetmaps datum) */ - int32_t longitude; - }; - - /** - * How humidity is stored - */ - class Humidity : public TimelineHeader { - public: - /** Relative humidity, 0-100% */ - uint8_t humidity; - }; - - /** - * How air pressure is stored - */ - class Pressure : public TimelineHeader { - public: - /** Air pressure in hectopascals (hPa) */ - int16_t pressure; - }; - - /** - * How special events are stored - */ - class Special : public TimelineHeader { - public: - /** Special event's type */ - specialtype type; - }; - - /** - * How air quality is stored - * - * These events are a bit more complex because the topic is not simple, - * the intention is to heavy-lift the annoying preprocessing from the watch - * this allows watchface or watchapp makers to generate accurate alerts and graphics - * - * If this needs further enforced standardization, pull requests are welcome - */ - class AirQuality : public TimelineHeader { - public: - /** - * The name of the pollution - * - * for the sake of better compatibility with watchapps - * that might want to use this data for say visuals - * don't localize the name. - * - * Ideally watchapp itself localizes the name, if it's at all needed. - * - * E.g. - * For generic ones use "PM0.1", "PM5", "PM10" - * For chemical compounds use the molecular formula e.g. "NO2", "CO2", "O3" - * For pollen use the genus, e.g. "Betula" for birch or "Alternaria" for that mold's spores - */ - std::string polluter; - /** - * Amount of the pollution in SI units, - * otherwise it's going to be difficult to create UI, alerts - * and so on and for. - * - * See more: - * https://ec.europa.eu/environment/air/quality/standards.htm - * http://www.ourair.org/wp-content/uploads/2012-aaqs2.pdf - * - * Example units: - * count/m³ for pollen - * µgC/m³ for micrograms of organic carbon - * µg/m³ sulfates, PM0.1, PM1, PM2, PM10 and so on, dust - * mg/m³ CO2, CO - * ng/m³ for heavy metals - * - * List is not comprehensive, should be improved. - * The current ones are what watchapps assume! - * - * Note: ppb and ppm to concentration should be calculated on the companion, using - * the correct formula (taking into account temperature and air pressure) - * - * Note2: The amount is off by times 100, for two decimal places of precision. - * E.g. 54.32µg/m³ is 5432 - * - */ - uint32_t amount; - }; - }; - } -} diff --git a/src/components/ble/weather/WeatherService.cpp b/src/components/ble/weather/WeatherService.cpp deleted file mode 100644 index 23f53b7..0000000 --- a/src/components/ble/weather/WeatherService.cpp +++ /dev/null @@ -1,604 +0,0 @@ -/* Copyright (C) 2021 Avamander - - This file is part of InfiniTime. - - InfiniTime is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published - by the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - InfiniTime is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see <https://www.gnu.org/licenses/>. -*/ -#include <qcbor/qcbor_spiffy_decode.h> -#include "WeatherService.h" -#include "libs/QCBOR/inc/qcbor/qcbor.h" -#include "systemtask/SystemTask.h" - -int WeatherCallback(uint16_t connHandle, uint16_t attrHandle, struct ble_gatt_access_ctxt* ctxt, void* arg) { - return static_cast<Pinetime::Controllers::WeatherService*>(arg)->OnCommand(connHandle, attrHandle, ctxt); -} - -namespace Pinetime { - namespace Controllers { - WeatherService::WeatherService(System::SystemTask& system, DateTime& dateTimeController) - : system(system), dateTimeController(dateTimeController) { - nullHeader = &nullTimelineheader; - nullTimelineheader->timestamp = 0; - } - - void WeatherService::Init() { - uint8_t res = 0; - res = ble_gatts_count_cfg(serviceDefinition); - ASSERT(res == 0); - - res = ble_gatts_add_svcs(serviceDefinition); - ASSERT(res == 0); - } - - int WeatherService::OnCommand(uint16_t connHandle, uint16_t attrHandle, struct ble_gatt_access_ctxt* ctxt) { - if (ctxt->op == BLE_GATT_ACCESS_OP_WRITE_CHR) { - const uint8_t packetLen = OS_MBUF_PKTLEN(ctxt->om); // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic) - if (packetLen <= 0) { - return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN; - } - // Decode - QCBORDecodeContext decodeContext; - UsefulBufC encodedCbor = {ctxt->om->om_data, OS_MBUF_PKTLEN(ctxt->om)}; // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic) - - QCBORDecode_Init(&decodeContext, encodedCbor, QCBOR_DECODE_MODE_NORMAL); - // KINDLY provide us a fixed-length map - QCBORDecode_EnterMap(&decodeContext, nullptr); - // Always encodes to the smallest number of bytes based on the value - int64_t tmpTimestamp = 0; - QCBORDecode_GetInt64InMapSZ(&decodeContext, "Timestamp", &tmpTimestamp); - if (QCBORDecode_GetError(&decodeContext) != QCBOR_SUCCESS) { - CleanUpQcbor(&decodeContext); - return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN; - } - int64_t tmpExpires = 0; - QCBORDecode_GetInt64InMapSZ(&decodeContext, "Expires", &tmpExpires); - if (QCBORDecode_GetError(&decodeContext) != QCBOR_SUCCESS || tmpExpires < 0 || tmpExpires > 4294967295) { - CleanUpQcbor(&decodeContext); - return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN; - } - int64_t tmpEventType = 0; - QCBORDecode_GetInt64InMapSZ(&decodeContext, "EventType", &tmpEventType); - if (QCBORDecode_GetError(&decodeContext) != QCBOR_SUCCESS || tmpEventType < 0 || - tmpEventType >= static_cast<int64_t>(WeatherData::eventtype::Length)) { - CleanUpQcbor(&decodeContext); - return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN; - } - - switch (static_cast<WeatherData::eventtype>(tmpEventType)) { - case WeatherData::eventtype::AirQuality: { - std::unique_ptr<WeatherData::AirQuality> airquality = std::make_unique<WeatherData::AirQuality>(); - airquality->timestamp = tmpTimestamp; - airquality->eventType = static_cast<WeatherData::eventtype>(tmpEventType); - airquality->expires = tmpExpires; - - UsefulBufC stringBuf; // TODO: Everything ok with lifecycle here? - QCBORDecode_GetTextStringInMapSZ(&decodeContext, "Polluter", &stringBuf); - if (UsefulBuf_IsNULLOrEmptyC(stringBuf) != 0) { - CleanUpQcbor(&decodeContext); - return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN; - } - airquality->polluter = std::string(static_cast<const char*>(stringBuf.ptr), stringBuf.len); - - int64_t tmpAmount = 0; - QCBORDecode_GetInt64InMapSZ(&decodeContext, "Amount", &tmpAmount); - if (tmpAmount < 0 || tmpAmount > 4294967295) { - CleanUpQcbor(&decodeContext); - return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN; - } - airquality->amount = tmpAmount; // NOLINT(bugprone-narrowing-conversions,cppcoreguidelines-narrowing-conversions) - - if (!AddEventToTimeline(std::move(airquality))) { - CleanUpQcbor(&decodeContext); - return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN; - } - break; - } - case WeatherData::eventtype::Obscuration: { - std::unique_ptr<WeatherData::Obscuration> obscuration = std::make_unique<WeatherData::Obscuration>(); - obscuration->timestamp = tmpTimestamp; - obscuration->eventType = static_cast<WeatherData::eventtype>(tmpEventType); - obscuration->expires = tmpExpires; - - int64_t tmpType = 0; - QCBORDecode_GetInt64InMapSZ(&decodeContext, "Type", &tmpType); - if (tmpType < 0 || tmpType >= static_cast<int64_t>(WeatherData::obscurationtype::Length)) { - CleanUpQcbor(&decodeContext); - return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN; - } - obscuration->type = static_cast<WeatherData::obscurationtype>(tmpType); - - int64_t tmpAmount = 0; - QCBORDecode_GetInt64InMapSZ(&decodeContext, "Amount", &tmpAmount); - if (tmpAmount < 0 || tmpAmount > 65535) { - CleanUpQcbor(&decodeContext); - return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN; - } - obscuration->amount = tmpAmount; // NOLINT(bugprone-narrowing-conversions,cppcoreguidelines-narrowing-conversions) - - if (!AddEventToTimeline(std::move(obscuration))) { - CleanUpQcbor(&decodeContext); - return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN; - } - break; - } - case WeatherData::eventtype::Precipitation: { - std::unique_ptr<WeatherData::Precipitation> precipitation = std::make_unique<WeatherData::Precipitation>(); - precipitation->timestamp = tmpTimestamp; - precipitation->eventType = static_cast<WeatherData::eventtype>(tmpEventType); - precipitation->expires = tmpExpires; - - int64_t tmpType = 0; - QCBORDecode_GetInt64InMapSZ(&decodeContext, "Type", &tmpType); - if (tmpType < 0 || tmpType >= static_cast<int64_t>(WeatherData::precipitationtype::Length)) { - CleanUpQcbor(&decodeContext); - return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN; - } - precipitation->type = static_cast<WeatherData::precipitationtype>(tmpType); - - int64_t tmpAmount = 0; - QCBORDecode_GetInt64InMapSZ(&decodeContext, "Amount", &tmpAmount); - if (tmpAmount < 0 || tmpAmount > 255) { - CleanUpQcbor(&decodeContext); - return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN; - } - precipitation->amount = tmpAmount; // NOLINT(bugprone-narrowing-conversions,cppcoreguidelines-narrowing-conversions) - - if (!AddEventToTimeline(std::move(precipitation))) { - CleanUpQcbor(&decodeContext); - return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN; - } - break; - } - case WeatherData::eventtype::Wind: { - std::unique_ptr<WeatherData::Wind> wind = std::make_unique<WeatherData::Wind>(); - wind->timestamp = tmpTimestamp; - wind->eventType = static_cast<WeatherData::eventtype>(tmpEventType); - wind->expires = tmpExpires; - - int64_t tmpMin = 0; - QCBORDecode_GetInt64InMapSZ(&decodeContext, "SpeedMin", &tmpMin); - if (tmpMin < 0 || tmpMin > 255) { - CleanUpQcbor(&decodeContext); - return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN; - } - wind->speedMin = tmpMin; // NOLINT(bugprone-narrowing-conversions,cppcoreguidelines-narrowing-conversions) - - int64_t tmpMax = 0; - QCBORDecode_GetInt64InMapSZ(&decodeContext, "SpeedMin", &tmpMax); - if (tmpMax < 0 || tmpMax > 255) { - CleanUpQcbor(&decodeContext); - return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN; - } - wind->speedMax = tmpMax; // NOLINT(bugprone-narrowing-conversions,cppcoreguidelines-narrowing-conversions) - - int64_t tmpDMin = 0; - QCBORDecode_GetInt64InMapSZ(&decodeContext, "DirectionMin", &tmpDMin); - if (tmpDMin < 0 || tmpDMin > 255) { - CleanUpQcbor(&decodeContext); - return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN; - } - wind->directionMin = tmpDMin; // NOLINT(bugprone-narrowing-conversions,cppcoreguidelines-narrowing-conversions) - - int64_t tmpDMax = 0; - QCBORDecode_GetInt64InMapSZ(&decodeContext, "DirectionMax", &tmpDMax); - if (tmpDMax < 0 || tmpDMax > 255) { - CleanUpQcbor(&decodeContext); - return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN; - } - wind->directionMax = tmpDMax; // NOLINT(bugprone-narrowing-conversions,cppcoreguidelines-narrowing-conversions) - - if (!AddEventToTimeline(std::move(wind))) { - CleanUpQcbor(&decodeContext); - return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN; - } - break; - } - case WeatherData::eventtype::Temperature: { - std::unique_ptr<WeatherData::Temperature> temperature = std::make_unique<WeatherData::Temperature>(); - temperature->timestamp = tmpTimestamp; - temperature->eventType = static_cast<WeatherData::eventtype>(tmpEventType); - temperature->expires = tmpExpires; - - int64_t tmpTemperature = 0; - QCBORDecode_GetInt64InMapSZ(&decodeContext, "Temperature", &tmpTemperature); - if (tmpTemperature < -32768 || tmpTemperature > 32767) { - CleanUpQcbor(&decodeContext); - return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN; - } - temperature->temperature = - static_cast<int16_t>(tmpTemperature); // NOLINT(bugprone-narrowing-conversions,cppcoreguidelines-narrowing-conversions) - - int64_t tmpDewPoint = 0; - QCBORDecode_GetInt64InMapSZ(&decodeContext, "DewPoint", &tmpDewPoint); - if (tmpDewPoint < -32768 || tmpDewPoint > 32767) { - CleanUpQcbor(&decodeContext); - return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN; - } - temperature->dewPoint = - static_cast<int16_t>(tmpDewPoint); // NOLINT(bugprone-narrowing-conversions,cppcoreguidelines-narrowing-conversions) - - if (!AddEventToTimeline(std::move(temperature))) { - CleanUpQcbor(&decodeContext); - return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN; - } - break; - } - case WeatherData::eventtype::Special: { - std::unique_ptr<WeatherData::Special> special = std::make_unique<WeatherData::Special>(); - special->timestamp = tmpTimestamp; - special->eventType = static_cast<WeatherData::eventtype>(tmpEventType); - special->expires = tmpExpires; - - int64_t tmpType = 0; - QCBORDecode_GetInt64InMapSZ(&decodeContext, "Type", &tmpType); - if (tmpType < 0 || tmpType >= static_cast<int64_t>(WeatherData::specialtype::Length)) { - CleanUpQcbor(&decodeContext); - return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN; - } - special->type = static_cast<WeatherData::specialtype>(tmpType); - - if (!AddEventToTimeline(std::move(special))) { - CleanUpQcbor(&decodeContext); - return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN; - } - break; - } - case WeatherData::eventtype::Pressure: { - std::unique_ptr<WeatherData::Pressure> pressure = std::make_unique<WeatherData::Pressure>(); - pressure->timestamp = tmpTimestamp; - pressure->eventType = static_cast<WeatherData::eventtype>(tmpEventType); - pressure->expires = tmpExpires; - - int64_t tmpPressure = 0; - QCBORDecode_GetInt64InMapSZ(&decodeContext, "Pressure", &tmpPressure); - if (tmpPressure < 0 || tmpPressure >= 65535) { - CleanUpQcbor(&decodeContext); - return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN; - } - pressure->pressure = tmpPressure; // NOLINT(bugprone-narrowing-conversions,cppcoreguidelines-narrowing-conversions) - - if (!AddEventToTimeline(std::move(pressure))) { - CleanUpQcbor(&decodeContext); - return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN; - } - break; - } - case WeatherData::eventtype::Location: { - std::unique_ptr<WeatherData::Location> location = std::make_unique<WeatherData::Location>(); - location->timestamp = tmpTimestamp; - location->eventType = static_cast<WeatherData::eventtype>(tmpEventType); - location->expires = tmpExpires; - - UsefulBufC stringBuf; // TODO: Everything ok with lifecycle here? - QCBORDecode_GetTextStringInMapSZ(&decodeContext, "Location", &stringBuf); - if (UsefulBuf_IsNULLOrEmptyC(stringBuf) != 0) { - CleanUpQcbor(&decodeContext); - return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN; - } - location->location = std::string(static_cast<const char*>(stringBuf.ptr), stringBuf.len); - - int64_t tmpAltitude = 0; - QCBORDecode_GetInt64InMapSZ(&decodeContext, "Altitude", &tmpAltitude); - if (tmpAltitude < -32768 || tmpAltitude >= 32767) { - CleanUpQcbor(&decodeContext); - return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN; - } - location->altitude = static_cast<int16_t>(tmpAltitude); - - int64_t tmpLatitude = 0; - QCBORDecode_GetInt64InMapSZ(&decodeContext, "Latitude", &tmpLatitude); - if (tmpLatitude < -2147483648 || tmpLatitude >= 2147483647) { - CleanUpQcbor(&decodeContext); - return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN; - } - location->latitude = static_cast<int32_t>(tmpLatitude); - - int64_t tmpLongitude = 0; - QCBORDecode_GetInt64InMapSZ(&decodeContext, "Longitude", &tmpLongitude); - if (tmpLongitude < -2147483648 || tmpLongitude >= 2147483647) { - CleanUpQcbor(&decodeContext); - return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN; - } - location->latitude = static_cast<int32_t>(tmpLongitude); - - if (!AddEventToTimeline(std::move(location))) { - CleanUpQcbor(&decodeContext); - return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN; - } - break; - } - case WeatherData::eventtype::Clouds: { - std::unique_ptr<WeatherData::Clouds> clouds = std::make_unique<WeatherData::Clouds>(); - clouds->timestamp = tmpTimestamp; - clouds->eventType = static_cast<WeatherData::eventtype>(tmpEventType); - clouds->expires = tmpExpires; - - int64_t tmpAmount = 0; - QCBORDecode_GetInt64InMapSZ(&decodeContext, "Amount", &tmpAmount); - if (tmpAmount < 0 || tmpAmount > 255) { - CleanUpQcbor(&decodeContext); - return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN; - } - clouds->amount = static_cast<uint8_t>(tmpAmount); - - if (!AddEventToTimeline(std::move(clouds))) { - CleanUpQcbor(&decodeContext); - return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN; - } - break; - } - case WeatherData::eventtype::Humidity: { - std::unique_ptr<WeatherData::Humidity> humidity = std::make_unique<WeatherData::Humidity>(); - humidity->timestamp = tmpTimestamp; - humidity->eventType = static_cast<WeatherData::eventtype>(tmpEventType); - humidity->expires = tmpExpires; - - int64_t tmpType = 0; - QCBORDecode_GetInt64InMapSZ(&decodeContext, "Humidity", &tmpType); - if (tmpType < 0 || tmpType >= 255) { - CleanUpQcbor(&decodeContext); - return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN; - } - humidity->humidity = static_cast<uint8_t>(tmpType); - - if (!AddEventToTimeline(std::move(humidity))) { - CleanUpQcbor(&decodeContext); - return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN; - } - break; - } - default: { - CleanUpQcbor(&decodeContext); - return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN; - } - } - - QCBORDecode_ExitMap(&decodeContext); - GetTimelineLength(); - TidyTimeline(); - - if (QCBORDecode_Finish(&decodeContext) != QCBOR_SUCCESS) { - return BLE_ATT_ERR_INSUFFICIENT_RES; - } - } else if (ctxt->op == BLE_GATT_ACCESS_OP_READ_CHR) { - // Encode - uint8_t buffer[64]; - QCBOREncodeContext encodeContext; - /* TODO: This is very much still a test endpoint - * it needs a characteristic UUID check - * and actual implementations that show - * what actually has to be read. - * WARN: Consider commands not part of the API for now! - */ - QCBOREncode_Init(&encodeContext, UsefulBuf_FROM_BYTE_ARRAY(buffer)); - QCBOREncode_OpenMap(&encodeContext); - QCBOREncode_AddTextToMap(&encodeContext, "test", UsefulBuf_FROM_SZ_LITERAL("test")); - QCBOREncode_AddInt64ToMap(&encodeContext, "test", 1ul); - QCBOREncode_CloseMap(&encodeContext); - - UsefulBufC encodedEvent; - auto uErr = QCBOREncode_Finish(&encodeContext, &encodedEvent); - if (uErr != 0) { - return BLE_ATT_ERR_INSUFFICIENT_RES; - } - auto res = os_mbuf_append(ctxt->om, &buffer, sizeof(buffer)); - if (res == 0) { - return BLE_ATT_ERR_INSUFFICIENT_RES; - } - - return 0; - } - return 0; - } - - std::unique_ptr<WeatherData::Clouds>& WeatherService::GetCurrentClouds() { - uint64_t currentTimestamp = GetCurrentUnixTimestamp(); - for (auto&& header : this->timeline) { - if (header->eventType == WeatherData::eventtype::Clouds && IsEventStillValid(header, currentTimestamp)) { - return reinterpret_cast<std::unique_ptr<WeatherData::Clouds>&>(header); - } - } - - return reinterpret_cast<std::unique_ptr<WeatherData::Clouds>&>(*this->nullHeader); - } - - std::unique_ptr<WeatherData::Obscuration>& WeatherService::GetCurrentObscuration() { - uint64_t currentTimestamp = GetCurrentUnixTimestamp(); - for (auto&& header : this->timeline) { - if (header->eventType == WeatherData::eventtype::Obscuration && IsEventStillValid(header, currentTimestamp)) { - return reinterpret_cast<std::unique_ptr<WeatherData::Obscuration>&>(header); - } - } - - return reinterpret_cast<std::unique_ptr<WeatherData::Obscuration>&>(*this->nullHeader); - } - - std::unique_ptr<WeatherData::Precipitation>& WeatherService::GetCurrentPrecipitation() { - uint64_t currentTimestamp = GetCurrentUnixTimestamp(); - for (auto&& header : this->timeline) { - if (header->eventType == WeatherData::eventtype::Precipitation && IsEventStillValid(header, currentTimestamp)) { - return reinterpret_cast<std::unique_ptr<WeatherData::Precipitation>&>(header); - } - } - - return reinterpret_cast<std::unique_ptr<WeatherData::Precipitation>&>(*this->nullHeader); - } - - std::unique_ptr<WeatherData::Wind>& WeatherService::GetCurrentWind() { - uint64_t currentTimestamp = GetCurrentUnixTimestamp(); - for (auto&& header : this->timeline) { - if (header->eventType == WeatherData::eventtype::Wind && IsEventStillValid(header, currentTimestamp)) { - return reinterpret_cast<std::unique_ptr<WeatherData::Wind>&>(header); - } - } - - return reinterpret_cast<std::unique_ptr<WeatherData::Wind>&>(*this->nullHeader); - } - - std::unique_ptr<WeatherData::Temperature>& WeatherService::GetCurrentTemperature() { - uint64_t currentTimestamp = GetCurrentUnixTimestamp(); - for (auto&& header : this->timeline) { - if (header->eventType == WeatherData::eventtype::Temperature && IsEventStillValid(header, currentTimestamp)) { - return reinterpret_cast<std::unique_ptr<WeatherData::Temperature>&>(header); - } - } - - return reinterpret_cast<std::unique_ptr<WeatherData::Temperature>&>(*this->nullHeader); - } - - std::unique_ptr<WeatherData::Humidity>& WeatherService::GetCurrentHumidity() { - uint64_t currentTimestamp = GetCurrentUnixTimestamp(); - for (auto&& header : this->timeline) { - if (header->eventType == WeatherData::eventtype::Humidity && IsEventStillValid(header, currentTimestamp)) { - return reinterpret_cast<std::unique_ptr<WeatherData::Humidity>&>(header); - } - } - - return reinterpret_cast<std::unique_ptr<WeatherData::Humidity>&>(*this->nullHeader); - } - - std::unique_ptr<WeatherData::Pressure>& WeatherService::GetCurrentPressure() { - uint64_t currentTimestamp = GetCurrentUnixTimestamp(); - for (auto&& header : this->timeline) { - if (header->eventType == WeatherData::eventtype::Pressure && IsEventStillValid(header, currentTimestamp)) { - return reinterpret_cast<std::unique_ptr<WeatherData::Pressure>&>(header); - } - } - - return reinterpret_cast<std::unique_ptr<WeatherData::Pressure>&>(*this->nullHeader); - } - - std::unique_ptr<WeatherData::Location>& WeatherService::GetCurrentLocation() { - uint64_t currentTimestamp = GetCurrentUnixTimestamp(); - for (auto&& header : this->timeline) { - if (header->eventType == WeatherData::eventtype::Location && IsEventStillValid(header, currentTimestamp)) { - return reinterpret_cast<std::unique_ptr<WeatherData::Location>&>(header); - } - } - - return reinterpret_cast<std::unique_ptr<WeatherData::Location>&>(*this->nullHeader); - } - - std::unique_ptr<WeatherData::AirQuality>& WeatherService::GetCurrentQuality() { - uint64_t currentTimestamp = GetCurrentUnixTimestamp(); - for (auto&& header : this->timeline) { - if (header->eventType == WeatherData::eventtype::AirQuality && IsEventStillValid(header, currentTimestamp)) { - return reinterpret_cast<std::unique_ptr<WeatherData::AirQuality>&>(header); - } - } - - return reinterpret_cast<std::unique_ptr<WeatherData::AirQuality>&>(*this->nullHeader); - } - - size_t WeatherService::GetTimelineLength() const { - return timeline.size(); - } - - bool WeatherService::AddEventToTimeline(std::unique_ptr<WeatherData::TimelineHeader> event) { - if (timeline.size() == timeline.max_size()) { - return false; - } - - timeline.push_back(std::move(event)); - return true; - } - - bool WeatherService::HasTimelineEventOfType(const WeatherData::eventtype type) const { - uint64_t currentTimestamp = GetCurrentUnixTimestamp(); - for (auto&& header : timeline) { - if (header->eventType == type && IsEventStillValid(header, currentTimestamp)) { - return true; - } - } - return false; - } - - void WeatherService::TidyTimeline() { - uint64_t timeCurrent = GetCurrentUnixTimestamp(); - timeline.erase(std::remove_if(std::begin(timeline), - std::end(timeline), - [&](std::unique_ptr<WeatherData::TimelineHeader> const& header) { - return !IsEventStillValid(header, timeCurrent); - }), - std::end(timeline)); - - std::sort(std::begin(timeline), std::end(timeline), CompareTimelineEvents); - } - - bool WeatherService::CompareTimelineEvents(const std::unique_ptr<WeatherData::TimelineHeader>& first, - const std::unique_ptr<WeatherData::TimelineHeader>& second) { - return first->timestamp > second->timestamp; - } - - bool WeatherService::IsEventStillValid(const std::unique_ptr<WeatherData::TimelineHeader>& uniquePtr, const uint64_t timestamp) { - // Not getting timestamp in isEventStillValid for more speed - return uniquePtr->timestamp + uniquePtr->expires >= timestamp; - } - - uint64_t WeatherService::GetCurrentUnixTimestamp() const { - return std::chrono::duration_cast<std::chrono::seconds>(dateTimeController.CurrentDateTime().time_since_epoch()).count(); - } - - int16_t WeatherService::GetTodayMinTemp() const { - uint64_t currentTimestamp = GetCurrentUnixTimestamp(); - uint64_t currentDayEnd = currentTimestamp - ((24 - dateTimeController.Hours()) * 60 * 60) - - ((60 - dateTimeController.Minutes()) * 60) - (60 - dateTimeController.Seconds()); - int16_t result = -32768; - for (auto&& header : this->timeline) { - if (header->eventType == WeatherData::eventtype::Temperature && IsEventStillValid(header, currentTimestamp) && - header->timestamp < currentDayEnd && - reinterpret_cast<const std::unique_ptr<WeatherData::Temperature>&>(header)->temperature != -32768) { - int16_t temperature = reinterpret_cast<const std::unique_ptr<WeatherData::Temperature>&>(header)->temperature; - if (result == -32768) { - result = temperature; - } else if (result > temperature) { - result = temperature; - } else { - // The temperature in this item is higher than the lowest we've found - } - } - } - - return result; - } - - int16_t WeatherService::GetTodayMaxTemp() const { - uint64_t currentTimestamp = GetCurrentUnixTimestamp(); - uint64_t currentDayEnd = currentTimestamp - ((24 - dateTimeController.Hours()) * 60 * 60) - - ((60 - dateTimeController.Minutes()) * 60) - (60 - dateTimeController.Seconds()); - int16_t result = -32768; - for (auto&& header : this->timeline) { - if (header->eventType == WeatherData::eventtype::Temperature && IsEventStillValid(header, currentTimestamp) && - header->timestamp < currentDayEnd && - reinterpret_cast<const std::unique_ptr<WeatherData::Temperature>&>(header)->temperature != -32768) { - int16_t temperature = reinterpret_cast<const std::unique_ptr<WeatherData::Temperature>&>(header)->temperature; - if (result == -32768) { - result = temperature; - } else if (result < temperature) { - result = temperature; - } else { - // The temperature in this item is lower than the highest we've found - } - } - } - - return result; - } - - void WeatherService::CleanUpQcbor(QCBORDecodeContext* decodeContext) { - QCBORDecode_ExitMap(decodeContext); - QCBORDecode_Finish(decodeContext); - } - } -} diff --git a/src/components/ble/weather/WeatherService.h b/src/components/ble/weather/WeatherService.h deleted file mode 100644 index eca70cb..0000000 --- a/src/components/ble/weather/WeatherService.h +++ /dev/null @@ -1,172 +0,0 @@ -/* Copyright (C) 2021 Avamander - - This file is part of InfiniTime. - - InfiniTime is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published - by the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - InfiniTime is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see <https://www.gnu.org/licenses/>. -*/ -#pragma once - -#include <cstdint> -#include <string> -#include <vector> -#include <memory> - -#define min // workaround: nimble's min/max macros conflict with libstdc++ -#define max -#include <host/ble_gap.h> -#include <host/ble_uuid.h> -#undef max -#undef min - -#include "WeatherData.h" -#include "libs/QCBOR/inc/qcbor/qcbor.h" -#include "components/datetime/DateTimeController.h" - -int WeatherCallback(uint16_t connHandle, uint16_t attrHandle, struct ble_gatt_access_ctxt* ctxt, void* arg); - -namespace Pinetime { - namespace System { - class SystemTask; - } - namespace Controllers { - - class WeatherService { - public: - explicit WeatherService(System::SystemTask& system, DateTime& dateTimeController); - - void Init(); - - int OnCommand(uint16_t connHandle, uint16_t attrHandle, struct ble_gatt_access_ctxt* ctxt); - - /* - * Helper functions for quick access to currently valid data - */ - std::unique_ptr<WeatherData::Location>& GetCurrentLocation(); - std::unique_ptr<WeatherData::Clouds>& GetCurrentClouds(); - std::unique_ptr<WeatherData::Obscuration>& GetCurrentObscuration(); - std::unique_ptr<WeatherData::Precipitation>& GetCurrentPrecipitation(); - std::unique_ptr<WeatherData::Wind>& GetCurrentWind(); - std::unique_ptr<WeatherData::Temperature>& GetCurrentTemperature(); - std::unique_ptr<WeatherData::Humidity>& GetCurrentHumidity(); - std::unique_ptr<WeatherData::Pressure>& GetCurrentPressure(); - std::unique_ptr<WeatherData::AirQuality>& GetCurrentQuality(); - - /** - * Searches for the current day's maximum temperature - * @return -32768 if there's no data, degrees Celsius times 100 otherwise - */ - int16_t GetTodayMaxTemp() const; - /** - * Searches for the current day's minimum temperature - * @return -32768 if there's no data, degrees Celsius times 100 otherwise - */ - int16_t GetTodayMinTemp() const; - - /* - * Management functions - */ - /** - * Adds an event to the timeline - * @return - */ - bool AddEventToTimeline(std::unique_ptr<WeatherData::TimelineHeader> event); - /** - * Gets the current timeline length - */ - size_t GetTimelineLength() const; - /** - * Checks if an event of a certain type exists in the timeline - */ - bool HasTimelineEventOfType(WeatherData::eventtype type) const; - - private: - // 00040000-78fc-48fe-8e23-433b3a1942d0 - static constexpr ble_uuid128_t BaseUuid() { - return CharUuid(0x00, 0x00); - } - - // 0004yyxx-78fc-48fe-8e23-433b3a1942d0 - static constexpr ble_uuid128_t CharUuid(uint8_t x, uint8_t y) { - return ble_uuid128_t {.u = {.type = BLE_UUID_TYPE_128}, - .value = {0xd0, 0x42, 0x19, 0x3a, 0x3b, 0x43, 0x23, 0x8e, 0xfe, 0x48, 0xfc, 0x78, y, x, 0x04, 0x00}}; - } - - ble_uuid128_t weatherUuid {BaseUuid()}; - - /** - * Just write timeline data here. - * - * See {@link WeatherData.h} for more information. - */ - ble_uuid128_t weatherDataCharUuid {CharUuid(0x00, 0x01)}; - /** - * This doesn't take timeline data, provides some control over it. - * - * NOTE: Currently not supported. Companion app implementer feedback required. - * There's very little point in solidifying an API before we know the needs. - */ - ble_uuid128_t weatherControlCharUuid {CharUuid(0x00, 0x02)}; - - const struct ble_gatt_chr_def characteristicDefinition[3] = { - {.uuid = &weatherDataCharUuid.u, - .access_cb = WeatherCallback, - .arg = this, - .flags = BLE_GATT_CHR_F_WRITE, - .val_handle = &eventHandle}, - {.uuid = &weatherControlCharUuid.u, .access_cb = WeatherCallback, .arg = this, .flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_READ}, - {nullptr}}; - const struct ble_gatt_svc_def serviceDefinition[2] = { - {.type = BLE_GATT_SVC_TYPE_PRIMARY, .uuid = &weatherUuid.u, .characteristics = characteristicDefinition}, {0}}; - - uint16_t eventHandle {}; - - Pinetime::System::SystemTask& system; - Pinetime::Controllers::DateTime& dateTimeController; - - std::vector<std::unique_ptr<WeatherData::TimelineHeader>> timeline; - std::unique_ptr<WeatherData::TimelineHeader> nullTimelineheader = std::make_unique<WeatherData::TimelineHeader>(); - std::unique_ptr<WeatherData::TimelineHeader>* nullHeader; - - /** - * Cleans up the timeline of expired events - */ - void TidyTimeline(); - - /** - * Compares two timeline events - */ - static bool CompareTimelineEvents(const std::unique_ptr<WeatherData::TimelineHeader>& first, - const std::unique_ptr<WeatherData::TimelineHeader>& second); - - /** - * Returns current UNIX timestamp - */ - uint64_t GetCurrentUnixTimestamp() const; - - /** - * Checks if the event hasn't gone past and expired - * - * @param header timeline event to check - * @param currentTimestamp what's the time right now - * @return if the event is valid - */ - static bool IsEventStillValid(const std::unique_ptr<WeatherData::TimelineHeader>& uniquePtr, const uint64_t timestamp); - - /** - * This is a helper function that closes a QCBOR map and decoding context cleanly - */ - void CleanUpQcbor(QCBORDecodeContext* decodeContext); - }; - } -} diff --git a/src/displayapp/screens/Weather.cpp b/src/displayapp/screens/Weather.cpp deleted file mode 100644 index 1d0a83b..0000000 --- a/src/displayapp/screens/Weather.cpp +++ /dev/null @@ -1,222 +0,0 @@ -/* Copyright (C) 2021 Avamander - - This file is part of InfiniTime. - - InfiniTime is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published - by the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - InfiniTime is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see <https://www.gnu.org/licenses/>. -*/ -#include "Weather.h" -#include <lvgl/lvgl.h> -#include <components/ble/weather/WeatherService.h> -#include "Label.h" -#include "components/battery/BatteryController.h" -#include "components/ble/BleController.h" -#include "components/ble/weather/WeatherData.h" - -using namespace Pinetime::Applications::Screens; - -Weather::Weather(Pinetime::Applications::DisplayApp* app, Pinetime::Controllers::WeatherService& weather) - : Screen(app), - dateTimeController {dateTimeController}, - weatherService(weather), - screens {app, - 0, - {[this]() -> std::unique_ptr<Screen> { - return CreateScreenTemperature(); - }, - [this]() -> std::unique_ptr<Screen> { - return CreateScreenAir(); - }, - [this]() -> std::unique_ptr<Screen> { - return CreateScreenClouds(); - }, - [this]() -> std::unique_ptr<Screen> { - return CreateScreenPrecipitation(); - }, - [this]() -> std::unique_ptr<Screen> { - return CreateScreenHumidity(); - }}, - Screens::ScreenListModes::UpDown} { -} - -Weather::~Weather() { - lv_obj_clean(lv_scr_act()); -} - -void Weather::Refresh() { - if (running) { - // screens.Refresh(); - } -} - -bool Weather::OnButtonPushed() { - running = false; - return true; -} - -bool Weather::OnTouchEvent(Pinetime::Applications::TouchEvents event) { - return screens.OnTouchEvent(event); -} - -std::unique_ptr<Screen> Weather::CreateScreenTemperature() { - lv_obj_t* label = lv_label_create(lv_scr_act(), nullptr); - lv_label_set_recolor(label, true); - std::unique_ptr<Controllers::WeatherData::Temperature>& current = weatherService.GetCurrentTemperature(); - if (current->timestamp == 0) { - // Do not use the data, it's invalid - lv_label_set_text_fmt(label, - "#FFFF00 Temperature#\n\n" - "#444444 %d#°C \n\n" - "#444444 %d#\n\n" - "%d\n" - "%d\n", - 0, - 0, - 0, - 0); - } else { - lv_label_set_text_fmt(label, - "#FFFF00 Temperature#\n\n" - "#444444 %d#°C \n\n" - "#444444 %hd#\n\n" - "%llu\n" - "%lu\n", - current->temperature / 100, - current->dewPoint, - current->timestamp, - current->expires); - } - lv_label_set_align(label, LV_LABEL_ALIGN_CENTER); - lv_obj_align(label, lv_scr_act(), LV_ALIGN_CENTER, 0, 0); - return std::unique_ptr<Screen>(new Screens::Label(0, 5, app, label)); -} - -std::unique_ptr<Screen> Weather::CreateScreenAir() { - lv_obj_t* label = lv_label_create(lv_scr_act(), nullptr); - lv_label_set_recolor(label, true); - std::unique_ptr<Controllers::WeatherData::AirQuality>& current = weatherService.GetCurrentQuality(); - if (current->timestamp == 0) { - // Do not use the data, it's invalid - lv_label_set_text_fmt(label, - "#FFFF00 Air quality#\n\n" - "#444444 %s#\n" - "#444444 %d#\n\n" - "%d\n" - "%d\n", - "", - 0, - 0, - 0); - } else { - lv_label_set_text_fmt(label, - "#FFFF00 Air quality#\n\n" - "#444444 %s#\n" - "#444444 %lu#\n\n" - "%llu\n" - "%lu\n", - current->polluter.c_str(), - (current->amount / 100), - current->timestamp, - current->expires); - } - lv_label_set_align(label, LV_LABEL_ALIGN_CENTER); - lv_obj_align(label, lv_scr_act(), LV_ALIGN_CENTER, 0, 0); - return std::unique_ptr<Screen>(new Screens::Label(0, 5, app, label)); -} - -std::unique_ptr<Screen> Weather::CreateScreenClouds() { - lv_obj_t* label = lv_label_create(lv_scr_act(), nullptr); - lv_label_set_recolor(label, true); - std::unique_ptr<Controllers::WeatherData::Clouds>& current = weatherService.GetCurrentClouds(); - if (current->timestamp == 0) { - // Do not use the data, it's invalid - lv_label_set_text_fmt(label, - "#FFFF00 Clouds#\n\n" - "#444444 %d%%#\n\n" - "%d\n" - "%d\n", - 0, - 0, - 0); - } else { - lv_label_set_text_fmt(label, - "#FFFF00 Clouds#\n\n" - "#444444 %hhu%%#\n\n" - "%llu\n" - "%lu\n", - current->amount, - current->timestamp, - current->expires); - } - lv_label_set_align(label, LV_LABEL_ALIGN_CENTER); - lv_obj_align(label, lv_scr_act(), LV_ALIGN_CENTER, 0, 0); - return std::unique_ptr<Screen>(new Screens::Label(0, 5, app, label)); -} - -std::unique_ptr<Screen> Weather::CreateScreenPrecipitation() { - lv_obj_t* label = lv_label_create(lv_scr_act(), nullptr); - lv_label_set_recolor(label, true); - std::unique_ptr<Controllers::WeatherData::Precipitation>& current = weatherService.GetCurrentPrecipitation(); - if (current->timestamp == 0) { - // Do not use the data, it's invalid - lv_label_set_text_fmt(label, - "#FFFF00 Precipitation#\n\n" - "#444444 %d%%#\n\n" - "%d\n" - "%d\n", - 0, - 0, - 0); - } else { - lv_label_set_text_fmt(label, - "#FFFF00 Precipitation#\n\n" - "#444444 %hhu%%#\n\n" - "%llu\n" - "%lu\n", - current->amount, - current->timestamp, - current->expires); - } - lv_label_set_align(label, LV_LABEL_ALIGN_CENTER); - lv_obj_align(label, lv_scr_act(), LV_ALIGN_CENTER, 0, 0); - return std::unique_ptr<Screen>(new Screens::Label(0, 5, app, label)); -} - -std::unique_ptr<Screen> Weather::CreateScreenHumidity() { - lv_obj_t* label = lv_label_create(lv_scr_act(), nullptr); - lv_label_set_recolor(label, true); - std::unique_ptr<Controllers::WeatherData::Humidity>& current = weatherService.GetCurrentHumidity(); - if (current->timestamp == 0) { - // Do not use the data, it's invalid - lv_label_set_text_fmt(label, - "#FFFF00 Humidity#\n\n" - "#444444 %d%%#\n\n" - "%d\n" - "%d\n", - 0, - 0, - 0); - } else { - lv_label_set_text_fmt(label, - "#FFFF00 Humidity#\n\n" - "#444444 %hhu%%#\n\n" - "%llu\n" - "%lu\n", - current->humidity, - current->timestamp, - current->expires); - } - lv_label_set_align(label, LV_LABEL_ALIGN_CENTER); - lv_obj_align(label, lv_scr_act(), LV_ALIGN_CENTER, 0, 0); - return std::unique_ptr<Screen>(new Screens::Label(0, 5, app, label)); -} diff --git a/src/displayapp/screens/Weather.h b/src/displayapp/screens/Weather.h deleted file mode 100644 index 34f95fc..0000000 --- a/src/displayapp/screens/Weather.h +++ /dev/null @@ -1,45 +0,0 @@ -#pragma once - -#include <memory> -#include <components/ble/weather/WeatherService.h> -#include "Screen.h" -#include "ScreenList.h" - -namespace Pinetime { - namespace Applications { - class DisplayApp; - - namespace Screens { - class Weather : public Screen { - public: - explicit Weather(DisplayApp* app, Pinetime::Controllers::WeatherService& weather); - - ~Weather() override; - - void Refresh() override; - - bool OnButtonPushed() override; - - bool OnTouchEvent(TouchEvents event) override; - - private: - bool running = true; - - Pinetime::Controllers::DateTime& dateTimeController; - Controllers::WeatherService& weatherService; - - ScreenList<5> screens; - - std::unique_ptr<Screen> CreateScreenTemperature(); - - std::unique_ptr<Screen> CreateScreenAir(); - - std::unique_ptr<Screen> CreateScreenClouds(); - - std::unique_ptr<Screen> CreateScreenPrecipitation(); - - std::unique_ptr<Screen> CreateScreenHumidity(); - }; - } - } -} |
