Merge remote-tracking branch 'origin/release-candidate' into release

This commit is contained in:
Aleksandr Kutuzov 2023-06-16 16:28:58 +09:00
commit 27a8f29a68
195 changed files with 6259 additions and 1111 deletions

View File

@ -9,7 +9,7 @@ on:
pull_request: pull_request:
env: env:
TARGETS: f7 TARGETS: f7 f18
DEFAULT_TARGET: f7 DEFAULT_TARGET: f7
FBT_TOOLCHAIN_PATH: /runner/_work FBT_TOOLCHAIN_PATH: /runner/_work
@ -52,10 +52,8 @@ jobs:
- name: 'Make artifacts directory' - name: 'Make artifacts directory'
run: | run: |
rm -rf artifacts rm -rf artifacts map_analyser_files
rm -rf map_analyser_files mkdir artifacts map_analyser_files
mkdir artifacts
mkdir map_analyser_files
- name: 'Bundle scripts' - name: 'Bundle scripts'
if: ${{ !github.event.pull_request.head.repo.fork }} if: ${{ !github.event.pull_request.head.repo.fork }}
@ -66,28 +64,21 @@ jobs:
run: | run: |
set -e set -e
for TARGET in ${TARGETS}; do for TARGET in ${TARGETS}; do
TARGET="$(echo "${TARGET}" | sed 's/f//')"; \ TARGET_HW="$(echo "${TARGET}" | sed 's/f//')"; \
./fbt TARGET_HW=$TARGET copro_dist updater_package \ ./fbt TARGET_HW=$TARGET_HW copro_dist updater_package \
${{ startsWith(github.ref, 'refs/tags') && 'DEBUG=0 COMPACT=1' || '' }} ${{ startsWith(github.ref, 'refs/tags') && 'DEBUG=0 COMPACT=1' || '' }}
done
- name: 'Move upload files'
if: ${{ !github.event.pull_request.head.repo.fork }}
run: |
set -e
for TARGET in ${TARGETS}; do
mv dist/${TARGET}-*/* artifacts/ mv dist/${TARGET}-*/* artifacts/
tar czpf "artifacts/flipper-z-${TARGET}-resources-${SUFFIX}.tgz" \
-C assets resources
./fbt TARGET_HW=$TARGET_HW fap_dist
tar czpf "artifacts/flipper-z-${TARGET}-debugapps-${SUFFIX}.tgz" \
-C dist/${TARGET}-*/apps/Debug .
done done
- name: "Check for uncommitted changes" - name: "Check for uncommitted changes"
run: | run: |
git diff --exit-code git diff --exit-code
- name: 'Bundle resources'
if: ${{ !github.event.pull_request.head.repo.fork }}
run: |
tar czpf "artifacts/flipper-z-any-resources-${SUFFIX}.tgz" -C assets resources
- name: 'Bundle core2 firmware' - name: 'Bundle core2 firmware'
if: ${{ !github.event.pull_request.head.repo.fork }} if: ${{ !github.event.pull_request.head.repo.fork }}
run: | run: |
@ -96,29 +87,35 @@ jobs:
- name: 'Copy map analyser files' - name: 'Copy map analyser files'
if: ${{ !github.event.pull_request.head.repo.fork }} if: ${{ !github.event.pull_request.head.repo.fork }}
run: | run: |
cp build/f7-firmware-*/firmware.elf.map map_analyser_files/firmware.elf.map cp build/${DEFAULT_TARGET}-firmware-*/firmware.elf.map map_analyser_files/firmware.elf.map
cp build/f7-firmware-*/firmware.elf map_analyser_files/firmware.elf cp build/${DEFAULT_TARGET}-firmware-*/firmware.elf map_analyser_files/firmware.elf
cp ${{ github.event_path }} map_analyser_files/event.json cp ${{ github.event_path }} map_analyser_files/event.json
- name: 'Upload map analyser files to storage' - name: 'Analyse map file'
if: ${{ !github.event.pull_request.head.repo.fork }} if: ${{ !github.event.pull_request.head.repo.fork }}
uses: prewk/s3-cp-action@v2 run: |
with: source scripts/toolchain/fbtenv.sh
aws_s3_endpoint: "${{ secrets.MAP_REPORT_AWS_ENDPOINT }}" get_size()
aws_access_key_id: "${{ secrets.MAP_REPORT_AWS_ACCESS_KEY }}" {
aws_secret_access_key: "${{ secrets.MAP_REPORT_AWS_SECRET_KEY }}" SECTION="$1";
source: "./map_analyser_files/" arm-none-eabi-size \
dest: "s3://${{ secrets.MAP_REPORT_AWS_BUCKET }}/${{steps.names.outputs.random_hash}}" -A map_analyser_files/firmware.elf \
flags: "--recursive --acl public-read" | grep "^$SECTION" | awk '{print $2}'
}
- name: 'Trigger map file reporter' export BSS_SIZE="$(get_size ".bss")"
if: ${{ !github.event.pull_request.head.repo.fork }} export TEXT_SIZE="$(get_size ".text")"
uses: peter-evans/repository-dispatch@v2 export RODATA_SIZE="$(get_size ".rodata")"
with: export DATA_SIZE="$(get_size ".data")"
repository: flipperdevices/flipper-map-reporter export FREE_FLASH_SIZE="$(get_size ".free_flash")"
token: ${{ secrets.REPOSITORY_DISPATCH_TOKEN }} python3 -m pip install mariadb==1.1.6 cxxfilt==0.3.0
event-type: map-file-analyse python3 scripts/map_parser.py map_analyser_files/firmware.elf.map map_analyser_files/firmware.elf.map.all
client-payload: '{"random_hash": "${{steps.names.outputs.random_hash}}", "event_type": "${{steps.names.outputs.event_type}}"}' python3 scripts/map_mariadb_insert.py \
${{ secrets.AMAP_MARIADB_USER }} \
${{ secrets.AMAP_MARIADB_PASSWORD }} \
${{ secrets.AMAP_MARIADB_HOST }} \
${{ secrets.AMAP_MARIADB_PORT }} \
${{ secrets.AMAP_MARIADB_DATABASE }} \
map_analyser_files/firmware.elf.map.all
- name: 'Upload artifacts to update server' - name: 'Upload artifacts to update server'
if: ${{ !github.event.pull_request.head.repo.fork }} if: ${{ !github.event.pull_request.head.repo.fork }}

View File

@ -171,7 +171,7 @@ distenv.Depends(firmware_env["FW_RESOURCES"], external_apps_artifacts.resources_
fap_deploy = distenv.PhonyTarget( fap_deploy = distenv.PhonyTarget(
"fap_deploy", "fap_deploy",
"${PYTHON3} ${ROOT_DIR}/scripts/storage.py send ${SOURCE} /ext/apps", "${PYTHON3} ${FBT_SCRIPT_DIR}/storage.py -p ${FLIP_PORT} send ${SOURCE} /ext/apps",
source=Dir("#/assets/resources/apps"), source=Dir("#/assets/resources/apps"),
) )
@ -323,7 +323,9 @@ distenv.PhonyTarget(
) )
# Start Flipper CLI via PySerial's miniterm # Start Flipper CLI via PySerial's miniterm
distenv.PhonyTarget("cli", "${PYTHON3} ${FBT_SCRIPT_DIR}/serial_cli.py") distenv.PhonyTarget(
"cli", "${PYTHON3} ${FBT_SCRIPT_DIR}/serial_cli.py -p ${FLIP_PORT}"
)
# Find blackmagic probe # Find blackmagic probe

View File

@ -377,7 +377,7 @@ int32_t hid_usb_app(void* p) {
bt_hid_connection_status_changed_callback(BtStatusConnected, app); bt_hid_connection_status_changed_callback(BtStatusConnected, app);
DOLPHIN_DEED(DolphinDeedPluginStart); dolphin_deed(DolphinDeedPluginStart);
view_dispatcher_run(app->view_dispatcher); view_dispatcher_run(app->view_dispatcher);
@ -417,7 +417,7 @@ int32_t hid_ble_app(void* p) {
furi_hal_bt_start_advertising(); furi_hal_bt_start_advertising();
bt_set_status_changed_callback(app->bt, bt_hid_connection_status_changed_callback, app); bt_set_status_changed_callback(app->bt, bt_hid_connection_status_changed_callback, app);
DOLPHIN_DEED(DolphinDeedPluginStart); dolphin_deed(DolphinDeedPluginStart);
view_dispatcher_run(app->view_dispatcher); view_dispatcher_run(app->view_dispatcher);

View File

@ -1112,7 +1112,7 @@ void mfkey32(ProgramState* program_state) {
} }
if(keyarray_size > 0) { if(keyarray_size > 0) {
// TODO: Should we use DolphinDeedNfcMfcAdd? // TODO: Should we use DolphinDeedNfcMfcAdd?
DOLPHIN_DEED(DolphinDeedNfcMfcAdd); dolphin_deed(DolphinDeedNfcMfcAdd);
} }
napi_mf_classic_nonce_array_free(nonce_arr); napi_mf_classic_nonce_array_free(nonce_arr);
napi_mf_classic_dict_free(user_dict); napi_mf_classic_dict_free(user_dict);

View File

@ -19,7 +19,7 @@ void picopass_scene_device_info_on_enter(void* context) {
FuriString* wiegand_str = furi_string_alloc(); FuriString* wiegand_str = furi_string_alloc();
FuriString* sio_str = furi_string_alloc(); FuriString* sio_str = furi_string_alloc();
DOLPHIN_DEED(DolphinDeedNfcReadSuccess); dolphin_deed(DolphinDeedNfcReadSuccess);
// Setup view // Setup view
PicopassBlock* AA1 = picopass->dev->dev_data.AA1; PicopassBlock* AA1 = picopass->dev->dev_data.AA1;

View File

@ -10,7 +10,7 @@ void picopass_read_card_worker_callback(PicopassWorkerEvent event, void* context
void picopass_scene_read_card_on_enter(void* context) { void picopass_scene_read_card_on_enter(void* context) {
Picopass* picopass = context; Picopass* picopass = context;
DOLPHIN_DEED(DolphinDeedNfcRead); dolphin_deed(DolphinDeedNfcRead);
// Setup view // Setup view
Popup* popup = picopass->popup; Popup* popup = picopass->popup;

View File

@ -21,7 +21,7 @@ void picopass_scene_read_card_success_on_enter(void* context) {
FuriString* wiegand_str = furi_string_alloc(); FuriString* wiegand_str = furi_string_alloc();
FuriString* sio_str = furi_string_alloc(); FuriString* sio_str = furi_string_alloc();
DOLPHIN_DEED(DolphinDeedNfcReadSuccess); dolphin_deed(DolphinDeedNfcReadSuccess);
// Send notification // Send notification
notification_message(picopass->notifications, &sequence_success); notification_message(picopass->notifications, &sequence_success);

View File

@ -19,7 +19,7 @@ void picopass_scene_read_factory_success_on_enter(void* context) {
FuriString* title = furi_string_alloc_set("Factory Default"); FuriString* title = furi_string_alloc_set("Factory Default");
FuriString* subtitle = furi_string_alloc_set(""); FuriString* subtitle = furi_string_alloc_set("");
DOLPHIN_DEED(DolphinDeedNfcReadSuccess); dolphin_deed(DolphinDeedNfcReadSuccess);
// Send notification // Send notification
notification_message(picopass->notifications, &sequence_success); notification_message(picopass->notifications, &sequence_success);

View File

@ -8,7 +8,7 @@ void picopass_scene_save_success_popup_callback(void* context) {
void picopass_scene_save_success_on_enter(void* context) { void picopass_scene_save_success_on_enter(void* context) {
Picopass* picopass = context; Picopass* picopass = context;
DOLPHIN_DEED(DolphinDeedNfcSave); dolphin_deed(DolphinDeedNfcSave);
// Setup view // Setup view
Popup* popup = picopass->popup; Popup* popup = picopass->popup;

View File

@ -9,7 +9,7 @@ void picopass_write_card_worker_callback(PicopassWorkerEvent event, void* contex
void picopass_scene_write_card_on_enter(void* context) { void picopass_scene_write_card_on_enter(void* context) {
Picopass* picopass = context; Picopass* picopass = context;
DOLPHIN_DEED(DolphinDeedNfcSave); dolphin_deed(DolphinDeedNfcSave);
// Setup view // Setup view
Popup* popup = picopass->popup; Popup* popup = picopass->popup;

View File

@ -18,7 +18,7 @@ void picopass_scene_write_card_success_on_enter(void* context) {
Widget* widget = picopass->widget; Widget* widget = picopass->widget;
FuriString* str = furi_string_alloc_set("Write Success!"); FuriString* str = furi_string_alloc_set("Write Success!");
DOLPHIN_DEED(DolphinDeedNfcReadSuccess); dolphin_deed(DolphinDeedNfcReadSuccess);
// Send notification // Send notification
notification_message(picopass->notifications, &sequence_success); notification_message(picopass->notifications, &sequence_success);

View File

@ -9,7 +9,7 @@ void picopass_write_key_worker_callback(PicopassWorkerEvent event, void* context
void picopass_scene_write_key_on_enter(void* context) { void picopass_scene_write_key_on_enter(void* context) {
Picopass* picopass = context; Picopass* picopass = context;
DOLPHIN_DEED(DolphinDeedNfcSave); dolphin_deed(DolphinDeedNfcSave);
// Setup view // Setup view
Popup* popup = picopass->popup; Popup* popup = picopass->popup;

View File

@ -346,7 +346,7 @@ int32_t snake_game_app(void* p) {
notification_message_block(notification, &sequence_display_backlight_enforce_on); notification_message_block(notification, &sequence_display_backlight_enforce_on);
DOLPHIN_DEED(DolphinDeedPluginGameStart); dolphin_deed(DolphinDeedPluginGameStart);
SnakeEvent event; SnakeEvent event;
for(bool processing = true; processing;) { for(bool processing = true; processing;) {

View 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,
};

View 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;

View File

@ -11,6 +11,7 @@ const SubGhzProtocol* weather_station_protocol_registry_items[] = {
&ws_protocol_lacrosse_tx, &ws_protocol_lacrosse_tx,
&ws_protocol_lacrosse_tx141thbv2, &ws_protocol_lacrosse_tx141thbv2,
&ws_protocol_oregon2, &ws_protocol_oregon2,
&ws_protocol_oregon3,
&ws_protocol_acurite_592txr, &ws_protocol_acurite_592txr,
&ws_protocol_ambient_weather, &ws_protocol_ambient_weather,
&ws_protocol_auriol_th, &ws_protocol_auriol_th,
@ -21,4 +22,4 @@ const SubGhzProtocol* weather_station_protocol_registry_items[] = {
const SubGhzProtocolRegistry weather_station_protocol_registry = { const SubGhzProtocolRegistry weather_station_protocol_registry = {
.items = weather_station_protocol_registry_items, .items = weather_station_protocol_registry_items,
.size = COUNT_OF(weather_station_protocol_registry_items)}; .size = COUNT_OF(weather_station_protocol_registry_items)};

View File

@ -11,6 +11,7 @@
#include "lacrosse_tx.h" #include "lacrosse_tx.h"
#include "lacrosse_tx141thbv2.h" #include "lacrosse_tx141thbv2.h"
#include "oregon2.h" #include "oregon2.h"
#include "oregon3.h"
#include "acurite_592txr.h" #include "acurite_592txr.h"
#include "ambient_weather.h" #include "ambient_weather.h"
#include "auriol_hg0601a.h" #include "auriol_hg0601a.h"

View File

@ -426,7 +426,7 @@ static int32_t bad_usb_worker(void* context) {
if(flags & WorkerEvtEnd) { if(flags & WorkerEvtEnd) {
break; break;
} else if(flags & WorkerEvtStartStop) { // Start executing script } else if(flags & WorkerEvtStartStop) { // Start executing script
DOLPHIN_DEED(DolphinDeedBadUsbPlayScript); dolphin_deed(DolphinDeedBadUsbPlayScript);
delay_val = 0; delay_val = 0;
bad_usb->buf_len = 0; bad_usb->buf_len = 0;
bad_usb->st.line_cur = 0; bad_usb->st.line_cur = 0;
@ -449,7 +449,7 @@ static int32_t bad_usb_worker(void* context) {
if(flags & WorkerEvtEnd) { if(flags & WorkerEvtEnd) {
break; break;
} else if(flags & WorkerEvtConnect) { // Start executing script } else if(flags & WorkerEvtConnect) { // Start executing script
DOLPHIN_DEED(DolphinDeedBadUsbPlayScript); dolphin_deed(DolphinDeedBadUsbPlayScript);
delay_val = 0; delay_val = 0;
bad_usb->buf_len = 0; bad_usb->buf_len = 0;
bad_usb->st.line_cur = 0; bad_usb->st.line_cur = 0;

View File

@ -89,7 +89,7 @@ bool gpio_scene_start_on_event(void* context, SceneManagerEvent event) {
} else if(event.event == GpioStartEventUsbUart) { } else if(event.event == GpioStartEventUsbUart) {
scene_manager_set_scene_state(app->scene_manager, GpioSceneStart, GpioItemUsbUart); scene_manager_set_scene_state(app->scene_manager, GpioSceneStart, GpioItemUsbUart);
if(!furi_hal_usb_is_locked()) { if(!furi_hal_usb_is_locked()) {
DOLPHIN_DEED(DolphinDeedGpioUartBridge); dolphin_deed(DolphinDeedGpioUartBridge);
scene_manager_next_scene(app->scene_manager, GpioSceneUsbUart); scene_manager_next_scene(app->scene_manager, GpioSceneUsbUart);
} else { } else {
scene_manager_next_scene(app->scene_manager, GpioSceneUsbUartCloseRpc); scene_manager_next_scene(app->scene_manager, GpioSceneUsbUartCloseRpc);

View File

@ -282,14 +282,14 @@ int32_t ibutton_app(void* arg) {
view_dispatcher_attach_to_gui( view_dispatcher_attach_to_gui(
ibutton->view_dispatcher, ibutton->gui, ViewDispatcherTypeDesktop); ibutton->view_dispatcher, ibutton->gui, ViewDispatcherTypeDesktop);
scene_manager_next_scene(ibutton->scene_manager, iButtonSceneRpc); scene_manager_next_scene(ibutton->scene_manager, iButtonSceneRpc);
DOLPHIN_DEED(DolphinDeedIbuttonEmulate); dolphin_deed(DolphinDeedIbuttonEmulate);
} else { } else {
view_dispatcher_attach_to_gui( view_dispatcher_attach_to_gui(
ibutton->view_dispatcher, ibutton->gui, ViewDispatcherTypeFullscreen); ibutton->view_dispatcher, ibutton->gui, ViewDispatcherTypeFullscreen);
if(key_loaded) { //-V547 if(key_loaded) { //-V547
scene_manager_next_scene(ibutton->scene_manager, iButtonSceneEmulate); scene_manager_next_scene(ibutton->scene_manager, iButtonSceneEmulate);
DOLPHIN_DEED(DolphinDeedIbuttonEmulate); dolphin_deed(DolphinDeedIbuttonEmulate);
} else { } else {
scene_manager_next_scene(ibutton->scene_manager, iButtonSceneStart); scene_manager_next_scene(ibutton->scene_manager, iButtonSceneStart);
} }

View File

@ -38,7 +38,7 @@ bool ibutton_scene_read_on_event(void* context, SceneManagerEvent event) {
ibutton_notification_message(ibutton, iButtonNotificationMessageSuccess); ibutton_notification_message(ibutton, iButtonNotificationMessageSuccess);
scene_manager_next_scene(scene_manager, iButtonSceneReadSuccess); scene_manager_next_scene(scene_manager, iButtonSceneReadSuccess);
DOLPHIN_DEED(DolphinDeedIbuttonReadSuccess); dolphin_deed(DolphinDeedIbuttonReadSuccess);
} else { } else {
scene_manager_next_scene(scene_manager, iButtonSceneReadError); scene_manager_next_scene(scene_manager, iButtonSceneReadError);

View File

@ -75,7 +75,7 @@ bool ibutton_scene_read_key_menu_on_event(void* context, SceneManagerEvent event
scene_manager_next_scene(scene_manager, iButtonSceneSaveName); scene_manager_next_scene(scene_manager, iButtonSceneSaveName);
} else if(event.event == SubmenuIndexEmulate) { } else if(event.event == SubmenuIndexEmulate) {
scene_manager_next_scene(scene_manager, iButtonSceneEmulate); scene_manager_next_scene(scene_manager, iButtonSceneEmulate);
DOLPHIN_DEED(DolphinDeedIbuttonEmulate); dolphin_deed(DolphinDeedIbuttonEmulate);
} else if(event.event == SubmenuIndexViewData) { } else if(event.event == SubmenuIndexViewData) {
scene_manager_next_scene(scene_manager, iButtonSceneViewData); scene_manager_next_scene(scene_manager, iButtonSceneViewData);
} else if(event.event == SubmenuIndexWriteBlank) { } else if(event.event == SubmenuIndexWriteBlank) {

View File

@ -58,9 +58,9 @@ bool ibutton_scene_save_name_on_event(void* context, SceneManagerEvent event) {
// Nothing, do not count editing as saving // Nothing, do not count editing as saving
} else if(scene_manager_has_previous_scene( } else if(scene_manager_has_previous_scene(
ibutton->scene_manager, iButtonSceneAddType)) { ibutton->scene_manager, iButtonSceneAddType)) {
DOLPHIN_DEED(DolphinDeedIbuttonAdd); dolphin_deed(DolphinDeedIbuttonAdd);
} else { } else {
DOLPHIN_DEED(DolphinDeedIbuttonSave); dolphin_deed(DolphinDeedIbuttonSave);
} }
} else { } else {

View File

@ -48,7 +48,7 @@ bool ibutton_scene_saved_key_menu_on_event(void* context, SceneManagerEvent even
consumed = true; consumed = true;
if(event.event == SubmenuIndexEmulate) { if(event.event == SubmenuIndexEmulate) {
scene_manager_next_scene(scene_manager, iButtonSceneEmulate); scene_manager_next_scene(scene_manager, iButtonSceneEmulate);
DOLPHIN_DEED(DolphinDeedIbuttonEmulate); dolphin_deed(DolphinDeedIbuttonEmulate);
} else if(event.event == SubmenuIndexWriteBlank) { } else if(event.event == SubmenuIndexWriteBlank) {
ibutton->write_mode = iButtonWriteModeBlank; ibutton->write_mode = iButtonWriteModeBlank;
scene_manager_next_scene(scene_manager, iButtonSceneWrite); scene_manager_next_scene(scene_manager, iButtonSceneWrite);

View File

@ -33,7 +33,7 @@ bool ibutton_scene_start_on_event(void* context, SceneManagerEvent event) {
consumed = true; consumed = true;
if(event.event == SubmenuIndexRead) { if(event.event == SubmenuIndexRead) {
scene_manager_next_scene(ibutton->scene_manager, iButtonSceneRead); scene_manager_next_scene(ibutton->scene_manager, iButtonSceneRead);
DOLPHIN_DEED(DolphinDeedIbuttonRead); dolphin_deed(DolphinDeedIbuttonRead);
} else if(event.event == SubmenuIndexSaved) { } else if(event.event == SubmenuIndexSaved) {
scene_manager_next_scene(ibutton->scene_manager, iButtonSceneSelectKey); scene_manager_next_scene(ibutton->scene_manager, iButtonSceneSelectKey);
} else if(event.event == SubmenuIndexAdd) { } else if(event.event == SubmenuIndexAdd) {

View File

@ -319,7 +319,7 @@ void infrared_tx_start_signal(Infrared* infrared, InfraredSignal* signal) {
infrared_worker_set_decoded_signal(infrared->worker, message); infrared_worker_set_decoded_signal(infrared->worker, message);
} }
DOLPHIN_DEED(DolphinDeedIrSend); dolphin_deed(DolphinDeedIrSend);
infrared_play_notification_message(infrared, InfraredNotificationMessageBlinkStartSend); infrared_play_notification_message(infrared, InfraredNotificationMessageBlinkStartSend);
infrared_worker_tx_set_get_signal_callback( infrared_worker_tx_set_get_signal_callback(

View File

@ -70,7 +70,7 @@ bool infrared_scene_universal_common_on_event(void* context, SceneManagerEvent e
uint32_t record_count; uint32_t record_count;
if(infrared_brute_force_start( if(infrared_brute_force_start(
brute_force, infrared_custom_event_get_value(event.event), &record_count)) { brute_force, infrared_custom_event_get_value(event.event), &record_count)) {
DOLPHIN_DEED(DolphinDeedIrSend); dolphin_deed(DolphinDeedIrSend);
infrared_scene_universal_common_show_popup(infrared, record_count); infrared_scene_universal_common_show_popup(infrared, record_count);
} else { } else {
scene_manager_next_scene(scene_manager, InfraredSceneErrorDatabases); scene_manager_next_scene(scene_manager, InfraredSceneErrorDatabases);

View File

@ -28,7 +28,7 @@ bool infrared_scene_learn_on_event(void* context, SceneManagerEvent event) {
if(event.event == InfraredCustomEventTypeSignalReceived) { if(event.event == InfraredCustomEventTypeSignalReceived) {
infrared_play_notification_message(infrared, InfraredNotificationMessageSuccess); infrared_play_notification_message(infrared, InfraredNotificationMessageSuccess);
scene_manager_next_scene(infrared->scene_manager, InfraredSceneLearnSuccess); scene_manager_next_scene(infrared->scene_manager, InfraredSceneLearnSuccess);
DOLPHIN_DEED(DolphinDeedIrLearnSuccess); dolphin_deed(DolphinDeedIrLearnSuccess);
consumed = true; consumed = true;
} }
} }

View File

@ -50,7 +50,7 @@ bool infrared_scene_learn_enter_name_on_event(void* context, SceneManagerEvent e
if(success) { if(success) {
scene_manager_next_scene(scene_manager, InfraredSceneLearnDone); scene_manager_next_scene(scene_manager, InfraredSceneLearnDone);
DOLPHIN_DEED(DolphinDeedIrSave); dolphin_deed(DolphinDeedIrSave);
} else { } else {
dialog_message_show_storage_error(infrared->dialogs, "Failed to save file"); dialog_message_show_storage_error(infrared->dialogs, "Failed to save file");
const uint32_t possible_scenes[] = {InfraredSceneRemoteList, InfraredSceneStart}; const uint32_t possible_scenes[] = {InfraredSceneRemoteList, InfraredSceneStart};

View File

@ -183,14 +183,14 @@ int32_t lfrfid_app(void* p) {
view_dispatcher_attach_to_gui( view_dispatcher_attach_to_gui(
app->view_dispatcher, app->gui, ViewDispatcherTypeDesktop); app->view_dispatcher, app->gui, ViewDispatcherTypeDesktop);
scene_manager_next_scene(app->scene_manager, LfRfidSceneRpc); scene_manager_next_scene(app->scene_manager, LfRfidSceneRpc);
DOLPHIN_DEED(DolphinDeedRfidEmulate); dolphin_deed(DolphinDeedRfidEmulate);
} else { } else {
furi_string_set(app->file_path, args); furi_string_set(app->file_path, args);
lfrfid_load_key_data(app, app->file_path, true); lfrfid_load_key_data(app, app->file_path, true);
view_dispatcher_attach_to_gui( view_dispatcher_attach_to_gui(
app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen); app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen);
scene_manager_next_scene(app->scene_manager, LfRfidSceneEmulate); scene_manager_next_scene(app->scene_manager, LfRfidSceneEmulate);
DOLPHIN_DEED(DolphinDeedRfidEmulate); dolphin_deed(DolphinDeedRfidEmulate);
} }
} else { } else {

View File

@ -58,12 +58,12 @@ bool lfrfid_scene_extra_actions_on_event(void* context, SceneManagerEvent event)
if(event.event == SubmenuIndexASK) { if(event.event == SubmenuIndexASK) {
app->read_type = LFRFIDWorkerReadTypeASKOnly; app->read_type = LFRFIDWorkerReadTypeASKOnly;
scene_manager_next_scene(app->scene_manager, LfRfidSceneRead); scene_manager_next_scene(app->scene_manager, LfRfidSceneRead);
DOLPHIN_DEED(DolphinDeedRfidRead); dolphin_deed(DolphinDeedRfidRead);
consumed = true; consumed = true;
} else if(event.event == SubmenuIndexPSK) { } else if(event.event == SubmenuIndexPSK) {
app->read_type = LFRFIDWorkerReadTypePSKOnly; app->read_type = LFRFIDWorkerReadTypePSKOnly;
scene_manager_next_scene(app->scene_manager, LfRfidSceneRead); scene_manager_next_scene(app->scene_manager, LfRfidSceneRead);
DOLPHIN_DEED(DolphinDeedRfidRead); dolphin_deed(DolphinDeedRfidRead);
consumed = true; consumed = true;
} else if(event.event == SubmenuIndexRAW) { } else if(event.event == SubmenuIndexRAW) {
scene_manager_next_scene(app->scene_manager, LfRfidSceneRawName); scene_manager_next_scene(app->scene_manager, LfRfidSceneRawName);

View File

@ -81,7 +81,7 @@ bool lfrfid_scene_read_on_event(void* context, SceneManagerEvent event) {
notification_message(app->notifications, &sequence_success); notification_message(app->notifications, &sequence_success);
furi_string_reset(app->file_name); furi_string_reset(app->file_name);
scene_manager_next_scene(app->scene_manager, LfRfidSceneReadSuccess); scene_manager_next_scene(app->scene_manager, LfRfidSceneReadSuccess);
DOLPHIN_DEED(DolphinDeedRfidReadSuccess); dolphin_deed(DolphinDeedRfidReadSuccess);
consumed = true; consumed = true;
} else if(event.event == LfRfidEventReadStartPSK) { } else if(event.event == LfRfidEventReadStartPSK) {
if(app->read_type == LFRFIDWorkerReadTypeAuto) { if(app->read_type == LFRFIDWorkerReadTypeAuto) {

View File

@ -44,7 +44,7 @@ bool lfrfid_scene_read_key_menu_on_event(void* context, SceneManagerEvent event)
consumed = true; consumed = true;
} else if(event.event == SubmenuIndexEmulate) { } else if(event.event == SubmenuIndexEmulate) {
scene_manager_next_scene(app->scene_manager, LfRfidSceneEmulate); scene_manager_next_scene(app->scene_manager, LfRfidSceneEmulate);
DOLPHIN_DEED(DolphinDeedRfidEmulate); dolphin_deed(DolphinDeedRfidEmulate);
consumed = true; consumed = true;
} }
scene_manager_set_scene_state(app->scene_manager, LfRfidSceneReadKeyMenu, event.event); scene_manager_set_scene_state(app->scene_manager, LfRfidSceneReadKeyMenu, event.event);

View File

@ -59,9 +59,9 @@ bool lfrfid_scene_save_name_on_event(void* context, SceneManagerEvent event) {
if(scene_manager_has_previous_scene(scene_manager, LfRfidSceneSavedKeyMenu)) { if(scene_manager_has_previous_scene(scene_manager, LfRfidSceneSavedKeyMenu)) {
// Nothing, do not count editing as saving // Nothing, do not count editing as saving
} else if(scene_manager_has_previous_scene(scene_manager, LfRfidSceneSaveType)) { } else if(scene_manager_has_previous_scene(scene_manager, LfRfidSceneSaveType)) {
DOLPHIN_DEED(DolphinDeedRfidAdd); dolphin_deed(DolphinDeedRfidAdd);
} else { } else {
DOLPHIN_DEED(DolphinDeedRfidSave); dolphin_deed(DolphinDeedRfidSave);
} }
} else { } else {
scene_manager_search_and_switch_to_previous_scene( scene_manager_search_and_switch_to_previous_scene(

View File

@ -43,7 +43,7 @@ bool lfrfid_scene_saved_key_menu_on_event(void* context, SceneManagerEvent event
if(event.type == SceneManagerEventTypeCustom) { if(event.type == SceneManagerEventTypeCustom) {
if(event.event == SubmenuIndexEmulate) { if(event.event == SubmenuIndexEmulate) {
scene_manager_next_scene(app->scene_manager, LfRfidSceneEmulate); scene_manager_next_scene(app->scene_manager, LfRfidSceneEmulate);
DOLPHIN_DEED(DolphinDeedRfidEmulate); dolphin_deed(DolphinDeedRfidEmulate);
consumed = true; consumed = true;
} else if(event.event == SubmenuIndexWrite) { } else if(event.event == SubmenuIndexWrite) {
scene_manager_next_scene(app->scene_manager, LfRfidSceneWrite); scene_manager_next_scene(app->scene_manager, LfRfidSceneWrite);

View File

@ -49,7 +49,7 @@ bool lfrfid_scene_start_on_event(void* context, SceneManagerEvent event) {
if(event.event == SubmenuIndexRead) { if(event.event == SubmenuIndexRead) {
scene_manager_set_scene_state(app->scene_manager, LfRfidSceneStart, SubmenuIndexRead); scene_manager_set_scene_state(app->scene_manager, LfRfidSceneStart, SubmenuIndexRead);
scene_manager_next_scene(app->scene_manager, LfRfidSceneRead); scene_manager_next_scene(app->scene_manager, LfRfidSceneRead);
DOLPHIN_DEED(DolphinDeedRfidRead); dolphin_deed(DolphinDeedRfidRead);
consumed = true; consumed = true;
} else if(event.event == SubmenuIndexSaved) { } else if(event.event == SubmenuIndexSaved) {
// Like in the other apps, explicitly save the scene state // Like in the other apps, explicitly save the scene state

View File

@ -12,4 +12,6 @@ enum NfcCustomEvent {
NfcCustomEventDictAttackSkip, NfcCustomEventDictAttackSkip,
NfcCustomEventRpcLoad, NfcCustomEventRpcLoad,
NfcCustomEventRpcSessionClose, NfcCustomEventRpcSessionClose,
NfcCustomEventUpdateLog,
NfcCustomEventSaveShadow,
}; };

View File

@ -286,15 +286,18 @@ int32_t nfc_app(void* p) {
if(nfc_device_load(nfc->dev, p, true)) { if(nfc_device_load(nfc->dev, p, true)) {
if(nfc->dev->format == NfcDeviceSaveFormatMifareUl) { if(nfc->dev->format == NfcDeviceSaveFormatMifareUl) {
scene_manager_next_scene(nfc->scene_manager, NfcSceneMfUltralightEmulate); scene_manager_next_scene(nfc->scene_manager, NfcSceneMfUltralightEmulate);
DOLPHIN_DEED(DolphinDeedNfcEmulate); dolphin_deed(DolphinDeedNfcEmulate);
} else if(nfc->dev->format == NfcDeviceSaveFormatMifareClassic) { } else if(nfc->dev->format == NfcDeviceSaveFormatMifareClassic) {
scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicEmulate); scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicEmulate);
DOLPHIN_DEED(DolphinDeedNfcEmulate); dolphin_deed(DolphinDeedNfcEmulate);
} else if(nfc->dev->format == NfcDeviceSaveFormatNfcV) {
scene_manager_next_scene(nfc->scene_manager, NfcSceneNfcVEmulate);
dolphin_deed(DolphinDeedNfcEmulate);
} else if(nfc->dev->format == NfcDeviceSaveFormatBankCard) { } else if(nfc->dev->format == NfcDeviceSaveFormatBankCard) {
scene_manager_next_scene(nfc->scene_manager, NfcSceneDeviceInfo); scene_manager_next_scene(nfc->scene_manager, NfcSceneDeviceInfo);
} else { } else {
scene_manager_next_scene(nfc->scene_manager, NfcSceneEmulateUid); scene_manager_next_scene(nfc->scene_manager, NfcSceneEmulateUid);
DOLPHIN_DEED(DolphinDeedNfcEmulate); dolphin_deed(DolphinDeedNfcEmulate);
} }
} else { } else {
// Exit app // Exit app

View File

@ -14,6 +14,13 @@ ADD_SCENE(nfc, file_select, FileSelect)
ADD_SCENE(nfc, emulate_uid, EmulateUid) ADD_SCENE(nfc, emulate_uid, EmulateUid)
ADD_SCENE(nfc, nfca_read_success, NfcaReadSuccess) ADD_SCENE(nfc, nfca_read_success, NfcaReadSuccess)
ADD_SCENE(nfc, nfca_menu, NfcaMenu) ADD_SCENE(nfc, nfca_menu, NfcaMenu)
ADD_SCENE(nfc, nfcv_menu, NfcVMenu)
ADD_SCENE(nfc, nfcv_unlock_menu, NfcVUnlockMenu)
ADD_SCENE(nfc, nfcv_key_input, NfcVKeyInput)
ADD_SCENE(nfc, nfcv_unlock, NfcVUnlock)
ADD_SCENE(nfc, nfcv_emulate, NfcVEmulate)
ADD_SCENE(nfc, nfcv_sniff, NfcVSniff)
ADD_SCENE(nfc, nfcv_read_success, NfcVReadSuccess)
ADD_SCENE(nfc, mf_ultralight_read_success, MfUltralightReadSuccess) ADD_SCENE(nfc, mf_ultralight_read_success, MfUltralightReadSuccess)
ADD_SCENE(nfc, mf_ultralight_data, MfUltralightData) ADD_SCENE(nfc, mf_ultralight_data, MfUltralightData)
ADD_SCENE(nfc, mf_ultralight_menu, MfUltralightMenu) ADD_SCENE(nfc, mf_ultralight_menu, MfUltralightMenu)

View File

@ -31,6 +31,8 @@ void nfc_scene_delete_on_enter(void* context) {
nfc->widget, 64, 24, AlignCenter, AlignTop, FontSecondary, furi_string_get_cstr(temp_str)); nfc->widget, 64, 24, AlignCenter, AlignTop, FontSecondary, furi_string_get_cstr(temp_str));
NfcProtocol protocol = nfc->dev->dev_data.protocol; NfcProtocol protocol = nfc->dev->dev_data.protocol;
const char* nfc_type = "NFC-A";
if(protocol == NfcDeviceProtocolEMV) { if(protocol == NfcDeviceProtocolEMV) {
furi_string_set(temp_str, "EMV bank card"); furi_string_set(temp_str, "EMV bank card");
} else if(protocol == NfcDeviceProtocolMifareUl) { } else if(protocol == NfcDeviceProtocolMifareUl) {
@ -39,12 +41,15 @@ void nfc_scene_delete_on_enter(void* context) {
furi_string_set(temp_str, nfc_mf_classic_type(nfc->dev->dev_data.mf_classic_data.type)); furi_string_set(temp_str, nfc_mf_classic_type(nfc->dev->dev_data.mf_classic_data.type));
} else if(protocol == NfcDeviceProtocolMifareDesfire) { } else if(protocol == NfcDeviceProtocolMifareDesfire) {
furi_string_set(temp_str, "MIFARE DESFire"); furi_string_set(temp_str, "MIFARE DESFire");
} else if(protocol == NfcDeviceProtocolNfcV) {
furi_string_set(temp_str, "ISO15693 tag");
nfc_type = "NFC-V";
} else { } else {
furi_string_set(temp_str, "Unknown ISO tag"); furi_string_set(temp_str, "Unknown ISO tag");
} }
widget_add_string_element( widget_add_string_element(
nfc->widget, 64, 34, AlignCenter, AlignTop, FontSecondary, furi_string_get_cstr(temp_str)); nfc->widget, 64, 34, AlignCenter, AlignTop, FontSecondary, furi_string_get_cstr(temp_str));
widget_add_string_element(nfc->widget, 64, 44, AlignCenter, AlignTop, FontSecondary, "NFC-A"); widget_add_string_element(nfc->widget, 64, 44, AlignCenter, AlignTop, FontSecondary, nfc_type);
furi_string_free(temp_str); furi_string_free(temp_str);
view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget); view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget);

View File

@ -4,6 +4,8 @@ enum SubmenuIndex {
SubmenuIndexReadCardType, SubmenuIndexReadCardType,
SubmenuIndexMfClassicKeys, SubmenuIndexMfClassicKeys,
SubmenuIndexMfUltralightUnlock, SubmenuIndexMfUltralightUnlock,
SubmenuIndexNfcVUnlock,
SubmenuIndexNfcVSniff,
}; };
void nfc_scene_extra_actions_submenu_callback(void* context, uint32_t index) { void nfc_scene_extra_actions_submenu_callback(void* context, uint32_t index) {
@ -34,6 +36,18 @@ void nfc_scene_extra_actions_on_enter(void* context) {
SubmenuIndexMfUltralightUnlock, SubmenuIndexMfUltralightUnlock,
nfc_scene_extra_actions_submenu_callback, nfc_scene_extra_actions_submenu_callback,
nfc); nfc);
submenu_add_item(
submenu,
"Unlock SLIX-L",
SubmenuIndexNfcVUnlock,
nfc_scene_extra_actions_submenu_callback,
nfc);
submenu_add_item(
submenu,
"Listen NfcV Reader",
SubmenuIndexNfcVSniff,
nfc_scene_extra_actions_submenu_callback,
nfc);
submenu_set_selected_item( submenu_set_selected_item(
submenu, scene_manager_get_scene_state(nfc->scene_manager, NfcSceneExtraActions)); submenu, scene_manager_get_scene_state(nfc->scene_manager, NfcSceneExtraActions));
view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewMenu); view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewMenu);
@ -58,6 +72,12 @@ bool nfc_scene_extra_actions_on_event(void* context, SceneManagerEvent event) {
scene_manager_set_scene_state(nfc->scene_manager, NfcSceneReadCardType, 0); scene_manager_set_scene_state(nfc->scene_manager, NfcSceneReadCardType, 0);
scene_manager_next_scene(nfc->scene_manager, NfcSceneReadCardType); scene_manager_next_scene(nfc->scene_manager, NfcSceneReadCardType);
consumed = true; consumed = true;
} else if(event.event == SubmenuIndexNfcVUnlock) {
scene_manager_next_scene(nfc->scene_manager, NfcSceneNfcVUnlockMenu);
consumed = true;
} else if(event.event == SubmenuIndexNfcVSniff) {
scene_manager_next_scene(nfc->scene_manager, NfcSceneNfcVSniff);
consumed = true;
} }
scene_manager_set_scene_state(nfc->scene_manager, NfcSceneExtraActions, event.event); scene_manager_set_scene_state(nfc->scene_manager, NfcSceneExtraActions, event.event);
} }

View File

@ -111,7 +111,7 @@ bool nfc_scene_mf_classic_dict_attack_on_event(void* context, SceneManagerEvent
} else { } else {
notification_message(nfc->notifications, &sequence_success); notification_message(nfc->notifications, &sequence_success);
scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicReadSuccess); scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicReadSuccess);
DOLPHIN_DEED(DolphinDeedNfcReadSuccess); dolphin_deed(DolphinDeedNfcReadSuccess);
consumed = true; consumed = true;
} }
} else if(event.event == NfcWorkerEventAborted) { } else if(event.event == NfcWorkerEventAborted) {
@ -123,7 +123,7 @@ bool nfc_scene_mf_classic_dict_attack_on_event(void* context, SceneManagerEvent
notification_message(nfc->notifications, &sequence_success); notification_message(nfc->notifications, &sequence_success);
scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicReadSuccess); scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicReadSuccess);
// Counting failed attempts too // Counting failed attempts too
DOLPHIN_DEED(DolphinDeedNfcReadSuccess); dolphin_deed(DolphinDeedNfcReadSuccess);
consumed = true; consumed = true;
} }
} else if(event.event == NfcWorkerEventCardDetected) { } else if(event.event == NfcWorkerEventCardDetected) {

View File

@ -37,7 +37,7 @@ bool nfc_scene_mf_classic_keys_add_on_event(void* context, SceneManagerEvent eve
nfc->scene_manager, NfcSceneMfClassicKeysWarnDuplicate); nfc->scene_manager, NfcSceneMfClassicKeysWarnDuplicate);
} else if(mf_classic_dict_add_key(dict, nfc->byte_input_store)) { } else if(mf_classic_dict_add_key(dict, nfc->byte_input_store)) {
scene_manager_next_scene(nfc->scene_manager, NfcSceneSaveSuccess); scene_manager_next_scene(nfc->scene_manager, NfcSceneSaveSuccess);
DOLPHIN_DEED(DolphinDeedNfcMfcAdd); dolphin_deed(DolphinDeedNfcMfcAdd);
} else { } else {
scene_manager_next_scene(nfc->scene_manager, NfcSceneDictNotFound); scene_manager_next_scene(nfc->scene_manager, NfcSceneDictNotFound);
} }

View File

@ -54,14 +54,14 @@ bool nfc_scene_mf_classic_menu_on_event(void* context, SceneManagerEvent event)
} else if(event.event == SubmenuIndexEmulate) { } else if(event.event == SubmenuIndexEmulate) {
scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicEmulate); scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicEmulate);
if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneSetType)) { if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneSetType)) {
DOLPHIN_DEED(DolphinDeedNfcAddEmulate); dolphin_deed(DolphinDeedNfcAddEmulate);
} else { } else {
DOLPHIN_DEED(DolphinDeedNfcEmulate); dolphin_deed(DolphinDeedNfcEmulate);
} }
consumed = true; consumed = true;
} else if(event.event == SubmenuIndexDetectReader) { } else if(event.event == SubmenuIndexDetectReader) {
scene_manager_next_scene(nfc->scene_manager, NfcSceneDetectReader); scene_manager_next_scene(nfc->scene_manager, NfcSceneDetectReader);
DOLPHIN_DEED(DolphinDeedNfcDetectReader); dolphin_deed(DolphinDeedNfcDetectReader);
consumed = true; consumed = true;
} else if(event.event == SubmenuIndexInfo) { } else if(event.event == SubmenuIndexInfo) {
scene_manager_next_scene(nfc->scene_manager, NfcSceneNfcDataInfo); scene_manager_next_scene(nfc->scene_manager, NfcSceneNfcDataInfo);

View File

@ -34,7 +34,7 @@ static void nfc_scene_mf_classic_update_setup_view(Nfc* nfc) {
void nfc_scene_mf_classic_update_on_enter(void* context) { void nfc_scene_mf_classic_update_on_enter(void* context) {
Nfc* nfc = context; Nfc* nfc = context;
DOLPHIN_DEED(DolphinDeedNfcEmulate); dolphin_deed(DolphinDeedNfcEmulate);
scene_manager_set_scene_state( scene_manager_set_scene_state(
nfc->scene_manager, NfcSceneMfClassicUpdate, NfcSceneMfClassicUpdateStateCardSearch); nfc->scene_manager, NfcSceneMfClassicUpdate, NfcSceneMfClassicUpdateStateCardSearch);

View File

@ -8,7 +8,7 @@ void nfc_scene_mf_classic_update_success_popup_callback(void* context) {
void nfc_scene_mf_classic_update_success_on_enter(void* context) { void nfc_scene_mf_classic_update_success_on_enter(void* context) {
Nfc* nfc = context; Nfc* nfc = context;
DOLPHIN_DEED(DolphinDeedNfcSave); dolphin_deed(DolphinDeedNfcSave);
notification_message(nfc->notifications, &sequence_success); notification_message(nfc->notifications, &sequence_success);

View File

@ -34,7 +34,7 @@ static void nfc_scene_mf_classic_write_setup_view(Nfc* nfc) {
void nfc_scene_mf_classic_write_on_enter(void* context) { void nfc_scene_mf_classic_write_on_enter(void* context) {
Nfc* nfc = context; Nfc* nfc = context;
DOLPHIN_DEED(DolphinDeedNfcEmulate); dolphin_deed(DolphinDeedNfcEmulate);
scene_manager_set_scene_state( scene_manager_set_scene_state(
nfc->scene_manager, NfcSceneMfClassicWrite, NfcSceneMfClassicWriteStateCardSearch); nfc->scene_manager, NfcSceneMfClassicWrite, NfcSceneMfClassicWriteStateCardSearch);

View File

@ -8,7 +8,7 @@ void nfc_scene_mf_classic_write_success_popup_callback(void* context) {
void nfc_scene_mf_classic_write_success_on_enter(void* context) { void nfc_scene_mf_classic_write_success_on_enter(void* context) {
Nfc* nfc = context; Nfc* nfc = context;
DOLPHIN_DEED(DolphinDeedNfcSave); dolphin_deed(DolphinDeedNfcSave);
notification_message(nfc->notifications, &sequence_success); notification_message(nfc->notifications, &sequence_success);

View File

@ -50,9 +50,9 @@ bool nfc_scene_mf_desfire_menu_on_event(void* context, SceneManagerEvent event)
} else if(event.event == SubmenuIndexEmulateUid) { } else if(event.event == SubmenuIndexEmulateUid) {
scene_manager_next_scene(nfc->scene_manager, NfcSceneEmulateUid); scene_manager_next_scene(nfc->scene_manager, NfcSceneEmulateUid);
if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneSetType)) { if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneSetType)) {
DOLPHIN_DEED(DolphinDeedNfcAddEmulate); dolphin_deed(DolphinDeedNfcAddEmulate);
} else { } else {
DOLPHIN_DEED(DolphinDeedNfcEmulate); dolphin_deed(DolphinDeedNfcEmulate);
} }
consumed = true; consumed = true;
} else if(event.event == SubmenuIndexInfo) { } else if(event.event == SubmenuIndexInfo) {

View File

@ -60,9 +60,9 @@ bool nfc_scene_mf_ultralight_menu_on_event(void* context, SceneManagerEvent even
} else if(event.event == SubmenuIndexEmulate) { } else if(event.event == SubmenuIndexEmulate) {
scene_manager_next_scene(nfc->scene_manager, NfcSceneMfUltralightEmulate); scene_manager_next_scene(nfc->scene_manager, NfcSceneMfUltralightEmulate);
if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneSetType)) { if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneSetType)) {
DOLPHIN_DEED(DolphinDeedNfcAddEmulate); dolphin_deed(DolphinDeedNfcAddEmulate);
} else { } else {
DOLPHIN_DEED(DolphinDeedNfcEmulate); dolphin_deed(DolphinDeedNfcEmulate);
} }
consumed = true; consumed = true;
} else if(event.event == SubmenuIndexUnlock) { } else if(event.event == SubmenuIndexUnlock) {

View File

@ -61,7 +61,7 @@ bool nfc_scene_mf_ultralight_unlock_warn_on_event(void* context, SceneManagerEve
if(event.type == SceneManagerEventTypeCustom) { if(event.type == SceneManagerEventTypeCustom) {
if(event.event == DialogExResultRight) { if(event.event == DialogExResultRight) {
scene_manager_next_scene(nfc->scene_manager, NfcSceneMfUltralightReadAuth); scene_manager_next_scene(nfc->scene_manager, NfcSceneMfUltralightReadAuth);
DOLPHIN_DEED(DolphinDeedNfcRead); dolphin_deed(DolphinDeedNfcRead);
consumed = true; consumed = true;
} else if(event.event == DialogExResultLeft) { } else if(event.event == DialogExResultLeft) {
if(auth_method == MfUltralightAuthMethodAuto) { if(auth_method == MfUltralightAuthMethodAuto) {
@ -79,7 +79,7 @@ bool nfc_scene_mf_ultralight_unlock_warn_on_event(void* context, SceneManagerEve
if(event.type == SceneManagerEventTypeCustom) { if(event.type == SceneManagerEventTypeCustom) {
if(event.event == DialogExResultCenter) { if(event.event == DialogExResultCenter) {
scene_manager_next_scene(nfc->scene_manager, NfcSceneMfUltralightReadAuth); scene_manager_next_scene(nfc->scene_manager, NfcSceneMfUltralightReadAuth);
DOLPHIN_DEED(DolphinDeedNfcRead); dolphin_deed(DolphinDeedNfcRead);
consumed = true; consumed = true;
} }
} }

View File

@ -41,19 +41,165 @@ void nfc_scene_nfc_data_info_on_enter(void* context) {
temp_str, "\e#%s\n", nfc_mf_classic_type(dev_data->mf_classic_data.type)); temp_str, "\e#%s\n", nfc_mf_classic_type(dev_data->mf_classic_data.type));
} else if(protocol == NfcDeviceProtocolMifareDesfire) { } else if(protocol == NfcDeviceProtocolMifareDesfire) {
furi_string_cat_printf(temp_str, "\e#MIFARE DESFire\n"); furi_string_cat_printf(temp_str, "\e#MIFARE DESFire\n");
} else if(protocol == NfcDeviceProtocolNfcV) {
switch(dev_data->nfcv_data.sub_type) {
case NfcVTypePlain:
furi_string_cat_printf(temp_str, "\e#ISO15693\n");
break;
case NfcVTypeSlix:
furi_string_cat_printf(temp_str, "\e#ISO15693 SLIX\n");
break;
case NfcVTypeSlixS:
furi_string_cat_printf(temp_str, "\e#ISO15693 SLIX-S\n");
break;
case NfcVTypeSlixL:
furi_string_cat_printf(temp_str, "\e#ISO15693 SLIX-L\n");
break;
case NfcVTypeSlix2:
furi_string_cat_printf(temp_str, "\e#ISO15693 SLIX2\n");
break;
default:
furi_string_cat_printf(temp_str, "\e#ISO15693 (unknown)\n");
break;
}
} else { } else {
furi_string_cat_printf(temp_str, "\e#Unknown ISO tag\n"); furi_string_cat_printf(temp_str, "\e#Unknown ISO tag\n");
} }
// Set tag iso data // Set tag iso data
char iso_type = FURI_BIT(nfc_data->sak, 5) ? '4' : '3'; if(protocol == NfcDeviceProtocolNfcV) {
furi_string_cat_printf(temp_str, "ISO 14443-%c (NFC-A)\n", iso_type); NfcVData* nfcv_data = &nfc->dev->dev_data.nfcv_data;
furi_string_cat_printf(temp_str, "UID:");
for(size_t i = 0; i < nfc_data->uid_len; i++) { furi_string_cat_printf(temp_str, "UID:\n");
furi_string_cat_printf(temp_str, " %02X", nfc_data->uid[i]); for(size_t i = 0; i < nfc_data->uid_len; i++) {
furi_string_cat_printf(temp_str, " %02X", nfc_data->uid[i]);
}
furi_string_cat_printf(temp_str, "\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, " SAK: %02X", nfc_data->sak);
} }
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 // Set application specific data
if(protocol == NfcDeviceProtocolMifareDesfire) { if(protocol == NfcDeviceProtocolMifareDesfire) {
@ -139,6 +285,8 @@ bool nfc_scene_nfc_data_info_on_event(void* context, SceneManagerEvent event) {
consumed = true; consumed = true;
} else if(protocol == NfcDeviceProtocolMifareClassic) { } else if(protocol == NfcDeviceProtocolMifareClassic) {
scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicData); scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicData);
} else if(protocol == NfcDeviceProtocolNfcV) {
scene_manager_next_scene(nfc->scene_manager, NfcSceneNfcVMenu);
consumed = true; consumed = true;
} }
} }

View File

@ -43,9 +43,9 @@ bool nfc_scene_nfca_menu_on_event(void* context, SceneManagerEvent event) {
} else if(event.event == SubmenuIndexEmulateUid) { } else if(event.event == SubmenuIndexEmulateUid) {
scene_manager_next_scene(nfc->scene_manager, NfcSceneEmulateUid); scene_manager_next_scene(nfc->scene_manager, NfcSceneEmulateUid);
if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneSetType)) { if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneSetType)) {
DOLPHIN_DEED(DolphinDeedNfcAddEmulate); dolphin_deed(DolphinDeedNfcAddEmulate);
} else { } else {
DOLPHIN_DEED(DolphinDeedNfcEmulate); dolphin_deed(DolphinDeedNfcEmulate);
} }
consumed = true; consumed = true;
} else if(event.event == SubmenuIndexInfo) { } else if(event.event == SubmenuIndexInfo) {

View 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);
}

View 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, "");
}

View 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);
}

View 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);
}

View 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);
}

View 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);
}

View 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);
}

View File

@ -61,29 +61,34 @@ bool nfc_scene_read_on_event(void* context, SceneManagerEvent event) {
(event.event == NfcWorkerEventReadUidNfcV)) { (event.event == NfcWorkerEventReadUidNfcV)) {
notification_message(nfc->notifications, &sequence_success); notification_message(nfc->notifications, &sequence_success);
scene_manager_next_scene(nfc->scene_manager, NfcSceneReadCardSuccess); scene_manager_next_scene(nfc->scene_manager, NfcSceneReadCardSuccess);
DOLPHIN_DEED(DolphinDeedNfcReadSuccess); dolphin_deed(DolphinDeedNfcReadSuccess);
consumed = true; consumed = true;
} else if(event.event == NfcWorkerEventReadUidNfcA) { } else if(event.event == NfcWorkerEventReadUidNfcA) {
notification_message(nfc->notifications, &sequence_success); notification_message(nfc->notifications, &sequence_success);
scene_manager_next_scene(nfc->scene_manager, NfcSceneNfcaReadSuccess); scene_manager_next_scene(nfc->scene_manager, NfcSceneNfcaReadSuccess);
DOLPHIN_DEED(DolphinDeedNfcReadSuccess); dolphin_deed(DolphinDeedNfcReadSuccess);
consumed = true;
} else if(event.event == NfcWorkerEventReadNfcV) {
notification_message(nfc->notifications, &sequence_success);
scene_manager_next_scene(nfc->scene_manager, NfcSceneNfcVReadSuccess);
dolphin_deed(DolphinDeedNfcReadSuccess);
consumed = true; consumed = true;
} else if(event.event == NfcWorkerEventReadMfUltralight) { } else if(event.event == NfcWorkerEventReadMfUltralight) {
notification_message(nfc->notifications, &sequence_success); notification_message(nfc->notifications, &sequence_success);
// Set unlock password input to 0xFFFFFFFF only on fresh read // Set unlock password input to 0xFFFFFFFF only on fresh read
memset(nfc->byte_input_store, 0xFF, sizeof(nfc->byte_input_store)); memset(nfc->byte_input_store, 0xFF, sizeof(nfc->byte_input_store));
scene_manager_next_scene(nfc->scene_manager, NfcSceneMfUltralightReadSuccess); scene_manager_next_scene(nfc->scene_manager, NfcSceneMfUltralightReadSuccess);
DOLPHIN_DEED(DolphinDeedNfcReadSuccess); dolphin_deed(DolphinDeedNfcReadSuccess);
consumed = true; consumed = true;
} else if(event.event == NfcWorkerEventReadMfClassicDone) { } else if(event.event == NfcWorkerEventReadMfClassicDone) {
notification_message(nfc->notifications, &sequence_success); notification_message(nfc->notifications, &sequence_success);
scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicReadSuccess); scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicReadSuccess);
DOLPHIN_DEED(DolphinDeedNfcReadSuccess); dolphin_deed(DolphinDeedNfcReadSuccess);
consumed = true; consumed = true;
} else if(event.event == NfcWorkerEventReadMfDesfire) { } else if(event.event == NfcWorkerEventReadMfDesfire) {
notification_message(nfc->notifications, &sequence_success); notification_message(nfc->notifications, &sequence_success);
scene_manager_next_scene(nfc->scene_manager, NfcSceneMfDesfireReadSuccess); scene_manager_next_scene(nfc->scene_manager, NfcSceneMfDesfireReadSuccess);
DOLPHIN_DEED(DolphinDeedNfcReadSuccess); dolphin_deed(DolphinDeedNfcReadSuccess);
consumed = true; consumed = true;
} else if(event.event == NfcWorkerEventReadMfClassicDictAttackRequired) { } else if(event.event == NfcWorkerEventReadMfClassicDictAttackRequired) {
if(mf_classic_dict_check_presence(MfClassicDictTypeSystem)) { if(mf_classic_dict_check_presence(MfClassicDictTypeSystem)) {

View File

@ -11,7 +11,7 @@ void nfc_scene_restore_original_confirm_on_enter(void* context) {
DialogEx* dialog_ex = nfc->dialog_ex; DialogEx* dialog_ex = nfc->dialog_ex;
dialog_ex_set_header(dialog_ex, "Restore Card Data?", 64, 0, AlignCenter, AlignTop); dialog_ex_set_header(dialog_ex, "Restore Card Data?", 64, 0, AlignCenter, AlignTop);
dialog_ex_set_icon(dialog_ex, 5, 15, &I_Restoring_38x32); dialog_ex_set_icon(dialog_ex, 5, 11, &I_ArrowC_1_36x36);
dialog_ex_set_text( dialog_ex_set_text(
dialog_ex, "It will be returned\nto its original state.", 47, 21, AlignLeft, AlignTop); dialog_ex, "It will be returned\nto its original state.", 47, 21, AlignLeft, AlignTop);
dialog_ex_set_left_button_text(dialog_ex, "Cancel"); dialog_ex_set_left_button_text(dialog_ex, "Cancel");

View File

@ -55,6 +55,13 @@ bool nfc_scene_rpc_on_event(void* context, SceneManagerEvent event) {
&nfc->dev->dev_data, &nfc->dev->dev_data,
nfc_scene_rpc_emulate_callback, nfc_scene_rpc_emulate_callback,
nfc); nfc);
} else if(nfc->dev->format == NfcDeviceSaveFormatNfcV) {
nfc_worker_start(
nfc->worker,
NfcWorkerStateNfcVEmulate,
&nfc->dev->dev_data,
nfc_scene_rpc_emulate_callback,
nfc);
} else { } else {
nfc_worker_start( nfc_worker_start(
nfc->worker, NfcWorkerStateUidEmulate, &nfc->dev->dev_data, NULL, nfc); nfc->worker, NfcWorkerStateUidEmulate, &nfc->dev->dev_data, NULL, nfc);

View File

@ -67,9 +67,9 @@ bool nfc_scene_save_name_on_event(void* context, SceneManagerEvent event) {
if(!scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneSavedMenu)) { if(!scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneSavedMenu)) {
// Nothing, do not count editing as saving // Nothing, do not count editing as saving
} else if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneSetType)) { } else if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneSetType)) {
DOLPHIN_DEED(DolphinDeedNfcAddSave); dolphin_deed(DolphinDeedNfcAddSave);
} else { } else {
DOLPHIN_DEED(DolphinDeedNfcSave); dolphin_deed(DolphinDeedNfcSave);
} }
consumed = true; consumed = true;
} else { } else {

View File

@ -44,6 +44,7 @@ void nfc_scene_saved_menu_on_enter(void* context) {
} else if( } else if(
(nfc->dev->format == NfcDeviceSaveFormatMifareUl && (nfc->dev->format == NfcDeviceSaveFormatMifareUl &&
mf_ul_emulation_supported(&nfc->dev->dev_data.mf_ul_data)) || mf_ul_emulation_supported(&nfc->dev->dev_data.mf_ul_data)) ||
nfc->dev->format == NfcDeviceSaveFormatNfcV ||
nfc->dev->format == NfcDeviceSaveFormatMifareClassic) { nfc->dev->format == NfcDeviceSaveFormatMifareClassic) {
submenu_add_item( submenu_add_item(
submenu, "Emulate", SubmenuIndexEmulate, nfc_scene_saved_menu_submenu_callback, nfc); submenu, "Emulate", SubmenuIndexEmulate, nfc_scene_saved_menu_submenu_callback, nfc);
@ -118,14 +119,16 @@ bool nfc_scene_saved_menu_on_event(void* context, SceneManagerEvent event) {
scene_manager_next_scene(nfc->scene_manager, NfcSceneMfUltralightEmulate); scene_manager_next_scene(nfc->scene_manager, NfcSceneMfUltralightEmulate);
} else if(nfc->dev->format == NfcDeviceSaveFormatMifareClassic) { } else if(nfc->dev->format == NfcDeviceSaveFormatMifareClassic) {
scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicEmulate); scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicEmulate);
} else if(nfc->dev->format == NfcDeviceSaveFormatNfcV) {
scene_manager_next_scene(nfc->scene_manager, NfcSceneNfcVEmulate);
} else { } else {
scene_manager_next_scene(nfc->scene_manager, NfcSceneEmulateUid); scene_manager_next_scene(nfc->scene_manager, NfcSceneEmulateUid);
} }
DOLPHIN_DEED(DolphinDeedNfcEmulate); dolphin_deed(DolphinDeedNfcEmulate);
consumed = true; consumed = true;
} else if(event.event == SubmenuIndexDetectReader) { } else if(event.event == SubmenuIndexDetectReader) {
scene_manager_next_scene(nfc->scene_manager, NfcSceneDetectReader); scene_manager_next_scene(nfc->scene_manager, NfcSceneDetectReader);
DOLPHIN_DEED(DolphinDeedNfcDetectReader); dolphin_deed(DolphinDeedNfcDetectReader);
consumed = true; consumed = true;
} else if(event.event == SubmenuIndexWrite) { } else if(event.event == SubmenuIndexWrite) {
scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicWrite); scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicWrite);

View File

@ -51,7 +51,7 @@ bool nfc_scene_start_on_event(void* context, SceneManagerEvent event) {
scene_manager_set_scene_state(nfc->scene_manager, NfcSceneStart, SubmenuIndexRead); scene_manager_set_scene_state(nfc->scene_manager, NfcSceneStart, SubmenuIndexRead);
nfc->dev->dev_data.read_mode = NfcReadModeAuto; nfc->dev->dev_data.read_mode = NfcReadModeAuto;
scene_manager_next_scene(nfc->scene_manager, NfcSceneRead); scene_manager_next_scene(nfc->scene_manager, NfcSceneRead);
DOLPHIN_DEED(DolphinDeedNfcRead); dolphin_deed(DolphinDeedNfcRead);
consumed = true; consumed = true;
} else if(event.event == SubmenuIndexDetectReader) { } else if(event.event == SubmenuIndexDetectReader) {
scene_manager_set_scene_state( scene_manager_set_scene_state(
@ -60,7 +60,7 @@ bool nfc_scene_start_on_event(void* context, SceneManagerEvent event) {
if(sd_exist) { if(sd_exist) {
nfc_device_data_clear(&nfc->dev->dev_data); nfc_device_data_clear(&nfc->dev->dev_data);
scene_manager_next_scene(nfc->scene_manager, NfcSceneDetectReader); scene_manager_next_scene(nfc->scene_manager, NfcSceneDetectReader);
DOLPHIN_DEED(DolphinDeedNfcDetectReader); dolphin_deed(DolphinDeedNfcDetectReader);
} else { } else {
scene_manager_next_scene(nfc->scene_manager, NfcSceneDictNotFound); scene_manager_next_scene(nfc->scene_manager, NfcSceneDictNotFound);
} }

View File

@ -204,7 +204,7 @@ bool subghz_scene_read_raw_on_event(void* context, SceneManagerEvent event) {
} else { } else {
if(scene_manager_has_previous_scene(subghz->scene_manager, SubGhzSceneSaved) || if(scene_manager_has_previous_scene(subghz->scene_manager, SubGhzSceneSaved) ||
!scene_manager_has_previous_scene(subghz->scene_manager, SubGhzSceneStart)) { !scene_manager_has_previous_scene(subghz->scene_manager, SubGhzSceneStart)) {
DOLPHIN_DEED(DolphinDeedSubGhzSend); dolphin_deed(DolphinDeedSubGhzSend);
} }
// set callback end tx // set callback end tx
subghz_txrx_set_raw_file_encoder_worker_callback_end( subghz_txrx_set_raw_file_encoder_worker_callback_end(
@ -259,7 +259,7 @@ bool subghz_scene_read_raw_on_event(void* context, SceneManagerEvent event) {
} else { } else {
SubGhzRadioPreset preset = subghz_txrx_get_preset(subghz->txrx); SubGhzRadioPreset preset = subghz_txrx_get_preset(subghz->txrx);
if(subghz_protocol_raw_save_to_file_init(decoder_raw, RAW_FILE_NAME, &preset)) { if(subghz_protocol_raw_save_to_file_init(decoder_raw, RAW_FILE_NAME, &preset)) {
DOLPHIN_DEED(DolphinDeedSubGhzRawRec); dolphin_deed(DolphinDeedSubGhzRawRec);
subghz_txrx_rx_start(subghz->txrx); subghz_txrx_rx_start(subghz->txrx);
subghz->state_notifications = SubGhzNotificationStateRx; subghz->state_notifications = SubGhzNotificationStateRx;
subghz_rx_key_state_set(subghz, SubGhzRxKeyStateAddKey); subghz_rx_key_state_set(subghz, SubGhzRxKeyStateAddKey);

View File

@ -163,7 +163,7 @@ bool subghz_scene_receiver_on_event(void* context, SceneManagerEvent event) {
case SubGhzCustomEventViewReceiverOK: case SubGhzCustomEventViewReceiverOK:
subghz->idx_menu_chosen = subghz_view_receiver_get_idx_menu(subghz->subghz_receiver); subghz->idx_menu_chosen = subghz_view_receiver_get_idx_menu(subghz->subghz_receiver);
scene_manager_next_scene(subghz->scene_manager, SubGhzSceneReceiverInfo); scene_manager_next_scene(subghz->scene_manager, SubGhzSceneReceiverInfo);
DOLPHIN_DEED(DolphinDeedSubGhzReceiverInfo); dolphin_deed(DolphinDeedSubGhzReceiverInfo);
consumed = true; consumed = true;
break; break;
case SubGhzCustomEventViewReceiverConfig: case SubGhzCustomEventViewReceiverConfig:

View File

@ -137,9 +137,9 @@ bool subghz_scene_save_name_on_event(void* context, SceneManagerEvent event) {
// Ditto, for RAW signals // Ditto, for RAW signals
} else if(scene_manager_has_previous_scene( } else if(scene_manager_has_previous_scene(
subghz->scene_manager, SubGhzSceneSetType)) { subghz->scene_manager, SubGhzSceneSetType)) {
DOLPHIN_DEED(DolphinDeedSubGhzAddManually); dolphin_deed(DolphinDeedSubGhzAddManually);
} else { } else {
DOLPHIN_DEED(DolphinDeedSubGhzSave); dolphin_deed(DolphinDeedSubGhzSave);
} }
return true; return true;
} else { } else {

View File

@ -92,7 +92,7 @@ bool subghz_scene_start_on_event(void* context, SceneManagerEvent event) {
scene_manager_set_scene_state( scene_manager_set_scene_state(
subghz->scene_manager, SubGhzSceneStart, SubmenuIndexFrequencyAnalyzer); subghz->scene_manager, SubGhzSceneStart, SubmenuIndexFrequencyAnalyzer);
scene_manager_next_scene(subghz->scene_manager, SubGhzSceneFrequencyAnalyzer); scene_manager_next_scene(subghz->scene_manager, SubGhzSceneFrequencyAnalyzer);
DOLPHIN_DEED(DolphinDeedSubGhzFrequencyAnalyzer); dolphin_deed(DolphinDeedSubGhzFrequencyAnalyzer);
return true; return true;
} else if(event.event == SubmenuIndexTest) { } else if(event.event == SubmenuIndexTest) {
scene_manager_set_scene_state( scene_manager_set_scene_state(

View File

@ -61,7 +61,7 @@ bool subghz_scene_transmitter_on_event(void* context, SceneManagerEvent event) {
if(subghz_tx_start(subghz, subghz_txrx_get_fff_data(subghz->txrx))) { if(subghz_tx_start(subghz, subghz_txrx_get_fff_data(subghz->txrx))) {
subghz->state_notifications = SubGhzNotificationStateTx; subghz->state_notifications = SubGhzNotificationStateTx;
subghz_scene_transmitter_update_data_show(subghz); subghz_scene_transmitter_update_data_show(subghz);
DOLPHIN_DEED(DolphinDeedSubGhzSend); dolphin_deed(DolphinDeedSubGhzSend);
} }
return true; return true;
} else if(event.event == SubGhzCustomEventViewTransmitterSendStop) { } else if(event.event == SubGhzCustomEventViewTransmitterSendStop) {

View File

@ -68,7 +68,7 @@ bool u2f_scene_main_on_event(void* context, SceneManagerEvent event) {
notification_message(app->notifications, &sequence_blink_magenta_10); notification_message(app->notifications, &sequence_blink_magenta_10);
} else if(event.event == U2fCustomEventAuthSuccess) { } else if(event.event == U2fCustomEventAuthSuccess) {
notification_message_block(app->notifications, &sequence_set_green_255); notification_message_block(app->notifications, &sequence_set_green_255);
DOLPHIN_DEED(DolphinDeedU2fAuthorized); dolphin_deed(DolphinDeedU2fAuthorized);
furi_timer_start(app->timer, U2F_SUCCESS_TIMEOUT); furi_timer_start(app->timer, U2F_SUCCESS_TIMEOUT);
app->event_cur = U2fCustomEventNone; app->event_cur = U2fCustomEventNone;
u2f_view_set_state(app->u2f_view, U2fMsgSuccess); u2f_view_set_state(app->u2f_view, U2fMsgSuccess);

View File

@ -1,7 +1,7 @@
#include "bt_i.h" #include "bt_i.h"
#include "battery_service.h"
#include "bt_keys_storage.h" #include "bt_keys_storage.h"
#include <services/battery_service.h>
#include <notification/notification_messages.h> #include <notification/notification_messages.h>
#include <gui/elements.h> #include <gui/elements.h>
#include <assets_icons.h> #include <assets_icons.h>

View File

@ -34,13 +34,13 @@ bool desktop_scene_debug_on_event(void* context, SceneManagerEvent event) {
break; break;
case DesktopDebugEventDeed: case DesktopDebugEventDeed:
dolphin_deed(dolphin, DolphinDeedTestRight); dolphin_deed(DolphinDeedTestRight);
desktop_debug_get_dolphin_data(desktop->debug_view); desktop_debug_get_dolphin_data(desktop->debug_view);
consumed = true; consumed = true;
break; break;
case DesktopDebugEventWrongDeed: case DesktopDebugEventWrongDeed:
dolphin_deed(dolphin, DolphinDeedTestLeft); dolphin_deed(DolphinDeedTestLeft);
desktop_debug_get_dolphin_data(desktop->debug_view); desktop_debug_get_dolphin_data(desktop->debug_view);
consumed = true; consumed = true;
break; break;

View File

@ -13,12 +13,13 @@
static void dolphin_update_clear_limits_timer_period(Dolphin* dolphin); static void dolphin_update_clear_limits_timer_period(Dolphin* dolphin);
void dolphin_deed(Dolphin* dolphin, DolphinDeed deed) { void dolphin_deed(DolphinDeed deed) {
furi_assert(dolphin); Dolphin* dolphin = (Dolphin*)furi_record_open(RECORD_DOLPHIN);
DolphinEvent event; DolphinEvent event;
event.type = DolphinEventTypeDeed; event.type = DolphinEventTypeDeed;
event.deed = deed; event.deed = deed;
dolphin_event_send_async(dolphin, &event); dolphin_event_send_async(dolphin, &event);
furi_record_close(RECORD_DOLPHIN);
} }
DolphinStats dolphin_stats(Dolphin* dolphin) { DolphinStats dolphin_stats(Dolphin* dolphin) {

View File

@ -26,18 +26,11 @@ typedef enum {
DolphinPubsubEventUpdate, DolphinPubsubEventUpdate,
} DolphinPubsubEvent; } DolphinPubsubEvent;
#define DOLPHIN_DEED(deed) \
do { \
Dolphin* dolphin = (Dolphin*)furi_record_open("dolphin"); \
dolphin_deed(dolphin, deed); \
furi_record_close("dolphin"); \
} while(0)
/** Deed complete notification. Call it on deed completion. /** Deed complete notification. Call it on deed completion.
* See dolphin_deed.h for available deeds. In futures it will become part of assets. * See dolphin_deed.h for available deeds. In futures it will become part of assets.
* Thread safe, async * Thread safe, async
*/ */
void dolphin_deed(Dolphin* dolphin, DolphinDeed deed); void dolphin_deed(DolphinDeed deed);
/** Retrieve dolphin stats /** Retrieve dolphin stats
* Thread safe, blocking * Thread safe, blocking

View File

@ -75,6 +75,8 @@ typedef enum {
NotificationMessageTypeForceDisplayBrightnessSetting, NotificationMessageTypeForceDisplayBrightnessSetting,
NotificationMessageTypeLedBrightnessSettingApply, NotificationMessageTypeLedBrightnessSettingApply,
NotificationMessageTypeLcdContrastUpdate,
} NotificationMessageType; } NotificationMessageType;
typedef struct { typedef struct {

View File

@ -3,6 +3,9 @@
#include <furi_hal.h> #include <furi_hal.h>
#include <storage/storage.h> #include <storage/storage.h>
#include <input/input.h> #include <input/input.h>
#include <gui/gui_i.h>
#include <u8g2_glue.h>
#include "notification.h" #include "notification.h"
#include "notification_messages.h" #include "notification_messages.h"
#include "notification_app.h" #include "notification_app.h"
@ -20,14 +23,14 @@ static const uint8_t reset_sound_mask = 1 << 4;
static const uint8_t reset_display_mask = 1 << 5; static const uint8_t reset_display_mask = 1 << 5;
static const uint8_t reset_blink_mask = 1 << 6; static const uint8_t reset_blink_mask = 1 << 6;
void notification_vibro_on(bool force); static void notification_vibro_on(bool force);
void notification_vibro_off(); static void notification_vibro_off();
void notification_sound_on(float freq, float volume, bool force); static void notification_sound_on(float freq, float volume, bool force);
void notification_sound_off(); static void notification_sound_off();
uint8_t notification_settings_get_display_brightness(NotificationApp* app, uint8_t value); static uint8_t notification_settings_get_display_brightness(NotificationApp* app, uint8_t value);
uint8_t notification_settings_get_rgb_led_brightness(NotificationApp* app, uint8_t value); static uint8_t notification_settings_get_rgb_led_brightness(NotificationApp* app, uint8_t value);
uint32_t notification_settings_display_off_delay_ticks(NotificationApp* app); static uint32_t notification_settings_display_off_delay_ticks(NotificationApp* app);
void notification_message_save_settings(NotificationApp* app) { void notification_message_save_settings(NotificationApp* app) {
NotificationAppMessage m = { NotificationAppMessage m = {
@ -39,7 +42,8 @@ void notification_message_save_settings(NotificationApp* app) {
}; };
// internal layer // internal layer
void notification_apply_internal_led_layer(NotificationLedLayer* layer, uint8_t layer_value) { static void
notification_apply_internal_led_layer(NotificationLedLayer* layer, uint8_t layer_value) {
furi_assert(layer); furi_assert(layer);
furi_assert(layer->index < LayerMAX); furi_assert(layer->index < LayerMAX);
@ -52,7 +56,13 @@ void notification_apply_internal_led_layer(NotificationLedLayer* layer, uint8_t
} }
} }
bool notification_is_any_led_layer_internal_and_not_empty(NotificationApp* app) { static void notification_apply_lcd_contrast(NotificationApp* app) {
Gui* gui = furi_record_open(RECORD_GUI);
u8x8_d_st756x_set_contrast(&gui->canvas->fb.u8x8, app->settings.contrast);
furi_record_close(RECORD_GUI);
}
static bool notification_is_any_led_layer_internal_and_not_empty(NotificationApp* app) {
bool result = false; bool result = false;
if((app->led[0].index == LayerInternal) || (app->led[1].index == LayerInternal) || if((app->led[0].index == LayerInternal) || (app->led[1].index == LayerInternal) ||
(app->led[2].index == LayerInternal)) { (app->led[2].index == LayerInternal)) {
@ -67,7 +77,7 @@ bool notification_is_any_led_layer_internal_and_not_empty(NotificationApp* app)
} }
// notification layer // notification layer
void notification_apply_notification_led_layer( static void notification_apply_notification_led_layer(
NotificationLedLayer* layer, NotificationLedLayer* layer,
const uint8_t layer_value) { const uint8_t layer_value) {
furi_assert(layer); furi_assert(layer);
@ -81,7 +91,7 @@ void notification_apply_notification_led_layer(
furi_hal_light_set(layer->light, layer->value[LayerNotification]); furi_hal_light_set(layer->light, layer->value[LayerNotification]);
} }
void notification_reset_notification_led_layer(NotificationLedLayer* layer) { static void notification_reset_notification_led_layer(NotificationLedLayer* layer) {
furi_assert(layer); furi_assert(layer);
furi_assert(layer->index < LayerMAX); furi_assert(layer->index < LayerMAX);
@ -94,7 +104,7 @@ void notification_reset_notification_led_layer(NotificationLedLayer* layer) {
furi_hal_light_set(layer->light, layer->value[LayerInternal]); furi_hal_light_set(layer->light, layer->value[LayerInternal]);
} }
void notification_reset_notification_layer(NotificationApp* app, uint8_t reset_mask) { static void notification_reset_notification_layer(NotificationApp* app, uint8_t reset_mask) {
if(reset_mask & reset_blink_mask) { if(reset_mask & reset_blink_mask) {
furi_hal_light_blink_stop(); furi_hal_light_blink_stop();
} }
@ -130,28 +140,28 @@ uint8_t notification_settings_get_display_brightness(NotificationApp* app, uint8
return (value * app->settings.display_brightness); return (value * app->settings.display_brightness);
} }
uint8_t notification_settings_get_rgb_led_brightness(NotificationApp* app, uint8_t value) { static uint8_t notification_settings_get_rgb_led_brightness(NotificationApp* app, uint8_t value) {
return (value * app->settings.led_brightness); return (value * app->settings.led_brightness);
} }
uint32_t notification_settings_display_off_delay_ticks(NotificationApp* app) { static uint32_t notification_settings_display_off_delay_ticks(NotificationApp* app) {
return ( return (
(float)(app->settings.display_off_delay_ms) / (float)(app->settings.display_off_delay_ms) /
(1000.0f / furi_kernel_get_tick_frequency())); (1000.0f / furi_kernel_get_tick_frequency()));
} }
// generics // generics
void notification_vibro_on(bool force) { static void notification_vibro_on(bool force) {
if(!furi_hal_rtc_is_flag_set(FuriHalRtcFlagStealthMode) || force) { if(!furi_hal_rtc_is_flag_set(FuriHalRtcFlagStealthMode) || force) {
furi_hal_vibro_on(true); furi_hal_vibro_on(true);
} }
} }
void notification_vibro_off() { static void notification_vibro_off() {
furi_hal_vibro_on(false); furi_hal_vibro_on(false);
} }
void notification_sound_on(float freq, float volume, bool force) { static void notification_sound_on(float freq, float volume, bool force) {
if(!furi_hal_rtc_is_flag_set(FuriHalRtcFlagStealthMode) || force) { if(!furi_hal_rtc_is_flag_set(FuriHalRtcFlagStealthMode) || force) {
if(furi_hal_speaker_is_mine() || furi_hal_speaker_acquire(30)) { if(furi_hal_speaker_is_mine() || furi_hal_speaker_acquire(30)) {
furi_hal_speaker_start(freq, volume); furi_hal_speaker_start(freq, volume);
@ -159,7 +169,7 @@ void notification_sound_on(float freq, float volume, bool force) {
} }
} }
void notification_sound_off() { static void notification_sound_off() {
if(furi_hal_speaker_is_mine()) { if(furi_hal_speaker_is_mine()) {
furi_hal_speaker_stop(); furi_hal_speaker_stop();
furi_hal_speaker_release(); furi_hal_speaker_release();
@ -174,7 +184,7 @@ static void notification_display_timer(void* ctx) {
} }
// message processing // message processing
void notification_process_notification_message( static void notification_process_notification_message(
NotificationApp* app, NotificationApp* app,
NotificationAppMessage* message) { NotificationAppMessage* message) {
uint32_t notification_message_index = 0; uint32_t notification_message_index = 0;
@ -333,6 +343,9 @@ void notification_process_notification_message(
reset_mask |= reset_green_mask; reset_mask |= reset_green_mask;
reset_mask |= reset_blue_mask; reset_mask |= reset_blue_mask;
break; break;
case NotificationMessageTypeLcdContrastUpdate:
notification_apply_lcd_contrast(app);
break;
} }
notification_message_index++; notification_message_index++;
notification_message = (*message->sequence)[notification_message_index]; notification_message = (*message->sequence)[notification_message_index];
@ -361,7 +374,8 @@ void notification_process_notification_message(
} }
} }
void notification_process_internal_message(NotificationApp* app, NotificationAppMessage* message) { static void
notification_process_internal_message(NotificationApp* app, NotificationAppMessage* message) {
uint32_t notification_message_index = 0; uint32_t notification_message_index = 0;
const NotificationMessage* notification_message; const NotificationMessage* notification_message;
notification_message = (*message->sequence)[notification_message_index]; notification_message = (*message->sequence)[notification_message_index];
@ -548,6 +562,7 @@ int32_t notification_srv(void* p) {
notification_apply_internal_led_layer(&app->led[0], 0x00); notification_apply_internal_led_layer(&app->led[0], 0x00);
notification_apply_internal_led_layer(&app->led[1], 0x00); notification_apply_internal_led_layer(&app->led[1], 0x00);
notification_apply_internal_led_layer(&app->led[2], 0x00); notification_apply_internal_led_layer(&app->led[2], 0x00);
notification_apply_lcd_contrast(app);
furi_record_create(RECORD_NOTIFICATION, app); furi_record_create(RECORD_NOTIFICATION, app);

View File

@ -32,7 +32,7 @@ typedef struct {
Light light; Light light;
} NotificationLedLayer; } NotificationLedLayer;
#define NOTIFICATION_SETTINGS_VERSION 0x01 #define NOTIFICATION_SETTINGS_VERSION 0x02
#define NOTIFICATION_SETTINGS_PATH INT_PATH(NOTIFICATION_SETTINGS_FILE_NAME) #define NOTIFICATION_SETTINGS_PATH INT_PATH(NOTIFICATION_SETTINGS_FILE_NAME)
typedef struct { typedef struct {
@ -41,6 +41,7 @@ typedef struct {
float led_brightness; float led_brightness;
float speaker_volume; float speaker_volume;
uint32_t display_off_delay_ms; uint32_t display_off_delay_ms;
int8_t contrast;
bool vibro_on; bool vibro_on;
} NotificationSettings; } NotificationSettings;

View File

@ -197,6 +197,10 @@ const NotificationMessage message_force_display_brightness_setting_1f = {
.data.forced_settings.display_brightness = 1.0f, .data.forced_settings.display_brightness = 1.0f,
}; };
const NotificationMessage message_lcd_contrast_update = {
.type = NotificationMessageTypeLcdContrastUpdate,
};
/****************************** Message sequences ******************************/ /****************************** Message sequences ******************************/
// Reset // Reset
@ -566,3 +570,8 @@ const NotificationSequence sequence_audiovisual_alert = {
&message_vibro_off, &message_vibro_off,
NULL, NULL,
}; };
const NotificationSequence sequence_lcd_contrast_update = {
&message_lcd_contrast_update,
NULL,
};

View File

@ -63,6 +63,9 @@ extern const NotificationMessage message_force_vibro_setting_on;
extern const NotificationMessage message_force_vibro_setting_off; extern const NotificationMessage message_force_vibro_setting_off;
extern const NotificationMessage message_force_display_brightness_setting_1f; extern const NotificationMessage message_force_display_brightness_setting_1f;
// LCD Messages
extern const NotificationMessage message_lcd_contrast_update;
/****************************** Message sequences ******************************/ /****************************** Message sequences ******************************/
// Reset // Reset
@ -138,6 +141,9 @@ extern const NotificationSequence sequence_success;
extern const NotificationSequence sequence_error; extern const NotificationSequence sequence_error;
extern const NotificationSequence sequence_audiovisual_alert; extern const NotificationSequence sequence_audiovisual_alert;
// LCD
extern const NotificationSequence sequence_lcd_contrast_update;
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif #endif

View File

@ -20,6 +20,34 @@ static const NotificationSequence sequence_note_c = {
NULL, NULL,
}; };
#define CONTRAST_COUNT 11
const char* const contrast_text[CONTRAST_COUNT] = {
"-5",
"-4",
"-3",
"-2",
"-1",
"0",
"+1",
"+2",
"+3",
"+4",
"+5",
};
const int32_t contrast_value[CONTRAST_COUNT] = {
-5,
-4,
-3,
-2,
-1,
0,
1,
2,
3,
4,
5,
};
#define BACKLIGHT_COUNT 5 #define BACKLIGHT_COUNT 5
const char* const backlight_text[BACKLIGHT_COUNT] = { const char* const backlight_text[BACKLIGHT_COUNT] = {
"0%", "0%",
@ -64,6 +92,15 @@ const char* const vibro_text[VIBRO_COUNT] = {
}; };
const bool vibro_value[VIBRO_COUNT] = {false, true}; const bool vibro_value[VIBRO_COUNT] = {false, true};
static void contrast_changed(VariableItem* item) {
NotificationAppSettings* app = variable_item_get_context(item);
uint8_t index = variable_item_get_current_value_index(item);
variable_item_set_current_value_text(item, contrast_text[index]);
app->notification->settings.contrast = contrast_value[index];
notification_message(app->notification, &sequence_lcd_contrast_update);
}
static void backlight_changed(VariableItem* item) { static void backlight_changed(VariableItem* item) {
NotificationAppSettings* app = variable_item_get_context(item); NotificationAppSettings* app = variable_item_get_context(item);
uint8_t index = variable_item_get_current_value_index(item); uint8_t index = variable_item_get_current_value_index(item);
@ -136,6 +173,13 @@ static NotificationAppSettings* alloc_settings() {
VariableItem* item; VariableItem* item;
uint8_t value_index; uint8_t value_index;
item = variable_item_list_add(
app->variable_item_list, "LCD Contrast", CONTRAST_COUNT, contrast_changed, app);
value_index =
value_index_int32(app->notification->settings.contrast, contrast_value, CONTRAST_COUNT);
variable_item_set_current_value_index(item, value_index);
variable_item_set_current_value_text(item, contrast_text[value_index]);
item = variable_item_list_add( item = variable_item_list_add(
app->variable_item_list, "LCD Backlight", BACKLIGHT_COUNT, backlight_changed, app); app->variable_item_list, "LCD Backlight", BACKLIGHT_COUNT, backlight_changed, app);
value_index = value_index_float( value_index = value_index_float(

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Some files were not shown because too many files have changed in this diff Show More