Skip to content

Commit 2d7c8dd

Browse files
committed
added playlist support, cleanup
1 parent 4ef694f commit 2d7c8dd

18 files changed

+428
-119
lines changed

actions/action.c

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,20 @@
11

2-
#include "app_state.h"
2+
#include "quac.h"
33
#include "item.h"
44
#include "action_i.h"
55

6-
void action_tx(void* context, Item* item) {
6+
void action_tx(void* context, Item* item, FuriString* error) {
77
FURI_LOG_I(TAG, "action_run: %s : %s", furi_string_get_cstr(item->name), item->ext);
88

99
if(!strcmp(item->ext, ".sub")) {
10-
action_subghz_tx(context, item);
10+
action_subghz_tx(context, item->path, error);
1111
} else if(!strcmp(item->ext, ".ir")) {
12-
action_ir_tx(context, item);
12+
action_ir_tx(context, item->path, error);
1313
} else if(!strcmp(item->ext, ".rfid")) {
14-
action_rfid_tx(context, item);
14+
action_rfid_tx(context, item->path, error);
15+
} else if(!strcmp(item->ext, ".qpl")) {
16+
action_qpl_tx(context, item->path, error);
1517
} else {
16-
FURI_LOG_E(TAG, "Unknown item type! %s", item->ext);
18+
FURI_LOG_E(TAG, "Unknown item file type! %s", item->ext);
1719
}
1820
}

actions/action.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,4 @@
22

33
struct Item;
44

5-
void action_tx(void* context, Item* item);
5+
void action_tx(void* context, Item* item, FuriString* error);

actions/action_i.h

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,11 @@
11
#pragma once
22

3-
#include "../flipper.h"
43
#include <furi.h>
54
#include <furi_hal.h>
65

7-
#include <flipper_format/flipper_format.h>
6+
#define ACTION_SET_ERROR(_msg_fmt, ...) furi_string_printf(error, _msg_fmt, ##__VA_ARGS__)
87

9-
#include "../app_state.h"
10-
#include "../item.h"
11-
12-
void action_subghz_tx(void* context, Item* item);
13-
void action_rfid_tx(void* context, Item* item);
14-
void action_ir_tx(void* context, Item* item);
8+
void action_subghz_tx(void* context, FuriString* action_path, FuriString* error);
9+
void action_rfid_tx(void* context, FuriString* action_path, FuriString* error);
10+
void action_ir_tx(void* context, FuriString* action_path, FuriString* error);
11+
void action_qpl_tx(void* context, FuriString* action_path, FuriString* error);

actions/action_ir.c

Lines changed: 41 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,47 @@
11
// Methods for IR transmission
22

3+
#include <infrared.h>
4+
#include <infrared/encoder_decoder/infrared.h>
5+
#include <applications/services/cli/cli.h>
6+
37
#include "action_i.h"
8+
#include "quac.h"
9+
10+
typedef struct {
11+
size_t timings_size; /**< Number of elements in the timings array. */
12+
uint32_t* timings; /**< Pointer to an array of timings describing the signal. */
13+
uint32_t frequency; /**< Carrier frequency of the signal. */
14+
float duty_cycle; /**< Duty cycle of the signal. */
15+
} InfraredRawSignal;
16+
17+
typedef struct InfraredSignal {
18+
bool is_raw;
19+
union {
20+
InfraredMessage message;
21+
InfraredRawSignal raw;
22+
} payload;
23+
} InfraredSignal;
424

5-
void action_ir_tx(void* context, Item* item) {
25+
InfraredSignal* infrared_signal_alloc() {
26+
InfraredSignal* signal = malloc(sizeof(InfraredSignal));
27+
signal->is_raw = false;
28+
signal->payload.message.protocol = InfraredProtocolUnknown;
29+
return signal;
30+
}
31+
32+
void action_ir_tx(void* context, FuriString* action_path, FuriString* error) {
33+
UNUSED(action_path);
34+
UNUSED(error);
635
UNUSED(context);
7-
UNUSED(item);
36+
// App* app = context;
37+
38+
// InfraredSignal* signal = infrared_signal_alloc();
39+
// const char* ir_file = furi_string_get_cstr(action_path);
40+
// bool success = infrared_parse_message(ir_file, signal) || infrared_parse_raw(ir_file, signal);
41+
// if(success) {
42+
// infrared_signal_transmit(signal);
43+
// } else {
44+
// ACTION_SET_ERROR("IR: Error sending signal");
45+
// }
46+
// infrared_signal_free(signal);
847
}

actions/action_qpl.c

Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
1+
// Methods for Quac Playlist transmission
2+
3+
#include <toolbox/stream/stream.h>
4+
#include <toolbox/stream/file_stream.h>
5+
#include <toolbox/path.h>
6+
#include <toolbox/args.h>
7+
8+
#include <notification/notification_messages.h>
9+
10+
#include "action_i.h"
11+
#include "quac.h"
12+
13+
/** Open the Playlist file and then transmit each action
14+
* Each line of the playlist file is one of:
15+
* <file_path>
16+
* Full SD card path, or relative path to action to be transmitted. Must be
17+
* one of the supported filetypes (.sub, .rfid, [.ir coming soon])
18+
* pause <ms> - NOT IMPLEMENTED
19+
* Pauses the playback for 'ms' milliseconds.
20+
*
21+
* Blank lines, and comments (start with '#') are ignored. Whitespace is trimmed.
22+
*
23+
* Not yet Implemented:
24+
* - For RFID files, if they have a space followed by a number after their name,
25+
* that number will be the duration of that RFID tx
26+
*/
27+
void action_qpl_tx(void* context, FuriString* action_path, FuriString* error) {
28+
App* app = context;
29+
30+
FuriString* buffer;
31+
buffer = furi_string_alloc();
32+
33+
Stream* file = file_stream_alloc(app->storage);
34+
if(file_stream_open(file, furi_string_get_cstr(action_path), FSAM_READ, FSOM_OPEN_EXISTING)) {
35+
while(stream_read_line(file, buffer)) {
36+
furi_string_trim(buffer); // remove '\n\r' line endings, cleanup spaces
37+
FURI_LOG_I(TAG, "line: %s", furi_string_get_cstr(buffer));
38+
39+
// Skip blank lines
40+
if(furi_string_size(buffer) == 0) {
41+
continue;
42+
}
43+
44+
// Skip comments
45+
char first_char = furi_string_get_char(buffer, 0);
46+
if(first_char == '#') {
47+
continue;
48+
}
49+
50+
// Check if buffer is a "command", and not just a filename
51+
// Commands will contain spaces
52+
bool processed_special_command = false;
53+
FuriString* args_tmp;
54+
args_tmp = furi_string_alloc();
55+
do {
56+
if(!args_read_string_and_trim(buffer, args_tmp)) {
57+
// No spaces found, buffer and args_tmp are now have same contents
58+
break;
59+
}
60+
61+
// FURI_LOG_I(
62+
// TAG,
63+
// "args_temp: '%s', buffer: '%s'",
64+
// furi_string_get_cstr(args_tmp),
65+
// furi_string_get_cstr(buffer));
66+
67+
// OK, there's a space, and args_tmp is the first token, buffer is the rest
68+
if(furi_string_cmpi_str(args_tmp, "pause") == 0) {
69+
processed_special_command = true;
70+
uint32_t pause_length = 0;
71+
if(sscanf(furi_string_get_cstr(buffer), "%lu", &pause_length) == 1) {
72+
FURI_LOG_I(TAG, "Pausing playlist for %lu ms", pause_length);
73+
furi_delay_ms(pause_length);
74+
} else {
75+
ACTION_SET_ERROR("Playlist: Invalid or missing pause time");
76+
}
77+
break;
78+
}
79+
80+
// FURI_LOG_I(TAG, "Still checking for commands...");
81+
// FURI_LOG_I(
82+
// TAG,
83+
// "args_temp: '%s', buffer: '%s'",
84+
// furi_string_get_cstr(args_tmp),
85+
// furi_string_get_cstr(buffer));
86+
87+
// First token wasn't "pause", so maybe args_tmp is a .rfid filename followed
88+
// by a transmit duration in ms in buffer
89+
// Note: Not using path_extract_extension since it expects to find slashes in the
90+
// path, and thus won't work if we have a relative path file
91+
char ext[MAX_EXT_LEN + 1] = "";
92+
size_t dot = furi_string_search_rchar(args_tmp, '.');
93+
if(dot != FURI_STRING_FAILURE && furi_string_size(args_tmp) - dot <= MAX_EXT_LEN) {
94+
strlcpy(ext, &(furi_string_get_cstr(args_tmp))[dot], MAX_EXT_LEN);
95+
}
96+
97+
// FURI_LOG_I(TAG, " - Found extension of %s", ext);
98+
uint32_t rfid_duration = 0;
99+
if(!strcmp(ext, ".rfid")) {
100+
// FURI_LOG_I(TAG, "RFID file with duration");
101+
if(sscanf(furi_string_get_cstr(buffer), "%lu", &rfid_duration) == 1) {
102+
FURI_LOG_I(TAG, "RFID duration = %lu", rfid_duration);
103+
// TODO: Need to get the duration to the action_rfid_tx command...
104+
}
105+
}
106+
107+
} while(false);
108+
109+
furi_string_swap(buffer, args_tmp);
110+
furi_string_free(args_tmp);
111+
112+
if(processed_special_command) {
113+
continue;
114+
}
115+
116+
first_char = furi_string_get_char(buffer, 0);
117+
// Using relative paths? Prepend path of our playlist file
118+
if(first_char != '/') {
119+
FuriString* dirname;
120+
dirname = furi_string_alloc();
121+
path_extract_dirname(furi_string_get_cstr(action_path), dirname);
122+
furi_string_cat_printf(dirname, "/%s", furi_string_get_cstr(buffer));
123+
furi_string_swap(dirname, buffer);
124+
furi_string_free(dirname);
125+
}
126+
127+
char ext[MAX_EXT_LEN + 1] = "";
128+
path_extract_extension(buffer, ext, MAX_EXT_LEN);
129+
if(!strcmp(ext, ".sub")) {
130+
action_subghz_tx(context, buffer, error);
131+
} else if(!strcmp(ext, ".ir")) {
132+
action_ir_tx(context, buffer, error);
133+
} else if(!strcmp(ext, ".rfid")) {
134+
action_rfid_tx(context, buffer, error);
135+
} else if(!strcmp(ext, ".qpl")) {
136+
ACTION_SET_ERROR("Playlist: Can't call playlist from playlist");
137+
} else {
138+
ACTION_SET_ERROR(
139+
"Playlist: Unknown file/command! %s", furi_string_get_cstr(buffer));
140+
}
141+
142+
if(furi_string_size(error)) {
143+
// Abort playing the playlist - one of our actions failed
144+
break;
145+
}
146+
147+
// Playlist action complete!
148+
// TODO: Do we need a small delay (say 25ms) between actions?
149+
// TODO: Should we blip the light a diff color to indicate that
150+
// we're done with one command and moving to the next?
151+
// furi_delay_ms(25);
152+
}
153+
} else {
154+
ACTION_SET_ERROR("Could not open playlist");
155+
}
156+
157+
furi_string_free(buffer);
158+
file_stream_close(file);
159+
stream_free(file);
160+
}

actions/action_rfid.c

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,17 @@
77
#include <lfrfid/lfrfid_raw_file.h>
88
#include <lib/toolbox/args.h>
99

10+
#include <flipper_format/flipper_format.h>
11+
1012
#include "action_i.h"
13+
#include "quac.h"
1114

1215
// lifted from flipperzero-firmware/applications/main/lfrfid/lfrfid_cli.c
13-
void action_rfid_tx(void* context, Item* item) {
16+
void action_rfid_tx(void* context, FuriString* action_path, FuriString* error) {
17+
UNUSED(error);
18+
1419
App* app = context;
15-
FuriString* file_name = item->path;
20+
FuriString* file_name = action_path;
1621

1722
FlipperFormat* fff_data_file = flipper_format_file_alloc(app->storage);
1823
FuriString* temp_str;
@@ -33,39 +38,39 @@ void action_rfid_tx(void* context, Item* item) {
3338
bool successful_read = false;
3439
do {
3540
if(!flipper_format_file_open_existing(fff_data_file, furi_string_get_cstr(file_name))) {
36-
FURI_LOG_E(TAG, "Error opening %s", furi_string_get_cstr(file_name));
41+
ACTION_SET_ERROR("RFID: Error opening %s", furi_string_get_cstr(file_name));
3742
break;
3843
}
3944
FURI_LOG_I(TAG, "Opened file");
4045
if(!flipper_format_read_header(fff_data_file, temp_str, &temp_data32)) {
41-
FURI_LOG_E(TAG, "Missing or incorrect header");
46+
ACTION_SET_ERROR("RFID: Missing or incorrect header");
4247
break;
4348
}
4449
FURI_LOG_I(TAG, "Read file headers");
4550
// TODO: add better header checks here...
4651
if(!strcmp(furi_string_get_cstr(temp_str), "Flipper RFID key")) {
4752
} else {
48-
FURI_LOG_E(TAG, "Type or version mismatch");
53+
ACTION_SET_ERROR("RFID: Type or version mismatch");
4954
break;
5055
}
5156

5257
// read and check the protocol field
5358
if(!flipper_format_read_string(fff_data_file, "Key type", protocol_name)) {
54-
FURI_LOG_E(TAG, "Error reading protocol");
59+
ACTION_SET_ERROR("RFID: Error reading protocol");
5560
break;
5661
}
5762
protocol = protocol_dict_get_protocol_by_name(dict, furi_string_get_cstr(protocol_name));
5863
if(protocol == PROTOCOL_NO) {
59-
FURI_LOG_E(TAG, "Unknown protocol: %s", furi_string_get_cstr(protocol_name));
64+
ACTION_SET_ERROR("RFID: Unknown protocol: %s", furi_string_get_cstr(protocol_name));
6065
break;
6166
}
62-
FURI_LOG_I(TAG, "Protocol OK");
6367

6468
// read and check data field
6569
size_t required_size = protocol_dict_get_data_size(dict, protocol);
6670
FURI_LOG_I(TAG, "Protocol req data size is %d", required_size);
6771
if(!flipper_format_read_hex(fff_data_file, "Data", data, required_size)) {
6872
FURI_LOG_E(TAG, "Error reading data");
73+
ACTION_SET_ERROR("RFID: Error reading data");
6974
break;
7075
}
7176
// FURI_LOG_I(TAG, "Data: %s", furi_string_get_cstr(data_text));
@@ -90,14 +95,14 @@ void action_rfid_tx(void* context, Item* item) {
9095
lfrfid_worker_start_thread(worker);
9196
lfrfid_worker_emulate_start(worker, protocol);
9297

93-
printf("Emulating RFID...\r\nPress Ctrl+C to abort\r\n");
98+
FURI_LOG_I(TAG, "Emulating RFID...");
9499
int16_t time_ms = 3000;
95100
int16_t interval_ms = 200;
96101
while(time_ms > 0) {
97102
furi_delay_ms(interval_ms);
98103
time_ms -= interval_ms;
99104
}
100-
printf("Emulation stopped\r\n");
105+
FURI_LOG_I(TAG, "Emulation stopped");
101106

102107
lfrfid_worker_stop(worker);
103108
lfrfid_worker_stop_thread(worker);

0 commit comments

Comments
 (0)