2023-12-09 14:39:08 -05:00
|
|
|
/* 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/>.
|
|
|
|
*/
|
2023-12-18 12:07:36 -05:00
|
|
|
|
|
|
|
#include "components/ble/SimpleWeatherService.h"
|
|
|
|
|
2023-12-09 14:39:08 -05:00
|
|
|
#include <algorithm>
|
2023-12-23 09:54:23 -05:00
|
|
|
#include <array>
|
2023-12-09 14:39:08 -05:00
|
|
|
#include <cstring>
|
|
|
|
#include <nrf_log.h>
|
2023-12-10 05:13:18 -05:00
|
|
|
|
2023-12-09 14:39:08 -05:00
|
|
|
using namespace Pinetime::Controllers;
|
|
|
|
|
|
|
|
namespace {
|
2023-12-18 12:07:36 -05:00
|
|
|
enum class MessageType : uint8_t { CurrentWeather, Forecast, Unknown };
|
|
|
|
|
|
|
|
uint64_t ToUInt64(const uint8_t* data) {
|
2023-12-23 11:22:32 -05:00
|
|
|
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]) << 48) + (static_cast<uint64_t>(data[6]) << 48) + (static_cast<uint64_t>(data[7]) << 56);
|
2023-12-23 11:18:41 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
int16_t ToInt16(const uint8_t* data) {
|
2023-12-23 11:22:32 -05:00
|
|
|
return data[0] + (data[1] << 8);
|
2023-12-18 12:07:36 -05:00
|
|
|
}
|
2023-12-09 14:39:08 -05:00
|
|
|
|
|
|
|
SimpleWeatherService::CurrentWeather CreateCurrentWeather(const uint8_t* dataBuffer) {
|
2023-12-23 09:54:23 -05:00
|
|
|
SimpleWeatherService::Location cityName;
|
2023-12-23 11:18:41 -05:00
|
|
|
std::memcpy(cityName.data(), &dataBuffer[16], 32);
|
2023-12-09 14:39:08 -05:00
|
|
|
cityName[32] = '\0';
|
2023-12-23 11:22:32 -05:00
|
|
|
return SimpleWeatherService::CurrentWeather(ToUInt64(&dataBuffer[2]),
|
2023-12-23 11:18:41 -05:00
|
|
|
ToInt16(&dataBuffer[10]),
|
2023-12-23 11:22:32 -05:00
|
|
|
ToInt16(&dataBuffer[12]),
|
|
|
|
ToInt16(&dataBuffer[14]),
|
|
|
|
SimpleWeatherService::Icons {dataBuffer[16 + 32]},
|
|
|
|
std::move(cityName));
|
2023-12-09 14:39:08 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
SimpleWeatherService::Forecast CreateForecast(const uint8_t* dataBuffer) {
|
2023-12-18 12:07:36 -05:00
|
|
|
auto timestamp = static_cast<uint64_t>(ToUInt64(&dataBuffer[2]));
|
2023-12-10 05:13:18 -05:00
|
|
|
|
|
|
|
std::array<SimpleWeatherService::Forecast::Day, SimpleWeatherService::MaxNbForecastDays> days;
|
|
|
|
const uint8_t nbDaysInBuffer = dataBuffer[10];
|
|
|
|
const uint8_t nbDays = std::min(SimpleWeatherService::MaxNbForecastDays, nbDaysInBuffer);
|
2023-12-09 14:39:08 -05:00
|
|
|
for (int i = 0; i < nbDays; i++) {
|
2023-12-23 11:22:32 -05:00
|
|
|
days[i] = SimpleWeatherService::Forecast::Day {ToInt16(&dataBuffer[11 + (i * 5)]),
|
|
|
|
ToInt16(&dataBuffer[13 + (i * 5)]),
|
|
|
|
SimpleWeatherService::Icons {dataBuffer[15 + (i * 5)]}};
|
2023-12-09 14:39:08 -05:00
|
|
|
}
|
|
|
|
return SimpleWeatherService::Forecast {timestamp, nbDays, days};
|
|
|
|
}
|
|
|
|
|
2023-12-18 12:15:25 -05:00
|
|
|
MessageType GetMessageType(const uint8_t* data) {
|
|
|
|
auto messageType = static_cast<MessageType>(*data);
|
|
|
|
if (messageType > MessageType::Unknown) {
|
|
|
|
return MessageType::Unknown;
|
2023-12-09 14:39:08 -05:00
|
|
|
}
|
2023-12-18 12:15:25 -05:00
|
|
|
return messageType;
|
|
|
|
}
|
2023-12-09 14:39:08 -05:00
|
|
|
|
|
|
|
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(const 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;
|
|
|
|
|
2023-12-09 15:05:50 -05:00
|
|
|
switch (GetMessageType(dataBuffer)) {
|
2023-12-09 14:39:08 -05:00
|
|
|
case MessageType::CurrentWeather:
|
2023-12-09 15:05:50 -05:00
|
|
|
if (GetVersion(dataBuffer) == 0) {
|
2023-12-09 14:39:08 -05:00
|
|
|
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,
|
|
|
|
currentWeather->minTemperature,
|
|
|
|
currentWeather->maxTemperature,
|
|
|
|
currentWeather->iconId,
|
2023-12-23 11:18:41 -05:00
|
|
|
currentWeather->location.data());
|
2023-12-09 14:39:08 -05:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
case MessageType::Forecast:
|
2023-12-09 15:05:50 -05:00
|
|
|
if (GetVersion(dataBuffer) == 0) {
|
2023-12-09 14:39:08 -05:00
|
|
|
forecast = CreateForecast(dataBuffer);
|
|
|
|
NRF_LOG_INFO("Forecast : Timestamp : %d", forecast->timestamp);
|
2023-12-09 15:05:50 -05:00
|
|
|
for (int i = 0; i < 5; i++) {
|
|
|
|
NRF_LOG_INFO("\t[%d] Min: %d - Max : %d - Icon : %d",
|
|
|
|
i,
|
|
|
|
forecast->days[i].minTemperature,
|
|
|
|
forecast->days[i].maxTemperature,
|
|
|
|
forecast->days[i].iconId);
|
2023-12-09 14:39:08 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::optional<SimpleWeatherService::CurrentWeather> SimpleWeatherService::Current() const {
|
2023-12-09 15:05:50 -05:00
|
|
|
if (currentWeather) {
|
2023-12-09 14:39:08 -05:00
|
|
|
auto currentTime = dateTimeController.UTCDateTime().time_since_epoch();
|
2023-12-09 15:05:50 -05:00
|
|
|
auto weatherTpSecond = std::chrono::seconds {currentWeather->timestamp};
|
2023-12-09 14:39:08 -05:00
|
|
|
auto weatherTp = std::chrono::duration_cast<std::chrono::seconds>(weatherTpSecond);
|
|
|
|
auto delta = currentTime - weatherTp;
|
|
|
|
|
2023-12-09 15:05:50 -05:00
|
|
|
if (delta < std::chrono::hours {24}) {
|
2023-12-09 14:39:08 -05:00
|
|
|
return currentWeather;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
|
|
|
|
std::optional<SimpleWeatherService::Forecast> SimpleWeatherService::GetForecast() const {
|
2023-12-09 15:05:50 -05:00
|
|
|
if (forecast) {
|
2023-12-09 14:39:08 -05:00
|
|
|
auto currentTime = dateTimeController.UTCDateTime().time_since_epoch();
|
2023-12-09 15:05:50 -05:00
|
|
|
auto weatherTpSecond = std::chrono::seconds {forecast->timestamp};
|
2023-12-09 14:39:08 -05:00
|
|
|
auto weatherTp = std::chrono::duration_cast<std::chrono::seconds>(weatherTpSecond);
|
|
|
|
auto delta = currentTime - weatherTp;
|
|
|
|
|
2023-12-09 15:05:50 -05:00
|
|
|
if (delta < std::chrono::hours {24}) {
|
2023-12-09 14:39:08 -05:00
|
|
|
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 &&
|
2023-12-23 09:54:23 -05:00
|
|
|
this->maxTemperature == other.maxTemperature && this->minTemperature == other.maxTemperature &&
|
|
|
|
std::strcmp(this->location.data(), other.location.data()) == 0;
|
2023-12-09 14:39:08 -05:00
|
|
|
}
|