Merge commit '9aacbf943c458df1db94c05c0c08cc87bbc93bb0' into mntm-dev

This commit is contained in:
WillyJL
2026-02-28 21:21:23 +01:00
14 changed files with 183 additions and 30 deletions
+4
View File
@@ -16,6 +16,8 @@
- UL: Add signals button editor and real remote simulation (full signal transmit with just one click) (by @Dmitry422)
- UL: KeeLoq add counter mode 7 (sends 7 signals increasing counter with 0x3333 steps) - may bypass counter on some receivers! (by @xMasterX)
- UL: TX Power setting (by @LeeroysHub)
- UL: Somfy Keytis button switch and Add Manually support (by @xMasterX)
- UL: Genius Echo/Bravo add 2 buttons hold simulation (0xB btn code) (by @xMasterX)
- UL: JS: Add IR capabilities to the JS engine (by @LuisMayo)
- UL: Docs: Add [full list of supported SubGHz protocols](https://github.com/Next-Flip/Momentum-Firmware/blob/dev/documentation/SubGHzSupportedSystems.md) and their frequencies/modulations that can be used for reading remotes (by @xMasterX)
@@ -43,6 +45,7 @@
- UL: BFT KeeLoq try decoding with zero seed too (by @xMasterX)
- UL: KeeLoq display BFT programming mode TX (when arrow button is held) (by @xMasterX)
- UL: Signal Settings Improvements (by @Dmitry422)
- UL: KeeLoq change delta size (by @xMasterX)
- Archive: Support opening and pinning ProtoPirate files from Archive (#510 by @LeeroysHub)
- OFW: API: Make `view_port_send_to_back()` public (by @loftyinclination)
@@ -54,6 +57,7 @@
- UL: Nice Flor S remove extra uint64 variable (by @xMasterX)
- UL: Fix Alutech AT4N false positives (by @xMasterX)
- UL: Fix documentation link for HT12A protocol (by @carlogrisetti)
- UL: BFT Mitto fix decode bug (seed was not resetting after one successful decode) (by @xMasterX)
- NFC:
- Fix sending 32+ byte ISO 15693-3 commands (by @WillyJL)
- Fixes to `READ_MULTI` and `GET_BLOCK_SECURITY` commands in ISO 15693-3 emulation (#501 by @WillyJL & aaronjamt)
@@ -78,6 +78,7 @@ typedef enum {
SetTypeFaacSLH_433,
SetTypeBFTMitto,
SetTypeSomfyTelis,
SetTypeSomfyKeytis,
SetTypeKingGatesStylo4k,
SetTypeBenincaARC,
SetTypeJarolift,
@@ -523,6 +523,15 @@ void subghz_scene_set_type_fill_generation_infos(GenInfo* infos_dest, SetType ty
.somfy_telis.btn = 0x02,
.somfy_telis.cnt = 0x03};
break;
case SetTypeSomfyKeytis:
gen_info = (GenInfo){
.type = GenSomfyKeytis,
.mod = "AM650",
.freq = 433420000,
.somfy_keytis.serial = (key & 0x0000FFFF) | 0x00D50000,
.somfy_keytis.btn = 0x04,
.somfy_keytis.cnt = 0x03};
break;
case SetTypeKingGatesStylo4k:
gen_info = (GenInfo){
.type = GenKingGatesStylo4k,
@@ -636,7 +645,7 @@ void subghz_scene_set_type_fill_generation_infos(GenInfo* infos_dest, SetType ty
.mod = "AM650",
.freq = 433920000,
.keeloq.serial = key & 0x00FFFFFF,
.keeloq.btn = 0x06,
.keeloq.btn = 0x09,
.keeloq.cnt = 0x03,
.keeloq.manuf = "Genius_Bravo"};
break;
@@ -10,6 +10,7 @@ typedef enum {
GenKeeloqBFT,
GenAlutechAt4n,
GenSomfyTelis,
GenSomfyKeytis,
GenKingGatesStylo4k,
GenBenincaARC,
GenJarolift,
@@ -64,6 +65,11 @@ typedef struct {
uint8_t btn;
uint16_t cnt;
} somfy_telis;
struct {
uint32_t serial;
uint8_t btn;
uint16_t cnt;
} somfy_keytis;
struct {
uint32_t serial;
uint8_t btn;
@@ -335,6 +335,36 @@ bool subghz_txrx_gen_somfy_telis_protocol(
return res;
}
bool subghz_txrx_gen_somfy_keytis_protocol(
void* context,
const char* preset_name,
uint32_t frequency,
uint32_t serial,
uint8_t btn,
uint16_t cnt) {
SubGhzTxRx* txrx = context;
bool res = false;
txrx->transmitter =
subghz_transmitter_alloc_init(txrx->environment, SUBGHZ_PROTOCOL_SOMFY_KEYTIS_NAME);
subghz_txrx_set_preset(txrx, preset_name, frequency, NULL, 0);
if(txrx->transmitter && subghz_protocol_somfy_keytis_create_data(
subghz_transmitter_get_protocol_instance(txrx->transmitter),
txrx->fff_data,
serial,
btn,
cnt,
txrx->preset)) {
res = true;
}
subghz_transmitter_free(txrx->transmitter);
return res;
}
bool subghz_txrx_gen_kinggates_stylo_4k_protocol(
void* context,
const char* preset_name,
@@ -108,6 +108,14 @@ bool subghz_txrx_gen_somfy_telis_protocol(
uint8_t btn,
uint16_t cnt);
bool subghz_txrx_gen_somfy_keytis_protocol(
void* context,
const char* preset_name,
uint32_t frequency,
uint32_t serial,
uint8_t btn,
uint16_t cnt);
bool subghz_txrx_gen_kinggates_stylo_4k_protocol(
void* context,
const char* preset_name,
@@ -36,6 +36,10 @@ void subghz_scene_set_button_on_enter(void* context) {
byte_ptr = &subghz->gen_info->somfy_telis.btn;
byte_count = sizeof(subghz->gen_info->somfy_telis.btn);
break;
case GenSomfyKeytis:
byte_ptr = &subghz->gen_info->somfy_keytis.btn;
byte_count = sizeof(subghz->gen_info->somfy_keytis.btn);
break;
case GenKingGatesStylo4k:
byte_ptr = &subghz->gen_info->kinggates_stylo_4k.btn;
byte_count = sizeof(subghz->gen_info->kinggates_stylo_4k.btn);
@@ -98,6 +102,7 @@ bool subghz_scene_set_button_on_event(void* context, SceneManagerEvent event) {
case GenBenincaARC:
case GenJarolift:
case GenNiceFlorS:
case GenSomfyKeytis:
case GenSecPlus2:
scene_manager_next_scene(subghz->scene_manager, SubGhzSceneSetCounter);
break;
@@ -42,6 +42,10 @@ void subghz_scene_set_counter_on_enter(void* context) {
byte_ptr = (uint8_t*)&subghz->gen_info->somfy_telis.cnt;
byte_count = sizeof(subghz->gen_info->somfy_telis.cnt);
break;
case GenSomfyKeytis:
byte_ptr = (uint8_t*)&subghz->gen_info->somfy_keytis.cnt;
byte_count = sizeof(subghz->gen_info->somfy_keytis.cnt);
break;
case GenKingGatesStylo4k:
byte_ptr = (uint8_t*)&subghz->gen_info->kinggates_stylo_4k.cnt;
byte_count = sizeof(subghz->gen_info->kinggates_stylo_4k.cnt);
@@ -125,6 +129,9 @@ bool subghz_scene_set_counter_on_event(void* context, SceneManagerEvent event) {
case GenSomfyTelis:
subghz->gen_info->somfy_telis.cnt = __bswap16(subghz->gen_info->somfy_telis.cnt);
break;
case GenSomfyKeytis:
subghz->gen_info->somfy_keytis.cnt = __bswap16(subghz->gen_info->somfy_keytis.cnt);
break;
case GenKingGatesStylo4k:
subghz->gen_info->kinggates_stylo_4k.cnt =
__bswap16(subghz->gen_info->kinggates_stylo_4k.cnt);
@@ -193,6 +200,15 @@ bool subghz_scene_set_counter_on_event(void* context, SceneManagerEvent event) {
subghz->gen_info->somfy_telis.btn,
subghz->gen_info->somfy_telis.cnt);
break;
case GenSomfyKeytis:
generated_protocol = subghz_txrx_gen_somfy_keytis_protocol(
subghz->txrx,
subghz->gen_info->mod,
subghz->gen_info->freq,
subghz->gen_info->somfy_keytis.serial,
subghz->gen_info->somfy_keytis.btn,
subghz->gen_info->somfy_keytis.cnt);
break;
case GenKingGatesStylo4k:
generated_protocol = subghz_txrx_gen_kinggates_stylo_4k_protocol(
subghz->txrx,
@@ -30,6 +30,7 @@ void subghz_scene_set_seed_on_enter(void* context) {
case GenKeeloq:
case GenAlutechAt4n:
case GenSomfyTelis:
case GenSomfyKeytis:
case GenKingGatesStylo4k:
case GenBenincaARC:
case GenJarolift:
@@ -92,6 +93,7 @@ bool subghz_scene_set_seed_on_event(void* context, SceneManagerEvent event) {
case GenKeeloq:
case GenAlutechAt4n:
case GenSomfyTelis:
case GenSomfyKeytis:
case GenKingGatesStylo4k:
case GenBenincaARC:
case GenJarolift:
@@ -42,6 +42,10 @@ void subghz_scene_set_serial_on_enter(void* context) {
byte_ptr = (uint8_t*)&subghz->gen_info->somfy_telis.serial;
byte_count = sizeof(subghz->gen_info->somfy_telis.serial);
break;
case GenSomfyKeytis:
byte_ptr = (uint8_t*)&subghz->gen_info->somfy_keytis.serial;
byte_count = sizeof(subghz->gen_info->somfy_keytis.serial);
break;
case GenKingGatesStylo4k:
byte_ptr = (uint8_t*)&subghz->gen_info->kinggates_stylo_4k.serial;
byte_count = sizeof(subghz->gen_info->kinggates_stylo_4k.serial);
@@ -122,6 +126,10 @@ bool subghz_scene_set_serial_on_event(void* context, SceneManagerEvent event) {
subghz->gen_info->somfy_telis.serial =
__bswap32(subghz->gen_info->somfy_telis.serial);
break;
case GenSomfyKeytis:
subghz->gen_info->somfy_keytis.serial =
__bswap32(subghz->gen_info->somfy_keytis.serial);
break;
case GenKingGatesStylo4k:
subghz->gen_info->kinggates_stylo_4k.serial =
__bswap32(subghz->gen_info->kinggates_stylo_4k.serial);
@@ -159,6 +167,7 @@ bool subghz_scene_set_serial_on_event(void* context, SceneManagerEvent event) {
case GenKeeloqBFT:
case GenAlutechAt4n:
case GenSomfyTelis:
case GenSomfyKeytis:
case GenKingGatesStylo4k:
case GenBenincaARC:
case GenJarolift:
@@ -16,6 +16,7 @@ static const char* submenu_names[SetTypeMAX] = {
[SetTypeFaacSLH_433] = "FAAC SLH 433MHz",
[SetTypeBFTMitto] = "BFT Mitto 433MHz",
[SetTypeSomfyTelis] = "Somfy Telis 433MHz",
[SetTypeSomfyKeytis] = "Somfy Keytis 433MHz",
[SetTypeANMotorsAT4] = "AN-Motors AT4 433MHz",
[SetTypeAlutechAT4N] = "Alutech AT4N 433MHz",
[SetTypeRoger_433] = "Roger 433MHz",
@@ -56,7 +57,7 @@ static const char* submenu_names[SetTypeMAX] = {
[SetTypeCardinS449_433FM] = "KL: Cardin S449 433MHz",
[SetTypeFAACRCXT_433_92] = "KL: FAAC RC,XT 433MHz",
[SetTypeFAACRCXT_868] = "KL: FAAC RC,XT 868MHz",
[SetTypeGeniusBravo433] = "KL: Genius Bravo 433MHz",
[SetTypeGeniusBravo433] = "KL: Genius TX4RC 433MHz",
[SetTypeNiceMHouse_433_92] = "KL: Mhouse 433MHz",
[SetTypeNiceSmilo_433_92] = "KL: Nice Smilo 433MHz",
[SetTypeNiceFlorS_433_92] = "Nice FloR-S 433MHz",
@@ -190,6 +191,15 @@ bool subghz_scene_set_type_generate_protocol_from_infos(SubGhz* subghz) {
gen_info.somfy_telis.btn,
gen_info.somfy_telis.cnt);
break;
case GenSomfyKeytis:
generated_protocol = subghz_txrx_gen_somfy_keytis_protocol(
subghz->txrx,
gen_info.mod,
gen_info.freq,
gen_info.somfy_keytis.serial,
gen_info.somfy_keytis.btn,
gen_info.somfy_keytis.cnt);
break;
case GenKingGatesStylo4k:
generated_protocol = subghz_txrx_gen_kinggates_stylo_4k_protocol(
subghz->txrx,
@@ -296,6 +306,7 @@ bool subghz_scene_set_type_on_event(void* context, SceneManagerEvent event) {
case GenKeeloqBFT: // Serial (u32), Button (u8), Counter (u16), Seed (u32)
case GenAlutechAt4n: // Serial (u32), Button (u8), Counter (u16)
case GenSomfyTelis: // Serial (u32), Button (u8), Counter (u16)
case GenSomfyKeytis: // Serial (u32), Button (u8), Counter (u16)
case GenKingGatesStylo4k: // Serial (u32), Button (u8), Counter (u16)
case GenBenincaARC: // Serial (u32), Button (u8), Counter (u32)
case GenJarolift: // Serial (u32), Button (u4), Counter (u16)
+1 -1
View File
@@ -53,7 +53,7 @@ That list is only for default SubGHz app, apps like *Weather Station* have their
- V2 Phoenix (Phox) `433.92MHz` `AM650` (52 bits, Dynamic) (receivers have option to enable Static mode, making them ignore rolling part of the key)
- Marantec `433.92MHz, 868MHz` `AM650` (49 bits, Static)
- Marantec24 `868MHz` `AM650` (24 bits, Static)
- Somfy Keytis `433.42MHz, 868MHz` `AM650` (80 bits, Dynamic)
- Somfy Keytis `433.42MHz, 868MHz` `AM650` (80 bits, Dynamic) (KeyGo 4 RTS 4 / Keytis NS 2RTS)
- ZKTeco `430.5MHz` `AM650` (24 bits, Static - Princeton based) - (Button codes (already mapped to arrow keys): `0x30 (UP)`, `0x03 (STOP)`, `0x0C (DOWN)`)
- Linear `300MHz` `AM650` (10 bits, Static)
- Linear Delta3 `AM650` (8 bits, Static)
+2
View File
@@ -805,6 +805,8 @@ void subghz_protocol_decoder_keeloq_reset(void* context) {
// TODO
instance->keystore->mfname = "";
instance->keystore->kl_type = 0;
// Reset seed?
instance->generic.seed = 0;
}
void subghz_protocol_decoder_keeloq_feed(void* context, bool level, uint32_t duration) {
+77 -27
View File
@@ -7,6 +7,8 @@
#include "../blocks/generic.h"
#include "../blocks/math.h"
#include "../blocks/custom_btn_i.h"
#define TAG "SubGhzProtocolSomfyKeytis"
static const SubGhzBlockConst subghz_protocol_somfy_keytis_const = {
@@ -24,7 +26,6 @@ struct SubGhzProtocolDecoderSomfyKeytis {
uint16_t header_count;
ManchesterState manchester_saved_state;
uint32_t press_duration_counter;
};
struct SubGhzProtocolEncoderSomfyKeytis {
@@ -124,17 +125,49 @@ void subghz_protocol_decoder_somfy_keytis_reset(void* context) {
NULL);
}
static void subghz_protocol_somfy_keytis_check_remote_controller(SubGhzBlockGeneric* instance);
static uint8_t subghz_protocol_somfy_keytis_get_btn_code(void) {
uint8_t custom_btn_id = subghz_custom_btn_get();
uint8_t original_btn_code = subghz_custom_btn_get_original();
uint8_t btn = original_btn_code;
// Set custom button
if((custom_btn_id == SUBGHZ_CUSTOM_BTN_OK) && (original_btn_code != 0)) {
// Restore original button code
btn = original_btn_code;
} else if(custom_btn_id == SUBGHZ_CUSTOM_BTN_UP) {
switch(original_btn_code) {
case 0x4:
btn = 0x3;
break;
case 0x3:
btn = 0x4;
break;
default:
break;
}
}
return btn;
}
static bool
subghz_protocol_somfy_keytis_gen_data(SubGhzProtocolEncoderSomfyKeytis* instance, uint8_t btn) {
UNUSED(btn);
uint64_t data = instance->generic.data ^ (instance->generic.data >> 8);
instance->generic.btn = (data >> 48) & 0xF;
instance->generic.cnt = (data >> 24) & 0xFFFF;
instance->generic.serial = data & 0xFFFFFF;
//instance->generic.btn = (data >> 48) & 0xF;
//instance->generic.cnt = (data >> 24) & 0xFFFF;
//instance->generic.serial = data & 0xFFFFFF;
// Save original button for later use
if(subghz_custom_btn_get_original() == 0) {
subghz_custom_btn_set_original(btn);
}
btn = subghz_protocol_somfy_keytis_get_btn_code();
// override button if we change it with signal settings button editor
if(subghz_block_generic_global_button_override_get(&instance->generic.btn))
FURI_LOG_D(TAG, "Button sucessfully changed to 0x%X", instance->generic.btn);
if(subghz_block_generic_global_button_override_get(&btn))
FURI_LOG_D(TAG, "Button sucessfully changed to 0x%X", btn);
// Check for OFEX (overflow experimental) mode
if(furi_hal_subghz_get_rolling_counter_mult() != -0x7FFFFFFF) {
@@ -158,7 +191,7 @@ static bool
}
uint8_t frame[10];
frame[0] = (0xA << 4) | instance->generic.btn;
frame[0] = (0xA << 4) | btn;
frame[1] = 0xF << 4;
frame[2] = instance->generic.cnt >> 8;
frame[3] = instance->generic.cnt;
@@ -180,7 +213,7 @@ static bool
for(uint8_t i = 1; i < 7; i++) {
frame[i] ^= frame[i - 1];
}
data = 0;
uint64_t data = 0;
for(uint8_t i = 0; i < 7; ++i) {
data <<= 8;
data |= frame[i];
@@ -191,7 +224,7 @@ static bool
data <<= 8;
data |= frame[i];
}
instance->generic.data_2 = data;
instance->generic.seed = data;
return true;
}
@@ -207,12 +240,25 @@ bool subghz_protocol_somfy_keytis_create_data(
instance->generic.serial = serial;
instance->generic.cnt = cnt;
instance->generic.data_count_bit = 80;
bool res = subghz_protocol_somfy_keytis_gen_data(instance, btn);
if(res) {
return SubGhzProtocolStatusOk ==
subghz_block_generic_serialize(&instance->generic, flipper_format, preset);
subghz_protocol_somfy_keytis_gen_data(instance, btn);
// Encode complete, now serialize
SubGhzProtocolStatus res =
subghz_block_generic_serialize(&instance->generic, flipper_format, preset);
if(!flipper_format_rewind(flipper_format)) {
FURI_LOG_E(TAG, "Rewind error");
res = SubGhzProtocolStatusErrorParserOthers;
}
return res;
if((res == SubGhzProtocolStatusOk) &&
!flipper_format_insert_or_update_uint32(
flipper_format, "Duration_Counter", &instance->generic.seed, 1)) {
FURI_LOG_E(TAG, "Unable to add Duration_Counter");
res = SubGhzProtocolStatusErrorParserOthers;
}
return res == SubGhzProtocolStatusOk;
}
/**
@@ -278,7 +324,7 @@ static bool subghz_protocol_encoder_somfy_keytis_get_upload(
}
for(uint8_t i = 24; i > 0; i--) {
if(bit_read(instance->generic.data_2, i - 1)) {
if(bit_read(instance->generic.seed, i - 1)) {
if(instance->encoder.upload[index - 1].level == LEVEL_DURATION_LEVEL_LOW) {
instance->encoder.upload[index - 1].duration *= 2; // 00
instance->encoder.upload[index++] = level_duration_make(
@@ -356,7 +402,7 @@ static bool subghz_protocol_encoder_somfy_keytis_get_upload(
}
for(uint8_t i = 24; i > 0; i--) {
if(bit_read(instance->generic.data_2, i - 1)) {
if(bit_read(instance->generic.seed, i - 1)) {
if(instance->encoder.upload[index - 1].level == LEVEL_DURATION_LEVEL_LOW) {
instance->encoder.upload[index - 1].duration *= 2; // 00
instance->encoder.upload[index++] = level_duration_make(
@@ -422,6 +468,8 @@ SubGhzProtocolStatus
flipper_format_read_uint32(
flipper_format, "Repeat", (uint32_t*)&instance->encoder.repeat, 1);
subghz_protocol_somfy_keytis_check_remote_controller(&instance->generic);
subghz_protocol_encoder_somfy_keytis_get_upload(instance, instance->generic.btn);
if(!flipper_format_rewind(flipper_format)) {
@@ -517,7 +565,7 @@ void subghz_protocol_decoder_somfy_keytis_feed(void* context, bool level, uint32
instance->decoder.parser_step = SomfyKeytisDecoderStepDecoderData;
instance->decoder.decode_data = 0;
instance->decoder.decode_count_bit = 0;
instance->press_duration_counter = 0;
instance->generic.seed = 0;
manchester_advance(
instance->manchester_saved_state,
ManchesterEventReset,
@@ -594,8 +642,7 @@ void subghz_protocol_decoder_somfy_keytis_feed(void* context, bool level, uint32
if(instance->decoder.decode_count_bit < 56) {
instance->decoder.decode_data = (instance->decoder.decode_data << 1) | data;
} else {
instance->press_duration_counter = (instance->press_duration_counter << 1) |
data;
instance->generic.seed = (instance->generic.seed << 1) | data;
}
instance->decoder.decode_count_bit++;
@@ -713,6 +760,12 @@ static void subghz_protocol_somfy_keytis_check_remote_controller(SubGhzBlockGene
instance->btn = (data >> 48) & 0xF;
instance->cnt = (data >> 24) & 0xFFFF;
instance->serial = data & 0xFFFFFF;
// Save original button for later use
if(subghz_custom_btn_get_original() == 0) {
subghz_custom_btn_set_original(instance->btn);
}
subghz_custom_btn_set_max(1);
}
/**
@@ -757,7 +810,7 @@ SubGhzProtocolStatus subghz_protocol_decoder_somfy_keytis_serialize(
subghz_block_generic_serialize(&instance->generic, flipper_format, preset);
if((ret == SubGhzProtocolStatusOk) &&
!flipper_format_write_uint32(
flipper_format, "Duration_Counter", &instance->press_duration_counter, 1)) {
flipper_format, "Duration_Counter", &instance->generic.seed, 1)) {
FURI_LOG_E(TAG, "Unable to add Duration_Counter");
ret = SubGhzProtocolStatusErrorParserOthers;
}
@@ -783,10 +836,7 @@ SubGhzProtocolStatus
break;
}
if(!flipper_format_read_uint32(
flipper_format,
"Duration_Counter",
(uint32_t*)&instance->press_duration_counter,
1)) {
flipper_format, "Duration_Counter", (uint32_t*)&instance->generic.seed, 1)) {
FURI_LOG_E(TAG, "Missing Duration_Counter");
ret = SubGhzProtocolStatusErrorParserOthers;
break;
@@ -824,7 +874,7 @@ void subghz_protocol_decoder_somfy_keytis_get_string(void* context, FuriString*
instance->generic.data_count_bit,
(uint32_t)(instance->generic.data >> 32),
(uint32_t)instance->generic.data,
instance->press_duration_counter,
instance->generic.seed,
instance->generic.serial,
instance->generic.cnt,
instance->generic.btn,