Merge remote-tracking branch 'origin/dev' into release-candidate
							
								
								
									
										75
									
								
								.github/workflows/build.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						| @ -9,7 +9,7 @@ on: | |||||||
|   pull_request: |   pull_request: | ||||||
| 
 | 
 | ||||||
| env: | env: | ||||||
|   TARGETS: f7 |   TARGETS: f7 f18 | ||||||
|   DEFAULT_TARGET: f7 |   DEFAULT_TARGET: f7 | ||||||
|   FBT_TOOLCHAIN_PATH: /runner/_work |   FBT_TOOLCHAIN_PATH: /runner/_work | ||||||
| 
 | 
 | ||||||
| @ -52,10 +52,8 @@ jobs: | |||||||
| 
 | 
 | ||||||
|       - name: 'Make artifacts directory' |       - name: 'Make artifacts directory' | ||||||
|         run: | |         run: | | ||||||
|           rm -rf artifacts |           rm -rf artifacts map_analyser_files | ||||||
|           rm -rf map_analyser_files |           mkdir artifacts map_analyser_files | ||||||
|           mkdir artifacts |  | ||||||
|           mkdir map_analyser_files |  | ||||||
| 
 | 
 | ||||||
|       - name: 'Bundle scripts' |       - name: 'Bundle scripts' | ||||||
|         if: ${{ !github.event.pull_request.head.repo.fork }} |         if: ${{ !github.event.pull_request.head.repo.fork }} | ||||||
| @ -66,28 +64,21 @@ jobs: | |||||||
|         run: | |         run: | | ||||||
|           set -e |           set -e | ||||||
|           for TARGET in ${TARGETS}; do |           for TARGET in ${TARGETS}; do | ||||||
|             TARGET="$(echo "${TARGET}" | sed 's/f//')"; \ |             TARGET_HW="$(echo "${TARGET}" | sed 's/f//')"; \ | ||||||
|             ./fbt TARGET_HW=$TARGET copro_dist updater_package \ |             ./fbt TARGET_HW=$TARGET_HW copro_dist updater_package \ | ||||||
|               ${{ startsWith(github.ref, 'refs/tags') && 'DEBUG=0 COMPACT=1' || '' }} |               ${{ startsWith(github.ref, 'refs/tags') && 'DEBUG=0 COMPACT=1' || '' }} | ||||||
|           done |  | ||||||
| 
 |  | ||||||
|       - name: 'Move upload files' |  | ||||||
|         if: ${{ !github.event.pull_request.head.repo.fork }} |  | ||||||
|         run: | |  | ||||||
|           set -e |  | ||||||
|           for TARGET in ${TARGETS}; do |  | ||||||
|             mv dist/${TARGET}-*/* artifacts/ |             mv dist/${TARGET}-*/* artifacts/ | ||||||
|  |             tar czpf "artifacts/flipper-z-${TARGET}-resources-${SUFFIX}.tgz" \ | ||||||
|  |               -C assets resources | ||||||
|  |             ./fbt TARGET_HW=$TARGET_HW fap_dist | ||||||
|  |             tar czpf "artifacts/flipper-z-${TARGET}-debugapps-${SUFFIX}.tgz" \ | ||||||
|  |               -C dist/${TARGET}-*/apps/Debug . | ||||||
|           done |           done | ||||||
| 
 | 
 | ||||||
|       - name: "Check for uncommitted changes" |       - name: "Check for uncommitted changes" | ||||||
|         run: | |         run: | | ||||||
|           git diff --exit-code |           git diff --exit-code | ||||||
| 
 | 
 | ||||||
|       - name: 'Bundle resources' |  | ||||||
|         if: ${{ !github.event.pull_request.head.repo.fork }} |  | ||||||
|         run: | |  | ||||||
|           tar czpf "artifacts/flipper-z-any-resources-${SUFFIX}.tgz" -C assets resources |  | ||||||
| 
 |  | ||||||
|       - name: 'Bundle core2 firmware' |       - name: 'Bundle core2 firmware' | ||||||
|         if: ${{ !github.event.pull_request.head.repo.fork }} |         if: ${{ !github.event.pull_request.head.repo.fork }} | ||||||
|         run: | |         run: | | ||||||
| @ -96,29 +87,35 @@ jobs: | |||||||
|       - name: 'Copy map analyser files' |       - name: 'Copy map analyser files' | ||||||
|         if: ${{ !github.event.pull_request.head.repo.fork }} |         if: ${{ !github.event.pull_request.head.repo.fork }} | ||||||
|         run: | |         run: | | ||||||
|           cp build/f7-firmware-*/firmware.elf.map map_analyser_files/firmware.elf.map |           cp build/${DEFAULT_TARGET}-firmware-*/firmware.elf.map map_analyser_files/firmware.elf.map | ||||||
|           cp build/f7-firmware-*/firmware.elf map_analyser_files/firmware.elf |           cp build/${DEFAULT_TARGET}-firmware-*/firmware.elf map_analyser_files/firmware.elf | ||||||
|           cp ${{ github.event_path }} map_analyser_files/event.json |           cp ${{ github.event_path }} map_analyser_files/event.json | ||||||
| 
 | 
 | ||||||
|       - name: 'Upload map analyser files to storage' |       - name: 'Analyse map file' | ||||||
|         if: ${{ !github.event.pull_request.head.repo.fork }} |         if: ${{ !github.event.pull_request.head.repo.fork }} | ||||||
|         uses: prewk/s3-cp-action@v2 |         run: | | ||||||
|         with: |           source scripts/toolchain/fbtenv.sh | ||||||
|           aws_s3_endpoint: "${{ secrets.MAP_REPORT_AWS_ENDPOINT }}" |           get_size() | ||||||
|           aws_access_key_id: "${{ secrets.MAP_REPORT_AWS_ACCESS_KEY }}" |           { | ||||||
|           aws_secret_access_key: "${{ secrets.MAP_REPORT_AWS_SECRET_KEY }}" |             SECTION="$1"; | ||||||
|           source: "./map_analyser_files/" |             arm-none-eabi-size \ | ||||||
|           dest: "s3://${{ secrets.MAP_REPORT_AWS_BUCKET }}/${{steps.names.outputs.random_hash}}" |               -A map_analyser_files/firmware.elf \ | ||||||
|           flags: "--recursive --acl public-read" |               | grep "^$SECTION" | awk '{print $2}' | ||||||
| 
 |           } | ||||||
|       - name: 'Trigger map file reporter' |           export BSS_SIZE="$(get_size ".bss")" | ||||||
|         if: ${{ !github.event.pull_request.head.repo.fork }} |           export TEXT_SIZE="$(get_size ".text")" | ||||||
|         uses: peter-evans/repository-dispatch@v2 |           export RODATA_SIZE="$(get_size ".rodata")" | ||||||
|         with: |           export DATA_SIZE="$(get_size ".data")" | ||||||
|           repository: flipperdevices/flipper-map-reporter |           export FREE_FLASH_SIZE="$(get_size ".free_flash")" | ||||||
|           token: ${{ secrets.REPOSITORY_DISPATCH_TOKEN }} |           python3 -m pip install mariadb==1.1.6 cxxfilt==0.3.0 | ||||||
|           event-type: map-file-analyse |           python3 scripts/map_parser.py map_analyser_files/firmware.elf.map map_analyser_files/firmware.elf.map.all | ||||||
|           client-payload: '{"random_hash": "${{steps.names.outputs.random_hash}}", "event_type": "${{steps.names.outputs.event_type}}"}' |           python3 scripts/map_mariadb_insert.py \ | ||||||
|  |               ${{ secrets.AMAP_MARIADB_USER }} \ | ||||||
|  |               ${{ secrets.AMAP_MARIADB_PASSWORD }} \ | ||||||
|  |               ${{ secrets.AMAP_MARIADB_HOST }} \ | ||||||
|  |               ${{ secrets.AMAP_MARIADB_PORT }} \ | ||||||
|  |               ${{ secrets.AMAP_MARIADB_DATABASE }} \ | ||||||
|  |               map_analyser_files/firmware.elf.map.all | ||||||
| 
 | 
 | ||||||
|       - name: 'Upload artifacts to update server' |       - name: 'Upload artifacts to update server' | ||||||
|         if: ${{ !github.event.pull_request.head.repo.fork }} |         if: ${{ !github.event.pull_request.head.repo.fork }} | ||||||
|  | |||||||
| @ -171,7 +171,7 @@ distenv.Depends(firmware_env["FW_RESOURCES"], external_apps_artifacts.resources_ | |||||||
| 
 | 
 | ||||||
| fap_deploy = distenv.PhonyTarget( | fap_deploy = distenv.PhonyTarget( | ||||||
|     "fap_deploy", |     "fap_deploy", | ||||||
|     "${PYTHON3} ${ROOT_DIR}/scripts/storage.py send ${SOURCE} /ext/apps", |     "${PYTHON3} ${FBT_SCRIPT_DIR}/storage.py -p ${FLIP_PORT} send ${SOURCE} /ext/apps", | ||||||
|     source=Dir("#/assets/resources/apps"), |     source=Dir("#/assets/resources/apps"), | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| @ -323,7 +323,9 @@ distenv.PhonyTarget( | |||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| # Start Flipper CLI via PySerial's miniterm | # Start Flipper CLI via PySerial's miniterm | ||||||
| distenv.PhonyTarget("cli", "${PYTHON3} ${FBT_SCRIPT_DIR}/serial_cli.py") | distenv.PhonyTarget( | ||||||
|  |     "cli", "${PYTHON3} ${FBT_SCRIPT_DIR}/serial_cli.py  -p ${FLIP_PORT}" | ||||||
|  | ) | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| # Find blackmagic probe | # Find blackmagic probe | ||||||
|  | |||||||
							
								
								
									
										4
									
								
								applications/external/hid_app/hid.c
									
									
									
									
										vendored
									
									
								
							
							
						
						| @ -377,7 +377,7 @@ int32_t hid_usb_app(void* p) { | |||||||
| 
 | 
 | ||||||
|     bt_hid_connection_status_changed_callback(BtStatusConnected, app); |     bt_hid_connection_status_changed_callback(BtStatusConnected, app); | ||||||
| 
 | 
 | ||||||
|     DOLPHIN_DEED(DolphinDeedPluginStart); |     dolphin_deed(DolphinDeedPluginStart); | ||||||
| 
 | 
 | ||||||
|     view_dispatcher_run(app->view_dispatcher); |     view_dispatcher_run(app->view_dispatcher); | ||||||
| 
 | 
 | ||||||
| @ -417,7 +417,7 @@ int32_t hid_ble_app(void* p) { | |||||||
|     furi_hal_bt_start_advertising(); |     furi_hal_bt_start_advertising(); | ||||||
|     bt_set_status_changed_callback(app->bt, bt_hid_connection_status_changed_callback, app); |     bt_set_status_changed_callback(app->bt, bt_hid_connection_status_changed_callback, app); | ||||||
| 
 | 
 | ||||||
|     DOLPHIN_DEED(DolphinDeedPluginStart); |     dolphin_deed(DolphinDeedPluginStart); | ||||||
| 
 | 
 | ||||||
|     view_dispatcher_run(app->view_dispatcher); |     view_dispatcher_run(app->view_dispatcher); | ||||||
| 
 | 
 | ||||||
|  | |||||||
							
								
								
									
										2
									
								
								applications/external/mfkey32/mfkey32.c
									
									
									
									
										vendored
									
									
								
							
							
						
						| @ -1112,7 +1112,7 @@ void mfkey32(ProgramState* program_state) { | |||||||
|     } |     } | ||||||
|     if(keyarray_size > 0) { |     if(keyarray_size > 0) { | ||||||
|         // TODO: Should we use DolphinDeedNfcMfcAdd?
 |         // TODO: Should we use DolphinDeedNfcMfcAdd?
 | ||||||
|         DOLPHIN_DEED(DolphinDeedNfcMfcAdd); |         dolphin_deed(DolphinDeedNfcMfcAdd); | ||||||
|     } |     } | ||||||
|     napi_mf_classic_nonce_array_free(nonce_arr); |     napi_mf_classic_nonce_array_free(nonce_arr); | ||||||
|     napi_mf_classic_dict_free(user_dict); |     napi_mf_classic_dict_free(user_dict); | ||||||
|  | |||||||
| @ -19,7 +19,7 @@ void picopass_scene_device_info_on_enter(void* context) { | |||||||
|     FuriString* wiegand_str = furi_string_alloc(); |     FuriString* wiegand_str = furi_string_alloc(); | ||||||
|     FuriString* sio_str = furi_string_alloc(); |     FuriString* sio_str = furi_string_alloc(); | ||||||
| 
 | 
 | ||||||
|     DOLPHIN_DEED(DolphinDeedNfcReadSuccess); |     dolphin_deed(DolphinDeedNfcReadSuccess); | ||||||
| 
 | 
 | ||||||
|     // Setup view
 |     // Setup view
 | ||||||
|     PicopassBlock* AA1 = picopass->dev->dev_data.AA1; |     PicopassBlock* AA1 = picopass->dev->dev_data.AA1; | ||||||
|  | |||||||
| @ -10,7 +10,7 @@ void picopass_read_card_worker_callback(PicopassWorkerEvent event, void* context | |||||||
| 
 | 
 | ||||||
| void picopass_scene_read_card_on_enter(void* context) { | void picopass_scene_read_card_on_enter(void* context) { | ||||||
|     Picopass* picopass = context; |     Picopass* picopass = context; | ||||||
|     DOLPHIN_DEED(DolphinDeedNfcRead); |     dolphin_deed(DolphinDeedNfcRead); | ||||||
| 
 | 
 | ||||||
|     // Setup view
 |     // Setup view
 | ||||||
|     Popup* popup = picopass->popup; |     Popup* popup = picopass->popup; | ||||||
|  | |||||||
| @ -21,7 +21,7 @@ void picopass_scene_read_card_success_on_enter(void* context) { | |||||||
|     FuriString* wiegand_str = furi_string_alloc(); |     FuriString* wiegand_str = furi_string_alloc(); | ||||||
|     FuriString* sio_str = furi_string_alloc(); |     FuriString* sio_str = furi_string_alloc(); | ||||||
| 
 | 
 | ||||||
|     DOLPHIN_DEED(DolphinDeedNfcReadSuccess); |     dolphin_deed(DolphinDeedNfcReadSuccess); | ||||||
| 
 | 
 | ||||||
|     // Send notification
 |     // Send notification
 | ||||||
|     notification_message(picopass->notifications, &sequence_success); |     notification_message(picopass->notifications, &sequence_success); | ||||||
|  | |||||||
| @ -19,7 +19,7 @@ void picopass_scene_read_factory_success_on_enter(void* context) { | |||||||
|     FuriString* title = furi_string_alloc_set("Factory Default"); |     FuriString* title = furi_string_alloc_set("Factory Default"); | ||||||
|     FuriString* subtitle = furi_string_alloc_set(""); |     FuriString* subtitle = furi_string_alloc_set(""); | ||||||
| 
 | 
 | ||||||
|     DOLPHIN_DEED(DolphinDeedNfcReadSuccess); |     dolphin_deed(DolphinDeedNfcReadSuccess); | ||||||
| 
 | 
 | ||||||
|     // Send notification
 |     // Send notification
 | ||||||
|     notification_message(picopass->notifications, &sequence_success); |     notification_message(picopass->notifications, &sequence_success); | ||||||
|  | |||||||
| @ -8,7 +8,7 @@ void picopass_scene_save_success_popup_callback(void* context) { | |||||||
| 
 | 
 | ||||||
| void picopass_scene_save_success_on_enter(void* context) { | void picopass_scene_save_success_on_enter(void* context) { | ||||||
|     Picopass* picopass = context; |     Picopass* picopass = context; | ||||||
|     DOLPHIN_DEED(DolphinDeedNfcSave); |     dolphin_deed(DolphinDeedNfcSave); | ||||||
| 
 | 
 | ||||||
|     // Setup view
 |     // Setup view
 | ||||||
|     Popup* popup = picopass->popup; |     Popup* popup = picopass->popup; | ||||||
|  | |||||||
| @ -9,7 +9,7 @@ void picopass_write_card_worker_callback(PicopassWorkerEvent event, void* contex | |||||||
| 
 | 
 | ||||||
| void picopass_scene_write_card_on_enter(void* context) { | void picopass_scene_write_card_on_enter(void* context) { | ||||||
|     Picopass* picopass = context; |     Picopass* picopass = context; | ||||||
|     DOLPHIN_DEED(DolphinDeedNfcSave); |     dolphin_deed(DolphinDeedNfcSave); | ||||||
| 
 | 
 | ||||||
|     // Setup view
 |     // Setup view
 | ||||||
|     Popup* popup = picopass->popup; |     Popup* popup = picopass->popup; | ||||||
|  | |||||||
| @ -18,7 +18,7 @@ void picopass_scene_write_card_success_on_enter(void* context) { | |||||||
|     Widget* widget = picopass->widget; |     Widget* widget = picopass->widget; | ||||||
|     FuriString* str = furi_string_alloc_set("Write Success!"); |     FuriString* str = furi_string_alloc_set("Write Success!"); | ||||||
| 
 | 
 | ||||||
|     DOLPHIN_DEED(DolphinDeedNfcReadSuccess); |     dolphin_deed(DolphinDeedNfcReadSuccess); | ||||||
| 
 | 
 | ||||||
|     // Send notification
 |     // Send notification
 | ||||||
|     notification_message(picopass->notifications, &sequence_success); |     notification_message(picopass->notifications, &sequence_success); | ||||||
|  | |||||||
| @ -9,7 +9,7 @@ void picopass_write_key_worker_callback(PicopassWorkerEvent event, void* context | |||||||
| 
 | 
 | ||||||
| void picopass_scene_write_key_on_enter(void* context) { | void picopass_scene_write_key_on_enter(void* context) { | ||||||
|     Picopass* picopass = context; |     Picopass* picopass = context; | ||||||
|     DOLPHIN_DEED(DolphinDeedNfcSave); |     dolphin_deed(DolphinDeedNfcSave); | ||||||
| 
 | 
 | ||||||
|     // Setup view
 |     // Setup view
 | ||||||
|     Popup* popup = picopass->popup; |     Popup* popup = picopass->popup; | ||||||
|  | |||||||
| @ -346,7 +346,7 @@ int32_t snake_game_app(void* p) { | |||||||
| 
 | 
 | ||||||
|     notification_message_block(notification, &sequence_display_backlight_enforce_on); |     notification_message_block(notification, &sequence_display_backlight_enforce_on); | ||||||
| 
 | 
 | ||||||
|     DOLPHIN_DEED(DolphinDeedPluginGameStart); |     dolphin_deed(DolphinDeedPluginGameStart); | ||||||
| 
 | 
 | ||||||
|     SnakeEvent event; |     SnakeEvent event; | ||||||
|     for(bool processing = true; processing;) { |     for(bool processing = true; processing;) { | ||||||
|  | |||||||
							
								
								
									
										365
									
								
								applications/external/weather_station/protocols/oregon3.c
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,365 @@ | |||||||
|  | #include "oregon3.h" | ||||||
|  | 
 | ||||||
|  | #include <lib/subghz/blocks/const.h> | ||||||
|  | #include <lib/subghz/blocks/decoder.h> | ||||||
|  | #include <lib/subghz/blocks/encoder.h> | ||||||
|  | #include <lib/subghz/blocks/math.h> | ||||||
|  | #include "ws_generic.h" | ||||||
|  | 
 | ||||||
|  | #include <lib/toolbox/manchester_decoder.h> | ||||||
|  | #include <lib/flipper_format/flipper_format_i.h> | ||||||
|  | 
 | ||||||
|  | #define TAG "WSProtocolOregon3" | ||||||
|  | 
 | ||||||
|  | static const SubGhzBlockConst ws_oregon3_const = { | ||||||
|  |     .te_long = 1100, | ||||||
|  |     .te_short = 500, | ||||||
|  |     .te_delta = 300, | ||||||
|  |     .min_count_bit_for_found = 32, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | #define OREGON3_PREAMBLE_BITS 28 | ||||||
|  | #define OREGON3_PREAMBLE_MASK 0b1111111111111111111111111111 | ||||||
|  | // 24 ones + 0101 (inverted A)
 | ||||||
|  | #define OREGON3_PREAMBLE 0b1111111111111111111111110101 | ||||||
|  | 
 | ||||||
|  | // Fixed part contains:
 | ||||||
|  | // - Sensor type: 16 bits
 | ||||||
|  | // - Channel: 4 bits
 | ||||||
|  | // - ID (changes when batteries are changed): 8 bits
 | ||||||
|  | // - Battery status: 4 bits
 | ||||||
|  | #define OREGON3_FIXED_PART_BITS (16 + 4 + 8 + 4) | ||||||
|  | #define OREGON3_SENSOR_ID(d) (((d) >> 16) & 0xFFFF) | ||||||
|  | #define OREGON3_CHECKSUM_BITS 8 | ||||||
|  | 
 | ||||||
|  | // bit indicating the low battery
 | ||||||
|  | #define OREGON3_FLAG_BAT_LOW 0x4 | ||||||
|  | 
 | ||||||
|  | /// Documentation for Oregon Scientific protocols can be found here:
 | ||||||
|  | /// https://www.osengr.org/Articles/OS-RF-Protocols-IV.pdf
 | ||||||
|  | // Sensors ID
 | ||||||
|  | #define ID_THGR221 0xf824 | ||||||
|  | 
 | ||||||
|  | struct WSProtocolDecoderOregon3 { | ||||||
|  |     SubGhzProtocolDecoderBase base; | ||||||
|  | 
 | ||||||
|  |     SubGhzBlockDecoder decoder; | ||||||
|  |     WSBlockGeneric generic; | ||||||
|  |     ManchesterState manchester_state; | ||||||
|  |     bool prev_bit; | ||||||
|  | 
 | ||||||
|  |     uint8_t var_bits; | ||||||
|  |     uint64_t var_data; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | typedef struct WSProtocolDecoderOregon3 WSProtocolDecoderOregon3; | ||||||
|  | 
 | ||||||
|  | typedef enum { | ||||||
|  |     Oregon3DecoderStepReset = 0, | ||||||
|  |     Oregon3DecoderStepFoundPreamble, | ||||||
|  |     Oregon3DecoderStepVarData, | ||||||
|  | } Oregon3DecoderStep; | ||||||
|  | 
 | ||||||
|  | void* ws_protocol_decoder_oregon3_alloc(SubGhzEnvironment* environment) { | ||||||
|  |     UNUSED(environment); | ||||||
|  |     WSProtocolDecoderOregon3* instance = malloc(sizeof(WSProtocolDecoderOregon3)); | ||||||
|  |     instance->base.protocol = &ws_protocol_oregon3; | ||||||
|  |     instance->generic.protocol_name = instance->base.protocol->name; | ||||||
|  |     instance->generic.humidity = WS_NO_HUMIDITY; | ||||||
|  |     instance->generic.temp = WS_NO_TEMPERATURE; | ||||||
|  |     instance->generic.btn = WS_NO_BTN; | ||||||
|  |     instance->generic.channel = WS_NO_CHANNEL; | ||||||
|  |     instance->generic.battery_low = WS_NO_BATT; | ||||||
|  |     instance->generic.id = WS_NO_ID; | ||||||
|  |     instance->prev_bit = false; | ||||||
|  |     return instance; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void ws_protocol_decoder_oregon3_free(void* context) { | ||||||
|  |     furi_assert(context); | ||||||
|  |     WSProtocolDecoderOregon3* instance = context; | ||||||
|  |     free(instance); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void ws_protocol_decoder_oregon3_reset(void* context) { | ||||||
|  |     furi_assert(context); | ||||||
|  |     WSProtocolDecoderOregon3* instance = context; | ||||||
|  |     instance->decoder.parser_step = Oregon3DecoderStepReset; | ||||||
|  |     instance->decoder.decode_data = 0UL; | ||||||
|  |     instance->decoder.decode_count_bit = 0; | ||||||
|  |     manchester_advance( | ||||||
|  |         instance->manchester_state, ManchesterEventReset, &instance->manchester_state, NULL); | ||||||
|  |     instance->prev_bit = false; | ||||||
|  |     instance->var_data = 0; | ||||||
|  |     instance->var_bits = 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static ManchesterEvent level_and_duration_to_event(bool level, uint32_t duration) { | ||||||
|  |     bool is_long = false; | ||||||
|  | 
 | ||||||
|  |     if(DURATION_DIFF(duration, ws_oregon3_const.te_long) < ws_oregon3_const.te_delta) { | ||||||
|  |         is_long = true; | ||||||
|  |     } else if(DURATION_DIFF(duration, ws_oregon3_const.te_short) < ws_oregon3_const.te_delta) { | ||||||
|  |         is_long = false; | ||||||
|  |     } else { | ||||||
|  |         return ManchesterEventReset; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if(level) | ||||||
|  |         return is_long ? ManchesterEventLongHigh : ManchesterEventShortHigh; | ||||||
|  |     else | ||||||
|  |         return is_long ? ManchesterEventLongLow : ManchesterEventShortLow; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // From sensor id code return amount of bits in variable section
 | ||||||
|  | // https://temofeev.ru/info/articles/o-dekodirovanii-protokola-pogodnykh-datchikov-oregon-scientific
 | ||||||
|  | static uint8_t oregon3_sensor_id_var_bits(uint16_t sensor_id) { | ||||||
|  |     switch(sensor_id) { | ||||||
|  |     case ID_THGR221: | ||||||
|  |     default: | ||||||
|  |         // nibbles: temp + hum + '0'
 | ||||||
|  |         return (4 + 2 + 1) * 4; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void ws_oregon3_decode_const_data(WSBlockGeneric* ws_block) { | ||||||
|  |     ws_block->id = OREGON3_SENSOR_ID(ws_block->data); | ||||||
|  |     ws_block->channel = (ws_block->data >> 12) & 0xF; | ||||||
|  |     ws_block->battery_low = (ws_block->data & OREGON3_FLAG_BAT_LOW) ? 1 : 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static uint16_t ws_oregon3_bcd_decode_short(uint32_t data) { | ||||||
|  |     return (data & 0xF) * 10 + ((data >> 4) & 0xF); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static float ws_oregon3_decode_temp(uint32_t data) { | ||||||
|  |     int32_t temp_val; | ||||||
|  |     temp_val = ws_oregon3_bcd_decode_short(data >> 4); | ||||||
|  |     temp_val *= 10; | ||||||
|  |     temp_val += (data >> 12) & 0xF; | ||||||
|  |     if(data & 0xF) temp_val = -temp_val; | ||||||
|  |     return (float)temp_val / 10.0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void ws_oregon3_decode_var_data(WSBlockGeneric* ws_b, uint16_t sensor_id, uint32_t data) { | ||||||
|  |     switch(sensor_id) { | ||||||
|  |     case ID_THGR221: | ||||||
|  |     default: | ||||||
|  |         ws_b->humidity = ws_oregon3_bcd_decode_short(data >> 4); | ||||||
|  |         ws_b->temp = ws_oregon3_decode_temp(data >> 12); | ||||||
|  |         break; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void ws_protocol_decoder_oregon3_feed(void* context, bool level, uint32_t duration) { | ||||||
|  |     furi_assert(context); | ||||||
|  |     WSProtocolDecoderOregon3* instance = context; | ||||||
|  |     // Oregon v3.0 protocol is inverted
 | ||||||
|  |     ManchesterEvent event = level_and_duration_to_event(!level, duration); | ||||||
|  | 
 | ||||||
|  |     // low-level bit sequence decoding
 | ||||||
|  |     if(event == ManchesterEventReset) { | ||||||
|  |         instance->decoder.parser_step = Oregon3DecoderStepReset; | ||||||
|  |         instance->prev_bit = false; | ||||||
|  |         instance->decoder.decode_data = 0UL; | ||||||
|  |         instance->decoder.decode_count_bit = 0; | ||||||
|  |     } | ||||||
|  |     if(manchester_advance( | ||||||
|  |            instance->manchester_state, event, &instance->manchester_state, &instance->prev_bit)) { | ||||||
|  |         subghz_protocol_blocks_add_bit(&instance->decoder, instance->prev_bit); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     switch(instance->decoder.parser_step) { | ||||||
|  |     case Oregon3DecoderStepReset: | ||||||
|  |         // waiting for fixed oregon3 preamble
 | ||||||
|  |         if(instance->decoder.decode_count_bit >= OREGON3_PREAMBLE_BITS && | ||||||
|  |            ((instance->decoder.decode_data & OREGON3_PREAMBLE_MASK) == OREGON3_PREAMBLE)) { | ||||||
|  |             instance->decoder.parser_step = Oregon3DecoderStepFoundPreamble; | ||||||
|  |             instance->decoder.decode_count_bit = 0; | ||||||
|  |             instance->decoder.decode_data = 0UL; | ||||||
|  |         } | ||||||
|  |         break; | ||||||
|  |     case Oregon3DecoderStepFoundPreamble: | ||||||
|  |         // waiting for fixed oregon3 data
 | ||||||
|  |         if(instance->decoder.decode_count_bit == OREGON3_FIXED_PART_BITS) { | ||||||
|  |             instance->generic.data = instance->decoder.decode_data; | ||||||
|  |             instance->generic.data_count_bit = instance->decoder.decode_count_bit; | ||||||
|  |             instance->decoder.decode_data = 0UL; | ||||||
|  |             instance->decoder.decode_count_bit = 0; | ||||||
|  | 
 | ||||||
|  |             // reverse nibbles in decoded data as oregon v3.0 is LSB first
 | ||||||
|  |             instance->generic.data = (instance->generic.data & 0x55555555) << 1 | | ||||||
|  |                                      (instance->generic.data & 0xAAAAAAAA) >> 1; | ||||||
|  |             instance->generic.data = (instance->generic.data & 0x33333333) << 2 | | ||||||
|  |                                      (instance->generic.data & 0xCCCCCCCC) >> 2; | ||||||
|  | 
 | ||||||
|  |             ws_oregon3_decode_const_data(&instance->generic); | ||||||
|  |             instance->var_bits = | ||||||
|  |                 oregon3_sensor_id_var_bits(OREGON3_SENSOR_ID(instance->generic.data)); | ||||||
|  | 
 | ||||||
|  |             if(!instance->var_bits) { | ||||||
|  |                 // sensor is not supported, stop decoding, but showing the decoded fixed part
 | ||||||
|  |                 instance->decoder.parser_step = Oregon3DecoderStepReset; | ||||||
|  |                 if(instance->base.callback) | ||||||
|  |                     instance->base.callback(&instance->base, instance->base.context); | ||||||
|  |             } else { | ||||||
|  |                 instance->decoder.parser_step = Oregon3DecoderStepVarData; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         break; | ||||||
|  |     case Oregon3DecoderStepVarData: | ||||||
|  |         // waiting for variable (sensor-specific data)
 | ||||||
|  |         if(instance->decoder.decode_count_bit == instance->var_bits + OREGON3_CHECKSUM_BITS) { | ||||||
|  |             instance->var_data = instance->decoder.decode_data & 0xFFFFFFFFFFFFFFFF; | ||||||
|  | 
 | ||||||
|  |             // reverse nibbles in var data
 | ||||||
|  |             instance->var_data = (instance->var_data & 0x5555555555555555) << 1 | | ||||||
|  |                                  (instance->var_data & 0xAAAAAAAAAAAAAAAA) >> 1; | ||||||
|  |             instance->var_data = (instance->var_data & 0x3333333333333333) << 2 | | ||||||
|  |                                  (instance->var_data & 0xCCCCCCCCCCCCCCCC) >> 2; | ||||||
|  | 
 | ||||||
|  |             ws_oregon3_decode_var_data( | ||||||
|  |                 &instance->generic, | ||||||
|  |                 OREGON3_SENSOR_ID(instance->generic.data), | ||||||
|  |                 instance->var_data >> OREGON3_CHECKSUM_BITS); | ||||||
|  | 
 | ||||||
|  |             instance->decoder.parser_step = Oregon3DecoderStepReset; | ||||||
|  |             if(instance->base.callback) | ||||||
|  |                 instance->base.callback(&instance->base, instance->base.context); | ||||||
|  |         } | ||||||
|  |         break; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | uint8_t ws_protocol_decoder_oregon3_get_hash_data(void* context) { | ||||||
|  |     furi_assert(context); | ||||||
|  |     WSProtocolDecoderOregon3* instance = context; | ||||||
|  |     return subghz_protocol_blocks_get_hash_data( | ||||||
|  |         &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | SubGhzProtocolStatus ws_protocol_decoder_oregon3_serialize( | ||||||
|  |     void* context, | ||||||
|  |     FlipperFormat* flipper_format, | ||||||
|  |     SubGhzRadioPreset* preset) { | ||||||
|  |     furi_assert(context); | ||||||
|  |     WSProtocolDecoderOregon3* instance = context; | ||||||
|  |     SubGhzProtocolStatus ret = SubGhzProtocolStatusError; | ||||||
|  |     ret = ws_block_generic_serialize(&instance->generic, flipper_format, preset); | ||||||
|  |     if(ret != SubGhzProtocolStatusOk) return ret; | ||||||
|  |     uint32_t temp = instance->var_bits; | ||||||
|  |     if(!flipper_format_write_uint32(flipper_format, "VarBits", &temp, 1)) { | ||||||
|  |         FURI_LOG_E(TAG, "Error adding VarBits"); | ||||||
|  |         return SubGhzProtocolStatusErrorParserOthers; | ||||||
|  |     } | ||||||
|  |     if(!flipper_format_write_hex( | ||||||
|  |            flipper_format, | ||||||
|  |            "VarData", | ||||||
|  |            (const uint8_t*)&instance->var_data, | ||||||
|  |            sizeof(instance->var_data))) { | ||||||
|  |         FURI_LOG_E(TAG, "Error adding VarData"); | ||||||
|  |         return SubGhzProtocolStatusErrorParserOthers; | ||||||
|  |     } | ||||||
|  |     return ret; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | SubGhzProtocolStatus | ||||||
|  |     ws_protocol_decoder_oregon3_deserialize(void* context, FlipperFormat* flipper_format) { | ||||||
|  |     furi_assert(context); | ||||||
|  |     WSProtocolDecoderOregon3* instance = context; | ||||||
|  |     uint32_t temp_data; | ||||||
|  |     SubGhzProtocolStatus ret = SubGhzProtocolStatusError; | ||||||
|  |     do { | ||||||
|  |         ret = ws_block_generic_deserialize(&instance->generic, flipper_format); | ||||||
|  |         if(ret != SubGhzProtocolStatusOk) { | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  |         if(!flipper_format_read_uint32(flipper_format, "VarBits", &temp_data, 1)) { | ||||||
|  |             FURI_LOG_E(TAG, "Missing VarLen"); | ||||||
|  |             ret = SubGhzProtocolStatusErrorParserOthers; | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  |         instance->var_bits = (uint8_t)temp_data; | ||||||
|  |         if(!flipper_format_read_hex( | ||||||
|  |                flipper_format, | ||||||
|  |                "VarData", | ||||||
|  |                (uint8_t*)&instance->var_data, | ||||||
|  |                sizeof(instance->var_data))) { //-V1051
 | ||||||
|  |             FURI_LOG_E(TAG, "Missing VarData"); | ||||||
|  |             ret = SubGhzProtocolStatusErrorParserOthers; | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  |         if(instance->generic.data_count_bit != ws_oregon3_const.min_count_bit_for_found) { | ||||||
|  |             FURI_LOG_E(TAG, "Wrong number of bits in key: %d", instance->generic.data_count_bit); | ||||||
|  |             ret = SubGhzProtocolStatusErrorValueBitCount; | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  |     } while(false); | ||||||
|  |     return ret; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void oregon3_append_check_sum(uint32_t fix_data, uint64_t var_data, FuriString* output) { | ||||||
|  |     uint8_t sum = fix_data & 0xF; | ||||||
|  |     uint8_t ref_sum = var_data & 0xFF; | ||||||
|  |     var_data >>= 4; | ||||||
|  | 
 | ||||||
|  |     for(uint8_t i = 1; i < 8; i++) { | ||||||
|  |         fix_data >>= 4; | ||||||
|  |         var_data >>= 4; | ||||||
|  |         sum += (fix_data & 0xF) + (var_data & 0xF); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     // swap calculated sum nibbles
 | ||||||
|  |     sum = (((sum >> 4) & 0xF) | (sum << 4)) & 0xFF; | ||||||
|  |     if(sum == ref_sum) | ||||||
|  |         furi_string_cat_printf(output, "Sum ok: 0x%hhX", ref_sum); | ||||||
|  |     else | ||||||
|  |         furi_string_cat_printf(output, "Sum err: 0x%hhX vs 0x%hhX", ref_sum, sum); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void ws_protocol_decoder_oregon3_get_string(void* context, FuriString* output) { | ||||||
|  |     furi_assert(context); | ||||||
|  |     WSProtocolDecoderOregon3* instance = context; | ||||||
|  |     furi_string_cat_printf( | ||||||
|  |         output, | ||||||
|  |         "%s\r\n" | ||||||
|  |         "ID: 0x%04lX, ch: %d, bat: %d, rc: 0x%02lX\r\n", | ||||||
|  |         instance->generic.protocol_name, | ||||||
|  |         instance->generic.id, | ||||||
|  |         instance->generic.channel, | ||||||
|  |         instance->generic.battery_low, | ||||||
|  |         (uint32_t)(instance->generic.data >> 4) & 0xFF); | ||||||
|  | 
 | ||||||
|  |     if(instance->var_bits > 0) { | ||||||
|  |         furi_string_cat_printf( | ||||||
|  |             output, | ||||||
|  |             "Temp:%d.%d C Hum:%d%%", | ||||||
|  |             (int16_t)instance->generic.temp, | ||||||
|  |             abs( | ||||||
|  |                 ((int16_t)(instance->generic.temp * 10) - | ||||||
|  |                  (((int16_t)instance->generic.temp) * 10))), | ||||||
|  |             instance->generic.humidity); | ||||||
|  |         oregon3_append_check_sum((uint32_t)instance->generic.data, instance->var_data, output); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | const SubGhzProtocolDecoder ws_protocol_oregon3_decoder = { | ||||||
|  |     .alloc = ws_protocol_decoder_oregon3_alloc, | ||||||
|  |     .free = ws_protocol_decoder_oregon3_free, | ||||||
|  | 
 | ||||||
|  |     .feed = ws_protocol_decoder_oregon3_feed, | ||||||
|  |     .reset = ws_protocol_decoder_oregon3_reset, | ||||||
|  | 
 | ||||||
|  |     .get_hash_data = ws_protocol_decoder_oregon3_get_hash_data, | ||||||
|  |     .serialize = ws_protocol_decoder_oregon3_serialize, | ||||||
|  |     .deserialize = ws_protocol_decoder_oregon3_deserialize, | ||||||
|  |     .get_string = ws_protocol_decoder_oregon3_get_string, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | const SubGhzProtocol ws_protocol_oregon3 = { | ||||||
|  |     .name = WS_PROTOCOL_OREGON3_NAME, | ||||||
|  |     .type = SubGhzProtocolWeatherStation, | ||||||
|  |     .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable, | ||||||
|  | 
 | ||||||
|  |     .decoder = &ws_protocol_oregon3_decoder, | ||||||
|  | }; | ||||||
							
								
								
									
										6
									
								
								applications/external/weather_station/protocols/oregon3.h
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,6 @@ | |||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | #include <lib/subghz/protocols/base.h> | ||||||
|  | 
 | ||||||
|  | #define WS_PROTOCOL_OREGON3_NAME "Oregon3" | ||||||
|  | extern const SubGhzProtocol ws_protocol_oregon3; | ||||||
| @ -11,6 +11,7 @@ const SubGhzProtocol* weather_station_protocol_registry_items[] = { | |||||||
|     &ws_protocol_lacrosse_tx, |     &ws_protocol_lacrosse_tx, | ||||||
|     &ws_protocol_lacrosse_tx141thbv2, |     &ws_protocol_lacrosse_tx141thbv2, | ||||||
|     &ws_protocol_oregon2, |     &ws_protocol_oregon2, | ||||||
|  |     &ws_protocol_oregon3, | ||||||
|     &ws_protocol_acurite_592txr, |     &ws_protocol_acurite_592txr, | ||||||
|     &ws_protocol_ambient_weather, |     &ws_protocol_ambient_weather, | ||||||
|     &ws_protocol_auriol_th, |     &ws_protocol_auriol_th, | ||||||
|  | |||||||
| @ -11,6 +11,7 @@ | |||||||
| #include "lacrosse_tx.h" | #include "lacrosse_tx.h" | ||||||
| #include "lacrosse_tx141thbv2.h" | #include "lacrosse_tx141thbv2.h" | ||||||
| #include "oregon2.h" | #include "oregon2.h" | ||||||
|  | #include "oregon3.h" | ||||||
| #include "acurite_592txr.h" | #include "acurite_592txr.h" | ||||||
| #include "ambient_weather.h" | #include "ambient_weather.h" | ||||||
| #include "auriol_hg0601a.h" | #include "auriol_hg0601a.h" | ||||||
|  | |||||||
| @ -426,7 +426,7 @@ static int32_t bad_usb_worker(void* context) { | |||||||
|             if(flags & WorkerEvtEnd) { |             if(flags & WorkerEvtEnd) { | ||||||
|                 break; |                 break; | ||||||
|             } else if(flags & WorkerEvtStartStop) { // Start executing script
 |             } else if(flags & WorkerEvtStartStop) { // Start executing script
 | ||||||
|                 DOLPHIN_DEED(DolphinDeedBadUsbPlayScript); |                 dolphin_deed(DolphinDeedBadUsbPlayScript); | ||||||
|                 delay_val = 0; |                 delay_val = 0; | ||||||
|                 bad_usb->buf_len = 0; |                 bad_usb->buf_len = 0; | ||||||
|                 bad_usb->st.line_cur = 0; |                 bad_usb->st.line_cur = 0; | ||||||
| @ -449,7 +449,7 @@ static int32_t bad_usb_worker(void* context) { | |||||||
|             if(flags & WorkerEvtEnd) { |             if(flags & WorkerEvtEnd) { | ||||||
|                 break; |                 break; | ||||||
|             } else if(flags & WorkerEvtConnect) { // Start executing script
 |             } else if(flags & WorkerEvtConnect) { // Start executing script
 | ||||||
|                 DOLPHIN_DEED(DolphinDeedBadUsbPlayScript); |                 dolphin_deed(DolphinDeedBadUsbPlayScript); | ||||||
|                 delay_val = 0; |                 delay_val = 0; | ||||||
|                 bad_usb->buf_len = 0; |                 bad_usb->buf_len = 0; | ||||||
|                 bad_usb->st.line_cur = 0; |                 bad_usb->st.line_cur = 0; | ||||||
|  | |||||||
| @ -89,7 +89,7 @@ bool gpio_scene_start_on_event(void* context, SceneManagerEvent event) { | |||||||
|         } else if(event.event == GpioStartEventUsbUart) { |         } else if(event.event == GpioStartEventUsbUart) { | ||||||
|             scene_manager_set_scene_state(app->scene_manager, GpioSceneStart, GpioItemUsbUart); |             scene_manager_set_scene_state(app->scene_manager, GpioSceneStart, GpioItemUsbUart); | ||||||
|             if(!furi_hal_usb_is_locked()) { |             if(!furi_hal_usb_is_locked()) { | ||||||
|                 DOLPHIN_DEED(DolphinDeedGpioUartBridge); |                 dolphin_deed(DolphinDeedGpioUartBridge); | ||||||
|                 scene_manager_next_scene(app->scene_manager, GpioSceneUsbUart); |                 scene_manager_next_scene(app->scene_manager, GpioSceneUsbUart); | ||||||
|             } else { |             } else { | ||||||
|                 scene_manager_next_scene(app->scene_manager, GpioSceneUsbUartCloseRpc); |                 scene_manager_next_scene(app->scene_manager, GpioSceneUsbUartCloseRpc); | ||||||
|  | |||||||
| @ -282,14 +282,14 @@ int32_t ibutton_app(void* arg) { | |||||||
|         view_dispatcher_attach_to_gui( |         view_dispatcher_attach_to_gui( | ||||||
|             ibutton->view_dispatcher, ibutton->gui, ViewDispatcherTypeDesktop); |             ibutton->view_dispatcher, ibutton->gui, ViewDispatcherTypeDesktop); | ||||||
|         scene_manager_next_scene(ibutton->scene_manager, iButtonSceneRpc); |         scene_manager_next_scene(ibutton->scene_manager, iButtonSceneRpc); | ||||||
|         DOLPHIN_DEED(DolphinDeedIbuttonEmulate); |         dolphin_deed(DolphinDeedIbuttonEmulate); | ||||||
| 
 | 
 | ||||||
|     } else { |     } else { | ||||||
|         view_dispatcher_attach_to_gui( |         view_dispatcher_attach_to_gui( | ||||||
|             ibutton->view_dispatcher, ibutton->gui, ViewDispatcherTypeFullscreen); |             ibutton->view_dispatcher, ibutton->gui, ViewDispatcherTypeFullscreen); | ||||||
|         if(key_loaded) { //-V547
 |         if(key_loaded) { //-V547
 | ||||||
|             scene_manager_next_scene(ibutton->scene_manager, iButtonSceneEmulate); |             scene_manager_next_scene(ibutton->scene_manager, iButtonSceneEmulate); | ||||||
|             DOLPHIN_DEED(DolphinDeedIbuttonEmulate); |             dolphin_deed(DolphinDeedIbuttonEmulate); | ||||||
|         } else { |         } else { | ||||||
|             scene_manager_next_scene(ibutton->scene_manager, iButtonSceneStart); |             scene_manager_next_scene(ibutton->scene_manager, iButtonSceneStart); | ||||||
|         } |         } | ||||||
|  | |||||||
| @ -38,7 +38,7 @@ bool ibutton_scene_read_on_event(void* context, SceneManagerEvent event) { | |||||||
|                 ibutton_notification_message(ibutton, iButtonNotificationMessageSuccess); |                 ibutton_notification_message(ibutton, iButtonNotificationMessageSuccess); | ||||||
|                 scene_manager_next_scene(scene_manager, iButtonSceneReadSuccess); |                 scene_manager_next_scene(scene_manager, iButtonSceneReadSuccess); | ||||||
| 
 | 
 | ||||||
|                 DOLPHIN_DEED(DolphinDeedIbuttonReadSuccess); |                 dolphin_deed(DolphinDeedIbuttonReadSuccess); | ||||||
| 
 | 
 | ||||||
|             } else { |             } else { | ||||||
|                 scene_manager_next_scene(scene_manager, iButtonSceneReadError); |                 scene_manager_next_scene(scene_manager, iButtonSceneReadError); | ||||||
|  | |||||||
| @ -75,7 +75,7 @@ bool ibutton_scene_read_key_menu_on_event(void* context, SceneManagerEvent event | |||||||
|             scene_manager_next_scene(scene_manager, iButtonSceneSaveName); |             scene_manager_next_scene(scene_manager, iButtonSceneSaveName); | ||||||
|         } else if(event.event == SubmenuIndexEmulate) { |         } else if(event.event == SubmenuIndexEmulate) { | ||||||
|             scene_manager_next_scene(scene_manager, iButtonSceneEmulate); |             scene_manager_next_scene(scene_manager, iButtonSceneEmulate); | ||||||
|             DOLPHIN_DEED(DolphinDeedIbuttonEmulate); |             dolphin_deed(DolphinDeedIbuttonEmulate); | ||||||
|         } else if(event.event == SubmenuIndexViewData) { |         } else if(event.event == SubmenuIndexViewData) { | ||||||
|             scene_manager_next_scene(scene_manager, iButtonSceneViewData); |             scene_manager_next_scene(scene_manager, iButtonSceneViewData); | ||||||
|         } else if(event.event == SubmenuIndexWriteBlank) { |         } else if(event.event == SubmenuIndexWriteBlank) { | ||||||
|  | |||||||
| @ -58,9 +58,9 @@ bool ibutton_scene_save_name_on_event(void* context, SceneManagerEvent event) { | |||||||
|                     // Nothing, do not count editing as saving
 |                     // Nothing, do not count editing as saving
 | ||||||
|                 } else if(scene_manager_has_previous_scene( |                 } else if(scene_manager_has_previous_scene( | ||||||
|                               ibutton->scene_manager, iButtonSceneAddType)) { |                               ibutton->scene_manager, iButtonSceneAddType)) { | ||||||
|                     DOLPHIN_DEED(DolphinDeedIbuttonAdd); |                     dolphin_deed(DolphinDeedIbuttonAdd); | ||||||
|                 } else { |                 } else { | ||||||
|                     DOLPHIN_DEED(DolphinDeedIbuttonSave); |                     dolphin_deed(DolphinDeedIbuttonSave); | ||||||
|                 } |                 } | ||||||
| 
 | 
 | ||||||
|             } else { |             } else { | ||||||
|  | |||||||
| @ -48,7 +48,7 @@ bool ibutton_scene_saved_key_menu_on_event(void* context, SceneManagerEvent even | |||||||
|         consumed = true; |         consumed = true; | ||||||
|         if(event.event == SubmenuIndexEmulate) { |         if(event.event == SubmenuIndexEmulate) { | ||||||
|             scene_manager_next_scene(scene_manager, iButtonSceneEmulate); |             scene_manager_next_scene(scene_manager, iButtonSceneEmulate); | ||||||
|             DOLPHIN_DEED(DolphinDeedIbuttonEmulate); |             dolphin_deed(DolphinDeedIbuttonEmulate); | ||||||
|         } else if(event.event == SubmenuIndexWriteBlank) { |         } else if(event.event == SubmenuIndexWriteBlank) { | ||||||
|             ibutton->write_mode = iButtonWriteModeBlank; |             ibutton->write_mode = iButtonWriteModeBlank; | ||||||
|             scene_manager_next_scene(scene_manager, iButtonSceneWrite); |             scene_manager_next_scene(scene_manager, iButtonSceneWrite); | ||||||
|  | |||||||
| @ -33,7 +33,7 @@ bool ibutton_scene_start_on_event(void* context, SceneManagerEvent event) { | |||||||
|         consumed = true; |         consumed = true; | ||||||
|         if(event.event == SubmenuIndexRead) { |         if(event.event == SubmenuIndexRead) { | ||||||
|             scene_manager_next_scene(ibutton->scene_manager, iButtonSceneRead); |             scene_manager_next_scene(ibutton->scene_manager, iButtonSceneRead); | ||||||
|             DOLPHIN_DEED(DolphinDeedIbuttonRead); |             dolphin_deed(DolphinDeedIbuttonRead); | ||||||
|         } else if(event.event == SubmenuIndexSaved) { |         } else if(event.event == SubmenuIndexSaved) { | ||||||
|             scene_manager_next_scene(ibutton->scene_manager, iButtonSceneSelectKey); |             scene_manager_next_scene(ibutton->scene_manager, iButtonSceneSelectKey); | ||||||
|         } else if(event.event == SubmenuIndexAdd) { |         } else if(event.event == SubmenuIndexAdd) { | ||||||
|  | |||||||
| @ -319,7 +319,7 @@ void infrared_tx_start_signal(Infrared* infrared, InfraredSignal* signal) { | |||||||
|         infrared_worker_set_decoded_signal(infrared->worker, message); |         infrared_worker_set_decoded_signal(infrared->worker, message); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     DOLPHIN_DEED(DolphinDeedIrSend); |     dolphin_deed(DolphinDeedIrSend); | ||||||
|     infrared_play_notification_message(infrared, InfraredNotificationMessageBlinkStartSend); |     infrared_play_notification_message(infrared, InfraredNotificationMessageBlinkStartSend); | ||||||
| 
 | 
 | ||||||
|     infrared_worker_tx_set_get_signal_callback( |     infrared_worker_tx_set_get_signal_callback( | ||||||
|  | |||||||
| @ -70,7 +70,7 @@ bool infrared_scene_universal_common_on_event(void* context, SceneManagerEvent e | |||||||
|                 uint32_t record_count; |                 uint32_t record_count; | ||||||
|                 if(infrared_brute_force_start( |                 if(infrared_brute_force_start( | ||||||
|                        brute_force, infrared_custom_event_get_value(event.event), &record_count)) { |                        brute_force, infrared_custom_event_get_value(event.event), &record_count)) { | ||||||
|                     DOLPHIN_DEED(DolphinDeedIrSend); |                     dolphin_deed(DolphinDeedIrSend); | ||||||
|                     infrared_scene_universal_common_show_popup(infrared, record_count); |                     infrared_scene_universal_common_show_popup(infrared, record_count); | ||||||
|                 } else { |                 } else { | ||||||
|                     scene_manager_next_scene(scene_manager, InfraredSceneErrorDatabases); |                     scene_manager_next_scene(scene_manager, InfraredSceneErrorDatabases); | ||||||
|  | |||||||
| @ -28,7 +28,7 @@ bool infrared_scene_learn_on_event(void* context, SceneManagerEvent event) { | |||||||
|         if(event.event == InfraredCustomEventTypeSignalReceived) { |         if(event.event == InfraredCustomEventTypeSignalReceived) { | ||||||
|             infrared_play_notification_message(infrared, InfraredNotificationMessageSuccess); |             infrared_play_notification_message(infrared, InfraredNotificationMessageSuccess); | ||||||
|             scene_manager_next_scene(infrared->scene_manager, InfraredSceneLearnSuccess); |             scene_manager_next_scene(infrared->scene_manager, InfraredSceneLearnSuccess); | ||||||
|             DOLPHIN_DEED(DolphinDeedIrLearnSuccess); |             dolphin_deed(DolphinDeedIrLearnSuccess); | ||||||
|             consumed = true; |             consumed = true; | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  | |||||||
| @ -50,7 +50,7 @@ bool infrared_scene_learn_enter_name_on_event(void* context, SceneManagerEvent e | |||||||
| 
 | 
 | ||||||
|             if(success) { |             if(success) { | ||||||
|                 scene_manager_next_scene(scene_manager, InfraredSceneLearnDone); |                 scene_manager_next_scene(scene_manager, InfraredSceneLearnDone); | ||||||
|                 DOLPHIN_DEED(DolphinDeedIrSave); |                 dolphin_deed(DolphinDeedIrSave); | ||||||
|             } else { |             } else { | ||||||
|                 dialog_message_show_storage_error(infrared->dialogs, "Failed to save file"); |                 dialog_message_show_storage_error(infrared->dialogs, "Failed to save file"); | ||||||
|                 const uint32_t possible_scenes[] = {InfraredSceneRemoteList, InfraredSceneStart}; |                 const uint32_t possible_scenes[] = {InfraredSceneRemoteList, InfraredSceneStart}; | ||||||
|  | |||||||
| @ -183,14 +183,14 @@ int32_t lfrfid_app(void* p) { | |||||||
|             view_dispatcher_attach_to_gui( |             view_dispatcher_attach_to_gui( | ||||||
|                 app->view_dispatcher, app->gui, ViewDispatcherTypeDesktop); |                 app->view_dispatcher, app->gui, ViewDispatcherTypeDesktop); | ||||||
|             scene_manager_next_scene(app->scene_manager, LfRfidSceneRpc); |             scene_manager_next_scene(app->scene_manager, LfRfidSceneRpc); | ||||||
|             DOLPHIN_DEED(DolphinDeedRfidEmulate); |             dolphin_deed(DolphinDeedRfidEmulate); | ||||||
|         } else { |         } else { | ||||||
|             furi_string_set(app->file_path, args); |             furi_string_set(app->file_path, args); | ||||||
|             lfrfid_load_key_data(app, app->file_path, true); |             lfrfid_load_key_data(app, app->file_path, true); | ||||||
|             view_dispatcher_attach_to_gui( |             view_dispatcher_attach_to_gui( | ||||||
|                 app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen); |                 app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen); | ||||||
|             scene_manager_next_scene(app->scene_manager, LfRfidSceneEmulate); |             scene_manager_next_scene(app->scene_manager, LfRfidSceneEmulate); | ||||||
|             DOLPHIN_DEED(DolphinDeedRfidEmulate); |             dolphin_deed(DolphinDeedRfidEmulate); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|     } else { |     } else { | ||||||
|  | |||||||
| @ -58,12 +58,12 @@ bool lfrfid_scene_extra_actions_on_event(void* context, SceneManagerEvent event) | |||||||
|         if(event.event == SubmenuIndexASK) { |         if(event.event == SubmenuIndexASK) { | ||||||
|             app->read_type = LFRFIDWorkerReadTypeASKOnly; |             app->read_type = LFRFIDWorkerReadTypeASKOnly; | ||||||
|             scene_manager_next_scene(app->scene_manager, LfRfidSceneRead); |             scene_manager_next_scene(app->scene_manager, LfRfidSceneRead); | ||||||
|             DOLPHIN_DEED(DolphinDeedRfidRead); |             dolphin_deed(DolphinDeedRfidRead); | ||||||
|             consumed = true; |             consumed = true; | ||||||
|         } else if(event.event == SubmenuIndexPSK) { |         } else if(event.event == SubmenuIndexPSK) { | ||||||
|             app->read_type = LFRFIDWorkerReadTypePSKOnly; |             app->read_type = LFRFIDWorkerReadTypePSKOnly; | ||||||
|             scene_manager_next_scene(app->scene_manager, LfRfidSceneRead); |             scene_manager_next_scene(app->scene_manager, LfRfidSceneRead); | ||||||
|             DOLPHIN_DEED(DolphinDeedRfidRead); |             dolphin_deed(DolphinDeedRfidRead); | ||||||
|             consumed = true; |             consumed = true; | ||||||
|         } else if(event.event == SubmenuIndexRAW) { |         } else if(event.event == SubmenuIndexRAW) { | ||||||
|             scene_manager_next_scene(app->scene_manager, LfRfidSceneRawName); |             scene_manager_next_scene(app->scene_manager, LfRfidSceneRawName); | ||||||
|  | |||||||
| @ -81,7 +81,7 @@ bool lfrfid_scene_read_on_event(void* context, SceneManagerEvent event) { | |||||||
|             notification_message(app->notifications, &sequence_success); |             notification_message(app->notifications, &sequence_success); | ||||||
|             furi_string_reset(app->file_name); |             furi_string_reset(app->file_name); | ||||||
|             scene_manager_next_scene(app->scene_manager, LfRfidSceneReadSuccess); |             scene_manager_next_scene(app->scene_manager, LfRfidSceneReadSuccess); | ||||||
|             DOLPHIN_DEED(DolphinDeedRfidReadSuccess); |             dolphin_deed(DolphinDeedRfidReadSuccess); | ||||||
|             consumed = true; |             consumed = true; | ||||||
|         } else if(event.event == LfRfidEventReadStartPSK) { |         } else if(event.event == LfRfidEventReadStartPSK) { | ||||||
|             if(app->read_type == LFRFIDWorkerReadTypeAuto) { |             if(app->read_type == LFRFIDWorkerReadTypeAuto) { | ||||||
|  | |||||||
| @ -44,7 +44,7 @@ bool lfrfid_scene_read_key_menu_on_event(void* context, SceneManagerEvent event) | |||||||
|             consumed = true; |             consumed = true; | ||||||
|         } else if(event.event == SubmenuIndexEmulate) { |         } else if(event.event == SubmenuIndexEmulate) { | ||||||
|             scene_manager_next_scene(app->scene_manager, LfRfidSceneEmulate); |             scene_manager_next_scene(app->scene_manager, LfRfidSceneEmulate); | ||||||
|             DOLPHIN_DEED(DolphinDeedRfidEmulate); |             dolphin_deed(DolphinDeedRfidEmulate); | ||||||
|             consumed = true; |             consumed = true; | ||||||
|         } |         } | ||||||
|         scene_manager_set_scene_state(app->scene_manager, LfRfidSceneReadKeyMenu, event.event); |         scene_manager_set_scene_state(app->scene_manager, LfRfidSceneReadKeyMenu, event.event); | ||||||
|  | |||||||
| @ -59,9 +59,9 @@ bool lfrfid_scene_save_name_on_event(void* context, SceneManagerEvent event) { | |||||||
|                 if(scene_manager_has_previous_scene(scene_manager, LfRfidSceneSavedKeyMenu)) { |                 if(scene_manager_has_previous_scene(scene_manager, LfRfidSceneSavedKeyMenu)) { | ||||||
|                     // Nothing, do not count editing as saving
 |                     // Nothing, do not count editing as saving
 | ||||||
|                 } else if(scene_manager_has_previous_scene(scene_manager, LfRfidSceneSaveType)) { |                 } else if(scene_manager_has_previous_scene(scene_manager, LfRfidSceneSaveType)) { | ||||||
|                     DOLPHIN_DEED(DolphinDeedRfidAdd); |                     dolphin_deed(DolphinDeedRfidAdd); | ||||||
|                 } else { |                 } else { | ||||||
|                     DOLPHIN_DEED(DolphinDeedRfidSave); |                     dolphin_deed(DolphinDeedRfidSave); | ||||||
|                 } |                 } | ||||||
|             } else { |             } else { | ||||||
|                 scene_manager_search_and_switch_to_previous_scene( |                 scene_manager_search_and_switch_to_previous_scene( | ||||||
|  | |||||||
| @ -43,7 +43,7 @@ bool lfrfid_scene_saved_key_menu_on_event(void* context, SceneManagerEvent event | |||||||
|     if(event.type == SceneManagerEventTypeCustom) { |     if(event.type == SceneManagerEventTypeCustom) { | ||||||
|         if(event.event == SubmenuIndexEmulate) { |         if(event.event == SubmenuIndexEmulate) { | ||||||
|             scene_manager_next_scene(app->scene_manager, LfRfidSceneEmulate); |             scene_manager_next_scene(app->scene_manager, LfRfidSceneEmulate); | ||||||
|             DOLPHIN_DEED(DolphinDeedRfidEmulate); |             dolphin_deed(DolphinDeedRfidEmulate); | ||||||
|             consumed = true; |             consumed = true; | ||||||
|         } else if(event.event == SubmenuIndexWrite) { |         } else if(event.event == SubmenuIndexWrite) { | ||||||
|             scene_manager_next_scene(app->scene_manager, LfRfidSceneWrite); |             scene_manager_next_scene(app->scene_manager, LfRfidSceneWrite); | ||||||
|  | |||||||
| @ -49,7 +49,7 @@ bool lfrfid_scene_start_on_event(void* context, SceneManagerEvent event) { | |||||||
|         if(event.event == SubmenuIndexRead) { |         if(event.event == SubmenuIndexRead) { | ||||||
|             scene_manager_set_scene_state(app->scene_manager, LfRfidSceneStart, SubmenuIndexRead); |             scene_manager_set_scene_state(app->scene_manager, LfRfidSceneStart, SubmenuIndexRead); | ||||||
|             scene_manager_next_scene(app->scene_manager, LfRfidSceneRead); |             scene_manager_next_scene(app->scene_manager, LfRfidSceneRead); | ||||||
|             DOLPHIN_DEED(DolphinDeedRfidRead); |             dolphin_deed(DolphinDeedRfidRead); | ||||||
|             consumed = true; |             consumed = true; | ||||||
|         } else if(event.event == SubmenuIndexSaved) { |         } else if(event.event == SubmenuIndexSaved) { | ||||||
|             // Like in the other apps, explicitly save the scene state
 |             // Like in the other apps, explicitly save the scene state
 | ||||||
|  | |||||||
| @ -12,4 +12,6 @@ enum NfcCustomEvent { | |||||||
|     NfcCustomEventDictAttackSkip, |     NfcCustomEventDictAttackSkip, | ||||||
|     NfcCustomEventRpcLoad, |     NfcCustomEventRpcLoad, | ||||||
|     NfcCustomEventRpcSessionClose, |     NfcCustomEventRpcSessionClose, | ||||||
|  |     NfcCustomEventUpdateLog, | ||||||
|  |     NfcCustomEventSaveShadow, | ||||||
| }; | }; | ||||||
|  | |||||||
| @ -286,15 +286,18 @@ int32_t nfc_app(void* p) { | |||||||
|             if(nfc_device_load(nfc->dev, p, true)) { |             if(nfc_device_load(nfc->dev, p, true)) { | ||||||
|                 if(nfc->dev->format == NfcDeviceSaveFormatMifareUl) { |                 if(nfc->dev->format == NfcDeviceSaveFormatMifareUl) { | ||||||
|                     scene_manager_next_scene(nfc->scene_manager, NfcSceneMfUltralightEmulate); |                     scene_manager_next_scene(nfc->scene_manager, NfcSceneMfUltralightEmulate); | ||||||
|                     DOLPHIN_DEED(DolphinDeedNfcEmulate); |                     dolphin_deed(DolphinDeedNfcEmulate); | ||||||
|                 } else if(nfc->dev->format == NfcDeviceSaveFormatMifareClassic) { |                 } else if(nfc->dev->format == NfcDeviceSaveFormatMifareClassic) { | ||||||
|                     scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicEmulate); |                     scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicEmulate); | ||||||
|                     DOLPHIN_DEED(DolphinDeedNfcEmulate); |                     dolphin_deed(DolphinDeedNfcEmulate); | ||||||
|  |                 } else if(nfc->dev->format == NfcDeviceSaveFormatNfcV) { | ||||||
|  |                     scene_manager_next_scene(nfc->scene_manager, NfcSceneNfcVEmulate); | ||||||
|  |                     dolphin_deed(DolphinDeedNfcEmulate); | ||||||
|                 } else if(nfc->dev->format == NfcDeviceSaveFormatBankCard) { |                 } else if(nfc->dev->format == NfcDeviceSaveFormatBankCard) { | ||||||
|                     scene_manager_next_scene(nfc->scene_manager, NfcSceneDeviceInfo); |                     scene_manager_next_scene(nfc->scene_manager, NfcSceneDeviceInfo); | ||||||
|                 } else { |                 } else { | ||||||
|                     scene_manager_next_scene(nfc->scene_manager, NfcSceneEmulateUid); |                     scene_manager_next_scene(nfc->scene_manager, NfcSceneEmulateUid); | ||||||
|                     DOLPHIN_DEED(DolphinDeedNfcEmulate); |                     dolphin_deed(DolphinDeedNfcEmulate); | ||||||
|                 } |                 } | ||||||
|             } else { |             } else { | ||||||
|                 // Exit app
 |                 // Exit app
 | ||||||
|  | |||||||
| @ -14,6 +14,13 @@ ADD_SCENE(nfc, file_select, FileSelect) | |||||||
| ADD_SCENE(nfc, emulate_uid, EmulateUid) | ADD_SCENE(nfc, emulate_uid, EmulateUid) | ||||||
| ADD_SCENE(nfc, nfca_read_success, NfcaReadSuccess) | ADD_SCENE(nfc, nfca_read_success, NfcaReadSuccess) | ||||||
| ADD_SCENE(nfc, nfca_menu, NfcaMenu) | ADD_SCENE(nfc, nfca_menu, NfcaMenu) | ||||||
|  | ADD_SCENE(nfc, nfcv_menu, NfcVMenu) | ||||||
|  | ADD_SCENE(nfc, nfcv_unlock_menu, NfcVUnlockMenu) | ||||||
|  | ADD_SCENE(nfc, nfcv_key_input, NfcVKeyInput) | ||||||
|  | ADD_SCENE(nfc, nfcv_unlock, NfcVUnlock) | ||||||
|  | ADD_SCENE(nfc, nfcv_emulate, NfcVEmulate) | ||||||
|  | ADD_SCENE(nfc, nfcv_sniff, NfcVSniff) | ||||||
|  | ADD_SCENE(nfc, nfcv_read_success, NfcVReadSuccess) | ||||||
| ADD_SCENE(nfc, mf_ultralight_read_success, MfUltralightReadSuccess) | ADD_SCENE(nfc, mf_ultralight_read_success, MfUltralightReadSuccess) | ||||||
| ADD_SCENE(nfc, mf_ultralight_data, MfUltralightData) | ADD_SCENE(nfc, mf_ultralight_data, MfUltralightData) | ||||||
| ADD_SCENE(nfc, mf_ultralight_menu, MfUltralightMenu) | ADD_SCENE(nfc, mf_ultralight_menu, MfUltralightMenu) | ||||||
|  | |||||||
| @ -31,6 +31,8 @@ void nfc_scene_delete_on_enter(void* context) { | |||||||
|         nfc->widget, 64, 24, AlignCenter, AlignTop, FontSecondary, furi_string_get_cstr(temp_str)); |         nfc->widget, 64, 24, AlignCenter, AlignTop, FontSecondary, furi_string_get_cstr(temp_str)); | ||||||
| 
 | 
 | ||||||
|     NfcProtocol protocol = nfc->dev->dev_data.protocol; |     NfcProtocol protocol = nfc->dev->dev_data.protocol; | ||||||
|  |     const char* nfc_type = "NFC-A"; | ||||||
|  | 
 | ||||||
|     if(protocol == NfcDeviceProtocolEMV) { |     if(protocol == NfcDeviceProtocolEMV) { | ||||||
|         furi_string_set(temp_str, "EMV bank card"); |         furi_string_set(temp_str, "EMV bank card"); | ||||||
|     } else if(protocol == NfcDeviceProtocolMifareUl) { |     } else if(protocol == NfcDeviceProtocolMifareUl) { | ||||||
| @ -39,12 +41,15 @@ void nfc_scene_delete_on_enter(void* context) { | |||||||
|         furi_string_set(temp_str, nfc_mf_classic_type(nfc->dev->dev_data.mf_classic_data.type)); |         furi_string_set(temp_str, nfc_mf_classic_type(nfc->dev->dev_data.mf_classic_data.type)); | ||||||
|     } else if(protocol == NfcDeviceProtocolMifareDesfire) { |     } else if(protocol == NfcDeviceProtocolMifareDesfire) { | ||||||
|         furi_string_set(temp_str, "MIFARE DESFire"); |         furi_string_set(temp_str, "MIFARE DESFire"); | ||||||
|  |     } else if(protocol == NfcDeviceProtocolNfcV) { | ||||||
|  |         furi_string_set(temp_str, "ISO15693 tag"); | ||||||
|  |         nfc_type = "NFC-V"; | ||||||
|     } else { |     } else { | ||||||
|         furi_string_set(temp_str, "Unknown ISO tag"); |         furi_string_set(temp_str, "Unknown ISO tag"); | ||||||
|     } |     } | ||||||
|     widget_add_string_element( |     widget_add_string_element( | ||||||
|         nfc->widget, 64, 34, AlignCenter, AlignTop, FontSecondary, furi_string_get_cstr(temp_str)); |         nfc->widget, 64, 34, AlignCenter, AlignTop, FontSecondary, furi_string_get_cstr(temp_str)); | ||||||
|     widget_add_string_element(nfc->widget, 64, 44, AlignCenter, AlignTop, FontSecondary, "NFC-A"); |     widget_add_string_element(nfc->widget, 64, 44, AlignCenter, AlignTop, FontSecondary, nfc_type); | ||||||
|     furi_string_free(temp_str); |     furi_string_free(temp_str); | ||||||
| 
 | 
 | ||||||
|     view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget); |     view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget); | ||||||
|  | |||||||
| @ -4,6 +4,8 @@ enum SubmenuIndex { | |||||||
|     SubmenuIndexReadCardType, |     SubmenuIndexReadCardType, | ||||||
|     SubmenuIndexMfClassicKeys, |     SubmenuIndexMfClassicKeys, | ||||||
|     SubmenuIndexMfUltralightUnlock, |     SubmenuIndexMfUltralightUnlock, | ||||||
|  |     SubmenuIndexNfcVUnlock, | ||||||
|  |     SubmenuIndexNfcVSniff, | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| void nfc_scene_extra_actions_submenu_callback(void* context, uint32_t index) { | void nfc_scene_extra_actions_submenu_callback(void* context, uint32_t index) { | ||||||
| @ -34,6 +36,18 @@ void nfc_scene_extra_actions_on_enter(void* context) { | |||||||
|         SubmenuIndexMfUltralightUnlock, |         SubmenuIndexMfUltralightUnlock, | ||||||
|         nfc_scene_extra_actions_submenu_callback, |         nfc_scene_extra_actions_submenu_callback, | ||||||
|         nfc); |         nfc); | ||||||
|  |     submenu_add_item( | ||||||
|  |         submenu, | ||||||
|  |         "Unlock SLIX-L", | ||||||
|  |         SubmenuIndexNfcVUnlock, | ||||||
|  |         nfc_scene_extra_actions_submenu_callback, | ||||||
|  |         nfc); | ||||||
|  |     submenu_add_item( | ||||||
|  |         submenu, | ||||||
|  |         "Listen NfcV Reader", | ||||||
|  |         SubmenuIndexNfcVSniff, | ||||||
|  |         nfc_scene_extra_actions_submenu_callback, | ||||||
|  |         nfc); | ||||||
|     submenu_set_selected_item( |     submenu_set_selected_item( | ||||||
|         submenu, scene_manager_get_scene_state(nfc->scene_manager, NfcSceneExtraActions)); |         submenu, scene_manager_get_scene_state(nfc->scene_manager, NfcSceneExtraActions)); | ||||||
|     view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewMenu); |     view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewMenu); | ||||||
| @ -58,6 +72,12 @@ bool nfc_scene_extra_actions_on_event(void* context, SceneManagerEvent event) { | |||||||
|             scene_manager_set_scene_state(nfc->scene_manager, NfcSceneReadCardType, 0); |             scene_manager_set_scene_state(nfc->scene_manager, NfcSceneReadCardType, 0); | ||||||
|             scene_manager_next_scene(nfc->scene_manager, NfcSceneReadCardType); |             scene_manager_next_scene(nfc->scene_manager, NfcSceneReadCardType); | ||||||
|             consumed = true; |             consumed = true; | ||||||
|  |         } else if(event.event == SubmenuIndexNfcVUnlock) { | ||||||
|  |             scene_manager_next_scene(nfc->scene_manager, NfcSceneNfcVUnlockMenu); | ||||||
|  |             consumed = true; | ||||||
|  |         } else if(event.event == SubmenuIndexNfcVSniff) { | ||||||
|  |             scene_manager_next_scene(nfc->scene_manager, NfcSceneNfcVSniff); | ||||||
|  |             consumed = true; | ||||||
|         } |         } | ||||||
|         scene_manager_set_scene_state(nfc->scene_manager, NfcSceneExtraActions, event.event); |         scene_manager_set_scene_state(nfc->scene_manager, NfcSceneExtraActions, event.event); | ||||||
|     } |     } | ||||||
|  | |||||||
| @ -111,7 +111,7 @@ bool nfc_scene_mf_classic_dict_attack_on_event(void* context, SceneManagerEvent | |||||||
|             } else { |             } else { | ||||||
|                 notification_message(nfc->notifications, &sequence_success); |                 notification_message(nfc->notifications, &sequence_success); | ||||||
|                 scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicReadSuccess); |                 scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicReadSuccess); | ||||||
|                 DOLPHIN_DEED(DolphinDeedNfcReadSuccess); |                 dolphin_deed(DolphinDeedNfcReadSuccess); | ||||||
|                 consumed = true; |                 consumed = true; | ||||||
|             } |             } | ||||||
|         } else if(event.event == NfcWorkerEventAborted) { |         } else if(event.event == NfcWorkerEventAborted) { | ||||||
| @ -123,7 +123,7 @@ bool nfc_scene_mf_classic_dict_attack_on_event(void* context, SceneManagerEvent | |||||||
|                 notification_message(nfc->notifications, &sequence_success); |                 notification_message(nfc->notifications, &sequence_success); | ||||||
|                 scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicReadSuccess); |                 scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicReadSuccess); | ||||||
|                 // Counting failed attempts too
 |                 // Counting failed attempts too
 | ||||||
|                 DOLPHIN_DEED(DolphinDeedNfcReadSuccess); |                 dolphin_deed(DolphinDeedNfcReadSuccess); | ||||||
|                 consumed = true; |                 consumed = true; | ||||||
|             } |             } | ||||||
|         } else if(event.event == NfcWorkerEventCardDetected) { |         } else if(event.event == NfcWorkerEventCardDetected) { | ||||||
|  | |||||||
| @ -37,7 +37,7 @@ bool nfc_scene_mf_classic_keys_add_on_event(void* context, SceneManagerEvent eve | |||||||
|                         nfc->scene_manager, NfcSceneMfClassicKeysWarnDuplicate); |                         nfc->scene_manager, NfcSceneMfClassicKeysWarnDuplicate); | ||||||
|                 } else if(mf_classic_dict_add_key(dict, nfc->byte_input_store)) { |                 } else if(mf_classic_dict_add_key(dict, nfc->byte_input_store)) { | ||||||
|                     scene_manager_next_scene(nfc->scene_manager, NfcSceneSaveSuccess); |                     scene_manager_next_scene(nfc->scene_manager, NfcSceneSaveSuccess); | ||||||
|                     DOLPHIN_DEED(DolphinDeedNfcMfcAdd); |                     dolphin_deed(DolphinDeedNfcMfcAdd); | ||||||
|                 } else { |                 } else { | ||||||
|                     scene_manager_next_scene(nfc->scene_manager, NfcSceneDictNotFound); |                     scene_manager_next_scene(nfc->scene_manager, NfcSceneDictNotFound); | ||||||
|                 } |                 } | ||||||
|  | |||||||
| @ -54,14 +54,14 @@ bool nfc_scene_mf_classic_menu_on_event(void* context, SceneManagerEvent event) | |||||||
|         } else if(event.event == SubmenuIndexEmulate) { |         } else if(event.event == SubmenuIndexEmulate) { | ||||||
|             scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicEmulate); |             scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicEmulate); | ||||||
|             if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneSetType)) { |             if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneSetType)) { | ||||||
|                 DOLPHIN_DEED(DolphinDeedNfcAddEmulate); |                 dolphin_deed(DolphinDeedNfcAddEmulate); | ||||||
|             } else { |             } else { | ||||||
|                 DOLPHIN_DEED(DolphinDeedNfcEmulate); |                 dolphin_deed(DolphinDeedNfcEmulate); | ||||||
|             } |             } | ||||||
|             consumed = true; |             consumed = true; | ||||||
|         } else if(event.event == SubmenuIndexDetectReader) { |         } else if(event.event == SubmenuIndexDetectReader) { | ||||||
|             scene_manager_next_scene(nfc->scene_manager, NfcSceneDetectReader); |             scene_manager_next_scene(nfc->scene_manager, NfcSceneDetectReader); | ||||||
|             DOLPHIN_DEED(DolphinDeedNfcDetectReader); |             dolphin_deed(DolphinDeedNfcDetectReader); | ||||||
|             consumed = true; |             consumed = true; | ||||||
|         } else if(event.event == SubmenuIndexInfo) { |         } else if(event.event == SubmenuIndexInfo) { | ||||||
|             scene_manager_next_scene(nfc->scene_manager, NfcSceneNfcDataInfo); |             scene_manager_next_scene(nfc->scene_manager, NfcSceneNfcDataInfo); | ||||||
|  | |||||||
| @ -34,7 +34,7 @@ static void nfc_scene_mf_classic_update_setup_view(Nfc* nfc) { | |||||||
| 
 | 
 | ||||||
| void nfc_scene_mf_classic_update_on_enter(void* context) { | void nfc_scene_mf_classic_update_on_enter(void* context) { | ||||||
|     Nfc* nfc = context; |     Nfc* nfc = context; | ||||||
|     DOLPHIN_DEED(DolphinDeedNfcEmulate); |     dolphin_deed(DolphinDeedNfcEmulate); | ||||||
| 
 | 
 | ||||||
|     scene_manager_set_scene_state( |     scene_manager_set_scene_state( | ||||||
|         nfc->scene_manager, NfcSceneMfClassicUpdate, NfcSceneMfClassicUpdateStateCardSearch); |         nfc->scene_manager, NfcSceneMfClassicUpdate, NfcSceneMfClassicUpdateStateCardSearch); | ||||||
|  | |||||||
| @ -8,7 +8,7 @@ void nfc_scene_mf_classic_update_success_popup_callback(void* context) { | |||||||
| 
 | 
 | ||||||
| void nfc_scene_mf_classic_update_success_on_enter(void* context) { | void nfc_scene_mf_classic_update_success_on_enter(void* context) { | ||||||
|     Nfc* nfc = context; |     Nfc* nfc = context; | ||||||
|     DOLPHIN_DEED(DolphinDeedNfcSave); |     dolphin_deed(DolphinDeedNfcSave); | ||||||
| 
 | 
 | ||||||
|     notification_message(nfc->notifications, &sequence_success); |     notification_message(nfc->notifications, &sequence_success); | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -34,7 +34,7 @@ static void nfc_scene_mf_classic_write_setup_view(Nfc* nfc) { | |||||||
| 
 | 
 | ||||||
| void nfc_scene_mf_classic_write_on_enter(void* context) { | void nfc_scene_mf_classic_write_on_enter(void* context) { | ||||||
|     Nfc* nfc = context; |     Nfc* nfc = context; | ||||||
|     DOLPHIN_DEED(DolphinDeedNfcEmulate); |     dolphin_deed(DolphinDeedNfcEmulate); | ||||||
| 
 | 
 | ||||||
|     scene_manager_set_scene_state( |     scene_manager_set_scene_state( | ||||||
|         nfc->scene_manager, NfcSceneMfClassicWrite, NfcSceneMfClassicWriteStateCardSearch); |         nfc->scene_manager, NfcSceneMfClassicWrite, NfcSceneMfClassicWriteStateCardSearch); | ||||||
|  | |||||||
| @ -8,7 +8,7 @@ void nfc_scene_mf_classic_write_success_popup_callback(void* context) { | |||||||
| 
 | 
 | ||||||
| void nfc_scene_mf_classic_write_success_on_enter(void* context) { | void nfc_scene_mf_classic_write_success_on_enter(void* context) { | ||||||
|     Nfc* nfc = context; |     Nfc* nfc = context; | ||||||
|     DOLPHIN_DEED(DolphinDeedNfcSave); |     dolphin_deed(DolphinDeedNfcSave); | ||||||
| 
 | 
 | ||||||
|     notification_message(nfc->notifications, &sequence_success); |     notification_message(nfc->notifications, &sequence_success); | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -50,9 +50,9 @@ bool nfc_scene_mf_desfire_menu_on_event(void* context, SceneManagerEvent event) | |||||||
|         } else if(event.event == SubmenuIndexEmulateUid) { |         } else if(event.event == SubmenuIndexEmulateUid) { | ||||||
|             scene_manager_next_scene(nfc->scene_manager, NfcSceneEmulateUid); |             scene_manager_next_scene(nfc->scene_manager, NfcSceneEmulateUid); | ||||||
|             if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneSetType)) { |             if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneSetType)) { | ||||||
|                 DOLPHIN_DEED(DolphinDeedNfcAddEmulate); |                 dolphin_deed(DolphinDeedNfcAddEmulate); | ||||||
|             } else { |             } else { | ||||||
|                 DOLPHIN_DEED(DolphinDeedNfcEmulate); |                 dolphin_deed(DolphinDeedNfcEmulate); | ||||||
|             } |             } | ||||||
|             consumed = true; |             consumed = true; | ||||||
|         } else if(event.event == SubmenuIndexInfo) { |         } else if(event.event == SubmenuIndexInfo) { | ||||||
|  | |||||||
| @ -60,9 +60,9 @@ bool nfc_scene_mf_ultralight_menu_on_event(void* context, SceneManagerEvent even | |||||||
|         } else if(event.event == SubmenuIndexEmulate) { |         } else if(event.event == SubmenuIndexEmulate) { | ||||||
|             scene_manager_next_scene(nfc->scene_manager, NfcSceneMfUltralightEmulate); |             scene_manager_next_scene(nfc->scene_manager, NfcSceneMfUltralightEmulate); | ||||||
|             if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneSetType)) { |             if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneSetType)) { | ||||||
|                 DOLPHIN_DEED(DolphinDeedNfcAddEmulate); |                 dolphin_deed(DolphinDeedNfcAddEmulate); | ||||||
|             } else { |             } else { | ||||||
|                 DOLPHIN_DEED(DolphinDeedNfcEmulate); |                 dolphin_deed(DolphinDeedNfcEmulate); | ||||||
|             } |             } | ||||||
|             consumed = true; |             consumed = true; | ||||||
|         } else if(event.event == SubmenuIndexUnlock) { |         } else if(event.event == SubmenuIndexUnlock) { | ||||||
|  | |||||||
| @ -61,7 +61,7 @@ bool nfc_scene_mf_ultralight_unlock_warn_on_event(void* context, SceneManagerEve | |||||||
|         if(event.type == SceneManagerEventTypeCustom) { |         if(event.type == SceneManagerEventTypeCustom) { | ||||||
|             if(event.event == DialogExResultRight) { |             if(event.event == DialogExResultRight) { | ||||||
|                 scene_manager_next_scene(nfc->scene_manager, NfcSceneMfUltralightReadAuth); |                 scene_manager_next_scene(nfc->scene_manager, NfcSceneMfUltralightReadAuth); | ||||||
|                 DOLPHIN_DEED(DolphinDeedNfcRead); |                 dolphin_deed(DolphinDeedNfcRead); | ||||||
|                 consumed = true; |                 consumed = true; | ||||||
|             } else if(event.event == DialogExResultLeft) { |             } else if(event.event == DialogExResultLeft) { | ||||||
|                 if(auth_method == MfUltralightAuthMethodAuto) { |                 if(auth_method == MfUltralightAuthMethodAuto) { | ||||||
| @ -79,7 +79,7 @@ bool nfc_scene_mf_ultralight_unlock_warn_on_event(void* context, SceneManagerEve | |||||||
|         if(event.type == SceneManagerEventTypeCustom) { |         if(event.type == SceneManagerEventTypeCustom) { | ||||||
|             if(event.event == DialogExResultCenter) { |             if(event.event == DialogExResultCenter) { | ||||||
|                 scene_manager_next_scene(nfc->scene_manager, NfcSceneMfUltralightReadAuth); |                 scene_manager_next_scene(nfc->scene_manager, NfcSceneMfUltralightReadAuth); | ||||||
|                 DOLPHIN_DEED(DolphinDeedNfcRead); |                 dolphin_deed(DolphinDeedNfcRead); | ||||||
|                 consumed = true; |                 consumed = true; | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|  | |||||||
| @ -41,19 +41,165 @@ void nfc_scene_nfc_data_info_on_enter(void* context) { | |||||||
|             temp_str, "\e#%s\n", nfc_mf_classic_type(dev_data->mf_classic_data.type)); |             temp_str, "\e#%s\n", nfc_mf_classic_type(dev_data->mf_classic_data.type)); | ||||||
|     } else if(protocol == NfcDeviceProtocolMifareDesfire) { |     } else if(protocol == NfcDeviceProtocolMifareDesfire) { | ||||||
|         furi_string_cat_printf(temp_str, "\e#MIFARE DESFire\n"); |         furi_string_cat_printf(temp_str, "\e#MIFARE DESFire\n"); | ||||||
|  |     } else if(protocol == NfcDeviceProtocolNfcV) { | ||||||
|  |         switch(dev_data->nfcv_data.sub_type) { | ||||||
|  |         case NfcVTypePlain: | ||||||
|  |             furi_string_cat_printf(temp_str, "\e#ISO15693\n"); | ||||||
|  |             break; | ||||||
|  |         case NfcVTypeSlix: | ||||||
|  |             furi_string_cat_printf(temp_str, "\e#ISO15693 SLIX\n"); | ||||||
|  |             break; | ||||||
|  |         case NfcVTypeSlixS: | ||||||
|  |             furi_string_cat_printf(temp_str, "\e#ISO15693 SLIX-S\n"); | ||||||
|  |             break; | ||||||
|  |         case NfcVTypeSlixL: | ||||||
|  |             furi_string_cat_printf(temp_str, "\e#ISO15693 SLIX-L\n"); | ||||||
|  |             break; | ||||||
|  |         case NfcVTypeSlix2: | ||||||
|  |             furi_string_cat_printf(temp_str, "\e#ISO15693 SLIX2\n"); | ||||||
|  |             break; | ||||||
|  |         default: | ||||||
|  |             furi_string_cat_printf(temp_str, "\e#ISO15693 (unknown)\n"); | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|     } else { |     } else { | ||||||
|         furi_string_cat_printf(temp_str, "\e#Unknown ISO tag\n"); |         furi_string_cat_printf(temp_str, "\e#Unknown ISO tag\n"); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     // Set tag iso data
 |     // Set tag iso data
 | ||||||
|  |     if(protocol == NfcDeviceProtocolNfcV) { | ||||||
|  |         NfcVData* nfcv_data = &nfc->dev->dev_data.nfcv_data; | ||||||
|  | 
 | ||||||
|  |         furi_string_cat_printf(temp_str, "UID:\n"); | ||||||
|  |         for(size_t i = 0; i < nfc_data->uid_len; i++) { | ||||||
|  |             furi_string_cat_printf(temp_str, " %02X", nfc_data->uid[i]); | ||||||
|  |         } | ||||||
|  |         furi_string_cat_printf(temp_str, "\n"); | ||||||
|  | 
 | ||||||
|  |         furi_string_cat_printf( | ||||||
|  |             temp_str, | ||||||
|  |             "DSFID: %02X %s\n", | ||||||
|  |             nfcv_data->dsfid, | ||||||
|  |             (nfcv_data->security_status[0] & NfcVLockBitDsfid) ? "(locked)" : ""); | ||||||
|  |         furi_string_cat_printf( | ||||||
|  |             temp_str, | ||||||
|  |             "AFI: %02X %s\n", | ||||||
|  |             nfcv_data->afi, | ||||||
|  |             (nfcv_data->security_status[0] & NfcVLockBitAfi) ? "(locked)" : ""); | ||||||
|  |         furi_string_cat_printf(temp_str, "IC Ref: %02X\n", nfcv_data->ic_ref); | ||||||
|  |         furi_string_cat_printf(temp_str, "Blocks: %02X\n", nfcv_data->block_num); | ||||||
|  |         furi_string_cat_printf(temp_str, "Blocksize: %02X\n", nfcv_data->block_size); | ||||||
|  | 
 | ||||||
|  |         switch(dev_data->nfcv_data.sub_type) { | ||||||
|  |         case NfcVTypePlain: | ||||||
|  |             furi_string_cat_printf(temp_str, "Type: Plain\n"); | ||||||
|  |             break; | ||||||
|  |         case NfcVTypeSlix: | ||||||
|  |             furi_string_cat_printf(temp_str, "Type: SLIX\n"); | ||||||
|  |             furi_string_cat_printf(temp_str, "Keys:\n"); | ||||||
|  |             furi_string_cat_printf( | ||||||
|  |                 temp_str, | ||||||
|  |                 " EAS      %08llX\n", | ||||||
|  |                 nfc_util_bytes2num(nfcv_data->sub_data.slix.key_eas, 4)); | ||||||
|  |             break; | ||||||
|  |         case NfcVTypeSlixS: | ||||||
|  |             furi_string_cat_printf(temp_str, "Type: SLIX-S\n"); | ||||||
|  |             furi_string_cat_printf(temp_str, "Keys:\n"); | ||||||
|  |             furi_string_cat_printf( | ||||||
|  |                 temp_str, | ||||||
|  |                 " Read     %08llX\n", | ||||||
|  |                 nfc_util_bytes2num(nfcv_data->sub_data.slix.key_read, 4)); | ||||||
|  |             furi_string_cat_printf( | ||||||
|  |                 temp_str, | ||||||
|  |                 " Write    %08llX\n", | ||||||
|  |                 nfc_util_bytes2num(nfcv_data->sub_data.slix.key_write, 4)); | ||||||
|  |             furi_string_cat_printf( | ||||||
|  |                 temp_str, | ||||||
|  |                 " Privacy  %08llX\n", | ||||||
|  |                 nfc_util_bytes2num(nfcv_data->sub_data.slix.key_privacy, 4)); | ||||||
|  |             furi_string_cat_printf( | ||||||
|  |                 temp_str, | ||||||
|  |                 " Destroy  %08llX\n", | ||||||
|  |                 nfc_util_bytes2num(nfcv_data->sub_data.slix.key_destroy, 4)); | ||||||
|  |             furi_string_cat_printf( | ||||||
|  |                 temp_str, | ||||||
|  |                 " EAS      %08llX\n", | ||||||
|  |                 nfc_util_bytes2num(nfcv_data->sub_data.slix.key_eas, 4)); | ||||||
|  |             break; | ||||||
|  |         case NfcVTypeSlixL: | ||||||
|  |             furi_string_cat_printf(temp_str, "Type: SLIX-L\n"); | ||||||
|  |             furi_string_cat_printf(temp_str, "Keys:\n"); | ||||||
|  |             furi_string_cat_printf( | ||||||
|  |                 temp_str, | ||||||
|  |                 " Privacy  %08llX\n", | ||||||
|  |                 nfc_util_bytes2num(nfcv_data->sub_data.slix.key_privacy, 4)); | ||||||
|  |             furi_string_cat_printf( | ||||||
|  |                 temp_str, | ||||||
|  |                 " Destroy  %08llX\n", | ||||||
|  |                 nfc_util_bytes2num(nfcv_data->sub_data.slix.key_destroy, 4)); | ||||||
|  |             furi_string_cat_printf( | ||||||
|  |                 temp_str, | ||||||
|  |                 " EAS      %08llX\n", | ||||||
|  |                 nfc_util_bytes2num(nfcv_data->sub_data.slix.key_eas, 4)); | ||||||
|  |             break; | ||||||
|  |         case NfcVTypeSlix2: | ||||||
|  |             furi_string_cat_printf(temp_str, "Type: SLIX2\n"); | ||||||
|  |             furi_string_cat_printf(temp_str, "Keys:\n"); | ||||||
|  |             furi_string_cat_printf( | ||||||
|  |                 temp_str, | ||||||
|  |                 " Read     %08llX\n", | ||||||
|  |                 nfc_util_bytes2num(nfcv_data->sub_data.slix.key_read, 4)); | ||||||
|  |             furi_string_cat_printf( | ||||||
|  |                 temp_str, | ||||||
|  |                 " Write    %08llX\n", | ||||||
|  |                 nfc_util_bytes2num(nfcv_data->sub_data.slix.key_write, 4)); | ||||||
|  |             furi_string_cat_printf( | ||||||
|  |                 temp_str, | ||||||
|  |                 " Privacy  %08llX\n", | ||||||
|  |                 nfc_util_bytes2num(nfcv_data->sub_data.slix.key_privacy, 4)); | ||||||
|  |             furi_string_cat_printf( | ||||||
|  |                 temp_str, | ||||||
|  |                 " Destroy  %08llX\n", | ||||||
|  |                 nfc_util_bytes2num(nfcv_data->sub_data.slix.key_destroy, 4)); | ||||||
|  |             furi_string_cat_printf( | ||||||
|  |                 temp_str, | ||||||
|  |                 " EAS      %08llX\n", | ||||||
|  |                 nfc_util_bytes2num(nfcv_data->sub_data.slix.key_eas, 4)); | ||||||
|  |             break; | ||||||
|  |         default: | ||||||
|  |             furi_string_cat_printf(temp_str, "\e#ISO15693 (unknown)\n"); | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         furi_string_cat_printf( | ||||||
|  |             temp_str, "Data (%d byte)\n", nfcv_data->block_num * nfcv_data->block_size); | ||||||
|  | 
 | ||||||
|  |         int maxBlocks = nfcv_data->block_num; | ||||||
|  |         if(maxBlocks > 32) { | ||||||
|  |             maxBlocks = 32; | ||||||
|  |             furi_string_cat_printf(temp_str, "(truncated to %d blocks)\n", maxBlocks); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         for(int block = 0; block < maxBlocks; block++) { | ||||||
|  |             const char* status = (nfcv_data->security_status[block] & 0x01) ? "(lck)" : ""; | ||||||
|  |             for(int pos = 0; pos < nfcv_data->block_size; pos++) { | ||||||
|  |                 furi_string_cat_printf( | ||||||
|  |                     temp_str, " %02X", nfcv_data->data[block * nfcv_data->block_size + pos]); | ||||||
|  |             } | ||||||
|  |             furi_string_cat_printf(temp_str, " %s\n", status); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |     } else { | ||||||
|         char iso_type = FURI_BIT(nfc_data->sak, 5) ? '4' : '3'; |         char iso_type = FURI_BIT(nfc_data->sak, 5) ? '4' : '3'; | ||||||
|         furi_string_cat_printf(temp_str, "ISO 14443-%c (NFC-A)\n", iso_type); |         furi_string_cat_printf(temp_str, "ISO 14443-%c (NFC-A)\n", iso_type); | ||||||
|         furi_string_cat_printf(temp_str, "UID:"); |         furi_string_cat_printf(temp_str, "UID:"); | ||||||
|         for(size_t i = 0; i < nfc_data->uid_len; i++) { |         for(size_t i = 0; i < nfc_data->uid_len; i++) { | ||||||
|             furi_string_cat_printf(temp_str, " %02X", nfc_data->uid[i]); |             furi_string_cat_printf(temp_str, " %02X", nfc_data->uid[i]); | ||||||
|         } |         } | ||||||
|     furi_string_cat_printf(temp_str, "\nATQA: %02X %02X ", nfc_data->atqa[1], nfc_data->atqa[0]); |         furi_string_cat_printf( | ||||||
|  |             temp_str, "\nATQA: %02X %02X ", nfc_data->atqa[1], nfc_data->atqa[0]); | ||||||
|         furi_string_cat_printf(temp_str, " SAK: %02X", nfc_data->sak); |         furi_string_cat_printf(temp_str, " SAK: %02X", nfc_data->sak); | ||||||
|  |     } | ||||||
| 
 | 
 | ||||||
|     // Set application specific data
 |     // Set application specific data
 | ||||||
|     if(protocol == NfcDeviceProtocolMifareDesfire) { |     if(protocol == NfcDeviceProtocolMifareDesfire) { | ||||||
| @ -139,6 +285,8 @@ bool nfc_scene_nfc_data_info_on_event(void* context, SceneManagerEvent event) { | |||||||
|                 consumed = true; |                 consumed = true; | ||||||
|             } else if(protocol == NfcDeviceProtocolMifareClassic) { |             } else if(protocol == NfcDeviceProtocolMifareClassic) { | ||||||
|                 scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicData); |                 scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicData); | ||||||
|  |             } else if(protocol == NfcDeviceProtocolNfcV) { | ||||||
|  |                 scene_manager_next_scene(nfc->scene_manager, NfcSceneNfcVMenu); | ||||||
|                 consumed = true; |                 consumed = true; | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|  | |||||||
| @ -43,9 +43,9 @@ bool nfc_scene_nfca_menu_on_event(void* context, SceneManagerEvent event) { | |||||||
|         } else if(event.event == SubmenuIndexEmulateUid) { |         } else if(event.event == SubmenuIndexEmulateUid) { | ||||||
|             scene_manager_next_scene(nfc->scene_manager, NfcSceneEmulateUid); |             scene_manager_next_scene(nfc->scene_manager, NfcSceneEmulateUid); | ||||||
|             if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneSetType)) { |             if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneSetType)) { | ||||||
|                 DOLPHIN_DEED(DolphinDeedNfcAddEmulate); |                 dolphin_deed(DolphinDeedNfcAddEmulate); | ||||||
|             } else { |             } else { | ||||||
|                 DOLPHIN_DEED(DolphinDeedNfcEmulate); |                 dolphin_deed(DolphinDeedNfcEmulate); | ||||||
|             } |             } | ||||||
|             consumed = true; |             consumed = true; | ||||||
|         } else if(event.event == SubmenuIndexInfo) { |         } else if(event.event == SubmenuIndexInfo) { | ||||||
|  | |||||||
							
								
								
									
										169
									
								
								applications/main/nfc/scenes/nfc_scene_nfcv_emulate.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,169 @@ | |||||||
|  | #include "../nfc_i.h" | ||||||
|  | 
 | ||||||
|  | #define NFC_SCENE_EMULATE_NFCV_LOG_SIZE_MAX (200) | ||||||
|  | 
 | ||||||
|  | enum { | ||||||
|  |     NfcSceneNfcVEmulateStateWidget, | ||||||
|  |     NfcSceneNfcVEmulateStateTextBox, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | bool nfc_scene_nfcv_emulate_worker_callback(NfcWorkerEvent event, void* context) { | ||||||
|  |     furi_assert(context); | ||||||
|  |     Nfc* nfc = context; | ||||||
|  | 
 | ||||||
|  |     switch(event) { | ||||||
|  |     case NfcWorkerEventNfcVCommandExecuted: | ||||||
|  |         if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) { | ||||||
|  |             view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventUpdateLog); | ||||||
|  |         } | ||||||
|  |         break; | ||||||
|  |     case NfcWorkerEventNfcVContentChanged: | ||||||
|  |         view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventSaveShadow); | ||||||
|  |         break; | ||||||
|  |     default: | ||||||
|  |         break; | ||||||
|  |     } | ||||||
|  |     return true; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void nfc_scene_nfcv_emulate_widget_callback(GuiButtonType result, InputType type, void* context) { | ||||||
|  |     furi_assert(context); | ||||||
|  |     Nfc* nfc = context; | ||||||
|  |     if(type == InputTypeShort) { | ||||||
|  |         view_dispatcher_send_custom_event(nfc->view_dispatcher, result); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void nfc_scene_nfcv_emulate_textbox_callback(void* context) { | ||||||
|  |     furi_assert(context); | ||||||
|  |     Nfc* nfc = context; | ||||||
|  |     view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventViewExit); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void nfc_scene_nfcv_emulate_widget_config(Nfc* nfc, bool data_received) { | ||||||
|  |     FuriHalNfcDevData* data = &nfc->dev->dev_data.nfc_data; | ||||||
|  |     Widget* widget = nfc->widget; | ||||||
|  |     widget_reset(widget); | ||||||
|  |     FuriString* info_str; | ||||||
|  |     info_str = furi_string_alloc(); | ||||||
|  | 
 | ||||||
|  |     widget_add_icon_element(widget, 0, 3, &I_NFC_dolphin_emulation_47x61); | ||||||
|  |     widget_add_string_multiline_element( | ||||||
|  |         widget, 87, 13, AlignCenter, AlignTop, FontPrimary, "Emulating\nNFC V"); | ||||||
|  |     if(strcmp(nfc->dev->dev_name, "") != 0) { | ||||||
|  |         furi_string_printf(info_str, "%s", nfc->dev->dev_name); | ||||||
|  |     } else { | ||||||
|  |         for(uint8_t i = 0; i < data->uid_len; i++) { | ||||||
|  |             furi_string_cat_printf(info_str, "%02X ", data->uid[i]); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     furi_string_trim(info_str); | ||||||
|  |     widget_add_text_box_element( | ||||||
|  |         widget, 52, 40, 70, 21, AlignCenter, AlignTop, furi_string_get_cstr(info_str), true); | ||||||
|  |     furi_string_free(info_str); | ||||||
|  |     if(data_received) { | ||||||
|  |         if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) { | ||||||
|  |             widget_add_button_element( | ||||||
|  |                 widget, GuiButtonTypeCenter, "Log", nfc_scene_nfcv_emulate_widget_callback, nfc); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void nfc_scene_nfcv_emulate_on_enter(void* context) { | ||||||
|  |     Nfc* nfc = context; | ||||||
|  | 
 | ||||||
|  |     // Setup Widget
 | ||||||
|  |     nfc_scene_nfcv_emulate_widget_config(nfc, false); | ||||||
|  |     // Setup TextBox
 | ||||||
|  |     TextBox* text_box = nfc->text_box; | ||||||
|  |     text_box_set_font(text_box, TextBoxFontHex); | ||||||
|  |     text_box_set_focus(text_box, TextBoxFocusEnd); | ||||||
|  |     text_box_set_text(text_box, ""); | ||||||
|  |     furi_string_reset(nfc->text_box_store); | ||||||
|  | 
 | ||||||
|  |     // Set Widget state and view
 | ||||||
|  |     scene_manager_set_scene_state( | ||||||
|  |         nfc->scene_manager, NfcSceneNfcVEmulate, NfcSceneNfcVEmulateStateWidget); | ||||||
|  |     view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget); | ||||||
|  |     // Start worker
 | ||||||
|  |     memset(&nfc->dev->dev_data.reader_data, 0, sizeof(NfcReaderRequestData)); | ||||||
|  |     nfc_worker_start( | ||||||
|  |         nfc->worker, | ||||||
|  |         NfcWorkerStateNfcVEmulate, | ||||||
|  |         &nfc->dev->dev_data, | ||||||
|  |         nfc_scene_nfcv_emulate_worker_callback, | ||||||
|  |         nfc); | ||||||
|  | 
 | ||||||
|  |     nfc_blink_emulate_start(nfc); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool nfc_scene_nfcv_emulate_on_event(void* context, SceneManagerEvent event) { | ||||||
|  |     Nfc* nfc = context; | ||||||
|  |     NfcVData* nfcv_data = &nfc->dev->dev_data.nfcv_data; | ||||||
|  |     uint32_t state = scene_manager_get_scene_state(nfc->scene_manager, NfcSceneNfcVEmulate); | ||||||
|  |     bool consumed = false; | ||||||
|  | 
 | ||||||
|  |     if(event.type == SceneManagerEventTypeCustom) { | ||||||
|  |         if(event.event == NfcCustomEventUpdateLog) { | ||||||
|  |             // Add data button to widget if data is received for the first time
 | ||||||
|  |             if(strlen(nfcv_data->last_command) > 0) { | ||||||
|  |                 if(!furi_string_size(nfc->text_box_store)) { | ||||||
|  |                     nfc_scene_nfcv_emulate_widget_config(nfc, true); | ||||||
|  |                 } | ||||||
|  |                 /* use the last n bytes from the log so there's enough space for the new log entry */ | ||||||
|  |                 size_t maxSize = | ||||||
|  |                     NFC_SCENE_EMULATE_NFCV_LOG_SIZE_MAX - (strlen(nfcv_data->last_command) + 1); | ||||||
|  |                 if(furi_string_size(nfc->text_box_store) >= maxSize) { | ||||||
|  |                     furi_string_right(nfc->text_box_store, (strlen(nfcv_data->last_command) + 1)); | ||||||
|  |                 } | ||||||
|  |                 furi_string_cat_printf(nfc->text_box_store, "%s", nfcv_data->last_command); | ||||||
|  |                 furi_string_push_back(nfc->text_box_store, '\n'); | ||||||
|  |                 text_box_set_text(nfc->text_box, furi_string_get_cstr(nfc->text_box_store)); | ||||||
|  | 
 | ||||||
|  |                 /* clear previously logged command */ | ||||||
|  |                 strcpy(nfcv_data->last_command, ""); | ||||||
|  |             } | ||||||
|  |             consumed = true; | ||||||
|  |         } else if(event.event == NfcCustomEventSaveShadow) { | ||||||
|  |             if(furi_string_size(nfc->dev->load_path)) { | ||||||
|  |                 nfc_device_save_shadow(nfc->dev, furi_string_get_cstr(nfc->dev->load_path)); | ||||||
|  |             } | ||||||
|  |             consumed = true; | ||||||
|  |         } else if(event.event == GuiButtonTypeCenter && state == NfcSceneNfcVEmulateStateWidget) { | ||||||
|  |             if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) { | ||||||
|  |                 view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewTextBox); | ||||||
|  |                 scene_manager_set_scene_state( | ||||||
|  |                     nfc->scene_manager, NfcSceneNfcVEmulate, NfcSceneNfcVEmulateStateTextBox); | ||||||
|  |             } | ||||||
|  |             consumed = true; | ||||||
|  |         } else if(event.event == NfcCustomEventViewExit && state == NfcSceneNfcVEmulateStateTextBox) { | ||||||
|  |             view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget); | ||||||
|  |             scene_manager_set_scene_state( | ||||||
|  |                 nfc->scene_manager, NfcSceneNfcVEmulate, NfcSceneNfcVEmulateStateWidget); | ||||||
|  |             consumed = true; | ||||||
|  |         } | ||||||
|  |     } else if(event.type == SceneManagerEventTypeBack) { | ||||||
|  |         if(state == NfcSceneNfcVEmulateStateTextBox) { | ||||||
|  |             view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget); | ||||||
|  |             scene_manager_set_scene_state( | ||||||
|  |                 nfc->scene_manager, NfcSceneNfcVEmulate, NfcSceneNfcVEmulateStateWidget); | ||||||
|  |             consumed = true; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return consumed; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void nfc_scene_nfcv_emulate_on_exit(void* context) { | ||||||
|  |     Nfc* nfc = context; | ||||||
|  | 
 | ||||||
|  |     // Stop worker
 | ||||||
|  |     nfc_worker_stop(nfc->worker); | ||||||
|  | 
 | ||||||
|  |     // Clear view
 | ||||||
|  |     widget_reset(nfc->widget); | ||||||
|  |     text_box_reset(nfc->text_box); | ||||||
|  |     furi_string_reset(nfc->text_box_store); | ||||||
|  | 
 | ||||||
|  |     nfc_blink_stop(nfc); | ||||||
|  | } | ||||||
							
								
								
									
										48
									
								
								applications/main/nfc/scenes/nfc_scene_nfcv_key_input.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,48 @@ | |||||||
|  | #include "../nfc_i.h" | ||||||
|  | #include <dolphin/dolphin.h> | ||||||
|  | 
 | ||||||
|  | void nfc_scene_nfcv_key_input_byte_input_callback(void* context) { | ||||||
|  |     Nfc* nfc = context; | ||||||
|  |     NfcVSlixData* data = &nfc->dev->dev_data.nfcv_data.sub_data.slix; | ||||||
|  | 
 | ||||||
|  |     memcpy(data->key_privacy, nfc->byte_input_store, 4); | ||||||
|  |     view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventByteInputDone); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void nfc_scene_nfcv_key_input_on_enter(void* context) { | ||||||
|  |     Nfc* nfc = context; | ||||||
|  | 
 | ||||||
|  |     // Setup view
 | ||||||
|  |     ByteInput* byte_input = nfc->byte_input; | ||||||
|  |     byte_input_set_header_text(byte_input, "Enter The Password In Hex"); | ||||||
|  |     byte_input_set_result_callback( | ||||||
|  |         byte_input, | ||||||
|  |         nfc_scene_nfcv_key_input_byte_input_callback, | ||||||
|  |         NULL, | ||||||
|  |         nfc, | ||||||
|  |         nfc->byte_input_store, | ||||||
|  |         4); | ||||||
|  |     view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewByteInput); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool nfc_scene_nfcv_key_input_on_event(void* context, SceneManagerEvent event) { | ||||||
|  |     Nfc* nfc = context; | ||||||
|  |     bool consumed = false; | ||||||
|  | 
 | ||||||
|  |     if(event.type == SceneManagerEventTypeCustom) { | ||||||
|  |         if(event.event == NfcCustomEventByteInputDone) { | ||||||
|  |             scene_manager_next_scene(nfc->scene_manager, NfcSceneNfcVUnlock); | ||||||
|  |             dolphin_deed(DolphinDeedNfcRead); | ||||||
|  |             consumed = true; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     return consumed; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void nfc_scene_nfcv_key_input_on_exit(void* context) { | ||||||
|  |     Nfc* nfc = context; | ||||||
|  | 
 | ||||||
|  |     // Clear view
 | ||||||
|  |     byte_input_set_result_callback(nfc->byte_input, NULL, NULL, NULL, NULL, 0); | ||||||
|  |     byte_input_set_header_text(nfc->byte_input, ""); | ||||||
|  | } | ||||||
							
								
								
									
										68
									
								
								applications/main/nfc/scenes/nfc_scene_nfcv_menu.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,68 @@ | |||||||
|  | #include "../nfc_i.h" | ||||||
|  | #include <dolphin/dolphin.h> | ||||||
|  | 
 | ||||||
|  | enum SubmenuIndex { | ||||||
|  |     SubmenuIndexSave, | ||||||
|  |     SubmenuIndexEmulate, | ||||||
|  |     SubmenuIndexInfo, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | void nfc_scene_nfcv_menu_submenu_callback(void* context, uint32_t index) { | ||||||
|  |     Nfc* nfc = context; | ||||||
|  | 
 | ||||||
|  |     view_dispatcher_send_custom_event(nfc->view_dispatcher, index); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void nfc_scene_nfcv_menu_on_enter(void* context) { | ||||||
|  |     Nfc* nfc = context; | ||||||
|  |     Submenu* submenu = nfc->submenu; | ||||||
|  | 
 | ||||||
|  |     submenu_add_item( | ||||||
|  |         submenu, "Emulate", SubmenuIndexEmulate, nfc_scene_nfcv_menu_submenu_callback, nfc); | ||||||
|  |     submenu_add_item(submenu, "Save", SubmenuIndexSave, nfc_scene_nfcv_menu_submenu_callback, nfc); | ||||||
|  |     submenu_add_item(submenu, "Info", SubmenuIndexInfo, nfc_scene_nfcv_menu_submenu_callback, nfc); | ||||||
|  | 
 | ||||||
|  |     submenu_set_selected_item( | ||||||
|  |         nfc->submenu, scene_manager_get_scene_state(nfc->scene_manager, NfcSceneNfcVMenu)); | ||||||
|  | 
 | ||||||
|  |     view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewMenu); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool nfc_scene_nfcv_menu_on_event(void* context, SceneManagerEvent event) { | ||||||
|  |     Nfc* nfc = context; | ||||||
|  |     bool consumed = false; | ||||||
|  | 
 | ||||||
|  |     if(event.type == SceneManagerEventTypeCustom) { | ||||||
|  |         if(event.event == SubmenuIndexSave) { | ||||||
|  |             nfc->dev->format = NfcDeviceSaveFormatNfcV; | ||||||
|  |             // Clear device name
 | ||||||
|  |             nfc_device_set_name(nfc->dev, ""); | ||||||
|  |             scene_manager_next_scene(nfc->scene_manager, NfcSceneSaveName); | ||||||
|  |             consumed = true; | ||||||
|  |         } else if(event.event == SubmenuIndexEmulate) { | ||||||
|  |             scene_manager_next_scene(nfc->scene_manager, NfcSceneNfcVEmulate); | ||||||
|  |             if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneSetType)) { | ||||||
|  |                 dolphin_deed(DolphinDeedNfcAddEmulate); | ||||||
|  |             } else { | ||||||
|  |                 dolphin_deed(DolphinDeedNfcEmulate); | ||||||
|  |             } | ||||||
|  |             consumed = true; | ||||||
|  |         } else if(event.event == SubmenuIndexInfo) { | ||||||
|  |             scene_manager_next_scene(nfc->scene_manager, NfcSceneNfcDataInfo); | ||||||
|  |             consumed = true; | ||||||
|  |         } | ||||||
|  |         scene_manager_set_scene_state(nfc->scene_manager, NfcSceneNfcVMenu, event.event); | ||||||
|  | 
 | ||||||
|  |     } else if(event.type == SceneManagerEventTypeBack) { | ||||||
|  |         consumed = scene_manager_previous_scene(nfc->scene_manager); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return consumed; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void nfc_scene_nfcv_menu_on_exit(void* context) { | ||||||
|  |     Nfc* nfc = context; | ||||||
|  | 
 | ||||||
|  |     // Clear view
 | ||||||
|  |     submenu_reset(nfc->submenu); | ||||||
|  | } | ||||||
							
								
								
									
										94
									
								
								applications/main/nfc/scenes/nfc_scene_nfcv_read_success.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,94 @@ | |||||||
|  | #include "../nfc_i.h" | ||||||
|  | 
 | ||||||
|  | void nfc_scene_nfcv_read_success_widget_callback( | ||||||
|  |     GuiButtonType result, | ||||||
|  |     InputType type, | ||||||
|  |     void* context) { | ||||||
|  |     furi_assert(context); | ||||||
|  |     Nfc* nfc = context; | ||||||
|  | 
 | ||||||
|  |     if(type == InputTypeShort) { | ||||||
|  |         view_dispatcher_send_custom_event(nfc->view_dispatcher, result); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void nfc_scene_nfcv_read_success_on_enter(void* context) { | ||||||
|  |     Nfc* nfc = context; | ||||||
|  |     NfcDeviceData* dev_data = &nfc->dev->dev_data; | ||||||
|  |     FuriHalNfcDevData* nfc_data = &nfc->dev->dev_data.nfc_data; | ||||||
|  |     NfcVData* nfcv_data = &nfc->dev->dev_data.nfcv_data; | ||||||
|  |     // Setup view
 | ||||||
|  |     Widget* widget = nfc->widget; | ||||||
|  |     widget_add_button_element( | ||||||
|  |         widget, GuiButtonTypeLeft, "Retry", nfc_scene_nfcv_read_success_widget_callback, nfc); | ||||||
|  |     widget_add_button_element( | ||||||
|  |         widget, GuiButtonTypeRight, "More", nfc_scene_nfcv_read_success_widget_callback, nfc); | ||||||
|  | 
 | ||||||
|  |     FuriString* temp_str = furi_string_alloc(); | ||||||
|  | 
 | ||||||
|  |     switch(dev_data->nfcv_data.sub_type) { | ||||||
|  |     case NfcVTypePlain: | ||||||
|  |         furi_string_cat_printf(temp_str, "\e#ISO15693\n"); | ||||||
|  |         break; | ||||||
|  |     case NfcVTypeSlix: | ||||||
|  |         furi_string_cat_printf(temp_str, "\e#ISO15693 SLIX\n"); | ||||||
|  |         break; | ||||||
|  |     case NfcVTypeSlixS: | ||||||
|  |         furi_string_cat_printf(temp_str, "\e#ISO15693 SLIX-S\n"); | ||||||
|  |         break; | ||||||
|  |     case NfcVTypeSlixL: | ||||||
|  |         furi_string_cat_printf(temp_str, "\e#ISO15693 SLIX-L\n"); | ||||||
|  |         break; | ||||||
|  |     case NfcVTypeSlix2: | ||||||
|  |         furi_string_cat_printf(temp_str, "\e#ISO15693 SLIX2\n"); | ||||||
|  |         break; | ||||||
|  |     default: | ||||||
|  |         furi_string_cat_printf(temp_str, "\e#ISO15693 (unknown)\n"); | ||||||
|  |         break; | ||||||
|  |     } | ||||||
|  |     furi_string_cat_printf(temp_str, "UID:"); | ||||||
|  |     for(size_t i = 0; i < nfc_data->uid_len; i++) { | ||||||
|  |         furi_string_cat_printf(temp_str, " %02X", nfc_data->uid[i]); | ||||||
|  |     } | ||||||
|  |     furi_string_cat_printf(temp_str, "\n"); | ||||||
|  |     furi_string_cat_printf(temp_str, "Blocks: %02X\n", nfcv_data->block_num); | ||||||
|  |     furi_string_cat_printf(temp_str, "Blocksize: %02X\n", nfcv_data->block_size); | ||||||
|  | 
 | ||||||
|  |     widget_add_text_scroll_element(widget, 0, 0, 128, 52, furi_string_get_cstr(temp_str)); | ||||||
|  |     furi_string_free(temp_str); | ||||||
|  | 
 | ||||||
|  |     notification_message_block(nfc->notifications, &sequence_set_green_255); | ||||||
|  | 
 | ||||||
|  |     view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool nfc_scene_nfcv_read_success_on_event(void* context, SceneManagerEvent event) { | ||||||
|  |     Nfc* nfc = context; | ||||||
|  |     bool consumed = false; | ||||||
|  | 
 | ||||||
|  |     if(event.type == SceneManagerEventTypeCustom) { | ||||||
|  |         if(event.event == GuiButtonTypeLeft) { | ||||||
|  |             scene_manager_next_scene(nfc->scene_manager, NfcSceneRetryConfirm); | ||||||
|  |             consumed = true; | ||||||
|  |         } else if(event.event == GuiButtonTypeRight) { | ||||||
|  |             // Clear device name
 | ||||||
|  |             nfc_device_set_name(nfc->dev, ""); | ||||||
|  |             scene_manager_next_scene(nfc->scene_manager, NfcSceneNfcVMenu); | ||||||
|  |             consumed = true; | ||||||
|  |         } | ||||||
|  |     } else if(event.type == SceneManagerEventTypeBack) { | ||||||
|  |         scene_manager_next_scene(nfc->scene_manager, NfcSceneExitConfirm); | ||||||
|  |         consumed = true; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return consumed; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void nfc_scene_nfcv_read_success_on_exit(void* context) { | ||||||
|  |     Nfc* nfc = context; | ||||||
|  | 
 | ||||||
|  |     notification_message_block(nfc->notifications, &sequence_reset_green); | ||||||
|  | 
 | ||||||
|  |     // Clear view
 | ||||||
|  |     widget_reset(nfc->widget); | ||||||
|  | } | ||||||
							
								
								
									
										155
									
								
								applications/main/nfc/scenes/nfc_scene_nfcv_sniff.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,155 @@ | |||||||
|  | #include "../nfc_i.h" | ||||||
|  | 
 | ||||||
|  | #define NFC_SCENE_EMULATE_NFCV_LOG_SIZE_MAX (800) | ||||||
|  | 
 | ||||||
|  | enum { | ||||||
|  |     NfcSceneNfcVSniffStateWidget, | ||||||
|  |     NfcSceneNfcVSniffStateTextBox, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | bool nfc_scene_nfcv_sniff_worker_callback(NfcWorkerEvent event, void* context) { | ||||||
|  |     UNUSED(event); | ||||||
|  |     furi_assert(context); | ||||||
|  |     Nfc* nfc = context; | ||||||
|  | 
 | ||||||
|  |     switch(event) { | ||||||
|  |     case NfcWorkerEventNfcVCommandExecuted: | ||||||
|  |         view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventUpdateLog); | ||||||
|  |         break; | ||||||
|  |     case NfcWorkerEventNfcVContentChanged: | ||||||
|  |         view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventSaveShadow); | ||||||
|  |         break; | ||||||
|  |     default: | ||||||
|  |         break; | ||||||
|  |     } | ||||||
|  |     return true; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void nfc_scene_nfcv_sniff_widget_callback(GuiButtonType result, InputType type, void* context) { | ||||||
|  |     furi_assert(context); | ||||||
|  |     Nfc* nfc = context; | ||||||
|  |     if(type == InputTypeShort) { | ||||||
|  |         view_dispatcher_send_custom_event(nfc->view_dispatcher, result); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void nfc_scene_nfcv_sniff_textbox_callback(void* context) { | ||||||
|  |     furi_assert(context); | ||||||
|  |     Nfc* nfc = context; | ||||||
|  |     view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventViewExit); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void nfc_scene_nfcv_sniff_widget_config(Nfc* nfc, bool data_received) { | ||||||
|  |     Widget* widget = nfc->widget; | ||||||
|  |     widget_reset(widget); | ||||||
|  |     FuriString* info_str; | ||||||
|  |     info_str = furi_string_alloc(); | ||||||
|  | 
 | ||||||
|  |     widget_add_icon_element(widget, 0, 3, &I_RFIDDolphinSend_97x61); | ||||||
|  |     widget_add_string_element(widget, 89, 32, AlignCenter, AlignTop, FontPrimary, "Listen NfcV"); | ||||||
|  |     furi_string_trim(info_str); | ||||||
|  |     widget_add_text_box_element( | ||||||
|  |         widget, 56, 43, 70, 21, AlignCenter, AlignTop, furi_string_get_cstr(info_str), true); | ||||||
|  |     furi_string_free(info_str); | ||||||
|  |     if(data_received) { | ||||||
|  |         widget_add_button_element( | ||||||
|  |             widget, GuiButtonTypeCenter, "Log", nfc_scene_nfcv_sniff_widget_callback, nfc); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void nfc_scene_nfcv_sniff_on_enter(void* context) { | ||||||
|  |     Nfc* nfc = context; | ||||||
|  | 
 | ||||||
|  |     // Setup Widget
 | ||||||
|  |     nfc_scene_nfcv_sniff_widget_config(nfc, false); | ||||||
|  |     // Setup TextBox
 | ||||||
|  |     TextBox* text_box = nfc->text_box; | ||||||
|  |     text_box_set_font(text_box, TextBoxFontHex); | ||||||
|  |     text_box_set_focus(text_box, TextBoxFocusEnd); | ||||||
|  |     text_box_set_text(text_box, ""); | ||||||
|  |     furi_string_reset(nfc->text_box_store); | ||||||
|  | 
 | ||||||
|  |     // Set Widget state and view
 | ||||||
|  |     scene_manager_set_scene_state( | ||||||
|  |         nfc->scene_manager, NfcSceneNfcVSniff, NfcSceneNfcVSniffStateWidget); | ||||||
|  |     view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget); | ||||||
|  |     // Start worker
 | ||||||
|  |     memset(&nfc->dev->dev_data.reader_data, 0, sizeof(NfcReaderRequestData)); | ||||||
|  |     nfc_worker_start( | ||||||
|  |         nfc->worker, | ||||||
|  |         NfcWorkerStateNfcVSniff, | ||||||
|  |         &nfc->dev->dev_data, | ||||||
|  |         nfc_scene_nfcv_sniff_worker_callback, | ||||||
|  |         nfc); | ||||||
|  | 
 | ||||||
|  |     nfc_blink_emulate_start(nfc); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool nfc_scene_nfcv_sniff_on_event(void* context, SceneManagerEvent event) { | ||||||
|  |     Nfc* nfc = context; | ||||||
|  |     NfcVData* nfcv_data = &nfc->dev->dev_data.nfcv_data; | ||||||
|  |     uint32_t state = scene_manager_get_scene_state(nfc->scene_manager, NfcSceneNfcVSniff); | ||||||
|  |     bool consumed = false; | ||||||
|  | 
 | ||||||
|  |     if(event.type == SceneManagerEventTypeCustom) { | ||||||
|  |         if(event.event == NfcCustomEventUpdateLog) { | ||||||
|  |             // Add data button to widget if data is received for the first time
 | ||||||
|  |             if(strlen(nfcv_data->last_command) > 0) { | ||||||
|  |                 if(!furi_string_size(nfc->text_box_store)) { | ||||||
|  |                     nfc_scene_nfcv_sniff_widget_config(nfc, true); | ||||||
|  |                 } | ||||||
|  |                 /* use the last n bytes from the log so there's enough space for the new log entry */ | ||||||
|  |                 size_t maxSize = | ||||||
|  |                     NFC_SCENE_EMULATE_NFCV_LOG_SIZE_MAX - (strlen(nfcv_data->last_command) + 1); | ||||||
|  |                 if(furi_string_size(nfc->text_box_store) >= maxSize) { | ||||||
|  |                     furi_string_right(nfc->text_box_store, (strlen(nfcv_data->last_command) + 1)); | ||||||
|  |                 } | ||||||
|  |                 furi_string_cat_printf(nfc->text_box_store, "%s", nfcv_data->last_command); | ||||||
|  |                 furi_string_push_back(nfc->text_box_store, '\n'); | ||||||
|  |                 text_box_set_text(nfc->text_box, furi_string_get_cstr(nfc->text_box_store)); | ||||||
|  | 
 | ||||||
|  |                 /* clear previously logged command */ | ||||||
|  |                 strcpy(nfcv_data->last_command, ""); | ||||||
|  |             } | ||||||
|  |             consumed = true; | ||||||
|  |         } else if(event.event == NfcCustomEventSaveShadow) { | ||||||
|  |             if(furi_string_size(nfc->dev->load_path)) { | ||||||
|  |                 nfc_device_save_shadow(nfc->dev, furi_string_get_cstr(nfc->dev->load_path)); | ||||||
|  |             } | ||||||
|  |             consumed = true; | ||||||
|  |         } else if(event.event == GuiButtonTypeCenter && state == NfcSceneNfcVSniffStateWidget) { | ||||||
|  |             view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewTextBox); | ||||||
|  |             scene_manager_set_scene_state( | ||||||
|  |                 nfc->scene_manager, NfcSceneNfcVSniff, NfcSceneNfcVSniffStateTextBox); | ||||||
|  |             consumed = true; | ||||||
|  |         } else if(event.event == NfcCustomEventViewExit && state == NfcSceneNfcVSniffStateTextBox) { | ||||||
|  |             view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget); | ||||||
|  |             scene_manager_set_scene_state( | ||||||
|  |                 nfc->scene_manager, NfcSceneNfcVSniff, NfcSceneNfcVSniffStateWidget); | ||||||
|  |             consumed = true; | ||||||
|  |         } | ||||||
|  |     } else if(event.type == SceneManagerEventTypeBack) { | ||||||
|  |         if(state == NfcSceneNfcVSniffStateTextBox) { | ||||||
|  |             view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget); | ||||||
|  |             scene_manager_set_scene_state( | ||||||
|  |                 nfc->scene_manager, NfcSceneNfcVSniff, NfcSceneNfcVSniffStateWidget); | ||||||
|  |             consumed = true; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return consumed; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void nfc_scene_nfcv_sniff_on_exit(void* context) { | ||||||
|  |     Nfc* nfc = context; | ||||||
|  | 
 | ||||||
|  |     // Stop worker
 | ||||||
|  |     nfc_worker_stop(nfc->worker); | ||||||
|  | 
 | ||||||
|  |     // Clear view
 | ||||||
|  |     widget_reset(nfc->widget); | ||||||
|  |     text_box_reset(nfc->text_box); | ||||||
|  |     furi_string_reset(nfc->text_box_store); | ||||||
|  | 
 | ||||||
|  |     nfc_blink_stop(nfc); | ||||||
|  | } | ||||||
							
								
								
									
										154
									
								
								applications/main/nfc/scenes/nfc_scene_nfcv_unlock.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,154 @@ | |||||||
|  | #include "../nfc_i.h" | ||||||
|  | #include <dolphin/dolphin.h> | ||||||
|  | 
 | ||||||
|  | typedef enum { | ||||||
|  |     NfcSceneNfcVUnlockStateIdle, | ||||||
|  |     NfcSceneNfcVUnlockStateDetecting, | ||||||
|  |     NfcSceneNfcVUnlockStateUnlocked, | ||||||
|  |     NfcSceneNfcVUnlockStateAlreadyUnlocked, | ||||||
|  |     NfcSceneNfcVUnlockStateNotSupportedCard, | ||||||
|  | } NfcSceneNfcVUnlockState; | ||||||
|  | 
 | ||||||
|  | static bool nfc_scene_nfcv_unlock_worker_callback(NfcWorkerEvent event, void* context) { | ||||||
|  |     Nfc* nfc = context; | ||||||
|  |     NfcVSlixData* data = &nfc->dev->dev_data.nfcv_data.sub_data.slix; | ||||||
|  | 
 | ||||||
|  |     if(event == NfcWorkerEventNfcVPassKey) { | ||||||
|  |         memcpy(data->key_privacy, nfc->byte_input_store, 4); | ||||||
|  |     } else { | ||||||
|  |         view_dispatcher_send_custom_event(nfc->view_dispatcher, event); | ||||||
|  |     } | ||||||
|  |     return true; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void nfc_scene_nfcv_unlock_popup_callback(void* context) { | ||||||
|  |     Nfc* nfc = context; | ||||||
|  |     view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventViewExit); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void nfc_scene_nfcv_unlock_set_state(Nfc* nfc, NfcSceneNfcVUnlockState state) { | ||||||
|  |     FuriHalNfcDevData* nfc_data = &(nfc->dev->dev_data.nfc_data); | ||||||
|  |     NfcVData* nfcv_data = &(nfc->dev->dev_data.nfcv_data); | ||||||
|  | 
 | ||||||
|  |     uint32_t curr_state = scene_manager_get_scene_state(nfc->scene_manager, NfcSceneNfcVUnlock); | ||||||
|  |     if(curr_state != state) { | ||||||
|  |         Popup* popup = nfc->popup; | ||||||
|  |         if(state == NfcSceneNfcVUnlockStateDetecting) { | ||||||
|  |             popup_reset(popup); | ||||||
|  |             popup_set_text( | ||||||
|  |                 popup, "Put figurine on\nFlipper's back", 97, 24, AlignCenter, AlignTop); | ||||||
|  |             popup_set_icon(popup, 0, 8, &I_NFC_manual_60x50); | ||||||
|  |         } else if(state == NfcSceneNfcVUnlockStateUnlocked) { | ||||||
|  |             popup_reset(popup); | ||||||
|  | 
 | ||||||
|  |             if(nfc_worker_get_state(nfc->worker) == NfcWorkerStateNfcVUnlockAndSave) { | ||||||
|  |                 snprintf( | ||||||
|  |                     nfc->dev->dev_name, | ||||||
|  |                     sizeof(nfc->dev->dev_name), | ||||||
|  |                     "SLIX_%02X%02X%02X%02X%02X%02X%02X%02X", | ||||||
|  |                     nfc_data->uid[0], | ||||||
|  |                     nfc_data->uid[1], | ||||||
|  |                     nfc_data->uid[2], | ||||||
|  |                     nfc_data->uid[3], | ||||||
|  |                     nfc_data->uid[4], | ||||||
|  |                     nfc_data->uid[5], | ||||||
|  |                     nfc_data->uid[6], | ||||||
|  |                     nfc_data->uid[7]); | ||||||
|  | 
 | ||||||
|  |                 nfc->dev->format = NfcDeviceSaveFormatNfcV; | ||||||
|  | 
 | ||||||
|  |                 if(nfc_save_file(nfc)) { | ||||||
|  |                     popup_set_header(popup, "Successfully\nsaved", 94, 3, AlignCenter, AlignTop); | ||||||
|  |                 } else { | ||||||
|  |                     popup_set_header( | ||||||
|  |                         popup, "Unlocked but\nsave failed!", 94, 3, AlignCenter, AlignTop); | ||||||
|  |                 } | ||||||
|  |             } else { | ||||||
|  |                 popup_set_header(popup, "Successfully\nunlocked", 94, 3, AlignCenter, AlignTop); | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             notification_message(nfc->notifications, &sequence_single_vibro); | ||||||
|  |             //notification_message(nfc->notifications, &sequence_success);
 | ||||||
|  | 
 | ||||||
|  |             popup_set_icon(popup, 0, 6, &I_RFIDDolphinSuccess_108x57); | ||||||
|  |             popup_set_context(popup, nfc); | ||||||
|  |             popup_set_callback(popup, nfc_scene_nfcv_unlock_popup_callback); | ||||||
|  |             popup_set_timeout(popup, 1500); | ||||||
|  | 
 | ||||||
|  |             view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewPopup); | ||||||
|  |             dolphin_deed(DolphinDeedNfcReadSuccess); | ||||||
|  | 
 | ||||||
|  |         } else if(state == NfcSceneNfcVUnlockStateAlreadyUnlocked) { | ||||||
|  |             popup_reset(popup); | ||||||
|  | 
 | ||||||
|  |             popup_set_header(popup, "Already\nUnlocked!", 94, 3, AlignCenter, AlignTop); | ||||||
|  |             popup_set_icon(popup, 0, 6, &I_RFIDDolphinSuccess_108x57); | ||||||
|  |             popup_set_context(popup, nfc); | ||||||
|  |             popup_set_callback(popup, nfc_scene_nfcv_unlock_popup_callback); | ||||||
|  |             popup_set_timeout(popup, 1500); | ||||||
|  | 
 | ||||||
|  |             view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewPopup); | ||||||
|  |         } else if(state == NfcSceneNfcVUnlockStateNotSupportedCard) { | ||||||
|  |             popup_reset(popup); | ||||||
|  |             popup_set_header(popup, "Wrong Type Of Card!", 64, 3, AlignCenter, AlignTop); | ||||||
|  |             popup_set_text(popup, nfcv_data->error, 4, 22, AlignLeft, AlignTop); | ||||||
|  |             popup_set_icon(popup, 73, 20, &I_DolphinCommon_56x48); | ||||||
|  |         } | ||||||
|  |         scene_manager_set_scene_state(nfc->scene_manager, NfcSceneNfcVUnlock, state); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void nfc_scene_nfcv_unlock_on_enter(void* context) { | ||||||
|  |     Nfc* nfc = context; | ||||||
|  | 
 | ||||||
|  |     nfc_device_clear(nfc->dev); | ||||||
|  |     // Setup view
 | ||||||
|  |     nfc_scene_nfcv_unlock_set_state(nfc, NfcSceneNfcVUnlockStateDetecting); | ||||||
|  |     view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewPopup); | ||||||
|  | 
 | ||||||
|  |     // Start worker
 | ||||||
|  |     nfc_worker_start( | ||||||
|  |         nfc->worker, | ||||||
|  |         NfcWorkerStateNfcVUnlockAndSave, | ||||||
|  |         &nfc->dev->dev_data, | ||||||
|  |         nfc_scene_nfcv_unlock_worker_callback, | ||||||
|  |         nfc); | ||||||
|  | 
 | ||||||
|  |     nfc_blink_read_start(nfc); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool nfc_scene_nfcv_unlock_on_event(void* context, SceneManagerEvent event) { | ||||||
|  |     Nfc* nfc = context; | ||||||
|  |     bool consumed = false; | ||||||
|  | 
 | ||||||
|  |     if(event.type == SceneManagerEventTypeCustom) { | ||||||
|  |         if(event.event == NfcWorkerEventCardDetected) { | ||||||
|  |             nfc_scene_nfcv_unlock_set_state(nfc, NfcSceneNfcVUnlockStateUnlocked); | ||||||
|  |             consumed = true; | ||||||
|  |         } else if(event.event == NfcWorkerEventAborted) { | ||||||
|  |             nfc_scene_nfcv_unlock_set_state(nfc, NfcSceneNfcVUnlockStateAlreadyUnlocked); | ||||||
|  |             consumed = true; | ||||||
|  |         } else if(event.event == NfcWorkerEventNoCardDetected) { | ||||||
|  |             nfc_scene_nfcv_unlock_set_state(nfc, NfcSceneNfcVUnlockStateDetecting); | ||||||
|  |             consumed = true; | ||||||
|  |         } else if(event.event == NfcWorkerEventWrongCardDetected) { | ||||||
|  |             nfc_scene_nfcv_unlock_set_state(nfc, NfcSceneNfcVUnlockStateNotSupportedCard); | ||||||
|  |         } | ||||||
|  |     } else if(event.type == SceneManagerEventTypeBack) { | ||||||
|  |         consumed = scene_manager_search_and_switch_to_previous_scene( | ||||||
|  |             nfc->scene_manager, NfcSceneNfcVUnlockMenu); | ||||||
|  |     } | ||||||
|  |     return consumed; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void nfc_scene_nfcv_unlock_on_exit(void* context) { | ||||||
|  |     Nfc* nfc = context; | ||||||
|  | 
 | ||||||
|  |     // Stop worker
 | ||||||
|  |     nfc_worker_stop(nfc->worker); | ||||||
|  |     // Clear view
 | ||||||
|  |     popup_reset(nfc->popup); | ||||||
|  |     nfc_blink_stop(nfc); | ||||||
|  |     scene_manager_set_scene_state( | ||||||
|  |         nfc->scene_manager, NfcSceneNfcVUnlock, NfcSceneNfcVUnlockStateIdle); | ||||||
|  | } | ||||||
							
								
								
									
										60
									
								
								applications/main/nfc/scenes/nfc_scene_nfcv_unlock_menu.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,60 @@ | |||||||
|  | #include "../nfc_i.h" | ||||||
|  | #include <dolphin/dolphin.h> | ||||||
|  | 
 | ||||||
|  | enum SubmenuIndex { | ||||||
|  |     SubmenuIndexNfcVUnlockMenuManual, | ||||||
|  |     SubmenuIndexNfcVUnlockMenuTonieBox, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | void nfc_scene_nfcv_unlock_menu_submenu_callback(void* context, uint32_t index) { | ||||||
|  |     Nfc* nfc = context; | ||||||
|  | 
 | ||||||
|  |     view_dispatcher_send_custom_event(nfc->view_dispatcher, index); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void nfc_scene_nfcv_unlock_menu_on_enter(void* context) { | ||||||
|  |     Nfc* nfc = context; | ||||||
|  |     Submenu* submenu = nfc->submenu; | ||||||
|  | 
 | ||||||
|  |     uint32_t state = scene_manager_get_scene_state(nfc->scene_manager, NfcSceneNfcVUnlockMenu); | ||||||
|  |     submenu_add_item( | ||||||
|  |         submenu, | ||||||
|  |         "Enter PWD Manually", | ||||||
|  |         SubmenuIndexNfcVUnlockMenuManual, | ||||||
|  |         nfc_scene_nfcv_unlock_menu_submenu_callback, | ||||||
|  |         nfc); | ||||||
|  |     submenu_add_item( | ||||||
|  |         submenu, | ||||||
|  |         "Auth As TonieBox", | ||||||
|  |         SubmenuIndexNfcVUnlockMenuTonieBox, | ||||||
|  |         nfc_scene_nfcv_unlock_menu_submenu_callback, | ||||||
|  |         nfc); | ||||||
|  |     submenu_set_selected_item(submenu, state); | ||||||
|  |     view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewMenu); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool nfc_scene_nfcv_unlock_menu_on_event(void* context, SceneManagerEvent event) { | ||||||
|  |     Nfc* nfc = context; | ||||||
|  |     bool consumed = false; | ||||||
|  | 
 | ||||||
|  |     if(event.type == SceneManagerEventTypeCustom) { | ||||||
|  |         if(event.event == SubmenuIndexNfcVUnlockMenuManual) { | ||||||
|  |             nfc->dev->dev_data.nfcv_data.auth_method = NfcVAuthMethodManual; | ||||||
|  |             scene_manager_next_scene(nfc->scene_manager, NfcSceneNfcVKeyInput); | ||||||
|  |             consumed = true; | ||||||
|  |         } else if(event.event == SubmenuIndexNfcVUnlockMenuTonieBox) { | ||||||
|  |             nfc->dev->dev_data.nfcv_data.auth_method = NfcVAuthMethodTonieBox; | ||||||
|  |             scene_manager_next_scene(nfc->scene_manager, NfcSceneNfcVUnlock); | ||||||
|  |             dolphin_deed(DolphinDeedNfcRead); | ||||||
|  |             consumed = true; | ||||||
|  |         } | ||||||
|  |         scene_manager_set_scene_state(nfc->scene_manager, NfcSceneNfcVUnlockMenu, event.event); | ||||||
|  |     } | ||||||
|  |     return consumed; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void nfc_scene_nfcv_unlock_menu_on_exit(void* context) { | ||||||
|  |     Nfc* nfc = context; | ||||||
|  | 
 | ||||||
|  |     submenu_reset(nfc->submenu); | ||||||
|  | } | ||||||
| @ -61,29 +61,34 @@ bool nfc_scene_read_on_event(void* context, SceneManagerEvent event) { | |||||||
|            (event.event == NfcWorkerEventReadUidNfcV)) { |            (event.event == NfcWorkerEventReadUidNfcV)) { | ||||||
|             notification_message(nfc->notifications, &sequence_success); |             notification_message(nfc->notifications, &sequence_success); | ||||||
|             scene_manager_next_scene(nfc->scene_manager, NfcSceneReadCardSuccess); |             scene_manager_next_scene(nfc->scene_manager, NfcSceneReadCardSuccess); | ||||||
|             DOLPHIN_DEED(DolphinDeedNfcReadSuccess); |             dolphin_deed(DolphinDeedNfcReadSuccess); | ||||||
|             consumed = true; |             consumed = true; | ||||||
|         } else if(event.event == NfcWorkerEventReadUidNfcA) { |         } else if(event.event == NfcWorkerEventReadUidNfcA) { | ||||||
|             notification_message(nfc->notifications, &sequence_success); |             notification_message(nfc->notifications, &sequence_success); | ||||||
|             scene_manager_next_scene(nfc->scene_manager, NfcSceneNfcaReadSuccess); |             scene_manager_next_scene(nfc->scene_manager, NfcSceneNfcaReadSuccess); | ||||||
|             DOLPHIN_DEED(DolphinDeedNfcReadSuccess); |             dolphin_deed(DolphinDeedNfcReadSuccess); | ||||||
|  |             consumed = true; | ||||||
|  |         } else if(event.event == NfcWorkerEventReadNfcV) { | ||||||
|  |             notification_message(nfc->notifications, &sequence_success); | ||||||
|  |             scene_manager_next_scene(nfc->scene_manager, NfcSceneNfcVReadSuccess); | ||||||
|  |             dolphin_deed(DolphinDeedNfcReadSuccess); | ||||||
|             consumed = true; |             consumed = true; | ||||||
|         } else if(event.event == NfcWorkerEventReadMfUltralight) { |         } else if(event.event == NfcWorkerEventReadMfUltralight) { | ||||||
|             notification_message(nfc->notifications, &sequence_success); |             notification_message(nfc->notifications, &sequence_success); | ||||||
|             // Set unlock password input to 0xFFFFFFFF only on fresh read
 |             // Set unlock password input to 0xFFFFFFFF only on fresh read
 | ||||||
|             memset(nfc->byte_input_store, 0xFF, sizeof(nfc->byte_input_store)); |             memset(nfc->byte_input_store, 0xFF, sizeof(nfc->byte_input_store)); | ||||||
|             scene_manager_next_scene(nfc->scene_manager, NfcSceneMfUltralightReadSuccess); |             scene_manager_next_scene(nfc->scene_manager, NfcSceneMfUltralightReadSuccess); | ||||||
|             DOLPHIN_DEED(DolphinDeedNfcReadSuccess); |             dolphin_deed(DolphinDeedNfcReadSuccess); | ||||||
|             consumed = true; |             consumed = true; | ||||||
|         } else if(event.event == NfcWorkerEventReadMfClassicDone) { |         } else if(event.event == NfcWorkerEventReadMfClassicDone) { | ||||||
|             notification_message(nfc->notifications, &sequence_success); |             notification_message(nfc->notifications, &sequence_success); | ||||||
|             scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicReadSuccess); |             scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicReadSuccess); | ||||||
|             DOLPHIN_DEED(DolphinDeedNfcReadSuccess); |             dolphin_deed(DolphinDeedNfcReadSuccess); | ||||||
|             consumed = true; |             consumed = true; | ||||||
|         } else if(event.event == NfcWorkerEventReadMfDesfire) { |         } else if(event.event == NfcWorkerEventReadMfDesfire) { | ||||||
|             notification_message(nfc->notifications, &sequence_success); |             notification_message(nfc->notifications, &sequence_success); | ||||||
|             scene_manager_next_scene(nfc->scene_manager, NfcSceneMfDesfireReadSuccess); |             scene_manager_next_scene(nfc->scene_manager, NfcSceneMfDesfireReadSuccess); | ||||||
|             DOLPHIN_DEED(DolphinDeedNfcReadSuccess); |             dolphin_deed(DolphinDeedNfcReadSuccess); | ||||||
|             consumed = true; |             consumed = true; | ||||||
|         } else if(event.event == NfcWorkerEventReadMfClassicDictAttackRequired) { |         } else if(event.event == NfcWorkerEventReadMfClassicDictAttackRequired) { | ||||||
|             if(mf_classic_dict_check_presence(MfClassicDictTypeSystem)) { |             if(mf_classic_dict_check_presence(MfClassicDictTypeSystem)) { | ||||||
|  | |||||||
| @ -11,7 +11,7 @@ void nfc_scene_restore_original_confirm_on_enter(void* context) { | |||||||
|     DialogEx* dialog_ex = nfc->dialog_ex; |     DialogEx* dialog_ex = nfc->dialog_ex; | ||||||
| 
 | 
 | ||||||
|     dialog_ex_set_header(dialog_ex, "Restore Card Data?", 64, 0, AlignCenter, AlignTop); |     dialog_ex_set_header(dialog_ex, "Restore Card Data?", 64, 0, AlignCenter, AlignTop); | ||||||
|     dialog_ex_set_icon(dialog_ex, 5, 15, &I_Restoring_38x32); |     dialog_ex_set_icon(dialog_ex, 5, 11, &I_ArrowC_1_36x36); | ||||||
|     dialog_ex_set_text( |     dialog_ex_set_text( | ||||||
|         dialog_ex, "It will be returned\nto its original state.", 47, 21, AlignLeft, AlignTop); |         dialog_ex, "It will be returned\nto its original state.", 47, 21, AlignLeft, AlignTop); | ||||||
|     dialog_ex_set_left_button_text(dialog_ex, "Cancel"); |     dialog_ex_set_left_button_text(dialog_ex, "Cancel"); | ||||||
|  | |||||||
| @ -55,6 +55,13 @@ bool nfc_scene_rpc_on_event(void* context, SceneManagerEvent event) { | |||||||
|                             &nfc->dev->dev_data, |                             &nfc->dev->dev_data, | ||||||
|                             nfc_scene_rpc_emulate_callback, |                             nfc_scene_rpc_emulate_callback, | ||||||
|                             nfc); |                             nfc); | ||||||
|  |                     } else if(nfc->dev->format == NfcDeviceSaveFormatNfcV) { | ||||||
|  |                         nfc_worker_start( | ||||||
|  |                             nfc->worker, | ||||||
|  |                             NfcWorkerStateNfcVEmulate, | ||||||
|  |                             &nfc->dev->dev_data, | ||||||
|  |                             nfc_scene_rpc_emulate_callback, | ||||||
|  |                             nfc); | ||||||
|                     } else { |                     } else { | ||||||
|                         nfc_worker_start( |                         nfc_worker_start( | ||||||
|                             nfc->worker, NfcWorkerStateUidEmulate, &nfc->dev->dev_data, NULL, nfc); |                             nfc->worker, NfcWorkerStateUidEmulate, &nfc->dev->dev_data, NULL, nfc); | ||||||
|  | |||||||
| @ -67,9 +67,9 @@ bool nfc_scene_save_name_on_event(void* context, SceneManagerEvent event) { | |||||||
|                 if(!scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneSavedMenu)) { |                 if(!scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneSavedMenu)) { | ||||||
|                     // Nothing, do not count editing as saving
 |                     // Nothing, do not count editing as saving
 | ||||||
|                 } else if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneSetType)) { |                 } else if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneSetType)) { | ||||||
|                     DOLPHIN_DEED(DolphinDeedNfcAddSave); |                     dolphin_deed(DolphinDeedNfcAddSave); | ||||||
|                 } else { |                 } else { | ||||||
|                     DOLPHIN_DEED(DolphinDeedNfcSave); |                     dolphin_deed(DolphinDeedNfcSave); | ||||||
|                 } |                 } | ||||||
|                 consumed = true; |                 consumed = true; | ||||||
|             } else { |             } else { | ||||||
|  | |||||||
| @ -44,6 +44,7 @@ void nfc_scene_saved_menu_on_enter(void* context) { | |||||||
|     } else if( |     } else if( | ||||||
|         (nfc->dev->format == NfcDeviceSaveFormatMifareUl && |         (nfc->dev->format == NfcDeviceSaveFormatMifareUl && | ||||||
|          mf_ul_emulation_supported(&nfc->dev->dev_data.mf_ul_data)) || |          mf_ul_emulation_supported(&nfc->dev->dev_data.mf_ul_data)) || | ||||||
|  |         nfc->dev->format == NfcDeviceSaveFormatNfcV || | ||||||
|         nfc->dev->format == NfcDeviceSaveFormatMifareClassic) { |         nfc->dev->format == NfcDeviceSaveFormatMifareClassic) { | ||||||
|         submenu_add_item( |         submenu_add_item( | ||||||
|             submenu, "Emulate", SubmenuIndexEmulate, nfc_scene_saved_menu_submenu_callback, nfc); |             submenu, "Emulate", SubmenuIndexEmulate, nfc_scene_saved_menu_submenu_callback, nfc); | ||||||
| @ -118,14 +119,16 @@ bool nfc_scene_saved_menu_on_event(void* context, SceneManagerEvent event) { | |||||||
|                 scene_manager_next_scene(nfc->scene_manager, NfcSceneMfUltralightEmulate); |                 scene_manager_next_scene(nfc->scene_manager, NfcSceneMfUltralightEmulate); | ||||||
|             } else if(nfc->dev->format == NfcDeviceSaveFormatMifareClassic) { |             } else if(nfc->dev->format == NfcDeviceSaveFormatMifareClassic) { | ||||||
|                 scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicEmulate); |                 scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicEmulate); | ||||||
|  |             } else if(nfc->dev->format == NfcDeviceSaveFormatNfcV) { | ||||||
|  |                 scene_manager_next_scene(nfc->scene_manager, NfcSceneNfcVEmulate); | ||||||
|             } else { |             } else { | ||||||
|                 scene_manager_next_scene(nfc->scene_manager, NfcSceneEmulateUid); |                 scene_manager_next_scene(nfc->scene_manager, NfcSceneEmulateUid); | ||||||
|             } |             } | ||||||
|             DOLPHIN_DEED(DolphinDeedNfcEmulate); |             dolphin_deed(DolphinDeedNfcEmulate); | ||||||
|             consumed = true; |             consumed = true; | ||||||
|         } else if(event.event == SubmenuIndexDetectReader) { |         } else if(event.event == SubmenuIndexDetectReader) { | ||||||
|             scene_manager_next_scene(nfc->scene_manager, NfcSceneDetectReader); |             scene_manager_next_scene(nfc->scene_manager, NfcSceneDetectReader); | ||||||
|             DOLPHIN_DEED(DolphinDeedNfcDetectReader); |             dolphin_deed(DolphinDeedNfcDetectReader); | ||||||
|             consumed = true; |             consumed = true; | ||||||
|         } else if(event.event == SubmenuIndexWrite) { |         } else if(event.event == SubmenuIndexWrite) { | ||||||
|             scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicWrite); |             scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicWrite); | ||||||
|  | |||||||
| @ -51,7 +51,7 @@ bool nfc_scene_start_on_event(void* context, SceneManagerEvent event) { | |||||||
|             scene_manager_set_scene_state(nfc->scene_manager, NfcSceneStart, SubmenuIndexRead); |             scene_manager_set_scene_state(nfc->scene_manager, NfcSceneStart, SubmenuIndexRead); | ||||||
|             nfc->dev->dev_data.read_mode = NfcReadModeAuto; |             nfc->dev->dev_data.read_mode = NfcReadModeAuto; | ||||||
|             scene_manager_next_scene(nfc->scene_manager, NfcSceneRead); |             scene_manager_next_scene(nfc->scene_manager, NfcSceneRead); | ||||||
|             DOLPHIN_DEED(DolphinDeedNfcRead); |             dolphin_deed(DolphinDeedNfcRead); | ||||||
|             consumed = true; |             consumed = true; | ||||||
|         } else if(event.event == SubmenuIndexDetectReader) { |         } else if(event.event == SubmenuIndexDetectReader) { | ||||||
|             scene_manager_set_scene_state( |             scene_manager_set_scene_state( | ||||||
| @ -60,7 +60,7 @@ bool nfc_scene_start_on_event(void* context, SceneManagerEvent event) { | |||||||
|             if(sd_exist) { |             if(sd_exist) { | ||||||
|                 nfc_device_data_clear(&nfc->dev->dev_data); |                 nfc_device_data_clear(&nfc->dev->dev_data); | ||||||
|                 scene_manager_next_scene(nfc->scene_manager, NfcSceneDetectReader); |                 scene_manager_next_scene(nfc->scene_manager, NfcSceneDetectReader); | ||||||
|                 DOLPHIN_DEED(DolphinDeedNfcDetectReader); |                 dolphin_deed(DolphinDeedNfcDetectReader); | ||||||
|             } else { |             } else { | ||||||
|                 scene_manager_next_scene(nfc->scene_manager, NfcSceneDictNotFound); |                 scene_manager_next_scene(nfc->scene_manager, NfcSceneDictNotFound); | ||||||
|             } |             } | ||||||
|  | |||||||
| @ -204,7 +204,7 @@ bool subghz_scene_read_raw_on_event(void* context, SceneManagerEvent event) { | |||||||
|                 } else { |                 } else { | ||||||
|                     if(scene_manager_has_previous_scene(subghz->scene_manager, SubGhzSceneSaved) || |                     if(scene_manager_has_previous_scene(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_txrx_set_raw_file_encoder_worker_callback_end( |                     subghz_txrx_set_raw_file_encoder_worker_callback_end( | ||||||
| @ -259,7 +259,7 @@ bool subghz_scene_read_raw_on_event(void* context, SceneManagerEvent event) { | |||||||
|             } else { |             } else { | ||||||
|                 SubGhzRadioPreset preset = subghz_txrx_get_preset(subghz->txrx); |                 SubGhzRadioPreset preset = subghz_txrx_get_preset(subghz->txrx); | ||||||
|                 if(subghz_protocol_raw_save_to_file_init(decoder_raw, RAW_FILE_NAME, &preset)) { |                 if(subghz_protocol_raw_save_to_file_init(decoder_raw, RAW_FILE_NAME, &preset)) { | ||||||
|                     DOLPHIN_DEED(DolphinDeedSubGhzRawRec); |                     dolphin_deed(DolphinDeedSubGhzRawRec); | ||||||
|                     subghz_txrx_rx_start(subghz->txrx); |                     subghz_txrx_rx_start(subghz->txrx); | ||||||
|                     subghz->state_notifications = SubGhzNotificationStateRx; |                     subghz->state_notifications = SubGhzNotificationStateRx; | ||||||
|                     subghz_rx_key_state_set(subghz, SubGhzRxKeyStateAddKey); |                     subghz_rx_key_state_set(subghz, SubGhzRxKeyStateAddKey); | ||||||
|  | |||||||
| @ -163,7 +163,7 @@ bool subghz_scene_receiver_on_event(void* context, SceneManagerEvent event) { | |||||||
|         case SubGhzCustomEventViewReceiverOK: |         case SubGhzCustomEventViewReceiverOK: | ||||||
|             subghz->idx_menu_chosen = subghz_view_receiver_get_idx_menu(subghz->subghz_receiver); |             subghz->idx_menu_chosen = 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: | ||||||
|  | |||||||
| @ -137,9 +137,9 @@ bool subghz_scene_save_name_on_event(void* context, SceneManagerEvent event) { | |||||||
|                     // Ditto, for RAW signals
 |                     // Ditto, for RAW signals
 | ||||||
|                 } else if(scene_manager_has_previous_scene( |                 } else if(scene_manager_has_previous_scene( | ||||||
|                               subghz->scene_manager, SubGhzSceneSetType)) { |                               subghz->scene_manager, SubGhzSceneSetType)) { | ||||||
|                     DOLPHIN_DEED(DolphinDeedSubGhzAddManually); |                     dolphin_deed(DolphinDeedSubGhzAddManually); | ||||||
|                 } else { |                 } else { | ||||||
|                     DOLPHIN_DEED(DolphinDeedSubGhzSave); |                     dolphin_deed(DolphinDeedSubGhzSave); | ||||||
|                 } |                 } | ||||||
|                 return true; |                 return true; | ||||||
|             } else { |             } else { | ||||||
|  | |||||||
| @ -92,7 +92,7 @@ bool subghz_scene_start_on_event(void* context, SceneManagerEvent event) { | |||||||
|             scene_manager_set_scene_state( |             scene_manager_set_scene_state( | ||||||
|                 subghz->scene_manager, SubGhzSceneStart, SubmenuIndexFrequencyAnalyzer); |                 subghz->scene_manager, SubGhzSceneStart, SubmenuIndexFrequencyAnalyzer); | ||||||
|             scene_manager_next_scene(subghz->scene_manager, SubGhzSceneFrequencyAnalyzer); |             scene_manager_next_scene(subghz->scene_manager, SubGhzSceneFrequencyAnalyzer); | ||||||
|             DOLPHIN_DEED(DolphinDeedSubGhzFrequencyAnalyzer); |             dolphin_deed(DolphinDeedSubGhzFrequencyAnalyzer); | ||||||
|             return true; |             return true; | ||||||
|         } else if(event.event == SubmenuIndexTest) { |         } else if(event.event == SubmenuIndexTest) { | ||||||
|             scene_manager_set_scene_state( |             scene_manager_set_scene_state( | ||||||
|  | |||||||
| @ -61,7 +61,7 @@ bool subghz_scene_transmitter_on_event(void* context, SceneManagerEvent event) { | |||||||
|             if(subghz_tx_start(subghz, subghz_txrx_get_fff_data(subghz->txrx))) { |             if(subghz_tx_start(subghz, subghz_txrx_get_fff_data(subghz->txrx))) { | ||||||
|                 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) { | ||||||
|  | |||||||
| @ -68,7 +68,7 @@ bool u2f_scene_main_on_event(void* context, SceneManagerEvent event) { | |||||||
|             notification_message(app->notifications, &sequence_blink_magenta_10); |             notification_message(app->notifications, &sequence_blink_magenta_10); | ||||||
|         } else if(event.event == U2fCustomEventAuthSuccess) { |         } else if(event.event == U2fCustomEventAuthSuccess) { | ||||||
|             notification_message_block(app->notifications, &sequence_set_green_255); |             notification_message_block(app->notifications, &sequence_set_green_255); | ||||||
|             DOLPHIN_DEED(DolphinDeedU2fAuthorized); |             dolphin_deed(DolphinDeedU2fAuthorized); | ||||||
|             furi_timer_start(app->timer, U2F_SUCCESS_TIMEOUT); |             furi_timer_start(app->timer, U2F_SUCCESS_TIMEOUT); | ||||||
|             app->event_cur = U2fCustomEventNone; |             app->event_cur = U2fCustomEventNone; | ||||||
|             u2f_view_set_state(app->u2f_view, U2fMsgSuccess); |             u2f_view_set_state(app->u2f_view, U2fMsgSuccess); | ||||||
|  | |||||||
| @ -1,7 +1,7 @@ | |||||||
| #include "bt_i.h" | #include "bt_i.h" | ||||||
| #include "battery_service.h" |  | ||||||
| #include "bt_keys_storage.h" | #include "bt_keys_storage.h" | ||||||
| 
 | 
 | ||||||
|  | #include <services/battery_service.h> | ||||||
| #include <notification/notification_messages.h> | #include <notification/notification_messages.h> | ||||||
| #include <gui/elements.h> | #include <gui/elements.h> | ||||||
| #include <assets_icons.h> | #include <assets_icons.h> | ||||||
|  | |||||||
| @ -34,13 +34,13 @@ bool desktop_scene_debug_on_event(void* context, SceneManagerEvent event) { | |||||||
|             break; |             break; | ||||||
| 
 | 
 | ||||||
|         case DesktopDebugEventDeed: |         case DesktopDebugEventDeed: | ||||||
|             dolphin_deed(dolphin, DolphinDeedTestRight); |             dolphin_deed(DolphinDeedTestRight); | ||||||
|             desktop_debug_get_dolphin_data(desktop->debug_view); |             desktop_debug_get_dolphin_data(desktop->debug_view); | ||||||
|             consumed = true; |             consumed = true; | ||||||
|             break; |             break; | ||||||
| 
 | 
 | ||||||
|         case DesktopDebugEventWrongDeed: |         case DesktopDebugEventWrongDeed: | ||||||
|             dolphin_deed(dolphin, DolphinDeedTestLeft); |             dolphin_deed(DolphinDeedTestLeft); | ||||||
|             desktop_debug_get_dolphin_data(desktop->debug_view); |             desktop_debug_get_dolphin_data(desktop->debug_view); | ||||||
|             consumed = true; |             consumed = true; | ||||||
|             break; |             break; | ||||||
|  | |||||||
| @ -13,12 +13,13 @@ | |||||||
| 
 | 
 | ||||||
| static void dolphin_update_clear_limits_timer_period(Dolphin* dolphin); | static void dolphin_update_clear_limits_timer_period(Dolphin* dolphin); | ||||||
| 
 | 
 | ||||||
| void dolphin_deed(Dolphin* dolphin, DolphinDeed deed) { | void dolphin_deed(DolphinDeed deed) { | ||||||
|     furi_assert(dolphin); |     Dolphin* dolphin = (Dolphin*)furi_record_open(RECORD_DOLPHIN); | ||||||
|     DolphinEvent event; |     DolphinEvent event; | ||||||
|     event.type = DolphinEventTypeDeed; |     event.type = DolphinEventTypeDeed; | ||||||
|     event.deed = deed; |     event.deed = deed; | ||||||
|     dolphin_event_send_async(dolphin, &event); |     dolphin_event_send_async(dolphin, &event); | ||||||
|  |     furi_record_close(RECORD_DOLPHIN); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| DolphinStats dolphin_stats(Dolphin* dolphin) { | DolphinStats dolphin_stats(Dolphin* dolphin) { | ||||||
|  | |||||||
| @ -26,18 +26,11 @@ typedef enum { | |||||||
|     DolphinPubsubEventUpdate, |     DolphinPubsubEventUpdate, | ||||||
| } DolphinPubsubEvent; | } DolphinPubsubEvent; | ||||||
| 
 | 
 | ||||||
| #define DOLPHIN_DEED(deed)                                        \ |  | ||||||
|     do {                                                          \ |  | ||||||
|         Dolphin* dolphin = (Dolphin*)furi_record_open("dolphin"); \ |  | ||||||
|         dolphin_deed(dolphin, deed);                              \ |  | ||||||
|         furi_record_close("dolphin");                             \ |  | ||||||
|     } while(0) |  | ||||||
| 
 |  | ||||||
| /** Deed complete notification. Call it on deed completion.
 | /** Deed complete notification. Call it on deed completion.
 | ||||||
|  * See dolphin_deed.h for available deeds. In futures it will become part of assets. |  * See dolphin_deed.h for available deeds. In futures it will become part of assets. | ||||||
|  * Thread safe, async |  * Thread safe, async | ||||||
|  */ |  */ | ||||||
| void dolphin_deed(Dolphin* dolphin, DolphinDeed deed); | void dolphin_deed(DolphinDeed deed); | ||||||
| 
 | 
 | ||||||
| /** Retrieve dolphin stats
 | /** Retrieve dolphin stats
 | ||||||
|  * Thread safe, blocking |  * Thread safe, blocking | ||||||
|  | |||||||
| @ -75,6 +75,8 @@ typedef enum { | |||||||
|     NotificationMessageTypeForceDisplayBrightnessSetting, |     NotificationMessageTypeForceDisplayBrightnessSetting, | ||||||
| 
 | 
 | ||||||
|     NotificationMessageTypeLedBrightnessSettingApply, |     NotificationMessageTypeLedBrightnessSettingApply, | ||||||
|  | 
 | ||||||
|  |     NotificationMessageTypeLcdContrastUpdate, | ||||||
| } NotificationMessageType; | } NotificationMessageType; | ||||||
| 
 | 
 | ||||||
| typedef struct { | typedef struct { | ||||||
|  | |||||||
| @ -3,6 +3,9 @@ | |||||||
| #include <furi_hal.h> | #include <furi_hal.h> | ||||||
| #include <storage/storage.h> | #include <storage/storage.h> | ||||||
| #include <input/input.h> | #include <input/input.h> | ||||||
|  | #include <gui/gui_i.h> | ||||||
|  | #include <u8g2_glue.h> | ||||||
|  | 
 | ||||||
| #include "notification.h" | #include "notification.h" | ||||||
| #include "notification_messages.h" | #include "notification_messages.h" | ||||||
| #include "notification_app.h" | #include "notification_app.h" | ||||||
| @ -20,14 +23,14 @@ static const uint8_t reset_sound_mask = 1 << 4; | |||||||
| static const uint8_t reset_display_mask = 1 << 5; | static const uint8_t reset_display_mask = 1 << 5; | ||||||
| static const uint8_t reset_blink_mask = 1 << 6; | static const uint8_t reset_blink_mask = 1 << 6; | ||||||
| 
 | 
 | ||||||
| void notification_vibro_on(bool force); | static void notification_vibro_on(bool force); | ||||||
| void notification_vibro_off(); | static void notification_vibro_off(); | ||||||
| void notification_sound_on(float freq, float volume, bool force); | static void notification_sound_on(float freq, float volume, bool force); | ||||||
| void notification_sound_off(); | static void notification_sound_off(); | ||||||
| 
 | 
 | ||||||
| uint8_t notification_settings_get_display_brightness(NotificationApp* app, uint8_t value); | static uint8_t notification_settings_get_display_brightness(NotificationApp* app, uint8_t value); | ||||||
| uint8_t notification_settings_get_rgb_led_brightness(NotificationApp* app, uint8_t value); | static uint8_t notification_settings_get_rgb_led_brightness(NotificationApp* app, uint8_t value); | ||||||
| uint32_t notification_settings_display_off_delay_ticks(NotificationApp* app); | static uint32_t notification_settings_display_off_delay_ticks(NotificationApp* app); | ||||||
| 
 | 
 | ||||||
| void notification_message_save_settings(NotificationApp* app) { | void notification_message_save_settings(NotificationApp* app) { | ||||||
|     NotificationAppMessage m = { |     NotificationAppMessage m = { | ||||||
| @ -39,7 +42,8 @@ void notification_message_save_settings(NotificationApp* app) { | |||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| // internal layer
 | // internal layer
 | ||||||
| void notification_apply_internal_led_layer(NotificationLedLayer* layer, uint8_t layer_value) { | static void | ||||||
|  |     notification_apply_internal_led_layer(NotificationLedLayer* layer, uint8_t layer_value) { | ||||||
|     furi_assert(layer); |     furi_assert(layer); | ||||||
|     furi_assert(layer->index < LayerMAX); |     furi_assert(layer->index < LayerMAX); | ||||||
| 
 | 
 | ||||||
| @ -52,7 +56,13 @@ void notification_apply_internal_led_layer(NotificationLedLayer* layer, uint8_t | |||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| bool notification_is_any_led_layer_internal_and_not_empty(NotificationApp* app) { | static void notification_apply_lcd_contrast(NotificationApp* app) { | ||||||
|  |     Gui* gui = furi_record_open(RECORD_GUI); | ||||||
|  |     u8x8_d_st756x_set_contrast(&gui->canvas->fb.u8x8, app->settings.contrast); | ||||||
|  |     furi_record_close(RECORD_GUI); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static bool notification_is_any_led_layer_internal_and_not_empty(NotificationApp* app) { | ||||||
|     bool result = false; |     bool result = false; | ||||||
|     if((app->led[0].index == LayerInternal) || (app->led[1].index == LayerInternal) || |     if((app->led[0].index == LayerInternal) || (app->led[1].index == LayerInternal) || | ||||||
|        (app->led[2].index == LayerInternal)) { |        (app->led[2].index == LayerInternal)) { | ||||||
| @ -67,7 +77,7 @@ bool notification_is_any_led_layer_internal_and_not_empty(NotificationApp* app) | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // notification layer
 | // notification layer
 | ||||||
| void notification_apply_notification_led_layer( | static void notification_apply_notification_led_layer( | ||||||
|     NotificationLedLayer* layer, |     NotificationLedLayer* layer, | ||||||
|     const uint8_t layer_value) { |     const uint8_t layer_value) { | ||||||
|     furi_assert(layer); |     furi_assert(layer); | ||||||
| @ -81,7 +91,7 @@ void notification_apply_notification_led_layer( | |||||||
|     furi_hal_light_set(layer->light, layer->value[LayerNotification]); |     furi_hal_light_set(layer->light, layer->value[LayerNotification]); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void notification_reset_notification_led_layer(NotificationLedLayer* layer) { | static void notification_reset_notification_led_layer(NotificationLedLayer* layer) { | ||||||
|     furi_assert(layer); |     furi_assert(layer); | ||||||
|     furi_assert(layer->index < LayerMAX); |     furi_assert(layer->index < LayerMAX); | ||||||
| 
 | 
 | ||||||
| @ -94,7 +104,7 @@ void notification_reset_notification_led_layer(NotificationLedLayer* layer) { | |||||||
|     furi_hal_light_set(layer->light, layer->value[LayerInternal]); |     furi_hal_light_set(layer->light, layer->value[LayerInternal]); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void notification_reset_notification_layer(NotificationApp* app, uint8_t reset_mask) { | static void notification_reset_notification_layer(NotificationApp* app, uint8_t reset_mask) { | ||||||
|     if(reset_mask & reset_blink_mask) { |     if(reset_mask & reset_blink_mask) { | ||||||
|         furi_hal_light_blink_stop(); |         furi_hal_light_blink_stop(); | ||||||
|     } |     } | ||||||
| @ -130,28 +140,28 @@ uint8_t notification_settings_get_display_brightness(NotificationApp* app, uint8 | |||||||
|     return (value * app->settings.display_brightness); |     return (value * app->settings.display_brightness); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| uint8_t notification_settings_get_rgb_led_brightness(NotificationApp* app, uint8_t value) { | static uint8_t notification_settings_get_rgb_led_brightness(NotificationApp* app, uint8_t value) { | ||||||
|     return (value * app->settings.led_brightness); |     return (value * app->settings.led_brightness); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| uint32_t notification_settings_display_off_delay_ticks(NotificationApp* app) { | static uint32_t notification_settings_display_off_delay_ticks(NotificationApp* app) { | ||||||
|     return ( |     return ( | ||||||
|         (float)(app->settings.display_off_delay_ms) / |         (float)(app->settings.display_off_delay_ms) / | ||||||
|         (1000.0f / furi_kernel_get_tick_frequency())); |         (1000.0f / furi_kernel_get_tick_frequency())); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // generics
 | // generics
 | ||||||
| void notification_vibro_on(bool force) { | static void notification_vibro_on(bool force) { | ||||||
|     if(!furi_hal_rtc_is_flag_set(FuriHalRtcFlagStealthMode) || force) { |     if(!furi_hal_rtc_is_flag_set(FuriHalRtcFlagStealthMode) || force) { | ||||||
|         furi_hal_vibro_on(true); |         furi_hal_vibro_on(true); | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void notification_vibro_off() { | static void notification_vibro_off() { | ||||||
|     furi_hal_vibro_on(false); |     furi_hal_vibro_on(false); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void notification_sound_on(float freq, float volume, bool force) { | static void notification_sound_on(float freq, float volume, bool force) { | ||||||
|     if(!furi_hal_rtc_is_flag_set(FuriHalRtcFlagStealthMode) || force) { |     if(!furi_hal_rtc_is_flag_set(FuriHalRtcFlagStealthMode) || force) { | ||||||
|         if(furi_hal_speaker_is_mine() || furi_hal_speaker_acquire(30)) { |         if(furi_hal_speaker_is_mine() || furi_hal_speaker_acquire(30)) { | ||||||
|             furi_hal_speaker_start(freq, volume); |             furi_hal_speaker_start(freq, volume); | ||||||
| @ -159,7 +169,7 @@ void notification_sound_on(float freq, float volume, bool force) { | |||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void notification_sound_off() { | static void notification_sound_off() { | ||||||
|     if(furi_hal_speaker_is_mine()) { |     if(furi_hal_speaker_is_mine()) { | ||||||
|         furi_hal_speaker_stop(); |         furi_hal_speaker_stop(); | ||||||
|         furi_hal_speaker_release(); |         furi_hal_speaker_release(); | ||||||
| @ -174,7 +184,7 @@ static void notification_display_timer(void* ctx) { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // message processing
 | // message processing
 | ||||||
| void notification_process_notification_message( | static void notification_process_notification_message( | ||||||
|     NotificationApp* app, |     NotificationApp* app, | ||||||
|     NotificationAppMessage* message) { |     NotificationAppMessage* message) { | ||||||
|     uint32_t notification_message_index = 0; |     uint32_t notification_message_index = 0; | ||||||
| @ -333,6 +343,9 @@ void notification_process_notification_message( | |||||||
|             reset_mask |= reset_green_mask; |             reset_mask |= reset_green_mask; | ||||||
|             reset_mask |= reset_blue_mask; |             reset_mask |= reset_blue_mask; | ||||||
|             break; |             break; | ||||||
|  |         case NotificationMessageTypeLcdContrastUpdate: | ||||||
|  |             notification_apply_lcd_contrast(app); | ||||||
|  |             break; | ||||||
|         } |         } | ||||||
|         notification_message_index++; |         notification_message_index++; | ||||||
|         notification_message = (*message->sequence)[notification_message_index]; |         notification_message = (*message->sequence)[notification_message_index]; | ||||||
| @ -361,7 +374,8 @@ void notification_process_notification_message( | |||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void notification_process_internal_message(NotificationApp* app, NotificationAppMessage* message) { | static void | ||||||
|  |     notification_process_internal_message(NotificationApp* app, NotificationAppMessage* message) { | ||||||
|     uint32_t notification_message_index = 0; |     uint32_t notification_message_index = 0; | ||||||
|     const NotificationMessage* notification_message; |     const NotificationMessage* notification_message; | ||||||
|     notification_message = (*message->sequence)[notification_message_index]; |     notification_message = (*message->sequence)[notification_message_index]; | ||||||
| @ -548,6 +562,7 @@ int32_t notification_srv(void* p) { | |||||||
|     notification_apply_internal_led_layer(&app->led[0], 0x00); |     notification_apply_internal_led_layer(&app->led[0], 0x00); | ||||||
|     notification_apply_internal_led_layer(&app->led[1], 0x00); |     notification_apply_internal_led_layer(&app->led[1], 0x00); | ||||||
|     notification_apply_internal_led_layer(&app->led[2], 0x00); |     notification_apply_internal_led_layer(&app->led[2], 0x00); | ||||||
|  |     notification_apply_lcd_contrast(app); | ||||||
| 
 | 
 | ||||||
|     furi_record_create(RECORD_NOTIFICATION, app); |     furi_record_create(RECORD_NOTIFICATION, app); | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -32,7 +32,7 @@ typedef struct { | |||||||
|     Light light; |     Light light; | ||||||
| } NotificationLedLayer; | } NotificationLedLayer; | ||||||
| 
 | 
 | ||||||
| #define NOTIFICATION_SETTINGS_VERSION 0x01 | #define NOTIFICATION_SETTINGS_VERSION 0x02 | ||||||
| #define NOTIFICATION_SETTINGS_PATH INT_PATH(NOTIFICATION_SETTINGS_FILE_NAME) | #define NOTIFICATION_SETTINGS_PATH INT_PATH(NOTIFICATION_SETTINGS_FILE_NAME) | ||||||
| 
 | 
 | ||||||
| typedef struct { | typedef struct { | ||||||
| @ -41,6 +41,7 @@ typedef struct { | |||||||
|     float led_brightness; |     float led_brightness; | ||||||
|     float speaker_volume; |     float speaker_volume; | ||||||
|     uint32_t display_off_delay_ms; |     uint32_t display_off_delay_ms; | ||||||
|  |     int8_t contrast; | ||||||
|     bool vibro_on; |     bool vibro_on; | ||||||
| } NotificationSettings; | } NotificationSettings; | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -197,6 +197,10 @@ const NotificationMessage message_force_display_brightness_setting_1f = { | |||||||
|     .data.forced_settings.display_brightness = 1.0f, |     .data.forced_settings.display_brightness = 1.0f, | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  | const NotificationMessage message_lcd_contrast_update = { | ||||||
|  |     .type = NotificationMessageTypeLcdContrastUpdate, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
| /****************************** Message sequences ******************************/ | /****************************** Message sequences ******************************/ | ||||||
| 
 | 
 | ||||||
| // Reset
 | // Reset
 | ||||||
| @ -566,3 +570,8 @@ const NotificationSequence sequence_audiovisual_alert = { | |||||||
|     &message_vibro_off, |     &message_vibro_off, | ||||||
|     NULL, |     NULL, | ||||||
| }; | }; | ||||||
|  | 
 | ||||||
|  | const NotificationSequence sequence_lcd_contrast_update = { | ||||||
|  |     &message_lcd_contrast_update, | ||||||
|  |     NULL, | ||||||
|  | }; | ||||||
|  | |||||||
| @ -63,6 +63,9 @@ extern const NotificationMessage message_force_vibro_setting_on; | |||||||
| extern const NotificationMessage message_force_vibro_setting_off; | extern const NotificationMessage message_force_vibro_setting_off; | ||||||
| extern const NotificationMessage message_force_display_brightness_setting_1f; | extern const NotificationMessage message_force_display_brightness_setting_1f; | ||||||
| 
 | 
 | ||||||
|  | // LCD Messages
 | ||||||
|  | extern const NotificationMessage message_lcd_contrast_update; | ||||||
|  | 
 | ||||||
| /****************************** Message sequences ******************************/ | /****************************** Message sequences ******************************/ | ||||||
| 
 | 
 | ||||||
| // Reset
 | // Reset
 | ||||||
| @ -138,6 +141,9 @@ extern const NotificationSequence sequence_success; | |||||||
| extern const NotificationSequence sequence_error; | extern const NotificationSequence sequence_error; | ||||||
| extern const NotificationSequence sequence_audiovisual_alert; | extern const NotificationSequence sequence_audiovisual_alert; | ||||||
| 
 | 
 | ||||||
|  | // LCD
 | ||||||
|  | extern const NotificationSequence sequence_lcd_contrast_update; | ||||||
|  | 
 | ||||||
| #ifdef __cplusplus | #ifdef __cplusplus | ||||||
| } | } | ||||||
| #endif | #endif | ||||||
|  | |||||||
| @ -20,6 +20,34 @@ static const NotificationSequence sequence_note_c = { | |||||||
|     NULL, |     NULL, | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  | #define CONTRAST_COUNT 11 | ||||||
|  | const char* const contrast_text[CONTRAST_COUNT] = { | ||||||
|  |     "-5", | ||||||
|  |     "-4", | ||||||
|  |     "-3", | ||||||
|  |     "-2", | ||||||
|  |     "-1", | ||||||
|  |     "0", | ||||||
|  |     "+1", | ||||||
|  |     "+2", | ||||||
|  |     "+3", | ||||||
|  |     "+4", | ||||||
|  |     "+5", | ||||||
|  | }; | ||||||
|  | const int32_t contrast_value[CONTRAST_COUNT] = { | ||||||
|  |     -5, | ||||||
|  |     -4, | ||||||
|  |     -3, | ||||||
|  |     -2, | ||||||
|  |     -1, | ||||||
|  |     0, | ||||||
|  |     1, | ||||||
|  |     2, | ||||||
|  |     3, | ||||||
|  |     4, | ||||||
|  |     5, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
| #define BACKLIGHT_COUNT 5 | #define BACKLIGHT_COUNT 5 | ||||||
| const char* const backlight_text[BACKLIGHT_COUNT] = { | const char* const backlight_text[BACKLIGHT_COUNT] = { | ||||||
|     "0%", |     "0%", | ||||||
| @ -64,6 +92,15 @@ const char* const vibro_text[VIBRO_COUNT] = { | |||||||
| }; | }; | ||||||
| const bool vibro_value[VIBRO_COUNT] = {false, true}; | const bool vibro_value[VIBRO_COUNT] = {false, true}; | ||||||
| 
 | 
 | ||||||
|  | static void contrast_changed(VariableItem* item) { | ||||||
|  |     NotificationAppSettings* app = variable_item_get_context(item); | ||||||
|  |     uint8_t index = variable_item_get_current_value_index(item); | ||||||
|  | 
 | ||||||
|  |     variable_item_set_current_value_text(item, contrast_text[index]); | ||||||
|  |     app->notification->settings.contrast = contrast_value[index]; | ||||||
|  |     notification_message(app->notification, &sequence_lcd_contrast_update); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| static void backlight_changed(VariableItem* item) { | static void backlight_changed(VariableItem* item) { | ||||||
|     NotificationAppSettings* app = variable_item_get_context(item); |     NotificationAppSettings* app = 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); | ||||||
| @ -136,6 +173,13 @@ static NotificationAppSettings* alloc_settings() { | |||||||
|     VariableItem* item; |     VariableItem* item; | ||||||
|     uint8_t value_index; |     uint8_t value_index; | ||||||
| 
 | 
 | ||||||
|  |     item = variable_item_list_add( | ||||||
|  |         app->variable_item_list, "LCD Contrast", CONTRAST_COUNT, contrast_changed, app); | ||||||
|  |     value_index = | ||||||
|  |         value_index_int32(app->notification->settings.contrast, contrast_value, CONTRAST_COUNT); | ||||||
|  |     variable_item_set_current_value_index(item, value_index); | ||||||
|  |     variable_item_set_current_value_text(item, contrast_text[value_index]); | ||||||
|  | 
 | ||||||
|     item = variable_item_list_add( |     item = variable_item_list_add( | ||||||
|         app->variable_item_list, "LCD Backlight", BACKLIGHT_COUNT, backlight_changed, app); |         app->variable_item_list, "LCD Backlight", BACKLIGHT_COUNT, backlight_changed, app); | ||||||
|     value_index = value_index_float( |     value_index = value_index_float( | ||||||
|  | |||||||
							
								
								
									
										
											BIN
										
									
								
								assets/dolphin/external/L2_Dj_128x64/frame_0.png
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 1.6 KiB | 
							
								
								
									
										
											BIN
										
									
								
								assets/dolphin/external/L2_Dj_128x64/frame_1.png
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 1.6 KiB | 
							
								
								
									
										
											BIN
										
									
								
								assets/dolphin/external/L2_Dj_128x64/frame_10.png
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 1.6 KiB | 
							
								
								
									
										
											BIN
										
									
								
								assets/dolphin/external/L2_Dj_128x64/frame_11.png
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 1.6 KiB | 
							
								
								
									
										
											BIN
										
									
								
								assets/dolphin/external/L2_Dj_128x64/frame_12.png
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 1.6 KiB | 
							
								
								
									
										
											BIN
										
									
								
								assets/dolphin/external/L2_Dj_128x64/frame_13.png
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 1.6 KiB | 
							
								
								
									
										
											BIN
										
									
								
								assets/dolphin/external/L2_Dj_128x64/frame_14.png
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 1.6 KiB | 
							
								
								
									
										
											BIN
										
									
								
								assets/dolphin/external/L2_Dj_128x64/frame_15.png
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 1.3 KiB | 
							
								
								
									
										
											BIN
										
									
								
								assets/dolphin/external/L2_Dj_128x64/frame_16.png
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 1.2 KiB | 
							
								
								
									
										
											BIN
										
									
								
								assets/dolphin/external/L2_Dj_128x64/frame_17.png
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 1.3 KiB | 
							
								
								
									
										
											BIN
										
									
								
								assets/dolphin/external/L2_Dj_128x64/frame_18.png
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 1.5 KiB | 
							
								
								
									
										
											BIN
										
									
								
								assets/dolphin/external/L2_Dj_128x64/frame_19.png
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 1.5 KiB | 
							
								
								
									
										
											BIN
										
									
								
								assets/dolphin/external/L2_Dj_128x64/frame_2.png
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 1.7 KiB | 
							
								
								
									
										
											BIN
										
									
								
								assets/dolphin/external/L2_Dj_128x64/frame_20.png
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 1.7 KiB | 
							
								
								
									
										
											BIN
										
									
								
								assets/dolphin/external/L2_Dj_128x64/frame_21.png
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 1.6 KiB | 
							
								
								
									
										
											BIN
										
									
								
								assets/dolphin/external/L2_Dj_128x64/frame_22.png
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 1.8 KiB | 
							
								
								
									
										
											BIN
										
									
								
								assets/dolphin/external/L2_Dj_128x64/frame_23.png
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 1.7 KiB | 
							
								
								
									
										
											BIN
										
									
								
								assets/dolphin/external/L2_Dj_128x64/frame_24.png
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 1.7 KiB | 
 Aleksandr Kutuzov
						Aleksandr Kutuzov