Skip to content

Commit c94a59e

Browse files
committed
SimpleWeather service : new weather implementation
This new implementation of the weather feature provides a new BLE API and a new weather service. The API uses a single characteristic that allows companion apps to write the weather conditions (current and forecast for the next 5 days). The SimpleWeather service exposes those data as std::optional fields. This new implementation replaces the previous WeahterService. The API is documented in docs/SimpleWeatherService.md.
1 parent 088082d commit c94a59e

17 files changed

+406
-1245
lines changed

doc/SimpleWeatherService.md

+68
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
# Simple Weather Service
2+
3+
## Introduction
4+
5+
The Simple Weather Service provide a simple and straightforward API to specify the current weather and the forecast for the next 5 days. It effectively replaces the original Weather Service (from InfiniTime 1.8) since InfiniTime 1.14
6+
7+
## Service
8+
9+
The service UUID is `00050000-78fc-48fe-8e23-433b3a1942d0`.
10+
11+
## Characteristics
12+
13+
## Weather data (UUID 00050001-78fc-48fe-8e23-433b3a1942d0)
14+
15+
The host uses this characteristic to update the current weather information and the forecast for the next 5 days.
16+
17+
This characteristics accepts a byte array with the following 2-Bytes header:
18+
19+
- [0] Message Type :
20+
- `0` : Current weather
21+
- `1` : Forecast
22+
- [1] Message Version : Version `0` is currently supported. Other versions might be added in future releases
23+
24+
### Current Weather
25+
26+
The byte array must contain the following data:
27+
28+
- [0] : Message type = `0`
29+
- [1] : Message version = `0`
30+
- [2][3][4][5][6][7][8][9] : Timestamp (64 bits UNIX timestamp, number of nanoseconds elapsed since 1 JAN 1970)
31+
- [10] : Current temperature (°C)
32+
- [11] : Minimum temperature (°C)
33+
- [12] : Maximum temperature (°C)
34+
- [13]..[44] : location (string, unused characters should be set to `0`)
35+
- [45] : icon ID
36+
- 0 = Sun, clear sky
37+
- 1 = Few clouds
38+
- 2 = Clouds
39+
- 3 = Heavy clouds
40+
- 4 = Clouds & rain
41+
- 5 = Rain
42+
- 6 = Thunderstorm
43+
- 7 = snow
44+
- 8 = mist, smog
45+
46+
### Forecast
47+
48+
The byte array must contain the following data:
49+
50+
- [0] : Message type = `0`
51+
- [0] : Message version = `0`
52+
- [2][3][4][5][6][7][8][9] : Timestamp (64 bits UNIX timestamp, number of nanoseconds elapsed since 1 JAN 1970)
53+
- [10] Number of days (Max 5, fields for unused days should be set to `0`)
54+
- [11] Day 0 Minimum temperature
55+
- [12] Day 0 Maximum temperature
56+
- [13] Day 0 Icon ID
57+
- [14] Day 1 Minimum temperature
58+
- [15] Day 1 Maximum temperature
59+
- [16] Day 1 Icon ID
60+
- [17] Day 2 Minimum temperature
61+
- [18] Day 2 Maximum temperature
62+
- [19] Day 2 Icon ID
63+
- [20] Day 3 Minimum temperature
64+
- [21] Day 3 Maximum temperature
65+
- [22] Day 3 Icon ID
66+
- [23] Day 4 Minimum temperature
67+
- [24] Day 4 Maximum temperature
68+
- [25] Day 4 Incon ID

doc/ble.md

+4-1
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,10 @@ The following custom services are implemented in InfiniTime:
9292

9393
- Since InfiniTime 1.8:
9494

95-
- [Weather Service](/src/components/ble/weather/WeatherService.h): `00040000-78fc-48fe-8e23-433b3a1942d0`
95+
- ~~Weather Service: `00040000-78fc-48fe-8e23-433b3a1942d0`~~ (replaced by Simple Weather Service in InfiniTime 1.14)
96+
97+
- Since InfiniTime 1.14
98+
- [Simple Weather Server](SimpleWeahterService.md) : `00050000-78fc-48fe-8e23-433b3a1942d0`
9699

97100
---
98101

src/CMakeLists.txt

+9-39
Original file line numberDiff line numberDiff line change
@@ -355,14 +355,6 @@ set(LVGL_SRC
355355
libs/lvgl/src/lv_widgets/lv_win.c
356356
)
357357

358-
set(QCBOR_SRC
359-
libs/QCBOR/src/ieee754.c
360-
libs/QCBOR/src/qcbor_decode.c
361-
libs/QCBOR/src/qcbor_encode.c
362-
libs/QCBOR/src/qcbor_err_to_str.c
363-
libs/QCBOR/src/UsefulBuf.c
364-
)
365-
366358
list(APPEND IMAGE_FILES
367359
displayapp/icons/battery/batteryicon.c
368360
)
@@ -384,7 +376,6 @@ list(APPEND SOURCE_FILES
384376
displayapp/screens/Label.cpp
385377
displayapp/screens/FirmwareUpdate.cpp
386378
displayapp/screens/Music.cpp
387-
displayapp/screens/Weather.cpp
388379
displayapp/screens/Navigation.cpp
389380
displayapp/screens/Metronome.cpp
390381
displayapp/screens/Motion.cpp
@@ -459,7 +450,7 @@ list(APPEND SOURCE_FILES
459450
components/ble/CurrentTimeService.cpp
460451
components/ble/AlertNotificationService.cpp
461452
components/ble/MusicService.cpp
462-
components/ble/weather/WeatherService.cpp
453+
components/ble/SimpleWeatherService.cpp
463454
components/ble/NavigationService.cpp
464455
components/ble/BatteryInformationService.cpp
465456
components/ble/FSService.cpp
@@ -528,7 +519,7 @@ list(APPEND RECOVERY_SOURCE_FILES
528519
components/ble/CurrentTimeService.cpp
529520
components/ble/AlertNotificationService.cpp
530521
components/ble/MusicService.cpp
531-
components/ble/weather/WeatherService.cpp
522+
components/ble/SimpleWeatherService.cpp
532523
components/ble/BatteryInformationService.cpp
533524
components/ble/FSService.cpp
534525
components/ble/ImmediateAlertService.cpp
@@ -655,7 +646,7 @@ set(INCLUDE_FILES
655646
components/ble/BleClient.h
656647
components/ble/HeartRateService.h
657648
components/ble/MotionService.h
658-
components/ble/weather/WeatherService.h
649+
components/ble/SimpleWeatherService.h
659650
components/settings/Settings.h
660651
components/timer/Timer.h
661652
components/alarm/AlarmController.h
@@ -891,27 +882,6 @@ target_compile_options(lvgl PRIVATE
891882
$<$<COMPILE_LANGUAGE:ASM>: ${ASM_FLAGS}>
892883
)
893884

894-
# QCBOR
895-
add_library(QCBOR STATIC ${QCBOR_SRC})
896-
target_include_directories(QCBOR SYSTEM PUBLIC libs/QCBOR/inc)
897-
# This is required with the current configuration
898-
target_compile_definitions(QCBOR PUBLIC QCBOR_DISABLE_FLOAT_HW_USE)
899-
# These are for space-saving
900-
target_compile_definitions(QCBOR PUBLIC QCBOR_DISABLE_PREFERRED_FLOAT)
901-
target_compile_definitions(QCBOR PUBLIC QCBOR_DISABLE_EXP_AND_MANTISSA)
902-
target_compile_definitions(QCBOR PUBLIC QCBOR_DISABLE_INDEFINITE_LENGTH_STRINGS)
903-
#target_compile_definitions(QCBOR PUBLIC QCBOR_DISABLE_INDEFINITE_LENGTH_ARRAYS)
904-
target_compile_definitions(QCBOR PUBLIC QCBOR_DISABLE_UNCOMMON_TAGS)
905-
target_compile_definitions(QCBOR PUBLIC USEFULBUF_CONFIG_LITTLE_ENDIAN)
906-
set_target_properties(QCBOR PROPERTIES LINKER_LANGUAGE C)
907-
target_compile_options(QCBOR PRIVATE
908-
${COMMON_FLAGS}
909-
$<$<CONFIG:DEBUG>: ${DEBUG_FLAGS}>
910-
$<$<CONFIG:RELEASE>: ${RELEASE_FLAGS}>
911-
$<$<COMPILE_LANGUAGE:ASM>: ${ASM_FLAGS}>
912-
-O3
913-
)
914-
915885
# LITTLEFS_SRC
916886
add_library(littlefs STATIC ${LITTLEFS_SRC})
917887
target_include_directories(littlefs SYSTEM PUBLIC . ../)
@@ -930,7 +900,7 @@ set(EXECUTABLE_FILE_NAME ${EXECUTABLE_NAME}-${pinetime_VERSION_MAJOR}.${pinetime
930900
set(NRF5_LINKER_SCRIPT "${CMAKE_SOURCE_DIR}/gcc_nrf52.ld")
931901
add_executable(${EXECUTABLE_NAME} ${SOURCE_FILES})
932902
set_target_properties(${EXECUTABLE_NAME} PROPERTIES OUTPUT_NAME ${EXECUTABLE_FILE_NAME})
933-
target_link_libraries(${EXECUTABLE_NAME} nimble nrf-sdk lvgl littlefs QCBOR infinitime_fonts)
903+
target_link_libraries(${EXECUTABLE_NAME} nimble nrf-sdk lvgl littlefs infinitime_fonts)
934904
target_compile_options(${EXECUTABLE_NAME} PUBLIC
935905
${COMMON_FLAGS}
936906
${WARNING_FLAGS}
@@ -964,7 +934,7 @@ set(IMAGE_MCUBOOT_FILE_NAME_BIN ${EXECUTABLE_MCUBOOT_NAME}-image-${pinetime_VERS
964934
set(DFU_MCUBOOT_FILE_NAME ${EXECUTABLE_MCUBOOT_NAME}-dfu-${pinetime_VERSION_MAJOR}.${pinetime_VERSION_MINOR}.${pinetime_VERSION_PATCH}.zip)
965935
set(NRF5_LINKER_SCRIPT_MCUBOOT "${CMAKE_SOURCE_DIR}/gcc_nrf52-mcuboot.ld")
966936
add_executable(${EXECUTABLE_MCUBOOT_NAME} ${SOURCE_FILES})
967-
target_link_libraries(${EXECUTABLE_MCUBOOT_NAME} nimble nrf-sdk lvgl littlefs QCBOR infinitime_fonts)
937+
target_link_libraries(${EXECUTABLE_MCUBOOT_NAME} nimble nrf-sdk lvgl littlefs infinitime_fonts)
968938
set_target_properties(${EXECUTABLE_MCUBOOT_NAME} PROPERTIES OUTPUT_NAME ${EXECUTABLE_MCUBOOT_FILE_NAME})
969939
target_compile_options(${EXECUTABLE_MCUBOOT_NAME} PUBLIC
970940
${COMMON_FLAGS}
@@ -1006,7 +976,7 @@ endif()
1006976
set(EXECUTABLE_RECOVERY_NAME "pinetime-recovery")
1007977
set(EXECUTABLE_RECOVERY_FILE_NAME ${EXECUTABLE_RECOVERY_NAME}-${pinetime_VERSION_MAJOR}.${pinetime_VERSION_MINOR}.${pinetime_VERSION_PATCH})
1008978
add_executable(${EXECUTABLE_RECOVERY_NAME} ${RECOVERY_SOURCE_FILES})
1009-
target_link_libraries(${EXECUTABLE_RECOVERY_NAME} nimble nrf-sdk littlefs QCBOR infinitime_fonts)
979+
target_link_libraries(${EXECUTABLE_RECOVERY_NAME} nimble nrf-sdk littlefs infinitime_fonts)
1010980
set_target_properties(${EXECUTABLE_RECOVERY_NAME} PROPERTIES OUTPUT_NAME ${EXECUTABLE_RECOVERY_FILE_NAME})
1011981
target_compile_definitions(${EXECUTABLE_RECOVERY_NAME} PUBLIC "PINETIME_IS_RECOVERY")
1012982
target_compile_options(${EXECUTABLE_RECOVERY_NAME} PUBLIC
@@ -1038,7 +1008,7 @@ set(IMAGE_RECOVERY_MCUBOOT_FILE_NAME ${EXECUTABLE_RECOVERY_MCUBOOT_NAME}-image-$
10381008
set(IMAGE_RECOVERY_MCUBOOT_FILE_NAME_HEX ${IMAGE_RECOVERY_MCUBOOT_FILE_NAME}.hex)
10391009
set(DFU_RECOVERY_MCUBOOT_FILE_NAME ${EXECUTABLE_RECOVERY_MCUBOOT_NAME}-dfu-${pinetime_VERSION_MAJOR}.${pinetime_VERSION_MINOR}.${pinetime_VERSION_PATCH}.zip)
10401010
add_executable(${EXECUTABLE_RECOVERY_MCUBOOT_NAME} ${RECOVERY_SOURCE_FILES})
1041-
target_link_libraries(${EXECUTABLE_RECOVERY_MCUBOOT_NAME} nimble nrf-sdk littlefs QCBOR infinitime_fonts)
1011+
target_link_libraries(${EXECUTABLE_RECOVERY_MCUBOOT_NAME} nimble nrf-sdk littlefs infinitime_fonts)
10421012
set_target_properties(${EXECUTABLE_RECOVERY_MCUBOOT_NAME} PROPERTIES OUTPUT_NAME ${EXECUTABLE_RECOVERY_MCUBOOT_FILE_NAME})
10431013
target_compile_definitions(${EXECUTABLE_RECOVERY_MCUBOOT_NAME} PUBLIC "PINETIME_IS_RECOVERY")
10441014
target_compile_options(${EXECUTABLE_RECOVERY_MCUBOOT_NAME} PUBLIC
@@ -1078,7 +1048,7 @@ endif()
10781048
set(EXECUTABLE_RECOVERYLOADER_NAME "pinetime-recovery-loader")
10791049
set(EXECUTABLE_RECOVERYLOADER_FILE_NAME ${EXECUTABLE_RECOVERYLOADER_NAME}-${pinetime_VERSION_MAJOR}.${pinetime_VERSION_MINOR}.${pinetime_VERSION_PATCH})
10801050
add_executable(${EXECUTABLE_RECOVERYLOADER_NAME} ${RECOVERYLOADER_SOURCE_FILES})
1081-
target_link_libraries(${EXECUTABLE_RECOVERYLOADER_NAME} nrf-sdk QCBOR infinitime_fonts)
1051+
target_link_libraries(${EXECUTABLE_RECOVERYLOADER_NAME} nrf-sdk infinitime_fonts)
10821052
set_target_properties(${EXECUTABLE_RECOVERYLOADER_NAME} PROPERTIES OUTPUT_NAME ${EXECUTABLE_RECOVERYLOADER_FILE_NAME})
10831053
target_compile_options(${EXECUTABLE_RECOVERYLOADER_NAME} PUBLIC
10841054
${COMMON_FLAGS}
@@ -1113,7 +1083,7 @@ set(IMAGE_MCUBOOT_RECOVERYLOADER_FILE_NAME ${EXECUTABLE_MCUBOOT_RECOVERYLOADER_N
11131083
set(IMAGE_MCUBOOT_RECOVERYLOADER_FILE_NAME_HEX ${IMAGE_MCUBOOT_RECOVERYLOADER_FILE_NAME}.hex)
11141084
set(DFU_MCUBOOT_RECOVERYLOADER_FILE_NAME ${EXECUTABLE_MCUBOOT_RECOVERYLOADER_NAME}-dfu-${pinetime_VERSION_MAJOR}.${pinetime_VERSION_MINOR}.${pinetime_VERSION_PATCH}.zip)
11151085
add_executable(${EXECUTABLE_MCUBOOT_RECOVERYLOADER_NAME} ${RECOVERYLOADER_SOURCE_FILES})
1116-
target_link_libraries(${EXECUTABLE_MCUBOOT_RECOVERYLOADER_NAME} nrf-sdk QCBOR infinitime_fonts)
1086+
target_link_libraries(${EXECUTABLE_MCUBOOT_RECOVERYLOADER_NAME} nrf-sdk infinitime_fonts)
11171087
set_target_properties(${EXECUTABLE_MCUBOOT_RECOVERYLOADER_NAME} PROPERTIES OUTPUT_NAME ${EXECUTABLE_MCUBOOT_RECOVERYLOADER_FILE_NAME})
11181088
target_compile_options(${EXECUTABLE_MCUBOOT_RECOVERYLOADER_NAME} PUBLIC
11191089
${COMMON_FLAGS}

src/components/ble/NimbleController.h

+3-3
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
#include "components/ble/NavigationService.h"
2222
#include "components/ble/ServiceDiscovery.h"
2323
#include "components/ble/MotionService.h"
24-
#include "components/ble/weather/WeatherService.h"
24+
#include "components/ble/SimpleWeatherService.h"
2525
#include "components/fs/FS.h"
2626

2727
namespace Pinetime {
@@ -67,7 +67,7 @@ namespace Pinetime {
6767
return anService;
6868
};
6969

70-
Pinetime::Controllers::WeatherService& weather() {
70+
Pinetime::Controllers::SimpleWeatherService& weather() {
7171
return weatherService;
7272
};
7373

@@ -99,7 +99,7 @@ namespace Pinetime {
9999
AlertNotificationClient alertNotificationClient;
100100
CurrentTimeService currentTimeService;
101101
MusicService musicService;
102-
WeatherService weatherService;
102+
SimpleWeatherService weatherService;
103103
NavigationService navService;
104104
BatteryInformationService batteryInformationService;
105105
ImmediateAlertService immediateAlertService;
+153
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
1+
/* Copyright (C) 2023 Jean-François Milants
2+
3+
This file is part of InfiniTime.
4+
5+
InfiniTime is free software: you can redistribute it and/or modify
6+
it under the terms of the GNU General Public License as published
7+
by the Free Software Foundation, either version 3 of the License, or
8+
(at your option) any later version.
9+
10+
InfiniTime is distributed in the hope that it will be useful,
11+
but WITHOUT ANY WARRANTY; without even the implied warranty of
12+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13+
GNU General Public License for more details.
14+
15+
You should have received a copy of the GNU General Public License
16+
along with this program. If not, see <https://www.gnu.org/licenses/>.
17+
*/
18+
#include <algorithm>
19+
#include "SimpleWeatherService.h"
20+
#include <cstring>
21+
#include <nrf_log.h>
22+
using namespace Pinetime::Controllers;
23+
24+
namespace {
25+
enum class MessageType {
26+
CurrentWeather,
27+
Forecast,
28+
Unknown
29+
};
30+
31+
SimpleWeatherService::CurrentWeather CreateCurrentWeather(const uint8_t* dataBuffer) {
32+
char cityName[33];
33+
std::memcpy(&cityName[0], &dataBuffer[13], 32);
34+
cityName[32] = '\0';
35+
return SimpleWeatherService::CurrentWeather{dataBuffer[2] + (dataBuffer[3] << 8) + (dataBuffer[4] << 16) + (dataBuffer[5] << 24) +
36+
((uint64_t) dataBuffer[6] << 32) + ((uint64_t) dataBuffer[7] << 40) + ((uint64_t) dataBuffer[8] << 48) +
37+
((uint64_t) dataBuffer[9] << 54),
38+
dataBuffer[10],
39+
dataBuffer[11],
40+
dataBuffer[12],
41+
dataBuffer[13 + 32],
42+
cityName};
43+
}
44+
45+
SimpleWeatherService::Forecast CreateForecast(const uint8_t* dataBuffer) {
46+
uint64_t timestamp = static_cast<uint64_t>(dataBuffer[2] + (dataBuffer[3] << 8) + (dataBuffer[4] << 16) + (dataBuffer[5] << 24) +
47+
((uint64_t) dataBuffer[6] << 32) + ((uint64_t) dataBuffer[7] << 40) + ((uint64_t) dataBuffer[8] << 48) +
48+
((uint64_t) dataBuffer[9] << 54));
49+
uint8_t nbDays = dataBuffer[10];
50+
std::array<SimpleWeatherService::Forecast::Day, 5> days;
51+
for (int i = 0; i < nbDays; i++) {
52+
days[i] = SimpleWeatherService::Forecast::Day {dataBuffer[11 + (i * 3)],
53+
dataBuffer[12 + (i * 3)],
54+
dataBuffer[13 + (i * 3)]};
55+
}
56+
return SimpleWeatherService::Forecast {timestamp, nbDays, days};
57+
}
58+
59+
MessageType GetMessageType(const uint8_t* dataBuffer) {
60+
switch(dataBuffer[0]) {
61+
case 0: return MessageType::CurrentWeather; break;
62+
case 1: return MessageType::Forecast; break;
63+
default: return MessageType::Unknown; break;
64+
}
65+
}
66+
67+
uint8_t GetVersion(const uint8_t* dataBuffer) {
68+
return dataBuffer[1];
69+
}
70+
}
71+
72+
int WeatherCallback(uint16_t /*connHandle*/, uint16_t /*attrHandle*/, struct ble_gatt_access_ctxt* ctxt, void* arg) {
73+
return static_cast<Pinetime::Controllers::SimpleWeatherService*>(arg)->OnCommand(ctxt);
74+
}
75+
76+
SimpleWeatherService::SimpleWeatherService(const DateTime& dateTimeController) : dateTimeController(dateTimeController) {
77+
78+
}
79+
80+
void SimpleWeatherService::Init() {
81+
ble_gatts_count_cfg(serviceDefinition);
82+
ble_gatts_add_svcs(serviceDefinition);
83+
}
84+
85+
int SimpleWeatherService::OnCommand(struct ble_gatt_access_ctxt* ctxt) {
86+
const auto* buffer = ctxt->om;
87+
const auto* dataBuffer = buffer->om_data;
88+
89+
switch(GetMessageType(dataBuffer)) {
90+
case MessageType::CurrentWeather:
91+
if(GetVersion(dataBuffer) == 0) {
92+
currentWeather = CreateCurrentWeather(dataBuffer);
93+
NRF_LOG_INFO("Current weather :\n\tTimestamp : %d\n\tTemperature:%d\n\tMin:%d\n\tMax:%d\n\tIcon:%d\n\tLocation:%s",
94+
currentWeather->timestamp,
95+
currentWeather->temperature,
96+
currentWeather->minTemperature,
97+
currentWeather->maxTemperature,
98+
currentWeather->iconId,
99+
currentWeather->location);
100+
}
101+
break;
102+
case MessageType::Forecast:
103+
if(GetVersion(dataBuffer) == 0) {
104+
forecast = CreateForecast(dataBuffer);
105+
NRF_LOG_INFO("Forecast : Timestamp : %d", forecast->timestamp);
106+
for(int i = 0; i < 5; i++) {
107+
NRF_LOG_INFO("\t[%d] Min: %d - Max : %d - Icon : %d", i, forecast->days[i].minTemperature, forecast->days[i].maxTemperature, forecast->days[i].iconId);
108+
}
109+
}
110+
break;
111+
default:
112+
break;
113+
}
114+
115+
return 0;
116+
}
117+
118+
std::optional<SimpleWeatherService::CurrentWeather> SimpleWeatherService::Current() const {
119+
if(currentWeather) {
120+
auto currentTime = dateTimeController.UTCDateTime().time_since_epoch();
121+
auto weatherTpSecond = std::chrono::seconds{currentWeather->timestamp};
122+
auto weatherTp = std::chrono::duration_cast<std::chrono::seconds>(weatherTpSecond);
123+
auto delta = currentTime - weatherTp;
124+
125+
if(delta < std::chrono::hours{24}) {
126+
return currentWeather;
127+
}
128+
}
129+
return {};
130+
}
131+
132+
std::optional<SimpleWeatherService::Forecast> SimpleWeatherService::GetForecast() const {
133+
if(forecast) {
134+
auto currentTime = dateTimeController.UTCDateTime().time_since_epoch();
135+
auto weatherTpSecond = std::chrono::seconds{forecast->timestamp};
136+
auto weatherTp = std::chrono::duration_cast<std::chrono::seconds>(weatherTpSecond);
137+
auto delta = currentTime - weatherTp;
138+
139+
if(delta < std::chrono::hours{24}) {
140+
return this->forecast;
141+
}
142+
}
143+
return {};
144+
}
145+
146+
bool SimpleWeatherService::CurrentWeather::operator==(const SimpleWeatherService::CurrentWeather& other) const {
147+
return this->iconId == other.iconId && this->temperature == other.temperature && this->timestamp == other.timestamp &&
148+
this->maxTemperature == other.maxTemperature && this->minTemperature == other.maxTemperature;
149+
}
150+
151+
bool SimpleWeatherService::CurrentWeather::operator!=(const SimpleWeatherService::CurrentWeather& other) const {
152+
return !operator==(other);
153+
}

0 commit comments

Comments
 (0)