mirror of
https://github.com/Next-Flip/Momentum-Firmware.git
synced 2026-06-29 21:52:03 -07:00
Merge branch 'dev' into DigitalSequence_PulseReader
This commit is contained in:
@@ -72,12 +72,12 @@ jobs:
|
||||
|
||||
- name: 'Get last release tag'
|
||||
id: release_tag
|
||||
if: success()
|
||||
if: always()
|
||||
run: |
|
||||
echo "tag=$(git tag -l --sort=-version:refname | grep -v "rc\|RC" | head -1)" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: 'Decontaminate previous build leftovers'
|
||||
if: success()
|
||||
if: always()
|
||||
run: |
|
||||
if [ -d .git ]; then
|
||||
git submodule status || git checkout "$(git rev-list --max-parents=0 HEAD | tail -n 1)"
|
||||
@@ -85,25 +85,25 @@ jobs:
|
||||
|
||||
- name: 'Checkout latest release'
|
||||
uses: actions/checkout@v3
|
||||
if: success()
|
||||
if: always()
|
||||
with:
|
||||
fetch-depth: 0
|
||||
ref: ${{ steps.release_tag.outputs.tag }}
|
||||
|
||||
- name: 'Flash last release'
|
||||
if: success()
|
||||
if: always()
|
||||
run: |
|
||||
./fbt flash OPENOCD_ADAPTER_SERIAL=2A0906016415303030303032 FIRMWARE_APP_SET=unit_tests FORCE=1
|
||||
|
||||
- name: 'Wait for flipper to finish updating'
|
||||
if: success()
|
||||
if: always()
|
||||
run: |
|
||||
source scripts/toolchain/fbtenv.sh
|
||||
python3 scripts/testing/await_flipper.py ${{steps.device.outputs.flipper}}
|
||||
|
||||
- name: 'Format flipper SD card'
|
||||
id: format
|
||||
if: success()
|
||||
if: always()
|
||||
run: |
|
||||
source scripts/toolchain/fbtenv.sh
|
||||
python3 scripts/storage.py -p ${{steps.device.outputs.flipper}} format_ext
|
||||
|
||||
@@ -46,6 +46,7 @@ Nfc* nfc_alloc() {
|
||||
|
||||
// Nfc device
|
||||
nfc->dev = nfc_device_alloc();
|
||||
furi_string_set(nfc->dev->folder, NFC_APP_FOLDER);
|
||||
|
||||
// Open GUI record
|
||||
nfc->gui = furi_record_open(RECORD_GUI);
|
||||
|
||||
@@ -44,6 +44,7 @@
|
||||
ARRAY_DEF(MfClassicUserKeys, char*, M_PTR_OPLIST);
|
||||
|
||||
#define NFC_TEXT_STORE_SIZE 128
|
||||
#define NFC_APP_FOLDER ANY_PATH("nfc")
|
||||
|
||||
typedef enum {
|
||||
NfcRpcStateIdle,
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
App(
|
||||
appid="clock",
|
||||
name="Clock",
|
||||
apptype=FlipperAppType.PLUGIN,
|
||||
entry_point="clock_app",
|
||||
requires=["gui"],
|
||||
stack_size=2 * 1024,
|
||||
fap_icon="clock.png",
|
||||
fap_category="Tools",
|
||||
)
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 1.9 KiB |
@@ -0,0 +1,136 @@
|
||||
#include <furi.h>
|
||||
#include <furi_hal.h>
|
||||
|
||||
#include <gui/gui.h>
|
||||
#include <locale/locale.h>
|
||||
|
||||
typedef enum {
|
||||
ClockEventTypeTick,
|
||||
ClockEventTypeKey,
|
||||
} ClockEventType;
|
||||
|
||||
typedef struct {
|
||||
ClockEventType type;
|
||||
InputEvent input;
|
||||
} ClockEvent;
|
||||
|
||||
typedef struct {
|
||||
FuriString* buffer;
|
||||
FuriHalRtcDateTime datetime;
|
||||
LocaleTimeFormat timeformat;
|
||||
LocaleDateFormat dateformat;
|
||||
} ClockData;
|
||||
|
||||
typedef struct {
|
||||
FuriMutex* mutex;
|
||||
FuriMessageQueue* queue;
|
||||
ClockData* data;
|
||||
} Clock;
|
||||
|
||||
static void clock_input_callback(InputEvent* input_event, FuriMessageQueue* queue) {
|
||||
furi_assert(queue);
|
||||
ClockEvent event = {.type = ClockEventTypeKey, .input = *input_event};
|
||||
furi_message_queue_put(queue, &event, FuriWaitForever);
|
||||
}
|
||||
|
||||
static void clock_render_callback(Canvas* canvas, void* ctx) {
|
||||
Clock* clock = ctx;
|
||||
if(furi_mutex_acquire(clock->mutex, 200) != FuriStatusOk) {
|
||||
return;
|
||||
}
|
||||
|
||||
ClockData* data = clock->data;
|
||||
|
||||
canvas_set_font(canvas, FontBigNumbers);
|
||||
locale_format_time(data->buffer, &data->datetime, data->timeformat, true);
|
||||
canvas_draw_str_aligned(
|
||||
canvas, 64, 28, AlignCenter, AlignCenter, furi_string_get_cstr(data->buffer));
|
||||
|
||||
// Special case to cover missing glyphs in FontBigNumbers
|
||||
if(data->timeformat == LocaleTimeFormat12h) {
|
||||
size_t time_width = canvas_string_width(canvas, furi_string_get_cstr(data->buffer));
|
||||
canvas_set_font(canvas, FontPrimary);
|
||||
canvas_draw_str_aligned(
|
||||
canvas,
|
||||
64 + (time_width / 2) - 10,
|
||||
31,
|
||||
AlignLeft,
|
||||
AlignCenter,
|
||||
(data->datetime.hour > 12) ? "AM" : "PM");
|
||||
}
|
||||
|
||||
canvas_set_font(canvas, FontSecondary);
|
||||
locale_format_date(data->buffer, &data->datetime, data->dateformat, "/");
|
||||
canvas_draw_str_aligned(
|
||||
canvas, 64, 42, AlignCenter, AlignTop, furi_string_get_cstr(data->buffer));
|
||||
|
||||
furi_mutex_release(clock->mutex);
|
||||
}
|
||||
|
||||
static void clock_tick(void* ctx) {
|
||||
furi_assert(ctx);
|
||||
FuriMessageQueue* queue = ctx;
|
||||
ClockEvent event = {.type = ClockEventTypeTick};
|
||||
// It's OK to loose this event if system overloaded
|
||||
furi_message_queue_put(queue, &event, 0);
|
||||
}
|
||||
|
||||
int32_t clock_app(void* p) {
|
||||
UNUSED(p);
|
||||
Clock* clock = malloc(sizeof(Clock));
|
||||
clock->data = malloc(sizeof(ClockData));
|
||||
clock->data->buffer = furi_string_alloc();
|
||||
|
||||
clock->queue = furi_message_queue_alloc(8, sizeof(ClockEvent));
|
||||
clock->mutex = furi_mutex_alloc(FuriMutexTypeNormal);
|
||||
|
||||
furi_hal_rtc_get_datetime(&clock->data->datetime);
|
||||
clock->data->timeformat = locale_get_time_format();
|
||||
clock->data->dateformat = locale_get_date_format();
|
||||
|
||||
// Set ViewPort callbacks
|
||||
ViewPort* view_port = view_port_alloc();
|
||||
view_port_draw_callback_set(view_port, clock_render_callback, clock);
|
||||
view_port_input_callback_set(view_port, clock_input_callback, clock->queue);
|
||||
|
||||
FuriTimer* timer = furi_timer_alloc(clock_tick, FuriTimerTypePeriodic, clock->queue);
|
||||
|
||||
// Open GUI and register view_port
|
||||
Gui* gui = furi_record_open(RECORD_GUI);
|
||||
gui_add_view_port(gui, view_port, GuiLayerFullscreen);
|
||||
|
||||
furi_timer_start(timer, 100);
|
||||
|
||||
// Main loop
|
||||
ClockEvent event;
|
||||
for(bool processing = true; processing;) {
|
||||
furi_check(furi_message_queue_get(clock->queue, &event, FuriWaitForever) == FuriStatusOk);
|
||||
furi_mutex_acquire(clock->mutex, FuriWaitForever);
|
||||
if(event.type == ClockEventTypeKey) {
|
||||
if(event.input.type == InputTypeShort && event.input.key == InputKeyBack) {
|
||||
processing = false;
|
||||
}
|
||||
} else if(event.type == ClockEventTypeTick) {
|
||||
furi_hal_rtc_get_datetime(&clock->data->datetime);
|
||||
}
|
||||
|
||||
furi_mutex_release(clock->mutex);
|
||||
view_port_update(view_port);
|
||||
}
|
||||
|
||||
furi_timer_free(timer);
|
||||
view_port_enabled_set(view_port, false);
|
||||
gui_remove_view_port(gui, view_port);
|
||||
view_port_free(view_port);
|
||||
furi_record_close(RECORD_GUI);
|
||||
|
||||
furi_message_queue_free(clock->queue);
|
||||
furi_mutex_free(clock->mutex);
|
||||
|
||||
furi_string_free(clock->data->buffer);
|
||||
|
||||
free(clock->data);
|
||||
free(clock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -49,6 +49,7 @@ NfcMagic* nfc_magic_alloc() {
|
||||
|
||||
// Nfc device
|
||||
nfc_magic->nfc_dev = nfc_device_alloc();
|
||||
furi_string_set(nfc_magic->nfc_dev->folder, NFC_APP_FOLDER);
|
||||
|
||||
// Open GUI record
|
||||
nfc_magic->gui = furi_record_open(RECORD_GUI);
|
||||
|
||||
@@ -27,6 +27,8 @@
|
||||
#include <lib/nfc/nfc_device.h>
|
||||
#include "nfc_magic_icons.h"
|
||||
|
||||
#define NFC_APP_FOLDER ANY_PATH("nfc")
|
||||
|
||||
enum NfcMagicCustomEvent {
|
||||
// Reserve first 100 events for button types and indexes, starting from 0
|
||||
NfcMagicCustomEventReserved = 100,
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
#include <furi.h>
|
||||
#include <furi_hal.h>
|
||||
|
||||
#define WS_VERSION_APP "0.6"
|
||||
#define WS_VERSION_APP "0.6.1"
|
||||
#define WS_DEVELOPED "SkorP"
|
||||
#define WS_GITHUB "https://github.com/flipperdevices/flipperzero-firmware"
|
||||
|
||||
|
||||
@@ -134,8 +134,8 @@ static void ws_protocol_ambient_weather_remote_controller(WSBlockGeneric* instan
|
||||
instance->id = (instance->data >> 32) & 0xFF;
|
||||
instance->battery_low = (instance->data >> 31) & 1;
|
||||
instance->channel = ((instance->data >> 28) & 0x07) + 1;
|
||||
instance->temp = ws_block_generic_fahrenheit_to_celsius(
|
||||
((float)((instance->data >> 16) & 0x0FFF) - 400.0f) / 10.0f);
|
||||
instance->temp =
|
||||
locale_fahrenheit_to_celsius(((float)((instance->data >> 16) & 0x0FFF) - 400.0f) / 10.0f);
|
||||
instance->humidity = (instance->data >> 8) & 0xFF;
|
||||
instance->btn = WS_NO_BTN;
|
||||
|
||||
|
||||
@@ -143,8 +143,8 @@ static void ws_protocol_infactory_remote_controller(WSBlockGeneric* instance) {
|
||||
instance->id = instance->data >> 32;
|
||||
instance->battery_low = (instance->data >> 26) & 1;
|
||||
instance->btn = WS_NO_BTN;
|
||||
instance->temp = ws_block_generic_fahrenheit_to_celsius(
|
||||
((float)((instance->data >> 12) & 0x0FFF) - 900.0f) / 10.0f);
|
||||
instance->temp =
|
||||
locale_fahrenheit_to_celsius(((float)((instance->data >> 12) & 0x0FFF) - 900.0f) / 10.0f);
|
||||
instance->humidity =
|
||||
(((instance->data >> 8) & 0x0F) * 10) + ((instance->data >> 4) & 0x0F); // BCD, 'A0'=100%rH
|
||||
instance->channel = instance->data & 0x03;
|
||||
|
||||
@@ -209,7 +209,3 @@ bool ws_block_generic_deserialize(WSBlockGeneric* instance, FlipperFormat* flipp
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
float ws_block_generic_fahrenheit_to_celsius(float fahrenheit) {
|
||||
return (fahrenheit - 32.0f) / 1.8f;
|
||||
}
|
||||
@@ -8,6 +8,7 @@
|
||||
#include "furi.h"
|
||||
#include "furi_hal.h"
|
||||
#include <lib/subghz/types.h>
|
||||
#include <locale/locale.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
@@ -62,8 +63,6 @@ bool ws_block_generic_serialize(
|
||||
*/
|
||||
bool ws_block_generic_deserialize(WSBlockGeneric* instance, FlipperFormat* flipper_format);
|
||||
|
||||
float ws_block_generic_fahrenheit_to_celsius(float fahrenheit);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -81,13 +81,33 @@ void ws_view_receiver_info_draw(Canvas* canvas, WSReceiverInfoModel* model) {
|
||||
|
||||
if(model->generic->temp != WS_NO_TEMPERATURE) {
|
||||
canvas_draw_icon(canvas, 6, 43, &I_Therm_7x16);
|
||||
snprintf(buffer, sizeof(buffer), "%3.1f C", (double)model->generic->temp);
|
||||
uint8_t temp_x1 = 47;
|
||||
uint8_t temp_x2 = 38;
|
||||
if(model->generic->temp < -9.0) {
|
||||
temp_x1 = 49;
|
||||
temp_x2 = 40;
|
||||
|
||||
uint8_t temp_x1 = 0;
|
||||
uint8_t temp_x2 = 0;
|
||||
if(furi_hal_rtc_get_locale_units() == FuriHalRtcLocaleUnitsMetric) {
|
||||
snprintf(buffer, sizeof(buffer), "%3.1f C", (double)model->generic->temp);
|
||||
if(model->generic->temp < -9.0f) {
|
||||
temp_x1 = 49;
|
||||
temp_x2 = 40;
|
||||
} else {
|
||||
temp_x1 = 47;
|
||||
temp_x2 = 38;
|
||||
}
|
||||
} else {
|
||||
snprintf(
|
||||
buffer,
|
||||
sizeof(buffer),
|
||||
"%3.1f F",
|
||||
(double)locale_celsius_to_fahrenheit(model->generic->temp));
|
||||
if((model->generic->temp < -27.77f) || (model->generic->temp > 37.77f)) {
|
||||
temp_x1 = 50;
|
||||
temp_x2 = 42;
|
||||
} else {
|
||||
temp_x1 = 48;
|
||||
temp_x2 = 40;
|
||||
}
|
||||
}
|
||||
|
||||
canvas_draw_str_aligned(canvas, temp_x1, 47, AlignRight, AlignTop, buffer);
|
||||
canvas_draw_circle(canvas, temp_x2, 46, 1);
|
||||
}
|
||||
|
||||
@@ -51,6 +51,9 @@ void locale_format_time(
|
||||
} else {
|
||||
am_pm = 1;
|
||||
}
|
||||
if(hours == 0) {
|
||||
hours = 12;
|
||||
}
|
||||
}
|
||||
|
||||
if(show_seconds) {
|
||||
|
||||
+47
-11
@@ -27,6 +27,7 @@ NfcDevice* nfc_device_alloc() {
|
||||
nfc_dev->dialogs = furi_record_open(RECORD_DIALOGS);
|
||||
nfc_dev->load_path = furi_string_alloc();
|
||||
nfc_dev->dev_data.parsed_data = furi_string_alloc();
|
||||
nfc_dev->folder = furi_string_alloc();
|
||||
|
||||
// Rename cache folder name for backward compatibility
|
||||
if(storage_common_stat(nfc_dev->storage, "/ext/nfc/cache", NULL) == FSE_OK) {
|
||||
@@ -42,6 +43,7 @@ void nfc_device_free(NfcDevice* nfc_dev) {
|
||||
furi_record_close(RECORD_DIALOGS);
|
||||
furi_string_free(nfc_dev->load_path);
|
||||
furi_string_free(nfc_dev->dev_data.parsed_data);
|
||||
furi_string_free(nfc_dev->folder);
|
||||
free(nfc_dev);
|
||||
}
|
||||
|
||||
@@ -1018,6 +1020,16 @@ static void nfc_device_get_shadow_path(FuriString* orig_path, FuriString* shadow
|
||||
furi_string_cat_printf(shadow_path, "%s", NFC_APP_SHADOW_EXTENSION);
|
||||
}
|
||||
|
||||
static void nfc_device_get_folder_from_path(FuriString* path, FuriString* folder) {
|
||||
size_t last_slash = furi_string_search_rchar(path, '/');
|
||||
if(last_slash == FURI_STRING_FAILURE) {
|
||||
// No slashes in the path, treat the whole path as a folder
|
||||
furi_string_set(folder, path);
|
||||
} else {
|
||||
furi_string_set_n(folder, path, 0, last_slash);
|
||||
}
|
||||
}
|
||||
|
||||
bool nfc_device_save(NfcDevice* dev, const char* dev_name) {
|
||||
furi_assert(dev);
|
||||
|
||||
@@ -1028,10 +1040,19 @@ bool nfc_device_save(NfcDevice* dev, const char* dev_name) {
|
||||
temp_str = furi_string_alloc();
|
||||
|
||||
do {
|
||||
// Create nfc directory if necessary
|
||||
if(!storage_simply_mkdir(dev->storage, NFC_APP_FOLDER)) break;
|
||||
// Create directory if necessary
|
||||
FuriString* folder = furi_string_alloc();
|
||||
// Get folder from filename (filename is in the form of "folder/filename.nfc", so the folder is "folder/")
|
||||
furi_string_set(temp_str, dev_name);
|
||||
// Get folder from filename
|
||||
nfc_device_get_folder_from_path(temp_str, folder);
|
||||
FURI_LOG_I("Nfc", "Saving to folder %s", furi_string_get_cstr(folder));
|
||||
if(!storage_simply_mkdir(dev->storage, furi_string_get_cstr(folder))) {
|
||||
FURI_LOG_E("Nfc", "Failed to create folder %s", furi_string_get_cstr(folder));
|
||||
break;
|
||||
}
|
||||
furi_string_free(folder);
|
||||
// First remove nfc device file if it was saved
|
||||
furi_string_printf(temp_str, "%s", dev_name);
|
||||
// Open file
|
||||
if(!flipper_format_file_open_always(file, furi_string_get_cstr(temp_str))) break;
|
||||
// Write header
|
||||
@@ -1199,10 +1220,9 @@ bool nfc_device_load(NfcDevice* dev, const char* file_path, bool show_dialog) {
|
||||
|
||||
bool nfc_file_select(NfcDevice* dev) {
|
||||
furi_assert(dev);
|
||||
const char* folder = furi_string_get_cstr(dev->folder);
|
||||
|
||||
// Input events and views are managed by file_browser
|
||||
FuriString* nfc_app_folder;
|
||||
nfc_app_folder = furi_string_alloc_set(NFC_APP_FOLDER);
|
||||
|
||||
const DialogsFileBrowserOptions browser_options = {
|
||||
.extension = NFC_APP_EXTENSION,
|
||||
@@ -1212,13 +1232,12 @@ bool nfc_file_select(NfcDevice* dev) {
|
||||
.hide_ext = true,
|
||||
.item_loader_callback = NULL,
|
||||
.item_loader_context = NULL,
|
||||
.base_path = NFC_APP_FOLDER,
|
||||
.base_path = folder,
|
||||
};
|
||||
|
||||
bool res =
|
||||
dialog_file_browser_show(dev->dialogs, dev->load_path, dev->load_path, &browser_options);
|
||||
|
||||
furi_string_free(nfc_app_folder);
|
||||
if(res) {
|
||||
FuriString* filename;
|
||||
filename = furi_string_alloc();
|
||||
@@ -1271,7 +1290,11 @@ bool nfc_device_delete(NfcDevice* dev, bool use_load_path) {
|
||||
furi_string_set(file_path, dev->load_path);
|
||||
} else {
|
||||
furi_string_printf(
|
||||
file_path, "%s/%s%s", NFC_APP_FOLDER, dev->dev_name, NFC_APP_EXTENSION);
|
||||
file_path,
|
||||
"%s/%s%s",
|
||||
furi_string_get_cstr(dev->folder),
|
||||
dev->dev_name,
|
||||
NFC_APP_EXTENSION);
|
||||
}
|
||||
if(!storage_simply_remove(dev->storage, furi_string_get_cstr(file_path))) break;
|
||||
// Delete shadow file if it exists
|
||||
@@ -1280,7 +1303,11 @@ bool nfc_device_delete(NfcDevice* dev, bool use_load_path) {
|
||||
nfc_device_get_shadow_path(dev->load_path, file_path);
|
||||
} else {
|
||||
furi_string_printf(
|
||||
file_path, "%s/%s%s", NFC_APP_FOLDER, dev->dev_name, NFC_APP_SHADOW_EXTENSION);
|
||||
file_path,
|
||||
"%s/%s%s",
|
||||
furi_string_get_cstr(dev->folder),
|
||||
dev->dev_name,
|
||||
NFC_APP_SHADOW_EXTENSION);
|
||||
}
|
||||
if(!storage_simply_remove(dev->storage, furi_string_get_cstr(file_path))) break;
|
||||
}
|
||||
@@ -1309,14 +1336,23 @@ bool nfc_device_restore(NfcDevice* dev, bool use_load_path) {
|
||||
nfc_device_get_shadow_path(dev->load_path, path);
|
||||
} else {
|
||||
furi_string_printf(
|
||||
path, "%s/%s%s", NFC_APP_FOLDER, dev->dev_name, NFC_APP_SHADOW_EXTENSION);
|
||||
path,
|
||||
"%s/%s%s",
|
||||
furi_string_get_cstr(dev->folder),
|
||||
dev->dev_name,
|
||||
NFC_APP_SHADOW_EXTENSION);
|
||||
}
|
||||
if(!storage_simply_remove(dev->storage, furi_string_get_cstr(path))) break;
|
||||
dev->shadow_file_exist = false;
|
||||
if(use_load_path && !furi_string_empty(dev->load_path)) {
|
||||
furi_string_set(path, dev->load_path);
|
||||
} else {
|
||||
furi_string_printf(path, "%s/%s%s", NFC_APP_FOLDER, dev->dev_name, NFC_APP_EXTENSION);
|
||||
furi_string_printf(
|
||||
path,
|
||||
"%s/%s%s",
|
||||
furi_string_get_cstr(dev->folder),
|
||||
dev->dev_name,
|
||||
NFC_APP_EXTENSION);
|
||||
}
|
||||
if(!nfc_device_load_data(dev, path, true)) break;
|
||||
restored = true;
|
||||
|
||||
@@ -20,7 +20,6 @@ extern "C" {
|
||||
#define NFC_READER_DATA_MAX_SIZE 64
|
||||
#define NFC_DICT_KEY_BATCH_SIZE 50
|
||||
|
||||
#define NFC_APP_FOLDER ANY_PATH("nfc")
|
||||
#define NFC_APP_EXTENSION ".nfc"
|
||||
#define NFC_APP_SHADOW_EXTENSION ".shd"
|
||||
|
||||
@@ -84,6 +83,7 @@ typedef struct {
|
||||
NfcDeviceData dev_data;
|
||||
char dev_name[NFC_DEV_NAME_MAX_LEN + 1];
|
||||
FuriString* load_path;
|
||||
FuriString* folder;
|
||||
NfcDeviceSaveFormat format;
|
||||
bool shadow_file_exist;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user