aboutsummaryrefslogtreecommitdiffstats
path: root/src/components/ble
diff options
context:
space:
mode:
Diffstat (limited to 'src/components/ble')
-rw-r--r--src/components/ble/DfuService.cpp10
-rw-r--r--src/components/ble/DfuService.h8
-rw-r--r--src/components/ble/HeartRateService.cpp8
-rw-r--r--src/components/ble/HeartRateService.h17
-rw-r--r--src/components/ble/MotionService.cpp14
-rw-r--r--src/components/ble/MotionService.h10
-rw-r--r--src/components/ble/MusicService.cpp8
-rw-r--r--src/components/ble/MusicService.h11
-rw-r--r--src/components/ble/NavigationService.cpp4
-rw-r--r--src/components/ble/NavigationService.h8
-rw-r--r--src/components/ble/NimbleController.cpp24
-rw-r--r--src/components/ble/NimbleController.h10
-rw-r--r--src/components/ble/NotificationManager.h7
-rw-r--r--src/components/ble/SimpleWeatherService.cpp173
-rw-r--r--src/components/ble/SimpleWeatherService.h174
-rw-r--r--src/components/ble/weather/WeatherData.h385
-rw-r--r--src/components/ble/weather/WeatherService.cpp607
-rw-r--r--src/components/ble/weather/WeatherService.h174
18 files changed, 417 insertions, 1235 deletions
diff --git a/src/components/ble/DfuService.cpp b/src/components/ble/DfuService.cpp
index 1f06b69e..2427513d 100644
--- a/src/components/ble/DfuService.cpp
+++ b/src/components/ble/DfuService.cpp
@@ -124,9 +124,11 @@ int DfuService::WritePacketHandler(uint16_t connectionHandle, os_mbuf* om) {
bootloaderSize,
applicationSize);
- // wait until SystemTask has finished waking up all devices
- while (systemTask.IsSleeping()) {
- vTaskDelay(50); // 50ms
+ // Wait until SystemTask has disabled sleeping
+ // This isn't quite correct, as we don't actually know
+ // if BleFirmwareUpdateStarted has been received yet
+ while (!systemTask.IsSleepDisabled()) {
+ vTaskDelay(pdMS_TO_TICKS(5));
}
dfuImage.Erase();
@@ -357,6 +359,8 @@ void DfuService::DfuImage::Init(size_t chunkSize, size_t totalSize, uint16_t exp
this->totalSize = totalSize;
this->expectedCrc = expectedCrc;
this->ready = true;
+ totalWriteIndex = 0;
+ bufferWriteIndex = 0;
}
void DfuService::DfuImage::Append(uint8_t* data, size_t size) {
diff --git a/src/components/ble/DfuService.h b/src/components/ble/DfuService.h
index b56911b9..6652cdc1 100644
--- a/src/components/ble/DfuService.h
+++ b/src/components/ble/DfuService.h
@@ -77,6 +77,10 @@ namespace Pinetime {
uint16_t ComputeCrc(uint8_t const* p_data, uint32_t size, uint16_t const* p_crc);
};
+ static constexpr ble_uuid128_t serviceUuid {
+ .u {.type = BLE_UUID_TYPE_128},
+ .value = {0x23, 0xD1, 0xBC, 0xEA, 0x5F, 0x78, 0x23, 0x15, 0xDE, 0xEF, 0x12, 0x12, 0x30, 0x15, 0x00, 0x00}};
+
private:
Pinetime::System::SystemTask& systemTask;
Pinetime::Controllers::Ble& bleController;
@@ -90,10 +94,6 @@ namespace Pinetime {
uint16_t revision {0x0008};
- static constexpr ble_uuid128_t serviceUuid {
- .u {.type = BLE_UUID_TYPE_128},
- .value = {0x23, 0xD1, 0xBC, 0xEA, 0x5F, 0x78, 0x23, 0x15, 0xDE, 0xEF, 0x12, 0x12, 0x30, 0x15, 0x00, 0x00}};
-
static constexpr ble_uuid128_t packetCharacteristicUuid {
.u {.type = BLE_UUID_TYPE_128},
.value = {0x23, 0xD1, 0xBC, 0xEA, 0x5F, 0x78, 0x23, 0x15, 0xDE, 0xEF, 0x12, 0x12, 0x32, 0x15, 0x00, 0x00}};
diff --git a/src/components/ble/HeartRateService.cpp b/src/components/ble/HeartRateService.cpp
index c522e67e..d34dbf83 100644
--- a/src/components/ble/HeartRateService.cpp
+++ b/src/components/ble/HeartRateService.cpp
@@ -1,6 +1,6 @@
#include "components/ble/HeartRateService.h"
#include "components/heartrate/HeartRateController.h"
-#include "systemtask/SystemTask.h"
+#include "components/ble/NimbleController.h"
#include <nrf_log.h>
using namespace Pinetime::Controllers;
@@ -16,8 +16,8 @@ namespace {
}
// TODO Refactoring - remove dependency to SystemTask
-HeartRateService::HeartRateService(Pinetime::System::SystemTask& system, Controllers::HeartRateController& heartRateController)
- : system {system},
+HeartRateService::HeartRateService(NimbleController& nimble, Controllers::HeartRateController& heartRateController)
+ : nimble {nimble},
heartRateController {heartRateController},
characteristicDefinition {{.uuid = &heartRateMeasurementUuid.u,
.access_cb = HeartRateServiceCallback,
@@ -63,7 +63,7 @@ void HeartRateService::OnNewHeartRateValue(uint8_t heartRateValue) {
uint8_t buffer[2] = {0, heartRateValue}; // [0] = flags, [1] = hr value
auto* om = ble_hs_mbuf_from_flat(buffer, 2);
- uint16_t connectionHandle = system.nimble().connHandle();
+ uint16_t connectionHandle = nimble.connHandle();
if (connectionHandle == 0 || connectionHandle == BLE_HS_CONN_HANDLE_NONE) {
return;
diff --git a/src/components/ble/HeartRateService.h b/src/components/ble/HeartRateService.h
index 003bdbd1..ca8f10fb 100644
--- a/src/components/ble/HeartRateService.h
+++ b/src/components/ble/HeartRateService.h
@@ -2,21 +2,18 @@
#define min // workaround: nimble's min/max macros conflict with libstdc++
#define max
#include <host/ble_gap.h>
-#include <atomic>
#undef max
#undef min
+#include <atomic>
namespace Pinetime {
- namespace System {
- class SystemTask;
- }
-
namespace Controllers {
class HeartRateController;
+ class NimbleController;
class HeartRateService {
public:
- HeartRateService(Pinetime::System::SystemTask& system, Controllers::HeartRateController& heartRateController);
+ HeartRateService(NimbleController& nimble, Controllers::HeartRateController& heartRateController);
void Init();
int OnHeartRateRequested(uint16_t attributeHandle, ble_gatt_access_ctxt* context);
void OnNewHeartRateValue(uint8_t hearRateValue);
@@ -24,14 +21,14 @@ namespace Pinetime {
void SubscribeNotification(uint16_t attributeHandle);
void UnsubscribeNotification(uint16_t attributeHandle);
+ static constexpr uint16_t heartRateServiceId {0x180D};
+ static constexpr ble_uuid16_t heartRateServiceUuid {.u {.type = BLE_UUID_TYPE_16}, .value = heartRateServiceId};
+
private:
- Pinetime::System::SystemTask& system;
+ NimbleController& nimble;
Controllers::HeartRateController& heartRateController;
- static constexpr uint16_t heartRateServiceId {0x180D};
static constexpr uint16_t heartRateMeasurementId {0x2A37};
- static constexpr ble_uuid16_t heartRateServiceUuid {.u {.type = BLE_UUID_TYPE_16}, .value = heartRateServiceId};
-
static constexpr ble_uuid16_t heartRateMeasurementUuid {.u {.type = BLE_UUID_TYPE_16}, .value = heartRateMeasurementId};
struct ble_gatt_chr_def characteristicDefinition[2];
diff --git a/src/components/ble/MotionService.cpp b/src/components/ble/MotionService.cpp
index 604f22d5..1626a5bf 100644
--- a/src/components/ble/MotionService.cpp
+++ b/src/components/ble/MotionService.cpp
@@ -1,6 +1,6 @@
#include "components/ble/MotionService.h"
#include "components/motion/MotionController.h"
-#include "systemtask/SystemTask.h"
+#include "components/ble/NimbleController.h"
#include <nrf_log.h>
using namespace Pinetime::Controllers;
@@ -28,8 +28,8 @@ namespace {
}
// TODO Refactoring - remove dependency to SystemTask
-MotionService::MotionService(Pinetime::System::SystemTask& system, Controllers::MotionController& motionController)
- : system {system},
+MotionService::MotionService(NimbleController& nimble, Controllers::MotionController& motionController)
+ : nimble {nimble},
motionController {motionController},
characteristicDefinition {{.uuid = &stepCountCharUuid.u,
.access_cb = MotionServiceCallback,
@@ -82,7 +82,7 @@ void MotionService::OnNewStepCountValue(uint32_t stepCount) {
uint32_t buffer = stepCount;
auto* om = ble_hs_mbuf_from_flat(&buffer, 4);
- uint16_t connectionHandle = system.nimble().connHandle();
+ uint16_t connectionHandle = nimble.connHandle();
if (connectionHandle == 0 || connectionHandle == BLE_HS_CONN_HANDLE_NONE) {
return;
@@ -98,7 +98,7 @@ void MotionService::OnNewMotionValues(int16_t x, int16_t y, int16_t z) {
int16_t buffer[3] = {x, y, z};
auto* om = ble_hs_mbuf_from_flat(buffer, 3 * sizeof(int16_t));
- uint16_t connectionHandle = system.nimble().connHandle();
+ uint16_t connectionHandle = nimble.connHandle();
if (connectionHandle == 0 || connectionHandle == BLE_HS_CONN_HANDLE_NONE) {
return;
@@ -120,3 +120,7 @@ void MotionService::UnsubscribeNotification(uint16_t attributeHandle) {
else if (attributeHandle == motionValuesHandle)
motionValuesNoficationEnabled = false;
}
+
+bool MotionService::IsMotionNotificationSubscribed() const {
+ return motionValuesNoficationEnabled;
+}
diff --git a/src/components/ble/MotionService.h b/src/components/ble/MotionService.h
index 1b172528..acc91e8d 100644
--- a/src/components/ble/MotionService.h
+++ b/src/components/ble/MotionService.h
@@ -7,16 +7,13 @@
#undef min
namespace Pinetime {
- namespace System {
- class SystemTask;
- }
-
namespace Controllers {
+ class NimbleController;
class MotionController;
class MotionService {
public:
- MotionService(Pinetime::System::SystemTask& system, Controllers::MotionController& motionController);
+ MotionService(NimbleController& nimble, Controllers::MotionController& motionController);
void Init();
int OnStepCountRequested(uint16_t attributeHandle, ble_gatt_access_ctxt* context);
void OnNewStepCountValue(uint32_t stepCount);
@@ -24,9 +21,10 @@ namespace Pinetime {
void SubscribeNotification(uint16_t attributeHandle);
void UnsubscribeNotification(uint16_t attributeHandle);
+ bool IsMotionNotificationSubscribed() const;
private:
- Pinetime::System::SystemTask& system;
+ NimbleController& nimble;
Controllers::MotionController& motionController;
struct ble_gatt_chr_def characteristicDefinition[3];
diff --git a/src/components/ble/MusicService.cpp b/src/components/ble/MusicService.cpp
index 403c957b..43cbec70 100644
--- a/src/components/ble/MusicService.cpp
+++ b/src/components/ble/MusicService.cpp
@@ -16,8 +16,10 @@
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include "components/ble/MusicService.h"
-#include "systemtask/SystemTask.h"
+#include "components/ble/NimbleController.h"
#include <cstring>
+#include <FreeRTOS.h>
+#include <task.h>
namespace {
// 0000yyxx-78fc-48fe-8e23-433b3a1942d0
@@ -53,7 +55,7 @@ namespace {
}
}
-Pinetime::Controllers::MusicService::MusicService(Pinetime::System::SystemTask& system) : m_system(system) {
+Pinetime::Controllers::MusicService::MusicService(Pinetime::Controllers::NimbleController& nimble) : nimble(nimble) {
characteristicDefinition[0] = {.uuid = &msEventCharUuid.u,
.access_cb = MusicCallback,
.arg = this,
@@ -212,7 +214,7 @@ int Pinetime::Controllers::MusicService::getTrackLength() const {
void Pinetime::Controllers::MusicService::event(char event) {
auto* om = ble_hs_mbuf_from_flat(&event, 1);
- uint16_t connectionHandle = m_system.nimble().connHandle();
+ uint16_t connectionHandle = nimble.connHandle();
if (connectionHandle == 0 || connectionHandle == BLE_HS_CONN_HANDLE_NONE) {
return;
diff --git a/src/components/ble/MusicService.h b/src/components/ble/MusicService.h
index 6aebc3c5..93d94a34 100644
--- a/src/components/ble/MusicService.h
+++ b/src/components/ble/MusicService.h
@@ -25,16 +25,15 @@
#include <host/ble_uuid.h>
#undef max
#undef min
+#include <FreeRTOS.h>
namespace Pinetime {
- namespace System {
- class SystemTask;
- }
-
namespace Controllers {
+ class NimbleController;
+
class MusicService {
public:
- explicit MusicService(Pinetime::System::SystemTask& system);
+ explicit MusicService(NimbleController& nimble);
void Init();
@@ -89,7 +88,7 @@ namespace Pinetime {
bool repeat {false};
bool shuffle {false};
- Pinetime::System::SystemTask& m_system;
+ NimbleController& nimble;
};
}
}
diff --git a/src/components/ble/NavigationService.cpp b/src/components/ble/NavigationService.cpp
index 5508d08c..4922237c 100644
--- a/src/components/ble/NavigationService.cpp
+++ b/src/components/ble/NavigationService.cpp
@@ -18,8 +18,6 @@
#include "components/ble/NavigationService.h"
-#include "systemtask/SystemTask.h"
-
namespace {
// 0001yyxx-78fc-48fe-8e23-433b3a1942d0
constexpr ble_uuid128_t CharUuid(uint8_t x, uint8_t y) {
@@ -45,7 +43,7 @@ namespace {
}
} // namespace
-Pinetime::Controllers::NavigationService::NavigationService(Pinetime::System::SystemTask& system) : m_system(system) {
+Pinetime::Controllers::NavigationService::NavigationService() {
characteristicDefinition[0] = {.uuid = &navFlagCharUuid.u,
.access_cb = NAVCallback,
.arg = this,
diff --git a/src/components/ble/NavigationService.h b/src/components/ble/NavigationService.h
index 1c1739ba..03e79ac5 100644
--- a/src/components/ble/NavigationService.h
+++ b/src/components/ble/NavigationService.h
@@ -27,15 +27,11 @@
#undef min
namespace Pinetime {
- namespace System {
- class SystemTask;
- }
-
namespace Controllers {
class NavigationService {
public:
- explicit NavigationService(Pinetime::System::SystemTask& system);
+ NavigationService();
void Init();
@@ -57,8 +53,6 @@ namespace Pinetime {
std::string m_narrative;
std::string m_manDist;
int m_progress;
-
- Pinetime::System::SystemTask& m_system;
};
}
}
diff --git a/src/components/ble/NimbleController.cpp b/src/components/ble/NimbleController.cpp
index 74c8f926..5059007a 100644
--- a/src/components/ble/NimbleController.cpp
+++ b/src/components/ble/NimbleController.cpp
@@ -42,13 +42,12 @@ NimbleController::NimbleController(Pinetime::System::SystemTask& systemTask,
anService {systemTask, notificationManager},
alertNotificationClient {systemTask, notificationManager},
currentTimeService {dateTimeController},
- musicService {systemTask},
- weatherService {systemTask, dateTimeController},
- navService {systemTask},
+ musicService {*this},
+ weatherService {dateTimeController},
batteryInformationService {batteryController},
immediateAlertService {systemTask, notificationManager},
- heartRateService {systemTask, heartRateController},
- motionService {systemTask, motionController},
+ heartRateService {*this, heartRateController},
+ motionService {*this, motionController},
fsService {systemTask, fs},
serviceDiscovery({&currentTimeClient, &alertNotificationClient}) {
}
@@ -159,7 +158,10 @@ void NimbleController::StartAdvertising() {
}
fields.flags = BLE_HS_ADV_F_DISC_GEN | BLE_HS_ADV_F_BREDR_UNSUP;
- fields.uuids128 = &dfuServiceUuid;
+ fields.uuids16 = &HeartRateService::heartRateServiceUuid;
+ fields.num_uuids16 = 1;
+ fields.uuids16_is_complete = 1;
+ fields.uuids128 = &DfuService::serviceUuid;
fields.num_uuids128 = 1;
fields.uuids128_is_complete = 1;
fields.tx_pwr_lvl = BLE_HS_ADV_TX_PWR_LVL_AUTO;
@@ -452,9 +454,15 @@ void NimbleController::PersistBond(struct ble_gap_conn_desc& desc) {
/* Wakeup Spi and SpiNorFlash before accessing the file system
* This should be fixed in the FS driver
*/
- systemTask.PushMessage(Pinetime::System::Messages::GoToRunning);
systemTask.PushMessage(Pinetime::System::Messages::DisableSleeping);
- vTaskDelay(10);
+
+ // This isn't quite correct
+ // SystemTask could receive EnableSleeping right after passing this check
+ // We need some guarantee that the SystemTask has processed the above message
+ // before we can continue
+ while (!systemTask.IsSleepDisabled()) {
+ vTaskDelay(pdMS_TO_TICKS(5));
+ }
lfs_file_t file_p;
diff --git a/src/components/ble/NimbleController.h b/src/components/ble/NimbleController.h
index 8f1dfed7..597ef0cc 100644
--- a/src/components/ble/NimbleController.h
+++ b/src/components/ble/NimbleController.h
@@ -21,7 +21,7 @@
#include "components/ble/NavigationService.h"
#include "components/ble/ServiceDiscovery.h"
#include "components/ble/MotionService.h"
-#include "components/ble/weather/WeatherService.h"
+#include "components/ble/SimpleWeatherService.h"
#include "components/fs/FS.h"
namespace Pinetime {
@@ -67,7 +67,7 @@ namespace Pinetime {
return anService;
};
- Pinetime::Controllers::WeatherService& weather() {
+ Pinetime::Controllers::SimpleWeatherService& weather() {
return weatherService;
};
@@ -99,7 +99,7 @@ namespace Pinetime {
AlertNotificationClient alertNotificationClient;
CurrentTimeService currentTimeService;
MusicService musicService;
- WeatherService weatherService;
+ SimpleWeatherService weatherService;
NavigationService navService;
BatteryInformationService batteryInformationService;
ImmediateAlertService immediateAlertService;
@@ -112,10 +112,6 @@ namespace Pinetime {
uint16_t connectionHandle = BLE_HS_CONN_HANDLE_NONE;
uint8_t fastAdvCount = 0;
uint8_t bondId[16] = {0};
-
- ble_uuid128_t dfuServiceUuid {
- .u {.type = BLE_UUID_TYPE_128},
- .value = {0x23, 0xD1, 0xBC, 0xEA, 0x5F, 0x78, 0x23, 0x15, 0xDE, 0xEF, 0x12, 0x12, 0x30, 0x15, 0x00, 0x00}};
};
static NimbleController* nptr;
diff --git a/src/components/ble/NotificationManager.h b/src/components/ble/NotificationManager.h
index f68a68af..57a9c715 100644
--- a/src/components/ble/NotificationManager.h
+++ b/src/components/ble/NotificationManager.h
@@ -27,11 +27,12 @@ namespace Pinetime {
struct Notification {
using Id = uint8_t;
using Idx = uint8_t;
- Id id = 0;
- bool valid = false;
+
+ std::array<char, MessageSize + 1> message{};
uint8_t size;
- std::array<char, MessageSize + 1> message {};
Categories category = Categories::Unknown;
+ Id id = 0;
+ bool valid = false;
const char* Message() const;
const char* Title() const;
diff --git a/src/components/ble/SimpleWeatherService.cpp b/src/components/ble/SimpleWeatherService.cpp
new file mode 100644
index 00000000..51baf543
--- /dev/null
+++ b/src/components/ble/SimpleWeatherService.cpp
@@ -0,0 +1,173 @@
+/* Copyright (C) 2023 Jean-François Milants
+
+ 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 "components/ble/SimpleWeatherService.h"
+
+#include <algorithm>
+#include <array>
+#include <cstring>
+#include <nrf_log.h>
+
+using namespace Pinetime::Controllers;
+
+namespace {
+ enum class MessageType : uint8_t { CurrentWeather, Forecast, Unknown };
+
+ uint64_t ToUInt64(const uint8_t* data) {
+ return data[0] + (data[1] << 8) + (data[2] << 16) + (data[3] << 24) + (static_cast<uint64_t>(data[4]) << 32) +
+ (static_cast<uint64_t>(data[5]) << 40) + (static_cast<uint64_t>(data[6]) << 48) + (static_cast<uint64_t>(data[7]) << 56);
+ }
+
+ int16_t ToInt16(const uint8_t* data) {
+ return data[0] + (data[1] << 8);
+ }
+
+ SimpleWeatherService::CurrentWeather CreateCurrentWeather(const uint8_t* dataBuffer) {
+ SimpleWeatherService::Location cityName;
+ std::memcpy(cityName.data(), &dataBuffer[16], 32);
+ cityName[32] = '\0';
+ return SimpleWeatherService::CurrentWeather(ToUInt64(&dataBuffer[2]),
+ SimpleWeatherService::Temperature(ToInt16(&dataBuffer[10])),
+ SimpleWeatherService::Temperature(ToInt16(&dataBuffer[12])),
+ SimpleWeatherService::Temperature(ToInt16(&dataBuffer[14])),
+ SimpleWeatherService::Icons {dataBuffer[16 + 32]},
+ std::move(cityName));
+ }
+
+ SimpleWeatherService::Forecast CreateForecast(const uint8_t* dataBuffer) {
+ auto timestamp = static_cast<uint64_t>(ToUInt64(&dataBuffer[2]));
+
+ std::array<std::optional<SimpleWeatherService::Forecast::Day>, SimpleWeatherService::MaxNbForecastDays> days;
+ const uint8_t nbDaysInBuffer = dataBuffer[10];
+ const uint8_t nbDays = std::min(SimpleWeatherService::MaxNbForecastDays, nbDaysInBuffer);
+ for (int i = 0; i < nbDays; i++) {
+ days[i] = SimpleWeatherService::Forecast::Day {SimpleWeatherService::Temperature(ToInt16(&dataBuffer[11 + (i * 5)])),
+ SimpleWeatherService::Temperature(ToInt16(&dataBuffer[13 + (i * 5)])),
+ SimpleWeatherService::Icons {dataBuffer[15 + (i * 5)]}};
+ }
+ return SimpleWeatherService::Forecast {timestamp, nbDays, days};
+ }
+
+ MessageType GetMessageType(const uint8_t* data) {
+ auto messageType = static_cast<MessageType>(*data);
+ if (messageType > MessageType::Unknown) {
+ return MessageType::Unknown;
+ }
+ return messageType;
+ }
+
+ uint8_t GetVersion(const uint8_t* dataBuffer) {
+ return dataBuffer[1];
+ }
+}
+
+int WeatherCallback(uint16_t /*connHandle*/, uint16_t /*attrHandle*/, struct ble_gatt_access_ctxt* ctxt, void* arg) {
+ return static_cast<Pinetime::Controllers::SimpleWeatherService*>(arg)->OnCommand(ctxt);
+}
+
+SimpleWeatherService::SimpleWeatherService(DateTime& dateTimeController) : dateTimeController(dateTimeController) {
+}
+
+void SimpleWeatherService::Init() {
+ ble_gatts_count_cfg(serviceDefinition);
+ ble_gatts_add_svcs(serviceDefinition);
+}
+
+int SimpleWeatherService::OnCommand(struct ble_gatt_access_ctxt* ctxt) {
+ const auto* buffer = ctxt->om;
+ const auto* dataBuffer = buffer->om_data;
+
+ switch (GetMessageType(dataBuffer)) {
+ case MessageType::CurrentWeather:
+ if (GetVersion(dataBuffer) == 0) {
+ currentWeather = CreateCurrentWeather(dataBuffer);
+ NRF_LOG_INFO("Current weather :\n\tTimestamp : %d\n\tTemperature:%d\n\tMin:%d\n\tMax:%d\n\tIcon:%d\n\tLocation:%s",
+ currentWeather->timestamp,
+ currentWeather->temperature.PreciseCelsius(),
+ currentWeather->minTemperature.PreciseCelsius(),
+ currentWeather->maxTemperature.PreciseCelsius(),
+ currentWeather->iconId,
+ currentWeather->location.data());
+ }
+ break;
+ case MessageType::Forecast:
+ if (GetVersion(dataBuffer) == 0) {
+ forecast = CreateForecast(dataBuffer);
+ NRF_LOG_INFO("Forecast : Timestamp : %d", forecast->timestamp);
+ for (int i = 0; i < 5; i++) {
+ NRF_LOG_INFO("\t[%d] Min: %d - Max : %d - Icon : %d",
+ i,
+ forecast->days[i]->minTemperature.PreciseCelsius(),
+ forecast->days[i]->maxTemperature.PreciseCelsius(),
+ forecast->days[i]->iconId);
+ }
+ }
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+std::optional<SimpleWeatherService::CurrentWeather> SimpleWeatherService::Current() const {
+ if (currentWeather) {
+ auto currentTime = dateTimeController.CurrentDateTime().time_since_epoch();
+ auto weatherTpSecond = std::chrono::seconds {currentWeather->timestamp};
+ auto weatherTp = std::chrono::duration_cast<std::chrono::seconds>(weatherTpSecond);
+ auto delta = currentTime - weatherTp;
+
+ if (delta < std::chrono::hours {24}) {
+ return currentWeather;
+ }
+ }
+ return {};
+}
+
+std::optional<SimpleWeatherService::Forecast> SimpleWeatherService::GetForecast() const {
+ if (forecast) {
+ auto currentTime = dateTimeController.CurrentDateTime().time_since_epoch();
+ auto weatherTpSecond = std::chrono::seconds {forecast->timestamp};
+ auto weatherTp = std::chrono::duration_cast<std::chrono::seconds>(weatherTpSecond);
+ auto delta = currentTime - weatherTp;
+
+ if (delta < std::chrono::hours {24}) {
+ return this->forecast;
+ }
+ }
+ return {};
+}
+
+bool SimpleWeatherService::CurrentWeather::operator==(const SimpleWeatherService::CurrentWeather& other) const {
+ return this->iconId == other.iconId && this->temperature == other.temperature && this->timestamp == other.timestamp &&
+ this->maxTemperature == other.maxTemperature && this->minTemperature == other.maxTemperature &&
+ std::strcmp(this->location.data(), other.location.data()) == 0;
+}
+
+bool SimpleWeatherService::Forecast::Day::operator==(const SimpleWeatherService::Forecast::Day& other) const {
+ return this->iconId == other.iconId && this->maxTemperature == other.maxTemperature && this->minTemperature == other.maxTemperature;
+}
+
+bool SimpleWeatherService::Forecast::operator==(const SimpleWeatherService::Forecast& other) const {
+ for (int i = 0; i < this->nbDays; i++) {
+ if (this->days[i] != other.days[i]) {
+ return false;
+ }
+ }
+ return this->timestamp == other.timestamp && this->nbDays == other.nbDays;
+}
diff --git a/src/components/ble/SimpleWeatherService.h b/src/components/ble/SimpleWeatherService.h
new file mode 100644
index 00000000..0f8c181b
--- /dev/null
+++ b/src/components/ble/SimpleWeatherService.h
@@ -0,0 +1,174 @@
+/* Copyright (C) 2023 Jean-François Milants
+
+ 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 <array>
+#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>
+#include <optional>
+#include <cstring>
+#undef max
+#undef min
+
+#include "components/datetime/DateTimeController.h"
+
+int WeatherCallback(uint16_t connHandle, uint16_t attrHandle, struct ble_gatt_access_ctxt* ctxt, void* arg);
+
+namespace Pinetime {
+ namespace Controllers {
+
+ class SimpleWeatherService {
+ public:
+ explicit SimpleWeatherService(DateTime& dateTimeController);
+
+ void Init();
+
+ int OnCommand(struct ble_gatt_access_ctxt* ctxt);
+
+ static constexpr uint8_t MaxNbForecastDays = 5;
+
+ enum class Icons : uint8_t {
+ Sun = 0, // ClearSky
+ CloudsSun = 1, // FewClouds
+ Clouds = 2, // Scattered clouds
+ BrokenClouds = 3,
+ CloudShowerHeavy = 4, // shower rain
+ CloudSunRain = 5, // rain
+ Thunderstorm = 6,
+ Snow = 7,
+ Smog = 8, // Mist
+ Unknown = 255
+ };
+
+ class Temperature {
+ public:
+ explicit Temperature(int16_t raw) : raw {raw} {
+ }
+
+ [[nodiscard]] int16_t PreciseCelsius() const {
+ return raw;
+ }
+
+ [[nodiscard]] int16_t PreciseFahrenheit() const {
+ return raw * 9 / 5 + 3200;
+ }
+
+ [[nodiscard]] int16_t Celsius() const {
+ return (PreciseCelsius() + 50) / 100;
+ }
+
+ [[nodiscard]] int16_t Fahrenheit() const {
+ return (PreciseFahrenheit() + 50) / 100;
+ }
+
+ bool operator==(const Temperature& other) const {
+ return raw == other.raw;
+ }
+
+ private:
+ int16_t raw;
+ };
+
+ using Location = std::array<char, 33>; // 32 char + \0 (end of string)
+
+ struct CurrentWeather {
+ CurrentWeather(uint64_t timestamp,
+ Temperature temperature,
+ Temperature minTemperature,
+ Temperature maxTemperature,
+ Icons iconId,
+ Location&& location)
+ : timestamp {timestamp},
+ temperature {temperature},
+ minTemperature {minTemperature},
+ maxTemperature {maxTemperature},
+ iconId {iconId},
+ location {std::move(location)} {
+ }
+
+ uint64_t timestamp;
+ Temperature temperature;
+ Temperature minTemperature;
+ Temperature maxTemperature;
+ Icons iconId;
+ Location location;
+
+ bool operator==(const CurrentWeather& other) const;
+ };
+
+ struct Forecast {
+ uint64_t timestamp;
+ uint8_t nbDays;
+
+ struct Day {
+ Temperature minTemperature;
+ Temperature maxTemperature;
+ Icons iconId;
+
+ bool operator==(const Day& other) const;
+ };
+
+ std::array<std::optional<Day>, MaxNbForecastDays> days;
+
+ bool operator==(const Forecast& other) const;
+ };
+
+ std::optional<CurrentWeather> Current() const;
+ std::optional<Forecast> GetForecast() const;
+
+ private:
+ // 00050000-78fc-48fe-8e23-433b3a1942d0
+ static constexpr ble_uuid128_t BaseUuid() {
+ return CharUuid(0x00, 0x00);
+ }
+
+ // 0005yyxx-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, 0x05, 0x00}};
+ }
+
+ ble_uuid128_t weatherUuid {BaseUuid()};
+
+ ble_uuid128_t weatherDataCharUuid {CharUuid(0x00, 0x01)};
+
+ const struct ble_gatt_chr_def characteristicDefinition[2] = {{.uuid = &weatherDataCharUuid.u,
+ .access_cb = WeatherCallback,
+ .arg = this,
+ .flags = BLE_GATT_CHR_F_WRITE,
+ .val_handle = &eventHandle},
+ {0}};
+ const struct ble_gatt_svc_def serviceDefinition[2] = {
+ {.type = BLE_GATT_SVC_TYPE_PRIMARY, .uuid = &weatherUuid.u, .characteristics = characteristicDefinition},
+ {0}};
+
+ uint16_t eventHandle {};
+
+ Pinetime::Controllers::DateTime& dateTimeController;
+
+ std::optional<CurrentWeather> currentWeather;
+ std::optional<Forecast> forecast;
+ };
+ }
+}
diff --git a/src/components/ble/weather/WeatherData.h b/src/components/ble/weather/WeatherData.h
deleted file mode 100644
index 1a995eb9..00000000
--- 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 watch faces 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 watch face
- * 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 watch face 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 fd13f819..00000000
--- a/src/components/ble/weather/WeatherService.cpp
+++ /dev/null
@@ -1,607 +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 <algorithm>
-#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(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(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());
- uint64_t currentDayStart = currentDayEnd - 86400;
- int16_t result = -32768;
- for (auto&& header : this->timeline) {
- if (header->eventType == WeatherData::eventtype::Temperature && header->timestamp >= currentDayStart &&
- 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());
- uint64_t currentDayStart = currentDayEnd - 86400;
- int16_t result = -32768;
- for (auto&& header : this->timeline) {
- if (header->eventType == WeatherData::eventtype::Temperature && header->timestamp >= currentDayStart &&
- 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 00650e90..00000000
--- a/src/components/ble/weather/WeatherService.h
+++ /dev/null
@@ -1,174 +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(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);
- };
- }
-}