11// Methods for IR transmission
22
3+ // infrared
34#include <infrared.h>
45#include <infrared/encoder_decoder/infrared.h>
5- #include <applications/services/cli/cli.h>
6+ #include <infrared/worker/infrared_transmit.h>
7+ #include <infrared/worker/infrared_worker.h>
8+
9+ #include <flipper_format/flipper_format.h>
610
711#include "action_i.h"
812#include "quac.h"
913
14+ #define INFRARED_FILE_TYPE "IR signals file"
15+ #define INFRARED_FILE_VERSION 1
16+
1017typedef struct {
1118 size_t timings_size ; /**< Number of elements in the timings array. */
1219 uint32_t * timings ; /**< Pointer to an array of timings describing the signal. */
@@ -17,7 +24,7 @@ typedef struct {
1724typedef struct InfraredSignal {
1825 bool is_raw ;
1926 union {
20- InfraredMessage message ;
27+ InfraredMessage message ; // protocol, address, command, repeat
2128 InfraredRawSignal raw ;
2229 } payload ;
2330} InfraredSignal ;
@@ -29,19 +36,149 @@ InfraredSignal* infrared_signal_alloc() {
2936 return signal ;
3037}
3138
39+ void infrared_signal_free (InfraredSignal * signal ) {
40+ if (signal -> is_raw ) {
41+ free (signal -> payload .raw .timings );
42+ signal -> payload .raw .timings = NULL ;
43+ }
44+ free (signal );
45+ }
46+
3247void action_ir_tx (void * context , const FuriString * action_path , FuriString * error ) {
33- UNUSED (action_path );
3448 UNUSED (error );
35- UNUSED (context );
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);
49+ App * app = context ;
50+ const char * file_name = furi_string_get_cstr (action_path );
51+ InfraredSignal * signal = infrared_signal_alloc ();
52+
53+ FlipperFormat * fff_data_file = flipper_format_file_alloc (app -> storage );
54+ FuriString * temp_str ;
55+ temp_str = furi_string_alloc ();
56+ uint32_t temp_data32 ;
57+
58+ // https://developer.flipper.net/flipperzero/doxygen/infrared_file_format.html
59+ // TODO: Right now we only read the first signal found in the file. Add support
60+ // for reading any signal by 'name'
61+ do {
62+ if (!flipper_format_file_open_existing (fff_data_file , file_name )) {
63+ ACTION_SET_ERROR ("IR: Error opening %s" , file_name );
64+ break ;
65+ }
66+ if (!flipper_format_read_header (fff_data_file , temp_str , & temp_data32 )) {
67+ ACTION_SET_ERROR ("IR: Missing or incorrect header" );
68+ break ;
69+ }
70+ if (!furi_string_cmp_str (temp_str , INFRARED_FILE_TYPE ) &&
71+ temp_data32 == INFRARED_FILE_VERSION ) {
72+ } else {
73+ ACTION_SET_ERROR ("IR: File type or version mismatch" );
74+ break ;
75+ }
76+ if (!flipper_format_read_string (fff_data_file , "name" , temp_str )) {
77+ ACTION_SET_ERROR ("IR: Invalid or missing name" );
78+ break ;
79+ }
80+ // FURI_LOG_I(TAG, "Reading signal %s", furi_string_get_cstr(temp_str));
81+ if (!flipper_format_read_string (fff_data_file , "type" , temp_str )) {
82+ ACTION_SET_ERROR ("IR: Type missing" );
83+ break ;
84+ }
85+ if (!furi_string_cmp_str (temp_str , "parsed" )) {
86+ // FURI_LOG_I(TAG, "IR File is PARSED");
87+ signal -> is_raw = false;
88+
89+ if (!flipper_format_read_string (fff_data_file , "protocol" , temp_str )) {
90+ ACTION_SET_ERROR ("IR: Invalid or missing protocol" );
91+ break ;
92+ }
93+ signal -> payload .message .protocol =
94+ infrared_get_protocol_by_name (furi_string_get_cstr (temp_str ));
95+ if (!infrared_is_protocol_valid (signal -> payload .message .protocol )) {
96+ ACTION_SET_ERROR ("IR: Invalid or unknown protocol" );
97+ break ;
98+ }
99+
100+ // Why do these methods exist, when the spec says the address and command
101+ // lengths MUST be 4 bytes?
102+
103+ // uint8_t address_len;
104+ // address_len = infrared_get_protocol_address_length(signal->payload.message.protocol);
105+
106+ // uint8_t command_len;
107+ // command_len = infrared_get_protocol_command_length(signal->payload.message.protocol);
108+
109+ if (!flipper_format_read_hex (
110+ fff_data_file , "address" , (uint8_t * )& signal -> payload .message .address , 4 )) {
111+ ACTION_SET_ERROR ("IR: Failed to read address" );
112+ break ;
113+ }
114+ if (!flipper_format_read_hex (
115+ fff_data_file , "command" , (uint8_t * )& signal -> payload .message .command , 4 )) {
116+ ACTION_SET_ERROR ("IR: Failed to read command" );
117+ break ;
118+ }
119+
120+ // FURI_LOG_I(
121+ // TAG,
122+ // "IR: Sending parsed => %s %lu %lu",
123+ // infrared_get_protocol_name(signal->payload.message.protocol),
124+ // signal->payload.message.address,
125+ // signal->payload.message.command);
126+
127+ infrared_send (& signal -> payload .message , 1 );
128+
129+ } else if (!furi_string_cmp_str (temp_str , "raw" )) {
130+ // FURI_LOG_I(TAG, "IR File is RAW");
131+ signal -> is_raw = true;
132+
133+ if (!flipper_format_read_uint32 (
134+ fff_data_file , "frequency" , & signal -> payload .raw .frequency , 1 )) {
135+ ACTION_SET_ERROR ("IR: Failed to read frequency" );
136+ break ;
137+ }
138+ if (!flipper_format_read_float (
139+ fff_data_file , "duty_cycle" , & signal -> payload .raw .duty_cycle , 1 )) {
140+ ACTION_SET_ERROR ("IR: Failed to read duty cycle" );
141+ break ;
142+ }
143+ if (!flipper_format_get_value_count (fff_data_file , "data" , & temp_data32 )) {
144+ ACTION_SET_ERROR ("IR: Failed to get size of data" );
145+ break ;
146+ }
147+ if (temp_data32 > MAX_TIMINGS_AMOUNT ) {
148+ ACTION_SET_ERROR ("IR: Data size exceeds limit" );
149+ break ;
150+ }
151+ signal -> payload .raw .timings_size = temp_data32 ;
152+
153+ signal -> payload .raw .timings =
154+ malloc (sizeof (uint32_t ) * signal -> payload .raw .timings_size );
155+ if (!flipper_format_read_uint32 (
156+ fff_data_file , "data" , signal -> payload .raw .timings , temp_data32 )) {
157+ ACTION_SET_ERROR ("IR: Failed to read data" );
158+ break ;
159+ }
160+
161+ // FURI_LOG_I(
162+ // TAG,
163+ // "IR: Sending raw => %d timings, %lu Hz, %f",
164+ // signal->payload.raw.timings_size,
165+ // signal->payload.raw.frequency,
166+ // (double)signal->payload.raw.duty_cycle);
167+
168+ infrared_send_raw_ext (
169+ signal -> payload .raw .timings ,
170+ signal -> payload .raw .timings_size ,
171+ true,
172+ signal -> payload .raw .frequency ,
173+ signal -> payload .raw .duty_cycle );
174+ } else {
175+ ACTION_SET_ERROR ("IR: Unknown type: %s" , furi_string_get_cstr (temp_str ));
176+ break ;
177+ }
178+
179+ } while (false);
180+
181+ furi_string_free (temp_str );
182+ flipper_format_free (fff_data_file );
183+ infrared_signal_free (signal );
47184}
0 commit comments