Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SimpleWeatherService: Add sunrise and sunset data #2100

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 9 additions & 5 deletions doc/SimpleWeatherService.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,23 +17,25 @@ The host uses this characteristic to update the current weather information and

This characteristics accepts a byte array with the following 2-Bytes header:

- [0] Message Type :
- [0] Message Type :
- `0` : Current weather
- `1` : Forecast
- [1] Message Version : Version `0` is currently supported. Other versions might be added in future releases
- [1] Message Version :
- `0` : Currently supported
- `1` : Adds support for sunrise and sunset

### Current Weather
### Current Weather

The byte array must contain the following data:

- [0] : Message type = `0`
- [1] : Message version = `0`
- [1] : Message version = `1`
- [2][3][4][5][6][7][8][9] : Timestamp (64 bits UNIX timestamp, number of seconds elapsed since 1 JAN 1970) in local time (the same timezone as the one used to set the time)
- [10, 11] : Current temperature (°C * 100)
- [12, 13] : Minimum temperature (°C * 100)
- [14, 15] : Maximum temperature (°C * 100)
- [16]..[47] : location (string, unused characters should be set to `0`)
- [48] : icon ID
- [48] : icon ID
- 0 = Sun, clear sky
- 1 = Few clouds
- 2 = Clouds
Expand All @@ -43,6 +45,8 @@ The byte array must contain the following data:
- 6 = Thunderstorm
- 7 = Snow
- 8 = Mist, smog
- [49, 50] : Sunrise (16 bits, number of minutes elapsed since midnight)
- [51, 52] : Sunset (16 bits, number of minutes elapsed since midnight)

### Forecast

Expand Down
37 changes: 34 additions & 3 deletions src/components/ble/SimpleWeatherService.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,10 @@ namespace {
(static_cast<uint64_t>(data[5]) << 40) + (static_cast<uint64_t>(data[6]) << 48) + (static_cast<uint64_t>(data[7]) << 56);
}

uint16_t ToUInt16(const uint8_t* data) {
return data[0] + (data[1] << 8);
}

int16_t ToInt16(const uint8_t* data) {
return data[0] + (data[1] << 8);
}
Expand All @@ -41,12 +45,20 @@ namespace {
SimpleWeatherService::Location cityName;
std::memcpy(cityName.data(), &dataBuffer[16], 32);
cityName[32] = '\0';
uint16_t sunrise = 0;
uint16_t sunset = 0;
if (dataBuffer[1] > 0) {
sunrise = ToUInt16(&dataBuffer[49]);
sunset = ToUInt16(&dataBuffer[51]);
}
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));
std::move(cityName),
sunrise,
sunset);
}

SimpleWeatherService::Forecast CreateForecast(const uint8_t* dataBuffer) {
Expand Down Expand Up @@ -94,7 +106,7 @@ int SimpleWeatherService::OnCommand(struct ble_gatt_access_ctxt* ctxt) {

switch (GetMessageType(dataBuffer)) {
case MessageType::CurrentWeather:
if (GetVersion(dataBuffer) == 0) {
if (GetVersion(dataBuffer) <= 1) {
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,
Expand All @@ -103,6 +115,9 @@ int SimpleWeatherService::OnCommand(struct ble_gatt_access_ctxt* ctxt) {
currentWeather->maxTemperature.PreciseCelsius(),
currentWeather->iconId,
currentWeather->location.data());
if (GetVersion(dataBuffer) == 1) {
NRF_LOG_INFO("Sunrise: %d\n\tSunset: %d", currentWeather->sunrise, currentWeather->sunset);
}
}
break;
case MessageType::Forecast:
Expand Down Expand Up @@ -153,10 +168,26 @@ std::optional<SimpleWeatherService::Forecast> SimpleWeatherService::GetForecast(
return {};
}

bool SimpleWeatherService::IsNight() const {
if (currentWeather && currentWeather->sunrise > 0 && currentWeather->sunset > 0) {
auto currentTime = dateTimeController.CurrentDateTime().time_since_epoch();

// Get timestamp for last midnight
auto midnight = std::chrono::floor<std::chrono::days>(currentTime);

// Calculate minutes since midnight
auto currentMinutes = std::chrono::duration_cast<std::chrono::minutes>(currentTime - midnight).count();

return currentMinutes < currentWeather->sunrise || currentMinutes > currentWeather->sunset;
}

return false;
}

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;
std::strcmp(this->location.data(), other.location.data()) == 0 && this->sunrise == other.sunrise && this->sunset == other.sunset;
}

bool SimpleWeatherService::Forecast::Day::operator==(const SimpleWeatherService::Forecast::Day& other) const {
Expand Down
12 changes: 10 additions & 2 deletions src/components/ble/SimpleWeatherService.h
Original file line number Diff line number Diff line change
Expand Up @@ -98,13 +98,17 @@ namespace Pinetime {
Temperature minTemperature,
Temperature maxTemperature,
Icons iconId,
Location&& location)
Location&& location,
uint16_t sunrise,
uint16_t sunset)
: timestamp {timestamp},
temperature {temperature},
minTemperature {minTemperature},
maxTemperature {maxTemperature},
iconId {iconId},
location {std::move(location)} {
location {std::move(location)},
sunrise {sunrise},
sunset {sunset} {
}

uint64_t timestamp;
Expand All @@ -113,6 +117,8 @@ namespace Pinetime {
Temperature maxTemperature;
Icons iconId;
Location location;
uint16_t sunrise;
uint16_t sunset;

bool operator==(const CurrentWeather& other) const;
};
Expand All @@ -137,6 +143,8 @@ namespace Pinetime {
std::optional<CurrentWeather> Current() const;
std::optional<Forecast> GetForecast() const;

bool IsNight() const;

private:
// 00050000-78fc-48fe-8e23-433b3a1942d0
static constexpr ble_uuid128_t BaseUuid() {
Expand Down
2 changes: 1 addition & 1 deletion src/displayapp/fonts/fonts.json
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@
"sources": [
{
"file": "FontAwesome5-Solid+Brands+Regular.woff",
"range": "0xf185, 0xf6c4, 0xf743, 0xf740, 0xf75f, 0xf0c2, 0xf05e, 0xf73b, 0xf0e7, 0xf2dc"
"range": "0xf185, 0xf186, 0xf6c3, 0xf6c4, 0xf73c, 0xf743, 0xf740, 0xf75f, 0xf0c2, 0xf05e, 0xf73b, 0xf0e7, 0xf2dc"
}
],
"bpp": 1,
Expand Down
3 changes: 3 additions & 0 deletions src/displayapp/screens/Symbols.h
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,11 @@ namespace Pinetime {

// fontawesome_weathericons.c
// static constexpr const char* sun = "\xEF\x86\x85";
static constexpr const char* moon = "\xEF\x86\x86"; // 0xf186
static constexpr const char* cloudSun = "\xEF\x9B\x84";
static constexpr const char* cloudMoon = "\xEF\x9B\x83"; // 0xf6c3
static constexpr const char* cloudSunRain = "\xEF\x9D\x83";
static constexpr const char* cloudMoonRain = "\xEF\x9C\xBC"; // 0xf73c
static constexpr const char* cloudShowersHeavy = "\xEF\x9D\x80";
static constexpr const char* smog = "\xEF\x9D\x9F";
static constexpr const char* cloud = "\xEF\x83\x82";
Expand Down
2 changes: 1 addition & 1 deletion src/displayapp/screens/WatchFaceDigital.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,7 @@ void WatchFaceDigital::Refresh() {
tempUnit = 'F';
}
lv_label_set_text_fmt(temperature, "%d°%c", temp, tempUnit);
lv_label_set_text(weatherIcon, Symbols::GetSymbol(optCurrentWeather->iconId));
lv_label_set_text(weatherIcon, Symbols::GetSymbol(optCurrentWeather->iconId, weatherService.IsNight()));
} else {
lv_label_set_text_static(temperature, "");
lv_label_set_text(weatherIcon, "");
Expand Down
2 changes: 1 addition & 1 deletion src/displayapp/screens/WatchFacePineTimeStyle.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -548,7 +548,7 @@ void WatchFacePineTimeStyle::Refresh() {
temp = optCurrentWeather->temperature.Fahrenheit();
}
lv_label_set_text_fmt(temperature, "%d°", temp);
lv_label_set_text(weatherIcon, Symbols::GetSymbol(optCurrentWeather->iconId));
lv_label_set_text(weatherIcon, Symbols::GetSymbol(optCurrentWeather->iconId, weatherService.IsNight()));
} else {
lv_label_set_text(temperature, "--");
lv_label_set_text(weatherIcon, Symbols::ban);
Expand Down
4 changes: 2 additions & 2 deletions src/displayapp/screens/Weather.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ void Weather::Refresh() {
LV_LABEL_PART_MAIN,
LV_STATE_DEFAULT,
TemperatureColor(optCurrentWeather->temperature));
lv_label_set_text(icon, Symbols::GetSymbol(optCurrentWeather->iconId));
lv_label_set_text(icon, Symbols::GetSymbol(optCurrentWeather->iconId, weatherService.IsNight()));
lv_label_set_text(condition, Symbols::GetCondition(optCurrentWeather->iconId));
lv_label_set_text_fmt(temperature, "%d°%c", temp, tempUnit);
lv_label_set_text_fmt(minTemperature, "%d°", minTemp);
Expand Down Expand Up @@ -168,7 +168,7 @@ void Weather::Refresh() {
}
const char* dayOfWeek = Controllers::DateTime::DayOfWeekShortToStringLow(static_cast<Controllers::DateTime::Days>(wday));
lv_table_set_cell_value(forecast, 0, i, dayOfWeek);
lv_table_set_cell_value(forecast, 1, i, Symbols::GetSymbol(optCurrentForecast->days[i]->iconId));
lv_table_set_cell_value(forecast, 1, i, Symbols::GetSymbol(optCurrentForecast->days[i]->iconId, false));
// Pad cells based on the largest number of digits on each column
char maxPadding[3] = " ";
char minPadding[3] = " ";
Expand Down
12 changes: 11 additions & 1 deletion src/displayapp/screens/WeatherSymbols.cpp
Original file line number Diff line number Diff line change
@@ -1,11 +1,18 @@
#include "displayapp/screens/WeatherSymbols.h"

const char* Pinetime::Applications::Screens::Symbols::GetSymbol(const Pinetime::Controllers::SimpleWeatherService::Icons icon) {
const char* Pinetime::Applications::Screens::Symbols::GetSymbol(const Pinetime::Controllers::SimpleWeatherService::Icons icon,
const bool isNight) {
switch (icon) {
case Pinetime::Controllers::SimpleWeatherService::Icons::Sun:
if (isNight) {
return Symbols::moon;
}
return Symbols::sun;
break;
case Pinetime::Controllers::SimpleWeatherService::Icons::CloudsSun:
if (isNight) {
return Symbols::cloudMoon;
}
return Symbols::cloudSun;
break;
case Pinetime::Controllers::SimpleWeatherService::Icons::Clouds:
Expand All @@ -24,6 +31,9 @@ const char* Pinetime::Applications::Screens::Symbols::GetSymbol(const Pinetime::
return Symbols::cloudShowersHeavy;
break;
case Pinetime::Controllers::SimpleWeatherService::Icons::CloudSunRain:
if (isNight) {
return Symbols::cloudMoonRain;
}
return Symbols::cloudSunRain;
break;
case Pinetime::Controllers::SimpleWeatherService::Icons::Smog:
Expand Down
2 changes: 1 addition & 1 deletion src/displayapp/screens/WeatherSymbols.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ namespace Pinetime {
namespace Applications {
namespace Screens {
namespace Symbols {
const char* GetSymbol(const Pinetime::Controllers::SimpleWeatherService::Icons icon);
const char* GetSymbol(const Pinetime::Controllers::SimpleWeatherService::Icons icon, const bool isNight);
const char* GetCondition(const Pinetime::Controllers::SimpleWeatherService::Icons icon);
}
}
Expand Down
Loading