From 92060da7df1e98affe33129e1401a66d20480428 Mon Sep 17 00:00:00 2001 From: Jakob Krantz Date: Tue, 29 Oct 2024 20:05:19 +0100 Subject: [PATCH] Weather App: Periodically fetch weather over HTTP. --- app/src/applications/weather/CMakeLists.txt | 2 +- app/src/applications/weather/weather_app.c | 46 +++++++++-- app/src/ui/utils/zsw_ui_utils.c | 92 ++++++++++++++++++++- app/src/ui/utils/zsw_ui_utils.h | 2 + 4 files changed, 134 insertions(+), 8 deletions(-) diff --git a/app/src/applications/weather/CMakeLists.txt b/app/src/applications/weather/CMakeLists.txt index ad8bd86c..ba5eb744 100644 --- a/app/src/applications/weather/CMakeLists.txt +++ b/app/src/applications/weather/CMakeLists.txt @@ -1,4 +1,4 @@ -if (CONFIG_ZSWATCH_PCB_REV GREATER_EQUAL 4) +if (CONFIG_ZSWATCH_PCB_REV GREATER_EQUAL 4 OR CONFIG_BOARD_NATIVE_POSIX) FILE(GLOB app_sources *.c) target_sources(app PRIVATE ${app_sources}) endif() \ No newline at end of file diff --git a/app/src/applications/weather/weather_app.c b/app/src/applications/weather/weather_app.c index 1525f520..e4c0b229 100644 --- a/app/src/applications/weather/weather_app.c +++ b/app/src/applications/weather/weather_app.c @@ -16,16 +16,22 @@ LOG_MODULE_REGISTER(weather_app, LOG_LEVEL_DBG); #define HTTP_REQUEST_URL_FMT "https://api.open-meteo.com/v1/forecast?latitude=%f&longitude=%f¤t=wind_speed_10m,temperature_2m,apparent_temperature,weather_code&daily=weather_code,temperature_2m_max,temperature_2m_min,apparent_temperature_max,apparent_temperature_min,precipitation_sum,rain_sum,precipitation_probability_max&wind_speed_unit=ms&timezone=auto&forecast_days=%d" #define MAX_GPS_AGED_TIME_MS 30 * 60 * 1000 +#define WEATHER_BACKGROUND_FETCH_INTERVAL_S (30 * 60) // Functions needed for all applications static void weather_app_start(lv_obj_t *root, lv_group_t *group); static void weather_app_stop(void); static void on_zbus_ble_data_callback(const struct zbus_channel *chan); +static void periodic_fetch_weather_data(struct k_work *work); +static void publish_weather_data(struct k_work *work); ZBUS_CHAN_DECLARE(ble_comm_data_chan); ZBUS_LISTENER_DEFINE(weather_ble_comm_lis, on_zbus_ble_data_callback); ZBUS_CHAN_ADD_OBS(ble_comm_data_chan, weather_ble_comm_lis, 1); +K_WORK_DELAYABLE_DEFINE(weather_app_fetch_work, periodic_fetch_weather_data); +K_WORK_DEFINE(weather_app_publish, publish_weather_data); + ZSW_LV_IMG_DECLARE(weather_app_icon); static bool active; @@ -34,6 +40,8 @@ static uint64_t last_update_weather_time; static double last_lat; static double last_lon; +static ble_comm_weather_t last_weather; + static application_t app = { .name = "Weather", .icon = ZSW_LV_IMG_USE(weather_app_icon), @@ -48,10 +56,6 @@ static void http_rsp_cb(ble_http_status_code_t status, char *response) weather_ui_forecast_data_t forecasts[WEATHER_UI_NUM_FORECASTS]; char *days[] = { "SUN", "MON", "TUE", "WED", "THU", "FRI", "SAT"}; - if (!active) { - return; - } - if (status == BLE_HTTP_STATUS_OK) { zsw_clock_get_time(&time_now); cJSON *parsed_response = cJSON_Parse(response); @@ -85,15 +89,32 @@ static void http_rsp_cb(ble_http_status_code_t status, char *response) weather_ui_set_weather_data(current_weather, forecasts, cJSON_GetArraySize(weather_code_list)); + ble_comm_request_gps_status(false); + last_weather.temperature_c = current_weather.temperature; + last_weather.humidity = 0; + last_weather.wind = current_weather.wind_speed; + last_weather.wind_direction = 0; + last_weather.weather_code = wmo_code_to_weather_code(current_weather_code->valueint); + strncpy(last_weather.report_text, current_weather.text, sizeof(last_weather.report_text)); + cJSON_Delete(parsed_response); last_update_weather_time = k_uptime_get(); - ble_comm_request_gps_status(false); + + k_work_submit(&weather_app_publish); } else { LOG_ERR("HTTP request failed\n"); weather_ui_set_error(status == BLE_HTTP_STATUS_TIMEOUT ? "Timeout" : "Failed"); } } +static void publish_weather_data(struct k_work *work) +{ + ble_comm_cb_data_t data; + data.type = BLE_COMM_DATA_TYPE_WEATHER; + data.data.weather = last_weather; + zbus_chan_pub(&ble_comm_data_chan, &data, K_MSEC(250)); +} + static void fetch_weather_data(double lat, double lon) { char weather_url[512]; @@ -105,6 +126,15 @@ static void fetch_weather_data(double lat, double lon) } } +static void periodic_fetch_weather_data(struct k_work *work) +{ + int ret = ble_comm_request_gps_status(true); + if (ret != 0) { + LOG_ERR("Failed to disable phone GPS: %d", ret); + } + k_work_reschedule(&weather_app_fetch_work, K_SECONDS(WEATHER_BACKGROUND_FETCH_INTERVAL_S)); +} + static void on_zbus_ble_data_callback(const struct zbus_channel *chan) { const struct ble_data_event *event = zbus_chan_const_msg(chan); @@ -117,6 +147,10 @@ static void on_zbus_ble_data_callback(const struct zbus_channel *chan) last_lat = event->data.data.gps.lat; last_lon = event->data.data.gps.lon; fetch_weather_data(event->data.data.gps.lat, event->data.data.gps.lon); + int ret = ble_comm_request_gps_status(false); + if (ret != 0) { + LOG_ERR("Failed to request GPS data: %d", ret); + } } } @@ -152,7 +186,7 @@ static int weather_app_add(void) { zsw_app_manager_add_application(&app); - // TODO Add periodic GPS and weather polling in backgrund. + k_work_reschedule(&weather_app_fetch_work, K_SECONDS(30)); return 0; } diff --git a/app/src/ui/utils/zsw_ui_utils.c b/app/src/ui/utils/zsw_ui_utils.c index 75ce376d..20f06cd1 100644 --- a/app/src/ui/utils/zsw_ui_utils.c +++ b/app/src/ui/utils/zsw_ui_utils.c @@ -132,9 +132,99 @@ const lv_img_dsc_t *zsw_ui_utils_icon_from_weather_code(int code, lv_color_t *ic } } +int wmo_code_to_weather_code(int wmo_code) +{ + switch (wmo_code) { + case 0: { + return 800; + } + case 1: { + return 800; + } + case 2: { + return 800; + } + case 3: { + return 803; + } + case 45: { + return 700; + } + case 48: { + return 700; + } + case 51: { + return 700; + } + case 53: { + return 512; + } + case 55: { + return 512; + } + case 80: { + return 512; + } + case 81: { + return 512; + } + case 82: { + return 512; + } + case 61: { + return 512; + } + case 63: { + return 512; + } + case 65: { + return 512; + } + case 56: { + return 512; + } + case 57: { + return 512; + } + case 66: { + return 512; + } + case 67: { + return 512; + } + case 71: { + return 511; + } + case 73: { + return 511; + } + case 75: { + return 511; + } + case 77: { + return 511; + } + case 85: { + return 511; + } + case 86: { + return 511; + } + case 95: { + return 200; + } + case 96: { + return 200; + } + case 99: { + return 200; + } + } + return -1; +} + const void *zsw_ui_utils_icon_from_wmo_weather_code(int code, lv_color_t *color, char **text) { - printk("WMO Code: %d\n", code); switch (code) { case 0: { *color = lv_color_hex(0xF1F1F1); diff --git a/app/src/ui/utils/zsw_ui_utils.h b/app/src/ui/utils/zsw_ui_utils.h index db2d9b4c..4c962aa8 100644 --- a/app/src/ui/utils/zsw_ui_utils.h +++ b/app/src/ui/utils/zsw_ui_utils.h @@ -37,6 +37,8 @@ const lv_img_dsc_t *zsw_ui_utils_icon_from_weather_code(int code, lv_color_t *ic const void *zsw_ui_utils_icon_from_wmo_weather_code(int code, lv_color_t *color, char **text); +int wmo_code_to_weather_code(int wmo_code); + const void *zsw_ui_utils_icon_from_notification(zsw_notification_src_t src); const char *zsw_ui_utils_source_from_notification(zsw_notification_src_t src);