Skip to content

Commit f88c69a

Browse files
vkarehmark9064
andauthored
SimpleWeatherService: Add sunrise and sunset data (#2100)
* SimpleWeatherService: Add sunrise and sunset data --------- Co-authored-by: mark9064 <30447455+mark9064@users.noreply.github.com>
1 parent 51a6fb6 commit f88c69a

File tree

10 files changed

+112
-17
lines changed

10 files changed

+112
-17
lines changed

doc/SimpleWeatherService.md

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,23 +17,25 @@ The host uses this characteristic to update the current weather information and
1717

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

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

25-
### Current Weather
27+
### Current Weather
2628

2729
The byte array must contain the following data:
2830

2931
- [0] : Message type = `0`
30-
- [1] : Message version = `0`
32+
- [1] : Message version = `1`
3133
- [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)
3234
- [10, 11] : Current temperature (°C * 100)
3335
- [12, 13] : Minimum temperature (°C * 100)
3436
- [14, 15] : Maximum temperature (°C * 100)
3537
- [16]..[47] : location (string, unused characters should be set to `0`)
36-
- [48] : icon ID
38+
- [48] : icon ID
3739
- 0 = Sun, clear sky
3840
- 1 = Few clouds
3941
- 2 = Clouds
@@ -43,6 +45,13 @@ The byte array must contain the following data:
4345
- 6 = Thunderstorm
4446
- 7 = Snow
4547
- 8 = Mist, smog
48+
- [49, 50] : Sunrise (number of minutes elapsed since midnight)
49+
- `0` sun already up when day starts
50+
- `-1` unknown
51+
- `-2` no sunrise (e.g. polar night)
52+
- [51, 52] : Sunset (number of minutes elapsed since midnight)
53+
- `-1` unknown
54+
- `-2` no sunset (e.g. polar day)
4655

4756
### Forecast
4857

src/components/ble/SimpleWeatherService.cpp

Lines changed: 68 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -41,12 +41,48 @@ namespace {
4141
SimpleWeatherService::Location cityName;
4242
std::memcpy(cityName.data(), &dataBuffer[16], 32);
4343
cityName[32] = '\0';
44+
int16_t sunrise = -1;
45+
int16_t sunset = -1;
46+
if (dataBuffer[1] > 0) {
47+
int16_t bufferSunrise = ToInt16(&dataBuffer[49]);
48+
int16_t bufferSunset = ToInt16(&dataBuffer[51]);
49+
50+
// Sunrise/sunset format
51+
52+
// Assume sun is down at minute 0 / midnight
53+
54+
// 0<=x<1440 sunrise happens this many minutes into the day
55+
// (0 day starts with sun up)
56+
// -1 unknown
57+
// -2 sun not rising today
58+
59+
// 0<x<1440 sunset happens this many minutes into the day
60+
// -1 unknown
61+
// -2 sun not setting today
62+
63+
// Check if the weather data is well formed
64+
// Disable boolean simplification suggestion, as simplifying it makes it unreadable
65+
if (!( // NOLINT(readability-simplify-boolean-expr)
66+
// Fail if either unknown
67+
(bufferSunrise == -1 || bufferSunset == -1)
68+
// Cannot be out of range
69+
|| (bufferSunrise < -2 || bufferSunrise > 1439 || bufferSunset < -2 || bufferSunset > 1439)
70+
// Cannot have sunset without sunrise
71+
|| (bufferSunrise == -2 && bufferSunset != -2)
72+
// Cannot have sunset before sunrise
73+
|| (bufferSunrise >= bufferSunset && bufferSunrise >= 0 && bufferSunset >= 0))) {
74+
sunrise = bufferSunrise;
75+
sunset = bufferSunset;
76+
}
77+
}
4478
return SimpleWeatherService::CurrentWeather(ToUInt64(&dataBuffer[2]),
4579
SimpleWeatherService::Temperature(ToInt16(&dataBuffer[10])),
4680
SimpleWeatherService::Temperature(ToInt16(&dataBuffer[12])),
4781
SimpleWeatherService::Temperature(ToInt16(&dataBuffer[14])),
4882
SimpleWeatherService::Icons {dataBuffer[16 + 32]},
49-
std::move(cityName));
83+
std::move(cityName),
84+
sunrise,
85+
sunset);
5086
}
5187

5288
SimpleWeatherService::Forecast CreateForecast(const uint8_t* dataBuffer) {
@@ -94,7 +130,7 @@ int SimpleWeatherService::OnCommand(struct ble_gatt_access_ctxt* ctxt) {
94130

95131
switch (GetMessageType(dataBuffer)) {
96132
case MessageType::CurrentWeather:
97-
if (GetVersion(dataBuffer) == 0) {
133+
if (GetVersion(dataBuffer) <= 1) {
98134
currentWeather = CreateCurrentWeather(dataBuffer);
99135
NRF_LOG_INFO("Current weather :\n\tTimestamp : %d\n\tTemperature:%d\n\tMin:%d\n\tMax:%d\n\tIcon:%d\n\tLocation:%s",
100136
currentWeather->timestamp,
@@ -103,6 +139,9 @@ int SimpleWeatherService::OnCommand(struct ble_gatt_access_ctxt* ctxt) {
103139
currentWeather->maxTemperature.PreciseCelsius(),
104140
currentWeather->iconId,
105141
currentWeather->location.data());
142+
if (GetVersion(dataBuffer) == 1) {
143+
NRF_LOG_INFO("Sunrise: %d\n\tSunset: %d", currentWeather->sunrise, currentWeather->sunset);
144+
}
106145
}
107146
break;
108147
case MessageType::Forecast:
@@ -153,10 +192,36 @@ std::optional<SimpleWeatherService::Forecast> SimpleWeatherService::GetForecast(
153192
return {};
154193
}
155194

195+
bool SimpleWeatherService::IsNight() const {
196+
if (currentWeather && currentWeather->sunrise != -1 && currentWeather->sunset != -1) {
197+
auto currentTime = dateTimeController.CurrentDateTime().time_since_epoch();
198+
199+
// Get timestamp for last midnight
200+
auto midnight = std::chrono::floor<std::chrono::days>(currentTime);
201+
202+
// Calculate minutes since midnight
203+
auto currentMinutes = std::chrono::duration_cast<std::chrono::minutes>(currentTime - midnight).count();
204+
205+
// Sun not rising today => night all hours
206+
if (currentWeather->sunrise == -2) {
207+
return true;
208+
}
209+
// Sun not setting today => check before sunrise
210+
if (currentWeather->sunset == -2) {
211+
return currentMinutes < currentWeather->sunrise;
212+
}
213+
214+
// Before sunrise or after sunset
215+
return currentMinutes < currentWeather->sunrise || currentMinutes >= currentWeather->sunset;
216+
}
217+
218+
return false;
219+
}
220+
156221
bool SimpleWeatherService::CurrentWeather::operator==(const SimpleWeatherService::CurrentWeather& other) const {
157222
return this->iconId == other.iconId && this->temperature == other.temperature && this->timestamp == other.timestamp &&
158223
this->maxTemperature == other.maxTemperature && this->minTemperature == other.maxTemperature &&
159-
std::strcmp(this->location.data(), other.location.data()) == 0;
224+
std::strcmp(this->location.data(), other.location.data()) == 0 && this->sunrise == other.sunrise && this->sunset == other.sunset;
160225
}
161226

162227
bool SimpleWeatherService::Forecast::Day::operator==(const SimpleWeatherService::Forecast::Day& other) const {

src/components/ble/SimpleWeatherService.h

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -113,13 +113,17 @@ namespace Pinetime {
113113
Temperature minTemperature,
114114
Temperature maxTemperature,
115115
Icons iconId,
116-
Location&& location)
116+
Location&& location,
117+
int16_t sunrise,
118+
int16_t sunset)
117119
: timestamp {timestamp},
118120
temperature {temperature},
119121
minTemperature {minTemperature},
120122
maxTemperature {maxTemperature},
121123
iconId {iconId},
122-
location {std::move(location)} {
124+
location {std::move(location)},
125+
sunrise {sunrise},
126+
sunset {sunset} {
123127
}
124128

125129
uint64_t timestamp;
@@ -128,6 +132,8 @@ namespace Pinetime {
128132
Temperature maxTemperature;
129133
Icons iconId;
130134
Location location;
135+
int16_t sunrise;
136+
int16_t sunset;
131137

132138
bool operator==(const CurrentWeather& other) const;
133139
};
@@ -152,6 +158,8 @@ namespace Pinetime {
152158
std::optional<CurrentWeather> Current() const;
153159
std::optional<Forecast> GetForecast() const;
154160

161+
[[nodiscard]] bool IsNight() const;
162+
155163
private:
156164
// 00050000-78fc-48fe-8e23-433b3a1942d0
157165
static constexpr ble_uuid128_t BaseUuid() {

src/displayapp/fonts/fonts.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@
6868
"sources": [
6969
{
7070
"file": "FontAwesome5-Solid+Brands+Regular.woff",
71-
"range": "0xf185, 0xf6c4, 0xf743, 0xf740, 0xf75f, 0xf0c2, 0xf05e, 0xf73b, 0xf0e7, 0xf2dc"
71+
"range": "0xf185, 0xf186, 0xf6c3, 0xf6c4, 0xf73c, 0xf743, 0xf740, 0xf75f, 0xf0c2, 0xf05e, 0xf73b, 0xf0e7, 0xf2dc"
7272
}
7373
],
7474
"bpp": 1,

src/displayapp/screens/Symbols.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,8 +45,11 @@ namespace Pinetime {
4545

4646
// fontawesome_weathericons.c
4747
// static constexpr const char* sun = "\xEF\x86\x85";
48+
static constexpr const char* moon = "\xEF\x86\x86"; // 0xf186
4849
static constexpr const char* cloudSun = "\xEF\x9B\x84";
50+
static constexpr const char* cloudMoon = "\xEF\x9B\x83"; // 0xf6c3
4951
static constexpr const char* cloudSunRain = "\xEF\x9D\x83";
52+
static constexpr const char* cloudMoonRain = "\xEF\x9C\xBC"; // 0xf73c
5053
static constexpr const char* cloudShowersHeavy = "\xEF\x9D\x80";
5154
static constexpr const char* smog = "\xEF\x9D\x9F";
5255
static constexpr const char* cloud = "\xEF\x83\x82";

src/displayapp/screens/WatchFaceDigital.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -183,7 +183,7 @@ void WatchFaceDigital::Refresh() {
183183
tempUnit = 'F';
184184
}
185185
lv_label_set_text_fmt(temperature, "%d°%c", temp, tempUnit);
186-
lv_label_set_text(weatherIcon, Symbols::GetSymbol(optCurrentWeather->iconId));
186+
lv_label_set_text(weatherIcon, Symbols::GetSymbol(optCurrentWeather->iconId, weatherService.IsNight()));
187187
} else {
188188
lv_label_set_text_static(temperature, "");
189189
lv_label_set_text(weatherIcon, "");

src/displayapp/screens/WatchFacePineTimeStyle.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -548,7 +548,7 @@ void WatchFacePineTimeStyle::Refresh() {
548548
temp = optCurrentWeather->temperature.Fahrenheit();
549549
}
550550
lv_label_set_text_fmt(temperature, "%d°", temp);
551-
lv_label_set_text(weatherIcon, Symbols::GetSymbol(optCurrentWeather->iconId));
551+
lv_label_set_text(weatherIcon, Symbols::GetSymbol(optCurrentWeather->iconId, weatherService.IsNight()));
552552
} else {
553553
lv_label_set_text(temperature, "--");
554554
lv_label_set_text(weatherIcon, Symbols::ban);

src/displayapp/screens/Weather.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,7 @@ void Weather::Refresh() {
118118
tempUnit = 'F';
119119
}
120120
lv_obj_set_style_local_text_color(temperature, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, optCurrentWeather->temperature.Color());
121-
lv_label_set_text(icon, Symbols::GetSymbol(optCurrentWeather->iconId));
121+
lv_label_set_text(icon, Symbols::GetSymbol(optCurrentWeather->iconId, weatherService.IsNight()));
122122
lv_label_set_text(condition, Symbols::GetCondition(optCurrentWeather->iconId));
123123
lv_label_set_text_fmt(temperature, "%d°%c", temp, tempUnit);
124124
lv_label_set_text_fmt(minTemperature, "%d°", minTemp);
@@ -154,7 +154,7 @@ void Weather::Refresh() {
154154
}
155155
const char* dayOfWeek = Controllers::DateTime::DayOfWeekShortToStringLow(static_cast<Controllers::DateTime::Days>(wday));
156156
lv_table_set_cell_value(forecast, 0, i, dayOfWeek);
157-
lv_table_set_cell_value(forecast, 1, i, Symbols::GetSymbol(optCurrentForecast->days[i]->iconId));
157+
lv_table_set_cell_value(forecast, 1, i, Symbols::GetSymbol(optCurrentForecast->days[i]->iconId, false));
158158
// Pad cells based on the largest number of digits on each column
159159
char maxPadding[3] = " ";
160160
char minPadding[3] = " ";

src/displayapp/screens/WeatherSymbols.cpp

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,18 @@
11
#include "displayapp/screens/WeatherSymbols.h"
22

3-
const char* Pinetime::Applications::Screens::Symbols::GetSymbol(const Pinetime::Controllers::SimpleWeatherService::Icons icon) {
3+
const char* Pinetime::Applications::Screens::Symbols::GetSymbol(const Pinetime::Controllers::SimpleWeatherService::Icons icon,
4+
const bool isNight) {
45
switch (icon) {
56
case Pinetime::Controllers::SimpleWeatherService::Icons::Sun:
7+
if (isNight) {
8+
return Symbols::moon;
9+
}
610
return Symbols::sun;
711
break;
812
case Pinetime::Controllers::SimpleWeatherService::Icons::CloudsSun:
13+
if (isNight) {
14+
return Symbols::cloudMoon;
15+
}
916
return Symbols::cloudSun;
1017
break;
1118
case Pinetime::Controllers::SimpleWeatherService::Icons::Clouds:
@@ -24,6 +31,9 @@ const char* Pinetime::Applications::Screens::Symbols::GetSymbol(const Pinetime::
2431
return Symbols::cloudShowersHeavy;
2532
break;
2633
case Pinetime::Controllers::SimpleWeatherService::Icons::CloudSunRain:
34+
if (isNight) {
35+
return Symbols::cloudMoonRain;
36+
}
2737
return Symbols::cloudSunRain;
2838
break;
2939
case Pinetime::Controllers::SimpleWeatherService::Icons::Smog:

src/displayapp/screens/WeatherSymbols.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ namespace Pinetime {
66
namespace Applications {
77
namespace Screens {
88
namespace Symbols {
9-
const char* GetSymbol(const Pinetime::Controllers::SimpleWeatherService::Icons icon);
9+
const char* GetSymbol(const Pinetime::Controllers::SimpleWeatherService::Icons icon, const bool isNight);
1010
const char* GetCondition(const Pinetime::Controllers::SimpleWeatherService::Icons icon);
1111
const char* GetSimpleCondition(const Pinetime::Controllers::SimpleWeatherService::Icons icon);
1212
}

0 commit comments

Comments
 (0)