Initial Weather service skeleton
This commit is contained in:
parent
6ba2878605
commit
bda96dc595
3
.gitmodules
vendored
3
.gitmodules
vendored
|
@ -4,3 +4,6 @@
|
||||||
[submodule "src/libs/littlefs"]
|
[submodule "src/libs/littlefs"]
|
||||||
path = src/libs/littlefs
|
path = src/libs/littlefs
|
||||||
url = https://github.com/littlefs-project/littlefs.git
|
url = https://github.com/littlefs-project/littlefs.git
|
||||||
|
[submodule "src/libs/QCBOR"]
|
||||||
|
path = src/libs/QCBOR
|
||||||
|
url = https://github.com/laurencelundblade/QCBOR.git
|
||||||
|
|
|
@ -36,6 +36,7 @@ NimbleController::NimbleController(Pinetime::System::SystemTask& systemTask,
|
||||||
alertNotificationClient {systemTask, notificationManager},
|
alertNotificationClient {systemTask, notificationManager},
|
||||||
currentTimeService {dateTimeController},
|
currentTimeService {dateTimeController},
|
||||||
musicService {systemTask},
|
musicService {systemTask},
|
||||||
|
weatherService {systemTask, dateTimeController},
|
||||||
navService {systemTask},
|
navService {systemTask},
|
||||||
batteryInformationService {batteryController},
|
batteryInformationService {batteryController},
|
||||||
immediateAlertService {systemTask, notificationManager},
|
immediateAlertService {systemTask, notificationManager},
|
||||||
|
@ -77,6 +78,7 @@ void NimbleController::Init() {
|
||||||
currentTimeClient.Init();
|
currentTimeClient.Init();
|
||||||
currentTimeService.Init();
|
currentTimeService.Init();
|
||||||
musicService.Init();
|
musicService.Init();
|
||||||
|
weatherService.Init();
|
||||||
navService.Init();
|
navService.Init();
|
||||||
anService.Init();
|
anService.Init();
|
||||||
dfuService.Init();
|
dfuService.Init();
|
||||||
|
|
|
@ -20,6 +20,7 @@
|
||||||
#include "components/ble/ServiceDiscovery.h"
|
#include "components/ble/ServiceDiscovery.h"
|
||||||
#include "components/ble/HeartRateService.h"
|
#include "components/ble/HeartRateService.h"
|
||||||
#include "components/ble/MotionService.h"
|
#include "components/ble/MotionService.h"
|
||||||
|
#include "components/ble/weather/WeatherService.h"
|
||||||
|
|
||||||
namespace Pinetime {
|
namespace Pinetime {
|
||||||
namespace Drivers {
|
namespace Drivers {
|
||||||
|
@ -93,6 +94,7 @@ namespace Pinetime {
|
||||||
AlertNotificationClient alertNotificationClient;
|
AlertNotificationClient alertNotificationClient;
|
||||||
CurrentTimeService currentTimeService;
|
CurrentTimeService currentTimeService;
|
||||||
MusicService musicService;
|
MusicService musicService;
|
||||||
|
WeatherService weatherService;
|
||||||
NavigationService navService;
|
NavigationService navService;
|
||||||
BatteryInformationService batteryInformationService;
|
BatteryInformationService batteryInformationService;
|
||||||
ImmediateAlertService immediateAlertService;
|
ImmediateAlertService immediateAlertService;
|
||||||
|
|
338
src/components/ble/weather/WeatherData.h
Normal file
338
src/components/ble/weather/WeatherData.h
Normal file
|
@ -0,0 +1,338 @@
|
||||||
|
/* 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}
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* 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,
|
||||||
|
/** Extremely small, 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,
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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,
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Events have types
|
||||||
|
* then they're easier to parse after sending them over the air
|
||||||
|
*/
|
||||||
|
enum class eventtype {
|
||||||
|
/** @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,
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Valid event query
|
||||||
|
*/
|
||||||
|
class valideventquery {
|
||||||
|
public:
|
||||||
|
static constexpr controlcodes code = controlcodes::HasValidEvent;
|
||||||
|
eventtype eventType;
|
||||||
|
};
|
||||||
|
|
||||||
|
/** The header used for further parsing */
|
||||||
|
class timelineheader {
|
||||||
|
public:
|
||||||
|
/** UNIX timestamp */
|
||||||
|
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 */
|
||||||
|
obscurationtype type;
|
||||||
|
/** Visibility distance in meters */
|
||||||
|
uint8_t amount;
|
||||||
|
};
|
||||||
|
|
||||||
|
/** Specifies how precipitation is stored */
|
||||||
|
class precipitation : public timelineheader {
|
||||||
|
public:
|
||||||
|
/** Type */
|
||||||
|
precipitationtype type;
|
||||||
|
/** How much is it going to rain? In millimeters */
|
||||||
|
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 watchfaces 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, microdegrees 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) */
|
||||||
|
int16_t temperature;
|
||||||
|
/** Dewpoint °C but multiplied by 100 (e.g. -12.50°C becomes -1250) */
|
||||||
|
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 watchface
|
||||||
|
* 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 watchface 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;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
208
src/components/ble/weather/WeatherService.cpp
Normal file
208
src/components/ble/weather/WeatherService.cpp
Normal file
|
@ -0,0 +1,208 @@
|
||||||
|
/* 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 <qcbor/qcbor_spiffy_decode.h>
|
||||||
|
#include "WeatherService.h"
|
||||||
|
#include "libs/QCBOR/inc/qcbor/qcbor.h"
|
||||||
|
#include "systemtask/SystemTask.h"
|
||||||
|
|
||||||
|
int WeatherCallback(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt* ctxt, void* arg) {
|
||||||
|
return static_cast<Pinetime::Controllers::WeatherService*>(arg)->OnCommand(conn_handle, attr_handle, ctxt);
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace Pinetime {
|
||||||
|
namespace Controllers {
|
||||||
|
WeatherService::WeatherService(System::SystemTask& system, DateTime& dateTimeController)
|
||||||
|
: system(system), dateTimeController(dateTimeController) {
|
||||||
|
}
|
||||||
|
|
||||||
|
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(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt* ctxt) {
|
||||||
|
if (ctxt->op == BLE_GATT_ACCESS_OP_WRITE_CHR) {
|
||||||
|
getCurrentPressure();
|
||||||
|
tidyTimeline();
|
||||||
|
getTimelineLength();
|
||||||
|
const auto packetLen = OS_MBUF_PKTLEN(ctxt->om);
|
||||||
|
if (packetLen <= 0) {
|
||||||
|
return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN;
|
||||||
|
}
|
||||||
|
// Decode
|
||||||
|
QCBORDecodeContext decodeContext;
|
||||||
|
UsefulBufC EncodedCBOR;
|
||||||
|
// TODO: Check uninit fine
|
||||||
|
QCBORDecode_Init(&decodeContext, EncodedCBOR, QCBOR_DECODE_MODE_NORMAL);
|
||||||
|
QCBORDecode_EnterMap(&decodeContext, nullptr);
|
||||||
|
WeatherData::timelineheader timelineHeader {};
|
||||||
|
// Always encodes to the smallest number of bytes based on the value
|
||||||
|
QCBORDecode_GetInt64InMapSZ(&decodeContext, "Timestamp", reinterpret_cast<int64_t*>(&(timelineHeader.timestamp)));
|
||||||
|
QCBORDecode_GetInt64InMapSZ(&decodeContext, "Expires", reinterpret_cast<int64_t*>(&(timelineHeader.expires)));
|
||||||
|
QCBORDecode_GetInt64InMapSZ(&decodeContext, "EventType", reinterpret_cast<int64_t*>(&(timelineHeader.eventType)));
|
||||||
|
switch (timelineHeader.eventType) {
|
||||||
|
// TODO: Populate
|
||||||
|
case WeatherData::eventtype::AirQuality: {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case WeatherData::eventtype::Obscuration: {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case WeatherData::eventtype::Precipitation: {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case WeatherData::eventtype::Wind: {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case WeatherData::eventtype::Temperature: {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case WeatherData::eventtype::Special: {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case WeatherData::eventtype::Pressure: {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case WeatherData::eventtype::Location: {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case WeatherData::eventtype::Clouds: {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
QCBORDecode_ExitMap(&decodeContext);
|
||||||
|
|
||||||
|
auto uErr = QCBORDecode_Finish(&decodeContext);
|
||||||
|
if (uErr != 0) {
|
||||||
|
return BLE_ATT_ERR_INSUFFICIENT_RES;
|
||||||
|
}
|
||||||
|
} else if (ctxt->op == BLE_GATT_ACCESS_OP_READ_CHR) {
|
||||||
|
// TODO: Detect control messages
|
||||||
|
|
||||||
|
// Encode
|
||||||
|
uint8_t buffer[64];
|
||||||
|
QCBOREncodeContext encodeContext;
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
WeatherData::location WeatherService::getCurrentLocation() const {
|
||||||
|
return WeatherData::location();
|
||||||
|
}
|
||||||
|
WeatherData::clouds WeatherService::getCurrentClouds() const {
|
||||||
|
return WeatherData::clouds();
|
||||||
|
}
|
||||||
|
WeatherData::obscuration WeatherService::getCurrentObscuration() const {
|
||||||
|
return WeatherData::obscuration();
|
||||||
|
}
|
||||||
|
WeatherData::precipitation WeatherService::getCurrentPrecipitation() const {
|
||||||
|
return WeatherData::precipitation();
|
||||||
|
}
|
||||||
|
WeatherData::wind WeatherService::getCurrentWind() const {
|
||||||
|
return WeatherData::wind();
|
||||||
|
}
|
||||||
|
WeatherData::temperature WeatherService::getCurrentTemperature() const {
|
||||||
|
return WeatherData::temperature();
|
||||||
|
}
|
||||||
|
WeatherData::humidity WeatherService::getCurrentHumidity() const {
|
||||||
|
return WeatherData::humidity();
|
||||||
|
}
|
||||||
|
WeatherData::pressure WeatherService::getCurrentPressure() const {
|
||||||
|
uint64_t currentTimestamp = getCurrentUNIXTimestamp();
|
||||||
|
for (auto&& header : timeline) {
|
||||||
|
if (header->eventType == WeatherData::eventtype::Pressure && header->timestamp + header->expires <= currentTimestamp) {
|
||||||
|
return WeatherData::pressure();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return WeatherData::pressure();
|
||||||
|
}
|
||||||
|
|
||||||
|
WeatherData::airquality WeatherService::getCurrentQuality() const {
|
||||||
|
return WeatherData::airquality();
|
||||||
|
}
|
||||||
|
|
||||||
|
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 && header->timestamp + header->expires <= currentTimestamp) {
|
||||||
|
// TODO: Check if its currently valid
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void WeatherService::tidyTimeline() {
|
||||||
|
uint64_t timeCurrent = 0;
|
||||||
|
timeline.erase(std::remove_if(std::begin(timeline),
|
||||||
|
std::end(timeline),
|
||||||
|
[&](std::unique_ptr<WeatherData::timelineheader> const& header) {
|
||||||
|
return header->timestamp + header->expires > 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t WeatherService::getCurrentUNIXTimestamp() const {
|
||||||
|
return std::chrono::duration_cast<std::chrono::seconds>(dateTimeController.CurrentDateTime().time_since_epoch()).count();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
139
src/components/ble/weather/WeatherService.h
Normal file
139
src/components/ble/weather/WeatherService.h
Normal file
|
@ -0,0 +1,139 @@
|
||||||
|
/* 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 <components/datetime/DateTimeController.h>
|
||||||
|
|
||||||
|
// 00030000-78fc-48fe-8e23-433b3a1942d0
|
||||||
|
#define WEATHER_SERVICE_UUID_BASE \
|
||||||
|
{ 0xd0, 0x42, 0x19, 0x3a, 0x3b, 0x43, 0x23, 0x8e, 0xfe, 0x48, 0xfc, 0x78, 0x00, 0x00, 0x03, 0x00 }
|
||||||
|
#define WEATHER_SERVICE_CHAR_UUID(y, x) \
|
||||||
|
{ 0xd0, 0x42, 0x19, 0x3a, 0x3b, 0x43, 0x23, 0x8e, 0xfe, 0x48, 0xfc, 0x78, (x), (y), 0x03, 0x00 }
|
||||||
|
|
||||||
|
int WeatherCallback(uint16_t conn_handle, uint16_t attr_handle, 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(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt* ctxt);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Helper functions for quick access to currently valid data
|
||||||
|
*/
|
||||||
|
WeatherData::location getCurrentLocation() const;
|
||||||
|
WeatherData::clouds getCurrentClouds() const;
|
||||||
|
WeatherData::obscuration getCurrentObscuration() const;
|
||||||
|
WeatherData::precipitation getCurrentPrecipitation() const;
|
||||||
|
WeatherData::wind getCurrentWind() const;
|
||||||
|
WeatherData::temperature getCurrentTemperature() const;
|
||||||
|
WeatherData::humidity getCurrentHumidity() const;
|
||||||
|
WeatherData::pressure getCurrentPressure() const;
|
||||||
|
WeatherData::airquality getCurrentQuality() 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
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
bool hasTimelineEventOfType(WeatherData::eventtype type) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
ble_uuid128_t msUuid {.u = {.type = BLE_UUID_TYPE_128}, .value = WEATHER_SERVICE_UUID_BASE};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Just write timeline data here
|
||||||
|
*/
|
||||||
|
ble_uuid128_t wDataCharUuid {.u = {.type = BLE_UUID_TYPE_128}, .value = WEATHER_SERVICE_CHAR_UUID(0x00, 0x01)};
|
||||||
|
/**
|
||||||
|
* This doesn't take timeline data
|
||||||
|
* but provides some control over it
|
||||||
|
*/
|
||||||
|
ble_uuid128_t wControlCharUuid {.u = {.type = BLE_UUID_TYPE_128}, .value = WEATHER_SERVICE_CHAR_UUID(0x00, 0x02)};
|
||||||
|
|
||||||
|
const struct ble_gatt_chr_def characteristicDefinition[2] = {{.uuid = reinterpret_cast<ble_uuid_t*>(&wDataCharUuid),
|
||||||
|
.access_cb = WeatherCallback,
|
||||||
|
.arg = this,
|
||||||
|
.flags = BLE_GATT_CHR_F_NOTIFY,
|
||||||
|
.val_handle = &eventHandle},
|
||||||
|
{.uuid = reinterpret_cast<ble_uuid_t*>(&wControlCharUuid),
|
||||||
|
.access_cb = WeatherCallback,
|
||||||
|
.arg = this,
|
||||||
|
.flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_READ}};
|
||||||
|
const struct ble_gatt_svc_def serviceDefinition[2] = {
|
||||||
|
{.type = BLE_GATT_SVC_TYPE_PRIMARY, .uuid = reinterpret_cast<ble_uuid_t*>(&msUuid), .characteristics = characteristicDefinition},
|
||||||
|
{0}};
|
||||||
|
|
||||||
|
uint16_t eventHandle {};
|
||||||
|
|
||||||
|
Pinetime::System::SystemTask& system;
|
||||||
|
Pinetime::Controllers::DateTime& dateTimeController;
|
||||||
|
|
||||||
|
std::vector<std::unique_ptr<WeatherData::timelineheader>> timeline;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cleans up the timeline of expired events
|
||||||
|
* @return result code
|
||||||
|
*/
|
||||||
|
void tidyTimeline();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compares two timeline events
|
||||||
|
*/
|
||||||
|
static bool compareTimelineEvents(const std::unique_ptr<WeatherData::timelineheader>& first,
|
||||||
|
const std::unique_ptr<WeatherData::timelineheader>& second);
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
uint64_t getCurrentUNIXTimestamp() const;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
246
src/displayapp/screens/Weather.cpp
Normal file
246
src/displayapp/screens/Weather.cpp
Normal file
|
@ -0,0 +1,246 @@
|
||||||
|
#include "Weather.h"
|
||||||
|
#include <lvgl/lvgl.h>
|
||||||
|
#include "../DisplayApp.h"
|
||||||
|
#include "Label.h"
|
||||||
|
#include "Version.h"
|
||||||
|
#include "components/battery/BatteryController.h"
|
||||||
|
#include "components/ble/BleController.h"
|
||||||
|
#include "components/brightness/BrightnessController.h"
|
||||||
|
#include "components/datetime/DateTimeController.h"
|
||||||
|
#include "drivers/Watchdog.h"
|
||||||
|
#include "components/ble/weather/WeatherData.h"
|
||||||
|
|
||||||
|
using namespace Pinetime::Applications::Screens;
|
||||||
|
|
||||||
|
Weather::Weather(Pinetime::Applications::DisplayApp* app,
|
||||||
|
Pinetime::Controllers::DateTime& dateTimeController,
|
||||||
|
Pinetime::Controllers::Battery& batteryController,
|
||||||
|
Pinetime::Controllers::BrightnessController& brightnessController,
|
||||||
|
Pinetime::Controllers::Ble& bleController,
|
||||||
|
Pinetime::Drivers::WatchdogView& watchdog)
|
||||||
|
: Screen(app),
|
||||||
|
dateTimeController {dateTimeController},
|
||||||
|
batteryController {batteryController},
|
||||||
|
brightnessController {brightnessController},
|
||||||
|
bleController {bleController},
|
||||||
|
watchdog {watchdog},
|
||||||
|
screens {app,
|
||||||
|
0,
|
||||||
|
{[this]() -> std::unique_ptr<Screen> {
|
||||||
|
return CreateScreen1();
|
||||||
|
},
|
||||||
|
[this]() -> std::unique_ptr<Screen> {
|
||||||
|
return CreateScreen2();
|
||||||
|
},
|
||||||
|
[this]() -> std::unique_ptr<Screen> {
|
||||||
|
return CreateScreen3();
|
||||||
|
},
|
||||||
|
[this]() -> std::unique_ptr<Screen> {
|
||||||
|
return CreateScreen4();
|
||||||
|
},
|
||||||
|
[this]() -> std::unique_ptr<Screen> {
|
||||||
|
return CreateScreen5();
|
||||||
|
}},
|
||||||
|
Screens::ScreenListModes::UpDown} {
|
||||||
|
}
|
||||||
|
|
||||||
|
Weather::~Weather() {
|
||||||
|
lv_obj_clean(lv_scr_act());
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Weather::Refresh() {
|
||||||
|
if (running) {
|
||||||
|
screens.Refresh();
|
||||||
|
}
|
||||||
|
return running;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Weather::OnButtonPushed() {
|
||||||
|
running = false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Weather::OnTouchEvent(Pinetime::Applications::TouchEvents event) {
|
||||||
|
return screens.OnTouchEvent(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<Screen> Weather::CreateScreen1() {
|
||||||
|
lv_obj_t* label = lv_label_create(lv_scr_act(), nullptr);
|
||||||
|
lv_label_set_recolor(label, true);
|
||||||
|
lv_label_set_text_fmt(label,
|
||||||
|
"#FFFF00 InfiniTime#\n\n"
|
||||||
|
"#444444 Version# %ld.%ld.%ld\n\n"
|
||||||
|
"#444444 Build date#\n"
|
||||||
|
"%s\n"
|
||||||
|
"%s\n",
|
||||||
|
Version::Major(),
|
||||||
|
Version::Minor(),
|
||||||
|
Version::Patch(),
|
||||||
|
__DATE__,
|
||||||
|
__TIME__);
|
||||||
|
lv_label_set_align(label, LV_LABEL_ALIGN_CENTER);
|
||||||
|
lv_obj_align(label, lv_scr_act(), LV_ALIGN_CENTER, 0, 0);
|
||||||
|
return std::unique_ptr<Screen>(new Screens::Label(0, 5, app, label));
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<Screen> Weather::CreateScreen2() {
|
||||||
|
auto batteryPercent = static_cast<uint8_t>(batteryController.PercentRemaining());
|
||||||
|
float batteryVoltage = batteryController.Voltage();
|
||||||
|
|
||||||
|
auto resetReason = [this]() {
|
||||||
|
switch (watchdog.ResetReason()) {
|
||||||
|
case Drivers::Watchdog::ResetReasons::Watchdog:
|
||||||
|
return "wtdg";
|
||||||
|
case Drivers::Watchdog::ResetReasons::HardReset:
|
||||||
|
return "hardr";
|
||||||
|
case Drivers::Watchdog::ResetReasons::NFC:
|
||||||
|
return "nfc";
|
||||||
|
case Drivers::Watchdog::ResetReasons::SoftReset:
|
||||||
|
return "softr";
|
||||||
|
case Drivers::Watchdog::ResetReasons::CpuLockup:
|
||||||
|
return "cpulock";
|
||||||
|
case Drivers::Watchdog::ResetReasons::SystemOff:
|
||||||
|
return "off";
|
||||||
|
case Drivers::Watchdog::ResetReasons::LpComp:
|
||||||
|
return "lpcomp";
|
||||||
|
case Drivers::Watchdog::ResetReasons::DebugInterface:
|
||||||
|
return "dbg";
|
||||||
|
case Drivers::Watchdog::ResetReasons::ResetPin:
|
||||||
|
return "rst";
|
||||||
|
default:
|
||||||
|
return "?";
|
||||||
|
}
|
||||||
|
}();
|
||||||
|
|
||||||
|
// uptime
|
||||||
|
static constexpr uint32_t secondsInADay = 60 * 60 * 24;
|
||||||
|
static constexpr uint32_t secondsInAnHour = 60 * 60;
|
||||||
|
static constexpr uint32_t secondsInAMinute = 60;
|
||||||
|
uint32_t uptimeSeconds = dateTimeController.Uptime().count();
|
||||||
|
uint32_t uptimeDays = (uptimeSeconds / secondsInADay);
|
||||||
|
uptimeSeconds = uptimeSeconds % secondsInADay;
|
||||||
|
uint32_t uptimeHours = uptimeSeconds / secondsInAnHour;
|
||||||
|
uptimeSeconds = uptimeSeconds % secondsInAnHour;
|
||||||
|
uint32_t uptimeMinutes = uptimeSeconds / secondsInAMinute;
|
||||||
|
uptimeSeconds = uptimeSeconds % secondsInAMinute;
|
||||||
|
// TODO handle more than 100 days of uptime
|
||||||
|
|
||||||
|
if (batteryPercent == -1)
|
||||||
|
batteryPercent = 0;
|
||||||
|
|
||||||
|
// hack to not use the flot functions from printf
|
||||||
|
uint8_t batteryVoltageBytes[2];
|
||||||
|
batteryVoltageBytes[1] = static_cast<uint8_t>(batteryVoltage); // truncate whole numbers
|
||||||
|
batteryVoltageBytes[0] =
|
||||||
|
static_cast<uint8_t>((batteryVoltage - batteryVoltageBytes[1]) * 100); // remove whole part of flt and shift 2 places over
|
||||||
|
//
|
||||||
|
|
||||||
|
lv_obj_t* label = lv_label_create(lv_scr_act(), nullptr);
|
||||||
|
lv_label_set_recolor(label, true);
|
||||||
|
lv_label_set_text_fmt(label,
|
||||||
|
"#444444 Date# %02d/%02d/%04d\n"
|
||||||
|
"#444444 Time# %02d:%02d:%02d\n"
|
||||||
|
"#444444 Uptime#\n %02lud %02lu:%02lu:%02lu\n"
|
||||||
|
"#444444 Battery# %d%%/%1i.%02iv\n"
|
||||||
|
"#444444 Backlight# %s\n"
|
||||||
|
"#444444 Last reset# %s\n",
|
||||||
|
dateTimeController.Day(),
|
||||||
|
static_cast<uint8_t>(dateTimeController.Month()),
|
||||||
|
dateTimeController.Year(),
|
||||||
|
dateTimeController.Hours(),
|
||||||
|
dateTimeController.Minutes(),
|
||||||
|
dateTimeController.Seconds(),
|
||||||
|
uptimeDays,
|
||||||
|
uptimeHours,
|
||||||
|
uptimeMinutes,
|
||||||
|
uptimeSeconds,
|
||||||
|
batteryPercent,
|
||||||
|
batteryVoltageBytes[1],
|
||||||
|
batteryVoltageBytes[0],
|
||||||
|
brightnessController.ToString(),
|
||||||
|
resetReason);
|
||||||
|
lv_obj_align(label, lv_scr_act(), LV_ALIGN_CENTER, 0, 0);
|
||||||
|
return std::unique_ptr<Screen>(new Screens::Label(1, 4, app, label));
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<Screen> Weather::CreateScreen3() {
|
||||||
|
lv_mem_monitor_t mon;
|
||||||
|
lv_mem_monitor(&mon);
|
||||||
|
|
||||||
|
lv_obj_t* label = lv_label_create(lv_scr_act(), nullptr);
|
||||||
|
lv_label_set_recolor(label, true);
|
||||||
|
auto& bleAddr = bleController.Address();
|
||||||
|
lv_label_set_text_fmt(label,
|
||||||
|
"#444444 BLE MAC#\n"
|
||||||
|
" %02x:%02x:%02x:%02x:%02x:%02x"
|
||||||
|
"\n"
|
||||||
|
"#444444 Memory#\n"
|
||||||
|
" #444444 used# %d (%d%%)\n"
|
||||||
|
" #444444 frag# %d%%\n"
|
||||||
|
" #444444 free# %d"
|
||||||
|
"\n"
|
||||||
|
"#444444 Steps# %li",
|
||||||
|
bleAddr[5],
|
||||||
|
bleAddr[4],
|
||||||
|
bleAddr[3],
|
||||||
|
bleAddr[2],
|
||||||
|
bleAddr[1],
|
||||||
|
bleAddr[0],
|
||||||
|
(int) mon.total_size - mon.free_size,
|
||||||
|
mon.used_pct,
|
||||||
|
mon.frag_pct,
|
||||||
|
(int) mon.free_biggest_size,
|
||||||
|
0);
|
||||||
|
lv_obj_align(label, lv_scr_act(), LV_ALIGN_CENTER, 0, 0);
|
||||||
|
return std::unique_ptr<Screen>(new Screens::Label(2, 5, app, label));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool sortById(const TaskStatus_t& lhs, const TaskStatus_t& rhs) {
|
||||||
|
return lhs.xTaskNumber < rhs.xTaskNumber;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<Screen> Weather::CreateScreen4() {
|
||||||
|
TaskStatus_t tasksStatus[7];
|
||||||
|
lv_obj_t* infoTask = lv_table_create(lv_scr_act(), NULL);
|
||||||
|
lv_table_set_col_cnt(infoTask, 3);
|
||||||
|
lv_table_set_row_cnt(infoTask, 8);
|
||||||
|
lv_obj_set_pos(infoTask, 10, 10);
|
||||||
|
|
||||||
|
lv_table_set_cell_value(infoTask, 0, 0, "#");
|
||||||
|
lv_table_set_col_width(infoTask, 0, 50);
|
||||||
|
lv_table_set_cell_value(infoTask, 0, 1, "Task");
|
||||||
|
lv_table_set_col_width(infoTask, 1, 80);
|
||||||
|
lv_table_set_cell_value(infoTask, 0, 2, "Free");
|
||||||
|
lv_table_set_col_width(infoTask, 2, 90);
|
||||||
|
|
||||||
|
auto nb = uxTaskGetSystemState(tasksStatus, 7, nullptr);
|
||||||
|
std::sort(tasksStatus, tasksStatus + nb, sortById);
|
||||||
|
for (uint8_t i = 0; i < nb; i++) {
|
||||||
|
|
||||||
|
lv_table_set_cell_value(infoTask, i + 1, 0, std::to_string(tasksStatus[i].xTaskNumber).c_str());
|
||||||
|
lv_table_set_cell_value(infoTask, i + 1, 1, tasksStatus[i].pcTaskName);
|
||||||
|
if (tasksStatus[i].usStackHighWaterMark < 20) {
|
||||||
|
std::string str1 = std::to_string(tasksStatus[i].usStackHighWaterMark) + " low";
|
||||||
|
lv_table_set_cell_value(infoTask, i + 1, 2, str1.c_str());
|
||||||
|
} else {
|
||||||
|
lv_table_set_cell_value(infoTask, i + 1, 2, std::to_string(tasksStatus[i].usStackHighWaterMark).c_str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return std::unique_ptr<Screen>(new Screens::Label(3, 5, app, infoTask));
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<Screen> Weather::CreateScreen5() {
|
||||||
|
lv_obj_t* label = lv_label_create(lv_scr_act(), nullptr);
|
||||||
|
lv_label_set_recolor(label, true);
|
||||||
|
lv_label_set_text_static(label,
|
||||||
|
"Software Licensed\n"
|
||||||
|
"under the terms of\n"
|
||||||
|
"the GNU General\n"
|
||||||
|
"Public License v3\n"
|
||||||
|
"#444444 Source code#\n"
|
||||||
|
"#FFFF00 https://github.com/#\n"
|
||||||
|
"#FFFF00 JF002/InfiniTime#");
|
||||||
|
lv_label_set_align(label, LV_LABEL_ALIGN_CENTER);
|
||||||
|
lv_obj_align(label, lv_scr_act(), LV_ALIGN_CENTER, 0, 0);
|
||||||
|
return std::unique_ptr<Screen>(new Screens::Label(4, 5, app, label));
|
||||||
|
}
|
54
src/displayapp/screens/Weather.h
Normal file
54
src/displayapp/screens/Weather.h
Normal file
|
@ -0,0 +1,54 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include "Screen.h"
|
||||||
|
#include "ScreenList.h"
|
||||||
|
|
||||||
|
namespace Pinetime {
|
||||||
|
namespace Controllers {
|
||||||
|
class DateTime;
|
||||||
|
class Battery;
|
||||||
|
class BrightnessController;
|
||||||
|
class Ble;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace Drivers {
|
||||||
|
class WatchdogView;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace Applications {
|
||||||
|
class DisplayApp;
|
||||||
|
|
||||||
|
namespace Screens {
|
||||||
|
class Weather : public Screen {
|
||||||
|
public:
|
||||||
|
explicit Weather(DisplayApp* app,
|
||||||
|
Pinetime::Controllers::DateTime& dateTimeController,
|
||||||
|
Pinetime::Controllers::Battery& batteryController,
|
||||||
|
Pinetime::Controllers::BrightnessController& brightnessController,
|
||||||
|
Pinetime::Controllers::Ble& bleController,
|
||||||
|
Pinetime::Drivers::WatchdogView& watchdog);
|
||||||
|
~Weather() override;
|
||||||
|
bool Refresh() override;
|
||||||
|
bool OnButtonPushed() override;
|
||||||
|
bool OnTouchEvent(TouchEvents event) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool running = true;
|
||||||
|
|
||||||
|
Pinetime::Controllers::DateTime& dateTimeController;
|
||||||
|
Pinetime::Controllers::Battery& batteryController;
|
||||||
|
Pinetime::Controllers::BrightnessController& brightnessController;
|
||||||
|
Pinetime::Controllers::Ble& bleController;
|
||||||
|
Pinetime::Drivers::WatchdogView& watchdog;
|
||||||
|
|
||||||
|
ScreenList<5> screens;
|
||||||
|
std::unique_ptr<Screen> CreateScreen1();
|
||||||
|
std::unique_ptr<Screen> CreateScreen2();
|
||||||
|
std::unique_ptr<Screen> CreateScreen3();
|
||||||
|
std::unique_ptr<Screen> CreateScreen4();
|
||||||
|
std::unique_ptr<Screen> CreateScreen5();
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue