Merge remote-tracking branch 'origin/release-candidate' into release
This commit is contained in:
		
						commit
						16b976478c
					
				
							
								
								
									
										2
									
								
								.github/CODEOWNERS
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.github/CODEOWNERS
									
									
									
									
										vendored
									
									
								
							| @ -56,7 +56,7 @@ | |||||||
| 
 | 
 | ||||||
| # Lib | # Lib | ||||||
| /lib/ST25RFAL002/ @skotopes @DrZlo13 @hedger @gornekich | /lib/ST25RFAL002/ @skotopes @DrZlo13 @hedger @gornekich | ||||||
| /lib/STM32CubeWB/ @skotopes @DrZlo13 @hedger @gornekich | /lib/stm32wb_copro/ @skotopes @DrZlo13 @hedger @gornekich | ||||||
| /lib/digital_signal/ @skotopes @DrZlo13 @hedger @gornekich | /lib/digital_signal/ @skotopes @DrZlo13 @hedger @gornekich | ||||||
| /lib/infrared/ @skotopes @DrZlo13 @hedger @gsurkov | /lib/infrared/ @skotopes @DrZlo13 @hedger @gsurkov | ||||||
| /lib/lfrfid/ @skotopes @DrZlo13 @hedger @nminaylov | /lib/lfrfid/ @skotopes @DrZlo13 @hedger @nminaylov | ||||||
|  | |||||||
							
								
								
									
										5
									
								
								.github/workflows/build.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										5
									
								
								.github/workflows/build.yml
									
									
									
									
										vendored
									
									
								
							| @ -4,7 +4,6 @@ on: | |||||||
|   push: |   push: | ||||||
|     branches: |     branches: | ||||||
|       - dev |       - dev | ||||||
|       - "release*" |  | ||||||
|     tags: |     tags: | ||||||
|       - '*' |       - '*' | ||||||
|   pull_request: |   pull_request: | ||||||
| @ -61,7 +60,7 @@ jobs: | |||||||
|       - name: 'Bundle scripts' |       - name: 'Bundle scripts' | ||||||
|         if: ${{ !github.event.pull_request.head.repo.fork }} |         if: ${{ !github.event.pull_request.head.repo.fork }} | ||||||
|         run: | |         run: | | ||||||
|           tar czpf artifacts/flipper-z-any-scripts-${SUFFIX}.tgz scripts debug |           tar czpf artifacts/flipper-z-any-scripts-${SUFFIX}.tgz scripts | ||||||
| 
 | 
 | ||||||
|       - name: 'Build the firmware' |       - name: 'Build the firmware' | ||||||
|         run: | |         run: | | ||||||
| @ -194,12 +193,14 @@ jobs: | |||||||
|           TARGET="$(echo '${{ matrix.target }}' | sed 's/f//')"; \ |           TARGET="$(echo '${{ matrix.target }}' | sed 's/f//')"; \ | ||||||
|           ./fbt TARGET_HW=$TARGET DEBUG=0 COMPACT=1 fap_dist updater_package |           ./fbt TARGET_HW=$TARGET DEBUG=0 COMPACT=1 fap_dist updater_package | ||||||
|           echo "sdk-file=$(ls dist/${{ matrix.target }}-*/flipper-z-${{ matrix.target }}-sdk-*.zip)" >> $GITHUB_OUTPUT |           echo "sdk-file=$(ls dist/${{ matrix.target }}-*/flipper-z-${{ matrix.target }}-sdk-*.zip)" >> $GITHUB_OUTPUT | ||||||
|  |           echo "hw-target-code=$TARGET" >> $GITHUB_OUTPUT | ||||||
| 
 | 
 | ||||||
|       - name: Deploy uFBT with SDK |       - name: Deploy uFBT with SDK | ||||||
|         uses: flipperdevices/flipperzero-ufbt-action@v0.1.0 |         uses: flipperdevices/flipperzero-ufbt-action@v0.1.0 | ||||||
|         with: |         with: | ||||||
|           task: setup |           task: setup | ||||||
|           sdk-file: ${{ steps.build-fw.outputs.sdk-file }} |           sdk-file: ${{ steps.build-fw.outputs.sdk-file }} | ||||||
|  |           sdk-hw-target: ${{ steps.build-fw.outputs.hw-target-code }} | ||||||
| 
 | 
 | ||||||
|       - name: Build test app with SDK |       - name: Build test app with SDK | ||||||
|         run: | |         run: | | ||||||
|  | |||||||
| @ -4,7 +4,6 @@ on: | |||||||
|   push: |   push: | ||||||
|     branches: |     branches: | ||||||
|       - dev |       - dev | ||||||
|       - "release*" |  | ||||||
|     tags: |     tags: | ||||||
|       - '*' |       - '*' | ||||||
|   pull_request: |   pull_request: | ||||||
|  | |||||||
							
								
								
									
										1
									
								
								.github/workflows/pvs_studio.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								.github/workflows/pvs_studio.yml
									
									
									
									
										vendored
									
									
								
							| @ -4,7 +4,6 @@ on: | |||||||
|   push: |   push: | ||||||
|     branches: |     branches: | ||||||
|       - dev |       - dev | ||||||
|       - "release*" |  | ||||||
|     tags: |     tags: | ||||||
|       - '*' |       - '*' | ||||||
|   pull_request: |   pull_request: | ||||||
|  | |||||||
							
								
								
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							| @ -2,6 +2,7 @@ | |||||||
| *.swp | *.swp | ||||||
| *.swo | *.swo | ||||||
| *.gdb_history | *.gdb_history | ||||||
|  | *.old | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| # LSP | # LSP | ||||||
|  | |||||||
							
								
								
									
										12
									
								
								.gitmodules
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										12
									
								
								.gitmodules
									
									
									
									
										vendored
									
									
								
							| @ -1,9 +1,6 @@ | |||||||
| [submodule "lib/mlib"] | [submodule "lib/mlib"] | ||||||
| 	path = lib/mlib | 	path = lib/mlib | ||||||
| 	url = https://github.com/P-p-H-d/mlib.git | 	url = https://github.com/P-p-H-d/mlib.git | ||||||
| [submodule "lib/STM32CubeWB"] |  | ||||||
| 	path = lib/STM32CubeWB |  | ||||||
| 	url = https://github.com/Flipper-Zero/STM32CubeWB.git |  | ||||||
| [submodule "lib/littlefs"] | [submodule "lib/littlefs"] | ||||||
| 	path = lib/littlefs | 	path = lib/littlefs | ||||||
| 	url = https://github.com/littlefs-project/littlefs.git | 	url = https://github.com/littlefs-project/littlefs.git | ||||||
| @ -34,3 +31,12 @@ | |||||||
| [submodule "lib/heatshrink"] | [submodule "lib/heatshrink"] | ||||||
| 	path = lib/heatshrink | 	path = lib/heatshrink | ||||||
| 	url = https://github.com/flipperdevices/heatshrink.git | 	url = https://github.com/flipperdevices/heatshrink.git | ||||||
|  | [submodule "lib/st_cmsis_device_wb"] | ||||||
|  | 	path = lib/stm32wb_cmsis | ||||||
|  | 	url = https://github.com/STMicroelectronics/cmsis_device_wb | ||||||
|  | [submodule "lib/stm32wbxx_hal_driver"] | ||||||
|  | 	path = lib/stm32wb_hal | ||||||
|  | 	url = https://github.com/STMicroelectronics/stm32wbxx_hal_driver | ||||||
|  | [submodule "lib/stm32wb_copro"] | ||||||
|  | 	path = lib/stm32wb_copro | ||||||
|  | 	url = https://github.com/flipperdevices/stm32wb_copro.git | ||||||
|  | |||||||
| @ -1 +1 @@ | |||||||
| --ignore-ccache -C gccarm --rules-config .pvsconfig -e lib/fatfs -e lib/fnv1a-hash -e lib/FreeRTOS-Kernel -e lib/heatshrink -e lib/libusb_stm32 -e lib/littlefs -e lib/mbedtls -e lib/micro-ecc -e lib/microtar -e lib/mlib -e lib/qrcode -e lib/ST25RFAL002 -e lib/STM32CubeWB -e lib/u8g2 -e lib/nanopb -e */arm-none-eabi/* -e applications/external/dap_link/lib/free-dap | --ignore-ccache -C gccarm --rules-config .pvsconfig -e lib/cmsis_core -e lib/fatfs -e lib/fnv1a-hash -e lib/FreeRTOS-Kernel -e lib/heatshrink -e lib/libusb_stm32 -e lib/littlefs -e lib/mbedtls -e lib/micro-ecc -e lib/microtar -e lib/mlib -e lib/qrcode -e lib/ST25RFAL002 -e lib/stm32wb_cmsis -e lib/stm32wb_copro -e lib/stm32wb_hal -e lib/u8g2 -e lib/nanopb -e */arm-none-eabi/* -e applications/external/dap_link/lib/free-dap | ||||||
|  | |||||||
							
								
								
									
										47
									
								
								.vscode/example/launch.json
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										47
									
								
								.vscode/example/launch.json
									
									
									
									
										vendored
									
									
								
							| @ -11,11 +11,10 @@ | |||||||
|             "args": { |             "args": { | ||||||
|                 "useSingleResult": true, |                 "useSingleResult": true, | ||||||
|                 "env": { |                 "env": { | ||||||
|                     "PATH": "${workspaceFolder};${env:PATH}", |                     "PATH": "${workspaceFolder}${command:extension.commandvariable.envListSep}${env:PATH}" | ||||||
|                     "FBT_QUIET": 1 |  | ||||||
|                 }, |                 }, | ||||||
|                 "command": "fbt get_blackmagic", |                 "command": "fbt -s get_blackmagic", | ||||||
|                 "description": "Get Blackmagic device", |                 "description": "Get Blackmagic device" | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|     ], |     ], | ||||||
| @ -28,20 +27,21 @@ | |||||||
|             "type": "cortex-debug", |             "type": "cortex-debug", | ||||||
|             "servertype": "openocd", |             "servertype": "openocd", | ||||||
|             "device": "stlink", |             "device": "stlink", | ||||||
|             "svdFile": "./debug/STM32WB55_CM4.svd", |             "svdFile": "./scripts/debug/STM32WB55_CM4.svd", | ||||||
|             // If you're debugging early in the boot process, before OS scheduler is running, |             // If you're debugging early in the boot process, before OS scheduler is running, | ||||||
|             // you have to comment out the following line. |             // you have to comment out the following line. | ||||||
|             "rtos": "FreeRTOS", |             "rtos": "FreeRTOS", | ||||||
|             "configFiles": [ |             "configFiles": [ | ||||||
|                 "interface/stlink.cfg", |                 "interface/stlink.cfg", | ||||||
|                 "./debug/stm32wbx.cfg", |                 "./scripts/debug/stm32wbx.cfg", | ||||||
|             ], |             ], | ||||||
|             "postAttachCommands": [ |             "postAttachCommands": [ | ||||||
|  |                 "source scripts/debug/flipperversion.py", | ||||||
|  |                 "fw-version", | ||||||
|                 // "compare-sections", |                 // "compare-sections", | ||||||
|                 "source debug/flipperapps.py", |                 "source scripts/debug/flipperapps.py", | ||||||
|                 "fap-set-debug-elf-root build/latest/.extapps", |                 "fap-set-debug-elf-root build/latest/.extapps", | ||||||
|                 // "source debug/FreeRTOS/FreeRTOS.py", |                 // "source scripts/debug/FreeRTOS/FreeRTOS.py", | ||||||
|                 // "svd_load debug/STM32WB55_CM4.svd" |  | ||||||
|             ] |             ] | ||||||
|             // "showDevDebugOutput": "raw", |             // "showDevDebugOutput": "raw", | ||||||
|         }, |         }, | ||||||
| @ -53,14 +53,16 @@ | |||||||
|             "type": "cortex-debug", |             "type": "cortex-debug", | ||||||
|             "servertype": "external", |             "servertype": "external", | ||||||
|             "gdbTarget": "${input:BLACKMAGIC}", |             "gdbTarget": "${input:BLACKMAGIC}", | ||||||
|             "svdFile": "./debug/STM32WB55_CM4.svd", |             "svdFile": "./scripts/debug/STM32WB55_CM4.svd", | ||||||
|             "rtos": "FreeRTOS", |             "rtos": "FreeRTOS", | ||||||
|             "postAttachCommands": [ |             "postAttachCommands": [ | ||||||
|                 "monitor swdp_scan", |                 "monitor swdp_scan", | ||||||
|                 "attach 1", |                 "attach 1", | ||||||
|                 "set confirm off", |                 "set confirm off", | ||||||
|                 "set mem inaccessible-by-default off", |                 "set mem inaccessible-by-default off", | ||||||
|                 "source debug/flipperapps.py", |                 "source scripts/debug/flipperversion.py", | ||||||
|  |                 "fw-version", | ||||||
|  |                 "source scripts/debug/flipperapps.py", | ||||||
|                 "fap-set-debug-elf-root build/latest/.extapps", |                 "fap-set-debug-elf-root build/latest/.extapps", | ||||||
|                 // "compare-sections", |                 // "compare-sections", | ||||||
|             ] |             ] | ||||||
| @ -75,10 +77,12 @@ | |||||||
|             "servertype": "jlink", |             "servertype": "jlink", | ||||||
|             "interface": "swd", |             "interface": "swd", | ||||||
|             "device": "STM32WB55RG", |             "device": "STM32WB55RG", | ||||||
|             "svdFile": "./debug/STM32WB55_CM4.svd", |             "svdFile": "./scripts/debug/STM32WB55_CM4.svd", | ||||||
|             "rtos": "FreeRTOS", |             "rtos": "FreeRTOS", | ||||||
|             "postAttachCommands": [ |             "postAttachCommands": [ | ||||||
|                 "source debug/flipperapps.py", |                 "source scripts/debug/flipperversion.py", | ||||||
|  |                 "fw-version", | ||||||
|  |                 "source scripts/debug/flipperapps.py", | ||||||
|                 "fap-set-debug-elf-root build/latest/.extapps", |                 "fap-set-debug-elf-root build/latest/.extapps", | ||||||
|             ] |             ] | ||||||
|             // "showDevDebugOutput": "raw", |             // "showDevDebugOutput": "raw", | ||||||
| @ -91,27 +95,20 @@ | |||||||
|             "type": "cortex-debug", |             "type": "cortex-debug", | ||||||
|             "servertype": "openocd", |             "servertype": "openocd", | ||||||
|             "device": "cmsis-dap", |             "device": "cmsis-dap", | ||||||
|             "svdFile": "./debug/STM32WB55_CM4.svd", |             "svdFile": "./scripts/debug/STM32WB55_CM4.svd", | ||||||
|             "rtos": "FreeRTOS", |             "rtos": "FreeRTOS", | ||||||
|             "configFiles": [ |             "configFiles": [ | ||||||
|                 "interface/cmsis-dap.cfg", |                 "interface/cmsis-dap.cfg", | ||||||
|                 "./debug/stm32wbx.cfg", |                 "./scripts/debug/stm32wbx.cfg", | ||||||
|             ], |             ], | ||||||
|             "postAttachCommands": [ |             "postAttachCommands": [ | ||||||
|                 "source debug/flipperapps.py", |                 "source scripts/debug/flipperversion.py", | ||||||
|  |                 "fw-version", | ||||||
|  |                 "source scripts/debug/flipperapps.py", | ||||||
|                 "fap-set-debug-elf-root build/latest/.extapps", |                 "fap-set-debug-elf-root build/latest/.extapps", | ||||||
|             ], |             ], | ||||||
|             // "showDevDebugOutput": "raw", |             // "showDevDebugOutput": "raw", | ||||||
|         }, |         }, | ||||||
|         { |  | ||||||
|             "name": "fbt debug", |  | ||||||
|             "type": "python", |  | ||||||
|             "request": "launch", |  | ||||||
|             "program": "./lib/scons/scripts/scons.py", |  | ||||||
|             "args": [ |  | ||||||
|                 "plugin_dist" |  | ||||||
|             ] |  | ||||||
|         }, |  | ||||||
|         { |         { | ||||||
|             "name": "python debug", |             "name": "python debug", | ||||||
|             "type": "python", |             "type": "python", | ||||||
|  | |||||||
							
								
								
									
										3
									
								
								.vscode/extensions.json
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										3
									
								
								.vscode/extensions.json
									
									
									
									
										vendored
									
									
								
							| @ -8,7 +8,8 @@ | |||||||
| 		"amiralizadeh9480.cpp-helper", | 		"amiralizadeh9480.cpp-helper", | ||||||
| 		"marus25.cortex-debug", | 		"marus25.cortex-debug", | ||||||
| 		"zxh404.vscode-proto3", | 		"zxh404.vscode-proto3", | ||||||
| 		"augustocdias.tasks-shell-input" | 		"augustocdias.tasks-shell-input", | ||||||
|  | 		"rioj7.command-variable" | ||||||
| 	], | 	], | ||||||
| 	// List of extensions recommended by VS Code that should not be recommended for users of this workspace. | 	// List of extensions recommended by VS Code that should not be recommended for users of this workspace. | ||||||
| 	"unwantedRecommendations": [ | 	"unwantedRecommendations": [ | ||||||
|  | |||||||
| @ -99,7 +99,6 @@ Make sure your Flipper is on, and your firmware is functioning. Connect your Fli | |||||||
| - `applications`    - applications and services used in firmware | - `applications`    - applications and services used in firmware | ||||||
| - `assets`          - assets used by applications and services | - `assets`          - assets used by applications and services | ||||||
| - `furi`            - Furi Core: OS-level primitives and helpers | - `furi`            - Furi Core: OS-level primitives and helpers | ||||||
| - `debug`           - debug tool: GDB plugins, an SVD file, etc. |  | ||||||
| - `documentation`   - documentation generation system configs and input files | - `documentation`   - documentation generation system configs and input files | ||||||
| - `firmware`        - firmware source code | - `firmware`        - firmware source code | ||||||
| - `lib`             - our and 3rd party libraries, drivers, etc. | - `lib`             - our and 3rd party libraries, drivers, etc. | ||||||
|  | |||||||
							
								
								
									
										22
									
								
								SConstruct
									
									
									
									
									
								
							
							
						
						
									
										22
									
								
								SConstruct
									
									
									
									
									
								
							| @ -239,19 +239,31 @@ distenv.PhonyTarget( | |||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| # Debug alien elf | # Debug alien elf | ||||||
|  | debug_other_opts = [ | ||||||
|  |     "-ex", | ||||||
|  |     "source ${FBT_DEBUG_DIR}/PyCortexMDebug/PyCortexMDebug.py", | ||||||
|  |     # "-ex", | ||||||
|  |     # "source ${FBT_DEBUG_DIR}/FreeRTOS/FreeRTOS.py", | ||||||
|  |     "-ex", | ||||||
|  |     "source ${FBT_DEBUG_DIR}/flipperversion.py", | ||||||
|  |     "-ex", | ||||||
|  |     "fw-version", | ||||||
|  | ] | ||||||
|  | 
 | ||||||
| distenv.PhonyTarget( | distenv.PhonyTarget( | ||||||
|     "debug_other", |     "debug_other", | ||||||
|     "${GDBPYCOM}", |     "${GDBPYCOM}", | ||||||
|     GDBOPTS="${GDBOPTS_BASE}", |     GDBOPTS="${GDBOPTS_BASE}", | ||||||
|     GDBREMOTE="${OPENOCD_GDB_PIPE}", |     GDBREMOTE="${OPENOCD_GDB_PIPE}", | ||||||
|     GDBPYOPTS='-ex "source ${FBT_DEBUG_DIR}/PyCortexMDebug/PyCortexMDebug.py" ', |     GDBPYOPTS=debug_other_opts, | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| distenv.PhonyTarget( | distenv.PhonyTarget( | ||||||
|     "debug_other_blackmagic", |     "debug_other_blackmagic", | ||||||
|     "${GDBPYCOM}", |     "${GDBPYCOM}", | ||||||
|     GDBOPTS="${GDBOPTS_BASE}  ${GDBOPTS_BLACKMAGIC}", |     GDBOPTS="${GDBOPTS_BASE}  ${GDBOPTS_BLACKMAGIC}", | ||||||
|     GDBREMOTE="$${BLACKMAGIC_ADDR}", |     GDBREMOTE="${BLACKMAGIC_ADDR}", | ||||||
|  |     GDBPYOPTS=debug_other_opts, | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| @ -335,3 +347,9 @@ vscode_dist = distenv.Install("#.vscode", distenv.Glob("#.vscode/example/*")) | |||||||
| distenv.Precious(vscode_dist) | distenv.Precious(vscode_dist) | ||||||
| distenv.NoClean(vscode_dist) | distenv.NoClean(vscode_dist) | ||||||
| distenv.Alias("vscode_dist", vscode_dist) | distenv.Alias("vscode_dist", vscode_dist) | ||||||
|  | 
 | ||||||
|  | # Configure shell with build tools | ||||||
|  | distenv.PhonyTarget( | ||||||
|  |     "env", | ||||||
|  |     "@echo $( ${FBT_SCRIPT_DIR}/toolchain/fbtenv.sh $)", | ||||||
|  | ) | ||||||
|  | |||||||
| @ -407,7 +407,7 @@ MU_TEST(subghz_decoder_ido_test) { | |||||||
|         "Test decoder " SUBGHZ_PROTOCOL_IDO_NAME " error\r\n"); |         "Test decoder " SUBGHZ_PROTOCOL_IDO_NAME " error\r\n"); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| MU_TEST(subghz_decoder_keelog_test) { | MU_TEST(subghz_decoder_keeloq_test) { | ||||||
|     mu_assert( |     mu_assert( | ||||||
|         subghz_decoder_test( |         subghz_decoder_test( | ||||||
|             EXT_PATH("unit_tests/subghz/doorhan_raw.sub"), SUBGHZ_PROTOCOL_KEELOQ_NAME), |             EXT_PATH("unit_tests/subghz/doorhan_raw.sub"), SUBGHZ_PROTOCOL_KEELOQ_NAME), | ||||||
| @ -676,7 +676,7 @@ MU_TEST(subghz_encoder_nice_flo_test) { | |||||||
|         "Test encoder " SUBGHZ_PROTOCOL_NICE_FLO_NAME " error\r\n"); |         "Test encoder " SUBGHZ_PROTOCOL_NICE_FLO_NAME " error\r\n"); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| MU_TEST(subghz_encoder_keelog_test) { | MU_TEST(subghz_encoder_keeloq_test) { | ||||||
|     mu_assert( |     mu_assert( | ||||||
|         subghz_encoder_test(EXT_PATH("unit_tests/subghz/doorhan.sub")), |         subghz_encoder_test(EXT_PATH("unit_tests/subghz/doorhan.sub")), | ||||||
|         "Test encoder " SUBGHZ_PROTOCOL_KEELOQ_NAME " error\r\n"); |         "Test encoder " SUBGHZ_PROTOCOL_KEELOQ_NAME " error\r\n"); | ||||||
| @ -813,7 +813,7 @@ MU_TEST_SUITE(subghz) { | |||||||
|     MU_RUN_TEST(subghz_decoder_gate_tx_test); |     MU_RUN_TEST(subghz_decoder_gate_tx_test); | ||||||
|     MU_RUN_TEST(subghz_decoder_hormann_hsm_test); |     MU_RUN_TEST(subghz_decoder_hormann_hsm_test); | ||||||
|     MU_RUN_TEST(subghz_decoder_ido_test); |     MU_RUN_TEST(subghz_decoder_ido_test); | ||||||
|     MU_RUN_TEST(subghz_decoder_keelog_test); |     MU_RUN_TEST(subghz_decoder_keeloq_test); | ||||||
|     MU_RUN_TEST(subghz_decoder_kia_seed_test); |     MU_RUN_TEST(subghz_decoder_kia_seed_test); | ||||||
|     MU_RUN_TEST(subghz_decoder_nero_radio_test); |     MU_RUN_TEST(subghz_decoder_nero_radio_test); | ||||||
|     MU_RUN_TEST(subghz_decoder_nero_sketch_test); |     MU_RUN_TEST(subghz_decoder_nero_sketch_test); | ||||||
| @ -852,7 +852,7 @@ MU_TEST_SUITE(subghz) { | |||||||
|     MU_RUN_TEST(subghz_encoder_came_twee_test); |     MU_RUN_TEST(subghz_encoder_came_twee_test); | ||||||
|     MU_RUN_TEST(subghz_encoder_gate_tx_test); |     MU_RUN_TEST(subghz_encoder_gate_tx_test); | ||||||
|     MU_RUN_TEST(subghz_encoder_nice_flo_test); |     MU_RUN_TEST(subghz_encoder_nice_flo_test); | ||||||
|     MU_RUN_TEST(subghz_encoder_keelog_test); |     MU_RUN_TEST(subghz_encoder_keeloq_test); | ||||||
|     MU_RUN_TEST(subghz_encoder_linear_test); |     MU_RUN_TEST(subghz_encoder_linear_test); | ||||||
|     MU_RUN_TEST(subghz_encoder_linear_delta3_test); |     MU_RUN_TEST(subghz_encoder_linear_delta3_test); | ||||||
|     MU_RUN_TEST(subghz_encoder_megacode_test); |     MU_RUN_TEST(subghz_encoder_megacode_test); | ||||||
|  | |||||||
							
								
								
									
										22
									
								
								applications/external/hid_app/hid.c
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										22
									
								
								applications/external/hid_app/hid.c
									
									
									
									
										vendored
									
									
								
							| @ -11,6 +11,7 @@ enum HidDebugSubmenuIndex { | |||||||
|     HidSubmenuIndexMedia, |     HidSubmenuIndexMedia, | ||||||
|     HidSubmenuIndexTikTok, |     HidSubmenuIndexTikTok, | ||||||
|     HidSubmenuIndexMouse, |     HidSubmenuIndexMouse, | ||||||
|  |     HidSubmenuIndexMouseClicker, | ||||||
|     HidSubmenuIndexMouseJiggler, |     HidSubmenuIndexMouseJiggler, | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| @ -32,6 +33,9 @@ static void hid_submenu_callback(void* context, uint32_t index) { | |||||||
|     } else if(index == HidSubmenuIndexTikTok) { |     } else if(index == HidSubmenuIndexTikTok) { | ||||||
|         app->view_id = BtHidViewTikTok; |         app->view_id = BtHidViewTikTok; | ||||||
|         view_dispatcher_switch_to_view(app->view_dispatcher, BtHidViewTikTok); |         view_dispatcher_switch_to_view(app->view_dispatcher, BtHidViewTikTok); | ||||||
|  |     } else if(index == HidSubmenuIndexMouseClicker) { | ||||||
|  |         app->view_id = HidViewMouseClicker; | ||||||
|  |         view_dispatcher_switch_to_view(app->view_dispatcher, HidViewMouseClicker); | ||||||
|     } else if(index == HidSubmenuIndexMouseJiggler) { |     } else if(index == HidSubmenuIndexMouseJiggler) { | ||||||
|         app->view_id = HidViewMouseJiggler; |         app->view_id = HidViewMouseJiggler; | ||||||
|         view_dispatcher_switch_to_view(app->view_dispatcher, HidViewMouseJiggler); |         view_dispatcher_switch_to_view(app->view_dispatcher, HidViewMouseJiggler); | ||||||
| @ -53,6 +57,7 @@ static void bt_hid_connection_status_changed_callback(BtStatus status, void* con | |||||||
|     hid_keyboard_set_connected_status(hid->hid_keyboard, connected); |     hid_keyboard_set_connected_status(hid->hid_keyboard, connected); | ||||||
|     hid_media_set_connected_status(hid->hid_media, connected); |     hid_media_set_connected_status(hid->hid_media, connected); | ||||||
|     hid_mouse_set_connected_status(hid->hid_mouse, connected); |     hid_mouse_set_connected_status(hid->hid_mouse, connected); | ||||||
|  |     hid_mouse_clicker_set_connected_status(hid->hid_mouse_clicker, connected); | ||||||
|     hid_mouse_jiggler_set_connected_status(hid->hid_mouse_jiggler, connected); |     hid_mouse_jiggler_set_connected_status(hid->hid_mouse_jiggler, connected); | ||||||
|     hid_tiktok_set_connected_status(hid->hid_tiktok, connected); |     hid_tiktok_set_connected_status(hid->hid_tiktok, connected); | ||||||
| } | } | ||||||
| @ -114,6 +119,12 @@ Hid* hid_alloc(HidTransport transport) { | |||||||
|             hid_submenu_callback, |             hid_submenu_callback, | ||||||
|             app); |             app); | ||||||
|     } |     } | ||||||
|  |     submenu_add_item( | ||||||
|  |         app->device_type_submenu, | ||||||
|  |         "Mouse Clicker", | ||||||
|  |         HidSubmenuIndexMouseClicker, | ||||||
|  |         hid_submenu_callback, | ||||||
|  |         app); | ||||||
|     submenu_add_item( |     submenu_add_item( | ||||||
|         app->device_type_submenu, |         app->device_type_submenu, | ||||||
|         "Mouse Jiggler", |         "Mouse Jiggler", | ||||||
| @ -172,6 +183,15 @@ Hid* hid_app_alloc_view(void* context) { | |||||||
|     view_dispatcher_add_view( |     view_dispatcher_add_view( | ||||||
|         app->view_dispatcher, HidViewMouse, hid_mouse_get_view(app->hid_mouse)); |         app->view_dispatcher, HidViewMouse, hid_mouse_get_view(app->hid_mouse)); | ||||||
| 
 | 
 | ||||||
|  |     // Mouse clicker view
 | ||||||
|  |     app->hid_mouse_clicker = hid_mouse_clicker_alloc(app); | ||||||
|  |     view_set_previous_callback( | ||||||
|  |         hid_mouse_clicker_get_view(app->hid_mouse_clicker), hid_exit_confirm_view); | ||||||
|  |     view_dispatcher_add_view( | ||||||
|  |         app->view_dispatcher, | ||||||
|  |         HidViewMouseClicker, | ||||||
|  |         hid_mouse_clicker_get_view(app->hid_mouse_clicker)); | ||||||
|  | 
 | ||||||
|     // Mouse jiggler view
 |     // Mouse jiggler view
 | ||||||
|     app->hid_mouse_jiggler = hid_mouse_jiggler_alloc(app); |     app->hid_mouse_jiggler = hid_mouse_jiggler_alloc(app); | ||||||
|     view_set_previous_callback( |     view_set_previous_callback( | ||||||
| @ -205,6 +225,8 @@ void hid_free(Hid* app) { | |||||||
|     hid_media_free(app->hid_media); |     hid_media_free(app->hid_media); | ||||||
|     view_dispatcher_remove_view(app->view_dispatcher, HidViewMouse); |     view_dispatcher_remove_view(app->view_dispatcher, HidViewMouse); | ||||||
|     hid_mouse_free(app->hid_mouse); |     hid_mouse_free(app->hid_mouse); | ||||||
|  |     view_dispatcher_remove_view(app->view_dispatcher, HidViewMouseClicker); | ||||||
|  |     hid_mouse_clicker_free(app->hid_mouse_clicker); | ||||||
|     view_dispatcher_remove_view(app->view_dispatcher, HidViewMouseJiggler); |     view_dispatcher_remove_view(app->view_dispatcher, HidViewMouseJiggler); | ||||||
|     hid_mouse_jiggler_free(app->hid_mouse_jiggler); |     hid_mouse_jiggler_free(app->hid_mouse_jiggler); | ||||||
|     view_dispatcher_remove_view(app->view_dispatcher, BtHidViewTikTok); |     view_dispatcher_remove_view(app->view_dispatcher, BtHidViewTikTok); | ||||||
|  | |||||||
							
								
								
									
										2
									
								
								applications/external/hid_app/hid.h
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								applications/external/hid_app/hid.h
									
									
									
									
										vendored
									
									
								
							| @ -20,6 +20,7 @@ | |||||||
| #include "views/hid_keyboard.h" | #include "views/hid_keyboard.h" | ||||||
| #include "views/hid_media.h" | #include "views/hid_media.h" | ||||||
| #include "views/hid_mouse.h" | #include "views/hid_mouse.h" | ||||||
|  | #include "views/hid_mouse_clicker.h" | ||||||
| #include "views/hid_mouse_jiggler.h" | #include "views/hid_mouse_jiggler.h" | ||||||
| #include "views/hid_tiktok.h" | #include "views/hid_tiktok.h" | ||||||
| 
 | 
 | ||||||
| @ -43,6 +44,7 @@ struct Hid { | |||||||
|     HidKeyboard* hid_keyboard; |     HidKeyboard* hid_keyboard; | ||||||
|     HidMedia* hid_media; |     HidMedia* hid_media; | ||||||
|     HidMouse* hid_mouse; |     HidMouse* hid_mouse; | ||||||
|  |     HidMouseClicker* hid_mouse_clicker; | ||||||
|     HidMouseJiggler* hid_mouse_jiggler; |     HidMouseJiggler* hid_mouse_jiggler; | ||||||
|     HidTikTok* hid_tiktok; |     HidTikTok* hid_tiktok; | ||||||
| 
 | 
 | ||||||
|  | |||||||
							
								
								
									
										1
									
								
								applications/external/hid_app/views.h
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								applications/external/hid_app/views.h
									
									
									
									
										vendored
									
									
								
							| @ -4,6 +4,7 @@ typedef enum { | |||||||
|     HidViewKeyboard, |     HidViewKeyboard, | ||||||
|     HidViewMedia, |     HidViewMedia, | ||||||
|     HidViewMouse, |     HidViewMouse, | ||||||
|  |     HidViewMouseClicker, | ||||||
|     HidViewMouseJiggler, |     HidViewMouseJiggler, | ||||||
|     BtHidViewTikTok, |     BtHidViewTikTok, | ||||||
|     HidViewExitConfirm, |     HidViewExitConfirm, | ||||||
|  | |||||||
							
								
								
									
										214
									
								
								applications/external/hid_app/views/hid_mouse_clicker.c
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										214
									
								
								applications/external/hid_app/views/hid_mouse_clicker.c
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,214 @@ | |||||||
|  | #include "hid_mouse_clicker.h" | ||||||
|  | #include <gui/elements.h> | ||||||
|  | #include "../hid.h" | ||||||
|  | 
 | ||||||
|  | #include "hid_icons.h" | ||||||
|  | 
 | ||||||
|  | #define TAG "HidMouseClicker" | ||||||
|  | #define DEFAULT_CLICK_RATE 1 | ||||||
|  | #define MAXIMUM_CLICK_RATE 60 | ||||||
|  | 
 | ||||||
|  | struct HidMouseClicker { | ||||||
|  |     View* view; | ||||||
|  |     Hid* hid; | ||||||
|  |     FuriTimer* timer; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | typedef struct { | ||||||
|  |     bool connected; | ||||||
|  |     bool running; | ||||||
|  |     int rate; | ||||||
|  |     HidTransport transport; | ||||||
|  | } HidMouseClickerModel; | ||||||
|  | 
 | ||||||
|  | static void hid_mouse_clicker_start_or_restart_timer(void* context) { | ||||||
|  |     furi_assert(context); | ||||||
|  |     HidMouseClicker* hid_mouse_clicker = context; | ||||||
|  | 
 | ||||||
|  |     if(furi_timer_is_running(hid_mouse_clicker->timer)) { | ||||||
|  |         furi_timer_stop(hid_mouse_clicker->timer); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     with_view_model( | ||||||
|  |         hid_mouse_clicker->view, | ||||||
|  |         HidMouseClickerModel * model, | ||||||
|  |         { | ||||||
|  |             furi_timer_start( | ||||||
|  |                 hid_mouse_clicker->timer, furi_kernel_get_tick_frequency() / model->rate); | ||||||
|  |         }, | ||||||
|  |         true); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void hid_mouse_clicker_draw_callback(Canvas* canvas, void* context) { | ||||||
|  |     furi_assert(context); | ||||||
|  |     HidMouseClickerModel* model = context; | ||||||
|  | 
 | ||||||
|  |     // Header
 | ||||||
|  |     if(model->transport == HidTransportBle) { | ||||||
|  |         if(model->connected) { | ||||||
|  |             canvas_draw_icon(canvas, 0, 0, &I_Ble_connected_15x15); | ||||||
|  |         } else { | ||||||
|  |             canvas_draw_icon(canvas, 0, 0, &I_Ble_disconnected_15x15); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     canvas_set_font(canvas, FontPrimary); | ||||||
|  |     elements_multiline_text_aligned(canvas, 17, 3, AlignLeft, AlignTop, "Mouse Clicker"); | ||||||
|  | 
 | ||||||
|  |     // Ok
 | ||||||
|  |     canvas_draw_icon(canvas, 63, 25, &I_Space_65x18); | ||||||
|  |     if(model->running) { | ||||||
|  |         canvas_set_font(canvas, FontPrimary); | ||||||
|  | 
 | ||||||
|  |         FuriString* rate_label = furi_string_alloc(); | ||||||
|  |         furi_string_printf(rate_label, "%d clicks/s\n\nUp / Down", model->rate); | ||||||
|  |         elements_multiline_text(canvas, AlignLeft, 35, furi_string_get_cstr(rate_label)); | ||||||
|  |         canvas_set_font(canvas, FontSecondary); | ||||||
|  |         furi_string_free(rate_label); | ||||||
|  | 
 | ||||||
|  |         elements_slightly_rounded_box(canvas, 66, 27, 60, 13); | ||||||
|  |         canvas_set_color(canvas, ColorWhite); | ||||||
|  |     } else { | ||||||
|  |         canvas_set_font(canvas, FontPrimary); | ||||||
|  |         elements_multiline_text(canvas, AlignLeft, 35, "Press Start\nto start\nclicking"); | ||||||
|  |         canvas_set_font(canvas, FontSecondary); | ||||||
|  |     } | ||||||
|  |     canvas_draw_icon(canvas, 74, 29, &I_Ok_btn_9x9); | ||||||
|  |     if(model->running) { | ||||||
|  |         elements_multiline_text_aligned(canvas, 91, 36, AlignLeft, AlignBottom, "Stop"); | ||||||
|  |     } else { | ||||||
|  |         elements_multiline_text_aligned(canvas, 91, 36, AlignLeft, AlignBottom, "Start"); | ||||||
|  |     } | ||||||
|  |     canvas_set_color(canvas, ColorBlack); | ||||||
|  | 
 | ||||||
|  |     // Back
 | ||||||
|  |     canvas_draw_icon(canvas, 74, 49, &I_Pin_back_arrow_10x8); | ||||||
|  |     elements_multiline_text_aligned(canvas, 91, 57, AlignLeft, AlignBottom, "Quit"); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void hid_mouse_clicker_timer_callback(void* context) { | ||||||
|  |     furi_assert(context); | ||||||
|  |     HidMouseClicker* hid_mouse_clicker = context; | ||||||
|  |     with_view_model( | ||||||
|  |         hid_mouse_clicker->view, | ||||||
|  |         HidMouseClickerModel * model, | ||||||
|  |         { | ||||||
|  |             if(model->running) { | ||||||
|  |                 hid_hal_mouse_press(hid_mouse_clicker->hid, HID_MOUSE_BTN_LEFT); | ||||||
|  |                 hid_hal_mouse_release(hid_mouse_clicker->hid, HID_MOUSE_BTN_LEFT); | ||||||
|  |             } | ||||||
|  |         }, | ||||||
|  |         false); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void hid_mouse_clicker_enter_callback(void* context) { | ||||||
|  |     hid_mouse_clicker_start_or_restart_timer(context); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void hid_mouse_clicker_exit_callback(void* context) { | ||||||
|  |     furi_assert(context); | ||||||
|  |     HidMouseClicker* hid_mouse_clicker = context; | ||||||
|  |     furi_timer_stop(hid_mouse_clicker->timer); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static bool hid_mouse_clicker_input_callback(InputEvent* event, void* context) { | ||||||
|  |     furi_assert(context); | ||||||
|  |     HidMouseClicker* hid_mouse_clicker = context; | ||||||
|  | 
 | ||||||
|  |     bool consumed = false; | ||||||
|  |     bool rate_changed = false; | ||||||
|  | 
 | ||||||
|  |     if(event->type != InputTypeShort && event->type != InputTypeRepeat) { | ||||||
|  |         return false; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     with_view_model( | ||||||
|  |         hid_mouse_clicker->view, | ||||||
|  |         HidMouseClickerModel * model, | ||||||
|  |         { | ||||||
|  |             switch(event->key) { | ||||||
|  |             case InputKeyOk: | ||||||
|  |                 model->running = !model->running; | ||||||
|  |                 consumed = true; | ||||||
|  |                 break; | ||||||
|  |             case InputKeyUp: | ||||||
|  |                 if(model->rate < MAXIMUM_CLICK_RATE) { | ||||||
|  |                     model->rate++; | ||||||
|  |                 } | ||||||
|  |                 rate_changed = true; | ||||||
|  |                 consumed = true; | ||||||
|  |                 break; | ||||||
|  |             case InputKeyDown: | ||||||
|  |                 if(model->rate > 1) { | ||||||
|  |                     model->rate--; | ||||||
|  |                 } | ||||||
|  |                 rate_changed = true; | ||||||
|  |                 consumed = true; | ||||||
|  |                 break; | ||||||
|  |             default: | ||||||
|  |                 consumed = true; | ||||||
|  |                 break; | ||||||
|  |             } | ||||||
|  |         }, | ||||||
|  |         true); | ||||||
|  | 
 | ||||||
|  |     if(rate_changed) { | ||||||
|  |         hid_mouse_clicker_start_or_restart_timer(context); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return consumed; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | HidMouseClicker* hid_mouse_clicker_alloc(Hid* hid) { | ||||||
|  |     HidMouseClicker* hid_mouse_clicker = malloc(sizeof(HidMouseClicker)); | ||||||
|  | 
 | ||||||
|  |     hid_mouse_clicker->view = view_alloc(); | ||||||
|  |     view_set_context(hid_mouse_clicker->view, hid_mouse_clicker); | ||||||
|  |     view_allocate_model( | ||||||
|  |         hid_mouse_clicker->view, ViewModelTypeLocking, sizeof(HidMouseClickerModel)); | ||||||
|  |     view_set_draw_callback(hid_mouse_clicker->view, hid_mouse_clicker_draw_callback); | ||||||
|  |     view_set_input_callback(hid_mouse_clicker->view, hid_mouse_clicker_input_callback); | ||||||
|  |     view_set_enter_callback(hid_mouse_clicker->view, hid_mouse_clicker_enter_callback); | ||||||
|  |     view_set_exit_callback(hid_mouse_clicker->view, hid_mouse_clicker_exit_callback); | ||||||
|  | 
 | ||||||
|  |     hid_mouse_clicker->hid = hid; | ||||||
|  | 
 | ||||||
|  |     hid_mouse_clicker->timer = furi_timer_alloc( | ||||||
|  |         hid_mouse_clicker_timer_callback, FuriTimerTypePeriodic, hid_mouse_clicker); | ||||||
|  | 
 | ||||||
|  |     with_view_model( | ||||||
|  |         hid_mouse_clicker->view, | ||||||
|  |         HidMouseClickerModel * model, | ||||||
|  |         { | ||||||
|  |             model->transport = hid->transport; | ||||||
|  |             model->rate = DEFAULT_CLICK_RATE; | ||||||
|  |         }, | ||||||
|  |         true); | ||||||
|  | 
 | ||||||
|  |     return hid_mouse_clicker; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void hid_mouse_clicker_free(HidMouseClicker* hid_mouse_clicker) { | ||||||
|  |     furi_assert(hid_mouse_clicker); | ||||||
|  | 
 | ||||||
|  |     furi_timer_stop(hid_mouse_clicker->timer); | ||||||
|  |     furi_timer_free(hid_mouse_clicker->timer); | ||||||
|  | 
 | ||||||
|  |     view_free(hid_mouse_clicker->view); | ||||||
|  | 
 | ||||||
|  |     free(hid_mouse_clicker); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | View* hid_mouse_clicker_get_view(HidMouseClicker* hid_mouse_clicker) { | ||||||
|  |     furi_assert(hid_mouse_clicker); | ||||||
|  |     return hid_mouse_clicker->view; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void hid_mouse_clicker_set_connected_status(HidMouseClicker* hid_mouse_clicker, bool connected) { | ||||||
|  |     furi_assert(hid_mouse_clicker); | ||||||
|  |     with_view_model( | ||||||
|  |         hid_mouse_clicker->view, | ||||||
|  |         HidMouseClickerModel * model, | ||||||
|  |         { model->connected = connected; }, | ||||||
|  |         true); | ||||||
|  | } | ||||||
							
								
								
									
										14
									
								
								applications/external/hid_app/views/hid_mouse_clicker.h
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								applications/external/hid_app/views/hid_mouse_clicker.h
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,14 @@ | |||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | #include <gui/view.h> | ||||||
|  | 
 | ||||||
|  | typedef struct Hid Hid; | ||||||
|  | typedef struct HidMouseClicker HidMouseClicker; | ||||||
|  | 
 | ||||||
|  | HidMouseClicker* hid_mouse_clicker_alloc(Hid* bt_hid); | ||||||
|  | 
 | ||||||
|  | void hid_mouse_clicker_free(HidMouseClicker* hid_mouse_clicker); | ||||||
|  | 
 | ||||||
|  | View* hid_mouse_clicker_get_view(HidMouseClicker* hid_mouse_clicker); | ||||||
|  | 
 | ||||||
|  | void hid_mouse_clicker_set_connected_status(HidMouseClicker* hid_mouse_clicker, bool connected); | ||||||
| @ -95,7 +95,7 @@ static bool hid_mouse_jiggler_input_callback(InputEvent* event, void* context) { | |||||||
| 
 | 
 | ||||||
|     bool consumed = false; |     bool consumed = false; | ||||||
| 
 | 
 | ||||||
|     if(event->key == InputKeyOk) { |     if(event->type == InputTypeShort && event->key == InputKeyOk) { | ||||||
|         with_view_model( |         with_view_model( | ||||||
|             hid_mouse_jiggler->view, |             hid_mouse_jiggler->view, | ||||||
|             HidMouseJigglerModel * model, |             HidMouseJigglerModel * model, | ||||||
|  | |||||||
							
								
								
									
										17
									
								
								applications/external/mfkey32/application.fam
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								applications/external/mfkey32/application.fam
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,17 @@ | |||||||
|  | App( | ||||||
|  |     appid="mfkey32", | ||||||
|  |     name="Mfkey32", | ||||||
|  |     apptype=FlipperAppType.EXTERNAL, | ||||||
|  |     targets=["f7"], | ||||||
|  |     entry_point="mfkey32_main", | ||||||
|  |     requires=[ | ||||||
|  |         "gui", | ||||||
|  |         "storage", | ||||||
|  |     ], | ||||||
|  |     stack_size=1 * 1024, | ||||||
|  |     fap_icon="mfkey.png", | ||||||
|  |     fap_category="Nfc", | ||||||
|  |     fap_author="noproto", | ||||||
|  |     fap_icon_assets="images", | ||||||
|  |     fap_weburl="https://github.com/noproto/FlipperMfkey", | ||||||
|  | ) | ||||||
							
								
								
									
										
											BIN
										
									
								
								applications/external/mfkey32/images/mfkey.png
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								applications/external/mfkey32/images/mfkey.png
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 8.8 KiB | 
							
								
								
									
										
											BIN
										
									
								
								applications/external/mfkey32/mfkey.png
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								applications/external/mfkey32/mfkey.png
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 8.8 KiB | 
							
								
								
									
										1349
									
								
								applications/external/mfkey32/mfkey32.c
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										1349
									
								
								applications/external/mfkey32/mfkey32.c
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @ -34,7 +34,7 @@ void picopass_scene_read_card_success_on_enter(void* context) { | |||||||
|     uint8_t csn[PICOPASS_BLOCK_LEN] = {0}; |     uint8_t csn[PICOPASS_BLOCK_LEN] = {0}; | ||||||
|     memcpy(csn, AA1[PICOPASS_CSN_BLOCK_INDEX].data, PICOPASS_BLOCK_LEN); |     memcpy(csn, AA1[PICOPASS_CSN_BLOCK_INDEX].data, PICOPASS_BLOCK_LEN); | ||||||
|     for(uint8_t i = 0; i < PICOPASS_BLOCK_LEN; i++) { |     for(uint8_t i = 0; i < PICOPASS_BLOCK_LEN; i++) { | ||||||
|         furi_string_cat_printf(csn_str, "%02X ", csn[i]); |         furi_string_cat_printf(csn_str, "%02X", csn[i]); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     bool no_key = picopass_is_memset(pacs->key, 0x00, PICOPASS_BLOCK_LEN); |     bool no_key = picopass_is_memset(pacs->key, 0x00, PICOPASS_BLOCK_LEN); | ||||||
|  | |||||||
| @ -16,6 +16,7 @@ const SubGhzProtocol* weather_station_protocol_registry_items[] = { | |||||||
|     &ws_protocol_auriol_th, |     &ws_protocol_auriol_th, | ||||||
|     &ws_protocol_oregon_v1, |     &ws_protocol_oregon_v1, | ||||||
|     &ws_protocol_tx_8300, |     &ws_protocol_tx_8300, | ||||||
|  |     &ws_protocol_wendox_w6726, | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| const SubGhzProtocolRegistry weather_station_protocol_registry = { | const SubGhzProtocolRegistry weather_station_protocol_registry = { | ||||||
|  | |||||||
| @ -16,5 +16,6 @@ | |||||||
| #include "auriol_hg0601a.h" | #include "auriol_hg0601a.h" | ||||||
| #include "oregon_v1.h" | #include "oregon_v1.h" | ||||||
| #include "tx_8300.h" | #include "tx_8300.h" | ||||||
|  | #include "wendox_w6726.h" | ||||||
| 
 | 
 | ||||||
| extern const SubGhzProtocolRegistry weather_station_protocol_registry; | extern const SubGhzProtocolRegistry weather_station_protocol_registry; | ||||||
|  | |||||||
							
								
								
									
										299
									
								
								applications/external/weather_station/protocols/wendox_w6726.c
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										299
									
								
								applications/external/weather_station/protocols/wendox_w6726.c
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,299 @@ | |||||||
|  | #include "wendox_w6726.h" | ||||||
|  | 
 | ||||||
|  | #define TAG "WSProtocolWendoxW6726" | ||||||
|  | 
 | ||||||
|  | /*
 | ||||||
|  |  * Wendox W6726 | ||||||
|  |  *    | ||||||
|  |  *  Temperature -50C to +70C | ||||||
|  |  *    _     _     _          __   _ | ||||||
|  |  *  _| |___| |___| |___ ... |  |_| |__...._______________ | ||||||
|  |  *    preamble                data           guard time | ||||||
|  |  *  | ||||||
|  |  *  3 reps every 3 minutes | ||||||
|  |  *  in the first message 11 bytes of the preamble in the rest by 7 | ||||||
|  |  *   | ||||||
|  |  *  bit 0: 1955-hi, 5865-lo | ||||||
|  |  *  bit 1: 5865-hi, 1955-lo | ||||||
|  |  *  guard time: 12*1955+(lo last bit)  | ||||||
|  |  *  data: 29 bit | ||||||
|  |  *  | ||||||
|  |  *  IIIII | ZTTTTTTTTT | uuuuuuuBuu | CCCC | ||||||
|  |  *  | ||||||
|  |  *  I: identification; | ||||||
|  |  *  Z: temperature sign; | ||||||
|  |  *  T: temperature sign dependent +12C; | ||||||
|  |  *  B: battery low; flag to indicate low battery voltage; | ||||||
|  |  *  C: CRC4 (polynomial = 0x9, start_data = 0xD); | ||||||
|  |  *  u: unknown;  | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | static const SubGhzBlockConst ws_protocol_wendox_w6726_const = { | ||||||
|  |     .te_short = 1955, | ||||||
|  |     .te_long = 5865, | ||||||
|  |     .te_delta = 300, | ||||||
|  |     .min_count_bit_for_found = 29, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | struct WSProtocolDecoderWendoxW6726 { | ||||||
|  |     SubGhzProtocolDecoderBase base; | ||||||
|  | 
 | ||||||
|  |     SubGhzBlockDecoder decoder; | ||||||
|  |     WSBlockGeneric generic; | ||||||
|  | 
 | ||||||
|  |     uint16_t header_count; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | struct WSProtocolEncoderWendoxW6726 { | ||||||
|  |     SubGhzProtocolEncoderBase base; | ||||||
|  | 
 | ||||||
|  |     SubGhzProtocolBlockEncoder encoder; | ||||||
|  |     WSBlockGeneric generic; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | typedef enum { | ||||||
|  |     WendoxW6726DecoderStepReset = 0, | ||||||
|  |     WendoxW6726DecoderStepCheckPreambule, | ||||||
|  |     WendoxW6726DecoderStepSaveDuration, | ||||||
|  |     WendoxW6726DecoderStepCheckDuration, | ||||||
|  | } WendoxW6726DecoderStep; | ||||||
|  | 
 | ||||||
|  | const SubGhzProtocolDecoder ws_protocol_wendox_w6726_decoder = { | ||||||
|  |     .alloc = ws_protocol_decoder_wendox_w6726_alloc, | ||||||
|  |     .free = ws_protocol_decoder_wendox_w6726_free, | ||||||
|  | 
 | ||||||
|  |     .feed = ws_protocol_decoder_wendox_w6726_feed, | ||||||
|  |     .reset = ws_protocol_decoder_wendox_w6726_reset, | ||||||
|  | 
 | ||||||
|  |     .get_hash_data = ws_protocol_decoder_wendox_w6726_get_hash_data, | ||||||
|  |     .serialize = ws_protocol_decoder_wendox_w6726_serialize, | ||||||
|  |     .deserialize = ws_protocol_decoder_wendox_w6726_deserialize, | ||||||
|  |     .get_string = ws_protocol_decoder_wendox_w6726_get_string, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | const SubGhzProtocolEncoder ws_protocol_wendox_w6726_encoder = { | ||||||
|  |     .alloc = NULL, | ||||||
|  |     .free = NULL, | ||||||
|  | 
 | ||||||
|  |     .deserialize = NULL, | ||||||
|  |     .stop = NULL, | ||||||
|  |     .yield = NULL, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | const SubGhzProtocol ws_protocol_wendox_w6726 = { | ||||||
|  |     .name = WS_PROTOCOL_WENDOX_W6726_NAME, | ||||||
|  |     .type = SubGhzProtocolWeatherStation, | ||||||
|  |     .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_315 | SubGhzProtocolFlag_868 | | ||||||
|  |             SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable, | ||||||
|  | 
 | ||||||
|  |     .decoder = &ws_protocol_wendox_w6726_decoder, | ||||||
|  |     .encoder = &ws_protocol_wendox_w6726_encoder, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | void* ws_protocol_decoder_wendox_w6726_alloc(SubGhzEnvironment* environment) { | ||||||
|  |     UNUSED(environment); | ||||||
|  |     WSProtocolDecoderWendoxW6726* instance = malloc(sizeof(WSProtocolDecoderWendoxW6726)); | ||||||
|  |     instance->base.protocol = &ws_protocol_wendox_w6726; | ||||||
|  |     instance->generic.protocol_name = instance->base.protocol->name; | ||||||
|  |     return instance; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void ws_protocol_decoder_wendox_w6726_free(void* context) { | ||||||
|  |     furi_assert(context); | ||||||
|  |     WSProtocolDecoderWendoxW6726* instance = context; | ||||||
|  |     free(instance); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void ws_protocol_decoder_wendox_w6726_reset(void* context) { | ||||||
|  |     furi_assert(context); | ||||||
|  |     WSProtocolDecoderWendoxW6726* instance = context; | ||||||
|  |     instance->decoder.parser_step = WendoxW6726DecoderStepReset; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static bool ws_protocol_wendox_w6726_check(WSProtocolDecoderWendoxW6726* instance) { | ||||||
|  |     if(!instance->decoder.decode_data) return false; | ||||||
|  |     uint8_t msg[] = { | ||||||
|  |         instance->decoder.decode_data >> 28, | ||||||
|  |         instance->decoder.decode_data >> 20, | ||||||
|  |         instance->decoder.decode_data >> 12, | ||||||
|  |         instance->decoder.decode_data >> 4}; | ||||||
|  | 
 | ||||||
|  |     uint8_t crc = subghz_protocol_blocks_crc4(msg, 4, 0x9, 0xD); | ||||||
|  |     return (crc == (instance->decoder.decode_data & 0x0F)); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Analysis of received data | ||||||
|  |  * @param instance Pointer to a WSBlockGeneric* instance | ||||||
|  |  */ | ||||||
|  | static void ws_protocol_wendox_w6726_remote_controller(WSBlockGeneric* instance) { | ||||||
|  |     instance->id = (instance->data >> 24) & 0xFF; | ||||||
|  |     instance->battery_low = (instance->data >> 6) & 1; | ||||||
|  |     instance->channel = WS_NO_CHANNEL; | ||||||
|  | 
 | ||||||
|  |     if(((instance->data >> 23) & 1)) { | ||||||
|  |         instance->temp = (float)(((instance->data >> 14) & 0x1FF) + 12) / 10.0f; | ||||||
|  |     } else { | ||||||
|  |         instance->temp = (float)((~(instance->data >> 14) & 0x1FF) + 1 - 12) / -10.0f; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if(instance->temp < -50.0f) { | ||||||
|  |         instance->temp = -50.0f; | ||||||
|  |     } else if(instance->temp > 70.0f) { | ||||||
|  |         instance->temp = 70.0f; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     instance->btn = WS_NO_BTN; | ||||||
|  |     instance->humidity = WS_NO_HUMIDITY; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void ws_protocol_decoder_wendox_w6726_feed(void* context, bool level, uint32_t duration) { | ||||||
|  |     furi_assert(context); | ||||||
|  |     WSProtocolDecoderWendoxW6726* instance = context; | ||||||
|  | 
 | ||||||
|  |     switch(instance->decoder.parser_step) { | ||||||
|  |     case WendoxW6726DecoderStepReset: | ||||||
|  |         if((level) && (DURATION_DIFF(duration, ws_protocol_wendox_w6726_const.te_short) < | ||||||
|  |                        ws_protocol_wendox_w6726_const.te_delta)) { | ||||||
|  |             instance->decoder.parser_step = WendoxW6726DecoderStepCheckPreambule; | ||||||
|  |             instance->decoder.te_last = duration; | ||||||
|  |             instance->header_count = 0; | ||||||
|  |         } | ||||||
|  |         break; | ||||||
|  | 
 | ||||||
|  |     case WendoxW6726DecoderStepCheckPreambule: | ||||||
|  |         if(level) { | ||||||
|  |             instance->decoder.te_last = duration; | ||||||
|  |         } else { | ||||||
|  |             if((DURATION_DIFF(instance->decoder.te_last, ws_protocol_wendox_w6726_const.te_short) < | ||||||
|  |                 ws_protocol_wendox_w6726_const.te_delta * 1) && | ||||||
|  |                (DURATION_DIFF(duration, ws_protocol_wendox_w6726_const.te_long) < | ||||||
|  |                 ws_protocol_wendox_w6726_const.te_delta * 2)) { | ||||||
|  |                 instance->header_count++; | ||||||
|  |             } else if((instance->header_count > 4) && (instance->header_count < 12)) { | ||||||
|  |                 if((DURATION_DIFF( | ||||||
|  |                         instance->decoder.te_last, ws_protocol_wendox_w6726_const.te_long) < | ||||||
|  |                     ws_protocol_wendox_w6726_const.te_delta * 2) && | ||||||
|  |                    (DURATION_DIFF(duration, ws_protocol_wendox_w6726_const.te_short) < | ||||||
|  |                     ws_protocol_wendox_w6726_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 = WendoxW6726DecoderStepSaveDuration; | ||||||
|  |                 } else { | ||||||
|  |                     instance->decoder.parser_step = WendoxW6726DecoderStepReset; | ||||||
|  |                 } | ||||||
|  | 
 | ||||||
|  |             } else { | ||||||
|  |                 instance->decoder.parser_step = WendoxW6726DecoderStepReset; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         break; | ||||||
|  | 
 | ||||||
|  |     case WendoxW6726DecoderStepSaveDuration: | ||||||
|  |         if(level) { | ||||||
|  |             instance->decoder.te_last = duration; | ||||||
|  |             instance->decoder.parser_step = WendoxW6726DecoderStepCheckDuration; | ||||||
|  |         } else { | ||||||
|  |             instance->decoder.parser_step = WendoxW6726DecoderStepReset; | ||||||
|  |         } | ||||||
|  |         break; | ||||||
|  | 
 | ||||||
|  |     case WendoxW6726DecoderStepCheckDuration: | ||||||
|  |         if(!level) { | ||||||
|  |             if(duration > | ||||||
|  |                ws_protocol_wendox_w6726_const.te_short + ws_protocol_wendox_w6726_const.te_long) { | ||||||
|  |                 if(DURATION_DIFF( | ||||||
|  |                        instance->decoder.te_last, ws_protocol_wendox_w6726_const.te_short) < | ||||||
|  |                    ws_protocol_wendox_w6726_const.te_delta) { | ||||||
|  |                     subghz_protocol_blocks_add_bit(&instance->decoder, 0); | ||||||
|  |                     instance->decoder.parser_step = WendoxW6726DecoderStepSaveDuration; | ||||||
|  |                 } else if( | ||||||
|  |                     DURATION_DIFF( | ||||||
|  |                         instance->decoder.te_last, ws_protocol_wendox_w6726_const.te_long) < | ||||||
|  |                     ws_protocol_wendox_w6726_const.te_delta * 2) { | ||||||
|  |                     subghz_protocol_blocks_add_bit(&instance->decoder, 1); | ||||||
|  |                     instance->decoder.parser_step = WendoxW6726DecoderStepSaveDuration; | ||||||
|  |                 } else { | ||||||
|  |                     instance->decoder.parser_step = WendoxW6726DecoderStepReset; | ||||||
|  |                 } | ||||||
|  |                 if((instance->decoder.decode_count_bit == | ||||||
|  |                     ws_protocol_wendox_w6726_const.min_count_bit_for_found) && | ||||||
|  |                    ws_protocol_wendox_w6726_check(instance)) { | ||||||
|  |                     instance->generic.data = instance->decoder.decode_data; | ||||||
|  |                     instance->generic.data_count_bit = instance->decoder.decode_count_bit; | ||||||
|  |                     ws_protocol_wendox_w6726_remote_controller(&instance->generic); | ||||||
|  |                     if(instance->base.callback) | ||||||
|  |                         instance->base.callback(&instance->base, instance->base.context); | ||||||
|  |                 } | ||||||
|  | 
 | ||||||
|  |                 instance->decoder.parser_step = WendoxW6726DecoderStepReset; | ||||||
|  |             } else if( | ||||||
|  |                 (DURATION_DIFF(instance->decoder.te_last, ws_protocol_wendox_w6726_const.te_short) < | ||||||
|  |                  ws_protocol_wendox_w6726_const.te_delta) && | ||||||
|  |                 (DURATION_DIFF(duration, ws_protocol_wendox_w6726_const.te_long) < | ||||||
|  |                  ws_protocol_wendox_w6726_const.te_delta * 3)) { | ||||||
|  |                 subghz_protocol_blocks_add_bit(&instance->decoder, 0); | ||||||
|  |                 instance->decoder.parser_step = WendoxW6726DecoderStepSaveDuration; | ||||||
|  |             } else if( | ||||||
|  |                 (DURATION_DIFF(instance->decoder.te_last, ws_protocol_wendox_w6726_const.te_long) < | ||||||
|  |                  ws_protocol_wendox_w6726_const.te_delta * 2) && | ||||||
|  |                 (DURATION_DIFF(duration, ws_protocol_wendox_w6726_const.te_short) < | ||||||
|  |                  ws_protocol_wendox_w6726_const.te_delta)) { | ||||||
|  |                 subghz_protocol_blocks_add_bit(&instance->decoder, 1); | ||||||
|  |                 instance->decoder.parser_step = WendoxW6726DecoderStepSaveDuration; | ||||||
|  |             } else { | ||||||
|  |                 instance->decoder.parser_step = WendoxW6726DecoderStepReset; | ||||||
|  |             } | ||||||
|  |         } else { | ||||||
|  |             instance->decoder.parser_step = WendoxW6726DecoderStepReset; | ||||||
|  |         } | ||||||
|  |         break; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | uint8_t ws_protocol_decoder_wendox_w6726_get_hash_data(void* context) { | ||||||
|  |     furi_assert(context); | ||||||
|  |     WSProtocolDecoderWendoxW6726* instance = context; | ||||||
|  |     return subghz_protocol_blocks_get_hash_data( | ||||||
|  |         &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | SubGhzProtocolStatus ws_protocol_decoder_wendox_w6726_serialize( | ||||||
|  |     void* context, | ||||||
|  |     FlipperFormat* flipper_format, | ||||||
|  |     SubGhzRadioPreset* preset) { | ||||||
|  |     furi_assert(context); | ||||||
|  |     WSProtocolDecoderWendoxW6726* instance = context; | ||||||
|  |     return ws_block_generic_serialize(&instance->generic, flipper_format, preset); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | SubGhzProtocolStatus | ||||||
|  |     ws_protocol_decoder_wendox_w6726_deserialize(void* context, FlipperFormat* flipper_format) { | ||||||
|  |     furi_assert(context); | ||||||
|  |     WSProtocolDecoderWendoxW6726* instance = context; | ||||||
|  |     return ws_block_generic_deserialize_check_count_bit( | ||||||
|  |         &instance->generic, | ||||||
|  |         flipper_format, | ||||||
|  |         ws_protocol_wendox_w6726_const.min_count_bit_for_found); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void ws_protocol_decoder_wendox_w6726_get_string(void* context, FuriString* output) { | ||||||
|  |     furi_assert(context); | ||||||
|  |     WSProtocolDecoderWendoxW6726* 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:%3.1f 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, | ||||||
|  |         (double)instance->generic.temp, | ||||||
|  |         instance->generic.humidity); | ||||||
|  | } | ||||||
							
								
								
									
										80
									
								
								applications/external/weather_station/protocols/wendox_w6726.h
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										80
									
								
								applications/external/weather_station/protocols/wendox_w6726.h
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,80 @@ | |||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | #include <lib/subghz/protocols/base.h> | ||||||
|  | 
 | ||||||
|  | #include <lib/subghz/blocks/const.h> | ||||||
|  | #include <lib/subghz/blocks/decoder.h> | ||||||
|  | #include <lib/subghz/blocks/encoder.h> | ||||||
|  | #include "ws_generic.h" | ||||||
|  | #include <lib/subghz/blocks/math.h> | ||||||
|  | 
 | ||||||
|  | #define WS_PROTOCOL_WENDOX_W6726_NAME "Wendox W6726" | ||||||
|  | 
 | ||||||
|  | typedef struct WSProtocolDecoderWendoxW6726 WSProtocolDecoderWendoxW6726; | ||||||
|  | typedef struct WSProtocolEncoderWendoxW6726 WSProtocolEncoderWendoxW6726; | ||||||
|  | 
 | ||||||
|  | extern const SubGhzProtocolDecoder ws_protocol_wendox_w6726_decoder; | ||||||
|  | extern const SubGhzProtocolEncoder ws_protocol_wendox_w6726_encoder; | ||||||
|  | extern const SubGhzProtocol ws_protocol_wendox_w6726; | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Allocate WSProtocolDecoderWendoxW6726. | ||||||
|  |  * @param environment Pointer to a SubGhzEnvironment instance | ||||||
|  |  * @return WSProtocolDecoderWendoxW6726* pointer to a WSProtocolDecoderWendoxW6726 instance | ||||||
|  |  */ | ||||||
|  | void* ws_protocol_decoder_wendox_w6726_alloc(SubGhzEnvironment* environment); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Free WSProtocolDecoderWendoxW6726. | ||||||
|  |  * @param context Pointer to a WSProtocolDecoderWendoxW6726 instance | ||||||
|  |  */ | ||||||
|  | void ws_protocol_decoder_wendox_w6726_free(void* context); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Reset decoder WSProtocolDecoderWendoxW6726. | ||||||
|  |  * @param context Pointer to a WSProtocolDecoderWendoxW6726 instance | ||||||
|  |  */ | ||||||
|  | void ws_protocol_decoder_wendox_w6726_reset(void* context); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Parse a raw sequence of levels and durations received from the air. | ||||||
|  |  * @param context Pointer to a WSProtocolDecoderWendoxW6726 instance | ||||||
|  |  * @param level Signal level true-high false-low | ||||||
|  |  * @param duration Duration of this level in, us | ||||||
|  |  */ | ||||||
|  | void ws_protocol_decoder_wendox_w6726_feed(void* context, bool level, uint32_t duration); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Getting the hash sum of the last randomly received parcel. | ||||||
|  |  * @param context Pointer to a WSProtocolDecoderWendoxW6726 instance | ||||||
|  |  * @return hash Hash sum | ||||||
|  |  */ | ||||||
|  | uint8_t ws_protocol_decoder_wendox_w6726_get_hash_data(void* context); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Serialize data WSProtocolDecoderWendoxW6726. | ||||||
|  |  * @param context Pointer to a WSProtocolDecoderWendoxW6726 instance | ||||||
|  |  * @param flipper_format Pointer to a FlipperFormat instance | ||||||
|  |  * @param preset The modulation on which the signal was received, SubGhzRadioPreset | ||||||
|  |  * @return status | ||||||
|  |  */ | ||||||
|  | SubGhzProtocolStatus ws_protocol_decoder_wendox_w6726_serialize( | ||||||
|  |     void* context, | ||||||
|  |     FlipperFormat* flipper_format, | ||||||
|  |     SubGhzRadioPreset* preset); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Deserialize data WSProtocolDecoderWendoxW6726. | ||||||
|  |  * @param context Pointer to a WSProtocolDecoderWendoxW6726 instance | ||||||
|  |  * @param flipper_format Pointer to a FlipperFormat instance | ||||||
|  |  * @return status | ||||||
|  |  */ | ||||||
|  | SubGhzProtocolStatus | ||||||
|  |     ws_protocol_decoder_wendox_w6726_deserialize(void* context, FlipperFormat* flipper_format); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Getting a textual representation of the received data. | ||||||
|  |  * @param context Pointer to a WSProtocolDecoderWendoxW6726 instance | ||||||
|  |  * @param output Resulting text | ||||||
|  |  */ | ||||||
|  | void ws_protocol_decoder_wendox_w6726_get_string(void* context, FuriString* output); | ||||||
| @ -12,7 +12,7 @@ | |||||||
| #define MENU_ITEMS 4u | #define MENU_ITEMS 4u | ||||||
| #define UNLOCK_CNT 3 | #define UNLOCK_CNT 3 | ||||||
| 
 | 
 | ||||||
| #define SUBGHZ_RAW_TRESHOLD_MIN -90.0f | #define SUBGHZ_RAW_THRESHOLD_MIN -90.0f | ||||||
| typedef struct { | typedef struct { | ||||||
|     FuriString* item_str; |     FuriString* item_str; | ||||||
|     uint8_t type; |     uint8_t type; | ||||||
| @ -69,10 +69,10 @@ void ws_view_receiver_set_rssi(WSReceiver* instance, float rssi) { | |||||||
|         instance->view, |         instance->view, | ||||||
|         WSReceiverModel * model, |         WSReceiverModel * model, | ||||||
|         { |         { | ||||||
|             if(rssi < SUBGHZ_RAW_TRESHOLD_MIN) { |             if(rssi < SUBGHZ_RAW_THRESHOLD_MIN) { | ||||||
|                 model->u_rssi = 0; |                 model->u_rssi = 0; | ||||||
|             } else { |             } else { | ||||||
|                 model->u_rssi = (uint8_t)(rssi - SUBGHZ_RAW_TRESHOLD_MIN); |                 model->u_rssi = (uint8_t)(rssi - SUBGHZ_RAW_THRESHOLD_MIN); | ||||||
|             } |             } | ||||||
|         }, |         }, | ||||||
|         true); |         true); | ||||||
|  | |||||||
| @ -152,22 +152,22 @@ static int32_t ducky_fnc_waitforbutton(BadUsbScript* bad_usb, const char* line, | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static const DuckyCmd ducky_commands[] = { | static const DuckyCmd ducky_commands[] = { | ||||||
|     {"REM ", NULL, -1}, |     {"REM", NULL, -1}, | ||||||
|     {"ID ", NULL, -1}, |     {"ID", NULL, -1}, | ||||||
|     {"DELAY ", ducky_fnc_delay, -1}, |     {"DELAY", ducky_fnc_delay, -1}, | ||||||
|     {"STRING ", ducky_fnc_string, 0}, |     {"STRING", ducky_fnc_string, 0}, | ||||||
|     {"STRINGLN ", ducky_fnc_string, 1}, |     {"STRINGLN", ducky_fnc_string, 1}, | ||||||
|     {"DEFAULT_DELAY ", ducky_fnc_defdelay, -1}, |     {"DEFAULT_DELAY", ducky_fnc_defdelay, -1}, | ||||||
|     {"DEFAULTDELAY ", ducky_fnc_defdelay, -1}, |     {"DEFAULTDELAY", ducky_fnc_defdelay, -1}, | ||||||
|     {"STRINGDELAY ", ducky_fnc_strdelay, -1}, |     {"STRINGDELAY", ducky_fnc_strdelay, -1}, | ||||||
|     {"STRING_DELAY ", ducky_fnc_strdelay, -1}, |     {"STRING_DELAY", ducky_fnc_strdelay, -1}, | ||||||
|     {"REPEAT ", ducky_fnc_repeat, -1}, |     {"REPEAT", ducky_fnc_repeat, -1}, | ||||||
|     {"SYSRQ ", ducky_fnc_sysrq, -1}, |     {"SYSRQ", ducky_fnc_sysrq, -1}, | ||||||
|     {"ALTCHAR ", ducky_fnc_altchar, -1}, |     {"ALTCHAR", ducky_fnc_altchar, -1}, | ||||||
|     {"ALTSTRING ", ducky_fnc_altstring, -1}, |     {"ALTSTRING", ducky_fnc_altstring, -1}, | ||||||
|     {"ALTCODE ", ducky_fnc_altstring, -1}, |     {"ALTCODE", ducky_fnc_altstring, -1}, | ||||||
|     {"HOLD ", ducky_fnc_hold, -1}, |     {"HOLD", ducky_fnc_hold, -1}, | ||||||
|     {"RELEASE ", ducky_fnc_release, -1}, |     {"RELEASE", ducky_fnc_release, -1}, | ||||||
|     {"WAIT_FOR_BUTTON_PRESS", ducky_fnc_waitforbutton, -1}, |     {"WAIT_FOR_BUTTON_PRESS", ducky_fnc_waitforbutton, -1}, | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| @ -175,8 +175,15 @@ static const DuckyCmd ducky_commands[] = { | |||||||
| #define WORKER_TAG TAG "Worker" | #define WORKER_TAG TAG "Worker" | ||||||
| 
 | 
 | ||||||
| int32_t ducky_execute_cmd(BadUsbScript* bad_usb, const char* line) { | int32_t ducky_execute_cmd(BadUsbScript* bad_usb, const char* line) { | ||||||
|  |     size_t cmd_word_len = strcspn(line, " "); | ||||||
|     for(size_t i = 0; i < COUNT_OF(ducky_commands); i++) { |     for(size_t i = 0; i < COUNT_OF(ducky_commands); i++) { | ||||||
|         if(strncmp(line, ducky_commands[i].name, strlen(ducky_commands[i].name)) == 0) { |         size_t cmd_compare_len = strlen(ducky_commands[i].name); | ||||||
|  | 
 | ||||||
|  |         if(cmd_compare_len != cmd_word_len) { | ||||||
|  |             continue; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         if(strncmp(line, ducky_commands[i].name, cmd_compare_len) == 0) { | ||||||
|             if(ducky_commands[i].callback == NULL) { |             if(ducky_commands[i].callback == NULL) { | ||||||
|                 return 0; |                 return 0; | ||||||
|             } else { |             } else { | ||||||
|  | |||||||
| @ -18,7 +18,7 @@ void nfc_scene_mfkey_complete_on_enter(void* context) { | |||||||
|         AlignCenter, |         AlignCenter, | ||||||
|         AlignCenter, |         AlignCenter, | ||||||
|         FontSecondary, |         FontSecondary, | ||||||
|         "Now use mfkey32v2\nto extract keys"); |         "Now use Mfkey32\nto extract keys"); | ||||||
|     widget_add_button_element( |     widget_add_button_element( | ||||||
|         nfc->widget, GuiButtonTypeCenter, "OK", nfc_scene_mfkey_complete_callback, nfc); |         nfc->widget, GuiButtonTypeCenter, "OK", nfc_scene_mfkey_complete_callback, nfc); | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -6,14 +6,13 @@ typedef enum { | |||||||
|     SubGhzCustomEventManagerSetRAW, |     SubGhzCustomEventManagerSetRAW, | ||||||
| 
 | 
 | ||||||
|     //SubmenuIndex
 |     //SubmenuIndex
 | ||||||
|     SubmenuIndexPricenton, |     SubmenuIndexPricenton_433, | ||||||
|  |     SubmenuIndexPricenton_315, | ||||||
|     SubmenuIndexNiceFlo12bit, |     SubmenuIndexNiceFlo12bit, | ||||||
|     SubmenuIndexNiceFlo24bit, |     SubmenuIndexNiceFlo24bit, | ||||||
|     SubmenuIndexCAME12bit, |     SubmenuIndexCAME12bit, | ||||||
|     SubmenuIndexCAME24bit, |     SubmenuIndexCAME24bit, | ||||||
|     SubmenuIndexCAMETwee, |     SubmenuIndexCAMETwee, | ||||||
|     SubmenuIndexNeroSketch, |  | ||||||
|     SubmenuIndexNeroRadio, |  | ||||||
|     SubmenuIndexGateTX, |     SubmenuIndexGateTX, | ||||||
|     SubmenuIndexDoorHan_315_00, |     SubmenuIndexDoorHan_315_00, | ||||||
|     SubmenuIndexDoorHan_433_92, |     SubmenuIndexDoorHan_433_92, | ||||||
|  | |||||||
| @ -261,7 +261,7 @@ SubGhzFrequencyAnalyzerWorker* subghz_frequency_analyzer_worker_alloc(void* cont | |||||||
|     instance->thread = furi_thread_alloc_ex( |     instance->thread = furi_thread_alloc_ex( | ||||||
|         "SubGhzFAWorker", 2048, subghz_frequency_analyzer_worker_thread, instance); |         "SubGhzFAWorker", 2048, subghz_frequency_analyzer_worker_thread, instance); | ||||||
|     SubGhz* subghz = context; |     SubGhz* subghz = context; | ||||||
|     instance->setting = subghz->setting; |     instance->setting = subghz_txrx_get_setting(subghz->txrx); | ||||||
|     return instance; |     return instance; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | |||||||
							
								
								
									
										60
									
								
								applications/main/subghz/helpers/subghz_threshold_rssi.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										60
									
								
								applications/main/subghz/helpers/subghz_threshold_rssi.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,60 @@ | |||||||
|  | #include "subghz_threshold_rssi.h" | ||||||
|  | #include <float_tools.h> | ||||||
|  | #include "../subghz_i.h" | ||||||
|  | 
 | ||||||
|  | #define TAG "SubGhzThresholdRssi" | ||||||
|  | #define THRESHOLD_RSSI_LOW_COUNT 10 | ||||||
|  | 
 | ||||||
|  | struct SubGhzThresholdRssi { | ||||||
|  |     float threshold_rssi; | ||||||
|  |     uint8_t threshold_rssi_low_count; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | SubGhzThresholdRssi* subghz_threshold_rssi_alloc(void) { | ||||||
|  |     SubGhzThresholdRssi* instance = malloc(sizeof(SubGhzThresholdRssi)); | ||||||
|  |     instance->threshold_rssi = SUBGHZ_RAW_THRESHOLD_MIN; | ||||||
|  |     instance->threshold_rssi_low_count = THRESHOLD_RSSI_LOW_COUNT; | ||||||
|  |     return instance; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void subghz_threshold_rssi_free(SubGhzThresholdRssi* instance) { | ||||||
|  |     furi_assert(instance); | ||||||
|  |     free(instance); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void subghz_threshold_rssi_set(SubGhzThresholdRssi* instance, float rssi) { | ||||||
|  |     furi_assert(instance); | ||||||
|  |     instance->threshold_rssi = rssi; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | float subghz_threshold_rssi_get(SubGhzThresholdRssi* instance) { | ||||||
|  |     furi_assert(instance); | ||||||
|  |     return instance->threshold_rssi; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | SubGhzThresholdRssiData subghz_threshold_get_rssi_data(SubGhzThresholdRssi* instance) { | ||||||
|  |     furi_assert(instance); | ||||||
|  |     float rssi = furi_hal_subghz_get_rssi(); | ||||||
|  |     SubGhzThresholdRssiData ret = {.rssi = rssi, .is_above = false}; | ||||||
|  | 
 | ||||||
|  |     if(float_is_equal(instance->threshold_rssi, SUBGHZ_RAW_THRESHOLD_MIN)) { | ||||||
|  |         ret.is_above = true; | ||||||
|  |     } else { | ||||||
|  |         if(rssi < instance->threshold_rssi) { | ||||||
|  |             instance->threshold_rssi_low_count++; | ||||||
|  |             if(instance->threshold_rssi_low_count > THRESHOLD_RSSI_LOW_COUNT) { | ||||||
|  |                 instance->threshold_rssi_low_count = THRESHOLD_RSSI_LOW_COUNT; | ||||||
|  |             } | ||||||
|  |             ret.is_above = false; | ||||||
|  |         } else { | ||||||
|  |             instance->threshold_rssi_low_count = 0; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         if(instance->threshold_rssi_low_count == THRESHOLD_RSSI_LOW_COUNT) { | ||||||
|  |             ret.is_above = false; | ||||||
|  |         } else { | ||||||
|  |             ret.is_above = true; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     return ret; | ||||||
|  | } | ||||||
							
								
								
									
										43
									
								
								applications/main/subghz/helpers/subghz_threshold_rssi.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										43
									
								
								applications/main/subghz/helpers/subghz_threshold_rssi.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,43 @@ | |||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | #include <furi.h> | ||||||
|  | 
 | ||||||
|  | typedef struct { | ||||||
|  |     float rssi; /**< Current RSSI */ | ||||||
|  |     bool is_above; /**< Exceeded threshold level */ | ||||||
|  | } SubGhzThresholdRssiData; | ||||||
|  | 
 | ||||||
|  | typedef struct SubGhzThresholdRssi SubGhzThresholdRssi; | ||||||
|  | 
 | ||||||
|  | /** Allocate SubGhzThresholdRssi
 | ||||||
|  |  *  | ||||||
|  |  * @return SubGhzThresholdRssi*  | ||||||
|  |  */ | ||||||
|  | SubGhzThresholdRssi* subghz_threshold_rssi_alloc(void); | ||||||
|  | 
 | ||||||
|  | /** Free SubGhzThresholdRssi
 | ||||||
|  |  *  | ||||||
|  |  * @param instance Pointer to a SubGhzThresholdRssi | ||||||
|  |  */ | ||||||
|  | void subghz_threshold_rssi_free(SubGhzThresholdRssi* instance); | ||||||
|  | 
 | ||||||
|  | /** Set threshold
 | ||||||
|  |  *  | ||||||
|  |  * @param instance Pointer to a SubGhzThresholdRssi | ||||||
|  |  * @param rssi RSSI threshold | ||||||
|  |  */ | ||||||
|  | void subghz_threshold_rssi_set(SubGhzThresholdRssi* instance, float rssi); | ||||||
|  | 
 | ||||||
|  | /** Get threshold
 | ||||||
|  |  *  | ||||||
|  |  * @param instance Pointer to a SubGhzThresholdRssi | ||||||
|  |  * @return float RSSI threshold | ||||||
|  |  */ | ||||||
|  | float subghz_threshold_rssi_get(SubGhzThresholdRssi* instance); | ||||||
|  | 
 | ||||||
|  | /** Check threshold
 | ||||||
|  |  *  | ||||||
|  |  * @param instance Pointer to a SubGhzThresholdRssi | ||||||
|  |  * @return SubGhzThresholdRssiData  | ||||||
|  |  */ | ||||||
|  | SubGhzThresholdRssiData subghz_threshold_get_rssi_data(SubGhzThresholdRssi* instance); | ||||||
							
								
								
									
										521
									
								
								applications/main/subghz/helpers/subghz_txrx.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										521
									
								
								applications/main/subghz/helpers/subghz_txrx.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,521 @@ | |||||||
|  | #include "subghz_txrx_i.h" | ||||||
|  | 
 | ||||||
|  | #include <lib/subghz/protocols/protocol_items.h> | ||||||
|  | 
 | ||||||
|  | #define TAG "SubGhz" | ||||||
|  | 
 | ||||||
|  | SubGhzTxRx* subghz_txrx_alloc() { | ||||||
|  |     SubGhzTxRx* instance = malloc(sizeof(SubGhzTxRx)); | ||||||
|  |     instance->setting = subghz_setting_alloc(); | ||||||
|  |     subghz_setting_load(instance->setting, EXT_PATH("subghz/assets/setting_user")); | ||||||
|  | 
 | ||||||
|  |     instance->preset = malloc(sizeof(SubGhzRadioPreset)); | ||||||
|  |     instance->preset->name = furi_string_alloc(); | ||||||
|  |     subghz_txrx_set_preset( | ||||||
|  |         instance, "AM650", subghz_setting_get_default_frequency(instance->setting), NULL, 0); | ||||||
|  | 
 | ||||||
|  |     instance->txrx_state = SubGhzTxRxStateSleep; | ||||||
|  | 
 | ||||||
|  |     subghz_txrx_hopper_set_state(instance, SubGhzHopperStateOFF); | ||||||
|  |     subghz_txrx_speaker_set_state(instance, SubGhzSpeakerStateDisable); | ||||||
|  | 
 | ||||||
|  |     instance->worker = subghz_worker_alloc(); | ||||||
|  |     instance->fff_data = flipper_format_string_alloc(); | ||||||
|  | 
 | ||||||
|  |     instance->environment = subghz_environment_alloc(); | ||||||
|  |     instance->is_database_loaded = subghz_environment_load_keystore( | ||||||
|  |         instance->environment, EXT_PATH("subghz/assets/keeloq_mfcodes")); | ||||||
|  |     subghz_environment_load_keystore( | ||||||
|  |         instance->environment, EXT_PATH("subghz/assets/keeloq_mfcodes_user")); | ||||||
|  |     subghz_environment_set_came_atomo_rainbow_table_file_name( | ||||||
|  |         instance->environment, EXT_PATH("subghz/assets/came_atomo")); | ||||||
|  |     subghz_environment_set_alutech_at_4n_rainbow_table_file_name( | ||||||
|  |         instance->environment, EXT_PATH("subghz/assets/alutech_at_4n")); | ||||||
|  |     subghz_environment_set_nice_flor_s_rainbow_table_file_name( | ||||||
|  |         instance->environment, EXT_PATH("subghz/assets/nice_flor_s")); | ||||||
|  |     subghz_environment_set_protocol_registry( | ||||||
|  |         instance->environment, (void*)&subghz_protocol_registry); | ||||||
|  |     instance->receiver = subghz_receiver_alloc_init(instance->environment); | ||||||
|  | 
 | ||||||
|  |     subghz_worker_set_overrun_callback( | ||||||
|  |         instance->worker, (SubGhzWorkerOverrunCallback)subghz_receiver_reset); | ||||||
|  |     subghz_worker_set_pair_callback( | ||||||
|  |         instance->worker, (SubGhzWorkerPairCallback)subghz_receiver_decode); | ||||||
|  |     subghz_worker_set_context(instance->worker, instance->receiver); | ||||||
|  | 
 | ||||||
|  |     return instance; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void subghz_txrx_free(SubGhzTxRx* instance) { | ||||||
|  |     furi_assert(instance); | ||||||
|  | 
 | ||||||
|  |     subghz_worker_free(instance->worker); | ||||||
|  |     subghz_receiver_free(instance->receiver); | ||||||
|  |     subghz_environment_free(instance->environment); | ||||||
|  |     flipper_format_free(instance->fff_data); | ||||||
|  |     furi_string_free(instance->preset->name); | ||||||
|  |     subghz_setting_free(instance->setting); | ||||||
|  |     free(instance->preset); | ||||||
|  |     free(instance); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool subghz_txrx_is_database_loaded(SubGhzTxRx* instance) { | ||||||
|  |     furi_assert(instance); | ||||||
|  |     return instance->is_database_loaded; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void subghz_txrx_set_preset( | ||||||
|  |     SubGhzTxRx* instance, | ||||||
|  |     const char* preset_name, | ||||||
|  |     uint32_t frequency, | ||||||
|  |     uint8_t* preset_data, | ||||||
|  |     size_t preset_data_size) { | ||||||
|  |     furi_assert(instance); | ||||||
|  |     furi_string_set(instance->preset->name, preset_name); | ||||||
|  |     SubGhzRadioPreset* preset = instance->preset; | ||||||
|  |     preset->frequency = frequency; | ||||||
|  |     preset->data = preset_data; | ||||||
|  |     preset->data_size = preset_data_size; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | const char* subghz_txrx_get_preset_name(SubGhzTxRx* instance, const char* preset) { | ||||||
|  |     UNUSED(instance); | ||||||
|  |     const char* preset_name = ""; | ||||||
|  |     if(!strcmp(preset, "FuriHalSubGhzPresetOok270Async")) { | ||||||
|  |         preset_name = "AM270"; | ||||||
|  |     } else if(!strcmp(preset, "FuriHalSubGhzPresetOok650Async")) { | ||||||
|  |         preset_name = "AM650"; | ||||||
|  |     } else if(!strcmp(preset, "FuriHalSubGhzPreset2FSKDev238Async")) { | ||||||
|  |         preset_name = "FM238"; | ||||||
|  |     } else if(!strcmp(preset, "FuriHalSubGhzPreset2FSKDev476Async")) { | ||||||
|  |         preset_name = "FM476"; | ||||||
|  |     } else if(!strcmp(preset, "FuriHalSubGhzPresetCustom")) { | ||||||
|  |         preset_name = "CUSTOM"; | ||||||
|  |     } else { | ||||||
|  |         FURI_LOG_E(TAG, "Unknown preset"); | ||||||
|  |     } | ||||||
|  |     return preset_name; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | SubGhzRadioPreset subghz_txrx_get_preset(SubGhzTxRx* instance) { | ||||||
|  |     furi_assert(instance); | ||||||
|  |     return *instance->preset; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void subghz_txrx_get_frequency_and_modulation( | ||||||
|  |     SubGhzTxRx* instance, | ||||||
|  |     FuriString* frequency, | ||||||
|  |     FuriString* modulation) { | ||||||
|  |     furi_assert(instance); | ||||||
|  |     SubGhzRadioPreset* preset = instance->preset; | ||||||
|  |     if(frequency != NULL) { | ||||||
|  |         furi_string_printf( | ||||||
|  |             frequency, | ||||||
|  |             "%03ld.%02ld", | ||||||
|  |             preset->frequency / 1000000 % 1000, | ||||||
|  |             preset->frequency / 10000 % 100); | ||||||
|  |     } | ||||||
|  |     if(modulation != NULL) { | ||||||
|  |         furi_string_printf(modulation, "%.2s", furi_string_get_cstr(preset->name)); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void subghz_txrx_begin(SubGhzTxRx* instance, uint8_t* preset_data) { | ||||||
|  |     furi_assert(instance); | ||||||
|  |     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); | ||||||
|  |     instance->txrx_state = SubGhzTxRxStateIDLE; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static uint32_t subghz_txrx_rx(SubGhzTxRx* instance, uint32_t frequency) { | ||||||
|  |     furi_assert(instance); | ||||||
|  |     if(!furi_hal_subghz_is_frequency_valid(frequency)) { | ||||||
|  |         furi_crash("SubGhz: Incorrect RX frequency."); | ||||||
|  |     } | ||||||
|  |     furi_assert( | ||||||
|  |         instance->txrx_state != SubGhzTxRxStateRx && instance->txrx_state != SubGhzTxRxStateSleep); | ||||||
|  | 
 | ||||||
|  |     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(); | ||||||
|  |     subghz_txrx_speaker_on(instance); | ||||||
|  |     furi_hal_subghz_rx(); | ||||||
|  | 
 | ||||||
|  |     furi_hal_subghz_start_async_rx(subghz_worker_rx_callback, instance->worker); | ||||||
|  |     subghz_worker_start(instance->worker); | ||||||
|  |     instance->txrx_state = SubGhzTxRxStateRx; | ||||||
|  |     return value; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void subghz_txrx_idle(SubGhzTxRx* instance) { | ||||||
|  |     furi_assert(instance); | ||||||
|  |     furi_assert(instance->txrx_state != SubGhzTxRxStateSleep); | ||||||
|  |     furi_hal_subghz_idle(); | ||||||
|  |     subghz_txrx_speaker_off(instance); | ||||||
|  |     instance->txrx_state = SubGhzTxRxStateIDLE; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void subghz_txrx_rx_end(SubGhzTxRx* instance) { | ||||||
|  |     furi_assert(instance); | ||||||
|  |     furi_assert(instance->txrx_state == SubGhzTxRxStateRx); | ||||||
|  | 
 | ||||||
|  |     if(subghz_worker_is_running(instance->worker)) { | ||||||
|  |         subghz_worker_stop(instance->worker); | ||||||
|  |         furi_hal_subghz_stop_async_rx(); | ||||||
|  |     } | ||||||
|  |     furi_hal_subghz_idle(); | ||||||
|  |     subghz_txrx_speaker_off(instance); | ||||||
|  |     instance->txrx_state = SubGhzTxRxStateIDLE; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void subghz_txrx_sleep(SubGhzTxRx* instance) { | ||||||
|  |     furi_assert(instance); | ||||||
|  |     furi_hal_subghz_sleep(); | ||||||
|  |     instance->txrx_state = SubGhzTxRxStateSleep; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static bool subghz_txrx_tx(SubGhzTxRx* instance, uint32_t frequency) { | ||||||
|  |     furi_assert(instance); | ||||||
|  |     if(!furi_hal_subghz_is_frequency_valid(frequency)) { | ||||||
|  |         furi_crash("SubGhz: Incorrect TX frequency."); | ||||||
|  |     } | ||||||
|  |     furi_assert(instance->txrx_state != SubGhzTxRxStateSleep); | ||||||
|  |     furi_hal_subghz_idle(); | ||||||
|  |     furi_hal_subghz_set_frequency_and_path(frequency); | ||||||
|  |     furi_hal_gpio_write(&gpio_cc1101_g0, false); | ||||||
|  |     furi_hal_gpio_init(&gpio_cc1101_g0, GpioModeOutputPushPull, GpioPullNo, GpioSpeedLow); | ||||||
|  |     bool ret = furi_hal_subghz_tx(); | ||||||
|  |     if(ret) { | ||||||
|  |         subghz_txrx_speaker_on(instance); | ||||||
|  |         instance->txrx_state = SubGhzTxRxStateTx; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return ret; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | SubGhzTxRxStartTxState subghz_txrx_tx_start(SubGhzTxRx* instance, FlipperFormat* flipper_format) { | ||||||
|  |     furi_assert(instance); | ||||||
|  |     furi_assert(flipper_format); | ||||||
|  | 
 | ||||||
|  |     subghz_txrx_stop(instance); | ||||||
|  | 
 | ||||||
|  |     SubGhzTxRxStartTxState ret = SubGhzTxRxStartTxStateErrorParserOthers; | ||||||
|  |     FuriString* temp_str = furi_string_alloc(); | ||||||
|  |     uint32_t repeat = 200; | ||||||
|  |     do { | ||||||
|  |         if(!flipper_format_rewind(flipper_format)) { | ||||||
|  |             FURI_LOG_E(TAG, "Rewind error"); | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  |         if(!flipper_format_read_string(flipper_format, "Protocol", temp_str)) { | ||||||
|  |             FURI_LOG_E(TAG, "Missing Protocol"); | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  |         if(!flipper_format_insert_or_update_uint32(flipper_format, "Repeat", &repeat, 1)) { | ||||||
|  |             FURI_LOG_E(TAG, "Unable Repeat"); | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  |         ret = SubGhzTxRxStartTxStateOk; | ||||||
|  | 
 | ||||||
|  |         SubGhzRadioPreset* preset = instance->preset; | ||||||
|  |         instance->transmitter = | ||||||
|  |             subghz_transmitter_alloc_init(instance->environment, furi_string_get_cstr(temp_str)); | ||||||
|  | 
 | ||||||
|  |         if(instance->transmitter) { | ||||||
|  |             if(subghz_transmitter_deserialize(instance->transmitter, flipper_format) == | ||||||
|  |                SubGhzProtocolStatusOk) { | ||||||
|  |                 if(strcmp(furi_string_get_cstr(preset->name), "") != 0) { | ||||||
|  |                     subghz_txrx_begin( | ||||||
|  |                         instance, | ||||||
|  |                         subghz_setting_get_preset_data_by_name( | ||||||
|  |                             instance->setting, furi_string_get_cstr(preset->name))); | ||||||
|  |                     if(preset->frequency) { | ||||||
|  |                         if(!subghz_txrx_tx(instance, preset->frequency)) { | ||||||
|  |                             FURI_LOG_E(TAG, "Only Rx"); | ||||||
|  |                             ret = SubGhzTxRxStartTxStateErrorOnlyRx; | ||||||
|  |                         } | ||||||
|  |                     } else { | ||||||
|  |                         ret = SubGhzTxRxStartTxStateErrorParserOthers; | ||||||
|  |                     } | ||||||
|  | 
 | ||||||
|  |                 } else { | ||||||
|  |                     FURI_LOG_E( | ||||||
|  |                         TAG, "Unknown name preset \" %s \"", furi_string_get_cstr(preset->name)); | ||||||
|  |                     ret = SubGhzTxRxStartTxStateErrorParserOthers; | ||||||
|  |                 } | ||||||
|  | 
 | ||||||
|  |                 if(ret == SubGhzTxRxStartTxStateOk) { | ||||||
|  |                     //Start TX
 | ||||||
|  |                     furi_hal_subghz_start_async_tx( | ||||||
|  |                         subghz_transmitter_yield, instance->transmitter); | ||||||
|  |                 } | ||||||
|  |             } else { | ||||||
|  |                 ret = SubGhzTxRxStartTxStateErrorParserOthers; | ||||||
|  |             } | ||||||
|  |         } else { | ||||||
|  |             ret = SubGhzTxRxStartTxStateErrorParserOthers; | ||||||
|  |         } | ||||||
|  |         if(ret != SubGhzTxRxStartTxStateOk) { | ||||||
|  |             subghz_transmitter_free(instance->transmitter); | ||||||
|  |             if(instance->txrx_state != SubGhzTxRxStateIDLE) { | ||||||
|  |                 subghz_txrx_idle(instance); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |     } while(false); | ||||||
|  |     furi_string_free(temp_str); | ||||||
|  |     return ret; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void subghz_txrx_rx_start(SubGhzTxRx* instance) { | ||||||
|  |     furi_assert(instance); | ||||||
|  |     subghz_txrx_stop(instance); | ||||||
|  |     subghz_txrx_begin( | ||||||
|  |         instance, | ||||||
|  |         subghz_setting_get_preset_data_by_name( | ||||||
|  |             subghz_txrx_get_setting(instance), furi_string_get_cstr(instance->preset->name))); | ||||||
|  |     subghz_txrx_rx(instance, instance->preset->frequency); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void subghz_txrx_set_need_save_callback( | ||||||
|  |     SubGhzTxRx* instance, | ||||||
|  |     SubGhzTxRxNeedSaveCallback callback, | ||||||
|  |     void* context) { | ||||||
|  |     furi_assert(instance); | ||||||
|  |     instance->need_save_callback = callback; | ||||||
|  |     instance->need_save_context = context; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void subghz_txrx_tx_stop(SubGhzTxRx* instance) { | ||||||
|  |     furi_assert(instance); | ||||||
|  |     furi_assert(instance->txrx_state == SubGhzTxRxStateTx); | ||||||
|  |     //Stop TX
 | ||||||
|  |     furi_hal_subghz_stop_async_tx(); | ||||||
|  |     subghz_transmitter_stop(instance->transmitter); | ||||||
|  |     subghz_transmitter_free(instance->transmitter); | ||||||
|  | 
 | ||||||
|  |     //if protocol dynamic then we save the last upload
 | ||||||
|  |     if(instance->decoder_result->protocol->type == SubGhzProtocolTypeDynamic) { | ||||||
|  |         if(instance->need_save_callback) { | ||||||
|  |             instance->need_save_callback(instance->need_save_context); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     subghz_txrx_idle(instance); | ||||||
|  |     subghz_txrx_speaker_off(instance); | ||||||
|  |     //Todo: Show message
 | ||||||
|  |     // notification_message(notifications, &sequence_reset_red);
 | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | FlipperFormat* subghz_txrx_get_fff_data(SubGhzTxRx* instance) { | ||||||
|  |     furi_assert(instance); | ||||||
|  |     return instance->fff_data; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | SubGhzSetting* subghz_txrx_get_setting(SubGhzTxRx* instance) { | ||||||
|  |     furi_assert(instance); | ||||||
|  |     return instance->setting; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void subghz_txrx_stop(SubGhzTxRx* instance) { | ||||||
|  |     furi_assert(instance); | ||||||
|  | 
 | ||||||
|  |     switch(instance->txrx_state) { | ||||||
|  |     case SubGhzTxRxStateTx: | ||||||
|  |         subghz_txrx_tx_stop(instance); | ||||||
|  |         subghz_txrx_speaker_unmute(instance); | ||||||
|  |         break; | ||||||
|  |     case SubGhzTxRxStateRx: | ||||||
|  |         subghz_txrx_rx_end(instance); | ||||||
|  |         subghz_txrx_speaker_mute(instance); | ||||||
|  |         break; | ||||||
|  | 
 | ||||||
|  |     default: | ||||||
|  |         break; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void subghz_txrx_hopper_update(SubGhzTxRx* instance) { | ||||||
|  |     furi_assert(instance); | ||||||
|  | 
 | ||||||
|  |     switch(instance->hopper_state) { | ||||||
|  |     case SubGhzHopperStateOFF: | ||||||
|  |     case SubGhzHopperStatePause: | ||||||
|  |         return; | ||||||
|  |     case SubGhzHopperStateRSSITimeOut: | ||||||
|  |         if(instance->hopper_timeout != 0) { | ||||||
|  |             instance->hopper_timeout--; | ||||||
|  |             return; | ||||||
|  |         } | ||||||
|  |         break; | ||||||
|  |     default: | ||||||
|  |         break; | ||||||
|  |     } | ||||||
|  |     float rssi = -127.0f; | ||||||
|  |     if(instance->hopper_state != SubGhzHopperStateRSSITimeOut) { | ||||||
|  |         // 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) { | ||||||
|  |             instance->hopper_timeout = 10; | ||||||
|  |             instance->hopper_state = SubGhzHopperStateRSSITimeOut; | ||||||
|  |             return; | ||||||
|  |         } | ||||||
|  |     } else { | ||||||
|  |         instance->hopper_state = SubGhzHopperStateRunnig; | ||||||
|  |     } | ||||||
|  |     // Select next frequency
 | ||||||
|  |     if(instance->hopper_idx_frequency < | ||||||
|  |        subghz_setting_get_hopper_frequency_count(instance->setting) - 1) { | ||||||
|  |         instance->hopper_idx_frequency++; | ||||||
|  |     } else { | ||||||
|  |         instance->hopper_idx_frequency = 0; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if(instance->txrx_state == SubGhzTxRxStateRx) { | ||||||
|  |         subghz_txrx_rx_end(instance); | ||||||
|  |     }; | ||||||
|  |     if(instance->txrx_state == SubGhzTxRxStateIDLE) { | ||||||
|  |         subghz_receiver_reset(instance->receiver); | ||||||
|  |         instance->preset->frequency = | ||||||
|  |             subghz_setting_get_hopper_frequency(instance->setting, instance->hopper_idx_frequency); | ||||||
|  |         subghz_txrx_rx(instance, instance->preset->frequency); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | SubGhzHopperState subghz_txrx_hopper_get_state(SubGhzTxRx* instance) { | ||||||
|  |     furi_assert(instance); | ||||||
|  |     return instance->hopper_state; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void subghz_txrx_hopper_set_state(SubGhzTxRx* instance, SubGhzHopperState state) { | ||||||
|  |     furi_assert(instance); | ||||||
|  |     instance->hopper_state = state; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void subghz_txrx_hopper_unpause(SubGhzTxRx* instance) { | ||||||
|  |     furi_assert(instance); | ||||||
|  |     if(instance->hopper_state == SubGhzHopperStatePause) { | ||||||
|  |         instance->hopper_state = SubGhzHopperStateRunnig; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void subghz_txrx_hopper_pause(SubGhzTxRx* instance) { | ||||||
|  |     furi_assert(instance); | ||||||
|  |     if(instance->hopper_state == SubGhzHopperStateRunnig) { | ||||||
|  |         instance->hopper_state = SubGhzHopperStatePause; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void subghz_txrx_speaker_on(SubGhzTxRx* instance) { | ||||||
|  |     furi_assert(instance); | ||||||
|  |     if(instance->speaker_state == SubGhzSpeakerStateEnable) { | ||||||
|  |         if(furi_hal_speaker_acquire(30)) { | ||||||
|  |             furi_hal_subghz_set_async_mirror_pin(&gpio_speaker); | ||||||
|  |         } else { | ||||||
|  |             instance->speaker_state = SubGhzSpeakerStateDisable; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void subghz_txrx_speaker_off(SubGhzTxRx* instance) { | ||||||
|  |     furi_assert(instance); | ||||||
|  |     if(instance->speaker_state != SubGhzSpeakerStateDisable) { | ||||||
|  |         if(furi_hal_speaker_is_mine()) { | ||||||
|  |             furi_hal_subghz_set_async_mirror_pin(NULL); | ||||||
|  |             furi_hal_speaker_release(); | ||||||
|  |             if(instance->speaker_state == SubGhzSpeakerStateShutdown) | ||||||
|  |                 instance->speaker_state = SubGhzSpeakerStateDisable; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void subghz_txrx_speaker_mute(SubGhzTxRx* instance) { | ||||||
|  |     furi_assert(instance); | ||||||
|  |     if(instance->speaker_state == SubGhzSpeakerStateEnable) { | ||||||
|  |         if(furi_hal_speaker_is_mine()) { | ||||||
|  |             furi_hal_subghz_set_async_mirror_pin(NULL); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void subghz_txrx_speaker_unmute(SubGhzTxRx* instance) { | ||||||
|  |     furi_assert(instance); | ||||||
|  |     if(instance->speaker_state == SubGhzSpeakerStateEnable) { | ||||||
|  |         if(furi_hal_speaker_is_mine()) { | ||||||
|  |             furi_hal_subghz_set_async_mirror_pin(&gpio_speaker); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void subghz_txrx_speaker_set_state(SubGhzTxRx* instance, SubGhzSpeakerState state) { | ||||||
|  |     furi_assert(instance); | ||||||
|  |     instance->speaker_state = state; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | SubGhzSpeakerState subghz_txrx_speaker_get_state(SubGhzTxRx* instance) { | ||||||
|  |     furi_assert(instance); | ||||||
|  |     return instance->speaker_state; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool subghz_txrx_load_decoder_by_name_protocol(SubGhzTxRx* instance, const char* name_protocol) { | ||||||
|  |     furi_assert(instance); | ||||||
|  |     furi_assert(name_protocol); | ||||||
|  |     bool res = false; | ||||||
|  |     instance->decoder_result = | ||||||
|  |         subghz_receiver_search_decoder_base_by_name(instance->receiver, name_protocol); | ||||||
|  |     if(instance->decoder_result) { | ||||||
|  |         res = true; | ||||||
|  |     } | ||||||
|  |     return res; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | SubGhzProtocolDecoderBase* subghz_txrx_get_decoder(SubGhzTxRx* instance) { | ||||||
|  |     furi_assert(instance); | ||||||
|  |     return instance->decoder_result; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool subghz_txrx_protocol_is_serializable(SubGhzTxRx* instance) { | ||||||
|  |     furi_assert(instance); | ||||||
|  |     return ( | ||||||
|  |         (instance->decoder_result->protocol->flag & SubGhzProtocolFlag_Save) == | ||||||
|  |         SubGhzProtocolFlag_Save); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool subghz_txrx_protocol_is_transmittable(SubGhzTxRx* instance, bool check_type) { | ||||||
|  |     furi_assert(instance); | ||||||
|  |     const SubGhzProtocol* protocol = instance->decoder_result->protocol; | ||||||
|  |     if(check_type) { | ||||||
|  |         return ( | ||||||
|  |             ((protocol->flag & SubGhzProtocolFlag_Send) == SubGhzProtocolFlag_Send) && | ||||||
|  |             protocol->encoder->deserialize && protocol->type == SubGhzProtocolTypeStatic); | ||||||
|  |     } | ||||||
|  |     return ( | ||||||
|  |         ((protocol->flag & SubGhzProtocolFlag_Send) == SubGhzProtocolFlag_Send) && | ||||||
|  |         protocol->encoder->deserialize); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void subghz_txrx_receiver_set_filter(SubGhzTxRx* instance, SubGhzProtocolFlag filter) { | ||||||
|  |     furi_assert(instance); | ||||||
|  |     subghz_receiver_set_filter(instance->receiver, filter); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void subghz_txrx_set_rx_calback( | ||||||
|  |     SubGhzTxRx* instance, | ||||||
|  |     SubGhzReceiverCallback callback, | ||||||
|  |     void* context) { | ||||||
|  |     subghz_receiver_set_rx_callback(instance->receiver, callback, context); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void subghz_txrx_set_raw_file_encoder_worker_callback_end( | ||||||
|  |     SubGhzTxRx* instance, | ||||||
|  |     SubGhzProtocolEncoderRAWCallbackEnd callback, | ||||||
|  |     void* context) { | ||||||
|  |     subghz_protocol_raw_file_encoder_worker_set_callback_end( | ||||||
|  |         (SubGhzProtocolEncoderRAW*)subghz_transmitter_get_protocol_instance(instance->transmitter), | ||||||
|  |         callback, | ||||||
|  |         context); | ||||||
|  | } | ||||||
							
								
								
									
										290
									
								
								applications/main/subghz/helpers/subghz_txrx.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										290
									
								
								applications/main/subghz/helpers/subghz_txrx.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,290 @@ | |||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | #include "subghz_types.h" | ||||||
|  | 
 | ||||||
|  | #include <lib/subghz/subghz_worker.h> | ||||||
|  | #include <lib/subghz/subghz_setting.h> | ||||||
|  | #include <lib/subghz/receiver.h> | ||||||
|  | #include <lib/subghz/transmitter.h> | ||||||
|  | #include <lib/subghz/protocols/raw.h> | ||||||
|  | 
 | ||||||
|  | typedef struct SubGhzTxRx SubGhzTxRx; | ||||||
|  | 
 | ||||||
|  | typedef void (*SubGhzTxRxNeedSaveCallback)(void* context); | ||||||
|  | 
 | ||||||
|  | typedef enum { | ||||||
|  |     SubGhzTxRxStartTxStateOk, | ||||||
|  |     SubGhzTxRxStartTxStateErrorOnlyRx, | ||||||
|  |     SubGhzTxRxStartTxStateErrorParserOthers, | ||||||
|  | } SubGhzTxRxStartTxState; | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Allocate SubGhzTxRx | ||||||
|  |  *  | ||||||
|  |  * @return SubGhzTxRx* pointer to SubGhzTxRx | ||||||
|  |  */ | ||||||
|  | SubGhzTxRx* subghz_txrx_alloc(); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Free SubGhzTxRx | ||||||
|  |  *  | ||||||
|  |  * @param instance Pointer to a SubGhzTxRx | ||||||
|  |  */ | ||||||
|  | void subghz_txrx_free(SubGhzTxRx* instance); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Check if the database is loaded | ||||||
|  |  *  | ||||||
|  |  * @param instance Pointer to a SubGhzTxRx | ||||||
|  |  * @return bool True if the database is loaded | ||||||
|  |  */ | ||||||
|  | bool subghz_txrx_is_database_loaded(SubGhzTxRx* instance); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Set preset  | ||||||
|  |  *  | ||||||
|  |  * @param instance Pointer to a SubGhzTxRx | ||||||
|  |  * @param preset_name Name of preset | ||||||
|  |  * @param frequency Frequency in Hz | ||||||
|  |  * @param preset_data Data of preset | ||||||
|  |  * @param preset_data_size Size of preset data | ||||||
|  |  */ | ||||||
|  | void subghz_txrx_set_preset( | ||||||
|  |     SubGhzTxRx* instance, | ||||||
|  |     const char* preset_name, | ||||||
|  |     uint32_t frequency, | ||||||
|  |     uint8_t* preset_data, | ||||||
|  |     size_t preset_data_size); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Get name of preset | ||||||
|  |  *  | ||||||
|  |  * @param instance Pointer to a SubGhzTxRx | ||||||
|  |  * @param preset String of preset  | ||||||
|  |  * @return const char*  Name of preset | ||||||
|  |  */ | ||||||
|  | const char* subghz_txrx_get_preset_name(SubGhzTxRx* instance, const char* preset); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Get of preset | ||||||
|  |  *  | ||||||
|  |  * @param instance Pointer to a SubGhzTxRx | ||||||
|  |  * @return SubGhzRadioPreset Preset | ||||||
|  |  */ | ||||||
|  | SubGhzRadioPreset subghz_txrx_get_preset(SubGhzTxRx* instance); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Get string frequency and modulation | ||||||
|  |  *  | ||||||
|  |  * @param instance Pointer to a SubGhzTxRx | ||||||
|  |  * @param frequency Pointer to a string frequency | ||||||
|  |  * @param modulation Pointer to a string modulation | ||||||
|  |  */ | ||||||
|  | void subghz_txrx_get_frequency_and_modulation( | ||||||
|  |     SubGhzTxRx* instance, | ||||||
|  |     FuriString* frequency, | ||||||
|  |     FuriString* modulation); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Start TX CC1101 | ||||||
|  |  *  | ||||||
|  |  * @param instance Pointer to a SubGhzTxRx | ||||||
|  |  * @param flipper_format Pointer to a FlipperFormat | ||||||
|  |  * @return SubGhzTxRxStartTxState  | ||||||
|  |  */ | ||||||
|  | SubGhzTxRxStartTxState subghz_txrx_tx_start(SubGhzTxRx* instance, FlipperFormat* flipper_format); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Start RX CC1101 | ||||||
|  |  *  | ||||||
|  |  * @param instance Pointer to a SubGhzTxRx | ||||||
|  |  */ | ||||||
|  | void subghz_txrx_rx_start(SubGhzTxRx* instance); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Stop TX/RX CC1101 | ||||||
|  |  *  | ||||||
|  |  * @param instance Pointer to a SubGhzTxRx | ||||||
|  |  */ | ||||||
|  | void subghz_txrx_stop(SubGhzTxRx* instance); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Set sleep mode CC1101 | ||||||
|  |  *  | ||||||
|  |  * @param instance Pointer to a SubGhzTxRx | ||||||
|  |  */ | ||||||
|  | void subghz_txrx_sleep(SubGhzTxRx* instance); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Update frequency CC1101 in automatic mode (hopper) | ||||||
|  |  *  | ||||||
|  |  * @param instance Pointer to a SubGhzTxRx | ||||||
|  |  */ | ||||||
|  | void subghz_txrx_hopper_update(SubGhzTxRx* instance); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Get state hopper | ||||||
|  |  *  | ||||||
|  |  * @param instance Pointer to a SubGhzTxRx | ||||||
|  |  * @return SubGhzHopperState  | ||||||
|  |  */ | ||||||
|  | SubGhzHopperState subghz_txrx_hopper_get_state(SubGhzTxRx* instance); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Set state hopper | ||||||
|  |  *  | ||||||
|  |  * @param instance Pointer to a SubGhzTxRx | ||||||
|  |  * @param state State hopper | ||||||
|  |  */ | ||||||
|  | void subghz_txrx_hopper_set_state(SubGhzTxRx* instance, SubGhzHopperState state); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Unpause hopper | ||||||
|  |  *  | ||||||
|  |  * @param instance Pointer to a SubGhzTxRx | ||||||
|  |  */ | ||||||
|  | void subghz_txrx_hopper_unpause(SubGhzTxRx* instance); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Set pause hopper | ||||||
|  |  *  | ||||||
|  |  * @param instance Pointer to a SubGhzTxRx | ||||||
|  |  */ | ||||||
|  | void subghz_txrx_hopper_pause(SubGhzTxRx* instance); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Speaker on | ||||||
|  |  *  | ||||||
|  |  * @param instance Pointer to a SubGhzTxRx  | ||||||
|  |  */ | ||||||
|  | void subghz_txrx_speaker_on(SubGhzTxRx* instance); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Speaker off | ||||||
|  |  *  | ||||||
|  |  * @param instance Pointer to a SubGhzTxRx  | ||||||
|  |  */ | ||||||
|  | void subghz_txrx_speaker_off(SubGhzTxRx* instance); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Speaker mute | ||||||
|  |  *  | ||||||
|  |  * @param instance Pointer to a SubGhzTxRx  | ||||||
|  |  */ | ||||||
|  | void subghz_txrx_speaker_mute(SubGhzTxRx* instance); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Speaker unmute | ||||||
|  |  *  | ||||||
|  |  * @param instance Pointer to a SubGhzTxRx  | ||||||
|  |  */ | ||||||
|  | void subghz_txrx_speaker_unmute(SubGhzTxRx* instance); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Set state speaker | ||||||
|  |  *  | ||||||
|  |  * @param instance Pointer to a SubGhzTxRx  | ||||||
|  |  * @param state State speaker | ||||||
|  |  */ | ||||||
|  | void subghz_txrx_speaker_set_state(SubGhzTxRx* instance, SubGhzSpeakerState state); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Get state speaker | ||||||
|  |  *  | ||||||
|  |  * @param instance Pointer to a SubGhzTxRx  | ||||||
|  |  * @return SubGhzSpeakerState  | ||||||
|  |  */ | ||||||
|  | SubGhzSpeakerState subghz_txrx_speaker_get_state(SubGhzTxRx* instance); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * load decoder by name protocol | ||||||
|  |  *  | ||||||
|  |  * @param instance Pointer to a SubGhzTxRx | ||||||
|  |  * @param name_protocol Name protocol | ||||||
|  |  * @return bool True if the decoder is loaded  | ||||||
|  |  */ | ||||||
|  | bool subghz_txrx_load_decoder_by_name_protocol(SubGhzTxRx* instance, const char* name_protocol); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Get decoder | ||||||
|  |  *  | ||||||
|  |  * @param instance Pointer to a SubGhzTxRx | ||||||
|  |  * @return SubGhzProtocolDecoderBase* Pointer to a SubGhzProtocolDecoderBase | ||||||
|  |  */ | ||||||
|  | SubGhzProtocolDecoderBase* subghz_txrx_get_decoder(SubGhzTxRx* instance); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Set callback for save data | ||||||
|  |  *  | ||||||
|  |  * @param instance Pointer to a SubGhzTxRx | ||||||
|  |  * @param callback Callback for save data | ||||||
|  |  * @param context Context for callback | ||||||
|  |  */ | ||||||
|  | void subghz_txrx_set_need_save_callback( | ||||||
|  |     SubGhzTxRx* instance, | ||||||
|  |     SubGhzTxRxNeedSaveCallback callback, | ||||||
|  |     void* context); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Get pointer to a load data key | ||||||
|  |  *  | ||||||
|  |  * @param instance Pointer to a SubGhzTxRx | ||||||
|  |  * @return FlipperFormat*  | ||||||
|  |  */ | ||||||
|  | FlipperFormat* subghz_txrx_get_fff_data(SubGhzTxRx* instance); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Get pointer to a SugGhzSetting | ||||||
|  |  *  | ||||||
|  |  * @param instance Pointer to a SubGhzTxRx | ||||||
|  |  * @return SubGhzSetting*  | ||||||
|  |  */ | ||||||
|  | SubGhzSetting* subghz_txrx_get_setting(SubGhzTxRx* instance); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Is it possible to save this protocol | ||||||
|  |  *  | ||||||
|  |  * @param instance Pointer to a SubGhzTxRx  | ||||||
|  |  * @return bool True if it is possible to save this protocol | ||||||
|  |  */ | ||||||
|  | bool subghz_txrx_protocol_is_serializable(SubGhzTxRx* instance); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Is it possible to send this protocol | ||||||
|  |  *  | ||||||
|  |  * @param instance Pointer to a SubGhzTxRx  | ||||||
|  |  * @return bool True if it is possible to send this protocol | ||||||
|  |  */ | ||||||
|  | bool subghz_txrx_protocol_is_transmittable(SubGhzTxRx* instance, bool check_type); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Set filter, what types of decoder to use  | ||||||
|  |  *  | ||||||
|  |  * @param instance Pointer to a SubGhzTxRx | ||||||
|  |  * @param filter Filter | ||||||
|  |  */ | ||||||
|  | void subghz_txrx_receiver_set_filter(SubGhzTxRx* instance, SubGhzProtocolFlag filter); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Set callback for receive data | ||||||
|  |  *  | ||||||
|  |  * @param instance Pointer to a SubGhzTxRx | ||||||
|  |  * @param callback Callback for receive data | ||||||
|  |  * @param context Context for callback | ||||||
|  |  */ | ||||||
|  | void subghz_txrx_set_rx_calback( | ||||||
|  |     SubGhzTxRx* instance, | ||||||
|  |     SubGhzReceiverCallback callback, | ||||||
|  |     void* context); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Set callback for Raw decoder, end of data transfer   | ||||||
|  |  *  | ||||||
|  |  * @param instance Pointer to a SubGhzTxRx | ||||||
|  |  * @param callback Callback for Raw decoder, end of data transfer  | ||||||
|  |  * @param context Context for callback | ||||||
|  |  */ | ||||||
|  | void subghz_txrx_set_raw_file_encoder_worker_callback_end( | ||||||
|  |     SubGhzTxRx* instance, | ||||||
|  |     SubGhzProtocolEncoderRAWCallbackEnd callback, | ||||||
|  |     void* context); | ||||||
| @ -0,0 +1,164 @@ | |||||||
|  | #include "subghz_txrx_i.h" | ||||||
|  | #include "subghz_txrx_create_protocol_key.h" | ||||||
|  | #include <lib/subghz/transmitter.h> | ||||||
|  | #include <lib/subghz/protocols/protocol_items.h> | ||||||
|  | #include <lib/subghz/protocols/protocol_items.h> | ||||||
|  | #include <lib/subghz/protocols/keeloq.h> | ||||||
|  | #include <lib/subghz/protocols/secplus_v1.h> | ||||||
|  | #include <lib/subghz/protocols/secplus_v2.h> | ||||||
|  | 
 | ||||||
|  | #include <flipper_format/flipper_format_i.h> | ||||||
|  | #include <lib/toolbox/stream/stream.h> | ||||||
|  | #include <lib/subghz/protocols/raw.h> | ||||||
|  | 
 | ||||||
|  | #define TAG "SubGhzCreateProtocolKey" | ||||||
|  | 
 | ||||||
|  | bool subghz_txrx_gen_data_protocol( | ||||||
|  |     void* context, | ||||||
|  |     const char* preset_name, | ||||||
|  |     uint32_t frequency, | ||||||
|  |     const char* protocol_name, | ||||||
|  |     uint64_t key, | ||||||
|  |     uint32_t bit) { | ||||||
|  |     furi_assert(context); | ||||||
|  |     SubGhzTxRx* instance = context; | ||||||
|  | 
 | ||||||
|  |     bool res = false; | ||||||
|  | 
 | ||||||
|  |     subghz_txrx_set_preset(instance, preset_name, frequency, NULL, 0); | ||||||
|  |     instance->decoder_result = | ||||||
|  |         subghz_receiver_search_decoder_base_by_name(instance->receiver, protocol_name); | ||||||
|  | 
 | ||||||
|  |     if(instance->decoder_result == NULL) { | ||||||
|  |         //TODO: Error
 | ||||||
|  |         // furi_string_set(error_str, "Protocol not\nfound!");
 | ||||||
|  |         // scene_manager_next_scene(scene_manager, SubGhzSceneShowErrorSub);
 | ||||||
|  |         FURI_LOG_E(TAG, "Protocol not found!"); | ||||||
|  |         return false; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     do { | ||||||
|  |         Stream* fff_data_stream = flipper_format_get_raw_stream(instance->fff_data); | ||||||
|  |         stream_clean(fff_data_stream); | ||||||
|  |         if(subghz_protocol_decoder_base_serialize( | ||||||
|  |                instance->decoder_result, instance->fff_data, instance->preset) != | ||||||
|  |            SubGhzProtocolStatusOk) { | ||||||
|  |             FURI_LOG_E(TAG, "Unable to serialize"); | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  |         if(!flipper_format_update_uint32(instance->fff_data, "Bit", &bit, 1)) { | ||||||
|  |             FURI_LOG_E(TAG, "Unable to update 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] = (key >> (i * 8)) & 0xFF; | ||||||
|  |         } | ||||||
|  |         if(!flipper_format_update_hex(instance->fff_data, "Key", key_data, sizeof(uint64_t))) { | ||||||
|  |             FURI_LOG_E(TAG, "Unable to update Key"); | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  |         res = true; | ||||||
|  |     } while(false); | ||||||
|  |     return res; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool subghz_txrx_gen_data_protocol_and_te( | ||||||
|  |     SubGhzTxRx* instance, | ||||||
|  |     const char* preset_name, | ||||||
|  |     uint32_t frequency, | ||||||
|  |     const char* protocol_name, | ||||||
|  |     uint64_t key, | ||||||
|  |     uint32_t bit, | ||||||
|  |     uint32_t te) { | ||||||
|  |     furi_assert(instance); | ||||||
|  |     bool ret = false; | ||||||
|  |     if(subghz_txrx_gen_data_protocol(instance, preset_name, frequency, protocol_name, key, bit)) { | ||||||
|  |         if(!flipper_format_update_uint32(instance->fff_data, "TE", (uint32_t*)&te, 1)) { | ||||||
|  |             FURI_LOG_E(TAG, "Unable to update Te"); | ||||||
|  |         } else { | ||||||
|  |             ret = true; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     return ret; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool subghz_txrx_gen_keeloq_protocol( | ||||||
|  |     SubGhzTxRx* instance, | ||||||
|  |     const char* name_preset, | ||||||
|  |     uint32_t frequency, | ||||||
|  |     const char* name_sysmem, | ||||||
|  |     uint32_t serial, | ||||||
|  |     uint8_t btn, | ||||||
|  |     uint16_t cnt) { | ||||||
|  |     furi_assert(instance); | ||||||
|  | 
 | ||||||
|  |     bool ret = false; | ||||||
|  |     serial &= 0x0FFFFFFF; | ||||||
|  |     instance->transmitter = | ||||||
|  |         subghz_transmitter_alloc_init(instance->environment, SUBGHZ_PROTOCOL_KEELOQ_NAME); | ||||||
|  |     subghz_txrx_set_preset(instance, name_preset, frequency, NULL, 0); | ||||||
|  |     if(instance->transmitter) { | ||||||
|  |         subghz_protocol_keeloq_create_data( | ||||||
|  |             subghz_transmitter_get_protocol_instance(instance->transmitter), | ||||||
|  |             instance->fff_data, | ||||||
|  |             serial, | ||||||
|  |             btn, | ||||||
|  |             cnt, | ||||||
|  |             name_sysmem, | ||||||
|  |             instance->preset); | ||||||
|  |         ret = true; | ||||||
|  |     } | ||||||
|  |     subghz_transmitter_free(instance->transmitter); | ||||||
|  |     return ret; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool subghz_txrx_gen_secplus_v2_protocol( | ||||||
|  |     SubGhzTxRx* instance, | ||||||
|  |     const char* name_preset, | ||||||
|  |     uint32_t frequency, | ||||||
|  |     uint32_t serial, | ||||||
|  |     uint8_t btn, | ||||||
|  |     uint32_t cnt) { | ||||||
|  |     furi_assert(instance); | ||||||
|  | 
 | ||||||
|  |     bool ret = false; | ||||||
|  |     instance->transmitter = | ||||||
|  |         subghz_transmitter_alloc_init(instance->environment, SUBGHZ_PROTOCOL_SECPLUS_V2_NAME); | ||||||
|  |     subghz_txrx_set_preset(instance, name_preset, frequency, NULL, 0); | ||||||
|  |     if(instance->transmitter) { | ||||||
|  |         subghz_protocol_secplus_v2_create_data( | ||||||
|  |             subghz_transmitter_get_protocol_instance(instance->transmitter), | ||||||
|  |             instance->fff_data, | ||||||
|  |             serial, | ||||||
|  |             btn, | ||||||
|  |             cnt, | ||||||
|  |             instance->preset); | ||||||
|  |         ret = true; | ||||||
|  |     } | ||||||
|  |     return ret; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool subghz_txrx_gen_secplus_v1_protocol( | ||||||
|  |     SubGhzTxRx* instance, | ||||||
|  |     const char* name_preset, | ||||||
|  |     uint32_t frequency) { | ||||||
|  |     furi_assert(instance); | ||||||
|  | 
 | ||||||
|  |     bool ret = false; | ||||||
|  |     uint32_t serial = (uint32_t)rand(); | ||||||
|  |     while(!subghz_protocol_secplus_v1_check_fixed(serial)) { | ||||||
|  |         serial = (uint32_t)rand(); | ||||||
|  |     } | ||||||
|  |     if(subghz_txrx_gen_data_protocol( | ||||||
|  |            instance, | ||||||
|  |            name_preset, | ||||||
|  |            frequency, | ||||||
|  |            SUBGHZ_PROTOCOL_SECPLUS_V1_NAME, | ||||||
|  |            (uint64_t)serial << 32 | 0xE6000000, | ||||||
|  |            42)) { | ||||||
|  |         ret = true; | ||||||
|  |     } | ||||||
|  |     return ret; | ||||||
|  | } | ||||||
| @ -0,0 +1,96 @@ | |||||||
|  | #pragma once | ||||||
|  | #include "subghz_types.h" | ||||||
|  | #include "subghz_txrx.h" | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Generate data for protocol | ||||||
|  |  *  | ||||||
|  |  * @param instance Pointer to a SubGhzTxRx | ||||||
|  |  * @param preset_name Name of preset | ||||||
|  |  * @param frequency Frequency in Hz | ||||||
|  |  * @param protocol_name Name of protocol | ||||||
|  |  * @param key Key | ||||||
|  |  * @param bit Bit | ||||||
|  |  * @return bool True if success | ||||||
|  |  */ | ||||||
|  | bool subghz_txrx_gen_data_protocol( | ||||||
|  |     void* context, | ||||||
|  |     const char* preset_name, | ||||||
|  |     uint32_t frequency, | ||||||
|  |     const char* protocol_name, | ||||||
|  |     uint64_t key, | ||||||
|  |     uint32_t bit); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Generate data for protocol and te | ||||||
|  |  *  | ||||||
|  |  * @param instance Pointer to a SubGhzTxRx | ||||||
|  |  * @param preset_name Name of preset | ||||||
|  |  * @param frequency Frequency in Hz | ||||||
|  |  * @param protocol_name Name of protocol | ||||||
|  |  * @param key Key | ||||||
|  |  * @param bit Bit | ||||||
|  |  * @param te Te | ||||||
|  |  * @return bool True if success | ||||||
|  |  */ | ||||||
|  | bool subghz_txrx_gen_data_protocol_and_te( | ||||||
|  |     SubGhzTxRx* instance, | ||||||
|  |     const char* preset_name, | ||||||
|  |     uint32_t frequency, | ||||||
|  |     const char* protocol_name, | ||||||
|  |     uint64_t key, | ||||||
|  |     uint32_t bit, | ||||||
|  |     uint32_t te); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Generate data Keeloq protocol | ||||||
|  |  *  | ||||||
|  |  * @param instance Pointer to a SubGhzTxRx | ||||||
|  |  * @param name_preset Name of preset | ||||||
|  |  * @param frequency Frequency in Hz | ||||||
|  |  * @param name_sysmem Name of Keeloq sysmem | ||||||
|  |  * @param serial Serial number | ||||||
|  |  * @param btn Button | ||||||
|  |  * @param cnt Counter | ||||||
|  |  * @return bool True if success | ||||||
|  |  */ | ||||||
|  | bool subghz_txrx_gen_keeloq_protocol( | ||||||
|  |     SubGhzTxRx* instance, | ||||||
|  |     const char* name_preset, | ||||||
|  |     uint32_t frequency, | ||||||
|  |     const char* name_sysmem, | ||||||
|  |     uint32_t serial, | ||||||
|  |     uint8_t btn, | ||||||
|  |     uint16_t cnt); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Generate data SecPlus v2 protocol | ||||||
|  |  *  | ||||||
|  |  * @param instance Pointer to a SubGhzTxRx | ||||||
|  |  * @param name_preset Name of preset | ||||||
|  |  * @param frequency Frequency in Hz | ||||||
|  |  * @param serial Serial number | ||||||
|  |  * @param btn Button | ||||||
|  |  * @param cnt Counter | ||||||
|  |  * @return bool True if success | ||||||
|  |  */ | ||||||
|  | bool subghz_txrx_gen_secplus_v2_protocol( | ||||||
|  |     SubGhzTxRx* instance, | ||||||
|  |     const char* name_preset, | ||||||
|  |     uint32_t frequency, | ||||||
|  |     uint32_t serial, | ||||||
|  |     uint8_t btn, | ||||||
|  |     uint32_t cnt); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Generate data SecPlus v1 protocol | ||||||
|  |  *  | ||||||
|  |  * @param instance Pointer to a SubGhzTxRx | ||||||
|  |  * @param name_preset Name of preset | ||||||
|  |  * @param frequency Frequency in Hz | ||||||
|  |  * @return bool True if success | ||||||
|  |  */ | ||||||
|  | bool subghz_txrx_gen_secplus_v1_protocol( | ||||||
|  |     SubGhzTxRx* instance, | ||||||
|  |     const char* name_preset, | ||||||
|  |     uint32_t frequency); | ||||||
							
								
								
									
										27
									
								
								applications/main/subghz/helpers/subghz_txrx_i.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								applications/main/subghz/helpers/subghz_txrx_i.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,27 @@ | |||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | #include "subghz_txrx.h" | ||||||
|  | 
 | ||||||
|  | struct SubGhzTxRx { | ||||||
|  |     SubGhzWorker* worker; | ||||||
|  | 
 | ||||||
|  |     SubGhzEnvironment* environment; | ||||||
|  |     SubGhzReceiver* receiver; | ||||||
|  |     SubGhzTransmitter* transmitter; | ||||||
|  |     SubGhzProtocolDecoderBase* decoder_result; | ||||||
|  |     FlipperFormat* fff_data; | ||||||
|  | 
 | ||||||
|  |     SubGhzRadioPreset* preset; | ||||||
|  |     SubGhzSetting* setting; | ||||||
|  | 
 | ||||||
|  |     uint8_t hopper_timeout; | ||||||
|  |     uint8_t hopper_idx_frequency; | ||||||
|  |     bool is_database_loaded; | ||||||
|  |     SubGhzHopperState hopper_state; | ||||||
|  | 
 | ||||||
|  |     SubGhzTxRxState txrx_state; | ||||||
|  |     SubGhzSpeakerState speaker_state; | ||||||
|  | 
 | ||||||
|  |     SubGhzTxRxNeedSaveCallback need_save_callback; | ||||||
|  |     void* need_save_context; | ||||||
|  | }; | ||||||
| @ -77,3 +77,10 @@ typedef enum { | |||||||
|     SubGhzViewIdTestCarrier, |     SubGhzViewIdTestCarrier, | ||||||
|     SubGhzViewIdTestPacket, |     SubGhzViewIdTestPacket, | ||||||
| } SubGhzViewId; | } SubGhzViewId; | ||||||
|  | 
 | ||||||
|  | /** SubGhz load type file */ | ||||||
|  | typedef enum { | ||||||
|  |     SubGhzLoadTypeFileNoLoad, | ||||||
|  |     SubGhzLoadTypeFileKey, | ||||||
|  |     SubGhzLoadTypeFileRaw, | ||||||
|  | } SubGhzLoadTypeFile; | ||||||
|  | |||||||
| @ -19,7 +19,7 @@ void subghz_scene_delete_on_enter(void* context) { | |||||||
|     modulation_str = furi_string_alloc(); |     modulation_str = furi_string_alloc(); | ||||||
|     text = furi_string_alloc(); |     text = furi_string_alloc(); | ||||||
| 
 | 
 | ||||||
|     subghz_get_frequency_modulation(subghz, frequency_str, modulation_str); |     subghz_txrx_get_frequency_and_modulation(subghz->txrx, frequency_str, modulation_str); | ||||||
|     widget_add_string_element( |     widget_add_string_element( | ||||||
|         subghz->widget, |         subghz->widget, | ||||||
|         78, |         78, | ||||||
| @ -37,7 +37,7 @@ void subghz_scene_delete_on_enter(void* context) { | |||||||
|         AlignTop, |         AlignTop, | ||||||
|         FontSecondary, |         FontSecondary, | ||||||
|         furi_string_get_cstr(modulation_str)); |         furi_string_get_cstr(modulation_str)); | ||||||
|     subghz_protocol_decoder_base_get_string(subghz->txrx->decoder_result, text); |     subghz_protocol_decoder_base_get_string(subghz_txrx_get_decoder(subghz->txrx), text); | ||||||
|     widget_add_string_multiline_element( |     widget_add_string_multiline_element( | ||||||
|         subghz->widget, 0, 0, AlignLeft, AlignTop, FontSecondary, furi_string_get_cstr(text)); |         subghz->widget, 0, 0, AlignLeft, AlignTop, FontSecondary, furi_string_get_cstr(text)); | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -33,7 +33,7 @@ void subghz_scene_delete_raw_on_enter(void* context) { | |||||||
| 
 | 
 | ||||||
|     widget_add_string_element( |     widget_add_string_element( | ||||||
|         subghz->widget, 38, 25, AlignLeft, AlignTop, FontSecondary, "RAW signal"); |         subghz->widget, 38, 25, AlignLeft, AlignTop, FontSecondary, "RAW signal"); | ||||||
|     subghz_get_frequency_modulation(subghz, frequency_str, modulation_str); |     subghz_txrx_get_frequency_and_modulation(subghz->txrx, frequency_str, modulation_str); | ||||||
|     widget_add_string_element( |     widget_add_string_element( | ||||||
|         subghz->widget, |         subghz->widget, | ||||||
|         35, |         35, | ||||||
|  | |||||||
| @ -37,27 +37,23 @@ void subghz_scene_need_saving_on_enter(void* context) { | |||||||
| bool subghz_scene_need_saving_on_event(void* context, SceneManagerEvent event) { | bool subghz_scene_need_saving_on_event(void* context, SceneManagerEvent event) { | ||||||
|     SubGhz* subghz = context; |     SubGhz* subghz = context; | ||||||
|     if(event.type == SceneManagerEventTypeBack) { |     if(event.type == SceneManagerEventTypeBack) { | ||||||
|         subghz->txrx->rx_key_state = SubGhzRxKeyStateBack; |         subghz_rx_key_state_set(subghz, SubGhzRxKeyStateBack); | ||||||
|         scene_manager_previous_scene(subghz->scene_manager); |         scene_manager_previous_scene(subghz->scene_manager); | ||||||
|         return true; |         return true; | ||||||
|     } else if(event.type == SceneManagerEventTypeCustom) { |     } else if(event.type == SceneManagerEventTypeCustom) { | ||||||
|         if(event.event == SubGhzCustomEventSceneStay) { |         if(event.event == SubGhzCustomEventSceneStay) { | ||||||
|             subghz->txrx->rx_key_state = SubGhzRxKeyStateBack; |             subghz_rx_key_state_set(subghz, SubGhzRxKeyStateBack); | ||||||
|             scene_manager_previous_scene(subghz->scene_manager); |             scene_manager_previous_scene(subghz->scene_manager); | ||||||
|             return true; |             return true; | ||||||
|         } else if(event.event == SubGhzCustomEventSceneExit) { |         } else if(event.event == SubGhzCustomEventSceneExit) { | ||||||
|             if(subghz->txrx->rx_key_state == SubGhzRxKeyStateExit) { |             SubGhzRxKeyState state = subghz_rx_key_state_get(subghz); | ||||||
|                 subghz->txrx->rx_key_state = SubGhzRxKeyStateIDLE; |             subghz_rx_key_state_set(subghz, SubGhzRxKeyStateIDLE); | ||||||
|                 subghz_preset_init( | 
 | ||||||
|                     subghz, |             if(state == SubGhzRxKeyStateExit) { | ||||||
|                     "AM650", |                 subghz_set_default_preset(subghz); | ||||||
|                     subghz_setting_get_default_frequency(subghz->setting), |  | ||||||
|                     NULL, |  | ||||||
|                     0); |  | ||||||
|                 scene_manager_search_and_switch_to_previous_scene( |                 scene_manager_search_and_switch_to_previous_scene( | ||||||
|                     subghz->scene_manager, SubGhzSceneStart); |                     subghz->scene_manager, SubGhzSceneStart); | ||||||
|             } else { |             } else { | ||||||
|                 subghz->txrx->rx_key_state = SubGhzRxKeyStateIDLE; |  | ||||||
|                 scene_manager_previous_scene(subghz->scene_manager); |                 scene_manager_previous_scene(subghz->scene_manager); | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -3,11 +3,9 @@ | |||||||
| #include <dolphin/dolphin.h> | #include <dolphin/dolphin.h> | ||||||
| #include <lib/subghz/protocols/raw.h> | #include <lib/subghz/protocols/raw.h> | ||||||
| #include <lib/toolbox/path.h> | #include <lib/toolbox/path.h> | ||||||
| #include <float_tools.h> |  | ||||||
| 
 | 
 | ||||||
| #define RAW_FILE_NAME "Raw_signal_" | #define RAW_FILE_NAME "Raw_signal_" | ||||||
| #define TAG "SubGhzSceneReadRAW" | #define TAG "SubGhzSceneReadRAW" | ||||||
| #define RAW_THRESHOLD_RSSI_LOW_COUNT 10 |  | ||||||
| 
 | 
 | ||||||
| bool subghz_scene_read_raw_update_filename(SubGhz* subghz) { | bool subghz_scene_read_raw_update_filename(SubGhz* subghz) { | ||||||
|     bool ret = false; |     bool ret = false; | ||||||
| @ -15,12 +13,13 @@ bool subghz_scene_read_raw_update_filename(SubGhz* subghz) { | |||||||
|     FuriString* temp_str; |     FuriString* temp_str; | ||||||
|     temp_str = furi_string_alloc(); |     temp_str = furi_string_alloc(); | ||||||
|     do { |     do { | ||||||
|         if(!flipper_format_rewind(subghz->txrx->fff_data)) { |         FlipperFormat* fff_data = subghz_txrx_get_fff_data(subghz->txrx); | ||||||
|  |         if(!flipper_format_rewind(fff_data)) { | ||||||
|             FURI_LOG_E(TAG, "Rewind error"); |             FURI_LOG_E(TAG, "Rewind error"); | ||||||
|             break; |             break; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         if(!flipper_format_read_string(subghz->txrx->fff_data, "File_name", temp_str)) { |         if(!flipper_format_read_string(fff_data, "File_name", temp_str)) { | ||||||
|             FURI_LOG_E(TAG, "Missing File_name"); |             FURI_LOG_E(TAG, "Missing File_name"); | ||||||
|             break; |             break; | ||||||
|         } |         } | ||||||
| @ -38,13 +37,10 @@ static void subghz_scene_read_raw_update_statusbar(void* context) { | |||||||
|     furi_assert(context); |     furi_assert(context); | ||||||
|     SubGhz* subghz = context; |     SubGhz* subghz = context; | ||||||
| 
 | 
 | ||||||
|     FuriString* frequency_str; |     FuriString* frequency_str = furi_string_alloc(); | ||||||
|     FuriString* modulation_str; |     FuriString* modulation_str = furi_string_alloc(); | ||||||
| 
 | 
 | ||||||
|     frequency_str = furi_string_alloc(); |     subghz_txrx_get_frequency_and_modulation(subghz->txrx, frequency_str, modulation_str); | ||||||
|     modulation_str = furi_string_alloc(); |  | ||||||
| 
 |  | ||||||
|     subghz_get_frequency_modulation(subghz, frequency_str, modulation_str); |  | ||||||
|     subghz_read_raw_add_data_statusbar( |     subghz_read_raw_add_data_statusbar( | ||||||
|         subghz->subghz_read_raw, |         subghz->subghz_read_raw, | ||||||
|         furi_string_get_cstr(frequency_str), |         furi_string_get_cstr(frequency_str), | ||||||
| @ -69,13 +65,13 @@ void subghz_scene_read_raw_callback_end_tx(void* context) { | |||||||
| 
 | 
 | ||||||
| void subghz_scene_read_raw_on_enter(void* context) { | void subghz_scene_read_raw_on_enter(void* context) { | ||||||
|     SubGhz* subghz = context; |     SubGhz* subghz = context; | ||||||
|     FuriString* file_name; |     FuriString* file_name = furi_string_alloc(); | ||||||
|     file_name = furi_string_alloc(); |  | ||||||
| 
 | 
 | ||||||
|     switch(subghz->txrx->rx_key_state) { |     float threshold_rssi = subghz_threshold_rssi_get(subghz->threshold_rssi); | ||||||
|  |     switch(subghz_rx_key_state_get(subghz)) { | ||||||
|     case SubGhzRxKeyStateBack: |     case SubGhzRxKeyStateBack: | ||||||
|         subghz_read_raw_set_status( |         subghz_read_raw_set_status( | ||||||
|             subghz->subghz_read_raw, SubGhzReadRAWStatusIDLE, "", subghz->txrx->raw_threshold_rssi); |             subghz->subghz_read_raw, SubGhzReadRAWStatusIDLE, "", threshold_rssi); | ||||||
|         break; |         break; | ||||||
|     case SubGhzRxKeyStateRAWLoad: |     case SubGhzRxKeyStateRAWLoad: | ||||||
|         path_extract_filename(subghz->file_path, file_name, true); |         path_extract_filename(subghz->file_path, file_name, true); | ||||||
| @ -83,8 +79,7 @@ void subghz_scene_read_raw_on_enter(void* context) { | |||||||
|             subghz->subghz_read_raw, |             subghz->subghz_read_raw, | ||||||
|             SubGhzReadRAWStatusLoadKeyTX, |             SubGhzReadRAWStatusLoadKeyTX, | ||||||
|             furi_string_get_cstr(file_name), |             furi_string_get_cstr(file_name), | ||||||
|             subghz->txrx->raw_threshold_rssi); |             threshold_rssi); | ||||||
|         subghz->txrx->rx_key_state = SubGhzRxKeyStateIDLE; |  | ||||||
|         break; |         break; | ||||||
|     case SubGhzRxKeyStateRAWSave: |     case SubGhzRxKeyStateRAWSave: | ||||||
|         path_extract_filename(subghz->file_path, file_name, true); |         path_extract_filename(subghz->file_path, file_name, true); | ||||||
| @ -92,66 +87,51 @@ void subghz_scene_read_raw_on_enter(void* context) { | |||||||
|             subghz->subghz_read_raw, |             subghz->subghz_read_raw, | ||||||
|             SubGhzReadRAWStatusSaveKey, |             SubGhzReadRAWStatusSaveKey, | ||||||
|             furi_string_get_cstr(file_name), |             furi_string_get_cstr(file_name), | ||||||
|             subghz->txrx->raw_threshold_rssi); |             threshold_rssi); | ||||||
|         subghz->txrx->rx_key_state = SubGhzRxKeyStateIDLE; |  | ||||||
|         break; |         break; | ||||||
|     default: |     default: | ||||||
|         subghz_read_raw_set_status( |         subghz_read_raw_set_status( | ||||||
|             subghz->subghz_read_raw, |             subghz->subghz_read_raw, SubGhzReadRAWStatusStart, "", threshold_rssi); | ||||||
|             SubGhzReadRAWStatusStart, |  | ||||||
|             "", |  | ||||||
|             subghz->txrx->raw_threshold_rssi); |  | ||||||
|         subghz->txrx->rx_key_state = SubGhzRxKeyStateIDLE; |  | ||||||
|         break; |         break; | ||||||
|     } |     } | ||||||
|  | 
 | ||||||
|  |     if(subghz_rx_key_state_get(subghz) != SubGhzRxKeyStateBack) { | ||||||
|  |         subghz_rx_key_state_set(subghz, SubGhzRxKeyStateIDLE); | ||||||
|  |     } | ||||||
|     furi_string_free(file_name); |     furi_string_free(file_name); | ||||||
|     subghz_scene_read_raw_update_statusbar(subghz); |     subghz_scene_read_raw_update_statusbar(subghz); | ||||||
| 
 | 
 | ||||||
|     //set callback view raw
 |     //set callback view raw
 | ||||||
|     subghz_read_raw_set_callback(subghz->subghz_read_raw, subghz_scene_read_raw_callback, subghz); |     subghz_read_raw_set_callback(subghz->subghz_read_raw, subghz_scene_read_raw_callback, subghz); | ||||||
| 
 | 
 | ||||||
|     subghz->txrx->decoder_result = subghz_receiver_search_decoder_base_by_name( |     furi_check(subghz_txrx_load_decoder_by_name_protocol(subghz->txrx, SUBGHZ_PROTOCOL_RAW_NAME)); | ||||||
|         subghz->txrx->receiver, SUBGHZ_PROTOCOL_RAW_NAME); |  | ||||||
|     furi_assert(subghz->txrx->decoder_result); |  | ||||||
| 
 | 
 | ||||||
|     //set filter RAW feed
 |     //set filter RAW feed
 | ||||||
|     subghz_receiver_set_filter(subghz->txrx->receiver, SubGhzProtocolFlag_RAW); |     subghz_txrx_receiver_set_filter(subghz->txrx, SubGhzProtocolFlag_RAW); | ||||||
|     view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewIdReadRAW); |     view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewIdReadRAW); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| bool subghz_scene_read_raw_on_event(void* context, SceneManagerEvent event) { | bool subghz_scene_read_raw_on_event(void* context, SceneManagerEvent event) { | ||||||
|     SubGhz* subghz = context; |     SubGhz* subghz = context; | ||||||
|     bool consumed = false; |     bool consumed = false; | ||||||
|  |     SubGhzProtocolDecoderRAW* decoder_raw = | ||||||
|  |         (SubGhzProtocolDecoderRAW*)subghz_txrx_get_decoder(subghz->txrx); | ||||||
|     if(event.type == SceneManagerEventTypeCustom) { |     if(event.type == SceneManagerEventTypeCustom) { | ||||||
|         switch(event.event) { |         switch(event.event) { | ||||||
|         case SubGhzCustomEventViewReadRAWBack: |         case SubGhzCustomEventViewReadRAWBack: | ||||||
|             //Stop TX
 | 
 | ||||||
|             if(subghz->txrx->txrx_state == SubGhzTxRxStateTx) { |             subghz_txrx_stop(subghz->txrx); | ||||||
|                 subghz_tx_stop(subghz); |  | ||||||
|                 subghz_sleep(subghz); |  | ||||||
|             } |  | ||||||
|             //Stop RX
 |  | ||||||
|             if(subghz->txrx->txrx_state == SubGhzTxRxStateRx) { |  | ||||||
|                 subghz_rx_end(subghz); |  | ||||||
|                 subghz_sleep(subghz); |  | ||||||
|             }; |  | ||||||
|             //Stop save file
 |             //Stop save file
 | ||||||
|             subghz_protocol_raw_save_to_file_stop( |             subghz_protocol_raw_save_to_file_stop(decoder_raw); | ||||||
|                 (SubGhzProtocolDecoderRAW*)subghz->txrx->decoder_result); |  | ||||||
|             subghz->state_notifications = SubGhzNotificationStateIDLE; |             subghz->state_notifications = SubGhzNotificationStateIDLE; | ||||||
|             //needed save?
 |             //needed save?
 | ||||||
|             if((subghz->txrx->rx_key_state == SubGhzRxKeyStateAddKey) || |             if((subghz_rx_key_state_get(subghz) == SubGhzRxKeyStateAddKey) || | ||||||
|                (subghz->txrx->rx_key_state == SubGhzRxKeyStateBack)) { |                (subghz_rx_key_state_get(subghz) == SubGhzRxKeyStateBack)) { | ||||||
|                 subghz->txrx->rx_key_state = SubGhzRxKeyStateExit; |                 subghz_rx_key_state_set(subghz, SubGhzRxKeyStateExit); | ||||||
|                 scene_manager_next_scene(subghz->scene_manager, SubGhzSceneNeedSaving); |                 scene_manager_next_scene(subghz->scene_manager, SubGhzSceneNeedSaving); | ||||||
|             } else { |             } else { | ||||||
|                 //Restore default setting
 |                 //Restore default setting
 | ||||||
|                 subghz_preset_init( |                 subghz_set_default_preset(subghz); | ||||||
|                     subghz, |  | ||||||
|                     "AM650", |  | ||||||
|                     subghz_setting_get_default_frequency(subghz->setting), |  | ||||||
|                     NULL, |  | ||||||
|                     0); |  | ||||||
|                 if(!scene_manager_search_and_switch_to_previous_scene( |                 if(!scene_manager_search_and_switch_to_previous_scene( | ||||||
|                        subghz->scene_manager, SubGhzSceneSaved)) { |                        subghz->scene_manager, SubGhzSceneSaved)) { | ||||||
|                     if(!scene_manager_search_and_switch_to_previous_scene( |                     if(!scene_manager_search_and_switch_to_previous_scene( | ||||||
| @ -165,16 +145,7 @@ bool subghz_scene_read_raw_on_event(void* context, SceneManagerEvent event) { | |||||||
|             break; |             break; | ||||||
| 
 | 
 | ||||||
|         case SubGhzCustomEventViewReadRAWTXRXStop: |         case SubGhzCustomEventViewReadRAWTXRXStop: | ||||||
|             //Stop TX
 |             subghz_txrx_stop(subghz->txrx); | ||||||
|             if(subghz->txrx->txrx_state == SubGhzTxRxStateTx) { |  | ||||||
|                 subghz_tx_stop(subghz); |  | ||||||
|                 subghz_sleep(subghz); |  | ||||||
|             } |  | ||||||
|             //Stop RX
 |  | ||||||
|             if(subghz->txrx->txrx_state == SubGhzTxRxStateRx) { |  | ||||||
|                 subghz_rx_end(subghz); |  | ||||||
|                 subghz_sleep(subghz); |  | ||||||
|             }; |  | ||||||
|             subghz->state_notifications = SubGhzNotificationStateIDLE; |             subghz->state_notifications = SubGhzNotificationStateIDLE; | ||||||
|             consumed = true; |             consumed = true; | ||||||
|             break; |             break; | ||||||
| @ -187,13 +158,13 @@ bool subghz_scene_read_raw_on_event(void* context, SceneManagerEvent event) { | |||||||
|             break; |             break; | ||||||
| 
 | 
 | ||||||
|         case SubGhzCustomEventViewReadRAWErase: |         case SubGhzCustomEventViewReadRAWErase: | ||||||
|             if(subghz->txrx->rx_key_state == SubGhzRxKeyStateAddKey) { |             if(subghz_rx_key_state_get(subghz) == SubGhzRxKeyStateAddKey) { | ||||||
|                 if(subghz_scene_read_raw_update_filename(subghz)) { |                 if(subghz_scene_read_raw_update_filename(subghz)) { | ||||||
|                     furi_string_set(subghz->file_path_tmp, subghz->file_path); |                     furi_string_set(subghz->file_path_tmp, subghz->file_path); | ||||||
|                     subghz_delete_file(subghz); |                     subghz_delete_file(subghz); | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|             subghz->txrx->rx_key_state = SubGhzRxKeyStateIDLE; |             subghz_rx_key_state_set(subghz, SubGhzRxKeyStateIDLE); | ||||||
|             notification_message(subghz->notifications, &sequence_reset_rgb); |             notification_message(subghz->notifications, &sequence_reset_rgb); | ||||||
|             consumed = true; |             consumed = true; | ||||||
|             break; |             break; | ||||||
| @ -203,7 +174,7 @@ bool subghz_scene_read_raw_on_event(void* context, SceneManagerEvent event) { | |||||||
|                 if(subghz_scene_read_raw_update_filename(subghz)) { |                 if(subghz_scene_read_raw_update_filename(subghz)) { | ||||||
|                     scene_manager_set_scene_state( |                     scene_manager_set_scene_state( | ||||||
|                         subghz->scene_manager, SubGhzSceneReadRAW, SubGhzCustomEventManagerSet); |                         subghz->scene_manager, SubGhzSceneReadRAW, SubGhzCustomEventManagerSet); | ||||||
|                     subghz->txrx->rx_key_state = SubGhzRxKeyStateRAWLoad; |                     subghz_rx_key_state_set(subghz, SubGhzRxKeyStateRAWLoad); | ||||||
|                     scene_manager_next_scene(subghz->scene_manager, SubGhzSceneMoreRAW); |                     scene_manager_next_scene(subghz->scene_manager, SubGhzSceneMoreRAW); | ||||||
|                     consumed = true; |                     consumed = true; | ||||||
|                 } else { |                 } else { | ||||||
| @ -223,34 +194,23 @@ bool subghz_scene_read_raw_on_event(void* context, SceneManagerEvent event) { | |||||||
|             if(subghz_file_available(subghz) && subghz_scene_read_raw_update_filename(subghz)) { |             if(subghz_file_available(subghz) && subghz_scene_read_raw_update_filename(subghz)) { | ||||||
|                 //start send
 |                 //start send
 | ||||||
|                 subghz->state_notifications = SubGhzNotificationStateIDLE; |                 subghz->state_notifications = SubGhzNotificationStateIDLE; | ||||||
|                 if(subghz->txrx->txrx_state == SubGhzTxRxStateRx) { |                 if(!subghz_tx_start(subghz, subghz_txrx_get_fff_data(subghz->txrx))) { | ||||||
|                     subghz_rx_end(subghz); |                     subghz_rx_key_state_set(subghz, SubGhzRxKeyStateBack); | ||||||
|                 } |  | ||||||
|                 if((subghz->txrx->txrx_state == SubGhzTxRxStateIDLE) || |  | ||||||
|                    (subghz->txrx->txrx_state == SubGhzTxRxStateSleep)) { |  | ||||||
|                     if(!subghz_tx_start(subghz, subghz->txrx->fff_data)) { |  | ||||||
|                         subghz->txrx->rx_key_state = SubGhzRxKeyStateBack; |  | ||||||
|                     subghz_read_raw_set_status( |                     subghz_read_raw_set_status( | ||||||
|                         subghz->subghz_read_raw, |                         subghz->subghz_read_raw, | ||||||
|                         SubGhzReadRAWStatusIDLE, |                         SubGhzReadRAWStatusIDLE, | ||||||
|                         "", |                         "", | ||||||
|                             subghz->txrx->raw_threshold_rssi); |                         subghz_threshold_rssi_get(subghz->threshold_rssi)); | ||||||
|                 } else { |                 } else { | ||||||
|                         if(scene_manager_has_previous_scene( |                     if(scene_manager_has_previous_scene(subghz->scene_manager, SubGhzSceneSaved) || | ||||||
|                                subghz->scene_manager, SubGhzSceneSaved) || |                        !scene_manager_has_previous_scene(subghz->scene_manager, SubGhzSceneStart)) { | ||||||
|                            !scene_manager_has_previous_scene( |  | ||||||
|                                subghz->scene_manager, SubGhzSceneStart)) { |  | ||||||
|                         DOLPHIN_DEED(DolphinDeedSubGhzSend); |                         DOLPHIN_DEED(DolphinDeedSubGhzSend); | ||||||
|                     } |                     } | ||||||
|                     // set callback end tx
 |                     // set callback end tx
 | ||||||
|                         subghz_protocol_raw_file_encoder_worker_set_callback_end( |                     subghz_txrx_set_raw_file_encoder_worker_callback_end( | ||||||
|                             (SubGhzProtocolEncoderRAW*)subghz_transmitter_get_protocol_instance( |                         subghz->txrx, subghz_scene_read_raw_callback_end_tx, subghz); | ||||||
|                                 subghz->txrx->transmitter), |  | ||||||
|                             subghz_scene_read_raw_callback_end_tx, |  | ||||||
|                             subghz); |  | ||||||
|                     subghz->state_notifications = SubGhzNotificationStateTx; |                     subghz->state_notifications = SubGhzNotificationStateTx; | ||||||
|                 } |                 } | ||||||
|                 } |  | ||||||
|             } else { |             } else { | ||||||
|                 if(!scene_manager_search_and_switch_to_previous_scene( |                 if(!scene_manager_search_and_switch_to_previous_scene( | ||||||
|                        subghz->scene_manager, SubGhzSceneStart)) { |                        subghz->scene_manager, SubGhzSceneStart)) { | ||||||
| @ -263,33 +223,22 @@ bool subghz_scene_read_raw_on_event(void* context, SceneManagerEvent event) { | |||||||
| 
 | 
 | ||||||
|         case SubGhzCustomEventViewReadRAWSendStop: |         case SubGhzCustomEventViewReadRAWSendStop: | ||||||
|             subghz->state_notifications = SubGhzNotificationStateIDLE; |             subghz->state_notifications = SubGhzNotificationStateIDLE; | ||||||
|             if(subghz->txrx->txrx_state == SubGhzTxRxStateTx) { |             subghz_txrx_stop(subghz->txrx); | ||||||
|                 subghz_speaker_unmute(subghz); |  | ||||||
|                 subghz_tx_stop(subghz); |  | ||||||
|                 subghz_sleep(subghz); |  | ||||||
|             } |  | ||||||
|             subghz_read_raw_stop_send(subghz->subghz_read_raw); |             subghz_read_raw_stop_send(subghz->subghz_read_raw); | ||||||
|             consumed = true; |             consumed = true; | ||||||
|             break; |             break; | ||||||
| 
 | 
 | ||||||
|         case SubGhzCustomEventViewReadRAWIDLE: |         case SubGhzCustomEventViewReadRAWIDLE: | ||||||
|             if(subghz->txrx->txrx_state == SubGhzTxRxStateRx) { |             subghz_txrx_stop(subghz->txrx); | ||||||
|                 subghz_rx_end(subghz); |             size_t spl_count = subghz_protocol_raw_get_sample_write(decoder_raw); | ||||||
|                 subghz_sleep(subghz); |  | ||||||
|             }; |  | ||||||
| 
 | 
 | ||||||
|             size_t spl_count = subghz_protocol_raw_get_sample_write( |             subghz_protocol_raw_save_to_file_stop(decoder_raw); | ||||||
|                 (SubGhzProtocolDecoderRAW*)subghz->txrx->decoder_result); |  | ||||||
| 
 | 
 | ||||||
|             subghz_protocol_raw_save_to_file_stop( |             FuriString* temp_str = furi_string_alloc(); | ||||||
|                 (SubGhzProtocolDecoderRAW*)subghz->txrx->decoder_result); |  | ||||||
| 
 |  | ||||||
|             FuriString* temp_str; |  | ||||||
|             temp_str = furi_string_alloc(); |  | ||||||
|             furi_string_printf( |             furi_string_printf( | ||||||
|                 temp_str, "%s/%s%s", SUBGHZ_RAW_FOLDER, RAW_FILE_NAME, SUBGHZ_APP_EXTENSION); |                 temp_str, "%s/%s%s", SUBGHZ_RAW_FOLDER, RAW_FILE_NAME, SUBGHZ_APP_EXTENSION); | ||||||
|             subghz_protocol_raw_gen_fff_data( |             subghz_protocol_raw_gen_fff_data( | ||||||
|                 subghz->txrx->fff_data, furi_string_get_cstr(temp_str)); |                 subghz_txrx_get_fff_data(subghz->txrx), furi_string_get_cstr(temp_str)); | ||||||
|             furi_string_free(temp_str); |             furi_string_free(temp_str); | ||||||
| 
 | 
 | ||||||
|             if(spl_count > 0) { |             if(spl_count > 0) { | ||||||
| @ -299,32 +248,21 @@ bool subghz_scene_read_raw_on_event(void* context, SceneManagerEvent event) { | |||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|             subghz->state_notifications = SubGhzNotificationStateIDLE; |             subghz->state_notifications = SubGhzNotificationStateIDLE; | ||||||
|             subghz->txrx->rx_key_state = SubGhzRxKeyStateAddKey; |             subghz_rx_key_state_set(subghz, SubGhzRxKeyStateAddKey); | ||||||
| 
 | 
 | ||||||
|             consumed = true; |             consumed = true; | ||||||
|             break; |             break; | ||||||
| 
 | 
 | ||||||
|         case SubGhzCustomEventViewReadRAWREC: |         case SubGhzCustomEventViewReadRAWREC: | ||||||
|             if(subghz->txrx->rx_key_state != SubGhzRxKeyStateIDLE) { |             if(subghz_rx_key_state_get(subghz) != SubGhzRxKeyStateIDLE) { | ||||||
|                 scene_manager_next_scene(subghz->scene_manager, SubGhzSceneNeedSaving); |                 scene_manager_next_scene(subghz->scene_manager, SubGhzSceneNeedSaving); | ||||||
|             } else { |             } else { | ||||||
|                 subghz->txrx->raw_threshold_rssi_low_count = RAW_THRESHOLD_RSSI_LOW_COUNT; |                 SubGhzRadioPreset preset = subghz_txrx_get_preset(subghz->txrx); | ||||||
|                 if(subghz_protocol_raw_save_to_file_init( |                 if(subghz_protocol_raw_save_to_file_init(decoder_raw, RAW_FILE_NAME, &preset)) { | ||||||
|                        (SubGhzProtocolDecoderRAW*)subghz->txrx->decoder_result, |  | ||||||
|                        RAW_FILE_NAME, |  | ||||||
|                        subghz->txrx->preset)) { |  | ||||||
|                     DOLPHIN_DEED(DolphinDeedSubGhzRawRec); |                     DOLPHIN_DEED(DolphinDeedSubGhzRawRec); | ||||||
|                     if((subghz->txrx->txrx_state == SubGhzTxRxStateIDLE) || |                     subghz_txrx_rx_start(subghz->txrx); | ||||||
|                        (subghz->txrx->txrx_state == SubGhzTxRxStateSleep)) { |  | ||||||
|                         subghz_begin( |  | ||||||
|                             subghz, |  | ||||||
|                             subghz_setting_get_preset_data_by_name( |  | ||||||
|                                 subghz->setting, |  | ||||||
|                                 furi_string_get_cstr(subghz->txrx->preset->name))); |  | ||||||
|                         subghz_rx(subghz, subghz->txrx->preset->frequency); |  | ||||||
|                     } |  | ||||||
|                     subghz->state_notifications = SubGhzNotificationStateRx; |                     subghz->state_notifications = SubGhzNotificationStateRx; | ||||||
|                     subghz->txrx->rx_key_state = SubGhzRxKeyStateAddKey; |                     subghz_rx_key_state_set(subghz, SubGhzRxKeyStateAddKey); | ||||||
|                 } else { |                 } else { | ||||||
|                     furi_string_set(subghz->error_str, "Function requires\nan SD card."); |                     furi_string_set(subghz->error_str, "Function requires\nan SD card."); | ||||||
|                     scene_manager_next_scene(subghz->scene_manager, SubGhzSceneShowError); |                     scene_manager_next_scene(subghz->scene_manager, SubGhzSceneShowError); | ||||||
| @ -337,7 +275,7 @@ bool subghz_scene_read_raw_on_event(void* context, SceneManagerEvent event) { | |||||||
|             if(subghz_file_available(subghz) && subghz_scene_read_raw_update_filename(subghz)) { |             if(subghz_file_available(subghz) && subghz_scene_read_raw_update_filename(subghz)) { | ||||||
|                 scene_manager_set_scene_state( |                 scene_manager_set_scene_state( | ||||||
|                     subghz->scene_manager, SubGhzSceneReadRAW, SubGhzCustomEventManagerSetRAW); |                     subghz->scene_manager, SubGhzSceneReadRAW, SubGhzCustomEventManagerSetRAW); | ||||||
|                 subghz->txrx->rx_key_state = SubGhzRxKeyStateBack; |                 subghz_rx_key_state_set(subghz, SubGhzRxKeyStateBack); | ||||||
|                 scene_manager_next_scene(subghz->scene_manager, SubGhzSceneSaveName); |                 scene_manager_next_scene(subghz->scene_manager, SubGhzSceneSaveName); | ||||||
|             } else { |             } else { | ||||||
|                 if(!scene_manager_search_and_switch_to_previous_scene( |                 if(!scene_manager_search_and_switch_to_previous_scene( | ||||||
| @ -356,41 +294,15 @@ bool subghz_scene_read_raw_on_event(void* context, SceneManagerEvent event) { | |||||||
|         switch(subghz->state_notifications) { |         switch(subghz->state_notifications) { | ||||||
|         case SubGhzNotificationStateRx: |         case SubGhzNotificationStateRx: | ||||||
|             notification_message(subghz->notifications, &sequence_blink_cyan_10); |             notification_message(subghz->notifications, &sequence_blink_cyan_10); | ||||||
|  | 
 | ||||||
|             subghz_read_raw_update_sample_write( |             subghz_read_raw_update_sample_write( | ||||||
|                 subghz->subghz_read_raw, |                 subghz->subghz_read_raw, subghz_protocol_raw_get_sample_write(decoder_raw)); | ||||||
|                 subghz_protocol_raw_get_sample_write( |  | ||||||
|                     (SubGhzProtocolDecoderRAW*)subghz->txrx->decoder_result)); |  | ||||||
| 
 |  | ||||||
|             float rssi = furi_hal_subghz_get_rssi(); |  | ||||||
| 
 |  | ||||||
|             if(float_is_equal(subghz->txrx->raw_threshold_rssi, SUBGHZ_RAW_TRESHOLD_MIN)) { |  | ||||||
|                 subghz_read_raw_add_data_rssi(subghz->subghz_read_raw, rssi, true); |  | ||||||
|                 subghz_protocol_raw_save_to_file_pause( |  | ||||||
|                     (SubGhzProtocolDecoderRAW*)subghz->txrx->decoder_result, false); |  | ||||||
|             } else { |  | ||||||
|                 if(rssi < subghz->txrx->raw_threshold_rssi) { |  | ||||||
|                     subghz->txrx->raw_threshold_rssi_low_count++; |  | ||||||
|                     if(subghz->txrx->raw_threshold_rssi_low_count > RAW_THRESHOLD_RSSI_LOW_COUNT) { |  | ||||||
|                         subghz->txrx->raw_threshold_rssi_low_count = RAW_THRESHOLD_RSSI_LOW_COUNT; |  | ||||||
|                     } |  | ||||||
|                     subghz_read_raw_add_data_rssi(subghz->subghz_read_raw, rssi, false); |  | ||||||
|                 } else { |  | ||||||
|                     subghz->txrx->raw_threshold_rssi_low_count = 0; |  | ||||||
|                 } |  | ||||||
| 
 |  | ||||||
|                 if(subghz->txrx->raw_threshold_rssi_low_count == RAW_THRESHOLD_RSSI_LOW_COUNT) { |  | ||||||
|                     subghz_read_raw_add_data_rssi(subghz->subghz_read_raw, rssi, false); |  | ||||||
|                     subghz_protocol_raw_save_to_file_pause( |  | ||||||
|                         (SubGhzProtocolDecoderRAW*)subghz->txrx->decoder_result, true); |  | ||||||
|                     subghz_speaker_mute(subghz); |  | ||||||
|                 } else { |  | ||||||
|                     subghz_read_raw_add_data_rssi(subghz->subghz_read_raw, rssi, true); |  | ||||||
|                     subghz_protocol_raw_save_to_file_pause( |  | ||||||
|                         (SubGhzProtocolDecoderRAW*)subghz->txrx->decoder_result, false); |  | ||||||
|                     subghz_speaker_unmute(subghz); |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
| 
 | 
 | ||||||
|  |             SubGhzThresholdRssiData ret_rssi = | ||||||
|  |                 subghz_threshold_get_rssi_data(subghz->threshold_rssi); | ||||||
|  |             subghz_read_raw_add_data_rssi( | ||||||
|  |                 subghz->subghz_read_raw, ret_rssi.rssi, ret_rssi.is_above); | ||||||
|  |             subghz_protocol_raw_save_to_file_pause(decoder_raw, !ret_rssi.is_above); | ||||||
|             break; |             break; | ||||||
|         case SubGhzNotificationStateTx: |         case SubGhzNotificationStateTx: | ||||||
|             notification_message(subghz->notifications, &sequence_blink_magenta_10); |             notification_message(subghz->notifications, &sequence_blink_magenta_10); | ||||||
| @ -407,13 +319,10 @@ void subghz_scene_read_raw_on_exit(void* context) { | |||||||
|     SubGhz* subghz = context; |     SubGhz* subghz = context; | ||||||
| 
 | 
 | ||||||
|     //Stop CC1101
 |     //Stop CC1101
 | ||||||
|     if(subghz->txrx->txrx_state == SubGhzTxRxStateRx) { |     subghz_txrx_stop(subghz->txrx); | ||||||
|         subghz_rx_end(subghz); |  | ||||||
|         subghz_sleep(subghz); |  | ||||||
|     }; |  | ||||||
|     subghz->state_notifications = SubGhzNotificationStateIDLE; |     subghz->state_notifications = SubGhzNotificationStateIDLE; | ||||||
|     notification_message(subghz->notifications, &sequence_reset_rgb); |     notification_message(subghz->notifications, &sequence_reset_rgb); | ||||||
| 
 | 
 | ||||||
|     //filter restoration
 |     //filter restoration
 | ||||||
|     subghz_receiver_set_filter(subghz->txrx->receiver, subghz->txrx->filter); |     subghz_txrx_receiver_set_filter(subghz->txrx, subghz->filter); | ||||||
| } | } | ||||||
|  | |||||||
| @ -35,16 +35,12 @@ static const NotificationSequence subghs_sequence_rx_locked = { | |||||||
| 
 | 
 | ||||||
| static void subghz_scene_receiver_update_statusbar(void* context) { | static void subghz_scene_receiver_update_statusbar(void* context) { | ||||||
|     SubGhz* subghz = context; |     SubGhz* subghz = context; | ||||||
|     FuriString* history_stat_str; |     FuriString* history_stat_str = furi_string_alloc(); | ||||||
|     history_stat_str = furi_string_alloc(); |     if(!subghz_history_get_text_space_left(subghz->history, history_stat_str)) { | ||||||
|     if(!subghz_history_get_text_space_left(subghz->txrx->history, history_stat_str)) { |         FuriString* frequency_str = furi_string_alloc(); | ||||||
|         FuriString* frequency_str; |         FuriString* modulation_str = furi_string_alloc(); | ||||||
|         FuriString* modulation_str; |  | ||||||
| 
 | 
 | ||||||
|         frequency_str = furi_string_alloc(); |         subghz_txrx_get_frequency_and_modulation(subghz->txrx, frequency_str, modulation_str); | ||||||
|         modulation_str = furi_string_alloc(); |  | ||||||
| 
 |  | ||||||
|         subghz_get_frequency_modulation(subghz, frequency_str, modulation_str); |  | ||||||
| 
 | 
 | ||||||
|         subghz_view_receiver_add_data_statusbar( |         subghz_view_receiver_add_data_statusbar( | ||||||
|             subghz->subghz_receiver, |             subghz->subghz_receiver, | ||||||
| @ -74,80 +70,68 @@ static void subghz_scene_add_to_history_callback( | |||||||
|     void* context) { |     void* context) { | ||||||
|     furi_assert(context); |     furi_assert(context); | ||||||
|     SubGhz* subghz = context; |     SubGhz* subghz = context; | ||||||
|     FuriString* str_buff; |     SubGhzHistory* history = subghz->history; | ||||||
|     str_buff = furi_string_alloc(); |     FuriString* str_buff = furi_string_alloc(); | ||||||
| 
 | 
 | ||||||
|     if(subghz_history_add_to_history(subghz->txrx->history, decoder_base, subghz->txrx->preset)) { |     SubGhzRadioPreset preset = subghz_txrx_get_preset(subghz->txrx); | ||||||
|  | 
 | ||||||
|  |     if(subghz_history_add_to_history(history, decoder_base, &preset)) { | ||||||
|         furi_string_reset(str_buff); |         furi_string_reset(str_buff); | ||||||
| 
 | 
 | ||||||
|         subghz->state_notifications = SubGhzNotificationStateRxDone; |         subghz->state_notifications = SubGhzNotificationStateRxDone; | ||||||
| 
 |         uint16_t item_history = subghz_history_get_item(history); | ||||||
|         subghz_history_get_text_item_menu( |         subghz_history_get_text_item_menu(history, str_buff, item_history - 1); | ||||||
|             subghz->txrx->history, str_buff, subghz_history_get_item(subghz->txrx->history) - 1); |  | ||||||
|         subghz_view_receiver_add_item_to_menu( |         subghz_view_receiver_add_item_to_menu( | ||||||
|             subghz->subghz_receiver, |             subghz->subghz_receiver, | ||||||
|             furi_string_get_cstr(str_buff), |             furi_string_get_cstr(str_buff), | ||||||
|             subghz_history_get_type_protocol( |             subghz_history_get_type_protocol(history, item_history - 1)); | ||||||
|                 subghz->txrx->history, subghz_history_get_item(subghz->txrx->history) - 1)); |  | ||||||
| 
 | 
 | ||||||
|         subghz_scene_receiver_update_statusbar(subghz); |         subghz_scene_receiver_update_statusbar(subghz); | ||||||
|     } |     } | ||||||
|     subghz_receiver_reset(receiver); |     subghz_receiver_reset(receiver); | ||||||
|     furi_string_free(str_buff); |     furi_string_free(str_buff); | ||||||
|     subghz->txrx->rx_key_state = SubGhzRxKeyStateAddKey; |     subghz_rx_key_state_set(subghz, SubGhzRxKeyStateAddKey); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void subghz_scene_receiver_on_enter(void* context) { | void subghz_scene_receiver_on_enter(void* context) { | ||||||
|     SubGhz* subghz = context; |     SubGhz* subghz = context; | ||||||
|  |     SubGhzHistory* history = subghz->history; | ||||||
| 
 | 
 | ||||||
|     FuriString* str_buff; |     FuriString* str_buff; | ||||||
|     str_buff = furi_string_alloc(); |     str_buff = furi_string_alloc(); | ||||||
| 
 | 
 | ||||||
|     if(subghz->txrx->rx_key_state == SubGhzRxKeyStateIDLE) { |     if(subghz_rx_key_state_get(subghz) == SubGhzRxKeyStateIDLE) { | ||||||
|         subghz_preset_init( |         subghz_set_default_preset(subghz); | ||||||
|             subghz, "AM650", subghz_setting_get_default_frequency(subghz->setting), NULL, 0); |         subghz_history_reset(history); | ||||||
|         subghz_history_reset(subghz->txrx->history); |         subghz_rx_key_state_set(subghz, SubGhzRxKeyStateStart); | ||||||
|         subghz->txrx->rx_key_state = SubGhzRxKeyStateStart; |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     subghz_view_receiver_set_lock(subghz->subghz_receiver, subghz->lock); |     subghz_view_receiver_set_lock(subghz->subghz_receiver, subghz_is_locked(subghz)); | ||||||
| 
 | 
 | ||||||
|     //Load history to receiver
 |     //Load history to receiver
 | ||||||
|     subghz_view_receiver_exit(subghz->subghz_receiver); |     subghz_view_receiver_exit(subghz->subghz_receiver); | ||||||
|     for(uint8_t i = 0; i < subghz_history_get_item(subghz->txrx->history); i++) { |     for(uint8_t i = 0; i < subghz_history_get_item(history); i++) { | ||||||
|         furi_string_reset(str_buff); |         furi_string_reset(str_buff); | ||||||
|         subghz_history_get_text_item_menu(subghz->txrx->history, str_buff, i); |         subghz_history_get_text_item_menu(history, str_buff, i); | ||||||
|         subghz_view_receiver_add_item_to_menu( |         subghz_view_receiver_add_item_to_menu( | ||||||
|             subghz->subghz_receiver, |             subghz->subghz_receiver, | ||||||
|             furi_string_get_cstr(str_buff), |             furi_string_get_cstr(str_buff), | ||||||
|             subghz_history_get_type_protocol(subghz->txrx->history, i)); |             subghz_history_get_type_protocol(history, i)); | ||||||
|         subghz->txrx->rx_key_state = SubGhzRxKeyStateAddKey; |         subghz_rx_key_state_set(subghz, SubGhzRxKeyStateAddKey); | ||||||
|     } |     } | ||||||
|     furi_string_free(str_buff); |     furi_string_free(str_buff); | ||||||
|     subghz_scene_receiver_update_statusbar(subghz); |     subghz_scene_receiver_update_statusbar(subghz); | ||||||
|     subghz_view_receiver_set_callback( |     subghz_view_receiver_set_callback( | ||||||
|         subghz->subghz_receiver, subghz_scene_receiver_callback, subghz); |         subghz->subghz_receiver, subghz_scene_receiver_callback, subghz); | ||||||
|     subghz_receiver_set_rx_callback( |     subghz_txrx_set_rx_calback(subghz->txrx, subghz_scene_add_to_history_callback, subghz); | ||||||
|         subghz->txrx->receiver, subghz_scene_add_to_history_callback, subghz); |  | ||||||
| 
 | 
 | ||||||
|     subghz->state_notifications = SubGhzNotificationStateRx; |     subghz->state_notifications = SubGhzNotificationStateRx; | ||||||
|     if(subghz->txrx->txrx_state == SubGhzTxRxStateRx) { |     subghz_txrx_rx_start(subghz->txrx); | ||||||
|         subghz_rx_end(subghz); |     subghz_view_receiver_set_idx_menu(subghz->subghz_receiver, subghz->idx_menu_chosen); | ||||||
|     }; |  | ||||||
|     if((subghz->txrx->txrx_state == SubGhzTxRxStateIDLE) || |  | ||||||
|        (subghz->txrx->txrx_state == SubGhzTxRxStateSleep)) { |  | ||||||
|         subghz_begin( |  | ||||||
|             subghz, |  | ||||||
|             subghz_setting_get_preset_data_by_name( |  | ||||||
|                 subghz->setting, furi_string_get_cstr(subghz->txrx->preset->name))); |  | ||||||
|         subghz_rx(subghz, subghz->txrx->preset->frequency); |  | ||||||
|     } |  | ||||||
|     subghz_view_receiver_set_idx_menu(subghz->subghz_receiver, subghz->txrx->idx_menu_chosen); |  | ||||||
| 
 | 
 | ||||||
|     //to use a universal decoder, we are looking for a link to it
 |     //to use a universal decoder, we are looking for a link to it
 | ||||||
|     subghz->txrx->decoder_result = subghz_receiver_search_decoder_base_by_name( |     furi_check( | ||||||
|         subghz->txrx->receiver, SUBGHZ_PROTOCOL_BIN_RAW_NAME); |         subghz_txrx_load_decoder_by_name_protocol(subghz->txrx, SUBGHZ_PROTOCOL_BIN_RAW_NAME)); | ||||||
|     furi_assert(subghz->txrx->decoder_result); |  | ||||||
| 
 | 
 | ||||||
|     view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewIdReceiver); |     view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewIdReceiver); | ||||||
| } | } | ||||||
| @ -160,41 +144,31 @@ bool subghz_scene_receiver_on_event(void* context, SceneManagerEvent event) { | |||||||
|         case SubGhzCustomEventViewReceiverBack: |         case SubGhzCustomEventViewReceiverBack: | ||||||
|             // Stop CC1101 Rx
 |             // Stop CC1101 Rx
 | ||||||
|             subghz->state_notifications = SubGhzNotificationStateIDLE; |             subghz->state_notifications = SubGhzNotificationStateIDLE; | ||||||
|             if(subghz->txrx->txrx_state == SubGhzTxRxStateRx) { |             subghz_txrx_stop(subghz->txrx); | ||||||
|                 subghz_rx_end(subghz); |             subghz_txrx_hopper_set_state(subghz->txrx, SubGhzHopperStateOFF); | ||||||
|                 subghz_sleep(subghz); |             subghz->idx_menu_chosen = 0; | ||||||
|             }; |             subghz_txrx_set_rx_calback(subghz->txrx, NULL, subghz); | ||||||
|             subghz->txrx->hopper_state = SubGhzHopperStateOFF; |  | ||||||
|             subghz->txrx->idx_menu_chosen = 0; |  | ||||||
|             subghz_receiver_set_rx_callback(subghz->txrx->receiver, NULL, subghz); |  | ||||||
| 
 | 
 | ||||||
|             if(subghz->txrx->rx_key_state == SubGhzRxKeyStateAddKey) { |             if(subghz_rx_key_state_get(subghz) == SubGhzRxKeyStateAddKey) { | ||||||
|                 subghz->txrx->rx_key_state = SubGhzRxKeyStateExit; |                 subghz_rx_key_state_set(subghz, SubGhzRxKeyStateExit); | ||||||
|                 scene_manager_next_scene(subghz->scene_manager, SubGhzSceneNeedSaving); |                 scene_manager_next_scene(subghz->scene_manager, SubGhzSceneNeedSaving); | ||||||
|             } else { |             } else { | ||||||
|                 subghz->txrx->rx_key_state = SubGhzRxKeyStateIDLE; |                 subghz_rx_key_state_set(subghz, SubGhzRxKeyStateIDLE); | ||||||
|                 subghz_preset_init( |                 subghz_set_default_preset(subghz); | ||||||
|                     subghz, |  | ||||||
|                     "AM650", |  | ||||||
|                     subghz_setting_get_default_frequency(subghz->setting), |  | ||||||
|                     NULL, |  | ||||||
|                     0); |  | ||||||
|                 scene_manager_search_and_switch_to_previous_scene( |                 scene_manager_search_and_switch_to_previous_scene( | ||||||
|                     subghz->scene_manager, SubGhzSceneStart); |                     subghz->scene_manager, SubGhzSceneStart); | ||||||
|             } |             } | ||||||
|             consumed = true; |             consumed = true; | ||||||
|             break; |             break; | ||||||
|         case SubGhzCustomEventViewReceiverOK: |         case SubGhzCustomEventViewReceiverOK: | ||||||
|             subghz->txrx->idx_menu_chosen = |             subghz->idx_menu_chosen = subghz_view_receiver_get_idx_menu(subghz->subghz_receiver); | ||||||
|                 subghz_view_receiver_get_idx_menu(subghz->subghz_receiver); |  | ||||||
|             scene_manager_next_scene(subghz->scene_manager, SubGhzSceneReceiverInfo); |             scene_manager_next_scene(subghz->scene_manager, SubGhzSceneReceiverInfo); | ||||||
|             DOLPHIN_DEED(DolphinDeedSubGhzReceiverInfo); |             DOLPHIN_DEED(DolphinDeedSubGhzReceiverInfo); | ||||||
|             consumed = true; |             consumed = true; | ||||||
|             break; |             break; | ||||||
|         case SubGhzCustomEventViewReceiverConfig: |         case SubGhzCustomEventViewReceiverConfig: | ||||||
|             subghz->state_notifications = SubGhzNotificationStateIDLE; |             subghz->state_notifications = SubGhzNotificationStateIDLE; | ||||||
|             subghz->txrx->idx_menu_chosen = |             subghz->idx_menu_chosen = subghz_view_receiver_get_idx_menu(subghz->subghz_receiver); | ||||||
|                 subghz_view_receiver_get_idx_menu(subghz->subghz_receiver); |  | ||||||
|             scene_manager_next_scene(subghz->scene_manager, SubGhzSceneReceiverConfig); |             scene_manager_next_scene(subghz->scene_manager, SubGhzSceneReceiverConfig); | ||||||
|             consumed = true; |             consumed = true; | ||||||
|             break; |             break; | ||||||
| @ -203,30 +177,30 @@ bool subghz_scene_receiver_on_event(void* context, SceneManagerEvent event) { | |||||||
|             consumed = true; |             consumed = true; | ||||||
|             break; |             break; | ||||||
|         case SubGhzCustomEventViewReceiverUnlock: |         case SubGhzCustomEventViewReceiverUnlock: | ||||||
|             subghz->lock = SubGhzLockOff; |             subghz_unlock(subghz); | ||||||
|             consumed = true; |             consumed = true; | ||||||
|             break; |             break; | ||||||
|         default: |         default: | ||||||
|             break; |             break; | ||||||
|         } |         } | ||||||
|     } else if(event.type == SceneManagerEventTypeTick) { |     } else if(event.type == SceneManagerEventTypeTick) { | ||||||
|         if(subghz->txrx->hopper_state != SubGhzHopperStateOFF) { |         if(subghz_txrx_hopper_get_state(subghz->txrx) != SubGhzHopperStateOFF) { | ||||||
|             subghz_hopper_update(subghz); |             subghz_txrx_hopper_update(subghz->txrx); | ||||||
|             subghz_scene_receiver_update_statusbar(subghz); |             subghz_scene_receiver_update_statusbar(subghz); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         //get RSSI
 |         SubGhzThresholdRssiData ret_rssi = subghz_threshold_get_rssi_data(subghz->threshold_rssi); | ||||||
|         float rssi = furi_hal_subghz_get_rssi(); | 
 | ||||||
|         subghz_receiver_rssi(subghz->subghz_receiver, rssi); |         subghz_receiver_rssi(subghz->subghz_receiver, ret_rssi.rssi); | ||||||
|         subghz_protocol_decoder_bin_raw_data_input_rssi( |         subghz_protocol_decoder_bin_raw_data_input_rssi( | ||||||
|             (SubGhzProtocolDecoderBinRAW*)subghz->txrx->decoder_result, rssi); |             (SubGhzProtocolDecoderBinRAW*)subghz_txrx_get_decoder(subghz->txrx), ret_rssi.rssi); | ||||||
| 
 | 
 | ||||||
|         switch(subghz->state_notifications) { |         switch(subghz->state_notifications) { | ||||||
|         case SubGhzNotificationStateRx: |         case SubGhzNotificationStateRx: | ||||||
|             notification_message(subghz->notifications, &sequence_blink_cyan_10); |             notification_message(subghz->notifications, &sequence_blink_cyan_10); | ||||||
|             break; |             break; | ||||||
|         case SubGhzNotificationStateRxDone: |         case SubGhzNotificationStateRxDone: | ||||||
|             if(subghz->lock != SubGhzLockOn) { |             if(!subghz_is_locked(subghz)) { | ||||||
|                 notification_message(subghz->notifications, &subghs_sequence_rx); |                 notification_message(subghz->notifications, &subghs_sequence_rx); | ||||||
|             } else { |             } else { | ||||||
|                 notification_message(subghz->notifications, &subghs_sequence_rx_locked); |                 notification_message(subghz->notifications, &subghs_sequence_rx_locked); | ||||||
|  | |||||||
| @ -72,13 +72,15 @@ const uint32_t bin_raw_value[BIN_RAW_COUNT] = { | |||||||
| uint8_t subghz_scene_receiver_config_next_frequency(const uint32_t value, void* context) { | uint8_t subghz_scene_receiver_config_next_frequency(const uint32_t value, void* context) { | ||||||
|     furi_assert(context); |     furi_assert(context); | ||||||
|     SubGhz* subghz = context; |     SubGhz* subghz = context; | ||||||
|  |     SubGhzSetting* setting = subghz_txrx_get_setting(subghz->txrx); | ||||||
|  | 
 | ||||||
|     uint8_t index = 0; |     uint8_t index = 0; | ||||||
|     for(uint8_t i = 0; i < subghz_setting_get_frequency_count(subghz->setting); i++) { |     for(uint8_t i = 0; i < subghz_setting_get_frequency_count(setting); i++) { | ||||||
|         if(value == subghz_setting_get_frequency(subghz->setting, i)) { |         if(value == subghz_setting_get_frequency(setting, i)) { | ||||||
|             index = i; |             index = i; | ||||||
|             break; |             break; | ||||||
|         } else { |         } else { | ||||||
|             index = subghz_setting_get_frequency_default_index(subghz->setting); |             index = subghz_setting_get_frequency_default_index(setting); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|     return index; |     return index; | ||||||
| @ -87,13 +89,15 @@ uint8_t subghz_scene_receiver_config_next_frequency(const uint32_t value, void* | |||||||
| uint8_t subghz_scene_receiver_config_next_preset(const char* preset_name, void* context) { | uint8_t subghz_scene_receiver_config_next_preset(const char* preset_name, void* context) { | ||||||
|     furi_assert(context); |     furi_assert(context); | ||||||
|     SubGhz* subghz = context; |     SubGhz* subghz = context; | ||||||
|  |     SubGhzSetting* setting = subghz_txrx_get_setting(subghz->txrx); | ||||||
|  | 
 | ||||||
|     uint8_t index = 0; |     uint8_t index = 0; | ||||||
|     for(uint8_t i = 0; i < subghz_setting_get_preset_count(subghz->setting); i++) { |     for(uint8_t i = 0; i < subghz_setting_get_preset_count(setting); i++) { | ||||||
|         if(!strcmp(subghz_setting_get_preset_name(subghz->setting, i), preset_name)) { |         if(!strcmp(subghz_setting_get_preset_name(setting, i), preset_name)) { | ||||||
|             index = i; |             index = i; | ||||||
|             break; |             break; | ||||||
|         } else { |         } else { | ||||||
|             //  index = subghz_setting_get_frequency_default_index(subghz->setting);
 |             //  index = subghz_setting_get_frequency_default_index(subghz_txrx_get_setting(subghz->txrx));
 | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|     return index; |     return index; | ||||||
| @ -122,70 +126,84 @@ uint8_t subghz_scene_receiver_config_hopper_value_index( | |||||||
| static void subghz_scene_receiver_config_set_frequency(VariableItem* item) { | static void subghz_scene_receiver_config_set_frequency(VariableItem* item) { | ||||||
|     SubGhz* subghz = variable_item_get_context(item); |     SubGhz* subghz = variable_item_get_context(item); | ||||||
|     uint8_t index = variable_item_get_current_value_index(item); |     uint8_t index = variable_item_get_current_value_index(item); | ||||||
|  |     SubGhzSetting* setting = subghz_txrx_get_setting(subghz->txrx); | ||||||
| 
 | 
 | ||||||
|     if(subghz->txrx->hopper_state == SubGhzHopperStateOFF) { |     if(subghz_txrx_hopper_get_state(subghz->txrx) == SubGhzHopperStateOFF) { | ||||||
|         char text_buf[10] = {0}; |         char text_buf[10] = {0}; | ||||||
|  |         uint32_t frequency = subghz_setting_get_frequency(setting, index); | ||||||
|  |         SubGhzRadioPreset preset = subghz_txrx_get_preset(subghz->txrx); | ||||||
|  | 
 | ||||||
|         snprintf( |         snprintf( | ||||||
|             text_buf, |             text_buf, | ||||||
|             sizeof(text_buf), |             sizeof(text_buf), | ||||||
|             "%lu.%02lu", |             "%lu.%02lu", | ||||||
|             subghz_setting_get_frequency(subghz->setting, index) / 1000000, |             frequency / 1000000, | ||||||
|             (subghz_setting_get_frequency(subghz->setting, index) % 1000000) / 10000); |             (frequency % 1000000) / 10000); | ||||||
|         variable_item_set_current_value_text(item, text_buf); |         variable_item_set_current_value_text(item, text_buf); | ||||||
|         subghz->txrx->preset->frequency = subghz_setting_get_frequency(subghz->setting, index); |         subghz_txrx_set_preset( | ||||||
|  |             subghz->txrx, | ||||||
|  |             furi_string_get_cstr(preset.name), | ||||||
|  |             frequency, | ||||||
|  |             preset.data, | ||||||
|  |             preset.data_size); | ||||||
|     } else { |     } else { | ||||||
|         variable_item_set_current_value_index( |         variable_item_set_current_value_index( | ||||||
|             item, subghz_setting_get_frequency_default_index(subghz->setting)); |             item, subghz_setting_get_frequency_default_index(setting)); | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static void subghz_scene_receiver_config_set_preset(VariableItem* item) { | static void subghz_scene_receiver_config_set_preset(VariableItem* item) { | ||||||
|     SubGhz* subghz = variable_item_get_context(item); |     SubGhz* subghz = variable_item_get_context(item); | ||||||
|     uint8_t index = variable_item_get_current_value_index(item); |     uint8_t index = variable_item_get_current_value_index(item); | ||||||
|     variable_item_set_current_value_text( |     SubGhzSetting* setting = subghz_txrx_get_setting(subghz->txrx); | ||||||
|         item, subghz_setting_get_preset_name(subghz->setting, index)); | 
 | ||||||
|     subghz_preset_init( |     variable_item_set_current_value_text(item, subghz_setting_get_preset_name(setting, index)); | ||||||
|         subghz, | 
 | ||||||
|         subghz_setting_get_preset_name(subghz->setting, index), |     SubGhzRadioPreset preset = subghz_txrx_get_preset(subghz->txrx); | ||||||
|         subghz->txrx->preset->frequency, |     subghz_txrx_set_preset( | ||||||
|         subghz_setting_get_preset_data(subghz->setting, index), |         subghz->txrx, | ||||||
|         subghz_setting_get_preset_data_size(subghz->setting, index)); |         subghz_setting_get_preset_name(setting, index), | ||||||
|  |         preset.frequency, | ||||||
|  |         subghz_setting_get_preset_data(setting, index), | ||||||
|  |         subghz_setting_get_preset_data_size(setting, index)); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static void subghz_scene_receiver_config_set_hopping_running(VariableItem* item) { | static void subghz_scene_receiver_config_set_hopping_running(VariableItem* item) { | ||||||
|     SubGhz* subghz = variable_item_get_context(item); |     SubGhz* subghz = variable_item_get_context(item); | ||||||
|     uint8_t index = variable_item_get_current_value_index(item); |     uint8_t 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); | ||||||
| 
 | 
 | ||||||
|     variable_item_set_current_value_text(item, hopping_text[index]); |     variable_item_set_current_value_text(item, hopping_text[index]); | ||||||
|     if(hopping_value[index] == SubGhzHopperStateOFF) { |     if(hopping_value[index] == SubGhzHopperStateOFF) { | ||||||
|         char text_buf[10] = {0}; |         char text_buf[10] = {0}; | ||||||
|  |         uint32_t frequency = subghz_setting_get_default_frequency(setting); | ||||||
|  |         SubGhzRadioPreset preset = subghz_txrx_get_preset(subghz->txrx); | ||||||
|  | 
 | ||||||
|         snprintf( |         snprintf( | ||||||
|             text_buf, |             text_buf, | ||||||
|             sizeof(text_buf), |             sizeof(text_buf), | ||||||
|             "%lu.%02lu", |             "%lu.%02lu", | ||||||
|             subghz_setting_get_default_frequency(subghz->setting) / 1000000, |             frequency / 1000000, | ||||||
|             (subghz_setting_get_default_frequency(subghz->setting) % 1000000) / 10000); |             (frequency % 1000000) / 10000); | ||||||
|         variable_item_set_current_value_text( |         variable_item_set_current_value_text(frequency_item, text_buf); | ||||||
|             (VariableItem*)scene_manager_get_scene_state( | 
 | ||||||
|                 subghz->scene_manager, SubGhzSceneReceiverConfig), |         subghz_txrx_set_preset( | ||||||
|             text_buf); |             subghz->txrx, | ||||||
|         subghz->txrx->preset->frequency = subghz_setting_get_default_frequency(subghz->setting); |             furi_string_get_cstr(preset.name), | ||||||
|  |             frequency, | ||||||
|  |             preset.data, | ||||||
|  |             preset.data_size); | ||||||
|         variable_item_set_current_value_index( |         variable_item_set_current_value_index( | ||||||
|             (VariableItem*)scene_manager_get_scene_state( |             frequency_item, subghz_setting_get_frequency_default_index(setting)); | ||||||
|                 subghz->scene_manager, SubGhzSceneReceiverConfig), |  | ||||||
|             subghz_setting_get_frequency_default_index(subghz->setting)); |  | ||||||
|     } else { |     } else { | ||||||
|         variable_item_set_current_value_text( |         variable_item_set_current_value_text(frequency_item, " -----"); | ||||||
|             (VariableItem*)scene_manager_get_scene_state( |  | ||||||
|                 subghz->scene_manager, SubGhzSceneReceiverConfig), |  | ||||||
|             " -----"); |  | ||||||
|         variable_item_set_current_value_index( |         variable_item_set_current_value_index( | ||||||
|             (VariableItem*)scene_manager_get_scene_state( |             frequency_item, subghz_setting_get_frequency_default_index(setting)); | ||||||
|                 subghz->scene_manager, SubGhzSceneReceiverConfig), |  | ||||||
|             subghz_setting_get_frequency_default_index(subghz->setting)); |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     subghz->txrx->hopper_state = hopping_value[index]; |     subghz_txrx_hopper_set_state(subghz->txrx, hopping_value[index]); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static void subghz_scene_receiver_config_set_speaker(VariableItem* item) { | static void subghz_scene_receiver_config_set_speaker(VariableItem* item) { | ||||||
| @ -193,7 +211,7 @@ static void subghz_scene_receiver_config_set_speaker(VariableItem* item) { | |||||||
|     uint8_t index = variable_item_get_current_value_index(item); |     uint8_t index = variable_item_get_current_value_index(item); | ||||||
| 
 | 
 | ||||||
|     variable_item_set_current_value_text(item, speaker_text[index]); |     variable_item_set_current_value_text(item, speaker_text[index]); | ||||||
|     subghz->txrx->speaker_state = speaker_value[index]; |     subghz_txrx_speaker_set_state(subghz->txrx, speaker_value[index]); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static void subghz_scene_receiver_config_set_bin_raw(VariableItem* item) { | static void subghz_scene_receiver_config_set_bin_raw(VariableItem* item) { | ||||||
| @ -201,8 +219,8 @@ static void subghz_scene_receiver_config_set_bin_raw(VariableItem* item) { | |||||||
|     uint8_t index = variable_item_get_current_value_index(item); |     uint8_t index = variable_item_get_current_value_index(item); | ||||||
| 
 | 
 | ||||||
|     variable_item_set_current_value_text(item, bin_raw_text[index]); |     variable_item_set_current_value_text(item, bin_raw_text[index]); | ||||||
|     subghz->txrx->filter = bin_raw_value[index]; |     subghz->filter = bin_raw_value[index]; | ||||||
|     subghz_receiver_set_filter(subghz->txrx->receiver, subghz->txrx->filter); |     subghz_txrx_receiver_set_filter(subghz->txrx, subghz->filter); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static void subghz_scene_receiver_config_set_raw_threshold_rssi(VariableItem* item) { | static void subghz_scene_receiver_config_set_raw_threshold_rssi(VariableItem* item) { | ||||||
| @ -210,7 +228,7 @@ static void subghz_scene_receiver_config_set_raw_threshold_rssi(VariableItem* it | |||||||
|     uint8_t index = variable_item_get_current_value_index(item); |     uint8_t index = variable_item_get_current_value_index(item); | ||||||
| 
 | 
 | ||||||
|     variable_item_set_current_value_text(item, raw_theshold_rssi_text[index]); |     variable_item_set_current_value_text(item, raw_theshold_rssi_text[index]); | ||||||
|     subghz->txrx->raw_threshold_rssi = raw_theshold_rssi_value[index]; |     subghz_threshold_rssi_set(subghz->threshold_rssi, raw_theshold_rssi_value[index]); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static void subghz_scene_receiver_config_var_list_enter_callback(void* context, uint32_t index) { | static void subghz_scene_receiver_config_var_list_enter_callback(void* context, uint32_t index) { | ||||||
| @ -226,25 +244,27 @@ void subghz_scene_receiver_config_on_enter(void* context) { | |||||||
|     SubGhz* subghz = context; |     SubGhz* subghz = context; | ||||||
|     VariableItem* item; |     VariableItem* item; | ||||||
|     uint8_t value_index; |     uint8_t value_index; | ||||||
|  |     SubGhzSetting* setting = subghz_txrx_get_setting(subghz->txrx); | ||||||
|  |     SubGhzRadioPreset preset = subghz_txrx_get_preset(subghz->txrx); | ||||||
| 
 | 
 | ||||||
|     item = variable_item_list_add( |     item = variable_item_list_add( | ||||||
|         subghz->variable_item_list, |         subghz->variable_item_list, | ||||||
|         "Frequency:", |         "Frequency:", | ||||||
|         subghz_setting_get_frequency_count(subghz->setting), |         subghz_setting_get_frequency_count(setting), | ||||||
|         subghz_scene_receiver_config_set_frequency, |         subghz_scene_receiver_config_set_frequency, | ||||||
|         subghz); |         subghz); | ||||||
|     value_index = |     value_index = subghz_scene_receiver_config_next_frequency(preset.frequency, subghz); | ||||||
|         subghz_scene_receiver_config_next_frequency(subghz->txrx->preset->frequency, subghz); |  | ||||||
|     scene_manager_set_scene_state( |     scene_manager_set_scene_state( | ||||||
|         subghz->scene_manager, SubGhzSceneReceiverConfig, (uint32_t)item); |         subghz->scene_manager, SubGhzSceneReceiverConfig, (uint32_t)item); | ||||||
|     variable_item_set_current_value_index(item, value_index); |     variable_item_set_current_value_index(item, value_index); | ||||||
|     char text_buf[10] = {0}; |     char text_buf[10] = {0}; | ||||||
|  |     uint32_t frequency = subghz_setting_get_frequency(setting, value_index); | ||||||
|     snprintf( |     snprintf( | ||||||
|         text_buf, |         text_buf, | ||||||
|         sizeof(text_buf), |         sizeof(text_buf), | ||||||
|         "%lu.%02lu", |         "%lu.%02lu", | ||||||
|         subghz_setting_get_frequency(subghz->setting, value_index) / 1000000, |         frequency / 1000000, | ||||||
|         (subghz_setting_get_frequency(subghz->setting, value_index) % 1000000) / 10000); |         (frequency % 1000000) / 10000); | ||||||
|     variable_item_set_current_value_text(item, text_buf); |     variable_item_set_current_value_text(item, text_buf); | ||||||
| 
 | 
 | ||||||
|     if(scene_manager_get_scene_state(subghz->scene_manager, SubGhzSceneReadRAW) != |     if(scene_manager_get_scene_state(subghz->scene_manager, SubGhzSceneReadRAW) != | ||||||
| @ -256,7 +276,7 @@ void subghz_scene_receiver_config_on_enter(void* context) { | |||||||
|             subghz_scene_receiver_config_set_hopping_running, |             subghz_scene_receiver_config_set_hopping_running, | ||||||
|             subghz); |             subghz); | ||||||
|         value_index = subghz_scene_receiver_config_hopper_value_index( |         value_index = subghz_scene_receiver_config_hopper_value_index( | ||||||
|             subghz->txrx->hopper_state, hopping_value, HOPPING_COUNT, subghz); |             subghz_txrx_hopper_get_state(subghz->txrx), hopping_value, HOPPING_COUNT, subghz); | ||||||
|         variable_item_set_current_value_index(item, value_index); |         variable_item_set_current_value_index(item, value_index); | ||||||
|         variable_item_set_current_value_text(item, hopping_text[value_index]); |         variable_item_set_current_value_text(item, hopping_text[value_index]); | ||||||
|     } |     } | ||||||
| @ -264,14 +284,14 @@ void subghz_scene_receiver_config_on_enter(void* context) { | |||||||
|     item = variable_item_list_add( |     item = variable_item_list_add( | ||||||
|         subghz->variable_item_list, |         subghz->variable_item_list, | ||||||
|         "Modulation:", |         "Modulation:", | ||||||
|         subghz_setting_get_preset_count(subghz->setting), |         subghz_setting_get_preset_count(setting), | ||||||
|         subghz_scene_receiver_config_set_preset, |         subghz_scene_receiver_config_set_preset, | ||||||
|         subghz); |         subghz); | ||||||
|     value_index = subghz_scene_receiver_config_next_preset( |     value_index = | ||||||
|         furi_string_get_cstr(subghz->txrx->preset->name), subghz); |         subghz_scene_receiver_config_next_preset(furi_string_get_cstr(preset.name), subghz); | ||||||
|     variable_item_set_current_value_index(item, value_index); |     variable_item_set_current_value_index(item, value_index); | ||||||
|     variable_item_set_current_value_text( |     variable_item_set_current_value_text( | ||||||
|         item, subghz_setting_get_preset_name(subghz->setting, value_index)); |         item, subghz_setting_get_preset_name(setting, value_index)); | ||||||
| 
 | 
 | ||||||
|     if(scene_manager_get_scene_state(subghz->scene_manager, SubGhzSceneReadRAW) != |     if(scene_manager_get_scene_state(subghz->scene_manager, SubGhzSceneReadRAW) != | ||||||
|        SubGhzCustomEventManagerSet) { |        SubGhzCustomEventManagerSet) { | ||||||
| @ -281,7 +301,7 @@ void subghz_scene_receiver_config_on_enter(void* context) { | |||||||
|             BIN_RAW_COUNT, |             BIN_RAW_COUNT, | ||||||
|             subghz_scene_receiver_config_set_bin_raw, |             subghz_scene_receiver_config_set_bin_raw, | ||||||
|             subghz); |             subghz); | ||||||
|         value_index = value_index_uint32(subghz->txrx->filter, bin_raw_value, BIN_RAW_COUNT); |         value_index = value_index_uint32(subghz->filter, bin_raw_value, BIN_RAW_COUNT); | ||||||
|         variable_item_set_current_value_index(item, value_index); |         variable_item_set_current_value_index(item, value_index); | ||||||
|         variable_item_set_current_value_text(item, bin_raw_text[value_index]); |         variable_item_set_current_value_text(item, bin_raw_text[value_index]); | ||||||
|     } |     } | ||||||
| @ -292,7 +312,8 @@ void subghz_scene_receiver_config_on_enter(void* context) { | |||||||
|         SPEAKER_COUNT, |         SPEAKER_COUNT, | ||||||
|         subghz_scene_receiver_config_set_speaker, |         subghz_scene_receiver_config_set_speaker, | ||||||
|         subghz); |         subghz); | ||||||
|     value_index = value_index_uint32(subghz->txrx->speaker_state, speaker_value, SPEAKER_COUNT); |     value_index = value_index_uint32( | ||||||
|  |         subghz_txrx_speaker_get_state(subghz->txrx), speaker_value, SPEAKER_COUNT); | ||||||
|     variable_item_set_current_value_index(item, value_index); |     variable_item_set_current_value_index(item, value_index); | ||||||
|     variable_item_set_current_value_text(item, speaker_text[value_index]); |     variable_item_set_current_value_text(item, speaker_text[value_index]); | ||||||
| 
 | 
 | ||||||
| @ -313,7 +334,9 @@ void subghz_scene_receiver_config_on_enter(void* context) { | |||||||
|             subghz_scene_receiver_config_set_raw_threshold_rssi, |             subghz_scene_receiver_config_set_raw_threshold_rssi, | ||||||
|             subghz); |             subghz); | ||||||
|         value_index = value_index_float( |         value_index = value_index_float( | ||||||
|             subghz->txrx->raw_threshold_rssi, raw_theshold_rssi_value, RAW_THRESHOLD_RSSI_COUNT); |             subghz_threshold_rssi_get(subghz->threshold_rssi), | ||||||
|  |             raw_theshold_rssi_value, | ||||||
|  |             RAW_THRESHOLD_RSSI_COUNT); | ||||||
|         variable_item_set_current_value_index(item, value_index); |         variable_item_set_current_value_index(item, value_index); | ||||||
|         variable_item_set_current_value_text(item, raw_theshold_rssi_text[value_index]); |         variable_item_set_current_value_text(item, raw_theshold_rssi_text[value_index]); | ||||||
|     } |     } | ||||||
| @ -326,7 +349,7 @@ bool subghz_scene_receiver_config_on_event(void* context, SceneManagerEvent even | |||||||
| 
 | 
 | ||||||
|     if(event.type == SceneManagerEventTypeCustom) { |     if(event.type == SceneManagerEventTypeCustom) { | ||||||
|         if(event.event == SubGhzCustomEventSceneSettingLock) { |         if(event.event == SubGhzCustomEventSceneSettingLock) { | ||||||
|             subghz->lock = SubGhzLockOn; |             subghz_lock(subghz); | ||||||
|             scene_manager_previous_scene(subghz->scene_manager); |             scene_manager_previous_scene(subghz->scene_manager); | ||||||
|             consumed = true; |             consumed = true; | ||||||
|         } |         } | ||||||
|  | |||||||
| @ -19,20 +19,19 @@ void subghz_scene_receiver_info_callback(GuiButtonType result, InputType type, v | |||||||
| 
 | 
 | ||||||
| static bool subghz_scene_receiver_info_update_parser(void* context) { | static bool subghz_scene_receiver_info_update_parser(void* context) { | ||||||
|     SubGhz* subghz = context; |     SubGhz* subghz = context; | ||||||
|     subghz->txrx->decoder_result = subghz_receiver_search_decoder_base_by_name( |  | ||||||
|         subghz->txrx->receiver, |  | ||||||
|         subghz_history_get_protocol_name(subghz->txrx->history, subghz->txrx->idx_menu_chosen)); |  | ||||||
| 
 | 
 | ||||||
|     if(subghz->txrx->decoder_result) { |     if(subghz_txrx_load_decoder_by_name_protocol( | ||||||
|  |            subghz->txrx, | ||||||
|  |            subghz_history_get_protocol_name(subghz->history, subghz->idx_menu_chosen))) { | ||||||
|         //todo we are trying to deserialize without checking for errors, since it is assumed that we just received this chignal
 |         //todo we are trying to deserialize without checking for errors, since it is assumed that we just received this chignal
 | ||||||
|         subghz_protocol_decoder_base_deserialize( |         subghz_protocol_decoder_base_deserialize( | ||||||
|             subghz->txrx->decoder_result, |             subghz_txrx_get_decoder(subghz->txrx), | ||||||
|             subghz_history_get_raw_data(subghz->txrx->history, subghz->txrx->idx_menu_chosen)); |             subghz_history_get_raw_data(subghz->history, subghz->idx_menu_chosen)); | ||||||
| 
 | 
 | ||||||
|         SubGhzRadioPreset* preset = |         SubGhzRadioPreset* preset = | ||||||
|             subghz_history_get_radio_preset(subghz->txrx->history, subghz->txrx->idx_menu_chosen); |             subghz_history_get_radio_preset(subghz->history, subghz->idx_menu_chosen); | ||||||
|         subghz_preset_init( |         subghz_txrx_set_preset( | ||||||
|             subghz, |             subghz->txrx, | ||||||
|             furi_string_get_cstr(preset->name), |             furi_string_get_cstr(preset->name), | ||||||
|             preset->frequency, |             preset->frequency, | ||||||
|             preset->data, |             preset->data, | ||||||
| @ -47,15 +46,11 @@ void subghz_scene_receiver_info_on_enter(void* context) { | |||||||
|     SubGhz* subghz = context; |     SubGhz* subghz = context; | ||||||
| 
 | 
 | ||||||
|     if(subghz_scene_receiver_info_update_parser(subghz)) { |     if(subghz_scene_receiver_info_update_parser(subghz)) { | ||||||
|         FuriString* frequency_str; |         FuriString* frequency_str = furi_string_alloc(); | ||||||
|         FuriString* modulation_str; |         FuriString* modulation_str = furi_string_alloc(); | ||||||
|         FuriString* text; |         FuriString* text = furi_string_alloc(); | ||||||
| 
 | 
 | ||||||
|         frequency_str = furi_string_alloc(); |         subghz_txrx_get_frequency_and_modulation(subghz->txrx, frequency_str, modulation_str); | ||||||
|         modulation_str = furi_string_alloc(); |  | ||||||
|         text = furi_string_alloc(); |  | ||||||
| 
 |  | ||||||
|         subghz_get_frequency_modulation(subghz, frequency_str, modulation_str); |  | ||||||
|         widget_add_string_element( |         widget_add_string_element( | ||||||
|             subghz->widget, |             subghz->widget, | ||||||
|             78, |             78, | ||||||
| @ -73,7 +68,7 @@ void subghz_scene_receiver_info_on_enter(void* context) { | |||||||
|             AlignTop, |             AlignTop, | ||||||
|             FontSecondary, |             FontSecondary, | ||||||
|             furi_string_get_cstr(modulation_str)); |             furi_string_get_cstr(modulation_str)); | ||||||
|         subghz_protocol_decoder_base_get_string(subghz->txrx->decoder_result, text); |         subghz_protocol_decoder_base_get_string(subghz_txrx_get_decoder(subghz->txrx), text); | ||||||
|         widget_add_string_multiline_element( |         widget_add_string_multiline_element( | ||||||
|             subghz->widget, 0, 0, AlignLeft, AlignTop, FontSecondary, furi_string_get_cstr(text)); |             subghz->widget, 0, 0, AlignLeft, AlignTop, FontSecondary, furi_string_get_cstr(text)); | ||||||
| 
 | 
 | ||||||
| @ -81,8 +76,7 @@ void subghz_scene_receiver_info_on_enter(void* context) { | |||||||
|         furi_string_free(modulation_str); |         furi_string_free(modulation_str); | ||||||
|         furi_string_free(text); |         furi_string_free(text); | ||||||
| 
 | 
 | ||||||
|         if((subghz->txrx->decoder_result->protocol->flag & SubGhzProtocolFlag_Save) == |         if(subghz_txrx_protocol_is_serializable(subghz->txrx)) { | ||||||
|            SubGhzProtocolFlag_Save) { |  | ||||||
|             widget_add_button_element( |             widget_add_button_element( | ||||||
|                 subghz->widget, |                 subghz->widget, | ||||||
|                 GuiButtonTypeRight, |                 GuiButtonTypeRight, | ||||||
| @ -90,10 +84,7 @@ void subghz_scene_receiver_info_on_enter(void* context) { | |||||||
|                 subghz_scene_receiver_info_callback, |                 subghz_scene_receiver_info_callback, | ||||||
|                 subghz); |                 subghz); | ||||||
|         } |         } | ||||||
|         if(((subghz->txrx->decoder_result->protocol->flag & SubGhzProtocolFlag_Send) == |         if(subghz_txrx_protocol_is_transmittable(subghz->txrx, true)) { | ||||||
|             SubGhzProtocolFlag_Send) && |  | ||||||
|            subghz->txrx->decoder_result->protocol->encoder->deserialize && |  | ||||||
|            subghz->txrx->decoder_result->protocol->type == SubGhzProtocolTypeStatic) { |  | ||||||
|             widget_add_button_element( |             widget_add_button_element( | ||||||
|                 subghz->widget, |                 subghz->widget, | ||||||
|                 GuiButtonTypeCenter, |                 GuiButtonTypeCenter, | ||||||
| @ -114,82 +105,49 @@ bool subghz_scene_receiver_info_on_event(void* context, SceneManagerEvent event) | |||||||
|     SubGhz* subghz = context; |     SubGhz* subghz = context; | ||||||
|     if(event.type == SceneManagerEventTypeCustom) { |     if(event.type == SceneManagerEventTypeCustom) { | ||||||
|         if(event.event == SubGhzCustomEventSceneReceiverInfoTxStart) { |         if(event.event == SubGhzCustomEventSceneReceiverInfoTxStart) { | ||||||
|             //CC1101 Stop RX -> Start TX
 |  | ||||||
|             if(subghz->txrx->hopper_state != SubGhzHopperStateOFF) { |  | ||||||
|                 subghz->txrx->hopper_state = SubGhzHopperStatePause; |  | ||||||
|             } |  | ||||||
|             if(subghz->txrx->txrx_state == SubGhzTxRxStateRx) { |  | ||||||
|                 subghz_rx_end(subghz); |  | ||||||
|             } |  | ||||||
|             if(!subghz_scene_receiver_info_update_parser(subghz)) { |             if(!subghz_scene_receiver_info_update_parser(subghz)) { | ||||||
|                 return false; |                 return false; | ||||||
|             } |             } | ||||||
|             if(subghz->txrx->txrx_state == SubGhzTxRxStateIDLE || |             //CC1101 Stop RX -> Start TX
 | ||||||
|                subghz->txrx->txrx_state == SubGhzTxRxStateSleep) { |             subghz_txrx_hopper_pause(subghz->txrx); | ||||||
|             if(!subghz_tx_start( |             if(!subghz_tx_start( | ||||||
|                    subghz, |                    subghz, | ||||||
|                        subghz_history_get_raw_data( |                    subghz_history_get_raw_data(subghz->history, subghz->idx_menu_chosen))) { | ||||||
|                            subghz->txrx->history, subghz->txrx->idx_menu_chosen))) { |                 subghz_txrx_rx_start(subghz->txrx); | ||||||
|                     if(subghz->txrx->txrx_state == SubGhzTxRxStateTx) { |                 subghz_txrx_hopper_unpause(subghz->txrx); | ||||||
|                         subghz_tx_stop(subghz); |  | ||||||
|                     } |  | ||||||
|                     if(subghz->txrx->txrx_state == SubGhzTxRxStateIDLE) { |  | ||||||
|                         subghz_begin( |  | ||||||
|                             subghz, |  | ||||||
|                             subghz_setting_get_preset_data_by_name( |  | ||||||
|                                 subghz->setting, |  | ||||||
|                                 furi_string_get_cstr(subghz->txrx->preset->name))); |  | ||||||
|                         subghz_rx(subghz, subghz->txrx->preset->frequency); |  | ||||||
|                     } |  | ||||||
|                     if(subghz->txrx->hopper_state == SubGhzHopperStatePause) { |  | ||||||
|                         subghz->txrx->hopper_state = SubGhzHopperStateRunnig; |  | ||||||
|                     } |  | ||||||
|                 subghz->state_notifications = SubGhzNotificationStateRx; |                 subghz->state_notifications = SubGhzNotificationStateRx; | ||||||
|             } else { |             } else { | ||||||
|                 subghz->state_notifications = SubGhzNotificationStateTx; |                 subghz->state_notifications = SubGhzNotificationStateTx; | ||||||
|             } |             } | ||||||
|             } |  | ||||||
|             return true; |             return true; | ||||||
|         } else if(event.event == SubGhzCustomEventSceneReceiverInfoTxStop) { |         } else if(event.event == SubGhzCustomEventSceneReceiverInfoTxStop) { | ||||||
|             //CC1101 Stop Tx -> Start RX
 |             //CC1101 Stop Tx -> Start RX
 | ||||||
|             subghz->state_notifications = SubGhzNotificationStateIDLE; |             subghz->state_notifications = SubGhzNotificationStateIDLE; | ||||||
|             if(subghz->txrx->txrx_state == SubGhzTxRxStateTx) { | 
 | ||||||
|                 subghz_tx_stop(subghz); |             subghz_txrx_rx_start(subghz->txrx); | ||||||
|             } | 
 | ||||||
|             if(subghz->txrx->txrx_state == SubGhzTxRxStateIDLE) { |             subghz_txrx_hopper_unpause(subghz->txrx); | ||||||
|                 subghz_begin( |  | ||||||
|                     subghz, |  | ||||||
|                     subghz_setting_get_preset_data_by_name( |  | ||||||
|                         subghz->setting, furi_string_get_cstr(subghz->txrx->preset->name))); |  | ||||||
|                 subghz_rx(subghz, subghz->txrx->preset->frequency); |  | ||||||
|             } |  | ||||||
|             if(subghz->txrx->hopper_state == SubGhzHopperStatePause) { |  | ||||||
|                 subghz->txrx->hopper_state = SubGhzHopperStateRunnig; |  | ||||||
|             } |  | ||||||
|             subghz->state_notifications = SubGhzNotificationStateRx; |             subghz->state_notifications = SubGhzNotificationStateRx; | ||||||
|             return true; |             return true; | ||||||
|         } else if(event.event == SubGhzCustomEventSceneReceiverInfoSave) { |         } else if(event.event == SubGhzCustomEventSceneReceiverInfoSave) { | ||||||
|             //CC1101 Stop RX -> Save
 |             //CC1101 Stop RX -> Save
 | ||||||
|             subghz->state_notifications = SubGhzNotificationStateIDLE; |             subghz->state_notifications = SubGhzNotificationStateIDLE; | ||||||
|             subghz->txrx->hopper_state = SubGhzHopperStateOFF; |             subghz_txrx_hopper_set_state(subghz->txrx, SubGhzHopperStateOFF); | ||||||
|             if(subghz->txrx->txrx_state == SubGhzTxRxStateRx) { | 
 | ||||||
|                 subghz_rx_end(subghz); |             subghz_txrx_stop(subghz->txrx); | ||||||
|                 subghz_sleep(subghz); |  | ||||||
|             } |  | ||||||
|             if(!subghz_scene_receiver_info_update_parser(subghz)) { |             if(!subghz_scene_receiver_info_update_parser(subghz)) { | ||||||
|                 return false; |                 return false; | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|             if((subghz->txrx->decoder_result->protocol->flag & SubGhzProtocolFlag_Save) == |             if(subghz_txrx_protocol_is_serializable(subghz->txrx)) { | ||||||
|                SubGhzProtocolFlag_Save) { |  | ||||||
|                 subghz_file_name_clear(subghz); |                 subghz_file_name_clear(subghz); | ||||||
|                 scene_manager_next_scene(subghz->scene_manager, SubGhzSceneSaveName); |                 scene_manager_next_scene(subghz->scene_manager, SubGhzSceneSaveName); | ||||||
|             } |             } | ||||||
|             return true; |             return true; | ||||||
|         } |         } | ||||||
|     } else if(event.type == SceneManagerEventTypeTick) { |     } else if(event.type == SceneManagerEventTypeTick) { | ||||||
|         if(subghz->txrx->hopper_state != SubGhzHopperStateOFF) { |         if(subghz_txrx_hopper_get_state(subghz->txrx) != SubGhzHopperStateOFF) { | ||||||
|             subghz_hopper_update(subghz); |             subghz_txrx_hopper_update(subghz->txrx); | ||||||
|         } |         } | ||||||
|         switch(subghz->state_notifications) { |         switch(subghz->state_notifications) { | ||||||
|         case SubGhzNotificationStateTx: |         case SubGhzNotificationStateTx: | ||||||
|  | |||||||
| @ -5,8 +5,7 @@ | |||||||
| void subghz_scene_region_info_on_enter(void* context) { | void subghz_scene_region_info_on_enter(void* context) { | ||||||
|     SubGhz* subghz = context; |     SubGhz* subghz = context; | ||||||
|     const FuriHalRegion* const region = furi_hal_region_get(); |     const FuriHalRegion* const region = furi_hal_region_get(); | ||||||
|     FuriString* buffer; |     FuriString* buffer = furi_string_alloc(); | ||||||
|     buffer = furi_string_alloc(); |  | ||||||
|     if(region) { |     if(region) { | ||||||
|         furi_string_cat_printf(buffer, "Region: %s,  bands:\n", region->country_code); |         furi_string_cat_printf(buffer, "Region: %s,  bands:\n", region->country_code); | ||||||
|         for(uint16_t i = 0; i < region->bands_count; ++i) { |         for(uint16_t i = 0; i < region->bands_count; ++i) { | ||||||
|  | |||||||
| @ -3,6 +3,7 @@ | |||||||
| typedef enum { | typedef enum { | ||||||
|     SubGhzRpcStateIdle, |     SubGhzRpcStateIdle, | ||||||
|     SubGhzRpcStateLoaded, |     SubGhzRpcStateLoaded, | ||||||
|  |     SubGhzRpcStateTx, | ||||||
| } SubGhzRpcState; | } SubGhzRpcState; | ||||||
| 
 | 
 | ||||||
| void subghz_scene_rpc_on_enter(void* context) { | void subghz_scene_rpc_on_enter(void* context) { | ||||||
| @ -38,9 +39,9 @@ bool subghz_scene_rpc_on_event(void* context, SceneManagerEvent event) { | |||||||
|             view_dispatcher_stop(subghz->view_dispatcher); |             view_dispatcher_stop(subghz->view_dispatcher); | ||||||
|         } else if(event.event == SubGhzCustomEventSceneRpcButtonPress) { |         } else if(event.event == SubGhzCustomEventSceneRpcButtonPress) { | ||||||
|             bool result = false; |             bool result = false; | ||||||
|             if((subghz->txrx->txrx_state == SubGhzTxRxStateSleep) && |             if((state == SubGhzRpcStateLoaded)) { | ||||||
|                (state == SubGhzRpcStateLoaded)) { |                 result = subghz_tx_start(subghz, subghz_txrx_get_fff_data(subghz->txrx)); | ||||||
|                 result = subghz_tx_start(subghz, subghz->txrx->fff_data); |                 state = SubGhzRpcStateTx; | ||||||
|                 if(result) subghz_blink_start(subghz); |                 if(result) subghz_blink_start(subghz); | ||||||
|             } |             } | ||||||
|             if(!result) { |             if(!result) { | ||||||
| @ -52,10 +53,10 @@ bool subghz_scene_rpc_on_event(void* context, SceneManagerEvent event) { | |||||||
|             rpc_system_app_confirm(subghz->rpc_ctx, RpcAppEventButtonPress, result); |             rpc_system_app_confirm(subghz->rpc_ctx, RpcAppEventButtonPress, result); | ||||||
|         } else if(event.event == SubGhzCustomEventSceneRpcButtonRelease) { |         } else if(event.event == SubGhzCustomEventSceneRpcButtonRelease) { | ||||||
|             bool result = false; |             bool result = false; | ||||||
|             if(subghz->txrx->txrx_state == SubGhzTxRxStateTx) { |             if(state == SubGhzRpcStateTx) { | ||||||
|  |                 subghz_txrx_stop(subghz->txrx); | ||||||
|                 subghz_blink_stop(subghz); |                 subghz_blink_stop(subghz); | ||||||
|                 subghz_tx_stop(subghz); |                 state = SubGhzRpcStateIdle; | ||||||
|                 subghz_sleep(subghz); |  | ||||||
|                 result = true; |                 result = true; | ||||||
|             } |             } | ||||||
|             rpc_system_app_confirm(subghz->rpc_ctx, RpcAppEventButtonRelease, result); |             rpc_system_app_confirm(subghz->rpc_ctx, RpcAppEventButtonRelease, result); | ||||||
| @ -93,10 +94,9 @@ bool subghz_scene_rpc_on_event(void* context, SceneManagerEvent event) { | |||||||
| 
 | 
 | ||||||
| void subghz_scene_rpc_on_exit(void* context) { | void subghz_scene_rpc_on_exit(void* context) { | ||||||
|     SubGhz* subghz = context; |     SubGhz* subghz = context; | ||||||
| 
 |     SubGhzRpcState state = scene_manager_get_scene_state(subghz->scene_manager, SubGhzSceneRpc); | ||||||
|     if(subghz->txrx->txrx_state == SubGhzTxRxStateTx) { |     if(state != SubGhzRpcStateIdle) { | ||||||
|         subghz_tx_stop(subghz); |         subghz_txrx_stop(subghz->txrx); | ||||||
|         subghz_sleep(subghz); |  | ||||||
|         subghz_blink_stop(subghz); |         subghz_blink_stop(subghz); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -35,10 +35,8 @@ void subghz_scene_save_name_on_enter(void* context) { | |||||||
|     TextInput* text_input = subghz->text_input; |     TextInput* text_input = subghz->text_input; | ||||||
|     bool dev_name_empty = false; |     bool dev_name_empty = false; | ||||||
| 
 | 
 | ||||||
|     FuriString* file_name; |     FuriString* file_name = furi_string_alloc(); | ||||||
|     FuriString* dir_name; |     FuriString* dir_name = furi_string_alloc(); | ||||||
|     file_name = furi_string_alloc(); |  | ||||||
|     dir_name = furi_string_alloc(); |  | ||||||
| 
 | 
 | ||||||
|     if(!subghz_path_is_file(subghz->file_path)) { |     if(!subghz_path_is_file(subghz->file_path)) { | ||||||
|         char file_name_buf[SUBGHZ_MAX_LEN_NAME] = {0}; |         char file_name_buf[SUBGHZ_MAX_LEN_NAME] = {0}; | ||||||
| @ -69,7 +67,7 @@ void subghz_scene_save_name_on_enter(void* context) { | |||||||
|         subghz_scene_save_name_text_input_callback, |         subghz_scene_save_name_text_input_callback, | ||||||
|         subghz, |         subghz, | ||||||
|         subghz->file_name_tmp, |         subghz->file_name_tmp, | ||||||
|         MAX_TEXT_INPUT_LEN, // buffer size
 |         MAX_TEXT_INPUT_LEN, | ||||||
|         dev_name_empty); |         dev_name_empty); | ||||||
| 
 | 
 | ||||||
|     ValidatorIsFile* validator_is_file = validator_is_file_alloc_init( |     ValidatorIsFile* validator_is_file = validator_is_file_alloc_init( | ||||||
| @ -106,7 +104,7 @@ bool subghz_scene_save_name_on_event(void* context, SceneManagerEvent event) { | |||||||
|                        SubGhzCustomEventManagerNoSet) { |                        SubGhzCustomEventManagerNoSet) { | ||||||
|                         subghz_save_protocol_to_file( |                         subghz_save_protocol_to_file( | ||||||
|                             subghz, |                             subghz, | ||||||
|                             subghz->txrx->fff_data, |                             subghz_txrx_get_fff_data(subghz->txrx), | ||||||
|                             furi_string_get_cstr(subghz->file_path)); |                             furi_string_get_cstr(subghz->file_path)); | ||||||
|                         scene_manager_set_scene_state( |                         scene_manager_set_scene_state( | ||||||
|                             subghz->scene_manager, |                             subghz->scene_manager, | ||||||
| @ -115,8 +113,7 @@ bool subghz_scene_save_name_on_event(void* context, SceneManagerEvent event) { | |||||||
|                     } else { |                     } else { | ||||||
|                         subghz_save_protocol_to_file( |                         subghz_save_protocol_to_file( | ||||||
|                             subghz, |                             subghz, | ||||||
|                             subghz_history_get_raw_data( |                             subghz_history_get_raw_data(subghz->history, subghz->idx_menu_chosen), | ||||||
|                                 subghz->txrx->history, subghz->txrx->idx_menu_chosen), |  | ||||||
|                             furi_string_get_cstr(subghz->file_path)); |                             furi_string_get_cstr(subghz->file_path)); | ||||||
|                     } |                     } | ||||||
|                 } |                 } | ||||||
| @ -124,7 +121,8 @@ bool subghz_scene_save_name_on_event(void* context, SceneManagerEvent event) { | |||||||
|                 if(scene_manager_get_scene_state(subghz->scene_manager, SubGhzSceneReadRAW) != |                 if(scene_manager_get_scene_state(subghz->scene_manager, SubGhzSceneReadRAW) != | ||||||
|                    SubGhzCustomEventManagerNoSet) { |                    SubGhzCustomEventManagerNoSet) { | ||||||
|                     subghz_protocol_raw_gen_fff_data( |                     subghz_protocol_raw_gen_fff_data( | ||||||
|                         subghz->txrx->fff_data, furi_string_get_cstr(subghz->file_path)); |                         subghz_txrx_get_fff_data(subghz->txrx), | ||||||
|  |                         furi_string_get_cstr(subghz->file_path)); | ||||||
|                     scene_manager_set_scene_state( |                     scene_manager_set_scene_state( | ||||||
|                         subghz->scene_manager, SubGhzSceneReadRAW, SubGhzCustomEventManagerNoSet); |                         subghz->scene_manager, SubGhzSceneReadRAW, SubGhzCustomEventManagerNoSet); | ||||||
|                 } else { |                 } else { | ||||||
|  | |||||||
| @ -26,10 +26,10 @@ bool subghz_scene_save_success_on_event(void* context, SceneManagerEvent event) | |||||||
|         if(event.event == SubGhzCustomEventSceneSaveSuccess) { |         if(event.event == SubGhzCustomEventSceneSaveSuccess) { | ||||||
|             if(!scene_manager_search_and_switch_to_previous_scene( |             if(!scene_manager_search_and_switch_to_previous_scene( | ||||||
|                    subghz->scene_manager, SubGhzSceneReceiver)) { |                    subghz->scene_manager, SubGhzSceneReceiver)) { | ||||||
|                 subghz->txrx->rx_key_state = SubGhzRxKeyStateRAWSave; |                 subghz_rx_key_state_set(subghz, SubGhzRxKeyStateRAWSave); | ||||||
|                 if(!scene_manager_search_and_switch_to_previous_scene( |                 if(!scene_manager_search_and_switch_to_previous_scene( | ||||||
|                        subghz->scene_manager, SubGhzSceneReadRAW)) { |                        subghz->scene_manager, SubGhzSceneReadRAW)) { | ||||||
|                     subghz->txrx->rx_key_state = SubGhzRxKeyStateIDLE; |                     subghz_rx_key_state_set(subghz, SubGhzRxKeyStateIDLE); | ||||||
|                     if(!scene_manager_search_and_switch_to_previous_scene( |                     if(!scene_manager_search_and_switch_to_previous_scene( | ||||||
|                            subghz->scene_manager, SubGhzSceneSaved)) { |                            subghz->scene_manager, SubGhzSceneSaved)) { | ||||||
|                         scene_manager_next_scene(subghz->scene_manager, SubGhzSceneSaved); |                         scene_manager_next_scene(subghz->scene_manager, SubGhzSceneSaved); | ||||||
|  | |||||||
| @ -4,8 +4,8 @@ void subghz_scene_saved_on_enter(void* context) { | |||||||
|     SubGhz* subghz = context; |     SubGhz* subghz = context; | ||||||
| 
 | 
 | ||||||
|     if(subghz_load_protocol_from_file(subghz)) { |     if(subghz_load_protocol_from_file(subghz)) { | ||||||
|         if((!strcmp(subghz->txrx->decoder_result->protocol->name, "RAW"))) { |         if(subghz_get_load_type_file(subghz) == SubGhzLoadTypeFileRaw) { | ||||||
|             subghz->txrx->rx_key_state = SubGhzRxKeyStateRAWLoad; |             subghz_rx_key_state_set(subghz, SubGhzRxKeyStateRAWLoad); | ||||||
|             scene_manager_next_scene(subghz->scene_manager, SubGhzSceneReadRAW); |             scene_manager_next_scene(subghz->scene_manager, SubGhzSceneReadRAW); | ||||||
|         } else { |         } else { | ||||||
|             scene_manager_next_scene(subghz->scene_manager, SubGhzSceneSavedMenu); |             scene_manager_next_scene(subghz->scene_manager, SubGhzSceneSavedMenu); | ||||||
|  | |||||||
| @ -1,63 +1,10 @@ | |||||||
| #include "../subghz_i.h" | #include "../subghz_i.h" | ||||||
| #include <lib/subghz/protocols/keeloq.h> | #include "../helpers/subghz_txrx_create_protocol_key.h" | ||||||
| #include <lib/subghz/protocols/secplus_v1.h> |  | ||||||
| #include <lib/subghz/protocols/secplus_v2.h> |  | ||||||
| #include <lib/subghz/blocks/math.h> | #include <lib/subghz/blocks/math.h> | ||||||
| #include <flipper_format/flipper_format_i.h> |  | ||||||
| #include <lib/toolbox/stream/stream.h> |  | ||||||
| #include <lib/subghz/protocols/protocol_items.h> | #include <lib/subghz/protocols/protocol_items.h> | ||||||
| 
 | 
 | ||||||
| #define TAG "SubGhzSetType" | #define TAG "SubGhzSetType" | ||||||
| 
 | 
 | ||||||
| bool subghz_scene_set_type_submenu_gen_data_protocol( |  | ||||||
|     void* context, |  | ||||||
|     const char* protocol_name, |  | ||||||
|     uint64_t key, |  | ||||||
|     uint32_t bit, |  | ||||||
|     uint32_t frequency, |  | ||||||
|     const char* preset_name) { |  | ||||||
|     furi_assert(context); |  | ||||||
|     SubGhz* subghz = context; |  | ||||||
| 
 |  | ||||||
|     bool res = false; |  | ||||||
| 
 |  | ||||||
|     subghz_preset_init(subghz, preset_name, frequency, NULL, 0); |  | ||||||
|     subghz->txrx->decoder_result = |  | ||||||
|         subghz_receiver_search_decoder_base_by_name(subghz->txrx->receiver, protocol_name); |  | ||||||
| 
 |  | ||||||
|     if(subghz->txrx->decoder_result == NULL) { |  | ||||||
|         furi_string_set(subghz->error_str, "Protocol not\nfound!"); |  | ||||||
|         scene_manager_next_scene(subghz->scene_manager, SubGhzSceneShowErrorSub); |  | ||||||
|         return false; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     do { |  | ||||||
|         Stream* fff_data_stream = flipper_format_get_raw_stream(subghz->txrx->fff_data); |  | ||||||
|         stream_clean(fff_data_stream); |  | ||||||
|         if(subghz_protocol_decoder_base_serialize( |  | ||||||
|                subghz->txrx->decoder_result, subghz->txrx->fff_data, subghz->txrx->preset) != |  | ||||||
|            SubGhzProtocolStatusOk) { |  | ||||||
|             FURI_LOG_E(TAG, "Unable to serialize"); |  | ||||||
|             break; |  | ||||||
|         } |  | ||||||
|         if(!flipper_format_update_uint32(subghz->txrx->fff_data, "Bit", &bit, 1)) { |  | ||||||
|             FURI_LOG_E(TAG, "Unable to update 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] = (key >> (i * 8)) & 0xFF; |  | ||||||
|         } |  | ||||||
|         if(!flipper_format_update_hex(subghz->txrx->fff_data, "Key", key_data, sizeof(uint64_t))) { |  | ||||||
|             FURI_LOG_E(TAG, "Unable to update Key"); |  | ||||||
|             break; |  | ||||||
|         } |  | ||||||
|         res = true; |  | ||||||
|     } while(false); |  | ||||||
|     return res; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void subghz_scene_set_type_submenu_callback(void* context, uint32_t index) { | void subghz_scene_set_type_submenu_callback(void* context, uint32_t index) { | ||||||
|     SubGhz* subghz = context; |     SubGhz* subghz = context; | ||||||
|     view_dispatcher_send_custom_event(subghz->view_dispatcher, index); |     view_dispatcher_send_custom_event(subghz->view_dispatcher, index); | ||||||
| @ -69,7 +16,13 @@ void subghz_scene_set_type_on_enter(void* context) { | |||||||
|     submenu_add_item( |     submenu_add_item( | ||||||
|         subghz->submenu, |         subghz->submenu, | ||||||
|         "Princeton_433", |         "Princeton_433", | ||||||
|         SubmenuIndexPricenton, |         SubmenuIndexPricenton_433, | ||||||
|  |         subghz_scene_set_type_submenu_callback, | ||||||
|  |         subghz); | ||||||
|  |     submenu_add_item( | ||||||
|  |         subghz->submenu, | ||||||
|  |         "Princeton_315", | ||||||
|  |         SubmenuIndexPricenton_315, | ||||||
|         subghz_scene_set_type_submenu_callback, |         subghz_scene_set_type_submenu_callback, | ||||||
|         subghz); |         subghz); | ||||||
|     submenu_add_item( |     submenu_add_item( | ||||||
| @ -108,10 +61,6 @@ void subghz_scene_set_type_on_enter(void* context) { | |||||||
|         SubmenuIndexCAMETwee, |         SubmenuIndexCAMETwee, | ||||||
|         subghz_scene_set_type_submenu_callback, |         subghz_scene_set_type_submenu_callback, | ||||||
|         subghz); |         subghz); | ||||||
|     // submenu_add_item(
 |  | ||||||
|     //     subghz->submenu, "Nero Sketch", SubmenuIndexNeroSketch, subghz_scene_set_type_submenu_callback, subghz);
 |  | ||||||
|     // submenu_add_item(
 |  | ||||||
|     //     subghz->submenu, "Nero Radio", SubmenuIndexNeroRadio, subghz_scene_set_type_submenu_callback, subghz);
 |  | ||||||
|     submenu_add_item( |     submenu_add_item( | ||||||
|         subghz->submenu, |         subghz->submenu, | ||||||
|         "Gate TX_433", |         "Gate TX_433", | ||||||
| @ -172,94 +121,59 @@ bool subghz_scene_set_type_on_event(void* context, SceneManagerEvent event) { | |||||||
|     bool generated_protocol = false; |     bool generated_protocol = false; | ||||||
| 
 | 
 | ||||||
|     if(event.type == SceneManagerEventTypeCustom) { |     if(event.type == SceneManagerEventTypeCustom) { | ||||||
|         //ToDo Fix
 |         uint32_t key = (uint32_t)rand(); | ||||||
|         uint32_t key = subghz_random_serial(); |  | ||||||
|         switch(event.event) { |         switch(event.event) { | ||||||
|         case SubmenuIndexPricenton: |         case SubmenuIndexPricenton_433: | ||||||
|             key = (key & 0x00FFFFF0) | 0x4; //btn 0x1, 0x2, 0x4, 0x8
 |             key = (key & 0x00FFFFF0) | 0x4; //btn 0x1, 0x2, 0x4, 0x8
 | ||||||
|             if(subghz_scene_set_type_submenu_gen_data_protocol( |             generated_protocol = subghz_txrx_gen_data_protocol_and_te( | ||||||
|                    subghz, SUBGHZ_PROTOCOL_PRINCETON_NAME, key, 24, 433920000, "AM650")) { |                 subghz->txrx, "AM650", 433920000, SUBGHZ_PROTOCOL_PRINCETON_NAME, key, 24, 400); | ||||||
|                 uint32_t te = 400; |             break; | ||||||
|                 flipper_format_update_uint32(subghz->txrx->fff_data, "TE", (uint32_t*)&te, 1); |         case SubmenuIndexPricenton_315: | ||||||
|                 generated_protocol = true; |             key = (key & 0x00FFFFF0) | 0x4; //btn 0x1, 0x2, 0x4, 0x8
 | ||||||
|             } |             generated_protocol = subghz_txrx_gen_data_protocol_and_te( | ||||||
|  |                 subghz->txrx, "AM650", 315000000, SUBGHZ_PROTOCOL_PRINCETON_NAME, key, 24, 400); | ||||||
|             break; |             break; | ||||||
|         case SubmenuIndexNiceFlo12bit: |         case SubmenuIndexNiceFlo12bit: | ||||||
|             key = (key & 0x0000FFF0) | 0x1; //btn 0x1, 0x2, 0x4
 |             key = (key & 0x0000FFF0) | 0x1; //btn 0x1, 0x2, 0x4
 | ||||||
|             if(subghz_scene_set_type_submenu_gen_data_protocol( |             generated_protocol = subghz_txrx_gen_data_protocol( | ||||||
|                    subghz, SUBGHZ_PROTOCOL_NICE_FLO_NAME, key, 12, 433920000, "AM650")) { |                 subghz->txrx, "AM650", 433920000, SUBGHZ_PROTOCOL_NICE_FLO_NAME, key, 12); | ||||||
|                 generated_protocol = true; |  | ||||||
|             } |  | ||||||
|             break; |             break; | ||||||
|         case SubmenuIndexNiceFlo24bit: |         case SubmenuIndexNiceFlo24bit: | ||||||
|             key = (key & 0x00FFFFF0) | 0x4; //btn 0x1, 0x2, 0x4, 0x8
 |             key = (key & 0x00FFFFF0) | 0x4; //btn 0x1, 0x2, 0x4, 0x8
 | ||||||
|             if(subghz_scene_set_type_submenu_gen_data_protocol( |             generated_protocol = subghz_txrx_gen_data_protocol( | ||||||
|                    subghz, SUBGHZ_PROTOCOL_NICE_FLO_NAME, key, 24, 433920000, "AM650")) { |                 subghz->txrx, "AM650", 433920000, SUBGHZ_PROTOCOL_NICE_FLO_NAME, key, 24); | ||||||
|                 generated_protocol = true; |  | ||||||
|             } |  | ||||||
|             break; |             break; | ||||||
|         case SubmenuIndexCAME12bit: |         case SubmenuIndexCAME12bit: | ||||||
|             key = (key & 0x0000FFF0) | 0x1; //btn 0x1, 0x2, 0x4
 |             key = (key & 0x0000FFF0) | 0x1; //btn 0x1, 0x2, 0x4
 | ||||||
|             if(subghz_scene_set_type_submenu_gen_data_protocol( |             generated_protocol = subghz_txrx_gen_data_protocol( | ||||||
|                    subghz, SUBGHZ_PROTOCOL_CAME_NAME, key, 12, 433920000, "AM650")) { |                 subghz->txrx, "AM650", 433920000, SUBGHZ_PROTOCOL_CAME_NAME, key, 12); | ||||||
|                 generated_protocol = true; |  | ||||||
|             } |  | ||||||
|             break; |             break; | ||||||
|         case SubmenuIndexCAME24bit: |         case SubmenuIndexCAME24bit: | ||||||
|             key = (key & 0x00FFFFF0) | 0x4; //btn 0x1, 0x2, 0x4, 0x8
 |             key = (key & 0x00FFFFF0) | 0x4; //btn 0x1, 0x2, 0x4, 0x8
 | ||||||
|             if(subghz_scene_set_type_submenu_gen_data_protocol( |             generated_protocol = subghz_txrx_gen_data_protocol( | ||||||
|                    subghz, SUBGHZ_PROTOCOL_CAME_NAME, key, 24, 433920000, "AM650")) { |                 subghz->txrx, "AM650", 433920000, SUBGHZ_PROTOCOL_CAME_NAME, key, 24); | ||||||
|                 generated_protocol = true; |  | ||||||
|             } |  | ||||||
|             break; |             break; | ||||||
|         case SubmenuIndexLinear_300_00: |         case SubmenuIndexLinear_300_00: | ||||||
|             key = (key & 0x3FF); |             key = (key & 0x3FF); | ||||||
|             if(subghz_scene_set_type_submenu_gen_data_protocol( |             generated_protocol = subghz_txrx_gen_data_protocol( | ||||||
|                    subghz, SUBGHZ_PROTOCOL_LINEAR_NAME, key, 10, 300000000, "AM650")) { |                 subghz->txrx, "AM650", 300000000, SUBGHZ_PROTOCOL_LINEAR_NAME, key, 10); | ||||||
|                 generated_protocol = true; |  | ||||||
|             } |  | ||||||
|             break; |             break; | ||||||
|         case SubmenuIndexCAMETwee: |         case SubmenuIndexCAMETwee: | ||||||
|             key = (key & 0x0FFFFFF0); |             key = (key & 0x0FFFFFF0); | ||||||
|             key = 0x003FFF7200000000 | (key ^ 0xE0E0E0EE); |             key = 0x003FFF7200000000 | (key ^ 0xE0E0E0EE); | ||||||
|             if(subghz_scene_set_type_submenu_gen_data_protocol( | 
 | ||||||
|                    subghz, SUBGHZ_PROTOCOL_CAME_TWEE_NAME, key, 54, 433920000, "AM650")) { |             generated_protocol = subghz_txrx_gen_data_protocol( | ||||||
|                 generated_protocol = true; |                 subghz->txrx, "AM650", 433920000, SUBGHZ_PROTOCOL_CAME_TWEE_NAME, key, 54); | ||||||
|             } |  | ||||||
|             break; |             break; | ||||||
|         // case SubmenuIndexNeroSketch:
 |  | ||||||
|         //     /* code */
 |  | ||||||
|         //     break;
 |  | ||||||
|         // case SubmenuIndexNeroRadio:
 |  | ||||||
|         //     /* code */
 |  | ||||||
|         //     break;
 |  | ||||||
|         case SubmenuIndexGateTX: |         case SubmenuIndexGateTX: | ||||||
|             key = (key & 0x00F0FF00) | 0xF << 16 | 0x40; //btn 0xF, 0xC, 0xA, 0x6 (?)
 |             key = (key & 0x00F0FF00) | 0xF << 16 | 0x40; //btn 0xF, 0xC, 0xA, 0x6 (?)
 | ||||||
|             uint64_t rev_key = subghz_protocol_blocks_reverse_key(key, 24); |             uint64_t rev_key = subghz_protocol_blocks_reverse_key(key, 24); | ||||||
|             if(subghz_scene_set_type_submenu_gen_data_protocol( |             generated_protocol = subghz_txrx_gen_data_protocol( | ||||||
|                    subghz, SUBGHZ_PROTOCOL_GATE_TX_NAME, rev_key, 24, 433920000, "AM650")) { |                 subghz->txrx, "AM650", 433920000, SUBGHZ_PROTOCOL_GATE_TX_NAME, rev_key, 24); | ||||||
|                 generated_protocol = true; |  | ||||||
|             } |  | ||||||
|             break; |             break; | ||||||
|         case SubmenuIndexDoorHan_433_92: |         case SubmenuIndexDoorHan_433_92: | ||||||
|             subghz->txrx->transmitter = subghz_transmitter_alloc_init( |             generated_protocol = subghz_txrx_gen_keeloq_protocol( | ||||||
|                 subghz->txrx->environment, SUBGHZ_PROTOCOL_KEELOQ_NAME); |                 subghz->txrx, "AM650", 433920000, "DoorHan", key, 0x2, 0x0003); | ||||||
|             subghz_preset_init( |  | ||||||
|                 subghz, "AM650", subghz_setting_get_default_frequency(subghz->setting), NULL, 0); |  | ||||||
|             if(subghz->txrx->transmitter) { |  | ||||||
|                 subghz_protocol_keeloq_create_data( |  | ||||||
|                     subghz_transmitter_get_protocol_instance(subghz->txrx->transmitter), |  | ||||||
|                     subghz->txrx->fff_data, |  | ||||||
|                     key & 0x0FFFFFFF, |  | ||||||
|                     0x2, |  | ||||||
|                     0x0003, |  | ||||||
|                     "DoorHan", |  | ||||||
|                     subghz->txrx->preset); |  | ||||||
|                 generated_protocol = true; |  | ||||||
|             } else { |  | ||||||
|                 generated_protocol = false; |  | ||||||
|             } |  | ||||||
|             subghz_transmitter_free(subghz->txrx->transmitter); |  | ||||||
|             if(!generated_protocol) { |             if(!generated_protocol) { | ||||||
|                 furi_string_set( |                 furi_string_set( | ||||||
|                     subghz->error_str, "Function requires\nan SD card with\nfresh databases."); |                     subghz->error_str, "Function requires\nan SD card with\nfresh databases."); | ||||||
| @ -267,23 +181,8 @@ bool subghz_scene_set_type_on_event(void* context, SceneManagerEvent event) { | |||||||
|             } |             } | ||||||
|             break; |             break; | ||||||
|         case SubmenuIndexDoorHan_315_00: |         case SubmenuIndexDoorHan_315_00: | ||||||
|             subghz->txrx->transmitter = subghz_transmitter_alloc_init( |             generated_protocol = subghz_txrx_gen_keeloq_protocol( | ||||||
|                 subghz->txrx->environment, SUBGHZ_PROTOCOL_KEELOQ_NAME); |                 subghz->txrx, "AM650", 315000000, "DoorHan", key, 0x2, 0x0003); | ||||||
|             subghz_preset_init(subghz, "AM650", 315000000, NULL, 0); |  | ||||||
|             if(subghz->txrx->transmitter) { |  | ||||||
|                 subghz_protocol_keeloq_create_data( |  | ||||||
|                     subghz_transmitter_get_protocol_instance(subghz->txrx->transmitter), |  | ||||||
|                     subghz->txrx->fff_data, |  | ||||||
|                     key & 0x0FFFFFFF, |  | ||||||
|                     0x2, |  | ||||||
|                     0x0003, |  | ||||||
|                     "DoorHan", |  | ||||||
|                     subghz->txrx->preset); |  | ||||||
|                 generated_protocol = true; |  | ||||||
|             } else { |  | ||||||
|                 generated_protocol = false; |  | ||||||
|             } |  | ||||||
|             subghz_transmitter_free(subghz->txrx->transmitter); |  | ||||||
|             if(!generated_protocol) { |             if(!generated_protocol) { | ||||||
|                 furi_string_set( |                 furi_string_set( | ||||||
|                     subghz->error_str, "Function requires\nan SD card with\nfresh databases."); |                     subghz->error_str, "Function requires\nan SD card with\nfresh databases."); | ||||||
| @ -291,86 +190,24 @@ bool subghz_scene_set_type_on_event(void* context, SceneManagerEvent event) { | |||||||
|             } |             } | ||||||
|             break; |             break; | ||||||
|         case SubmenuIndexLiftMaster_315_00: |         case SubmenuIndexLiftMaster_315_00: | ||||||
|             while(!subghz_protocol_secplus_v1_check_fixed(key)) { |             generated_protocol = | ||||||
|                 key = subghz_random_serial(); |                 subghz_txrx_gen_secplus_v1_protocol(subghz->txrx, "AM650", 315000000); | ||||||
|             } |  | ||||||
|             if(subghz_scene_set_type_submenu_gen_data_protocol( |  | ||||||
|                    subghz, |  | ||||||
|                    SUBGHZ_PROTOCOL_SECPLUS_V1_NAME, |  | ||||||
|                    (uint64_t)key << 32 | 0xE6000000, |  | ||||||
|                    42, |  | ||||||
|                    315000000, |  | ||||||
|                    "AM650")) { |  | ||||||
|                 generated_protocol = true; |  | ||||||
|             } |  | ||||||
|             break; |             break; | ||||||
|         case SubmenuIndexLiftMaster_390_00: |         case SubmenuIndexLiftMaster_390_00: | ||||||
|             while(!subghz_protocol_secplus_v1_check_fixed(key)) { |             generated_protocol = | ||||||
|                 key = subghz_random_serial(); |                 subghz_txrx_gen_secplus_v1_protocol(subghz->txrx, "AM650", 390000000); | ||||||
|             } |  | ||||||
|             if(subghz_scene_set_type_submenu_gen_data_protocol( |  | ||||||
|                    subghz, |  | ||||||
|                    SUBGHZ_PROTOCOL_SECPLUS_V1_NAME, |  | ||||||
|                    (uint64_t)key << 32 | 0xE6000000, |  | ||||||
|                    42, |  | ||||||
|                    390000000, |  | ||||||
|                    "AM650")) { |  | ||||||
|                 generated_protocol = true; |  | ||||||
|             } |  | ||||||
|             break; |             break; | ||||||
|         case SubmenuIndexSecPlus_v2_310_00: |         case SubmenuIndexSecPlus_v2_310_00: | ||||||
|             subghz->txrx->transmitter = subghz_transmitter_alloc_init( |             generated_protocol = subghz_txrx_gen_secplus_v2_protocol( | ||||||
|                 subghz->txrx->environment, SUBGHZ_PROTOCOL_SECPLUS_V2_NAME); |                 subghz->txrx, "AM650", 310000000, key, 0x68, 0xE500000); | ||||||
|             subghz_preset_init(subghz, "AM650", 310000000, NULL, 0); |  | ||||||
|             if(subghz->txrx->transmitter) { |  | ||||||
|                 subghz_protocol_secplus_v2_create_data( |  | ||||||
|                     subghz_transmitter_get_protocol_instance(subghz->txrx->transmitter), |  | ||||||
|                     subghz->txrx->fff_data, |  | ||||||
|                     key, |  | ||||||
|                     0x68, |  | ||||||
|                     0xE500000, |  | ||||||
|                     subghz->txrx->preset); |  | ||||||
|                 generated_protocol = true; |  | ||||||
|             } else { |  | ||||||
|                 generated_protocol = false; |  | ||||||
|             } |  | ||||||
|             subghz_transmitter_free(subghz->txrx->transmitter); |  | ||||||
|             break; |             break; | ||||||
|         case SubmenuIndexSecPlus_v2_315_00: |         case SubmenuIndexSecPlus_v2_315_00: | ||||||
|             subghz->txrx->transmitter = subghz_transmitter_alloc_init( |             generated_protocol = subghz_txrx_gen_secplus_v2_protocol( | ||||||
|                 subghz->txrx->environment, SUBGHZ_PROTOCOL_SECPLUS_V2_NAME); |                 subghz->txrx, "AM650", 315000000, key, 0x68, 0xE500000); | ||||||
|             subghz_preset_init(subghz, "AM650", 315000000, NULL, 0); |  | ||||||
|             if(subghz->txrx->transmitter) { |  | ||||||
|                 subghz_protocol_secplus_v2_create_data( |  | ||||||
|                     subghz_transmitter_get_protocol_instance(subghz->txrx->transmitter), |  | ||||||
|                     subghz->txrx->fff_data, |  | ||||||
|                     key, |  | ||||||
|                     0x68, |  | ||||||
|                     0xE500000, |  | ||||||
|                     subghz->txrx->preset); |  | ||||||
|                 generated_protocol = true; |  | ||||||
|             } else { |  | ||||||
|                 generated_protocol = false; |  | ||||||
|             } |  | ||||||
|             subghz_transmitter_free(subghz->txrx->transmitter); |  | ||||||
|             break; |             break; | ||||||
|         case SubmenuIndexSecPlus_v2_390_00: |         case SubmenuIndexSecPlus_v2_390_00: | ||||||
|             subghz->txrx->transmitter = subghz_transmitter_alloc_init( |             generated_protocol = subghz_txrx_gen_secplus_v2_protocol( | ||||||
|                 subghz->txrx->environment, SUBGHZ_PROTOCOL_SECPLUS_V2_NAME); |                 subghz->txrx, "AM650", 390000000, key, 0x68, 0xE500000); | ||||||
|             subghz_preset_init(subghz, "AM650", 390000000, NULL, 0); |  | ||||||
|             if(subghz->txrx->transmitter) { |  | ||||||
|                 subghz_protocol_secplus_v2_create_data( |  | ||||||
|                     subghz_transmitter_get_protocol_instance(subghz->txrx->transmitter), |  | ||||||
|                     subghz->txrx->fff_data, |  | ||||||
|                     key, |  | ||||||
|                     0x68, |  | ||||||
|                     0xE500000, |  | ||||||
|                     subghz->txrx->preset); |  | ||||||
|                 generated_protocol = true; |  | ||||||
|             } else { |  | ||||||
|                 generated_protocol = false; |  | ||||||
|             } |  | ||||||
|             subghz_transmitter_free(subghz->txrx->transmitter); |  | ||||||
|             break; |             break; | ||||||
|         default: |         default: | ||||||
|             return false; |             return false; | ||||||
|  | |||||||
| @ -50,9 +50,10 @@ void subghz_scene_show_error_on_enter(void* context) { | |||||||
| 
 | 
 | ||||||
| bool subghz_scene_show_error_on_event(void* context, SceneManagerEvent event) { | bool subghz_scene_show_error_on_event(void* context, SceneManagerEvent event) { | ||||||
|     SubGhz* subghz = context; |     SubGhz* subghz = context; | ||||||
|  |     SubGhzCustomEvent scene_state = | ||||||
|  |         scene_manager_get_scene_state(subghz->scene_manager, SubGhzSceneShowError); | ||||||
|     if(event.type == SceneManagerEventTypeBack) { |     if(event.type == SceneManagerEventTypeBack) { | ||||||
|         if(scene_manager_get_scene_state(subghz->scene_manager, SubGhzSceneShowError) == |         if(scene_state == SubGhzCustomEventManagerSet) { | ||||||
|            SubGhzCustomEventManagerSet) { |  | ||||||
|             return false; |             return false; | ||||||
|         } else { |         } else { | ||||||
|             scene_manager_search_and_switch_to_previous_scene( |             scene_manager_search_and_switch_to_previous_scene( | ||||||
| @ -61,14 +62,12 @@ bool subghz_scene_show_error_on_event(void* context, SceneManagerEvent event) { | |||||||
|         return true; |         return true; | ||||||
|     } else if(event.type == SceneManagerEventTypeCustom) { |     } else if(event.type == SceneManagerEventTypeCustom) { | ||||||
|         if(event.event == SubGhzCustomEventSceneShowErrorOk) { |         if(event.event == SubGhzCustomEventSceneShowErrorOk) { | ||||||
|             if(scene_manager_get_scene_state(subghz->scene_manager, SubGhzSceneShowError) == |             if(scene_state == SubGhzCustomEventManagerSet) { | ||||||
|                SubGhzCustomEventManagerSet) { |  | ||||||
|                 scene_manager_next_scene(subghz->scene_manager, SubGhzSceneStart); |                 scene_manager_next_scene(subghz->scene_manager, SubGhzSceneStart); | ||||||
|             } |             } | ||||||
|             return true; |             return true; | ||||||
|         } else if(event.event == SubGhzCustomEventSceneShowErrorBack) { |         } else if(event.event == SubGhzCustomEventSceneShowErrorBack) { | ||||||
|             if(scene_manager_get_scene_state(subghz->scene_manager, SubGhzSceneShowError) == |             if(scene_state == SubGhzCustomEventManagerSet) { | ||||||
|                SubGhzCustomEventManagerSet) { |  | ||||||
|                 //exit app
 |                 //exit app
 | ||||||
|                 if(!scene_manager_previous_scene(subghz->scene_manager)) { |                 if(!scene_manager_previous_scene(subghz->scene_manager)) { | ||||||
|                     scene_manager_stop(subghz->scene_manager); |                     scene_manager_stop(subghz->scene_manager); | ||||||
|  | |||||||
| @ -70,7 +70,7 @@ bool subghz_scene_start_on_event(void* context, SceneManagerEvent event) { | |||||||
|         if(event.event == SubmenuIndexReadRAW) { |         if(event.event == SubmenuIndexReadRAW) { | ||||||
|             scene_manager_set_scene_state( |             scene_manager_set_scene_state( | ||||||
|                 subghz->scene_manager, SubGhzSceneStart, SubmenuIndexReadRAW); |                 subghz->scene_manager, SubGhzSceneStart, SubmenuIndexReadRAW); | ||||||
|             subghz->txrx->rx_key_state = SubGhzRxKeyStateIDLE; |             subghz_rx_key_state_set(subghz, SubGhzRxKeyStateIDLE); | ||||||
|             scene_manager_next_scene(subghz->scene_manager, SubGhzSceneReadRAW); |             scene_manager_next_scene(subghz->scene_manager, SubGhzSceneReadRAW); | ||||||
|             return true; |             return true; | ||||||
|         } else if(event.event == SubmenuIndexRead) { |         } else if(event.event == SubmenuIndexRead) { | ||||||
|  | |||||||
| @ -11,32 +11,24 @@ void subghz_scene_transmitter_callback(SubGhzCustomEvent event, void* context) { | |||||||
| bool subghz_scene_transmitter_update_data_show(void* context) { | bool subghz_scene_transmitter_update_data_show(void* context) { | ||||||
|     SubGhz* subghz = context; |     SubGhz* subghz = context; | ||||||
|     bool ret = false; |     bool ret = false; | ||||||
|     if(subghz->txrx->decoder_result) { |     SubGhzProtocolDecoderBase* decoder = subghz_txrx_get_decoder(subghz->txrx); | ||||||
|         FuriString* key_str; |  | ||||||
|         FuriString* frequency_str; |  | ||||||
|         FuriString* modulation_str; |  | ||||||
| 
 | 
 | ||||||
|         key_str = furi_string_alloc(); |     if(decoder) { | ||||||
|         frequency_str = furi_string_alloc(); |         FuriString* key_str = furi_string_alloc(); | ||||||
|         modulation_str = furi_string_alloc(); |         FuriString* frequency_str = furi_string_alloc(); | ||||||
|         uint8_t show_button = 0; |         FuriString* modulation_str = furi_string_alloc(); | ||||||
| 
 | 
 | ||||||
|         if(subghz_protocol_decoder_base_deserialize( |         if(subghz_protocol_decoder_base_deserialize( | ||||||
|                subghz->txrx->decoder_result, subghz->txrx->fff_data) == SubGhzProtocolStatusOk) { |                decoder, subghz_txrx_get_fff_data(subghz->txrx)) == SubGhzProtocolStatusOk) { | ||||||
|             subghz_protocol_decoder_base_get_string(subghz->txrx->decoder_result, key_str); |             subghz_protocol_decoder_base_get_string(decoder, key_str); | ||||||
| 
 | 
 | ||||||
|             if((subghz->txrx->decoder_result->protocol->flag & SubGhzProtocolFlag_Send) == |             subghz_txrx_get_frequency_and_modulation(subghz->txrx, frequency_str, modulation_str); | ||||||
|                SubGhzProtocolFlag_Send) { |  | ||||||
|                 show_button = 1; |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             subghz_get_frequency_modulation(subghz, frequency_str, modulation_str); |  | ||||||
|             subghz_view_transmitter_add_data_to_show( |             subghz_view_transmitter_add_data_to_show( | ||||||
|                 subghz->subghz_transmitter, |                 subghz->subghz_transmitter, | ||||||
|                 furi_string_get_cstr(key_str), |                 furi_string_get_cstr(key_str), | ||||||
|                 furi_string_get_cstr(frequency_str), |                 furi_string_get_cstr(frequency_str), | ||||||
|                 furi_string_get_cstr(modulation_str), |                 furi_string_get_cstr(modulation_str), | ||||||
|                 show_button); |                 subghz_txrx_protocol_is_transmittable(subghz->txrx, false)); | ||||||
|             ret = true; |             ret = true; | ||||||
|         } |         } | ||||||
|         furi_string_free(frequency_str); |         furi_string_free(frequency_str); | ||||||
| @ -65,24 +57,16 @@ bool subghz_scene_transmitter_on_event(void* context, SceneManagerEvent event) { | |||||||
|     if(event.type == SceneManagerEventTypeCustom) { |     if(event.type == SceneManagerEventTypeCustom) { | ||||||
|         if(event.event == SubGhzCustomEventViewTransmitterSendStart) { |         if(event.event == SubGhzCustomEventViewTransmitterSendStart) { | ||||||
|             subghz->state_notifications = SubGhzNotificationStateIDLE; |             subghz->state_notifications = SubGhzNotificationStateIDLE; | ||||||
|             if(subghz->txrx->txrx_state == SubGhzTxRxStateRx) { | 
 | ||||||
|                 subghz_rx_end(subghz); |             if(subghz_tx_start(subghz, subghz_txrx_get_fff_data(subghz->txrx))) { | ||||||
|             } |  | ||||||
|             if((subghz->txrx->txrx_state == SubGhzTxRxStateIDLE) || |  | ||||||
|                (subghz->txrx->txrx_state == SubGhzTxRxStateSleep)) { |  | ||||||
|                 if(subghz_tx_start(subghz, subghz->txrx->fff_data)) { |  | ||||||
|                 subghz->state_notifications = SubGhzNotificationStateTx; |                 subghz->state_notifications = SubGhzNotificationStateTx; | ||||||
|                 subghz_scene_transmitter_update_data_show(subghz); |                 subghz_scene_transmitter_update_data_show(subghz); | ||||||
|                 DOLPHIN_DEED(DolphinDeedSubGhzSend); |                 DOLPHIN_DEED(DolphinDeedSubGhzSend); | ||||||
|             } |             } | ||||||
|             } |  | ||||||
|             return true; |             return true; | ||||||
|         } else if(event.event == SubGhzCustomEventViewTransmitterSendStop) { |         } else if(event.event == SubGhzCustomEventViewTransmitterSendStop) { | ||||||
|             subghz->state_notifications = SubGhzNotificationStateIDLE; |             subghz->state_notifications = SubGhzNotificationStateIDLE; | ||||||
|             if(subghz->txrx->txrx_state == SubGhzTxRxStateTx) { |             subghz_txrx_stop(subghz->txrx); | ||||||
|                 subghz_tx_stop(subghz); |  | ||||||
|                 subghz_sleep(subghz); |  | ||||||
|             } |  | ||||||
|             return true; |             return true; | ||||||
|         } else if(event.event == SubGhzCustomEventViewTransmitterBack) { |         } else if(event.event == SubGhzCustomEventViewTransmitterBack) { | ||||||
|             subghz->state_notifications = SubGhzNotificationStateIDLE; |             subghz->state_notifications = SubGhzNotificationStateIDLE; | ||||||
|  | |||||||
| @ -1,9 +1,6 @@ | |||||||
| /* Abandon hope, all ye who enter here. */ | /* Abandon hope, all ye who enter here. */ | ||||||
| 
 | 
 | ||||||
| #include "subghz/types.h" |  | ||||||
| #include "subghz_i.h" | #include "subghz_i.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); | ||||||
| @ -49,16 +46,6 @@ static void subghz_rpc_command_callback(RpcAppSystemEvent event, void* context) | |||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void subghz_blink_start(SubGhz* instance) { |  | ||||||
|     furi_assert(instance); |  | ||||||
|     notification_message(instance->notifications, &sequence_blink_start_magenta); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void subghz_blink_stop(SubGhz* instance) { |  | ||||||
|     furi_assert(instance); |  | ||||||
|     notification_message(instance->notifications, &sequence_blink_stop); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| SubGhz* subghz_alloc() { | SubGhz* subghz_alloc() { | ||||||
|     SubGhz* subghz = malloc(sizeof(SubGhz)); |     SubGhz* subghz = malloc(sizeof(SubGhz)); | ||||||
| 
 | 
 | ||||||
| @ -163,45 +150,18 @@ SubGhz* subghz_alloc() { | |||||||
|         SubGhzViewIdStatic, |         SubGhzViewIdStatic, | ||||||
|         subghz_test_static_get_view(subghz->subghz_test_static)); |         subghz_test_static_get_view(subghz->subghz_test_static)); | ||||||
| 
 | 
 | ||||||
|     //init setting
 |     //init threshold rssi
 | ||||||
|     subghz->setting = subghz_setting_alloc(); |     subghz->threshold_rssi = subghz_threshold_rssi_alloc(); | ||||||
|     subghz_setting_load(subghz->setting, EXT_PATH("subghz/assets/setting_user")); |  | ||||||
| 
 | 
 | ||||||
|     //init Worker & Protocol & History & KeyBoard
 |     subghz_unlock(subghz); | ||||||
|     subghz->lock = SubGhzLockOff; |     subghz_rx_key_state_set(subghz, SubGhzRxKeyStateIDLE); | ||||||
|     subghz->txrx = malloc(sizeof(SubGhzTxRx)); |     subghz->history = subghz_history_alloc(); | ||||||
|     subghz->txrx->preset = malloc(sizeof(SubGhzRadioPreset)); |     subghz->filter = SubGhzProtocolFlag_Decodable; | ||||||
|     subghz->txrx->preset->name = furi_string_alloc(); |  | ||||||
|     subghz_preset_init( |  | ||||||
|         subghz, "AM650", subghz_setting_get_default_frequency(subghz->setting), NULL, 0); |  | ||||||
| 
 | 
 | ||||||
|     subghz->txrx->txrx_state = SubGhzTxRxStateSleep; |     //init TxRx & History & KeyBoard
 | ||||||
|     subghz->txrx->hopper_state = SubGhzHopperStateOFF; |     subghz->txrx = subghz_txrx_alloc(); | ||||||
|     subghz->txrx->speaker_state = SubGhzSpeakerStateDisable; |     subghz_txrx_receiver_set_filter(subghz->txrx, subghz->filter); | ||||||
|     subghz->txrx->rx_key_state = SubGhzRxKeyStateIDLE; |     subghz_txrx_set_need_save_callback(subghz->txrx, subghz_save_to_file, subghz); | ||||||
|     subghz->txrx->raw_threshold_rssi = SUBGHZ_RAW_TRESHOLD_MIN; |  | ||||||
|     subghz->txrx->history = subghz_history_alloc(); |  | ||||||
|     subghz->txrx->worker = subghz_worker_alloc(); |  | ||||||
|     subghz->txrx->fff_data = flipper_format_string_alloc(); |  | ||||||
| 
 |  | ||||||
|     subghz->txrx->environment = subghz_environment_alloc(); |  | ||||||
|     subghz_environment_set_came_atomo_rainbow_table_file_name( |  | ||||||
|         subghz->txrx->environment, EXT_PATH("subghz/assets/came_atomo")); |  | ||||||
|     subghz_environment_set_alutech_at_4n_rainbow_table_file_name( |  | ||||||
|         subghz->txrx->environment, EXT_PATH("subghz/assets/alutech_at_4n")); |  | ||||||
|     subghz_environment_set_nice_flor_s_rainbow_table_file_name( |  | ||||||
|         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->filter = SubGhzProtocolFlag_Decodable; |  | ||||||
|     subghz_receiver_set_filter(subghz->txrx->receiver, subghz->txrx->filter); |  | ||||||
| 
 |  | ||||||
|     subghz_worker_set_overrun_callback( |  | ||||||
|         subghz->txrx->worker, (SubGhzWorkerOverrunCallback)subghz_receiver_reset); |  | ||||||
|     subghz_worker_set_pair_callback( |  | ||||||
|         subghz->txrx->worker, (SubGhzWorkerPairCallback)subghz_receiver_decode); |  | ||||||
|     subghz_worker_set_context(subghz->txrx->worker, subghz->txrx->receiver); |  | ||||||
| 
 | 
 | ||||||
|     //Init Error_str
 |     //Init Error_str
 | ||||||
|     subghz->error_str = furi_string_alloc(); |     subghz->error_str = furi_string_alloc(); | ||||||
| @ -219,7 +179,9 @@ void subghz_free(SubGhz* subghz) { | |||||||
|         subghz->rpc_ctx = NULL; |         subghz->rpc_ctx = NULL; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     subghz_speaker_off(subghz); |     subghz_txrx_speaker_off(subghz->txrx); | ||||||
|  |     subghz_txrx_stop(subghz->txrx); | ||||||
|  |     subghz_txrx_sleep(subghz->txrx); | ||||||
| 
 | 
 | ||||||
|     // Packet Test
 |     // Packet Test
 | ||||||
|     view_dispatcher_remove_view(subghz->view_dispatcher, SubGhzViewIdTestPacket); |     view_dispatcher_remove_view(subghz->view_dispatcher, SubGhzViewIdTestPacket); | ||||||
| @ -282,18 +244,14 @@ void subghz_free(SubGhz* subghz) { | |||||||
|     furi_record_close(RECORD_GUI); |     furi_record_close(RECORD_GUI); | ||||||
|     subghz->gui = NULL; |     subghz->gui = NULL; | ||||||
| 
 | 
 | ||||||
|     //setting
 |     // threshold rssi
 | ||||||
|     subghz_setting_free(subghz->setting); |     subghz_threshold_rssi_free(subghz->threshold_rssi); | ||||||
| 
 | 
 | ||||||
|     //Worker & Protocol & History
 |     //Worker & Protocol & History
 | ||||||
|     subghz_receiver_free(subghz->txrx->receiver); |     subghz_history_free(subghz->history); | ||||||
|     subghz_environment_free(subghz->txrx->environment); | 
 | ||||||
|     subghz_worker_free(subghz->txrx->worker); |     //TxRx
 | ||||||
|     flipper_format_free(subghz->txrx->fff_data); |     subghz_txrx_free(subghz->txrx); | ||||||
|     subghz_history_free(subghz->txrx->history); |  | ||||||
|     furi_string_free(subghz->txrx->preset->name); |  | ||||||
|     free(subghz->txrx->preset); |  | ||||||
|     free(subghz->txrx); |  | ||||||
| 
 | 
 | ||||||
|     //Error string
 |     //Error string
 | ||||||
|     furi_string_free(subghz->error_str); |     furi_string_free(subghz->error_str); | ||||||
| @ -319,11 +277,6 @@ int32_t subghz_app(void* p) { | |||||||
|         return 1; |         return 1; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     //Load database
 |  | ||||||
|     bool load_database = subghz_environment_load_keystore( |  | ||||||
|         subghz->txrx->environment, EXT_PATH("subghz/assets/keeloq_mfcodes")); |  | ||||||
|     subghz_environment_load_keystore( |  | ||||||
|         subghz->txrx->environment, EXT_PATH("subghz/assets/keeloq_mfcodes_user")); |  | ||||||
|     // Check argument and run corresponding scene
 |     // Check argument and run corresponding scene
 | ||||||
|     if(p && strlen(p)) { |     if(p && strlen(p)) { | ||||||
|         uint32_t rpc_ctx = 0; |         uint32_t rpc_ctx = 0; | ||||||
| @ -340,9 +293,9 @@ int32_t subghz_app(void* p) { | |||||||
|             if(subghz_key_load(subghz, p, true)) { |             if(subghz_key_load(subghz, p, true)) { | ||||||
|                 furi_string_set(subghz->file_path, (const char*)p); |                 furi_string_set(subghz->file_path, (const char*)p); | ||||||
| 
 | 
 | ||||||
|                 if((!strcmp(subghz->txrx->decoder_result->protocol->name, "RAW"))) { |                 if(subghz_get_load_type_file(subghz) == SubGhzLoadTypeFileRaw) { | ||||||
|                     //Load Raw TX
 |                     //Load Raw TX
 | ||||||
|                     subghz->txrx->rx_key_state = SubGhzRxKeyStateRAWLoad; |                     subghz_rx_key_state_set(subghz, SubGhzRxKeyStateRAWLoad); | ||||||
|                     scene_manager_next_scene(subghz->scene_manager, SubGhzSceneReadRAW); |                     scene_manager_next_scene(subghz->scene_manager, SubGhzSceneReadRAW); | ||||||
|                 } else { |                 } else { | ||||||
|                     //Load transmitter TX
 |                     //Load transmitter TX
 | ||||||
| @ -358,7 +311,7 @@ int32_t subghz_app(void* p) { | |||||||
|         view_dispatcher_attach_to_gui( |         view_dispatcher_attach_to_gui( | ||||||
|             subghz->view_dispatcher, subghz->gui, ViewDispatcherTypeFullscreen); |             subghz->view_dispatcher, subghz->gui, ViewDispatcherTypeFullscreen); | ||||||
|         furi_string_set(subghz->file_path, SUBGHZ_APP_FOLDER); |         furi_string_set(subghz->file_path, SUBGHZ_APP_FOLDER); | ||||||
|         if(load_database) { |         if(subghz_txrx_is_database_loaded(subghz->txrx)) { | ||||||
|             scene_manager_next_scene(subghz->scene_manager, SubGhzSceneStart); |             scene_manager_next_scene(subghz->scene_manager, SubGhzSceneStart); | ||||||
|         } else { |         } else { | ||||||
|             scene_manager_set_scene_state( |             scene_manager_set_scene_state( | ||||||
|  | |||||||
| @ -18,214 +18,42 @@ | |||||||
| 
 | 
 | ||||||
| #define TAG "SubGhz" | #define TAG "SubGhz" | ||||||
| 
 | 
 | ||||||
| void subghz_preset_init( | void subghz_set_default_preset(SubGhz* subghz) { | ||||||
|     void* context, |  | ||||||
|     const char* preset_name, |  | ||||||
|     uint32_t frequency, |  | ||||||
|     uint8_t* preset_data, |  | ||||||
|     size_t preset_data_size) { |  | ||||||
|     furi_assert(context); |  | ||||||
|     SubGhz* subghz = context; |  | ||||||
|     furi_string_set(subghz->txrx->preset->name, preset_name); |  | ||||||
|     subghz->txrx->preset->frequency = frequency; |  | ||||||
|     subghz->txrx->preset->data = preset_data; |  | ||||||
|     subghz->txrx->preset->data_size = preset_data_size; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| bool subghz_set_preset(SubGhz* subghz, const char* preset) { |  | ||||||
|     if(!strcmp(preset, "FuriHalSubGhzPresetOok270Async")) { |  | ||||||
|         furi_string_set(subghz->txrx->preset->name, "AM270"); |  | ||||||
|     } else if(!strcmp(preset, "FuriHalSubGhzPresetOok650Async")) { |  | ||||||
|         furi_string_set(subghz->txrx->preset->name, "AM650"); |  | ||||||
|     } else if(!strcmp(preset, "FuriHalSubGhzPreset2FSKDev238Async")) { |  | ||||||
|         furi_string_set(subghz->txrx->preset->name, "FM238"); |  | ||||||
|     } else if(!strcmp(preset, "FuriHalSubGhzPreset2FSKDev476Async")) { |  | ||||||
|         furi_string_set(subghz->txrx->preset->name, "FM476"); |  | ||||||
|     } else if(!strcmp(preset, "FuriHalSubGhzPresetCustom")) { |  | ||||||
|         furi_string_set(subghz->txrx->preset->name, "CUSTOM"); |  | ||||||
|     } else { |  | ||||||
|         FURI_LOG_E(TAG, "Unknown preset"); |  | ||||||
|         return false; |  | ||||||
|     } |  | ||||||
|     return true; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void subghz_get_frequency_modulation(SubGhz* subghz, FuriString* frequency, FuriString* modulation) { |  | ||||||
|     furi_assert(subghz); |     furi_assert(subghz); | ||||||
|     if(frequency != NULL) { |     subghz_txrx_set_preset( | ||||||
|         furi_string_printf( |         subghz->txrx, | ||||||
|             frequency, |         "AM650", | ||||||
|             "%03ld.%02ld", |         subghz_setting_get_default_frequency(subghz_txrx_get_setting(subghz->txrx)), | ||||||
|             subghz->txrx->preset->frequency / 1000000 % 1000, |         NULL, | ||||||
|             subghz->txrx->preset->frequency / 10000 % 100); |         0); | ||||||
|     } |  | ||||||
|     if(modulation != NULL) { |  | ||||||
|         furi_string_printf(modulation, "%.2s", furi_string_get_cstr(subghz->txrx->preset->name)); |  | ||||||
|     } |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void subghz_begin(SubGhz* subghz, uint8_t* preset_data) { | void subghz_blink_start(SubGhz* subghz) { | ||||||
|     furi_assert(subghz); |     furi_assert(subghz); | ||||||
|     furi_hal_subghz_reset(); |     notification_message(subghz->notifications, &sequence_blink_stop); | ||||||
|     furi_hal_subghz_idle(); |     notification_message(subghz->notifications, &sequence_blink_start_magenta); | ||||||
|     furi_hal_subghz_load_custom_preset(preset_data); |  | ||||||
|     furi_hal_gpio_init(&gpio_cc1101_g0, GpioModeInput, GpioPullNo, GpioSpeedLow); |  | ||||||
|     subghz->txrx->txrx_state = SubGhzTxRxStateIDLE; |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| uint32_t subghz_rx(SubGhz* subghz, uint32_t frequency) { | void subghz_blink_stop(SubGhz* subghz) { | ||||||
|     furi_assert(subghz); |     furi_assert(subghz); | ||||||
|     if(!furi_hal_subghz_is_frequency_valid(frequency)) { |     notification_message(subghz->notifications, &sequence_blink_stop); | ||||||
|         furi_crash("SubGhz: Incorrect RX frequency."); |  | ||||||
|     } |  | ||||||
|     furi_assert( |  | ||||||
|         subghz->txrx->txrx_state != SubGhzTxRxStateRx && |  | ||||||
|         subghz->txrx->txrx_state != SubGhzTxRxStateSleep); |  | ||||||
| 
 |  | ||||||
|     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(); |  | ||||||
|     subghz_speaker_on(subghz); |  | ||||||
|     furi_hal_subghz_rx(); |  | ||||||
| 
 |  | ||||||
|     furi_hal_subghz_start_async_rx(subghz_worker_rx_callback, subghz->txrx->worker); |  | ||||||
|     subghz_worker_start(subghz->txrx->worker); |  | ||||||
|     subghz->txrx->txrx_state = SubGhzTxRxStateRx; |  | ||||||
|     return value; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| static bool subghz_tx(SubGhz* subghz, uint32_t frequency) { |  | ||||||
|     furi_assert(subghz); |  | ||||||
|     if(!furi_hal_subghz_is_frequency_valid(frequency)) { |  | ||||||
|         furi_crash("SubGhz: Incorrect TX frequency."); |  | ||||||
|     } |  | ||||||
|     furi_assert(subghz->txrx->txrx_state != SubGhzTxRxStateSleep); |  | ||||||
|     furi_hal_subghz_idle(); |  | ||||||
|     furi_hal_subghz_set_frequency_and_path(frequency); |  | ||||||
|     furi_hal_gpio_write(&gpio_cc1101_g0, false); |  | ||||||
|     furi_hal_gpio_init(&gpio_cc1101_g0, GpioModeOutputPushPull, GpioPullNo, GpioSpeedLow); |  | ||||||
|     bool ret = furi_hal_subghz_tx(); |  | ||||||
|     if(ret) { |  | ||||||
|         subghz_speaker_on(subghz); |  | ||||||
|         subghz->txrx->txrx_state = SubGhzTxRxStateTx; |  | ||||||
|     } |  | ||||||
|     return ret; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void subghz_idle(SubGhz* subghz) { |  | ||||||
|     furi_assert(subghz); |  | ||||||
|     furi_assert(subghz->txrx->txrx_state != SubGhzTxRxStateSleep); |  | ||||||
|     furi_hal_subghz_idle(); |  | ||||||
|     subghz_speaker_off(subghz); |  | ||||||
|     subghz->txrx->txrx_state = SubGhzTxRxStateIDLE; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void subghz_rx_end(SubGhz* subghz) { |  | ||||||
|     furi_assert(subghz); |  | ||||||
|     furi_assert(subghz->txrx->txrx_state == SubGhzTxRxStateRx); |  | ||||||
| 
 |  | ||||||
|     if(subghz_worker_is_running(subghz->txrx->worker)) { |  | ||||||
|         subghz_worker_stop(subghz->txrx->worker); |  | ||||||
|         furi_hal_subghz_stop_async_rx(); |  | ||||||
|     } |  | ||||||
|     furi_hal_subghz_idle(); |  | ||||||
|     subghz_speaker_off(subghz); |  | ||||||
|     subghz->txrx->txrx_state = SubGhzTxRxStateIDLE; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void subghz_sleep(SubGhz* subghz) { |  | ||||||
|     furi_assert(subghz); |  | ||||||
|     furi_hal_subghz_sleep(); |  | ||||||
|     subghz->txrx->txrx_state = SubGhzTxRxStateSleep; |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| bool subghz_tx_start(SubGhz* subghz, FlipperFormat* flipper_format) { | bool subghz_tx_start(SubGhz* subghz, FlipperFormat* flipper_format) { | ||||||
|     furi_assert(subghz); |     switch(subghz_txrx_tx_start(subghz->txrx, flipper_format)) { | ||||||
| 
 |     case SubGhzTxRxStartTxStateErrorParserOthers: | ||||||
|     bool ret = false; |  | ||||||
|     FuriString* temp_str; |  | ||||||
|     temp_str = furi_string_alloc(); |  | ||||||
|     uint32_t repeat = 200; |  | ||||||
|     do { |  | ||||||
|         if(!flipper_format_rewind(flipper_format)) { |  | ||||||
|             FURI_LOG_E(TAG, "Rewind error"); |  | ||||||
|             break; |  | ||||||
|         } |  | ||||||
|         if(!flipper_format_read_string(flipper_format, "Protocol", temp_str)) { |  | ||||||
|             FURI_LOG_E(TAG, "Missing Protocol"); |  | ||||||
|             break; |  | ||||||
|         } |  | ||||||
|         if(!flipper_format_insert_or_update_uint32(flipper_format, "Repeat", &repeat, 1)) { |  | ||||||
|             FURI_LOG_E(TAG, "Unable Repeat"); |  | ||||||
|             break; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         subghz->txrx->transmitter = subghz_transmitter_alloc_init( |  | ||||||
|             subghz->txrx->environment, furi_string_get_cstr(temp_str)); |  | ||||||
| 
 |  | ||||||
|         if(subghz->txrx->transmitter) { |  | ||||||
|             if(subghz_transmitter_deserialize(subghz->txrx->transmitter, flipper_format) == |  | ||||||
|                SubGhzProtocolStatusOk) { |  | ||||||
|                 if(strcmp(furi_string_get_cstr(subghz->txrx->preset->name), "") != 0) { |  | ||||||
|                     subghz_begin( |  | ||||||
|                         subghz, |  | ||||||
|                         subghz_setting_get_preset_data_by_name( |  | ||||||
|                             subghz->setting, furi_string_get_cstr(subghz->txrx->preset->name))); |  | ||||||
|                 } else { |  | ||||||
|                     FURI_LOG_E( |  | ||||||
|                         TAG, |  | ||||||
|                         "Unknown name preset \" %s \"", |  | ||||||
|                         furi_string_get_cstr(subghz->txrx->preset->name)); |  | ||||||
|                     subghz_begin( |  | ||||||
|                         subghz, subghz_setting_get_preset_data_by_name(subghz->setting, "AM650")); |  | ||||||
|                 } |  | ||||||
|                 if(subghz->txrx->preset->frequency) { |  | ||||||
|                     ret = subghz_tx(subghz, subghz->txrx->preset->frequency); |  | ||||||
|                 } else { |  | ||||||
|                     ret = subghz_tx(subghz, 433920000); |  | ||||||
|                 } |  | ||||||
|                 if(ret) { |  | ||||||
|                     //Start TX
 |  | ||||||
|                     furi_hal_subghz_start_async_tx( |  | ||||||
|                         subghz_transmitter_yield, subghz->txrx->transmitter); |  | ||||||
|                 } else { |  | ||||||
|                     subghz_dialog_message_show_only_rx(subghz); |  | ||||||
|                 } |  | ||||||
|             } else { |  | ||||||
|         dialog_message_show_storage_error( |         dialog_message_show_storage_error( | ||||||
|             subghz->dialogs, "Error in protocol\nparameters\ndescription"); |             subghz->dialogs, "Error in protocol\nparameters\ndescription"); | ||||||
|             } |         break; | ||||||
|         } |     case SubGhzTxRxStartTxStateErrorOnlyRx: | ||||||
|         if(!ret) { |         subghz_dialog_message_show_only_rx(subghz); | ||||||
|             subghz_transmitter_free(subghz->txrx->transmitter); |         break; | ||||||
|             if(subghz->txrx->txrx_state != SubGhzTxRxStateSleep) { |  | ||||||
|                 subghz_idle(subghz); |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
| 
 | 
 | ||||||
|     } while(false); |     default: | ||||||
|     furi_string_free(temp_str); |         return true; | ||||||
|     return ret; |         break; | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void subghz_tx_stop(SubGhz* subghz) { |  | ||||||
|     furi_assert(subghz); |  | ||||||
|     furi_assert(subghz->txrx->txrx_state == SubGhzTxRxStateTx); |  | ||||||
|     //Stop TX
 |  | ||||||
|     furi_hal_subghz_stop_async_tx(); |  | ||||||
|     subghz_transmitter_stop(subghz->txrx->transmitter); |  | ||||||
|     subghz_transmitter_free(subghz->txrx->transmitter); |  | ||||||
| 
 |  | ||||||
|     //if protocol dynamic then we save the last upload
 |  | ||||||
|     if((subghz->txrx->decoder_result->protocol->type == SubGhzProtocolTypeDynamic) && |  | ||||||
|        (subghz_path_is_file(subghz->file_path))) { |  | ||||||
|         subghz_save_protocol_to_file( |  | ||||||
|             subghz, subghz->txrx->fff_data, furi_string_get_cstr(subghz->file_path)); |  | ||||||
|     } |     } | ||||||
|     subghz_idle(subghz); |     return false; | ||||||
|     subghz_speaker_off(subghz); |  | ||||||
|     notification_message(subghz->notifications, &sequence_reset_red); |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void subghz_dialog_message_show_only_rx(SubGhz* subghz) { | void subghz_dialog_message_show_only_rx(SubGhz* subghz) { | ||||||
| @ -254,11 +82,11 @@ bool subghz_key_load(SubGhz* subghz, const char* file_path, bool show_dialog) { | |||||||
| 
 | 
 | ||||||
|     Storage* storage = furi_record_open(RECORD_STORAGE); |     Storage* storage = furi_record_open(RECORD_STORAGE); | ||||||
|     FlipperFormat* fff_data_file = flipper_format_file_alloc(storage); |     FlipperFormat* fff_data_file = flipper_format_file_alloc(storage); | ||||||
|     Stream* fff_data_stream = flipper_format_get_raw_stream(subghz->txrx->fff_data); |     Stream* fff_data_stream = | ||||||
|  |         flipper_format_get_raw_stream(subghz_txrx_get_fff_data(subghz->txrx)); | ||||||
| 
 | 
 | ||||||
|     SubGhzLoadKeyState load_key_state = SubGhzLoadKeyStateParseErr; |     SubGhzLoadKeyState load_key_state = SubGhzLoadKeyStateParseErr; | ||||||
|     FuriString* temp_str; |     FuriString* temp_str = furi_string_alloc(); | ||||||
|     temp_str = furi_string_alloc(); |  | ||||||
|     uint32_t temp_data32; |     uint32_t temp_data32; | ||||||
| 
 | 
 | ||||||
|     do { |     do { | ||||||
| @ -281,6 +109,7 @@ bool subghz_key_load(SubGhz* subghz, const char* file_path, bool show_dialog) { | |||||||
|             break; |             break; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|  |         //Load frequency
 | ||||||
|         if(!flipper_format_read_uint32(fff_data_file, "Frequency", &temp_data32, 1)) { |         if(!flipper_format_read_uint32(fff_data_file, "Frequency", &temp_data32, 1)) { | ||||||
|             FURI_LOG_E(TAG, "Missing Frequency"); |             FURI_LOG_E(TAG, "Missing Frequency"); | ||||||
|             break; |             break; | ||||||
| @ -291,58 +120,61 @@ bool subghz_key_load(SubGhz* subghz, const char* file_path, bool show_dialog) { | |||||||
|             break; |             break; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         subghz->txrx->preset->frequency = temp_data32; |         //Load preset
 | ||||||
| 
 |  | ||||||
|         if(!flipper_format_read_string(fff_data_file, "Preset", temp_str)) { |         if(!flipper_format_read_string(fff_data_file, "Preset", temp_str)) { | ||||||
|             FURI_LOG_E(TAG, "Missing Preset"); |             FURI_LOG_E(TAG, "Missing Preset"); | ||||||
|             break; |             break; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         if(!subghz_set_preset(subghz, furi_string_get_cstr(temp_str))) { |         furi_string_set_str( | ||||||
|  |             temp_str, subghz_txrx_get_preset_name(subghz->txrx, furi_string_get_cstr(temp_str))); | ||||||
|  |         if(!strcmp(furi_string_get_cstr(temp_str), "")) { | ||||||
|             break; |             break; | ||||||
|         } |         } | ||||||
|  |         SubGhzSetting* setting = subghz_txrx_get_setting(subghz->txrx); | ||||||
| 
 | 
 | ||||||
|         if(!strcmp(furi_string_get_cstr(temp_str), "FuriHalSubGhzPresetCustom")) { |         if(!strcmp(furi_string_get_cstr(temp_str), "CUSTOM")) { | ||||||
|             //Todo add Custom_preset_module
 |             //Todo add Custom_preset_module
 | ||||||
|             //delete preset if it already exists
 |             //delete preset if it already exists
 | ||||||
|             subghz_setting_delete_custom_preset( |             subghz_setting_delete_custom_preset(setting, furi_string_get_cstr(temp_str)); | ||||||
|                 subghz->setting, furi_string_get_cstr(subghz->txrx->preset->name)); |  | ||||||
|             //load custom preset from file
 |             //load custom preset from file
 | ||||||
|             if(!subghz_setting_load_custom_preset( |             if(!subghz_setting_load_custom_preset( | ||||||
|                    subghz->setting, |                    setting, furi_string_get_cstr(temp_str), fff_data_file)) { | ||||||
|                    furi_string_get_cstr(subghz->txrx->preset->name), |  | ||||||
|                    fff_data_file)) { |  | ||||||
|                 FURI_LOG_E(TAG, "Missing Custom preset"); |                 FURI_LOG_E(TAG, "Missing Custom preset"); | ||||||
|                 break; |                 break; | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|         size_t preset_index = subghz_setting_get_inx_preset_by_name( |         size_t preset_index = | ||||||
|             subghz->setting, furi_string_get_cstr(subghz->txrx->preset->name)); |             subghz_setting_get_inx_preset_by_name(setting, furi_string_get_cstr(temp_str)); | ||||||
|         subghz_preset_init( |         subghz_txrx_set_preset( | ||||||
|             subghz, |             subghz->txrx, | ||||||
|             furi_string_get_cstr(subghz->txrx->preset->name), |             furi_string_get_cstr(temp_str), | ||||||
|             subghz->txrx->preset->frequency, |             temp_data32, | ||||||
|             subghz_setting_get_preset_data(subghz->setting, preset_index), |             subghz_setting_get_preset_data(setting, preset_index), | ||||||
|             subghz_setting_get_preset_data_size(subghz->setting, preset_index)); |             subghz_setting_get_preset_data_size(setting, preset_index)); | ||||||
| 
 | 
 | ||||||
|  |         //Load protocol
 | ||||||
|         if(!flipper_format_read_string(fff_data_file, "Protocol", temp_str)) { |         if(!flipper_format_read_string(fff_data_file, "Protocol", temp_str)) { | ||||||
|             FURI_LOG_E(TAG, "Missing Protocol"); |             FURI_LOG_E(TAG, "Missing Protocol"); | ||||||
|             break; |             break; | ||||||
|         } |         } | ||||||
|  | 
 | ||||||
|  |         FlipperFormat* fff_data = subghz_txrx_get_fff_data(subghz->txrx); | ||||||
|         if(!strcmp(furi_string_get_cstr(temp_str), "RAW")) { |         if(!strcmp(furi_string_get_cstr(temp_str), "RAW")) { | ||||||
|             //if RAW
 |             //if RAW
 | ||||||
|             subghz_protocol_raw_gen_fff_data(subghz->txrx->fff_data, file_path); |             subghz->load_type_file = SubGhzLoadTypeFileRaw; | ||||||
|  |             subghz_protocol_raw_gen_fff_data(fff_data, file_path); | ||||||
|         } else { |         } else { | ||||||
|  |             subghz->load_type_file = SubGhzLoadTypeFileKey; | ||||||
|             stream_copy_full( |             stream_copy_full( | ||||||
|                 flipper_format_get_raw_stream(fff_data_file), |                 flipper_format_get_raw_stream(fff_data_file), | ||||||
|                 flipper_format_get_raw_stream(subghz->txrx->fff_data)); |                 flipper_format_get_raw_stream(fff_data)); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         subghz->txrx->decoder_result = subghz_receiver_search_decoder_base_by_name( |         if(subghz_txrx_load_decoder_by_name_protocol( | ||||||
|             subghz->txrx->receiver, furi_string_get_cstr(temp_str)); |                subghz->txrx, furi_string_get_cstr(temp_str))) { | ||||||
|         if(subghz->txrx->decoder_result) { |  | ||||||
|             SubGhzProtocolStatus status = subghz_protocol_decoder_base_deserialize( |             SubGhzProtocolStatus status = subghz_protocol_decoder_base_deserialize( | ||||||
|                 subghz->txrx->decoder_result, subghz->txrx->fff_data); |                 subghz_txrx_get_decoder(subghz->txrx), fff_data); | ||||||
|             if(status != SubGhzProtocolStatusOk) { |             if(status != SubGhzProtocolStatusOk) { | ||||||
|                 load_key_state = SubGhzLoadKeyStateProtocolDescriptionErr; |                 load_key_state = SubGhzLoadKeyStateProtocolDescriptionErr; | ||||||
|                 break; |                 break; | ||||||
| @ -381,17 +213,18 @@ bool subghz_key_load(SubGhz* subghz, const char* file_path, bool show_dialog) { | |||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | SubGhzLoadTypeFile subghz_get_load_type_file(SubGhz* subghz) { | ||||||
|  |     furi_assert(subghz); | ||||||
|  |     return subghz->load_type_file; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| bool subghz_get_next_name_file(SubGhz* subghz, uint8_t max_len) { | bool subghz_get_next_name_file(SubGhz* subghz, uint8_t max_len) { | ||||||
|     furi_assert(subghz); |     furi_assert(subghz); | ||||||
| 
 | 
 | ||||||
|     Storage* storage = furi_record_open(RECORD_STORAGE); |     Storage* storage = furi_record_open(RECORD_STORAGE); | ||||||
|     FuriString* temp_str; |     FuriString* temp_str = furi_string_alloc(); | ||||||
|     FuriString* file_name; |     FuriString* file_name = furi_string_alloc(); | ||||||
|     FuriString* file_path; |     FuriString* file_path = furi_string_alloc(); | ||||||
| 
 |  | ||||||
|     temp_str = furi_string_alloc(); |  | ||||||
|     file_name = furi_string_alloc(); |  | ||||||
|     file_path = furi_string_alloc(); |  | ||||||
| 
 | 
 | ||||||
|     bool res = false; |     bool res = false; | ||||||
| 
 | 
 | ||||||
| @ -438,8 +271,7 @@ bool subghz_save_protocol_to_file( | |||||||
|     Stream* flipper_format_stream = flipper_format_get_raw_stream(flipper_format); |     Stream* flipper_format_stream = flipper_format_get_raw_stream(flipper_format); | ||||||
| 
 | 
 | ||||||
|     bool saved = false; |     bool saved = false; | ||||||
|     FuriString* file_dir; |     FuriString* file_dir = furi_string_alloc(); | ||||||
|     file_dir = furi_string_alloc(); |  | ||||||
| 
 | 
 | ||||||
|     path_extract_dirname(dev_file_name, file_dir); |     path_extract_dirname(dev_file_name, file_dir); | ||||||
|     do { |     do { | ||||||
| @ -467,11 +299,21 @@ bool subghz_save_protocol_to_file( | |||||||
|     return saved; |     return saved; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | void subghz_save_to_file(void* context) { | ||||||
|  |     furi_assert(context); | ||||||
|  |     SubGhz* subghz = context; | ||||||
|  |     if(subghz_path_is_file(subghz->file_path)) { | ||||||
|  |         subghz_save_protocol_to_file( | ||||||
|  |             subghz, | ||||||
|  |             subghz_txrx_get_fff_data(subghz->txrx), | ||||||
|  |             furi_string_get_cstr(subghz->file_path)); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
| bool subghz_load_protocol_from_file(SubGhz* subghz) { | bool subghz_load_protocol_from_file(SubGhz* subghz) { | ||||||
|     furi_assert(subghz); |     furi_assert(subghz); | ||||||
| 
 | 
 | ||||||
|     FuriString* file_path; |     FuriString* file_path = furi_string_alloc(); | ||||||
|     file_path = furi_string_alloc(); |  | ||||||
| 
 | 
 | ||||||
|     DialogsFileBrowserOptions browser_options; |     DialogsFileBrowserOptions browser_options; | ||||||
|     dialog_file_browser_set_basic_options(&browser_options, SUBGHZ_APP_EXTENSION, &I_sub1_10px); |     dialog_file_browser_set_basic_options(&browser_options, SUBGHZ_APP_EXTENSION, &I_sub1_10px); | ||||||
| @ -551,92 +393,27 @@ bool subghz_path_is_file(FuriString* path) { | |||||||
|     return furi_string_end_with(path, SUBGHZ_APP_EXTENSION); |     return furi_string_end_with(path, SUBGHZ_APP_EXTENSION); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| uint32_t subghz_random_serial(void) { | void subghz_lock(SubGhz* subghz) { | ||||||
|     return (uint32_t)rand(); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void subghz_hopper_update(SubGhz* subghz) { |  | ||||||
|     furi_assert(subghz); |     furi_assert(subghz); | ||||||
| 
 |     subghz->lock = SubGhzLockOn; | ||||||
|     switch(subghz->txrx->hopper_state) { |  | ||||||
|     case SubGhzHopperStateOFF: |  | ||||||
|     case SubGhzHopperStatePause: |  | ||||||
|         return; |  | ||||||
|     case SubGhzHopperStateRSSITimeOut: |  | ||||||
|         if(subghz->txrx->hopper_timeout != 0) { |  | ||||||
|             subghz->txrx->hopper_timeout--; |  | ||||||
|             return; |  | ||||||
|         } |  | ||||||
|         break; |  | ||||||
|     default: |  | ||||||
|         break; |  | ||||||
|     } |  | ||||||
|     float rssi = -127.0f; |  | ||||||
|     if(subghz->txrx->hopper_state != SubGhzHopperStateRSSITimeOut) { |  | ||||||
|         // 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) { |  | ||||||
|             subghz->txrx->hopper_timeout = 10; |  | ||||||
|             subghz->txrx->hopper_state = SubGhzHopperStateRSSITimeOut; |  | ||||||
|             return; |  | ||||||
|         } |  | ||||||
|     } else { |  | ||||||
|         subghz->txrx->hopper_state = SubGhzHopperStateRunnig; |  | ||||||
|     } |  | ||||||
|     // Select next frequency
 |  | ||||||
|     if(subghz->txrx->hopper_idx_frequency < |  | ||||||
|        subghz_setting_get_hopper_frequency_count(subghz->setting) - 1) { |  | ||||||
|         subghz->txrx->hopper_idx_frequency++; |  | ||||||
|     } else { |  | ||||||
|         subghz->txrx->hopper_idx_frequency = 0; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     if(subghz->txrx->txrx_state == SubGhzTxRxStateRx) { |  | ||||||
|         subghz_rx_end(subghz); |  | ||||||
|     }; |  | ||||||
|     if(subghz->txrx->txrx_state == SubGhzTxRxStateIDLE) { |  | ||||||
|         subghz_receiver_reset(subghz->txrx->receiver); |  | ||||||
|         subghz->txrx->preset->frequency = subghz_setting_get_hopper_frequency( |  | ||||||
|             subghz->setting, subghz->txrx->hopper_idx_frequency); |  | ||||||
|         subghz_rx(subghz, subghz->txrx->preset->frequency); |  | ||||||
|     } |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void subghz_speaker_on(SubGhz* subghz) { | void subghz_unlock(SubGhz* subghz) { | ||||||
|     if(subghz->txrx->speaker_state == SubGhzSpeakerStateEnable) { |     furi_assert(subghz); | ||||||
|         if(furi_hal_speaker_acquire(30)) { |     subghz->lock = SubGhzLockOff; | ||||||
|             furi_hal_subghz_set_async_mirror_pin(&gpio_speaker); |  | ||||||
|         } else { |  | ||||||
|             subghz->txrx->speaker_state = SubGhzSpeakerStateDisable; |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void subghz_speaker_off(SubGhz* subghz) { | bool subghz_is_locked(SubGhz* subghz) { | ||||||
|     if(subghz->txrx->speaker_state != SubGhzSpeakerStateDisable) { |     furi_assert(subghz); | ||||||
|         if(furi_hal_speaker_is_mine()) { |     return (subghz->lock == SubGhzLockOn); | ||||||
|             furi_hal_subghz_set_async_mirror_pin(NULL); |  | ||||||
|             furi_hal_speaker_release(); |  | ||||||
|             if(subghz->txrx->speaker_state == SubGhzSpeakerStateShutdown) |  | ||||||
|                 subghz->txrx->speaker_state = SubGhzSpeakerStateDisable; |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void subghz_speaker_mute(SubGhz* subghz) { | void subghz_rx_key_state_set(SubGhz* subghz, SubGhzRxKeyState state) { | ||||||
|     if(subghz->txrx->speaker_state == SubGhzSpeakerStateEnable) { |     furi_assert(subghz); | ||||||
|         if(furi_hal_speaker_is_mine()) { |     subghz->rx_key_state = state; | ||||||
|             furi_hal_subghz_set_async_mirror_pin(NULL); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void subghz_speaker_unmute(SubGhz* subghz) { | SubGhzRxKeyState subghz_rx_key_state_get(SubGhz* subghz) { | ||||||
|     if(subghz->txrx->speaker_state == SubGhzSpeakerStateEnable) { |     furi_assert(subghz); | ||||||
|         if(furi_hal_speaker_is_mine()) { |     return subghz->rx_key_state; | ||||||
|             furi_hal_subghz_set_async_mirror_pin(&gpio_speaker); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } | } | ||||||
|  | |||||||
| @ -25,10 +25,6 @@ | |||||||
| #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_setting.h> |  | ||||||
| #include <lib/subghz/receiver.h> |  | ||||||
| #include <lib/subghz/transmitter.h> |  | ||||||
| 
 | 
 | ||||||
| #include "subghz_history.h" | #include "subghz_history.h" | ||||||
| 
 | 
 | ||||||
| @ -37,34 +33,12 @@ | |||||||
| 
 | 
 | ||||||
| #include "rpc/rpc_app.h" | #include "rpc/rpc_app.h" | ||||||
| 
 | 
 | ||||||
|  | #include "helpers/subghz_threshold_rssi.h" | ||||||
|  | 
 | ||||||
|  | #include "helpers/subghz_txrx.h" | ||||||
|  | 
 | ||||||
| #define SUBGHZ_MAX_LEN_NAME 64 | #define SUBGHZ_MAX_LEN_NAME 64 | ||||||
| 
 | 
 | ||||||
| struct SubGhzTxRx { |  | ||||||
|     SubGhzWorker* worker; |  | ||||||
| 
 |  | ||||||
|     SubGhzEnvironment* environment; |  | ||||||
|     SubGhzReceiver* receiver; |  | ||||||
|     SubGhzTransmitter* transmitter; |  | ||||||
|     SubGhzProtocolFlag filter; |  | ||||||
|     SubGhzProtocolDecoderBase* decoder_result; |  | ||||||
|     FlipperFormat* fff_data; |  | ||||||
| 
 |  | ||||||
|     SubGhzRadioPreset* preset; |  | ||||||
|     SubGhzHistory* history; |  | ||||||
|     uint16_t idx_menu_chosen; |  | ||||||
|     SubGhzTxRxState txrx_state; |  | ||||||
|     SubGhzHopperState hopper_state; |  | ||||||
|     SubGhzSpeakerState speaker_state; |  | ||||||
|     uint8_t hopper_timeout; |  | ||||||
|     uint8_t hopper_idx_frequency; |  | ||||||
|     SubGhzRxKeyState rx_key_state; |  | ||||||
| 
 |  | ||||||
|     float raw_threshold_rssi; |  | ||||||
|     uint8_t raw_threshold_rssi_low_count; |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| typedef struct SubGhzTxRx SubGhzTxRx; |  | ||||||
| 
 |  | ||||||
| struct SubGhz { | struct SubGhz { | ||||||
|     Gui* gui; |     Gui* gui; | ||||||
|     NotificationApp* notifications; |     NotificationApp* notifications; | ||||||
| @ -93,47 +67,43 @@ struct SubGhz { | |||||||
|     SubGhzTestStatic* subghz_test_static; |     SubGhzTestStatic* subghz_test_static; | ||||||
|     SubGhzTestCarrier* subghz_test_carrier; |     SubGhzTestCarrier* subghz_test_carrier; | ||||||
|     SubGhzTestPacket* subghz_test_packet; |     SubGhzTestPacket* subghz_test_packet; | ||||||
|     FuriString* error_str; |  | ||||||
|     SubGhzSetting* setting; |  | ||||||
|     SubGhzLock lock; |  | ||||||
| 
 | 
 | ||||||
|  |     SubGhzProtocolFlag filter; | ||||||
|  |     FuriString* error_str; | ||||||
|  |     SubGhzLock lock; | ||||||
|  |     SubGhzThresholdRssi* threshold_rssi; | ||||||
|  |     SubGhzRxKeyState rx_key_state; | ||||||
|  |     SubGhzHistory* history; | ||||||
|  |     uint16_t idx_menu_chosen; | ||||||
|  |     SubGhzLoadTypeFile load_type_file; | ||||||
|     void* rpc_ctx; |     void* rpc_ctx; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| void subghz_preset_init( | void subghz_set_default_preset(SubGhz* subghz); | ||||||
|     void* context, | void subghz_blink_start(SubGhz* subghz); | ||||||
|     const char* preset_name, | void subghz_blink_stop(SubGhz* subghz); | ||||||
|     uint32_t frequency, |  | ||||||
|     uint8_t* preset_data, |  | ||||||
|     size_t preset_data_size); |  | ||||||
| bool subghz_set_preset(SubGhz* subghz, const char* preset); |  | ||||||
| void subghz_get_frequency_modulation(SubGhz* subghz, FuriString* frequency, FuriString* modulation); |  | ||||||
| void subghz_begin(SubGhz* subghz, uint8_t* preset_data); |  | ||||||
| uint32_t subghz_rx(SubGhz* subghz, uint32_t frequency); |  | ||||||
| void subghz_rx_end(SubGhz* subghz); |  | ||||||
| void subghz_sleep(SubGhz* subghz); |  | ||||||
| 
 |  | ||||||
| void subghz_blink_start(SubGhz* instance); |  | ||||||
| void subghz_blink_stop(SubGhz* instance); |  | ||||||
| 
 | 
 | ||||||
| bool subghz_tx_start(SubGhz* subghz, FlipperFormat* flipper_format); | bool subghz_tx_start(SubGhz* subghz, FlipperFormat* flipper_format); | ||||||
| void subghz_tx_stop(SubGhz* subghz); |  | ||||||
| void subghz_dialog_message_show_only_rx(SubGhz* subghz); | void subghz_dialog_message_show_only_rx(SubGhz* subghz); | ||||||
|  | 
 | ||||||
| bool subghz_key_load(SubGhz* subghz, const char* file_path, bool show_dialog); | bool subghz_key_load(SubGhz* subghz, const char* file_path, bool show_dialog); | ||||||
| bool subghz_get_next_name_file(SubGhz* subghz, uint8_t max_len); | bool subghz_get_next_name_file(SubGhz* subghz, uint8_t max_len); | ||||||
| bool subghz_save_protocol_to_file( | bool subghz_save_protocol_to_file( | ||||||
|     SubGhz* subghz, |     SubGhz* subghz, | ||||||
|     FlipperFormat* flipper_format, |     FlipperFormat* flipper_format, | ||||||
|     const char* dev_file_name); |     const char* dev_file_name); | ||||||
|  | void subghz_save_to_file(void* context); | ||||||
| bool subghz_load_protocol_from_file(SubGhz* subghz); | bool subghz_load_protocol_from_file(SubGhz* subghz); | ||||||
| bool subghz_rename_file(SubGhz* subghz); | bool subghz_rename_file(SubGhz* subghz); | ||||||
| bool subghz_file_available(SubGhz* subghz); | bool subghz_file_available(SubGhz* subghz); | ||||||
| bool subghz_delete_file(SubGhz* subghz); | bool subghz_delete_file(SubGhz* subghz); | ||||||
| void subghz_file_name_clear(SubGhz* subghz); | void subghz_file_name_clear(SubGhz* subghz); | ||||||
| bool subghz_path_is_file(FuriString* path); | bool subghz_path_is_file(FuriString* path); | ||||||
| uint32_t subghz_random_serial(void); | SubGhzLoadTypeFile subghz_get_load_type_file(SubGhz* subghz); | ||||||
| void subghz_hopper_update(SubGhz* subghz); | 
 | ||||||
| void subghz_speaker_on(SubGhz* subghz); | void subghz_lock(SubGhz* subghz); | ||||||
| void subghz_speaker_off(SubGhz* subghz); | void subghz_unlock(SubGhz* subghz); | ||||||
| void subghz_speaker_mute(SubGhz* subghz); | bool subghz_is_locked(SubGhz* subghz); | ||||||
| void subghz_speaker_unmute(SubGhz* subghz); | 
 | ||||||
|  | void subghz_rx_key_state_set(SubGhz* subghz, SubGhzRxKeyState state); | ||||||
|  | SubGhzRxKeyState subghz_rx_key_state_get(SubGhz* subghz); | ||||||
|  | |||||||
| @ -12,7 +12,7 @@ | |||||||
| #define MENU_ITEMS 4u | #define MENU_ITEMS 4u | ||||||
| #define UNLOCK_CNT 3 | #define UNLOCK_CNT 3 | ||||||
| 
 | 
 | ||||||
| #define SUBGHZ_RAW_TRESHOLD_MIN -90.0f | #define SUBGHZ_RAW_THRESHOLD_MIN -90.0f | ||||||
| 
 | 
 | ||||||
| typedef struct { | typedef struct { | ||||||
|     FuriString* item_str; |     FuriString* item_str; | ||||||
| @ -44,7 +44,7 @@ typedef enum { | |||||||
| } SubGhzViewReceiverBarShow; | } SubGhzViewReceiverBarShow; | ||||||
| 
 | 
 | ||||||
| struct SubGhzViewReceiver { | struct SubGhzViewReceiver { | ||||||
|     SubGhzLock lock; |     bool lock; | ||||||
|     uint8_t lock_count; |     uint8_t lock_count; | ||||||
|     FuriTimer* timer; |     FuriTimer* timer; | ||||||
|     View* view; |     View* view; | ||||||
| @ -70,20 +70,21 @@ void subghz_receiver_rssi(SubGhzViewReceiver* instance, float rssi) { | |||||||
|         instance->view, |         instance->view, | ||||||
|         SubGhzViewReceiverModel * model, |         SubGhzViewReceiverModel * model, | ||||||
|         { |         { | ||||||
|             if(rssi < SUBGHZ_RAW_TRESHOLD_MIN) { |             if(rssi < SUBGHZ_RAW_THRESHOLD_MIN) { | ||||||
|                 model->u_rssi = 0; |                 model->u_rssi = 0; | ||||||
|             } else { |             } else { | ||||||
|                 model->u_rssi = (uint8_t)(rssi - SUBGHZ_RAW_TRESHOLD_MIN); |                 model->u_rssi = (uint8_t)(rssi - SUBGHZ_RAW_THRESHOLD_MIN); | ||||||
|             } |             } | ||||||
|         }, |         }, | ||||||
|         true); |         true); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void subghz_view_receiver_set_lock(SubGhzViewReceiver* subghz_receiver, SubGhzLock lock) { | void subghz_view_receiver_set_lock(SubGhzViewReceiver* subghz_receiver, bool lock) { | ||||||
|     furi_assert(subghz_receiver); |     furi_assert(subghz_receiver); | ||||||
|     subghz_receiver->lock_count = 0; |     subghz_receiver->lock_count = 0; | ||||||
|     if(lock == SubGhzLockOn) { | 
 | ||||||
|         subghz_receiver->lock = lock; |     if(lock == true) { | ||||||
|  |         subghz_receiver->lock = true; | ||||||
|         with_view_model( |         with_view_model( | ||||||
|             subghz_receiver->view, |             subghz_receiver->view, | ||||||
|             SubGhzViewReceiverModel * model, |             SubGhzViewReceiverModel * model, | ||||||
| @ -280,7 +281,7 @@ static void subghz_view_receiver_timer_callback(void* context) { | |||||||
|         subghz_receiver->callback( |         subghz_receiver->callback( | ||||||
|             SubGhzCustomEventViewReceiverOffDisplay, subghz_receiver->context); |             SubGhzCustomEventViewReceiverOffDisplay, subghz_receiver->context); | ||||||
|     } else { |     } else { | ||||||
|         subghz_receiver->lock = SubGhzLockOff; |         subghz_receiver->lock = false; | ||||||
|         subghz_receiver->callback(SubGhzCustomEventViewReceiverUnlock, subghz_receiver->context); |         subghz_receiver->callback(SubGhzCustomEventViewReceiverUnlock, subghz_receiver->context); | ||||||
|     } |     } | ||||||
|     subghz_receiver->lock_count = 0; |     subghz_receiver->lock_count = 0; | ||||||
| @ -290,7 +291,7 @@ bool subghz_view_receiver_input(InputEvent* event, void* context) { | |||||||
|     furi_assert(context); |     furi_assert(context); | ||||||
|     SubGhzViewReceiver* subghz_receiver = context; |     SubGhzViewReceiver* subghz_receiver = context; | ||||||
| 
 | 
 | ||||||
|     if(subghz_receiver->lock == SubGhzLockOn) { |     if(subghz_receiver->lock == true) { | ||||||
|         with_view_model( |         with_view_model( | ||||||
|             subghz_receiver->view, |             subghz_receiver->view, | ||||||
|             SubGhzViewReceiverModel * model, |             SubGhzViewReceiverModel * model, | ||||||
| @ -310,7 +311,7 @@ bool subghz_view_receiver_input(InputEvent* event, void* context) { | |||||||
|                 SubGhzViewReceiverModel * model, |                 SubGhzViewReceiverModel * model, | ||||||
|                 { model->bar_show = SubGhzViewReceiverBarShowUnlock; }, |                 { model->bar_show = SubGhzViewReceiverBarShowUnlock; }, | ||||||
|                 true); |                 true); | ||||||
|             //subghz_receiver->lock = SubGhzLockOff;
 |             //subghz_receiver->lock = false;
 | ||||||
|             furi_timer_start(subghz_receiver->timer, pdMS_TO_TICKS(650)); |             furi_timer_start(subghz_receiver->timer, pdMS_TO_TICKS(650)); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
| @ -394,7 +395,7 @@ SubGhzViewReceiver* subghz_view_receiver_alloc() { | |||||||
|     // View allocation and configuration
 |     // View allocation and configuration
 | ||||||
|     subghz_receiver->view = view_alloc(); |     subghz_receiver->view = view_alloc(); | ||||||
| 
 | 
 | ||||||
|     subghz_receiver->lock = SubGhzLockOff; |     subghz_receiver->lock = false; | ||||||
|     subghz_receiver->lock_count = 0; |     subghz_receiver->lock_count = 0; | ||||||
|     view_allocate_model( |     view_allocate_model( | ||||||
|         subghz_receiver->view, ViewModelTypeLocking, sizeof(SubGhzViewReceiverModel)); |         subghz_receiver->view, ViewModelTypeLocking, sizeof(SubGhzViewReceiverModel)); | ||||||
|  | |||||||
| @ -10,7 +10,7 @@ typedef void (*SubGhzViewReceiverCallback)(SubGhzCustomEvent event, void* contex | |||||||
| 
 | 
 | ||||||
| void subghz_receiver_rssi(SubGhzViewReceiver* instance, float rssi); | void subghz_receiver_rssi(SubGhzViewReceiver* instance, float rssi); | ||||||
| 
 | 
 | ||||||
| void subghz_view_receiver_set_lock(SubGhzViewReceiver* subghz_receiver, SubGhzLock keyboard); | void subghz_view_receiver_set_lock(SubGhzViewReceiver* subghz_receiver, bool keyboard); | ||||||
| 
 | 
 | ||||||
| void subghz_view_receiver_set_callback( | void subghz_view_receiver_set_callback( | ||||||
|     SubGhzViewReceiver* subghz_receiver, |     SubGhzViewReceiver* subghz_receiver, | ||||||
|  | |||||||
| @ -60,10 +60,10 @@ void subghz_read_raw_add_data_rssi(SubGhzReadRAW* instance, float rssi, bool tra | |||||||
|     furi_assert(instance); |     furi_assert(instance); | ||||||
|     uint8_t u_rssi = 0; |     uint8_t u_rssi = 0; | ||||||
| 
 | 
 | ||||||
|     if(rssi < SUBGHZ_RAW_TRESHOLD_MIN) { |     if(rssi < SUBGHZ_RAW_THRESHOLD_MIN) { | ||||||
|         u_rssi = 0; |         u_rssi = 0; | ||||||
|     } else { |     } else { | ||||||
|         u_rssi = (uint8_t)((rssi - SUBGHZ_RAW_TRESHOLD_MIN) / 2.7); |         u_rssi = (uint8_t)((rssi - SUBGHZ_RAW_THRESHOLD_MIN) / 2.7); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     with_view_model( |     with_view_model( | ||||||
| @ -261,9 +261,9 @@ void subghz_read_raw_draw_threshold_rssi(Canvas* canvas, SubGhzReadRAWModel* mod | |||||||
|     uint8_t x = 118; |     uint8_t x = 118; | ||||||
|     uint8_t y = 48; |     uint8_t y = 48; | ||||||
| 
 | 
 | ||||||
|     if(model->raw_threshold_rssi > SUBGHZ_RAW_TRESHOLD_MIN) { |     if(model->raw_threshold_rssi > SUBGHZ_RAW_THRESHOLD_MIN) { | ||||||
|         uint8_t x = 118; |         uint8_t x = 118; | ||||||
|         y -= (uint8_t)((model->raw_threshold_rssi - SUBGHZ_RAW_TRESHOLD_MIN) / 2.7); |         y -= (uint8_t)((model->raw_threshold_rssi - SUBGHZ_RAW_THRESHOLD_MIN) / 2.7); | ||||||
| 
 | 
 | ||||||
|         uint8_t width = 3; |         uint8_t width = 3; | ||||||
|         for(uint8_t i = 0; i < x; i += width * 2) { |         for(uint8_t i = 0; i < x; i += width * 2) { | ||||||
|  | |||||||
| @ -3,7 +3,7 @@ | |||||||
| #include <gui/view.h> | #include <gui/view.h> | ||||||
| #include "../helpers/subghz_custom_event.h" | #include "../helpers/subghz_custom_event.h" | ||||||
| 
 | 
 | ||||||
| #define SUBGHZ_RAW_TRESHOLD_MIN -90.0f | #define SUBGHZ_RAW_THRESHOLD_MIN -90.0f | ||||||
| 
 | 
 | ||||||
| typedef struct SubGhzReadRAW SubGhzReadRAW; | typedef struct SubGhzReadRAW SubGhzReadRAW; | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -14,7 +14,7 @@ typedef struct { | |||||||
|     FuriString* frequency_str; |     FuriString* frequency_str; | ||||||
|     FuriString* preset_str; |     FuriString* preset_str; | ||||||
|     FuriString* key_str; |     FuriString* key_str; | ||||||
|     uint8_t show_button; |     bool show_button; | ||||||
| } SubGhzViewTransmitterModel; | } SubGhzViewTransmitterModel; | ||||||
| 
 | 
 | ||||||
| void subghz_view_transmitter_set_callback( | void subghz_view_transmitter_set_callback( | ||||||
| @ -32,7 +32,7 @@ void subghz_view_transmitter_add_data_to_show( | |||||||
|     const char* key_str, |     const char* key_str, | ||||||
|     const char* frequency_str, |     const char* frequency_str, | ||||||
|     const char* preset_str, |     const char* preset_str, | ||||||
|     uint8_t show_button) { |     bool show_button) { | ||||||
|     furi_assert(subghz_transmitter); |     furi_assert(subghz_transmitter); | ||||||
|     with_view_model( |     with_view_model( | ||||||
|         subghz_transmitter->view, |         subghz_transmitter->view, | ||||||
| @ -104,7 +104,7 @@ bool subghz_view_transmitter_input(InputEvent* event, void* context) { | |||||||
|                 furi_string_reset(model->frequency_str); |                 furi_string_reset(model->frequency_str); | ||||||
|                 furi_string_reset(model->preset_str); |                 furi_string_reset(model->preset_str); | ||||||
|                 furi_string_reset(model->key_str); |                 furi_string_reset(model->key_str); | ||||||
|                 model->show_button = 0; |                 model->show_button = false; | ||||||
|             }, |             }, | ||||||
|             false); |             false); | ||||||
|         return false; |         return false; | ||||||
|  | |||||||
| @ -23,4 +23,4 @@ void subghz_view_transmitter_add_data_to_show( | |||||||
|     const char* key_str, |     const char* key_str, | ||||||
|     const char* frequency_str, |     const char* frequency_str, | ||||||
|     const char* preset_str, |     const char* preset_str, | ||||||
|     uint8_t show_button); |     bool show_button); | ||||||
|  | |||||||
| @ -220,11 +220,9 @@ void cli_command_sysctl_debug(Cli* cli, FuriString* args, void* context) { | |||||||
|     UNUSED(context); |     UNUSED(context); | ||||||
|     if(!furi_string_cmp(args, "0")) { |     if(!furi_string_cmp(args, "0")) { | ||||||
|         furi_hal_rtc_reset_flag(FuriHalRtcFlagDebug); |         furi_hal_rtc_reset_flag(FuriHalRtcFlagDebug); | ||||||
|         loader_update_menu(); |  | ||||||
|         printf("Debug disabled."); |         printf("Debug disabled."); | ||||||
|     } else if(!furi_string_cmp(args, "1")) { |     } else if(!furi_string_cmp(args, "1")) { | ||||||
|         furi_hal_rtc_set_flag(FuriHalRtcFlagDebug); |         furi_hal_rtc_set_flag(FuriHalRtcFlagDebug); | ||||||
|         loader_update_menu(); |  | ||||||
|         printf("Debug enabled."); |         printf("Debug enabled."); | ||||||
|     } else { |     } else { | ||||||
|         cli_print_usage("sysctl debug", "<1|0>", furi_string_get_cstr(args)); |         cli_print_usage("sysctl debug", "<1|0>", furi_string_get_cstr(args)); | ||||||
|  | |||||||
| @ -6,6 +6,8 @@ | |||||||
| #include <notification/notification_messages.h> | #include <notification/notification_messages.h> | ||||||
| #include <furi.h> | #include <furi.h> | ||||||
| #include <furi_hal.h> | #include <furi_hal.h> | ||||||
|  | #include <cli/cli.h> | ||||||
|  | #include <cli/cli_vcp.h> | ||||||
| 
 | 
 | ||||||
| #include "animations/animation_manager.h" | #include "animations/animation_manager.h" | ||||||
| #include "desktop/scenes/desktop_scene.h" | #include "desktop/scenes/desktop_scene.h" | ||||||
| @ -14,7 +16,7 @@ | |||||||
| #include "desktop/views/desktop_view_pin_input.h" | #include "desktop/views/desktop_view_pin_input.h" | ||||||
| #include "desktop/views/desktop_view_pin_timeout.h" | #include "desktop/views/desktop_view_pin_timeout.h" | ||||||
| #include "desktop_i.h" | #include "desktop_i.h" | ||||||
| #include "helpers/pin_lock.h" | #include "helpers/pin.h" | ||||||
| #include "helpers/slideshow_filename.h" | #include "helpers/slideshow_filename.h" | ||||||
| 
 | 
 | ||||||
| #define TAG "Desktop" | #define TAG "Desktop" | ||||||
| @ -132,6 +134,14 @@ static void desktop_auto_lock_inhibit(Desktop* desktop) { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void desktop_lock(Desktop* desktop) { | void desktop_lock(Desktop* desktop) { | ||||||
|  |     furi_hal_rtc_set_flag(FuriHalRtcFlagLock); | ||||||
|  | 
 | ||||||
|  |     if(desktop->settings.pin_code.length) { | ||||||
|  |         Cli* cli = furi_record_open(RECORD_CLI); | ||||||
|  |         cli_session_close(cli); | ||||||
|  |         furi_record_close(RECORD_CLI); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     desktop_auto_lock_inhibit(desktop); |     desktop_auto_lock_inhibit(desktop); | ||||||
|     scene_manager_set_scene_state( |     scene_manager_set_scene_state( | ||||||
|         desktop->scene_manager, DesktopSceneLocked, SCENE_LOCKED_FIRST_ENTER); |         desktop->scene_manager, DesktopSceneLocked, SCENE_LOCKED_FIRST_ENTER); | ||||||
| @ -147,6 +157,14 @@ void desktop_unlock(Desktop* desktop) { | |||||||
|     desktop_view_locked_unlock(desktop->locked_view); |     desktop_view_locked_unlock(desktop->locked_view); | ||||||
|     scene_manager_search_and_switch_to_previous_scene(desktop->scene_manager, DesktopSceneMain); |     scene_manager_search_and_switch_to_previous_scene(desktop->scene_manager, DesktopSceneMain); | ||||||
|     desktop_auto_lock_arm(desktop); |     desktop_auto_lock_arm(desktop); | ||||||
|  |     furi_hal_rtc_reset_flag(FuriHalRtcFlagLock); | ||||||
|  |     furi_hal_rtc_set_pin_fails(0); | ||||||
|  | 
 | ||||||
|  |     if(desktop->settings.pin_code.length) { | ||||||
|  |         Cli* cli = furi_record_open(RECORD_CLI); | ||||||
|  |         cli_session_open(cli, &cli_vcp); | ||||||
|  |         furi_record_close(RECORD_CLI); | ||||||
|  |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void desktop_set_dummy_mode_state(Desktop* desktop, bool enabled) { | void desktop_set_dummy_mode_state(Desktop* desktop, bool enabled) { | ||||||
| @ -290,11 +308,14 @@ Desktop* desktop_alloc() { | |||||||
|     desktop->auto_lock_timer = |     desktop->auto_lock_timer = | ||||||
|         furi_timer_alloc(desktop_auto_lock_timer_callback, FuriTimerTypeOnce, desktop); |         furi_timer_alloc(desktop_auto_lock_timer_callback, FuriTimerTypeOnce, desktop); | ||||||
| 
 | 
 | ||||||
|  |     furi_record_create(RECORD_DESKTOP, desktop); | ||||||
|  | 
 | ||||||
|     return desktop; |     return desktop; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void desktop_free(Desktop* desktop) { | void desktop_free(Desktop* desktop) { | ||||||
|     furi_assert(desktop); |     furi_assert(desktop); | ||||||
|  |     furi_check(furi_record_destroy(RECORD_DESKTOP)); | ||||||
| 
 | 
 | ||||||
|     furi_pubsub_unsubscribe( |     furi_pubsub_unsubscribe( | ||||||
|         loader_get_pubsub(desktop->loader), desktop->app_start_stop_subscription); |         loader_get_pubsub(desktop->loader), desktop->app_start_stop_subscription); | ||||||
| @ -352,6 +373,16 @@ static bool desktop_check_file_flag(const char* flag_path) { | |||||||
|     return exists; |     return exists; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | bool desktop_api_is_locked(Desktop* instance) { | ||||||
|  |     furi_assert(instance); | ||||||
|  |     return furi_hal_rtc_is_flag_set(FuriHalRtcFlagLock); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void desktop_api_unlock(Desktop* instance) { | ||||||
|  |     furi_assert(instance); | ||||||
|  |     view_dispatcher_send_custom_event(instance->view_dispatcher, DesktopLockedEventUnlocked); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| int32_t desktop_srv(void* p) { | int32_t desktop_srv(void* p) { | ||||||
|     UNUSED(p); |     UNUSED(p); | ||||||
| 
 | 
 | ||||||
| @ -375,14 +406,12 @@ int32_t desktop_srv(void* p) { | |||||||
| 
 | 
 | ||||||
|     scene_manager_next_scene(desktop->scene_manager, DesktopSceneMain); |     scene_manager_next_scene(desktop->scene_manager, DesktopSceneMain); | ||||||
| 
 | 
 | ||||||
|     desktop_pin_lock_init(&desktop->settings); |     if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagLock)) { | ||||||
| 
 |         desktop_lock(desktop); | ||||||
|     if(!desktop_pin_lock_is_locked()) { |     } else { | ||||||
|         if(!loader_is_locked(desktop->loader)) { |         if(!loader_is_locked(desktop->loader)) { | ||||||
|             desktop_auto_lock_arm(desktop); |             desktop_auto_lock_arm(desktop); | ||||||
|         } |         } | ||||||
|     } else { |  | ||||||
|         desktop_lock(desktop); |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     if(desktop_check_file_flag(SLIDESHOW_FS_PATH)) { |     if(desktop_check_file_flag(SLIDESHOW_FS_PATH)) { | ||||||
|  | |||||||
| @ -1,3 +1,9 @@ | |||||||
| #pragma once | #pragma once | ||||||
| 
 | 
 | ||||||
| typedef struct Desktop Desktop; | typedef struct Desktop Desktop; | ||||||
|  | 
 | ||||||
|  | #define RECORD_DESKTOP "desktop" | ||||||
|  | 
 | ||||||
|  | bool desktop_api_is_locked(Desktop* instance); | ||||||
|  | 
 | ||||||
|  | void desktop_api_unlock(Desktop* instance); | ||||||
|  | |||||||
| @ -8,7 +8,7 @@ | |||||||
| #include <toolbox/saved_struct.h> | #include <toolbox/saved_struct.h> | ||||||
| #include <storage/storage.h> | #include <storage/storage.h> | ||||||
| 
 | 
 | ||||||
| #define DESKTOP_SETTINGS_VER (6) | #define DESKTOP_SETTINGS_VER (7) | ||||||
| 
 | 
 | ||||||
| #define DESKTOP_SETTINGS_PATH INT_PATH(DESKTOP_SETTINGS_FILE_NAME) | #define DESKTOP_SETTINGS_PATH INT_PATH(DESKTOP_SETTINGS_FILE_NAME) | ||||||
| #define DESKTOP_SETTINGS_MAGIC (0x17) | #define DESKTOP_SETTINGS_MAGIC (0x17) | ||||||
| @ -52,7 +52,6 @@ typedef struct { | |||||||
|     FavoriteApp favorite_primary; |     FavoriteApp favorite_primary; | ||||||
|     FavoriteApp favorite_secondary; |     FavoriteApp favorite_secondary; | ||||||
|     PinCode pin_code; |     PinCode pin_code; | ||||||
|     uint8_t is_locked; |  | ||||||
|     uint32_t auto_lock_delay_ms; |     uint32_t auto_lock_delay_ms; | ||||||
|     uint8_t dummy_mode; |     uint8_t dummy_mode; | ||||||
| } DesktopSettings; | } DesktopSettings; | ||||||
|  | |||||||
							
								
								
									
										74
									
								
								applications/services/desktop/helpers/pin.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										74
									
								
								applications/services/desktop/helpers/pin.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,74 @@ | |||||||
|  | #include "pin.h" | ||||||
|  | 
 | ||||||
|  | #include <notification/notification.h> | ||||||
|  | #include <notification/notification_messages.h> | ||||||
|  | #include <stddef.h> | ||||||
|  | #include <furi.h> | ||||||
|  | #include <furi_hal.h> | ||||||
|  | #include <gui/gui.h> | ||||||
|  | 
 | ||||||
|  | #include "../desktop_i.h" | ||||||
|  | 
 | ||||||
|  | static const NotificationSequence sequence_pin_fail = { | ||||||
|  |     &message_display_backlight_on, | ||||||
|  | 
 | ||||||
|  |     &message_red_255, | ||||||
|  |     &message_vibro_on, | ||||||
|  |     &message_delay_100, | ||||||
|  |     &message_vibro_off, | ||||||
|  |     &message_red_0, | ||||||
|  | 
 | ||||||
|  |     &message_delay_250, | ||||||
|  | 
 | ||||||
|  |     &message_red_255, | ||||||
|  |     &message_vibro_on, | ||||||
|  |     &message_delay_100, | ||||||
|  |     &message_vibro_off, | ||||||
|  |     &message_red_0, | ||||||
|  |     NULL, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | static const uint8_t desktop_helpers_fails_timeout[] = { | ||||||
|  |     0, | ||||||
|  |     0, | ||||||
|  |     0, | ||||||
|  |     0, | ||||||
|  |     30, | ||||||
|  |     60, | ||||||
|  |     90, | ||||||
|  |     120, | ||||||
|  |     150, | ||||||
|  |     180, | ||||||
|  |     /* +60 for every next fail */ | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | void desktop_pin_lock_error_notify() { | ||||||
|  |     NotificationApp* notification = furi_record_open(RECORD_NOTIFICATION); | ||||||
|  |     notification_message(notification, &sequence_pin_fail); | ||||||
|  |     furi_record_close(RECORD_NOTIFICATION); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | uint32_t desktop_pin_lock_get_fail_timeout() { | ||||||
|  |     uint32_t pin_fails = furi_hal_rtc_get_pin_fails(); | ||||||
|  |     uint32_t pin_timeout = 0; | ||||||
|  |     uint32_t max_index = COUNT_OF(desktop_helpers_fails_timeout) - 1; | ||||||
|  |     if(pin_fails <= max_index) { | ||||||
|  |         pin_timeout = desktop_helpers_fails_timeout[pin_fails]; | ||||||
|  |     } else { | ||||||
|  |         pin_timeout = desktop_helpers_fails_timeout[max_index] + (pin_fails - max_index) * 60; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return pin_timeout; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool desktop_pin_compare(const PinCode* pin_code1, const PinCode* pin_code2) { | ||||||
|  |     furi_assert(pin_code1); | ||||||
|  |     furi_assert(pin_code2); | ||||||
|  |     bool result = false; | ||||||
|  | 
 | ||||||
|  |     if(pin_code1->length == pin_code2->length) { | ||||||
|  |         result = !memcmp(pin_code1->data, pin_code2->data, pin_code1->length); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return result; | ||||||
|  | } | ||||||
							
								
								
									
										11
									
								
								applications/services/desktop/helpers/pin.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								applications/services/desktop/helpers/pin.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,11 @@ | |||||||
|  | #pragma once | ||||||
|  | #include <stdbool.h> | ||||||
|  | #include <stdint.h> | ||||||
|  | #include "../desktop.h" | ||||||
|  | #include <desktop/desktop_settings.h> | ||||||
|  | 
 | ||||||
|  | void desktop_pin_lock_error_notify(); | ||||||
|  | 
 | ||||||
|  | uint32_t desktop_pin_lock_get_fail_timeout(); | ||||||
|  | 
 | ||||||
|  | bool desktop_pin_compare(const PinCode* pin_code1, const PinCode* pin_code2); | ||||||
| @ -1,140 +0,0 @@ | |||||||
| 
 |  | ||||||
| #include <notification/notification.h> |  | ||||||
| #include <notification/notification_messages.h> |  | ||||||
| #include <stddef.h> |  | ||||||
| #include <furi.h> |  | ||||||
| #include <furi_hal.h> |  | ||||||
| #include <gui/gui.h> |  | ||||||
| 
 |  | ||||||
| #include "../helpers/pin_lock.h" |  | ||||||
| #include "../desktop_i.h" |  | ||||||
| #include <cli/cli.h> |  | ||||||
| #include <cli/cli_vcp.h> |  | ||||||
| 
 |  | ||||||
| static const NotificationSequence sequence_pin_fail = { |  | ||||||
|     &message_display_backlight_on, |  | ||||||
| 
 |  | ||||||
|     &message_red_255, |  | ||||||
|     &message_vibro_on, |  | ||||||
|     &message_delay_100, |  | ||||||
|     &message_vibro_off, |  | ||||||
|     &message_red_0, |  | ||||||
| 
 |  | ||||||
|     &message_delay_250, |  | ||||||
| 
 |  | ||||||
|     &message_red_255, |  | ||||||
|     &message_vibro_on, |  | ||||||
|     &message_delay_100, |  | ||||||
|     &message_vibro_off, |  | ||||||
|     &message_red_0, |  | ||||||
|     NULL, |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| static const uint8_t desktop_helpers_fails_timeout[] = { |  | ||||||
|     0, |  | ||||||
|     0, |  | ||||||
|     0, |  | ||||||
|     0, |  | ||||||
|     30, |  | ||||||
|     60, |  | ||||||
|     90, |  | ||||||
|     120, |  | ||||||
|     150, |  | ||||||
|     180, |  | ||||||
|     /* +60 for every next fail */ |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| void desktop_pin_lock_error_notify() { |  | ||||||
|     NotificationApp* notification = furi_record_open(RECORD_NOTIFICATION); |  | ||||||
|     notification_message(notification, &sequence_pin_fail); |  | ||||||
|     furi_record_close(RECORD_NOTIFICATION); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| uint32_t desktop_pin_lock_get_fail_timeout() { |  | ||||||
|     uint32_t pin_fails = furi_hal_rtc_get_pin_fails(); |  | ||||||
|     uint32_t pin_timeout = 0; |  | ||||||
|     uint32_t max_index = COUNT_OF(desktop_helpers_fails_timeout) - 1; |  | ||||||
|     if(pin_fails <= max_index) { |  | ||||||
|         pin_timeout = desktop_helpers_fails_timeout[pin_fails]; |  | ||||||
|     } else { |  | ||||||
|         pin_timeout = desktop_helpers_fails_timeout[max_index] + (pin_fails - max_index) * 60; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     return pin_timeout; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void desktop_pin_lock(DesktopSettings* settings) { |  | ||||||
|     furi_assert(settings); |  | ||||||
| 
 |  | ||||||
|     furi_hal_rtc_set_pin_fails(0); |  | ||||||
|     furi_hal_rtc_set_flag(FuriHalRtcFlagLock); |  | ||||||
|     Cli* cli = furi_record_open(RECORD_CLI); |  | ||||||
|     cli_session_close(cli); |  | ||||||
|     furi_record_close(RECORD_CLI); |  | ||||||
|     settings->is_locked = 1; |  | ||||||
|     DESKTOP_SETTINGS_SAVE(settings); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void desktop_pin_unlock(DesktopSettings* settings) { |  | ||||||
|     furi_assert(settings); |  | ||||||
| 
 |  | ||||||
|     furi_hal_rtc_reset_flag(FuriHalRtcFlagLock); |  | ||||||
|     Cli* cli = furi_record_open(RECORD_CLI); |  | ||||||
|     cli_session_open(cli, &cli_vcp); |  | ||||||
|     furi_record_close(RECORD_CLI); |  | ||||||
|     settings->is_locked = 0; |  | ||||||
|     DESKTOP_SETTINGS_SAVE(settings); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void desktop_pin_lock_init(DesktopSettings* settings) { |  | ||||||
|     furi_assert(settings); |  | ||||||
| 
 |  | ||||||
|     if(settings->pin_code.length > 0) { |  | ||||||
|         if(settings->is_locked == 1) { |  | ||||||
|             furi_hal_rtc_set_flag(FuriHalRtcFlagLock); |  | ||||||
|         } else { |  | ||||||
|             if(desktop_pin_lock_is_locked()) { |  | ||||||
|                 settings->is_locked = 1; |  | ||||||
|                 DESKTOP_SETTINGS_SAVE(settings); |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|     } else { |  | ||||||
|         furi_hal_rtc_set_pin_fails(0); |  | ||||||
|         furi_hal_rtc_reset_flag(FuriHalRtcFlagLock); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     if(desktop_pin_lock_is_locked()) { |  | ||||||
|         Cli* cli = furi_record_open(RECORD_CLI); |  | ||||||
|         cli_session_close(cli); |  | ||||||
|         furi_record_close(RECORD_CLI); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| bool desktop_pin_lock_verify(const PinCode* pin_set, const PinCode* pin_entered) { |  | ||||||
|     bool result = false; |  | ||||||
|     if(desktop_pins_are_equal(pin_set, pin_entered)) { |  | ||||||
|         furi_hal_rtc_set_pin_fails(0); |  | ||||||
|         result = true; |  | ||||||
|     } else { |  | ||||||
|         uint32_t pin_fails = furi_hal_rtc_get_pin_fails(); |  | ||||||
|         furi_hal_rtc_set_pin_fails(pin_fails + 1); |  | ||||||
|         result = false; |  | ||||||
|     } |  | ||||||
|     return result; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| bool desktop_pin_lock_is_locked() { |  | ||||||
|     return furi_hal_rtc_is_flag_set(FuriHalRtcFlagLock); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| bool desktop_pins_are_equal(const PinCode* pin_code1, const PinCode* pin_code2) { |  | ||||||
|     furi_assert(pin_code1); |  | ||||||
|     furi_assert(pin_code2); |  | ||||||
|     bool result = false; |  | ||||||
| 
 |  | ||||||
|     if(pin_code1->length == pin_code2->length) { |  | ||||||
|         result = !memcmp(pin_code1->data, pin_code2->data, pin_code1->length); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     return result; |  | ||||||
| } |  | ||||||
| @ -1,21 +0,0 @@ | |||||||
| #pragma once |  | ||||||
| #include <stdbool.h> |  | ||||||
| #include <stdint.h> |  | ||||||
| #include "../desktop.h" |  | ||||||
| #include <desktop/desktop_settings.h> |  | ||||||
| 
 |  | ||||||
| void desktop_pin_lock_error_notify(); |  | ||||||
| 
 |  | ||||||
| uint32_t desktop_pin_lock_get_fail_timeout(); |  | ||||||
| 
 |  | ||||||
| void desktop_pin_lock(DesktopSettings* settings); |  | ||||||
| 
 |  | ||||||
| void desktop_pin_unlock(DesktopSettings* settings); |  | ||||||
| 
 |  | ||||||
| bool desktop_pin_lock_is_locked(); |  | ||||||
| 
 |  | ||||||
| void desktop_pin_lock_init(DesktopSettings* settings); |  | ||||||
| 
 |  | ||||||
| bool desktop_pin_lock_verify(const PinCode* pin_set, const PinCode* pin_entered); |  | ||||||
| 
 |  | ||||||
| bool desktop_pins_are_equal(const PinCode* pin_code1, const PinCode* pin_code2); |  | ||||||
| @ -10,7 +10,7 @@ | |||||||
| #include "../views/desktop_view_lock_menu.h" | #include "../views/desktop_view_lock_menu.h" | ||||||
| #include "desktop_scene_i.h" | #include "desktop_scene_i.h" | ||||||
| #include "desktop_scene.h" | #include "desktop_scene.h" | ||||||
| #include "../helpers/pin_lock.h" | #include "../helpers/pin.h" | ||||||
| 
 | 
 | ||||||
| #define TAG "DesktopSceneLock" | #define TAG "DesktopSceneLock" | ||||||
| 
 | 
 | ||||||
| @ -25,7 +25,6 @@ void desktop_scene_lock_menu_on_enter(void* context) { | |||||||
|     DESKTOP_SETTINGS_LOAD(&desktop->settings); |     DESKTOP_SETTINGS_LOAD(&desktop->settings); | ||||||
|     scene_manager_set_scene_state(desktop->scene_manager, DesktopSceneLockMenu, 0); |     scene_manager_set_scene_state(desktop->scene_manager, DesktopSceneLockMenu, 0); | ||||||
|     desktop_lock_menu_set_callback(desktop->lock_menu, desktop_scene_lock_menu_callback, desktop); |     desktop_lock_menu_set_callback(desktop->lock_menu, desktop_scene_lock_menu_callback, desktop); | ||||||
|     desktop_lock_menu_set_pin_state(desktop->lock_menu, desktop->settings.pin_code.length > 0); |  | ||||||
|     desktop_lock_menu_set_dummy_mode_state(desktop->lock_menu, desktop->settings.dummy_mode); |     desktop_lock_menu_set_dummy_mode_state(desktop->lock_menu, desktop->settings.dummy_mode); | ||||||
|     desktop_lock_menu_set_stealth_mode_state( |     desktop_lock_menu_set_stealth_mode_state( | ||||||
|         desktop->lock_menu, furi_hal_rtc_is_flag_set(FuriHalRtcFlagStealthMode)); |         desktop->lock_menu, furi_hal_rtc_is_flag_set(FuriHalRtcFlagStealthMode)); | ||||||
| @ -44,7 +43,6 @@ bool desktop_scene_lock_menu_on_event(void* context, SceneManagerEvent event) { | |||||||
|         if(check_pin_changed) { |         if(check_pin_changed) { | ||||||
|             DESKTOP_SETTINGS_LOAD(&desktop->settings); |             DESKTOP_SETTINGS_LOAD(&desktop->settings); | ||||||
|             if(desktop->settings.pin_code.length > 0) { |             if(desktop->settings.pin_code.length > 0) { | ||||||
|                 desktop_lock_menu_set_pin_state(desktop->lock_menu, true); |  | ||||||
|                 scene_manager_set_scene_state(desktop->scene_manager, DesktopSceneLockMenu, 0); |                 scene_manager_set_scene_state(desktop->scene_manager, DesktopSceneLockMenu, 0); | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
| @ -55,21 +53,6 @@ bool desktop_scene_lock_menu_on_event(void* context, SceneManagerEvent event) { | |||||||
|             desktop_lock(desktop); |             desktop_lock(desktop); | ||||||
|             consumed = true; |             consumed = true; | ||||||
|             break; |             break; | ||||||
|         case DesktopLockMenuEventPinLock: |  | ||||||
|             if(desktop->settings.pin_code.length > 0) { |  | ||||||
|                 desktop_pin_lock(&desktop->settings); |  | ||||||
|                 desktop_lock(desktop); |  | ||||||
|             } else { |  | ||||||
|                 LoaderStatus status = |  | ||||||
|                     loader_start(desktop->loader, "Desktop", DESKTOP_SETTINGS_RUN_PIN_SETUP_ARG); |  | ||||||
|                 if(status == LoaderStatusOk) { |  | ||||||
|                     scene_manager_set_scene_state(desktop->scene_manager, DesktopSceneLockMenu, 1); |  | ||||||
|                 } else { |  | ||||||
|                     FURI_LOG_E(TAG, "Unable to start desktop settings"); |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|             consumed = true; |  | ||||||
|             break; |  | ||||||
|         case DesktopLockMenuEventDummyModeOn: |         case DesktopLockMenuEventDummyModeOn: | ||||||
|             desktop_set_dummy_mode_state(desktop, true); |             desktop_set_dummy_mode_state(desktop, true); | ||||||
|             scene_manager_search_and_switch_to_previous_scene( |             scene_manager_search_and_switch_to_previous_scene( | ||||||
|  | |||||||
| @ -7,7 +7,7 @@ | |||||||
| 
 | 
 | ||||||
| #include "../desktop.h" | #include "../desktop.h" | ||||||
| #include "../desktop_i.h" | #include "../desktop_i.h" | ||||||
| #include "../helpers/pin_lock.h" | #include "../helpers/pin.h" | ||||||
| #include "../animations/animation_manager.h" | #include "../animations/animation_manager.h" | ||||||
| #include "../views/desktop_events.h" | #include "../views/desktop_events.h" | ||||||
| #include "../views/desktop_view_pin_input.h" | #include "../views/desktop_view_pin_input.h" | ||||||
| @ -45,7 +45,7 @@ void desktop_scene_locked_on_enter(void* context) { | |||||||
|     bool switch_to_timeout_scene = false; |     bool switch_to_timeout_scene = false; | ||||||
|     uint32_t state = scene_manager_get_scene_state(desktop->scene_manager, DesktopSceneLocked); |     uint32_t state = scene_manager_get_scene_state(desktop->scene_manager, DesktopSceneLocked); | ||||||
|     if(state == SCENE_LOCKED_FIRST_ENTER) { |     if(state == SCENE_LOCKED_FIRST_ENTER) { | ||||||
|         bool pin_locked = desktop_pin_lock_is_locked(); |         bool pin_locked = desktop->settings.pin_code.length > 0; | ||||||
|         view_port_enabled_set(desktop->lock_icon_viewport, true); |         view_port_enabled_set(desktop->lock_icon_viewport, true); | ||||||
|         Gui* gui = furi_record_open(RECORD_GUI); |         Gui* gui = furi_record_open(RECORD_GUI); | ||||||
|         gui_set_lockdown(gui, true); |         gui_set_lockdown(gui, true); | ||||||
|  | |||||||
| @ -106,10 +106,12 @@ bool desktop_scene_main_on_event(void* context, SceneManagerEvent event) { | |||||||
| 
 | 
 | ||||||
|     if(event.type == SceneManagerEventTypeCustom) { |     if(event.type == SceneManagerEventTypeCustom) { | ||||||
|         switch(event.event) { |         switch(event.event) { | ||||||
|         case DesktopMainEventOpenMenu: |         case DesktopMainEventOpenMenu: { | ||||||
|             loader_show_menu(); |             Loader* loader = furi_record_open(RECORD_LOADER); | ||||||
|  |             loader_show_menu(loader); | ||||||
|  |             furi_record_close(RECORD_LOADER); | ||||||
|             consumed = true; |             consumed = true; | ||||||
|             break; |         } break; | ||||||
| 
 | 
 | ||||||
|         case DesktopMainEventOpenLockMenu: |         case DesktopMainEventOpenLockMenu: | ||||||
|             scene_manager_next_scene(desktop->scene_manager, DesktopSceneLockMenu); |             scene_manager_next_scene(desktop->scene_manager, DesktopSceneLockMenu); | ||||||
|  | |||||||
| @ -12,7 +12,7 @@ | |||||||
| #include "../animations/animation_manager.h" | #include "../animations/animation_manager.h" | ||||||
| #include "../views/desktop_events.h" | #include "../views/desktop_events.h" | ||||||
| #include "../views/desktop_view_pin_input.h" | #include "../views/desktop_view_pin_input.h" | ||||||
| #include "../helpers/pin_lock.h" | #include "../helpers/pin.h" | ||||||
| #include "desktop_scene.h" | #include "desktop_scene.h" | ||||||
| #include "desktop_scene_i.h" | #include "desktop_scene_i.h" | ||||||
| 
 | 
 | ||||||
| @ -54,9 +54,11 @@ static void desktop_scene_pin_input_back_callback(void* context) { | |||||||
| 
 | 
 | ||||||
| static void desktop_scene_pin_input_done_callback(const PinCode* pin_code, void* context) { | static void desktop_scene_pin_input_done_callback(const PinCode* pin_code, void* context) { | ||||||
|     Desktop* desktop = (Desktop*)context; |     Desktop* desktop = (Desktop*)context; | ||||||
|     if(desktop_pin_lock_verify(&desktop->settings.pin_code, pin_code)) { |     if(desktop_pin_compare(&desktop->settings.pin_code, pin_code)) { | ||||||
|         view_dispatcher_send_custom_event(desktop->view_dispatcher, DesktopPinInputEventUnlocked); |         view_dispatcher_send_custom_event(desktop->view_dispatcher, DesktopPinInputEventUnlocked); | ||||||
|     } else { |     } else { | ||||||
|  |         uint32_t pin_fails = furi_hal_rtc_get_pin_fails(); | ||||||
|  |         furi_hal_rtc_set_pin_fails(pin_fails + 1); | ||||||
|         view_dispatcher_send_custom_event( |         view_dispatcher_send_custom_event( | ||||||
|             desktop->view_dispatcher, DesktopPinInputEventUnlockFailed); |             desktop->view_dispatcher, DesktopPinInputEventUnlockFailed); | ||||||
|     } |     } | ||||||
| @ -126,7 +128,6 @@ bool desktop_scene_pin_input_on_event(void* context, SceneManagerEvent event) { | |||||||
|             consumed = true; |             consumed = true; | ||||||
|             break; |             break; | ||||||
|         case DesktopPinInputEventUnlocked: |         case DesktopPinInputEventUnlocked: | ||||||
|             desktop_pin_unlock(&desktop->settings); |  | ||||||
|             desktop_unlock(desktop); |             desktop_unlock(desktop); | ||||||
|             consumed = true; |             consumed = true; | ||||||
|             break; |             break; | ||||||
|  | |||||||
| @ -31,7 +31,6 @@ typedef enum { | |||||||
|     DesktopDebugEventExit, |     DesktopDebugEventExit, | ||||||
| 
 | 
 | ||||||
|     DesktopLockMenuEventLock, |     DesktopLockMenuEventLock, | ||||||
|     DesktopLockMenuEventPinLock, |  | ||||||
|     DesktopLockMenuEventDummyModeOn, |     DesktopLockMenuEventDummyModeOn, | ||||||
|     DesktopLockMenuEventDummyModeOff, |     DesktopLockMenuEventDummyModeOff, | ||||||
|     DesktopLockMenuEventStealthModeOn, |     DesktopLockMenuEventStealthModeOn, | ||||||
|  | |||||||
| @ -65,13 +65,16 @@ void desktop_debug_render(Canvas* canvas, void* model) { | |||||||
|             version_get_builddate(ver)); |             version_get_builddate(ver)); | ||||||
|         canvas_draw_str(canvas, 0, 30 + STATUS_BAR_Y_SHIFT, buffer); |         canvas_draw_str(canvas, 0, 30 + STATUS_BAR_Y_SHIFT, buffer); | ||||||
| 
 | 
 | ||||||
|  |         uint16_t api_major, api_minor; | ||||||
|  |         furi_hal_info_get_api_version(&api_major, &api_minor); | ||||||
|         snprintf( |         snprintf( | ||||||
|             buffer, |             buffer, | ||||||
|             sizeof(buffer), |             sizeof(buffer), | ||||||
|             "%s%s [%s] %s", |             "%s%s [%d.%d] %s", | ||||||
|             version_get_dirty_flag(ver) ? "[!] " : "", |             version_get_dirty_flag(ver) ? "[!] " : "", | ||||||
|             version_get_githash(ver), |             version_get_githash(ver), | ||||||
|             version_get_gitbranchnum(ver), |             api_major, | ||||||
|  |             api_minor, | ||||||
|             c2_ver ? c2_ver->StackTypeString : "<none>"); |             c2_ver ? c2_ver->StackTypeString : "<none>"); | ||||||
|         canvas_draw_str(canvas, 0, 40 + STATUS_BAR_Y_SHIFT, buffer); |         canvas_draw_str(canvas, 0, 40 + STATUS_BAR_Y_SHIFT, buffer); | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -23,14 +23,6 @@ void desktop_lock_menu_set_callback( | |||||||
|     lock_menu->context = context; |     lock_menu->context = context; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void desktop_lock_menu_set_pin_state(DesktopLockMenuView* lock_menu, bool pin_is_set) { |  | ||||||
|     with_view_model( |  | ||||||
|         lock_menu->view, |  | ||||||
|         DesktopLockMenuViewModel * model, |  | ||||||
|         { model->pin_is_set = pin_is_set; }, |  | ||||||
|         true); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void desktop_lock_menu_set_dummy_mode_state(DesktopLockMenuView* lock_menu, bool dummy_mode) { | void desktop_lock_menu_set_dummy_mode_state(DesktopLockMenuView* lock_menu, bool dummy_mode) { | ||||||
|     with_view_model( |     with_view_model( | ||||||
|         lock_menu->view, |         lock_menu->view, | ||||||
| @ -102,7 +94,6 @@ bool desktop_lock_menu_input_callback(InputEvent* event, void* context) { | |||||||
|     bool consumed = false; |     bool consumed = false; | ||||||
|     bool dummy_mode = false; |     bool dummy_mode = false; | ||||||
|     bool stealth_mode = false; |     bool stealth_mode = false; | ||||||
|     bool pin_is_set = false; |  | ||||||
|     bool update = false; |     bool update = false; | ||||||
| 
 | 
 | ||||||
|     with_view_model( |     with_view_model( | ||||||
| @ -131,15 +122,12 @@ bool desktop_lock_menu_input_callback(InputEvent* event, void* context) { | |||||||
|             idx = model->idx; |             idx = model->idx; | ||||||
|             dummy_mode = model->dummy_mode; |             dummy_mode = model->dummy_mode; | ||||||
|             stealth_mode = model->stealth_mode; |             stealth_mode = model->stealth_mode; | ||||||
|             pin_is_set = model->pin_is_set; |  | ||||||
|         }, |         }, | ||||||
|         update); |         update); | ||||||
| 
 | 
 | ||||||
|     if(event->key == InputKeyOk) { |     if(event->key == InputKeyOk) { | ||||||
|         if((idx == DesktopLockMenuIndexLock)) { |         if((idx == DesktopLockMenuIndexLock)) { | ||||||
|             if((pin_is_set) && (event->type == InputTypeShort)) { |             if((event->type == InputTypeShort)) { | ||||||
|                 lock_menu->callback(DesktopLockMenuEventPinLock, lock_menu->context); |  | ||||||
|             } else if((pin_is_set == false) && (event->type == InputTypeShort)) { |  | ||||||
|                 lock_menu->callback(DesktopLockMenuEventLock, lock_menu->context); |                 lock_menu->callback(DesktopLockMenuEventLock, lock_menu->context); | ||||||
|             } |             } | ||||||
|         } else if(idx == DesktopLockMenuIndexStealth) { |         } else if(idx == DesktopLockMenuIndexStealth) { | ||||||
|  | |||||||
| @ -17,7 +17,6 @@ struct DesktopLockMenuView { | |||||||
| 
 | 
 | ||||||
| typedef struct { | typedef struct { | ||||||
|     uint8_t idx; |     uint8_t idx; | ||||||
|     bool pin_is_set; |  | ||||||
|     bool dummy_mode; |     bool dummy_mode; | ||||||
|     bool stealth_mode; |     bool stealth_mode; | ||||||
| } DesktopLockMenuViewModel; | } DesktopLockMenuViewModel; | ||||||
| @ -28,7 +27,6 @@ void desktop_lock_menu_set_callback( | |||||||
|     void* context); |     void* context); | ||||||
| 
 | 
 | ||||||
| View* desktop_lock_menu_get_view(DesktopLockMenuView* lock_menu); | View* desktop_lock_menu_get_view(DesktopLockMenuView* lock_menu); | ||||||
| void desktop_lock_menu_set_pin_state(DesktopLockMenuView* lock_menu, bool pin_is_set); |  | ||||||
| void desktop_lock_menu_set_dummy_mode_state(DesktopLockMenuView* lock_menu, bool dummy_mode); | void desktop_lock_menu_set_dummy_mode_state(DesktopLockMenuView* lock_menu, bool dummy_mode); | ||||||
| void desktop_lock_menu_set_stealth_mode_state(DesktopLockMenuView* lock_menu, bool stealth_mode); | void desktop_lock_menu_set_stealth_mode_state(DesktopLockMenuView* lock_menu, bool stealth_mode); | ||||||
| void desktop_lock_menu_set_idx(DesktopLockMenuView* lock_menu, uint8_t idx); | void desktop_lock_menu_set_idx(DesktopLockMenuView* lock_menu, uint8_t idx); | ||||||
|  | |||||||
| @ -1,79 +0,0 @@ | |||||||
| #include <furi.h> |  | ||||||
| #include <furi_hal.h> |  | ||||||
| #include <gui/elements.h> |  | ||||||
| #include <gui/canvas.h> |  | ||||||
| #include <toolbox/version.h> |  | ||||||
| #include <assets_icons.h> |  | ||||||
| #include <dolphin/helpers/dolphin_state.h> |  | ||||||
| #include <dolphin/dolphin.h> |  | ||||||
| 
 |  | ||||||
| #include "../desktop_i.h" |  | ||||||
| #include "desktop_view_pin_setup_done.h" |  | ||||||
| 
 |  | ||||||
| struct DesktopViewPinSetupDone { |  | ||||||
|     View* view; |  | ||||||
|     DesktopViewPinSetupDoneDoneCallback callback; |  | ||||||
|     void* context; |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| static void desktop_view_pin_done_draw(Canvas* canvas, void* model) { |  | ||||||
|     furi_assert(canvas); |  | ||||||
|     UNUSED(model); |  | ||||||
| 
 |  | ||||||
|     canvas_set_font(canvas, FontPrimary); |  | ||||||
|     elements_multiline_text_aligned( |  | ||||||
|         canvas, 64, 0, AlignCenter, AlignTop, "Prepare to use\narrows as\nPIN symbols"); |  | ||||||
| 
 |  | ||||||
|     canvas_set_font(canvas, FontSecondary); |  | ||||||
|     elements_multiline_text(canvas, 58, 24, "Prepare to use\narrows as\nPIN symbols"); |  | ||||||
| 
 |  | ||||||
|     canvas_draw_icon(canvas, 16, 18, &I_Pin_attention_dpad_29x29); |  | ||||||
|     elements_button_right(canvas, "Next"); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| static bool desktop_view_pin_done_input(InputEvent* event, void* context) { |  | ||||||
|     furi_assert(event); |  | ||||||
|     furi_assert(context); |  | ||||||
| 
 |  | ||||||
|     DesktopViewPinSetupDone* instance = context; |  | ||||||
|     bool consumed = false; |  | ||||||
| 
 |  | ||||||
|     if((event->key == InputKeyRight) && (event->type == InputTypeShort)) { |  | ||||||
|         instance->callback(instance->context); |  | ||||||
|         consumed = true; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     return consumed; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void desktop_view_pin_done_set_callback( |  | ||||||
|     DesktopViewPinSetupDone* instance, |  | ||||||
|     DesktopViewPinSetupDoneDoneCallback callback, |  | ||||||
|     void* context) { |  | ||||||
|     furi_assert(instance); |  | ||||||
|     furi_assert(callback); |  | ||||||
|     instance->callback = callback; |  | ||||||
|     instance->context = context; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| DesktopViewPinSetupDone* desktop_view_pin_done_alloc() { |  | ||||||
|     DesktopViewPinSetupDone* view = malloc(sizeof(DesktopViewPinSetupDone)); |  | ||||||
|     view->view = view_alloc(); |  | ||||||
|     view_set_context(view->view, view); |  | ||||||
|     view_set_draw_callback(view->view, desktop_view_pin_done_draw); |  | ||||||
|     view_set_input_callback(view->view, desktop_view_pin_done_input); |  | ||||||
| 
 |  | ||||||
|     return view; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void desktop_view_pin_done_free(DesktopViewPinSetupDone* instance) { |  | ||||||
|     furi_assert(instance); |  | ||||||
| 
 |  | ||||||
|     view_free(instance->view); |  | ||||||
|     free(instance); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| View* desktop_view_pin_done_get_view(DesktopViewPinSetupDone* instance) { |  | ||||||
|     furi_assert(instance); |  | ||||||
|     return instance->view; |  | ||||||
| } |  | ||||||
| @ -1,15 +0,0 @@ | |||||||
| #pragma once |  | ||||||
| 
 |  | ||||||
| #include <gui/view.h> |  | ||||||
| 
 |  | ||||||
| typedef struct DesktopViewPinSetupDone DesktopViewPinSetupDone; |  | ||||||
| 
 |  | ||||||
| typedef void (*DesktopViewPinSetupDoneDoneCallback)(void*); |  | ||||||
| 
 |  | ||||||
| void desktop_view_pin_done_set_callback( |  | ||||||
|     DesktopViewPinSetupDone* instance, |  | ||||||
|     DesktopViewPinSetupDoneDoneCallback callback, |  | ||||||
|     void* context); |  | ||||||
| DesktopViewPinSetupDone* desktop_view_pin_done_alloc(); |  | ||||||
| void desktop_view_pin_done_free(DesktopViewPinSetupDone* instance); |  | ||||||
| View* desktop_view_pin_done_get_view(DesktopViewPinSetupDone* instance); |  | ||||||
| @ -154,6 +154,8 @@ Menu* menu_alloc() { | |||||||
| void menu_free(Menu* menu) { | void menu_free(Menu* menu) { | ||||||
|     furi_assert(menu); |     furi_assert(menu); | ||||||
|     menu_reset(menu); |     menu_reset(menu); | ||||||
|  |     with_view_model( | ||||||
|  |         menu->view, MenuModel * model, { MenuItemArray_clear(model->items); }, false); | ||||||
|     view_free(menu->view); |     view_free(menu->view); | ||||||
|     free(menu); |     free(menu); | ||||||
| } | } | ||||||
|  | |||||||
| @ -19,19 +19,16 @@ void view_tie_icon_animation(View* view, IconAnimation* icon_animation) { | |||||||
| 
 | 
 | ||||||
| void view_set_draw_callback(View* view, ViewDrawCallback callback) { | void view_set_draw_callback(View* view, ViewDrawCallback callback) { | ||||||
|     furi_assert(view); |     furi_assert(view); | ||||||
|     furi_assert(view->draw_callback == NULL); |  | ||||||
|     view->draw_callback = callback; |     view->draw_callback = callback; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void view_set_input_callback(View* view, ViewInputCallback callback) { | void view_set_input_callback(View* view, ViewInputCallback callback) { | ||||||
|     furi_assert(view); |     furi_assert(view); | ||||||
|     furi_assert(view->input_callback == NULL); |  | ||||||
|     view->input_callback = callback; |     view->input_callback = callback; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void view_set_custom_callback(View* view, ViewCustomCallback callback) { | void view_set_custom_callback(View* view, ViewCustomCallback callback) { | ||||||
|     furi_assert(view); |     furi_assert(view); | ||||||
|     furi_assert(callback); |  | ||||||
|     view->custom_callback = callback; |     view->custom_callback = callback; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @ -62,7 +59,6 @@ void view_set_update_callback_context(View* view, void* context) { | |||||||
| 
 | 
 | ||||||
| void view_set_context(View* view, void* context) { | void view_set_context(View* view, void* context) { | ||||||
|     furi_assert(view); |     furi_assert(view); | ||||||
|     furi_assert(context); |  | ||||||
|     view->context = context; |     view->context = context; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -5,6 +5,7 @@ App( | |||||||
|     entry_point="loader_srv", |     entry_point="loader_srv", | ||||||
|     cdefines=["SRV_LOADER"], |     cdefines=["SRV_LOADER"], | ||||||
|     requires=["gui"], |     requires=["gui"], | ||||||
|  |     provides=["loader_start"], | ||||||
|     stack_size=2 * 1024, |     stack_size=2 * 1024, | ||||||
|     order=90, |     order=90, | ||||||
|     sdk_headers=[ |     sdk_headers=[ | ||||||
| @ -12,3 +13,11 @@ App( | |||||||
|         "firmware_api/firmware_api.h", |         "firmware_api/firmware_api.h", | ||||||
|     ], |     ], | ||||||
| ) | ) | ||||||
|  | 
 | ||||||
|  | App( | ||||||
|  |     appid="loader_start", | ||||||
|  |     apptype=FlipperAppType.STARTUP, | ||||||
|  |     entry_point="loader_on_system_start", | ||||||
|  |     requires=["loader"], | ||||||
|  |     order=90, | ||||||
|  | ) | ||||||
|  | |||||||
| @ -6,6 +6,8 @@ | |||||||
| /* Generated table */ | /* Generated table */ | ||||||
| #include <firmware_api_table.h> | #include <firmware_api_table.h> | ||||||
| 
 | 
 | ||||||
|  | #include <furi_hal_info.h> | ||||||
|  | 
 | ||||||
| static_assert(!has_hash_collisions(elf_api_table), "Detected API method hash collision!"); | static_assert(!has_hash_collisions(elf_api_table), "Detected API method hash collision!"); | ||||||
| 
 | 
 | ||||||
| constexpr HashtableApiInterface elf_api_interface{ | constexpr HashtableApiInterface elf_api_interface{ | ||||||
| @ -19,3 +21,8 @@ constexpr HashtableApiInterface elf_api_interface{ | |||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| const ElfApiInterface* const firmware_api_interface = &elf_api_interface; | const ElfApiInterface* const firmware_api_interface = &elf_api_interface; | ||||||
|  | 
 | ||||||
|  | extern "C" void furi_hal_info_get_api_version(uint16_t* major, uint16_t* minor) { | ||||||
|  |     *major = elf_api_interface.api_version_major; | ||||||
|  |     *minor = elf_api_interface.api_version_minor; | ||||||
|  | } | ||||||
| @ -1,76 +1,114 @@ | |||||||
| #include "applications.h" | #include "loader.h" | ||||||
| #include <furi.h> |  | ||||||
| #include "loader/loader.h" |  | ||||||
| #include "loader_i.h" | #include "loader_i.h" | ||||||
|  | #include "loader_menu.h" | ||||||
|  | #include <applications.h> | ||||||
|  | #include <furi_hal.h> | ||||||
| 
 | 
 | ||||||
| #define TAG "LoaderSrv" | #define TAG "Loader" | ||||||
|  | #define LOADER_MAGIC_THREAD_VALUE 0xDEADBEEF | ||||||
|  | // api
 | ||||||
| 
 | 
 | ||||||
| #define LOADER_THREAD_FLAG_SHOW_MENU (1 << 0) | LoaderStatus loader_start(Loader* loader, const char* name, const char* args) { | ||||||
| #define LOADER_THREAD_FLAG_ALL (LOADER_THREAD_FLAG_SHOW_MENU) |     LoaderMessage message; | ||||||
|  |     LoaderMessageLoaderStatusResult result; | ||||||
| 
 | 
 | ||||||
| static Loader* loader_instance = NULL; |     message.type = LoaderMessageTypeStartByName; | ||||||
| 
 |     message.start.name = name; | ||||||
| static bool |     message.start.args = args; | ||||||
|     loader_start_application(const FlipperApplication* application, const char* arguments) { |     message.api_lock = api_lock_alloc_locked(); | ||||||
|     loader_instance->application = application; |     message.status_value = &result; | ||||||
| 
 |     furi_message_queue_put(loader->queue, &message, FuriWaitForever); | ||||||
|     furi_assert(loader_instance->application_arguments == NULL); |     api_lock_wait_unlock_and_free(message.api_lock); | ||||||
|     if(arguments && strlen(arguments) > 0) { |     return result.value; | ||||||
|         loader_instance->application_arguments = strdup(arguments); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     FURI_LOG_I(TAG, "Starting: %s", loader_instance->application->name); |  | ||||||
| 
 |  | ||||||
|     FuriHalRtcHeapTrackMode mode = furi_hal_rtc_get_heap_track_mode(); |  | ||||||
|     if(mode > FuriHalRtcHeapTrackModeNone) { |  | ||||||
|         furi_thread_enable_heap_trace(loader_instance->application_thread); |  | ||||||
|     } else { |  | ||||||
|         furi_thread_disable_heap_trace(loader_instance->application_thread); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     furi_thread_set_name(loader_instance->application_thread, loader_instance->application->name); |  | ||||||
|     furi_thread_set_appid( |  | ||||||
|         loader_instance->application_thread, loader_instance->application->appid); |  | ||||||
|     furi_thread_set_stack_size( |  | ||||||
|         loader_instance->application_thread, loader_instance->application->stack_size); |  | ||||||
|     furi_thread_set_context( |  | ||||||
|         loader_instance->application_thread, loader_instance->application_arguments); |  | ||||||
|     furi_thread_set_callback( |  | ||||||
|         loader_instance->application_thread, loader_instance->application->app); |  | ||||||
| 
 |  | ||||||
|     furi_thread_start(loader_instance->application_thread); |  | ||||||
| 
 |  | ||||||
|     return true; |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static void loader_menu_callback(void* _ctx, uint32_t index) { | bool loader_lock(Loader* loader) { | ||||||
|     UNUSED(index); |     LoaderMessage message; | ||||||
|     const FlipperApplication* application = _ctx; |     LoaderMessageBoolResult result; | ||||||
|  |     message.type = LoaderMessageTypeLock; | ||||||
|  |     message.api_lock = api_lock_alloc_locked(); | ||||||
|  |     message.bool_value = &result; | ||||||
|  |     furi_message_queue_put(loader->queue, &message, FuriWaitForever); | ||||||
|  |     api_lock_wait_unlock_and_free(message.api_lock); | ||||||
|  |     return result.value; | ||||||
|  | } | ||||||
| 
 | 
 | ||||||
|     furi_assert(application->app); | void loader_unlock(Loader* loader) { | ||||||
|     furi_assert(application->name); |     LoaderMessage message; | ||||||
|  |     message.type = LoaderMessageTypeUnlock; | ||||||
|  |     furi_message_queue_put(loader->queue, &message, FuriWaitForever); | ||||||
|  | } | ||||||
| 
 | 
 | ||||||
|     if(!loader_lock(loader_instance)) { | bool loader_is_locked(Loader* loader) { | ||||||
|         FURI_LOG_E(TAG, "Loader is locked"); |     LoaderMessage message; | ||||||
|         return; |     LoaderMessageBoolResult result; | ||||||
|  |     message.type = LoaderMessageTypeIsLocked; | ||||||
|  |     message.api_lock = api_lock_alloc_locked(); | ||||||
|  |     message.bool_value = &result; | ||||||
|  |     furi_message_queue_put(loader->queue, &message, FuriWaitForever); | ||||||
|  |     api_lock_wait_unlock_and_free(message.api_lock); | ||||||
|  |     return result.value; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void loader_show_menu(Loader* loader) { | ||||||
|  |     LoaderMessage message; | ||||||
|  |     message.type = LoaderMessageTypeShowMenu; | ||||||
|  |     furi_message_queue_put(loader->queue, &message, FuriWaitForever); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | FuriPubSub* loader_get_pubsub(Loader* loader) { | ||||||
|  |     furi_assert(loader); | ||||||
|  |     // it's safe to return pubsub without locking
 | ||||||
|  |     // because it's never freed and loader is never exited
 | ||||||
|  |     // also the loader instance cannot be obtained until the pubsub is created
 | ||||||
|  |     return loader->pubsub; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // callbacks
 | ||||||
|  | 
 | ||||||
|  | static void loader_menu_closed_callback(void* context) { | ||||||
|  |     Loader* loader = context; | ||||||
|  |     LoaderMessage message; | ||||||
|  |     message.type = LoaderMessageTypeMenuClosed; | ||||||
|  |     furi_message_queue_put(loader->queue, &message, FuriWaitForever); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void loader_menu_click_callback(const char* name, void* context) { | ||||||
|  |     Loader* loader = context; | ||||||
|  |     loader_start(loader, name, NULL); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void loader_thread_state_callback(FuriThreadState thread_state, void* context) { | ||||||
|  |     furi_assert(context); | ||||||
|  | 
 | ||||||
|  |     Loader* loader = context; | ||||||
|  |     LoaderEvent event; | ||||||
|  | 
 | ||||||
|  |     if(thread_state == FuriThreadStateRunning) { | ||||||
|  |         event.type = LoaderEventTypeApplicationStarted; | ||||||
|  |         furi_pubsub_publish(loader->pubsub, &event); | ||||||
|  |     } else if(thread_state == FuriThreadStateStopped) { | ||||||
|  |         LoaderMessage message; | ||||||
|  |         message.type = LoaderMessageTypeAppClosed; | ||||||
|  |         furi_message_queue_put(loader->queue, &message, FuriWaitForever); | ||||||
|  | 
 | ||||||
|  |         event.type = LoaderEventTypeApplicationStopped; | ||||||
|  |         furi_pubsub_publish(loader->pubsub, &event); | ||||||
|     } |     } | ||||||
| 
 |  | ||||||
|     loader_start_application(application, NULL); |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static void loader_submenu_callback(void* context, uint32_t index) { | // implementation
 | ||||||
|     UNUSED(index); |  | ||||||
|     uint32_t view_id = (uint32_t)context; |  | ||||||
|     view_dispatcher_switch_to_view(loader_instance->view_dispatcher, view_id); |  | ||||||
| } |  | ||||||
| 
 | 
 | ||||||
| static void loader_cli_print_usage() { | static Loader* loader_alloc() { | ||||||
|     printf("Usage:\r\n"); |     Loader* loader = malloc(sizeof(Loader)); | ||||||
|     printf("loader <cmd> <args>\r\n"); |     loader->pubsub = furi_pubsub_alloc(); | ||||||
|     printf("Cmd list:\r\n"); |     loader->queue = furi_message_queue_alloc(1, sizeof(LoaderMessage)); | ||||||
|     printf("\tlist\t - List available applications\r\n"); |     loader->loader_menu = NULL; | ||||||
|     printf("\topen <Application Name:string>\t - Open application by name\r\n"); |     loader->app.args = NULL; | ||||||
|     printf("\tinfo\t - Show loader state\r\n"); |     loader->app.name = NULL; | ||||||
|  |     loader->app.thread = NULL; | ||||||
|  |     loader->app.insomniac = false; | ||||||
|  |     return loader; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static FlipperApplication const* loader_find_application_by_name_in_list( | static FlipperApplication const* loader_find_application_by_name_in_list( | ||||||
| @ -85,7 +123,7 @@ static FlipperApplication const* loader_find_application_by_name_in_list( | |||||||
|     return NULL; |     return NULL; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| const FlipperApplication* loader_find_application_by_name(const char* name) { | static const FlipperApplication* loader_find_application_by_name(const char* name) { | ||||||
|     const FlipperApplication* application = NULL; |     const FlipperApplication* application = NULL; | ||||||
|     application = loader_find_application_by_name_in_list(name, FLIPPER_APPS, FLIPPER_APPS_COUNT); |     application = loader_find_application_by_name_in_list(name, FLIPPER_APPS, FLIPPER_APPS_COUNT); | ||||||
|     if(!application) { |     if(!application) { | ||||||
| @ -100,346 +138,168 @@ const FlipperApplication* loader_find_application_by_name(const char* name) { | |||||||
|     return application; |     return application; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static void loader_cli_open(Cli* cli, FuriString* args, Loader* instance) { | static void | ||||||
|     UNUSED(cli); |     loader_start_internal_app(Loader* loader, const FlipperApplication* app, const char* args) { | ||||||
|     if(loader_is_locked(instance)) { |     FURI_LOG_I(TAG, "Starting %s", app->name); | ||||||
|         if(instance->application) { | 
 | ||||||
|             furi_assert(instance->application->name); |     // store args
 | ||||||
|             printf("Can't start, %s application is running", instance->application->name); |     furi_assert(loader->app.args == NULL); | ||||||
|  |     if(args && strlen(args) > 0) { | ||||||
|  |         loader->app.args = strdup(args); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     // store name
 | ||||||
|  |     furi_assert(loader->app.name == NULL); | ||||||
|  |     loader->app.name = strdup(app->name); | ||||||
|  | 
 | ||||||
|  |     // setup app thread
 | ||||||
|  |     loader->app.thread = | ||||||
|  |         furi_thread_alloc_ex(app->name, app->stack_size, app->app, loader->app.args); | ||||||
|  |     furi_thread_set_appid(loader->app.thread, app->appid); | ||||||
|  | 
 | ||||||
|  |     // setup heap trace
 | ||||||
|  |     FuriHalRtcHeapTrackMode mode = furi_hal_rtc_get_heap_track_mode(); | ||||||
|  |     if(mode > FuriHalRtcHeapTrackModeNone) { | ||||||
|  |         furi_thread_enable_heap_trace(loader->app.thread); | ||||||
|     } else { |     } else { | ||||||
|             printf("Can't start, furi application is running"); |         furi_thread_disable_heap_trace(loader->app.thread); | ||||||
|         } |  | ||||||
|         return; |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     FuriString* application_name; |     // setup insomnia
 | ||||||
|     application_name = furi_string_alloc(); |     if(!(app->flags & FlipperApplicationFlagInsomniaSafe)) { | ||||||
| 
 |         furi_hal_power_insomnia_enter(); | ||||||
|     do { |         loader->app.insomniac = true; | ||||||
|         if(!args_read_probably_quoted_string_and_trim(args, application_name)) { |  | ||||||
|             printf("No application provided\r\n"); |  | ||||||
|             break; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         const FlipperApplication* application = |  | ||||||
|             loader_find_application_by_name(furi_string_get_cstr(application_name)); |  | ||||||
|         if(!application) { |  | ||||||
|             printf("%s doesn't exists\r\n", furi_string_get_cstr(application_name)); |  | ||||||
|             break; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         furi_string_trim(args); |  | ||||||
|         if(!loader_start_application(application, furi_string_get_cstr(args))) { |  | ||||||
|             printf("Can't start, furi application is running"); |  | ||||||
|             return; |  | ||||||
|     } else { |     } else { | ||||||
|             // We must to increment lock counter to keep balance
 |         loader->app.insomniac = false; | ||||||
|             // TODO: rewrite whole thing, it's complex as hell
 |  | ||||||
|             FURI_CRITICAL_ENTER(); |  | ||||||
|             instance->lock_count++; |  | ||||||
|             FURI_CRITICAL_EXIT(); |  | ||||||
|     } |     } | ||||||
|     } while(false); |  | ||||||
| 
 | 
 | ||||||
|     furi_string_free(application_name); |     // setup app thread callbacks
 | ||||||
|  |     furi_thread_set_state_context(loader->app.thread, loader); | ||||||
|  |     furi_thread_set_state_callback(loader->app.thread, loader_thread_state_callback); | ||||||
|  | 
 | ||||||
|  |     // start app thread
 | ||||||
|  |     furi_thread_start(loader->app.thread); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static void loader_cli_list(Cli* cli, FuriString* args, Loader* instance) { | // process messages
 | ||||||
|     UNUSED(cli); | 
 | ||||||
|     UNUSED(args); | static void loader_do_menu_show(Loader* loader) { | ||||||
|     UNUSED(instance); |     if(!loader->loader_menu) { | ||||||
|     printf("Applications:\r\n"); |         loader->loader_menu = loader_menu_alloc(); | ||||||
|     for(size_t i = 0; i < FLIPPER_APPS_COUNT; i++) { |         loader_menu_set_closed_callback(loader->loader_menu, loader_menu_closed_callback, loader); | ||||||
|         printf("\t%s\r\n", FLIPPER_APPS[i].name); |         loader_menu_set_click_callback(loader->loader_menu, loader_menu_click_callback, loader); | ||||||
|  |         loader_menu_start(loader->loader_menu); | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static void loader_cli_info(Cli* cli, FuriString* args, Loader* instance) { | static void loader_do_menu_closed(Loader* loader) { | ||||||
|     UNUSED(cli); |     if(loader->loader_menu) { | ||||||
|     UNUSED(args); |         loader_menu_stop(loader->loader_menu); | ||||||
|     if(!loader_is_locked(instance)) { |         loader_menu_free(loader->loader_menu); | ||||||
|         printf("No application is running\r\n"); |         loader->loader_menu = NULL; | ||||||
|     } else { |  | ||||||
|         printf("Running application: "); |  | ||||||
|         if(instance->application) { |  | ||||||
|             furi_assert(instance->application->name); |  | ||||||
|             printf("%s\r\n", instance->application->name); |  | ||||||
|         } else { |  | ||||||
|             printf("unknown\r\n"); |  | ||||||
|         } |  | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static void loader_cli(Cli* cli, FuriString* args, void* _ctx) { | static bool loader_do_is_locked(Loader* loader) { | ||||||
|     furi_assert(_ctx); |     return loader->app.thread != NULL; | ||||||
|     Loader* instance = _ctx; |  | ||||||
| 
 |  | ||||||
|     FuriString* cmd; |  | ||||||
|     cmd = furi_string_alloc(); |  | ||||||
| 
 |  | ||||||
|     do { |  | ||||||
|         if(!args_read_string_and_trim(args, cmd)) { |  | ||||||
|             loader_cli_print_usage(); |  | ||||||
|             break; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         if(furi_string_cmp_str(cmd, "list") == 0) { |  | ||||||
|             loader_cli_list(cli, args, instance); |  | ||||||
|             break; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         if(furi_string_cmp_str(cmd, "open") == 0) { |  | ||||||
|             loader_cli_open(cli, args, instance); |  | ||||||
|             break; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         if(furi_string_cmp_str(cmd, "info") == 0) { |  | ||||||
|             loader_cli_info(cli, args, instance); |  | ||||||
|             break; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         loader_cli_print_usage(); |  | ||||||
|     } while(false); |  | ||||||
| 
 |  | ||||||
|     furi_string_free(cmd); |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| LoaderStatus loader_start(Loader* instance, const char* name, const char* args) { | static LoaderStatus loader_do_start_by_name(Loader* loader, const char* name, const char* args) { | ||||||
|     UNUSED(instance); |     if(loader_do_is_locked(loader)) { | ||||||
|     furi_assert(name); |  | ||||||
| 
 |  | ||||||
|     const FlipperApplication* application = loader_find_application_by_name(name); |  | ||||||
| 
 |  | ||||||
|     if(!application) { |  | ||||||
|         FURI_LOG_E(TAG, "Can't find application with name %s", name); |  | ||||||
|         return LoaderStatusErrorUnknownApp; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     if(!loader_lock(loader_instance)) { |  | ||||||
|         FURI_LOG_E(TAG, "Loader is locked"); |  | ||||||
|         return LoaderStatusErrorAppStarted; |         return LoaderStatusErrorAppStarted; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     if(!loader_start_application(application, args)) { |     const FlipperApplication* app = loader_find_application_by_name(name); | ||||||
|         return LoaderStatusErrorInternal; | 
 | ||||||
|  |     if(!app) { | ||||||
|  |         return LoaderStatusErrorUnknownApp; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     loader_start_internal_app(loader, app, args); | ||||||
|     return LoaderStatusOk; |     return LoaderStatusOk; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| bool loader_lock(Loader* instance) { | static bool loader_do_lock(Loader* loader) { | ||||||
|     FURI_CRITICAL_ENTER(); |     if(loader->app.thread) { | ||||||
|     bool result = false; |         return false; | ||||||
|     if(instance->lock_count == 0) { |  | ||||||
|         instance->lock_count++; |  | ||||||
|         result = true; |  | ||||||
|     } |     } | ||||||
|     FURI_CRITICAL_EXIT(); | 
 | ||||||
|     return result; |     loader->app.thread = (FuriThread*)LOADER_MAGIC_THREAD_VALUE; | ||||||
|  |     return true; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void loader_unlock(Loader* instance) { | static void loader_do_unlock(Loader* loader) { | ||||||
|     FURI_CRITICAL_ENTER(); |     furi_assert(loader->app.thread == (FuriThread*)LOADER_MAGIC_THREAD_VALUE); | ||||||
|     if(instance->lock_count > 0) instance->lock_count--; |     loader->app.thread = NULL; | ||||||
|     FURI_CRITICAL_EXIT(); |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| bool loader_is_locked(const Loader* instance) { | static void loader_do_app_closed(Loader* loader) { | ||||||
|     return instance->lock_count > 0; |     furi_assert(loader->app.thread); | ||||||
| } |  | ||||||
| 
 |  | ||||||
| static void loader_thread_state_callback(FuriThreadState thread_state, void* context) { |  | ||||||
|     furi_assert(context); |  | ||||||
| 
 |  | ||||||
|     Loader* instance = context; |  | ||||||
|     LoaderEvent event; |  | ||||||
| 
 |  | ||||||
|     if(thread_state == FuriThreadStateRunning) { |  | ||||||
|         event.type = LoaderEventTypeApplicationStarted; |  | ||||||
|         furi_pubsub_publish(loader_instance->pubsub, &event); |  | ||||||
| 
 |  | ||||||
|         if(!(loader_instance->application->flags & FlipperApplicationFlagInsomniaSafe)) { |  | ||||||
|             furi_hal_power_insomnia_enter(); |  | ||||||
|         } |  | ||||||
|     } else if(thread_state == FuriThreadStateStopped) { |  | ||||||
|     FURI_LOG_I(TAG, "Application stopped. Free heap: %zu", memmgr_get_free_heap()); |     FURI_LOG_I(TAG, "Application stopped. Free heap: %zu", memmgr_get_free_heap()); | ||||||
| 
 |     if(loader->app.args) { | ||||||
|         if(loader_instance->application_arguments) { |         free(loader->app.args); | ||||||
|             free(loader_instance->application_arguments); |         loader->app.args = NULL; | ||||||
|             loader_instance->application_arguments = NULL; |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|         if(!(loader_instance->application->flags & FlipperApplicationFlagInsomniaSafe)) { |     if(loader->app.insomniac) { | ||||||
|         furi_hal_power_insomnia_exit(); |         furi_hal_power_insomnia_exit(); | ||||||
|     } |     } | ||||||
|         loader_unlock(instance); |  | ||||||
| 
 | 
 | ||||||
|         event.type = LoaderEventTypeApplicationStopped; |     free(loader->app.name); | ||||||
|         furi_pubsub_publish(loader_instance->pubsub, &event); |     loader->app.name = NULL; | ||||||
|     } | 
 | ||||||
|  |     furi_thread_join(loader->app.thread); | ||||||
|  |     furi_thread_free(loader->app.thread); | ||||||
|  |     loader->app.thread = NULL; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static uint32_t loader_hide_menu(void* context) { | // app
 | ||||||
|     UNUSED(context); |  | ||||||
|     return VIEW_NONE; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| static uint32_t loader_back_to_primary_menu(void* context) { |  | ||||||
|     furi_assert(context); |  | ||||||
|     Submenu* submenu = context; |  | ||||||
|     submenu_set_selected_item(submenu, 0); |  | ||||||
|     return LoaderMenuViewPrimary; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| static Loader* loader_alloc() { |  | ||||||
|     Loader* instance = malloc(sizeof(Loader)); |  | ||||||
| 
 |  | ||||||
|     instance->application_thread = furi_thread_alloc(); |  | ||||||
| 
 |  | ||||||
|     furi_thread_set_state_context(instance->application_thread, instance); |  | ||||||
|     furi_thread_set_state_callback(instance->application_thread, loader_thread_state_callback); |  | ||||||
| 
 |  | ||||||
|     instance->pubsub = furi_pubsub_alloc(); |  | ||||||
| 
 |  | ||||||
| #ifdef SRV_CLI |  | ||||||
|     instance->cli = furi_record_open(RECORD_CLI); |  | ||||||
|     cli_add_command( |  | ||||||
|         instance->cli, RECORD_LOADER, CliCommandFlagParallelSafe, loader_cli, instance); |  | ||||||
| #else |  | ||||||
|     UNUSED(loader_cli); |  | ||||||
| #endif |  | ||||||
| 
 |  | ||||||
|     instance->loader_thread = furi_thread_get_current_id(); |  | ||||||
| 
 |  | ||||||
|     // Gui
 |  | ||||||
|     instance->gui = furi_record_open(RECORD_GUI); |  | ||||||
|     instance->view_dispatcher = view_dispatcher_alloc(); |  | ||||||
|     view_dispatcher_attach_to_gui( |  | ||||||
|         instance->view_dispatcher, instance->gui, ViewDispatcherTypeFullscreen); |  | ||||||
|     // Primary menu
 |  | ||||||
|     instance->primary_menu = menu_alloc(); |  | ||||||
|     view_set_previous_callback(menu_get_view(instance->primary_menu), loader_hide_menu); |  | ||||||
|     view_dispatcher_add_view( |  | ||||||
|         instance->view_dispatcher, LoaderMenuViewPrimary, menu_get_view(instance->primary_menu)); |  | ||||||
|     // Settings menu
 |  | ||||||
|     instance->settings_menu = submenu_alloc(); |  | ||||||
|     view_set_context(submenu_get_view(instance->settings_menu), instance->settings_menu); |  | ||||||
|     view_set_previous_callback( |  | ||||||
|         submenu_get_view(instance->settings_menu), loader_back_to_primary_menu); |  | ||||||
|     view_dispatcher_add_view( |  | ||||||
|         instance->view_dispatcher, |  | ||||||
|         LoaderMenuViewSettings, |  | ||||||
|         submenu_get_view(instance->settings_menu)); |  | ||||||
| 
 |  | ||||||
|     view_dispatcher_enable_queue(instance->view_dispatcher); |  | ||||||
| 
 |  | ||||||
|     return instance; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| static void loader_free(Loader* instance) { |  | ||||||
|     furi_assert(instance); |  | ||||||
| 
 |  | ||||||
|     if(instance->cli) { |  | ||||||
|         furi_record_close(RECORD_CLI); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     furi_pubsub_free(instance->pubsub); |  | ||||||
| 
 |  | ||||||
|     furi_thread_free(instance->application_thread); |  | ||||||
| 
 |  | ||||||
|     menu_free(loader_instance->primary_menu); |  | ||||||
|     view_dispatcher_remove_view(loader_instance->view_dispatcher, LoaderMenuViewPrimary); |  | ||||||
|     submenu_free(loader_instance->settings_menu); |  | ||||||
|     view_dispatcher_remove_view(loader_instance->view_dispatcher, LoaderMenuViewSettings); |  | ||||||
|     view_dispatcher_free(loader_instance->view_dispatcher); |  | ||||||
| 
 |  | ||||||
|     furi_record_close(RECORD_GUI); |  | ||||||
| 
 |  | ||||||
|     free(instance); |  | ||||||
|     instance = NULL; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| static void loader_build_menu() { |  | ||||||
|     FURI_LOG_I(TAG, "Building main menu"); |  | ||||||
|     size_t i; |  | ||||||
|     for(i = 0; i < FLIPPER_APPS_COUNT; i++) { |  | ||||||
|         menu_add_item( |  | ||||||
|             loader_instance->primary_menu, |  | ||||||
|             FLIPPER_APPS[i].name, |  | ||||||
|             FLIPPER_APPS[i].icon, |  | ||||||
|             i, |  | ||||||
|             loader_menu_callback, |  | ||||||
|             (void*)&FLIPPER_APPS[i]); |  | ||||||
|     } |  | ||||||
|     menu_add_item( |  | ||||||
|         loader_instance->primary_menu, |  | ||||||
|         "Settings", |  | ||||||
|         &A_Settings_14, |  | ||||||
|         i++, |  | ||||||
|         loader_submenu_callback, |  | ||||||
|         (void*)LoaderMenuViewSettings); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| static void loader_build_submenu() { |  | ||||||
|     FURI_LOG_I(TAG, "Building settings menu"); |  | ||||||
|     for(size_t i = 0; i < FLIPPER_SETTINGS_APPS_COUNT; i++) { |  | ||||||
|         submenu_add_item( |  | ||||||
|             loader_instance->settings_menu, |  | ||||||
|             FLIPPER_SETTINGS_APPS[i].name, |  | ||||||
|             i, |  | ||||||
|             loader_menu_callback, |  | ||||||
|             (void*)&FLIPPER_SETTINGS_APPS[i]); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void loader_show_menu() { |  | ||||||
|     furi_assert(loader_instance); |  | ||||||
|     furi_thread_flags_set(loader_instance->loader_thread, LOADER_THREAD_FLAG_SHOW_MENU); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void loader_update_menu() { |  | ||||||
|     menu_reset(loader_instance->primary_menu); |  | ||||||
|     loader_build_menu(); |  | ||||||
| } |  | ||||||
| 
 | 
 | ||||||
| int32_t loader_srv(void* p) { | int32_t loader_srv(void* p) { | ||||||
|     UNUSED(p); |     UNUSED(p); | ||||||
|  |     Loader* loader = loader_alloc(); | ||||||
|  |     furi_record_create(RECORD_LOADER, loader); | ||||||
|  | 
 | ||||||
|     FURI_LOG_I(TAG, "Executing system start hooks"); |     FURI_LOG_I(TAG, "Executing system start hooks"); | ||||||
|     for(size_t i = 0; i < FLIPPER_ON_SYSTEM_START_COUNT; i++) { |     for(size_t i = 0; i < FLIPPER_ON_SYSTEM_START_COUNT; i++) { | ||||||
|         FLIPPER_ON_SYSTEM_START[i](); |         FLIPPER_ON_SYSTEM_START[i](); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     FURI_LOG_I(TAG, "Starting"); |  | ||||||
|     loader_instance = loader_alloc(); |  | ||||||
| 
 |  | ||||||
|     loader_build_menu(); |  | ||||||
|     loader_build_submenu(); |  | ||||||
| 
 |  | ||||||
|     FURI_LOG_I(TAG, "Started"); |  | ||||||
| 
 |  | ||||||
|     furi_record_create(RECORD_LOADER, loader_instance); |  | ||||||
| 
 |  | ||||||
|     if(FLIPPER_AUTORUN_APP_NAME && strlen(FLIPPER_AUTORUN_APP_NAME)) { |     if(FLIPPER_AUTORUN_APP_NAME && strlen(FLIPPER_AUTORUN_APP_NAME)) { | ||||||
|         loader_start(loader_instance, FLIPPER_AUTORUN_APP_NAME, NULL); |         loader_do_start_by_name(loader, FLIPPER_AUTORUN_APP_NAME, NULL); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     while(1) { |     LoaderMessage message; | ||||||
|         uint32_t flags = |     while(true) { | ||||||
|             furi_thread_flags_wait(LOADER_THREAD_FLAG_ALL, FuriFlagWaitAny, FuriWaitForever); |         if(furi_message_queue_get(loader->queue, &message, FuriWaitForever) == FuriStatusOk) { | ||||||
|         if(flags & LOADER_THREAD_FLAG_SHOW_MENU) { |             switch(message.type) { | ||||||
|             menu_set_selected_item(loader_instance->primary_menu, 0); |             case LoaderMessageTypeStartByName: | ||||||
|             view_dispatcher_switch_to_view( |                 message.status_value->value = | ||||||
|                 loader_instance->view_dispatcher, LoaderMenuViewPrimary); |                     loader_do_start_by_name(loader, message.start.name, message.start.args); | ||||||
|             view_dispatcher_run(loader_instance->view_dispatcher); |                 api_lock_unlock(message.api_lock); | ||||||
|  |                 break; | ||||||
|  |             case LoaderMessageTypeShowMenu: | ||||||
|  |                 loader_do_menu_show(loader); | ||||||
|  |                 break; | ||||||
|  |             case LoaderMessageTypeMenuClosed: | ||||||
|  |                 loader_do_menu_closed(loader); | ||||||
|  |                 break; | ||||||
|  |             case LoaderMessageTypeIsLocked: | ||||||
|  |                 message.bool_value->value = loader_do_is_locked(loader); | ||||||
|  |                 api_lock_unlock(message.api_lock); | ||||||
|  |                 break; | ||||||
|  |             case LoaderMessageTypeAppClosed: | ||||||
|  |                 loader_do_app_closed(loader); | ||||||
|  |                 break; | ||||||
|  |             case LoaderMessageTypeLock: | ||||||
|  |                 message.bool_value->value = loader_do_lock(loader); | ||||||
|  |                 api_lock_unlock(message.api_lock); | ||||||
|  |                 break; | ||||||
|  |             case LoaderMessageTypeUnlock: | ||||||
|  |                 loader_do_unlock(loader); | ||||||
|  |             } | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 |  | ||||||
|     furi_record_destroy(RECORD_LOADER); |  | ||||||
|     loader_free(loader_instance); |  | ||||||
| 
 | 
 | ||||||
|     return 0; |     return 0; | ||||||
| } | } | ||||||
| 
 |  | ||||||
| FuriPubSub* loader_get_pubsub(Loader* instance) { |  | ||||||
|     return instance->pubsub; |  | ||||||
| } |  | ||||||
|  | |||||||
| @ -1,7 +1,5 @@ | |||||||
| #pragma once | #pragma once | ||||||
| 
 | #include <furi.h> | ||||||
| #include <core/pubsub.h> |  | ||||||
| #include <stdbool.h> |  | ||||||
| 
 | 
 | ||||||
| #ifdef __cplusplus | #ifdef __cplusplus | ||||||
| extern "C" { | extern "C" { | ||||||
| @ -43,13 +41,10 @@ bool loader_lock(Loader* instance); | |||||||
| void loader_unlock(Loader* instance); | void loader_unlock(Loader* instance); | ||||||
| 
 | 
 | ||||||
| /** Get loader lock status */ | /** Get loader lock status */ | ||||||
| bool loader_is_locked(const Loader* instance); | bool loader_is_locked(Loader* instance); | ||||||
| 
 | 
 | ||||||
| /** Show primary loader */ | /** Show primary loader */ | ||||||
| void loader_show_menu(); | void loader_show_menu(Loader* instance); | ||||||
| 
 |  | ||||||
| /** Show primary loader */ |  | ||||||
| void loader_update_menu(); |  | ||||||
| 
 | 
 | ||||||
| /** Show primary loader */ | /** Show primary loader */ | ||||||
| FuriPubSub* loader_get_pubsub(Loader* instance); | FuriPubSub* loader_get_pubsub(Loader* instance); | ||||||
|  | |||||||
							
								
								
									
										117
									
								
								applications/services/loader/loader_cli.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										117
									
								
								applications/services/loader/loader_cli.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,117 @@ | |||||||
|  | #include <furi.h> | ||||||
|  | #include <cli/cli.h> | ||||||
|  | #include <applications.h> | ||||||
|  | #include <lib/toolbox/args.h> | ||||||
|  | #include "loader.h" | ||||||
|  | 
 | ||||||
|  | static void loader_cli_print_usage() { | ||||||
|  |     printf("Usage:\r\n"); | ||||||
|  |     printf("loader <cmd> <args>\r\n"); | ||||||
|  |     printf("Cmd list:\r\n"); | ||||||
|  |     printf("\tlist\t - List available applications\r\n"); | ||||||
|  |     printf("\topen <Application Name:string>\t - Open application by name\r\n"); | ||||||
|  |     printf("\tinfo\t - Show loader state\r\n"); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void loader_cli_list() { | ||||||
|  |     printf("Applications:\r\n"); | ||||||
|  |     for(size_t i = 0; i < FLIPPER_APPS_COUNT; i++) { | ||||||
|  |         printf("\t%s\r\n", FLIPPER_APPS[i].name); | ||||||
|  |     } | ||||||
|  |     printf("Settings:\r\n"); | ||||||
|  |     for(size_t i = 0; i < FLIPPER_SETTINGS_APPS_COUNT; i++) { | ||||||
|  |         printf("\t%s\r\n", FLIPPER_SETTINGS_APPS[i].name); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void loader_cli_info(Loader* loader) { | ||||||
|  |     if(!loader_is_locked(loader)) { | ||||||
|  |         printf("No application is running\r\n"); | ||||||
|  |     } else { | ||||||
|  |         // TODO: print application name ???
 | ||||||
|  |         printf("Application is running\r\n"); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void loader_cli_open(FuriString* args, Loader* loader) { | ||||||
|  |     FuriString* app_name = furi_string_alloc(); | ||||||
|  | 
 | ||||||
|  |     do { | ||||||
|  |         if(!args_read_probably_quoted_string_and_trim(args, app_name)) { | ||||||
|  |             printf("No application provided\r\n"); | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  |         furi_string_trim(args); | ||||||
|  | 
 | ||||||
|  |         const char* args_str = furi_string_get_cstr(args); | ||||||
|  |         if(strlen(args_str) == 0) { | ||||||
|  |             args_str = NULL; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         const char* app_name_str = furi_string_get_cstr(app_name); | ||||||
|  | 
 | ||||||
|  |         LoaderStatus status = loader_start(loader, app_name_str, args_str); | ||||||
|  | 
 | ||||||
|  |         switch(status) { | ||||||
|  |         case LoaderStatusOk: | ||||||
|  |             break; | ||||||
|  |         case LoaderStatusErrorAppStarted: | ||||||
|  |             printf("Can't start, application is running"); | ||||||
|  |             break; | ||||||
|  |         case LoaderStatusErrorUnknownApp: | ||||||
|  |             printf("%s doesn't exists\r\n", app_name_str); | ||||||
|  |             break; | ||||||
|  |         case LoaderStatusErrorInternal: | ||||||
|  |             printf("Internal error\r\n"); | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  |     } while(false); | ||||||
|  | 
 | ||||||
|  |     furi_string_free(app_name); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void loader_cli(Cli* cli, FuriString* args, void* context) { | ||||||
|  |     UNUSED(cli); | ||||||
|  |     UNUSED(context); | ||||||
|  |     Loader* loader = furi_record_open(RECORD_LOADER); | ||||||
|  | 
 | ||||||
|  |     FuriString* cmd; | ||||||
|  |     cmd = furi_string_alloc(); | ||||||
|  | 
 | ||||||
|  |     do { | ||||||
|  |         if(!args_read_string_and_trim(args, cmd)) { | ||||||
|  |             loader_cli_print_usage(); | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         if(furi_string_cmp_str(cmd, "list") == 0) { | ||||||
|  |             loader_cli_list(); | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         if(furi_string_cmp_str(cmd, "open") == 0) { | ||||||
|  |             loader_cli_open(args, loader); | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         if(furi_string_cmp_str(cmd, "info") == 0) { | ||||||
|  |             loader_cli_info(loader); | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         loader_cli_print_usage(); | ||||||
|  |     } while(false); | ||||||
|  | 
 | ||||||
|  |     furi_string_free(cmd); | ||||||
|  |     furi_record_close(RECORD_LOADER); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void loader_on_system_start() { | ||||||
|  | #ifdef SRV_CLI | ||||||
|  |     Cli* cli = furi_record_open(RECORD_CLI); | ||||||
|  |     cli_add_command(cli, RECORD_LOADER, CliCommandFlagParallelSafe, loader_cli, NULL); | ||||||
|  |     furi_record_close(RECORD_CLI); | ||||||
|  | #else | ||||||
|  |     UNUSED(loader_cli); | ||||||
|  | #endif | ||||||
|  | } | ||||||
| @ -1,39 +1,56 @@ | |||||||
| #include "loader.h" | #pragma once | ||||||
| 
 |  | ||||||
| #include <furi.h> | #include <furi.h> | ||||||
| #include <furi_hal.h> | #include <toolbox/api_lock.h> | ||||||
| #include <core/pubsub.h> | #include "loader.h" | ||||||
| #include <cli/cli.h> | #include "loader_menu.h" | ||||||
| #include <lib/toolbox/args.h> |  | ||||||
| 
 | 
 | ||||||
| #include <gui/view_dispatcher.h> | typedef struct { | ||||||
| 
 |     char* args; | ||||||
| #include <gui/modules/menu.h> |     char* name; | ||||||
| #include <gui/modules/submenu.h> |     FuriThread* thread; | ||||||
| 
 |     bool insomniac; | ||||||
| #include <applications.h> | } LoaderAppData; | ||||||
| #include <assets_icons.h> |  | ||||||
| 
 | 
 | ||||||
| struct Loader { | struct Loader { | ||||||
|     FuriThreadId loader_thread; |  | ||||||
| 
 |  | ||||||
|     const FlipperApplication* application; |  | ||||||
|     FuriThread* application_thread; |  | ||||||
|     char* application_arguments; |  | ||||||
| 
 |  | ||||||
|     Cli* cli; |  | ||||||
|     Gui* gui; |  | ||||||
| 
 |  | ||||||
|     ViewDispatcher* view_dispatcher; |  | ||||||
|     Menu* primary_menu; |  | ||||||
|     Submenu* settings_menu; |  | ||||||
| 
 |  | ||||||
|     volatile uint8_t lock_count; |  | ||||||
| 
 |  | ||||||
|     FuriPubSub* pubsub; |     FuriPubSub* pubsub; | ||||||
|  |     FuriMessageQueue* queue; | ||||||
|  |     LoaderMenu* loader_menu; | ||||||
|  |     LoaderAppData app; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| typedef enum { | typedef enum { | ||||||
|     LoaderMenuViewPrimary, |     LoaderMessageTypeStartByName, | ||||||
|     LoaderMenuViewSettings, |     LoaderMessageTypeAppClosed, | ||||||
| } LoaderMenuView; |     LoaderMessageTypeShowMenu, | ||||||
|  |     LoaderMessageTypeMenuClosed, | ||||||
|  |     LoaderMessageTypeLock, | ||||||
|  |     LoaderMessageTypeUnlock, | ||||||
|  |     LoaderMessageTypeIsLocked, | ||||||
|  | } LoaderMessageType; | ||||||
|  | 
 | ||||||
|  | typedef struct { | ||||||
|  |     const char* name; | ||||||
|  |     const char* args; | ||||||
|  | } LoaderMessageStartByName; | ||||||
|  | 
 | ||||||
|  | typedef struct { | ||||||
|  |     LoaderStatus value; | ||||||
|  | } LoaderMessageLoaderStatusResult; | ||||||
|  | 
 | ||||||
|  | typedef struct { | ||||||
|  |     bool value; | ||||||
|  | } LoaderMessageBoolResult; | ||||||
|  | 
 | ||||||
|  | typedef struct { | ||||||
|  |     FuriApiLock api_lock; | ||||||
|  |     LoaderMessageType type; | ||||||
|  | 
 | ||||||
|  |     union { | ||||||
|  |         LoaderMessageStartByName start; | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     union { | ||||||
|  |         LoaderMessageLoaderStatusResult* status_value; | ||||||
|  |         LoaderMessageBoolResult* bool_value; | ||||||
|  |     }; | ||||||
|  | } LoaderMessage; | ||||||
|  | |||||||
							
								
								
									
										187
									
								
								applications/services/loader/loader_menu.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										187
									
								
								applications/services/loader/loader_menu.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,187 @@ | |||||||
|  | #include <gui/gui.h> | ||||||
|  | #include <gui/view_dispatcher.h> | ||||||
|  | #include <gui/modules/menu.h> | ||||||
|  | #include <gui/modules/submenu.h> | ||||||
|  | #include <assets_icons.h> | ||||||
|  | #include <applications.h> | ||||||
|  | 
 | ||||||
|  | #include "loader_menu.h" | ||||||
|  | 
 | ||||||
|  | #define TAG "LoaderMenu" | ||||||
|  | 
 | ||||||
|  | struct LoaderMenu { | ||||||
|  |     Gui* gui; | ||||||
|  |     ViewDispatcher* view_dispatcher; | ||||||
|  |     Menu* primary_menu; | ||||||
|  |     Submenu* settings_menu; | ||||||
|  | 
 | ||||||
|  |     void (*closed_callback)(void*); | ||||||
|  |     void* closed_callback_context; | ||||||
|  | 
 | ||||||
|  |     void (*click_callback)(const char*, void*); | ||||||
|  |     void* click_callback_context; | ||||||
|  | 
 | ||||||
|  |     FuriThread* thread; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | typedef enum { | ||||||
|  |     LoaderMenuViewPrimary, | ||||||
|  |     LoaderMenuViewSettings, | ||||||
|  | } LoaderMenuView; | ||||||
|  | 
 | ||||||
|  | static int32_t loader_menu_thread(void* p); | ||||||
|  | 
 | ||||||
|  | LoaderMenu* loader_menu_alloc() { | ||||||
|  |     LoaderMenu* loader_menu = malloc(sizeof(LoaderMenu)); | ||||||
|  |     loader_menu->gui = furi_record_open(RECORD_GUI); | ||||||
|  |     loader_menu->view_dispatcher = view_dispatcher_alloc(); | ||||||
|  |     loader_menu->primary_menu = menu_alloc(); | ||||||
|  |     loader_menu->settings_menu = submenu_alloc(); | ||||||
|  |     loader_menu->thread = NULL; | ||||||
|  |     return loader_menu; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void loader_menu_free(LoaderMenu* loader_menu) { | ||||||
|  |     furi_assert(loader_menu); | ||||||
|  |     // check if thread is running
 | ||||||
|  |     furi_assert(!loader_menu->thread); | ||||||
|  | 
 | ||||||
|  |     submenu_free(loader_menu->settings_menu); | ||||||
|  |     menu_free(loader_menu->primary_menu); | ||||||
|  |     view_dispatcher_free(loader_menu->view_dispatcher); | ||||||
|  |     furi_record_close(RECORD_GUI); | ||||||
|  |     free(loader_menu); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void loader_menu_start(LoaderMenu* loader_menu) { | ||||||
|  |     furi_assert(loader_menu); | ||||||
|  |     furi_assert(!loader_menu->thread); | ||||||
|  |     loader_menu->thread = furi_thread_alloc_ex(TAG, 1024, loader_menu_thread, loader_menu); | ||||||
|  |     furi_thread_start(loader_menu->thread); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void loader_menu_stop(LoaderMenu* loader_menu) { | ||||||
|  |     furi_assert(loader_menu); | ||||||
|  |     furi_assert(loader_menu->thread); | ||||||
|  |     view_dispatcher_stop(loader_menu->view_dispatcher); | ||||||
|  |     furi_thread_join(loader_menu->thread); | ||||||
|  |     furi_thread_free(loader_menu->thread); | ||||||
|  |     loader_menu->thread = NULL; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void loader_menu_set_closed_callback( | ||||||
|  |     LoaderMenu* loader_menu, | ||||||
|  |     void (*callback)(void*), | ||||||
|  |     void* context) { | ||||||
|  |     loader_menu->closed_callback = callback; | ||||||
|  |     loader_menu->closed_callback_context = context; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void loader_menu_set_click_callback( | ||||||
|  |     LoaderMenu* loader_menu, | ||||||
|  |     void (*callback)(const char*, void*), | ||||||
|  |     void* context) { | ||||||
|  |     loader_menu->click_callback = callback; | ||||||
|  |     loader_menu->click_callback_context = context; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void loader_menu_callback(void* context, uint32_t index) { | ||||||
|  |     LoaderMenu* loader_menu = context; | ||||||
|  |     const char* name = FLIPPER_APPS[index].name; | ||||||
|  |     if(loader_menu->click_callback) { | ||||||
|  |         loader_menu->click_callback(name, loader_menu->click_callback_context); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void loader_menu_settings_menu_callback(void* context, uint32_t index) { | ||||||
|  |     LoaderMenu* loader_menu = context; | ||||||
|  |     const char* name = FLIPPER_SETTINGS_APPS[index].name; | ||||||
|  |     if(loader_menu->click_callback) { | ||||||
|  |         loader_menu->click_callback(name, loader_menu->click_callback_context); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void loader_menu_switch_to_settings(void* context, uint32_t index) { | ||||||
|  |     UNUSED(index); | ||||||
|  |     LoaderMenu* loader_menu = context; | ||||||
|  |     view_dispatcher_switch_to_view(loader_menu->view_dispatcher, LoaderMenuViewSettings); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static uint32_t loader_menu_switch_to_primary(void* context) { | ||||||
|  |     UNUSED(context); | ||||||
|  |     return LoaderMenuViewPrimary; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static uint32_t loader_menu_exit(void* context) { | ||||||
|  |     UNUSED(context); | ||||||
|  |     return VIEW_NONE; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void loader_menu_build_menu(LoaderMenu* loader_menu) { | ||||||
|  |     size_t i; | ||||||
|  |     for(i = 0; i < FLIPPER_APPS_COUNT; i++) { | ||||||
|  |         menu_add_item( | ||||||
|  |             loader_menu->primary_menu, | ||||||
|  |             FLIPPER_APPS[i].name, | ||||||
|  |             FLIPPER_APPS[i].icon, | ||||||
|  |             i, | ||||||
|  |             loader_menu_callback, | ||||||
|  |             (void*)loader_menu); | ||||||
|  |     } | ||||||
|  |     menu_add_item( | ||||||
|  |         loader_menu->primary_menu, | ||||||
|  |         "Settings", | ||||||
|  |         &A_Settings_14, | ||||||
|  |         i++, | ||||||
|  |         loader_menu_switch_to_settings, | ||||||
|  |         loader_menu); | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | static void loader_menu_build_submenu(LoaderMenu* loader_menu) { | ||||||
|  |     for(size_t i = 0; i < FLIPPER_SETTINGS_APPS_COUNT; i++) { | ||||||
|  |         submenu_add_item( | ||||||
|  |             loader_menu->settings_menu, | ||||||
|  |             FLIPPER_SETTINGS_APPS[i].name, | ||||||
|  |             i, | ||||||
|  |             loader_menu_settings_menu_callback, | ||||||
|  |             loader_menu); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static int32_t loader_menu_thread(void* p) { | ||||||
|  |     LoaderMenu* loader_menu = p; | ||||||
|  |     furi_assert(loader_menu); | ||||||
|  | 
 | ||||||
|  |     loader_menu_build_menu(loader_menu); | ||||||
|  |     loader_menu_build_submenu(loader_menu); | ||||||
|  | 
 | ||||||
|  |     view_dispatcher_attach_to_gui( | ||||||
|  |         loader_menu->view_dispatcher, loader_menu->gui, ViewDispatcherTypeFullscreen); | ||||||
|  | 
 | ||||||
|  |     // Primary menu
 | ||||||
|  |     View* primary_view = menu_get_view(loader_menu->primary_menu); | ||||||
|  |     view_set_context(primary_view, loader_menu->primary_menu); | ||||||
|  |     view_set_previous_callback(primary_view, loader_menu_exit); | ||||||
|  |     view_dispatcher_add_view(loader_menu->view_dispatcher, LoaderMenuViewPrimary, primary_view); | ||||||
|  | 
 | ||||||
|  |     // Settings menu
 | ||||||
|  |     View* settings_view = submenu_get_view(loader_menu->settings_menu); | ||||||
|  |     view_set_context(settings_view, loader_menu->settings_menu); | ||||||
|  |     view_set_previous_callback(settings_view, loader_menu_switch_to_primary); | ||||||
|  |     view_dispatcher_add_view(loader_menu->view_dispatcher, LoaderMenuViewSettings, settings_view); | ||||||
|  | 
 | ||||||
|  |     view_dispatcher_enable_queue(loader_menu->view_dispatcher); | ||||||
|  |     view_dispatcher_switch_to_view(loader_menu->view_dispatcher, LoaderMenuViewPrimary); | ||||||
|  | 
 | ||||||
|  |     // run view dispatcher
 | ||||||
|  |     view_dispatcher_run(loader_menu->view_dispatcher); | ||||||
|  | 
 | ||||||
|  |     view_dispatcher_remove_view(loader_menu->view_dispatcher, LoaderMenuViewPrimary); | ||||||
|  |     view_dispatcher_remove_view(loader_menu->view_dispatcher, LoaderMenuViewSettings); | ||||||
|  | 
 | ||||||
|  |     if(loader_menu->closed_callback) { | ||||||
|  |         loader_menu->closed_callback(loader_menu->closed_callback_context); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return 0; | ||||||
|  | } | ||||||
							
								
								
									
										30
									
								
								applications/services/loader/loader_menu.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								applications/services/loader/loader_menu.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,30 @@ | |||||||
|  | #pragma once | ||||||
|  | #include <furi.h> | ||||||
|  | 
 | ||||||
|  | #ifdef __cplusplus | ||||||
|  | extern "C" { | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | typedef struct LoaderMenu LoaderMenu; | ||||||
|  | 
 | ||||||
|  | LoaderMenu* loader_menu_alloc(); | ||||||
|  | 
 | ||||||
|  | void loader_menu_free(LoaderMenu* loader_menu); | ||||||
|  | 
 | ||||||
|  | void loader_menu_start(LoaderMenu* loader_menu); | ||||||
|  | 
 | ||||||
|  | void loader_menu_stop(LoaderMenu* loader_menu); | ||||||
|  | 
 | ||||||
|  | void loader_menu_set_closed_callback( | ||||||
|  |     LoaderMenu* loader_menu, | ||||||
|  |     void (*callback)(void*), | ||||||
|  |     void* context); | ||||||
|  | 
 | ||||||
|  | void loader_menu_set_click_callback( | ||||||
|  |     LoaderMenu* loader_menu, | ||||||
|  |     void (*callback)(const char*, void*), | ||||||
|  |     void* context); | ||||||
|  | 
 | ||||||
|  | #ifdef __cplusplus | ||||||
|  | } | ||||||
|  | #endif | ||||||
| @ -57,6 +57,10 @@ static RpcSystemCallbacks rpc_systems[] = { | |||||||
|         .alloc = rpc_system_property_alloc, |         .alloc = rpc_system_property_alloc, | ||||||
|         .free = NULL, |         .free = NULL, | ||||||
|     }, |     }, | ||||||
|  |     { | ||||||
|  |         .alloc = rpc_desktop_alloc, | ||||||
|  |         .free = rpc_desktop_free, | ||||||
|  |     }, | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| struct RpcSession { | struct RpcSession { | ||||||
| @ -326,15 +330,13 @@ static int32_t rpc_session_worker(void* context) { | |||||||
|     return 0; |     return 0; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static void rpc_session_free_callback(FuriThreadState thread_state, void* context) { | static void rpc_session_thread_pending_callback(void* context, uint32_t arg) { | ||||||
|     furi_assert(context); |     UNUSED(arg); | ||||||
| 
 |  | ||||||
|     RpcSession* session = (RpcSession*)context; |     RpcSession* session = (RpcSession*)context; | ||||||
| 
 | 
 | ||||||
|     if(thread_state == FuriThreadStateStopped) { |  | ||||||
|     for(size_t i = 0; i < COUNT_OF(rpc_systems); ++i) { |     for(size_t i = 0; i < COUNT_OF(rpc_systems); ++i) { | ||||||
|         if(rpc_systems[i].free) { |         if(rpc_systems[i].free) { | ||||||
|                 rpc_systems[i].free(session->system_contexts[i]); |             (rpc_systems[i].free)(session->system_contexts[i]); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|     free(session->system_contexts); |     free(session->system_contexts); | ||||||
| @ -349,8 +351,14 @@ static void rpc_session_free_callback(FuriThreadState thread_state, void* contex | |||||||
|     furi_mutex_release(session->callbacks_mutex); |     furi_mutex_release(session->callbacks_mutex); | ||||||
| 
 | 
 | ||||||
|     furi_mutex_free(session->callbacks_mutex); |     furi_mutex_free(session->callbacks_mutex); | ||||||
|  |     furi_thread_join(session->thread); | ||||||
|     furi_thread_free(session->thread); |     furi_thread_free(session->thread); | ||||||
|     free(session); |     free(session); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void rpc_session_thread_state_callback(FuriThreadState thread_state, void* context) { | ||||||
|  |     if(thread_state == FuriThreadStateStopped) { | ||||||
|  |         furi_timer_pending_callback(rpc_session_thread_pending_callback, context, 0); | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @ -385,7 +393,7 @@ RpcSession* rpc_session_open(Rpc* rpc, RpcOwner owner) { | |||||||
|     session->thread = furi_thread_alloc_ex("RpcSessionWorker", 3072, rpc_session_worker, session); |     session->thread = furi_thread_alloc_ex("RpcSessionWorker", 3072, rpc_session_worker, session); | ||||||
| 
 | 
 | ||||||
|     furi_thread_set_state_context(session->thread, session); |     furi_thread_set_state_context(session->thread, session); | ||||||
|     furi_thread_set_state_callback(session->thread, rpc_session_free_callback); |     furi_thread_set_state_callback(session->thread, rpc_session_thread_state_callback); | ||||||
| 
 | 
 | ||||||
|     furi_thread_start(session->thread); |     furi_thread_start(session->thread); | ||||||
| 
 | 
 | ||||||
|  | |||||||
							
								
								
									
										73
									
								
								applications/services/rpc/rpc_desktop.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										73
									
								
								applications/services/rpc/rpc_desktop.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,73 @@ | |||||||
|  | #include "flipper.pb.h" | ||||||
|  | #include "rpc_i.h" | ||||||
|  | #include <desktop/desktop.h> | ||||||
|  | #include "desktop.pb.h" | ||||||
|  | 
 | ||||||
|  | #define TAG "RpcDesktop" | ||||||
|  | 
 | ||||||
|  | typedef struct { | ||||||
|  |     RpcSession* session; | ||||||
|  |     Desktop* desktop; | ||||||
|  | } RpcDesktop; | ||||||
|  | 
 | ||||||
|  | static void rpc_desktop_on_is_locked_request(const PB_Main* request, void* context) { | ||||||
|  |     furi_assert(request); | ||||||
|  |     furi_assert(context); | ||||||
|  |     furi_assert(request->which_content == PB_Main_desktop_is_locked_request_tag); | ||||||
|  | 
 | ||||||
|  |     FURI_LOG_D(TAG, "IsLockedRequest"); | ||||||
|  |     RpcDesktop* rpc_desktop = context; | ||||||
|  |     RpcSession* session = rpc_desktop->session; | ||||||
|  | 
 | ||||||
|  |     PB_CommandStatus ret = desktop_api_is_locked(rpc_desktop->desktop) ? PB_CommandStatus_OK : | ||||||
|  |                                                                          PB_CommandStatus_ERROR; | ||||||
|  | 
 | ||||||
|  |     rpc_send_and_release_empty(session, request->command_id, ret); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void rpc_desktop_on_unlock_request(const PB_Main* request, void* context) { | ||||||
|  |     furi_assert(request); | ||||||
|  |     furi_assert(context); | ||||||
|  |     furi_assert(request->which_content == PB_Main_desktop_unlock_request_tag); | ||||||
|  | 
 | ||||||
|  |     FURI_LOG_D(TAG, "UnlockRequest"); | ||||||
|  |     RpcDesktop* rpc_desktop = context; | ||||||
|  |     RpcSession* session = rpc_desktop->session; | ||||||
|  | 
 | ||||||
|  |     desktop_api_unlock(rpc_desktop->desktop); | ||||||
|  | 
 | ||||||
|  |     rpc_send_and_release_empty(session, request->command_id, PB_CommandStatus_OK); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void* rpc_desktop_alloc(RpcSession* session) { | ||||||
|  |     furi_assert(session); | ||||||
|  | 
 | ||||||
|  |     RpcDesktop* rpc_desktop = malloc(sizeof(RpcDesktop)); | ||||||
|  |     rpc_desktop->desktop = furi_record_open(RECORD_DESKTOP); | ||||||
|  |     rpc_desktop->session = session; | ||||||
|  | 
 | ||||||
|  |     RpcHandler rpc_handler = { | ||||||
|  |         .message_handler = NULL, | ||||||
|  |         .decode_submessage = NULL, | ||||||
|  |         .context = rpc_desktop, | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     rpc_handler.message_handler = rpc_desktop_on_is_locked_request; | ||||||
|  |     rpc_add_handler(session, PB_Main_desktop_is_locked_request_tag, &rpc_handler); | ||||||
|  | 
 | ||||||
|  |     rpc_handler.message_handler = rpc_desktop_on_unlock_request; | ||||||
|  |     rpc_add_handler(session, PB_Main_desktop_unlock_request_tag, &rpc_handler); | ||||||
|  | 
 | ||||||
|  |     return rpc_desktop; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void rpc_desktop_free(void* context) { | ||||||
|  |     furi_assert(context); | ||||||
|  |     RpcDesktop* rpc_desktop = context; | ||||||
|  | 
 | ||||||
|  |     furi_assert(rpc_desktop->desktop); | ||||||
|  |     furi_record_close(RECORD_DESKTOP); | ||||||
|  | 
 | ||||||
|  |     rpc_desktop->session = NULL; | ||||||
|  |     free(rpc_desktop); | ||||||
|  | } | ||||||
| @ -36,6 +36,9 @@ void* rpc_system_gpio_alloc(RpcSession* session); | |||||||
| void rpc_system_gpio_free(void* ctx); | void rpc_system_gpio_free(void* ctx); | ||||||
| void* rpc_system_property_alloc(RpcSession* session); | void* rpc_system_property_alloc(RpcSession* session); | ||||||
| 
 | 
 | ||||||
|  | void* rpc_desktop_alloc(RpcSession* session); | ||||||
|  | void rpc_desktop_free(void* ctx); | ||||||
|  | 
 | ||||||
| void rpc_debug_print_message(const PB_Main* message); | void rpc_debug_print_message(const PB_Main* message); | ||||||
| void rpc_debug_print_data(const char* prefix, uint8_t* buffer, size_t size); | void rpc_debug_print_data(const char* prefix, uint8_t* buffer, size_t size); | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -803,6 +803,7 @@ void storage_file_free(File* file) { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| FuriPubSub* storage_get_pubsub(Storage* storage) { | FuriPubSub* storage_get_pubsub(Storage* storage) { | ||||||
|  |     furi_assert(storage); | ||||||
|     return storage->pubsub; |     return storage->pubsub; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -337,6 +337,7 @@ static bool storage_ext_file_close(void* ctx, File* file) { | |||||||
|     file->internal_error_id = f_close(file_data); |     file->internal_error_id = f_close(file_data); | ||||||
|     file->error_id = storage_ext_parse_error(file->internal_error_id); |     file->error_id = storage_ext_parse_error(file->internal_error_id); | ||||||
|     free(file_data); |     free(file_data); | ||||||
|  |     storage_set_storage_file_data(file, NULL, storage); | ||||||
|     return (file->error_id == FSE_OK); |     return (file->error_id == FSE_OK); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -7,6 +7,7 @@ | |||||||
| #include <furi_hal_version.h> | #include <furi_hal_version.h> | ||||||
| #include <furi_hal_region.h> | #include <furi_hal_region.h> | ||||||
| #include <furi_hal_bt.h> | #include <furi_hal_bt.h> | ||||||
|  | #include <furi_hal_info.h> | ||||||
| 
 | 
 | ||||||
| typedef DialogMessageButton (*AboutDialogScreen)(DialogsApp* dialogs, DialogMessage* message); | typedef DialogMessageButton (*AboutDialogScreen)(DialogsApp* dialogs, DialogMessage* message); | ||||||
| 
 | 
 | ||||||
| @ -134,14 +135,17 @@ static DialogMessageButton fw_version_screen(DialogsApp* dialogs, DialogMessage* | |||||||
|     if(!ver) { //-V1051
 |     if(!ver) { //-V1051
 | ||||||
|         furi_string_cat_printf(buffer, "No info\n"); |         furi_string_cat_printf(buffer, "No info\n"); | ||||||
|     } else { |     } else { | ||||||
|  |         uint16_t api_major, api_minor; | ||||||
|  |         furi_hal_info_get_api_version(&api_major, &api_minor); | ||||||
|         furi_string_cat_printf( |         furi_string_cat_printf( | ||||||
|             buffer, |             buffer, | ||||||
|             "%s [%s]\n%s%s [%s] %s\n[%d] %s", |             "%s [%s]\n%s%s [%d.%d] %s\n[%d] %s", | ||||||
|             version_get_version(ver), |             version_get_version(ver), | ||||||
|             version_get_builddate(ver), |             version_get_builddate(ver), | ||||||
|             version_get_dirty_flag(ver) ? "[!] " : "", |             version_get_dirty_flag(ver) ? "[!] " : "", | ||||||
|             version_get_githash(ver), |             version_get_githash(ver), | ||||||
|             version_get_gitbranchnum(ver), |             api_major, | ||||||
|  |             api_minor, | ||||||
|             c2_ver ? c2_ver->StackTypeString : "<none>", |             c2_ver ? c2_ver->StackTypeString : "<none>", | ||||||
|             version_get_target(ver), |             version_get_target(ver), | ||||||
|             version_get_gitbranch(ver)); |             version_get_gitbranch(ver)); | ||||||
|  | |||||||
| @ -1,7 +1,7 @@ | |||||||
| #include <stdint.h> | #include <stdint.h> | ||||||
| #include <core/check.h> | #include <core/check.h> | ||||||
| #include <gui/scene_manager.h> | #include <gui/scene_manager.h> | ||||||
| #include <desktop/helpers/pin_lock.h> | #include <desktop/helpers/pin.h> | ||||||
| #include "../desktop_settings_app.h" | #include "../desktop_settings_app.h" | ||||||
| #include <desktop/desktop_settings.h> | #include <desktop/desktop_settings.h> | ||||||
| #include <desktop/views/desktop_view_pin_input.h> | #include <desktop/views/desktop_view_pin_input.h> | ||||||
| @ -18,7 +18,7 @@ static void pin_auth_done_callback(const PinCode* pin_code, void* context) { | |||||||
|     DesktopSettingsApp* app = context; |     DesktopSettingsApp* app = context; | ||||||
| 
 | 
 | ||||||
|     app->pincode_buffer = *pin_code; |     app->pincode_buffer = *pin_code; | ||||||
|     if(desktop_pins_are_equal(&app->settings.pin_code, pin_code)) { |     if(desktop_pin_compare(&app->settings.pin_code, pin_code)) { | ||||||
|         view_dispatcher_send_custom_event(app->view_dispatcher, SCENE_EVENT_PINS_EQUAL); |         view_dispatcher_send_custom_event(app->view_dispatcher, SCENE_EVENT_PINS_EQUAL); | ||||||
|     } else { |     } else { | ||||||
|         view_dispatcher_send_custom_event(app->view_dispatcher, SCENE_EVENT_PINS_DIFFERENT); |         view_dispatcher_send_custom_event(app->view_dispatcher, SCENE_EVENT_PINS_DIFFERENT); | ||||||
|  | |||||||
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
	 Aleksandr Kutuzov
						Aleksandr Kutuzov