Merge remote-tracking branch 'origin/dev' into release-candidate
							
								
								
									
										75
									
								
								.github/workflows/build.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						| @ -9,7 +9,7 @@ on: | ||||
|   pull_request: | ||||
| 
 | ||||
| env: | ||||
|   TARGETS: f7 | ||||
|   TARGETS: f7 f18 | ||||
|   DEFAULT_TARGET: f7 | ||||
|   FBT_TOOLCHAIN_PATH: /runner/_work | ||||
| 
 | ||||
| @ -52,10 +52,8 @@ jobs: | ||||
| 
 | ||||
|       - name: 'Make artifacts directory' | ||||
|         run: | | ||||
|           rm -rf artifacts | ||||
|           rm -rf map_analyser_files | ||||
|           mkdir artifacts | ||||
|           mkdir map_analyser_files | ||||
|           rm -rf artifacts map_analyser_files | ||||
|           mkdir artifacts map_analyser_files | ||||
| 
 | ||||
|       - name: 'Bundle scripts' | ||||
|         if: ${{ !github.event.pull_request.head.repo.fork }} | ||||
| @ -66,28 +64,21 @@ jobs: | ||||
|         run: | | ||||
|           set -e | ||||
|           for TARGET in ${TARGETS}; do | ||||
|             TARGET="$(echo "${TARGET}" | sed 's/f//')"; \ | ||||
|             ./fbt TARGET_HW=$TARGET copro_dist updater_package \ | ||||
|             TARGET_HW="$(echo "${TARGET}" | sed 's/f//')"; \ | ||||
|             ./fbt TARGET_HW=$TARGET_HW copro_dist updater_package \ | ||||
|               ${{ 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/ | ||||
|             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 | ||||
| 
 | ||||
|       - name: "Check for uncommitted changes" | ||||
|         run: | | ||||
|           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' | ||||
|         if: ${{ !github.event.pull_request.head.repo.fork }} | ||||
|         run: | | ||||
| @ -96,29 +87,35 @@ jobs: | ||||
|       - name: 'Copy map analyser files' | ||||
|         if: ${{ !github.event.pull_request.head.repo.fork }} | ||||
|         run: | | ||||
|           cp build/f7-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 map_analyser_files/firmware.elf.map | ||||
|           cp build/${DEFAULT_TARGET}-firmware-*/firmware.elf map_analyser_files/firmware.elf | ||||
|           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 }} | ||||
|         uses: prewk/s3-cp-action@v2 | ||||
|         with: | ||||
|           aws_s3_endpoint: "${{ secrets.MAP_REPORT_AWS_ENDPOINT }}" | ||||
|           aws_access_key_id: "${{ secrets.MAP_REPORT_AWS_ACCESS_KEY }}" | ||||
|           aws_secret_access_key: "${{ secrets.MAP_REPORT_AWS_SECRET_KEY }}" | ||||
|           source: "./map_analyser_files/" | ||||
|           dest: "s3://${{ secrets.MAP_REPORT_AWS_BUCKET }}/${{steps.names.outputs.random_hash}}" | ||||
|           flags: "--recursive --acl public-read" | ||||
| 
 | ||||
|       - name: 'Trigger map file reporter' | ||||
|         if: ${{ !github.event.pull_request.head.repo.fork }} | ||||
|         uses: peter-evans/repository-dispatch@v2 | ||||
|         with: | ||||
|           repository: flipperdevices/flipper-map-reporter | ||||
|           token: ${{ secrets.REPOSITORY_DISPATCH_TOKEN }} | ||||
|           event-type: map-file-analyse | ||||
|           client-payload: '{"random_hash": "${{steps.names.outputs.random_hash}}", "event_type": "${{steps.names.outputs.event_type}}"}' | ||||
|         run: | | ||||
|           source scripts/toolchain/fbtenv.sh | ||||
|           get_size() | ||||
|           { | ||||
|             SECTION="$1"; | ||||
|             arm-none-eabi-size \ | ||||
|               -A map_analyser_files/firmware.elf \ | ||||
|               | grep "^$SECTION" | awk '{print $2}' | ||||
|           } | ||||
|           export BSS_SIZE="$(get_size ".bss")" | ||||
|           export TEXT_SIZE="$(get_size ".text")" | ||||
|           export RODATA_SIZE="$(get_size ".rodata")" | ||||
|           export DATA_SIZE="$(get_size ".data")" | ||||
|           export FREE_FLASH_SIZE="$(get_size ".free_flash")" | ||||
|           python3 -m pip install mariadb==1.1.6 cxxfilt==0.3.0 | ||||
|           python3 scripts/map_parser.py map_analyser_files/firmware.elf.map map_analyser_files/firmware.elf.map.all | ||||
|           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' | ||||
|         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", | ||||
|     "${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"), | ||||
| ) | ||||
| 
 | ||||
| @ -323,7 +323,9 @@ distenv.PhonyTarget( | ||||
| ) | ||||
| 
 | ||||
| # 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 | ||||
|  | ||||
							
								
								
									
										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); | ||||
| 
 | ||||
|     DOLPHIN_DEED(DolphinDeedPluginStart); | ||||
|     dolphin_deed(DolphinDeedPluginStart); | ||||
| 
 | ||||
|     view_dispatcher_run(app->view_dispatcher); | ||||
| 
 | ||||
| @ -417,7 +417,7 @@ int32_t hid_ble_app(void* p) { | ||||
|     furi_hal_bt_start_advertising(); | ||||
|     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); | ||||
| 
 | ||||
|  | ||||
							
								
								
									
										2
									
								
								applications/external/mfkey32/mfkey32.c
									
									
									
									
										vendored
									
									
								
							
							
						
						| @ -1112,7 +1112,7 @@ void mfkey32(ProgramState* program_state) { | ||||
|     } | ||||
|     if(keyarray_size > 0) { | ||||
|         // TODO: Should we use DolphinDeedNfcMfcAdd?
 | ||||
|         DOLPHIN_DEED(DolphinDeedNfcMfcAdd); | ||||
|         dolphin_deed(DolphinDeedNfcMfcAdd); | ||||
|     } | ||||
|     napi_mf_classic_nonce_array_free(nonce_arr); | ||||
|     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* sio_str = furi_string_alloc(); | ||||
| 
 | ||||
|     DOLPHIN_DEED(DolphinDeedNfcReadSuccess); | ||||
|     dolphin_deed(DolphinDeedNfcReadSuccess); | ||||
| 
 | ||||
|     // Setup view
 | ||||
|     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) { | ||||
|     Picopass* picopass = context; | ||||
|     DOLPHIN_DEED(DolphinDeedNfcRead); | ||||
|     dolphin_deed(DolphinDeedNfcRead); | ||||
| 
 | ||||
|     // Setup view
 | ||||
|     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* sio_str = furi_string_alloc(); | ||||
| 
 | ||||
|     DOLPHIN_DEED(DolphinDeedNfcReadSuccess); | ||||
|     dolphin_deed(DolphinDeedNfcReadSuccess); | ||||
| 
 | ||||
|     // Send notification
 | ||||
|     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* subtitle = furi_string_alloc_set(""); | ||||
| 
 | ||||
|     DOLPHIN_DEED(DolphinDeedNfcReadSuccess); | ||||
|     dolphin_deed(DolphinDeedNfcReadSuccess); | ||||
| 
 | ||||
|     // Send notification
 | ||||
|     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) { | ||||
|     Picopass* picopass = context; | ||||
|     DOLPHIN_DEED(DolphinDeedNfcSave); | ||||
|     dolphin_deed(DolphinDeedNfcSave); | ||||
| 
 | ||||
|     // Setup view
 | ||||
|     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) { | ||||
|     Picopass* picopass = context; | ||||
|     DOLPHIN_DEED(DolphinDeedNfcSave); | ||||
|     dolphin_deed(DolphinDeedNfcSave); | ||||
| 
 | ||||
|     // Setup view
 | ||||
|     Popup* popup = picopass->popup; | ||||
|  | ||||
| @ -18,7 +18,7 @@ void picopass_scene_write_card_success_on_enter(void* context) { | ||||
|     Widget* widget = picopass->widget; | ||||
|     FuriString* str = furi_string_alloc_set("Write Success!"); | ||||
| 
 | ||||
|     DOLPHIN_DEED(DolphinDeedNfcReadSuccess); | ||||
|     dolphin_deed(DolphinDeedNfcReadSuccess); | ||||
| 
 | ||||
|     // Send notification
 | ||||
|     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) { | ||||
|     Picopass* picopass = context; | ||||
|     DOLPHIN_DEED(DolphinDeedNfcSave); | ||||
|     dolphin_deed(DolphinDeedNfcSave); | ||||
| 
 | ||||
|     // Setup view
 | ||||
|     Popup* popup = picopass->popup; | ||||
|  | ||||
| @ -346,7 +346,7 @@ int32_t snake_game_app(void* p) { | ||||
| 
 | ||||
|     notification_message_block(notification, &sequence_display_backlight_enforce_on); | ||||
| 
 | ||||
|     DOLPHIN_DEED(DolphinDeedPluginGameStart); | ||||
|     dolphin_deed(DolphinDeedPluginGameStart); | ||||
| 
 | ||||
|     SnakeEvent event; | ||||
|     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_tx141thbv2, | ||||
|     &ws_protocol_oregon2, | ||||
|     &ws_protocol_oregon3, | ||||
|     &ws_protocol_acurite_592txr, | ||||
|     &ws_protocol_ambient_weather, | ||||
|     &ws_protocol_auriol_th, | ||||
|  | ||||
| @ -11,6 +11,7 @@ | ||||
| #include "lacrosse_tx.h" | ||||
| #include "lacrosse_tx141thbv2.h" | ||||
| #include "oregon2.h" | ||||
| #include "oregon3.h" | ||||
| #include "acurite_592txr.h" | ||||
| #include "ambient_weather.h" | ||||
| #include "auriol_hg0601a.h" | ||||
|  | ||||
| @ -426,7 +426,7 @@ static int32_t bad_usb_worker(void* context) { | ||||
|             if(flags & WorkerEvtEnd) { | ||||
|                 break; | ||||
|             } else if(flags & WorkerEvtStartStop) { // Start executing script
 | ||||
|                 DOLPHIN_DEED(DolphinDeedBadUsbPlayScript); | ||||
|                 dolphin_deed(DolphinDeedBadUsbPlayScript); | ||||
|                 delay_val = 0; | ||||
|                 bad_usb->buf_len = 0; | ||||
|                 bad_usb->st.line_cur = 0; | ||||
| @ -449,7 +449,7 @@ static int32_t bad_usb_worker(void* context) { | ||||
|             if(flags & WorkerEvtEnd) { | ||||
|                 break; | ||||
|             } else if(flags & WorkerEvtConnect) { // Start executing script
 | ||||
|                 DOLPHIN_DEED(DolphinDeedBadUsbPlayScript); | ||||
|                 dolphin_deed(DolphinDeedBadUsbPlayScript); | ||||
|                 delay_val = 0; | ||||
|                 bad_usb->buf_len = 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) { | ||||
|             scene_manager_set_scene_state(app->scene_manager, GpioSceneStart, GpioItemUsbUart); | ||||
|             if(!furi_hal_usb_is_locked()) { | ||||
|                 DOLPHIN_DEED(DolphinDeedGpioUartBridge); | ||||
|                 dolphin_deed(DolphinDeedGpioUartBridge); | ||||
|                 scene_manager_next_scene(app->scene_manager, GpioSceneUsbUart); | ||||
|             } else { | ||||
|                 scene_manager_next_scene(app->scene_manager, GpioSceneUsbUartCloseRpc); | ||||
|  | ||||
| @ -282,14 +282,14 @@ int32_t ibutton_app(void* arg) { | ||||
|         view_dispatcher_attach_to_gui( | ||||
|             ibutton->view_dispatcher, ibutton->gui, ViewDispatcherTypeDesktop); | ||||
|         scene_manager_next_scene(ibutton->scene_manager, iButtonSceneRpc); | ||||
|         DOLPHIN_DEED(DolphinDeedIbuttonEmulate); | ||||
|         dolphin_deed(DolphinDeedIbuttonEmulate); | ||||
| 
 | ||||
|     } else { | ||||
|         view_dispatcher_attach_to_gui( | ||||
|             ibutton->view_dispatcher, ibutton->gui, ViewDispatcherTypeFullscreen); | ||||
|         if(key_loaded) { //-V547
 | ||||
|             scene_manager_next_scene(ibutton->scene_manager, iButtonSceneEmulate); | ||||
|             DOLPHIN_DEED(DolphinDeedIbuttonEmulate); | ||||
|             dolphin_deed(DolphinDeedIbuttonEmulate); | ||||
|         } else { | ||||
|             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); | ||||
|                 scene_manager_next_scene(scene_manager, iButtonSceneReadSuccess); | ||||
| 
 | ||||
|                 DOLPHIN_DEED(DolphinDeedIbuttonReadSuccess); | ||||
|                 dolphin_deed(DolphinDeedIbuttonReadSuccess); | ||||
| 
 | ||||
|             } else { | ||||
|                 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); | ||||
|         } else if(event.event == SubmenuIndexEmulate) { | ||||
|             scene_manager_next_scene(scene_manager, iButtonSceneEmulate); | ||||
|             DOLPHIN_DEED(DolphinDeedIbuttonEmulate); | ||||
|             dolphin_deed(DolphinDeedIbuttonEmulate); | ||||
|         } else if(event.event == SubmenuIndexViewData) { | ||||
|             scene_manager_next_scene(scene_manager, iButtonSceneViewData); | ||||
|         } 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
 | ||||
|                 } else if(scene_manager_has_previous_scene( | ||||
|                               ibutton->scene_manager, iButtonSceneAddType)) { | ||||
|                     DOLPHIN_DEED(DolphinDeedIbuttonAdd); | ||||
|                     dolphin_deed(DolphinDeedIbuttonAdd); | ||||
|                 } else { | ||||
|                     DOLPHIN_DEED(DolphinDeedIbuttonSave); | ||||
|                     dolphin_deed(DolphinDeedIbuttonSave); | ||||
|                 } | ||||
| 
 | ||||
|             } else { | ||||
|  | ||||
| @ -48,7 +48,7 @@ bool ibutton_scene_saved_key_menu_on_event(void* context, SceneManagerEvent even | ||||
|         consumed = true; | ||||
|         if(event.event == SubmenuIndexEmulate) { | ||||
|             scene_manager_next_scene(scene_manager, iButtonSceneEmulate); | ||||
|             DOLPHIN_DEED(DolphinDeedIbuttonEmulate); | ||||
|             dolphin_deed(DolphinDeedIbuttonEmulate); | ||||
|         } else if(event.event == SubmenuIndexWriteBlank) { | ||||
|             ibutton->write_mode = iButtonWriteModeBlank; | ||||
|             scene_manager_next_scene(scene_manager, iButtonSceneWrite); | ||||
|  | ||||
| @ -33,7 +33,7 @@ bool ibutton_scene_start_on_event(void* context, SceneManagerEvent event) { | ||||
|         consumed = true; | ||||
|         if(event.event == SubmenuIndexRead) { | ||||
|             scene_manager_next_scene(ibutton->scene_manager, iButtonSceneRead); | ||||
|             DOLPHIN_DEED(DolphinDeedIbuttonRead); | ||||
|             dolphin_deed(DolphinDeedIbuttonRead); | ||||
|         } else if(event.event == SubmenuIndexSaved) { | ||||
|             scene_manager_next_scene(ibutton->scene_manager, iButtonSceneSelectKey); | ||||
|         } 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); | ||||
|     } | ||||
| 
 | ||||
|     DOLPHIN_DEED(DolphinDeedIrSend); | ||||
|     dolphin_deed(DolphinDeedIrSend); | ||||
|     infrared_play_notification_message(infrared, InfraredNotificationMessageBlinkStartSend); | ||||
| 
 | ||||
|     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; | ||||
|                 if(infrared_brute_force_start( | ||||
|                        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); | ||||
|                 } else { | ||||
|                     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) { | ||||
|             infrared_play_notification_message(infrared, InfraredNotificationMessageSuccess); | ||||
|             scene_manager_next_scene(infrared->scene_manager, InfraredSceneLearnSuccess); | ||||
|             DOLPHIN_DEED(DolphinDeedIrLearnSuccess); | ||||
|             dolphin_deed(DolphinDeedIrLearnSuccess); | ||||
|             consumed = true; | ||||
|         } | ||||
|     } | ||||
|  | ||||
| @ -50,7 +50,7 @@ bool infrared_scene_learn_enter_name_on_event(void* context, SceneManagerEvent e | ||||
| 
 | ||||
|             if(success) { | ||||
|                 scene_manager_next_scene(scene_manager, InfraredSceneLearnDone); | ||||
|                 DOLPHIN_DEED(DolphinDeedIrSave); | ||||
|                 dolphin_deed(DolphinDeedIrSave); | ||||
|             } else { | ||||
|                 dialog_message_show_storage_error(infrared->dialogs, "Failed to save file"); | ||||
|                 const uint32_t possible_scenes[] = {InfraredSceneRemoteList, InfraredSceneStart}; | ||||
|  | ||||
| @ -183,14 +183,14 @@ int32_t lfrfid_app(void* p) { | ||||
|             view_dispatcher_attach_to_gui( | ||||
|                 app->view_dispatcher, app->gui, ViewDispatcherTypeDesktop); | ||||
|             scene_manager_next_scene(app->scene_manager, LfRfidSceneRpc); | ||||
|             DOLPHIN_DEED(DolphinDeedRfidEmulate); | ||||
|             dolphin_deed(DolphinDeedRfidEmulate); | ||||
|         } else { | ||||
|             furi_string_set(app->file_path, args); | ||||
|             lfrfid_load_key_data(app, app->file_path, true); | ||||
|             view_dispatcher_attach_to_gui( | ||||
|                 app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen); | ||||
|             scene_manager_next_scene(app->scene_manager, LfRfidSceneEmulate); | ||||
|             DOLPHIN_DEED(DolphinDeedRfidEmulate); | ||||
|             dolphin_deed(DolphinDeedRfidEmulate); | ||||
|         } | ||||
| 
 | ||||
|     } else { | ||||
|  | ||||
| @ -58,12 +58,12 @@ bool lfrfid_scene_extra_actions_on_event(void* context, SceneManagerEvent event) | ||||
|         if(event.event == SubmenuIndexASK) { | ||||
|             app->read_type = LFRFIDWorkerReadTypeASKOnly; | ||||
|             scene_manager_next_scene(app->scene_manager, LfRfidSceneRead); | ||||
|             DOLPHIN_DEED(DolphinDeedRfidRead); | ||||
|             dolphin_deed(DolphinDeedRfidRead); | ||||
|             consumed = true; | ||||
|         } else if(event.event == SubmenuIndexPSK) { | ||||
|             app->read_type = LFRFIDWorkerReadTypePSKOnly; | ||||
|             scene_manager_next_scene(app->scene_manager, LfRfidSceneRead); | ||||
|             DOLPHIN_DEED(DolphinDeedRfidRead); | ||||
|             dolphin_deed(DolphinDeedRfidRead); | ||||
|             consumed = true; | ||||
|         } else if(event.event == SubmenuIndexRAW) { | ||||
|             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); | ||||
|             furi_string_reset(app->file_name); | ||||
|             scene_manager_next_scene(app->scene_manager, LfRfidSceneReadSuccess); | ||||
|             DOLPHIN_DEED(DolphinDeedRfidReadSuccess); | ||||
|             dolphin_deed(DolphinDeedRfidReadSuccess); | ||||
|             consumed = true; | ||||
|         } else if(event.event == LfRfidEventReadStartPSK) { | ||||
|             if(app->read_type == LFRFIDWorkerReadTypeAuto) { | ||||
|  | ||||
| @ -44,7 +44,7 @@ bool lfrfid_scene_read_key_menu_on_event(void* context, SceneManagerEvent event) | ||||
|             consumed = true; | ||||
|         } else if(event.event == SubmenuIndexEmulate) { | ||||
|             scene_manager_next_scene(app->scene_manager, LfRfidSceneEmulate); | ||||
|             DOLPHIN_DEED(DolphinDeedRfidEmulate); | ||||
|             dolphin_deed(DolphinDeedRfidEmulate); | ||||
|             consumed = true; | ||||
|         } | ||||
|         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)) { | ||||
|                     // Nothing, do not count editing as saving
 | ||||
|                 } else if(scene_manager_has_previous_scene(scene_manager, LfRfidSceneSaveType)) { | ||||
|                     DOLPHIN_DEED(DolphinDeedRfidAdd); | ||||
|                     dolphin_deed(DolphinDeedRfidAdd); | ||||
|                 } else { | ||||
|                     DOLPHIN_DEED(DolphinDeedRfidSave); | ||||
|                     dolphin_deed(DolphinDeedRfidSave); | ||||
|                 } | ||||
|             } else { | ||||
|                 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.event == SubmenuIndexEmulate) { | ||||
|             scene_manager_next_scene(app->scene_manager, LfRfidSceneEmulate); | ||||
|             DOLPHIN_DEED(DolphinDeedRfidEmulate); | ||||
|             dolphin_deed(DolphinDeedRfidEmulate); | ||||
|             consumed = true; | ||||
|         } else if(event.event == SubmenuIndexWrite) { | ||||
|             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) { | ||||
|             scene_manager_set_scene_state(app->scene_manager, LfRfidSceneStart, SubmenuIndexRead); | ||||
|             scene_manager_next_scene(app->scene_manager, LfRfidSceneRead); | ||||
|             DOLPHIN_DEED(DolphinDeedRfidRead); | ||||
|             dolphin_deed(DolphinDeedRfidRead); | ||||
|             consumed = true; | ||||
|         } else if(event.event == SubmenuIndexSaved) { | ||||
|             // Like in the other apps, explicitly save the scene state
 | ||||
|  | ||||
| @ -12,4 +12,6 @@ enum NfcCustomEvent { | ||||
|     NfcCustomEventDictAttackSkip, | ||||
|     NfcCustomEventRpcLoad, | ||||
|     NfcCustomEventRpcSessionClose, | ||||
|     NfcCustomEventUpdateLog, | ||||
|     NfcCustomEventSaveShadow, | ||||
| }; | ||||
|  | ||||
| @ -286,15 +286,18 @@ int32_t nfc_app(void* p) { | ||||
|             if(nfc_device_load(nfc->dev, p, true)) { | ||||
|                 if(nfc->dev->format == NfcDeviceSaveFormatMifareUl) { | ||||
|                     scene_manager_next_scene(nfc->scene_manager, NfcSceneMfUltralightEmulate); | ||||
|                     DOLPHIN_DEED(DolphinDeedNfcEmulate); | ||||
|                     dolphin_deed(DolphinDeedNfcEmulate); | ||||
|                 } else if(nfc->dev->format == NfcDeviceSaveFormatMifareClassic) { | ||||
|                     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) { | ||||
|                     scene_manager_next_scene(nfc->scene_manager, NfcSceneDeviceInfo); | ||||
|                 } else { | ||||
|                     scene_manager_next_scene(nfc->scene_manager, NfcSceneEmulateUid); | ||||
|                     DOLPHIN_DEED(DolphinDeedNfcEmulate); | ||||
|                     dolphin_deed(DolphinDeedNfcEmulate); | ||||
|                 } | ||||
|             } else { | ||||
|                 // Exit app
 | ||||
|  | ||||
| @ -14,6 +14,13 @@ ADD_SCENE(nfc, file_select, FileSelect) | ||||
| ADD_SCENE(nfc, emulate_uid, EmulateUid) | ||||
| ADD_SCENE(nfc, nfca_read_success, NfcaReadSuccess) | ||||
| 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_data, MfUltralightData) | ||||
| 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)); | ||||
| 
 | ||||
|     NfcProtocol protocol = nfc->dev->dev_data.protocol; | ||||
|     const char* nfc_type = "NFC-A"; | ||||
| 
 | ||||
|     if(protocol == NfcDeviceProtocolEMV) { | ||||
|         furi_string_set(temp_str, "EMV bank card"); | ||||
|     } 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)); | ||||
|     } else if(protocol == NfcDeviceProtocolMifareDesfire) { | ||||
|         furi_string_set(temp_str, "MIFARE DESFire"); | ||||
|     } else if(protocol == NfcDeviceProtocolNfcV) { | ||||
|         furi_string_set(temp_str, "ISO15693 tag"); | ||||
|         nfc_type = "NFC-V"; | ||||
|     } else { | ||||
|         furi_string_set(temp_str, "Unknown ISO tag"); | ||||
|     } | ||||
|     widget_add_string_element( | ||||
|         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); | ||||
| 
 | ||||
|     view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget); | ||||
|  | ||||
| @ -4,6 +4,8 @@ enum SubmenuIndex { | ||||
|     SubmenuIndexReadCardType, | ||||
|     SubmenuIndexMfClassicKeys, | ||||
|     SubmenuIndexMfUltralightUnlock, | ||||
|     SubmenuIndexNfcVUnlock, | ||||
|     SubmenuIndexNfcVSniff, | ||||
| }; | ||||
| 
 | ||||
| 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, | ||||
|         nfc_scene_extra_actions_submenu_callback, | ||||
|         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, scene_manager_get_scene_state(nfc->scene_manager, NfcSceneExtraActions)); | ||||
|     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_next_scene(nfc->scene_manager, NfcSceneReadCardType); | ||||
|             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); | ||||
|     } | ||||
|  | ||||
| @ -111,7 +111,7 @@ bool nfc_scene_mf_classic_dict_attack_on_event(void* context, SceneManagerEvent | ||||
|             } else { | ||||
|                 notification_message(nfc->notifications, &sequence_success); | ||||
|                 scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicReadSuccess); | ||||
|                 DOLPHIN_DEED(DolphinDeedNfcReadSuccess); | ||||
|                 dolphin_deed(DolphinDeedNfcReadSuccess); | ||||
|                 consumed = true; | ||||
|             } | ||||
|         } 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); | ||||
|                 scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicReadSuccess); | ||||
|                 // Counting failed attempts too
 | ||||
|                 DOLPHIN_DEED(DolphinDeedNfcReadSuccess); | ||||
|                 dolphin_deed(DolphinDeedNfcReadSuccess); | ||||
|                 consumed = true; | ||||
|             } | ||||
|         } 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); | ||||
|                 } else if(mf_classic_dict_add_key(dict, nfc->byte_input_store)) { | ||||
|                     scene_manager_next_scene(nfc->scene_manager, NfcSceneSaveSuccess); | ||||
|                     DOLPHIN_DEED(DolphinDeedNfcMfcAdd); | ||||
|                     dolphin_deed(DolphinDeedNfcMfcAdd); | ||||
|                 } else { | ||||
|                     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) { | ||||
|             scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicEmulate); | ||||
|             if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneSetType)) { | ||||
|                 DOLPHIN_DEED(DolphinDeedNfcAddEmulate); | ||||
|                 dolphin_deed(DolphinDeedNfcAddEmulate); | ||||
|             } else { | ||||
|                 DOLPHIN_DEED(DolphinDeedNfcEmulate); | ||||
|                 dolphin_deed(DolphinDeedNfcEmulate); | ||||
|             } | ||||
|             consumed = true; | ||||
|         } else if(event.event == SubmenuIndexDetectReader) { | ||||
|             scene_manager_next_scene(nfc->scene_manager, NfcSceneDetectReader); | ||||
|             DOLPHIN_DEED(DolphinDeedNfcDetectReader); | ||||
|             dolphin_deed(DolphinDeedNfcDetectReader); | ||||
|             consumed = true; | ||||
|         } else if(event.event == SubmenuIndexInfo) { | ||||
|             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) { | ||||
|     Nfc* nfc = context; | ||||
|     DOLPHIN_DEED(DolphinDeedNfcEmulate); | ||||
|     dolphin_deed(DolphinDeedNfcEmulate); | ||||
| 
 | ||||
|     scene_manager_set_scene_state( | ||||
|         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) { | ||||
|     Nfc* nfc = context; | ||||
|     DOLPHIN_DEED(DolphinDeedNfcSave); | ||||
|     dolphin_deed(DolphinDeedNfcSave); | ||||
| 
 | ||||
|     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) { | ||||
|     Nfc* nfc = context; | ||||
|     DOLPHIN_DEED(DolphinDeedNfcEmulate); | ||||
|     dolphin_deed(DolphinDeedNfcEmulate); | ||||
| 
 | ||||
|     scene_manager_set_scene_state( | ||||
|         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) { | ||||
|     Nfc* nfc = context; | ||||
|     DOLPHIN_DEED(DolphinDeedNfcSave); | ||||
|     dolphin_deed(DolphinDeedNfcSave); | ||||
| 
 | ||||
|     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) { | ||||
|             scene_manager_next_scene(nfc->scene_manager, NfcSceneEmulateUid); | ||||
|             if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneSetType)) { | ||||
|                 DOLPHIN_DEED(DolphinDeedNfcAddEmulate); | ||||
|                 dolphin_deed(DolphinDeedNfcAddEmulate); | ||||
|             } else { | ||||
|                 DOLPHIN_DEED(DolphinDeedNfcEmulate); | ||||
|                 dolphin_deed(DolphinDeedNfcEmulate); | ||||
|             } | ||||
|             consumed = true; | ||||
|         } 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) { | ||||
|             scene_manager_next_scene(nfc->scene_manager, NfcSceneMfUltralightEmulate); | ||||
|             if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneSetType)) { | ||||
|                 DOLPHIN_DEED(DolphinDeedNfcAddEmulate); | ||||
|                 dolphin_deed(DolphinDeedNfcAddEmulate); | ||||
|             } else { | ||||
|                 DOLPHIN_DEED(DolphinDeedNfcEmulate); | ||||
|                 dolphin_deed(DolphinDeedNfcEmulate); | ||||
|             } | ||||
|             consumed = true; | ||||
|         } 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.event == DialogExResultRight) { | ||||
|                 scene_manager_next_scene(nfc->scene_manager, NfcSceneMfUltralightReadAuth); | ||||
|                 DOLPHIN_DEED(DolphinDeedNfcRead); | ||||
|                 dolphin_deed(DolphinDeedNfcRead); | ||||
|                 consumed = true; | ||||
|             } else if(event.event == DialogExResultLeft) { | ||||
|                 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.event == DialogExResultCenter) { | ||||
|                 scene_manager_next_scene(nfc->scene_manager, NfcSceneMfUltralightReadAuth); | ||||
|                 DOLPHIN_DEED(DolphinDeedNfcRead); | ||||
|                 dolphin_deed(DolphinDeedNfcRead); | ||||
|                 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)); | ||||
|     } else if(protocol == NfcDeviceProtocolMifareDesfire) { | ||||
|         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 { | ||||
|         furi_string_cat_printf(temp_str, "\e#Unknown ISO tag\n"); | ||||
|     } | ||||
| 
 | ||||
|     // 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'; | ||||
|         furi_string_cat_printf(temp_str, "ISO 14443-%c (NFC-A)\n", iso_type); | ||||
|         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, "\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); | ||||
|     } | ||||
| 
 | ||||
|     // Set application specific data
 | ||||
|     if(protocol == NfcDeviceProtocolMifareDesfire) { | ||||
| @ -139,6 +285,8 @@ bool nfc_scene_nfc_data_info_on_event(void* context, SceneManagerEvent event) { | ||||
|                 consumed = true; | ||||
|             } else if(protocol == NfcDeviceProtocolMifareClassic) { | ||||
|                 scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicData); | ||||
|             } else if(protocol == NfcDeviceProtocolNfcV) { | ||||
|                 scene_manager_next_scene(nfc->scene_manager, NfcSceneNfcVMenu); | ||||
|                 consumed = true; | ||||
|             } | ||||
|         } | ||||
|  | ||||
| @ -43,9 +43,9 @@ bool nfc_scene_nfca_menu_on_event(void* context, SceneManagerEvent event) { | ||||
|         } else if(event.event == SubmenuIndexEmulateUid) { | ||||
|             scene_manager_next_scene(nfc->scene_manager, NfcSceneEmulateUid); | ||||
|             if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneSetType)) { | ||||
|                 DOLPHIN_DEED(DolphinDeedNfcAddEmulate); | ||||
|                 dolphin_deed(DolphinDeedNfcAddEmulate); | ||||
|             } else { | ||||
|                 DOLPHIN_DEED(DolphinDeedNfcEmulate); | ||||
|                 dolphin_deed(DolphinDeedNfcEmulate); | ||||
|             } | ||||
|             consumed = true; | ||||
|         } 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)) { | ||||
|             notification_message(nfc->notifications, &sequence_success); | ||||
|             scene_manager_next_scene(nfc->scene_manager, NfcSceneReadCardSuccess); | ||||
|             DOLPHIN_DEED(DolphinDeedNfcReadSuccess); | ||||
|             dolphin_deed(DolphinDeedNfcReadSuccess); | ||||
|             consumed = true; | ||||
|         } else if(event.event == NfcWorkerEventReadUidNfcA) { | ||||
|             notification_message(nfc->notifications, &sequence_success); | ||||
|             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; | ||||
|         } else if(event.event == NfcWorkerEventReadMfUltralight) { | ||||
|             notification_message(nfc->notifications, &sequence_success); | ||||
|             // Set unlock password input to 0xFFFFFFFF only on fresh read
 | ||||
|             memset(nfc->byte_input_store, 0xFF, sizeof(nfc->byte_input_store)); | ||||
|             scene_manager_next_scene(nfc->scene_manager, NfcSceneMfUltralightReadSuccess); | ||||
|             DOLPHIN_DEED(DolphinDeedNfcReadSuccess); | ||||
|             dolphin_deed(DolphinDeedNfcReadSuccess); | ||||
|             consumed = true; | ||||
|         } else if(event.event == NfcWorkerEventReadMfClassicDone) { | ||||
|             notification_message(nfc->notifications, &sequence_success); | ||||
|             scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicReadSuccess); | ||||
|             DOLPHIN_DEED(DolphinDeedNfcReadSuccess); | ||||
|             dolphin_deed(DolphinDeedNfcReadSuccess); | ||||
|             consumed = true; | ||||
|         } else if(event.event == NfcWorkerEventReadMfDesfire) { | ||||
|             notification_message(nfc->notifications, &sequence_success); | ||||
|             scene_manager_next_scene(nfc->scene_manager, NfcSceneMfDesfireReadSuccess); | ||||
|             DOLPHIN_DEED(DolphinDeedNfcReadSuccess); | ||||
|             dolphin_deed(DolphinDeedNfcReadSuccess); | ||||
|             consumed = true; | ||||
|         } else if(event.event == NfcWorkerEventReadMfClassicDictAttackRequired) { | ||||
|             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; | ||||
| 
 | ||||
|     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, "It will be returned\nto its original state.", 47, 21, AlignLeft, AlignTop); | ||||
|     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_scene_rpc_emulate_callback, | ||||
|                             nfc); | ||||
|                     } else if(nfc->dev->format == NfcDeviceSaveFormatNfcV) { | ||||
|                         nfc_worker_start( | ||||
|                             nfc->worker, | ||||
|                             NfcWorkerStateNfcVEmulate, | ||||
|                             &nfc->dev->dev_data, | ||||
|                             nfc_scene_rpc_emulate_callback, | ||||
|                             nfc); | ||||
|                     } else { | ||||
|                         nfc_worker_start( | ||||
|                             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)) { | ||||
|                     // Nothing, do not count editing as saving
 | ||||
|                 } else if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneSetType)) { | ||||
|                     DOLPHIN_DEED(DolphinDeedNfcAddSave); | ||||
|                     dolphin_deed(DolphinDeedNfcAddSave); | ||||
|                 } else { | ||||
|                     DOLPHIN_DEED(DolphinDeedNfcSave); | ||||
|                     dolphin_deed(DolphinDeedNfcSave); | ||||
|                 } | ||||
|                 consumed = true; | ||||
|             } else { | ||||
|  | ||||
| @ -44,6 +44,7 @@ void nfc_scene_saved_menu_on_enter(void* context) { | ||||
|     } else if( | ||||
|         (nfc->dev->format == NfcDeviceSaveFormatMifareUl && | ||||
|          mf_ul_emulation_supported(&nfc->dev->dev_data.mf_ul_data)) || | ||||
|         nfc->dev->format == NfcDeviceSaveFormatNfcV || | ||||
|         nfc->dev->format == NfcDeviceSaveFormatMifareClassic) { | ||||
|         submenu_add_item( | ||||
|             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); | ||||
|             } else if(nfc->dev->format == NfcDeviceSaveFormatMifareClassic) { | ||||
|                 scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicEmulate); | ||||
|             } else if(nfc->dev->format == NfcDeviceSaveFormatNfcV) { | ||||
|                 scene_manager_next_scene(nfc->scene_manager, NfcSceneNfcVEmulate); | ||||
|             } else { | ||||
|                 scene_manager_next_scene(nfc->scene_manager, NfcSceneEmulateUid); | ||||
|             } | ||||
|             DOLPHIN_DEED(DolphinDeedNfcEmulate); | ||||
|             dolphin_deed(DolphinDeedNfcEmulate); | ||||
|             consumed = true; | ||||
|         } else if(event.event == SubmenuIndexDetectReader) { | ||||
|             scene_manager_next_scene(nfc->scene_manager, NfcSceneDetectReader); | ||||
|             DOLPHIN_DEED(DolphinDeedNfcDetectReader); | ||||
|             dolphin_deed(DolphinDeedNfcDetectReader); | ||||
|             consumed = true; | ||||
|         } else if(event.event == SubmenuIndexWrite) { | ||||
|             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); | ||||
|             nfc->dev->dev_data.read_mode = NfcReadModeAuto; | ||||
|             scene_manager_next_scene(nfc->scene_manager, NfcSceneRead); | ||||
|             DOLPHIN_DEED(DolphinDeedNfcRead); | ||||
|             dolphin_deed(DolphinDeedNfcRead); | ||||
|             consumed = true; | ||||
|         } else if(event.event == SubmenuIndexDetectReader) { | ||||
|             scene_manager_set_scene_state( | ||||
| @ -60,7 +60,7 @@ bool nfc_scene_start_on_event(void* context, SceneManagerEvent event) { | ||||
|             if(sd_exist) { | ||||
|                 nfc_device_data_clear(&nfc->dev->dev_data); | ||||
|                 scene_manager_next_scene(nfc->scene_manager, NfcSceneDetectReader); | ||||
|                 DOLPHIN_DEED(DolphinDeedNfcDetectReader); | ||||
|                 dolphin_deed(DolphinDeedNfcDetectReader); | ||||
|             } else { | ||||
|                 scene_manager_next_scene(nfc->scene_manager, NfcSceneDictNotFound); | ||||
|             } | ||||
|  | ||||
| @ -204,7 +204,7 @@ bool subghz_scene_read_raw_on_event(void* context, SceneManagerEvent event) { | ||||
|                 } else { | ||||
|                     if(scene_manager_has_previous_scene(subghz->scene_manager, SubGhzSceneSaved) || | ||||
|                        !scene_manager_has_previous_scene(subghz->scene_manager, SubGhzSceneStart)) { | ||||
|                         DOLPHIN_DEED(DolphinDeedSubGhzSend); | ||||
|                         dolphin_deed(DolphinDeedSubGhzSend); | ||||
|                     } | ||||
|                     // set callback end tx
 | ||||
|                     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 { | ||||
|                 SubGhzRadioPreset preset = subghz_txrx_get_preset(subghz->txrx); | ||||
|                 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->state_notifications = SubGhzNotificationStateRx; | ||||
|                     subghz_rx_key_state_set(subghz, SubGhzRxKeyStateAddKey); | ||||
|  | ||||
| @ -163,7 +163,7 @@ bool subghz_scene_receiver_on_event(void* context, SceneManagerEvent event) { | ||||
|         case SubGhzCustomEventViewReceiverOK: | ||||
|             subghz->idx_menu_chosen = subghz_view_receiver_get_idx_menu(subghz->subghz_receiver); | ||||
|             scene_manager_next_scene(subghz->scene_manager, SubGhzSceneReceiverInfo); | ||||
|             DOLPHIN_DEED(DolphinDeedSubGhzReceiverInfo); | ||||
|             dolphin_deed(DolphinDeedSubGhzReceiverInfo); | ||||
|             consumed = true; | ||||
|             break; | ||||
|         case SubGhzCustomEventViewReceiverConfig: | ||||
|  | ||||
| @ -137,9 +137,9 @@ bool subghz_scene_save_name_on_event(void* context, SceneManagerEvent event) { | ||||
|                     // Ditto, for RAW signals
 | ||||
|                 } else if(scene_manager_has_previous_scene( | ||||
|                               subghz->scene_manager, SubGhzSceneSetType)) { | ||||
|                     DOLPHIN_DEED(DolphinDeedSubGhzAddManually); | ||||
|                     dolphin_deed(DolphinDeedSubGhzAddManually); | ||||
|                 } else { | ||||
|                     DOLPHIN_DEED(DolphinDeedSubGhzSave); | ||||
|                     dolphin_deed(DolphinDeedSubGhzSave); | ||||
|                 } | ||||
|                 return true; | ||||
|             } else { | ||||
|  | ||||
| @ -92,7 +92,7 @@ bool subghz_scene_start_on_event(void* context, SceneManagerEvent event) { | ||||
|             scene_manager_set_scene_state( | ||||
|                 subghz->scene_manager, SubGhzSceneStart, SubmenuIndexFrequencyAnalyzer); | ||||
|             scene_manager_next_scene(subghz->scene_manager, SubGhzSceneFrequencyAnalyzer); | ||||
|             DOLPHIN_DEED(DolphinDeedSubGhzFrequencyAnalyzer); | ||||
|             dolphin_deed(DolphinDeedSubGhzFrequencyAnalyzer); | ||||
|             return true; | ||||
|         } else if(event.event == SubmenuIndexTest) { | ||||
|             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))) { | ||||
|                 subghz->state_notifications = SubGhzNotificationStateTx; | ||||
|                 subghz_scene_transmitter_update_data_show(subghz); | ||||
|                 DOLPHIN_DEED(DolphinDeedSubGhzSend); | ||||
|                 dolphin_deed(DolphinDeedSubGhzSend); | ||||
|             } | ||||
|             return true; | ||||
|         } 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); | ||||
|         } else if(event.event == U2fCustomEventAuthSuccess) { | ||||
|             notification_message_block(app->notifications, &sequence_set_green_255); | ||||
|             DOLPHIN_DEED(DolphinDeedU2fAuthorized); | ||||
|             dolphin_deed(DolphinDeedU2fAuthorized); | ||||
|             furi_timer_start(app->timer, U2F_SUCCESS_TIMEOUT); | ||||
|             app->event_cur = U2fCustomEventNone; | ||||
|             u2f_view_set_state(app->u2f_view, U2fMsgSuccess); | ||||
|  | ||||
| @ -1,7 +1,7 @@ | ||||
| #include "bt_i.h" | ||||
| #include "battery_service.h" | ||||
| #include "bt_keys_storage.h" | ||||
| 
 | ||||
| #include <services/battery_service.h> | ||||
| #include <notification/notification_messages.h> | ||||
| #include <gui/elements.h> | ||||
| #include <assets_icons.h> | ||||
|  | ||||
| @ -34,13 +34,13 @@ bool desktop_scene_debug_on_event(void* context, SceneManagerEvent event) { | ||||
|             break; | ||||
| 
 | ||||
|         case DesktopDebugEventDeed: | ||||
|             dolphin_deed(dolphin, DolphinDeedTestRight); | ||||
|             dolphin_deed(DolphinDeedTestRight); | ||||
|             desktop_debug_get_dolphin_data(desktop->debug_view); | ||||
|             consumed = true; | ||||
|             break; | ||||
| 
 | ||||
|         case DesktopDebugEventWrongDeed: | ||||
|             dolphin_deed(dolphin, DolphinDeedTestLeft); | ||||
|             dolphin_deed(DolphinDeedTestLeft); | ||||
|             desktop_debug_get_dolphin_data(desktop->debug_view); | ||||
|             consumed = true; | ||||
|             break; | ||||
|  | ||||
| @ -13,12 +13,13 @@ | ||||
| 
 | ||||
| static void dolphin_update_clear_limits_timer_period(Dolphin* dolphin); | ||||
| 
 | ||||
| void dolphin_deed(Dolphin* dolphin, DolphinDeed deed) { | ||||
|     furi_assert(dolphin); | ||||
| void dolphin_deed(DolphinDeed deed) { | ||||
|     Dolphin* dolphin = (Dolphin*)furi_record_open(RECORD_DOLPHIN); | ||||
|     DolphinEvent event; | ||||
|     event.type = DolphinEventTypeDeed; | ||||
|     event.deed = deed; | ||||
|     dolphin_event_send_async(dolphin, &event); | ||||
|     furi_record_close(RECORD_DOLPHIN); | ||||
| } | ||||
| 
 | ||||
| DolphinStats dolphin_stats(Dolphin* dolphin) { | ||||
|  | ||||
| @ -26,18 +26,11 @@ typedef enum { | ||||
|     DolphinPubsubEventUpdate, | ||||
| } 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.
 | ||||
|  * See dolphin_deed.h for available deeds. In futures it will become part of assets. | ||||
|  * Thread safe, async | ||||
|  */ | ||||
| void dolphin_deed(Dolphin* dolphin, DolphinDeed deed); | ||||
| void dolphin_deed(DolphinDeed deed); | ||||
| 
 | ||||
| /** Retrieve dolphin stats
 | ||||
|  * Thread safe, blocking | ||||
|  | ||||
| @ -75,6 +75,8 @@ typedef enum { | ||||
|     NotificationMessageTypeForceDisplayBrightnessSetting, | ||||
| 
 | ||||
|     NotificationMessageTypeLedBrightnessSettingApply, | ||||
| 
 | ||||
|     NotificationMessageTypeLcdContrastUpdate, | ||||
| } NotificationMessageType; | ||||
| 
 | ||||
| typedef struct { | ||||
|  | ||||
| @ -3,6 +3,9 @@ | ||||
| #include <furi_hal.h> | ||||
| #include <storage/storage.h> | ||||
| #include <input/input.h> | ||||
| #include <gui/gui_i.h> | ||||
| #include <u8g2_glue.h> | ||||
| 
 | ||||
| #include "notification.h" | ||||
| #include "notification_messages.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_blink_mask = 1 << 6; | ||||
| 
 | ||||
| void notification_vibro_on(bool force); | ||||
| void notification_vibro_off(); | ||||
| void notification_sound_on(float freq, float volume, bool force); | ||||
| void notification_sound_off(); | ||||
| static void notification_vibro_on(bool force); | ||||
| static void notification_vibro_off(); | ||||
| static void notification_sound_on(float freq, float volume, bool force); | ||||
| static void notification_sound_off(); | ||||
| 
 | ||||
| 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); | ||||
| uint32_t notification_settings_display_off_delay_ticks(NotificationApp* app); | ||||
| static uint8_t notification_settings_get_display_brightness(NotificationApp* app, uint8_t value); | ||||
| static uint8_t notification_settings_get_rgb_led_brightness(NotificationApp* app, uint8_t value); | ||||
| static uint32_t notification_settings_display_off_delay_ticks(NotificationApp* app); | ||||
| 
 | ||||
| void notification_message_save_settings(NotificationApp* app) { | ||||
|     NotificationAppMessage m = { | ||||
| @ -39,7 +42,8 @@ void notification_message_save_settings(NotificationApp* app) { | ||||
| }; | ||||
| 
 | ||||
| // 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->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; | ||||
|     if((app->led[0].index == LayerInternal) || (app->led[1].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
 | ||||
| void notification_apply_notification_led_layer( | ||||
| static void notification_apply_notification_led_layer( | ||||
|     NotificationLedLayer* layer, | ||||
|     const uint8_t layer_value) { | ||||
|     furi_assert(layer); | ||||
| @ -81,7 +91,7 @@ void notification_apply_notification_led_layer( | ||||
|     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->index < LayerMAX); | ||||
| 
 | ||||
| @ -94,7 +104,7 @@ void notification_reset_notification_led_layer(NotificationLedLayer* layer) { | ||||
|     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) { | ||||
|         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); | ||||
| } | ||||
| 
 | ||||
| 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); | ||||
| } | ||||
| 
 | ||||
| uint32_t notification_settings_display_off_delay_ticks(NotificationApp* app) { | ||||
| static uint32_t notification_settings_display_off_delay_ticks(NotificationApp* app) { | ||||
|     return ( | ||||
|         (float)(app->settings.display_off_delay_ms) / | ||||
|         (1000.0f / furi_kernel_get_tick_frequency())); | ||||
| } | ||||
| 
 | ||||
| // generics
 | ||||
| void notification_vibro_on(bool force) { | ||||
| static void notification_vibro_on(bool force) { | ||||
|     if(!furi_hal_rtc_is_flag_set(FuriHalRtcFlagStealthMode) || force) { | ||||
|         furi_hal_vibro_on(true); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void notification_vibro_off() { | ||||
| static void notification_vibro_off() { | ||||
|     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_speaker_is_mine() || furi_hal_speaker_acquire(30)) { | ||||
|             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()) { | ||||
|         furi_hal_speaker_stop(); | ||||
|         furi_hal_speaker_release(); | ||||
| @ -174,7 +184,7 @@ static void notification_display_timer(void* ctx) { | ||||
| } | ||||
| 
 | ||||
| // message processing
 | ||||
| void notification_process_notification_message( | ||||
| static void notification_process_notification_message( | ||||
|     NotificationApp* app, | ||||
|     NotificationAppMessage* message) { | ||||
|     uint32_t notification_message_index = 0; | ||||
| @ -333,6 +343,9 @@ void notification_process_notification_message( | ||||
|             reset_mask |= reset_green_mask; | ||||
|             reset_mask |= reset_blue_mask; | ||||
|             break; | ||||
|         case NotificationMessageTypeLcdContrastUpdate: | ||||
|             notification_apply_lcd_contrast(app); | ||||
|             break; | ||||
|         } | ||||
|         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; | ||||
|     const NotificationMessage* notification_message; | ||||
|     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[1], 0x00); | ||||
|     notification_apply_internal_led_layer(&app->led[2], 0x00); | ||||
|     notification_apply_lcd_contrast(app); | ||||
| 
 | ||||
|     furi_record_create(RECORD_NOTIFICATION, app); | ||||
| 
 | ||||
|  | ||||
| @ -32,7 +32,7 @@ typedef struct { | ||||
|     Light light; | ||||
| } NotificationLedLayer; | ||||
| 
 | ||||
| #define NOTIFICATION_SETTINGS_VERSION 0x01 | ||||
| #define NOTIFICATION_SETTINGS_VERSION 0x02 | ||||
| #define NOTIFICATION_SETTINGS_PATH INT_PATH(NOTIFICATION_SETTINGS_FILE_NAME) | ||||
| 
 | ||||
| typedef struct { | ||||
| @ -41,6 +41,7 @@ typedef struct { | ||||
|     float led_brightness; | ||||
|     float speaker_volume; | ||||
|     uint32_t display_off_delay_ms; | ||||
|     int8_t contrast; | ||||
|     bool vibro_on; | ||||
| } NotificationSettings; | ||||
| 
 | ||||
|  | ||||
| @ -197,6 +197,10 @@ const NotificationMessage message_force_display_brightness_setting_1f = { | ||||
|     .data.forced_settings.display_brightness = 1.0f, | ||||
| }; | ||||
| 
 | ||||
| const NotificationMessage message_lcd_contrast_update = { | ||||
|     .type = NotificationMessageTypeLcdContrastUpdate, | ||||
| }; | ||||
| 
 | ||||
| /****************************** Message sequences ******************************/ | ||||
| 
 | ||||
| // Reset
 | ||||
| @ -566,3 +570,8 @@ const NotificationSequence sequence_audiovisual_alert = { | ||||
|     &message_vibro_off, | ||||
|     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_display_brightness_setting_1f; | ||||
| 
 | ||||
| // LCD Messages
 | ||||
| extern const NotificationMessage message_lcd_contrast_update; | ||||
| 
 | ||||
| /****************************** Message sequences ******************************/ | ||||
| 
 | ||||
| // Reset
 | ||||
| @ -138,6 +141,9 @@ extern const NotificationSequence sequence_success; | ||||
| extern const NotificationSequence sequence_error; | ||||
| extern const NotificationSequence sequence_audiovisual_alert; | ||||
| 
 | ||||
| // LCD
 | ||||
| extern const NotificationSequence sequence_lcd_contrast_update; | ||||
| 
 | ||||
| #ifdef __cplusplus | ||||
| } | ||||
| #endif | ||||
|  | ||||
| @ -20,6 +20,34 @@ static const NotificationSequence sequence_note_c = { | ||||
|     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 | ||||
| const char* const backlight_text[BACKLIGHT_COUNT] = { | ||||
|     "0%", | ||||
| @ -64,6 +92,15 @@ const char* const vibro_text[VIBRO_COUNT] = { | ||||
| }; | ||||
| 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) { | ||||
|     NotificationAppSettings* app = variable_item_get_context(item); | ||||
|     uint8_t index = variable_item_get_current_value_index(item); | ||||
| @ -136,6 +173,13 @@ static NotificationAppSettings* alloc_settings() { | ||||
|     VariableItem* item; | ||||
|     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( | ||||
|         app->variable_item_list, "LCD Backlight", BACKLIGHT_COUNT, backlight_changed, app); | ||||
|     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