[FL-2904, FL-2900, FL-2890] WS: add app WeatherStation (#1833)
* WeatherStation: start
* SubGhz: rename protocol magellen -> magellan
* WeatherStation: err Unresolved symbols: {'subghz_protocol_decoder_base_get_string'}
* WeatherStation: fix Unresolved symbols: {'subghz_protocol_decoder_base_get_string'}
* Subghz: add set protocol_items
* WeatherStation: adding your protocols
* WS: add Infactory protocol
* WS: add history
* WS: add setting
* WS: add lock
* WS: add hopper frequency
* WS: fix history
* WS fix string_t -> FuriString*
* WS: add images
* WS: history record update when receiving data from the sensor again
* WS: add receiver info, delete extra code
* WS: add protocol ThermoPRO_TX4
* [FL-2900] SubGhz: Move icons in Sub-GHz
* WS: add Notification
* [FL-2890] SubGhz: Rename *_user files in resources to _user.example
* WS: add about scene
* WS: removing redundant code
* WS: add  protocol Nexus-TH
* WS: add protocol GT_WT03
* WS: fix notification and rename "Weather Station" -> "Read Weather Station"
* SubGhz: partial unit tests fix
* SubGhz: fix unit_test
* SubGhz: remove dead code
* SubGhz: rename SubGhzPresetDefinition into SubGhzRadioPreset, cleanup subghz types.
Co-authored-by: Aleksandr Kutuzov <alleteam@gmail.com>
			
			
This commit is contained in:
		
							parent
							
								
									79c3040629
								
							
						
					
					
						commit
						9a9abd59e9
					
				| @ -5,7 +5,7 @@ | |||||||
| #include <lib/subghz/transmitter.h> | #include <lib/subghz/transmitter.h> | ||||||
| #include <lib/subghz/subghz_keystore.h> | #include <lib/subghz/subghz_keystore.h> | ||||||
| #include <lib/subghz/subghz_file_encoder_worker.h> | #include <lib/subghz/subghz_file_encoder_worker.h> | ||||||
| #include <lib/subghz/protocols/registry.h> | #include <lib/subghz/protocols/protocol_items.h> | ||||||
| #include <flipper_format/flipper_format_i.h> | #include <flipper_format/flipper_format_i.h> | ||||||
| 
 | 
 | ||||||
| #define TAG "SubGhz TEST" | #define TAG "SubGhz TEST" | ||||||
| @ -43,6 +43,8 @@ static void subghz_test_init(void) { | |||||||
|         environment_handler, CAME_ATOMO_DIR_NAME); |         environment_handler, CAME_ATOMO_DIR_NAME); | ||||||
|     subghz_environment_set_nice_flor_s_rainbow_table_file_name( |     subghz_environment_set_nice_flor_s_rainbow_table_file_name( | ||||||
|         environment_handler, NICE_FLOR_S_DIR_NAME); |         environment_handler, NICE_FLOR_S_DIR_NAME); | ||||||
|  |     subghz_environment_set_protocol_registry( | ||||||
|  |         environment_handler, (void*)&subghz_protocol_registry); | ||||||
| 
 | 
 | ||||||
|     receiver_handler = subghz_receiver_alloc_init(environment_handler); |     receiver_handler = subghz_receiver_alloc_init(environment_handler); | ||||||
|     subghz_receiver_set_filter(receiver_handler, SubGhzProtocolFlag_Decodable); |     subghz_receiver_set_filter(receiver_handler, SubGhzProtocolFlag_Decodable); | ||||||
| @ -413,11 +415,11 @@ MU_TEST(subghz_decoder_honeywell_wdb_test) { | |||||||
|         "Test decoder " SUBGHZ_PROTOCOL_HONEYWELL_WDB_NAME " error\r\n"); |         "Test decoder " SUBGHZ_PROTOCOL_HONEYWELL_WDB_NAME " error\r\n"); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| MU_TEST(subghz_decoder_magellen_test) { | MU_TEST(subghz_decoder_magellan_test) { | ||||||
|     mu_assert( |     mu_assert( | ||||||
|         subghz_decoder_test( |         subghz_decoder_test( | ||||||
|             EXT_PATH("unit_tests/subghz/magellen_raw.sub"), SUBGHZ_PROTOCOL_MAGELLEN_NAME), |             EXT_PATH("unit_tests/subghz/magellan_raw.sub"), SUBGHZ_PROTOCOL_MAGELLAN_NAME), | ||||||
|         "Test decoder " SUBGHZ_PROTOCOL_MAGELLEN_NAME " error\r\n"); |         "Test decoder " SUBGHZ_PROTOCOL_MAGELLAN_NAME " error\r\n"); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| MU_TEST(subghz_decoder_intertechno_v3_test) { | MU_TEST(subghz_decoder_intertechno_v3_test) { | ||||||
| @ -545,10 +547,10 @@ MU_TEST(subghz_encoder_honeywell_wdb_test) { | |||||||
|         "Test encoder " SUBGHZ_PROTOCOL_HONEYWELL_WDB_NAME " error\r\n"); |         "Test encoder " SUBGHZ_PROTOCOL_HONEYWELL_WDB_NAME " error\r\n"); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| MU_TEST(subghz_encoder_magellen_test) { | MU_TEST(subghz_encoder_magellan_test) { | ||||||
|     mu_assert( |     mu_assert( | ||||||
|         subghz_encoder_test(EXT_PATH("unit_tests/subghz/magellen.sub")), |         subghz_encoder_test(EXT_PATH("unit_tests/subghz/magellan.sub")), | ||||||
|         "Test encoder " SUBGHZ_PROTOCOL_MAGELLEN_NAME " error\r\n"); |         "Test encoder " SUBGHZ_PROTOCOL_MAGELLAN_NAME " error\r\n"); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| MU_TEST(subghz_encoder_intertechno_v3_test) { | MU_TEST(subghz_encoder_intertechno_v3_test) { | ||||||
| @ -600,7 +602,7 @@ MU_TEST_SUITE(subghz) { | |||||||
|     MU_RUN_TEST(subghz_decoder_doitrand_test); |     MU_RUN_TEST(subghz_decoder_doitrand_test); | ||||||
|     MU_RUN_TEST(subghz_decoder_phoenix_v2_test); |     MU_RUN_TEST(subghz_decoder_phoenix_v2_test); | ||||||
|     MU_RUN_TEST(subghz_decoder_honeywell_wdb_test); |     MU_RUN_TEST(subghz_decoder_honeywell_wdb_test); | ||||||
|     MU_RUN_TEST(subghz_decoder_magellen_test); |     MU_RUN_TEST(subghz_decoder_magellan_test); | ||||||
|     MU_RUN_TEST(subghz_decoder_intertechno_v3_test); |     MU_RUN_TEST(subghz_decoder_intertechno_v3_test); | ||||||
|     MU_RUN_TEST(subghz_decoder_clemsa_test); |     MU_RUN_TEST(subghz_decoder_clemsa_test); | ||||||
|     MU_RUN_TEST(subghz_decoder_oregon2_test); |     MU_RUN_TEST(subghz_decoder_oregon2_test); | ||||||
| @ -622,7 +624,7 @@ MU_TEST_SUITE(subghz) { | |||||||
|     MU_RUN_TEST(subghz_encoder_doitrand_test); |     MU_RUN_TEST(subghz_encoder_doitrand_test); | ||||||
|     MU_RUN_TEST(subghz_encoder_phoenix_v2_test); |     MU_RUN_TEST(subghz_encoder_phoenix_v2_test); | ||||||
|     MU_RUN_TEST(subghz_encoder_honeywell_wdb_test); |     MU_RUN_TEST(subghz_encoder_honeywell_wdb_test); | ||||||
|     MU_RUN_TEST(subghz_encoder_magellen_test); |     MU_RUN_TEST(subghz_encoder_magellan_test); | ||||||
|     MU_RUN_TEST(subghz_encoder_intertechno_v3_test); |     MU_RUN_TEST(subghz_encoder_intertechno_v3_test); | ||||||
|     MU_RUN_TEST(subghz_encoder_clemsa_test); |     MU_RUN_TEST(subghz_encoder_clemsa_test); | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -34,7 +34,7 @@ void lfrfid_scene_raw_info_on_enter(void* context) { | |||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     view_dispatcher_switch_to_view(app->view_dispatcher, LfRfidViewWidget); |     view_dispatcher_switch_to_view(app->view_dispatcher, LfRfidViewWidget); | ||||||
|     //string_clear(tmp_string);
 |     //furi_string_free(tmp_string);
 | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| bool lfrfid_scene_raw_info_on_event(void* context, SceneManagerEvent event) { | bool lfrfid_scene_raw_info_on_event(void* context, SceneManagerEvent event) { | ||||||
|  | |||||||
| @ -69,12 +69,3 @@ typedef enum { | |||||||
|     SubGhzViewIdTestCarrier, |     SubGhzViewIdTestCarrier, | ||||||
|     SubGhzViewIdTestPacket, |     SubGhzViewIdTestPacket, | ||||||
| } SubGhzViewId; | } SubGhzViewId; | ||||||
| 
 |  | ||||||
| struct SubGhzPresetDefinition { |  | ||||||
|     FuriString* name; |  | ||||||
|     uint32_t frequency; |  | ||||||
|     uint8_t* data; |  | ||||||
|     size_t data_size; |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| typedef struct SubGhzPresetDefinition SubGhzPresetDefinition; |  | ||||||
|  | |||||||
| @ -28,8 +28,8 @@ static bool subghz_scene_receiver_info_update_parser(void* context) { | |||||||
|             subghz->txrx->decoder_result, |             subghz->txrx->decoder_result, | ||||||
|             subghz_history_get_raw_data(subghz->txrx->history, subghz->txrx->idx_menu_chosen)); |             subghz_history_get_raw_data(subghz->txrx->history, subghz->txrx->idx_menu_chosen)); | ||||||
| 
 | 
 | ||||||
|         SubGhzPresetDefinition* preset = |         SubGhzRadioPreset* preset = | ||||||
|             subghz_history_get_preset_def(subghz->txrx->history, subghz->txrx->idx_menu_chosen); |             subghz_history_get_radio_preset(subghz->txrx->history, subghz->txrx->idx_menu_chosen); | ||||||
|         subghz_preset_init( |         subghz_preset_init( | ||||||
|             subghz, |             subghz, | ||||||
|             furi_string_get_cstr(preset->name), |             furi_string_get_cstr(preset->name), | ||||||
|  | |||||||
| @ -6,7 +6,7 @@ | |||||||
| #include <dolphin/dolphin.h> | #include <dolphin/dolphin.h> | ||||||
| #include <flipper_format/flipper_format_i.h> | #include <flipper_format/flipper_format_i.h> | ||||||
| #include <lib/toolbox/stream/stream.h> | #include <lib/toolbox/stream/stream.h> | ||||||
| #include <lib/subghz/protocols/registry.h> | #include <lib/subghz/protocols/protocol_items.h> | ||||||
| 
 | 
 | ||||||
| #define TAG "SubGhzSetType" | #define TAG "SubGhzSetType" | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -3,6 +3,7 @@ | |||||||
| #include "subghz/types.h" | #include "subghz/types.h" | ||||||
| #include "subghz_i.h" | #include "subghz_i.h" | ||||||
| #include <lib/toolbox/path.h> | #include <lib/toolbox/path.h> | ||||||
|  | #include <lib/subghz/protocols/protocol_items.h> | ||||||
| 
 | 
 | ||||||
| bool subghz_custom_event_callback(void* context, uint32_t event) { | bool subghz_custom_event_callback(void* context, uint32_t event) { | ||||||
|     furi_assert(context); |     furi_assert(context); | ||||||
| @ -169,7 +170,7 @@ SubGhz* subghz_alloc() { | |||||||
|     //init Worker & Protocol & History & KeyBoard
 |     //init Worker & Protocol & History & KeyBoard
 | ||||||
|     subghz->lock = SubGhzLockOff; |     subghz->lock = SubGhzLockOff; | ||||||
|     subghz->txrx = malloc(sizeof(SubGhzTxRx)); |     subghz->txrx = malloc(sizeof(SubGhzTxRx)); | ||||||
|     subghz->txrx->preset = malloc(sizeof(SubGhzPresetDefinition)); |     subghz->txrx->preset = malloc(sizeof(SubGhzRadioPreset)); | ||||||
|     subghz->txrx->preset->name = furi_string_alloc(); |     subghz->txrx->preset->name = furi_string_alloc(); | ||||||
|     subghz_preset_init( |     subghz_preset_init( | ||||||
|         subghz, "AM650", subghz_setting_get_default_frequency(subghz->setting), NULL, 0); |         subghz, "AM650", subghz_setting_get_default_frequency(subghz->setting), NULL, 0); | ||||||
| @ -186,6 +187,8 @@ SubGhz* subghz_alloc() { | |||||||
|         subghz->txrx->environment, EXT_PATH("subghz/assets/came_atomo")); |         subghz->txrx->environment, EXT_PATH("subghz/assets/came_atomo")); | ||||||
|     subghz_environment_set_nice_flor_s_rainbow_table_file_name( |     subghz_environment_set_nice_flor_s_rainbow_table_file_name( | ||||||
|         subghz->txrx->environment, EXT_PATH("subghz/assets/nice_flor_s")); |         subghz->txrx->environment, EXT_PATH("subghz/assets/nice_flor_s")); | ||||||
|  |     subghz_environment_set_protocol_registry( | ||||||
|  |         subghz->txrx->environment, (void*)&subghz_protocol_registry); | ||||||
|     subghz->txrx->receiver = subghz_receiver_alloc_init(subghz->txrx->environment); |     subghz->txrx->receiver = subghz_receiver_alloc_init(subghz->txrx->environment); | ||||||
|     subghz_receiver_set_filter(subghz->txrx->receiver, SubGhzProtocolFlag_Decodable); |     subghz_receiver_set_filter(subghz->txrx->receiver, SubGhzProtocolFlag_Decodable); | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -9,6 +9,7 @@ | |||||||
| #include <lib/subghz/receiver.h> | #include <lib/subghz/receiver.h> | ||||||
| #include <lib/subghz/transmitter.h> | #include <lib/subghz/transmitter.h> | ||||||
| #include <lib/subghz/subghz_file_encoder_worker.h> | #include <lib/subghz/subghz_file_encoder_worker.h> | ||||||
|  | #include <lib/subghz/protocols/protocol_items.h> | ||||||
| 
 | 
 | ||||||
| #include "helpers/subghz_chat.h" | #include "helpers/subghz_chat.h" | ||||||
| 
 | 
 | ||||||
| @ -164,6 +165,7 @@ void subghz_cli_command_tx(Cli* cli, FuriString* args, void* context) { | |||||||
|     stream_write_cstring(stream, furi_string_get_cstr(flipper_format_string)); |     stream_write_cstring(stream, furi_string_get_cstr(flipper_format_string)); | ||||||
| 
 | 
 | ||||||
|     SubGhzEnvironment* environment = subghz_environment_alloc(); |     SubGhzEnvironment* environment = subghz_environment_alloc(); | ||||||
|  |     subghz_environment_set_protocol_registry(environment, (void*)&subghz_protocol_registry); | ||||||
| 
 | 
 | ||||||
|     SubGhzTransmitter* transmitter = subghz_transmitter_alloc_init(environment, "Princeton"); |     SubGhzTransmitter* transmitter = subghz_transmitter_alloc_init(environment, "Princeton"); | ||||||
|     subghz_transmitter_deserialize(transmitter, flipper_format); |     subghz_transmitter_deserialize(transmitter, flipper_format); | ||||||
| @ -257,6 +259,7 @@ void subghz_cli_command_rx(Cli* cli, FuriString* args, void* context) { | |||||||
|         environment, EXT_PATH("subghz/assets/came_atomo")); |         environment, EXT_PATH("subghz/assets/came_atomo")); | ||||||
|     subghz_environment_set_nice_flor_s_rainbow_table_file_name( |     subghz_environment_set_nice_flor_s_rainbow_table_file_name( | ||||||
|         environment, EXT_PATH("subghz/assets/nice_flor_s")); |         environment, EXT_PATH("subghz/assets/nice_flor_s")); | ||||||
|  |     subghz_environment_set_protocol_registry(environment, (void*)&subghz_protocol_registry); | ||||||
| 
 | 
 | ||||||
|     SubGhzReceiver* receiver = subghz_receiver_alloc_init(environment); |     SubGhzReceiver* receiver = subghz_receiver_alloc_init(environment); | ||||||
|     subghz_receiver_set_filter(receiver, SubGhzProtocolFlag_Decodable); |     subghz_receiver_set_filter(receiver, SubGhzProtocolFlag_Decodable); | ||||||
| @ -376,6 +379,7 @@ void subghz_cli_command_decode_raw(Cli* cli, FuriString* args, void* context) { | |||||||
|             environment, EXT_PATH("subghz/assets/came_atomo")); |             environment, EXT_PATH("subghz/assets/came_atomo")); | ||||||
|         subghz_environment_set_nice_flor_s_rainbow_table_file_name( |         subghz_environment_set_nice_flor_s_rainbow_table_file_name( | ||||||
|             environment, EXT_PATH("subghz/assets/nice_flor_s")); |             environment, EXT_PATH("subghz/assets/nice_flor_s")); | ||||||
|  |         subghz_environment_set_protocol_registry(environment, (void*)&subghz_protocol_registry); | ||||||
| 
 | 
 | ||||||
|         SubGhzReceiver* receiver = subghz_receiver_alloc_init(environment); |         SubGhzReceiver* receiver = subghz_receiver_alloc_init(environment); | ||||||
|         subghz_receiver_set_filter(receiver, SubGhzProtocolFlag_Decodable); |         subghz_receiver_set_filter(receiver, SubGhzProtocolFlag_Decodable); | ||||||
|  | |||||||
| @ -11,7 +11,7 @@ typedef struct { | |||||||
|     FuriString* item_str; |     FuriString* item_str; | ||||||
|     FlipperFormat* flipper_string; |     FlipperFormat* flipper_string; | ||||||
|     uint8_t type; |     uint8_t type; | ||||||
|     SubGhzPresetDefinition* preset; |     SubGhzRadioPreset* preset; | ||||||
| } SubGhzHistoryItem; | } SubGhzHistoryItem; | ||||||
| 
 | 
 | ||||||
| ARRAY_DEF(SubGhzHistoryItemArray, SubGhzHistoryItem, M_POD_OPLIST) | ARRAY_DEF(SubGhzHistoryItemArray, SubGhzHistoryItem, M_POD_OPLIST) | ||||||
| @ -60,7 +60,7 @@ uint32_t subghz_history_get_frequency(SubGhzHistory* instance, uint16_t idx) { | |||||||
|     return item->preset->frequency; |     return item->preset->frequency; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| SubGhzPresetDefinition* subghz_history_get_preset_def(SubGhzHistory* instance, uint16_t idx) { | SubGhzRadioPreset* subghz_history_get_radio_preset(SubGhzHistory* instance, uint16_t idx) { | ||||||
|     furi_assert(instance); |     furi_assert(instance); | ||||||
|     SubGhzHistoryItem* item = SubGhzHistoryItemArray_get(instance->history->data, idx); |     SubGhzHistoryItem* item = SubGhzHistoryItemArray_get(instance->history->data, idx); | ||||||
|     return item->preset; |     return item->preset; | ||||||
| @ -138,7 +138,7 @@ void subghz_history_get_text_item_menu(SubGhzHistory* instance, FuriString* outp | |||||||
| bool subghz_history_add_to_history( | bool subghz_history_add_to_history( | ||||||
|     SubGhzHistory* instance, |     SubGhzHistory* instance, | ||||||
|     void* context, |     void* context, | ||||||
|     SubGhzPresetDefinition* preset) { |     SubGhzRadioPreset* preset) { | ||||||
|     furi_assert(instance); |     furi_assert(instance); | ||||||
|     furi_assert(context); |     furi_assert(context); | ||||||
| 
 | 
 | ||||||
| @ -158,7 +158,7 @@ bool subghz_history_add_to_history( | |||||||
|     FuriString* text; |     FuriString* text; | ||||||
|     text = furi_string_alloc(); |     text = furi_string_alloc(); | ||||||
|     SubGhzHistoryItem* item = SubGhzHistoryItemArray_push_raw(instance->history->data); |     SubGhzHistoryItem* item = SubGhzHistoryItemArray_push_raw(instance->history->data); | ||||||
|     item->preset = malloc(sizeof(SubGhzPresetDefinition)); |     item->preset = malloc(sizeof(SubGhzRadioPreset)); | ||||||
|     item->type = decoder_base->protocol->type; |     item->type = decoder_base->protocol->type; | ||||||
|     item->preset->frequency = preset->frequency; |     item->preset->frequency = preset->frequency; | ||||||
|     item->preset->name = furi_string_alloc(); |     item->preset->name = furi_string_alloc(); | ||||||
|  | |||||||
| @ -5,7 +5,7 @@ | |||||||
| #include <furi.h> | #include <furi.h> | ||||||
| #include <furi_hal.h> | #include <furi_hal.h> | ||||||
| #include <lib/flipper_format/flipper_format.h> | #include <lib/flipper_format/flipper_format.h> | ||||||
| #include "helpers/subghz_types.h" | #include <lib/subghz/types.h> | ||||||
| 
 | 
 | ||||||
| typedef struct SubGhzHistory SubGhzHistory; | typedef struct SubGhzHistory SubGhzHistory; | ||||||
| 
 | 
 | ||||||
| @ -35,7 +35,7 @@ void subghz_history_reset(SubGhzHistory* instance); | |||||||
|  */ |  */ | ||||||
| uint32_t subghz_history_get_frequency(SubGhzHistory* instance, uint16_t idx); | uint32_t subghz_history_get_frequency(SubGhzHistory* instance, uint16_t idx); | ||||||
| 
 | 
 | ||||||
| SubGhzPresetDefinition* subghz_history_get_preset_def(SubGhzHistory* instance, uint16_t idx); | SubGhzRadioPreset* subghz_history_get_radio_preset(SubGhzHistory* instance, uint16_t idx); | ||||||
| 
 | 
 | ||||||
| /** Get preset to history[idx]
 | /** Get preset to history[idx]
 | ||||||
|  *  |  *  | ||||||
| @ -88,13 +88,13 @@ bool subghz_history_get_text_space_left(SubGhzHistory* instance, FuriString* out | |||||||
|  *  |  *  | ||||||
|  * @param instance  - SubGhzHistory instance |  * @param instance  - SubGhzHistory instance | ||||||
|  * @param context    - SubGhzProtocolCommon context |  * @param context    - SubGhzProtocolCommon context | ||||||
|  * @param preset    - SubGhzPresetDefinition preset |  * @param preset    - SubGhzRadioPreset preset | ||||||
|  * @return bool; |  * @return bool; | ||||||
|  */ |  */ | ||||||
| bool subghz_history_add_to_history( | bool subghz_history_add_to_history( | ||||||
|     SubGhzHistory* instance, |     SubGhzHistory* instance, | ||||||
|     void* context, |     void* context, | ||||||
|     SubGhzPresetDefinition* preset); |     SubGhzRadioPreset* preset); | ||||||
| 
 | 
 | ||||||
| /** Get SubGhzProtocolCommonLoad to load into the protocol decoder bin data
 | /** Get SubGhzProtocolCommonLoad to load into the protocol decoder bin data
 | ||||||
|  *  |  *  | ||||||
|  | |||||||
| @ -1,6 +1,7 @@ | |||||||
| #pragma once | #pragma once | ||||||
| 
 | 
 | ||||||
| #include "helpers/subghz_types.h" | #include "helpers/subghz_types.h" | ||||||
|  | #include <lib/subghz/types.h> | ||||||
| #include "subghz.h" | #include "subghz.h" | ||||||
| #include "views/receiver.h" | #include "views/receiver.h" | ||||||
| #include "views/transmitter.h" | #include "views/transmitter.h" | ||||||
| @ -11,8 +12,6 @@ | |||||||
| #include "views/subghz_test_carrier.h" | #include "views/subghz_test_carrier.h" | ||||||
| #include "views/subghz_test_packet.h" | #include "views/subghz_test_packet.h" | ||||||
| 
 | 
 | ||||||
| // #include <furi.h>
 |  | ||||||
| // #include <furi_hal.h>
 |  | ||||||
| #include <gui/gui.h> | #include <gui/gui.h> | ||||||
| #include <dialogs/dialogs.h> | #include <dialogs/dialogs.h> | ||||||
| #include <gui/scene_manager.h> | #include <gui/scene_manager.h> | ||||||
| @ -24,14 +23,12 @@ | |||||||
| #include <gui/modules/widget.h> | #include <gui/modules/widget.h> | ||||||
| 
 | 
 | ||||||
| #include <subghz/scenes/subghz_scene.h> | #include <subghz/scenes/subghz_scene.h> | ||||||
| 
 |  | ||||||
| #include <lib/subghz/subghz_worker.h> | #include <lib/subghz/subghz_worker.h> | ||||||
| 
 | #include <lib/subghz/subghz_setting.h> | ||||||
| #include <lib/subghz/receiver.h> | #include <lib/subghz/receiver.h> | ||||||
| #include <lib/subghz/transmitter.h> | #include <lib/subghz/transmitter.h> | ||||||
| 
 | 
 | ||||||
| #include "subghz_history.h" | #include "subghz_history.h" | ||||||
| #include "subghz_setting.h" |  | ||||||
| 
 | 
 | ||||||
| #include <gui/modules/variable_item_list.h> | #include <gui/modules/variable_item_list.h> | ||||||
| #include <lib/toolbox/path.h> | #include <lib/toolbox/path.h> | ||||||
| @ -49,7 +46,7 @@ struct SubGhzTxRx { | |||||||
|     SubGhzProtocolDecoderBase* decoder_result; |     SubGhzProtocolDecoderBase* decoder_result; | ||||||
|     FlipperFormat* fff_data; |     FlipperFormat* fff_data; | ||||||
| 
 | 
 | ||||||
|     SubGhzPresetDefinition* preset; |     SubGhzRadioPreset* preset; | ||||||
|     SubGhzHistory* history; |     SubGhzHistory* history; | ||||||
|     uint16_t idx_menu_chosen; |     uint16_t idx_menu_chosen; | ||||||
|     SubGhzTxRxState txrx_state; |     SubGhzTxRxState txrx_state; | ||||||
|  | |||||||
| @ -192,7 +192,7 @@ void subghz_view_receiver_draw(Canvas* canvas, SubGhzViewReceiverModel* model) { | |||||||
|         } else { |         } else { | ||||||
|             canvas_set_color(canvas, ColorBlack); |             canvas_set_color(canvas, ColorBlack); | ||||||
|         } |         } | ||||||
|         canvas_draw_icon(canvas, 1, 2 + i * FRAME_HEIGHT, ReceiverItemIcons[item_menu->type]); |         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)); |         canvas_draw_str(canvas, 15, 9 + i * FRAME_HEIGHT, furi_string_get_cstr(str_buff)); | ||||||
|         furi_string_reset(str_buff); |         furi_string_reset(str_buff); | ||||||
|     } |     } | ||||||
|  | |||||||
							
								
								
									
										13
									
								
								applications/plugins/weather_station/application.fam
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								applications/plugins/weather_station/application.fam
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,13 @@ | |||||||
|  | App( | ||||||
|  |     appid="weather_station", | ||||||
|  |     name="Weather Station", | ||||||
|  |     apptype=FlipperAppType.PLUGIN, | ||||||
|  |     entry_point="weather_station_app", | ||||||
|  |     cdefines=["APP_WEATHER_STATION"], | ||||||
|  |     requires=["gui"], | ||||||
|  |     stack_size=4 * 1024, | ||||||
|  |     order=50, | ||||||
|  |     fap_icon="weather_station_10px.png", | ||||||
|  |     fap_category="Tools", | ||||||
|  |     fap_icon_assets="images", | ||||||
|  | ) | ||||||
| @ -0,0 +1,14 @@ | |||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | typedef enum { | ||||||
|  |     //WSCustomEvent
 | ||||||
|  |     WSCustomEventStartId = 100, | ||||||
|  | 
 | ||||||
|  |     WSCustomEventSceneSettingLock, | ||||||
|  | 
 | ||||||
|  |     WSCustomEventViewReceiverOK, | ||||||
|  |     WSCustomEventViewReceiverConfig, | ||||||
|  |     WSCustomEventViewReceiverBack, | ||||||
|  |     WSCustomEventViewReceiverOffDisplay, | ||||||
|  |     WSCustomEventViewReceiverUnlock, | ||||||
|  | } WSCustomEvent; | ||||||
| @ -0,0 +1,49 @@ | |||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | #include <furi.h> | ||||||
|  | #include <furi_hal.h> | ||||||
|  | 
 | ||||||
|  | #define WS_VERSION_APP "0.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; | ||||||
							
								
								
									
										
											BIN
										
									
								
								applications/plugins/weather_station/images/Humid_10x15.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								applications/plugins/weather_station/images/Humid_10x15.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 3.5 KiB | 
							
								
								
									
										
											BIN
										
									
								
								applications/plugins/weather_station/images/Therm_7x16.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								applications/plugins/weather_station/images/Therm_7x16.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 3.5 KiB | 
							
								
								
									
										
											BIN
										
									
								
								applications/plugins/weather_station/images/station_icon.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								applications/plugins/weather_station/images/station_icon.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 3.5 KiB | 
							
								
								
									
										341
									
								
								applications/plugins/weather_station/protocols/gt_wt_03.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										341
									
								
								applications/plugins/weather_station/protocols/gt_wt_03.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,341 @@ | |||||||
|  | #include "gt_wt_03.h" | ||||||
|  | 
 | ||||||
|  | #define TAG "WSProtocolGT_WT03" | ||||||
|  | 
 | ||||||
|  | /*
 | ||||||
|  |  * Help | ||||||
|  |  * https://github.com/merbanan/rtl_433/blob/5f0ff6db624270a4598958ab9dd79bb385ced3ef/src/devices/gt_wt_03.c
 | ||||||
|  |  *  | ||||||
|  |  *  | ||||||
|  |  * Globaltronics GT-WT-03 sensor on 433.92MHz. | ||||||
|  |  * The 01-set sensor has 60 ms packet gap with 10 repeats. | ||||||
|  |  * The 02-set sensor has no packet gap with 23 repeats. | ||||||
|  |  * Example: | ||||||
|  |  *     {41} 17 cf be fa 6a 80 [ S1 C1 26,1 C 78.9 F 48% Bat-Good Manual-Yes ] | ||||||
|  |  *     {41} 17 cf be fa 6a 80 [ S1 C1 26,1 C 78.9 F 48% Bat-Good Manual-Yes Batt-Changed ] | ||||||
|  |  *     {41} 17 cf fe fa ea 80 [ S1 C1 26,1 C 78.9 F 48% Bat-Good Manual-No  Batt-Changed ] | ||||||
|  |  *     {41} 01 cf 6f 11 b2 80 [ S2 C2 23,8 C 74.8 F 48% Bat-LOW  Manual-No ] | ||||||
|  |  *     {41} 01 c8 d0 2b 76 80 [ S2 C3 -4,4 C 24.1 F 55% Bat-Good Manual-No  Batt-Changed ] | ||||||
|  |  * Format string: | ||||||
|  |  *     ID:8h HUM:8d B:b M:b C:2d TEMP:12d CHK:8h 1x | ||||||
|  |  * Data layout: | ||||||
|  |  *    TYP IIIIIIII HHHHHHHH BMCCTTTT TTTTTTTT XXXXXXXX | ||||||
|  |  * - I: Random Device Code: changes with battery reset | ||||||
|  |  * - H: Humidity: 8 Bit 00-99, Display LL=10%, Display HH=110% (Range 20-95%) | ||||||
|  |  * - B: Battery: 0=OK 1=LOW | ||||||
|  |  * - M: Manual Send Button Pressed: 0=not pressed, 1=pressed | ||||||
|  |  * - C: Channel: 00=CH1, 01=CH2, 10=CH3 | ||||||
|  |  * - T: Temperature: 12 Bit 2's complement, scaled by 10, range-50.0 C (-50.1 shown as Lo) to +70.0 C (+70.1 C is shown as Hi) | ||||||
|  |  * - X: Checksum, xor shifting key per byte | ||||||
|  |  * Humidity: | ||||||
|  |  * - the working range is 20-95 % | ||||||
|  |  * - if "LL" in display view it sends 10 % | ||||||
|  |  * - if "HH" in display view it sends 110% | ||||||
|  |  * Checksum: | ||||||
|  |  * Per byte xor the key for each 1-bit, shift per bit. Key list per bit, starting at MSB: | ||||||
|  |  * - 0x00 [07] | ||||||
|  |  * - 0x80 [06] | ||||||
|  |  * - 0x40 [05] | ||||||
|  |  * - 0x20 [04] | ||||||
|  |  * - 0x10 [03] | ||||||
|  |  * - 0x88 [02] | ||||||
|  |  * - 0xc4 [01] | ||||||
|  |  * - 0x62 [00] | ||||||
|  |  * Note: this can also be seen as lower byte of a Galois/Fibonacci LFSR-16, gen 0x00, init 0x3100 (or 0x62 if reversed) resetting at every byte. | ||||||
|  |  * Battery voltages: | ||||||
|  |  * - U=<2,65V +- ~5% Battery indicator | ||||||
|  |  * - U=>2.10C +- 5% plausible readings | ||||||
|  |  * - U=2,00V +- ~5% Temperature offset -5°C Humidity offset unknown | ||||||
|  |  * - U=<1,95V +- ~5% does not initialize anymore | ||||||
|  |  * - U=1,90V +- 5% temperature offset -15°C | ||||||
|  |  * - U=1,80V +- 5% Display is showing refresh pattern | ||||||
|  |  * - U=1.75V +- ~5% TX causes cut out | ||||||
|  |  *  | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | static const SubGhzBlockConst ws_protocol_gt_wt_03_const = { | ||||||
|  |     .te_short = 285, | ||||||
|  |     .te_long = 570, | ||||||
|  |     .te_delta = 120, | ||||||
|  |     .min_count_bit_for_found = 41, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | struct WSProtocolDecoderGT_WT03 { | ||||||
|  |     SubGhzProtocolDecoderBase base; | ||||||
|  | 
 | ||||||
|  |     SubGhzBlockDecoder decoder; | ||||||
|  |     WSBlockGeneric generic; | ||||||
|  | 
 | ||||||
|  |     uint16_t header_count; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | struct WSProtocolEncoderGT_WT03 { | ||||||
|  |     SubGhzProtocolEncoderBase base; | ||||||
|  | 
 | ||||||
|  |     SubGhzProtocolBlockEncoder encoder; | ||||||
|  |     WSBlockGeneric generic; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | typedef enum { | ||||||
|  |     GT_WT03DecoderStepReset = 0, | ||||||
|  |     GT_WT03DecoderStepCheckPreambule, | ||||||
|  |     GT_WT03DecoderStepSaveDuration, | ||||||
|  |     GT_WT03DecoderStepCheckDuration, | ||||||
|  | } GT_WT03DecoderStep; | ||||||
|  | 
 | ||||||
|  | const SubGhzProtocolDecoder ws_protocol_gt_wt_03_decoder = { | ||||||
|  |     .alloc = ws_protocol_decoder_gt_wt_03_alloc, | ||||||
|  |     .free = ws_protocol_decoder_gt_wt_03_free, | ||||||
|  | 
 | ||||||
|  |     .feed = ws_protocol_decoder_gt_wt_03_feed, | ||||||
|  |     .reset = ws_protocol_decoder_gt_wt_03_reset, | ||||||
|  | 
 | ||||||
|  |     .get_hash_data = ws_protocol_decoder_gt_wt_03_get_hash_data, | ||||||
|  |     .serialize = ws_protocol_decoder_gt_wt_03_serialize, | ||||||
|  |     .deserialize = ws_protocol_decoder_gt_wt_03_deserialize, | ||||||
|  |     .get_string = ws_protocol_decoder_gt_wt_03_get_string, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | const SubGhzProtocolEncoder ws_protocol_gt_wt_03_encoder = { | ||||||
|  |     .alloc = NULL, | ||||||
|  |     .free = NULL, | ||||||
|  | 
 | ||||||
|  |     .deserialize = NULL, | ||||||
|  |     .stop = NULL, | ||||||
|  |     .yield = NULL, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | 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, | ||||||
|  | 
 | ||||||
|  |     .decoder = &ws_protocol_gt_wt_03_decoder, | ||||||
|  |     .encoder = &ws_protocol_gt_wt_03_encoder, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | void* ws_protocol_decoder_gt_wt_03_alloc(SubGhzEnvironment* environment) { | ||||||
|  |     UNUSED(environment); | ||||||
|  |     WSProtocolDecoderGT_WT03* instance = malloc(sizeof(WSProtocolDecoderGT_WT03)); | ||||||
|  |     instance->base.protocol = &ws_protocol_gt_wt_03; | ||||||
|  |     instance->generic.protocol_name = instance->base.protocol->name; | ||||||
|  |     return instance; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void ws_protocol_decoder_gt_wt_03_free(void* context) { | ||||||
|  |     furi_assert(context); | ||||||
|  |     WSProtocolDecoderGT_WT03* instance = context; | ||||||
|  |     free(instance); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void ws_protocol_decoder_gt_wt_03_reset(void* context) { | ||||||
|  |     furi_assert(context); | ||||||
|  |     WSProtocolDecoderGT_WT03* instance = context; | ||||||
|  |     instance->decoder.parser_step = GT_WT03DecoderStepReset; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static bool ws_protocol_gt_wt_03_check_crc(WSProtocolDecoderGT_WT03* instance) { | ||||||
|  |     uint8_t msg[] = { | ||||||
|  |         instance->decoder.decode_data >> 33, | ||||||
|  |         instance->decoder.decode_data >> 25, | ||||||
|  |         instance->decoder.decode_data >> 17, | ||||||
|  |         instance->decoder.decode_data >> 9}; | ||||||
|  | 
 | ||||||
|  |     uint8_t sum = 0; | ||||||
|  |     for(unsigned k = 0; k < sizeof(msg); ++k) { | ||||||
|  |         uint8_t data = msg[k]; | ||||||
|  |         uint16_t key = 0x3100; | ||||||
|  |         for(int i = 7; i >= 0; --i) { | ||||||
|  |             // XOR key into sum if data bit is set
 | ||||||
|  |             if((data >> i) & 1) sum ^= key & 0xff; | ||||||
|  |             // roll the key right
 | ||||||
|  |             key = (key >> 1); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     return ((sum ^ (uint8_t)((instance->decoder.decode_data >> 1) & 0xFF)) == 0x2D); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Analysis of received data | ||||||
|  |  * @param instance Pointer to a WSBlockGeneric* instance | ||||||
|  |  */ | ||||||
|  | static void ws_protocol_gt_wt_03_remote_controller(WSBlockGeneric* instance) { | ||||||
|  |     instance->id = instance->data >> 33; | ||||||
|  |     instance->humidity = (instance->data >> 25) & 0xFF; | ||||||
|  | 
 | ||||||
|  |     if(instance->humidity <= 10) { // actually the sensors sends 10 below working range of 20%
 | ||||||
|  |         instance->humidity = 0; | ||||||
|  |     } else if(instance->humidity > 95) { // actually the sensors sends 110 above working range of 90%
 | ||||||
|  |         instance->humidity = 100; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     instance->battery_low = (instance->data >> 24) & 1; | ||||||
|  |     instance->btn = (instance->data >> 23) & 1; | ||||||
|  |     instance->channel = ((instance->data >> 21) & 0x03) + 1; | ||||||
|  | 
 | ||||||
|  |     if(!((instance->data >> 20) & 1)) { | ||||||
|  |         instance->temp = (float)((instance->data >> 9) & 0x07FF) / 10.0f; | ||||||
|  |     } else { | ||||||
|  |         instance->temp = (float)((~(instance->data >> 9) & 0x07FF) + 1) / -10.0f; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void ws_protocol_decoder_gt_wt_03_feed(void* context, bool level, uint32_t duration) { | ||||||
|  |     furi_assert(context); | ||||||
|  |     WSProtocolDecoderGT_WT03* instance = context; | ||||||
|  | 
 | ||||||
|  |     switch(instance->decoder.parser_step) { | ||||||
|  |     case GT_WT03DecoderStepReset: | ||||||
|  |         if((level) && (DURATION_DIFF(duration, ws_protocol_gt_wt_03_const.te_short * 3) < | ||||||
|  |                        ws_protocol_gt_wt_03_const.te_delta * 2)) { | ||||||
|  |             instance->decoder.parser_step = GT_WT03DecoderStepCheckPreambule; | ||||||
|  |             instance->decoder.te_last = duration; | ||||||
|  |             instance->header_count = 0; | ||||||
|  |         } | ||||||
|  |         break; | ||||||
|  | 
 | ||||||
|  |     case GT_WT03DecoderStepCheckPreambule: | ||||||
|  |         if(level) { | ||||||
|  |             instance->decoder.te_last = duration; | ||||||
|  |         } else { | ||||||
|  |             if((DURATION_DIFF(instance->decoder.te_last, ws_protocol_gt_wt_03_const.te_short * 3) < | ||||||
|  |                 ws_protocol_gt_wt_03_const.te_delta * 2) && | ||||||
|  |                (DURATION_DIFF(duration, ws_protocol_gt_wt_03_const.te_short * 3) < | ||||||
|  |                 ws_protocol_gt_wt_03_const.te_delta * 2)) { | ||||||
|  |                 //Found preambule
 | ||||||
|  |                 instance->header_count++; | ||||||
|  |             } else if(instance->header_count == 4) { | ||||||
|  |                 if((DURATION_DIFF(instance->decoder.te_last, ws_protocol_gt_wt_03_const.te_short) < | ||||||
|  |                     ws_protocol_gt_wt_03_const.te_delta) && | ||||||
|  |                    (DURATION_DIFF(duration, ws_protocol_gt_wt_03_const.te_long) < | ||||||
|  |                     ws_protocol_gt_wt_03_const.te_delta)) { | ||||||
|  |                     instance->decoder.decode_data = 0; | ||||||
|  |                     instance->decoder.decode_count_bit = 0; | ||||||
|  |                     subghz_protocol_blocks_add_bit(&instance->decoder, 0); | ||||||
|  |                     instance->decoder.parser_step = GT_WT03DecoderStepSaveDuration; | ||||||
|  |                 } else if( | ||||||
|  |                     (DURATION_DIFF(instance->decoder.te_last, ws_protocol_gt_wt_03_const.te_long) < | ||||||
|  |                      ws_protocol_gt_wt_03_const.te_delta) && | ||||||
|  |                     (DURATION_DIFF(duration, ws_protocol_gt_wt_03_const.te_short) < | ||||||
|  |                      ws_protocol_gt_wt_03_const.te_delta)) { | ||||||
|  |                     instance->decoder.decode_data = 0; | ||||||
|  |                     instance->decoder.decode_count_bit = 0; | ||||||
|  |                     subghz_protocol_blocks_add_bit(&instance->decoder, 1); | ||||||
|  |                     instance->decoder.parser_step = GT_WT03DecoderStepSaveDuration; | ||||||
|  |                 } else { | ||||||
|  |                     instance->decoder.parser_step = GT_WT03DecoderStepReset; | ||||||
|  |                 } | ||||||
|  |             } else { | ||||||
|  |                 instance->decoder.parser_step = GT_WT03DecoderStepReset; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         break; | ||||||
|  | 
 | ||||||
|  |     case GT_WT03DecoderStepSaveDuration: | ||||||
|  |         if(level) { | ||||||
|  |             instance->decoder.te_last = duration; | ||||||
|  |             instance->decoder.parser_step = GT_WT03DecoderStepCheckDuration; | ||||||
|  |         } else { | ||||||
|  |             instance->decoder.parser_step = GT_WT03DecoderStepReset; | ||||||
|  |         } | ||||||
|  |         break; | ||||||
|  | 
 | ||||||
|  |     case GT_WT03DecoderStepCheckDuration: | ||||||
|  |         if(!level) { | ||||||
|  |             if(((DURATION_DIFF(instance->decoder.te_last, ws_protocol_gt_wt_03_const.te_short * 3) < | ||||||
|  |                  ws_protocol_gt_wt_03_const.te_delta * 2) && | ||||||
|  |                 (DURATION_DIFF(duration, ws_protocol_gt_wt_03_const.te_short * 3) < | ||||||
|  |                  ws_protocol_gt_wt_03_const.te_delta * 2))) { | ||||||
|  |                 if((instance->decoder.decode_count_bit == | ||||||
|  |                     ws_protocol_gt_wt_03_const.min_count_bit_for_found) && | ||||||
|  |                    ws_protocol_gt_wt_03_check_crc(instance)) { | ||||||
|  |                     instance->generic.data = instance->decoder.decode_data; | ||||||
|  |                     instance->generic.data_count_bit = instance->decoder.decode_count_bit; | ||||||
|  |                     ws_protocol_gt_wt_03_remote_controller(&instance->generic); | ||||||
|  |                     if(instance->base.callback) | ||||||
|  |                         instance->base.callback(&instance->base, instance->base.context); | ||||||
|  |                 } | ||||||
|  |                 instance->decoder.decode_data = 0; | ||||||
|  |                 instance->decoder.decode_count_bit = 0; | ||||||
|  |                 instance->header_count = 1; | ||||||
|  |                 instance->decoder.parser_step = GT_WT03DecoderStepCheckPreambule; | ||||||
|  |                 break; | ||||||
|  |             } else if( | ||||||
|  |                 (DURATION_DIFF(instance->decoder.te_last, ws_protocol_gt_wt_03_const.te_short) < | ||||||
|  |                  ws_protocol_gt_wt_03_const.te_delta) && | ||||||
|  |                 (DURATION_DIFF(duration, ws_protocol_gt_wt_03_const.te_long) < | ||||||
|  |                  ws_protocol_gt_wt_03_const.te_delta)) { | ||||||
|  |                 subghz_protocol_blocks_add_bit(&instance->decoder, 0); | ||||||
|  |                 instance->decoder.parser_step = GT_WT03DecoderStepSaveDuration; | ||||||
|  |             } else if( | ||||||
|  |                 (DURATION_DIFF(instance->decoder.te_last, ws_protocol_gt_wt_03_const.te_long) < | ||||||
|  |                  ws_protocol_gt_wt_03_const.te_delta) && | ||||||
|  |                 (DURATION_DIFF(duration, ws_protocol_gt_wt_03_const.te_short) < | ||||||
|  |                  ws_protocol_gt_wt_03_const.te_delta)) { | ||||||
|  |                 subghz_protocol_blocks_add_bit(&instance->decoder, 1); | ||||||
|  |                 instance->decoder.parser_step = GT_WT03DecoderStepSaveDuration; | ||||||
|  |             } else { | ||||||
|  |                 instance->decoder.parser_step = GT_WT03DecoderStepReset; | ||||||
|  |             } | ||||||
|  |         } else { | ||||||
|  |             instance->decoder.parser_step = GT_WT03DecoderStepReset; | ||||||
|  |         } | ||||||
|  |         break; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | uint8_t ws_protocol_decoder_gt_wt_03_get_hash_data(void* context) { | ||||||
|  |     furi_assert(context); | ||||||
|  |     WSProtocolDecoderGT_WT03* instance = context; | ||||||
|  |     return subghz_protocol_blocks_get_hash_data( | ||||||
|  |         &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool ws_protocol_decoder_gt_wt_03_serialize( | ||||||
|  |     void* context, | ||||||
|  |     FlipperFormat* flipper_format, | ||||||
|  |     SubGhzRadioPreset* preset) { | ||||||
|  |     furi_assert(context); | ||||||
|  |     WSProtocolDecoderGT_WT03* instance = context; | ||||||
|  |     return ws_block_generic_serialize(&instance->generic, flipper_format, preset); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool ws_protocol_decoder_gt_wt_03_deserialize(void* context, FlipperFormat* flipper_format) { | ||||||
|  |     furi_assert(context); | ||||||
|  |     WSProtocolDecoderGT_WT03* instance = context; | ||||||
|  |     bool ret = false; | ||||||
|  |     do { | ||||||
|  |         if(!ws_block_generic_deserialize(&instance->generic, flipper_format)) { | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  |         if(instance->generic.data_count_bit != | ||||||
|  |            ws_protocol_gt_wt_03_const.min_count_bit_for_found) { | ||||||
|  |             FURI_LOG_E(TAG, "Wrong number of bits in key"); | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  |         ret = true; | ||||||
|  |     } while(false); | ||||||
|  |     return ret; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void ws_protocol_decoder_gt_wt_03_get_string(void* context, FuriString* output) { | ||||||
|  |     furi_assert(context); | ||||||
|  |     WSProtocolDecoderGT_WT03* instance = context; | ||||||
|  |     furi_string_printf( | ||||||
|  |         output, | ||||||
|  |         "%s %dbit\r\n" | ||||||
|  |         "Key:0x%lX%08lX\r\n" | ||||||
|  |         "Sn:0x%lX Ch:%d  Bat:%d\r\n" | ||||||
|  |         "Temp:%d.%d C Hum:%d%%", | ||||||
|  |         instance->generic.protocol_name, | ||||||
|  |         instance->generic.data_count_bit, | ||||||
|  |         (uint32_t)(instance->generic.data >> 32), | ||||||
|  |         (uint32_t)(instance->generic.data), | ||||||
|  |         instance->generic.id, | ||||||
|  |         instance->generic.channel, | ||||||
|  |         instance->generic.battery_low, | ||||||
|  |         (int16_t)instance->generic.temp, | ||||||
|  |         abs(((int16_t)(instance->generic.temp * 10) - (((int16_t)instance->generic.temp) * 10))), | ||||||
|  |         instance->generic.humidity); | ||||||
|  | } | ||||||
							
								
								
									
										79
									
								
								applications/plugins/weather_station/protocols/gt_wt_03.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										79
									
								
								applications/plugins/weather_station/protocols/gt_wt_03.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,79 @@ | |||||||
|  | #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 "ws_generic.h" | ||||||
|  | #include <lib/subghz/blocks/math.h> | ||||||
|  | 
 | ||||||
|  | #define WS_PROTOCOL_GT_WT_03_NAME "GT-WT03" | ||||||
|  | 
 | ||||||
|  | typedef struct WSProtocolDecoderGT_WT03 WSProtocolDecoderGT_WT03; | ||||||
|  | typedef struct WSProtocolEncoderGT_WT03 WSProtocolEncoderGT_WT03; | ||||||
|  | 
 | ||||||
|  | extern const SubGhzProtocolDecoder ws_protocol_gt_wt_03_decoder; | ||||||
|  | extern const SubGhzProtocolEncoder ws_protocol_gt_wt_03_encoder; | ||||||
|  | extern const SubGhzProtocol ws_protocol_gt_wt_03; | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Allocate WSProtocolDecoderGT_WT03. | ||||||
|  |  * @param environment Pointer to a SubGhzEnvironment instance | ||||||
|  |  * @return WSProtocolDecoderGT_WT03* pointer to a WSProtocolDecoderGT_WT03 instance | ||||||
|  |  */ | ||||||
|  | void* ws_protocol_decoder_gt_wt_03_alloc(SubGhzEnvironment* environment); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Free WSProtocolDecoderGT_WT03. | ||||||
|  |  * @param context Pointer to a WSProtocolDecoderGT_WT03 instance | ||||||
|  |  */ | ||||||
|  | void ws_protocol_decoder_gt_wt_03_free(void* context); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Reset decoder WSProtocolDecoderGT_WT03. | ||||||
|  |  * @param context Pointer to a WSProtocolDecoderGT_WT03 instance | ||||||
|  |  */ | ||||||
|  | void ws_protocol_decoder_gt_wt_03_reset(void* context); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Parse a raw sequence of levels and durations received from the air. | ||||||
|  |  * @param context Pointer to a WSProtocolDecoderGT_WT03 instance | ||||||
|  |  * @param level Signal level true-high false-low | ||||||
|  |  * @param duration Duration of this level in, us | ||||||
|  |  */ | ||||||
|  | void ws_protocol_decoder_gt_wt_03_feed(void* context, bool level, uint32_t duration); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Getting the hash sum of the last randomly received parcel. | ||||||
|  |  * @param context Pointer to a WSProtocolDecoderGT_WT03 instance | ||||||
|  |  * @return hash Hash sum | ||||||
|  |  */ | ||||||
|  | uint8_t ws_protocol_decoder_gt_wt_03_get_hash_data(void* context); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Serialize data WSProtocolDecoderGT_WT03. | ||||||
|  |  * @param context Pointer to a WSProtocolDecoderGT_WT03 instance | ||||||
|  |  * @param flipper_format Pointer to a FlipperFormat instance | ||||||
|  |  * @param preset The modulation on which the signal was received, SubGhzRadioPreset | ||||||
|  |  * @return true On success | ||||||
|  |  */ | ||||||
|  | bool ws_protocol_decoder_gt_wt_03_serialize( | ||||||
|  |     void* context, | ||||||
|  |     FlipperFormat* flipper_format, | ||||||
|  |     SubGhzRadioPreset* preset); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Deserialize data WSProtocolDecoderGT_WT03. | ||||||
|  |  * @param context Pointer to a WSProtocolDecoderGT_WT03 instance | ||||||
|  |  * @param flipper_format Pointer to a FlipperFormat instance | ||||||
|  |  * @return true On success | ||||||
|  |  */ | ||||||
|  | bool ws_protocol_decoder_gt_wt_03_deserialize(void* context, FlipperFormat* flipper_format); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Getting a textual representation of the received data. | ||||||
|  |  * @param context Pointer to a WSProtocolDecoderGT_WT03 instance | ||||||
|  |  * @param output Resulting text | ||||||
|  |  */ | ||||||
|  | void ws_protocol_decoder_gt_wt_03_get_string(void* context, FuriString* output); | ||||||
							
								
								
									
										296
									
								
								applications/plugins/weather_station/protocols/infactory.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										296
									
								
								applications/plugins/weather_station/protocols/infactory.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,296 @@ | |||||||
|  | #include "infactory.h" | ||||||
|  | 
 | ||||||
|  | #define TAG "WSProtocolInfactory" | ||||||
|  | 
 | ||||||
|  | /*
 | ||||||
|  |  * Help | ||||||
|  |  * https://github.com/merbanan/rtl_433/blob/master/src/devices/infactory.c
 | ||||||
|  |  * | ||||||
|  |  * Analysis using Genuino (see http://gitlab.com/hp-uno, e.g. uno_log_433):
 | ||||||
|  |  * Observed On-Off-Key (OOK) data pattern: | ||||||
|  |  *     preamble            syncPrefix        data...(40 bit)                        syncPostfix | ||||||
|  |  *     HHLL HHLL HHLL HHLL HLLLLLLLLLLLLLLLL (HLLLL HLLLLLLLL HLLLL HLLLLLLLL ....) HLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL | ||||||
|  |  * Breakdown: | ||||||
|  |  * - four preamble pairs '1'/'0' each with a length of ca. 1000us | ||||||
|  |  * - syncPre, syncPost, data0, data1 have a '1' start pulse of ca. 500us | ||||||
|  |  * - syncPre pulse before dataPtr has a '0' pulse length of ca. 8000us | ||||||
|  |  * - data0 (0-bits) have then a '0' pulse length of ca. 2000us | ||||||
|  |  * - data1 (1-bits) have then a '0' pulse length of ca. 4000us | ||||||
|  |  * - syncPost after dataPtr has a '0' pulse length of ca. 16000us | ||||||
|  |  * This analysis is the reason for the new r_device definitions below. | ||||||
|  |  * NB: pulse_slicer_ppm does not use .gap_limit if .tolerance is set. | ||||||
|  |  *  | ||||||
|  |  * Outdoor sensor, transmits temperature and humidity data | ||||||
|  |  * - inFactory NC-3982-913/NX-5817-902, Pearl (for FWS-686 station) | ||||||
|  |  * - nor-tec 73383 (weather station + sensor), Schou Company AS, Denmark | ||||||
|  |  * - DAY 73365 (weather station + sensor), Schou Company AS, Denmark | ||||||
|  |  * Known brand names: inFactory, nor-tec, GreenBlue, DAY. Manufacturer in China. | ||||||
|  |  * Transmissions includes an id. Every 60 seconds the sensor transmits 6 packets: | ||||||
|  |  *     0000 1111 | 0011 0000 | 0101 1100 | 1110 0111 | 0110 0001 | ||||||
|  |  *     iiii iiii | cccc ub?? | tttt tttt | tttt hhhh | hhhh ??nn | ||||||
|  |  * - i: identification; changes on battery switch | ||||||
|  |  * - c: CRC-4; CCITT checksum, see below for computation specifics | ||||||
|  |  * - u: unknown; (sometimes set at power-on, but not always) | ||||||
|  |  * - b: battery low; flag to indicate low battery voltage | ||||||
|  |  * - h: Humidity; BCD-encoded, each nibble is one digit, 'A0' means 100%rH | ||||||
|  |  * - t: Temperature; in °F as binary number with one decimal place + 90 °F offset | ||||||
|  |  * - n: Channel; Channel number 1 - 3 | ||||||
|  |  *  | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | static const SubGhzBlockConst ws_protocol_infactory_const = { | ||||||
|  |     .te_short = 500, | ||||||
|  |     .te_long = 2000, | ||||||
|  |     .te_delta = 150, | ||||||
|  |     .min_count_bit_for_found = 40, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | struct WSProtocolDecoderInfactory { | ||||||
|  |     SubGhzProtocolDecoderBase base; | ||||||
|  | 
 | ||||||
|  |     SubGhzBlockDecoder decoder; | ||||||
|  |     WSBlockGeneric generic; | ||||||
|  | 
 | ||||||
|  |     uint16_t header_count; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | struct WSProtocolEncoderInfactory { | ||||||
|  |     SubGhzProtocolEncoderBase base; | ||||||
|  | 
 | ||||||
|  |     SubGhzProtocolBlockEncoder encoder; | ||||||
|  |     WSBlockGeneric generic; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | typedef enum { | ||||||
|  |     InfactoryDecoderStepReset = 0, | ||||||
|  |     InfactoryDecoderStepCheckPreambule, | ||||||
|  |     InfactoryDecoderStepSaveDuration, | ||||||
|  |     InfactoryDecoderStepCheckDuration, | ||||||
|  | } InfactoryDecoderStep; | ||||||
|  | 
 | ||||||
|  | const SubGhzProtocolDecoder ws_protocol_infactory_decoder = { | ||||||
|  |     .alloc = ws_protocol_decoder_infactory_alloc, | ||||||
|  |     .free = ws_protocol_decoder_infactory_free, | ||||||
|  | 
 | ||||||
|  |     .feed = ws_protocol_decoder_infactory_feed, | ||||||
|  |     .reset = ws_protocol_decoder_infactory_reset, | ||||||
|  | 
 | ||||||
|  |     .get_hash_data = ws_protocol_decoder_infactory_get_hash_data, | ||||||
|  |     .serialize = ws_protocol_decoder_infactory_serialize, | ||||||
|  |     .deserialize = ws_protocol_decoder_infactory_deserialize, | ||||||
|  |     .get_string = ws_protocol_decoder_infactory_get_string, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | const SubGhzProtocolEncoder ws_protocol_infactory_encoder = { | ||||||
|  |     .alloc = NULL, | ||||||
|  |     .free = NULL, | ||||||
|  | 
 | ||||||
|  |     .deserialize = NULL, | ||||||
|  |     .stop = NULL, | ||||||
|  |     .yield = NULL, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | const SubGhzProtocol ws_protocol_infactory = { | ||||||
|  |     .name = WS_PROTOCOL_INFACTORY_NAME, | ||||||
|  |     .type = SubGhzProtocolWeatherStation, | ||||||
|  |     .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_315 | SubGhzProtocolFlag_868 | | ||||||
|  |             SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable, | ||||||
|  | 
 | ||||||
|  |     .decoder = &ws_protocol_infactory_decoder, | ||||||
|  |     .encoder = &ws_protocol_infactory_encoder, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | void* ws_protocol_decoder_infactory_alloc(SubGhzEnvironment* environment) { | ||||||
|  |     UNUSED(environment); | ||||||
|  |     WSProtocolDecoderInfactory* instance = malloc(sizeof(WSProtocolDecoderInfactory)); | ||||||
|  |     instance->base.protocol = &ws_protocol_infactory; | ||||||
|  |     instance->generic.protocol_name = instance->base.protocol->name; | ||||||
|  |     return instance; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void ws_protocol_decoder_infactory_free(void* context) { | ||||||
|  |     furi_assert(context); | ||||||
|  |     WSProtocolDecoderInfactory* instance = context; | ||||||
|  |     free(instance); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void ws_protocol_decoder_infactory_reset(void* context) { | ||||||
|  |     furi_assert(context); | ||||||
|  |     WSProtocolDecoderInfactory* instance = context; | ||||||
|  |     instance->decoder.parser_step = InfactoryDecoderStepReset; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static bool ws_protocol_infactory_check_crc(WSProtocolDecoderInfactory* instance) { | ||||||
|  |     uint8_t msg[] = { | ||||||
|  |         instance->decoder.decode_data >> 32, | ||||||
|  |         (((instance->decoder.decode_data >> 24) & 0x0F) | (instance->decoder.decode_data & 0x0F) | ||||||
|  |                                                               << 4), | ||||||
|  |         instance->decoder.decode_data >> 16, | ||||||
|  |         instance->decoder.decode_data >> 8, | ||||||
|  |         instance->decoder.decode_data}; | ||||||
|  | 
 | ||||||
|  |     uint8_t crc = | ||||||
|  |         subghz_protocol_blocks_crc4(msg, 4, 0x13, 0); // Koopmann 0x9, CCITT-4; FP-4; ITU-T G.704
 | ||||||
|  |     crc ^= msg[4] >> 4; // last nibble is only XORed
 | ||||||
|  |     return (crc == ((instance->decoder.decode_data >> 28) & 0x0F)); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Analysis of received data | ||||||
|  |  * @param instance Pointer to a WSBlockGeneric* instance | ||||||
|  |  */ | ||||||
|  | static void ws_protocol_infactory_remote_controller(WSBlockGeneric* instance) { | ||||||
|  |     instance->id = instance->data >> 32; | ||||||
|  |     instance->battery_low = (instance->data >> 26) & 1; | ||||||
|  |     instance->temp = ws_block_generic_fahrenheit_to_celsius( | ||||||
|  |         ((float)((instance->data >> 12) & 0x0FFF) - 900.0f) / 10.0f); | ||||||
|  |     instance->humidity = | ||||||
|  |         (((instance->data >> 8) & 0x0F) * 10) + ((instance->data >> 4) & 0x0F); // BCD, 'A0'=100%rH
 | ||||||
|  |     instance->channel = instance->data & 0x03; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void ws_protocol_decoder_infactory_feed(void* context, bool level, uint32_t duration) { | ||||||
|  |     furi_assert(context); | ||||||
|  |     WSProtocolDecoderInfactory* instance = context; | ||||||
|  | 
 | ||||||
|  |     switch(instance->decoder.parser_step) { | ||||||
|  |     case InfactoryDecoderStepReset: | ||||||
|  |         if((level) && (DURATION_DIFF(duration, ws_protocol_infactory_const.te_short * 2) < | ||||||
|  |                        ws_protocol_infactory_const.te_delta * 2)) { | ||||||
|  |             instance->decoder.parser_step = InfactoryDecoderStepCheckPreambule; | ||||||
|  |             instance->decoder.te_last = duration; | ||||||
|  |             instance->header_count = 0; | ||||||
|  |         } | ||||||
|  |         break; | ||||||
|  | 
 | ||||||
|  |     case InfactoryDecoderStepCheckPreambule: | ||||||
|  |         if(level) { | ||||||
|  |             instance->decoder.te_last = duration; | ||||||
|  |         } else { | ||||||
|  |             if((DURATION_DIFF(instance->decoder.te_last, ws_protocol_infactory_const.te_short * 2) < | ||||||
|  |                 ws_protocol_infactory_const.te_delta * 2) && | ||||||
|  |                (DURATION_DIFF(duration, ws_protocol_infactory_const.te_short * 2) < | ||||||
|  |                 ws_protocol_infactory_const.te_delta * 2)) { | ||||||
|  |                 //Found preambule
 | ||||||
|  |                 instance->header_count++; | ||||||
|  |             } else if( | ||||||
|  |                 (DURATION_DIFF(instance->decoder.te_last, ws_protocol_infactory_const.te_short) < | ||||||
|  |                  ws_protocol_infactory_const.te_delta) && | ||||||
|  |                 (DURATION_DIFF(duration, ws_protocol_infactory_const.te_short * 16) < | ||||||
|  |                  ws_protocol_infactory_const.te_delta * 8)) { | ||||||
|  |                 //Found syncPrefix
 | ||||||
|  |                 if(instance->header_count > 3) { | ||||||
|  |                     instance->decoder.parser_step = InfactoryDecoderStepSaveDuration; | ||||||
|  |                     instance->decoder.decode_data = 0; | ||||||
|  |                     instance->decoder.decode_count_bit = 0; | ||||||
|  |                 } | ||||||
|  |             } else { | ||||||
|  |                 instance->decoder.parser_step = InfactoryDecoderStepReset; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         break; | ||||||
|  | 
 | ||||||
|  |     case InfactoryDecoderStepSaveDuration: | ||||||
|  |         if(level) { | ||||||
|  |             instance->decoder.te_last = duration; | ||||||
|  |             instance->decoder.parser_step = InfactoryDecoderStepCheckDuration; | ||||||
|  |         } else { | ||||||
|  |             instance->decoder.parser_step = InfactoryDecoderStepReset; | ||||||
|  |         } | ||||||
|  |         break; | ||||||
|  | 
 | ||||||
|  |     case InfactoryDecoderStepCheckDuration: | ||||||
|  |         if(!level) { | ||||||
|  |             if(duration >= ((uint32_t)ws_protocol_infactory_const.te_short * 30)) { | ||||||
|  |                 //Found syncPostfix
 | ||||||
|  |                 if((instance->decoder.decode_count_bit == | ||||||
|  |                     ws_protocol_infactory_const.min_count_bit_for_found) && | ||||||
|  |                    ws_protocol_infactory_check_crc(instance)) { | ||||||
|  |                     instance->generic.data = instance->decoder.decode_data; | ||||||
|  |                     instance->generic.data_count_bit = instance->decoder.decode_count_bit; | ||||||
|  |                     ws_protocol_infactory_remote_controller(&instance->generic); | ||||||
|  |                     if(instance->base.callback) | ||||||
|  |                         instance->base.callback(&instance->base, instance->base.context); | ||||||
|  |                 } | ||||||
|  |                 instance->decoder.decode_data = 0; | ||||||
|  |                 instance->decoder.decode_count_bit = 0; | ||||||
|  |                 instance->decoder.parser_step = InfactoryDecoderStepReset; | ||||||
|  |                 break; | ||||||
|  |             } else if( | ||||||
|  |                 (DURATION_DIFF(instance->decoder.te_last, ws_protocol_infactory_const.te_short) < | ||||||
|  |                  ws_protocol_infactory_const.te_delta) && | ||||||
|  |                 (DURATION_DIFF(duration, ws_protocol_infactory_const.te_long) < | ||||||
|  |                  ws_protocol_infactory_const.te_delta * 2)) { | ||||||
|  |                 subghz_protocol_blocks_add_bit(&instance->decoder, 0); | ||||||
|  |                 instance->decoder.parser_step = InfactoryDecoderStepSaveDuration; | ||||||
|  |             } else if( | ||||||
|  |                 (DURATION_DIFF(instance->decoder.te_last, ws_protocol_infactory_const.te_short) < | ||||||
|  |                  ws_protocol_infactory_const.te_delta) && | ||||||
|  |                 (DURATION_DIFF(duration, ws_protocol_infactory_const.te_long * 2) < | ||||||
|  |                  ws_protocol_infactory_const.te_delta * 4)) { | ||||||
|  |                 subghz_protocol_blocks_add_bit(&instance->decoder, 1); | ||||||
|  |                 instance->decoder.parser_step = InfactoryDecoderStepSaveDuration; | ||||||
|  |             } else { | ||||||
|  |                 instance->decoder.parser_step = InfactoryDecoderStepReset; | ||||||
|  |             } | ||||||
|  |         } else { | ||||||
|  |             instance->decoder.parser_step = InfactoryDecoderStepReset; | ||||||
|  |         } | ||||||
|  |         break; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | uint8_t ws_protocol_decoder_infactory_get_hash_data(void* context) { | ||||||
|  |     furi_assert(context); | ||||||
|  |     WSProtocolDecoderInfactory* instance = context; | ||||||
|  |     return subghz_protocol_blocks_get_hash_data( | ||||||
|  |         &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool ws_protocol_decoder_infactory_serialize( | ||||||
|  |     void* context, | ||||||
|  |     FlipperFormat* flipper_format, | ||||||
|  |     SubGhzRadioPreset* preset) { | ||||||
|  |     furi_assert(context); | ||||||
|  |     WSProtocolDecoderInfactory* instance = context; | ||||||
|  |     return ws_block_generic_serialize(&instance->generic, flipper_format, preset); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool ws_protocol_decoder_infactory_deserialize(void* context, FlipperFormat* flipper_format) { | ||||||
|  |     furi_assert(context); | ||||||
|  |     WSProtocolDecoderInfactory* instance = context; | ||||||
|  |     bool ret = false; | ||||||
|  |     do { | ||||||
|  |         if(!ws_block_generic_deserialize(&instance->generic, flipper_format)) { | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  |         if(instance->generic.data_count_bit != | ||||||
|  |            ws_protocol_infactory_const.min_count_bit_for_found) { | ||||||
|  |             FURI_LOG_E(TAG, "Wrong number of bits in key"); | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  |         ret = true; | ||||||
|  |     } while(false); | ||||||
|  |     return ret; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void ws_protocol_decoder_infactory_get_string(void* context, FuriString* output) { | ||||||
|  |     furi_assert(context); | ||||||
|  |     WSProtocolDecoderInfactory* instance = context; | ||||||
|  |     furi_string_printf( | ||||||
|  |         output, | ||||||
|  |         "%s %dbit\r\n" | ||||||
|  |         "Key:0x%lX%08lX\r\n" | ||||||
|  |         "Sn:0x%lX Ch:%d  Bat:%d\r\n" | ||||||
|  |         "Temp:%d.%d C Hum:%d%%", | ||||||
|  |         instance->generic.protocol_name, | ||||||
|  |         instance->generic.data_count_bit, | ||||||
|  |         (uint32_t)(instance->generic.data >> 32), | ||||||
|  |         (uint32_t)(instance->generic.data), | ||||||
|  |         instance->generic.id, | ||||||
|  |         instance->generic.channel, | ||||||
|  |         instance->generic.battery_low, | ||||||
|  |         (int16_t)instance->generic.temp, | ||||||
|  |         abs(((int16_t)(instance->generic.temp * 10) - (((int16_t)instance->generic.temp) * 10))), | ||||||
|  |         instance->generic.humidity); | ||||||
|  | } | ||||||
							
								
								
									
										79
									
								
								applications/plugins/weather_station/protocols/infactory.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										79
									
								
								applications/plugins/weather_station/protocols/infactory.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,79 @@ | |||||||
|  | #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 "ws_generic.h" | ||||||
|  | #include <lib/subghz/blocks/math.h> | ||||||
|  | 
 | ||||||
|  | #define WS_PROTOCOL_INFACTORY_NAME "inFactory-TH" | ||||||
|  | 
 | ||||||
|  | typedef struct WSProtocolDecoderInfactory WSProtocolDecoderInfactory; | ||||||
|  | typedef struct WSProtocolEncoderInfactory WSProtocolEncoderInfactory; | ||||||
|  | 
 | ||||||
|  | extern const SubGhzProtocolDecoder ws_protocol_infactory_decoder; | ||||||
|  | extern const SubGhzProtocolEncoder ws_protocol_infactory_encoder; | ||||||
|  | extern const SubGhzProtocol ws_protocol_infactory; | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Allocate WSProtocolDecoderInfactory. | ||||||
|  |  * @param environment Pointer to a SubGhzEnvironment instance | ||||||
|  |  * @return WSProtocolDecoderInfactory* pointer to a WSProtocolDecoderInfactory instance | ||||||
|  |  */ | ||||||
|  | void* ws_protocol_decoder_infactory_alloc(SubGhzEnvironment* environment); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Free WSProtocolDecoderInfactory. | ||||||
|  |  * @param context Pointer to a WSProtocolDecoderInfactory instance | ||||||
|  |  */ | ||||||
|  | void ws_protocol_decoder_infactory_free(void* context); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Reset decoder WSProtocolDecoderInfactory. | ||||||
|  |  * @param context Pointer to a WSProtocolDecoderInfactory instance | ||||||
|  |  */ | ||||||
|  | void ws_protocol_decoder_infactory_reset(void* context); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Parse a raw sequence of levels and durations received from the air. | ||||||
|  |  * @param context Pointer to a WSProtocolDecoderInfactory instance | ||||||
|  |  * @param level Signal level true-high false-low | ||||||
|  |  * @param duration Duration of this level in, us | ||||||
|  |  */ | ||||||
|  | void ws_protocol_decoder_infactory_feed(void* context, bool level, uint32_t duration); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Getting the hash sum of the last randomly received parcel. | ||||||
|  |  * @param context Pointer to a WSProtocolDecoderInfactory instance | ||||||
|  |  * @return hash Hash sum | ||||||
|  |  */ | ||||||
|  | uint8_t ws_protocol_decoder_infactory_get_hash_data(void* context); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Serialize data WSProtocolDecoderInfactory. | ||||||
|  |  * @param context Pointer to a WSProtocolDecoderInfactory instance | ||||||
|  |  * @param flipper_format Pointer to a FlipperFormat instance | ||||||
|  |  * @param preset The modulation on which the signal was received, SubGhzRadioPreset | ||||||
|  |  * @return true On success | ||||||
|  |  */ | ||||||
|  | bool ws_protocol_decoder_infactory_serialize( | ||||||
|  |     void* context, | ||||||
|  |     FlipperFormat* flipper_format, | ||||||
|  |     SubGhzRadioPreset* preset); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Deserialize data WSProtocolDecoderInfactory. | ||||||
|  |  * @param context Pointer to a WSProtocolDecoderInfactory instance | ||||||
|  |  * @param flipper_format Pointer to a FlipperFormat instance | ||||||
|  |  * @return true On success | ||||||
|  |  */ | ||||||
|  | bool ws_protocol_decoder_infactory_deserialize(void* context, FlipperFormat* flipper_format); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Getting a textual representation of the received data. | ||||||
|  |  * @param context Pointer to a WSProtocolDecoderInfactory instance | ||||||
|  |  * @param output Resulting text | ||||||
|  |  */ | ||||||
|  | void ws_protocol_decoder_infactory_get_string(void* context, FuriString* output); | ||||||
							
								
								
									
										261
									
								
								applications/plugins/weather_station/protocols/nexus_th.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										261
									
								
								applications/plugins/weather_station/protocols/nexus_th.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,261 @@ | |||||||
|  | #include "nexus_th.h" | ||||||
|  | 
 | ||||||
|  | #define TAG "WSProtocolNexus_TH" | ||||||
|  | 
 | ||||||
|  | /*
 | ||||||
|  |  * Help | ||||||
|  |  * https://github.com/merbanan/rtl_433/blob/ef2d37cf51e3264d11cde9149ef87de2f0a4d37a/src/devices/nexus.c
 | ||||||
|  |  * | ||||||
|  |  * Nexus sensor protocol with ID, temperature and optional humidity | ||||||
|  |  * also FreeTec (Pearl) NC-7345 sensors for FreeTec Weatherstation NC-7344, | ||||||
|  |  * also infactory/FreeTec (Pearl) NX-3980 sensors for infactory/FreeTec NX-3974 station, | ||||||
|  |  * also Solight TE82S sensors for Solight TE76/TE82/TE83/TE84 stations, | ||||||
|  |  * also TFA 30.3209.02 temperature/humidity sensor. | ||||||
|  |  * The sensor sends 36 bits 12 times, | ||||||
|  |  * the packets are ppm modulated (distance coding) with a pulse of ~500 us | ||||||
|  |  * followed by a short gap of ~1000 us for a 0 bit or a long ~2000 us gap for a | ||||||
|  |  * 1 bit, the sync gap is ~4000 us. | ||||||
|  |  * The data is grouped in 9 nibbles: | ||||||
|  |  *     [id0] [id1] [flags] [temp0] [temp1] [temp2] [const] [humi0] [humi1] | ||||||
|  |  * - The 8-bit id changes when the battery is changed in the sensor. | ||||||
|  |  * - flags are 4 bits B 0 C C, where B is the battery status: 1=OK, 0=LOW | ||||||
|  |  * - and CC is the channel: 0=CH1, 1=CH2, 2=CH3 | ||||||
|  |  * - temp is 12 bit signed scaled by 10 | ||||||
|  |  * - const is always 1111 (0x0F) | ||||||
|  |  * - humidity is 8 bits | ||||||
|  |  * The sensors can be bought at Clas Ohlsen (Nexus) and Pearl (infactory/FreeTec). | ||||||
|  |  *  | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | #define NEXUS_TH_CONST_DATA 0b1111 | ||||||
|  | 
 | ||||||
|  | static const SubGhzBlockConst ws_protocol_nexus_th_const = { | ||||||
|  |     .te_short = 500, | ||||||
|  |     .te_long = 2000, | ||||||
|  |     .te_delta = 150, | ||||||
|  |     .min_count_bit_for_found = 36, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | struct WSProtocolDecoderNexus_TH { | ||||||
|  |     SubGhzProtocolDecoderBase base; | ||||||
|  | 
 | ||||||
|  |     SubGhzBlockDecoder decoder; | ||||||
|  |     WSBlockGeneric generic; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | struct WSProtocolEncoderNexus_TH { | ||||||
|  |     SubGhzProtocolEncoderBase base; | ||||||
|  | 
 | ||||||
|  |     SubGhzProtocolBlockEncoder encoder; | ||||||
|  |     WSBlockGeneric generic; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | typedef enum { | ||||||
|  |     Nexus_THDecoderStepReset = 0, | ||||||
|  |     Nexus_THDecoderStepSaveDuration, | ||||||
|  |     Nexus_THDecoderStepCheckDuration, | ||||||
|  | } Nexus_THDecoderStep; | ||||||
|  | 
 | ||||||
|  | const SubGhzProtocolDecoder ws_protocol_nexus_th_decoder = { | ||||||
|  |     .alloc = ws_protocol_decoder_nexus_th_alloc, | ||||||
|  |     .free = ws_protocol_decoder_nexus_th_free, | ||||||
|  | 
 | ||||||
|  |     .feed = ws_protocol_decoder_nexus_th_feed, | ||||||
|  |     .reset = ws_protocol_decoder_nexus_th_reset, | ||||||
|  | 
 | ||||||
|  |     .get_hash_data = ws_protocol_decoder_nexus_th_get_hash_data, | ||||||
|  |     .serialize = ws_protocol_decoder_nexus_th_serialize, | ||||||
|  |     .deserialize = ws_protocol_decoder_nexus_th_deserialize, | ||||||
|  |     .get_string = ws_protocol_decoder_nexus_th_get_string, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | const SubGhzProtocolEncoder ws_protocol_nexus_th_encoder = { | ||||||
|  |     .alloc = NULL, | ||||||
|  |     .free = NULL, | ||||||
|  | 
 | ||||||
|  |     .deserialize = NULL, | ||||||
|  |     .stop = NULL, | ||||||
|  |     .yield = NULL, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | 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, | ||||||
|  | 
 | ||||||
|  |     .decoder = &ws_protocol_nexus_th_decoder, | ||||||
|  |     .encoder = &ws_protocol_nexus_th_encoder, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | void* ws_protocol_decoder_nexus_th_alloc(SubGhzEnvironment* environment) { | ||||||
|  |     UNUSED(environment); | ||||||
|  |     WSProtocolDecoderNexus_TH* instance = malloc(sizeof(WSProtocolDecoderNexus_TH)); | ||||||
|  |     instance->base.protocol = &ws_protocol_nexus_th; | ||||||
|  |     instance->generic.protocol_name = instance->base.protocol->name; | ||||||
|  |     return instance; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void ws_protocol_decoder_nexus_th_free(void* context) { | ||||||
|  |     furi_assert(context); | ||||||
|  |     WSProtocolDecoderNexus_TH* instance = context; | ||||||
|  |     free(instance); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void ws_protocol_decoder_nexus_th_reset(void* context) { | ||||||
|  |     furi_assert(context); | ||||||
|  |     WSProtocolDecoderNexus_TH* instance = context; | ||||||
|  |     instance->decoder.parser_step = Nexus_THDecoderStepReset; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static bool ws_protocol_nexus_th_check(WSProtocolDecoderNexus_TH* instance) { | ||||||
|  |     uint8_t type = (instance->decoder.decode_data >> 8) & 0x0F; | ||||||
|  | 
 | ||||||
|  |     if((type == NEXUS_TH_CONST_DATA) && ((instance->decoder.decode_data >> 4) != 0xffffffff)) { | ||||||
|  |         return true; | ||||||
|  |     } else { | ||||||
|  |         return false; | ||||||
|  |     } | ||||||
|  |     return true; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Analysis of received data | ||||||
|  |  * @param instance Pointer to a WSBlockGeneric* instance | ||||||
|  |  */ | ||||||
|  | static void ws_protocol_nexus_th_remote_controller(WSBlockGeneric* instance) { | ||||||
|  |     instance->id = (instance->data >> 28) & 0xFF; | ||||||
|  |     instance->battery_low = !((instance->data >> 27) & 1); | ||||||
|  |     instance->channel = ((instance->data >> 24) & 0x03) + 1; | ||||||
|  | 
 | ||||||
|  |     if(!((instance->data >> 23) & 1)) { | ||||||
|  |         instance->temp = (float)((instance->data >> 12) & 0x07FF) / 10.0f; | ||||||
|  |     } else { | ||||||
|  |         instance->temp = (float)((~(instance->data >> 12) & 0x07FF) + 1) / -10.0f; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     instance->humidity = instance->data & 0xFF; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void ws_protocol_decoder_nexus_th_feed(void* context, bool level, uint32_t duration) { | ||||||
|  |     furi_assert(context); | ||||||
|  |     WSProtocolDecoderNexus_TH* instance = context; | ||||||
|  | 
 | ||||||
|  |     switch(instance->decoder.parser_step) { | ||||||
|  |     case Nexus_THDecoderStepReset: | ||||||
|  |         if((!level) && (DURATION_DIFF(duration, ws_protocol_nexus_th_const.te_short * 8) < | ||||||
|  |                         ws_protocol_nexus_th_const.te_delta * 4)) { | ||||||
|  |             //Found sync
 | ||||||
|  |             instance->decoder.parser_step = Nexus_THDecoderStepSaveDuration; | ||||||
|  |             instance->decoder.decode_data = 0; | ||||||
|  |             instance->decoder.decode_count_bit = 0; | ||||||
|  |         } | ||||||
|  |         break; | ||||||
|  | 
 | ||||||
|  |     case Nexus_THDecoderStepSaveDuration: | ||||||
|  |         if(level) { | ||||||
|  |             instance->decoder.te_last = duration; | ||||||
|  |             instance->decoder.parser_step = Nexus_THDecoderStepCheckDuration; | ||||||
|  |         } else { | ||||||
|  |             instance->decoder.parser_step = Nexus_THDecoderStepReset; | ||||||
|  |         } | ||||||
|  |         break; | ||||||
|  | 
 | ||||||
|  |     case Nexus_THDecoderStepCheckDuration: | ||||||
|  |         if(!level) { | ||||||
|  |             if(DURATION_DIFF(duration, ws_protocol_nexus_th_const.te_short * 8) < | ||||||
|  |                ws_protocol_nexus_th_const.te_delta * 4) { | ||||||
|  |                 //Found sync
 | ||||||
|  |                 instance->decoder.parser_step = Nexus_THDecoderStepReset; | ||||||
|  |                 if((instance->decoder.decode_count_bit == | ||||||
|  |                     ws_protocol_nexus_th_const.min_count_bit_for_found) && | ||||||
|  |                    ws_protocol_nexus_th_check(instance)) { | ||||||
|  |                     instance->generic.data = instance->decoder.decode_data; | ||||||
|  |                     instance->generic.data_count_bit = instance->decoder.decode_count_bit; | ||||||
|  |                     ws_protocol_nexus_th_remote_controller(&instance->generic); | ||||||
|  |                     if(instance->base.callback) | ||||||
|  |                         instance->base.callback(&instance->base, instance->base.context); | ||||||
|  |                     instance->decoder.parser_step = Nexus_THDecoderStepCheckDuration; | ||||||
|  |                 } | ||||||
|  |                 instance->decoder.decode_data = 0; | ||||||
|  |                 instance->decoder.decode_count_bit = 0; | ||||||
|  | 
 | ||||||
|  |                 break; | ||||||
|  |             } else if( | ||||||
|  |                 (DURATION_DIFF(instance->decoder.te_last, ws_protocol_nexus_th_const.te_short) < | ||||||
|  |                  ws_protocol_nexus_th_const.te_delta) && | ||||||
|  |                 (DURATION_DIFF(duration, ws_protocol_nexus_th_const.te_short * 2) < | ||||||
|  |                  ws_protocol_nexus_th_const.te_delta * 2)) { | ||||||
|  |                 subghz_protocol_blocks_add_bit(&instance->decoder, 0); | ||||||
|  |                 instance->decoder.parser_step = Nexus_THDecoderStepSaveDuration; | ||||||
|  |             } else if( | ||||||
|  |                 (DURATION_DIFF(instance->decoder.te_last, ws_protocol_nexus_th_const.te_short) < | ||||||
|  |                  ws_protocol_nexus_th_const.te_delta) && | ||||||
|  |                 (DURATION_DIFF(duration, ws_protocol_nexus_th_const.te_short * 4) < | ||||||
|  |                  ws_protocol_nexus_th_const.te_delta * 4)) { | ||||||
|  |                 subghz_protocol_blocks_add_bit(&instance->decoder, 1); | ||||||
|  |                 instance->decoder.parser_step = Nexus_THDecoderStepSaveDuration; | ||||||
|  |             } else { | ||||||
|  |                 instance->decoder.parser_step = Nexus_THDecoderStepReset; | ||||||
|  |             } | ||||||
|  |         } else { | ||||||
|  |             instance->decoder.parser_step = Nexus_THDecoderStepReset; | ||||||
|  |         } | ||||||
|  |         break; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | uint8_t ws_protocol_decoder_nexus_th_get_hash_data(void* context) { | ||||||
|  |     furi_assert(context); | ||||||
|  |     WSProtocolDecoderNexus_TH* instance = context; | ||||||
|  |     return subghz_protocol_blocks_get_hash_data( | ||||||
|  |         &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool ws_protocol_decoder_nexus_th_serialize( | ||||||
|  |     void* context, | ||||||
|  |     FlipperFormat* flipper_format, | ||||||
|  |     SubGhzRadioPreset* preset) { | ||||||
|  |     furi_assert(context); | ||||||
|  |     WSProtocolDecoderNexus_TH* instance = context; | ||||||
|  |     return ws_block_generic_serialize(&instance->generic, flipper_format, preset); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool ws_protocol_decoder_nexus_th_deserialize(void* context, FlipperFormat* flipper_format) { | ||||||
|  |     furi_assert(context); | ||||||
|  |     WSProtocolDecoderNexus_TH* instance = context; | ||||||
|  |     bool ret = false; | ||||||
|  |     do { | ||||||
|  |         if(!ws_block_generic_deserialize(&instance->generic, flipper_format)) { | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  |         if(instance->generic.data_count_bit != | ||||||
|  |            ws_protocol_nexus_th_const.min_count_bit_for_found) { | ||||||
|  |             FURI_LOG_E(TAG, "Wrong number of bits in key"); | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  |         ret = true; | ||||||
|  |     } while(false); | ||||||
|  |     return ret; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void ws_protocol_decoder_nexus_th_get_string(void* context, FuriString* output) { | ||||||
|  |     furi_assert(context); | ||||||
|  |     WSProtocolDecoderNexus_TH* instance = context; | ||||||
|  |     furi_string_printf( | ||||||
|  |         output, | ||||||
|  |         "%s %dbit\r\n" | ||||||
|  |         "Key:0x%lX%08lX\r\n" | ||||||
|  |         "Sn:0x%lX Ch:%d  Bat:%d\r\n" | ||||||
|  |         "Temp:%d.%d C Hum:%d%%", | ||||||
|  |         instance->generic.protocol_name, | ||||||
|  |         instance->generic.data_count_bit, | ||||||
|  |         (uint32_t)(instance->generic.data >> 32), | ||||||
|  |         (uint32_t)(instance->generic.data), | ||||||
|  |         instance->generic.id, | ||||||
|  |         instance->generic.channel, | ||||||
|  |         instance->generic.battery_low, | ||||||
|  |         (int16_t)instance->generic.temp, | ||||||
|  |         abs(((int16_t)(instance->generic.temp * 10) - (((int16_t)instance->generic.temp) * 10))), | ||||||
|  |         instance->generic.humidity); | ||||||
|  | } | ||||||
							
								
								
									
										79
									
								
								applications/plugins/weather_station/protocols/nexus_th.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										79
									
								
								applications/plugins/weather_station/protocols/nexus_th.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,79 @@ | |||||||
|  | #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 "ws_generic.h" | ||||||
|  | #include <lib/subghz/blocks/math.h> | ||||||
|  | 
 | ||||||
|  | #define WS_PROTOCOL_NEXUS_TH_NAME "Nexus-TH" | ||||||
|  | 
 | ||||||
|  | typedef struct WSProtocolDecoderNexus_TH WSProtocolDecoderNexus_TH; | ||||||
|  | typedef struct WSProtocolEncoderNexus_TH WSProtocolEncoderNexus_TH; | ||||||
|  | 
 | ||||||
|  | extern const SubGhzProtocolDecoder ws_protocol_nexus_th_decoder; | ||||||
|  | extern const SubGhzProtocolEncoder ws_protocol_nexus_th_encoder; | ||||||
|  | extern const SubGhzProtocol ws_protocol_nexus_th; | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Allocate WSProtocolDecoderNexus_TH. | ||||||
|  |  * @param environment Pointer to a SubGhzEnvironment instance | ||||||
|  |  * @return WSProtocolDecoderNexus_TH* pointer to a WSProtocolDecoderNexus_TH instance | ||||||
|  |  */ | ||||||
|  | void* ws_protocol_decoder_nexus_th_alloc(SubGhzEnvironment* environment); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Free WSProtocolDecoderNexus_TH. | ||||||
|  |  * @param context Pointer to a WSProtocolDecoderNexus_TH instance | ||||||
|  |  */ | ||||||
|  | void ws_protocol_decoder_nexus_th_free(void* context); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Reset decoder WSProtocolDecoderNexus_TH. | ||||||
|  |  * @param context Pointer to a WSProtocolDecoderNexus_TH instance | ||||||
|  |  */ | ||||||
|  | void ws_protocol_decoder_nexus_th_reset(void* context); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Parse a raw sequence of levels and durations received from the air. | ||||||
|  |  * @param context Pointer to a WSProtocolDecoderNexus_TH instance | ||||||
|  |  * @param level Signal level true-high false-low | ||||||
|  |  * @param duration Duration of this level in, us | ||||||
|  |  */ | ||||||
|  | void ws_protocol_decoder_nexus_th_feed(void* context, bool level, uint32_t duration); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Getting the hash sum of the last randomly received parcel. | ||||||
|  |  * @param context Pointer to a WSProtocolDecoderNexus_TH instance | ||||||
|  |  * @return hash Hash sum | ||||||
|  |  */ | ||||||
|  | uint8_t ws_protocol_decoder_nexus_th_get_hash_data(void* context); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Serialize data WSProtocolDecoderNexus_TH. | ||||||
|  |  * @param context Pointer to a WSProtocolDecoderNexus_TH instance | ||||||
|  |  * @param flipper_format Pointer to a FlipperFormat instance | ||||||
|  |  * @param preset The modulation on which the signal was received, SubGhzRadioPreset | ||||||
|  |  * @return true On success | ||||||
|  |  */ | ||||||
|  | bool ws_protocol_decoder_nexus_th_serialize( | ||||||
|  |     void* context, | ||||||
|  |     FlipperFormat* flipper_format, | ||||||
|  |     SubGhzRadioPreset* preset); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Deserialize data WSProtocolDecoderNexus_TH. | ||||||
|  |  * @param context Pointer to a WSProtocolDecoderNexus_TH instance | ||||||
|  |  * @param flipper_format Pointer to a FlipperFormat instance | ||||||
|  |  * @return true On success | ||||||
|  |  */ | ||||||
|  | bool ws_protocol_decoder_nexus_th_deserialize(void* context, FlipperFormat* flipper_format); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Getting a textual representation of the received data. | ||||||
|  |  * @param context Pointer to a WSProtocolDecoderNexus_TH instance | ||||||
|  |  * @param output Resulting text | ||||||
|  |  */ | ||||||
|  | void ws_protocol_decoder_nexus_th_get_string(void* context, FuriString* output); | ||||||
| @ -0,0 +1,12 @@ | |||||||
|  | #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_03, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | const SubGhzProtocolRegistry weather_station_protocol_registry = { | ||||||
|  |     .items = weather_station_protocol_registry_items, | ||||||
|  |     .size = COUNT_OF(weather_station_protocol_registry_items)}; | ||||||
| @ -0,0 +1,9 @@ | |||||||
|  | #pragma once | ||||||
|  | #include "../weather_station_app_i.h" | ||||||
|  | 
 | ||||||
|  | #include "infactory.h" | ||||||
|  | #include "thermopro_tx4.h" | ||||||
|  | #include "nexus_th.h" | ||||||
|  | #include "gt_wt_03.h" | ||||||
|  | 
 | ||||||
|  | extern const SubGhzProtocolRegistry weather_station_protocol_registry; | ||||||
							
								
								
									
										260
									
								
								applications/plugins/weather_station/protocols/thermopro_tx4.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										260
									
								
								applications/plugins/weather_station/protocols/thermopro_tx4.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,260 @@ | |||||||
|  | #include "thermopro_tx4.h" | ||||||
|  | 
 | ||||||
|  | #define TAG "WSProtocolThermoPRO_TX4" | ||||||
|  | 
 | ||||||
|  | /*
 | ||||||
|  |  * Help | ||||||
|  |  * https://github.com/merbanan/rtl_433/blob/master/src/devices/thermopro_tx2.c
 | ||||||
|  |  * | ||||||
|  |   * The sensor sends 37 bits 6 times, before the first packet there is a sync pulse. | ||||||
|  |  * The packets are ppm modulated (distance coding) with a pulse of ~500 us | ||||||
|  |  * followed by a short gap of ~2000 us for a 0 bit or a long ~4000 us gap for a | ||||||
|  |  * 1 bit, the sync gap is ~9000 us. | ||||||
|  |  * The data is grouped in 9 nibbles | ||||||
|  |  *     [type] [id0] [id1] [flags] [temp0] [temp1] [temp2] [humi0] [humi1] | ||||||
|  |  * - type: 4 bit fixed 1001 (9) or 0110 (5) | ||||||
|  |  * - id: 8 bit a random id that is generated when the sensor starts, could include battery status | ||||||
|  |  *   the same batteries often generate the same id | ||||||
|  |  * - flags(3): is 1 when the battery is low, otherwise 0 (ok) | ||||||
|  |  * - flags(2): is 1 when the sensor sends a reading when pressing the button on the sensor | ||||||
|  |  * - flags(1,0): the channel number that can be set by the sensor (1, 2, 3, X) | ||||||
|  |  * - temp: 12 bit signed scaled by 10 | ||||||
|  |  * - humi: 8 bit always 11001100 (0xCC) if no humidity sensor is available | ||||||
|  |  *  | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | #define THERMO_PRO_TX4_TYPE_1 0b1001 | ||||||
|  | #define THERMO_PRO_TX4_TYPE_2 0b0110 | ||||||
|  | 
 | ||||||
|  | static const SubGhzBlockConst ws_protocol_thermopro_tx4_const = { | ||||||
|  |     .te_short = 500, | ||||||
|  |     .te_long = 2000, | ||||||
|  |     .te_delta = 150, | ||||||
|  |     .min_count_bit_for_found = 37, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | struct WSProtocolDecoderThermoPRO_TX4 { | ||||||
|  |     SubGhzProtocolDecoderBase base; | ||||||
|  | 
 | ||||||
|  |     SubGhzBlockDecoder decoder; | ||||||
|  |     WSBlockGeneric generic; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | struct WSProtocolEncoderThermoPRO_TX4 { | ||||||
|  |     SubGhzProtocolEncoderBase base; | ||||||
|  | 
 | ||||||
|  |     SubGhzProtocolBlockEncoder encoder; | ||||||
|  |     WSBlockGeneric generic; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | typedef enum { | ||||||
|  |     ThermoPRO_TX4DecoderStepReset = 0, | ||||||
|  |     ThermoPRO_TX4DecoderStepSaveDuration, | ||||||
|  |     ThermoPRO_TX4DecoderStepCheckDuration, | ||||||
|  | } ThermoPRO_TX4DecoderStep; | ||||||
|  | 
 | ||||||
|  | const SubGhzProtocolDecoder ws_protocol_thermopro_tx4_decoder = { | ||||||
|  |     .alloc = ws_protocol_decoder_thermopro_tx4_alloc, | ||||||
|  |     .free = ws_protocol_decoder_thermopro_tx4_free, | ||||||
|  | 
 | ||||||
|  |     .feed = ws_protocol_decoder_thermopro_tx4_feed, | ||||||
|  |     .reset = ws_protocol_decoder_thermopro_tx4_reset, | ||||||
|  | 
 | ||||||
|  |     .get_hash_data = ws_protocol_decoder_thermopro_tx4_get_hash_data, | ||||||
|  |     .serialize = ws_protocol_decoder_thermopro_tx4_serialize, | ||||||
|  |     .deserialize = ws_protocol_decoder_thermopro_tx4_deserialize, | ||||||
|  |     .get_string = ws_protocol_decoder_thermopro_tx4_get_string, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | const SubGhzProtocolEncoder ws_protocol_thermopro_tx4_encoder = { | ||||||
|  |     .alloc = NULL, | ||||||
|  |     .free = NULL, | ||||||
|  | 
 | ||||||
|  |     .deserialize = NULL, | ||||||
|  |     .stop = NULL, | ||||||
|  |     .yield = NULL, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | 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, | ||||||
|  | 
 | ||||||
|  |     .decoder = &ws_protocol_thermopro_tx4_decoder, | ||||||
|  |     .encoder = &ws_protocol_thermopro_tx4_encoder, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | void* ws_protocol_decoder_thermopro_tx4_alloc(SubGhzEnvironment* environment) { | ||||||
|  |     UNUSED(environment); | ||||||
|  |     WSProtocolDecoderThermoPRO_TX4* instance = malloc(sizeof(WSProtocolDecoderThermoPRO_TX4)); | ||||||
|  |     instance->base.protocol = &ws_protocol_thermopro_tx4; | ||||||
|  |     instance->generic.protocol_name = instance->base.protocol->name; | ||||||
|  |     return instance; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void ws_protocol_decoder_thermopro_tx4_free(void* context) { | ||||||
|  |     furi_assert(context); | ||||||
|  |     WSProtocolDecoderThermoPRO_TX4* instance = context; | ||||||
|  |     free(instance); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void ws_protocol_decoder_thermopro_tx4_reset(void* context) { | ||||||
|  |     furi_assert(context); | ||||||
|  |     WSProtocolDecoderThermoPRO_TX4* instance = context; | ||||||
|  |     instance->decoder.parser_step = ThermoPRO_TX4DecoderStepReset; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static bool ws_protocol_thermopro_tx4_check(WSProtocolDecoderThermoPRO_TX4* instance) { | ||||||
|  |     uint8_t type = instance->decoder.decode_data >> 33; | ||||||
|  | 
 | ||||||
|  |     if((type == THERMO_PRO_TX4_TYPE_1) || (type == THERMO_PRO_TX4_TYPE_2)) { | ||||||
|  |         return true; | ||||||
|  |     } else { | ||||||
|  |         return false; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Analysis of received data | ||||||
|  |  * @param instance Pointer to a WSBlockGeneric* instance | ||||||
|  |  */ | ||||||
|  | static void ws_protocol_thermopro_tx4_remote_controller(WSBlockGeneric* instance) { | ||||||
|  |     instance->id = (instance->data >> 25) & 0xFF; | ||||||
|  |     instance->battery_low = (instance->data >> 24) & 1; | ||||||
|  |     instance->btn = (instance->data >> 23) & 1; | ||||||
|  |     instance->channel = ((instance->data >> 21) & 0x03) + 1; | ||||||
|  | 
 | ||||||
|  |     if(!((instance->data >> 20) & 1)) { | ||||||
|  |         instance->temp = (float)((instance->data >> 9) & 0x07FF) / 10.0f; | ||||||
|  |     } else { | ||||||
|  |         instance->temp = (float)((~(instance->data >> 9) & 0x07FF) + 1) / -10.0f; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     instance->humidity = (instance->data >> 1) & 0xFF; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void ws_protocol_decoder_thermopro_tx4_feed(void* context, bool level, uint32_t duration) { | ||||||
|  |     furi_assert(context); | ||||||
|  |     WSProtocolDecoderThermoPRO_TX4* instance = context; | ||||||
|  | 
 | ||||||
|  |     switch(instance->decoder.parser_step) { | ||||||
|  |     case ThermoPRO_TX4DecoderStepReset: | ||||||
|  |         if((!level) && (DURATION_DIFF(duration, ws_protocol_thermopro_tx4_const.te_short * 18) < | ||||||
|  |                         ws_protocol_thermopro_tx4_const.te_delta * 10)) { | ||||||
|  |             //Found sync
 | ||||||
|  |             instance->decoder.parser_step = ThermoPRO_TX4DecoderStepSaveDuration; | ||||||
|  |             instance->decoder.decode_data = 0; | ||||||
|  |             instance->decoder.decode_count_bit = 0; | ||||||
|  |         } | ||||||
|  |         break; | ||||||
|  | 
 | ||||||
|  |     case ThermoPRO_TX4DecoderStepSaveDuration: | ||||||
|  |         if(level) { | ||||||
|  |             instance->decoder.te_last = duration; | ||||||
|  |             instance->decoder.parser_step = ThermoPRO_TX4DecoderStepCheckDuration; | ||||||
|  |         } else { | ||||||
|  |             instance->decoder.parser_step = ThermoPRO_TX4DecoderStepReset; | ||||||
|  |         } | ||||||
|  |         break; | ||||||
|  | 
 | ||||||
|  |     case ThermoPRO_TX4DecoderStepCheckDuration: | ||||||
|  |         if(!level) { | ||||||
|  |             if(DURATION_DIFF(duration, ws_protocol_thermopro_tx4_const.te_short * 18) < | ||||||
|  |                ws_protocol_thermopro_tx4_const.te_delta * 10) { | ||||||
|  |                 //Found sync
 | ||||||
|  |                 instance->decoder.parser_step = ThermoPRO_TX4DecoderStepReset; | ||||||
|  |                 if((instance->decoder.decode_count_bit == | ||||||
|  |                     ws_protocol_thermopro_tx4_const.min_count_bit_for_found) && | ||||||
|  |                    ws_protocol_thermopro_tx4_check(instance)) { | ||||||
|  |                     instance->generic.data = instance->decoder.decode_data; | ||||||
|  |                     instance->generic.data_count_bit = instance->decoder.decode_count_bit; | ||||||
|  |                     ws_protocol_thermopro_tx4_remote_controller(&instance->generic); | ||||||
|  |                     if(instance->base.callback) | ||||||
|  |                         instance->base.callback(&instance->base, instance->base.context); | ||||||
|  |                     instance->decoder.parser_step = ThermoPRO_TX4DecoderStepCheckDuration; | ||||||
|  |                 } | ||||||
|  |                 instance->decoder.decode_data = 0; | ||||||
|  |                 instance->decoder.decode_count_bit = 0; | ||||||
|  | 
 | ||||||
|  |                 break; | ||||||
|  |             } else if( | ||||||
|  |                 (DURATION_DIFF( | ||||||
|  |                      instance->decoder.te_last, ws_protocol_thermopro_tx4_const.te_short) < | ||||||
|  |                  ws_protocol_thermopro_tx4_const.te_delta) && | ||||||
|  |                 (DURATION_DIFF(duration, ws_protocol_thermopro_tx4_const.te_long) < | ||||||
|  |                  ws_protocol_thermopro_tx4_const.te_delta * 2)) { | ||||||
|  |                 subghz_protocol_blocks_add_bit(&instance->decoder, 0); | ||||||
|  |                 instance->decoder.parser_step = ThermoPRO_TX4DecoderStepSaveDuration; | ||||||
|  |             } else if( | ||||||
|  |                 (DURATION_DIFF( | ||||||
|  |                      instance->decoder.te_last, ws_protocol_thermopro_tx4_const.te_short) < | ||||||
|  |                  ws_protocol_thermopro_tx4_const.te_delta) && | ||||||
|  |                 (DURATION_DIFF(duration, ws_protocol_thermopro_tx4_const.te_long * 2) < | ||||||
|  |                  ws_protocol_thermopro_tx4_const.te_delta * 4)) { | ||||||
|  |                 subghz_protocol_blocks_add_bit(&instance->decoder, 1); | ||||||
|  |                 instance->decoder.parser_step = ThermoPRO_TX4DecoderStepSaveDuration; | ||||||
|  |             } else { | ||||||
|  |                 instance->decoder.parser_step = ThermoPRO_TX4DecoderStepReset; | ||||||
|  |             } | ||||||
|  |         } else { | ||||||
|  |             instance->decoder.parser_step = ThermoPRO_TX4DecoderStepReset; | ||||||
|  |         } | ||||||
|  |         break; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | uint8_t ws_protocol_decoder_thermopro_tx4_get_hash_data(void* context) { | ||||||
|  |     furi_assert(context); | ||||||
|  |     WSProtocolDecoderThermoPRO_TX4* instance = context; | ||||||
|  |     return subghz_protocol_blocks_get_hash_data( | ||||||
|  |         &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool ws_protocol_decoder_thermopro_tx4_serialize( | ||||||
|  |     void* context, | ||||||
|  |     FlipperFormat* flipper_format, | ||||||
|  |     SubGhzRadioPreset* preset) { | ||||||
|  |     furi_assert(context); | ||||||
|  |     WSProtocolDecoderThermoPRO_TX4* instance = context; | ||||||
|  |     return ws_block_generic_serialize(&instance->generic, flipper_format, preset); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool ws_protocol_decoder_thermopro_tx4_deserialize(void* context, FlipperFormat* flipper_format) { | ||||||
|  |     furi_assert(context); | ||||||
|  |     WSProtocolDecoderThermoPRO_TX4* instance = context; | ||||||
|  |     bool ret = false; | ||||||
|  |     do { | ||||||
|  |         if(!ws_block_generic_deserialize(&instance->generic, flipper_format)) { | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  |         if(instance->generic.data_count_bit != | ||||||
|  |            ws_protocol_thermopro_tx4_const.min_count_bit_for_found) { | ||||||
|  |             FURI_LOG_E(TAG, "Wrong number of bits in key"); | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  |         ret = true; | ||||||
|  |     } while(false); | ||||||
|  |     return ret; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void ws_protocol_decoder_thermopro_tx4_get_string(void* context, FuriString* output) { | ||||||
|  |     furi_assert(context); | ||||||
|  |     WSProtocolDecoderThermoPRO_TX4* instance = context; | ||||||
|  |     furi_string_printf( | ||||||
|  |         output, | ||||||
|  |         "%s %dbit\r\n" | ||||||
|  |         "Key:0x%lX%08lX\r\n" | ||||||
|  |         "Sn:0x%lX Ch:%d  Bat:%d\r\n" | ||||||
|  |         "Temp:%d.%d C Hum:%d%%", | ||||||
|  |         instance->generic.protocol_name, | ||||||
|  |         instance->generic.data_count_bit, | ||||||
|  |         (uint32_t)(instance->generic.data >> 32), | ||||||
|  |         (uint32_t)(instance->generic.data), | ||||||
|  |         instance->generic.id, | ||||||
|  |         instance->generic.channel, | ||||||
|  |         instance->generic.battery_low, | ||||||
|  |         (int16_t)instance->generic.temp, | ||||||
|  |         abs(((int16_t)(instance->generic.temp * 10) - (((int16_t)instance->generic.temp) * 10))), | ||||||
|  |         instance->generic.humidity); | ||||||
|  | } | ||||||
| @ -0,0 +1,79 @@ | |||||||
|  | #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 "ws_generic.h" | ||||||
|  | #include <lib/subghz/blocks/math.h> | ||||||
|  | 
 | ||||||
|  | #define WS_PROTOCOL_THERMOPRO_TX4_NAME "ThermoPRO-TX4" | ||||||
|  | 
 | ||||||
|  | typedef struct WSProtocolDecoderThermoPRO_TX4 WSProtocolDecoderThermoPRO_TX4; | ||||||
|  | typedef struct WSProtocolEncoderThermoPRO_TX4 WSProtocolEncoderThermoPRO_TX4; | ||||||
|  | 
 | ||||||
|  | extern const SubGhzProtocolDecoder ws_protocol_thermopro_tx4_decoder; | ||||||
|  | extern const SubGhzProtocolEncoder ws_protocol_thermopro_tx4_encoder; | ||||||
|  | extern const SubGhzProtocol ws_protocol_thermopro_tx4; | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Allocate WSProtocolDecoderThermoPRO_TX4. | ||||||
|  |  * @param environment Pointer to a SubGhzEnvironment instance | ||||||
|  |  * @return WSProtocolDecoderThermoPRO_TX4* pointer to a WSProtocolDecoderThermoPRO_TX4 instance | ||||||
|  |  */ | ||||||
|  | void* ws_protocol_decoder_thermopro_tx4_alloc(SubGhzEnvironment* environment); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Free WSProtocolDecoderThermoPRO_TX4. | ||||||
|  |  * @param context Pointer to a WSProtocolDecoderThermoPRO_TX4 instance | ||||||
|  |  */ | ||||||
|  | void ws_protocol_decoder_thermopro_tx4_free(void* context); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Reset decoder WSProtocolDecoderThermoPRO_TX4. | ||||||
|  |  * @param context Pointer to a WSProtocolDecoderThermoPRO_TX4 instance | ||||||
|  |  */ | ||||||
|  | void ws_protocol_decoder_thermopro_tx4_reset(void* context); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Parse a raw sequence of levels and durations received from the air. | ||||||
|  |  * @param context Pointer to a WSProtocolDecoderThermoPRO_TX4 instance | ||||||
|  |  * @param level Signal level true-high false-low | ||||||
|  |  * @param duration Duration of this level in, us | ||||||
|  |  */ | ||||||
|  | void ws_protocol_decoder_thermopro_tx4_feed(void* context, bool level, uint32_t duration); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Getting the hash sum of the last randomly received parcel. | ||||||
|  |  * @param context Pointer to a WSProtocolDecoderThermoPRO_TX4 instance | ||||||
|  |  * @return hash Hash sum | ||||||
|  |  */ | ||||||
|  | uint8_t ws_protocol_decoder_thermopro_tx4_get_hash_data(void* context); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Serialize data WSProtocolDecoderThermoPRO_TX4. | ||||||
|  |  * @param context Pointer to a WSProtocolDecoderThermoPRO_TX4 instance | ||||||
|  |  * @param flipper_format Pointer to a FlipperFormat instance | ||||||
|  |  * @param preset The modulation on which the signal was received, SubGhzRadioPreset | ||||||
|  |  * @return true On success | ||||||
|  |  */ | ||||||
|  | bool ws_protocol_decoder_thermopro_tx4_serialize( | ||||||
|  |     void* context, | ||||||
|  |     FlipperFormat* flipper_format, | ||||||
|  |     SubGhzRadioPreset* preset); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Deserialize data WSProtocolDecoderThermoPRO_TX4. | ||||||
|  |  * @param context Pointer to a WSProtocolDecoderThermoPRO_TX4 instance | ||||||
|  |  * @param flipper_format Pointer to a FlipperFormat instance | ||||||
|  |  * @return true On success | ||||||
|  |  */ | ||||||
|  | bool ws_protocol_decoder_thermopro_tx4_deserialize(void* context, FlipperFormat* flipper_format); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Getting a textual representation of the received data. | ||||||
|  |  * @param context Pointer to a WSProtocolDecoderThermoPRO_TX4 instance | ||||||
|  |  * @param output Resulting text | ||||||
|  |  */ | ||||||
|  | void ws_protocol_decoder_thermopro_tx4_get_string(void* context, FuriString* output); | ||||||
							
								
								
									
										198
									
								
								applications/plugins/weather_station/protocols/ws_generic.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										198
									
								
								applications/plugins/weather_station/protocols/ws_generic.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,198 @@ | |||||||
|  | #include "ws_generic.h" | ||||||
|  | #include <lib/toolbox/stream/stream.h> | ||||||
|  | #include <lib/flipper_format/flipper_format_i.h> | ||||||
|  | #include "../helpers/weather_station_types.h" | ||||||
|  | 
 | ||||||
|  | #define TAG "WSBlockGeneric" | ||||||
|  | 
 | ||||||
|  | void ws_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); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool ws_block_generic_serialize( | ||||||
|  |     WSBlockGeneric* instance, | ||||||
|  |     FlipperFormat* flipper_format, | ||||||
|  |     SubGhzRadioPreset* preset) { | ||||||
|  |     furi_assert(instance); | ||||||
|  |     bool res = false; | ||||||
|  |     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, WS_KEY_FILE_TYPE, WS_KEY_FILE_VERSION)) { | ||||||
|  |             FURI_LOG_E(TAG, "Unable to add header"); | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         if(!flipper_format_write_uint32(flipper_format, "Frequency", &preset->frequency, 1)) { | ||||||
|  |             FURI_LOG_E(TAG, "Unable to add Frequency"); | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         ws_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"); | ||||||
|  |             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"); | ||||||
|  |                 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"); | ||||||
|  |                 break; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         if(!flipper_format_write_string_cstr(flipper_format, "Protocol", instance->protocol_name)) { | ||||||
|  |             FURI_LOG_E(TAG, "Unable to add Protocol"); | ||||||
|  |             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"); | ||||||
|  |             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"); | ||||||
|  |             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"); | ||||||
|  |             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"); | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         temp_data = instance->humidity; | ||||||
|  |         if(!flipper_format_write_uint32(flipper_format, "Hum", &temp_data, 1)) { | ||||||
|  |             FURI_LOG_E(TAG, "Unable to add Humidity"); | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         temp_data = instance->channel; | ||||||
|  |         if(!flipper_format_write_uint32(flipper_format, "Ch", &temp_data, 1)) { | ||||||
|  |             FURI_LOG_E(TAG, "Unable to add Channel"); | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         // temp_data = instance->btn;
 | ||||||
|  |         // if(!flipper_format_write_uint32(flipper_format, "Btn", &temp_data, 1)) {
 | ||||||
|  |         //     FURI_LOG_E(TAG, "Unable to add Btn");
 | ||||||
|  |         //     break;
 | ||||||
|  |         // }
 | ||||||
|  | 
 | ||||||
|  |         float temp = instance->temp; | ||||||
|  |         if(!flipper_format_write_float(flipper_format, "Temp", &temp, 1)) { | ||||||
|  |             FURI_LOG_E(TAG, "Unable to add Temperature"); | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         res = true; | ||||||
|  |     } while(false); | ||||||
|  |     furi_string_free(temp_str); | ||||||
|  |     return res; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool ws_block_generic_deserialize(WSBlockGeneric* instance, FlipperFormat* flipper_format) { | ||||||
|  |     furi_assert(instance); | ||||||
|  |     bool res = false; | ||||||
|  |     uint32_t temp_data = 0; | ||||||
|  | 
 | ||||||
|  |     do { | ||||||
|  |         if(!flipper_format_rewind(flipper_format)) { | ||||||
|  |             FURI_LOG_E(TAG, "Rewind error"); | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         if(!flipper_format_read_uint32(flipper_format, "Id", (uint32_t*)&temp_data, 1)) { | ||||||
|  |             FURI_LOG_E(TAG, "Missing Id"); | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  |         instance->id = (uint32_t)temp_data; | ||||||
|  | 
 | ||||||
|  |         if(!flipper_format_read_uint32(flipper_format, "Bit", (uint32_t*)&temp_data, 1)) { | ||||||
|  |             FURI_LOG_E(TAG, "Missing Bit"); | ||||||
|  |             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"); | ||||||
|  |             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", (uint32_t*)&temp_data, 1)) { | ||||||
|  |             FURI_LOG_E(TAG, "Missing Battery_low"); | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  |         instance->battery_low = (uint8_t)temp_data; | ||||||
|  | 
 | ||||||
|  |         if(!flipper_format_read_uint32(flipper_format, "Hum", (uint32_t*)&temp_data, 1)) { | ||||||
|  |             FURI_LOG_E(TAG, "Missing Humidity"); | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  |         instance->humidity = (uint8_t)temp_data; | ||||||
|  | 
 | ||||||
|  |         if(!flipper_format_read_uint32(flipper_format, "Ch", (uint32_t*)&temp_data, 1)) { | ||||||
|  |             FURI_LOG_E(TAG, "Missing Channel"); | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  |         instance->channel = (uint8_t)temp_data; | ||||||
|  | 
 | ||||||
|  |         // if(!flipper_format_read_uint32(flipper_format, "Btn", (uint32_t*)&temp_data, 1)) {
 | ||||||
|  |         //     FURI_LOG_E(TAG, "Missing Btn");
 | ||||||
|  |         //     break;
 | ||||||
|  |         // }
 | ||||||
|  |         // instance->btn = (uint8_t)temp_data;
 | ||||||
|  | 
 | ||||||
|  |         float temp; | ||||||
|  |         if(!flipper_format_read_float(flipper_format, "Temp", (float*)&temp, 1)) { | ||||||
|  |             FURI_LOG_E(TAG, "Missing Temperature"); | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  |         instance->temp = temp; | ||||||
|  | 
 | ||||||
|  |         res = true; | ||||||
|  |     } while(0); | ||||||
|  | 
 | ||||||
|  |     return res; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | float ws_block_generic_fahrenheit_to_celsius(float fahrenheit) { | ||||||
|  |     return (fahrenheit - 32.0f) / 1.8f; | ||||||
|  | } | ||||||
							
								
								
									
										61
									
								
								applications/plugins/weather_station/protocols/ws_generic.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										61
									
								
								applications/plugins/weather_station/protocols/ws_generic.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,61 @@ | |||||||
|  | #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> | ||||||
|  | 
 | ||||||
|  | #ifdef __cplusplus | ||||||
|  | extern "C" { | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | typedef struct WSBlockGeneric WSBlockGeneric; | ||||||
|  | 
 | ||||||
|  | struct WSBlockGeneric { | ||||||
|  |     const char* protocol_name; | ||||||
|  |     uint64_t data; | ||||||
|  |     uint32_t id; | ||||||
|  |     uint8_t data_count_bit; | ||||||
|  |     uint8_t battery_low; | ||||||
|  |     uint8_t humidity; | ||||||
|  |     uint8_t channel; | ||||||
|  |     uint8_t btn; | ||||||
|  |     float temp; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Get name preset. | ||||||
|  |  * @param preset_name name preset | ||||||
|  |  * @param preset_str Output name preset | ||||||
|  |  */ | ||||||
|  | void ws_block_generic_get_preset_name(const char* preset_name, FuriString* preset_str); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Serialize data WSBlockGeneric. | ||||||
|  |  * @param instance Pointer to a WSBlockGeneric instance | ||||||
|  |  * @param flipper_format Pointer to a FlipperFormat instance | ||||||
|  |  * @param preset The modulation on which the signal was received, SubGhzRadioPreset | ||||||
|  |  * @return true On success | ||||||
|  |  */ | ||||||
|  | bool ws_block_generic_serialize( | ||||||
|  |     WSBlockGeneric* instance, | ||||||
|  |     FlipperFormat* flipper_format, | ||||||
|  |     SubGhzRadioPreset* preset); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Deserialize data WSBlockGeneric. | ||||||
|  |  * @param instance Pointer to a WSBlockGeneric instance | ||||||
|  |  * @param flipper_format Pointer to a FlipperFormat instance | ||||||
|  |  * @return true On success | ||||||
|  |  */ | ||||||
|  | bool ws_block_generic_deserialize(WSBlockGeneric* instance, FlipperFormat* flipper_format); | ||||||
|  | 
 | ||||||
|  | float ws_block_generic_fahrenheit_to_celsius(float fahrenheit); | ||||||
|  | 
 | ||||||
|  | #ifdef __cplusplus | ||||||
|  | } | ||||||
|  | #endif | ||||||
| @ -0,0 +1,207 @@ | |||||||
|  | #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)); | ||||||
|  | 
 | ||||||
|  |         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), "", ""); | ||||||
|  |     } | ||||||
|  |     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); | ||||||
|  |         } | ||||||
|  |         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); | ||||||
|  | } | ||||||
| @ -0,0 +1,30 @@ | |||||||
|  | #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, | ||||||
|  | }; | ||||||
| @ -0,0 +1,29 @@ | |||||||
|  | #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 | ||||||
| @ -0,0 +1,78 @@ | |||||||
|  | #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 station 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); | ||||||
|  | } | ||||||
| @ -0,0 +1,5 @@ | |||||||
|  | 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) | ||||||
| @ -0,0 +1,223 @@ | |||||||
|  | #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); | ||||||
|  | } | ||||||
| @ -0,0 +1,50 @@ | |||||||
|  | #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); | ||||||
|  | } | ||||||
| @ -0,0 +1,58 @@ | |||||||
|  | #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); | ||||||
|  | } | ||||||
| @ -0,0 +1,437 @@ | |||||||
|  | #include "weather_station_receiver.h" | ||||||
|  | #include "../weather_station_app_i.h" | ||||||
|  | #include "weather_station_icons.h" | ||||||
|  | #include <math.h> | ||||||
|  | 
 | ||||||
|  | #include <input/input.h> | ||||||
|  | #include <gui/elements.h> | ||||||
|  | #include <assets_icons.h> | ||||||
|  | #include <m-array.h> | ||||||
|  | 
 | ||||||
|  | #define FRAME_HEIGHT 12 | ||||||
|  | #define MAX_LEN_PX 100 | ||||||
|  | #define MENU_ITEMS 4u | ||||||
|  | #define UNLOCK_CNT 3 | ||||||
|  | 
 | ||||||
|  | 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; | ||||||
|  | } WSReceiverModel; | ||||||
|  | 
 | ||||||
|  | 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) { | ||||||
|  |     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); | ||||||
|  |         }, | ||||||
|  |         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); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 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"); | ||||||
|  |     canvas_draw_line(canvas, 46, 51, 125, 51); | ||||||
|  | 
 | ||||||
|  |     bool scrollbar = model->history_item > 4; | ||||||
|  |     FuriString* str_buff; | ||||||
|  |     str_buff = furi_string_alloc(); | ||||||
|  | 
 | ||||||
|  |     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, 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, &I_Scanning_123x52); | ||||||
|  |         canvas_set_font(canvas, FontPrimary); | ||||||
|  |         canvas_draw_str(canvas, 63, 46, "Scanning..."); | ||||||
|  |         canvas_draw_line(canvas, 46, 51, 125, 51); | ||||||
|  |         canvas_set_font(canvas, FontSecondary); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     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->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)); | ||||||
|  |             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); | ||||||
|  | } | ||||||
| @ -0,0 +1,36 @@ | |||||||
|  | #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_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); | ||||||
|  | 
 | ||||||
|  | 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); | ||||||
| @ -0,0 +1,150 @@ | |||||||
|  | #include "weather_station_receiver.h" | ||||||
|  | #include "../weather_station_app_i.h" | ||||||
|  | #include "weather_station_icons.h" | ||||||
|  | #include "../protocols/ws_generic.h" | ||||||
|  | #include <input/input.h> | ||||||
|  | #include <gui/elements.h> | ||||||
|  | #include "math.h" | ||||||
|  | 
 | ||||||
|  | #define abs(x) ((x) > 0 ? (x) : -(x)) | ||||||
|  | 
 | ||||||
|  | struct WSReceiverInfo { | ||||||
|  |     View* view; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | typedef struct { | ||||||
|  |     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); | ||||||
|  |         }, | ||||||
|  |         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, 5, 8, buffer); | ||||||
|  | 
 | ||||||
|  |     snprintf(buffer, sizeof(buffer), "Ch: %01d", model->generic->channel); | ||||||
|  |     canvas_draw_str(canvas, 105, 8, buffer); | ||||||
|  | 
 | ||||||
|  |     snprintf(buffer, sizeof(buffer), "Sn: 0x%02lX", model->generic->id); | ||||||
|  |     canvas_draw_str(canvas, 5, 20, buffer); | ||||||
|  | 
 | ||||||
|  |     snprintf(buffer, sizeof(buffer), "Batt: %s", (!model->generic->battery_low ? "ok" : "low")); | ||||||
|  |     canvas_draw_str(canvas, 85, 20, buffer); | ||||||
|  | 
 | ||||||
|  |     snprintf(buffer, sizeof(buffer), "Data: 0x%llX", model->generic->data); | ||||||
|  |     canvas_draw_str(canvas, 5, 32, buffer); | ||||||
|  | 
 | ||||||
|  |     elements_bold_rounded_frame(canvas, 2, 37, 123, 25); | ||||||
|  |     canvas_set_font(canvas, FontPrimary); | ||||||
|  | 
 | ||||||
|  |     canvas_draw_icon(canvas, 13 + 5, 42, &I_Therm_7x16); | ||||||
|  |     snprintf( | ||||||
|  |         buffer, | ||||||
|  |         sizeof(buffer), | ||||||
|  |         "%3.2d.%d C", | ||||||
|  |         (int16_t)model->generic->temp, | ||||||
|  |         abs(((int16_t)(model->generic->temp * 10) - (((int16_t)model->generic->temp) * 10)))); | ||||||
|  |     canvas_draw_str_aligned(canvas, 58 + 5, 46, AlignRight, AlignTop, buffer); | ||||||
|  |     canvas_draw_circle(canvas, 50 + 5, 45, 1); | ||||||
|  | 
 | ||||||
|  |     canvas_draw_icon(canvas, 70 + 5, 42, &I_Humid_10x15); | ||||||
|  |     snprintf(buffer, sizeof(buffer), "%d%%", model->generic->humidity); | ||||||
|  |     canvas_draw_str(canvas, 86 + 5, 54, 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; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void ws_view_receiver_info_enter(void* context) { | ||||||
|  |     furi_assert(context); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void ws_view_receiver_info_exit(void* context) { | ||||||
|  |     furi_assert(context); | ||||||
|  |     WSReceiverInfo* ws_receiver_info = context; | ||||||
|  | 
 | ||||||
|  |     with_view_model( | ||||||
|  |         ws_receiver_info->view, | ||||||
|  |         WSReceiverInfoModel * model, | ||||||
|  |         { furi_string_reset(model->protocol_name); }, | ||||||
|  |         false); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 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); | ||||||
|  | 
 | ||||||
|  |     return ws_receiver_info; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void ws_view_receiver_info_free(WSReceiverInfo* ws_receiver_info) { | ||||||
|  |     furi_assert(ws_receiver_info); | ||||||
|  | 
 | ||||||
|  |     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; | ||||||
|  | } | ||||||
| @ -0,0 +1,16 @@ | |||||||
|  | #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); | ||||||
							
								
								
									
										
											BIN
										
									
								
								applications/plugins/weather_station/weather_station_10px.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								applications/plugins/weather_station/weather_station_10px.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 175 B | 
							
								
								
									
										178
									
								
								applications/plugins/weather_station/weather_station_app.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										178
									
								
								applications/plugins/weather_station/weather_station_app.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,178 @@ | |||||||
|  | #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_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); | ||||||
|  | 
 | ||||||
|  |     //CC1101 off
 | ||||||
|  |     ws_sleep(app); | ||||||
|  | 
 | ||||||
|  |     // 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; | ||||||
|  | } | ||||||
							
								
								
									
										159
									
								
								applications/plugins/weather_station/weather_station_app_i.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										159
									
								
								applications/plugins/weather_station/weather_station_app_i.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,159 @@ | |||||||
|  | #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); | ||||||
|  |     UNUSED(preset_data); | ||||||
|  |     furi_hal_subghz_reset(); | ||||||
|  |     furi_hal_subghz_idle(); | ||||||
|  |     furi_hal_subghz_load_custom_preset(preset_data); | ||||||
|  |     furi_hal_gpio_init(&gpio_cc1101_g0, GpioModeInput, GpioPullNo, GpioSpeedLow); | ||||||
|  |     app->txrx->txrx_state = WSTxRxStateIDLE; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | uint32_t ws_rx(WeatherStationApp* app, uint32_t frequency) { | ||||||
|  |     furi_assert(app); | ||||||
|  |     if(!furi_hal_subghz_is_frequency_valid(frequency)) { | ||||||
|  |         furi_crash("WeatherStation: Incorrect RX frequency."); | ||||||
|  |     } | ||||||
|  |     furi_assert( | ||||||
|  |         app->txrx->txrx_state != WSTxRxStateRx && app->txrx->txrx_state != WSTxRxStateSleep); | ||||||
|  | 
 | ||||||
|  |     furi_hal_subghz_idle(); | ||||||
|  |     uint32_t value = furi_hal_subghz_set_frequency_and_path(frequency); | ||||||
|  |     furi_hal_gpio_init(&gpio_cc1101_g0, GpioModeInput, GpioPullNo, GpioSpeedLow); | ||||||
|  |     furi_hal_subghz_flush_rx(); | ||||||
|  |     furi_hal_subghz_rx(); | ||||||
|  | 
 | ||||||
|  |     furi_hal_subghz_start_async_rx(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); | ||||||
|  |     furi_hal_subghz_idle(); | ||||||
|  |     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); | ||||||
|  |         furi_hal_subghz_stop_async_rx(); | ||||||
|  |     } | ||||||
|  |     furi_hal_subghz_idle(); | ||||||
|  |     app->txrx->txrx_state = WSTxRxStateIDLE; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void ws_sleep(WeatherStationApp* app) { | ||||||
|  |     furi_assert(app); | ||||||
|  |     furi_hal_subghz_sleep(); | ||||||
|  |     app->txrx->txrx_state = WSTxRxStateSleep; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void ws_hopper_update(WeatherStationApp* app) { | ||||||
|  |     furi_assert(app); | ||||||
|  | 
 | ||||||
|  |     switch(app->txrx->hopper_state) { | ||||||
|  |     case WSHopperStateOFF: | ||||||
|  |         return; | ||||||
|  |         break; | ||||||
|  |     case WSHopperStatePause: | ||||||
|  |         return; | ||||||
|  |         break; | ||||||
|  |     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 = furi_hal_subghz_get_rssi(); | ||||||
|  | 
 | ||||||
|  |         // 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); | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										73
									
								
								applications/plugins/weather_station/weather_station_app_i.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										73
									
								
								applications/plugins/weather_station/weather_station_app_i.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,73 @@ | |||||||
|  | #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> | ||||||
|  | 
 | ||||||
|  | typedef struct WeatherStationApp WeatherStationApp; | ||||||
|  | 
 | ||||||
|  | struct WeatherStationTxRx { | ||||||
|  |     SubGhzWorker* worker; | ||||||
|  | 
 | ||||||
|  |     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); | ||||||
							
								
								
									
										246
									
								
								applications/plugins/weather_station/weather_station_history.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										246
									
								
								applications/plugins/weather_station/weather_station_history.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,246 @@ | |||||||
|  | #include "weather_station_history.h" | ||||||
|  | #include <flipper_format/flipper_format_i.h> | ||||||
|  | #include <lib/toolbox/stream/stream.h> | ||||||
|  | #include <lib/subghz/receiver.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) { | ||||||
|  |         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]; | ||||||
|  |             } | ||||||
|  |             if(!(uint32_t)(data >> 32)) { | ||||||
|  |                 furi_string_printf( | ||||||
|  |                     item->item_str, | ||||||
|  |                     "%s %lX", | ||||||
|  |                     furi_string_get_cstr(instance->tmp_string), | ||||||
|  |                     (uint32_t)(data & 0xFFFFFFFF)); | ||||||
|  |             } else { | ||||||
|  |                 furi_string_printf( | ||||||
|  |                     item->item_str, | ||||||
|  |                     "%s %lX%08lX", | ||||||
|  |                     furi_string_get_cstr(instance->tmp_string), | ||||||
|  |                     (uint32_t)(data >> 32), | ||||||
|  |                     (uint32_t)(data & 0xFFFFFFFF)); | ||||||
|  |             } | ||||||
|  |         } while(false); | ||||||
|  |         instance->last_index_write++; | ||||||
|  |         return WSHistoryStateAddKeyNewDada; | ||||||
|  |     } | ||||||
|  |     return WSHistoryStateAddKeyUnknown; | ||||||
|  | } | ||||||
							
								
								
									
										112
									
								
								applications/plugins/weather_station/weather_station_history.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										112
									
								
								applications/plugins/weather_station/weather_station_history.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,112 @@ | |||||||
|  | 
 | ||||||
|  | #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); | ||||||
| @ -1,3 +1,4 @@ | |||||||
|  | # to use manual settings and prevent them from being deleted on upgrade, rename *_user.example files to *_user | ||||||
| # for adding manufacture keys | # for adding manufacture keys | ||||||
| # AABBCCDDEEFFAABB:X:NAME\r\n | # AABBCCDDEEFFAABB:X:NAME\r\n | ||||||
| # AABBCCDDEEFFAABB - man 64 bit | # AABBCCDDEEFFAABB - man 64 bit | ||||||
| @ -1,3 +1,4 @@ | |||||||
|  | # to use manual settings and prevent them from being deleted on upgrade, rename *_user.example files to *_user | ||||||
| Filetype: Flipper SubGhz Setting File | Filetype: Flipper SubGhz Setting File | ||||||
| Version: 1 | Version: 1 | ||||||
| 
 | 
 | ||||||
| @ -2,6 +2,6 @@ Filetype: Flipper SubGhz Key File | |||||||
| Version: 1 | Version: 1 | ||||||
| Frequency: 433920000 | Frequency: 433920000 | ||||||
| Preset: FuriHalSubGhzPresetOok650Async | Preset: FuriHalSubGhzPresetOok650Async | ||||||
| Protocol: Magellen | Protocol: Magellan | ||||||
| Bit: 32 | Bit: 32 | ||||||
| Key: 00 00 00 00 37 AE 48 28 | Key: 00 00 00 00 37 AE 48 28 | ||||||
| @ -1,5 +1,5 @@ | |||||||
| entry,status,name,type,params | entry,status,name,type,params | ||||||
| Version,+,2.4,, | Version,+,3.3,, | ||||||
| Header,+,applications/services/bt/bt_service/bt.h,, | Header,+,applications/services/bt/bt_service/bt.h,, | ||||||
| Header,+,applications/services/cli/cli.h,, | Header,+,applications/services/cli/cli.h,, | ||||||
| Header,+,applications/services/cli/cli_vcp.h,, | Header,+,applications/services/cli/cli_vcp.h,, | ||||||
| @ -124,9 +124,15 @@ Header,+,lib/one_wire/one_wire_host.h,, | |||||||
| Header,+,lib/one_wire/one_wire_host_timing.h,, | Header,+,lib/one_wire/one_wire_host_timing.h,, | ||||||
| Header,+,lib/one_wire/one_wire_slave.h,, | Header,+,lib/one_wire/one_wire_slave.h,, | ||||||
| Header,+,lib/print/wrappers.h,, | Header,+,lib/print/wrappers.h,, | ||||||
|  | Header,+,lib/subghz/blocks/const.h,, | ||||||
|  | Header,+,lib/subghz/blocks/decoder.h,, | ||||||
|  | Header,+,lib/subghz/blocks/encoder.h,, | ||||||
|  | Header,+,lib/subghz/blocks/generic.h,, | ||||||
|  | Header,+,lib/subghz/blocks/math.h,, | ||||||
| Header,+,lib/subghz/environment.h,, | Header,+,lib/subghz/environment.h,, | ||||||
| Header,+,lib/subghz/protocols/raw.h,, | Header,+,lib/subghz/protocols/raw.h,, | ||||||
| Header,+,lib/subghz/receiver.h,, | Header,+,lib/subghz/receiver.h,, | ||||||
|  | Header,+,lib/subghz/subghz_setting.h,, | ||||||
| Header,+,lib/subghz/subghz_tx_rx_worker.h,, | Header,+,lib/subghz/subghz_tx_rx_worker.h,, | ||||||
| Header,+,lib/subghz/subghz_worker.h,, | Header,+,lib/subghz/subghz_worker.h,, | ||||||
| Header,+,lib/subghz/transmitter.h,, | Header,+,lib/subghz/transmitter.h,, | ||||||
| @ -2240,14 +2246,20 @@ Function,-,strupr,char*,char* | |||||||
| Function,-,strverscmp,int,"const char*, const char*" | Function,-,strverscmp,int,"const char*, const char*" | ||||||
| Function,-,strxfrm,size_t,"char*, const char*, size_t" | Function,-,strxfrm,size_t,"char*, const char*, size_t" | ||||||
| Function,-,strxfrm_l,size_t,"char*, const char*, size_t, locale_t" | Function,-,strxfrm_l,size_t,"char*, const char*, size_t, locale_t" | ||||||
|  | Function,+,subghz_block_generic_deserialize,_Bool,"SubGhzBlockGeneric*, FlipperFormat*" | ||||||
|  | Function,+,subghz_block_generic_get_preset_name,void,"const char*, FuriString*" | ||||||
|  | Function,+,subghz_block_generic_serialize,_Bool,"SubGhzBlockGeneric*, FlipperFormat*, SubGhzRadioPreset*" | ||||||
| Function,+,subghz_environment_alloc,SubGhzEnvironment*, | Function,+,subghz_environment_alloc,SubGhzEnvironment*, | ||||||
| Function,+,subghz_environment_free,void,SubGhzEnvironment* | Function,+,subghz_environment_free,void,SubGhzEnvironment* | ||||||
| Function,-,subghz_environment_get_came_atomo_rainbow_table_file_name,const char*,SubGhzEnvironment* | Function,+,subghz_environment_get_came_atomo_rainbow_table_file_name,const char*,SubGhzEnvironment* | ||||||
| Function,-,subghz_environment_get_keystore,SubGhzKeystore*,SubGhzEnvironment* | Function,+,subghz_environment_get_keystore,SubGhzKeystore*,SubGhzEnvironment* | ||||||
| Function,-,subghz_environment_get_nice_flor_s_rainbow_table_file_name,const char*,SubGhzEnvironment* | Function,+,subghz_environment_get_nice_flor_s_rainbow_table_file_name,const char*,SubGhzEnvironment* | ||||||
|  | Function,+,subghz_environment_get_protocol_name_registry,const char*,"SubGhzEnvironment*, size_t" | ||||||
|  | Function,+,subghz_environment_get_protocol_registry,void*,SubGhzEnvironment* | ||||||
| Function,+,subghz_environment_load_keystore,_Bool,"SubGhzEnvironment*, const char*" | Function,+,subghz_environment_load_keystore,_Bool,"SubGhzEnvironment*, const char*" | ||||||
| Function,-,subghz_environment_set_came_atomo_rainbow_table_file_name,void,"SubGhzEnvironment*, const char*" | Function,+,subghz_environment_set_came_atomo_rainbow_table_file_name,void,"SubGhzEnvironment*, const char*" | ||||||
| Function,-,subghz_environment_set_nice_flor_s_rainbow_table_file_name,void,"SubGhzEnvironment*, const char*" | Function,+,subghz_environment_set_nice_flor_s_rainbow_table_file_name,void,"SubGhzEnvironment*, const char*" | ||||||
|  | Function,+,subghz_environment_set_protocol_registry,void,"SubGhzEnvironment*, void*" | ||||||
| Function,-,subghz_keystore_alloc,SubGhzKeystore*, | Function,-,subghz_keystore_alloc,SubGhzKeystore*, | ||||||
| Function,-,subghz_keystore_free,void,SubGhzKeystore* | Function,-,subghz_keystore_free,void,SubGhzKeystore* | ||||||
| Function,-,subghz_keystore_get_data,SubGhzKeyArray_t*,SubGhzKeystore* | Function,-,subghz_keystore_get_data,SubGhzKeyArray_t*,SubGhzKeystore* | ||||||
| @ -2255,10 +2267,20 @@ Function,-,subghz_keystore_load,_Bool,"SubGhzKeystore*, const char*" | |||||||
| Function,-,subghz_keystore_raw_encrypted_save,_Bool,"const char*, const char*, uint8_t*" | Function,-,subghz_keystore_raw_encrypted_save,_Bool,"const char*, const char*, uint8_t*" | ||||||
| Function,-,subghz_keystore_raw_get_data,_Bool,"const char*, size_t, uint8_t*, size_t" | Function,-,subghz_keystore_raw_get_data,_Bool,"const char*, size_t, uint8_t*, size_t" | ||||||
| Function,-,subghz_keystore_save,_Bool,"SubGhzKeystore*, const char*, uint8_t*" | Function,-,subghz_keystore_save,_Bool,"SubGhzKeystore*, const char*, uint8_t*" | ||||||
|  | Function,+,subghz_protocol_blocks_add_bit,void,"SubGhzBlockDecoder*, uint8_t" | ||||||
|  | Function,+,subghz_protocol_blocks_crc4,uint8_t,"const uint8_t[], unsigned, uint8_t, uint8_t" | ||||||
|  | Function,+,subghz_protocol_blocks_crc7,uint8_t,"const uint8_t[], unsigned, uint8_t, uint8_t" | ||||||
|  | Function,+,subghz_protocol_blocks_crc8,uint8_t,"const uint8_t[], unsigned, uint8_t, uint8_t" | ||||||
|  | Function,+,subghz_protocol_blocks_get_bit_array,_Bool,"uint8_t[], size_t" | ||||||
|  | Function,+,subghz_protocol_blocks_get_hash_data,uint8_t,"SubGhzBlockDecoder*, size_t" | ||||||
|  | Function,+,subghz_protocol_blocks_get_parity,uint8_t,"uint64_t, uint8_t" | ||||||
|  | Function,+,subghz_protocol_blocks_get_upload,size_t,"uint8_t[], size_t, LevelDuration*, size_t, uint32_t" | ||||||
|  | Function,+,subghz_protocol_blocks_reverse_key,uint64_t,"uint64_t, uint8_t" | ||||||
|  | Function,+,subghz_protocol_blocks_set_bit_array,void,"_Bool, uint8_t[], size_t, size_t" | ||||||
| Function,-,subghz_protocol_decoder_base_deserialize,_Bool,"SubGhzProtocolDecoderBase*, FlipperFormat*" | Function,-,subghz_protocol_decoder_base_deserialize,_Bool,"SubGhzProtocolDecoderBase*, FlipperFormat*" | ||||||
| Function,-,subghz_protocol_decoder_base_get_hash_data,uint8_t,SubGhzProtocolDecoderBase* | Function,+,subghz_protocol_decoder_base_get_hash_data,uint8_t,SubGhzProtocolDecoderBase* | ||||||
| Function,-,subghz_protocol_decoder_base_get_string,_Bool,"SubGhzProtocolDecoderBase*, FuriString*" | Function,+,subghz_protocol_decoder_base_get_string,_Bool,"SubGhzProtocolDecoderBase*, FuriString*" | ||||||
| Function,+,subghz_protocol_decoder_base_serialize,_Bool,"SubGhzProtocolDecoderBase*, FlipperFormat*, SubGhzPresetDefinition*" | Function,+,subghz_protocol_decoder_base_serialize,_Bool,"SubGhzProtocolDecoderBase*, FlipperFormat*, SubGhzRadioPreset*" | ||||||
| Function,-,subghz_protocol_decoder_base_set_decoder_callback,void,"SubGhzProtocolDecoderBase*, SubGhzProtocolDecoderBaseRxCallback, void*" | Function,-,subghz_protocol_decoder_base_set_decoder_callback,void,"SubGhzProtocolDecoderBase*, SubGhzProtocolDecoderBaseRxCallback, void*" | ||||||
| Function,+,subghz_protocol_decoder_raw_alloc,void*,SubGhzEnvironment* | Function,+,subghz_protocol_decoder_raw_alloc,void*,SubGhzEnvironment* | ||||||
| Function,+,subghz_protocol_decoder_raw_deserialize,_Bool,"void*, FlipperFormat*" | Function,+,subghz_protocol_decoder_raw_deserialize,_Bool,"void*, FlipperFormat*" | ||||||
| @ -2274,7 +2296,7 @@ Function,+,subghz_protocol_encoder_raw_yield,LevelDuration,void* | |||||||
| Function,+,subghz_protocol_raw_file_encoder_worker_set_callback_end,void,"SubGhzProtocolEncoderRAW*, SubGhzProtocolEncoderRAWCallbackEnd, void*" | Function,+,subghz_protocol_raw_file_encoder_worker_set_callback_end,void,"SubGhzProtocolEncoderRAW*, SubGhzProtocolEncoderRAWCallbackEnd, void*" | ||||||
| Function,+,subghz_protocol_raw_gen_fff_data,void,"FlipperFormat*, const char*" | Function,+,subghz_protocol_raw_gen_fff_data,void,"FlipperFormat*, const char*" | ||||||
| Function,+,subghz_protocol_raw_get_sample_write,size_t,SubGhzProtocolDecoderRAW* | Function,+,subghz_protocol_raw_get_sample_write,size_t,SubGhzProtocolDecoderRAW* | ||||||
| Function,+,subghz_protocol_raw_save_to_file_init,_Bool,"SubGhzProtocolDecoderRAW*, const char*, SubGhzPresetDefinition*" | Function,+,subghz_protocol_raw_save_to_file_init,_Bool,"SubGhzProtocolDecoderRAW*, const char*, SubGhzRadioPreset*" | ||||||
| Function,+,subghz_protocol_raw_save_to_file_stop,void,SubGhzProtocolDecoderRAW* | Function,+,subghz_protocol_raw_save_to_file_stop,void,SubGhzProtocolDecoderRAW* | ||||||
| Function,+,subghz_receiver_alloc_init,SubGhzReceiver*,SubGhzEnvironment* | Function,+,subghz_receiver_alloc_init,SubGhzReceiver*,SubGhzEnvironment* | ||||||
| Function,+,subghz_receiver_decode,void,"SubGhzReceiver*, _Bool, uint32_t" | Function,+,subghz_receiver_decode,void,"SubGhzReceiver*, _Bool, uint32_t" | ||||||
| @ -2283,6 +2305,23 @@ Function,+,subghz_receiver_reset,void,SubGhzReceiver* | |||||||
| Function,+,subghz_receiver_search_decoder_base_by_name,SubGhzProtocolDecoderBase*,"SubGhzReceiver*, const char*" | Function,+,subghz_receiver_search_decoder_base_by_name,SubGhzProtocolDecoderBase*,"SubGhzReceiver*, const char*" | ||||||
| Function,+,subghz_receiver_set_filter,void,"SubGhzReceiver*, SubGhzProtocolFlag" | Function,+,subghz_receiver_set_filter,void,"SubGhzReceiver*, SubGhzProtocolFlag" | ||||||
| Function,+,subghz_receiver_set_rx_callback,void,"SubGhzReceiver*, SubGhzReceiverCallback, void*" | Function,+,subghz_receiver_set_rx_callback,void,"SubGhzReceiver*, SubGhzReceiverCallback, void*" | ||||||
|  | Function,+,subghz_setting_alloc,SubGhzSetting*, | ||||||
|  | Function,+,subghz_setting_delete_custom_preset,_Bool,"SubGhzSetting*, const char*" | ||||||
|  | Function,+,subghz_setting_free,void,SubGhzSetting* | ||||||
|  | Function,+,subghz_setting_get_default_frequency,uint32_t,SubGhzSetting* | ||||||
|  | Function,+,subghz_setting_get_frequency,uint32_t,"SubGhzSetting*, size_t" | ||||||
|  | Function,+,subghz_setting_get_frequency_count,size_t,SubGhzSetting* | ||||||
|  | Function,+,subghz_setting_get_frequency_default_index,uint32_t,SubGhzSetting* | ||||||
|  | Function,+,subghz_setting_get_hopper_frequency,uint32_t,"SubGhzSetting*, size_t" | ||||||
|  | Function,+,subghz_setting_get_hopper_frequency_count,size_t,SubGhzSetting* | ||||||
|  | Function,+,subghz_setting_get_inx_preset_by_name,int,"SubGhzSetting*, const char*" | ||||||
|  | Function,+,subghz_setting_get_preset_count,size_t,SubGhzSetting* | ||||||
|  | Function,+,subghz_setting_get_preset_data,uint8_t*,"SubGhzSetting*, size_t" | ||||||
|  | Function,+,subghz_setting_get_preset_data_by_name,uint8_t*,"SubGhzSetting*, const char*" | ||||||
|  | Function,+,subghz_setting_get_preset_data_size,size_t,"SubGhzSetting*, size_t" | ||||||
|  | Function,+,subghz_setting_get_preset_name,const char*,"SubGhzSetting*, size_t" | ||||||
|  | Function,+,subghz_setting_load,void,"SubGhzSetting*, const char*" | ||||||
|  | Function,+,subghz_setting_load_custom_preset,_Bool,"SubGhzSetting*, const char*, FlipperFormat*" | ||||||
| Function,+,subghz_transmitter_alloc_init,SubGhzTransmitter*,"SubGhzEnvironment*, const char*" | Function,+,subghz_transmitter_alloc_init,SubGhzTransmitter*,"SubGhzEnvironment*, const char*" | ||||||
| Function,+,subghz_transmitter_deserialize,_Bool,"SubGhzTransmitter*, FlipperFormat*" | Function,+,subghz_transmitter_deserialize,_Bool,"SubGhzTransmitter*, FlipperFormat*" | ||||||
| Function,+,subghz_transmitter_free,void,SubGhzTransmitter* | Function,+,subghz_transmitter_free,void,SubGhzTransmitter* | ||||||
|  | |||||||
| 
 | 
| @ -11,6 +11,12 @@ env.Append( | |||||||
|         File("#/lib/subghz/subghz_tx_rx_worker.h"), |         File("#/lib/subghz/subghz_tx_rx_worker.h"), | ||||||
|         File("#/lib/subghz/transmitter.h"), |         File("#/lib/subghz/transmitter.h"), | ||||||
|         File("#/lib/subghz/protocols/raw.h"), |         File("#/lib/subghz/protocols/raw.h"), | ||||||
|  |         File("#/lib/subghz/blocks/const.h"), | ||||||
|  |         File("#/lib/subghz/blocks/decoder.h"), | ||||||
|  |         File("#/lib/subghz/blocks/encoder.h"), | ||||||
|  |         File("#/lib/subghz/blocks/generic.h"), | ||||||
|  |         File("#/lib/subghz/blocks/math.h"), | ||||||
|  |         File("#/lib/subghz/subghz_setting.h"), | ||||||
|     ], |     ], | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -4,9 +4,17 @@ | |||||||
| #include <stdint.h> | #include <stdint.h> | ||||||
| #include <stddef.h> | #include <stddef.h> | ||||||
| 
 | 
 | ||||||
|  | #ifdef __cplusplus | ||||||
|  | extern "C" { | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
| typedef struct { | typedef struct { | ||||||
|     const uint16_t te_long; |     const uint16_t te_long; | ||||||
|     const uint16_t te_short; |     const uint16_t te_short; | ||||||
|     const uint16_t te_delta; |     const uint16_t te_delta; | ||||||
|     const uint8_t min_count_bit_for_found; |     const uint8_t min_count_bit_for_found; | ||||||
| } SubGhzBlockConst; | } SubGhzBlockConst; | ||||||
|  | 
 | ||||||
|  | #ifdef __cplusplus | ||||||
|  | } | ||||||
|  | #endif | ||||||
| @ -4,6 +4,10 @@ | |||||||
| #include <stdint.h> | #include <stdint.h> | ||||||
| #include <stddef.h> | #include <stddef.h> | ||||||
| 
 | 
 | ||||||
|  | #ifdef __cplusplus | ||||||
|  | extern "C" { | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
| typedef struct SubGhzBlockDecoder SubGhzBlockDecoder; | typedef struct SubGhzBlockDecoder SubGhzBlockDecoder; | ||||||
| 
 | 
 | ||||||
| struct SubGhzBlockDecoder { | struct SubGhzBlockDecoder { | ||||||
| @ -26,3 +30,7 @@ void subghz_protocol_blocks_add_bit(SubGhzBlockDecoder* decoder, uint8_t bit); | |||||||
|  * @return hash Hash sum |  * @return hash Hash sum | ||||||
|  */ |  */ | ||||||
| uint8_t subghz_protocol_blocks_get_hash_data(SubGhzBlockDecoder* decoder, size_t len); | uint8_t subghz_protocol_blocks_get_hash_data(SubGhzBlockDecoder* decoder, size_t len); | ||||||
|  | 
 | ||||||
|  | #ifdef __cplusplus | ||||||
|  | } | ||||||
|  | #endif | ||||||
| @ -6,6 +6,10 @@ | |||||||
| 
 | 
 | ||||||
| #include <lib/toolbox/level_duration.h> | #include <lib/toolbox/level_duration.h> | ||||||
| 
 | 
 | ||||||
|  | #ifdef __cplusplus | ||||||
|  | extern "C" { | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
| typedef struct { | typedef struct { | ||||||
|     bool is_running; |     bool is_running; | ||||||
|     size_t repeat; |     size_t repeat; | ||||||
| @ -50,3 +54,7 @@ size_t subghz_protocol_blocks_get_upload( | |||||||
|     LevelDuration* upload, |     LevelDuration* upload, | ||||||
|     size_t max_size_upload, |     size_t max_size_upload, | ||||||
|     uint32_t duration_bit); |     uint32_t duration_bit); | ||||||
|  | 
 | ||||||
|  | #ifdef __cplusplus | ||||||
|  | } | ||||||
|  | #endif | ||||||
| @ -23,7 +23,7 @@ void subghz_block_generic_get_preset_name(const char* preset_name, FuriString* p | |||||||
| bool subghz_block_generic_serialize( | bool subghz_block_generic_serialize( | ||||||
|     SubGhzBlockGeneric* instance, |     SubGhzBlockGeneric* instance, | ||||||
|     FlipperFormat* flipper_format, |     FlipperFormat* flipper_format, | ||||||
|     SubGhzPresetDefinition* preset) { |     SubGhzRadioPreset* preset) { | ||||||
|     furi_assert(instance); |     furi_assert(instance); | ||||||
|     bool res = false; |     bool res = false; | ||||||
|     FuriString* temp_str; |     FuriString* temp_str; | ||||||
|  | |||||||
| @ -9,6 +9,10 @@ | |||||||
| #include "furi_hal.h" | #include "furi_hal.h" | ||||||
| #include "../types.h" | #include "../types.h" | ||||||
| 
 | 
 | ||||||
|  | #ifdef __cplusplus | ||||||
|  | extern "C" { | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
| typedef struct SubGhzBlockGeneric SubGhzBlockGeneric; | typedef struct SubGhzBlockGeneric SubGhzBlockGeneric; | ||||||
| 
 | 
 | ||||||
| struct SubGhzBlockGeneric { | struct SubGhzBlockGeneric { | ||||||
| @ -31,13 +35,13 @@ void subghz_block_generic_get_preset_name(const char* preset_name, FuriString* p | |||||||
|  * Serialize data SubGhzBlockGeneric. |  * Serialize data SubGhzBlockGeneric. | ||||||
|  * @param instance Pointer to a SubGhzBlockGeneric instance |  * @param instance Pointer to a SubGhzBlockGeneric instance | ||||||
|  * @param flipper_format Pointer to a FlipperFormat instance |  * @param flipper_format Pointer to a FlipperFormat instance | ||||||
|  * @param preset The modulation on which the signal was received, SubGhzPresetDefinition |  * @param preset The modulation on which the signal was received, SubGhzRadioPreset | ||||||
|  * @return true On success |  * @return true On success | ||||||
|  */ |  */ | ||||||
| bool subghz_block_generic_serialize( | bool subghz_block_generic_serialize( | ||||||
|     SubGhzBlockGeneric* instance, |     SubGhzBlockGeneric* instance, | ||||||
|     FlipperFormat* flipper_format, |     FlipperFormat* flipper_format, | ||||||
|     SubGhzPresetDefinition* preset); |     SubGhzRadioPreset* preset); | ||||||
| 
 | 
 | ||||||
| /**
 | /**
 | ||||||
|  * Deserialize data SubGhzBlockGeneric. |  * Deserialize data SubGhzBlockGeneric. | ||||||
| @ -46,3 +50,7 @@ bool subghz_block_generic_serialize( | |||||||
|  * @return true On success |  * @return true On success | ||||||
|  */ |  */ | ||||||
| bool subghz_block_generic_deserialize(SubGhzBlockGeneric* instance, FlipperFormat* flipper_format); | bool subghz_block_generic_deserialize(SubGhzBlockGeneric* instance, FlipperFormat* flipper_format); | ||||||
|  | 
 | ||||||
|  | #ifdef __cplusplus | ||||||
|  | } | ||||||
|  | #endif | ||||||
| @ -15,3 +15,68 @@ uint8_t subghz_protocol_blocks_get_parity(uint64_t key, uint8_t count_bit) { | |||||||
|     } |     } | ||||||
|     return parity & 0x01; |     return parity & 0x01; | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | uint8_t subghz_protocol_blocks_crc4( | ||||||
|  |     uint8_t const message[], | ||||||
|  |     unsigned nBytes, | ||||||
|  |     uint8_t polynomial, | ||||||
|  |     uint8_t init) { | ||||||
|  |     unsigned remainder = init << 4; // LSBs are unused
 | ||||||
|  |     unsigned poly = polynomial << 4; | ||||||
|  |     unsigned bit; | ||||||
|  | 
 | ||||||
|  |     while(nBytes--) { | ||||||
|  |         remainder ^= *message++; | ||||||
|  |         for(bit = 0; bit < 8; bit++) { | ||||||
|  |             if(remainder & 0x80) { | ||||||
|  |                 remainder = (remainder << 1) ^ poly; | ||||||
|  |             } else { | ||||||
|  |                 remainder = (remainder << 1); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     return remainder >> 4 & 0x0f; // discard the LSBs
 | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | uint8_t subghz_protocol_blocks_crc7( | ||||||
|  |     uint8_t const message[], | ||||||
|  |     unsigned nBytes, | ||||||
|  |     uint8_t polynomial, | ||||||
|  |     uint8_t init) { | ||||||
|  |     unsigned remainder = init << 1; // LSB is unused
 | ||||||
|  |     unsigned poly = polynomial << 1; | ||||||
|  |     unsigned byte, bit; | ||||||
|  | 
 | ||||||
|  |     for(byte = 0; byte < nBytes; ++byte) { | ||||||
|  |         remainder ^= message[byte]; | ||||||
|  |         for(bit = 0; bit < 8; ++bit) { | ||||||
|  |             if(remainder & 0x80) { | ||||||
|  |                 remainder = (remainder << 1) ^ poly; | ||||||
|  |             } else { | ||||||
|  |                 remainder = (remainder << 1); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     return remainder >> 1 & 0x7f; // discard the LSB
 | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | uint8_t subghz_protocol_blocks_crc8( | ||||||
|  |     uint8_t const message[], | ||||||
|  |     unsigned nBytes, | ||||||
|  |     uint8_t polynomial, | ||||||
|  |     uint8_t init) { | ||||||
|  |     uint8_t remainder = init; | ||||||
|  |     unsigned byte, bit; | ||||||
|  | 
 | ||||||
|  |     for(byte = 0; byte < nBytes; ++byte) { | ||||||
|  |         remainder ^= message[byte]; | ||||||
|  |         for(bit = 0; bit < 8; ++bit) { | ||||||
|  |             if(remainder & 0x80) { | ||||||
|  |                 remainder = (remainder << 1) ^ polynomial; | ||||||
|  |             } else { | ||||||
|  |                 remainder = (remainder << 1); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     return remainder; | ||||||
|  | } | ||||||
| @ -9,7 +9,11 @@ | |||||||
| #define bit_clear(value, bit) ((value) &= ~(1UL << (bit))) | #define bit_clear(value, bit) ((value) &= ~(1UL << (bit))) | ||||||
| #define bit_write(value, bit, bitvalue) (bitvalue ? bit_set(value, bit) : bit_clear(value, bit)) | #define bit_write(value, bit, bitvalue) (bitvalue ? bit_set(value, bit) : bit_clear(value, bit)) | ||||||
| #define DURATION_DIFF(x, y) ((x < y) ? (y - x) : (x - y)) | #define DURATION_DIFF(x, y) ((x < y) ? (y - x) : (x - y)) | ||||||
|  | #define abs(x) ((x) > 0 ? (x) : -(x)) | ||||||
| 
 | 
 | ||||||
|  | #ifdef __cplusplus | ||||||
|  | extern "C" { | ||||||
|  | #endif | ||||||
| /**
 | /**
 | ||||||
|  * Flip the data bitwise. |  * Flip the data bitwise. | ||||||
|  * @param key In data |  * @param key In data | ||||||
| @ -25,3 +29,51 @@ uint64_t subghz_protocol_blocks_reverse_key(uint64_t key, uint8_t count_bit); | |||||||
|  * @return parity |  * @return parity | ||||||
|  */ |  */ | ||||||
| uint8_t subghz_protocol_blocks_get_parity(uint64_t key, uint8_t count_bit); | uint8_t subghz_protocol_blocks_get_parity(uint64_t key, uint8_t count_bit); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * CRC-4. | ||||||
|  |  * @param message array of bytes to check | ||||||
|  |  * @param nBytes number of bytes in message | ||||||
|  |  * @param polynomial CRC polynomial | ||||||
|  |  * @param init starting crc value | ||||||
|  |  * @return CRC value | ||||||
|  |  */ | ||||||
|  | uint8_t subghz_protocol_blocks_crc4( | ||||||
|  |     uint8_t const message[], | ||||||
|  |     unsigned nBytes, | ||||||
|  |     uint8_t polynomial, | ||||||
|  |     uint8_t init); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * CRC-7. | ||||||
|  |  * @param message array of bytes to check | ||||||
|  |  * @param nBytes number of bytes in message | ||||||
|  |  * @param polynomial CRC polynomial | ||||||
|  |  * @param init starting crc value | ||||||
|  |  * @return CRC value | ||||||
|  |  */ | ||||||
|  | uint8_t subghz_protocol_blocks_crc7( | ||||||
|  |     uint8_t const message[], | ||||||
|  |     unsigned nBytes, | ||||||
|  |     uint8_t polynomial, | ||||||
|  |     uint8_t init); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Generic Cyclic Redundancy Check CRC-8. | ||||||
|  |  * Example polynomial: 0x31 = x8 + x5 + x4 + 1 (x8 is implicit) | ||||||
|  |  * Example polynomial: 0x80 = x8 + x7 (a normal bit-by-bit parity XOR) | ||||||
|  |  * @param message array of bytes to check | ||||||
|  |  * @param nBytes number of bytes in message | ||||||
|  |  * @param polynomial byte is from x^7 to x^0 (x^8 is implicitly one) | ||||||
|  |  * @param init starting crc value | ||||||
|  |  * @return CRC value | ||||||
|  |  */ | ||||||
|  | uint8_t subghz_protocol_blocks_crc8( | ||||||
|  |     uint8_t const message[], | ||||||
|  |     unsigned nBytes, | ||||||
|  |     uint8_t polynomial, | ||||||
|  |     uint8_t init); | ||||||
|  | 
 | ||||||
|  | #ifdef __cplusplus | ||||||
|  | } | ||||||
|  | #endif | ||||||
|  | |||||||
| @ -1,7 +1,9 @@ | |||||||
| #include "environment.h" | #include "environment.h" | ||||||
|  | #include "registry.h" | ||||||
| 
 | 
 | ||||||
| struct SubGhzEnvironment { | struct SubGhzEnvironment { | ||||||
|     SubGhzKeystore* keystore; |     SubGhzKeystore* keystore; | ||||||
|  |     const SubGhzProtocolRegistry* protocol_registry; | ||||||
|     const char* came_atomo_rainbow_table_file_name; |     const char* came_atomo_rainbow_table_file_name; | ||||||
|     const char* nice_flor_s_rainbow_table_file_name; |     const char* nice_flor_s_rainbow_table_file_name; | ||||||
| }; | }; | ||||||
| @ -10,6 +12,7 @@ SubGhzEnvironment* subghz_environment_alloc() { | |||||||
|     SubGhzEnvironment* instance = malloc(sizeof(SubGhzEnvironment)); |     SubGhzEnvironment* instance = malloc(sizeof(SubGhzEnvironment)); | ||||||
| 
 | 
 | ||||||
|     instance->keystore = subghz_keystore_alloc(); |     instance->keystore = subghz_keystore_alloc(); | ||||||
|  |     instance->protocol_registry = NULL; | ||||||
|     instance->came_atomo_rainbow_table_file_name = NULL; |     instance->came_atomo_rainbow_table_file_name = NULL; | ||||||
|     instance->nice_flor_s_rainbow_table_file_name = NULL; |     instance->nice_flor_s_rainbow_table_file_name = NULL; | ||||||
| 
 | 
 | ||||||
| @ -19,6 +22,9 @@ SubGhzEnvironment* subghz_environment_alloc() { | |||||||
| void subghz_environment_free(SubGhzEnvironment* instance) { | void subghz_environment_free(SubGhzEnvironment* instance) { | ||||||
|     furi_assert(instance); |     furi_assert(instance); | ||||||
| 
 | 
 | ||||||
|  |     instance->protocol_registry = NULL; | ||||||
|  |     instance->came_atomo_rainbow_table_file_name = NULL; | ||||||
|  |     instance->nice_flor_s_rainbow_table_file_name = NULL; | ||||||
|     subghz_keystore_free(instance->keystore); |     subghz_keystore_free(instance->keystore); | ||||||
| 
 | 
 | ||||||
|     free(instance); |     free(instance); | ||||||
| @ -65,3 +71,30 @@ const char* | |||||||
| 
 | 
 | ||||||
|     return instance->nice_flor_s_rainbow_table_file_name; |     return instance->nice_flor_s_rainbow_table_file_name; | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | void subghz_environment_set_protocol_registry( | ||||||
|  |     SubGhzEnvironment* instance, | ||||||
|  |     void* protocol_registry_items) { | ||||||
|  |     furi_assert(instance); | ||||||
|  |     const SubGhzProtocolRegistry* protocol_registry = protocol_registry_items; | ||||||
|  |     instance->protocol_registry = protocol_registry; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void* subghz_environment_get_protocol_registry(SubGhzEnvironment* instance) { | ||||||
|  |     furi_assert(instance); | ||||||
|  |     furi_assert(instance->protocol_registry); | ||||||
|  |     return (void*)instance->protocol_registry; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | const char* | ||||||
|  |     subghz_environment_get_protocol_name_registry(SubGhzEnvironment* instance, size_t idx) { | ||||||
|  |     furi_assert(instance); | ||||||
|  |     furi_assert(instance->protocol_registry); | ||||||
|  |     const SubGhzProtocol* protocol = | ||||||
|  |         subghz_protocol_registry_get_by_index(instance->protocol_registry, idx); | ||||||
|  |     if(protocol != NULL) { | ||||||
|  |         return protocol->name; | ||||||
|  |     } else { | ||||||
|  |         return NULL; | ||||||
|  |     } | ||||||
|  | } | ||||||
| @ -69,6 +69,30 @@ void subghz_environment_set_nice_flor_s_rainbow_table_file_name( | |||||||
| const char* | const char* | ||||||
|     subghz_environment_get_nice_flor_s_rainbow_table_file_name(SubGhzEnvironment* instance); |     subghz_environment_get_nice_flor_s_rainbow_table_file_name(SubGhzEnvironment* instance); | ||||||
| 
 | 
 | ||||||
|  | /**
 | ||||||
|  |  * Set list of protocols to work. | ||||||
|  |  * @param instance Pointer to a SubGhzEnvironment instance | ||||||
|  |  * @param protocol_registry_items Pointer to a SubGhzProtocolRegistry | ||||||
|  |  */ | ||||||
|  | void subghz_environment_set_protocol_registry( | ||||||
|  |     SubGhzEnvironment* instance, | ||||||
|  |     void* protocol_registry_items); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Get list of protocols to work. | ||||||
|  |  * @param instance Pointer to a SubGhzEnvironment instance | ||||||
|  |  * @return Pointer to a SubGhzProtocolRegistry | ||||||
|  |  */ | ||||||
|  | void* subghz_environment_get_protocol_registry(SubGhzEnvironment* instance); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Get list of protocols names. | ||||||
|  |  * @param instance Pointer to a SubGhzEnvironment instance | ||||||
|  |  * @param idx index protocols | ||||||
|  |  * @return Pointer to a SubGhzProtocolRegistry | ||||||
|  |  */ | ||||||
|  | const char* subghz_environment_get_protocol_name_registry(SubGhzEnvironment* instance, size_t idx); | ||||||
|  | 
 | ||||||
| #ifdef __cplusplus | #ifdef __cplusplus | ||||||
| } | } | ||||||
| #endif | #endif | ||||||
|  | |||||||
| @ -26,7 +26,7 @@ bool subghz_protocol_decoder_base_get_string( | |||||||
| bool subghz_protocol_decoder_base_serialize( | bool subghz_protocol_decoder_base_serialize( | ||||||
|     SubGhzProtocolDecoderBase* decoder_base, |     SubGhzProtocolDecoderBase* decoder_base, | ||||||
|     FlipperFormat* flipper_format, |     FlipperFormat* flipper_format, | ||||||
|     SubGhzPresetDefinition* preset) { |     SubGhzRadioPreset* preset) { | ||||||
|     bool status = false; |     bool status = false; | ||||||
| 
 | 
 | ||||||
|     if(decoder_base->protocol && decoder_base->protocol->decoder && |     if(decoder_base->protocol && decoder_base->protocol->decoder && | ||||||
|  | |||||||
| @ -48,13 +48,13 @@ bool subghz_protocol_decoder_base_get_string( | |||||||
|  * Serialize data SubGhzProtocolDecoderBase. |  * Serialize data SubGhzProtocolDecoderBase. | ||||||
|  * @param decoder_base Pointer to a SubGhzProtocolDecoderBase instance |  * @param decoder_base Pointer to a SubGhzProtocolDecoderBase instance | ||||||
|  * @param flipper_format Pointer to a FlipperFormat instance |  * @param flipper_format Pointer to a FlipperFormat instance | ||||||
|  * @param preset The modulation on which the signal was received, SubGhzPresetDefinition |  * @param preset The modulation on which the signal was received, SubGhzRadioPreset | ||||||
|  * @return true On success |  * @return true On success | ||||||
|  */ |  */ | ||||||
| bool subghz_protocol_decoder_base_serialize( | bool subghz_protocol_decoder_base_serialize( | ||||||
|     SubGhzProtocolDecoderBase* decoder_base, |     SubGhzProtocolDecoderBase* decoder_base, | ||||||
|     FlipperFormat* flipper_format, |     FlipperFormat* flipper_format, | ||||||
|     SubGhzPresetDefinition* preset); |     SubGhzRadioPreset* preset); | ||||||
| 
 | 
 | ||||||
| /**
 | /**
 | ||||||
|  * Deserialize data SubGhzProtocolDecoderBase. |  * Deserialize data SubGhzProtocolDecoderBase. | ||||||
|  | |||||||
| @ -299,7 +299,7 @@ uint8_t subghz_protocol_decoder_bett_get_hash_data(void* context) { | |||||||
| bool subghz_protocol_decoder_bett_serialize( | bool subghz_protocol_decoder_bett_serialize( | ||||||
|     void* context, |     void* context, | ||||||
|     FlipperFormat* flipper_format, |     FlipperFormat* flipper_format, | ||||||
|     SubGhzPresetDefinition* preset) { |     SubGhzRadioPreset* preset) { | ||||||
|     furi_assert(context); |     furi_assert(context); | ||||||
|     SubGhzProtocolDecoderBETT* instance = context; |     SubGhzProtocolDecoderBETT* instance = context; | ||||||
|     return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); |     return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); | ||||||
|  | |||||||
| @ -83,13 +83,13 @@ uint8_t subghz_protocol_decoder_bett_get_hash_data(void* context); | |||||||
|  * Serialize data SubGhzProtocolDecoderBETT. |  * Serialize data SubGhzProtocolDecoderBETT. | ||||||
|  * @param context Pointer to a SubGhzProtocolDecoderBETT instance |  * @param context Pointer to a SubGhzProtocolDecoderBETT instance | ||||||
|  * @param flipper_format Pointer to a FlipperFormat instance |  * @param flipper_format Pointer to a FlipperFormat instance | ||||||
|  * @param preset The modulation on which the signal was received, SubGhzPresetDefinition |  * @param preset The modulation on which the signal was received, SubGhzRadioPreset | ||||||
|  * @return true On success |  * @return true On success | ||||||
|  */ |  */ | ||||||
| bool subghz_protocol_decoder_bett_serialize( | bool subghz_protocol_decoder_bett_serialize( | ||||||
|     void* context, |     void* context, | ||||||
|     FlipperFormat* flipper_format, |     FlipperFormat* flipper_format, | ||||||
|     SubGhzPresetDefinition* preset); |     SubGhzRadioPreset* preset); | ||||||
| 
 | 
 | ||||||
| /**
 | /**
 | ||||||
|  * Deserialize data SubGhzProtocolDecoderBETT. |  * Deserialize data SubGhzProtocolDecoderBETT. | ||||||
|  | |||||||
| @ -295,7 +295,7 @@ uint8_t subghz_protocol_decoder_came_get_hash_data(void* context) { | |||||||
| bool subghz_protocol_decoder_came_serialize( | bool subghz_protocol_decoder_came_serialize( | ||||||
|     void* context, |     void* context, | ||||||
|     FlipperFormat* flipper_format, |     FlipperFormat* flipper_format, | ||||||
|     SubGhzPresetDefinition* preset) { |     SubGhzRadioPreset* preset) { | ||||||
|     furi_assert(context); |     furi_assert(context); | ||||||
|     SubGhzProtocolDecoderCame* instance = context; |     SubGhzProtocolDecoderCame* instance = context; | ||||||
|     return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); |     return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); | ||||||
|  | |||||||
| @ -83,13 +83,13 @@ uint8_t subghz_protocol_decoder_came_get_hash_data(void* context); | |||||||
|  * Serialize data SubGhzProtocolDecoderCame. |  * Serialize data SubGhzProtocolDecoderCame. | ||||||
|  * @param context Pointer to a SubGhzProtocolDecoderCame instance |  * @param context Pointer to a SubGhzProtocolDecoderCame instance | ||||||
|  * @param flipper_format Pointer to a FlipperFormat instance |  * @param flipper_format Pointer to a FlipperFormat instance | ||||||
|  * @param preset The modulation on which the signal was received, SubGhzPresetDefinition |  * @param preset The modulation on which the signal was received, SubGhzRadioPreset | ||||||
|  * @return true On success |  * @return true On success | ||||||
|  */ |  */ | ||||||
| bool subghz_protocol_decoder_came_serialize( | bool subghz_protocol_decoder_came_serialize( | ||||||
|     void* context, |     void* context, | ||||||
|     FlipperFormat* flipper_format, |     FlipperFormat* flipper_format, | ||||||
|     SubGhzPresetDefinition* preset); |     SubGhzRadioPreset* preset); | ||||||
| 
 | 
 | ||||||
| /**
 | /**
 | ||||||
|  * Deserialize data SubGhzProtocolDecoderCame. |  * Deserialize data SubGhzProtocolDecoderCame. | ||||||
|  | |||||||
| @ -301,7 +301,7 @@ uint8_t subghz_protocol_decoder_came_atomo_get_hash_data(void* context) { | |||||||
| bool subghz_protocol_decoder_came_atomo_serialize( | bool subghz_protocol_decoder_came_atomo_serialize( | ||||||
|     void* context, |     void* context, | ||||||
|     FlipperFormat* flipper_format, |     FlipperFormat* flipper_format, | ||||||
|     SubGhzPresetDefinition* preset) { |     SubGhzRadioPreset* preset) { | ||||||
|     furi_assert(context); |     furi_assert(context); | ||||||
|     SubGhzProtocolDecoderCameAtomo* instance = context; |     SubGhzProtocolDecoderCameAtomo* instance = context; | ||||||
|     return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); |     return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); | ||||||
|  | |||||||
| @ -48,13 +48,13 @@ uint8_t subghz_protocol_decoder_came_atomo_get_hash_data(void* context); | |||||||
|  * Serialize data SubGhzProtocolDecoderCameAtomo. |  * Serialize data SubGhzProtocolDecoderCameAtomo. | ||||||
|  * @param context Pointer to a SubGhzProtocolDecoderCameAtomo instance |  * @param context Pointer to a SubGhzProtocolDecoderCameAtomo instance | ||||||
|  * @param flipper_format Pointer to a FlipperFormat instance |  * @param flipper_format Pointer to a FlipperFormat instance | ||||||
|  * @param preset The modulation on which the signal was received, SubGhzPresetDefinition |  * @param preset The modulation on which the signal was received, SubGhzRadioPreset | ||||||
|  * @return true On success |  * @return true On success | ||||||
|  */ |  */ | ||||||
| bool subghz_protocol_decoder_came_atomo_serialize( | bool subghz_protocol_decoder_came_atomo_serialize( | ||||||
|     void* context, |     void* context, | ||||||
|     FlipperFormat* flipper_format, |     FlipperFormat* flipper_format, | ||||||
|     SubGhzPresetDefinition* preset); |     SubGhzRadioPreset* preset); | ||||||
| 
 | 
 | ||||||
| /**
 | /**
 | ||||||
|  * Deserialize data SubGhzProtocolDecoderCameAtomo. |  * Deserialize data SubGhzProtocolDecoderCameAtomo. | ||||||
|  | |||||||
| @ -422,7 +422,7 @@ uint8_t subghz_protocol_decoder_came_twee_get_hash_data(void* context) { | |||||||
| bool subghz_protocol_decoder_came_twee_serialize( | bool subghz_protocol_decoder_came_twee_serialize( | ||||||
|     void* context, |     void* context, | ||||||
|     FlipperFormat* flipper_format, |     FlipperFormat* flipper_format, | ||||||
|     SubGhzPresetDefinition* preset) { |     SubGhzRadioPreset* preset) { | ||||||
|     furi_assert(context); |     furi_assert(context); | ||||||
|     SubGhzProtocolDecoderCameTwee* instance = context; |     SubGhzProtocolDecoderCameTwee* instance = context; | ||||||
|     return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); |     return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); | ||||||
|  | |||||||
| @ -83,13 +83,13 @@ uint8_t subghz_protocol_decoder_came_twee_get_hash_data(void* context); | |||||||
|  * Serialize data SubGhzProtocolDecoderCameTwee. |  * Serialize data SubGhzProtocolDecoderCameTwee. | ||||||
|  * @param context Pointer to a SubGhzProtocolDecoderCameTwee instance |  * @param context Pointer to a SubGhzProtocolDecoderCameTwee instance | ||||||
|  * @param flipper_format Pointer to a FlipperFormat instance |  * @param flipper_format Pointer to a FlipperFormat instance | ||||||
|  * @param preset The modulation on which the signal was received, SubGhzPresetDefinition |  * @param preset The modulation on which the signal was received, SubGhzRadioPreset | ||||||
|  * @return true On success |  * @return true On success | ||||||
|  */ |  */ | ||||||
| bool subghz_protocol_decoder_came_twee_serialize( | bool subghz_protocol_decoder_came_twee_serialize( | ||||||
|     void* context, |     void* context, | ||||||
|     FlipperFormat* flipper_format, |     FlipperFormat* flipper_format, | ||||||
|     SubGhzPresetDefinition* preset); |     SubGhzRadioPreset* preset); | ||||||
| 
 | 
 | ||||||
| /**
 | /**
 | ||||||
|  * Deserialize data SubGhzProtocolDecoderCameTwee. |  * Deserialize data SubGhzProtocolDecoderCameTwee. | ||||||
|  | |||||||
| @ -427,7 +427,7 @@ uint8_t subghz_protocol_decoder_chamb_code_get_hash_data(void* context) { | |||||||
| bool subghz_protocol_decoder_chamb_code_serialize( | bool subghz_protocol_decoder_chamb_code_serialize( | ||||||
|     void* context, |     void* context, | ||||||
|     FlipperFormat* flipper_format, |     FlipperFormat* flipper_format, | ||||||
|     SubGhzPresetDefinition* preset) { |     SubGhzRadioPreset* preset) { | ||||||
|     furi_assert(context); |     furi_assert(context); | ||||||
|     SubGhzProtocolDecoderChamb_Code* instance = context; |     SubGhzProtocolDecoderChamb_Code* instance = context; | ||||||
|     return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); |     return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); | ||||||
|  | |||||||
| @ -83,13 +83,13 @@ uint8_t subghz_protocol_decoder_chamb_code_get_hash_data(void* context); | |||||||
|  * Serialize data SubGhzProtocolDecoderChamb_Code. |  * Serialize data SubGhzProtocolDecoderChamb_Code. | ||||||
|  * @param context Pointer to a SubGhzProtocolDecoderChamb_Code instance |  * @param context Pointer to a SubGhzProtocolDecoderChamb_Code instance | ||||||
|  * @param flipper_format Pointer to a FlipperFormat instance |  * @param flipper_format Pointer to a FlipperFormat instance | ||||||
|  * @param preset The modulation on which the signal was received, SubGhzPresetDefinition |  * @param preset The modulation on which the signal was received, SubGhzRadioPreset | ||||||
|  * @return true On success |  * @return true On success | ||||||
|  */ |  */ | ||||||
| bool subghz_protocol_decoder_chamb_code_serialize( | bool subghz_protocol_decoder_chamb_code_serialize( | ||||||
|     void* context, |     void* context, | ||||||
|     FlipperFormat* flipper_format, |     FlipperFormat* flipper_format, | ||||||
|     SubGhzPresetDefinition* preset); |     SubGhzRadioPreset* preset); | ||||||
| 
 | 
 | ||||||
| /**
 | /**
 | ||||||
|  * Deserialize data SubGhzProtocolDecoderChamb_Code. |  * Deserialize data SubGhzProtocolDecoderChamb_Code. | ||||||
|  | |||||||
| @ -319,7 +319,7 @@ uint8_t subghz_protocol_decoder_clemsa_get_hash_data(void* context) { | |||||||
| bool subghz_protocol_decoder_clemsa_serialize( | bool subghz_protocol_decoder_clemsa_serialize( | ||||||
|     void* context, |     void* context, | ||||||
|     FlipperFormat* flipper_format, |     FlipperFormat* flipper_format, | ||||||
|     SubGhzPresetDefinition* preset) { |     SubGhzRadioPreset* preset) { | ||||||
|     furi_assert(context); |     furi_assert(context); | ||||||
|     SubGhzProtocolDecoderClemsa* instance = context; |     SubGhzProtocolDecoderClemsa* instance = context; | ||||||
|     return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); |     return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); | ||||||
|  | |||||||
| @ -83,13 +83,13 @@ uint8_t subghz_protocol_decoder_clemsa_get_hash_data(void* context); | |||||||
|  * Serialize data SubGhzProtocolDecoderClemsa. |  * Serialize data SubGhzProtocolDecoderClemsa. | ||||||
|  * @param context Pointer to a SubGhzProtocolDecoderClemsa instance |  * @param context Pointer to a SubGhzProtocolDecoderClemsa instance | ||||||
|  * @param flipper_format Pointer to a FlipperFormat instance |  * @param flipper_format Pointer to a FlipperFormat instance | ||||||
|  * @param preset The modulation on which the signal was received, SubGhzPresetDefinition |  * @param preset The modulation on which the signal was received, SubGhzRadioPreset | ||||||
|  * @return true On success |  * @return true On success | ||||||
|  */ |  */ | ||||||
| bool subghz_protocol_decoder_clemsa_serialize( | bool subghz_protocol_decoder_clemsa_serialize( | ||||||
|     void* context, |     void* context, | ||||||
|     FlipperFormat* flipper_format, |     FlipperFormat* flipper_format, | ||||||
|     SubGhzPresetDefinition* preset); |     SubGhzRadioPreset* preset); | ||||||
| 
 | 
 | ||||||
| /**
 | /**
 | ||||||
|  * Deserialize data SubGhzProtocolDecoderClemsa. |  * Deserialize data SubGhzProtocolDecoderClemsa. | ||||||
|  | |||||||
| @ -313,7 +313,7 @@ uint8_t subghz_protocol_decoder_doitrand_get_hash_data(void* context) { | |||||||
| bool subghz_protocol_decoder_doitrand_serialize( | bool subghz_protocol_decoder_doitrand_serialize( | ||||||
|     void* context, |     void* context, | ||||||
|     FlipperFormat* flipper_format, |     FlipperFormat* flipper_format, | ||||||
|     SubGhzPresetDefinition* preset) { |     SubGhzRadioPreset* preset) { | ||||||
|     furi_assert(context); |     furi_assert(context); | ||||||
|     SubGhzProtocolDecoderDoitrand* instance = context; |     SubGhzProtocolDecoderDoitrand* instance = context; | ||||||
|     return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); |     return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); | ||||||
|  | |||||||
| @ -83,13 +83,13 @@ uint8_t subghz_protocol_decoder_doitrand_get_hash_data(void* context); | |||||||
|  * Serialize data SubGhzProtocolDecoderDoitrand. |  * Serialize data SubGhzProtocolDecoderDoitrand. | ||||||
|  * @param context Pointer to a SubGhzProtocolDecoderDoitrand instance |  * @param context Pointer to a SubGhzProtocolDecoderDoitrand instance | ||||||
|  * @param flipper_format Pointer to a FlipperFormat instance |  * @param flipper_format Pointer to a FlipperFormat instance | ||||||
|  * @param preset The modulation on which the signal was received, SubGhzPresetDefinition |  * @param preset The modulation on which the signal was received, SubGhzRadioPreset | ||||||
|  * @return true On success |  * @return true On success | ||||||
|  */ |  */ | ||||||
| bool subghz_protocol_decoder_doitrand_serialize( | bool subghz_protocol_decoder_doitrand_serialize( | ||||||
|     void* context, |     void* context, | ||||||
|     FlipperFormat* flipper_format, |     FlipperFormat* flipper_format, | ||||||
|     SubGhzPresetDefinition* preset); |     SubGhzRadioPreset* preset); | ||||||
| 
 | 
 | ||||||
| /**
 | /**
 | ||||||
|  * Deserialize data SubGhzProtocolDecoderDoitrand. |  * Deserialize data SubGhzProtocolDecoderDoitrand. | ||||||
|  | |||||||
| @ -183,7 +183,7 @@ uint8_t subghz_protocol_decoder_faac_slh_get_hash_data(void* context) { | |||||||
| bool subghz_protocol_decoder_faac_slh_serialize( | bool subghz_protocol_decoder_faac_slh_serialize( | ||||||
|     void* context, |     void* context, | ||||||
|     FlipperFormat* flipper_format, |     FlipperFormat* flipper_format, | ||||||
|     SubGhzPresetDefinition* preset) { |     SubGhzRadioPreset* preset) { | ||||||
|     furi_assert(context); |     furi_assert(context); | ||||||
|     SubGhzProtocolDecoderFaacSLH* instance = context; |     SubGhzProtocolDecoderFaacSLH* instance = context; | ||||||
|     return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); |     return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); | ||||||
|  | |||||||
| @ -49,13 +49,13 @@ uint8_t subghz_protocol_decoder_faac_slh_get_hash_data(void* context); | |||||||
|  * Serialize data SubGhzProtocolDecoderFaacSLH. |  * Serialize data SubGhzProtocolDecoderFaacSLH. | ||||||
|  * @param context Pointer to a SubGhzProtocolDecoderFaacSLH instance |  * @param context Pointer to a SubGhzProtocolDecoderFaacSLH instance | ||||||
|  * @param flipper_format Pointer to a FlipperFormat instance |  * @param flipper_format Pointer to a FlipperFormat instance | ||||||
|  * @param preset The modulation on which the signal was received, SubGhzPresetDefinition |  * @param preset The modulation on which the signal was received, SubGhzRadioPreset | ||||||
|  * @return true On success |  * @return true On success | ||||||
|  */ |  */ | ||||||
| bool subghz_protocol_decoder_faac_slh_serialize( | bool subghz_protocol_decoder_faac_slh_serialize( | ||||||
|     void* context, |     void* context, | ||||||
|     FlipperFormat* flipper_format, |     FlipperFormat* flipper_format, | ||||||
|     SubGhzPresetDefinition* preset); |     SubGhzRadioPreset* preset); | ||||||
| 
 | 
 | ||||||
| /**
 | /**
 | ||||||
|  * Deserialize data SubGhzProtocolDecoderFaacSLH. |  * Deserialize data SubGhzProtocolDecoderFaacSLH. | ||||||
|  | |||||||
| @ -293,7 +293,7 @@ uint8_t subghz_protocol_decoder_gate_tx_get_hash_data(void* context) { | |||||||
| bool subghz_protocol_decoder_gate_tx_serialize( | bool subghz_protocol_decoder_gate_tx_serialize( | ||||||
|     void* context, |     void* context, | ||||||
|     FlipperFormat* flipper_format, |     FlipperFormat* flipper_format, | ||||||
|     SubGhzPresetDefinition* preset) { |     SubGhzRadioPreset* preset) { | ||||||
|     furi_assert(context); |     furi_assert(context); | ||||||
|     SubGhzProtocolDecoderGateTx* instance = context; |     SubGhzProtocolDecoderGateTx* instance = context; | ||||||
|     return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); |     return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); | ||||||
|  | |||||||
| @ -83,13 +83,13 @@ uint8_t subghz_protocol_decoder_gate_tx_get_hash_data(void* context); | |||||||
|  * Serialize data SubGhzProtocolDecoderGateTx. |  * Serialize data SubGhzProtocolDecoderGateTx. | ||||||
|  * @param context Pointer to a SubGhzProtocolDecoderGateTx instance |  * @param context Pointer to a SubGhzProtocolDecoderGateTx instance | ||||||
|  * @param flipper_format Pointer to a FlipperFormat instance |  * @param flipper_format Pointer to a FlipperFormat instance | ||||||
|  * @param preset The modulation on which the signal was received, SubGhzPresetDefinition |  * @param preset The modulation on which the signal was received, SubGhzRadioPreset | ||||||
|  * @return true On success |  * @return true On success | ||||||
|  */ |  */ | ||||||
| bool subghz_protocol_decoder_gate_tx_serialize( | bool subghz_protocol_decoder_gate_tx_serialize( | ||||||
|     void* context, |     void* context, | ||||||
|     FlipperFormat* flipper_format, |     FlipperFormat* flipper_format, | ||||||
|     SubGhzPresetDefinition* preset); |     SubGhzRadioPreset* preset); | ||||||
| 
 | 
 | ||||||
| /**
 | /**
 | ||||||
|  * Deserialize data SubGhzProtocolDecoderGateTx. |  * Deserialize data SubGhzProtocolDecoderGateTx. | ||||||
|  | |||||||
| @ -326,7 +326,7 @@ uint8_t subghz_protocol_decoder_holtek_get_hash_data(void* context) { | |||||||
| bool subghz_protocol_decoder_holtek_serialize( | bool subghz_protocol_decoder_holtek_serialize( | ||||||
|     void* context, |     void* context, | ||||||
|     FlipperFormat* flipper_format, |     FlipperFormat* flipper_format, | ||||||
|     SubGhzPresetDefinition* preset) { |     SubGhzRadioPreset* preset) { | ||||||
|     furi_assert(context); |     furi_assert(context); | ||||||
|     SubGhzProtocolDecoderHoltek* instance = context; |     SubGhzProtocolDecoderHoltek* instance = context; | ||||||
|     return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); |     return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); | ||||||
|  | |||||||
| @ -83,13 +83,13 @@ uint8_t subghz_protocol_decoder_holtek_get_hash_data(void* context); | |||||||
|  * Serialize data SubGhzProtocolDecoderHoltek. |  * Serialize data SubGhzProtocolDecoderHoltek. | ||||||
|  * @param context Pointer to a SubGhzProtocolDecoderHoltek instance |  * @param context Pointer to a SubGhzProtocolDecoderHoltek instance | ||||||
|  * @param flipper_format Pointer to a FlipperFormat instance |  * @param flipper_format Pointer to a FlipperFormat instance | ||||||
|  * @param preset The modulation on which the signal was received, SubGhzPresetDefinition |  * @param preset The modulation on which the signal was received, SubGhzRadioPreset | ||||||
|  * @return true On success |  * @return true On success | ||||||
|  */ |  */ | ||||||
| bool subghz_protocol_decoder_holtek_serialize( | bool subghz_protocol_decoder_holtek_serialize( | ||||||
|     void* context, |     void* context, | ||||||
|     FlipperFormat* flipper_format, |     FlipperFormat* flipper_format, | ||||||
|     SubGhzPresetDefinition* preset); |     SubGhzRadioPreset* preset); | ||||||
| 
 | 
 | ||||||
| /**
 | /**
 | ||||||
|  * Deserialize data SubGhzProtocolDecoderHoltek. |  * Deserialize data SubGhzProtocolDecoderHoltek. | ||||||
|  | |||||||
| @ -348,7 +348,7 @@ uint8_t subghz_protocol_decoder_honeywell_wdb_get_hash_data(void* context) { | |||||||
| bool subghz_protocol_decoder_honeywell_wdb_serialize( | bool subghz_protocol_decoder_honeywell_wdb_serialize( | ||||||
|     void* context, |     void* context, | ||||||
|     FlipperFormat* flipper_format, |     FlipperFormat* flipper_format, | ||||||
|     SubGhzPresetDefinition* preset) { |     SubGhzRadioPreset* preset) { | ||||||
|     furi_assert(context); |     furi_assert(context); | ||||||
|     SubGhzProtocolDecoderHoneywell_WDB* instance = context; |     SubGhzProtocolDecoderHoneywell_WDB* instance = context; | ||||||
|     return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); |     return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); | ||||||
|  | |||||||
| @ -85,13 +85,13 @@ uint8_t subghz_protocol_decoder_honeywell_wdb_get_hash_data(void* context); | |||||||
|  * Serialize data SubGhzProtocolDecoderHoneywell_WDB. |  * Serialize data SubGhzProtocolDecoderHoneywell_WDB. | ||||||
|  * @param context Pointer to a SubGhzProtocolDecoderHoneywell_WDB instance |  * @param context Pointer to a SubGhzProtocolDecoderHoneywell_WDB instance | ||||||
|  * @param flipper_format Pointer to a FlipperFormat instance |  * @param flipper_format Pointer to a FlipperFormat instance | ||||||
|  * @param preset The modulation on which the signal was received, SubGhzPresetDefinition |  * @param preset The modulation on which the signal was received, SubGhzRadioPreset | ||||||
|  * @return true On success |  * @return true On success | ||||||
|  */ |  */ | ||||||
| bool subghz_protocol_decoder_honeywell_wdb_serialize( | bool subghz_protocol_decoder_honeywell_wdb_serialize( | ||||||
|     void* context, |     void* context, | ||||||
|     FlipperFormat* flipper_format, |     FlipperFormat* flipper_format, | ||||||
|     SubGhzPresetDefinition* preset); |     SubGhzRadioPreset* preset); | ||||||
| 
 | 
 | ||||||
| /**
 | /**
 | ||||||
|  * Deserialize data SubGhzProtocolDecoderHoneywell_WDB. |  * Deserialize data SubGhzProtocolDecoderHoneywell_WDB. | ||||||
|  | |||||||
| @ -314,7 +314,7 @@ uint8_t subghz_protocol_decoder_hormann_get_hash_data(void* context) { | |||||||
| bool subghz_protocol_decoder_hormann_serialize( | bool subghz_protocol_decoder_hormann_serialize( | ||||||
|     void* context, |     void* context, | ||||||
|     FlipperFormat* flipper_format, |     FlipperFormat* flipper_format, | ||||||
|     SubGhzPresetDefinition* preset) { |     SubGhzRadioPreset* preset) { | ||||||
|     furi_assert(context); |     furi_assert(context); | ||||||
|     SubGhzProtocolDecoderHormann* instance = context; |     SubGhzProtocolDecoderHormann* instance = context; | ||||||
|     return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); |     return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); | ||||||
|  | |||||||
| @ -83,13 +83,13 @@ uint8_t subghz_protocol_decoder_hormann_get_hash_data(void* context); | |||||||
|  * Serialize data SubGhzProtocolDecoderHormann. |  * Serialize data SubGhzProtocolDecoderHormann. | ||||||
|  * @param context Pointer to a SubGhzProtocolDecoderHormann instance |  * @param context Pointer to a SubGhzProtocolDecoderHormann instance | ||||||
|  * @param flipper_format Pointer to a FlipperFormat instance |  * @param flipper_format Pointer to a FlipperFormat instance | ||||||
|  * @param preset The modulation on which the signal was received, SubGhzPresetDefinition |  * @param preset The modulation on which the signal was received, SubGhzRadioPreset | ||||||
|  * @return true On success |  * @return true On success | ||||||
|  */ |  */ | ||||||
| bool subghz_protocol_decoder_hormann_serialize( | bool subghz_protocol_decoder_hormann_serialize( | ||||||
|     void* context, |     void* context, | ||||||
|     FlipperFormat* flipper_format, |     FlipperFormat* flipper_format, | ||||||
|     SubGhzPresetDefinition* preset); |     SubGhzRadioPreset* preset); | ||||||
| 
 | 
 | ||||||
| /**
 | /**
 | ||||||
|  * Deserialize data SubGhzProtocolDecoderHormann. |  * Deserialize data SubGhzProtocolDecoderHormann. | ||||||
|  | |||||||
| @ -182,7 +182,7 @@ uint8_t subghz_protocol_decoder_ido_get_hash_data(void* context) { | |||||||
| bool subghz_protocol_decoder_ido_serialize( | bool subghz_protocol_decoder_ido_serialize( | ||||||
|     void* context, |     void* context, | ||||||
|     FlipperFormat* flipper_format, |     FlipperFormat* flipper_format, | ||||||
|     SubGhzPresetDefinition* preset) { |     SubGhzRadioPreset* preset) { | ||||||
|     furi_assert(context); |     furi_assert(context); | ||||||
|     SubGhzProtocolDecoderIDo* instance = context; |     SubGhzProtocolDecoderIDo* instance = context; | ||||||
|     return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); |     return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); | ||||||
|  | |||||||
| @ -49,13 +49,13 @@ uint8_t subghz_protocol_decoder_ido_get_hash_data(void* context); | |||||||
|  * Serialize data SubGhzProtocolDecoderIDo. |  * Serialize data SubGhzProtocolDecoderIDo. | ||||||
|  * @param context Pointer to a SubGhzProtocolDecoderIDo instance |  * @param context Pointer to a SubGhzProtocolDecoderIDo instance | ||||||
|  * @param flipper_format Pointer to a FlipperFormat instance |  * @param flipper_format Pointer to a FlipperFormat instance | ||||||
|  * @param preset The modulation on which the signal was received, SubGhzPresetDefinition |  * @param preset The modulation on which the signal was received, SubGhzRadioPreset | ||||||
|  * @return true On success |  * @return true On success | ||||||
|  */ |  */ | ||||||
| bool subghz_protocol_decoder_ido_serialize( | bool subghz_protocol_decoder_ido_serialize( | ||||||
|     void* context, |     void* context, | ||||||
|     FlipperFormat* flipper_format, |     FlipperFormat* flipper_format, | ||||||
|     SubGhzPresetDefinition* preset); |     SubGhzRadioPreset* preset); | ||||||
| 
 | 
 | ||||||
| /**
 | /**
 | ||||||
|  * Deserialize data SubGhzProtocolDecoderIDo. |  * Deserialize data SubGhzProtocolDecoderIDo. | ||||||
|  | |||||||
| @ -407,7 +407,7 @@ uint8_t subghz_protocol_decoder_intertechno_v3_get_hash_data(void* context) { | |||||||
| bool subghz_protocol_decoder_intertechno_v3_serialize( | bool subghz_protocol_decoder_intertechno_v3_serialize( | ||||||
|     void* context, |     void* context, | ||||||
|     FlipperFormat* flipper_format, |     FlipperFormat* flipper_format, | ||||||
|     SubGhzPresetDefinition* preset) { |     SubGhzRadioPreset* preset) { | ||||||
|     furi_assert(context); |     furi_assert(context); | ||||||
|     SubGhzProtocolDecoderIntertechno_V3* instance = context; |     SubGhzProtocolDecoderIntertechno_V3* instance = context; | ||||||
|     return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); |     return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); | ||||||
|  | |||||||
| @ -85,13 +85,13 @@ uint8_t subghz_protocol_decoder_intertechno_v3_get_hash_data(void* context); | |||||||
|  * Serialize data SubGhzProtocolDecoderIntertechno_V3. |  * Serialize data SubGhzProtocolDecoderIntertechno_V3. | ||||||
|  * @param context Pointer to a SubGhzProtocolDecoderIntertechno_V3 instance |  * @param context Pointer to a SubGhzProtocolDecoderIntertechno_V3 instance | ||||||
|  * @param flipper_format Pointer to a FlipperFormat instance |  * @param flipper_format Pointer to a FlipperFormat instance | ||||||
|  * @param preset The modulation on which the signal was received, SubGhzPresetDefinition |  * @param preset The modulation on which the signal was received, SubGhzRadioPreset | ||||||
|  * @return true On success |  * @return true On success | ||||||
|  */ |  */ | ||||||
| bool subghz_protocol_decoder_intertechno_v3_serialize( | bool subghz_protocol_decoder_intertechno_v3_serialize( | ||||||
|     void* context, |     void* context, | ||||||
|     FlipperFormat* flipper_format, |     FlipperFormat* flipper_format, | ||||||
|     SubGhzPresetDefinition* preset); |     SubGhzRadioPreset* preset); | ||||||
| 
 | 
 | ||||||
| /**
 | /**
 | ||||||
|  * Deserialize data SubGhzProtocolDecoderIntertechno_V3. |  * Deserialize data SubGhzProtocolDecoderIntertechno_V3. | ||||||
|  | |||||||
| @ -173,7 +173,7 @@ bool subghz_protocol_keeloq_create_data( | |||||||
|     uint8_t btn, |     uint8_t btn, | ||||||
|     uint16_t cnt, |     uint16_t cnt, | ||||||
|     const char* manufacture_name, |     const char* manufacture_name, | ||||||
|     SubGhzPresetDefinition* preset) { |     SubGhzRadioPreset* preset) { | ||||||
|     furi_assert(context); |     furi_assert(context); | ||||||
|     SubGhzProtocolEncoderKeeloq* instance = context; |     SubGhzProtocolEncoderKeeloq* instance = context; | ||||||
|     instance->generic.serial = serial; |     instance->generic.serial = serial; | ||||||
| @ -646,7 +646,7 @@ uint8_t subghz_protocol_decoder_keeloq_get_hash_data(void* context) { | |||||||
| bool subghz_protocol_decoder_keeloq_serialize( | bool subghz_protocol_decoder_keeloq_serialize( | ||||||
|     void* context, |     void* context, | ||||||
|     FlipperFormat* flipper_format, |     FlipperFormat* flipper_format, | ||||||
|     SubGhzPresetDefinition* preset) { |     SubGhzRadioPreset* preset) { | ||||||
|     furi_assert(context); |     furi_assert(context); | ||||||
|     SubGhzProtocolDecoderKeeloq* instance = context; |     SubGhzProtocolDecoderKeeloq* instance = context; | ||||||
|     subghz_protocol_keeloq_check_remote_controller( |     subghz_protocol_keeloq_check_remote_controller( | ||||||
|  | |||||||
| @ -32,7 +32,7 @@ void subghz_protocol_encoder_keeloq_free(void* context); | |||||||
|  * @param btn Button number, 4 bit |  * @param btn Button number, 4 bit | ||||||
|  * @param cnt Container value, 16 bit |  * @param cnt Container value, 16 bit | ||||||
|  * @param manufacture_name Name of manufacturer's key |  * @param manufacture_name Name of manufacturer's key | ||||||
|  * @param preset Modulation, SubGhzPresetDefinition |  * @param preset Modulation, SubGhzRadioPreset | ||||||
|  * @return true On success |  * @return true On success | ||||||
|  */ |  */ | ||||||
| bool subghz_protocol_keeloq_create_data( | bool subghz_protocol_keeloq_create_data( | ||||||
| @ -42,7 +42,7 @@ bool subghz_protocol_keeloq_create_data( | |||||||
|     uint8_t btn, |     uint8_t btn, | ||||||
|     uint16_t cnt, |     uint16_t cnt, | ||||||
|     const char* manufacture_name, |     const char* manufacture_name, | ||||||
|     SubGhzPresetDefinition* preset); |     SubGhzRadioPreset* preset); | ||||||
| 
 | 
 | ||||||
| /**
 | /**
 | ||||||
|  * Deserialize and generating an upload to send. |  * Deserialize and generating an upload to send. | ||||||
| @ -103,13 +103,13 @@ uint8_t subghz_protocol_decoder_keeloq_get_hash_data(void* context); | |||||||
|  * Serialize data SubGhzProtocolDecoderKeeloq. |  * Serialize data SubGhzProtocolDecoderKeeloq. | ||||||
|  * @param context Pointer to a SubGhzProtocolDecoderKeeloq instance |  * @param context Pointer to a SubGhzProtocolDecoderKeeloq instance | ||||||
|  * @param flipper_format Pointer to a FlipperFormat instance |  * @param flipper_format Pointer to a FlipperFormat instance | ||||||
|  * @param preset The modulation on which the signal was received, SubGhzPresetDefinition |  * @param preset The modulation on which the signal was received, SubGhzRadioPreset | ||||||
|  * @return true On success |  * @return true On success | ||||||
|  */ |  */ | ||||||
| bool subghz_protocol_decoder_keeloq_serialize( | bool subghz_protocol_decoder_keeloq_serialize( | ||||||
|     void* context, |     void* context, | ||||||
|     FlipperFormat* flipper_format, |     FlipperFormat* flipper_format, | ||||||
|     SubGhzPresetDefinition* preset); |     SubGhzRadioPreset* preset); | ||||||
| 
 | 
 | ||||||
| /**
 | /**
 | ||||||
|  * Deserialize data SubGhzProtocolDecoderKeeloq. |  * Deserialize data SubGhzProtocolDecoderKeeloq. | ||||||
|  | |||||||
| @ -233,7 +233,7 @@ uint8_t subghz_protocol_decoder_kia_get_hash_data(void* context) { | |||||||
| bool subghz_protocol_decoder_kia_serialize( | bool subghz_protocol_decoder_kia_serialize( | ||||||
|     void* context, |     void* context, | ||||||
|     FlipperFormat* flipper_format, |     FlipperFormat* flipper_format, | ||||||
|     SubGhzPresetDefinition* preset) { |     SubGhzRadioPreset* preset) { | ||||||
|     furi_assert(context); |     furi_assert(context); | ||||||
|     SubGhzProtocolDecoderKIA* instance = context; |     SubGhzProtocolDecoderKIA* instance = context; | ||||||
|     return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); |     return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); | ||||||
|  | |||||||
| @ -49,13 +49,13 @@ uint8_t subghz_protocol_decoder_kia_get_hash_data(void* context); | |||||||
|  * Serialize data SubGhzProtocolDecoderKIA. |  * Serialize data SubGhzProtocolDecoderKIA. | ||||||
|  * @param context Pointer to a SubGhzProtocolDecoderKIA instance |  * @param context Pointer to a SubGhzProtocolDecoderKIA instance | ||||||
|  * @param flipper_format Pointer to a FlipperFormat instance |  * @param flipper_format Pointer to a FlipperFormat instance | ||||||
|  * @param preset The modulation on which the signal was received, SubGhzPresetDefinition |  * @param preset The modulation on which the signal was received, SubGhzRadioPreset | ||||||
|  * @return true On success |  * @return true On success | ||||||
|  */ |  */ | ||||||
| bool subghz_protocol_decoder_kia_serialize( | bool subghz_protocol_decoder_kia_serialize( | ||||||
|     void* context, |     void* context, | ||||||
|     FlipperFormat* flipper_format, |     FlipperFormat* flipper_format, | ||||||
|     SubGhzPresetDefinition* preset); |     SubGhzRadioPreset* preset); | ||||||
| 
 | 
 | ||||||
| /**
 | /**
 | ||||||
|  * Deserialize data SubGhzProtocolDecoderKIA. |  * Deserialize data SubGhzProtocolDecoderKIA. | ||||||
|  | |||||||
| @ -303,7 +303,7 @@ uint8_t subghz_protocol_decoder_linear_get_hash_data(void* context) { | |||||||
| bool subghz_protocol_decoder_linear_serialize( | bool subghz_protocol_decoder_linear_serialize( | ||||||
|     void* context, |     void* context, | ||||||
|     FlipperFormat* flipper_format, |     FlipperFormat* flipper_format, | ||||||
|     SubGhzPresetDefinition* preset) { |     SubGhzRadioPreset* preset) { | ||||||
|     furi_assert(context); |     furi_assert(context); | ||||||
|     SubGhzProtocolDecoderLinear* instance = context; |     SubGhzProtocolDecoderLinear* instance = context; | ||||||
|     return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); |     return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); | ||||||
|  | |||||||
| @ -83,13 +83,13 @@ uint8_t subghz_protocol_decoder_linear_get_hash_data(void* context); | |||||||
|  * Serialize data SubGhzProtocolDecoderLinear. |  * Serialize data SubGhzProtocolDecoderLinear. | ||||||
|  * @param context Pointer to a SubGhzProtocolDecoderLinear instance |  * @param context Pointer to a SubGhzProtocolDecoderLinear instance | ||||||
|  * @param flipper_format Pointer to a FlipperFormat instance |  * @param flipper_format Pointer to a FlipperFormat instance | ||||||
|  * @param preset The modulation on which the signal was received, SubGhzPresetDefinition |  * @param preset The modulation on which the signal was received, SubGhzRadioPreset | ||||||
|  * @return true On success |  * @return true On success | ||||||
|  */ |  */ | ||||||
| bool subghz_protocol_decoder_linear_serialize( | bool subghz_protocol_decoder_linear_serialize( | ||||||
|     void* context, |     void* context, | ||||||
|     FlipperFormat* flipper_format, |     FlipperFormat* flipper_format, | ||||||
|     SubGhzPresetDefinition* preset); |     SubGhzRadioPreset* preset); | ||||||
| 
 | 
 | ||||||
| /**
 | /**
 | ||||||
|  * Deserialize data SubGhzProtocolDecoderLinear. |  * Deserialize data SubGhzProtocolDecoderLinear. | ||||||
|  | |||||||
| @ -1,4 +1,4 @@ | |||||||
| #include "magellen.h" | #include "magellan.h" | ||||||
| 
 | 
 | ||||||
| #include "../blocks/const.h" | #include "../blocks/const.h" | ||||||
| #include "../blocks/decoder.h" | #include "../blocks/decoder.h" | ||||||
| @ -6,16 +6,16 @@ | |||||||
| #include "../blocks/generic.h" | #include "../blocks/generic.h" | ||||||
| #include "../blocks/math.h" | #include "../blocks/math.h" | ||||||
| 
 | 
 | ||||||
| #define TAG "SubGhzProtocolMagellen" | #define TAG "SubGhzProtocolMagellan" | ||||||
| 
 | 
 | ||||||
| static const SubGhzBlockConst subghz_protocol_magellen_const = { | static const SubGhzBlockConst subghz_protocol_magellan_const = { | ||||||
|     .te_short = 200, |     .te_short = 200, | ||||||
|     .te_long = 400, |     .te_long = 400, | ||||||
|     .te_delta = 100, |     .te_delta = 100, | ||||||
|     .min_count_bit_for_found = 32, |     .min_count_bit_for_found = 32, | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| struct SubGhzProtocolDecoderMagellen { | struct SubGhzProtocolDecoderMagellan { | ||||||
|     SubGhzProtocolDecoderBase base; |     SubGhzProtocolDecoderBase base; | ||||||
| 
 | 
 | ||||||
|     SubGhzBlockDecoder decoder; |     SubGhzBlockDecoder decoder; | ||||||
| @ -23,7 +23,7 @@ struct SubGhzProtocolDecoderMagellen { | |||||||
|     uint16_t header_count; |     uint16_t header_count; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| struct SubGhzProtocolEncoderMagellen { | struct SubGhzProtocolEncoderMagellan { | ||||||
|     SubGhzProtocolEncoderBase base; |     SubGhzProtocolEncoderBase base; | ||||||
| 
 | 
 | ||||||
|     SubGhzProtocolBlockEncoder encoder; |     SubGhzProtocolBlockEncoder encoder; | ||||||
| @ -31,50 +31,50 @@ struct SubGhzProtocolEncoderMagellen { | |||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| typedef enum { | typedef enum { | ||||||
|     MagellenDecoderStepReset = 0, |     MagellanDecoderStepReset = 0, | ||||||
|     MagellenDecoderStepCheckPreambula, |     MagellanDecoderStepCheckPreambula, | ||||||
|     MagellenDecoderStepFoundPreambula, |     MagellanDecoderStepFoundPreambula, | ||||||
|     MagellenDecoderStepSaveDuration, |     MagellanDecoderStepSaveDuration, | ||||||
|     MagellenDecoderStepCheckDuration, |     MagellanDecoderStepCheckDuration, | ||||||
| } MagellenDecoderStep; | } MagellanDecoderStep; | ||||||
| 
 | 
 | ||||||
| const SubGhzProtocolDecoder subghz_protocol_magellen_decoder = { | const SubGhzProtocolDecoder subghz_protocol_magellan_decoder = { | ||||||
|     .alloc = subghz_protocol_decoder_magellen_alloc, |     .alloc = subghz_protocol_decoder_magellan_alloc, | ||||||
|     .free = subghz_protocol_decoder_magellen_free, |     .free = subghz_protocol_decoder_magellan_free, | ||||||
| 
 | 
 | ||||||
|     .feed = subghz_protocol_decoder_magellen_feed, |     .feed = subghz_protocol_decoder_magellan_feed, | ||||||
|     .reset = subghz_protocol_decoder_magellen_reset, |     .reset = subghz_protocol_decoder_magellan_reset, | ||||||
| 
 | 
 | ||||||
|     .get_hash_data = subghz_protocol_decoder_magellen_get_hash_data, |     .get_hash_data = subghz_protocol_decoder_magellan_get_hash_data, | ||||||
|     .serialize = subghz_protocol_decoder_magellen_serialize, |     .serialize = subghz_protocol_decoder_magellan_serialize, | ||||||
|     .deserialize = subghz_protocol_decoder_magellen_deserialize, |     .deserialize = subghz_protocol_decoder_magellan_deserialize, | ||||||
|     .get_string = subghz_protocol_decoder_magellen_get_string, |     .get_string = subghz_protocol_decoder_magellan_get_string, | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| const SubGhzProtocolEncoder subghz_protocol_magellen_encoder = { | const SubGhzProtocolEncoder subghz_protocol_magellan_encoder = { | ||||||
|     .alloc = subghz_protocol_encoder_magellen_alloc, |     .alloc = subghz_protocol_encoder_magellan_alloc, | ||||||
|     .free = subghz_protocol_encoder_magellen_free, |     .free = subghz_protocol_encoder_magellan_free, | ||||||
| 
 | 
 | ||||||
|     .deserialize = subghz_protocol_encoder_magellen_deserialize, |     .deserialize = subghz_protocol_encoder_magellan_deserialize, | ||||||
|     .stop = subghz_protocol_encoder_magellen_stop, |     .stop = subghz_protocol_encoder_magellan_stop, | ||||||
|     .yield = subghz_protocol_encoder_magellen_yield, |     .yield = subghz_protocol_encoder_magellan_yield, | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| const SubGhzProtocol subghz_protocol_magellen = { | const SubGhzProtocol subghz_protocol_magellan = { | ||||||
|     .name = SUBGHZ_PROTOCOL_MAGELLEN_NAME, |     .name = SUBGHZ_PROTOCOL_MAGELLAN_NAME, | ||||||
|     .type = SubGhzProtocolTypeStatic, |     .type = SubGhzProtocolTypeStatic, | ||||||
|     .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable | |     .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable | | ||||||
|             SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Send, |             SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Send, | ||||||
| 
 | 
 | ||||||
|     .decoder = &subghz_protocol_magellen_decoder, |     .decoder = &subghz_protocol_magellan_decoder, | ||||||
|     .encoder = &subghz_protocol_magellen_encoder, |     .encoder = &subghz_protocol_magellan_encoder, | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| void* subghz_protocol_encoder_magellen_alloc(SubGhzEnvironment* environment) { | void* subghz_protocol_encoder_magellan_alloc(SubGhzEnvironment* environment) { | ||||||
|     UNUSED(environment); |     UNUSED(environment); | ||||||
|     SubGhzProtocolEncoderMagellen* instance = malloc(sizeof(SubGhzProtocolEncoderMagellen)); |     SubGhzProtocolEncoderMagellan* instance = malloc(sizeof(SubGhzProtocolEncoderMagellan)); | ||||||
| 
 | 
 | ||||||
|     instance->base.protocol = &subghz_protocol_magellen; |     instance->base.protocol = &subghz_protocol_magellan; | ||||||
|     instance->generic.protocol_name = instance->base.protocol->name; |     instance->generic.protocol_name = instance->base.protocol->name; | ||||||
| 
 | 
 | ||||||
|     instance->encoder.repeat = 10; |     instance->encoder.repeat = 10; | ||||||
| @ -84,75 +84,75 @@ void* subghz_protocol_encoder_magellen_alloc(SubGhzEnvironment* environment) { | |||||||
|     return instance; |     return instance; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void subghz_protocol_encoder_magellen_free(void* context) { | void subghz_protocol_encoder_magellan_free(void* context) { | ||||||
|     furi_assert(context); |     furi_assert(context); | ||||||
|     SubGhzProtocolEncoderMagellen* instance = context; |     SubGhzProtocolEncoderMagellan* instance = context; | ||||||
|     free(instance->encoder.upload); |     free(instance->encoder.upload); | ||||||
|     free(instance); |     free(instance); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /**
 | /**
 | ||||||
|  * Generating an upload from data. |  * Generating an upload from data. | ||||||
|  * @param instance Pointer to a SubGhzProtocolEncoderMagellen instance |  * @param instance Pointer to a SubGhzProtocolEncoderMagellan instance | ||||||
|  * @return true On success |  * @return true On success | ||||||
|  */ |  */ | ||||||
| static bool subghz_protocol_encoder_magellen_get_upload(SubGhzProtocolEncoderMagellen* instance) { | static bool subghz_protocol_encoder_magellan_get_upload(SubGhzProtocolEncoderMagellan* instance) { | ||||||
|     furi_assert(instance); |     furi_assert(instance); | ||||||
| 
 | 
 | ||||||
|     size_t index = 0; |     size_t index = 0; | ||||||
| 
 | 
 | ||||||
|     //Send header
 |     //Send header
 | ||||||
|     instance->encoder.upload[index++] = |     instance->encoder.upload[index++] = | ||||||
|         level_duration_make(true, (uint32_t)subghz_protocol_magellen_const.te_short * 4); |         level_duration_make(true, (uint32_t)subghz_protocol_magellan_const.te_short * 4); | ||||||
|     instance->encoder.upload[index++] = |     instance->encoder.upload[index++] = | ||||||
|         level_duration_make(false, (uint32_t)subghz_protocol_magellen_const.te_short); |         level_duration_make(false, (uint32_t)subghz_protocol_magellan_const.te_short); | ||||||
|     for(uint8_t i = 0; i < 12; i++) { |     for(uint8_t i = 0; i < 12; i++) { | ||||||
|         instance->encoder.upload[index++] = |         instance->encoder.upload[index++] = | ||||||
|             level_duration_make(true, (uint32_t)subghz_protocol_magellen_const.te_short); |             level_duration_make(true, (uint32_t)subghz_protocol_magellan_const.te_short); | ||||||
|         instance->encoder.upload[index++] = |         instance->encoder.upload[index++] = | ||||||
|             level_duration_make(false, (uint32_t)subghz_protocol_magellen_const.te_short); |             level_duration_make(false, (uint32_t)subghz_protocol_magellan_const.te_short); | ||||||
|     } |     } | ||||||
|     instance->encoder.upload[index++] = |     instance->encoder.upload[index++] = | ||||||
|         level_duration_make(true, (uint32_t)subghz_protocol_magellen_const.te_short); |         level_duration_make(true, (uint32_t)subghz_protocol_magellan_const.te_short); | ||||||
|     instance->encoder.upload[index++] = |     instance->encoder.upload[index++] = | ||||||
|         level_duration_make(false, (uint32_t)subghz_protocol_magellen_const.te_long); |         level_duration_make(false, (uint32_t)subghz_protocol_magellan_const.te_long); | ||||||
| 
 | 
 | ||||||
|     //Send start bit
 |     //Send start bit
 | ||||||
|     instance->encoder.upload[index++] = |     instance->encoder.upload[index++] = | ||||||
|         level_duration_make(true, (uint32_t)subghz_protocol_magellen_const.te_long * 3); |         level_duration_make(true, (uint32_t)subghz_protocol_magellan_const.te_long * 3); | ||||||
|     instance->encoder.upload[index++] = |     instance->encoder.upload[index++] = | ||||||
|         level_duration_make(false, (uint32_t)subghz_protocol_magellen_const.te_long); |         level_duration_make(false, (uint32_t)subghz_protocol_magellan_const.te_long); | ||||||
| 
 | 
 | ||||||
|     //Send key data
 |     //Send key data
 | ||||||
|     for(uint8_t i = instance->generic.data_count_bit; i > 0; i--) { |     for(uint8_t i = instance->generic.data_count_bit; i > 0; i--) { | ||||||
|         if(bit_read(instance->generic.data, i - 1)) { |         if(bit_read(instance->generic.data, i - 1)) { | ||||||
|             //send bit 1
 |             //send bit 1
 | ||||||
|             instance->encoder.upload[index++] = |             instance->encoder.upload[index++] = | ||||||
|                 level_duration_make(true, (uint32_t)subghz_protocol_magellen_const.te_short); |                 level_duration_make(true, (uint32_t)subghz_protocol_magellan_const.te_short); | ||||||
|             instance->encoder.upload[index++] = |             instance->encoder.upload[index++] = | ||||||
|                 level_duration_make(false, (uint32_t)subghz_protocol_magellen_const.te_long); |                 level_duration_make(false, (uint32_t)subghz_protocol_magellan_const.te_long); | ||||||
|         } else { |         } else { | ||||||
|             //send bit 0
 |             //send bit 0
 | ||||||
|             instance->encoder.upload[index++] = |             instance->encoder.upload[index++] = | ||||||
|                 level_duration_make(true, (uint32_t)subghz_protocol_magellen_const.te_long); |                 level_duration_make(true, (uint32_t)subghz_protocol_magellan_const.te_long); | ||||||
|             instance->encoder.upload[index++] = |             instance->encoder.upload[index++] = | ||||||
|                 level_duration_make(false, (uint32_t)subghz_protocol_magellen_const.te_short); |                 level_duration_make(false, (uint32_t)subghz_protocol_magellan_const.te_short); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     //Send stop bit
 |     //Send stop bit
 | ||||||
|     instance->encoder.upload[index++] = |     instance->encoder.upload[index++] = | ||||||
|         level_duration_make(true, (uint32_t)subghz_protocol_magellen_const.te_short); |         level_duration_make(true, (uint32_t)subghz_protocol_magellan_const.te_short); | ||||||
|     instance->encoder.upload[index++] = |     instance->encoder.upload[index++] = | ||||||
|         level_duration_make(false, (uint32_t)subghz_protocol_magellen_const.te_long * 100); |         level_duration_make(false, (uint32_t)subghz_protocol_magellan_const.te_long * 100); | ||||||
| 
 | 
 | ||||||
|     instance->encoder.size_upload = index; |     instance->encoder.size_upload = index; | ||||||
|     return true; |     return true; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| bool subghz_protocol_encoder_magellen_deserialize(void* context, FlipperFormat* flipper_format) { | bool subghz_protocol_encoder_magellan_deserialize(void* context, FlipperFormat* flipper_format) { | ||||||
|     furi_assert(context); |     furi_assert(context); | ||||||
|     SubGhzProtocolEncoderMagellen* instance = context; |     SubGhzProtocolEncoderMagellan* instance = context; | ||||||
|     bool res = false; |     bool res = false; | ||||||
|     do { |     do { | ||||||
|         if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { |         if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { | ||||||
| @ -160,7 +160,7 @@ bool subghz_protocol_encoder_magellen_deserialize(void* context, FlipperFormat* | |||||||
|             break; |             break; | ||||||
|         } |         } | ||||||
|         if(instance->generic.data_count_bit != |         if(instance->generic.data_count_bit != | ||||||
|            subghz_protocol_magellen_const.min_count_bit_for_found) { |            subghz_protocol_magellan_const.min_count_bit_for_found) { | ||||||
|             FURI_LOG_E(TAG, "Wrong number of bits in key"); |             FURI_LOG_E(TAG, "Wrong number of bits in key"); | ||||||
|             break; |             break; | ||||||
|         } |         } | ||||||
| @ -168,7 +168,7 @@ bool subghz_protocol_encoder_magellen_deserialize(void* context, FlipperFormat* | |||||||
|         flipper_format_read_uint32( |         flipper_format_read_uint32( | ||||||
|             flipper_format, "Repeat", (uint32_t*)&instance->encoder.repeat, 1); |             flipper_format, "Repeat", (uint32_t*)&instance->encoder.repeat, 1); | ||||||
| 
 | 
 | ||||||
|         if(!subghz_protocol_encoder_magellen_get_upload(instance)) break; |         if(!subghz_protocol_encoder_magellan_get_upload(instance)) break; | ||||||
|         instance->encoder.is_running = true; |         instance->encoder.is_running = true; | ||||||
| 
 | 
 | ||||||
|         res = true; |         res = true; | ||||||
| @ -177,13 +177,13 @@ bool subghz_protocol_encoder_magellen_deserialize(void* context, FlipperFormat* | |||||||
|     return res; |     return res; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void subghz_protocol_encoder_magellen_stop(void* context) { | void subghz_protocol_encoder_magellan_stop(void* context) { | ||||||
|     SubGhzProtocolEncoderMagellen* instance = context; |     SubGhzProtocolEncoderMagellan* instance = context; | ||||||
|     instance->encoder.is_running = false; |     instance->encoder.is_running = false; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| LevelDuration subghz_protocol_encoder_magellen_yield(void* context) { | LevelDuration subghz_protocol_encoder_magellan_yield(void* context) { | ||||||
|     SubGhzProtocolEncoderMagellen* instance = context; |     SubGhzProtocolEncoderMagellan* instance = context; | ||||||
| 
 | 
 | ||||||
|     if(instance->encoder.repeat == 0 || !instance->encoder.is_running) { |     if(instance->encoder.repeat == 0 || !instance->encoder.is_running) { | ||||||
|         instance->encoder.is_running = false; |         instance->encoder.is_running = false; | ||||||
| @ -200,27 +200,27 @@ LevelDuration subghz_protocol_encoder_magellen_yield(void* context) { | |||||||
|     return ret; |     return ret; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void* subghz_protocol_decoder_magellen_alloc(SubGhzEnvironment* environment) { | void* subghz_protocol_decoder_magellan_alloc(SubGhzEnvironment* environment) { | ||||||
|     UNUSED(environment); |     UNUSED(environment); | ||||||
|     SubGhzProtocolDecoderMagellen* instance = malloc(sizeof(SubGhzProtocolDecoderMagellen)); |     SubGhzProtocolDecoderMagellan* instance = malloc(sizeof(SubGhzProtocolDecoderMagellan)); | ||||||
|     instance->base.protocol = &subghz_protocol_magellen; |     instance->base.protocol = &subghz_protocol_magellan; | ||||||
|     instance->generic.protocol_name = instance->base.protocol->name; |     instance->generic.protocol_name = instance->base.protocol->name; | ||||||
|     return instance; |     return instance; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void subghz_protocol_decoder_magellen_free(void* context) { | void subghz_protocol_decoder_magellan_free(void* context) { | ||||||
|     furi_assert(context); |     furi_assert(context); | ||||||
|     SubGhzProtocolDecoderMagellen* instance = context; |     SubGhzProtocolDecoderMagellan* instance = context; | ||||||
|     free(instance); |     free(instance); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void subghz_protocol_decoder_magellen_reset(void* context) { | void subghz_protocol_decoder_magellan_reset(void* context) { | ||||||
|     furi_assert(context); |     furi_assert(context); | ||||||
|     SubGhzProtocolDecoderMagellen* instance = context; |     SubGhzProtocolDecoderMagellan* instance = context; | ||||||
|     instance->decoder.parser_step = MagellenDecoderStepReset; |     instance->decoder.parser_step = MagellanDecoderStepReset; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| uint8_t subghz_protocol_magellen_crc8(uint8_t* data, size_t len) { | uint8_t subghz_protocol_magellan_crc8(uint8_t* data, size_t len) { | ||||||
|     uint8_t crc = 0x00; |     uint8_t crc = 0x00; | ||||||
|     size_t i, j; |     size_t i, j; | ||||||
|     for(i = 0; i < len; i++) { |     for(i = 0; i < len; i++) { | ||||||
| @ -235,99 +235,99 @@ uint8_t subghz_protocol_magellen_crc8(uint8_t* data, size_t len) { | |||||||
|     return crc; |     return crc; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static bool subghz_protocol_magellen_check_crc(SubGhzProtocolDecoderMagellen* instance) { | static bool subghz_protocol_magellan_check_crc(SubGhzProtocolDecoderMagellan* instance) { | ||||||
|     uint8_t data[3] = { |     uint8_t data[3] = { | ||||||
|         instance->decoder.decode_data >> 24, |         instance->decoder.decode_data >> 24, | ||||||
|         instance->decoder.decode_data >> 16, |         instance->decoder.decode_data >> 16, | ||||||
|         instance->decoder.decode_data >> 8}; |         instance->decoder.decode_data >> 8}; | ||||||
|     return (instance->decoder.decode_data & 0xFF) == |     return (instance->decoder.decode_data & 0xFF) == | ||||||
|            subghz_protocol_magellen_crc8(data, sizeof(data)); |            subghz_protocol_magellan_crc8(data, sizeof(data)); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void subghz_protocol_decoder_magellen_feed(void* context, bool level, uint32_t duration) { | void subghz_protocol_decoder_magellan_feed(void* context, bool level, uint32_t duration) { | ||||||
|     furi_assert(context); |     furi_assert(context); | ||||||
|     SubGhzProtocolDecoderMagellen* instance = context; |     SubGhzProtocolDecoderMagellan* instance = context; | ||||||
| 
 | 
 | ||||||
|     switch(instance->decoder.parser_step) { |     switch(instance->decoder.parser_step) { | ||||||
|     case MagellenDecoderStepReset: |     case MagellanDecoderStepReset: | ||||||
|         if((level) && (DURATION_DIFF(duration, subghz_protocol_magellen_const.te_short) < |         if((level) && (DURATION_DIFF(duration, subghz_protocol_magellan_const.te_short) < | ||||||
|                        subghz_protocol_magellen_const.te_delta)) { |                        subghz_protocol_magellan_const.te_delta)) { | ||||||
|             instance->decoder.parser_step = MagellenDecoderStepCheckPreambula; |             instance->decoder.parser_step = MagellanDecoderStepCheckPreambula; | ||||||
|             instance->decoder.te_last = duration; |             instance->decoder.te_last = duration; | ||||||
|             instance->header_count = 0; |             instance->header_count = 0; | ||||||
|         } |         } | ||||||
|         break; |         break; | ||||||
| 
 | 
 | ||||||
|     case MagellenDecoderStepCheckPreambula: |     case MagellanDecoderStepCheckPreambula: | ||||||
|         if(level) { |         if(level) { | ||||||
|             instance->decoder.te_last = duration; |             instance->decoder.te_last = duration; | ||||||
|         } else { |         } else { | ||||||
|             if((DURATION_DIFF(instance->decoder.te_last, subghz_protocol_magellen_const.te_short) < |             if((DURATION_DIFF(instance->decoder.te_last, subghz_protocol_magellan_const.te_short) < | ||||||
|                 subghz_protocol_magellen_const.te_delta) && |                 subghz_protocol_magellan_const.te_delta) && | ||||||
|                (DURATION_DIFF(duration, subghz_protocol_magellen_const.te_short) < |                (DURATION_DIFF(duration, subghz_protocol_magellan_const.te_short) < | ||||||
|                 subghz_protocol_magellen_const.te_delta)) { |                 subghz_protocol_magellan_const.te_delta)) { | ||||||
|                 // Found header
 |                 // Found header
 | ||||||
|                 instance->header_count++; |                 instance->header_count++; | ||||||
|             } else if( |             } else if( | ||||||
|                 (DURATION_DIFF(instance->decoder.te_last, subghz_protocol_magellen_const.te_short) < |                 (DURATION_DIFF(instance->decoder.te_last, subghz_protocol_magellan_const.te_short) < | ||||||
|                  subghz_protocol_magellen_const.te_delta) && |                  subghz_protocol_magellan_const.te_delta) && | ||||||
|                 (DURATION_DIFF(duration, subghz_protocol_magellen_const.te_long) < |                 (DURATION_DIFF(duration, subghz_protocol_magellan_const.te_long) < | ||||||
|                  subghz_protocol_magellen_const.te_delta * 2) && |                  subghz_protocol_magellan_const.te_delta * 2) && | ||||||
|                 (instance->header_count > 10)) { |                 (instance->header_count > 10)) { | ||||||
|                 instance->decoder.parser_step = MagellenDecoderStepFoundPreambula; |                 instance->decoder.parser_step = MagellanDecoderStepFoundPreambula; | ||||||
|             } else { |             } else { | ||||||
|                 instance->decoder.parser_step = MagellenDecoderStepReset; |                 instance->decoder.parser_step = MagellanDecoderStepReset; | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|         break; |         break; | ||||||
| 
 | 
 | ||||||
|     case MagellenDecoderStepFoundPreambula: |     case MagellanDecoderStepFoundPreambula: | ||||||
|         if(level) { |         if(level) { | ||||||
|             instance->decoder.te_last = duration; |             instance->decoder.te_last = duration; | ||||||
|         } else { |         } else { | ||||||
|             if((DURATION_DIFF( |             if((DURATION_DIFF( | ||||||
|                     instance->decoder.te_last, subghz_protocol_magellen_const.te_short * 6) < |                     instance->decoder.te_last, subghz_protocol_magellan_const.te_short * 6) < | ||||||
|                 subghz_protocol_magellen_const.te_delta * 3) && |                 subghz_protocol_magellan_const.te_delta * 3) && | ||||||
|                (DURATION_DIFF(duration, subghz_protocol_magellen_const.te_long) < |                (DURATION_DIFF(duration, subghz_protocol_magellan_const.te_long) < | ||||||
|                 subghz_protocol_magellen_const.te_delta * 2)) { |                 subghz_protocol_magellan_const.te_delta * 2)) { | ||||||
|                 instance->decoder.parser_step = MagellenDecoderStepSaveDuration; |                 instance->decoder.parser_step = MagellanDecoderStepSaveDuration; | ||||||
|                 instance->decoder.decode_data = 0; |                 instance->decoder.decode_data = 0; | ||||||
|                 instance->decoder.decode_count_bit = 0; |                 instance->decoder.decode_count_bit = 0; | ||||||
|             } else { |             } else { | ||||||
|                 instance->decoder.parser_step = MagellenDecoderStepReset; |                 instance->decoder.parser_step = MagellanDecoderStepReset; | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|         break; |         break; | ||||||
| 
 | 
 | ||||||
|     case MagellenDecoderStepSaveDuration: |     case MagellanDecoderStepSaveDuration: | ||||||
|         if(level) { |         if(level) { | ||||||
|             instance->decoder.te_last = duration; |             instance->decoder.te_last = duration; | ||||||
|             instance->decoder.parser_step = MagellenDecoderStepCheckDuration; |             instance->decoder.parser_step = MagellanDecoderStepCheckDuration; | ||||||
|         } else { |         } else { | ||||||
|             instance->decoder.parser_step = MagellenDecoderStepReset; |             instance->decoder.parser_step = MagellanDecoderStepReset; | ||||||
|         } |         } | ||||||
|         break; |         break; | ||||||
| 
 | 
 | ||||||
|     case MagellenDecoderStepCheckDuration: |     case MagellanDecoderStepCheckDuration: | ||||||
|         if(!level) { |         if(!level) { | ||||||
|             if((DURATION_DIFF(instance->decoder.te_last, subghz_protocol_magellen_const.te_short) < |             if((DURATION_DIFF(instance->decoder.te_last, subghz_protocol_magellan_const.te_short) < | ||||||
|                 subghz_protocol_magellen_const.te_delta) && |                 subghz_protocol_magellan_const.te_delta) && | ||||||
|                (DURATION_DIFF(duration, subghz_protocol_magellen_const.te_long) < |                (DURATION_DIFF(duration, subghz_protocol_magellan_const.te_long) < | ||||||
|                 subghz_protocol_magellen_const.te_delta)) { |                 subghz_protocol_magellan_const.te_delta)) { | ||||||
|                 subghz_protocol_blocks_add_bit(&instance->decoder, 1); |                 subghz_protocol_blocks_add_bit(&instance->decoder, 1); | ||||||
|                 instance->decoder.parser_step = MagellenDecoderStepSaveDuration; |                 instance->decoder.parser_step = MagellanDecoderStepSaveDuration; | ||||||
|             } else if( |             } else if( | ||||||
|                 (DURATION_DIFF(instance->decoder.te_last, subghz_protocol_magellen_const.te_long) < |                 (DURATION_DIFF(instance->decoder.te_last, subghz_protocol_magellan_const.te_long) < | ||||||
|                  subghz_protocol_magellen_const.te_delta) && |                  subghz_protocol_magellan_const.te_delta) && | ||||||
|                 (DURATION_DIFF(duration, subghz_protocol_magellen_const.te_short) < |                 (DURATION_DIFF(duration, subghz_protocol_magellan_const.te_short) < | ||||||
|                  subghz_protocol_magellen_const.te_delta)) { |                  subghz_protocol_magellan_const.te_delta)) { | ||||||
|                 subghz_protocol_blocks_add_bit(&instance->decoder, 0); |                 subghz_protocol_blocks_add_bit(&instance->decoder, 0); | ||||||
|                 instance->decoder.parser_step = MagellenDecoderStepSaveDuration; |                 instance->decoder.parser_step = MagellanDecoderStepSaveDuration; | ||||||
|             } else if(duration >= (subghz_protocol_magellen_const.te_long * 3)) { |             } else if(duration >= (subghz_protocol_magellan_const.te_long * 3)) { | ||||||
|                 //Found stop bit
 |                 //Found stop bit
 | ||||||
|                 if((instance->decoder.decode_count_bit == |                 if((instance->decoder.decode_count_bit == | ||||||
|                     subghz_protocol_magellen_const.min_count_bit_for_found) && |                     subghz_protocol_magellan_const.min_count_bit_for_found) && | ||||||
|                    subghz_protocol_magellen_check_crc(instance)) { |                    subghz_protocol_magellan_check_crc(instance)) { | ||||||
|                     instance->generic.data = instance->decoder.decode_data; |                     instance->generic.data = instance->decoder.decode_data; | ||||||
|                     instance->generic.data_count_bit = instance->decoder.decode_count_bit; |                     instance->generic.data_count_bit = instance->decoder.decode_count_bit; | ||||||
|                     if(instance->base.callback) |                     if(instance->base.callback) | ||||||
| @ -335,12 +335,12 @@ void subghz_protocol_decoder_magellen_feed(void* context, bool level, uint32_t d | |||||||
|                 } |                 } | ||||||
|                 instance->decoder.decode_data = 0; |                 instance->decoder.decode_data = 0; | ||||||
|                 instance->decoder.decode_count_bit = 0; |                 instance->decoder.decode_count_bit = 0; | ||||||
|                 instance->decoder.parser_step = MagellenDecoderStepReset; |                 instance->decoder.parser_step = MagellanDecoderStepReset; | ||||||
|             } else { |             } else { | ||||||
|                 instance->decoder.parser_step = MagellenDecoderStepReset; |                 instance->decoder.parser_step = MagellanDecoderStepReset; | ||||||
|             } |             } | ||||||
|         } else { |         } else { | ||||||
|             instance->decoder.parser_step = MagellenDecoderStepReset; |             instance->decoder.parser_step = MagellanDecoderStepReset; | ||||||
|         } |         } | ||||||
|         break; |         break; | ||||||
|     } |     } | ||||||
| @ -350,7 +350,7 @@ void subghz_protocol_decoder_magellen_feed(void* context, bool level, uint32_t d | |||||||
|  * Analysis of received data |  * Analysis of received data | ||||||
|  * @param instance Pointer to a SubGhzBlockGeneric* instance |  * @param instance Pointer to a SubGhzBlockGeneric* instance | ||||||
|  */ |  */ | ||||||
| static void subghz_protocol_magellen_check_remote_controller(SubGhzBlockGeneric* instance) { | static void subghz_protocol_magellan_check_remote_controller(SubGhzBlockGeneric* instance) { | ||||||
|     /*
 |     /*
 | ||||||
| *   package 32b            data 24b           CRC8 | *   package 32b            data 24b           CRC8 | ||||||
| *   0x037AE4828 => 001101111010111001001000 00101000 | *   0x037AE4828 => 001101111010111001001000 00101000 | ||||||
| @ -375,7 +375,7 @@ static void subghz_protocol_magellen_check_remote_controller(SubGhzBlockGeneric* | |||||||
|     instance->btn = (data_rev >> 16) & 0xFF; |     instance->btn = (data_rev >> 16) & 0xFF; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static void subghz_protocol_magellen_get_event_serialize(uint8_t event, FuriString* output) { | static void subghz_protocol_magellan_get_event_serialize(uint8_t event, FuriString* output) { | ||||||
|     furi_string_cat_printf( |     furi_string_cat_printf( | ||||||
|         output, |         output, | ||||||
|         "%s%s%s%s%s%s%s%s", |         "%s%s%s%s%s%s%s%s", | ||||||
| @ -390,32 +390,32 @@ static void subghz_protocol_magellen_get_event_serialize(uint8_t event, FuriStri | |||||||
|         ((event >> 7) & 0x1 ? ", ?" : "")); |         ((event >> 7) & 0x1 ? ", ?" : "")); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| uint8_t subghz_protocol_decoder_magellen_get_hash_data(void* context) { | uint8_t subghz_protocol_decoder_magellan_get_hash_data(void* context) { | ||||||
|     furi_assert(context); |     furi_assert(context); | ||||||
|     SubGhzProtocolDecoderMagellen* instance = context; |     SubGhzProtocolDecoderMagellan* instance = context; | ||||||
|     return subghz_protocol_blocks_get_hash_data( |     return subghz_protocol_blocks_get_hash_data( | ||||||
|         &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); |         &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| bool subghz_protocol_decoder_magellen_serialize( | bool subghz_protocol_decoder_magellan_serialize( | ||||||
|     void* context, |     void* context, | ||||||
|     FlipperFormat* flipper_format, |     FlipperFormat* flipper_format, | ||||||
|     SubGhzPresetDefinition* preset) { |     SubGhzRadioPreset* preset) { | ||||||
|     furi_assert(context); |     furi_assert(context); | ||||||
|     SubGhzProtocolDecoderMagellen* instance = context; |     SubGhzProtocolDecoderMagellan* instance = context; | ||||||
|     return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); |     return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| bool subghz_protocol_decoder_magellen_deserialize(void* context, FlipperFormat* flipper_format) { | bool subghz_protocol_decoder_magellan_deserialize(void* context, FlipperFormat* flipper_format) { | ||||||
|     furi_assert(context); |     furi_assert(context); | ||||||
|     SubGhzProtocolDecoderMagellen* instance = context; |     SubGhzProtocolDecoderMagellan* instance = context; | ||||||
|     bool ret = false; |     bool ret = false; | ||||||
|     do { |     do { | ||||||
|         if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { |         if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { | ||||||
|             break; |             break; | ||||||
|         } |         } | ||||||
|         if(instance->generic.data_count_bit != |         if(instance->generic.data_count_bit != | ||||||
|            subghz_protocol_magellen_const.min_count_bit_for_found) { |            subghz_protocol_magellan_const.min_count_bit_for_found) { | ||||||
|             FURI_LOG_E(TAG, "Wrong number of bits in key"); |             FURI_LOG_E(TAG, "Wrong number of bits in key"); | ||||||
|             break; |             break; | ||||||
|         } |         } | ||||||
| @ -424,10 +424,10 @@ bool subghz_protocol_decoder_magellen_deserialize(void* context, FlipperFormat* | |||||||
|     return ret; |     return ret; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void subghz_protocol_decoder_magellen_get_string(void* context, FuriString* output) { | void subghz_protocol_decoder_magellan_get_string(void* context, FuriString* output) { | ||||||
|     furi_assert(context); |     furi_assert(context); | ||||||
|     SubGhzProtocolDecoderMagellen* instance = context; |     SubGhzProtocolDecoderMagellan* instance = context; | ||||||
|     subghz_protocol_magellen_check_remote_controller(&instance->generic); |     subghz_protocol_magellan_check_remote_controller(&instance->generic); | ||||||
|     furi_string_cat_printf( |     furi_string_cat_printf( | ||||||
|         output, |         output, | ||||||
|         "%s %dbit\r\n" |         "%s %dbit\r\n" | ||||||
| @ -441,5 +441,5 @@ void subghz_protocol_decoder_magellen_get_string(void* context, FuriString* outp | |||||||
|         instance->generic.serial & 0xFF, |         instance->generic.serial & 0xFF, | ||||||
|         instance->generic.btn); |         instance->generic.btn); | ||||||
| 
 | 
 | ||||||
|     subghz_protocol_magellen_get_event_serialize(instance->generic.btn, output); |     subghz_protocol_magellan_get_event_serialize(instance->generic.btn, output); | ||||||
| } | } | ||||||
							
								
								
									
										107
									
								
								lib/subghz/protocols/magellan.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										107
									
								
								lib/subghz/protocols/magellan.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,107 @@ | |||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | #include "base.h" | ||||||
|  | 
 | ||||||
|  | #define SUBGHZ_PROTOCOL_MAGELLAN_NAME "Magellan" | ||||||
|  | 
 | ||||||
|  | typedef struct SubGhzProtocolDecoderMagellan SubGhzProtocolDecoderMagellan; | ||||||
|  | typedef struct SubGhzProtocolEncoderMagellan SubGhzProtocolEncoderMagellan; | ||||||
|  | 
 | ||||||
|  | extern const SubGhzProtocolDecoder subghz_protocol_magellan_decoder; | ||||||
|  | extern const SubGhzProtocolEncoder subghz_protocol_magellan_encoder; | ||||||
|  | extern const SubGhzProtocol subghz_protocol_magellan; | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Allocate SubGhzProtocolEncoderMagellan. | ||||||
|  |  * @param environment Pointer to a SubGhzEnvironment instance | ||||||
|  |  * @return SubGhzProtocolEncoderMagellan* pointer to a SubGhzProtocolEncoderMagellan instance | ||||||
|  |  */ | ||||||
|  | void* subghz_protocol_encoder_magellan_alloc(SubGhzEnvironment* environment); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Free SubGhzProtocolEncoderMagellan. | ||||||
|  |  * @param context Pointer to a SubGhzProtocolEncoderMagellan instance | ||||||
|  |  */ | ||||||
|  | void subghz_protocol_encoder_magellan_free(void* context); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Deserialize and generating an upload to send. | ||||||
|  |  * @param context Pointer to a SubGhzProtocolEncoderMagellan instance | ||||||
|  |  * @param flipper_format Pointer to a FlipperFormat instance | ||||||
|  |  * @return true On success | ||||||
|  |  */ | ||||||
|  | bool subghz_protocol_encoder_magellan_deserialize(void* context, FlipperFormat* flipper_format); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Forced transmission stop. | ||||||
|  |  * @param context Pointer to a SubGhzProtocolEncoderMagellan instance | ||||||
|  |  */ | ||||||
|  | void subghz_protocol_encoder_magellan_stop(void* context); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Getting the level and duration of the upload to be loaded into DMA. | ||||||
|  |  * @param context Pointer to a SubGhzProtocolEncoderMagellan instance | ||||||
|  |  * @return LevelDuration  | ||||||
|  |  */ | ||||||
|  | LevelDuration subghz_protocol_encoder_magellan_yield(void* context); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Allocate SubGhzProtocolDecoderMagellan. | ||||||
|  |  * @param environment Pointer to a SubGhzEnvironment instance | ||||||
|  |  * @return SubGhzProtocolDecoderMagellan* pointer to a SubGhzProtocolDecoderMagellan instance | ||||||
|  |  */ | ||||||
|  | void* subghz_protocol_decoder_magellan_alloc(SubGhzEnvironment* environment); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Free SubGhzProtocolDecoderMagellan. | ||||||
|  |  * @param context Pointer to a SubGhzProtocolDecoderMagellan instance | ||||||
|  |  */ | ||||||
|  | void subghz_protocol_decoder_magellan_free(void* context); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Reset decoder SubGhzProtocolDecoderMagellan. | ||||||
|  |  * @param context Pointer to a SubGhzProtocolDecoderMagellan instance | ||||||
|  |  */ | ||||||
|  | void subghz_protocol_decoder_magellan_reset(void* context); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Parse a raw sequence of levels and durations received from the air. | ||||||
|  |  * @param context Pointer to a SubGhzProtocolDecoderMagellan instance | ||||||
|  |  * @param level Signal level true-high false-low | ||||||
|  |  * @param duration Duration of this level in, us | ||||||
|  |  */ | ||||||
|  | void subghz_protocol_decoder_magellan_feed(void* context, bool level, uint32_t duration); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Getting the hash sum of the last randomly received parcel. | ||||||
|  |  * @param context Pointer to a SubGhzProtocolDecoderMagellan instance | ||||||
|  |  * @return hash Hash sum | ||||||
|  |  */ | ||||||
|  | uint8_t subghz_protocol_decoder_magellan_get_hash_data(void* context); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Serialize data SubGhzProtocolDecoderMagellan. | ||||||
|  |  * @param context Pointer to a SubGhzProtocolDecoderMagellan instance | ||||||
|  |  * @param flipper_format Pointer to a FlipperFormat instance | ||||||
|  |  * @param preset The modulation on which the signal was received, SubGhzRadioPreset | ||||||
|  |  * @return true On success | ||||||
|  |  */ | ||||||
|  | bool subghz_protocol_decoder_magellan_serialize( | ||||||
|  |     void* context, | ||||||
|  |     FlipperFormat* flipper_format, | ||||||
|  |     SubGhzRadioPreset* preset); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Deserialize data SubGhzProtocolDecoderMagellan. | ||||||
|  |  * @param context Pointer to a SubGhzProtocolDecoderMagellan instance | ||||||
|  |  * @param flipper_format Pointer to a FlipperFormat instance | ||||||
|  |  * @return true On success | ||||||
|  |  */ | ||||||
|  | bool subghz_protocol_decoder_magellan_deserialize(void* context, FlipperFormat* flipper_format); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Getting a textual representation of the received data. | ||||||
|  |  * @param context Pointer to a SubGhzProtocolDecoderMagellan instance | ||||||
|  |  * @param output Resulting text | ||||||
|  |  */ | ||||||
|  | void subghz_protocol_decoder_magellan_get_string(void* context, FuriString* output); | ||||||
Some files were not shown because too many files have changed in this diff Show More
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 Skorpionm
						Skorpionm