mirror of
https://github.com/Next-Flip/Momentum-Firmware.git
synced 2026-05-24 05:34:45 -07:00
Merge branch 'ofwdev' into 420
This commit is contained in:
@@ -23,13 +23,17 @@ struct InfraredBruteForce {
|
||||
FlipperFormat* ff;
|
||||
const char* db_filename;
|
||||
string_t current_record_name;
|
||||
InfraredSignal* current_signal;
|
||||
InfraredBruteForceRecordDict_t records;
|
||||
bool is_started;
|
||||
};
|
||||
|
||||
InfraredBruteForce* infrared_brute_force_alloc() {
|
||||
InfraredBruteForce* brute_force = malloc(sizeof(InfraredBruteForce));
|
||||
brute_force->ff = NULL;
|
||||
brute_force->db_filename = NULL;
|
||||
brute_force->current_signal = NULL;
|
||||
brute_force->is_started = false;
|
||||
string_init(brute_force->current_record_name);
|
||||
InfraredBruteForceRecordDict_init(brute_force->records);
|
||||
return brute_force;
|
||||
@@ -40,17 +44,19 @@ void infrared_brute_force_clear_records(InfraredBruteForce* brute_force) {
|
||||
}
|
||||
|
||||
void infrared_brute_force_free(InfraredBruteForce* brute_force) {
|
||||
furi_assert(!brute_force->ff);
|
||||
furi_assert(!brute_force->is_started);
|
||||
InfraredBruteForceRecordDict_clear(brute_force->records);
|
||||
string_clear(brute_force->current_record_name);
|
||||
free(brute_force);
|
||||
}
|
||||
|
||||
void infrared_brute_force_set_db_filename(InfraredBruteForce* brute_force, const char* db_filename) {
|
||||
furi_assert(!brute_force->is_started);
|
||||
brute_force->db_filename = db_filename;
|
||||
}
|
||||
|
||||
bool infrared_brute_force_calculate_messages(InfraredBruteForce* brute_force) {
|
||||
furi_assert(!brute_force->is_started);
|
||||
furi_assert(brute_force->db_filename);
|
||||
bool success = false;
|
||||
|
||||
@@ -80,6 +86,7 @@ bool infrared_brute_force_start(
|
||||
InfraredBruteForce* brute_force,
|
||||
uint32_t index,
|
||||
uint32_t* record_count) {
|
||||
furi_assert(!brute_force->is_started);
|
||||
bool success = false;
|
||||
*record_count = 0;
|
||||
|
||||
@@ -100,50 +107,37 @@ bool infrared_brute_force_start(
|
||||
if(*record_count) {
|
||||
Storage* storage = furi_record_open(RECORD_STORAGE);
|
||||
brute_force->ff = flipper_format_buffered_file_alloc(storage);
|
||||
brute_force->current_signal = infrared_signal_alloc();
|
||||
brute_force->is_started = true;
|
||||
success =
|
||||
flipper_format_buffered_file_open_existing(brute_force->ff, brute_force->db_filename);
|
||||
if(!success) {
|
||||
flipper_format_free(brute_force->ff);
|
||||
brute_force->ff = NULL;
|
||||
furi_record_close(RECORD_STORAGE);
|
||||
}
|
||||
if(!success) infrared_brute_force_stop(brute_force);
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
bool infrared_brute_force_is_started(InfraredBruteForce* brute_force) {
|
||||
return brute_force->ff;
|
||||
return brute_force->is_started;
|
||||
}
|
||||
|
||||
void infrared_brute_force_stop(InfraredBruteForce* brute_force) {
|
||||
furi_assert(string_size(brute_force->current_record_name));
|
||||
furi_assert(brute_force->ff);
|
||||
|
||||
furi_assert(brute_force->is_started);
|
||||
string_reset(brute_force->current_record_name);
|
||||
infrared_signal_free(brute_force->current_signal);
|
||||
flipper_format_free(brute_force->ff);
|
||||
furi_record_close(RECORD_STORAGE);
|
||||
brute_force->current_signal = NULL;
|
||||
brute_force->ff = NULL;
|
||||
brute_force->is_started = false;
|
||||
furi_record_close(RECORD_STORAGE);
|
||||
}
|
||||
|
||||
bool infrared_brute_force_send_next(InfraredBruteForce* brute_force) {
|
||||
furi_assert(string_size(brute_force->current_record_name));
|
||||
furi_assert(brute_force->ff);
|
||||
bool success = false;
|
||||
|
||||
string_t signal_name;
|
||||
string_init(signal_name);
|
||||
InfraredSignal* signal = infrared_signal_alloc();
|
||||
|
||||
do {
|
||||
success = infrared_signal_read(signal, brute_force->ff, signal_name);
|
||||
} while(success && !string_equal_p(brute_force->current_record_name, signal_name));
|
||||
|
||||
furi_assert(brute_force->is_started);
|
||||
const bool success = infrared_signal_search_and_read(
|
||||
brute_force->current_signal, brute_force->ff, brute_force->current_record_name);
|
||||
if(success) {
|
||||
infrared_signal_transmit(signal);
|
||||
infrared_signal_transmit(brute_force->current_signal);
|
||||
}
|
||||
|
||||
infrared_signal_free(signal);
|
||||
string_clear(signal_name);
|
||||
return success;
|
||||
}
|
||||
|
||||
@@ -157,3 +151,8 @@ void infrared_brute_force_add_record(
|
||||
InfraredBruteForceRecordDict_set_at(brute_force->records, key, value);
|
||||
string_clear(key);
|
||||
}
|
||||
|
||||
void infrared_brute_force_reset(InfraredBruteForce* brute_force) {
|
||||
furi_assert(!brute_force->is_started);
|
||||
InfraredBruteForceRecordDict_reset(brute_force->records);
|
||||
}
|
||||
|
||||
@@ -146,6 +146,26 @@ static inline bool infrared_signal_read_raw(InfraredSignal* signal, FlipperForma
|
||||
return success;
|
||||
}
|
||||
|
||||
static bool infrared_signal_read_body(InfraredSignal* signal, FlipperFormat* ff) {
|
||||
string_t tmp;
|
||||
string_init(tmp);
|
||||
bool success = false;
|
||||
|
||||
do {
|
||||
if(!flipper_format_read_string(ff, "type", tmp)) break;
|
||||
if(string_equal_p(tmp, "raw")) {
|
||||
success = infrared_signal_read_raw(signal, ff);
|
||||
} else if(string_equal_p(tmp, "parsed")) {
|
||||
success = infrared_signal_read_message(signal, ff);
|
||||
} else {
|
||||
FURI_LOG_E(TAG, "Unknown signal type");
|
||||
}
|
||||
} while(false);
|
||||
|
||||
string_clear(tmp);
|
||||
return success;
|
||||
}
|
||||
|
||||
InfraredSignal* infrared_signal_alloc() {
|
||||
InfraredSignal* signal = malloc(sizeof(InfraredSignal));
|
||||
|
||||
@@ -227,24 +247,41 @@ bool infrared_signal_save(InfraredSignal* signal, FlipperFormat* ff, const char*
|
||||
}
|
||||
|
||||
bool infrared_signal_read(InfraredSignal* signal, FlipperFormat* ff, string_t name) {
|
||||
string_t buf;
|
||||
string_init(buf);
|
||||
string_t tmp;
|
||||
string_init(tmp);
|
||||
bool success = false;
|
||||
|
||||
do {
|
||||
if(!flipper_format_read_string(ff, "name", buf)) break;
|
||||
string_set(name, buf);
|
||||
if(!flipper_format_read_string(ff, "type", buf)) break;
|
||||
if(!string_cmp_str(buf, "raw")) {
|
||||
success = infrared_signal_read_raw(signal, ff);
|
||||
} else if(!string_cmp_str(buf, "parsed")) {
|
||||
success = infrared_signal_read_message(signal, ff);
|
||||
} else {
|
||||
FURI_LOG_E(TAG, "Unknown type of signal (allowed - raw/parsed) ");
|
||||
}
|
||||
if(!flipper_format_read_string(ff, "name", tmp)) break;
|
||||
string_set(name, tmp);
|
||||
if(!infrared_signal_read_body(signal, ff)) break;
|
||||
success = true;
|
||||
} while(0);
|
||||
|
||||
string_clear(buf);
|
||||
string_clear(tmp);
|
||||
return success;
|
||||
}
|
||||
|
||||
bool infrared_signal_search_and_read(
|
||||
InfraredSignal* signal,
|
||||
FlipperFormat* ff,
|
||||
const string_t name) {
|
||||
bool success = false;
|
||||
string_t tmp;
|
||||
string_init(tmp);
|
||||
|
||||
do {
|
||||
bool is_name_found = false;
|
||||
while(flipper_format_read_string(ff, "name", tmp)) {
|
||||
is_name_found = string_equal_p(name, tmp);
|
||||
if(is_name_found) break;
|
||||
}
|
||||
if(!is_name_found) break;
|
||||
if(!infrared_signal_read_body(signal, ff)) break;
|
||||
success = true;
|
||||
} while(false);
|
||||
|
||||
string_clear(tmp);
|
||||
return success;
|
||||
}
|
||||
|
||||
|
||||
@@ -37,5 +37,9 @@ InfraredMessage* infrared_signal_get_message(InfraredSignal* signal);
|
||||
|
||||
bool infrared_signal_save(InfraredSignal* signal, FlipperFormat* ff, const char* name);
|
||||
bool infrared_signal_read(InfraredSignal* signal, FlipperFormat* ff, string_t name);
|
||||
bool infrared_signal_search_and_read(
|
||||
InfraredSignal* signal,
|
||||
FlipperFormat* ff,
|
||||
const string_t name);
|
||||
|
||||
void infrared_signal_transmit(InfraredSignal* signal);
|
||||
|
||||
40
documentation/UniversalRemotes.md
Normal file
40
documentation/UniversalRemotes.md
Normal file
@@ -0,0 +1,40 @@
|
||||
# Universal Remotes
|
||||
## Air Conditioners
|
||||
### Recording signals
|
||||
Air conditioners differ from most other infrared-controlled devices because their state is tracked by the remote.
|
||||
The majority of A/C remotes have a small display which shows current mode, temperature and other settings.
|
||||
When the user presses a button, a whole set of parameters is transmitted to the device, which must be recorded and used as a whole.
|
||||
|
||||
In order to add a particular air conditioner to the universal remote, 6 signals must be recorded: `Off`, `Dh`, `Cool_hi`, `Cool_lo`, `Heat_hi`, `Heat_lo`.
|
||||
Each signal (except `Off`) is recorded using the following algorithm:
|
||||
|
||||
1. Get the remote and press the **Power Button** so that the display shows that A/C is ON.
|
||||
2. Set the A/C to the corresponding mode (see table below), while leaving other parameters such as fan speed or vane on **AUTO** (if applicable).
|
||||
3. Press the **POWER** button to switch the A/C off.
|
||||
4. Start learning a new remote on Flipper if it's the first button or press `+` to add a new button otherwise.
|
||||
5. Point the remote to Flipper's IR receiver as directed and press **POWER** button once again.
|
||||
6. Save the resulting signal under the specified name.
|
||||
7. Repeat the steps 2-6 for each signal from the table below.
|
||||
|
||||
| Signal | Mode | Temperature | Note |
|
||||
| :-----: | :--------: | :---------: | ----------------------------------- |
|
||||
| Dh | Dehumidify | N/A | |
|
||||
| Cool_hi | Cooling | See note | Lowest temperature in cooling mode |
|
||||
| Cool_lo | Cooling | 23°C | |
|
||||
| Heat_hi | Heating | See note | Highest temperature in heating mode |
|
||||
| Heat_lo | Heating | 23°C | |
|
||||
|
||||
Finally, record the `Off` signal:
|
||||
1. Make sure the display shows that A/C is ON.
|
||||
2. Start learning a new signal on Flipper and point the remote towards the IR receiver.
|
||||
3. Press the **POWER** button so that the remote shows the OFF state.
|
||||
4. Save the resulting signal under the name `Off`.
|
||||
|
||||
The resulting remote file should now contain 6 signals. Any of them can be omitted, but that will mean that this functionality will not be used.
|
||||
Test the file against the actual device. Every signal must do what it's supposed to.
|
||||
|
||||
If everything checks out, append these signals **to the end** of the [A/C universal remote file](/assets/resources/infrared/assets/ac.ir).
|
||||
|
||||
The order of signals is not important, but they must be preceded by a following comment: `# Model: <Your model name>` in order to keep the library organised.
|
||||
|
||||
When done, open a pull request containing the changed file.
|
||||
@@ -209,8 +209,24 @@ void mf_df_cat_file(MifareDesfireFile* file, string_t out) {
|
||||
uint8_t* data = file->contents;
|
||||
if(data) {
|
||||
for(int rec = 0; rec < num; rec++) {
|
||||
for(int ch = 0; ch < size; ch++) {
|
||||
string_cat_printf(out, "%02x", data[rec * size + ch]);
|
||||
string_cat_printf(out, "record %d\n", rec);
|
||||
for(int ch = 0; ch < size; ch += 4) {
|
||||
string_cat_printf(out, "%03x|", ch);
|
||||
for(int i = 0; i < 4; i++) {
|
||||
if(ch + i < size) {
|
||||
string_cat_printf(out, "%02x ", data[rec * size + ch + i]);
|
||||
} else {
|
||||
string_cat_printf(out, " ");
|
||||
}
|
||||
}
|
||||
for(int i = 0; i < 4 && ch + i < size; i++) {
|
||||
if(isprint(data[rec * size + ch + i])) {
|
||||
string_cat_printf(out, "%c", data[rec * size + ch + i]);
|
||||
} else {
|
||||
string_cat_printf(out, ".");
|
||||
}
|
||||
}
|
||||
string_cat_printf(out, "\n");
|
||||
}
|
||||
string_cat_printf(out, " \n");
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user