WIP integrate weather, tpms and pocsag to SubGhz app
@@ -1,14 +0,0 @@
|
||||
App(
|
||||
appid="pocsag_pager",
|
||||
name="POCSAG Pager",
|
||||
apptype=FlipperAppType.EXTERNAL,
|
||||
entry_point="pocsag_pager_app",
|
||||
requires=["gui"],
|
||||
stack_size=4 * 1024,
|
||||
fap_icon="pocsag_pager_10px.png",
|
||||
fap_category="Sub-GHz",
|
||||
fap_icon_assets="images",
|
||||
fap_author="@xMasterX & @Shmuma",
|
||||
fap_version="1.0",
|
||||
fap_description="App can capture POCSAG 1200 messages on CC1101 supported frequencies.",
|
||||
)
|
||||
@@ -1,14 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
typedef enum {
|
||||
//PCSGCustomEvent
|
||||
PCSGCustomEventStartId = 100,
|
||||
|
||||
PCSGCustomEventSceneSettingLock,
|
||||
|
||||
PCSGCustomEventViewReceiverOK,
|
||||
PCSGCustomEventViewReceiverConfig,
|
||||
PCSGCustomEventViewReceiverBack,
|
||||
PCSGCustomEventViewReceiverOffDisplay,
|
||||
PCSGCustomEventViewReceiverUnlock,
|
||||
} PCSGCustomEvent;
|
||||
@@ -1,49 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <furi.h>
|
||||
#include <furi_hal.h>
|
||||
|
||||
#define PCSG_VERSION_APP "0.1"
|
||||
#define PCSG_DEVELOPED "@xMasterX & @Shmuma"
|
||||
#define PCSG_GITHUB "https://github.com/xMasterX/flipper-pager"
|
||||
|
||||
#define PCSG_KEY_FILE_VERSION 1
|
||||
#define PCSG_KEY_FILE_TYPE "Flipper POCSAG Pager Key File"
|
||||
|
||||
/** PCSGRxKeyState state */
|
||||
typedef enum {
|
||||
PCSGRxKeyStateIDLE,
|
||||
PCSGRxKeyStateBack,
|
||||
PCSGRxKeyStateStart,
|
||||
PCSGRxKeyStateAddKey,
|
||||
} PCSGRxKeyState;
|
||||
|
||||
/** PCSGHopperState state */
|
||||
typedef enum {
|
||||
PCSGHopperStateOFF,
|
||||
PCSGHopperStateRunnig,
|
||||
PCSGHopperStatePause,
|
||||
PCSGHopperStateRSSITimeOut,
|
||||
} PCSGHopperState;
|
||||
|
||||
/** PCSGLock */
|
||||
typedef enum {
|
||||
PCSGLockOff,
|
||||
PCSGLockOn,
|
||||
} PCSGLock;
|
||||
|
||||
typedef enum {
|
||||
POCSAGPagerViewVariableItemList,
|
||||
POCSAGPagerViewSubmenu,
|
||||
POCSAGPagerViewReceiver,
|
||||
POCSAGPagerViewReceiverInfo,
|
||||
POCSAGPagerViewWidget,
|
||||
} POCSAGPagerView;
|
||||
|
||||
/** POCSAGPagerTxRx state */
|
||||
typedef enum {
|
||||
PCSGTxRxStateIDLE,
|
||||
PCSGTxRxStateRx,
|
||||
PCSGTxRxStateTx,
|
||||
PCSGTxRxStateSleep,
|
||||
} PCSGTxRxState;
|
||||
@@ -1,66 +0,0 @@
|
||||
#include "radio_device_loader.h"
|
||||
|
||||
#include <applications/drivers/subghz/cc1101_ext/cc1101_ext_interconnect.h>
|
||||
#include <lib/subghz/devices/cc1101_int/cc1101_int_interconnect.h>
|
||||
|
||||
static void radio_device_loader_power_on() {
|
||||
uint8_t attempts = 0;
|
||||
while(!furi_hal_power_is_otg_enabled() && attempts++ < 5) {
|
||||
furi_hal_power_enable_otg();
|
||||
//CC1101 power-up time
|
||||
furi_delay_ms(10);
|
||||
}
|
||||
}
|
||||
|
||||
static void radio_device_loader_power_off() {
|
||||
if(furi_hal_power_is_otg_enabled()) furi_hal_power_disable_otg();
|
||||
}
|
||||
|
||||
bool radio_device_loader_is_connect_external(const char* name) {
|
||||
bool is_connect = false;
|
||||
bool is_otg_enabled = furi_hal_power_is_otg_enabled();
|
||||
|
||||
if(!is_otg_enabled) {
|
||||
radio_device_loader_power_on();
|
||||
}
|
||||
|
||||
is_connect = subghz_devices_is_connect(subghz_devices_get_by_name(name));
|
||||
|
||||
if(!is_otg_enabled) {
|
||||
radio_device_loader_power_off();
|
||||
}
|
||||
return is_connect;
|
||||
}
|
||||
|
||||
const SubGhzDevice* radio_device_loader_set(
|
||||
const SubGhzDevice* current_radio_device,
|
||||
SubGhzRadioDeviceType radio_device_type) {
|
||||
const SubGhzDevice* radio_device;
|
||||
|
||||
if(radio_device_type == SubGhzRadioDeviceTypeExternalCC1101 &&
|
||||
radio_device_loader_is_connect_external(SUBGHZ_DEVICE_CC1101_EXT_NAME)) {
|
||||
radio_device_loader_power_on();
|
||||
radio_device = subghz_devices_get_by_name(SUBGHZ_DEVICE_CC1101_EXT_NAME);
|
||||
subghz_devices_begin(radio_device);
|
||||
} else if(current_radio_device == NULL) {
|
||||
radio_device = subghz_devices_get_by_name(SUBGHZ_DEVICE_CC1101_INT_NAME);
|
||||
} else {
|
||||
radio_device_loader_end(current_radio_device);
|
||||
radio_device = subghz_devices_get_by_name(SUBGHZ_DEVICE_CC1101_INT_NAME);
|
||||
}
|
||||
|
||||
return radio_device;
|
||||
}
|
||||
|
||||
bool radio_device_loader_is_external(const SubGhzDevice* radio_device) {
|
||||
furi_assert(radio_device);
|
||||
return (radio_device != subghz_devices_get_by_name(SUBGHZ_DEVICE_CC1101_INT_NAME));
|
||||
}
|
||||
|
||||
void radio_device_loader_end(const SubGhzDevice* radio_device) {
|
||||
furi_assert(radio_device);
|
||||
radio_device_loader_power_off();
|
||||
if(radio_device != subghz_devices_get_by_name(SUBGHZ_DEVICE_CC1101_INT_NAME)) {
|
||||
subghz_devices_end(radio_device);
|
||||
}
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <lib/subghz/devices/devices.h>
|
||||
|
||||
/** SubGhzRadioDeviceType */
|
||||
typedef enum {
|
||||
SubGhzRadioDeviceTypeInternal,
|
||||
SubGhzRadioDeviceTypeExternalCC1101,
|
||||
} SubGhzRadioDeviceType;
|
||||
|
||||
const SubGhzDevice* radio_device_loader_set(
|
||||
const SubGhzDevice* current_radio_device,
|
||||
SubGhzRadioDeviceType radio_device_type);
|
||||
|
||||
bool radio_device_loader_is_external(const SubGhzDevice* radio_device);
|
||||
|
||||
void radio_device_loader_end(const SubGhzDevice* radio_device);
|
||||
|
Before Width: | Height: | Size: 130 B |
|
Before Width: | Height: | Size: 134 B |
@@ -1,206 +0,0 @@
|
||||
#include "pocsag_pager_app_i.h"
|
||||
|
||||
#include <furi.h>
|
||||
#include <furi_hal.h>
|
||||
#include <lib/flipper_format/flipper_format.h>
|
||||
#include "protocols/protocol_items.h"
|
||||
|
||||
static bool pocsag_pager_app_custom_event_callback(void* context, uint32_t event) {
|
||||
furi_assert(context);
|
||||
POCSAGPagerApp* app = context;
|
||||
return scene_manager_handle_custom_event(app->scene_manager, event);
|
||||
}
|
||||
|
||||
static bool pocsag_pager_app_back_event_callback(void* context) {
|
||||
furi_assert(context);
|
||||
POCSAGPagerApp* app = context;
|
||||
return scene_manager_handle_back_event(app->scene_manager);
|
||||
}
|
||||
|
||||
static void pocsag_pager_app_tick_event_callback(void* context) {
|
||||
furi_assert(context);
|
||||
POCSAGPagerApp* app = context;
|
||||
scene_manager_handle_tick_event(app->scene_manager);
|
||||
}
|
||||
|
||||
POCSAGPagerApp* pocsag_pager_app_alloc() {
|
||||
POCSAGPagerApp* app = malloc(sizeof(POCSAGPagerApp));
|
||||
|
||||
// GUI
|
||||
app->gui = furi_record_open(RECORD_GUI);
|
||||
|
||||
// View Dispatcher
|
||||
app->view_dispatcher = view_dispatcher_alloc();
|
||||
app->scene_manager = scene_manager_alloc(&pocsag_pager_scene_handlers, app);
|
||||
view_dispatcher_enable_queue(app->view_dispatcher);
|
||||
|
||||
view_dispatcher_set_event_callback_context(app->view_dispatcher, app);
|
||||
view_dispatcher_set_custom_event_callback(
|
||||
app->view_dispatcher, pocsag_pager_app_custom_event_callback);
|
||||
view_dispatcher_set_navigation_event_callback(
|
||||
app->view_dispatcher, pocsag_pager_app_back_event_callback);
|
||||
view_dispatcher_set_tick_event_callback(
|
||||
app->view_dispatcher, pocsag_pager_app_tick_event_callback, 100);
|
||||
|
||||
view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen);
|
||||
|
||||
// Open Notification record
|
||||
app->notifications = furi_record_open(RECORD_NOTIFICATION);
|
||||
|
||||
// Variable Item List
|
||||
app->variable_item_list = variable_item_list_alloc();
|
||||
view_dispatcher_add_view(
|
||||
app->view_dispatcher,
|
||||
POCSAGPagerViewVariableItemList,
|
||||
variable_item_list_get_view(app->variable_item_list));
|
||||
|
||||
// SubMenu
|
||||
app->submenu = submenu_alloc();
|
||||
view_dispatcher_add_view(
|
||||
app->view_dispatcher, POCSAGPagerViewSubmenu, submenu_get_view(app->submenu));
|
||||
|
||||
// Widget
|
||||
app->widget = widget_alloc();
|
||||
view_dispatcher_add_view(
|
||||
app->view_dispatcher, POCSAGPagerViewWidget, widget_get_view(app->widget));
|
||||
|
||||
// Receiver
|
||||
app->pcsg_receiver = pcsg_view_receiver_alloc();
|
||||
view_dispatcher_add_view(
|
||||
app->view_dispatcher,
|
||||
POCSAGPagerViewReceiver,
|
||||
pcsg_view_receiver_get_view(app->pcsg_receiver));
|
||||
|
||||
// Receiver Info
|
||||
app->pcsg_receiver_info = pcsg_view_receiver_info_alloc();
|
||||
view_dispatcher_add_view(
|
||||
app->view_dispatcher,
|
||||
POCSAGPagerViewReceiverInfo,
|
||||
pcsg_view_receiver_info_get_view(app->pcsg_receiver_info));
|
||||
|
||||
//init setting
|
||||
app->setting = subghz_setting_alloc();
|
||||
|
||||
//ToDo FIX file name setting
|
||||
|
||||
subghz_setting_load(app->setting, EXT_PATH("pocsag/settings.txt"));
|
||||
|
||||
//init Worker & Protocol & History
|
||||
app->lock = PCSGLockOff;
|
||||
app->txrx = malloc(sizeof(POCSAGPagerTxRx));
|
||||
app->txrx->preset = malloc(sizeof(SubGhzRadioPreset));
|
||||
app->txrx->preset->name = furi_string_alloc();
|
||||
|
||||
furi_hal_power_suppress_charge_enter();
|
||||
|
||||
// Radio Devices init & load
|
||||
subghz_devices_init();
|
||||
app->txrx->radio_device =
|
||||
radio_device_loader_set(app->txrx->radio_device, SubGhzRadioDeviceTypeExternalCC1101);
|
||||
|
||||
subghz_devices_reset(app->txrx->radio_device);
|
||||
subghz_devices_idle(app->txrx->radio_device);
|
||||
|
||||
// Custom Presets load without using config file
|
||||
|
||||
FlipperFormat* temp_fm_preset = flipper_format_string_alloc();
|
||||
flipper_format_write_string_cstr(
|
||||
temp_fm_preset,
|
||||
(const char*)"Custom_preset_data",
|
||||
(const char*)"02 0D 0B 06 08 32 07 04 14 00 13 02 12 04 11 83 10 67 15 24 18 18 19 16 1D 91 1C 00 1B 07 20 FB 22 10 21 56 00 00 C0 00 00 00 00 00 00 00");
|
||||
flipper_format_rewind(temp_fm_preset);
|
||||
subghz_setting_load_custom_preset(app->setting, (const char*)"FM95", temp_fm_preset);
|
||||
|
||||
flipper_format_free(temp_fm_preset);
|
||||
|
||||
// custom presets loading - end
|
||||
|
||||
pcsg_preset_init(app, "FM95", 439987500, NULL, 0);
|
||||
|
||||
app->txrx->hopper_state = PCSGHopperStateOFF;
|
||||
app->txrx->history = pcsg_history_alloc();
|
||||
app->txrx->worker = subghz_worker_alloc();
|
||||
app->txrx->environment = subghz_environment_alloc();
|
||||
subghz_environment_set_protocol_registry(
|
||||
app->txrx->environment, (void*)&pocsag_pager_protocol_registry);
|
||||
app->txrx->receiver = subghz_receiver_alloc_init(app->txrx->environment);
|
||||
|
||||
subghz_receiver_set_filter(app->txrx->receiver, SubGhzProtocolFlag_Decodable);
|
||||
subghz_worker_set_overrun_callback(
|
||||
app->txrx->worker, (SubGhzWorkerOverrunCallback)subghz_receiver_reset);
|
||||
subghz_worker_set_pair_callback(
|
||||
app->txrx->worker, (SubGhzWorkerPairCallback)subghz_receiver_decode);
|
||||
subghz_worker_set_context(app->txrx->worker, app->txrx->receiver);
|
||||
|
||||
scene_manager_next_scene(app->scene_manager, POCSAGPagerSceneStart);
|
||||
|
||||
return app;
|
||||
}
|
||||
|
||||
void pocsag_pager_app_free(POCSAGPagerApp* app) {
|
||||
furi_assert(app);
|
||||
|
||||
// Radio Devices sleep & off
|
||||
pcsg_sleep(app);
|
||||
radio_device_loader_end(app->txrx->radio_device);
|
||||
|
||||
subghz_devices_deinit();
|
||||
|
||||
// Submenu
|
||||
view_dispatcher_remove_view(app->view_dispatcher, POCSAGPagerViewSubmenu);
|
||||
submenu_free(app->submenu);
|
||||
|
||||
// Variable Item List
|
||||
view_dispatcher_remove_view(app->view_dispatcher, POCSAGPagerViewVariableItemList);
|
||||
variable_item_list_free(app->variable_item_list);
|
||||
|
||||
// Widget
|
||||
view_dispatcher_remove_view(app->view_dispatcher, POCSAGPagerViewWidget);
|
||||
widget_free(app->widget);
|
||||
|
||||
// Receiver
|
||||
view_dispatcher_remove_view(app->view_dispatcher, POCSAGPagerViewReceiver);
|
||||
pcsg_view_receiver_free(app->pcsg_receiver);
|
||||
|
||||
// Receiver Info
|
||||
view_dispatcher_remove_view(app->view_dispatcher, POCSAGPagerViewReceiverInfo);
|
||||
pcsg_view_receiver_info_free(app->pcsg_receiver_info);
|
||||
|
||||
//setting
|
||||
subghz_setting_free(app->setting);
|
||||
|
||||
//Worker & Protocol & History
|
||||
subghz_receiver_free(app->txrx->receiver);
|
||||
subghz_environment_free(app->txrx->environment);
|
||||
pcsg_history_free(app->txrx->history);
|
||||
subghz_worker_free(app->txrx->worker);
|
||||
furi_string_free(app->txrx->preset->name);
|
||||
free(app->txrx->preset);
|
||||
free(app->txrx);
|
||||
|
||||
// View dispatcher
|
||||
view_dispatcher_free(app->view_dispatcher);
|
||||
scene_manager_free(app->scene_manager);
|
||||
|
||||
// Notifications
|
||||
furi_record_close(RECORD_NOTIFICATION);
|
||||
app->notifications = NULL;
|
||||
|
||||
// Close records
|
||||
furi_record_close(RECORD_GUI);
|
||||
|
||||
furi_hal_power_suppress_charge_exit();
|
||||
|
||||
free(app);
|
||||
}
|
||||
|
||||
int32_t pocsag_pager_app(void* p) {
|
||||
UNUSED(p);
|
||||
POCSAGPagerApp* pocsag_pager_app = pocsag_pager_app_alloc();
|
||||
|
||||
view_dispatcher_run(pocsag_pager_app->view_dispatcher);
|
||||
|
||||
pocsag_pager_app_free(pocsag_pager_app);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1,146 +0,0 @@
|
||||
#include "pocsag_pager_app_i.h"
|
||||
|
||||
#define TAG "POCSAGPager"
|
||||
#include <flipper_format/flipper_format_i.h>
|
||||
|
||||
void pcsg_preset_init(
|
||||
void* context,
|
||||
const char* preset_name,
|
||||
uint32_t frequency,
|
||||
uint8_t* preset_data,
|
||||
size_t preset_data_size) {
|
||||
furi_assert(context);
|
||||
POCSAGPagerApp* app = context;
|
||||
furi_string_set(app->txrx->preset->name, preset_name);
|
||||
app->txrx->preset->frequency = frequency;
|
||||
app->txrx->preset->data = preset_data;
|
||||
app->txrx->preset->data_size = preset_data_size;
|
||||
}
|
||||
|
||||
void pcsg_get_frequency_modulation(
|
||||
POCSAGPagerApp* app,
|
||||
FuriString* frequency,
|
||||
FuriString* modulation) {
|
||||
furi_assert(app);
|
||||
if(frequency != NULL) {
|
||||
furi_string_printf(
|
||||
frequency,
|
||||
"%03ld.%02ld",
|
||||
app->txrx->preset->frequency / 1000000 % 1000,
|
||||
app->txrx->preset->frequency / 10000 % 100);
|
||||
}
|
||||
if(modulation != NULL) {
|
||||
furi_string_printf(modulation, "%.2s", furi_string_get_cstr(app->txrx->preset->name));
|
||||
}
|
||||
}
|
||||
|
||||
void pcsg_begin(POCSAGPagerApp* app, uint8_t* preset_data) {
|
||||
furi_assert(app);
|
||||
|
||||
subghz_devices_reset(app->txrx->radio_device);
|
||||
subghz_devices_idle(app->txrx->radio_device);
|
||||
subghz_devices_load_preset(app->txrx->radio_device, FuriHalSubGhzPresetCustom, preset_data);
|
||||
|
||||
// furi_hal_gpio_init(furi_hal_subghz.cc1101_g0_pin, GpioModeInput, GpioPullNo, GpioSpeedLow);
|
||||
app->txrx->txrx_state = PCSGTxRxStateIDLE;
|
||||
}
|
||||
|
||||
uint32_t pcsg_rx(POCSAGPagerApp* app, uint32_t frequency) {
|
||||
furi_assert(app);
|
||||
if(!subghz_devices_is_frequency_valid(app->txrx->radio_device, frequency)) {
|
||||
furi_crash("POCSAGPager: Incorrect RX frequency.");
|
||||
}
|
||||
furi_assert(
|
||||
app->txrx->txrx_state != PCSGTxRxStateRx && app->txrx->txrx_state != PCSGTxRxStateSleep);
|
||||
|
||||
subghz_devices_idle(app->txrx->radio_device);
|
||||
uint32_t value = subghz_devices_set_frequency(app->txrx->radio_device, frequency);
|
||||
|
||||
// Not need. init in subghz_devices_start_async_tx
|
||||
// furi_hal_gpio_init(furi_hal_subghz.cc1101_g0_pin, GpioModeInput, GpioPullNo, GpioSpeedLow);
|
||||
|
||||
subghz_devices_flush_rx(app->txrx->radio_device);
|
||||
subghz_devices_set_rx(app->txrx->radio_device);
|
||||
|
||||
subghz_devices_start_async_rx(
|
||||
app->txrx->radio_device, subghz_worker_rx_callback, app->txrx->worker);
|
||||
subghz_worker_start(app->txrx->worker);
|
||||
app->txrx->txrx_state = PCSGTxRxStateRx;
|
||||
return value;
|
||||
}
|
||||
|
||||
void pcsg_idle(POCSAGPagerApp* app) {
|
||||
furi_assert(app);
|
||||
furi_assert(app->txrx->txrx_state != PCSGTxRxStateSleep);
|
||||
subghz_devices_idle(app->txrx->radio_device);
|
||||
app->txrx->txrx_state = PCSGTxRxStateIDLE;
|
||||
}
|
||||
|
||||
void pcsg_rx_end(POCSAGPagerApp* app) {
|
||||
furi_assert(app);
|
||||
furi_assert(app->txrx->txrx_state == PCSGTxRxStateRx);
|
||||
if(subghz_worker_is_running(app->txrx->worker)) {
|
||||
subghz_worker_stop(app->txrx->worker);
|
||||
subghz_devices_stop_async_rx(app->txrx->radio_device);
|
||||
}
|
||||
subghz_devices_idle(app->txrx->radio_device);
|
||||
app->txrx->txrx_state = PCSGTxRxStateIDLE;
|
||||
}
|
||||
|
||||
void pcsg_sleep(POCSAGPagerApp* app) {
|
||||
furi_assert(app);
|
||||
subghz_devices_sleep(app->txrx->radio_device);
|
||||
app->txrx->txrx_state = PCSGTxRxStateSleep;
|
||||
}
|
||||
|
||||
void pcsg_hopper_update(POCSAGPagerApp* app) {
|
||||
furi_assert(app);
|
||||
|
||||
switch(app->txrx->hopper_state) {
|
||||
case PCSGHopperStateOFF:
|
||||
return;
|
||||
break;
|
||||
case PCSGHopperStatePause:
|
||||
return;
|
||||
break;
|
||||
case PCSGHopperStateRSSITimeOut:
|
||||
if(app->txrx->hopper_timeout != 0) {
|
||||
app->txrx->hopper_timeout--;
|
||||
return;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
float rssi = -127.0f;
|
||||
if(app->txrx->hopper_state != PCSGHopperStateRSSITimeOut) {
|
||||
// See RSSI Calculation timings in CC1101 17.3 RSSI
|
||||
rssi = subghz_devices_get_rssi(app->txrx->radio_device);
|
||||
|
||||
// Stay if RSSI is high enough
|
||||
if(rssi > -90.0f) {
|
||||
app->txrx->hopper_timeout = 10;
|
||||
app->txrx->hopper_state = PCSGHopperStateRSSITimeOut;
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
app->txrx->hopper_state = PCSGHopperStateRunnig;
|
||||
}
|
||||
// Select next frequency
|
||||
if(app->txrx->hopper_idx_frequency <
|
||||
subghz_setting_get_hopper_frequency_count(app->setting) - 1) {
|
||||
app->txrx->hopper_idx_frequency++;
|
||||
} else {
|
||||
app->txrx->hopper_idx_frequency = 0;
|
||||
}
|
||||
|
||||
if(app->txrx->txrx_state == PCSGTxRxStateRx) {
|
||||
pcsg_rx_end(app);
|
||||
};
|
||||
if(app->txrx->txrx_state == PCSGTxRxStateIDLE) {
|
||||
subghz_receiver_reset(app->txrx->receiver);
|
||||
app->txrx->preset->frequency =
|
||||
subghz_setting_get_hopper_frequency(app->setting, app->txrx->hopper_idx_frequency);
|
||||
pcsg_rx(app, app->txrx->preset->frequency);
|
||||
}
|
||||
}
|
||||
@@ -1,76 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "helpers/pocsag_pager_types.h"
|
||||
#include "helpers/radio_device_loader.h"
|
||||
|
||||
#include "scenes/pocsag_pager_scene.h"
|
||||
#include <gui/gui.h>
|
||||
#include <gui/view_dispatcher.h>
|
||||
#include <gui/scene_manager.h>
|
||||
#include <gui/modules/submenu.h>
|
||||
#include <gui/modules/variable_item_list.h>
|
||||
#include <gui/modules/widget.h>
|
||||
#include <notification/notification_messages.h>
|
||||
#include "views/pocsag_pager_receiver.h"
|
||||
#include "views/pocsag_pager_receiver_info.h"
|
||||
#include "pocsag_pager_history.h"
|
||||
|
||||
#include <lib/subghz/subghz_setting.h>
|
||||
#include <lib/subghz/subghz_worker.h>
|
||||
#include <lib/subghz/receiver.h>
|
||||
#include <lib/subghz/transmitter.h>
|
||||
#include <lib/subghz/registry.h>
|
||||
#include <lib/subghz/devices/devices.h>
|
||||
|
||||
typedef struct POCSAGPagerApp POCSAGPagerApp;
|
||||
|
||||
struct POCSAGPagerTxRx {
|
||||
SubGhzWorker* worker;
|
||||
|
||||
SubGhzEnvironment* environment;
|
||||
SubGhzReceiver* receiver;
|
||||
SubGhzRadioPreset* preset;
|
||||
PCSGHistory* history;
|
||||
uint16_t idx_menu_chosen;
|
||||
PCSGTxRxState txrx_state;
|
||||
PCSGHopperState hopper_state;
|
||||
uint8_t hopper_timeout;
|
||||
uint8_t hopper_idx_frequency;
|
||||
PCSGRxKeyState rx_key_state;
|
||||
|
||||
const SubGhzDevice* radio_device;
|
||||
};
|
||||
|
||||
typedef struct POCSAGPagerTxRx POCSAGPagerTxRx;
|
||||
|
||||
struct POCSAGPagerApp {
|
||||
Gui* gui;
|
||||
ViewDispatcher* view_dispatcher;
|
||||
POCSAGPagerTxRx* txrx;
|
||||
SceneManager* scene_manager;
|
||||
NotificationApp* notifications;
|
||||
VariableItemList* variable_item_list;
|
||||
Submenu* submenu;
|
||||
Widget* widget;
|
||||
PCSGReceiver* pcsg_receiver;
|
||||
PCSGReceiverInfo* pcsg_receiver_info;
|
||||
PCSGLock lock;
|
||||
SubGhzSetting* setting;
|
||||
};
|
||||
|
||||
void pcsg_preset_init(
|
||||
void* context,
|
||||
const char* preset_name,
|
||||
uint32_t frequency,
|
||||
uint8_t* preset_data,
|
||||
size_t preset_data_size);
|
||||
void pcsg_get_frequency_modulation(
|
||||
POCSAGPagerApp* app,
|
||||
FuriString* frequency,
|
||||
FuriString* modulation);
|
||||
void pcsg_begin(POCSAGPagerApp* app, uint8_t* preset_data);
|
||||
uint32_t pcsg_rx(POCSAGPagerApp* app, uint32_t frequency);
|
||||
void pcsg_idle(POCSAGPagerApp* app);
|
||||
void pcsg_rx_end(POCSAGPagerApp* app);
|
||||
void pcsg_sleep(POCSAGPagerApp* app);
|
||||
void pcsg_hopper_update(POCSAGPagerApp* app);
|
||||
@@ -1,223 +0,0 @@
|
||||
#include "pocsag_pager_history.h"
|
||||
#include <flipper_format/flipper_format_i.h>
|
||||
#include <lib/toolbox/stream/stream.h>
|
||||
#include <lib/subghz/receiver.h>
|
||||
#include "protocols/pcsg_generic.h"
|
||||
|
||||
#include <furi.h>
|
||||
|
||||
#define PCSG_HISTORY_MAX 50
|
||||
#define TAG "PCSGHistory"
|
||||
|
||||
typedef struct {
|
||||
FuriString* item_str;
|
||||
FlipperFormat* flipper_string;
|
||||
uint8_t type;
|
||||
SubGhzRadioPreset* preset;
|
||||
} PCSGHistoryItem;
|
||||
|
||||
ARRAY_DEF(PCSGHistoryItemArray, PCSGHistoryItem, M_POD_OPLIST)
|
||||
|
||||
#define M_OPL_PCSGHistoryItemArray_t() ARRAY_OPLIST(PCSGHistoryItemArray, M_POD_OPLIST)
|
||||
|
||||
typedef struct {
|
||||
PCSGHistoryItemArray_t data;
|
||||
} PCSGHistoryStruct;
|
||||
|
||||
struct PCSGHistory {
|
||||
uint32_t last_update_timestamp;
|
||||
uint16_t last_index_write;
|
||||
uint8_t code_last_hash_data;
|
||||
FuriString* tmp_string;
|
||||
PCSGHistoryStruct* history;
|
||||
};
|
||||
|
||||
PCSGHistory* pcsg_history_alloc(void) {
|
||||
PCSGHistory* instance = malloc(sizeof(PCSGHistory));
|
||||
instance->tmp_string = furi_string_alloc();
|
||||
instance->history = malloc(sizeof(PCSGHistoryStruct));
|
||||
PCSGHistoryItemArray_init(instance->history->data);
|
||||
return instance;
|
||||
}
|
||||
|
||||
void pcsg_history_free(PCSGHistory* instance) {
|
||||
furi_assert(instance);
|
||||
furi_string_free(instance->tmp_string);
|
||||
for
|
||||
M_EACH(item, instance->history->data, PCSGHistoryItemArray_t) {
|
||||
furi_string_free(item->item_str);
|
||||
furi_string_free(item->preset->name);
|
||||
free(item->preset);
|
||||
flipper_format_free(item->flipper_string);
|
||||
item->type = 0;
|
||||
}
|
||||
PCSGHistoryItemArray_clear(instance->history->data);
|
||||
free(instance->history);
|
||||
free(instance);
|
||||
}
|
||||
|
||||
uint32_t pcsg_history_get_frequency(PCSGHistory* instance, uint16_t idx) {
|
||||
furi_assert(instance);
|
||||
PCSGHistoryItem* item = PCSGHistoryItemArray_get(instance->history->data, idx);
|
||||
return item->preset->frequency;
|
||||
}
|
||||
|
||||
SubGhzRadioPreset* pcsg_history_get_radio_preset(PCSGHistory* instance, uint16_t idx) {
|
||||
furi_assert(instance);
|
||||
PCSGHistoryItem* item = PCSGHistoryItemArray_get(instance->history->data, idx);
|
||||
return item->preset;
|
||||
}
|
||||
|
||||
const char* pcsg_history_get_preset(PCSGHistory* instance, uint16_t idx) {
|
||||
furi_assert(instance);
|
||||
PCSGHistoryItem* item = PCSGHistoryItemArray_get(instance->history->data, idx);
|
||||
return furi_string_get_cstr(item->preset->name);
|
||||
}
|
||||
|
||||
void pcsg_history_reset(PCSGHistory* instance) {
|
||||
furi_assert(instance);
|
||||
furi_string_reset(instance->tmp_string);
|
||||
for
|
||||
M_EACH(item, instance->history->data, PCSGHistoryItemArray_t) {
|
||||
furi_string_free(item->item_str);
|
||||
furi_string_free(item->preset->name);
|
||||
free(item->preset);
|
||||
flipper_format_free(item->flipper_string);
|
||||
item->type = 0;
|
||||
}
|
||||
PCSGHistoryItemArray_reset(instance->history->data);
|
||||
instance->last_index_write = 0;
|
||||
instance->code_last_hash_data = 0;
|
||||
}
|
||||
|
||||
uint16_t pcsg_history_get_item(PCSGHistory* instance) {
|
||||
furi_assert(instance);
|
||||
return instance->last_index_write;
|
||||
}
|
||||
|
||||
uint8_t pcsg_history_get_type_protocol(PCSGHistory* instance, uint16_t idx) {
|
||||
furi_assert(instance);
|
||||
PCSGHistoryItem* item = PCSGHistoryItemArray_get(instance->history->data, idx);
|
||||
return item->type;
|
||||
}
|
||||
|
||||
const char* pcsg_history_get_protocol_name(PCSGHistory* instance, uint16_t idx) {
|
||||
furi_assert(instance);
|
||||
PCSGHistoryItem* item = PCSGHistoryItemArray_get(instance->history->data, idx);
|
||||
flipper_format_rewind(item->flipper_string);
|
||||
if(!flipper_format_read_string(item->flipper_string, "Protocol", instance->tmp_string)) {
|
||||
FURI_LOG_E(TAG, "Missing Protocol");
|
||||
furi_string_reset(instance->tmp_string);
|
||||
}
|
||||
return furi_string_get_cstr(instance->tmp_string);
|
||||
}
|
||||
|
||||
FlipperFormat* pcsg_history_get_raw_data(PCSGHistory* instance, uint16_t idx) {
|
||||
furi_assert(instance);
|
||||
PCSGHistoryItem* item = PCSGHistoryItemArray_get(instance->history->data, idx);
|
||||
if(item->flipper_string) {
|
||||
return item->flipper_string;
|
||||
} else {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
bool pcsg_history_get_text_space_left(PCSGHistory* instance, FuriString* output) {
|
||||
furi_assert(instance);
|
||||
if(instance->last_index_write == PCSG_HISTORY_MAX) {
|
||||
if(output != NULL) furi_string_printf(output, "Memory is FULL");
|
||||
return true;
|
||||
}
|
||||
if(output != NULL)
|
||||
furi_string_printf(output, "%02u/%02u", instance->last_index_write, PCSG_HISTORY_MAX);
|
||||
return false;
|
||||
}
|
||||
|
||||
void pcsg_history_get_text_item_menu(PCSGHistory* instance, FuriString* output, uint16_t idx) {
|
||||
PCSGHistoryItem* item = PCSGHistoryItemArray_get(instance->history->data, idx);
|
||||
furi_string_set(output, item->item_str);
|
||||
}
|
||||
|
||||
PCSGHistoryStateAddKey
|
||||
pcsg_history_add_to_history(PCSGHistory* instance, void* context, SubGhzRadioPreset* preset) {
|
||||
furi_assert(instance);
|
||||
furi_assert(context);
|
||||
|
||||
if(instance->last_index_write >= PCSG_HISTORY_MAX) return PCSGHistoryStateAddKeyOverflow;
|
||||
|
||||
SubGhzProtocolDecoderBase* decoder_base = context;
|
||||
if((instance->code_last_hash_data ==
|
||||
subghz_protocol_decoder_base_get_hash_data(decoder_base)) &&
|
||||
((furi_get_tick() - instance->last_update_timestamp) < 500)) {
|
||||
instance->last_update_timestamp = furi_get_tick();
|
||||
return PCSGHistoryStateAddKeyTimeOut;
|
||||
}
|
||||
|
||||
instance->code_last_hash_data = subghz_protocol_decoder_base_get_hash_data(decoder_base);
|
||||
instance->last_update_timestamp = furi_get_tick();
|
||||
|
||||
FlipperFormat* fff = flipper_format_string_alloc();
|
||||
subghz_protocol_decoder_base_serialize(decoder_base, fff, preset);
|
||||
|
||||
do {
|
||||
if(!flipper_format_rewind(fff)) {
|
||||
FURI_LOG_E(TAG, "Rewind error");
|
||||
break;
|
||||
}
|
||||
|
||||
} while(false);
|
||||
flipper_format_free(fff);
|
||||
|
||||
PCSGHistoryItem* item = PCSGHistoryItemArray_push_raw(instance->history->data);
|
||||
item->preset = malloc(sizeof(SubGhzRadioPreset));
|
||||
item->type = decoder_base->protocol->type;
|
||||
item->preset->frequency = preset->frequency;
|
||||
item->preset->name = furi_string_alloc();
|
||||
furi_string_set(item->preset->name, preset->name);
|
||||
item->preset->data = preset->data;
|
||||
item->preset->data_size = preset->data_size;
|
||||
|
||||
item->item_str = furi_string_alloc();
|
||||
item->flipper_string = flipper_format_string_alloc();
|
||||
subghz_protocol_decoder_base_serialize(decoder_base, item->flipper_string, preset);
|
||||
|
||||
do {
|
||||
if(!flipper_format_rewind(item->flipper_string)) {
|
||||
FURI_LOG_E(TAG, "Rewind error");
|
||||
break;
|
||||
}
|
||||
if(!flipper_format_read_string(item->flipper_string, "Protocol", instance->tmp_string)) {
|
||||
FURI_LOG_E(TAG, "Missing Protocol");
|
||||
break;
|
||||
}
|
||||
|
||||
if(!flipper_format_rewind(item->flipper_string)) {
|
||||
FURI_LOG_E(TAG, "Rewind error");
|
||||
break;
|
||||
}
|
||||
FuriString* temp_ric = furi_string_alloc();
|
||||
if(!flipper_format_read_string(item->flipper_string, "Ric", temp_ric)) {
|
||||
FURI_LOG_E(TAG, "Missing Ric");
|
||||
break;
|
||||
}
|
||||
|
||||
FuriString* temp_message = furi_string_alloc();
|
||||
if(!flipper_format_read_string(item->flipper_string, "Message", temp_message)) {
|
||||
FURI_LOG_E(TAG, "Missing Message");
|
||||
break;
|
||||
}
|
||||
|
||||
furi_string_printf(
|
||||
item->item_str,
|
||||
"%s%s",
|
||||
furi_string_get_cstr(temp_ric),
|
||||
furi_string_get_cstr(temp_message));
|
||||
|
||||
furi_string_free(temp_message);
|
||||
furi_string_free(temp_ric);
|
||||
|
||||
} while(false);
|
||||
instance->last_index_write++;
|
||||
return PCSGHistoryStateAddKeyNewDada;
|
||||
|
||||
return PCSGHistoryStateAddKeyUnknown;
|
||||
}
|
||||
@@ -1,112 +0,0 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <math.h>
|
||||
#include <furi.h>
|
||||
#include <furi_hal.h>
|
||||
#include <lib/flipper_format/flipper_format.h>
|
||||
#include <lib/subghz/types.h>
|
||||
|
||||
typedef struct PCSGHistory PCSGHistory;
|
||||
|
||||
/** History state add key */
|
||||
typedef enum {
|
||||
PCSGHistoryStateAddKeyUnknown,
|
||||
PCSGHistoryStateAddKeyTimeOut,
|
||||
PCSGHistoryStateAddKeyNewDada,
|
||||
PCSGHistoryStateAddKeyUpdateData,
|
||||
PCSGHistoryStateAddKeyOverflow,
|
||||
} PCSGHistoryStateAddKey;
|
||||
|
||||
/** Allocate PCSGHistory
|
||||
*
|
||||
* @return PCSGHistory*
|
||||
*/
|
||||
PCSGHistory* pcsg_history_alloc(void);
|
||||
|
||||
/** Free PCSGHistory
|
||||
*
|
||||
* @param instance - PCSGHistory instance
|
||||
*/
|
||||
void pcsg_history_free(PCSGHistory* instance);
|
||||
|
||||
/** Clear history
|
||||
*
|
||||
* @param instance - PCSGHistory instance
|
||||
*/
|
||||
void pcsg_history_reset(PCSGHistory* instance);
|
||||
|
||||
/** Get frequency to history[idx]
|
||||
*
|
||||
* @param instance - PCSGHistory instance
|
||||
* @param idx - record index
|
||||
* @return frequency - frequency Hz
|
||||
*/
|
||||
uint32_t pcsg_history_get_frequency(PCSGHistory* instance, uint16_t idx);
|
||||
|
||||
SubGhzRadioPreset* pcsg_history_get_radio_preset(PCSGHistory* instance, uint16_t idx);
|
||||
|
||||
/** Get preset to history[idx]
|
||||
*
|
||||
* @param instance - PCSGHistory instance
|
||||
* @param idx - record index
|
||||
* @return preset - preset name
|
||||
*/
|
||||
const char* pcsg_history_get_preset(PCSGHistory* instance, uint16_t idx);
|
||||
|
||||
/** Get history index write
|
||||
*
|
||||
* @param instance - PCSGHistory instance
|
||||
* @return idx - current record index
|
||||
*/
|
||||
uint16_t pcsg_history_get_item(PCSGHistory* instance);
|
||||
|
||||
/** Get type protocol to history[idx]
|
||||
*
|
||||
* @param instance - PCSGHistory instance
|
||||
* @param idx - record index
|
||||
* @return type - type protocol
|
||||
*/
|
||||
uint8_t pcsg_history_get_type_protocol(PCSGHistory* instance, uint16_t idx);
|
||||
|
||||
/** Get name protocol to history[idx]
|
||||
*
|
||||
* @param instance - PCSGHistory instance
|
||||
* @param idx - record index
|
||||
* @return name - const char* name protocol
|
||||
*/
|
||||
const char* pcsg_history_get_protocol_name(PCSGHistory* instance, uint16_t idx);
|
||||
|
||||
/** Get string item menu to history[idx]
|
||||
*
|
||||
* @param instance - PCSGHistory instance
|
||||
* @param output - FuriString* output
|
||||
* @param idx - record index
|
||||
*/
|
||||
void pcsg_history_get_text_item_menu(PCSGHistory* instance, FuriString* output, uint16_t idx);
|
||||
|
||||
/** Get string the remaining number of records to history
|
||||
*
|
||||
* @param instance - PCSGHistory instance
|
||||
* @param output - FuriString* output
|
||||
* @return bool - is FUUL
|
||||
*/
|
||||
bool pcsg_history_get_text_space_left(PCSGHistory* instance, FuriString* output);
|
||||
|
||||
/** Add protocol to history
|
||||
*
|
||||
* @param instance - PCSGHistory instance
|
||||
* @param context - SubGhzProtocolCommon context
|
||||
* @param preset - SubGhzRadioPreset preset
|
||||
* @return PCSGHistoryStateAddKey;
|
||||
*/
|
||||
PCSGHistoryStateAddKey
|
||||
pcsg_history_add_to_history(PCSGHistory* instance, void* context, SubGhzRadioPreset* preset);
|
||||
|
||||
/** Get SubGhzProtocolCommonLoad to load into the protocol decoder bin data
|
||||
*
|
||||
* @param instance - PCSGHistory instance
|
||||
* @param idx - record index
|
||||
* @return SubGhzProtocolCommonLoad*
|
||||
*/
|
||||
FlipperFormat* pcsg_history_get_raw_data(PCSGHistory* instance, uint16_t idx);
|
||||
@@ -1,9 +0,0 @@
|
||||
#include "protocol_items.h"
|
||||
|
||||
const SubGhzProtocol* pocsag_pager_protocol_registry_items[] = {
|
||||
&subghz_protocol_pocsag,
|
||||
};
|
||||
|
||||
const SubGhzProtocolRegistry pocsag_pager_protocol_registry = {
|
||||
.items = pocsag_pager_protocol_registry_items,
|
||||
.size = COUNT_OF(pocsag_pager_protocol_registry_items)};
|
||||
@@ -1,6 +0,0 @@
|
||||
#pragma once
|
||||
#include "../pocsag_pager_app_i.h"
|
||||
|
||||
#include "pocsag.h"
|
||||
|
||||
extern const SubGhzProtocolRegistry pocsag_pager_protocol_registry;
|
||||
@@ -1,214 +0,0 @@
|
||||
#include "../pocsag_pager_app_i.h"
|
||||
#include "../views/pocsag_pager_receiver.h"
|
||||
|
||||
static const NotificationSequence subghs_sequence_rx = {
|
||||
&message_green_255,
|
||||
|
||||
&message_vibro_on,
|
||||
&message_note_c6,
|
||||
&message_delay_50,
|
||||
&message_sound_off,
|
||||
&message_vibro_off,
|
||||
|
||||
&message_delay_50,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static const NotificationSequence subghs_sequence_rx_locked = {
|
||||
&message_green_255,
|
||||
|
||||
&message_display_backlight_on,
|
||||
|
||||
&message_vibro_on,
|
||||
&message_note_c6,
|
||||
&message_delay_50,
|
||||
&message_sound_off,
|
||||
&message_vibro_off,
|
||||
|
||||
&message_delay_500,
|
||||
|
||||
&message_display_backlight_off,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static void pocsag_pager_scene_receiver_update_statusbar(void* context) {
|
||||
POCSAGPagerApp* app = context;
|
||||
FuriString* history_stat_str;
|
||||
history_stat_str = furi_string_alloc();
|
||||
if(!pcsg_history_get_text_space_left(app->txrx->history, history_stat_str)) {
|
||||
FuriString* frequency_str;
|
||||
FuriString* modulation_str;
|
||||
|
||||
frequency_str = furi_string_alloc();
|
||||
modulation_str = furi_string_alloc();
|
||||
|
||||
pcsg_get_frequency_modulation(app, frequency_str, modulation_str);
|
||||
|
||||
pcsg_view_receiver_add_data_statusbar(
|
||||
app->pcsg_receiver,
|
||||
furi_string_get_cstr(frequency_str),
|
||||
furi_string_get_cstr(modulation_str),
|
||||
furi_string_get_cstr(history_stat_str));
|
||||
|
||||
furi_string_free(frequency_str);
|
||||
furi_string_free(modulation_str);
|
||||
} else {
|
||||
pcsg_view_receiver_add_data_statusbar(
|
||||
app->pcsg_receiver, furi_string_get_cstr(history_stat_str), "", "");
|
||||
}
|
||||
furi_string_free(history_stat_str);
|
||||
}
|
||||
|
||||
void pocsag_pager_scene_receiver_callback(PCSGCustomEvent event, void* context) {
|
||||
furi_assert(context);
|
||||
POCSAGPagerApp* app = context;
|
||||
view_dispatcher_send_custom_event(app->view_dispatcher, event);
|
||||
}
|
||||
|
||||
static void pocsag_pager_scene_receiver_add_to_history_callback(
|
||||
SubGhzReceiver* receiver,
|
||||
SubGhzProtocolDecoderBase* decoder_base,
|
||||
void* context) {
|
||||
furi_assert(context);
|
||||
POCSAGPagerApp* app = context;
|
||||
FuriString* str_buff;
|
||||
str_buff = furi_string_alloc();
|
||||
|
||||
if(pcsg_history_add_to_history(app->txrx->history, decoder_base, app->txrx->preset) ==
|
||||
PCSGHistoryStateAddKeyNewDada) {
|
||||
furi_string_reset(str_buff);
|
||||
|
||||
pcsg_history_get_text_item_menu(
|
||||
app->txrx->history, str_buff, pcsg_history_get_item(app->txrx->history) - 1);
|
||||
pcsg_view_receiver_add_item_to_menu(
|
||||
app->pcsg_receiver,
|
||||
furi_string_get_cstr(str_buff),
|
||||
pcsg_history_get_type_protocol(
|
||||
app->txrx->history, pcsg_history_get_item(app->txrx->history) - 1));
|
||||
|
||||
pocsag_pager_scene_receiver_update_statusbar(app);
|
||||
notification_message(app->notifications, &sequence_blink_green_10);
|
||||
if(app->lock != PCSGLockOn) {
|
||||
notification_message(app->notifications, &subghs_sequence_rx);
|
||||
} else {
|
||||
notification_message(app->notifications, &subghs_sequence_rx_locked);
|
||||
}
|
||||
}
|
||||
subghz_receiver_reset(receiver);
|
||||
furi_string_free(str_buff);
|
||||
app->txrx->rx_key_state = PCSGRxKeyStateAddKey;
|
||||
}
|
||||
|
||||
void pocsag_pager_scene_receiver_on_enter(void* context) {
|
||||
POCSAGPagerApp* app = context;
|
||||
|
||||
FuriString* str_buff;
|
||||
str_buff = furi_string_alloc();
|
||||
|
||||
if(app->txrx->rx_key_state == PCSGRxKeyStateIDLE) {
|
||||
pcsg_preset_init(app, "FM95", 439987500, NULL, 0);
|
||||
pcsg_history_reset(app->txrx->history);
|
||||
app->txrx->rx_key_state = PCSGRxKeyStateStart;
|
||||
}
|
||||
|
||||
pcsg_view_receiver_set_lock(app->pcsg_receiver, app->lock);
|
||||
pcsg_view_receiver_set_ext_module_state(
|
||||
app->pcsg_receiver, radio_device_loader_is_external(app->txrx->radio_device));
|
||||
|
||||
//Load history to receiver
|
||||
pcsg_view_receiver_exit(app->pcsg_receiver);
|
||||
for(uint8_t i = 0; i < pcsg_history_get_item(app->txrx->history); i++) {
|
||||
furi_string_reset(str_buff);
|
||||
pcsg_history_get_text_item_menu(app->txrx->history, str_buff, i);
|
||||
pcsg_view_receiver_add_item_to_menu(
|
||||
app->pcsg_receiver,
|
||||
furi_string_get_cstr(str_buff),
|
||||
pcsg_history_get_type_protocol(app->txrx->history, i));
|
||||
app->txrx->rx_key_state = PCSGRxKeyStateAddKey;
|
||||
}
|
||||
furi_string_free(str_buff);
|
||||
pocsag_pager_scene_receiver_update_statusbar(app);
|
||||
|
||||
pcsg_view_receiver_set_callback(app->pcsg_receiver, pocsag_pager_scene_receiver_callback, app);
|
||||
subghz_receiver_set_rx_callback(
|
||||
app->txrx->receiver, pocsag_pager_scene_receiver_add_to_history_callback, app);
|
||||
|
||||
if(app->txrx->txrx_state == PCSGTxRxStateRx) {
|
||||
pcsg_rx_end(app);
|
||||
};
|
||||
if((app->txrx->txrx_state == PCSGTxRxStateIDLE) ||
|
||||
(app->txrx->txrx_state == PCSGTxRxStateSleep)) {
|
||||
// Start RX
|
||||
pcsg_begin(
|
||||
app,
|
||||
subghz_setting_get_preset_data_by_name(
|
||||
app->setting, furi_string_get_cstr(app->txrx->preset->name)));
|
||||
|
||||
pcsg_rx(app, app->txrx->preset->frequency);
|
||||
}
|
||||
|
||||
pcsg_view_receiver_set_idx_menu(app->pcsg_receiver, app->txrx->idx_menu_chosen);
|
||||
view_dispatcher_switch_to_view(app->view_dispatcher, POCSAGPagerViewReceiver);
|
||||
}
|
||||
|
||||
bool pocsag_pager_scene_receiver_on_event(void* context, SceneManagerEvent event) {
|
||||
POCSAGPagerApp* app = context;
|
||||
bool consumed = false;
|
||||
if(event.type == SceneManagerEventTypeCustom) {
|
||||
switch(event.event) {
|
||||
case PCSGCustomEventViewReceiverBack:
|
||||
// Stop CC1101 Rx
|
||||
if(app->txrx->txrx_state == PCSGTxRxStateRx) {
|
||||
pcsg_rx_end(app);
|
||||
pcsg_idle(app);
|
||||
};
|
||||
app->txrx->hopper_state = PCSGHopperStateOFF;
|
||||
app->txrx->idx_menu_chosen = 0;
|
||||
subghz_receiver_set_rx_callback(app->txrx->receiver, NULL, app);
|
||||
|
||||
app->txrx->rx_key_state = PCSGRxKeyStateIDLE;
|
||||
pcsg_preset_init(app, "FM95", 439987500, NULL, 0);
|
||||
scene_manager_search_and_switch_to_previous_scene(
|
||||
app->scene_manager, POCSAGPagerSceneStart);
|
||||
consumed = true;
|
||||
break;
|
||||
case PCSGCustomEventViewReceiverOK:
|
||||
app->txrx->idx_menu_chosen = pcsg_view_receiver_get_idx_menu(app->pcsg_receiver);
|
||||
scene_manager_next_scene(app->scene_manager, POCSAGPagerSceneReceiverInfo);
|
||||
consumed = true;
|
||||
break;
|
||||
case PCSGCustomEventViewReceiverConfig:
|
||||
app->txrx->idx_menu_chosen = pcsg_view_receiver_get_idx_menu(app->pcsg_receiver);
|
||||
scene_manager_next_scene(app->scene_manager, POCSAGPagerSceneReceiverConfig);
|
||||
consumed = true;
|
||||
break;
|
||||
case PCSGCustomEventViewReceiverOffDisplay:
|
||||
notification_message(app->notifications, &sequence_display_backlight_off);
|
||||
consumed = true;
|
||||
break;
|
||||
case PCSGCustomEventViewReceiverUnlock:
|
||||
app->lock = PCSGLockOff;
|
||||
consumed = true;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
} else if(event.type == SceneManagerEventTypeTick) {
|
||||
if(app->txrx->hopper_state != PCSGHopperStateOFF) {
|
||||
pcsg_hopper_update(app);
|
||||
pocsag_pager_scene_receiver_update_statusbar(app);
|
||||
}
|
||||
// Get current RSSI
|
||||
float rssi = subghz_devices_get_rssi(app->txrx->radio_device);
|
||||
pcsg_receiver_rssi(app->pcsg_receiver, rssi);
|
||||
|
||||
if(app->txrx->txrx_state == PCSGTxRxStateRx) {
|
||||
notification_message(app->notifications, &sequence_blink_cyan_10);
|
||||
}
|
||||
}
|
||||
return consumed;
|
||||
}
|
||||
|
||||
void pocsag_pager_scene_receiver_on_exit(void* context) {
|
||||
UNUSED(context);
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
#include "../pocsag_pager_app_i.h"
|
||||
|
||||
// Generate scene on_enter handlers array
|
||||
#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_enter,
|
||||
void (*const pocsag_pager_scene_on_enter_handlers[])(void*) = {
|
||||
#include "pocsag_pager_scene_config.h"
|
||||
};
|
||||
#undef ADD_SCENE
|
||||
|
||||
// Generate scene on_event handlers array
|
||||
#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_event,
|
||||
bool (*const pocsag_pager_scene_on_event_handlers[])(void* context, SceneManagerEvent event) = {
|
||||
#include "pocsag_pager_scene_config.h"
|
||||
};
|
||||
#undef ADD_SCENE
|
||||
|
||||
// Generate scene on_exit handlers array
|
||||
#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_exit,
|
||||
void (*const pocsag_pager_scene_on_exit_handlers[])(void* context) = {
|
||||
#include "pocsag_pager_scene_config.h"
|
||||
};
|
||||
#undef ADD_SCENE
|
||||
|
||||
// Initialize scene handlers configuration structure
|
||||
const SceneManagerHandlers pocsag_pager_scene_handlers = {
|
||||
.on_enter_handlers = pocsag_pager_scene_on_enter_handlers,
|
||||
.on_event_handlers = pocsag_pager_scene_on_event_handlers,
|
||||
.on_exit_handlers = pocsag_pager_scene_on_exit_handlers,
|
||||
.scene_num = POCSAGPagerSceneNum,
|
||||
};
|
||||
@@ -1,29 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <gui/scene_manager.h>
|
||||
|
||||
// Generate scene id and total number
|
||||
#define ADD_SCENE(prefix, name, id) POCSAGPagerScene##id,
|
||||
typedef enum {
|
||||
#include "pocsag_pager_scene_config.h"
|
||||
POCSAGPagerSceneNum,
|
||||
} POCSAGPagerScene;
|
||||
#undef ADD_SCENE
|
||||
|
||||
extern const SceneManagerHandlers pocsag_pager_scene_handlers;
|
||||
|
||||
// Generate scene on_enter handlers declaration
|
||||
#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_enter(void*);
|
||||
#include "pocsag_pager_scene_config.h"
|
||||
#undef ADD_SCENE
|
||||
|
||||
// Generate scene on_event handlers declaration
|
||||
#define ADD_SCENE(prefix, name, id) \
|
||||
bool prefix##_scene_##name##_on_event(void* context, SceneManagerEvent event);
|
||||
#include "pocsag_pager_scene_config.h"
|
||||
#undef ADD_SCENE
|
||||
|
||||
// Generate scene on_exit handlers declaration
|
||||
#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_exit(void* context);
|
||||
#include "pocsag_pager_scene_config.h"
|
||||
#undef ADD_SCENE
|
||||
@@ -1,74 +0,0 @@
|
||||
#include "../pocsag_pager_app_i.h"
|
||||
#include "../helpers/pocsag_pager_types.h"
|
||||
|
||||
void pocsag_pager_scene_about_widget_callback(GuiButtonType result, InputType type, void* context) {
|
||||
POCSAGPagerApp* app = context;
|
||||
if(type == InputTypeShort) {
|
||||
view_dispatcher_send_custom_event(app->view_dispatcher, result);
|
||||
}
|
||||
}
|
||||
|
||||
void pocsag_pager_scene_about_on_enter(void* context) {
|
||||
POCSAGPagerApp* app = context;
|
||||
|
||||
FuriString* temp_str;
|
||||
temp_str = furi_string_alloc();
|
||||
furi_string_printf(temp_str, "\e#%s\n", "Information");
|
||||
|
||||
furi_string_cat_printf(temp_str, "Version: %s\n", PCSG_VERSION_APP);
|
||||
furi_string_cat_printf(temp_str, "Developed by:\n%s\n\n", PCSG_DEVELOPED);
|
||||
furi_string_cat_printf(temp_str, "Github: %s\n\n", PCSG_GITHUB);
|
||||
|
||||
furi_string_cat_printf(temp_str, "\e#%s\n", "Description");
|
||||
furi_string_cat_printf(temp_str, "Receiving POCSAG Pager \nmessages\n\n");
|
||||
|
||||
furi_string_cat_printf(temp_str, "Supported protocols:\n");
|
||||
size_t i = 0;
|
||||
const char* protocol_name =
|
||||
subghz_environment_get_protocol_name_registry(app->txrx->environment, i++);
|
||||
do {
|
||||
furi_string_cat_printf(temp_str, "%s\n", protocol_name);
|
||||
protocol_name = subghz_environment_get_protocol_name_registry(app->txrx->environment, i++);
|
||||
} while(protocol_name != NULL);
|
||||
|
||||
widget_add_text_box_element(
|
||||
app->widget,
|
||||
0,
|
||||
0,
|
||||
128,
|
||||
14,
|
||||
AlignCenter,
|
||||
AlignBottom,
|
||||
"\e#\e! \e!\n",
|
||||
false);
|
||||
widget_add_text_box_element(
|
||||
app->widget,
|
||||
0,
|
||||
2,
|
||||
128,
|
||||
14,
|
||||
AlignCenter,
|
||||
AlignBottom,
|
||||
"\e#\e! POCSAG Pager \e!\n",
|
||||
false);
|
||||
widget_add_text_scroll_element(app->widget, 0, 16, 128, 50, furi_string_get_cstr(temp_str));
|
||||
furi_string_free(temp_str);
|
||||
|
||||
view_dispatcher_switch_to_view(app->view_dispatcher, POCSAGPagerViewWidget);
|
||||
}
|
||||
|
||||
bool pocsag_pager_scene_about_on_event(void* context, SceneManagerEvent event) {
|
||||
POCSAGPagerApp* app = context;
|
||||
bool consumed = false;
|
||||
UNUSED(app);
|
||||
UNUSED(event);
|
||||
|
||||
return consumed;
|
||||
}
|
||||
|
||||
void pocsag_pager_scene_about_on_exit(void* context) {
|
||||
POCSAGPagerApp* app = context;
|
||||
|
||||
// Clear views
|
||||
widget_reset(app->widget);
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
ADD_SCENE(pocsag_pager, start, Start)
|
||||
ADD_SCENE(pocsag_pager, about, About)
|
||||
ADD_SCENE(pocsag_pager, receiver, Receiver)
|
||||
ADD_SCENE(pocsag_pager, receiver_config, ReceiverConfig)
|
||||
ADD_SCENE(pocsag_pager, receiver_info, ReceiverInfo)
|
||||
@@ -1,221 +0,0 @@
|
||||
#include "../pocsag_pager_app_i.h"
|
||||
|
||||
enum PCSGSettingIndex {
|
||||
PCSGSettingIndexFrequency,
|
||||
PCSGSettingIndexHopping,
|
||||
PCSGSettingIndexModulation,
|
||||
PCSGSettingIndexLock,
|
||||
};
|
||||
|
||||
#define HOPPING_COUNT 2
|
||||
const char* const hopping_text[HOPPING_COUNT] = {
|
||||
"OFF",
|
||||
"ON",
|
||||
};
|
||||
const uint32_t hopping_value[HOPPING_COUNT] = {
|
||||
PCSGHopperStateOFF,
|
||||
PCSGHopperStateRunnig,
|
||||
};
|
||||
|
||||
uint8_t pocsag_pager_scene_receiver_config_next_frequency(const uint32_t value, void* context) {
|
||||
furi_assert(context);
|
||||
POCSAGPagerApp* app = context;
|
||||
uint8_t index = 0;
|
||||
for(uint8_t i = 0; i < subghz_setting_get_frequency_count(app->setting); i++) {
|
||||
if(value == subghz_setting_get_frequency(app->setting, i)) {
|
||||
index = i;
|
||||
break;
|
||||
} else {
|
||||
index = subghz_setting_get_frequency_default_index(app->setting);
|
||||
}
|
||||
}
|
||||
return index;
|
||||
}
|
||||
|
||||
uint8_t pocsag_pager_scene_receiver_config_next_preset(const char* preset_name, void* context) {
|
||||
furi_assert(context);
|
||||
POCSAGPagerApp* app = context;
|
||||
uint8_t index = 0;
|
||||
for(uint8_t i = 0; i < subghz_setting_get_preset_count(app->setting); i++) {
|
||||
if(!strcmp(subghz_setting_get_preset_name(app->setting, i), preset_name)) {
|
||||
index = i;
|
||||
break;
|
||||
} else {
|
||||
// index = subghz_setting_get_frequency_default_index(app ->setting);
|
||||
}
|
||||
}
|
||||
return index;
|
||||
}
|
||||
|
||||
uint8_t pocsag_pager_scene_receiver_config_hopper_value_index(
|
||||
const uint32_t value,
|
||||
const uint32_t values[],
|
||||
uint8_t values_count,
|
||||
void* context) {
|
||||
furi_assert(context);
|
||||
UNUSED(values_count);
|
||||
POCSAGPagerApp* app = context;
|
||||
|
||||
if(value == values[0]) {
|
||||
return 0;
|
||||
} else {
|
||||
variable_item_set_current_value_text(
|
||||
(VariableItem*)scene_manager_get_scene_state(
|
||||
app->scene_manager, POCSAGPagerSceneReceiverConfig),
|
||||
" -----");
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
static void pocsag_pager_scene_receiver_config_set_frequency(VariableItem* item) {
|
||||
POCSAGPagerApp* app = variable_item_get_context(item);
|
||||
uint8_t index = variable_item_get_current_value_index(item);
|
||||
|
||||
if(app->txrx->hopper_state == PCSGHopperStateOFF) {
|
||||
char text_buf[10] = {0};
|
||||
snprintf(
|
||||
text_buf,
|
||||
sizeof(text_buf),
|
||||
"%lu.%02lu",
|
||||
subghz_setting_get_frequency(app->setting, index) / 1000000,
|
||||
(subghz_setting_get_frequency(app->setting, index) % 1000000) / 10000);
|
||||
variable_item_set_current_value_text(item, text_buf);
|
||||
app->txrx->preset->frequency = subghz_setting_get_frequency(app->setting, index);
|
||||
} else {
|
||||
variable_item_set_current_value_index(
|
||||
item, subghz_setting_get_frequency_default_index(app->setting));
|
||||
}
|
||||
}
|
||||
|
||||
static void pocsag_pager_scene_receiver_config_set_preset(VariableItem* item) {
|
||||
POCSAGPagerApp* app = variable_item_get_context(item);
|
||||
uint8_t index = variable_item_get_current_value_index(item);
|
||||
variable_item_set_current_value_text(
|
||||
item, subghz_setting_get_preset_name(app->setting, index));
|
||||
pcsg_preset_init(
|
||||
app,
|
||||
subghz_setting_get_preset_name(app->setting, index),
|
||||
app->txrx->preset->frequency,
|
||||
subghz_setting_get_preset_data(app->setting, index),
|
||||
subghz_setting_get_preset_data_size(app->setting, index));
|
||||
}
|
||||
|
||||
static void pocsag_pager_scene_receiver_config_set_hopping_running(VariableItem* item) {
|
||||
POCSAGPagerApp* app = variable_item_get_context(item);
|
||||
uint8_t index = variable_item_get_current_value_index(item);
|
||||
|
||||
variable_item_set_current_value_text(item, hopping_text[index]);
|
||||
if(hopping_value[index] == PCSGHopperStateOFF) {
|
||||
char text_buf[10] = {0};
|
||||
snprintf(
|
||||
text_buf,
|
||||
sizeof(text_buf),
|
||||
"%lu.%02lu",
|
||||
subghz_setting_get_default_frequency(app->setting) / 1000000,
|
||||
(subghz_setting_get_default_frequency(app->setting) % 1000000) / 10000);
|
||||
variable_item_set_current_value_text(
|
||||
(VariableItem*)scene_manager_get_scene_state(
|
||||
app->scene_manager, POCSAGPagerSceneReceiverConfig),
|
||||
text_buf);
|
||||
app->txrx->preset->frequency = subghz_setting_get_default_frequency(app->setting);
|
||||
variable_item_set_current_value_index(
|
||||
(VariableItem*)scene_manager_get_scene_state(
|
||||
app->scene_manager, POCSAGPagerSceneReceiverConfig),
|
||||
subghz_setting_get_frequency_default_index(app->setting));
|
||||
} else {
|
||||
variable_item_set_current_value_text(
|
||||
(VariableItem*)scene_manager_get_scene_state(
|
||||
app->scene_manager, POCSAGPagerSceneReceiverConfig),
|
||||
" -----");
|
||||
variable_item_set_current_value_index(
|
||||
(VariableItem*)scene_manager_get_scene_state(
|
||||
app->scene_manager, POCSAGPagerSceneReceiverConfig),
|
||||
subghz_setting_get_frequency_default_index(app->setting));
|
||||
}
|
||||
|
||||
app->txrx->hopper_state = hopping_value[index];
|
||||
}
|
||||
|
||||
static void
|
||||
pocsag_pager_scene_receiver_config_var_list_enter_callback(void* context, uint32_t index) {
|
||||
furi_assert(context);
|
||||
POCSAGPagerApp* app = context;
|
||||
if(index == PCSGSettingIndexLock) {
|
||||
view_dispatcher_send_custom_event(app->view_dispatcher, PCSGCustomEventSceneSettingLock);
|
||||
}
|
||||
}
|
||||
|
||||
void pocsag_pager_scene_receiver_config_on_enter(void* context) {
|
||||
POCSAGPagerApp* app = context;
|
||||
VariableItem* item;
|
||||
uint8_t value_index;
|
||||
|
||||
item = variable_item_list_add(
|
||||
app->variable_item_list,
|
||||
"Frequency:",
|
||||
subghz_setting_get_frequency_count(app->setting),
|
||||
pocsag_pager_scene_receiver_config_set_frequency,
|
||||
app);
|
||||
value_index =
|
||||
pocsag_pager_scene_receiver_config_next_frequency(app->txrx->preset->frequency, app);
|
||||
scene_manager_set_scene_state(
|
||||
app->scene_manager, POCSAGPagerSceneReceiverConfig, (uint32_t)item);
|
||||
variable_item_set_current_value_index(item, value_index);
|
||||
char text_buf[10] = {0};
|
||||
snprintf(
|
||||
text_buf,
|
||||
sizeof(text_buf),
|
||||
"%lu.%02lu",
|
||||
subghz_setting_get_frequency(app->setting, value_index) / 1000000,
|
||||
(subghz_setting_get_frequency(app->setting, value_index) % 1000000) / 10000);
|
||||
variable_item_set_current_value_text(item, text_buf);
|
||||
|
||||
item = variable_item_list_add(
|
||||
app->variable_item_list,
|
||||
"Hopping:",
|
||||
HOPPING_COUNT,
|
||||
pocsag_pager_scene_receiver_config_set_hopping_running,
|
||||
app);
|
||||
value_index = pocsag_pager_scene_receiver_config_hopper_value_index(
|
||||
app->txrx->hopper_state, hopping_value, HOPPING_COUNT, app);
|
||||
variable_item_set_current_value_index(item, value_index);
|
||||
variable_item_set_current_value_text(item, hopping_text[value_index]);
|
||||
|
||||
item = variable_item_list_add(
|
||||
app->variable_item_list,
|
||||
"Modulation:",
|
||||
subghz_setting_get_preset_count(app->setting),
|
||||
pocsag_pager_scene_receiver_config_set_preset,
|
||||
app);
|
||||
value_index = pocsag_pager_scene_receiver_config_next_preset(
|
||||
furi_string_get_cstr(app->txrx->preset->name), app);
|
||||
variable_item_set_current_value_index(item, value_index);
|
||||
variable_item_set_current_value_text(
|
||||
item, subghz_setting_get_preset_name(app->setting, value_index));
|
||||
|
||||
variable_item_list_add(app->variable_item_list, "Lock Keyboard", 1, NULL, NULL);
|
||||
variable_item_list_set_enter_callback(
|
||||
app->variable_item_list, pocsag_pager_scene_receiver_config_var_list_enter_callback, app);
|
||||
|
||||
view_dispatcher_switch_to_view(app->view_dispatcher, POCSAGPagerViewVariableItemList);
|
||||
}
|
||||
|
||||
bool pocsag_pager_scene_receiver_config_on_event(void* context, SceneManagerEvent event) {
|
||||
POCSAGPagerApp* app = context;
|
||||
bool consumed = false;
|
||||
|
||||
if(event.type == SceneManagerEventTypeCustom) {
|
||||
if(event.event == PCSGCustomEventSceneSettingLock) {
|
||||
app->lock = PCSGLockOn;
|
||||
scene_manager_previous_scene(app->scene_manager);
|
||||
consumed = true;
|
||||
}
|
||||
}
|
||||
return consumed;
|
||||
}
|
||||
|
||||
void pocsag_pager_scene_receiver_config_on_exit(void* context) {
|
||||
POCSAGPagerApp* app = context;
|
||||
variable_item_list_set_selected_item(app->variable_item_list, 0);
|
||||
variable_item_list_reset(app->variable_item_list);
|
||||
}
|
||||
@@ -1,50 +0,0 @@
|
||||
#include "../pocsag_pager_app_i.h"
|
||||
#include "../views/pocsag_pager_receiver.h"
|
||||
|
||||
void pocsag_pager_scene_receiver_info_callback(PCSGCustomEvent event, void* context) {
|
||||
furi_assert(context);
|
||||
POCSAGPagerApp* app = context;
|
||||
view_dispatcher_send_custom_event(app->view_dispatcher, event);
|
||||
}
|
||||
|
||||
static void pocsag_pager_scene_receiver_info_add_to_history_callback(
|
||||
SubGhzReceiver* receiver,
|
||||
SubGhzProtocolDecoderBase* decoder_base,
|
||||
void* context) {
|
||||
furi_assert(context);
|
||||
POCSAGPagerApp* app = context;
|
||||
|
||||
if(pcsg_history_add_to_history(app->txrx->history, decoder_base, app->txrx->preset) ==
|
||||
PCSGHistoryStateAddKeyUpdateData) {
|
||||
pcsg_view_receiver_info_update(
|
||||
app->pcsg_receiver_info,
|
||||
pcsg_history_get_raw_data(app->txrx->history, app->txrx->idx_menu_chosen));
|
||||
subghz_receiver_reset(receiver);
|
||||
|
||||
notification_message(app->notifications, &sequence_blink_green_10);
|
||||
app->txrx->rx_key_state = PCSGRxKeyStateAddKey;
|
||||
}
|
||||
}
|
||||
|
||||
void pocsag_pager_scene_receiver_info_on_enter(void* context) {
|
||||
POCSAGPagerApp* app = context;
|
||||
|
||||
subghz_receiver_set_rx_callback(
|
||||
app->txrx->receiver, pocsag_pager_scene_receiver_info_add_to_history_callback, app);
|
||||
pcsg_view_receiver_info_update(
|
||||
app->pcsg_receiver_info,
|
||||
pcsg_history_get_raw_data(app->txrx->history, app->txrx->idx_menu_chosen));
|
||||
view_dispatcher_switch_to_view(app->view_dispatcher, POCSAGPagerViewReceiverInfo);
|
||||
}
|
||||
|
||||
bool pocsag_pager_scene_receiver_info_on_event(void* context, SceneManagerEvent event) {
|
||||
POCSAGPagerApp* app = context;
|
||||
bool consumed = false;
|
||||
UNUSED(app);
|
||||
UNUSED(event);
|
||||
return consumed;
|
||||
}
|
||||
|
||||
void pocsag_pager_scene_receiver_info_on_exit(void* context) {
|
||||
UNUSED(context);
|
||||
}
|
||||
@@ -1,58 +0,0 @@
|
||||
#include "../pocsag_pager_app_i.h"
|
||||
|
||||
typedef enum {
|
||||
SubmenuIndexPOCSAGPagerReceiver,
|
||||
SubmenuIndexPOCSAGPagerAbout,
|
||||
} SubmenuIndex;
|
||||
|
||||
void pocsag_pager_scene_start_submenu_callback(void* context, uint32_t index) {
|
||||
POCSAGPagerApp* app = context;
|
||||
view_dispatcher_send_custom_event(app->view_dispatcher, index);
|
||||
}
|
||||
|
||||
void pocsag_pager_scene_start_on_enter(void* context) {
|
||||
UNUSED(context);
|
||||
POCSAGPagerApp* app = context;
|
||||
Submenu* submenu = app->submenu;
|
||||
|
||||
submenu_add_item(
|
||||
submenu,
|
||||
"Receive messages",
|
||||
SubmenuIndexPOCSAGPagerReceiver,
|
||||
pocsag_pager_scene_start_submenu_callback,
|
||||
app);
|
||||
submenu_add_item(
|
||||
submenu,
|
||||
"About",
|
||||
SubmenuIndexPOCSAGPagerAbout,
|
||||
pocsag_pager_scene_start_submenu_callback,
|
||||
app);
|
||||
|
||||
submenu_set_selected_item(
|
||||
submenu, scene_manager_get_scene_state(app->scene_manager, POCSAGPagerSceneStart));
|
||||
|
||||
view_dispatcher_switch_to_view(app->view_dispatcher, POCSAGPagerViewSubmenu);
|
||||
}
|
||||
|
||||
bool pocsag_pager_scene_start_on_event(void* context, SceneManagerEvent event) {
|
||||
POCSAGPagerApp* app = context;
|
||||
bool consumed = false;
|
||||
|
||||
if(event.type == SceneManagerEventTypeCustom) {
|
||||
if(event.event == SubmenuIndexPOCSAGPagerAbout) {
|
||||
scene_manager_next_scene(app->scene_manager, POCSAGPagerSceneAbout);
|
||||
consumed = true;
|
||||
} else if(event.event == SubmenuIndexPOCSAGPagerReceiver) {
|
||||
scene_manager_next_scene(app->scene_manager, POCSAGPagerSceneReceiver);
|
||||
consumed = true;
|
||||
}
|
||||
scene_manager_set_scene_state(app->scene_manager, POCSAGPagerSceneStart, event.event);
|
||||
}
|
||||
|
||||
return consumed;
|
||||
}
|
||||
|
||||
void pocsag_pager_scene_start_on_exit(void* context) {
|
||||
POCSAGPagerApp* app = context;
|
||||
submenu_reset(app->submenu);
|
||||
}
|
||||
@@ -1,479 +0,0 @@
|
||||
#include "pocsag_pager_receiver.h"
|
||||
#include "../pocsag_pager_app_i.h"
|
||||
#include "pocsag_pager_icons.h"
|
||||
#include <assets_icons.h>
|
||||
#include <math.h>
|
||||
|
||||
#include <input/input.h>
|
||||
#include <gui/elements.h>
|
||||
#include <m-array.h>
|
||||
|
||||
#define FRAME_HEIGHT 12
|
||||
#define MAX_LEN_PX 112
|
||||
#define MENU_ITEMS 4u
|
||||
#define UNLOCK_CNT 3
|
||||
|
||||
#define SUBGHZ_RAW_THRESHOLD_MIN -90.0f
|
||||
|
||||
typedef struct {
|
||||
FuriString* item_str;
|
||||
uint8_t type;
|
||||
} PCSGReceiverMenuItem;
|
||||
|
||||
ARRAY_DEF(PCSGReceiverMenuItemArray, PCSGReceiverMenuItem, M_POD_OPLIST)
|
||||
|
||||
#define M_OPL_PCSGReceiverMenuItemArray_t() ARRAY_OPLIST(PCSGReceiverMenuItemArray, M_POD_OPLIST)
|
||||
|
||||
struct PCSGReceiverHistory {
|
||||
PCSGReceiverMenuItemArray_t data;
|
||||
};
|
||||
|
||||
typedef struct PCSGReceiverHistory PCSGReceiverHistory;
|
||||
|
||||
static const Icon* ReceiverItemIcons[] = {
|
||||
[SubGhzProtocolTypeUnknown] = &I_Quest_7x8,
|
||||
[SubGhzProtocolTypeStatic] = &I_Message_8x7,
|
||||
[SubGhzProtocolTypeDynamic] = &I_Lock_7x8,
|
||||
};
|
||||
|
||||
typedef enum {
|
||||
PCSGReceiverBarShowDefault,
|
||||
PCSGReceiverBarShowLock,
|
||||
PCSGReceiverBarShowToUnlockPress,
|
||||
PCSGReceiverBarShowUnlock,
|
||||
} PCSGReceiverBarShow;
|
||||
|
||||
struct PCSGReceiver {
|
||||
PCSGLock lock;
|
||||
uint8_t lock_count;
|
||||
FuriTimer* timer;
|
||||
View* view;
|
||||
PCSGReceiverCallback callback;
|
||||
void* context;
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
FuriString* frequency_str;
|
||||
FuriString* preset_str;
|
||||
FuriString* history_stat_str;
|
||||
PCSGReceiverHistory* history;
|
||||
uint16_t idx;
|
||||
uint16_t list_offset;
|
||||
uint16_t history_item;
|
||||
PCSGReceiverBarShow bar_show;
|
||||
uint8_t u_rssi;
|
||||
bool ext_module;
|
||||
} PCSGReceiverModel;
|
||||
|
||||
void pcsg_receiver_rssi(PCSGReceiver* instance, float rssi) {
|
||||
furi_assert(instance);
|
||||
with_view_model(
|
||||
instance->view,
|
||||
PCSGReceiverModel * model,
|
||||
{
|
||||
if(rssi < SUBGHZ_RAW_THRESHOLD_MIN) {
|
||||
model->u_rssi = 0;
|
||||
} else {
|
||||
model->u_rssi = (uint8_t)(rssi - SUBGHZ_RAW_THRESHOLD_MIN);
|
||||
}
|
||||
},
|
||||
true);
|
||||
}
|
||||
|
||||
void pcsg_view_receiver_set_lock(PCSGReceiver* pcsg_receiver, PCSGLock lock) {
|
||||
furi_assert(pcsg_receiver);
|
||||
pcsg_receiver->lock_count = 0;
|
||||
if(lock == PCSGLockOn) {
|
||||
pcsg_receiver->lock = lock;
|
||||
with_view_model(
|
||||
pcsg_receiver->view,
|
||||
PCSGReceiverModel * model,
|
||||
{ model->bar_show = PCSGReceiverBarShowLock; },
|
||||
true);
|
||||
furi_timer_start(pcsg_receiver->timer, pdMS_TO_TICKS(1000));
|
||||
} else {
|
||||
with_view_model(
|
||||
pcsg_receiver->view,
|
||||
PCSGReceiverModel * model,
|
||||
{ model->bar_show = PCSGReceiverBarShowDefault; },
|
||||
true);
|
||||
}
|
||||
}
|
||||
|
||||
void pcsg_view_receiver_set_ext_module_state(PCSGReceiver* pcsg_receiver, bool is_external) {
|
||||
furi_assert(pcsg_receiver);
|
||||
with_view_model(
|
||||
pcsg_receiver->view, PCSGReceiverModel * model, { model->ext_module = is_external; }, true);
|
||||
}
|
||||
|
||||
void pcsg_view_receiver_set_callback(
|
||||
PCSGReceiver* pcsg_receiver,
|
||||
PCSGReceiverCallback callback,
|
||||
void* context) {
|
||||
furi_assert(pcsg_receiver);
|
||||
furi_assert(callback);
|
||||
pcsg_receiver->callback = callback;
|
||||
pcsg_receiver->context = context;
|
||||
}
|
||||
|
||||
static void pcsg_view_receiver_update_offset(PCSGReceiver* pcsg_receiver) {
|
||||
furi_assert(pcsg_receiver);
|
||||
|
||||
with_view_model(
|
||||
pcsg_receiver->view,
|
||||
PCSGReceiverModel * model,
|
||||
{
|
||||
size_t history_item = model->history_item;
|
||||
uint16_t bounds = history_item > 3 ? 2 : history_item;
|
||||
|
||||
if(history_item > 3 && model->idx >= (int16_t)(history_item - 1)) {
|
||||
model->list_offset = model->idx - 3;
|
||||
} else if(model->list_offset < model->idx - bounds) {
|
||||
model->list_offset =
|
||||
CLAMP(model->list_offset + 1, (int16_t)(history_item - bounds), 0);
|
||||
} else if(model->list_offset > model->idx - bounds) {
|
||||
model->list_offset = CLAMP(model->idx - 1, (int16_t)(history_item - bounds), 0);
|
||||
}
|
||||
},
|
||||
true);
|
||||
}
|
||||
|
||||
void pcsg_view_receiver_add_item_to_menu(
|
||||
PCSGReceiver* pcsg_receiver,
|
||||
const char* name,
|
||||
uint8_t type) {
|
||||
furi_assert(pcsg_receiver);
|
||||
with_view_model(
|
||||
pcsg_receiver->view,
|
||||
PCSGReceiverModel * model,
|
||||
{
|
||||
PCSGReceiverMenuItem* item_menu =
|
||||
PCSGReceiverMenuItemArray_push_raw(model->history->data);
|
||||
item_menu->item_str = furi_string_alloc_set(name);
|
||||
item_menu->type = type;
|
||||
if((model->idx == model->history_item - 1)) {
|
||||
model->history_item++;
|
||||
model->idx++;
|
||||
} else {
|
||||
model->history_item++;
|
||||
}
|
||||
},
|
||||
true);
|
||||
pcsg_view_receiver_update_offset(pcsg_receiver);
|
||||
}
|
||||
|
||||
void pcsg_view_receiver_add_data_statusbar(
|
||||
PCSGReceiver* pcsg_receiver,
|
||||
const char* frequency_str,
|
||||
const char* preset_str,
|
||||
const char* history_stat_str) {
|
||||
furi_assert(pcsg_receiver);
|
||||
with_view_model(
|
||||
pcsg_receiver->view,
|
||||
PCSGReceiverModel * model,
|
||||
{
|
||||
furi_string_set_str(model->frequency_str, frequency_str);
|
||||
furi_string_set_str(model->preset_str, preset_str);
|
||||
furi_string_set_str(model->history_stat_str, history_stat_str);
|
||||
},
|
||||
true);
|
||||
}
|
||||
|
||||
static void pcsg_view_receiver_draw_frame(Canvas* canvas, uint16_t idx, bool scrollbar) {
|
||||
canvas_set_color(canvas, ColorBlack);
|
||||
canvas_draw_box(canvas, 0, 0 + idx * FRAME_HEIGHT, scrollbar ? 122 : 127, FRAME_HEIGHT);
|
||||
|
||||
canvas_set_color(canvas, ColorWhite);
|
||||
canvas_draw_dot(canvas, 0, 0 + idx * FRAME_HEIGHT);
|
||||
canvas_draw_dot(canvas, 1, 0 + idx * FRAME_HEIGHT);
|
||||
canvas_draw_dot(canvas, 0, (0 + idx * FRAME_HEIGHT) + 1);
|
||||
|
||||
canvas_draw_dot(canvas, 0, (0 + idx * FRAME_HEIGHT) + 11);
|
||||
canvas_draw_dot(canvas, scrollbar ? 121 : 126, 0 + idx * FRAME_HEIGHT);
|
||||
canvas_draw_dot(canvas, scrollbar ? 121 : 126, (0 + idx * FRAME_HEIGHT) + 11);
|
||||
}
|
||||
|
||||
static void pcsg_view_rssi_draw(Canvas* canvas, PCSGReceiverModel* model) {
|
||||
for(uint8_t i = 1; i < model->u_rssi; i++) {
|
||||
if(i % 5) {
|
||||
canvas_draw_dot(canvas, 46 + i, 50);
|
||||
canvas_draw_dot(canvas, 47 + i, 51);
|
||||
canvas_draw_dot(canvas, 46 + i, 52);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void pcsg_view_receiver_draw(Canvas* canvas, PCSGReceiverModel* model) {
|
||||
canvas_clear(canvas);
|
||||
canvas_set_color(canvas, ColorBlack);
|
||||
canvas_set_font(canvas, FontSecondary);
|
||||
|
||||
elements_button_left(canvas, "Config");
|
||||
//canvas_draw_line(canvas, 46, 51, 125, 51);
|
||||
|
||||
bool scrollbar = model->history_item > 4;
|
||||
FuriString* str_buff;
|
||||
str_buff = furi_string_alloc();
|
||||
|
||||
PCSGReceiverMenuItem* item_menu;
|
||||
|
||||
for(size_t i = 0; i < MIN(model->history_item, MENU_ITEMS); ++i) {
|
||||
size_t idx = CLAMP((uint16_t)(i + model->list_offset), model->history_item, 0);
|
||||
item_menu = PCSGReceiverMenuItemArray_get(model->history->data, idx);
|
||||
furi_string_set(str_buff, item_menu->item_str);
|
||||
furi_string_replace_all(str_buff, "#", "");
|
||||
elements_string_fit_width(canvas, str_buff, scrollbar ? MAX_LEN_PX - 7 : MAX_LEN_PX);
|
||||
if(model->idx == idx) {
|
||||
pcsg_view_receiver_draw_frame(canvas, i, scrollbar);
|
||||
} else {
|
||||
canvas_set_color(canvas, ColorBlack);
|
||||
}
|
||||
canvas_draw_icon(canvas, 4, 2 + i * FRAME_HEIGHT, ReceiverItemIcons[item_menu->type]);
|
||||
canvas_draw_str(canvas, 15, 9 + i * FRAME_HEIGHT, furi_string_get_cstr(str_buff));
|
||||
furi_string_reset(str_buff);
|
||||
}
|
||||
if(scrollbar) {
|
||||
elements_scrollbar_pos(canvas, 128, 0, 49, model->idx, model->history_item);
|
||||
}
|
||||
furi_string_free(str_buff);
|
||||
|
||||
canvas_set_color(canvas, ColorBlack);
|
||||
|
||||
if(model->history_item == 0) {
|
||||
canvas_draw_icon(canvas, 0, 0, model->ext_module ? &I_Fishing_123x52 : &I_Scanning_123x52);
|
||||
canvas_set_font(canvas, FontPrimary);
|
||||
canvas_draw_str(canvas, 63, 46, "Scanning...");
|
||||
canvas_set_font(canvas, FontSecondary);
|
||||
canvas_draw_str(canvas, 44, 10, model->ext_module ? "Ext" : "Int");
|
||||
}
|
||||
|
||||
// Draw RSSI
|
||||
pcsg_view_rssi_draw(canvas, model);
|
||||
|
||||
switch(model->bar_show) {
|
||||
case PCSGReceiverBarShowLock:
|
||||
canvas_draw_icon(canvas, 64, 55, &I_Lock_7x8);
|
||||
canvas_draw_str(canvas, 74, 62, "Locked");
|
||||
break;
|
||||
case PCSGReceiverBarShowToUnlockPress:
|
||||
canvas_draw_str(canvas, 44, 62, furi_string_get_cstr(model->frequency_str));
|
||||
canvas_draw_str(canvas, 79, 62, furi_string_get_cstr(model->preset_str));
|
||||
canvas_draw_str(canvas, 96, 62, furi_string_get_cstr(model->history_stat_str));
|
||||
canvas_set_font(canvas, FontSecondary);
|
||||
elements_bold_rounded_frame(canvas, 14, 8, 99, 48);
|
||||
elements_multiline_text(canvas, 65, 26, "To unlock\npress:");
|
||||
canvas_draw_icon(canvas, 65, 42, &I_Pin_back_arrow_10x8);
|
||||
canvas_draw_icon(canvas, 80, 42, &I_Pin_back_arrow_10x8);
|
||||
canvas_draw_icon(canvas, 95, 42, &I_Pin_back_arrow_10x8);
|
||||
canvas_draw_icon(canvas, 16, 13, &I_WarningDolphin_45x42);
|
||||
canvas_draw_dot(canvas, 17, 61);
|
||||
break;
|
||||
case PCSGReceiverBarShowUnlock:
|
||||
canvas_draw_icon(canvas, 64, 55, &I_Unlock_7x8);
|
||||
canvas_draw_str(canvas, 74, 62, "Unlocked");
|
||||
break;
|
||||
default:
|
||||
canvas_draw_str(canvas, 44, 62, furi_string_get_cstr(model->frequency_str));
|
||||
canvas_draw_str(canvas, 79, 62, furi_string_get_cstr(model->preset_str));
|
||||
canvas_draw_str(canvas, 96, 62, furi_string_get_cstr(model->history_stat_str));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void pcsg_view_receiver_timer_callback(void* context) {
|
||||
furi_assert(context);
|
||||
PCSGReceiver* pcsg_receiver = context;
|
||||
with_view_model(
|
||||
pcsg_receiver->view,
|
||||
PCSGReceiverModel * model,
|
||||
{ model->bar_show = PCSGReceiverBarShowDefault; },
|
||||
true);
|
||||
if(pcsg_receiver->lock_count < UNLOCK_CNT) {
|
||||
pcsg_receiver->callback(PCSGCustomEventViewReceiverOffDisplay, pcsg_receiver->context);
|
||||
} else {
|
||||
pcsg_receiver->lock = PCSGLockOff;
|
||||
pcsg_receiver->callback(PCSGCustomEventViewReceiverUnlock, pcsg_receiver->context);
|
||||
}
|
||||
pcsg_receiver->lock_count = 0;
|
||||
}
|
||||
|
||||
bool pcsg_view_receiver_input(InputEvent* event, void* context) {
|
||||
furi_assert(context);
|
||||
PCSGReceiver* pcsg_receiver = context;
|
||||
|
||||
if(pcsg_receiver->lock == PCSGLockOn) {
|
||||
with_view_model(
|
||||
pcsg_receiver->view,
|
||||
PCSGReceiverModel * model,
|
||||
{ model->bar_show = PCSGReceiverBarShowToUnlockPress; },
|
||||
true);
|
||||
if(pcsg_receiver->lock_count == 0) {
|
||||
furi_timer_start(pcsg_receiver->timer, pdMS_TO_TICKS(1000));
|
||||
}
|
||||
if(event->key == InputKeyBack && event->type == InputTypeShort) {
|
||||
pcsg_receiver->lock_count++;
|
||||
}
|
||||
if(pcsg_receiver->lock_count >= UNLOCK_CNT) {
|
||||
pcsg_receiver->callback(PCSGCustomEventViewReceiverUnlock, pcsg_receiver->context);
|
||||
with_view_model(
|
||||
pcsg_receiver->view,
|
||||
PCSGReceiverModel * model,
|
||||
{ model->bar_show = PCSGReceiverBarShowUnlock; },
|
||||
true);
|
||||
pcsg_receiver->lock = PCSGLockOff;
|
||||
furi_timer_start(pcsg_receiver->timer, pdMS_TO_TICKS(650));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
if(event->key == InputKeyBack && event->type == InputTypeShort) {
|
||||
pcsg_receiver->callback(PCSGCustomEventViewReceiverBack, pcsg_receiver->context);
|
||||
} else if(
|
||||
event->key == InputKeyUp &&
|
||||
(event->type == InputTypeShort || event->type == InputTypeRepeat)) {
|
||||
with_view_model(
|
||||
pcsg_receiver->view,
|
||||
PCSGReceiverModel * model,
|
||||
{
|
||||
if(model->idx != 0) model->idx--;
|
||||
},
|
||||
true);
|
||||
} else if(
|
||||
event->key == InputKeyDown &&
|
||||
(event->type == InputTypeShort || event->type == InputTypeRepeat)) {
|
||||
with_view_model(
|
||||
pcsg_receiver->view,
|
||||
PCSGReceiverModel * model,
|
||||
{
|
||||
if(model->history_item && model->idx != model->history_item - 1) model->idx++;
|
||||
},
|
||||
true);
|
||||
} else if(event->key == InputKeyLeft && event->type == InputTypeShort) {
|
||||
pcsg_receiver->callback(PCSGCustomEventViewReceiverConfig, pcsg_receiver->context);
|
||||
} else if(event->key == InputKeyOk && event->type == InputTypeShort) {
|
||||
with_view_model(
|
||||
pcsg_receiver->view,
|
||||
PCSGReceiverModel * model,
|
||||
{
|
||||
if(model->history_item != 0) {
|
||||
pcsg_receiver->callback(PCSGCustomEventViewReceiverOK, pcsg_receiver->context);
|
||||
}
|
||||
},
|
||||
false);
|
||||
}
|
||||
|
||||
pcsg_view_receiver_update_offset(pcsg_receiver);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void pcsg_view_receiver_enter(void* context) {
|
||||
furi_assert(context);
|
||||
}
|
||||
|
||||
void pcsg_view_receiver_exit(void* context) {
|
||||
furi_assert(context);
|
||||
PCSGReceiver* pcsg_receiver = context;
|
||||
with_view_model(
|
||||
pcsg_receiver->view,
|
||||
PCSGReceiverModel * model,
|
||||
{
|
||||
furi_string_reset(model->frequency_str);
|
||||
furi_string_reset(model->preset_str);
|
||||
furi_string_reset(model->history_stat_str);
|
||||
for
|
||||
M_EACH(item_menu, model->history->data, PCSGReceiverMenuItemArray_t) {
|
||||
furi_string_free(item_menu->item_str);
|
||||
item_menu->type = 0;
|
||||
}
|
||||
PCSGReceiverMenuItemArray_reset(model->history->data);
|
||||
model->idx = 0;
|
||||
model->list_offset = 0;
|
||||
model->history_item = 0;
|
||||
},
|
||||
false);
|
||||
furi_timer_stop(pcsg_receiver->timer);
|
||||
}
|
||||
|
||||
PCSGReceiver* pcsg_view_receiver_alloc() {
|
||||
PCSGReceiver* pcsg_receiver = malloc(sizeof(PCSGReceiver));
|
||||
|
||||
// View allocation and configuration
|
||||
pcsg_receiver->view = view_alloc();
|
||||
|
||||
pcsg_receiver->lock = PCSGLockOff;
|
||||
pcsg_receiver->lock_count = 0;
|
||||
view_allocate_model(pcsg_receiver->view, ViewModelTypeLocking, sizeof(PCSGReceiverModel));
|
||||
view_set_context(pcsg_receiver->view, pcsg_receiver);
|
||||
view_set_draw_callback(pcsg_receiver->view, (ViewDrawCallback)pcsg_view_receiver_draw);
|
||||
view_set_input_callback(pcsg_receiver->view, pcsg_view_receiver_input);
|
||||
view_set_enter_callback(pcsg_receiver->view, pcsg_view_receiver_enter);
|
||||
view_set_exit_callback(pcsg_receiver->view, pcsg_view_receiver_exit);
|
||||
|
||||
with_view_model(
|
||||
pcsg_receiver->view,
|
||||
PCSGReceiverModel * model,
|
||||
{
|
||||
model->frequency_str = furi_string_alloc();
|
||||
model->preset_str = furi_string_alloc();
|
||||
model->history_stat_str = furi_string_alloc();
|
||||
model->bar_show = PCSGReceiverBarShowDefault;
|
||||
model->history = malloc(sizeof(PCSGReceiverHistory));
|
||||
PCSGReceiverMenuItemArray_init(model->history->data);
|
||||
},
|
||||
true);
|
||||
pcsg_receiver->timer =
|
||||
furi_timer_alloc(pcsg_view_receiver_timer_callback, FuriTimerTypeOnce, pcsg_receiver);
|
||||
return pcsg_receiver;
|
||||
}
|
||||
|
||||
void pcsg_view_receiver_free(PCSGReceiver* pcsg_receiver) {
|
||||
furi_assert(pcsg_receiver);
|
||||
|
||||
with_view_model(
|
||||
pcsg_receiver->view,
|
||||
PCSGReceiverModel * model,
|
||||
{
|
||||
furi_string_free(model->frequency_str);
|
||||
furi_string_free(model->preset_str);
|
||||
furi_string_free(model->history_stat_str);
|
||||
for
|
||||
M_EACH(item_menu, model->history->data, PCSGReceiverMenuItemArray_t) {
|
||||
furi_string_free(item_menu->item_str);
|
||||
item_menu->type = 0;
|
||||
}
|
||||
PCSGReceiverMenuItemArray_clear(model->history->data);
|
||||
free(model->history);
|
||||
},
|
||||
false);
|
||||
furi_timer_free(pcsg_receiver->timer);
|
||||
view_free(pcsg_receiver->view);
|
||||
free(pcsg_receiver);
|
||||
}
|
||||
|
||||
View* pcsg_view_receiver_get_view(PCSGReceiver* pcsg_receiver) {
|
||||
furi_assert(pcsg_receiver);
|
||||
return pcsg_receiver->view;
|
||||
}
|
||||
|
||||
uint16_t pcsg_view_receiver_get_idx_menu(PCSGReceiver* pcsg_receiver) {
|
||||
furi_assert(pcsg_receiver);
|
||||
uint32_t idx = 0;
|
||||
with_view_model(
|
||||
pcsg_receiver->view, PCSGReceiverModel * model, { idx = model->idx; }, false);
|
||||
return idx;
|
||||
}
|
||||
|
||||
void pcsg_view_receiver_set_idx_menu(PCSGReceiver* pcsg_receiver, uint16_t idx) {
|
||||
furi_assert(pcsg_receiver);
|
||||
with_view_model(
|
||||
pcsg_receiver->view,
|
||||
PCSGReceiverModel * model,
|
||||
{
|
||||
model->idx = idx;
|
||||
if(model->idx > 2) model->list_offset = idx - 2;
|
||||
},
|
||||
true);
|
||||
pcsg_view_receiver_update_offset(pcsg_receiver);
|
||||
}
|
||||
@@ -1,43 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <gui/view.h>
|
||||
#include "../helpers/pocsag_pager_types.h"
|
||||
#include "../helpers/pocsag_pager_event.h"
|
||||
|
||||
typedef struct PCSGReceiver PCSGReceiver;
|
||||
|
||||
typedef void (*PCSGReceiverCallback)(PCSGCustomEvent event, void* context);
|
||||
|
||||
void pcsg_receiver_rssi(PCSGReceiver* instance, float rssi);
|
||||
|
||||
void pcsg_view_receiver_set_lock(PCSGReceiver* pcsg_receiver, PCSGLock keyboard);
|
||||
|
||||
void pcsg_view_receiver_set_ext_module_state(PCSGReceiver* pcsg_receiver, bool is_external);
|
||||
|
||||
void pcsg_view_receiver_set_callback(
|
||||
PCSGReceiver* pcsg_receiver,
|
||||
PCSGReceiverCallback callback,
|
||||
void* context);
|
||||
|
||||
PCSGReceiver* pcsg_view_receiver_alloc();
|
||||
|
||||
void pcsg_view_receiver_free(PCSGReceiver* pcsg_receiver);
|
||||
|
||||
View* pcsg_view_receiver_get_view(PCSGReceiver* pcsg_receiver);
|
||||
|
||||
void pcsg_view_receiver_add_data_statusbar(
|
||||
PCSGReceiver* pcsg_receiver,
|
||||
const char* frequency_str,
|
||||
const char* preset_str,
|
||||
const char* history_stat_str);
|
||||
|
||||
void pcsg_view_receiver_add_item_to_menu(
|
||||
PCSGReceiver* pcsg_receiver,
|
||||
const char* name,
|
||||
uint8_t type);
|
||||
|
||||
uint16_t pcsg_view_receiver_get_idx_menu(PCSGReceiver* pcsg_receiver);
|
||||
|
||||
void pcsg_view_receiver_set_idx_menu(PCSGReceiver* pcsg_receiver, uint16_t idx);
|
||||
|
||||
void pcsg_view_receiver_exit(void* context);
|
||||
@@ -1,138 +0,0 @@
|
||||
#include "pocsag_pager_receiver.h"
|
||||
#include "../pocsag_pager_app_i.h"
|
||||
#include "pocsag_pager_icons.h"
|
||||
#include <assets_icons.h>
|
||||
#include "../protocols/pcsg_generic.h"
|
||||
#include <input/input.h>
|
||||
#include <gui/elements.h>
|
||||
|
||||
#define abs(x) ((x) > 0 ? (x) : -(x))
|
||||
|
||||
struct PCSGReceiverInfo {
|
||||
View* view;
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
FuriString* protocol_name;
|
||||
PCSGBlockGeneric* generic;
|
||||
} PCSGReceiverInfoModel;
|
||||
|
||||
void pcsg_view_receiver_info_update(PCSGReceiverInfo* pcsg_receiver_info, FlipperFormat* fff) {
|
||||
furi_assert(pcsg_receiver_info);
|
||||
furi_assert(fff);
|
||||
|
||||
with_view_model(
|
||||
pcsg_receiver_info->view,
|
||||
PCSGReceiverInfoModel * model,
|
||||
{
|
||||
flipper_format_rewind(fff);
|
||||
flipper_format_read_string(fff, "Protocol", model->protocol_name);
|
||||
|
||||
pcsg_block_generic_deserialize(model->generic, fff);
|
||||
},
|
||||
true);
|
||||
}
|
||||
|
||||
void pcsg_view_receiver_info_draw(Canvas* canvas, PCSGReceiverInfoModel* model) {
|
||||
canvas_clear(canvas);
|
||||
canvas_set_color(canvas, ColorBlack);
|
||||
canvas_set_font(canvas, FontSecondary);
|
||||
if(model->generic->result_ric != NULL) {
|
||||
elements_text_box(
|
||||
canvas,
|
||||
0,
|
||||
0,
|
||||
128,
|
||||
64,
|
||||
AlignLeft,
|
||||
AlignTop,
|
||||
furi_string_get_cstr(model->generic->result_ric),
|
||||
false);
|
||||
}
|
||||
if(model->generic->result_msg != NULL) {
|
||||
elements_text_box(
|
||||
canvas,
|
||||
0,
|
||||
12,
|
||||
128,
|
||||
64,
|
||||
AlignLeft,
|
||||
AlignTop,
|
||||
furi_string_get_cstr(model->generic->result_msg),
|
||||
false);
|
||||
}
|
||||
}
|
||||
|
||||
bool pcsg_view_receiver_info_input(InputEvent* event, void* context) {
|
||||
furi_assert(context);
|
||||
//PCSGReceiverInfo* pcsg_receiver_info = context;
|
||||
|
||||
if(event->key == InputKeyBack) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void pcsg_view_receiver_info_enter(void* context) {
|
||||
furi_assert(context);
|
||||
}
|
||||
|
||||
void pcsg_view_receiver_info_exit(void* context) {
|
||||
furi_assert(context);
|
||||
PCSGReceiverInfo* pcsg_receiver_info = context;
|
||||
|
||||
with_view_model(
|
||||
pcsg_receiver_info->view,
|
||||
PCSGReceiverInfoModel * model,
|
||||
{ furi_string_reset(model->protocol_name); },
|
||||
false);
|
||||
}
|
||||
|
||||
PCSGReceiverInfo* pcsg_view_receiver_info_alloc() {
|
||||
PCSGReceiverInfo* pcsg_receiver_info = malloc(sizeof(PCSGReceiverInfo));
|
||||
|
||||
// View allocation and configuration
|
||||
pcsg_receiver_info->view = view_alloc();
|
||||
|
||||
view_allocate_model(
|
||||
pcsg_receiver_info->view, ViewModelTypeLocking, sizeof(PCSGReceiverInfoModel));
|
||||
view_set_context(pcsg_receiver_info->view, pcsg_receiver_info);
|
||||
view_set_draw_callback(
|
||||
pcsg_receiver_info->view, (ViewDrawCallback)pcsg_view_receiver_info_draw);
|
||||
view_set_input_callback(pcsg_receiver_info->view, pcsg_view_receiver_info_input);
|
||||
view_set_enter_callback(pcsg_receiver_info->view, pcsg_view_receiver_info_enter);
|
||||
view_set_exit_callback(pcsg_receiver_info->view, pcsg_view_receiver_info_exit);
|
||||
|
||||
with_view_model(
|
||||
pcsg_receiver_info->view,
|
||||
PCSGReceiverInfoModel * model,
|
||||
{
|
||||
model->generic = malloc(sizeof(PCSGBlockGeneric));
|
||||
model->protocol_name = furi_string_alloc();
|
||||
},
|
||||
true);
|
||||
|
||||
return pcsg_receiver_info;
|
||||
}
|
||||
|
||||
void pcsg_view_receiver_info_free(PCSGReceiverInfo* pcsg_receiver_info) {
|
||||
furi_assert(pcsg_receiver_info);
|
||||
|
||||
with_view_model(
|
||||
pcsg_receiver_info->view,
|
||||
PCSGReceiverInfoModel * model,
|
||||
{
|
||||
furi_string_free(model->protocol_name);
|
||||
free(model->generic);
|
||||
},
|
||||
false);
|
||||
|
||||
view_free(pcsg_receiver_info->view);
|
||||
free(pcsg_receiver_info);
|
||||
}
|
||||
|
||||
View* pcsg_view_receiver_info_get_view(PCSGReceiverInfo* pcsg_receiver_info) {
|
||||
furi_assert(pcsg_receiver_info);
|
||||
return pcsg_receiver_info->view;
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <gui/view.h>
|
||||
#include "../helpers/pocsag_pager_types.h"
|
||||
#include "../helpers/pocsag_pager_event.h"
|
||||
#include <lib/flipper_format/flipper_format.h>
|
||||
|
||||
typedef struct PCSGReceiverInfo PCSGReceiverInfo;
|
||||
|
||||
void pcsg_view_receiver_info_update(PCSGReceiverInfo* pcsg_receiver_info, FlipperFormat* fff);
|
||||
|
||||
PCSGReceiverInfo* pcsg_view_receiver_info_alloc();
|
||||
|
||||
void pcsg_view_receiver_info_free(PCSGReceiverInfo* pcsg_receiver_info);
|
||||
|
||||
View* pcsg_view_receiver_info_get_view(PCSGReceiverInfo* pcsg_receiver_info);
|
||||
@@ -1,14 +0,0 @@
|
||||
App(
|
||||
appid="weather_station",
|
||||
name="Weather Station",
|
||||
apptype=FlipperAppType.EXTERNAL,
|
||||
targets=["f7"],
|
||||
entry_point="weather_station_app",
|
||||
requires=["gui"],
|
||||
stack_size=4 * 1024,
|
||||
fap_description="Receive weather data from a wide range of supported Sub-1GHz remote sensor",
|
||||
fap_version="1.1",
|
||||
fap_icon="weather_station_10px.png",
|
||||
fap_category="Sub-GHz",
|
||||
fap_icon_assets="images",
|
||||
)
|
||||
@@ -1,69 +0,0 @@
|
||||
#include "radio_device_loader.h"
|
||||
|
||||
#include <applications/drivers/subghz/cc1101_ext/cc1101_ext_interconnect.h>
|
||||
#include <lib/subghz/devices/cc1101_int/cc1101_int_interconnect.h>
|
||||
|
||||
static void radio_device_loader_power_on() {
|
||||
uint8_t attempts = 0;
|
||||
while(!furi_hal_power_is_otg_enabled() && attempts++ < 5) {
|
||||
furi_hal_power_enable_otg();
|
||||
//CC1101 power-up time
|
||||
furi_delay_ms(10);
|
||||
}
|
||||
}
|
||||
|
||||
static void radio_device_loader_power_off() {
|
||||
if(furi_hal_power_is_otg_enabled()) furi_hal_power_disable_otg();
|
||||
}
|
||||
|
||||
bool radio_device_loader_is_connect_external(const char* name) {
|
||||
bool is_connect = false;
|
||||
bool is_otg_enabled = furi_hal_power_is_otg_enabled();
|
||||
|
||||
if(!is_otg_enabled) {
|
||||
radio_device_loader_power_on();
|
||||
}
|
||||
|
||||
const SubGhzDevice* device = subghz_devices_get_by_name(name);
|
||||
if(device) {
|
||||
is_connect = subghz_devices_is_connect(device);
|
||||
}
|
||||
|
||||
if(!is_otg_enabled) {
|
||||
radio_device_loader_power_off();
|
||||
}
|
||||
return is_connect;
|
||||
}
|
||||
|
||||
const SubGhzDevice* radio_device_loader_set(
|
||||
const SubGhzDevice* current_radio_device,
|
||||
SubGhzRadioDeviceType radio_device_type) {
|
||||
const SubGhzDevice* radio_device;
|
||||
|
||||
if(radio_device_type == SubGhzRadioDeviceTypeExternalCC1101 &&
|
||||
radio_device_loader_is_connect_external(SUBGHZ_DEVICE_CC1101_EXT_NAME)) {
|
||||
radio_device_loader_power_on();
|
||||
radio_device = subghz_devices_get_by_name(SUBGHZ_DEVICE_CC1101_EXT_NAME);
|
||||
subghz_devices_begin(radio_device);
|
||||
} else if(current_radio_device == NULL) {
|
||||
radio_device = subghz_devices_get_by_name(SUBGHZ_DEVICE_CC1101_INT_NAME);
|
||||
} else {
|
||||
radio_device_loader_end(current_radio_device);
|
||||
radio_device = subghz_devices_get_by_name(SUBGHZ_DEVICE_CC1101_INT_NAME);
|
||||
}
|
||||
|
||||
return radio_device;
|
||||
}
|
||||
|
||||
bool radio_device_loader_is_external(const SubGhzDevice* radio_device) {
|
||||
furi_assert(radio_device);
|
||||
return (radio_device != subghz_devices_get_by_name(SUBGHZ_DEVICE_CC1101_INT_NAME));
|
||||
}
|
||||
|
||||
void radio_device_loader_end(const SubGhzDevice* radio_device) {
|
||||
furi_assert(radio_device);
|
||||
radio_device_loader_power_off();
|
||||
if(radio_device != subghz_devices_get_by_name(SUBGHZ_DEVICE_CC1101_INT_NAME)) {
|
||||
subghz_devices_end(radio_device);
|
||||
}
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <lib/subghz/devices/devices.h>
|
||||
|
||||
/** SubGhzRadioDeviceType */
|
||||
typedef enum {
|
||||
SubGhzRadioDeviceTypeInternal,
|
||||
SubGhzRadioDeviceTypeExternalCC1101,
|
||||
} SubGhzRadioDeviceType;
|
||||
|
||||
const SubGhzDevice* radio_device_loader_set(
|
||||
const SubGhzDevice* current_radio_device,
|
||||
SubGhzRadioDeviceType radio_device_type);
|
||||
|
||||
bool radio_device_loader_is_external(const SubGhzDevice* radio_device);
|
||||
|
||||
void radio_device_loader_end(const SubGhzDevice* radio_device);
|
||||
@@ -1,14 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
typedef enum {
|
||||
//WSCustomEvent
|
||||
WSCustomEventStartId = 100,
|
||||
|
||||
WSCustomEventSceneSettingLock,
|
||||
|
||||
WSCustomEventViewReceiverOK,
|
||||
WSCustomEventViewReceiverConfig,
|
||||
WSCustomEventViewReceiverBack,
|
||||
WSCustomEventViewReceiverOffDisplay,
|
||||
WSCustomEventViewReceiverUnlock,
|
||||
} WSCustomEvent;
|
||||
@@ -1,49 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <furi.h>
|
||||
#include <furi_hal.h>
|
||||
|
||||
#define WS_VERSION_APP "1.1"
|
||||
#define WS_DEVELOPED "SkorP"
|
||||
#define WS_GITHUB "https://github.com/flipperdevices/flipperzero-firmware"
|
||||
|
||||
#define WS_KEY_FILE_VERSION 1
|
||||
#define WS_KEY_FILE_TYPE "Flipper Weather Station Key File"
|
||||
|
||||
/** WSRxKeyState state */
|
||||
typedef enum {
|
||||
WSRxKeyStateIDLE,
|
||||
WSRxKeyStateBack,
|
||||
WSRxKeyStateStart,
|
||||
WSRxKeyStateAddKey,
|
||||
} WSRxKeyState;
|
||||
|
||||
/** WSHopperState state */
|
||||
typedef enum {
|
||||
WSHopperStateOFF,
|
||||
WSHopperStateRunnig,
|
||||
WSHopperStatePause,
|
||||
WSHopperStateRSSITimeOut,
|
||||
} WSHopperState;
|
||||
|
||||
/** WSLock */
|
||||
typedef enum {
|
||||
WSLockOff,
|
||||
WSLockOn,
|
||||
} WSLock;
|
||||
|
||||
typedef enum {
|
||||
WeatherStationViewVariableItemList,
|
||||
WeatherStationViewSubmenu,
|
||||
WeatherStationViewReceiver,
|
||||
WeatherStationViewReceiverInfo,
|
||||
WeatherStationViewWidget,
|
||||
} WeatherStationView;
|
||||
|
||||
/** WeatherStationTxRx state */
|
||||
typedef enum {
|
||||
WSTxRxStateIDLE,
|
||||
WSTxRxStateRx,
|
||||
WSTxRxStateTx,
|
||||
WSTxRxStateSleep,
|
||||
} WSTxRxState;
|
||||
|
Before Width: | Height: | Size: 3.5 KiB |
|
Before Width: | Height: | Size: 3.5 KiB |
|
Before Width: | Height: | Size: 3.5 KiB |
|
Before Width: | Height: | Size: 3.5 KiB |
|
Before Width: | Height: | Size: 3.5 KiB |
@@ -1,26 +0,0 @@
|
||||
#include "protocol_items.h"
|
||||
|
||||
const SubGhzProtocol* weather_station_protocol_registry_items[] = {
|
||||
&ws_protocol_infactory,
|
||||
&ws_protocol_thermopro_tx4,
|
||||
&ws_protocol_nexus_th,
|
||||
&ws_protocol_gt_wt_02,
|
||||
&ws_protocol_gt_wt_03,
|
||||
&ws_protocol_acurite_606tx,
|
||||
&ws_protocol_acurite_609txc,
|
||||
&ws_protocol_lacrosse_tx,
|
||||
&ws_protocol_lacrosse_tx141thbv2,
|
||||
&ws_protocol_oregon2,
|
||||
&ws_protocol_oregon3,
|
||||
&ws_protocol_acurite_592txr,
|
||||
&ws_protocol_ambient_weather,
|
||||
&ws_protocol_auriol_th,
|
||||
&ws_protocol_oregon_v1,
|
||||
&ws_protocol_tx_8300,
|
||||
&ws_protocol_wendox_w6726,
|
||||
&ws_protocol_auriol_ahfl,
|
||||
};
|
||||
|
||||
const SubGhzProtocolRegistry weather_station_protocol_registry = {
|
||||
.items = weather_station_protocol_registry_items,
|
||||
.size = COUNT_OF(weather_station_protocol_registry_items)};
|
||||
@@ -1,23 +0,0 @@
|
||||
#pragma once
|
||||
#include "../weather_station_app_i.h"
|
||||
|
||||
#include "infactory.h"
|
||||
#include "thermopro_tx4.h"
|
||||
#include "nexus_th.h"
|
||||
#include "gt_wt_02.h"
|
||||
#include "gt_wt_03.h"
|
||||
#include "acurite_606tx.h"
|
||||
#include "acurite_609txc.h"
|
||||
#include "lacrosse_tx.h"
|
||||
#include "lacrosse_tx141thbv2.h"
|
||||
#include "oregon2.h"
|
||||
#include "oregon3.h"
|
||||
#include "acurite_592txr.h"
|
||||
#include "ambient_weather.h"
|
||||
#include "auriol_hg0601a.h"
|
||||
#include "oregon_v1.h"
|
||||
#include "tx_8300.h"
|
||||
#include "wendox_w6726.h"
|
||||
#include "auriol_ahfl.h"
|
||||
|
||||
extern const SubGhzProtocolRegistry weather_station_protocol_registry;
|
||||
@@ -1,216 +0,0 @@
|
||||
#include "../weather_station_app_i.h"
|
||||
#include "../views/weather_station_receiver.h"
|
||||
|
||||
static const NotificationSequence subghs_sequence_rx = {
|
||||
&message_green_255,
|
||||
|
||||
&message_vibro_on,
|
||||
&message_note_c6,
|
||||
&message_delay_50,
|
||||
&message_sound_off,
|
||||
&message_vibro_off,
|
||||
|
||||
&message_delay_50,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static const NotificationSequence subghs_sequence_rx_locked = {
|
||||
&message_green_255,
|
||||
|
||||
&message_display_backlight_on,
|
||||
|
||||
&message_vibro_on,
|
||||
&message_note_c6,
|
||||
&message_delay_50,
|
||||
&message_sound_off,
|
||||
&message_vibro_off,
|
||||
|
||||
&message_delay_500,
|
||||
|
||||
&message_display_backlight_off,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static void weather_station_scene_receiver_update_statusbar(void* context) {
|
||||
WeatherStationApp* app = context;
|
||||
FuriString* history_stat_str;
|
||||
history_stat_str = furi_string_alloc();
|
||||
if(!ws_history_get_text_space_left(app->txrx->history, history_stat_str)) {
|
||||
FuriString* frequency_str;
|
||||
FuriString* modulation_str;
|
||||
|
||||
frequency_str = furi_string_alloc();
|
||||
modulation_str = furi_string_alloc();
|
||||
|
||||
ws_get_frequency_modulation(app, frequency_str, modulation_str);
|
||||
|
||||
ws_view_receiver_add_data_statusbar(
|
||||
app->ws_receiver,
|
||||
furi_string_get_cstr(frequency_str),
|
||||
furi_string_get_cstr(modulation_str),
|
||||
furi_string_get_cstr(history_stat_str),
|
||||
radio_device_loader_is_external(app->txrx->radio_device));
|
||||
|
||||
furi_string_free(frequency_str);
|
||||
furi_string_free(modulation_str);
|
||||
} else {
|
||||
ws_view_receiver_add_data_statusbar(
|
||||
app->ws_receiver,
|
||||
furi_string_get_cstr(history_stat_str),
|
||||
"",
|
||||
"",
|
||||
radio_device_loader_is_external(app->txrx->radio_device));
|
||||
}
|
||||
furi_string_free(history_stat_str);
|
||||
}
|
||||
|
||||
void weather_station_scene_receiver_callback(WSCustomEvent event, void* context) {
|
||||
furi_assert(context);
|
||||
WeatherStationApp* app = context;
|
||||
view_dispatcher_send_custom_event(app->view_dispatcher, event);
|
||||
}
|
||||
|
||||
static void weather_station_scene_receiver_add_to_history_callback(
|
||||
SubGhzReceiver* receiver,
|
||||
SubGhzProtocolDecoderBase* decoder_base,
|
||||
void* context) {
|
||||
furi_assert(context);
|
||||
WeatherStationApp* app = context;
|
||||
FuriString* str_buff;
|
||||
str_buff = furi_string_alloc();
|
||||
|
||||
if(ws_history_add_to_history(app->txrx->history, decoder_base, app->txrx->preset) ==
|
||||
WSHistoryStateAddKeyNewDada) {
|
||||
furi_string_reset(str_buff);
|
||||
|
||||
ws_history_get_text_item_menu(
|
||||
app->txrx->history, str_buff, ws_history_get_item(app->txrx->history) - 1);
|
||||
ws_view_receiver_add_item_to_menu(
|
||||
app->ws_receiver,
|
||||
furi_string_get_cstr(str_buff),
|
||||
ws_history_get_type_protocol(
|
||||
app->txrx->history, ws_history_get_item(app->txrx->history) - 1));
|
||||
|
||||
weather_station_scene_receiver_update_statusbar(app);
|
||||
notification_message(app->notifications, &sequence_blink_green_10);
|
||||
if(app->lock != WSLockOn) {
|
||||
notification_message(app->notifications, &subghs_sequence_rx);
|
||||
} else {
|
||||
notification_message(app->notifications, &subghs_sequence_rx_locked);
|
||||
}
|
||||
}
|
||||
subghz_receiver_reset(receiver);
|
||||
furi_string_free(str_buff);
|
||||
app->txrx->rx_key_state = WSRxKeyStateAddKey;
|
||||
}
|
||||
|
||||
void weather_station_scene_receiver_on_enter(void* context) {
|
||||
WeatherStationApp* app = context;
|
||||
|
||||
FuriString* str_buff;
|
||||
str_buff = furi_string_alloc();
|
||||
|
||||
if(app->txrx->rx_key_state == WSRxKeyStateIDLE) {
|
||||
ws_preset_init(app, "AM650", subghz_setting_get_default_frequency(app->setting), NULL, 0);
|
||||
ws_history_reset(app->txrx->history);
|
||||
app->txrx->rx_key_state = WSRxKeyStateStart;
|
||||
}
|
||||
|
||||
ws_view_receiver_set_lock(app->ws_receiver, app->lock);
|
||||
|
||||
//Load history to receiver
|
||||
ws_view_receiver_exit(app->ws_receiver);
|
||||
for(uint8_t i = 0; i < ws_history_get_item(app->txrx->history); i++) {
|
||||
furi_string_reset(str_buff);
|
||||
ws_history_get_text_item_menu(app->txrx->history, str_buff, i);
|
||||
ws_view_receiver_add_item_to_menu(
|
||||
app->ws_receiver,
|
||||
furi_string_get_cstr(str_buff),
|
||||
ws_history_get_type_protocol(app->txrx->history, i));
|
||||
app->txrx->rx_key_state = WSRxKeyStateAddKey;
|
||||
}
|
||||
furi_string_free(str_buff);
|
||||
weather_station_scene_receiver_update_statusbar(app);
|
||||
|
||||
ws_view_receiver_set_callback(app->ws_receiver, weather_station_scene_receiver_callback, app);
|
||||
subghz_receiver_set_rx_callback(
|
||||
app->txrx->receiver, weather_station_scene_receiver_add_to_history_callback, app);
|
||||
|
||||
if(app->txrx->txrx_state == WSTxRxStateRx) {
|
||||
ws_rx_end(app);
|
||||
};
|
||||
if((app->txrx->txrx_state == WSTxRxStateIDLE) || (app->txrx->txrx_state == WSTxRxStateSleep)) {
|
||||
ws_begin(
|
||||
app,
|
||||
subghz_setting_get_preset_data_by_name(
|
||||
app->setting, furi_string_get_cstr(app->txrx->preset->name)));
|
||||
|
||||
ws_rx(app, app->txrx->preset->frequency);
|
||||
}
|
||||
|
||||
ws_view_receiver_set_idx_menu(app->ws_receiver, app->txrx->idx_menu_chosen);
|
||||
view_dispatcher_switch_to_view(app->view_dispatcher, WeatherStationViewReceiver);
|
||||
}
|
||||
|
||||
bool weather_station_scene_receiver_on_event(void* context, SceneManagerEvent event) {
|
||||
WeatherStationApp* app = context;
|
||||
bool consumed = false;
|
||||
if(event.type == SceneManagerEventTypeCustom) {
|
||||
switch(event.event) {
|
||||
case WSCustomEventViewReceiverBack:
|
||||
// Stop CC1101 Rx
|
||||
if(app->txrx->txrx_state == WSTxRxStateRx) {
|
||||
ws_rx_end(app);
|
||||
ws_sleep(app);
|
||||
};
|
||||
app->txrx->hopper_state = WSHopperStateOFF;
|
||||
app->txrx->idx_menu_chosen = 0;
|
||||
subghz_receiver_set_rx_callback(app->txrx->receiver, NULL, app);
|
||||
|
||||
app->txrx->rx_key_state = WSRxKeyStateIDLE;
|
||||
ws_preset_init(
|
||||
app, "AM650", subghz_setting_get_default_frequency(app->setting), NULL, 0);
|
||||
scene_manager_search_and_switch_to_previous_scene(
|
||||
app->scene_manager, WeatherStationSceneStart);
|
||||
consumed = true;
|
||||
break;
|
||||
case WSCustomEventViewReceiverOK:
|
||||
app->txrx->idx_menu_chosen = ws_view_receiver_get_idx_menu(app->ws_receiver);
|
||||
scene_manager_next_scene(app->scene_manager, WeatherStationSceneReceiverInfo);
|
||||
consumed = true;
|
||||
break;
|
||||
case WSCustomEventViewReceiverConfig:
|
||||
app->txrx->idx_menu_chosen = ws_view_receiver_get_idx_menu(app->ws_receiver);
|
||||
scene_manager_next_scene(app->scene_manager, WeatherStationSceneReceiverConfig);
|
||||
consumed = true;
|
||||
break;
|
||||
case WSCustomEventViewReceiverOffDisplay:
|
||||
notification_message(app->notifications, &sequence_display_backlight_off);
|
||||
consumed = true;
|
||||
break;
|
||||
case WSCustomEventViewReceiverUnlock:
|
||||
app->lock = WSLockOff;
|
||||
consumed = true;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
} else if(event.type == SceneManagerEventTypeTick) {
|
||||
if(app->txrx->hopper_state != WSHopperStateOFF) {
|
||||
ws_hopper_update(app);
|
||||
weather_station_scene_receiver_update_statusbar(app);
|
||||
}
|
||||
// Get current RSSI
|
||||
float rssi = subghz_devices_get_rssi(app->txrx->radio_device);
|
||||
ws_view_receiver_set_rssi(app->ws_receiver, rssi);
|
||||
|
||||
if(app->txrx->txrx_state == WSTxRxStateRx) {
|
||||
notification_message(app->notifications, &sequence_blink_cyan_10);
|
||||
}
|
||||
}
|
||||
return consumed;
|
||||
}
|
||||
|
||||
void weather_station_scene_receiver_on_exit(void* context) {
|
||||
UNUSED(context);
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
#include "../weather_station_app_i.h"
|
||||
|
||||
// Generate scene on_enter handlers array
|
||||
#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_enter,
|
||||
void (*const weather_station_scene_on_enter_handlers[])(void*) = {
|
||||
#include "weather_station_scene_config.h"
|
||||
};
|
||||
#undef ADD_SCENE
|
||||
|
||||
// Generate scene on_event handlers array
|
||||
#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_event,
|
||||
bool (*const weather_station_scene_on_event_handlers[])(void* context, SceneManagerEvent event) = {
|
||||
#include "weather_station_scene_config.h"
|
||||
};
|
||||
#undef ADD_SCENE
|
||||
|
||||
// Generate scene on_exit handlers array
|
||||
#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_exit,
|
||||
void (*const weather_station_scene_on_exit_handlers[])(void* context) = {
|
||||
#include "weather_station_scene_config.h"
|
||||
};
|
||||
#undef ADD_SCENE
|
||||
|
||||
// Initialize scene handlers configuration structure
|
||||
const SceneManagerHandlers weather_station_scene_handlers = {
|
||||
.on_enter_handlers = weather_station_scene_on_enter_handlers,
|
||||
.on_event_handlers = weather_station_scene_on_event_handlers,
|
||||
.on_exit_handlers = weather_station_scene_on_exit_handlers,
|
||||
.scene_num = WeatherStationSceneNum,
|
||||
};
|
||||
@@ -1,29 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <gui/scene_manager.h>
|
||||
|
||||
// Generate scene id and total number
|
||||
#define ADD_SCENE(prefix, name, id) WeatherStationScene##id,
|
||||
typedef enum {
|
||||
#include "weather_station_scene_config.h"
|
||||
WeatherStationSceneNum,
|
||||
} WeatherStationScene;
|
||||
#undef ADD_SCENE
|
||||
|
||||
extern const SceneManagerHandlers weather_station_scene_handlers;
|
||||
|
||||
// Generate scene on_enter handlers declaration
|
||||
#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_enter(void*);
|
||||
#include "weather_station_scene_config.h"
|
||||
#undef ADD_SCENE
|
||||
|
||||
// Generate scene on_event handlers declaration
|
||||
#define ADD_SCENE(prefix, name, id) \
|
||||
bool prefix##_scene_##name##_on_event(void* context, SceneManagerEvent event);
|
||||
#include "weather_station_scene_config.h"
|
||||
#undef ADD_SCENE
|
||||
|
||||
// Generate scene on_exit handlers declaration
|
||||
#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_exit(void* context);
|
||||
#include "weather_station_scene_config.h"
|
||||
#undef ADD_SCENE
|
||||
@@ -1,78 +0,0 @@
|
||||
#include "../weather_station_app_i.h"
|
||||
#include "../helpers/weather_station_types.h"
|
||||
|
||||
void weather_station_scene_about_widget_callback(
|
||||
GuiButtonType result,
|
||||
InputType type,
|
||||
void* context) {
|
||||
WeatherStationApp* app = context;
|
||||
if(type == InputTypeShort) {
|
||||
view_dispatcher_send_custom_event(app->view_dispatcher, result);
|
||||
}
|
||||
}
|
||||
|
||||
void weather_station_scene_about_on_enter(void* context) {
|
||||
WeatherStationApp* app = context;
|
||||
|
||||
FuriString* temp_str;
|
||||
temp_str = furi_string_alloc();
|
||||
furi_string_printf(temp_str, "\e#%s\n", "Information");
|
||||
|
||||
furi_string_cat_printf(temp_str, "Version: %s\n", WS_VERSION_APP);
|
||||
furi_string_cat_printf(temp_str, "Developed by: %s\n", WS_DEVELOPED);
|
||||
furi_string_cat_printf(temp_str, "Github: %s\n\n", WS_GITHUB);
|
||||
|
||||
furi_string_cat_printf(temp_str, "\e#%s\n", "Description");
|
||||
furi_string_cat_printf(
|
||||
temp_str, "Reading messages from\nweather stations that work\nwith SubGhz sensors\n\n");
|
||||
|
||||
furi_string_cat_printf(temp_str, "Supported protocols:\n");
|
||||
size_t i = 0;
|
||||
const char* protocol_name =
|
||||
subghz_environment_get_protocol_name_registry(app->txrx->environment, i++);
|
||||
do {
|
||||
furi_string_cat_printf(temp_str, "%s\n", protocol_name);
|
||||
protocol_name = subghz_environment_get_protocol_name_registry(app->txrx->environment, i++);
|
||||
} while(protocol_name != NULL);
|
||||
|
||||
widget_add_text_box_element(
|
||||
app->widget,
|
||||
0,
|
||||
0,
|
||||
128,
|
||||
14,
|
||||
AlignCenter,
|
||||
AlignBottom,
|
||||
"\e#\e! \e!\n",
|
||||
false);
|
||||
widget_add_text_box_element(
|
||||
app->widget,
|
||||
0,
|
||||
2,
|
||||
128,
|
||||
14,
|
||||
AlignCenter,
|
||||
AlignBottom,
|
||||
"\e#\e! Weather station \e!\n",
|
||||
false);
|
||||
widget_add_text_scroll_element(app->widget, 0, 16, 128, 50, furi_string_get_cstr(temp_str));
|
||||
furi_string_free(temp_str);
|
||||
|
||||
view_dispatcher_switch_to_view(app->view_dispatcher, WeatherStationViewWidget);
|
||||
}
|
||||
|
||||
bool weather_station_scene_about_on_event(void* context, SceneManagerEvent event) {
|
||||
WeatherStationApp* app = context;
|
||||
bool consumed = false;
|
||||
UNUSED(app);
|
||||
UNUSED(event);
|
||||
|
||||
return consumed;
|
||||
}
|
||||
|
||||
void weather_station_scene_about_on_exit(void* context) {
|
||||
WeatherStationApp* app = context;
|
||||
|
||||
// Clear views
|
||||
widget_reset(app->widget);
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
ADD_SCENE(weather_station, start, Start)
|
||||
ADD_SCENE(weather_station, about, About)
|
||||
ADD_SCENE(weather_station, receiver, Receiver)
|
||||
ADD_SCENE(weather_station, receiver_config, ReceiverConfig)
|
||||
ADD_SCENE(weather_station, receiver_info, ReceiverInfo)
|
||||
@@ -1,223 +0,0 @@
|
||||
#include "../weather_station_app_i.h"
|
||||
|
||||
enum WSSettingIndex {
|
||||
WSSettingIndexFrequency,
|
||||
WSSettingIndexHopping,
|
||||
WSSettingIndexModulation,
|
||||
WSSettingIndexLock,
|
||||
};
|
||||
|
||||
#define HOPPING_COUNT 2
|
||||
const char* const hopping_text[HOPPING_COUNT] = {
|
||||
"OFF",
|
||||
"ON",
|
||||
};
|
||||
const uint32_t hopping_value[HOPPING_COUNT] = {
|
||||
WSHopperStateOFF,
|
||||
WSHopperStateRunnig,
|
||||
};
|
||||
|
||||
uint8_t weather_station_scene_receiver_config_next_frequency(const uint32_t value, void* context) {
|
||||
furi_assert(context);
|
||||
WeatherStationApp* app = context;
|
||||
uint8_t index = 0;
|
||||
for(uint8_t i = 0; i < subghz_setting_get_frequency_count(app->setting); i++) {
|
||||
if(value == subghz_setting_get_frequency(app->setting, i)) {
|
||||
index = i;
|
||||
break;
|
||||
} else {
|
||||
index = subghz_setting_get_frequency_default_index(app->setting);
|
||||
}
|
||||
}
|
||||
return index;
|
||||
}
|
||||
|
||||
uint8_t weather_station_scene_receiver_config_next_preset(const char* preset_name, void* context) {
|
||||
furi_assert(context);
|
||||
WeatherStationApp* app = context;
|
||||
uint8_t index = 0;
|
||||
for(uint8_t i = 0; i < subghz_setting_get_preset_count(app->setting); i++) {
|
||||
if(!strcmp(subghz_setting_get_preset_name(app->setting, i), preset_name)) {
|
||||
index = i;
|
||||
break;
|
||||
} else {
|
||||
// index = subghz_setting_get_frequency_default_index(app ->setting);
|
||||
}
|
||||
}
|
||||
return index;
|
||||
}
|
||||
|
||||
uint8_t weather_station_scene_receiver_config_hopper_value_index(
|
||||
const uint32_t value,
|
||||
const uint32_t values[],
|
||||
uint8_t values_count,
|
||||
void* context) {
|
||||
furi_assert(context);
|
||||
UNUSED(values_count);
|
||||
WeatherStationApp* app = context;
|
||||
|
||||
if(value == values[0]) {
|
||||
return 0;
|
||||
} else {
|
||||
variable_item_set_current_value_text(
|
||||
(VariableItem*)scene_manager_get_scene_state(
|
||||
app->scene_manager, WeatherStationSceneReceiverConfig),
|
||||
" -----");
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
static void weather_station_scene_receiver_config_set_frequency(VariableItem* item) {
|
||||
WeatherStationApp* app = variable_item_get_context(item);
|
||||
uint8_t index = variable_item_get_current_value_index(item);
|
||||
|
||||
if(app->txrx->hopper_state == WSHopperStateOFF) {
|
||||
char text_buf[10] = {0};
|
||||
snprintf(
|
||||
text_buf,
|
||||
sizeof(text_buf),
|
||||
"%lu.%02lu",
|
||||
subghz_setting_get_frequency(app->setting, index) / 1000000,
|
||||
(subghz_setting_get_frequency(app->setting, index) % 1000000) / 10000);
|
||||
variable_item_set_current_value_text(item, text_buf);
|
||||
app->txrx->preset->frequency = subghz_setting_get_frequency(app->setting, index);
|
||||
} else {
|
||||
variable_item_set_current_value_index(
|
||||
item, subghz_setting_get_frequency_default_index(app->setting));
|
||||
}
|
||||
}
|
||||
|
||||
static void weather_station_scene_receiver_config_set_preset(VariableItem* item) {
|
||||
WeatherStationApp* app = variable_item_get_context(item);
|
||||
uint8_t index = variable_item_get_current_value_index(item);
|
||||
variable_item_set_current_value_text(
|
||||
item, subghz_setting_get_preset_name(app->setting, index));
|
||||
ws_preset_init(
|
||||
app,
|
||||
subghz_setting_get_preset_name(app->setting, index),
|
||||
app->txrx->preset->frequency,
|
||||
subghz_setting_get_preset_data(app->setting, index),
|
||||
subghz_setting_get_preset_data_size(app->setting, index));
|
||||
}
|
||||
|
||||
static void weather_station_scene_receiver_config_set_hopping_running(VariableItem* item) {
|
||||
WeatherStationApp* app = variable_item_get_context(item);
|
||||
uint8_t index = variable_item_get_current_value_index(item);
|
||||
|
||||
variable_item_set_current_value_text(item, hopping_text[index]);
|
||||
if(hopping_value[index] == WSHopperStateOFF) {
|
||||
char text_buf[10] = {0};
|
||||
snprintf(
|
||||
text_buf,
|
||||
sizeof(text_buf),
|
||||
"%lu.%02lu",
|
||||
subghz_setting_get_default_frequency(app->setting) / 1000000,
|
||||
(subghz_setting_get_default_frequency(app->setting) % 1000000) / 10000);
|
||||
variable_item_set_current_value_text(
|
||||
(VariableItem*)scene_manager_get_scene_state(
|
||||
app->scene_manager, WeatherStationSceneReceiverConfig),
|
||||
text_buf);
|
||||
app->txrx->preset->frequency = subghz_setting_get_default_frequency(app->setting);
|
||||
variable_item_set_current_value_index(
|
||||
(VariableItem*)scene_manager_get_scene_state(
|
||||
app->scene_manager, WeatherStationSceneReceiverConfig),
|
||||
subghz_setting_get_frequency_default_index(app->setting));
|
||||
} else {
|
||||
variable_item_set_current_value_text(
|
||||
(VariableItem*)scene_manager_get_scene_state(
|
||||
app->scene_manager, WeatherStationSceneReceiverConfig),
|
||||
" -----");
|
||||
variable_item_set_current_value_index(
|
||||
(VariableItem*)scene_manager_get_scene_state(
|
||||
app->scene_manager, WeatherStationSceneReceiverConfig),
|
||||
subghz_setting_get_frequency_default_index(app->setting));
|
||||
}
|
||||
|
||||
app->txrx->hopper_state = hopping_value[index];
|
||||
}
|
||||
|
||||
static void
|
||||
weather_station_scene_receiver_config_var_list_enter_callback(void* context, uint32_t index) {
|
||||
furi_assert(context);
|
||||
WeatherStationApp* app = context;
|
||||
if(index == WSSettingIndexLock) {
|
||||
view_dispatcher_send_custom_event(app->view_dispatcher, WSCustomEventSceneSettingLock);
|
||||
}
|
||||
}
|
||||
|
||||
void weather_station_scene_receiver_config_on_enter(void* context) {
|
||||
WeatherStationApp* app = context;
|
||||
VariableItem* item;
|
||||
uint8_t value_index;
|
||||
|
||||
item = variable_item_list_add(
|
||||
app->variable_item_list,
|
||||
"Frequency:",
|
||||
subghz_setting_get_frequency_count(app->setting),
|
||||
weather_station_scene_receiver_config_set_frequency,
|
||||
app);
|
||||
value_index =
|
||||
weather_station_scene_receiver_config_next_frequency(app->txrx->preset->frequency, app);
|
||||
scene_manager_set_scene_state(
|
||||
app->scene_manager, WeatherStationSceneReceiverConfig, (uint32_t)item);
|
||||
variable_item_set_current_value_index(item, value_index);
|
||||
char text_buf[10] = {0};
|
||||
snprintf(
|
||||
text_buf,
|
||||
sizeof(text_buf),
|
||||
"%lu.%02lu",
|
||||
subghz_setting_get_frequency(app->setting, value_index) / 1000000,
|
||||
(subghz_setting_get_frequency(app->setting, value_index) % 1000000) / 10000);
|
||||
variable_item_set_current_value_text(item, text_buf);
|
||||
|
||||
item = variable_item_list_add(
|
||||
app->variable_item_list,
|
||||
"Hopping:",
|
||||
HOPPING_COUNT,
|
||||
weather_station_scene_receiver_config_set_hopping_running,
|
||||
app);
|
||||
value_index = weather_station_scene_receiver_config_hopper_value_index(
|
||||
app->txrx->hopper_state, hopping_value, HOPPING_COUNT, app);
|
||||
variable_item_set_current_value_index(item, value_index);
|
||||
variable_item_set_current_value_text(item, hopping_text[value_index]);
|
||||
|
||||
item = variable_item_list_add(
|
||||
app->variable_item_list,
|
||||
"Modulation:",
|
||||
subghz_setting_get_preset_count(app->setting),
|
||||
weather_station_scene_receiver_config_set_preset,
|
||||
app);
|
||||
value_index = weather_station_scene_receiver_config_next_preset(
|
||||
furi_string_get_cstr(app->txrx->preset->name), app);
|
||||
variable_item_set_current_value_index(item, value_index);
|
||||
variable_item_set_current_value_text(
|
||||
item, subghz_setting_get_preset_name(app->setting, value_index));
|
||||
|
||||
variable_item_list_add(app->variable_item_list, "Lock Keyboard", 1, NULL, NULL);
|
||||
variable_item_list_set_enter_callback(
|
||||
app->variable_item_list,
|
||||
weather_station_scene_receiver_config_var_list_enter_callback,
|
||||
app);
|
||||
|
||||
view_dispatcher_switch_to_view(app->view_dispatcher, WeatherStationViewVariableItemList);
|
||||
}
|
||||
|
||||
bool weather_station_scene_receiver_config_on_event(void* context, SceneManagerEvent event) {
|
||||
WeatherStationApp* app = context;
|
||||
bool consumed = false;
|
||||
|
||||
if(event.type == SceneManagerEventTypeCustom) {
|
||||
if(event.event == WSCustomEventSceneSettingLock) {
|
||||
app->lock = WSLockOn;
|
||||
scene_manager_previous_scene(app->scene_manager);
|
||||
consumed = true;
|
||||
}
|
||||
}
|
||||
return consumed;
|
||||
}
|
||||
|
||||
void weather_station_scene_receiver_config_on_exit(void* context) {
|
||||
WeatherStationApp* app = context;
|
||||
variable_item_list_set_selected_item(app->variable_item_list, 0);
|
||||
variable_item_list_reset(app->variable_item_list);
|
||||
}
|
||||
@@ -1,50 +0,0 @@
|
||||
#include "../weather_station_app_i.h"
|
||||
#include "../views/weather_station_receiver.h"
|
||||
|
||||
void weather_station_scene_receiver_info_callback(WSCustomEvent event, void* context) {
|
||||
furi_assert(context);
|
||||
WeatherStationApp* app = context;
|
||||
view_dispatcher_send_custom_event(app->view_dispatcher, event);
|
||||
}
|
||||
|
||||
static void weather_station_scene_receiver_info_add_to_history_callback(
|
||||
SubGhzReceiver* receiver,
|
||||
SubGhzProtocolDecoderBase* decoder_base,
|
||||
void* context) {
|
||||
furi_assert(context);
|
||||
WeatherStationApp* app = context;
|
||||
|
||||
if(ws_history_add_to_history(app->txrx->history, decoder_base, app->txrx->preset) ==
|
||||
WSHistoryStateAddKeyUpdateData) {
|
||||
ws_view_receiver_info_update(
|
||||
app->ws_receiver_info,
|
||||
ws_history_get_raw_data(app->txrx->history, app->txrx->idx_menu_chosen));
|
||||
subghz_receiver_reset(receiver);
|
||||
|
||||
notification_message(app->notifications, &sequence_blink_green_10);
|
||||
app->txrx->rx_key_state = WSRxKeyStateAddKey;
|
||||
}
|
||||
}
|
||||
|
||||
void weather_station_scene_receiver_info_on_enter(void* context) {
|
||||
WeatherStationApp* app = context;
|
||||
|
||||
subghz_receiver_set_rx_callback(
|
||||
app->txrx->receiver, weather_station_scene_receiver_info_add_to_history_callback, app);
|
||||
ws_view_receiver_info_update(
|
||||
app->ws_receiver_info,
|
||||
ws_history_get_raw_data(app->txrx->history, app->txrx->idx_menu_chosen));
|
||||
view_dispatcher_switch_to_view(app->view_dispatcher, WeatherStationViewReceiverInfo);
|
||||
}
|
||||
|
||||
bool weather_station_scene_receiver_info_on_event(void* context, SceneManagerEvent event) {
|
||||
WeatherStationApp* app = context;
|
||||
bool consumed = false;
|
||||
UNUSED(app);
|
||||
UNUSED(event);
|
||||
return consumed;
|
||||
}
|
||||
|
||||
void weather_station_scene_receiver_info_on_exit(void* context) {
|
||||
UNUSED(context);
|
||||
}
|
||||
@@ -1,58 +0,0 @@
|
||||
#include "../weather_station_app_i.h"
|
||||
|
||||
typedef enum {
|
||||
SubmenuIndexWeatherStationReceiver,
|
||||
SubmenuIndexWeatherStationAbout,
|
||||
} SubmenuIndex;
|
||||
|
||||
void weather_station_scene_start_submenu_callback(void* context, uint32_t index) {
|
||||
WeatherStationApp* app = context;
|
||||
view_dispatcher_send_custom_event(app->view_dispatcher, index);
|
||||
}
|
||||
|
||||
void weather_station_scene_start_on_enter(void* context) {
|
||||
UNUSED(context);
|
||||
WeatherStationApp* app = context;
|
||||
Submenu* submenu = app->submenu;
|
||||
|
||||
submenu_add_item(
|
||||
submenu,
|
||||
"Read Weather Station",
|
||||
SubmenuIndexWeatherStationReceiver,
|
||||
weather_station_scene_start_submenu_callback,
|
||||
app);
|
||||
submenu_add_item(
|
||||
submenu,
|
||||
"About",
|
||||
SubmenuIndexWeatherStationAbout,
|
||||
weather_station_scene_start_submenu_callback,
|
||||
app);
|
||||
|
||||
submenu_set_selected_item(
|
||||
submenu, scene_manager_get_scene_state(app->scene_manager, WeatherStationSceneStart));
|
||||
|
||||
view_dispatcher_switch_to_view(app->view_dispatcher, WeatherStationViewSubmenu);
|
||||
}
|
||||
|
||||
bool weather_station_scene_start_on_event(void* context, SceneManagerEvent event) {
|
||||
WeatherStationApp* app = context;
|
||||
bool consumed = false;
|
||||
|
||||
if(event.type == SceneManagerEventTypeCustom) {
|
||||
if(event.event == SubmenuIndexWeatherStationAbout) {
|
||||
scene_manager_next_scene(app->scene_manager, WeatherStationSceneAbout);
|
||||
consumed = true;
|
||||
} else if(event.event == SubmenuIndexWeatherStationReceiver) {
|
||||
scene_manager_next_scene(app->scene_manager, WeatherStationSceneReceiver);
|
||||
consumed = true;
|
||||
}
|
||||
scene_manager_set_scene_state(app->scene_manager, WeatherStationSceneStart, event.event);
|
||||
}
|
||||
|
||||
return consumed;
|
||||
}
|
||||
|
||||
void weather_station_scene_start_on_exit(void* context) {
|
||||
WeatherStationApp* app = context;
|
||||
submenu_reset(app->submenu);
|
||||
}
|
||||
@@ -1,473 +0,0 @@
|
||||
#include "weather_station_receiver.h"
|
||||
#include "../weather_station_app_i.h"
|
||||
#include "weather_station_icons.h"
|
||||
#include <assets_icons.h>
|
||||
#include <math.h>
|
||||
|
||||
#include <input/input.h>
|
||||
#include <gui/elements.h>
|
||||
#include <m-array.h>
|
||||
|
||||
#define FRAME_HEIGHT 12
|
||||
#define MAX_LEN_PX 112
|
||||
#define MENU_ITEMS 4u
|
||||
#define UNLOCK_CNT 3
|
||||
|
||||
#define SUBGHZ_RAW_THRESHOLD_MIN -90.0f
|
||||
typedef struct {
|
||||
FuriString* item_str;
|
||||
uint8_t type;
|
||||
} WSReceiverMenuItem;
|
||||
|
||||
ARRAY_DEF(WSReceiverMenuItemArray, WSReceiverMenuItem, M_POD_OPLIST)
|
||||
|
||||
#define M_OPL_WSReceiverMenuItemArray_t() ARRAY_OPLIST(WSReceiverMenuItemArray, M_POD_OPLIST)
|
||||
|
||||
struct WSReceiverHistory {
|
||||
WSReceiverMenuItemArray_t data;
|
||||
};
|
||||
|
||||
typedef struct WSReceiverHistory WSReceiverHistory;
|
||||
|
||||
static const Icon* ReceiverItemIcons[] = {
|
||||
[SubGhzProtocolTypeUnknown] = &I_Quest_7x8,
|
||||
[SubGhzProtocolTypeStatic] = &I_Unlock_7x8,
|
||||
[SubGhzProtocolTypeDynamic] = &I_Lock_7x8,
|
||||
[SubGhzProtocolWeatherStation] = &I_station_icon,
|
||||
};
|
||||
|
||||
typedef enum {
|
||||
WSReceiverBarShowDefault,
|
||||
WSReceiverBarShowLock,
|
||||
WSReceiverBarShowToUnlockPress,
|
||||
WSReceiverBarShowUnlock,
|
||||
} WSReceiverBarShow;
|
||||
|
||||
struct WSReceiver {
|
||||
WSLock lock;
|
||||
uint8_t lock_count;
|
||||
FuriTimer* timer;
|
||||
View* view;
|
||||
WSReceiverCallback callback;
|
||||
void* context;
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
FuriString* frequency_str;
|
||||
FuriString* preset_str;
|
||||
FuriString* history_stat_str;
|
||||
WSReceiverHistory* history;
|
||||
uint16_t idx;
|
||||
uint16_t list_offset;
|
||||
uint16_t history_item;
|
||||
WSReceiverBarShow bar_show;
|
||||
uint8_t u_rssi;
|
||||
bool external_redio;
|
||||
} WSReceiverModel;
|
||||
|
||||
void ws_view_receiver_set_rssi(WSReceiver* instance, float rssi) {
|
||||
furi_assert(instance);
|
||||
with_view_model(
|
||||
instance->view,
|
||||
WSReceiverModel * model,
|
||||
{
|
||||
if(rssi < SUBGHZ_RAW_THRESHOLD_MIN) {
|
||||
model->u_rssi = 0;
|
||||
} else {
|
||||
model->u_rssi = (uint8_t)(rssi - SUBGHZ_RAW_THRESHOLD_MIN);
|
||||
}
|
||||
},
|
||||
true);
|
||||
}
|
||||
|
||||
void ws_view_receiver_set_lock(WSReceiver* ws_receiver, WSLock lock) {
|
||||
furi_assert(ws_receiver);
|
||||
ws_receiver->lock_count = 0;
|
||||
if(lock == WSLockOn) {
|
||||
ws_receiver->lock = lock;
|
||||
with_view_model(
|
||||
ws_receiver->view,
|
||||
WSReceiverModel * model,
|
||||
{ model->bar_show = WSReceiverBarShowLock; },
|
||||
true);
|
||||
furi_timer_start(ws_receiver->timer, pdMS_TO_TICKS(1000));
|
||||
} else {
|
||||
with_view_model(
|
||||
ws_receiver->view,
|
||||
WSReceiverModel * model,
|
||||
{ model->bar_show = WSReceiverBarShowDefault; },
|
||||
true);
|
||||
}
|
||||
}
|
||||
|
||||
void ws_view_receiver_set_callback(
|
||||
WSReceiver* ws_receiver,
|
||||
WSReceiverCallback callback,
|
||||
void* context) {
|
||||
furi_assert(ws_receiver);
|
||||
furi_assert(callback);
|
||||
ws_receiver->callback = callback;
|
||||
ws_receiver->context = context;
|
||||
}
|
||||
|
||||
static void ws_view_receiver_update_offset(WSReceiver* ws_receiver) {
|
||||
furi_assert(ws_receiver);
|
||||
|
||||
with_view_model(
|
||||
ws_receiver->view,
|
||||
WSReceiverModel * model,
|
||||
{
|
||||
size_t history_item = model->history_item;
|
||||
uint16_t bounds = history_item > 3 ? 2 : history_item;
|
||||
|
||||
if(history_item > 3 && model->idx >= (int16_t)(history_item - 1)) {
|
||||
model->list_offset = model->idx - 3;
|
||||
} else if(model->list_offset < model->idx - bounds) {
|
||||
model->list_offset =
|
||||
CLAMP(model->list_offset + 1, (int16_t)(history_item - bounds), 0);
|
||||
} else if(model->list_offset > model->idx - bounds) {
|
||||
model->list_offset = CLAMP(model->idx - 1, (int16_t)(history_item - bounds), 0);
|
||||
}
|
||||
},
|
||||
true);
|
||||
}
|
||||
|
||||
void ws_view_receiver_add_item_to_menu(WSReceiver* ws_receiver, const char* name, uint8_t type) {
|
||||
furi_assert(ws_receiver);
|
||||
with_view_model(
|
||||
ws_receiver->view,
|
||||
WSReceiverModel * model,
|
||||
{
|
||||
WSReceiverMenuItem* item_menu = WSReceiverMenuItemArray_push_raw(model->history->data);
|
||||
item_menu->item_str = furi_string_alloc_set(name);
|
||||
item_menu->type = type;
|
||||
if((model->idx == model->history_item - 1)) {
|
||||
model->history_item++;
|
||||
model->idx++;
|
||||
} else {
|
||||
model->history_item++;
|
||||
}
|
||||
},
|
||||
true);
|
||||
ws_view_receiver_update_offset(ws_receiver);
|
||||
}
|
||||
|
||||
void ws_view_receiver_add_data_statusbar(
|
||||
WSReceiver* ws_receiver,
|
||||
const char* frequency_str,
|
||||
const char* preset_str,
|
||||
const char* history_stat_str,
|
||||
bool external) {
|
||||
furi_assert(ws_receiver);
|
||||
with_view_model(
|
||||
ws_receiver->view,
|
||||
WSReceiverModel * model,
|
||||
{
|
||||
furi_string_set_str(model->frequency_str, frequency_str);
|
||||
furi_string_set_str(model->preset_str, preset_str);
|
||||
furi_string_set_str(model->history_stat_str, history_stat_str);
|
||||
model->external_redio = external;
|
||||
},
|
||||
true);
|
||||
}
|
||||
|
||||
static void ws_view_receiver_draw_frame(Canvas* canvas, uint16_t idx, bool scrollbar) {
|
||||
canvas_set_color(canvas, ColorBlack);
|
||||
canvas_draw_box(canvas, 0, 0 + idx * FRAME_HEIGHT, scrollbar ? 122 : 127, FRAME_HEIGHT);
|
||||
|
||||
canvas_set_color(canvas, ColorWhite);
|
||||
canvas_draw_dot(canvas, 0, 0 + idx * FRAME_HEIGHT);
|
||||
canvas_draw_dot(canvas, 1, 0 + idx * FRAME_HEIGHT);
|
||||
canvas_draw_dot(canvas, 0, (0 + idx * FRAME_HEIGHT) + 1);
|
||||
|
||||
canvas_draw_dot(canvas, 0, (0 + idx * FRAME_HEIGHT) + 11);
|
||||
canvas_draw_dot(canvas, scrollbar ? 121 : 126, 0 + idx * FRAME_HEIGHT);
|
||||
canvas_draw_dot(canvas, scrollbar ? 121 : 126, (0 + idx * FRAME_HEIGHT) + 11);
|
||||
}
|
||||
|
||||
static void ws_view_rssi_draw(Canvas* canvas, WSReceiverModel* model) {
|
||||
for(uint8_t i = 1; i < model->u_rssi; i++) {
|
||||
if(i % 5) {
|
||||
canvas_draw_dot(canvas, 46 + i, 50);
|
||||
canvas_draw_dot(canvas, 47 + i, 51);
|
||||
canvas_draw_dot(canvas, 46 + i, 52);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ws_view_receiver_draw(Canvas* canvas, WSReceiverModel* model) {
|
||||
canvas_clear(canvas);
|
||||
canvas_set_color(canvas, ColorBlack);
|
||||
canvas_set_font(canvas, FontSecondary);
|
||||
|
||||
elements_button_left(canvas, "Config");
|
||||
|
||||
bool scrollbar = model->history_item > 4;
|
||||
FuriString* str_buff;
|
||||
str_buff = furi_string_alloc();
|
||||
|
||||
// bool ext_module = furi_hal_subghz_get_radio_type();
|
||||
|
||||
WSReceiverMenuItem* item_menu;
|
||||
|
||||
for(size_t i = 0; i < MIN(model->history_item, MENU_ITEMS); ++i) {
|
||||
size_t idx = CLAMP((uint16_t)(i + model->list_offset), model->history_item, 0);
|
||||
item_menu = WSReceiverMenuItemArray_get(model->history->data, idx);
|
||||
furi_string_set(str_buff, item_menu->item_str);
|
||||
elements_string_fit_width(canvas, str_buff, scrollbar ? MAX_LEN_PX - 6 : MAX_LEN_PX);
|
||||
if(model->idx == idx) {
|
||||
ws_view_receiver_draw_frame(canvas, i, scrollbar);
|
||||
} else {
|
||||
canvas_set_color(canvas, ColorBlack);
|
||||
}
|
||||
canvas_draw_icon(canvas, 4, 2 + i * FRAME_HEIGHT, ReceiverItemIcons[item_menu->type]);
|
||||
canvas_draw_str(canvas, 14, 9 + i * FRAME_HEIGHT, furi_string_get_cstr(str_buff));
|
||||
furi_string_reset(str_buff);
|
||||
}
|
||||
if(scrollbar) {
|
||||
elements_scrollbar_pos(canvas, 128, 0, 49, model->idx, model->history_item);
|
||||
}
|
||||
furi_string_free(str_buff);
|
||||
|
||||
canvas_set_color(canvas, ColorBlack);
|
||||
|
||||
if(model->history_item == 0) {
|
||||
canvas_draw_icon(
|
||||
canvas, 0, 0, model->external_redio ? &I_Fishing_123x52 : &I_Scanning_123x52);
|
||||
canvas_set_font(canvas, FontPrimary);
|
||||
canvas_draw_str(canvas, 63, 46, "Scanning...");
|
||||
canvas_set_font(canvas, FontSecondary);
|
||||
canvas_draw_str(canvas, 44, 10, model->external_redio ? "Ext" : "Int");
|
||||
}
|
||||
|
||||
// Draw RSSI
|
||||
ws_view_rssi_draw(canvas, model);
|
||||
|
||||
switch(model->bar_show) {
|
||||
case WSReceiverBarShowLock:
|
||||
canvas_draw_icon(canvas, 64, 55, &I_Lock_7x8);
|
||||
canvas_draw_str(canvas, 74, 62, "Locked");
|
||||
break;
|
||||
case WSReceiverBarShowToUnlockPress:
|
||||
canvas_draw_str(canvas, 44, 62, furi_string_get_cstr(model->frequency_str));
|
||||
canvas_draw_str(canvas, 79, 62, furi_string_get_cstr(model->preset_str));
|
||||
canvas_draw_str(canvas, 96, 62, furi_string_get_cstr(model->history_stat_str));
|
||||
canvas_set_font(canvas, FontSecondary);
|
||||
elements_bold_rounded_frame(canvas, 14, 8, 99, 48);
|
||||
elements_multiline_text(canvas, 65, 26, "To unlock\npress:");
|
||||
canvas_draw_icon(canvas, 65, 42, &I_Pin_back_arrow_10x8);
|
||||
canvas_draw_icon(canvas, 80, 42, &I_Pin_back_arrow_10x8);
|
||||
canvas_draw_icon(canvas, 95, 42, &I_Pin_back_arrow_10x8);
|
||||
canvas_draw_icon(canvas, 16, 13, &I_WarningDolphin_45x42);
|
||||
canvas_draw_dot(canvas, 17, 61);
|
||||
break;
|
||||
case WSReceiverBarShowUnlock:
|
||||
canvas_draw_icon(canvas, 64, 55, &I_Unlock_7x8);
|
||||
canvas_draw_str(canvas, 74, 62, "Unlocked");
|
||||
break;
|
||||
default:
|
||||
canvas_draw_str(canvas, 44, 62, furi_string_get_cstr(model->frequency_str));
|
||||
canvas_draw_str(canvas, 79, 62, furi_string_get_cstr(model->preset_str));
|
||||
canvas_draw_str(canvas, 96, 62, furi_string_get_cstr(model->history_stat_str));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void ws_view_receiver_timer_callback(void* context) {
|
||||
furi_assert(context);
|
||||
WSReceiver* ws_receiver = context;
|
||||
with_view_model(
|
||||
ws_receiver->view,
|
||||
WSReceiverModel * model,
|
||||
{ model->bar_show = WSReceiverBarShowDefault; },
|
||||
true);
|
||||
if(ws_receiver->lock_count < UNLOCK_CNT) {
|
||||
ws_receiver->callback(WSCustomEventViewReceiverOffDisplay, ws_receiver->context);
|
||||
} else {
|
||||
ws_receiver->lock = WSLockOff;
|
||||
ws_receiver->callback(WSCustomEventViewReceiverUnlock, ws_receiver->context);
|
||||
}
|
||||
ws_receiver->lock_count = 0;
|
||||
}
|
||||
|
||||
bool ws_view_receiver_input(InputEvent* event, void* context) {
|
||||
furi_assert(context);
|
||||
WSReceiver* ws_receiver = context;
|
||||
|
||||
if(ws_receiver->lock == WSLockOn) {
|
||||
with_view_model(
|
||||
ws_receiver->view,
|
||||
WSReceiverModel * model,
|
||||
{ model->bar_show = WSReceiverBarShowToUnlockPress; },
|
||||
true);
|
||||
if(ws_receiver->lock_count == 0) {
|
||||
furi_timer_start(ws_receiver->timer, pdMS_TO_TICKS(1000));
|
||||
}
|
||||
if(event->key == InputKeyBack && event->type == InputTypeShort) {
|
||||
ws_receiver->lock_count++;
|
||||
}
|
||||
if(ws_receiver->lock_count >= UNLOCK_CNT) {
|
||||
ws_receiver->callback(WSCustomEventViewReceiverUnlock, ws_receiver->context);
|
||||
with_view_model(
|
||||
ws_receiver->view,
|
||||
WSReceiverModel * model,
|
||||
{ model->bar_show = WSReceiverBarShowUnlock; },
|
||||
true);
|
||||
ws_receiver->lock = WSLockOff;
|
||||
furi_timer_start(ws_receiver->timer, pdMS_TO_TICKS(650));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
if(event->key == InputKeyBack && event->type == InputTypeShort) {
|
||||
ws_receiver->callback(WSCustomEventViewReceiverBack, ws_receiver->context);
|
||||
} else if(
|
||||
event->key == InputKeyUp &&
|
||||
(event->type == InputTypeShort || event->type == InputTypeRepeat)) {
|
||||
with_view_model(
|
||||
ws_receiver->view,
|
||||
WSReceiverModel * model,
|
||||
{
|
||||
if(model->idx != 0) model->idx--;
|
||||
},
|
||||
true);
|
||||
} else if(
|
||||
event->key == InputKeyDown &&
|
||||
(event->type == InputTypeShort || event->type == InputTypeRepeat)) {
|
||||
with_view_model(
|
||||
ws_receiver->view,
|
||||
WSReceiverModel * model,
|
||||
{
|
||||
if(model->history_item && model->idx != model->history_item - 1) model->idx++;
|
||||
},
|
||||
true);
|
||||
} else if(event->key == InputKeyLeft && event->type == InputTypeShort) {
|
||||
ws_receiver->callback(WSCustomEventViewReceiverConfig, ws_receiver->context);
|
||||
} else if(event->key == InputKeyOk && event->type == InputTypeShort) {
|
||||
with_view_model(
|
||||
ws_receiver->view,
|
||||
WSReceiverModel * model,
|
||||
{
|
||||
if(model->history_item != 0) {
|
||||
ws_receiver->callback(WSCustomEventViewReceiverOK, ws_receiver->context);
|
||||
}
|
||||
},
|
||||
false);
|
||||
}
|
||||
|
||||
ws_view_receiver_update_offset(ws_receiver);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void ws_view_receiver_enter(void* context) {
|
||||
furi_assert(context);
|
||||
}
|
||||
|
||||
void ws_view_receiver_exit(void* context) {
|
||||
furi_assert(context);
|
||||
WSReceiver* ws_receiver = context;
|
||||
with_view_model(
|
||||
ws_receiver->view,
|
||||
WSReceiverModel * model,
|
||||
{
|
||||
furi_string_reset(model->frequency_str);
|
||||
furi_string_reset(model->preset_str);
|
||||
furi_string_reset(model->history_stat_str);
|
||||
for
|
||||
M_EACH(item_menu, model->history->data, WSReceiverMenuItemArray_t) {
|
||||
furi_string_free(item_menu->item_str);
|
||||
item_menu->type = 0;
|
||||
}
|
||||
WSReceiverMenuItemArray_reset(model->history->data);
|
||||
model->idx = 0;
|
||||
model->list_offset = 0;
|
||||
model->history_item = 0;
|
||||
},
|
||||
false);
|
||||
furi_timer_stop(ws_receiver->timer);
|
||||
}
|
||||
|
||||
WSReceiver* ws_view_receiver_alloc() {
|
||||
WSReceiver* ws_receiver = malloc(sizeof(WSReceiver));
|
||||
|
||||
// View allocation and configuration
|
||||
ws_receiver->view = view_alloc();
|
||||
|
||||
ws_receiver->lock = WSLockOff;
|
||||
ws_receiver->lock_count = 0;
|
||||
view_allocate_model(ws_receiver->view, ViewModelTypeLocking, sizeof(WSReceiverModel));
|
||||
view_set_context(ws_receiver->view, ws_receiver);
|
||||
view_set_draw_callback(ws_receiver->view, (ViewDrawCallback)ws_view_receiver_draw);
|
||||
view_set_input_callback(ws_receiver->view, ws_view_receiver_input);
|
||||
view_set_enter_callback(ws_receiver->view, ws_view_receiver_enter);
|
||||
view_set_exit_callback(ws_receiver->view, ws_view_receiver_exit);
|
||||
|
||||
with_view_model(
|
||||
ws_receiver->view,
|
||||
WSReceiverModel * model,
|
||||
{
|
||||
model->frequency_str = furi_string_alloc();
|
||||
model->preset_str = furi_string_alloc();
|
||||
model->history_stat_str = furi_string_alloc();
|
||||
model->bar_show = WSReceiverBarShowDefault;
|
||||
model->history = malloc(sizeof(WSReceiverHistory));
|
||||
model->external_redio = false;
|
||||
WSReceiverMenuItemArray_init(model->history->data);
|
||||
},
|
||||
true);
|
||||
ws_receiver->timer =
|
||||
furi_timer_alloc(ws_view_receiver_timer_callback, FuriTimerTypeOnce, ws_receiver);
|
||||
return ws_receiver;
|
||||
}
|
||||
|
||||
void ws_view_receiver_free(WSReceiver* ws_receiver) {
|
||||
furi_assert(ws_receiver);
|
||||
|
||||
with_view_model(
|
||||
ws_receiver->view,
|
||||
WSReceiverModel * model,
|
||||
{
|
||||
furi_string_free(model->frequency_str);
|
||||
furi_string_free(model->preset_str);
|
||||
furi_string_free(model->history_stat_str);
|
||||
for
|
||||
M_EACH(item_menu, model->history->data, WSReceiverMenuItemArray_t) {
|
||||
furi_string_free(item_menu->item_str);
|
||||
item_menu->type = 0;
|
||||
}
|
||||
WSReceiverMenuItemArray_clear(model->history->data);
|
||||
free(model->history);
|
||||
},
|
||||
false);
|
||||
furi_timer_free(ws_receiver->timer);
|
||||
view_free(ws_receiver->view);
|
||||
free(ws_receiver);
|
||||
}
|
||||
|
||||
View* ws_view_receiver_get_view(WSReceiver* ws_receiver) {
|
||||
furi_assert(ws_receiver);
|
||||
return ws_receiver->view;
|
||||
}
|
||||
|
||||
uint16_t ws_view_receiver_get_idx_menu(WSReceiver* ws_receiver) {
|
||||
furi_assert(ws_receiver);
|
||||
uint32_t idx = 0;
|
||||
with_view_model(
|
||||
ws_receiver->view, WSReceiverModel * model, { idx = model->idx; }, false);
|
||||
return idx;
|
||||
}
|
||||
|
||||
void ws_view_receiver_set_idx_menu(WSReceiver* ws_receiver, uint16_t idx) {
|
||||
furi_assert(ws_receiver);
|
||||
with_view_model(
|
||||
ws_receiver->view,
|
||||
WSReceiverModel * model,
|
||||
{
|
||||
model->idx = idx;
|
||||
if(model->idx > 2) model->list_offset = idx - 2;
|
||||
},
|
||||
true);
|
||||
ws_view_receiver_update_offset(ws_receiver);
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <gui/view.h>
|
||||
#include "../helpers/weather_station_types.h"
|
||||
#include "../helpers/weather_station_event.h"
|
||||
|
||||
typedef struct WSReceiver WSReceiver;
|
||||
|
||||
typedef void (*WSReceiverCallback)(WSCustomEvent event, void* context);
|
||||
|
||||
void ws_view_receiver_set_rssi(WSReceiver* instance, float rssi);
|
||||
|
||||
void ws_view_receiver_set_lock(WSReceiver* ws_receiver, WSLock keyboard);
|
||||
|
||||
void ws_view_receiver_set_callback(
|
||||
WSReceiver* ws_receiver,
|
||||
WSReceiverCallback callback,
|
||||
void* context);
|
||||
|
||||
WSReceiver* ws_view_receiver_alloc();
|
||||
|
||||
void ws_view_receiver_free(WSReceiver* ws_receiver);
|
||||
|
||||
View* ws_view_receiver_get_view(WSReceiver* ws_receiver);
|
||||
|
||||
void ws_view_receiver_add_data_statusbar(
|
||||
WSReceiver* ws_receiver,
|
||||
const char* frequency_str,
|
||||
const char* preset_str,
|
||||
const char* history_stat_str,
|
||||
bool external);
|
||||
|
||||
void ws_view_receiver_add_item_to_menu(WSReceiver* ws_receiver, const char* name, uint8_t type);
|
||||
|
||||
uint16_t ws_view_receiver_get_idx_menu(WSReceiver* ws_receiver);
|
||||
|
||||
void ws_view_receiver_set_idx_menu(WSReceiver* ws_receiver, uint16_t idx);
|
||||
|
||||
void ws_view_receiver_exit(void* context);
|
||||
@@ -1,246 +0,0 @@
|
||||
#include "weather_station_receiver.h"
|
||||
#include "../weather_station_app_i.h"
|
||||
#include "weather_station_icons.h"
|
||||
#include <assets_icons.h>
|
||||
#include "../protocols/ws_generic.h"
|
||||
#include <input/input.h>
|
||||
#include <gui/elements.h>
|
||||
#include <float_tools.h>
|
||||
|
||||
struct WSReceiverInfo {
|
||||
View* view;
|
||||
FuriTimer* timer;
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
uint32_t curr_ts;
|
||||
FuriString* protocol_name;
|
||||
WSBlockGeneric* generic;
|
||||
} WSReceiverInfoModel;
|
||||
|
||||
void ws_view_receiver_info_update(WSReceiverInfo* ws_receiver_info, FlipperFormat* fff) {
|
||||
furi_assert(ws_receiver_info);
|
||||
furi_assert(fff);
|
||||
|
||||
with_view_model(
|
||||
ws_receiver_info->view,
|
||||
WSReceiverInfoModel * model,
|
||||
{
|
||||
flipper_format_rewind(fff);
|
||||
flipper_format_read_string(fff, "Protocol", model->protocol_name);
|
||||
|
||||
ws_block_generic_deserialize(model->generic, fff);
|
||||
|
||||
FuriHalRtcDateTime curr_dt;
|
||||
furi_hal_rtc_get_datetime(&curr_dt);
|
||||
model->curr_ts = furi_hal_rtc_datetime_to_timestamp(&curr_dt);
|
||||
},
|
||||
true);
|
||||
}
|
||||
|
||||
void ws_view_receiver_info_draw(Canvas* canvas, WSReceiverInfoModel* model) {
|
||||
char buffer[64];
|
||||
canvas_clear(canvas);
|
||||
canvas_set_color(canvas, ColorBlack);
|
||||
canvas_set_font(canvas, FontSecondary);
|
||||
|
||||
snprintf(
|
||||
buffer,
|
||||
sizeof(buffer),
|
||||
"%s %db",
|
||||
furi_string_get_cstr(model->protocol_name),
|
||||
model->generic->data_count_bit);
|
||||
canvas_draw_str(canvas, 0, 8, buffer);
|
||||
|
||||
if(model->generic->channel != WS_NO_CHANNEL) {
|
||||
snprintf(buffer, sizeof(buffer), "Ch: %01d", model->generic->channel);
|
||||
canvas_draw_str(canvas, 106, 8, buffer);
|
||||
}
|
||||
|
||||
if(model->generic->id != WS_NO_ID) {
|
||||
snprintf(buffer, sizeof(buffer), "Sn: 0x%02lX", model->generic->id);
|
||||
canvas_draw_str(canvas, 0, 20, buffer);
|
||||
}
|
||||
|
||||
if(model->generic->btn != WS_NO_BTN) {
|
||||
snprintf(buffer, sizeof(buffer), "Btn: %01d", model->generic->btn);
|
||||
canvas_draw_str(canvas, 57, 20, buffer);
|
||||
}
|
||||
|
||||
if(model->generic->battery_low != WS_NO_BATT) {
|
||||
snprintf(
|
||||
buffer, sizeof(buffer), "Batt: %s", (!model->generic->battery_low ? "ok" : "low"));
|
||||
canvas_draw_str_aligned(canvas, 126, 17, AlignRight, AlignCenter, buffer);
|
||||
}
|
||||
|
||||
snprintf(buffer, sizeof(buffer), "Data: 0x%llX", model->generic->data);
|
||||
canvas_draw_str(canvas, 0, 32, buffer);
|
||||
|
||||
elements_bold_rounded_frame(canvas, 0, 38, 127, 25);
|
||||
canvas_set_font(canvas, FontPrimary);
|
||||
|
||||
if(!float_is_equal(model->generic->temp, WS_NO_TEMPERATURE)) {
|
||||
canvas_draw_icon(canvas, 6, 43, &I_Therm_7x16);
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
if(model->generic->humidity != WS_NO_HUMIDITY) {
|
||||
canvas_draw_icon(canvas, 53, 44, &I_Humid_8x13);
|
||||
snprintf(buffer, sizeof(buffer), "%d%%", model->generic->humidity);
|
||||
canvas_draw_str(canvas, 64, 55, buffer);
|
||||
}
|
||||
|
||||
if((int)model->generic->timestamp > 0 && model->curr_ts) {
|
||||
int ts_diff = (int)model->curr_ts - (int)model->generic->timestamp;
|
||||
|
||||
canvas_draw_icon(canvas, 91, 46, &I_Timer_11x11);
|
||||
|
||||
if(ts_diff > 60) {
|
||||
int tmp_sec = ts_diff;
|
||||
int cnt_min = 1;
|
||||
for(int i = 1; tmp_sec > 60; i++) {
|
||||
tmp_sec = tmp_sec - 60;
|
||||
cnt_min = i;
|
||||
}
|
||||
|
||||
if(model->curr_ts % 2 == 0) {
|
||||
canvas_draw_str_aligned(canvas, 105, 51, AlignLeft, AlignCenter, "Old");
|
||||
} else {
|
||||
if(cnt_min >= 59) {
|
||||
canvas_draw_str_aligned(canvas, 105, 51, AlignLeft, AlignCenter, "Old");
|
||||
} else {
|
||||
snprintf(buffer, sizeof(buffer), "%dm", cnt_min);
|
||||
canvas_draw_str_aligned(canvas, 114, 51, AlignCenter, AlignCenter, buffer);
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
snprintf(buffer, sizeof(buffer), "%d", ts_diff);
|
||||
canvas_draw_str_aligned(canvas, 112, 51, AlignCenter, AlignCenter, buffer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool ws_view_receiver_info_input(InputEvent* event, void* context) {
|
||||
furi_assert(context);
|
||||
//WSReceiverInfo* ws_receiver_info = context;
|
||||
|
||||
if(event->key == InputKeyBack) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void ws_view_receiver_info_enter(void* context) {
|
||||
furi_assert(context);
|
||||
WSReceiverInfo* ws_receiver_info = context;
|
||||
|
||||
furi_timer_start(ws_receiver_info->timer, 1000);
|
||||
}
|
||||
|
||||
static void ws_view_receiver_info_exit(void* context) {
|
||||
furi_assert(context);
|
||||
WSReceiverInfo* ws_receiver_info = context;
|
||||
|
||||
furi_timer_stop(ws_receiver_info->timer);
|
||||
|
||||
with_view_model(
|
||||
ws_receiver_info->view,
|
||||
WSReceiverInfoModel * model,
|
||||
{ furi_string_reset(model->protocol_name); },
|
||||
false);
|
||||
}
|
||||
|
||||
static void ws_view_receiver_info_timer(void* context) {
|
||||
WSReceiverInfo* ws_receiver_info = context;
|
||||
// Force redraw
|
||||
with_view_model(
|
||||
ws_receiver_info->view,
|
||||
WSReceiverInfoModel * model,
|
||||
{
|
||||
FuriHalRtcDateTime curr_dt;
|
||||
furi_hal_rtc_get_datetime(&curr_dt);
|
||||
model->curr_ts = furi_hal_rtc_datetime_to_timestamp(&curr_dt);
|
||||
},
|
||||
true);
|
||||
}
|
||||
|
||||
WSReceiverInfo* ws_view_receiver_info_alloc() {
|
||||
WSReceiverInfo* ws_receiver_info = malloc(sizeof(WSReceiverInfo));
|
||||
|
||||
// View allocation and configuration
|
||||
ws_receiver_info->view = view_alloc();
|
||||
|
||||
view_allocate_model(ws_receiver_info->view, ViewModelTypeLocking, sizeof(WSReceiverInfoModel));
|
||||
view_set_context(ws_receiver_info->view, ws_receiver_info);
|
||||
view_set_draw_callback(ws_receiver_info->view, (ViewDrawCallback)ws_view_receiver_info_draw);
|
||||
view_set_input_callback(ws_receiver_info->view, ws_view_receiver_info_input);
|
||||
view_set_enter_callback(ws_receiver_info->view, ws_view_receiver_info_enter);
|
||||
view_set_exit_callback(ws_receiver_info->view, ws_view_receiver_info_exit);
|
||||
|
||||
with_view_model(
|
||||
ws_receiver_info->view,
|
||||
WSReceiverInfoModel * model,
|
||||
{
|
||||
model->generic = malloc(sizeof(WSBlockGeneric));
|
||||
model->protocol_name = furi_string_alloc();
|
||||
},
|
||||
true);
|
||||
|
||||
ws_receiver_info->timer =
|
||||
furi_timer_alloc(ws_view_receiver_info_timer, FuriTimerTypePeriodic, ws_receiver_info);
|
||||
|
||||
return ws_receiver_info;
|
||||
}
|
||||
|
||||
void ws_view_receiver_info_free(WSReceiverInfo* ws_receiver_info) {
|
||||
furi_assert(ws_receiver_info);
|
||||
|
||||
furi_timer_free(ws_receiver_info->timer);
|
||||
|
||||
with_view_model(
|
||||
ws_receiver_info->view,
|
||||
WSReceiverInfoModel * model,
|
||||
{
|
||||
furi_string_free(model->protocol_name);
|
||||
free(model->generic);
|
||||
},
|
||||
false);
|
||||
|
||||
view_free(ws_receiver_info->view);
|
||||
free(ws_receiver_info);
|
||||
}
|
||||
|
||||
View* ws_view_receiver_info_get_view(WSReceiverInfo* ws_receiver_info) {
|
||||
furi_assert(ws_receiver_info);
|
||||
return ws_receiver_info->view;
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <gui/view.h>
|
||||
#include "../helpers/weather_station_types.h"
|
||||
#include "../helpers/weather_station_event.h"
|
||||
#include <lib/flipper_format/flipper_format.h>
|
||||
|
||||
typedef struct WSReceiverInfo WSReceiverInfo;
|
||||
|
||||
void ws_view_receiver_info_update(WSReceiverInfo* ws_receiver_info, FlipperFormat* fff);
|
||||
|
||||
WSReceiverInfo* ws_view_receiver_info_alloc();
|
||||
|
||||
void ws_view_receiver_info_free(WSReceiverInfo* ws_receiver_info);
|
||||
|
||||
View* ws_view_receiver_info_get_view(WSReceiverInfo* ws_receiver_info);
|
||||
|
Before Width: | Height: | Size: 175 B |
@@ -1,188 +0,0 @@
|
||||
#include "weather_station_app_i.h"
|
||||
|
||||
#include <furi.h>
|
||||
#include <furi_hal.h>
|
||||
#include "protocols/protocol_items.h"
|
||||
|
||||
static bool weather_station_app_custom_event_callback(void* context, uint32_t event) {
|
||||
furi_assert(context);
|
||||
WeatherStationApp* app = context;
|
||||
return scene_manager_handle_custom_event(app->scene_manager, event);
|
||||
}
|
||||
|
||||
static bool weather_station_app_back_event_callback(void* context) {
|
||||
furi_assert(context);
|
||||
WeatherStationApp* app = context;
|
||||
return scene_manager_handle_back_event(app->scene_manager);
|
||||
}
|
||||
|
||||
static void weather_station_app_tick_event_callback(void* context) {
|
||||
furi_assert(context);
|
||||
WeatherStationApp* app = context;
|
||||
scene_manager_handle_tick_event(app->scene_manager);
|
||||
}
|
||||
|
||||
WeatherStationApp* weather_station_app_alloc() {
|
||||
WeatherStationApp* app = malloc(sizeof(WeatherStationApp));
|
||||
|
||||
// GUI
|
||||
app->gui = furi_record_open(RECORD_GUI);
|
||||
|
||||
// View Dispatcher
|
||||
app->view_dispatcher = view_dispatcher_alloc();
|
||||
app->scene_manager = scene_manager_alloc(&weather_station_scene_handlers, app);
|
||||
view_dispatcher_enable_queue(app->view_dispatcher);
|
||||
|
||||
view_dispatcher_set_event_callback_context(app->view_dispatcher, app);
|
||||
view_dispatcher_set_custom_event_callback(
|
||||
app->view_dispatcher, weather_station_app_custom_event_callback);
|
||||
view_dispatcher_set_navigation_event_callback(
|
||||
app->view_dispatcher, weather_station_app_back_event_callback);
|
||||
view_dispatcher_set_tick_event_callback(
|
||||
app->view_dispatcher, weather_station_app_tick_event_callback, 100);
|
||||
|
||||
view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen);
|
||||
|
||||
// Open Notification record
|
||||
app->notifications = furi_record_open(RECORD_NOTIFICATION);
|
||||
|
||||
// Variable Item List
|
||||
app->variable_item_list = variable_item_list_alloc();
|
||||
view_dispatcher_add_view(
|
||||
app->view_dispatcher,
|
||||
WeatherStationViewVariableItemList,
|
||||
variable_item_list_get_view(app->variable_item_list));
|
||||
|
||||
// SubMenu
|
||||
app->submenu = submenu_alloc();
|
||||
view_dispatcher_add_view(
|
||||
app->view_dispatcher, WeatherStationViewSubmenu, submenu_get_view(app->submenu));
|
||||
|
||||
// Widget
|
||||
app->widget = widget_alloc();
|
||||
view_dispatcher_add_view(
|
||||
app->view_dispatcher, WeatherStationViewWidget, widget_get_view(app->widget));
|
||||
|
||||
// Receiver
|
||||
app->ws_receiver = ws_view_receiver_alloc();
|
||||
view_dispatcher_add_view(
|
||||
app->view_dispatcher,
|
||||
WeatherStationViewReceiver,
|
||||
ws_view_receiver_get_view(app->ws_receiver));
|
||||
|
||||
// Receiver Info
|
||||
app->ws_receiver_info = ws_view_receiver_info_alloc();
|
||||
view_dispatcher_add_view(
|
||||
app->view_dispatcher,
|
||||
WeatherStationViewReceiverInfo,
|
||||
ws_view_receiver_info_get_view(app->ws_receiver_info));
|
||||
|
||||
//init setting
|
||||
app->setting = subghz_setting_alloc();
|
||||
|
||||
//ToDo FIX file name setting
|
||||
subghz_setting_load(app->setting, EXT_PATH("subghz/assets/setting_user"));
|
||||
|
||||
//init Worker & Protocol & History
|
||||
app->lock = WSLockOff;
|
||||
app->txrx = malloc(sizeof(WeatherStationTxRx));
|
||||
app->txrx->preset = malloc(sizeof(SubGhzRadioPreset));
|
||||
app->txrx->preset->name = furi_string_alloc();
|
||||
ws_preset_init(app, "AM650", subghz_setting_get_default_frequency(app->setting), NULL, 0);
|
||||
|
||||
app->txrx->hopper_state = WSHopperStateOFF;
|
||||
app->txrx->history = ws_history_alloc();
|
||||
app->txrx->worker = subghz_worker_alloc();
|
||||
app->txrx->environment = subghz_environment_alloc();
|
||||
subghz_environment_set_protocol_registry(
|
||||
app->txrx->environment, (void*)&weather_station_protocol_registry);
|
||||
app->txrx->receiver = subghz_receiver_alloc_init(app->txrx->environment);
|
||||
|
||||
subghz_devices_init();
|
||||
|
||||
app->txrx->radio_device =
|
||||
radio_device_loader_set(app->txrx->radio_device, SubGhzRadioDeviceTypeExternalCC1101);
|
||||
|
||||
subghz_devices_reset(app->txrx->radio_device);
|
||||
subghz_devices_idle(app->txrx->radio_device);
|
||||
|
||||
subghz_receiver_set_filter(app->txrx->receiver, SubGhzProtocolFlag_Decodable);
|
||||
subghz_worker_set_overrun_callback(
|
||||
app->txrx->worker, (SubGhzWorkerOverrunCallback)subghz_receiver_reset);
|
||||
subghz_worker_set_pair_callback(
|
||||
app->txrx->worker, (SubGhzWorkerPairCallback)subghz_receiver_decode);
|
||||
subghz_worker_set_context(app->txrx->worker, app->txrx->receiver);
|
||||
|
||||
furi_hal_power_suppress_charge_enter();
|
||||
|
||||
scene_manager_next_scene(app->scene_manager, WeatherStationSceneStart);
|
||||
|
||||
return app;
|
||||
}
|
||||
|
||||
void weather_station_app_free(WeatherStationApp* app) {
|
||||
furi_assert(app);
|
||||
|
||||
subghz_devices_sleep(app->txrx->radio_device);
|
||||
radio_device_loader_end(app->txrx->radio_device);
|
||||
|
||||
subghz_devices_deinit();
|
||||
|
||||
// Submenu
|
||||
view_dispatcher_remove_view(app->view_dispatcher, WeatherStationViewSubmenu);
|
||||
submenu_free(app->submenu);
|
||||
|
||||
// Variable Item List
|
||||
view_dispatcher_remove_view(app->view_dispatcher, WeatherStationViewVariableItemList);
|
||||
variable_item_list_free(app->variable_item_list);
|
||||
|
||||
// Widget
|
||||
view_dispatcher_remove_view(app->view_dispatcher, WeatherStationViewWidget);
|
||||
widget_free(app->widget);
|
||||
|
||||
// Receiver
|
||||
view_dispatcher_remove_view(app->view_dispatcher, WeatherStationViewReceiver);
|
||||
ws_view_receiver_free(app->ws_receiver);
|
||||
|
||||
// Receiver Info
|
||||
view_dispatcher_remove_view(app->view_dispatcher, WeatherStationViewReceiverInfo);
|
||||
ws_view_receiver_info_free(app->ws_receiver_info);
|
||||
|
||||
//setting
|
||||
subghz_setting_free(app->setting);
|
||||
|
||||
//Worker & Protocol & History
|
||||
subghz_receiver_free(app->txrx->receiver);
|
||||
subghz_environment_free(app->txrx->environment);
|
||||
ws_history_free(app->txrx->history);
|
||||
subghz_worker_free(app->txrx->worker);
|
||||
furi_string_free(app->txrx->preset->name);
|
||||
free(app->txrx->preset);
|
||||
free(app->txrx);
|
||||
|
||||
// View dispatcher
|
||||
view_dispatcher_free(app->view_dispatcher);
|
||||
scene_manager_free(app->scene_manager);
|
||||
|
||||
// Notifications
|
||||
furi_record_close(RECORD_NOTIFICATION);
|
||||
app->notifications = NULL;
|
||||
|
||||
// Close records
|
||||
furi_record_close(RECORD_GUI);
|
||||
|
||||
furi_hal_power_suppress_charge_exit();
|
||||
|
||||
free(app);
|
||||
}
|
||||
|
||||
int32_t weather_station_app(void* p) {
|
||||
UNUSED(p);
|
||||
WeatherStationApp* weather_station_app = weather_station_app_alloc();
|
||||
|
||||
view_dispatcher_run(weather_station_app->view_dispatcher);
|
||||
|
||||
weather_station_app_free(weather_station_app);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1,155 +0,0 @@
|
||||
#include "weather_station_app_i.h"
|
||||
|
||||
#define TAG "WeatherStation"
|
||||
#include <flipper_format/flipper_format_i.h>
|
||||
|
||||
void ws_preset_init(
|
||||
void* context,
|
||||
const char* preset_name,
|
||||
uint32_t frequency,
|
||||
uint8_t* preset_data,
|
||||
size_t preset_data_size) {
|
||||
furi_assert(context);
|
||||
WeatherStationApp* app = context;
|
||||
furi_string_set(app->txrx->preset->name, preset_name);
|
||||
app->txrx->preset->frequency = frequency;
|
||||
app->txrx->preset->data = preset_data;
|
||||
app->txrx->preset->data_size = preset_data_size;
|
||||
}
|
||||
|
||||
bool ws_set_preset(WeatherStationApp* app, const char* preset) {
|
||||
if(!strcmp(preset, "FuriHalSubGhzPresetOok270Async")) {
|
||||
furi_string_set(app->txrx->preset->name, "AM270");
|
||||
} else if(!strcmp(preset, "FuriHalSubGhzPresetOok650Async")) {
|
||||
furi_string_set(app->txrx->preset->name, "AM650");
|
||||
} else if(!strcmp(preset, "FuriHalSubGhzPreset2FSKDev238Async")) {
|
||||
furi_string_set(app->txrx->preset->name, "FM238");
|
||||
} else if(!strcmp(preset, "FuriHalSubGhzPreset2FSKDev476Async")) {
|
||||
furi_string_set(app->txrx->preset->name, "FM476");
|
||||
} else if(!strcmp(preset, "FuriHalSubGhzPresetCustom")) {
|
||||
furi_string_set(app->txrx->preset->name, "CUSTOM");
|
||||
} else {
|
||||
FURI_LOG_E(TAG, "Unknown preset");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void ws_get_frequency_modulation(
|
||||
WeatherStationApp* app,
|
||||
FuriString* frequency,
|
||||
FuriString* modulation) {
|
||||
furi_assert(app);
|
||||
if(frequency != NULL) {
|
||||
furi_string_printf(
|
||||
frequency,
|
||||
"%03ld.%02ld",
|
||||
app->txrx->preset->frequency / 1000000 % 1000,
|
||||
app->txrx->preset->frequency / 10000 % 100);
|
||||
}
|
||||
if(modulation != NULL) {
|
||||
furi_string_printf(modulation, "%.2s", furi_string_get_cstr(app->txrx->preset->name));
|
||||
}
|
||||
}
|
||||
|
||||
void ws_begin(WeatherStationApp* app, uint8_t* preset_data) {
|
||||
furi_assert(app);
|
||||
subghz_devices_reset(app->txrx->radio_device);
|
||||
subghz_devices_idle(app->txrx->radio_device);
|
||||
subghz_devices_load_preset(app->txrx->radio_device, FuriHalSubGhzPresetCustom, preset_data);
|
||||
app->txrx->txrx_state = WSTxRxStateIDLE;
|
||||
}
|
||||
|
||||
uint32_t ws_rx(WeatherStationApp* app, uint32_t frequency) {
|
||||
furi_assert(app);
|
||||
if(!subghz_devices_is_frequency_valid(app->txrx->radio_device, frequency)) {
|
||||
furi_crash("WeatherStation: Incorrect RX frequency.");
|
||||
}
|
||||
furi_assert(
|
||||
app->txrx->txrx_state != WSTxRxStateRx && app->txrx->txrx_state != WSTxRxStateSleep);
|
||||
|
||||
subghz_devices_idle(app->txrx->radio_device);
|
||||
uint32_t value = subghz_devices_set_frequency(app->txrx->radio_device, frequency);
|
||||
subghz_devices_flush_rx(app->txrx->radio_device);
|
||||
subghz_devices_set_rx(app->txrx->radio_device);
|
||||
|
||||
subghz_devices_start_async_rx(
|
||||
app->txrx->radio_device, subghz_worker_rx_callback, app->txrx->worker);
|
||||
|
||||
subghz_worker_start(app->txrx->worker);
|
||||
app->txrx->txrx_state = WSTxRxStateRx;
|
||||
return value;
|
||||
}
|
||||
|
||||
void ws_idle(WeatherStationApp* app) {
|
||||
furi_assert(app);
|
||||
furi_assert(app->txrx->txrx_state != WSTxRxStateSleep);
|
||||
subghz_devices_idle(app->txrx->radio_device);
|
||||
app->txrx->txrx_state = WSTxRxStateIDLE;
|
||||
}
|
||||
|
||||
void ws_rx_end(WeatherStationApp* app) {
|
||||
furi_assert(app);
|
||||
furi_assert(app->txrx->txrx_state == WSTxRxStateRx);
|
||||
if(subghz_worker_is_running(app->txrx->worker)) {
|
||||
subghz_worker_stop(app->txrx->worker);
|
||||
subghz_devices_stop_async_rx(app->txrx->radio_device);
|
||||
}
|
||||
subghz_devices_idle(app->txrx->radio_device);
|
||||
app->txrx->txrx_state = WSTxRxStateIDLE;
|
||||
}
|
||||
|
||||
void ws_sleep(WeatherStationApp* app) {
|
||||
furi_assert(app);
|
||||
subghz_devices_sleep(app->txrx->radio_device);
|
||||
app->txrx->txrx_state = WSTxRxStateSleep;
|
||||
}
|
||||
|
||||
void ws_hopper_update(WeatherStationApp* app) {
|
||||
furi_assert(app);
|
||||
|
||||
switch(app->txrx->hopper_state) {
|
||||
case WSHopperStateOFF:
|
||||
case WSHopperStatePause:
|
||||
return;
|
||||
case WSHopperStateRSSITimeOut:
|
||||
if(app->txrx->hopper_timeout != 0) {
|
||||
app->txrx->hopper_timeout--;
|
||||
return;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
float rssi = -127.0f;
|
||||
if(app->txrx->hopper_state != WSHopperStateRSSITimeOut) {
|
||||
// See RSSI Calculation timings in CC1101 17.3 RSSI
|
||||
rssi = subghz_devices_get_rssi(app->txrx->radio_device);
|
||||
|
||||
// Stay if RSSI is high enough
|
||||
if(rssi > -90.0f) {
|
||||
app->txrx->hopper_timeout = 10;
|
||||
app->txrx->hopper_state = WSHopperStateRSSITimeOut;
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
app->txrx->hopper_state = WSHopperStateRunnig;
|
||||
}
|
||||
// Select next frequency
|
||||
if(app->txrx->hopper_idx_frequency <
|
||||
subghz_setting_get_hopper_frequency_count(app->setting) - 1) {
|
||||
app->txrx->hopper_idx_frequency++;
|
||||
} else {
|
||||
app->txrx->hopper_idx_frequency = 0;
|
||||
}
|
||||
|
||||
if(app->txrx->txrx_state == WSTxRxStateRx) {
|
||||
ws_rx_end(app);
|
||||
};
|
||||
if(app->txrx->txrx_state == WSTxRxStateIDLE) {
|
||||
subghz_receiver_reset(app->txrx->receiver);
|
||||
app->txrx->preset->frequency =
|
||||
subghz_setting_get_hopper_frequency(app->setting, app->txrx->hopper_idx_frequency);
|
||||
ws_rx(app, app->txrx->preset->frequency);
|
||||
}
|
||||
}
|
||||
@@ -1,76 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "helpers/weather_station_types.h"
|
||||
|
||||
#include "scenes/weather_station_scene.h"
|
||||
#include <gui/gui.h>
|
||||
#include <gui/view_dispatcher.h>
|
||||
#include <gui/scene_manager.h>
|
||||
#include <gui/modules/submenu.h>
|
||||
#include <gui/modules/variable_item_list.h>
|
||||
#include <gui/modules/widget.h>
|
||||
#include <notification/notification_messages.h>
|
||||
#include "views/weather_station_receiver.h"
|
||||
#include "views/weather_station_receiver_info.h"
|
||||
#include "weather_station_history.h"
|
||||
|
||||
#include <lib/subghz/subghz_setting.h>
|
||||
#include <lib/subghz/subghz_worker.h>
|
||||
#include <lib/subghz/receiver.h>
|
||||
#include <lib/subghz/transmitter.h>
|
||||
#include <lib/subghz/registry.h>
|
||||
|
||||
#include "helpers/radio_device_loader.h"
|
||||
|
||||
typedef struct WeatherStationApp WeatherStationApp;
|
||||
|
||||
struct WeatherStationTxRx {
|
||||
SubGhzWorker* worker;
|
||||
|
||||
const SubGhzDevice* radio_device;
|
||||
SubGhzEnvironment* environment;
|
||||
SubGhzReceiver* receiver;
|
||||
SubGhzRadioPreset* preset;
|
||||
WSHistory* history;
|
||||
uint16_t idx_menu_chosen;
|
||||
WSTxRxState txrx_state;
|
||||
WSHopperState hopper_state;
|
||||
uint8_t hopper_timeout;
|
||||
uint8_t hopper_idx_frequency;
|
||||
WSRxKeyState rx_key_state;
|
||||
};
|
||||
|
||||
typedef struct WeatherStationTxRx WeatherStationTxRx;
|
||||
|
||||
struct WeatherStationApp {
|
||||
Gui* gui;
|
||||
ViewDispatcher* view_dispatcher;
|
||||
WeatherStationTxRx* txrx;
|
||||
SceneManager* scene_manager;
|
||||
NotificationApp* notifications;
|
||||
VariableItemList* variable_item_list;
|
||||
Submenu* submenu;
|
||||
Widget* widget;
|
||||
WSReceiver* ws_receiver;
|
||||
WSReceiverInfo* ws_receiver_info;
|
||||
WSLock lock;
|
||||
SubGhzSetting* setting;
|
||||
};
|
||||
|
||||
void ws_preset_init(
|
||||
void* context,
|
||||
const char* preset_name,
|
||||
uint32_t frequency,
|
||||
uint8_t* preset_data,
|
||||
size_t preset_data_size);
|
||||
bool ws_set_preset(WeatherStationApp* app, const char* preset);
|
||||
void ws_get_frequency_modulation(
|
||||
WeatherStationApp* app,
|
||||
FuriString* frequency,
|
||||
FuriString* modulation);
|
||||
void ws_begin(WeatherStationApp* app, uint8_t* preset_data);
|
||||
uint32_t ws_rx(WeatherStationApp* app, uint32_t frequency);
|
||||
void ws_idle(WeatherStationApp* app);
|
||||
void ws_rx_end(WeatherStationApp* app);
|
||||
void ws_sleep(WeatherStationApp* app);
|
||||
void ws_hopper_update(WeatherStationApp* app);
|
||||
@@ -1,245 +0,0 @@
|
||||
#include "weather_station_history.h"
|
||||
#include <flipper_format/flipper_format_i.h>
|
||||
#include <lib/toolbox/stream/stream.h>
|
||||
#include <lib/subghz/receiver.h>
|
||||
#include "protocols/ws_generic.h"
|
||||
|
||||
#include <furi.h>
|
||||
|
||||
#define WS_HISTORY_MAX 50
|
||||
#define TAG "WSHistory"
|
||||
|
||||
typedef struct {
|
||||
FuriString* item_str;
|
||||
FlipperFormat* flipper_string;
|
||||
uint8_t type;
|
||||
uint32_t id;
|
||||
SubGhzRadioPreset* preset;
|
||||
} WSHistoryItem;
|
||||
|
||||
ARRAY_DEF(WSHistoryItemArray, WSHistoryItem, M_POD_OPLIST)
|
||||
|
||||
#define M_OPL_WSHistoryItemArray_t() ARRAY_OPLIST(WSHistoryItemArray, M_POD_OPLIST)
|
||||
|
||||
typedef struct {
|
||||
WSHistoryItemArray_t data;
|
||||
} WSHistoryStruct;
|
||||
|
||||
struct WSHistory {
|
||||
uint32_t last_update_timestamp;
|
||||
uint16_t last_index_write;
|
||||
uint8_t code_last_hash_data;
|
||||
FuriString* tmp_string;
|
||||
WSHistoryStruct* history;
|
||||
};
|
||||
|
||||
WSHistory* ws_history_alloc(void) {
|
||||
WSHistory* instance = malloc(sizeof(WSHistory));
|
||||
instance->tmp_string = furi_string_alloc();
|
||||
instance->history = malloc(sizeof(WSHistoryStruct));
|
||||
WSHistoryItemArray_init(instance->history->data);
|
||||
return instance;
|
||||
}
|
||||
|
||||
void ws_history_free(WSHistory* instance) {
|
||||
furi_assert(instance);
|
||||
furi_string_free(instance->tmp_string);
|
||||
for
|
||||
M_EACH(item, instance->history->data, WSHistoryItemArray_t) {
|
||||
furi_string_free(item->item_str);
|
||||
furi_string_free(item->preset->name);
|
||||
free(item->preset);
|
||||
flipper_format_free(item->flipper_string);
|
||||
item->type = 0;
|
||||
}
|
||||
WSHistoryItemArray_clear(instance->history->data);
|
||||
free(instance->history);
|
||||
free(instance);
|
||||
}
|
||||
|
||||
uint32_t ws_history_get_frequency(WSHistory* instance, uint16_t idx) {
|
||||
furi_assert(instance);
|
||||
WSHistoryItem* item = WSHistoryItemArray_get(instance->history->data, idx);
|
||||
return item->preset->frequency;
|
||||
}
|
||||
|
||||
SubGhzRadioPreset* ws_history_get_radio_preset(WSHistory* instance, uint16_t idx) {
|
||||
furi_assert(instance);
|
||||
WSHistoryItem* item = WSHistoryItemArray_get(instance->history->data, idx);
|
||||
return item->preset;
|
||||
}
|
||||
|
||||
const char* ws_history_get_preset(WSHistory* instance, uint16_t idx) {
|
||||
furi_assert(instance);
|
||||
WSHistoryItem* item = WSHistoryItemArray_get(instance->history->data, idx);
|
||||
return furi_string_get_cstr(item->preset->name);
|
||||
}
|
||||
|
||||
void ws_history_reset(WSHistory* instance) {
|
||||
furi_assert(instance);
|
||||
furi_string_reset(instance->tmp_string);
|
||||
for
|
||||
M_EACH(item, instance->history->data, WSHistoryItemArray_t) {
|
||||
furi_string_free(item->item_str);
|
||||
furi_string_free(item->preset->name);
|
||||
free(item->preset);
|
||||
flipper_format_free(item->flipper_string);
|
||||
item->type = 0;
|
||||
}
|
||||
WSHistoryItemArray_reset(instance->history->data);
|
||||
instance->last_index_write = 0;
|
||||
instance->code_last_hash_data = 0;
|
||||
}
|
||||
|
||||
uint16_t ws_history_get_item(WSHistory* instance) {
|
||||
furi_assert(instance);
|
||||
return instance->last_index_write;
|
||||
}
|
||||
|
||||
uint8_t ws_history_get_type_protocol(WSHistory* instance, uint16_t idx) {
|
||||
furi_assert(instance);
|
||||
WSHistoryItem* item = WSHistoryItemArray_get(instance->history->data, idx);
|
||||
return item->type;
|
||||
}
|
||||
|
||||
const char* ws_history_get_protocol_name(WSHistory* instance, uint16_t idx) {
|
||||
furi_assert(instance);
|
||||
WSHistoryItem* item = WSHistoryItemArray_get(instance->history->data, idx);
|
||||
flipper_format_rewind(item->flipper_string);
|
||||
if(!flipper_format_read_string(item->flipper_string, "Protocol", instance->tmp_string)) {
|
||||
FURI_LOG_E(TAG, "Missing Protocol");
|
||||
furi_string_reset(instance->tmp_string);
|
||||
}
|
||||
return furi_string_get_cstr(instance->tmp_string);
|
||||
}
|
||||
|
||||
FlipperFormat* ws_history_get_raw_data(WSHistory* instance, uint16_t idx) {
|
||||
furi_assert(instance);
|
||||
WSHistoryItem* item = WSHistoryItemArray_get(instance->history->data, idx);
|
||||
if(item->flipper_string) {
|
||||
return item->flipper_string;
|
||||
} else {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
bool ws_history_get_text_space_left(WSHistory* instance, FuriString* output) {
|
||||
furi_assert(instance);
|
||||
if(instance->last_index_write == WS_HISTORY_MAX) {
|
||||
if(output != NULL) furi_string_printf(output, "Memory is FULL");
|
||||
return true;
|
||||
}
|
||||
if(output != NULL)
|
||||
furi_string_printf(output, "%02u/%02u", instance->last_index_write, WS_HISTORY_MAX);
|
||||
return false;
|
||||
}
|
||||
|
||||
void ws_history_get_text_item_menu(WSHistory* instance, FuriString* output, uint16_t idx) {
|
||||
WSHistoryItem* item = WSHistoryItemArray_get(instance->history->data, idx);
|
||||
furi_string_set(output, item->item_str);
|
||||
}
|
||||
|
||||
WSHistoryStateAddKey
|
||||
ws_history_add_to_history(WSHistory* instance, void* context, SubGhzRadioPreset* preset) {
|
||||
furi_assert(instance);
|
||||
furi_assert(context);
|
||||
|
||||
if(instance->last_index_write >= WS_HISTORY_MAX) return WSHistoryStateAddKeyOverflow;
|
||||
|
||||
SubGhzProtocolDecoderBase* decoder_base = context;
|
||||
if((instance->code_last_hash_data ==
|
||||
subghz_protocol_decoder_base_get_hash_data(decoder_base)) &&
|
||||
((furi_get_tick() - instance->last_update_timestamp) < 500)) {
|
||||
instance->last_update_timestamp = furi_get_tick();
|
||||
return WSHistoryStateAddKeyTimeOut;
|
||||
}
|
||||
|
||||
instance->code_last_hash_data = subghz_protocol_decoder_base_get_hash_data(decoder_base);
|
||||
instance->last_update_timestamp = furi_get_tick();
|
||||
|
||||
FlipperFormat* fff = flipper_format_string_alloc();
|
||||
uint32_t id = 0;
|
||||
subghz_protocol_decoder_base_serialize(decoder_base, fff, preset);
|
||||
|
||||
do {
|
||||
if(!flipper_format_rewind(fff)) {
|
||||
FURI_LOG_E(TAG, "Rewind error");
|
||||
break;
|
||||
}
|
||||
if(!flipper_format_read_uint32(fff, "Id", (uint32_t*)&id, 1)) {
|
||||
FURI_LOG_E(TAG, "Missing Id");
|
||||
break;
|
||||
}
|
||||
} while(false);
|
||||
flipper_format_free(fff);
|
||||
|
||||
//Update record if found
|
||||
bool sensor_found = false;
|
||||
for(size_t i = 0; i < WSHistoryItemArray_size(instance->history->data); i++) {
|
||||
WSHistoryItem* item = WSHistoryItemArray_get(instance->history->data, i);
|
||||
if(item->id == id) {
|
||||
sensor_found = true;
|
||||
Stream* flipper_string_stream = flipper_format_get_raw_stream(item->flipper_string);
|
||||
stream_clean(flipper_string_stream);
|
||||
subghz_protocol_decoder_base_serialize(decoder_base, item->flipper_string, preset);
|
||||
return WSHistoryStateAddKeyUpdateData;
|
||||
}
|
||||
}
|
||||
|
||||
// or add new record
|
||||
if(!sensor_found) { //-V547
|
||||
WSHistoryItem* item = WSHistoryItemArray_push_raw(instance->history->data);
|
||||
item->preset = malloc(sizeof(SubGhzRadioPreset));
|
||||
item->type = decoder_base->protocol->type;
|
||||
item->preset->frequency = preset->frequency;
|
||||
item->preset->name = furi_string_alloc();
|
||||
furi_string_set(item->preset->name, preset->name);
|
||||
item->preset->data = preset->data;
|
||||
item->preset->data_size = preset->data_size;
|
||||
item->id = id;
|
||||
|
||||
item->item_str = furi_string_alloc();
|
||||
item->flipper_string = flipper_format_string_alloc();
|
||||
subghz_protocol_decoder_base_serialize(decoder_base, item->flipper_string, preset);
|
||||
|
||||
do {
|
||||
if(!flipper_format_rewind(item->flipper_string)) {
|
||||
FURI_LOG_E(TAG, "Rewind error");
|
||||
break;
|
||||
}
|
||||
if(!flipper_format_read_string(
|
||||
item->flipper_string, "Protocol", instance->tmp_string)) {
|
||||
FURI_LOG_E(TAG, "Missing Protocol");
|
||||
break;
|
||||
}
|
||||
|
||||
if(!flipper_format_rewind(item->flipper_string)) {
|
||||
FURI_LOG_E(TAG, "Rewind error");
|
||||
break;
|
||||
}
|
||||
uint8_t key_data[sizeof(uint64_t)] = {0};
|
||||
if(!flipper_format_read_hex(item->flipper_string, "Data", key_data, sizeof(uint64_t))) {
|
||||
FURI_LOG_E(TAG, "Missing Data");
|
||||
break;
|
||||
}
|
||||
uint64_t data = 0;
|
||||
for(uint8_t i = 0; i < sizeof(uint64_t); i++) {
|
||||
data = (data << 8) | key_data[i];
|
||||
}
|
||||
uint32_t temp_data = 0;
|
||||
if(!flipper_format_read_uint32(item->flipper_string, "Ch", (uint32_t*)&temp_data, 1)) {
|
||||
FURI_LOG_E(TAG, "Missing Channel");
|
||||
break;
|
||||
}
|
||||
if(temp_data != WS_NO_CHANNEL) {
|
||||
furi_string_cat_printf(instance->tmp_string, " Ch:%X", (uint8_t)temp_data);
|
||||
}
|
||||
|
||||
furi_string_printf(
|
||||
item->item_str, "%s %llX", furi_string_get_cstr(instance->tmp_string), data);
|
||||
|
||||
} while(false);
|
||||
instance->last_index_write++;
|
||||
return WSHistoryStateAddKeyNewDada;
|
||||
}
|
||||
return WSHistoryStateAddKeyUnknown;
|
||||
}
|
||||
@@ -1,112 +0,0 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <math.h>
|
||||
#include <furi.h>
|
||||
#include <furi_hal.h>
|
||||
#include <lib/flipper_format/flipper_format.h>
|
||||
#include <lib/subghz/types.h>
|
||||
|
||||
typedef struct WSHistory WSHistory;
|
||||
|
||||
/** History state add key */
|
||||
typedef enum {
|
||||
WSHistoryStateAddKeyUnknown,
|
||||
WSHistoryStateAddKeyTimeOut,
|
||||
WSHistoryStateAddKeyNewDada,
|
||||
WSHistoryStateAddKeyUpdateData,
|
||||
WSHistoryStateAddKeyOverflow,
|
||||
} WSHistoryStateAddKey;
|
||||
|
||||
/** Allocate WSHistory
|
||||
*
|
||||
* @return WSHistory*
|
||||
*/
|
||||
WSHistory* ws_history_alloc(void);
|
||||
|
||||
/** Free WSHistory
|
||||
*
|
||||
* @param instance - WSHistory instance
|
||||
*/
|
||||
void ws_history_free(WSHistory* instance);
|
||||
|
||||
/** Clear history
|
||||
*
|
||||
* @param instance - WSHistory instance
|
||||
*/
|
||||
void ws_history_reset(WSHistory* instance);
|
||||
|
||||
/** Get frequency to history[idx]
|
||||
*
|
||||
* @param instance - WSHistory instance
|
||||
* @param idx - record index
|
||||
* @return frequency - frequency Hz
|
||||
*/
|
||||
uint32_t ws_history_get_frequency(WSHistory* instance, uint16_t idx);
|
||||
|
||||
SubGhzRadioPreset* ws_history_get_radio_preset(WSHistory* instance, uint16_t idx);
|
||||
|
||||
/** Get preset to history[idx]
|
||||
*
|
||||
* @param instance - WSHistory instance
|
||||
* @param idx - record index
|
||||
* @return preset - preset name
|
||||
*/
|
||||
const char* ws_history_get_preset(WSHistory* instance, uint16_t idx);
|
||||
|
||||
/** Get history index write
|
||||
*
|
||||
* @param instance - WSHistory instance
|
||||
* @return idx - current record index
|
||||
*/
|
||||
uint16_t ws_history_get_item(WSHistory* instance);
|
||||
|
||||
/** Get type protocol to history[idx]
|
||||
*
|
||||
* @param instance - WSHistory instance
|
||||
* @param idx - record index
|
||||
* @return type - type protocol
|
||||
*/
|
||||
uint8_t ws_history_get_type_protocol(WSHistory* instance, uint16_t idx);
|
||||
|
||||
/** Get name protocol to history[idx]
|
||||
*
|
||||
* @param instance - WSHistory instance
|
||||
* @param idx - record index
|
||||
* @return name - const char* name protocol
|
||||
*/
|
||||
const char* ws_history_get_protocol_name(WSHistory* instance, uint16_t idx);
|
||||
|
||||
/** Get string item menu to history[idx]
|
||||
*
|
||||
* @param instance - WSHistory instance
|
||||
* @param output - FuriString* output
|
||||
* @param idx - record index
|
||||
*/
|
||||
void ws_history_get_text_item_menu(WSHistory* instance, FuriString* output, uint16_t idx);
|
||||
|
||||
/** Get string the remaining number of records to history
|
||||
*
|
||||
* @param instance - WSHistory instance
|
||||
* @param output - FuriString* output
|
||||
* @return bool - is FUUL
|
||||
*/
|
||||
bool ws_history_get_text_space_left(WSHistory* instance, FuriString* output);
|
||||
|
||||
/** Add protocol to history
|
||||
*
|
||||
* @param instance - WSHistory instance
|
||||
* @param context - SubGhzProtocolCommon context
|
||||
* @param preset - SubGhzRadioPreset preset
|
||||
* @return WSHistoryStateAddKey;
|
||||
*/
|
||||
WSHistoryStateAddKey
|
||||
ws_history_add_to_history(WSHistory* instance, void* context, SubGhzRadioPreset* preset);
|
||||
|
||||
/** Get SubGhzProtocolCommonLoad to load into the protocol decoder bin data
|
||||
*
|
||||
* @param instance - WSHistory instance
|
||||
* @param idx - record index
|
||||
* @return SubGhzProtocolCommonLoad*
|
||||
*/
|
||||
FlipperFormat* ws_history_get_raw_data(WSHistory* instance, uint16_t idx);
|
||||
@@ -11,6 +11,8 @@ enum SubGhzSettingIndex {
|
||||
SubGhzSettingIndexIgnoreStarline,
|
||||
SubGhzSettingIndexIgnoreCars,
|
||||
SubGhzSettingIndexIgnoreMagellan,
|
||||
SubGhzSettingIndexIgnoreWeather,
|
||||
SubGhzSettingIndexIgnoreTPMS,
|
||||
SubGhzSettingIndexIgnorePrinceton,
|
||||
SubGhzSettingIndexSound,
|
||||
SubGhzSettingIndexResetToDefault,
|
||||
@@ -274,6 +276,13 @@ static void subghz_scene_receiver_config_set_magellan(VariableItem* item) {
|
||||
subghz_scene_receiver_config_set_ignore_filter(item, SubGhzProtocolFlag_Magellan);
|
||||
}
|
||||
|
||||
static void subghz_scene_receiver_config_set_weather(VariableItem* item) {
|
||||
subghz_scene_receiver_config_set_ignore_filter(item, SubGhzProtocolFlag_Weather);
|
||||
}
|
||||
static void subghz_scene_receiver_config_set_tpms(VariableItem* item) {
|
||||
subghz_scene_receiver_config_set_ignore_filter(item, SubGhzProtocolFlag_TPMS);
|
||||
}
|
||||
|
||||
static void subghz_scene_receiver_config_set_princeton(VariableItem* item) {
|
||||
subghz_scene_receiver_config_set_ignore_filter(item, SubGhzProtocolFlag_Princeton);
|
||||
}
|
||||
@@ -433,6 +442,30 @@ void subghz_scene_receiver_config_on_enter(void* context) {
|
||||
variable_item_set_current_value_index(item, value_index);
|
||||
variable_item_set_current_value_text(item, combobox_text[value_index]);
|
||||
|
||||
item = variable_item_list_add(
|
||||
subghz->variable_item_list,
|
||||
"Ignore Weather:",
|
||||
COMBO_BOX_COUNT,
|
||||
subghz_scene_receiver_config_set_weather,
|
||||
subghz);
|
||||
|
||||
value_index = subghz_scene_receiver_config_ignore_filter_get_index(
|
||||
subghz->ignore_filter, SubGhzProtocolFlag_Weather);
|
||||
variable_item_set_current_value_index(item, value_index);
|
||||
variable_item_set_current_value_text(item, combobox_text[value_index]);
|
||||
|
||||
item = variable_item_list_add(
|
||||
subghz->variable_item_list,
|
||||
"Ignore TPMS:",
|
||||
COMBO_BOX_COUNT,
|
||||
subghz_scene_receiver_config_set_tpms,
|
||||
subghz);
|
||||
|
||||
value_index = subghz_scene_receiver_config_ignore_filter_get_index(
|
||||
subghz->ignore_filter, SubGhzProtocolFlag_TPMS);
|
||||
variable_item_set_current_value_index(item, value_index);
|
||||
variable_item_set_current_value_text(item, combobox_text[value_index]);
|
||||
|
||||
item = variable_item_list_add(
|
||||
subghz->variable_item_list,
|
||||
"Ignore Princeton:",
|
||||
|
||||
@@ -84,7 +84,7 @@ const SubGhzProtocol ws_protocol_acurite_592txr = {
|
||||
.name = WS_PROTOCOL_ACURITE_592TXR_NAME,
|
||||
.type = SubGhzProtocolWeatherStation,
|
||||
.flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_315 | SubGhzProtocolFlag_868 |
|
||||
SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable,
|
||||
SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable | SubGhzProtocolFlag_Weather,
|
||||
|
||||
.decoder = &ws_protocol_acurite_592txr_decoder,
|
||||
.encoder = &ws_protocol_acurite_592txr_encoder,
|
||||
@@ -280,7 +280,7 @@ SubGhzProtocolStatus
|
||||
void ws_protocol_decoder_acurite_592txr_get_string(void* context, FuriString* output) {
|
||||
furi_assert(context);
|
||||
WSProtocolDecoderAcurite_592TXR* instance = context;
|
||||
furi_string_printf(
|
||||
furi_string_cat_printf(
|
||||
output,
|
||||
"%s %dbit\r\n"
|
||||
"Key:0x%lX%08lX\r\n"
|
||||
@@ -69,7 +69,7 @@ const SubGhzProtocol ws_protocol_acurite_606tx = {
|
||||
.name = WS_PROTOCOL_ACURITE_606TX_NAME,
|
||||
.type = SubGhzProtocolWeatherStation,
|
||||
.flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_315 | SubGhzProtocolFlag_868 |
|
||||
SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable,
|
||||
SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable | SubGhzProtocolFlag_Weather,
|
||||
|
||||
.decoder = &ws_protocol_acurite_606tx_decoder,
|
||||
.encoder = &ws_protocol_acurite_606tx_encoder,
|
||||
@@ -221,7 +221,7 @@ SubGhzProtocolStatus
|
||||
void ws_protocol_decoder_acurite_606tx_get_string(void* context, FuriString* output) {
|
||||
furi_assert(context);
|
||||
WSProtocolDecoderAcurite_606TX* instance = context;
|
||||
furi_string_printf(
|
||||
furi_string_cat_printf(
|
||||
output,
|
||||
"%s %dbit\r\n"
|
||||
"Key:0x%lX%08lX\r\n"
|
||||
@@ -70,7 +70,7 @@ const SubGhzProtocol ws_protocol_acurite_609txc = {
|
||||
.name = WS_PROTOCOL_ACURITE_609TXC_NAME,
|
||||
.type = SubGhzProtocolWeatherStation,
|
||||
.flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_315 | SubGhzProtocolFlag_868 |
|
||||
SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable,
|
||||
SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable | SubGhzProtocolFlag_Weather,
|
||||
|
||||
.decoder = &ws_protocol_acurite_609txc_decoder,
|
||||
.encoder = &ws_protocol_acurite_609txc_encoder,
|
||||
@@ -221,7 +221,7 @@ SubGhzProtocolStatus
|
||||
void ws_protocol_decoder_acurite_609txc_get_string(void* context, FuriString* output) {
|
||||
furi_assert(context);
|
||||
WSProtocolDecoderAcurite_609TXC* instance = context;
|
||||
furi_string_printf(
|
||||
furi_string_cat_printf(
|
||||
output,
|
||||
"%s %dbit\r\n"
|
||||
"Key:0x%lX%08lX\r\n"
|
||||
@@ -84,7 +84,7 @@ const SubGhzProtocol ws_protocol_ambient_weather = {
|
||||
.name = WS_PROTOCOL_AMBIENT_WEATHER_NAME,
|
||||
.type = SubGhzProtocolWeatherStation,
|
||||
.flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_315 | SubGhzProtocolFlag_868 |
|
||||
SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable,
|
||||
SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable | SubGhzProtocolFlag_Weather,
|
||||
|
||||
.decoder = &ws_protocol_ambient_weather_decoder,
|
||||
.encoder = &ws_protocol_ambient_weather_encoder,
|
||||
@@ -250,7 +250,7 @@ SubGhzProtocolStatus
|
||||
void ws_protocol_decoder_ambient_weather_get_string(void* context, FuriString* output) {
|
||||
furi_assert(context);
|
||||
WSProtocolDecoderAmbient_Weather* instance = context;
|
||||
furi_string_printf(
|
||||
furi_string_cat_printf(
|
||||
output,
|
||||
"%s %dbit\r\n"
|
||||
"Key:0x%lX%08lX\r\n"
|
||||
@@ -81,7 +81,7 @@ const SubGhzProtocol ws_protocol_auriol_ahfl = {
|
||||
.name = WS_PROTOCOL_AURIOL_AHFL_NAME,
|
||||
.type = SubGhzProtocolWeatherStation,
|
||||
.flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_315 | SubGhzProtocolFlag_868 |
|
||||
SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable,
|
||||
SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable | SubGhzProtocolFlag_Weather,
|
||||
|
||||
.decoder = &ws_protocol_auriol_ahfl_decoder,
|
||||
.encoder = &ws_protocol_auriol_ahfl_encoder,
|
||||
@@ -236,7 +236,7 @@ SubGhzProtocolStatus
|
||||
void ws_protocol_decoder_auriol_ahfl_get_string(void* context, FuriString* output) {
|
||||
furi_assert(context);
|
||||
WSProtocolDecoderAuriol_AHFL* instance = context;
|
||||
furi_string_printf(
|
||||
furi_string_cat_printf(
|
||||
output,
|
||||
"%s %dbit\r\n"
|
||||
"Key:0x%lX%08lX\r\n"
|
||||
@@ -80,7 +80,7 @@ const SubGhzProtocol ws_protocol_auriol_th = {
|
||||
.name = WS_PROTOCOL_AURIOL_TH_NAME,
|
||||
.type = SubGhzProtocolWeatherStation,
|
||||
.flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_315 | SubGhzProtocolFlag_868 |
|
||||
SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable,
|
||||
SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable | SubGhzProtocolFlag_Weather,
|
||||
|
||||
.decoder = &ws_protocol_auriol_th_decoder,
|
||||
.encoder = &ws_protocol_auriol_th_encoder,
|
||||
@@ -230,7 +230,7 @@ SubGhzProtocolStatus
|
||||
void ws_protocol_decoder_auriol_th_get_string(void* context, FuriString* output) {
|
||||
furi_assert(context);
|
||||
WSProtocolDecoderAuriol_TH* instance = context;
|
||||
furi_string_printf(
|
||||
furi_string_cat_printf(
|
||||
output,
|
||||
"%s %dbit\r\n"
|
||||
"Key:0x%lX%08lX\r\n"
|
||||
@@ -9,9 +9,9 @@
|
||||
* GT-WT-02 sensor on 433.92MHz.
|
||||
* Example and frame description provided by https://github.com/ludwich66
|
||||
* [01] {37} 34 00 ed 47 60 : 00110100 00000000 11101101 01000111 01100000
|
||||
* code, BatOK,not-man-send, Channel1, +23,7°C, 35%
|
||||
* code, BatOK,not-man-send, Channel1, +23,7<EFBFBD>C, 35%
|
||||
* [01] {37} 34 8f 87 15 90 : 00110100 10001111 10000111 00010101 10010000
|
||||
* code, BatOK,not-man-send, Channel1,-12,1°C, 10%
|
||||
* code, BatOK,not-man-send, Channel1,-12,1<EFBFBD>C, 10%
|
||||
* Humidity:
|
||||
* - the working range is 20-90 %
|
||||
* - if "LL" in display view it sends 10 %
|
||||
@@ -83,7 +83,7 @@ const SubGhzProtocol ws_protocol_gt_wt_02 = {
|
||||
.name = WS_PROTOCOL_GT_WT_02_NAME,
|
||||
.type = SubGhzProtocolWeatherStation,
|
||||
.flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_315 | SubGhzProtocolFlag_868 |
|
||||
SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable,
|
||||
SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable | SubGhzProtocolFlag_Weather,
|
||||
|
||||
.decoder = &ws_protocol_gt_wt_02_decoder,
|
||||
.encoder = &ws_protocol_gt_wt_02_encoder,
|
||||
@@ -237,7 +237,7 @@ SubGhzProtocolStatus
|
||||
void ws_protocol_decoder_gt_wt_02_get_string(void* context, FuriString* output) {
|
||||
furi_assert(context);
|
||||
WSProtocolDecoderGT_WT02* instance = context;
|
||||
furi_string_printf(
|
||||
furi_string_cat_printf(
|
||||
output,
|
||||
"%s %dbit\r\n"
|
||||
"Key:0x%lX%08lX\r\n"
|
||||
@@ -109,7 +109,7 @@ const SubGhzProtocol ws_protocol_gt_wt_03 = {
|
||||
.name = WS_PROTOCOL_GT_WT_03_NAME,
|
||||
.type = SubGhzProtocolWeatherStation,
|
||||
.flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_315 | SubGhzProtocolFlag_868 |
|
||||
SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable,
|
||||
SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable | SubGhzProtocolFlag_Weather,
|
||||
|
||||
.decoder = &ws_protocol_gt_wt_03_decoder,
|
||||
.encoder = &ws_protocol_gt_wt_03_encoder,
|
||||
@@ -312,7 +312,7 @@ SubGhzProtocolStatus
|
||||
void ws_protocol_decoder_gt_wt_03_get_string(void* context, FuriString* output) {
|
||||
furi_assert(context);
|
||||
WSProtocolDecoderGT_WT03* instance = context;
|
||||
furi_string_printf(
|
||||
furi_string_cat_printf(
|
||||
output,
|
||||
"%s %dbit\r\n"
|
||||
"Key:0x%lX%08lX\r\n"
|
||||
@@ -94,7 +94,7 @@ const SubGhzProtocol ws_protocol_infactory = {
|
||||
.name = WS_PROTOCOL_INFACTORY_NAME,
|
||||
.type = SubGhzProtocolWeatherStation,
|
||||
.flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_315 | SubGhzProtocolFlag_868 |
|
||||
SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable,
|
||||
SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable | SubGhzProtocolFlag_Weather,
|
||||
|
||||
.decoder = &ws_protocol_infactory_decoder,
|
||||
.encoder = &ws_protocol_infactory_encoder,
|
||||
@@ -268,7 +268,7 @@ SubGhzProtocolStatus
|
||||
void ws_protocol_decoder_infactory_get_string(void* context, FuriString* output) {
|
||||
furi_assert(context);
|
||||
WSProtocolDecoderInfactory* instance = context;
|
||||
furi_string_printf(
|
||||
furi_string_cat_printf(
|
||||
output,
|
||||
"%s %dbit\r\n"
|
||||
"Key:0x%lX%08lX\r\n"
|
||||
@@ -98,7 +98,7 @@ const SubGhzProtocol ws_protocol_lacrosse_tx = {
|
||||
.name = WS_PROTOCOL_LACROSSE_TX_NAME,
|
||||
.type = SubGhzProtocolWeatherStation,
|
||||
.flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_315 | SubGhzProtocolFlag_868 |
|
||||
SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable,
|
||||
SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable | SubGhzProtocolFlag_Weather,
|
||||
|
||||
.decoder = &ws_protocol_lacrosse_tx_decoder,
|
||||
.encoder = &ws_protocol_lacrosse_tx_encoder,
|
||||
@@ -301,7 +301,7 @@ SubGhzProtocolStatus
|
||||
void ws_protocol_decoder_lacrosse_tx_get_string(void* context, FuriString* output) {
|
||||
furi_assert(context);
|
||||
WSProtocolDecoderLaCrosse_TX* instance = context;
|
||||
furi_string_printf(
|
||||
furi_string_cat_printf(
|
||||
output,
|
||||
"%s %dbit\r\n"
|
||||
"Key:0x%lX%08lX\r\n"
|
||||
@@ -76,7 +76,7 @@ const SubGhzProtocol ws_protocol_lacrosse_tx141thbv2 = {
|
||||
.name = WS_PROTOCOL_LACROSSE_TX141THBV2_NAME,
|
||||
.type = SubGhzProtocolWeatherStation,
|
||||
.flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_315 | SubGhzProtocolFlag_868 |
|
||||
SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable,
|
||||
SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable | SubGhzProtocolFlag_Weather,
|
||||
|
||||
.decoder = &ws_protocol_lacrosse_tx141thbv2_decoder,
|
||||
.encoder = &ws_protocol_lacrosse_tx141thbv2_encoder,
|
||||
@@ -276,7 +276,7 @@ SubGhzProtocolStatus ws_protocol_decoder_lacrosse_tx141thbv2_deserialize(
|
||||
void ws_protocol_decoder_lacrosse_tx141thbv2_get_string(void* context, FuriString* output) {
|
||||
furi_assert(context);
|
||||
WSProtocolDecoderLaCrosse_TX141THBv2* instance = context;
|
||||
furi_string_printf(
|
||||
furi_string_cat_printf(
|
||||
output,
|
||||
"%s %dbit\r\n"
|
||||
"Key:0x%lX%08lX\r\n"
|
||||
@@ -82,7 +82,7 @@ const SubGhzProtocol ws_protocol_nexus_th = {
|
||||
.name = WS_PROTOCOL_NEXUS_TH_NAME,
|
||||
.type = SubGhzProtocolWeatherStation,
|
||||
.flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_315 | SubGhzProtocolFlag_868 |
|
||||
SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable,
|
||||
SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable | SubGhzProtocolFlag_Weather,
|
||||
|
||||
.decoder = &ws_protocol_nexus_th_decoder,
|
||||
.encoder = &ws_protocol_nexus_th_encoder,
|
||||
@@ -236,7 +236,7 @@ SubGhzProtocolStatus
|
||||
void ws_protocol_decoder_nexus_th_get_string(void* context, FuriString* output) {
|
||||
furi_assert(context);
|
||||
WSProtocolDecoderNexus_TH* instance = context;
|
||||
furi_string_printf(
|
||||
furi_string_cat_printf(
|
||||
output,
|
||||
"%s %dbit\r\n"
|
||||
"Key:0x%lX%08lX\r\n"
|
||||
@@ -423,7 +423,8 @@ const SubGhzProtocolDecoder ws_protocol_oregon2_decoder = {
|
||||
const SubGhzProtocol ws_protocol_oregon2 = {
|
||||
.name = WS_PROTOCOL_OREGON2_NAME,
|
||||
.type = SubGhzProtocolWeatherStation,
|
||||
.flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable,
|
||||
.flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable |
|
||||
SubGhzProtocolFlag_Weather,
|
||||
|
||||
.decoder = &ws_protocol_oregon2_decoder,
|
||||
};
|
||||
@@ -359,7 +359,8 @@ const SubGhzProtocolDecoder ws_protocol_oregon3_decoder = {
|
||||
const SubGhzProtocol ws_protocol_oregon3 = {
|
||||
.name = WS_PROTOCOL_OREGON3_NAME,
|
||||
.type = SubGhzProtocolWeatherStation,
|
||||
.flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable,
|
||||
.flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable |
|
||||
SubGhzProtocolFlag_Weather,
|
||||
|
||||
.decoder = &ws_protocol_oregon3_decoder,
|
||||
};
|
||||
@@ -30,9 +30,9 @@
|
||||
* - u: unknown;
|
||||
* - b: battery low; flag to indicate low battery voltage
|
||||
* - s: temperature sign
|
||||
* - T: BCD, Temperature; in °C * 10
|
||||
* - t: BCD, Temperature; in °C * 1
|
||||
* - z: BCD, Temperature; in °C * 0.1
|
||||
* - T: BCD, Temperature; in <EFBFBD>C * 10
|
||||
* - t: BCD, Temperature; in <EFBFBD>C * 1
|
||||
* - z: BCD, Temperature; in <EFBFBD>C * 0.1
|
||||
* - c: Channel 00=CH1, 01=CH2, 10=CH3
|
||||
*
|
||||
*/
|
||||
@@ -95,7 +95,7 @@ const SubGhzProtocol ws_protocol_oregon_v1 = {
|
||||
.name = WS_PROTOCOL_OREGON_V1_NAME,
|
||||
.type = SubGhzProtocolWeatherStation,
|
||||
.flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_315 | SubGhzProtocolFlag_868 |
|
||||
SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable,
|
||||
SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable | SubGhzProtocolFlag_Weather,
|
||||
|
||||
.decoder = &ws_protocol_oregon_v1_decoder,
|
||||
.encoder = &ws_protocol_oregon_v1_encoder,
|
||||
@@ -303,7 +303,7 @@ SubGhzProtocolStatus
|
||||
void ws_protocol_decoder_oregon_v1_get_string(void* context, FuriString* output) {
|
||||
furi_assert(context);
|
||||
WSProtocolDecoderOregon_V1* instance = context;
|
||||
furi_string_printf(
|
||||
furi_string_cat_printf(
|
||||
output,
|
||||
"%s %dbit\r\n"
|
||||
"Key:0x%lX%08lX\r\n"
|
||||
@@ -1,7 +1,6 @@
|
||||
#include "pcsg_generic.h"
|
||||
#include <lib/toolbox/stream/stream.h>
|
||||
#include <lib/flipper_format/flipper_format_i.h>
|
||||
#include "../helpers/pocsag_pager_types.h"
|
||||
|
||||
#define TAG "PCSGBlockGeneric"
|
||||
|
||||
@@ -13,6 +13,9 @@
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define PCSG_KEY_FILE_VERSION 1
|
||||
#define PCSG_KEY_FILE_TYPE "Flipper POCSAG Pager Key File"
|
||||
|
||||
typedef struct PCSGBlockGeneric PCSGBlockGeneric;
|
||||
|
||||
struct PCSGBlockGeneric {
|
||||
@@ -42,6 +42,26 @@ const SubGhzProtocol* subghz_protocol_registry_items[] = {
|
||||
&subghz_protocol_dooya,
|
||||
&subghz_protocol_alutech_at_4n,
|
||||
&subghz_protocol_kinggates_stylo_4k,
|
||||
&ws_protocol_infactory,
|
||||
&ws_protocol_thermopro_tx4,
|
||||
&ws_protocol_nexus_th,
|
||||
&ws_protocol_gt_wt_02,
|
||||
&ws_protocol_gt_wt_03,
|
||||
&ws_protocol_acurite_606tx,
|
||||
&ws_protocol_acurite_609txc,
|
||||
&ws_protocol_lacrosse_tx,
|
||||
&ws_protocol_lacrosse_tx141thbv2,
|
||||
&ws_protocol_oregon2,
|
||||
&ws_protocol_oregon3,
|
||||
&ws_protocol_acurite_592txr,
|
||||
&ws_protocol_ambient_weather,
|
||||
&ws_protocol_auriol_th,
|
||||
&ws_protocol_oregon_v1,
|
||||
&ws_protocol_tx_8300,
|
||||
&ws_protocol_wendox_w6726,
|
||||
&ws_protocol_auriol_ahfl,
|
||||
&subghz_protocol_pocsag,
|
||||
&tpms_protocol_schrader_gg4,
|
||||
&subghz_protocol_bin_raw,
|
||||
};
|
||||
|
||||
|
||||
@@ -43,4 +43,24 @@
|
||||
#include "dooya.h"
|
||||
#include "alutech_at_4n.h"
|
||||
#include "kinggates_stylo_4k.h"
|
||||
#include "infactory.h"
|
||||
#include "thermopro_tx4.h"
|
||||
#include "nexus_th.h"
|
||||
#include "gt_wt_02.h"
|
||||
#include "gt_wt_03.h"
|
||||
#include "acurite_606tx.h"
|
||||
#include "acurite_609txc.h"
|
||||
#include "lacrosse_tx.h"
|
||||
#include "lacrosse_tx141thbv2.h"
|
||||
#include "oregon2.h"
|
||||
#include "oregon3.h"
|
||||
#include "acurite_592txr.h"
|
||||
#include "ambient_weather.h"
|
||||
#include "auriol_hg0601a.h"
|
||||
#include "oregon_v1.h"
|
||||
#include "tx_8300.h"
|
||||
#include "wendox_w6726.h"
|
||||
#include "auriol_ahfl.h"
|
||||
#include "pocsag.h"
|
||||
#include "schrader_gg4.h"
|
||||
#include "bin_raw.h"
|
||||
|
||||
295
lib/subghz/protocols/schrader_gg4.c
Normal file
@@ -0,0 +1,295 @@
|
||||
#include "schrader_gg4.h"
|
||||
#include <lib/toolbox/manchester_decoder.h>
|
||||
|
||||
#define TAG "Schrader"
|
||||
|
||||
// https://github.com/merbanan/rtl_433/blob/master/src/devices/schraeder.c
|
||||
// https://elib.dlr.de/81155/1/TPMS_for_Trafffic_Management_purposes.pdf
|
||||
// https://github.com/furrtek/portapack-havoc/issues/349
|
||||
// https://fccid.io/MRXGG4
|
||||
// https://fccid.io/MRXGG4T
|
||||
|
||||
/**
|
||||
* Schrader 3013/3015 MRX-GG4
|
||||
|
||||
OEM:
|
||||
KIA Sportage CGA 11-SPT1504-RA
|
||||
Mercedes-Benz A0009054100
|
||||
|
||||
* Frequency: 433.92MHz+-38KHz
|
||||
* Modulation: ASK
|
||||
* Working Temperature: -50°C to 125°C
|
||||
* Tire monitoring range value: 0kPa-350kPa+-7kPa
|
||||
|
||||
Examples in normal environmental conditions:
|
||||
3000878456094cd0
|
||||
3000878456084ecb
|
||||
3000878456074d01
|
||||
|
||||
Data layout:
|
||||
* | Byte 0 | Byte 1 | Byte 2 | Byte 3 | Byte 4 | Byte 5 | Byte 6 | Byte 7 |
|
||||
* | --------- | --------- | --------- | --------- | --------- | --------- | --------- | --------- |
|
||||
* | SSSS SSSS | IIII IIII | IIII IIII | IIII IIII | IIII IIII | PPPP PPPP | TTTT TTTT | CCCC CCCC |
|
||||
*
|
||||
|
||||
- The preamble is 0b000
|
||||
- S: always 0x30 in relearn state
|
||||
- I: 32 bit ID
|
||||
- P: 8 bit Pressure (multiplyed by 2.5 = PSI)
|
||||
- T: 8 bit Temperature (deg. C offset by 50)
|
||||
- C: 8 bit Checksum (CRC8, Poly 0x7, Init 0x0)
|
||||
*/
|
||||
|
||||
#define PREAMBLE 0b000
|
||||
#define PREAMBLE_BITS_LEN 3
|
||||
|
||||
static const SubGhzBlockConst tpms_protocol_schrader_gg4_const = {
|
||||
.te_short = 120,
|
||||
.te_long = 240,
|
||||
.te_delta = 55, // 50% of te_short due to poor sensitivity
|
||||
.min_count_bit_for_found = 64,
|
||||
};
|
||||
|
||||
struct TPMSProtocolDecoderSchraderGG4 {
|
||||
SubGhzProtocolDecoderBase base;
|
||||
|
||||
SubGhzBlockDecoder decoder;
|
||||
TPMSBlockGeneric generic;
|
||||
|
||||
ManchesterState manchester_saved_state;
|
||||
uint16_t header_count;
|
||||
};
|
||||
|
||||
struct TPMSProtocolEncoderSchraderGG4 {
|
||||
SubGhzProtocolEncoderBase base;
|
||||
|
||||
SubGhzProtocolBlockEncoder encoder;
|
||||
TPMSBlockGeneric generic;
|
||||
};
|
||||
|
||||
typedef enum {
|
||||
SchraderGG4DecoderStepReset = 0,
|
||||
SchraderGG4DecoderStepCheckPreamble,
|
||||
SchraderGG4DecoderStepDecoderData,
|
||||
SchraderGG4DecoderStepSaveDuration,
|
||||
SchraderGG4DecoderStepCheckDuration,
|
||||
} SchraderGG4DecoderStep;
|
||||
|
||||
const SubGhzProtocolDecoder tpms_protocol_schrader_gg4_decoder = {
|
||||
.alloc = tpms_protocol_decoder_schrader_gg4_alloc,
|
||||
.free = tpms_protocol_decoder_schrader_gg4_free,
|
||||
|
||||
.feed = tpms_protocol_decoder_schrader_gg4_feed,
|
||||
.reset = tpms_protocol_decoder_schrader_gg4_reset,
|
||||
|
||||
.get_hash_data = tpms_protocol_decoder_schrader_gg4_get_hash_data,
|
||||
.serialize = tpms_protocol_decoder_schrader_gg4_serialize,
|
||||
.deserialize = tpms_protocol_decoder_schrader_gg4_deserialize,
|
||||
.get_string = tpms_protocol_decoder_schrader_gg4_get_string,
|
||||
};
|
||||
|
||||
const SubGhzProtocolEncoder tpms_protocol_schrader_gg4_encoder = {
|
||||
.alloc = NULL,
|
||||
.free = NULL,
|
||||
|
||||
.deserialize = NULL,
|
||||
.stop = NULL,
|
||||
.yield = NULL,
|
||||
};
|
||||
|
||||
const SubGhzProtocol tpms_protocol_schrader_gg4 = {
|
||||
.name = TPMS_PROTOCOL_SCHRADER_GG4_NAME,
|
||||
.type = SubGhzProtocolTypeStatic,
|
||||
.flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_315 | SubGhzProtocolFlag_AM |
|
||||
SubGhzProtocolFlag_Decodable | SubGhzProtocolFlag_TPMS,
|
||||
|
||||
.decoder = &tpms_protocol_schrader_gg4_decoder,
|
||||
.encoder = &tpms_protocol_schrader_gg4_encoder,
|
||||
};
|
||||
|
||||
void* tpms_protocol_decoder_schrader_gg4_alloc(SubGhzEnvironment* environment) {
|
||||
UNUSED(environment);
|
||||
TPMSProtocolDecoderSchraderGG4* instance = malloc(sizeof(TPMSProtocolDecoderSchraderGG4));
|
||||
instance->base.protocol = &tpms_protocol_schrader_gg4;
|
||||
instance->generic.protocol_name = instance->base.protocol->name;
|
||||
return instance;
|
||||
}
|
||||
|
||||
void tpms_protocol_decoder_schrader_gg4_free(void* context) {
|
||||
furi_assert(context);
|
||||
TPMSProtocolDecoderSchraderGG4* instance = context;
|
||||
free(instance);
|
||||
}
|
||||
|
||||
void tpms_protocol_decoder_schrader_gg4_reset(void* context) {
|
||||
furi_assert(context);
|
||||
TPMSProtocolDecoderSchraderGG4* instance = context;
|
||||
instance->decoder.parser_step = SchraderGG4DecoderStepReset;
|
||||
}
|
||||
|
||||
static bool tpms_protocol_schrader_gg4_check_crc(TPMSProtocolDecoderSchraderGG4* instance) {
|
||||
uint8_t msg[] = {
|
||||
instance->decoder.decode_data >> 48,
|
||||
instance->decoder.decode_data >> 40,
|
||||
instance->decoder.decode_data >> 32,
|
||||
instance->decoder.decode_data >> 24,
|
||||
instance->decoder.decode_data >> 16,
|
||||
instance->decoder.decode_data >> 8};
|
||||
|
||||
uint8_t crc = subghz_protocol_blocks_crc8(msg, 6, 0x7, 0);
|
||||
return (crc == (instance->decoder.decode_data & 0xFF));
|
||||
}
|
||||
|
||||
/**
|
||||
* Analysis of received data
|
||||
* @param instance Pointer to a TPMSBlockGeneric* instance
|
||||
*/
|
||||
static void tpms_protocol_schrader_gg4_analyze(TPMSBlockGeneric* instance) {
|
||||
instance->id = instance->data >> 24;
|
||||
|
||||
// TODO locate and fix
|
||||
instance->battery_low = TPMS_NO_BATT;
|
||||
|
||||
instance->temperature = ((instance->data >> 8) & 0xFF) - 50;
|
||||
instance->pressure = ((instance->data >> 16) & 0xFF) * 2.5 * 0.069;
|
||||
}
|
||||
|
||||
static ManchesterEvent level_and_duration_to_event(bool level, uint32_t duration) {
|
||||
bool is_long = false;
|
||||
|
||||
if(DURATION_DIFF(duration, tpms_protocol_schrader_gg4_const.te_long) <
|
||||
tpms_protocol_schrader_gg4_const.te_delta) {
|
||||
is_long = true;
|
||||
} else if(
|
||||
DURATION_DIFF(duration, tpms_protocol_schrader_gg4_const.te_short) <
|
||||
tpms_protocol_schrader_gg4_const.te_delta) {
|
||||
is_long = false;
|
||||
} else {
|
||||
return ManchesterEventReset;
|
||||
}
|
||||
|
||||
if(level)
|
||||
return is_long ? ManchesterEventLongHigh : ManchesterEventShortHigh;
|
||||
else
|
||||
return is_long ? ManchesterEventLongLow : ManchesterEventShortLow;
|
||||
}
|
||||
|
||||
void tpms_protocol_decoder_schrader_gg4_feed(void* context, bool level, uint32_t duration) {
|
||||
furi_assert(context);
|
||||
bool bit = false;
|
||||
bool have_bit = false;
|
||||
TPMSProtocolDecoderSchraderGG4* instance = context;
|
||||
|
||||
// low-level bit sequence decoding
|
||||
if(instance->decoder.parser_step != SchraderGG4DecoderStepReset) {
|
||||
ManchesterEvent event = level_and_duration_to_event(level, duration);
|
||||
|
||||
if(event == ManchesterEventReset) {
|
||||
if((instance->decoder.parser_step == SchraderGG4DecoderStepDecoderData) &&
|
||||
instance->decoder.decode_count_bit) {
|
||||
// FURI_LOG_D(TAG, "%d-%ld", level, duration);
|
||||
FURI_LOG_D(
|
||||
TAG,
|
||||
"reset accumulated %d bits: %llx",
|
||||
instance->decoder.decode_count_bit,
|
||||
instance->decoder.decode_data);
|
||||
}
|
||||
|
||||
instance->decoder.parser_step = SchraderGG4DecoderStepReset;
|
||||
} else {
|
||||
have_bit = manchester_advance(
|
||||
instance->manchester_saved_state, event, &instance->manchester_saved_state, &bit);
|
||||
if(!have_bit) return;
|
||||
|
||||
// Invert value, due to signal is Manchester II and decoder is Manchester I
|
||||
bit = !bit;
|
||||
}
|
||||
}
|
||||
|
||||
switch(instance->decoder.parser_step) {
|
||||
case SchraderGG4DecoderStepReset:
|
||||
// wait for start ~480us pulse
|
||||
if((level) && (DURATION_DIFF(duration, tpms_protocol_schrader_gg4_const.te_long * 2) <
|
||||
tpms_protocol_schrader_gg4_const.te_delta)) {
|
||||
instance->decoder.parser_step = SchraderGG4DecoderStepCheckPreamble;
|
||||
instance->header_count = 0;
|
||||
instance->decoder.decode_data = 0;
|
||||
instance->decoder.decode_count_bit = 0;
|
||||
|
||||
// First will be short space, so set correct initial state for machine
|
||||
// https://clearwater.com.au/images/rc5/rc5-state-machine.gif
|
||||
instance->manchester_saved_state = ManchesterStateStart1;
|
||||
}
|
||||
break;
|
||||
case SchraderGG4DecoderStepCheckPreamble:
|
||||
if(bit != 0) {
|
||||
instance->decoder.parser_step = SchraderGG4DecoderStepReset;
|
||||
break;
|
||||
}
|
||||
|
||||
instance->header_count++;
|
||||
if(instance->header_count == PREAMBLE_BITS_LEN)
|
||||
instance->decoder.parser_step = SchraderGG4DecoderStepDecoderData;
|
||||
break;
|
||||
|
||||
case SchraderGG4DecoderStepDecoderData:
|
||||
subghz_protocol_blocks_add_bit(&instance->decoder, bit);
|
||||
if(instance->decoder.decode_count_bit ==
|
||||
tpms_protocol_schrader_gg4_const.min_count_bit_for_found) {
|
||||
FURI_LOG_D(TAG, "%016llx", instance->decoder.decode_data);
|
||||
if(!tpms_protocol_schrader_gg4_check_crc(instance)) {
|
||||
FURI_LOG_D(TAG, "CRC mismatch drop");
|
||||
} else {
|
||||
instance->generic.data = instance->decoder.decode_data;
|
||||
instance->generic.data_count_bit = instance->decoder.decode_count_bit;
|
||||
tpms_protocol_schrader_gg4_analyze(&instance->generic);
|
||||
if(instance->base.callback)
|
||||
instance->base.callback(&instance->base, instance->base.context);
|
||||
}
|
||||
instance->decoder.parser_step = SchraderGG4DecoderStepReset;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t tpms_protocol_decoder_schrader_gg4_get_hash_data(void* context) {
|
||||
furi_assert(context);
|
||||
TPMSProtocolDecoderSchraderGG4* instance = context;
|
||||
return subghz_protocol_blocks_get_hash_data(
|
||||
&instance->decoder, (instance->decoder.decode_count_bit / 8) + 1);
|
||||
}
|
||||
|
||||
SubGhzProtocolStatus tpms_protocol_decoder_schrader_gg4_serialize(
|
||||
void* context,
|
||||
FlipperFormat* flipper_format,
|
||||
SubGhzRadioPreset* preset) {
|
||||
furi_assert(context);
|
||||
TPMSProtocolDecoderSchraderGG4* instance = context;
|
||||
return tpms_block_generic_serialize(&instance->generic, flipper_format, preset);
|
||||
}
|
||||
|
||||
SubGhzProtocolStatus
|
||||
tpms_protocol_decoder_schrader_gg4_deserialize(void* context, FlipperFormat* flipper_format) {
|
||||
furi_assert(context);
|
||||
TPMSProtocolDecoderSchraderGG4* instance = context;
|
||||
return tpms_block_generic_deserialize_check_count_bit(
|
||||
&instance->generic,
|
||||
flipper_format,
|
||||
tpms_protocol_schrader_gg4_const.min_count_bit_for_found);
|
||||
}
|
||||
|
||||
void tpms_protocol_decoder_schrader_gg4_get_string(void* context, FuriString* output) {
|
||||
furi_assert(context);
|
||||
TPMSProtocolDecoderSchraderGG4* instance = context;
|
||||
furi_string_cat_printf(
|
||||
output,
|
||||
"%s\r\n"
|
||||
"Id:0x%08lX\r\n"
|
||||
"Bat:%d\r\n"
|
||||
"Temp:%2.0f C Bar:%2.1f",
|
||||
instance->generic.protocol_name,
|
||||
instance->generic.id,
|
||||
instance->generic.battery_low,
|
||||
(double)instance->generic.temperature,
|
||||
(double)instance->generic.pressure);
|
||||
}
|
||||
80
lib/subghz/protocols/schrader_gg4.h
Normal file
@@ -0,0 +1,80 @@
|
||||
#pragma once
|
||||
|
||||
#include <lib/subghz/protocols/base.h>
|
||||
|
||||
#include <lib/subghz/blocks/const.h>
|
||||
#include <lib/subghz/blocks/decoder.h>
|
||||
#include <lib/subghz/blocks/encoder.h>
|
||||
#include "tpms_generic.h"
|
||||
#include <lib/subghz/blocks/math.h>
|
||||
|
||||
#define TPMS_PROTOCOL_SCHRADER_GG4_NAME "Schrader GG4"
|
||||
|
||||
typedef struct TPMSProtocolDecoderSchraderGG4 TPMSProtocolDecoderSchraderGG4;
|
||||
typedef struct TPMSProtocolEncoderSchraderGG4 TPMSProtocolEncoderSchraderGG4;
|
||||
|
||||
extern const SubGhzProtocolDecoder tpms_protocol_schrader_gg4_decoder;
|
||||
extern const SubGhzProtocolEncoder tpms_protocol_schrader_gg4_encoder;
|
||||
extern const SubGhzProtocol tpms_protocol_schrader_gg4;
|
||||
|
||||
/**
|
||||
* Allocate TPMSProtocolDecoderSchraderGG4.
|
||||
* @param environment Pointer to a SubGhzEnvironment instance
|
||||
* @return TPMSProtocolDecoderSchraderGG4* pointer to a TPMSProtocolDecoderSchraderGG4 instance
|
||||
*/
|
||||
void* tpms_protocol_decoder_schrader_gg4_alloc(SubGhzEnvironment* environment);
|
||||
|
||||
/**
|
||||
* Free TPMSProtocolDecoderSchraderGG4.
|
||||
* @param context Pointer to a TPMSProtocolDecoderSchraderGG4 instance
|
||||
*/
|
||||
void tpms_protocol_decoder_schrader_gg4_free(void* context);
|
||||
|
||||
/**
|
||||
* Reset decoder TPMSProtocolDecoderSchraderGG4.
|
||||
* @param context Pointer to a TPMSProtocolDecoderSchraderGG4 instance
|
||||
*/
|
||||
void tpms_protocol_decoder_schrader_gg4_reset(void* context);
|
||||
|
||||
/**
|
||||
* Parse a raw sequence of levels and durations received from the air.
|
||||
* @param context Pointer to a TPMSProtocolDecoderSchraderGG4 instance
|
||||
* @param level Signal level true-high false-low
|
||||
* @param duration Duration of this level in, us
|
||||
*/
|
||||
void tpms_protocol_decoder_schrader_gg4_feed(void* context, bool level, uint32_t duration);
|
||||
|
||||
/**
|
||||
* Getting the hash sum of the last randomly received parcel.
|
||||
* @param context Pointer to a TPMSProtocolDecoderSchraderGG4 instance
|
||||
* @return hash Hash sum
|
||||
*/
|
||||
uint8_t tpms_protocol_decoder_schrader_gg4_get_hash_data(void* context);
|
||||
|
||||
/**
|
||||
* Serialize data TPMSProtocolDecoderSchraderGG4.
|
||||
* @param context Pointer to a TPMSProtocolDecoderSchraderGG4 instance
|
||||
* @param flipper_format Pointer to a FlipperFormat instance
|
||||
* @param preset The modulation on which the signal was received, SubGhzRadioPreset
|
||||
* @return status
|
||||
*/
|
||||
SubGhzProtocolStatus tpms_protocol_decoder_schrader_gg4_serialize(
|
||||
void* context,
|
||||
FlipperFormat* flipper_format,
|
||||
SubGhzRadioPreset* preset);
|
||||
|
||||
/**
|
||||
* Deserialize data TPMSProtocolDecoderSchraderGG4.
|
||||
* @param context Pointer to a TPMSProtocolDecoderSchraderGG4 instance
|
||||
* @param flipper_format Pointer to a FlipperFormat instance
|
||||
* @return status
|
||||
*/
|
||||
SubGhzProtocolStatus
|
||||
tpms_protocol_decoder_schrader_gg4_deserialize(void* context, FlipperFormat* flipper_format);
|
||||
|
||||
/**
|
||||
* Getting a textual representation of the received data.
|
||||
* @param context Pointer to a TPMSProtocolDecoderSchraderGG4 instance
|
||||
* @param output Resulting text
|
||||
*/
|
||||
void tpms_protocol_decoder_schrader_gg4_get_string(void* context, FuriString* output);
|
||||
@@ -79,7 +79,7 @@ const SubGhzProtocol ws_protocol_thermopro_tx4 = {
|
||||
.name = WS_PROTOCOL_THERMOPRO_TX4_NAME,
|
||||
.type = SubGhzProtocolWeatherStation,
|
||||
.flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_315 | SubGhzProtocolFlag_868 |
|
||||
SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable,
|
||||
SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable | SubGhzProtocolFlag_Weather,
|
||||
|
||||
.decoder = &ws_protocol_thermopro_tx4_decoder,
|
||||
.encoder = &ws_protocol_thermopro_tx4_encoder,
|
||||
@@ -233,7 +233,7 @@ SubGhzProtocolStatus
|
||||
void ws_protocol_decoder_thermopro_tx4_get_string(void* context, FuriString* output) {
|
||||
furi_assert(context);
|
||||
WSProtocolDecoderThermoPRO_TX4* instance = context;
|
||||
furi_string_printf(
|
||||
furi_string_cat_printf(
|
||||
output,
|
||||
"%s %dbit\r\n"
|
||||
"Key:0x%lX%08lX\r\n"
|
||||
227
lib/subghz/protocols/tpms_generic.c
Normal file
@@ -0,0 +1,227 @@
|
||||
#include "tpms_generic.h"
|
||||
#include <lib/toolbox/stream/stream.h>
|
||||
#include <lib/flipper_format/flipper_format_i.h>
|
||||
|
||||
#define TAG "TPMSBlockGeneric"
|
||||
|
||||
void tpms_block_generic_get_preset_name(const char* preset_name, FuriString* preset_str) {
|
||||
const char* preset_name_temp;
|
||||
if(!strcmp(preset_name, "AM270")) {
|
||||
preset_name_temp = "FuriHalSubGhzPresetOok270Async";
|
||||
} else if(!strcmp(preset_name, "AM650")) {
|
||||
preset_name_temp = "FuriHalSubGhzPresetOok650Async";
|
||||
} else if(!strcmp(preset_name, "FM238")) {
|
||||
preset_name_temp = "FuriHalSubGhzPreset2FSKDev238Async";
|
||||
} else if(!strcmp(preset_name, "FM476")) {
|
||||
preset_name_temp = "FuriHalSubGhzPreset2FSKDev476Async";
|
||||
} else {
|
||||
preset_name_temp = "FuriHalSubGhzPresetCustom";
|
||||
}
|
||||
furi_string_set(preset_str, preset_name_temp);
|
||||
}
|
||||
|
||||
SubGhzProtocolStatus tpms_block_generic_serialize(
|
||||
TPMSBlockGeneric* instance,
|
||||
FlipperFormat* flipper_format,
|
||||
SubGhzRadioPreset* preset) {
|
||||
furi_assert(instance);
|
||||
SubGhzProtocolStatus res = SubGhzProtocolStatusError;
|
||||
FuriString* temp_str;
|
||||
temp_str = furi_string_alloc();
|
||||
do {
|
||||
stream_clean(flipper_format_get_raw_stream(flipper_format));
|
||||
if(!flipper_format_write_header_cstr(
|
||||
flipper_format, TPMS_KEY_FILE_TYPE, TPMS_KEY_FILE_VERSION)) {
|
||||
FURI_LOG_E(TAG, "Unable to add header");
|
||||
res = SubGhzProtocolStatusErrorParserHeader;
|
||||
break;
|
||||
}
|
||||
|
||||
if(!flipper_format_write_uint32(flipper_format, "Frequency", &preset->frequency, 1)) {
|
||||
FURI_LOG_E(TAG, "Unable to add Frequency");
|
||||
res = SubGhzProtocolStatusErrorParserFrequency;
|
||||
break;
|
||||
}
|
||||
|
||||
tpms_block_generic_get_preset_name(furi_string_get_cstr(preset->name), temp_str);
|
||||
if(!flipper_format_write_string_cstr(
|
||||
flipper_format, "Preset", furi_string_get_cstr(temp_str))) {
|
||||
FURI_LOG_E(TAG, "Unable to add Preset");
|
||||
res = SubGhzProtocolStatusErrorParserPreset;
|
||||
break;
|
||||
}
|
||||
if(!strcmp(furi_string_get_cstr(temp_str), "FuriHalSubGhzPresetCustom")) {
|
||||
if(!flipper_format_write_string_cstr(
|
||||
flipper_format, "Custom_preset_module", "CC1101")) {
|
||||
FURI_LOG_E(TAG, "Unable to add Custom_preset_module");
|
||||
res = SubGhzProtocolStatusErrorParserCustomPreset;
|
||||
break;
|
||||
}
|
||||
if(!flipper_format_write_hex(
|
||||
flipper_format, "Custom_preset_data", preset->data, preset->data_size)) {
|
||||
FURI_LOG_E(TAG, "Unable to add Custom_preset_data");
|
||||
res = SubGhzProtocolStatusErrorParserCustomPreset;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(!flipper_format_write_string_cstr(flipper_format, "Protocol", instance->protocol_name)) {
|
||||
FURI_LOG_E(TAG, "Unable to add Protocol");
|
||||
res = SubGhzProtocolStatusErrorParserProtocolName;
|
||||
break;
|
||||
}
|
||||
|
||||
uint32_t temp_data = instance->id;
|
||||
if(!flipper_format_write_uint32(flipper_format, "Id", &temp_data, 1)) {
|
||||
FURI_LOG_E(TAG, "Unable to add Id");
|
||||
res = SubGhzProtocolStatusErrorParserOthers;
|
||||
break;
|
||||
}
|
||||
|
||||
temp_data = instance->data_count_bit;
|
||||
if(!flipper_format_write_uint32(flipper_format, "Bit", &temp_data, 1)) {
|
||||
FURI_LOG_E(TAG, "Unable to add Bit");
|
||||
res = SubGhzProtocolStatusErrorParserBitCount;
|
||||
break;
|
||||
}
|
||||
|
||||
uint8_t key_data[sizeof(uint64_t)] = {0};
|
||||
for(size_t i = 0; i < sizeof(uint64_t); i++) {
|
||||
key_data[sizeof(uint64_t) - i - 1] = (instance->data >> (i * 8)) & 0xFF;
|
||||
}
|
||||
|
||||
if(!flipper_format_write_hex(flipper_format, "Data", key_data, sizeof(uint64_t))) {
|
||||
FURI_LOG_E(TAG, "Unable to add Data");
|
||||
res = SubGhzProtocolStatusErrorParserOthers;
|
||||
break;
|
||||
}
|
||||
|
||||
temp_data = instance->battery_low;
|
||||
if(!flipper_format_write_uint32(flipper_format, "Batt", &temp_data, 1)) {
|
||||
FURI_LOG_E(TAG, "Unable to add Battery_low");
|
||||
res = SubGhzProtocolStatusErrorParserOthers;
|
||||
break;
|
||||
}
|
||||
|
||||
float temp = instance->pressure;
|
||||
if(!flipper_format_write_float(flipper_format, "Pressure", &temp, 1)) {
|
||||
FURI_LOG_E(TAG, "Unable to add Pressure");
|
||||
res = SubGhzProtocolStatusErrorParserOthers;
|
||||
break;
|
||||
}
|
||||
|
||||
//DATE AGE set
|
||||
FuriHalRtcDateTime curr_dt;
|
||||
furi_hal_rtc_get_datetime(&curr_dt);
|
||||
uint32_t curr_ts = furi_hal_rtc_datetime_to_timestamp(&curr_dt);
|
||||
|
||||
temp_data = curr_ts;
|
||||
if(!flipper_format_write_uint32(flipper_format, "Ts", &temp_data, 1)) {
|
||||
FURI_LOG_E(TAG, "Unable to add timestamp");
|
||||
res = SubGhzProtocolStatusErrorParserOthers;
|
||||
break;
|
||||
}
|
||||
|
||||
temp = instance->temperature;
|
||||
if(!flipper_format_write_float(flipper_format, "Temp", &temp, 1)) {
|
||||
FURI_LOG_E(TAG, "Unable to add Temperature");
|
||||
res = SubGhzProtocolStatusErrorParserOthers;
|
||||
break;
|
||||
}
|
||||
|
||||
res = SubGhzProtocolStatusOk;
|
||||
} while(false);
|
||||
furi_string_free(temp_str);
|
||||
return res;
|
||||
}
|
||||
|
||||
SubGhzProtocolStatus
|
||||
tpms_block_generic_deserialize(TPMSBlockGeneric* instance, FlipperFormat* flipper_format) {
|
||||
furi_assert(instance);
|
||||
SubGhzProtocolStatus res = SubGhzProtocolStatusError;
|
||||
uint32_t temp_data = 0;
|
||||
|
||||
do {
|
||||
if(!flipper_format_rewind(flipper_format)) {
|
||||
FURI_LOG_E(TAG, "Rewind error");
|
||||
res = SubGhzProtocolStatusErrorParserOthers;
|
||||
break;
|
||||
}
|
||||
|
||||
if(!flipper_format_read_uint32(flipper_format, "Id", &temp_data, 1)) {
|
||||
FURI_LOG_E(TAG, "Missing Id");
|
||||
res = SubGhzProtocolStatusErrorParserOthers;
|
||||
break;
|
||||
}
|
||||
instance->id = temp_data;
|
||||
|
||||
if(!flipper_format_read_uint32(flipper_format, "Bit", &temp_data, 1)) {
|
||||
FURI_LOG_E(TAG, "Missing Bit");
|
||||
res = SubGhzProtocolStatusErrorParserBitCount;
|
||||
break;
|
||||
}
|
||||
instance->data_count_bit = (uint8_t)temp_data;
|
||||
|
||||
uint8_t key_data[sizeof(uint64_t)] = {0};
|
||||
if(!flipper_format_read_hex(flipper_format, "Data", key_data, sizeof(uint64_t))) {
|
||||
FURI_LOG_E(TAG, "Missing Data");
|
||||
res = SubGhzProtocolStatusErrorParserOthers;
|
||||
break;
|
||||
}
|
||||
|
||||
for(uint8_t i = 0; i < sizeof(uint64_t); i++) {
|
||||
instance->data = instance->data << 8 | key_data[i];
|
||||
}
|
||||
|
||||
if(!flipper_format_read_uint32(flipper_format, "Batt", &temp_data, 1)) {
|
||||
FURI_LOG_E(TAG, "Missing Battery_low");
|
||||
res = SubGhzProtocolStatusErrorParserOthers;
|
||||
break;
|
||||
}
|
||||
instance->battery_low = (uint8_t)temp_data;
|
||||
|
||||
float temp;
|
||||
if(!flipper_format_read_float(flipper_format, "Pressure", &temp, 1)) {
|
||||
FURI_LOG_E(TAG, "Missing Pressure");
|
||||
res = SubGhzProtocolStatusErrorParserOthers;
|
||||
break;
|
||||
}
|
||||
instance->pressure = temp;
|
||||
|
||||
if(!flipper_format_read_uint32(flipper_format, "Ts", &temp_data, 1)) {
|
||||
FURI_LOG_E(TAG, "Missing timestamp");
|
||||
res = SubGhzProtocolStatusErrorParserOthers;
|
||||
break;
|
||||
}
|
||||
instance->timestamp = temp_data;
|
||||
|
||||
if(!flipper_format_read_float(flipper_format, "Temp", &temp, 1)) {
|
||||
FURI_LOG_E(TAG, "Missing Temperature");
|
||||
res = SubGhzProtocolStatusErrorParserOthers;
|
||||
break;
|
||||
}
|
||||
instance->temperature = temp;
|
||||
|
||||
res = SubGhzProtocolStatusOk;
|
||||
} while(0);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
SubGhzProtocolStatus tpms_block_generic_deserialize_check_count_bit(
|
||||
TPMSBlockGeneric* instance,
|
||||
FlipperFormat* flipper_format,
|
||||
uint16_t count_bit) {
|
||||
SubGhzProtocolStatus ret = SubGhzProtocolStatusError;
|
||||
do {
|
||||
ret = tpms_block_generic_deserialize(instance, flipper_format);
|
||||
if(ret != SubGhzProtocolStatusOk) {
|
||||
break;
|
||||
}
|
||||
if(instance->data_count_bit != count_bit) {
|
||||
FURI_LOG_E(TAG, "Wrong number of bits in key");
|
||||
ret = SubGhzProtocolStatusErrorValueBitCount;
|
||||
break;
|
||||
}
|
||||
} while(false);
|
||||
return ret;
|
||||
}
|
||||
80
lib/subghz/protocols/tpms_generic.h
Normal file
@@ -0,0 +1,80 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#include <lib/flipper_format/flipper_format.h>
|
||||
#include "furi.h"
|
||||
#include <furi_hal.h>
|
||||
#include <lib/subghz/types.h>
|
||||
#include <locale/locale.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define TPMS_KEY_FILE_VERSION 1
|
||||
#define TPMS_KEY_FILE_TYPE "Flipper Tire Pressure Monitoring System Key File"
|
||||
|
||||
#define TPMS_NO_BATT 0xFF
|
||||
|
||||
typedef struct TPMSBlockGeneric TPMSBlockGeneric;
|
||||
|
||||
struct TPMSBlockGeneric {
|
||||
const char* protocol_name;
|
||||
uint64_t data;
|
||||
uint8_t data_count_bit;
|
||||
|
||||
uint32_t timestamp;
|
||||
|
||||
uint32_t id;
|
||||
uint8_t battery_low;
|
||||
// bool storage;
|
||||
float pressure; // bar
|
||||
float temperature; // celsius
|
||||
};
|
||||
|
||||
/**
|
||||
* Get name preset.
|
||||
* @param preset_name name preset
|
||||
* @param preset_str Output name preset
|
||||
*/
|
||||
void tpms_block_generic_get_preset_name(const char* preset_name, FuriString* preset_str);
|
||||
|
||||
/**
|
||||
* Serialize data TPMSBlockGeneric.
|
||||
* @param instance Pointer to a TPMSBlockGeneric instance
|
||||
* @param flipper_format Pointer to a FlipperFormat instance
|
||||
* @param preset The modulation on which the signal was received, SubGhzRadioPreset
|
||||
* @return status
|
||||
*/
|
||||
SubGhzProtocolStatus tpms_block_generic_serialize(
|
||||
TPMSBlockGeneric* instance,
|
||||
FlipperFormat* flipper_format,
|
||||
SubGhzRadioPreset* preset);
|
||||
|
||||
/**
|
||||
* Deserialize data TPMSBlockGeneric.
|
||||
* @param instance Pointer to a TPMSBlockGeneric instance
|
||||
* @param flipper_format Pointer to a FlipperFormat instance
|
||||
* @return status
|
||||
*/
|
||||
SubGhzProtocolStatus
|
||||
tpms_block_generic_deserialize(TPMSBlockGeneric* instance, FlipperFormat* flipper_format);
|
||||
|
||||
/**
|
||||
* Deserialize data TPMSBlockGeneric.
|
||||
* @param instance Pointer to a TPMSBlockGeneric instance
|
||||
* @param flipper_format Pointer to a FlipperFormat instance
|
||||
* @param count_bit Count bit protocol
|
||||
* @return status
|
||||
*/
|
||||
SubGhzProtocolStatus tpms_block_generic_deserialize_check_count_bit(
|
||||
TPMSBlockGeneric* instance,
|
||||
FlipperFormat* flipper_format,
|
||||
uint16_t count_bit);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -90,7 +90,7 @@ const SubGhzProtocol ws_protocol_tx_8300 = {
|
||||
.name = WS_PROTOCOL_TX_8300_NAME,
|
||||
.type = SubGhzProtocolWeatherStation,
|
||||
.flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_315 | SubGhzProtocolFlag_868 |
|
||||
SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable,
|
||||
SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable | SubGhzProtocolFlag_Weather,
|
||||
|
||||
.decoder = &ws_protocol_tx_8300_decoder,
|
||||
.encoder = &ws_protocol_tx_8300_encoder,
|
||||
@@ -266,7 +266,7 @@ SubGhzProtocolStatus
|
||||
void ws_protocol_decoder_tx_8300_get_string(void* context, FuriString* output) {
|
||||
furi_assert(context);
|
||||
WSProtocolDecoderTX_8300* instance = context;
|
||||
furi_string_printf(
|
||||
furi_string_cat_printf(
|
||||
output,
|
||||
"%s %dbit\r\n"
|
||||
"Key:0x%lX%08lX\r\n"
|
||||