diff --git a/applications/main/subghz/helpers/subghz_custom_event.h b/applications/main/subghz/helpers/subghz_custom_event.h index 3a005de74..469ce4491 100644 --- a/applications/main/subghz/helpers/subghz_custom_event.h +++ b/applications/main/subghz/helpers/subghz_custom_event.h @@ -82,6 +82,7 @@ typedef enum { SubGhzCustomEventSceneShowOnlyRX, SubGhzCustomEventSceneAnalyzerLock, SubGhzCustomEventSceneAnalyzerUnlock, + SubGhzCustomEventSceneSettingRepeater, SubGhzCustomEventSceneSettingRemoveDuplicates, SubGhzCustomEventSceneSettingLock, SubGhzCustomEventSceneSettingResetToDefault, @@ -95,6 +96,8 @@ typedef enum { SubGhzCustomEventSceneRpcSessionClose, SubGhzCustomEventViewReceiverOK, + SubGhzCustomEventViewReceiverOKLong, + SubGhzCustomEventViewReceiverOKRelease, SubGhzCustomEventViewReceiverConfig, SubGhzCustomEventViewReceiverBack, SubGhzCustomEventViewReceiverOffDisplay, @@ -120,5 +123,8 @@ typedef enum { SubGhzCustomEventViewFreqAnalOkShort, SubGhzCustomEventViewFreqAnalOkLong, + SubGhzCustomEventViewRepeaterStart, + SubGhzCustomEventViewRepeaterStop, + SubGhzCustomEventByteInputDone, } SubGhzCustomEvent; diff --git a/applications/main/subghz/helpers/subghz_types.h b/applications/main/subghz/helpers/subghz_types.h index 0d748a80b..77e6496d8 100644 --- a/applications/main/subghz/helpers/subghz_types.h +++ b/applications/main/subghz/helpers/subghz_types.h @@ -53,6 +53,7 @@ typedef enum { SubGhzRxKeyStateExit, SubGhzRxKeyStateRAWLoad, SubGhzRxKeyStateRAWSave, + SubGhzRxKeyStateTX, } SubGhzRxKeyState; /** SubGhzLoadKeyState state */ @@ -101,4 +102,12 @@ typedef enum { SubGhzDecodeRawStateStart, SubGhzDecodeRawStateLoading, SubGhzDecodeRawStateLoaded, -} SubGhzDecodeRawState; \ No newline at end of file +} SubGhzDecodeRawState; + +/** SubGhz Repeater */ +typedef enum { + SubGhzRepeaterStateOff, + SubGhzRepeaterStateOn, + SubGhzRepeaterStateOnLong, + SubGhzRepeaterStateOnShort, +} SubGhzRepeaterState; diff --git a/applications/main/subghz/scenes/subghz_scene_decode_raw.c b/applications/main/subghz/scenes/subghz_scene_decode_raw.c index 7bd3933c2..b2be9b8e2 100644 --- a/applications/main/subghz/scenes/subghz_scene_decode_raw.c +++ b/applications/main/subghz/scenes/subghz_scene_decode_raw.c @@ -20,7 +20,8 @@ static void subghz_scene_receiver_update_statusbar(void* context) { furi_string_get_cstr(modulation_str), furi_string_get_cstr(history_stat_str), subghz_txrx_hopper_get_state(subghz->txrx) != SubGhzHopperStateOFF, - READ_BIT(subghz->filter, SubGhzProtocolFlag_BinRAW) > 0); + READ_BIT(subghz->filter, SubGhzProtocolFlag_BinRAW) > 0, + subghz->repeater); furi_string_free(frequency_str); furi_string_free(modulation_str); @@ -31,7 +32,8 @@ static void subghz_scene_receiver_update_statusbar(void* context) { "", "", subghz_txrx_hopper_get_state(subghz->txrx) != SubGhzHopperStateOFF, - READ_BIT(subghz->filter, SubGhzProtocolFlag_BinRAW) > 0); + READ_BIT(subghz->filter, SubGhzProtocolFlag_BinRAW) > 0, + subghz->repeater); } furi_string_free(history_stat_str); } diff --git a/applications/main/subghz/scenes/subghz_scene_need_saving.c b/applications/main/subghz/scenes/subghz_scene_need_saving.c index 9a2d047ba..3368bda04 100644 --- a/applications/main/subghz/scenes/subghz_scene_need_saving.c +++ b/applications/main/subghz/scenes/subghz_scene_need_saving.c @@ -16,7 +16,7 @@ void subghz_scene_need_saving_on_enter(void* context) { SubGhz* subghz = context; widget_add_string_multiline_element( - subghz->widget, 64, 13, AlignCenter, AlignCenter, FontPrimary, "Exit to Sub-GHz Menu?"); + subghz->widget, 64, 13, AlignCenter, AlignCenter, FontPrimary, "Discard Signals?"); widget_add_string_multiline_element( subghz->widget, 64, @@ -29,7 +29,7 @@ void subghz_scene_need_saving_on_enter(void* context) { widget_add_button_element( subghz->widget, GuiButtonTypeRight, "Stay", subghz_scene_need_saving_callback, subghz); widget_add_button_element( - subghz->widget, GuiButtonTypeLeft, "Exit", subghz_scene_need_saving_callback, subghz); + subghz->widget, GuiButtonTypeLeft, "Continue", subghz_scene_need_saving_callback, subghz); view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewIdWidget); } @@ -54,6 +54,15 @@ bool subghz_scene_need_saving_on_event(void* context, SceneManagerEvent event) { subghz->txrx, "AM650", subghz->last_settings->frequency, 0, 0, NULL, 0); scene_manager_search_and_switch_to_previous_scene( subghz->scene_manager, SubGhzSceneStart); + } else if(state == SubGhzRxKeyStateTX) { + subghz->repeater = SubGhzRepeaterStateOn; + subghz->last_settings->repeater_state = SubGhzRepeaterStateOn; + if((subghz->filter & SubGhzProtocolFlag_BinRAW) == 0) { + subghz->filter = SubGhzProtocolFlag_Decodable | SubGhzProtocolFlag_BinRAW; + subghz_txrx_receiver_set_filter(subghz->txrx, subghz->filter); + subghz->repeater_bin_raw_was_off = true; + } + scene_manager_previous_scene(subghz->scene_manager); } else { scene_manager_previous_scene(subghz->scene_manager); } diff --git a/applications/main/subghz/scenes/subghz_scene_read_raw.c b/applications/main/subghz/scenes/subghz_scene_read_raw.c index 690166723..43b53677a 100644 --- a/applications/main/subghz/scenes/subghz_scene_read_raw.c +++ b/applications/main/subghz/scenes/subghz_scene_read_raw.c @@ -130,15 +130,6 @@ void subghz_scene_read_raw_on_enter(void* context) { if(subghz->fav_timeout) { scene_manager_handle_custom_event( subghz->scene_manager, SubGhzCustomEventViewReadRAWSendStart); - // with_view_model( - // subghz->subghz_read_raw->view, - // SubGhzReadRAWModel * model, - // { - // scene_manager_handle_custom_event( - // subghz->scene_manager, SubGhzCustomEventViewReadRAWSendStart); - // model->status = SubGhzReadRAWStatusTXRepeat; - // }, - // true); } } diff --git a/applications/main/subghz/scenes/subghz_scene_receiver.c b/applications/main/subghz/scenes/subghz_scene_receiver.c index 95a5fb06d..713a67f19 100644 --- a/applications/main/subghz/scenes/subghz_scene_receiver.c +++ b/applications/main/subghz/scenes/subghz_scene_receiver.c @@ -36,6 +36,16 @@ const NotificationSequence subghz_sequence_rx_locked = { NULL, }; +const NotificationSequence subghz_sequence_tx_beep = { + &message_vibro_on, + &message_note_c6, + &message_delay_50, + &message_sound_off, + &message_vibro_off, + &message_delay_50, + NULL, +}; + static void subghz_scene_receiver_update_statusbar(void* context) { SubGhz* subghz = context; FuriString* history_stat_str = furi_string_alloc(); @@ -72,7 +82,8 @@ static void subghz_scene_receiver_update_statusbar(void* context) { furi_string_get_cstr(modulation_str), furi_string_get_cstr(history_stat_str), subghz_txrx_hopper_get_state(subghz->txrx) != SubGhzHopperStateOFF, - READ_BIT(subghz->filter, SubGhzProtocolFlag_BinRAW) > 0); + READ_BIT(subghz->filter, SubGhzProtocolFlag_BinRAW) > 0, + subghz->repeater); furi_string_free(frequency_str); furi_string_free(modulation_str); @@ -83,7 +94,8 @@ static void subghz_scene_receiver_update_statusbar(void* context) { "", "", subghz_txrx_hopper_get_state(subghz->txrx) != SubGhzHopperStateOFF, - READ_BIT(subghz->filter, SubGhzProtocolFlag_BinRAW) > 0); + READ_BIT(subghz->filter, SubGhzProtocolFlag_BinRAW) > 0, + subghz->repeater); subghz->state_notifications = SubGhzNotificationStateIDLE; } furi_string_free(history_stat_str); @@ -98,6 +110,11 @@ void subghz_scene_receiver_callback(SubGhzCustomEvent event, void* context) { view_dispatcher_send_custom_event(subghz->view_dispatcher, event); } +void repeater_stop_callback(void* context) { + SubGhz* subghz = context; + scene_manager_handle_custom_event(subghz->scene_manager, SubGhzCustomEventViewRepeaterStop); +} + static void subghz_scene_add_to_history_callback( SubGhzReceiver* receiver, SubGhzProtocolDecoderBase* decoder_base, @@ -120,50 +137,57 @@ static void subghz_scene_add_to_history_callback( furi_string_reset(item_name); furi_string_reset(item_time); - subghz->state_notifications = SubGhzNotificationStateRxDone; + //If the repeater is on, dont add to the menu, just TX the signal. + if(subghz->repeater != SubGhzRepeaterStateOff) { + //subghz_scene_receiver_update_statusbar(subghz); + view_dispatcher_send_custom_event( + subghz->view_dispatcher, SubGhzCustomEventViewRepeaterStart); + } else { + subghz->state_notifications = SubGhzNotificationStateRxDone; - if(subghz->remove_duplicates) { - // Look in history for signal hash - uint8_t hash_data = subghz_protocol_decoder_base_get_hash_data(decoder_base); - uint16_t menu_idx = subghz_view_receiver_get_idx_menu(subghz->subghz_receiver); - subghz_view_receiver_disable_draw_callback(subghz->subghz_receiver); - for(uint16_t i = idx; i > 0; i--) { - i--; // Iterating in reverse with off by one - if(subghz_history_get_hash_data(subghz->history, i) == hash_data) { - // Remove previous instance and update menu index - subghz_history_delete_item(subghz->history, i); - subghz_view_receiver_set_idx_menu(subghz->subghz_receiver, i); - subghz_view_receiver_delete_element_callback(subghz->subghz_receiver); - if(menu_idx > i) { - menu_idx--; - idx--; + if(subghz->remove_duplicates) { + // Look in history for signal hash + uint8_t hash_data = subghz_protocol_decoder_base_get_hash_data(decoder_base); + uint16_t menu_idx = subghz_view_receiver_get_idx_menu(subghz->subghz_receiver); + subghz_view_receiver_disable_draw_callback(subghz->subghz_receiver); + for(uint16_t i = idx; i > 0; i--) { + i--; // Iterating in reverse with off by one + if(subghz_history_get_hash_data(subghz->history, i) == hash_data) { + // Remove previous instance and update menu index + subghz_history_delete_item(subghz->history, i); + subghz_view_receiver_set_idx_menu(subghz->subghz_receiver, i); + subghz_view_receiver_delete_element_callback(subghz->subghz_receiver); + if(menu_idx > i) { + menu_idx--; + idx--; + } } + i++; + } + // Restore ui state + subghz_view_receiver_set_idx_menu(subghz->subghz_receiver, menu_idx); + subghz->idx_menu_chosen = + subghz_view_receiver_get_idx_menu(subghz->subghz_receiver); + subghz_view_receiver_enable_draw_callback(subghz->subghz_receiver); + if(subghz_history_get_last_index(subghz->history) == 0) { + subghz_rx_key_state_set(subghz, SubGhzRxKeyStateStart); } - i++; } - // Restore ui state - subghz_view_receiver_set_idx_menu(subghz->subghz_receiver, menu_idx); - subghz->idx_menu_chosen = - subghz_view_receiver_get_idx_menu(subghz->subghz_receiver); - subghz_view_receiver_enable_draw_callback(subghz->subghz_receiver); - if(subghz_history_get_last_index(subghz->history) == 0) { - subghz_rx_key_state_set(subghz, SubGhzRxKeyStateStart); + + subghz_history_get_text_item_menu(history, item_name, idx); + subghz_history_get_time_item_menu(history, item_time, idx); + subghz_view_receiver_add_item_to_menu( + subghz->subghz_receiver, + furi_string_get_cstr(item_name), + furi_string_get_cstr(item_time), + subghz_history_get_type_protocol(history, idx), + subghz_history_get_repeats(history, idx)); + + subghz_scene_receiver_update_statusbar(subghz); + if(subghz_history_get_text_space_left(subghz->history, NULL, 0)) { + notification_message(subghz->notifications, &sequence_error); } } - - subghz_history_get_text_item_menu(history, item_name, idx); - subghz_history_get_time_item_menu(history, item_time, idx); - subghz_view_receiver_add_item_to_menu( - subghz->subghz_receiver, - furi_string_get_cstr(item_name), - furi_string_get_cstr(item_time), - subghz_history_get_type_protocol(history, idx), - subghz_history_get_repeats(history, idx)); - - subghz_scene_receiver_update_statusbar(subghz); - if(subghz_history_get_text_space_left(subghz->history, NULL, 0)) { - notification_message(subghz->notifications, &sequence_error); - } } subghz_receiver_reset(receiver); furi_string_free(item_name); @@ -233,6 +257,32 @@ void subghz_scene_receiver_on_enter(void* context) { subghz_txrx_hopper_set_state(subghz->txrx, SubGhzHopperStateOFF); } + // Check if Sound was enabled, and restart the Speaker. + subghz_txrx_speaker_set_state( + subghz->txrx, + subghz->last_settings->enable_sound ? SubGhzSpeakerStateEnable : + SubGhzSpeakerStateShutdown); + + //Set up a timer for the repeater (recycled favorites timeout TX timer!). + if(!subghz->timer) + subghz->timer = furi_timer_alloc(repeater_stop_callback, FuriTimerTypeOnce, subghz); + + //Remember if the repeater was loaded, and do cleanups we need. + subghz->repeater = subghz->last_settings->repeater_state; + + //Did the user set BinRAW or me? + if(subghz->repeater != SubGhzRepeaterStateOff) { + //User had BinRAW on if the last settings had BinRAW on, if not, repeater is on, and BinRAW goes on, but State CHanged is false! + subghz->repeater_bin_raw_was_off = + (subghz->last_settings->filter & SubGhzProtocolFlag_BinRAW) == 0; + if((subghz->filter & SubGhzProtocolFlag_BinRAW) == 0) { + subghz->filter = SubGhzProtocolFlag_Decodable | SubGhzProtocolFlag_BinRAW; + subghz_txrx_receiver_set_filter(subghz->txrx, subghz->filter); + } + } else { + subghz->repeater_bin_raw_was_off = false; + } + subghz_txrx_rx_start(subghz->txrx); subghz_view_receiver_set_idx_menu(subghz->subghz_receiver, subghz->idx_menu_chosen); @@ -309,35 +359,104 @@ bool subghz_scene_receiver_on_event(void* context, SceneManagerEvent event) { subghz_unlock(subghz); consumed = true; break; + case SubGhzCustomEventViewRepeaterStart: + subghz_txrx_stop(subghz->txrx); + subghz_txrx_hopper_pause(subghz->txrx); + + FlipperFormat* key_repeat_data = subghz_history_get_raw_data( + subghz->history, subghz_history_get_last_index(subghz->history) - 1); + + uint32_t tmpTe = 300; + if(!flipper_format_rewind(key_repeat_data)) { + FURI_LOG_E(TAG, "Rewind error"); + } else if(!flipper_format_read_uint32(key_repeat_data, "TE", (uint32_t*)&tmpTe, 1)) { + FURI_LOG_E(TAG, "Missing TE"); + } + + if(!subghz_tx_start(subghz, key_repeat_data)) { + view_dispatcher_send_custom_event( + subghz->view_dispatcher, SubGhzCustomEventViewRepeaterStop); + } else { + subghz->state_notifications = SubGhzNotificationStateTx; + notification_message(subghz->notifications, &subghz_sequence_tx_beep); + + uint32_t repeatnormal = (tmpTe > 1000) ? 1 : 3; + uint32_t repeat_time = ((subghz->repeater & SubGhzRepeaterStateOnLong) != 0) ? + 2 * repeatnormal * tmpTe : + ((subghz->repeater & SubGhzRepeaterStateOnShort) != 0) ? + 1 * tmpTe : + repeatnormal * tmpTe; + furi_timer_start(subghz->timer, repeat_time); + } + subghz_rx_key_state_set(subghz, SubGhzRxKeyStateTX); + break; + case SubGhzCustomEventViewRepeaterStop: + subghz_txrx_stop(subghz->txrx); + subghz_history_delete_item( + subghz->history, subghz_history_get_last_index(subghz->history) - 1); + subghz_txrx_rx_start(subghz->txrx); + subghz_txrx_hopper_unpause(subghz->txrx); + subghz_rx_key_state_set(subghz, SubGhzRxKeyStateStart); + subghz->state_notifications = SubGhzNotificationStateRx; + break; + case SubGhzCustomEventViewReceiverOKLong: + subghz_txrx_stop(subghz->txrx); + subghz_txrx_hopper_pause(subghz->txrx); + if(!subghz_tx_start( + subghz, + subghz_history_get_raw_data( + subghz->history, + subghz_view_receiver_get_idx_menu(subghz->subghz_receiver)))) { + view_dispatcher_send_custom_event( + subghz->view_dispatcher, SubGhzCustomEventViewReceiverOKRelease); + } else { + subghz->state_notifications = SubGhzNotificationStateTx; + notification_message(subghz->notifications, &subghz_sequence_tx_beep); + } + subghz_rx_key_state_set(subghz, SubGhzRxKeyStateTX); + break; + case SubGhzCustomEventViewReceiverOKRelease: + if(subghz_rx_key_state_get(subghz) == SubGhzRxKeyStateTX) { + subghz_txrx_stop(subghz->txrx); + subghz_txrx_rx_start(subghz->txrx); + subghz_txrx_hopper_unpause(subghz->txrx); + subghz_rx_key_state_set(subghz, SubGhzRxKeyStateStart); + subghz->state_notifications = SubGhzNotificationStateRx; + break; + } + break; default: break; } } else if(event.type == SceneManagerEventTypeTick) { - if(subghz_txrx_hopper_get_state(subghz->txrx) != SubGhzHopperStateOFF) { - subghz_txrx_hopper_update(subghz->txrx); - subghz_scene_receiver_update_statusbar(subghz); - } - - SubGhzThresholdRssiData ret_rssi = subghz_threshold_get_rssi_data( - subghz->threshold_rssi, subghz_txrx_radio_device_get_rssi(subghz->txrx)); - - if(subghz->last_settings->gps_baudrate != 0) { - FuriHalRtcDateTime datetime; - furi_hal_rtc_get_datetime(&datetime); - if((datetime.second - subghz->gps->fix_second) > 15) { - subghz->gps->latitude = NAN; - subghz->gps->longitude = NAN; - subghz->gps->satellites = 0; - subghz->gps->fix_hour = 0; - subghz->gps->fix_minute = 0; - subghz->gps->fix_second = 0; + if(subghz_rx_key_state_get(subghz) != SubGhzRxKeyStateTX) { + if(subghz_txrx_hopper_get_state(subghz->txrx) != SubGhzHopperStateOFF) { + subghz_txrx_hopper_update(subghz->txrx); + subghz_scene_receiver_update_statusbar(subghz); } - subghz_scene_receiver_update_statusbar(subghz); - } - subghz_receiver_rssi(subghz->subghz_receiver, ret_rssi.rssi); - subghz_protocol_decoder_bin_raw_data_input_rssi( - (SubGhzProtocolDecoderBinRAW*)subghz_txrx_get_decoder(subghz->txrx), ret_rssi.rssi); + SubGhzThresholdRssiData ret_rssi = subghz_threshold_get_rssi_data( + subghz->threshold_rssi, subghz_txrx_radio_device_get_rssi(subghz->txrx)); + + if(subghz->last_settings->gps_baudrate != 0) { + FuriHalRtcDateTime datetime; + furi_hal_rtc_get_datetime(&datetime); + if((datetime.second - subghz->gps->fix_second) > 15) { + subghz->gps->latitude = NAN; + subghz->gps->longitude = NAN; + subghz->gps->satellites = 0; + subghz->gps->fix_hour = 0; + subghz->gps->fix_minute = 0; + subghz->gps->fix_second = 0; + } + subghz_scene_receiver_update_statusbar(subghz); + } + + subghz_receiver_rssi(subghz->subghz_receiver, ret_rssi.rssi); + subghz_protocol_decoder_bin_raw_data_input_rssi( + (SubGhzProtocolDecoderBinRAW*)subghz_txrx_get_decoder(subghz->txrx), + ret_rssi.rssi); + } switch(subghz->state_notifications) { case SubGhzNotificationStateRx: @@ -359,6 +478,9 @@ bool subghz_scene_receiver_on_event(void* context, SceneManagerEvent event) { } subghz->state_notifications = SubGhzNotificationStateRx; break; + case SubGhzNotificationStateTx: + notification_message(subghz->notifications, &sequence_blink_magenta_10); + break; default: break; } diff --git a/applications/main/subghz/scenes/subghz_scene_receiver_config.c b/applications/main/subghz/scenes/subghz_scene_receiver_config.c index e2f1061e6..ee2dafb21 100644 --- a/applications/main/subghz/scenes/subghz_scene_receiver_config.c +++ b/applications/main/subghz/scenes/subghz_scene_receiver_config.c @@ -10,6 +10,7 @@ enum SubGhzSettingIndex { SubGhzSettingIndexRAWSound = SubGhzSettingIndexHopping, SubGhzSettingIndexBinRAW, SubGhzSettingIndexRAWRSSIThreshold = SubGhzSettingIndexBinRAW, + SubGhzSettingIndexRepeater, SubGhzSettingIndexRemoveDuplicates, SubGhzSettingIndexIgnoreStarline, SubGhzSettingIndexIgnoreCars, @@ -74,6 +75,20 @@ const char* const combobox_text[COMBO_BOX_COUNT] = { "ON", }; +#define REPEATER_COUNT 4 +const char* const repeater_text[REPEATER_COUNT] = { + "OFF", + "Normal", + "Long", + "Short", +}; +const uint32_t repeater_value[REPEATER_COUNT] = { + SubGhzRepeaterStateOff, + SubGhzRepeaterStateOn, + SubGhzRepeaterStateOnLong, + SubGhzRepeaterStateOnShort, +}; + static void subghz_scene_receiver_config_set_ignore_filter( VariableItem* item, SubGhzProtocolFilter filter) { @@ -133,8 +148,7 @@ SubGhzHopperState subghz_scene_receiver_config_hopper_value_index(void* context) return SubGhzHopperStateOFF; } else { variable_item_set_current_value_text( - (VariableItem*)scene_manager_get_scene_state( - subghz->scene_manager, SubGhzSceneReceiverConfig), + variable_item_list_get(subghz->variable_item_list, SubGhzSettingIndexFrequency), " -----"); return SubGhzHopperStateRunning; } @@ -200,8 +214,8 @@ static void subghz_scene_receiver_config_set_hopping_running(VariableItem* item) SubGhz* subghz = variable_item_get_context(item); SubGhzHopperState index = variable_item_get_current_value_index(item); SubGhzSetting* setting = subghz_txrx_get_setting(subghz->txrx); - VariableItem* frequency_item = (VariableItem*)scene_manager_get_scene_state( - subghz->scene_manager, SubGhzSceneReceiverConfig); + VariableItem* frequency_item = + variable_item_list_get(subghz->variable_item_list, SubGhzSettingIndexFrequency); variable_item_set_current_value_text(item, combobox_text[(uint8_t)index]); @@ -244,6 +258,7 @@ static void subghz_scene_receiver_config_set_speaker(VariableItem* item) { variable_item_set_current_value_text(item, combobox_text[index]); subghz_txrx_speaker_set_state(subghz->txrx, speaker_value[index]); + subghz->last_settings->enable_sound = (speaker_value[index] == SubGhzSpeakerStateEnable); } static void subghz_scene_receiver_config_set_bin_raw(VariableItem* item) { @@ -256,6 +271,59 @@ static void subghz_scene_receiver_config_set_bin_raw(VariableItem* item) { // We can set here, but during subghz_last_settings_save filter was changed to ignore BinRAW subghz->last_settings->filter = subghz->filter; + + //If the user changed BinRAW menu, dont reset it with the repeater. + subghz->repeater_bin_raw_was_off = false; +} + +static void subghz_scene_receiver_config_set_repeater(VariableItem* item) { + SubGhz* subghz = variable_item_get_context(item); + uint8_t index = variable_item_get_current_value_index(item); + + if(subghz->repeater == SubGhzRepeaterStateOff && + (subghz_rx_key_state_get(subghz) == SubGhzRxKeyStateAddKey || + subghz_rx_key_state_get(subghz) == SubGhzRxKeyStateBack)) { + scene_manager_set_scene_state( + subghz->scene_manager, SubGhzSceneReceiverConfig, SubGhzSettingIndexRepeater); + view_dispatcher_send_custom_event( + subghz->view_dispatcher, SubGhzCustomEventSceneSettingRepeater); + return; + } + + //Set menu Text. + variable_item_set_current_value_text(item, repeater_text[index]); + + //Save state and in last settings. + subghz->repeater = repeater_value[index]; + subghz->last_settings->repeater_state = repeater_value[index]; + + //Get the BinRAW menu for state change. + VariableItem* bin_raw_menu = + variable_item_list_get(subghz->variable_item_list, SubGhzSettingIndexBinRAW); + + //Change BinRAW to ON or OFF as required, and remember whether I changed it! (Put back for the user.) + if(repeater_value[index] != SubGhzRepeaterStateOff) { + if((subghz->filter & SubGhzProtocolFlag_BinRAW) == 0) { + //Repeater is on, Binraw is Off. + variable_item_set_current_value_index( + bin_raw_menu, 1 /*Index of ON in BIN_Raw menu!*/); + subghz_scene_receiver_config_set_bin_raw(bin_raw_menu); + subghz->repeater_bin_raw_was_off = true; + } + + //Lock the BinRAW menu, Flipper doesnt understand everything so BinRAW makes every key send. + variable_item_set_locked(bin_raw_menu, true, NULL); + } else { + //Put BinRAW back how it was, if we changed it. + if(subghz->repeater_bin_raw_was_off) { + variable_item_set_current_value_index( + bin_raw_menu, 0 /*Index of OFF in BIN_Raw menu!*/); + subghz_scene_receiver_config_set_bin_raw(bin_raw_menu); + } + + //Lock the BinRAW menu, Flipper doesnt understand everything so BinRAW makes very key send. + variable_item_set_locked(bin_raw_menu, false, NULL); + } } static void subghz_scene_receiver_config_set_raw_threshold_rssi(VariableItem* item) { @@ -347,8 +415,11 @@ static void subghz_scene_receiver_config_var_list_enter_callback(void* context, subghz->last_settings->remove_duplicates = subghz->remove_duplicates; subghz->last_settings->ignore_filter = subghz->ignore_filter; subghz->last_settings->filter = subghz->filter; + subghz->last_settings->repeater_state = SubGhzRepeaterStateOff; + subghz->repeater = SubGhzRepeaterStateOff; subghz_txrx_speaker_set_state(subghz->txrx, speaker_value[default_index]); + subghz->last_settings->enable_sound = false; subghz_txrx_hopper_set_state(subghz->txrx, hopping_value[default_index]); subghz->last_settings->enable_hopping = hopping_value[default_index]; @@ -379,8 +450,6 @@ void subghz_scene_receiver_config_on_enter(void* context) { subghz_scene_receiver_config_set_frequency, subghz); value_index = subghz_scene_receiver_config_next_frequency(preset.frequency, subghz); - scene_manager_set_scene_state( - subghz->scene_manager, SubGhzSceneReceiverConfig, (uint32_t)item); variable_item_set_current_value_index(item, value_index); char text_buf[10] = {0}; uint32_t frequency = subghz_setting_get_frequency(setting, value_index); @@ -431,10 +500,20 @@ void subghz_scene_receiver_config_on_enter(void* context) { value_index = value_index_uint32(subghz->filter, bin_raw_value, COMBO_BOX_COUNT); variable_item_set_current_value_index(item, value_index); variable_item_set_current_value_text(item, combobox_text[value_index]); - } + variable_item_set_locked( + item, subghz->repeater != SubGhzRepeaterStateOff, "Turn off\nRepeater\nto do that!"); + + item = variable_item_list_add( + subghz->variable_item_list, + "Repeater", + REPEATER_COUNT, + subghz_scene_receiver_config_set_repeater, + subghz); + + value_index = value_index_uint32(subghz->repeater, repeater_value, REPEATER_COUNT); + variable_item_set_current_value_index(item, value_index); + variable_item_set_current_value_text(item, repeater_text[value_index]); - if(scene_manager_get_scene_state(subghz->scene_manager, SubGhzSceneReadRAW) != - SubGhzCustomEventManagerSet) { item = variable_item_list_add( subghz->variable_item_list, "Remove Duplicates", @@ -578,6 +657,12 @@ void subghz_scene_receiver_config_on_enter(void* context) { variable_item_set_current_value_index(item, value_index); variable_item_set_current_value_text(item, raw_threshold_rssi_text[value_index]); } + + variable_item_list_set_selected_item( + subghz->variable_item_list, + scene_manager_get_scene_state(subghz->scene_manager, SubGhzSceneReceiverConfig)); + scene_manager_set_scene_state(subghz->scene_manager, SubGhzSceneReceiverConfig, 0); + view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewIdVariableItemList); } @@ -586,7 +671,11 @@ bool subghz_scene_receiver_config_on_event(void* context, SceneManagerEvent even bool consumed = false; if(event.type == SceneManagerEventTypeCustom) { - if(event.event == SubGhzCustomEventSceneSettingRemoveDuplicates) { + if(event.event == SubGhzCustomEventSceneSettingRepeater) { + subghz_rx_key_state_set(subghz, SubGhzRxKeyStateTX); + scene_manager_next_scene(subghz->scene_manager, SubGhzSceneNeedSaving); + consumed = true; + } else if(event.event == SubGhzCustomEventSceneSettingRemoveDuplicates) { subghz_history_remove_duplicates(subghz->history); scene_manager_previous_scene(subghz->scene_manager); consumed = true; @@ -604,6 +693,11 @@ bool subghz_scene_receiver_config_on_event(void* context, SceneManagerEvent even void subghz_scene_receiver_config_on_exit(void* context) { SubGhz* subghz = context; + + if(subghz->repeater_bin_raw_was_off) { + subghz->last_settings->filter = bin_raw_value[0 /*BinRAW Off*/]; + } + variable_item_list_set_selected_item(subghz->variable_item_list, 0); variable_item_list_reset(subghz->variable_item_list); #ifdef FURI_DEBUG diff --git a/applications/main/subghz/scenes/subghz_scene_transmitter.c b/applications/main/subghz/scenes/subghz_scene_transmitter.c index 6347f684d..dc1c83303 100644 --- a/applications/main/subghz/scenes/subghz_scene_transmitter.c +++ b/applications/main/subghz/scenes/subghz_scene_transmitter.c @@ -70,19 +70,12 @@ void subghz_scene_transmitter_on_enter(void* context) { // Auto send and exit with favorites if(subghz->fav_timeout) { - // subghz_custom_btn_set(0); + furi_check(!subghz->timer, "SubGhz fav timer exists"); + subghz->timer = furi_timer_alloc(fav_timer_callback, FuriTimerTypeOnce, subghz); scene_manager_handle_custom_event( subghz->scene_manager, SubGhzCustomEventViewTransmitterSendStart); - // with_view_model( - // subghz->subghz_transmitter->view, - // SubGhzViewTransmitterModel * model, - // { model->show_button = false; }, - // true); - subghz->fav_timer = furi_timer_alloc(fav_timer_callback, FuriTimerTypeOnce, subghz); furi_timer_start( - subghz->fav_timer, - xtreme_settings.favorite_timeout * furi_kernel_get_tick_frequency()); - // subghz->state_notifications = SubGhzNotificationStateTx; + subghz->timer, xtreme_settings.favorite_timeout * furi_kernel_get_tick_frequency()); } } diff --git a/applications/main/subghz/subghz.c b/applications/main/subghz/subghz.c index a972d05b5..0f67ddcd6 100644 --- a/applications/main/subghz/subghz.c +++ b/applications/main/subghz/subghz.c @@ -429,9 +429,9 @@ int32_t subghz_app(char* p) { view_dispatcher_run(subghz->view_dispatcher); - if(subghz->fav_timer) { - furi_timer_stop(subghz->fav_timer); - furi_timer_free(subghz->fav_timer); + if(subghz->timer) { + furi_timer_stop(subghz->timer); + furi_timer_free(subghz->timer); } furi_hal_power_suppress_charge_exit(); diff --git a/applications/main/subghz/subghz_i.h b/applications/main/subghz/subghz_i.h index 5357808d4..5587b3788 100644 --- a/applications/main/subghz/subghz_i.h +++ b/applications/main/subghz/subghz_i.h @@ -96,12 +96,14 @@ struct SubGhz { SubGhzRxKeyState rx_key_state; SubGhzHistory* history; SubGhzGPS* gps; + SubGhzRepeaterState repeater; + bool repeater_bin_raw_was_off; uint16_t idx_menu_chosen; SubGhzLoadTypeFile load_type_file; bool fav_timeout; - FuriTimer* fav_timer; + FuriTimer* timer; void* rpc_ctx; }; diff --git a/applications/main/subghz/subghz_last_settings.c b/applications/main/subghz/subghz_last_settings.c index 08929af7b..8745f86fd 100644 --- a/applications/main/subghz/subghz_last_settings.c +++ b/applications/main/subghz/subghz_last_settings.c @@ -21,6 +21,8 @@ #define SUBGHZ_LAST_SETTING_FIELD_IGNORE_FILTER "IgnoreFilter" #define SUBGHZ_LAST_SETTING_FIELD_FILTER "Filter" #define SUBGHZ_LAST_SETTING_FIELD_RSSI_THRESHOLD "RSSI" +#define SUBGHZ_LAST_SETTING_FIELD_REPEATER "Repeater" +#define SUBGHZ_LAST_SETTING_FIELD_ENABLE_SOUND "Sound" SubGhzLastSettings* subghz_last_settings_alloc(void) { SubGhzLastSettings* instance = malloc(sizeof(SubGhzLastSettings)); @@ -46,6 +48,8 @@ void subghz_last_settings_load(SubGhzLastSettings* instance, size_t preset_count bool temp_external_module_power_amp = false; bool temp_timestamp_file_names = false; bool temp_enable_hopping = false; + bool temp_enable_sound = false; + uint32_t temp_repeater_state; bool temp_remove_duplicates = false; uint32_t temp_ignore_filter = 0; uint32_t temp_filter = 0; @@ -59,6 +63,9 @@ void subghz_last_settings_load(SubGhzLastSettings* instance, size_t preset_count bool remove_duplicates_was_read = false; bool frequency_analyzer_feedback_level_was_read = false; bool frequency_analyzer_trigger_was_read = false; + bool repeater_was_read = false; + bool enable_sound_was_read = false; + uint32_t temp_gps_baudrate = 0; if(FSE_OK == storage_sd_status(storage) && SUBGHZ_LAST_SETTINGS_PATH && @@ -118,6 +125,11 @@ void subghz_last_settings_load(SubGhzLastSettings* instance, size_t preset_count 1); filter_was_read = flipper_format_read_uint32( fff_data_file, SUBGHZ_LAST_SETTING_FIELD_FILTER, (uint32_t*)&temp_filter, 1); + repeater_was_read = flipper_format_read_uint32( + fff_data_file, SUBGHZ_LAST_SETTING_FIELD_REPEATER, (uint32_t*)&temp_repeater_state, 1); + enable_sound_was_read = flipper_format_read_bool( + fff_data_file, SUBGHZ_LAST_SETTING_FIELD_ENABLE_SOUND, (bool*)&temp_enable_sound, 1); + } else { FURI_LOG_E(TAG, "Error open file %s", SUBGHZ_LAST_SETTINGS_PATH); } @@ -136,6 +148,8 @@ void subghz_last_settings_load(SubGhzLastSettings* instance, size_t preset_count instance->gps_baudrate = 0; instance->enable_hopping = false; instance->remove_duplicates = false; + instance->repeater_state = 0; + instance->enable_sound = 0; instance->ignore_filter = 0x00; // See bin_raw_value in applications/main/subghz/scenes/subghz_scene_receiver_config.c instance->filter = SubGhzProtocolFlag_Decodable; @@ -175,6 +189,8 @@ void subghz_last_settings_load(SubGhzLastSettings* instance, size_t preset_count instance->rssi = rssi_was_read ? temp_rssi : SUBGHZ_RAW_THRESHOLD_MIN; instance->enable_hopping = temp_enable_hopping; + instance->repeater_state = repeater_was_read ? temp_repeater_state : 0; + instance->enable_sound = enable_sound_was_read ? temp_enable_sound : false; instance->remove_duplicates = remove_duplicates_was_read ? temp_remove_duplicates : false; instance->ignore_filter = ignore_filter_was_read ? temp_ignore_filter : 0x00; #if SUBGHZ_LAST_SETTING_SAVE_BIN_RAW @@ -298,6 +314,14 @@ bool subghz_last_settings_save(SubGhzLastSettings* instance) { file, SUBGHZ_LAST_SETTING_FIELD_FILTER, &instance->filter, 1)) { break; } + if(!flipper_format_insert_or_update_uint32( + file, SUBGHZ_LAST_SETTING_FIELD_REPEATER, &instance->repeater_state, 1)) { + break; + } + if(!flipper_format_insert_or_update_bool( + file, SUBGHZ_LAST_SETTING_FIELD_ENABLE_SOUND, &instance->enable_sound, 1)) { + break; + } saved = true; } while(0); @@ -331,7 +355,7 @@ void subghz_last_settings_log(SubGhzLastSettings* instance) { TAG, "Frequency: %03ld.%02ld, FeedbackLevel: %ld, FATrigger: %.2f, External: %s, ExtPower: %s, TimestampNames: %s, ExtPowerAmp: %s,\n" "GPSBaudrate: %ld, Hopping: %s,\nPreset: %ld, RSSI: %.2f, " - "BinRAW: %s, Duplicates: %s, Starline: %s, Cars: %s, Magellan: %s, NiceFloR-S: %s, Weather: %s, TPMS: %s", + "BinRAW: %s, Repeater: %lu, Duplicates: %s, Starline: %s, Cars: %s, Magellan: %s, NiceFloR-S: %s, Weather: %s, TPMS: %s, Sound: %s", instance->frequency / 1000000 % 1000, instance->frequency / 10000 % 100, instance->frequency_analyzer_feedback_level, @@ -345,7 +369,8 @@ void subghz_last_settings_log(SubGhzLastSettings* instance) { instance->preset_index, (double)instance->rssi, subghz_last_settings_log_filter_get_index(instance->filter, SubGhzProtocolFlag_BinRAW), - instance->remove_duplicates ? LOG_ON : LOG_OFF, + instance->repeater_state, + bool_to_char(instance->remove_duplicates), subghz_last_settings_log_filter_get_index( instance->ignore_filter, SubGhzProtocolFilter_StarLine), subghz_last_settings_log_filter_get_index( @@ -357,5 +382,6 @@ void subghz_last_settings_log(SubGhzLastSettings* instance) { subghz_last_settings_log_filter_get_index( instance->ignore_filter, SubGhzProtocolFilter_Weather), subghz_last_settings_log_filter_get_index( - instance->ignore_filter, SubGhzProtocolFilter_TPMS)); + instance->ignore_filter, SubGhzProtocolFilter_TPMS), + bool_to_char(instance->enable_sound)); } diff --git a/applications/main/subghz/subghz_last_settings.h b/applications/main/subghz/subghz_last_settings.h index 7ec241158..a7b89f802 100644 --- a/applications/main/subghz/subghz_last_settings.h +++ b/applications/main/subghz/subghz_last_settings.h @@ -28,6 +28,8 @@ typedef struct { bool timestamp_file_names; uint32_t gps_baudrate; bool enable_hopping; + uint32_t repeater_state; + bool enable_sound; bool remove_duplicates; uint32_t ignore_filter; uint32_t filter; diff --git a/applications/main/subghz/views/receiver.c b/applications/main/subghz/views/receiver.c index 0fea59af5..e2b3a1a46 100644 --- a/applications/main/subghz/views/receiver.c +++ b/applications/main/subghz/views/receiver.c @@ -65,6 +65,7 @@ typedef struct { FuriString* progress_str; bool hopping_enabled; bool bin_raw_enabled; + SubGhzRepeaterState repeater_state; SubGhzReceiverHistory* history; uint16_t idx; uint16_t list_offset; @@ -207,7 +208,8 @@ void subghz_view_receiver_add_data_statusbar( const char* preset_str, const char* history_stat_str, bool hopping_enabled, - bool bin_raw_enabled) { + bool bin_raw_enabled, + SubGhzRepeaterState repeater_state) { furi_assert(subghz_receiver); with_view_model( subghz_receiver->view, @@ -218,6 +220,7 @@ void subghz_view_receiver_add_data_statusbar( furi_string_set(model->history_stat_str, history_stat_str); model->hopping_enabled = hopping_enabled; model->bin_raw_enabled = bin_raw_enabled; + model->repeater_state = repeater_state; }, true); } @@ -336,7 +339,9 @@ void subghz_view_receiver_draw(Canvas* canvas, SubGhzViewReceiverModel* model) { (model->device_type == SubGhzRadioDeviceTypeInternal) ? &I_Scanning_123x52 : &I_Fishing_123x52); canvas_set_font(canvas, FontPrimary); - if(model->hopping_enabled) { + if(model->repeater_state != SubGhzRepeaterStateOff) { + canvas_draw_str(canvas, 59, 46, "Repeater..."); + } else if(model->hopping_enabled) { canvas_draw_str(canvas, 59, 46, "Hopper scan..."); } else { canvas_draw_str(canvas, 59, 46, "Fixed scan..."); @@ -545,18 +550,36 @@ bool subghz_view_receiver_input(InputEvent* event, void* context) { }, false); consumed = true; - } else if(event->key == InputKeyOk && event->type == InputTypeShort) { - with_view_model( - subghz_receiver->view, - SubGhzViewReceiverModel * model, - { - if(model->history_item != 0) { - subghz_receiver->callback( - SubGhzCustomEventViewReceiverOK, subghz_receiver->context); - } - }, - false); - consumed = true; + } else if(event->key == InputKeyOk) { + SubGhzCustomEvent new_event; + + switch(event->type) { + case InputTypeShort: + new_event = SubGhzCustomEventViewReceiverOK; + break; + case InputTypeLong: + new_event = SubGhzCustomEventViewReceiverOKLong; + break; + case InputTypeRelease: + new_event = SubGhzCustomEventViewReceiverOKRelease; + break; + default: + new_event = 0; + break; + } + + if(new_event) { + with_view_model( + subghz_receiver->view, + SubGhzViewReceiverModel * model, + { + if(model->history_item != 0) { + subghz_receiver->callback(new_event, subghz_receiver->context); + } + }, + false); + consumed = true; + } } if(consumed) { @@ -595,6 +618,7 @@ void subghz_view_receiver_exit(void* context) { model->nodraw = false; model->hopping_enabled = false; model->bin_raw_enabled = false; + model->repeater_state = SubGhzRepeaterStateOff; }, false); furi_timer_stop(subghz_receiver->timer); diff --git a/applications/main/subghz/views/receiver.h b/applications/main/subghz/views/receiver.h index 6f8a5863f..f376aca51 100644 --- a/applications/main/subghz/views/receiver.h +++ b/applications/main/subghz/views/receiver.h @@ -33,7 +33,8 @@ void subghz_view_receiver_add_data_statusbar( const char* preset_str, const char* history_stat_str, bool hopping_enabled, - bool bin_raw_enabled); + bool bin_raw_enabled, + SubGhzRepeaterState repeater_enabled); void subghz_view_receiver_set_radio_device_type( SubGhzViewReceiver* subghz_receiver, diff --git a/applications/services/gui/modules/variable_item_list.c b/applications/services/gui/modules/variable_item_list.c index 25e4f39fe..4d5a05344 100644 --- a/applications/services/gui/modules/variable_item_list.c +++ b/applications/services/gui/modules/variable_item_list.c @@ -594,9 +594,10 @@ void variable_item_set_current_value_text(VariableItem* item, const char* curren void variable_item_set_locked(VariableItem* item, bool locked, const char* locked_message) { item->locked = locked; - if(locked) { - furi_assert(locked_message); + if(locked_message) { furi_string_set(item->locked_message, locked_message); + } else if(locked && furi_string_empty(item->locked_message)) { + furi_string_set(item->locked_message, "Locked!"); } }