subghz: add jarolift protocol

and various fixes
This commit is contained in:
MX
2026-01-26 11:51:28 +03:00
parent 209ae6f76f
commit 271c65a969
20 changed files with 1081 additions and 82 deletions

View File

@@ -72,6 +72,7 @@ typedef enum {
SetTypeSomfyTelis,
SetTypeKingGatesStylo4k,
SetTypeBenincaARC,
SetTypeJarolift,
SetTypeANMotorsAT4,
SetTypeAlutechAT4N,
SetTypePhoenix_V2_433,

View File

@@ -541,6 +541,15 @@ void subghz_scene_set_type_fill_generation_infos(GenInfo* infos_dest, SetType ty
.beninca_arc.btn = 0x02,
.beninca_arc.cnt = 0x03};
break;
case SetTypeJarolift:
gen_info = (GenInfo){
.type = GenJarolift,
.mod = "AM650",
.freq = 433920000,
.jarolift.serial = key & 0xFFFFF00,
.jarolift.btn = 0x02,
.jarolift.cnt = 0x03};
break;
case SetTypeMotorline433:
gen_info = (GenInfo){
.type = GenKeeloq,

View File

@@ -12,6 +12,7 @@ typedef enum {
GenSomfyTelis,
GenKingGatesStylo4k,
GenBenincaARC,
GenJarolift,
GenNiceFlorS,
GenSecPlus1,
GenSecPlus2,
@@ -73,6 +74,11 @@ typedef struct {
uint8_t btn;
uint32_t cnt;
} beninca_arc;
struct {
uint32_t serial;
uint8_t btn;
uint16_t cnt;
} jarolift;
struct {
uint32_t serial;
uint8_t btn;

View File

@@ -395,6 +395,36 @@ bool subghz_txrx_gen_beninca_arc_protocol(
return res;
}
bool subghz_txrx_gen_jarolift_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_JAROLIFT_NAME);
subghz_txrx_set_preset(txrx, preset_name, frequency, NULL, 0);
if(txrx->transmitter && subghz_protocol_jarolift_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_secplus_v2_protocol(
SubGhzTxRx* instance,
const char* name_preset,

View File

@@ -124,6 +124,14 @@ bool subghz_txrx_gen_beninca_arc_protocol(
uint8_t btn,
uint32_t cnt);
bool subghz_txrx_gen_jarolift_protocol(
void* context,
const char* preset_name,
uint32_t frequency,
uint32_t serial,
uint8_t btn,
uint16_t cnt);
bool subghz_txrx_gen_came_atomo_protocol(
void* context,
const char* preset_name,

View File

@@ -1,71 +1,72 @@
Filetype: Flipper SubGhz Keystore File
Version: 0
Encryption: 1
IV: 4F 77 4F 20 66 75 72 72 79 20 49 56 20 55 77 55
1D0560740B25F58EE0E85BF949139971E5AA08C5499CC74B11992D124C281012
C05E2D2C715D8E24C518EF2841DA02173C05DD5BA5310EE85D09709500DB1726
9EA5721836369FF918859077F50E33100F7AC53E8E8F31E25296579F875359DF
D2A8AD1B65BC66B459525124CDC5011C79D98F542702FC69EABD64F908C0D80D
2FA5F078BEB59851D42BC7E4E331AE3A8C384892DF003238CDA82450A6CD02AC
E54ED5F49A093BC938521195C86FECF35FB6EC463C54C6E1609592DC5FA03CCB5E1EDA362FC9AB008C85E66B60147EBA
048F4A28B18496487D65A924F4E37766C3563F41090D442DE61D7A5DD82F5FF1
DA876A11401727E5102B578F87CCF9596AB9D9925FC90CDF99C9DEC7261B2C8C
03D3842335D69A3AA42452274130B3FAAEE6087CB8D783B0E770062C034BB302
7F50401E9FFAF10D1F61067C2E830EBBEC7C8B3B20F5C0AC2E10E68912BF2C82
160BC0CA2FF01E076830F29846C1F6CCBAC7857F2043E8163449048BA8C99AA8
09357F089CB148DDC578E0F11EC10659EF68A57440700F584922CB9842E2BF08AC977CCAA11355E89FA5C18113349F5E
910D166F40F264225BD4C8EB16C5CC6374F8F8E1202D5BF28FA2E8BA38E420A36E67611E6D151051F7C74843E4A72BBC
D9ACEBC528D7CA74B894A0378095E03C9BDBBE13DDA3FCD6D60CD3CAC49746B8
90F94FD262E1859B7E5C08E7ED5B16CDB56D0E930034E315CD011DE3759150DA
6620E176FC61DF250EF2BBC2AB27E0DA45A6E1E27F4E94405EE01C36E892EFBF
7D6A062453236E4C353F19B875D079157D016B223B7D429C8F5BEA9D0F703EED
20631589CB541B32C23CA7EA93B12E2016B5A90A2C9CAA6F5849C0B67158EA635858F26A86282C124AEA3FD31B7FD365
E6CF167CEA0639D1504E0BFC8BAAA7FC7FA36A2286CBBE1312803422018A9F00
0EB81E23FF37366BA4890CBD46BC8AF5A2CD56E9802B8DF5CDDD5114F677CDF6
62195292F6F920DAF91F812FC3B94E8539C157D35D3BC94F2FB7A8481406C655AC0F112C4E10AC36892D43D95827BC1D
65FB9BEBDB9BE975168D47B02CA8A2E0ADE2CD1949E90B06689B0475395663BD5AF3C8FEC42C4138CBBB8956AC55B475
EF0A6961C754FCDBAE0222099DD8AA38DD6A92BCD53A576E105BC5BBD23400B7
A608752C8602A5BD538441DE046DD7AE011FBA87210B57372BCBB471ECC2B720
222C83DDC445F72152E563CD068851389364C1D83C9F7D6353325EFD553560B1
96977544F2F821408C1A88FADB9B1E12D9CA97638A622190F83BC640508B6029
B596A98BDDA5BD2FF1F812BE67FD00456D4D313E9497147E2439B51972B6B752
FC276CAC90756F397C3BC616631CE8B9257E6C25D0DC15C5ED1CEB439ACF04D9
383DD624B98E650E5B4BA28990B4D1912B785C689E6B6A05D77B47B6501CD98F
410814DC8B38DD6EF781B55CA02730092F252082A77400AD90F22BF45A41C849
8DA240E13E8B512B50FF4504A61037D0A3920B00523D51EEF9996CC3907C175E
E655B7C31E346154C5EF7C59E3A710A2A2F145E7403E4ADB388B3A27D6FF59DE
E2FB7F96EA16B20589995E95D0959B4ECC1EDDF86E347EB85FD29D0D5933A2A1
00D910F2B050900735A8446220FF7321813252862A69C05A7A534118E50E61DB
BA1B9E7E26E04542183F085060421553CDD3FD9034AAAFCB7EE980A68B98087E
216EBA33FCE4B834BF64621E557923D8AE41F5895266B7BBDFCA6EAF985F036E
2E9075A45ED6D86C172C9ADAAF5E991DA8DA9CBF2F24D746D22A331E236FBA4A
04E4B185C150AF45A67E15D68282C7558B13BFFB05BFCB71BCFD2B92DE5D9701
1FDC4D759EA89681F76A8F7D336118FE6801EFD10D73925C2749775D9DFED282
FFB32167FBF860418AAABF29B0D4FEF57BB07454ECF4BD2CF175D44E84C04CED
7C6E419658872D298F2E7B02568B9ED870FDFFE5082ED0BBE689FD04EFFFE7E7
ADAB0F3108398C75ECC6D2E572960B5685C336DDF3D6F5B9C12D069F27BA15A8
DAC772C1A81181738CDB8C0E89C2E5B7A57E2DA65CA15232DE96A3C4A599A0E9
7F6204274D90E88B3F5D5AC86EEEE76C27C0D083E79ADA7BCF7D060FE6F05A3E
5BAB4CCB593418CD8965C09C0925EDB78BB4C8A10892F264DF12F50E532F006E
7C67525C921ABAEC4BFAA376162A7B2B2827AE4C9840DC37F067FE3B72BE7304B6EEB5FB1AD17CFF5F079EFECC04AD56
94FF6DD4CE63381778E86E61423EDDDD9CC71C3462D66F1AD9A0AD5D378AECE2
E9CA5BA5C6D6101EA3A51F28E48D49789D60273A9F70D56020D003265517AFA3
EBF55649D226E9FDAAB57C2E1D75E3FD3A8D216488E97D00E932B3542E731D20
2B576C3616DDAFEE23A8ECE24BBD89590C8C2D551F14D8EFA4DBB30216F89C31
EE8D3CF3C503DE4FF368F23585F1A7017FF66A910696A6760AFD2B9822911960
A32910F791C5EE9998DCE5371B3252427315C9D11AD506CE65760611D873C81D
740646D11A32C65A8549B3AEA8A499866C35D926B2BA21ED73934AA37ADCC1E6
0E66EF4CA934A5D1ACED28CBEEAA3AF7941E10918DA79379090B6339F11E267E
D79D8666FD947B0D0D504FF10B048B147CB000AD8CDC1F0DEB395FB72B789963
2F7BA07F18F4A91AEDC08867E9CC4B8689B0831A7CE0E0AEF3D92C0CA9BBC698
1AB4351CFF02CC600C972CE87F69B23F8ECCC32A90BD5F429F8017A80306F23D
ED2AD447E7DF7A34D78A313395FA1C3AF63CE02B77A5B08CE19493CFA1173232
C8C8DEAF10AA3994EE7D6DC8E1EB403042627E0F3524409F40C03A7C0C106A80
5778B4A3E7BE82C07BA6A311A87649F3C7AE5107A89571E14AEF05B9E285C87A
30080347DB3B580B18E8EAC66E1B7227F791773F0342EE0DF8267EC993EC3F24
2DB3B2A17C165B5C6A1D4944A5B595016588F028DD4F763C4ED6B7FE7849E918
C1F0CF343B77F31D9A2E810821393EE9D1E0D4B54A87B2DD8CCFBB16FBD77A75B50A0E78D1E8A86310572443731B9DB5
88EF373C37AADAE1155E7DBDBB7E0B048E3BFEFB412DC49EA8A48E1544B6DC87
98694781F3EE698ABA8D2CDFFB1CA0425AA17BFE904FC7812E65A78DF1CA06C2
6BBBCA6672311E1A3BF7001B3222890C8A68A8B7D87DE91624BB9D1FEC0E2728
4550A44B654085C3A3620B5D4D2C6A7F962275BC5926B9B7E3A706F128BF6D6C967E2BF2ACD4DD000BDC8BDE69684F6F
IV: 6E 6F 74 69 63 65 73 20 62 75 6C 67 65 4F 77 4F
212F687B2D38E6E9066E95894E455A6AD3A8861CDDAFD09DBC2557506676332D
2CFF25E9743E4588820D24998B5047E8019C6E200922BCB66830C7F722822A79
74AA0EF345C27F583042C4EDDBB83D5446387D4E5B31DFD3F7B9D09F0662A0AA95BEB40BF37678954CAE0200B2898D22
6998443F71D17938425CD231B8500E769976601315D79D673AA37E4F9BDCE50B
3335691566FEDDDE5828BE0A5BBBD9AC013987A1134E68020D3F0F477E09BC71
1B3A3CBB3D56783220BEAECEA15FFF5CD92AF44F63547F6F84F4533D2B3D820B
A1ECFE74E714D0E1514D0C18903598E7FB7E7AF5C0165DEDE85DFAE05B26C0EC
BA3F2EFBE7CB6671206EA90DA903FF48CCABEC9F393D55B9DB3B7E207A48C059
CA91805C8ADF02501B41D6BF0C69D7C3DD6E218D4B4EB6BED2FDE1367256E544
5465E78D877F2594AF40115723C8AC71D09F60ACCC8121CD3B6BA271BEA43B5C
BB1D3DE0A048694FC86CA8E79BB7BFBB1EF91515C44E917FD5017676EE5C4B13
48C08D8B2B42D025D2557CC650EDA3C5CC301CD71169CFE0044C07A95224109B
3DAE6C5B94C237FF10CF82FAA58517DB4DD4E4D82FD81CFAAA4836C9B8460CE1
645F8F5D213889BAB0F437497856A0CF402E3D4A3679258BB0BF2B768D1F1714
AFC3C462698C795B1A407F27F499CD1757931BD676BA43E4B41E8EF2672EC4AE
5A19F0D1CD87C40883D7BE51377CDAD94C977B4F6CFCCECED91D56867C9EC211
716645BD4F5B18002417A92D6A2BD29D18087F7ADB549A90A2661EB4163EF190
54B83F7F7F50AD4C3DC3498822240AEB7D9A43728704F14691A39C79FAC9B6A3
FFDDCEFAFF3ADA3616B388684B1AD4987B83E459BA5037E377FE17CC7EDAA430
9CC98590A371E580B164A5F1369945A6DB653797331EC898755E84BE93EDF3EF
63697AD15064194A3986CC1F3111A09E3D86AC8D370A8EC4D22EDDA4B9661B63
F7CE4FA63EA9D388AE4AB6A468768AE91E2E3CF6F1D9A5EA17845466A6801A8C
38B2DE32CF36D68842CB31FD97394892B76FB4E81FCFB33164676BAB354939DE
93192FA1D7E347AF9FC98EE49A8B02869527511D0B263FD3B08CCFE645A39F51
3D5F832CF75D94B58B078B4EE0DFC1D987795CD49959517F391F9E9F09CA187C
6CBCCCE0F8E6A833D5CA18E09E821A7F93B9FD7D912632E277B1721985F4B701
5896050E9F4950CD7061ED4DD3BA9FC6013CA52F5C1B0493110FA3934116AC66
F0D3FF283FBD7CFBFE697DBE752EDD5A1F124DCCAE3B629A146E0E85A7ED4E06
6B6C7C365A5CABA2F1023ED1F97D182F0B9D575C64393272E19B8B809DF3C329
1AF30761D85AE1C6DD4D78D4ECE79983C7F58ECE859709570262811CFCA8C97F
FC86201D96F4AE36C63A1C00DA34AC14BFDE317383D19A534362E6149E860366
54F55F34A27E1D690F56269D29C795105C8407A4E1A9ED431588F559948C115A
4DD62ECD2FE8CBB783E3D7F0B1D95496D585405DE5AEB07C8A4E053B3AAAD808
EDB356AFA869EFB987F099239449D6B9F1469EA6D8C9F0A6B4171F685E2853A1
03C058A2C0DB83179E520F035BBAAB01673EFE4FDE717A0F5D1AB6B81BC9D033
D2F33C08136AB161EED6121E721F8C479CED5F26A1B7DEEDEA8580314C232C5E
51CB3275D6D7250CD88A446242590C3DFD36DF83F5E34379DE73601511CD24B9
ED4EC928AC505CBEFA6ACB2901C8E2221FC5073624B0B78F8365DD0442EFBA10
76F959BBF8A4EC4AC22F41F06719BCBA58F070CB65B282BFC2C99F6CC02E30DB
0A44A7C5D4ECF2F08CBDF3FE947D45C6AC586D4558DBFB36719927F58EBB525B
F096777DDC5BF671CBF1A9682EBEE8308AA1002F6CB6899D575A30CFACB30125C8378864C77922360ADD0AC486EFF7F4
372863D5B6F6D7A671AB734A99403AF230DEBBBA435AB8F236B179960FAFDFC6
6CC31C5E86CF6851D92FA973253B3B1FC9023B150980A216A63720CD74592EB7
6643B2CA94415FE4A4E25A3086B8370172C53283360A7BD54B322BD889A5A831
03209037BD7E96A16C69A4F0B6FEB3F2ABA1408353BE09C53485CB7C2362A490
BE4619EC34F7FB8DFE53A8ABFDE6A831895B88D0AB1A9B4861BC7EC7F1121611
41223F7AD8B0F28D08C1EEC2B96324F337F6428759032CAE58A04E8511BC4CBCE497EE1502F18BED5B5038E719F4F085
1533D9B78F16803DFD8AC0C9C85ECEB6BABE9ADF83BF0B6692857BAA03843444064FCDBC89464D9E002AAFF53C65098B
466CC36F82D1AF9BA0704789B2F8B249E79D4964E7E5CB74917760E7D30AB7FA
E9673C0CF35137C179132DDCA57869E285FE6014BB32C5DD78E7549ED0076906
A511A9999C777750FA348772A9E2881A75DEC94ED833AC282037B3E10913B150
F20652B2CA0E0ADD34331D2462CD5AFACC4E01F24EF17C97CFF57089039AD7D8
AFCB6518894626B9E8AFC7E7D9D0430D1D820969CE79B61B91E63D08D4995308D8403705AE4E25AB0F254D47FB03A1A2
C43987548689A1DD9A65A3BE76F97A8470263ACAC0696B5882383AE97893B5D6
7FB47AC9A16094C38D436F9932041A40F6B4644A633361B47C6F6E4A8F2E7C26
BDD48C4689912872055999ECADCF93D152ACDEC34C6318F2FBBBA1A34C8CC166FCBA31AED11F95C973B21A646828CEEA
1582E265DF46C8A9A019AF338104C267873FD3C2C7AF290263FEF921CC9926E76F37FA31881307C675CF37F8A5A236D7
790D8AF6D9F5B634F953F5751378BA5412425B950C696549902EB6A9383630D0
6F44FF0DB55651218C81B41153C9D77B1911C5BFA70650FB9326DD6B8A1479EE
4B0891C1C24E004AB2869A858941C47346EF0789DFAEA1CD1B0E008A73AF2F7E
7E9F990652FD6632612866B6ECA726EAE2112234EBCA59D0C1BF220B94E1F472
9C08E9DAE977DD30E61E6634F51BFD654330C34E9FB3AA2E8856B428141D7164
7C0E756C593FFBBE2609F63D142A0D53F99772EB0722616DFC7B203BA70AA5AE
52C03D8984496DFF95503D843CA1980A6CA46AAE3390C6D4D38E277A0937429C185C118B98B6EBE0711F10F47F8D8F3F
DE8A5BFD2AB351BCAD1D7817737946EF2445E3C974C9E11BF7AD058463F0E437
D09ABB320F59A75C4B2EE8B52E45D52C46226DD8BC8339F53229578733928F1F
C2E6F135E638C83363097EB79ED9A58E94A8E5F626E27083D1363E8BC61B55A4
F06CC55446D0FCCCE5AD007949AE3FCA9C6FCB3C0CA1B5DE1BA65544B55326F1CF726E1F1E5CA90CA2B986930C9B9D83

View File

@@ -44,6 +44,10 @@ void subghz_scene_set_button_on_enter(void* context) {
byte_ptr = &subghz->gen_info->beninca_arc.btn;
byte_count = sizeof(subghz->gen_info->beninca_arc.btn);
break;
case GenJarolift:
byte_ptr = &subghz->gen_info->jarolift.btn;
byte_count = sizeof(subghz->gen_info->jarolift.btn);
break;
case GenNiceFlorS:
byte_ptr = &subghz->gen_info->nice_flor_s.btn;
byte_count = sizeof(subghz->gen_info->nice_flor_s.btn);
@@ -92,6 +96,7 @@ bool subghz_scene_set_button_on_event(void* context, SceneManagerEvent event) {
case GenSomfyTelis:
case GenKingGatesStylo4k:
case GenBenincaARC:
case GenJarolift:
case GenNiceFlorS:
case GenSecPlus2:
scene_manager_next_scene(subghz->scene_manager, SubGhzSceneSetCounter);

View File

@@ -50,6 +50,10 @@ void subghz_scene_set_counter_on_enter(void* context) {
byte_ptr = (uint8_t*)&subghz->gen_info->beninca_arc.cnt;
byte_count = sizeof(subghz->gen_info->beninca_arc.cnt);
break;
case GenJarolift:
byte_ptr = (uint8_t*)&subghz->gen_info->jarolift.cnt;
byte_count = sizeof(subghz->gen_info->jarolift.cnt);
break;
case GenNiceFlorS:
byte_ptr = (uint8_t*)&subghz->gen_info->nice_flor_s.cnt;
byte_count = sizeof(subghz->gen_info->nice_flor_s.cnt);
@@ -128,6 +132,9 @@ bool subghz_scene_set_counter_on_event(void* context, SceneManagerEvent event) {
case GenBenincaARC:
subghz->gen_info->beninca_arc.cnt = __bswap32(subghz->gen_info->beninca_arc.cnt);
break;
case GenJarolift:
subghz->gen_info->jarolift.cnt = __bswap16(subghz->gen_info->jarolift.cnt);
break;
case GenNiceFlorS:
subghz->gen_info->nice_flor_s.cnt = __bswap16(subghz->gen_info->nice_flor_s.cnt);
break;
@@ -204,6 +211,15 @@ bool subghz_scene_set_counter_on_event(void* context, SceneManagerEvent event) {
subghz->gen_info->beninca_arc.btn,
subghz->gen_info->beninca_arc.cnt);
break;
case GenJarolift:
generated_protocol = subghz_txrx_gen_jarolift_protocol(
subghz->txrx,
subghz->gen_info->mod,
subghz->gen_info->freq,
subghz->gen_info->jarolift.serial,
subghz->gen_info->jarolift.btn,
subghz->gen_info->jarolift.cnt);
break;
case GenNiceFlorS:
generated_protocol = subghz_txrx_gen_nice_flor_s_protocol(
subghz->txrx,

View File

@@ -32,6 +32,7 @@ void subghz_scene_set_seed_on_enter(void* context) {
case GenSomfyTelis:
case GenKingGatesStylo4k:
case GenBenincaARC:
case GenJarolift:
case GenNiceFlorS:
case GenSecPlus2:
case GenPhoenixV2:
@@ -93,6 +94,7 @@ bool subghz_scene_set_seed_on_event(void* context, SceneManagerEvent event) {
case GenSomfyTelis:
case GenKingGatesStylo4k:
case GenBenincaARC:
case GenJarolift:
case GenNiceFlorS:
case GenSecPlus2:
case GenPhoenixV2:

View File

@@ -50,6 +50,10 @@ void subghz_scene_set_serial_on_enter(void* context) {
byte_ptr = (uint8_t*)&subghz->gen_info->beninca_arc.serial;
byte_count = sizeof(subghz->gen_info->beninca_arc.serial);
break;
case GenJarolift:
byte_ptr = (uint8_t*)&subghz->gen_info->jarolift.serial;
byte_count = sizeof(subghz->gen_info->jarolift.serial);
break;
case GenNiceFlorS:
byte_ptr = (uint8_t*)&subghz->gen_info->nice_flor_s.serial;
byte_count = sizeof(subghz->gen_info->nice_flor_s.serial);
@@ -122,6 +126,9 @@ bool subghz_scene_set_serial_on_event(void* context, SceneManagerEvent event) {
subghz->gen_info->kinggates_stylo_4k.serial =
__bswap32(subghz->gen_info->kinggates_stylo_4k.serial);
break;
case GenJarolift:
subghz->gen_info->jarolift.serial = __bswap32(subghz->gen_info->jarolift.serial);
break;
case GenBenincaARC:
subghz->gen_info->beninca_arc.serial =
__bswap32(subghz->gen_info->beninca_arc.serial);
@@ -154,6 +161,7 @@ bool subghz_scene_set_serial_on_event(void* context, SceneManagerEvent event) {
case GenSomfyTelis:
case GenKingGatesStylo4k:
case GenBenincaARC:
case GenJarolift:
case GenNiceFlorS:
case GenSecPlus2:
scene_manager_next_scene(subghz->scene_manager, SubGhzSceneSetButton);

View File

@@ -22,6 +22,7 @@ static const char* submenu_names[SetTypeMAX] = {
[SetTypePhoenix_V2_433] = "V2 Phoenix 433MHz",
[SetTypeKingGatesStylo4k] = "KingGates Stylo4k 433M.",
[SetTypeBenincaARC] = "Beninca ARC 433MHz",
[SetTypeJarolift] = "Jarolift 433MHz",
[SetTypeHCS101_433_92] = "KL: HCS101 433MHz",
[SetTypeDoorHan_315_00] = "KL: DoorHan 315MHz",
[SetTypeDoorHan_433_92] = "KL: DoorHan 433MHz",
@@ -207,6 +208,15 @@ bool subghz_scene_set_type_generate_protocol_from_infos(SubGhz* subghz) {
gen_info.beninca_arc.btn,
gen_info.beninca_arc.cnt);
break;
case GenJarolift:
generated_protocol = subghz_txrx_gen_jarolift_protocol(
subghz->txrx,
gen_info.mod,
gen_info.freq,
gen_info.jarolift.serial,
gen_info.jarolift.btn,
gen_info.jarolift.cnt);
break;
case GenNiceFlorS:
generated_protocol = subghz_txrx_gen_nice_flor_s_protocol(
subghz->txrx,
@@ -288,6 +298,7 @@ bool subghz_scene_set_type_on_event(void* context, SceneManagerEvent event) {
case GenSomfyTelis: // 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)
case GenNiceFlorS: // Serial (u32), Button (u8), Counter (u16)
case GenSecPlus2: // Serial (u32), Button (u8), Counter (u32)
case GenPhoenixV2: // Serial (u32), Counter (u16)

View File

@@ -25,7 +25,6 @@ struct SubGhzProtocolDecoderAlutech_at_4n {
SubGhzBlockDecoder decoder;
SubGhzBlockGeneric generic;
uint64_t data;
uint32_t crc;
uint16_t header_count;
@@ -578,13 +577,12 @@ void subghz_protocol_decoder_alutech_at_4n_feed(void* context, bool level, uint3
instance->decoder.parser_step = Alutech_at_4nDecoderStepReset;
break;
}
if((instance->header_count > 2) &&
if((instance->header_count > 9) &&
(DURATION_DIFF(duration, subghz_protocol_alutech_at_4n_const.te_short * 10) <
subghz_protocol_alutech_at_4n_const.te_delta * 10)) {
// Found header
instance->decoder.parser_step = Alutech_at_4nDecoderStepSaveDuration;
instance->decoder.decode_data = 0;
instance->data = 0;
instance->decoder.decode_count_bit = 0;
} else {
instance->decoder.parser_step = Alutech_at_4nDecoderStepReset;
@@ -617,8 +615,8 @@ void subghz_protocol_decoder_alutech_at_4n_feed(void* context, bool level, uint3
instance->decoder.parser_step = Alutech_at_4nDecoderStepReset;
if(instance->decoder.decode_count_bit ==
subghz_protocol_alutech_at_4n_const.min_count_bit_for_found) {
if(instance->generic.data != instance->data) {
instance->generic.data = instance->data;
if(instance->generic.data != instance->generic.data_2) {
instance->generic.data = instance->generic.data_2;
instance->generic.data_count_bit = instance->decoder.decode_count_bit;
instance->crc = instance->decoder.decode_data;
@@ -627,7 +625,6 @@ void subghz_protocol_decoder_alutech_at_4n_feed(void* context, bool level, uint3
instance->base.callback(&instance->base, instance->base.context);
}
instance->decoder.decode_data = 0;
instance->data = 0;
instance->decoder.decode_count_bit = 0;
instance->header_count = 0;
}
@@ -640,7 +637,7 @@ void subghz_protocol_decoder_alutech_at_4n_feed(void* context, bool level, uint3
subghz_protocol_alutech_at_4n_const.te_delta * 2)) {
subghz_protocol_blocks_add_bit(&instance->decoder, 1);
if(instance->decoder.decode_count_bit == 64) {
instance->data = instance->decoder.decode_data;
instance->generic.data_2 = instance->decoder.decode_data;
instance->decoder.decode_data = 0;
}
instance->decoder.parser_step = Alutech_at_4nDecoderStepSaveDuration;
@@ -652,7 +649,7 @@ void subghz_protocol_decoder_alutech_at_4n_feed(void* context, bool level, uint3
subghz_protocol_alutech_at_4n_const.te_delta)) {
subghz_protocol_blocks_add_bit(&instance->decoder, 0);
if(instance->decoder.decode_count_bit == 64) {
instance->data = instance->decoder.decode_data;
instance->generic.data_2 = instance->decoder.decode_data;
instance->decoder.decode_data = 0;
}
instance->decoder.parser_step = Alutech_at_4nDecoderStepSaveDuration;

View File

@@ -0,0 +1,777 @@
#include "jarolift.h"
#include "core/log.h"
#include "keeloq_common.h"
#include "../subghz_keystore.h"
#include "../blocks/const.h"
#include "../blocks/decoder.h"
#include "../blocks/encoder.h"
#include "../blocks/generic.h"
#include "../blocks/math.h"
#include "../blocks/custom_btn_i.h"
#define TAG "SubGhzProtocolJarolift"
static const SubGhzBlockConst subghz_protocol_jarolift_const = {
.te_short = 400,
.te_long = 800,
.te_delta = 167,
.min_count_bit_for_found = 72,
};
struct SubGhzProtocolDecoderJarolift {
SubGhzProtocolDecoderBase base;
SubGhzBlockDecoder decoder;
SubGhzBlockGeneric generic;
uint16_t header_count;
SubGhzKeystore* keystore;
};
struct SubGhzProtocolEncoderJarolift {
SubGhzProtocolEncoderBase base;
SubGhzProtocolBlockEncoder encoder;
SubGhzBlockGeneric generic;
SubGhzKeystore* keystore;
};
typedef enum {
JaroliftDecoderStepReset = 0,
JaroliftDecoderStepCheckPreambula,
JaroliftDecoderStepSaveDuration,
JaroliftDecoderStepCheckDuration,
} JaroliftDecoderStep;
const SubGhzProtocolDecoder subghz_protocol_jarolift_decoder = {
.alloc = subghz_protocol_decoder_jarolift_alloc,
.free = subghz_protocol_decoder_jarolift_free,
.feed = subghz_protocol_decoder_jarolift_feed,
.reset = subghz_protocol_decoder_jarolift_reset,
.get_hash_data = subghz_protocol_decoder_jarolift_get_hash_data,
.serialize = subghz_protocol_decoder_jarolift_serialize,
.deserialize = subghz_protocol_decoder_jarolift_deserialize,
.get_string = subghz_protocol_decoder_jarolift_get_string,
};
const SubGhzProtocolEncoder subghz_protocol_jarolift_encoder = {
.alloc = subghz_protocol_encoder_jarolift_alloc,
.free = subghz_protocol_encoder_jarolift_free,
.deserialize = subghz_protocol_encoder_jarolift_deserialize,
.stop = subghz_protocol_encoder_jarolift_stop,
.yield = subghz_protocol_encoder_jarolift_yield,
};
const SubGhzProtocol subghz_protocol_jarolift = {
.name = SUBGHZ_PROTOCOL_JAROLIFT_NAME,
.type = SubGhzProtocolTypeDynamic,
.flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable |
SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Send,
.decoder = &subghz_protocol_jarolift_decoder,
.encoder = &subghz_protocol_jarolift_encoder,
};
//
// Encoder
//
// Pre define function
static void subghz_protocol_jarolift_remote_controller(
SubGhzBlockGeneric* instance,
SubGhzKeystore* keystore);
/**
* Defines the button value for the current btn_id
* Basic set | 0x1 | 0x2 | 0x4 | 0x8 |
* @return Button code
*/
static uint8_t subghz_protocol_jarolift_get_btn_code(void);
void* subghz_protocol_encoder_jarolift_alloc(SubGhzEnvironment* environment) {
SubGhzProtocolEncoderJarolift* instance = malloc(sizeof(SubGhzProtocolEncoderJarolift));
instance->base.protocol = &subghz_protocol_jarolift;
instance->generic.protocol_name = instance->base.protocol->name;
instance->keystore = subghz_environment_get_keystore(environment);
instance->encoder.repeat = 10;
instance->encoder.size_upload = 256;
instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration));
instance->encoder.is_running = false;
return instance;
}
void subghz_protocol_encoder_jarolift_free(void* context) {
furi_assert(context);
SubGhzProtocolEncoderJarolift* instance = context;
free(instance->encoder.upload);
free(instance);
}
void subghz_protocol_encoder_jarolift_stop(void* context) {
SubGhzProtocolEncoderJarolift* instance = context;
instance->encoder.is_running = false;
}
LevelDuration subghz_protocol_encoder_jarolift_yield(void* context) {
SubGhzProtocolEncoderJarolift* instance = context;
if(instance->encoder.repeat == 0 || !instance->encoder.is_running) {
instance->encoder.is_running = false;
return level_duration_reset();
}
LevelDuration ret = instance->encoder.upload[instance->encoder.front];
if(++instance->encoder.front == instance->encoder.size_upload) {
instance->encoder.repeat--;
instance->encoder.front = 0;
}
return ret;
}
/**
* Key generation from simple data
* @param instance Pointer to a SubGhzProtocolEncoderJarolift* instance
* @param btn Button number, 4 bit
*/
static bool
subghz_protocol_jarolift_gen_data(SubGhzProtocolEncoderJarolift* instance, uint8_t btn) {
// Save original button for later use
if(subghz_custom_btn_get_original() == 0) {
subghz_custom_btn_set_original(btn);
}
btn = subghz_protocol_jarolift_get_btn_code();
// Check for OFEX (overflow experimental) mode
if(furi_hal_subghz_get_rolling_counter_mult() != -0x7FFFFFFF) {
// standart counter mode. PULL data from subghz_block_generic_global variables
if(!subghz_block_generic_global_counter_override_get(&instance->generic.cnt)) {
// if counter_override_get return FALSE then counter was not changed and we increase counter by standart mult value
if((instance->generic.cnt + furi_hal_subghz_get_rolling_counter_mult()) > 0xFFFF) {
instance->generic.cnt = 0;
} else {
instance->generic.cnt += furi_hal_subghz_get_rolling_counter_mult();
}
}
} else {
if((instance->generic.cnt + 0x1) > 0xFFFF) {
instance->generic.cnt = 0;
} else if(instance->generic.cnt >= 0x1 && instance->generic.cnt != 0xFFFE) {
instance->generic.cnt = 0xFFFE;
} else {
instance->generic.cnt++;
}
}
//(instance->generic.seed >> 8) = 8 bit grouping channel 0-7
uint32_t hop_decrypted = (uint64_t)((instance->generic.seed >> 8) & 0xFF) << 24 |
((instance->generic.serial & 0xFF) << 16) |
(instance->generic.cnt & 0xFFFF);
uint64_t hop_encrypted = 0;
for
M_EACH(manufacture_code, *subghz_keystore_get_data(instance->keystore), SubGhzKeyArray_t) {
if(manufacture_code->type == KEELOQ_LEARNING_NORMAL_JAROLIFT) {
// Normal Learning
uint64_t man = subghz_protocol_keeloq_common_normal_learning(
instance->generic.serial, manufacture_code->key);
hop_encrypted = subghz_protocol_keeloq_common_encrypt(hop_decrypted, man);
break;
}
}
// If we got some issue, return false
if(hop_encrypted == 0) {
return false;
}
uint64_t fix = (uint64_t)btn << 60 | ((uint64_t)(instance->generic.serial & 0xFFFFFFF) << 32) |
hop_encrypted;
instance->generic.data = subghz_protocol_blocks_reverse_key(fix, 64);
//(instance->generic.seed & 0xFF) = 8 bit for grouping 8-16
instance->generic.data_2 =
subghz_protocol_blocks_reverse_key((instance->generic.seed & 0xFF), 8);
return true;
}
bool subghz_protocol_jarolift_create_data(
void* context,
FlipperFormat* flipper_format,
uint32_t serial,
uint8_t btn,
uint16_t cnt,
SubGhzRadioPreset* preset) {
furi_assert(context);
SubGhzProtocolEncoderJarolift* instance = context;
instance->generic.serial = (serial & 0xFFFFF00);
instance->generic.cnt = cnt;
instance->generic.btn = btn;
instance->generic.seed = 0x0100;
instance->generic.data_count_bit = 72;
// Encode data
//(instance->generic.seed >> 8) = 8 bit grouping channel 0-7
uint32_t hop_decrypted = (uint64_t)((instance->generic.seed >> 8) & 0xFF) << 24 |
((instance->generic.serial & 0xFF) << 16) |
(instance->generic.cnt & 0xFFFF);
uint64_t hop_encrypted = 0;
for
M_EACH(manufacture_code, *subghz_keystore_get_data(instance->keystore), SubGhzKeyArray_t) {
if(manufacture_code->type == KEELOQ_LEARNING_NORMAL_JAROLIFT) {
// Normal Learning
uint64_t man = subghz_protocol_keeloq_common_normal_learning(
instance->generic.serial, manufacture_code->key);
hop_encrypted = subghz_protocol_keeloq_common_encrypt(hop_decrypted, man);
break;
}
}
uint64_t fix = (uint64_t)instance->generic.btn << 60 |
((uint64_t)(instance->generic.serial & 0xFFFFFFF) << 32) | hop_encrypted;
instance->generic.data = subghz_protocol_blocks_reverse_key(fix, 64);
//(instance->generic.seed & 0xFF) = 8 bit for grouping 8-16
instance->generic.data_2 =
subghz_protocol_blocks_reverse_key((instance->generic.seed & 0xFF), 8);
// Encode complete, now serialize
SubGhzProtocolStatus res =
subghz_block_generic_serialize(&instance->generic, flipper_format, preset);
uint8_t key_data[sizeof(uint64_t)] = {0};
for(size_t i = 0; i < sizeof(uint64_t); i++) {
key_data[sizeof(uint64_t) - i - 1] = (instance->generic.data_2 >> (i * 8)) & 0xFF;
}
if(!flipper_format_rewind(flipper_format)) {
FURI_LOG_E(TAG, "Rewind error");
res = SubGhzProtocolStatusErrorParserOthers;
}
if((res == SubGhzProtocolStatusOk) &&
!flipper_format_insert_or_update_hex(flipper_format, "Data", key_data, sizeof(uint64_t))) {
FURI_LOG_E(TAG, "Unable to add Data2");
res = SubGhzProtocolStatusErrorParserOthers;
}
return res == SubGhzProtocolStatusOk;
}
/**
* Generating an upload from data.
* @param instance Pointer to a SubGhzProtocolEncoderJarolift instance
* @return true On success
*/
static bool subghz_protocol_encoder_jarolift_get_upload(
SubGhzProtocolEncoderJarolift* instance,
uint8_t btn) {
furi_assert(instance);
// Gen new key
if(!subghz_protocol_jarolift_gen_data(instance, btn)) {
return false;
}
size_t index = 0;
// Start 14k us delay
instance->encoder.upload[index++] =
level_duration_make(false, (uint32_t)subghz_protocol_jarolift_const.te_long * 18);
// First header bit
instance->encoder.upload[index++] = level_duration_make(true, (uint32_t)1500);
instance->encoder.upload[index++] =
level_duration_make(false, (uint32_t)subghz_protocol_jarolift_const.te_short);
// Finish header
for(uint8_t i = 8; i > 0; i--) {
instance->encoder.upload[index++] =
level_duration_make(true, (uint32_t)subghz_protocol_jarolift_const.te_short);
instance->encoder.upload[index++] =
level_duration_make(false, (uint32_t)subghz_protocol_jarolift_const.te_short);
}
// After header
instance->encoder.upload[index - 1].duration = (uint32_t)3800; // Adjust last low duration
// Send key fix
for(uint8_t i = 64; i > 0; i--) {
if(bit_read(instance->generic.data, i - 1)) {
//send bit 1
instance->encoder.upload[index++] =
level_duration_make(true, (uint32_t)subghz_protocol_jarolift_const.te_short);
instance->encoder.upload[index++] =
level_duration_make(false, (uint32_t)subghz_protocol_jarolift_const.te_long);
} else {
//send bit 0
instance->encoder.upload[index++] =
level_duration_make(true, (uint32_t)subghz_protocol_jarolift_const.te_long);
instance->encoder.upload[index++] =
level_duration_make(false, (uint32_t)subghz_protocol_jarolift_const.te_short);
}
}
// Send grouping byte
for(uint8_t i = 8; i > 0; i--) {
if(bit_read(instance->generic.data_2, i - 1)) {
//send bit 1
instance->encoder.upload[index++] =
level_duration_make(true, (uint32_t)subghz_protocol_jarolift_const.te_short);
instance->encoder.upload[index++] =
level_duration_make(false, (uint32_t)subghz_protocol_jarolift_const.te_long);
} else {
//send bit 0
instance->encoder.upload[index++] =
level_duration_make(true, (uint32_t)subghz_protocol_jarolift_const.te_long);
instance->encoder.upload[index++] =
level_duration_make(false, (uint32_t)subghz_protocol_jarolift_const.te_short);
}
}
// Set upload size after generating upload, fix it later
instance->encoder.size_upload = index;
return true;
}
SubGhzProtocolStatus
subghz_protocol_encoder_jarolift_deserialize(void* context, FlipperFormat* flipper_format) {
furi_assert(context);
SubGhzProtocolEncoderJarolift* instance = context;
SubGhzProtocolStatus res = SubGhzProtocolStatusError;
do {
if(SubGhzProtocolStatusOk !=
subghz_block_generic_deserialize(&instance->generic, flipper_format)) {
FURI_LOG_E(TAG, "Deserialize error");
break;
}
// Optional value
flipper_format_read_uint32(
flipper_format, "Repeat", (uint32_t*)&instance->encoder.repeat, 1);
if(!flipper_format_rewind(flipper_format)) {
FURI_LOG_E(TAG, "Rewind error");
break;
}
uint8_t key_data[sizeof(uint64_t)] = {0};
if(!flipper_format_read_hex(flipper_format, "Data", key_data, sizeof(uint64_t))) {
FURI_LOG_E(TAG, "Missing Data");
break;
}
for(uint8_t i = 0; i < sizeof(uint64_t); i++) {
instance->generic.data_2 = instance->generic.data_2 << 8 | key_data[i];
}
subghz_protocol_jarolift_remote_controller(&instance->generic, instance->keystore);
subghz_protocol_encoder_jarolift_get_upload(instance, instance->generic.btn);
if(!flipper_format_rewind(flipper_format)) {
FURI_LOG_E(TAG, "Rewind error");
break;
}
for(size_t i = 0; i < sizeof(uint64_t); i++) {
key_data[sizeof(uint64_t) - i - 1] = (instance->generic.data >> i * 8) & 0xFF;
}
if(!flipper_format_update_hex(flipper_format, "Key", key_data, sizeof(uint64_t))) {
FURI_LOG_E(TAG, "Unable to update Key");
break;
}
for(size_t i = 0; i < sizeof(uint64_t); i++) {
key_data[sizeof(uint64_t) - i - 1] = (instance->generic.data_2 >> i * 8) & 0xFF;
}
if(!flipper_format_update_hex(flipper_format, "Data", key_data, sizeof(uint64_t))) {
FURI_LOG_E(TAG, "Unable to update Data");
break;
}
instance->encoder.is_running = true;
res = SubGhzProtocolStatusOk;
} while(false);
return res;
}
//
// Decoder
//
void* subghz_protocol_decoder_jarolift_alloc(SubGhzEnvironment* environment) {
SubGhzProtocolDecoderJarolift* instance = malloc(sizeof(SubGhzProtocolDecoderJarolift));
instance->base.protocol = &subghz_protocol_jarolift;
instance->generic.protocol_name = instance->base.protocol->name;
instance->keystore = subghz_environment_get_keystore(environment);
return instance;
}
void subghz_protocol_decoder_jarolift_free(void* context) {
furi_assert(context);
SubGhzProtocolDecoderJarolift* instance = context;
free(instance);
}
void subghz_protocol_decoder_jarolift_reset(void* context) {
furi_assert(context);
SubGhzProtocolDecoderJarolift* instance = context;
instance->decoder.parser_step = JaroliftDecoderStepReset;
}
void subghz_protocol_decoder_jarolift_feed(void* context, bool level, uint32_t duration) {
furi_assert(context);
SubGhzProtocolDecoderJarolift* instance = context;
switch(instance->decoder.parser_step) {
case JaroliftDecoderStepReset:
if((level) && DURATION_DIFF(duration, subghz_protocol_jarolift_const.te_short) <
subghz_protocol_jarolift_const.te_delta) {
instance->decoder.parser_step = JaroliftDecoderStepCheckPreambula;
instance->header_count++;
}
break;
case JaroliftDecoderStepCheckPreambula:
if((!level) && (DURATION_DIFF(duration, subghz_protocol_jarolift_const.te_short) <
subghz_protocol_jarolift_const.te_delta)) {
instance->decoder.parser_step = JaroliftDecoderStepReset;
break;
}
if((!level) && (instance->header_count == 8) &&
(DURATION_DIFF(duration, subghz_protocol_jarolift_const.te_long * 5) <
subghz_protocol_jarolift_const.te_delta * 6)) {
// Found gap after header - 4000us +- 996us
instance->decoder.parser_step = JaroliftDecoderStepSaveDuration;
instance->decoder.decode_data = 0;
instance->decoder.decode_count_bit = 0;
instance->header_count = 0;
break;
} else {
instance->decoder.parser_step = JaroliftDecoderStepReset;
instance->header_count = 0;
}
break;
case JaroliftDecoderStepSaveDuration:
if(level) {
instance->decoder.te_last = duration;
instance->decoder.parser_step = JaroliftDecoderStepCheckDuration;
} else {
instance->header_count = 0;
instance->decoder.parser_step = JaroliftDecoderStepReset;
}
break;
case JaroliftDecoderStepCheckDuration:
if(!level) {
if(instance->decoder.decode_count_bit == 64) {
instance->generic.data = instance->decoder.decode_data;
instance->decoder.decode_data = 0;
}
if((DURATION_DIFF(instance->decoder.te_last, subghz_protocol_jarolift_const.te_short) <
subghz_protocol_jarolift_const.te_delta) &&
(DURATION_DIFF(duration, subghz_protocol_jarolift_const.te_long) <
subghz_protocol_jarolift_const.te_delta)) {
subghz_protocol_blocks_add_bit(&instance->decoder, 1);
instance->decoder.parser_step = JaroliftDecoderStepSaveDuration;
} else if(
(DURATION_DIFF(instance->decoder.te_last, subghz_protocol_jarolift_const.te_long) <
subghz_protocol_jarolift_const.te_delta) &&
(DURATION_DIFF(duration, subghz_protocol_jarolift_const.te_short) <
subghz_protocol_jarolift_const.te_delta)) {
subghz_protocol_blocks_add_bit(&instance->decoder, 0);
instance->decoder.parser_step = JaroliftDecoderStepSaveDuration;
} else {
if(duration >= ((uint32_t)subghz_protocol_jarolift_const.te_long * 3)) {
// Add endbit
if((DURATION_DIFF(
instance->decoder.te_last, subghz_protocol_jarolift_const.te_long) <
subghz_protocol_jarolift_const.te_delta)) {
subghz_protocol_blocks_add_bit(&instance->decoder, 0);
} else if((DURATION_DIFF(
instance->decoder.te_last,
subghz_protocol_jarolift_const.te_short) <
subghz_protocol_jarolift_const.te_delta)) {
subghz_protocol_blocks_add_bit(&instance->decoder, 1);
}
if(instance->decoder.decode_count_bit ==
subghz_protocol_jarolift_const.min_count_bit_for_found) {
instance->generic.data_2 = instance->decoder.decode_data;
instance->generic.data_count_bit = instance->decoder.decode_count_bit;
if(instance->base.callback)
instance->base.callback(&instance->base, instance->base.context);
}
instance->decoder.parser_step = JaroliftDecoderStepReset;
instance->decoder.decode_data = 0;
instance->decoder.decode_count_bit = 0;
instance->header_count = 0;
break;
}
instance->decoder.parser_step = JaroliftDecoderStepReset;
instance->header_count = 0;
}
} else {
instance->decoder.parser_step = JaroliftDecoderStepReset;
instance->header_count = 0;
}
break;
}
}
/**
* Get button name.
* @param btn Button number, 4 bit
*/
static const char* subghz_protocol_jarolift_get_button_name(uint8_t btn) {
const char* btn_name;
switch(btn) {
case 0x1:
btn_name = "Learn";
break;
case 0x2:
btn_name = "Down";
break;
case 0x4:
btn_name = "Stop";
break;
case 0x8:
btn_name = "Up";
break;
default:
btn_name = "Unkn";
break;
}
return btn_name;
}
/**
* Analysis of received data
* @param instance Pointer to a SubGhzBlockGeneric* instance
* @param data Input encrypted data
* @param keystore Pointer to a SubGhzKeystore* instance
*/
static void subghz_protocol_jarolift_remote_controller(
SubGhzBlockGeneric* instance,
SubGhzKeystore* keystore) {
// Jarolift Decoder
// 01.2026 - @xMasterX (MMX) & d82k & Steffen (@bastelbudenbuben de)
// Key samples (reversed)
// 0x821EB600EAC2EAD4 - Btn Up - cnt 0059 group 0100
// 0x821EB6007D9BD66A - Btn Up - cnt 005A group 0100
// 0x821EB600A029FA0E - Btn Up - cnt 005B group 0100
uint32_t group = subghz_protocol_blocks_reverse_key(instance->data_2, 8);
uint64_t key = subghz_protocol_blocks_reverse_key(instance->data, 64);
bool ret = false;
uint32_t decrypt = 0;
instance->serial = (key >> 32) & 0xFFFFFFF;
uint32_t hop = key & 0xFFFFFFFF;
for
M_EACH(manufacture_code, *subghz_keystore_get_data(keystore), SubGhzKeyArray_t) {
if(manufacture_code->type == KEELOQ_LEARNING_NORMAL_JAROLIFT) {
uint64_t man = subghz_protocol_keeloq_common_normal_learning(
instance->serial, manufacture_code->key);
decrypt = subghz_protocol_keeloq_common_decrypt(hop, man);
if(((decrypt >> 16) & 0xFF) == (instance->serial & 0xFF)) {
ret = true;
}
break;
}
}
if(ret) {
instance->btn = (key >> 60) & 0xF;
instance->seed = ((decrypt >> 24) << 8) | (group >> 8);
instance->cnt = decrypt & 0xFFFF;
// 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(3);
} else {
instance->btn = 0;
instance->serial = 0;
instance->cnt = 0;
instance->seed = 0;
}
}
uint8_t subghz_protocol_decoder_jarolift_get_hash_data(void* context) {
furi_assert(context);
SubGhzProtocolDecoderJarolift* instance = context;
uint8_t hash = 0;
uint8_t* p = (uint8_t*)&instance->generic.data;
for(size_t i = 0; i < 16; i++) {
hash ^= p[i];
}
return hash;
}
SubGhzProtocolStatus subghz_protocol_decoder_jarolift_serialize(
void* context,
FlipperFormat* flipper_format,
SubGhzRadioPreset* preset) {
furi_assert(context);
SubGhzProtocolDecoderJarolift* instance = context;
SubGhzProtocolStatus ret =
subghz_block_generic_serialize(&instance->generic, flipper_format, preset);
uint8_t key_data[sizeof(uint64_t)] = {0};
for(size_t i = 0; i < sizeof(uint64_t); i++) {
key_data[sizeof(uint64_t) - i - 1] = (instance->generic.data_2 >> (i * 8)) & 0xFF;
}
if(!flipper_format_rewind(flipper_format)) {
FURI_LOG_E(TAG, "Rewind error");
ret = SubGhzProtocolStatusErrorParserOthers;
}
if((ret == SubGhzProtocolStatusOk) &&
!flipper_format_insert_or_update_hex(flipper_format, "Data", key_data, sizeof(uint64_t))) {
FURI_LOG_E(TAG, "Unable to add Data");
ret = SubGhzProtocolStatusErrorParserOthers;
}
return ret;
}
SubGhzProtocolStatus
subghz_protocol_decoder_jarolift_deserialize(void* context, FlipperFormat* flipper_format) {
furi_assert(context);
SubGhzProtocolDecoderJarolift* instance = context;
SubGhzProtocolStatus ret = SubGhzProtocolStatusError;
do {
ret = subghz_block_generic_deserialize_check_count_bit(
&instance->generic,
flipper_format,
subghz_protocol_jarolift_const.min_count_bit_for_found);
if(ret != SubGhzProtocolStatusOk) {
break;
}
if(!flipper_format_rewind(flipper_format)) {
FURI_LOG_E(TAG, "Rewind error");
ret = SubGhzProtocolStatusErrorParserOthers;
break;
}
uint8_t key_data[sizeof(uint64_t)] = {0};
if(!flipper_format_read_hex(flipper_format, "Data", key_data, sizeof(uint64_t))) {
FURI_LOG_E(TAG, "Missing Data");
ret = SubGhzProtocolStatusErrorParserOthers;
break;
}
for(uint8_t i = 0; i < sizeof(uint64_t); i++) {
instance->generic.data_2 = instance->generic.data_2 << 8 | key_data[i];
}
} while(false);
return ret;
}
static uint8_t subghz_protocol_jarolift_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 0x1:
btn = 0x2;
break;
case 0x2:
btn = 0x1;
break;
case 0x4:
btn = 0x1;
break;
case 0x8:
btn = 0x1;
break;
default:
break;
}
} else if(custom_btn_id == SUBGHZ_CUSTOM_BTN_DOWN) {
switch(original_btn_code) {
case 0x1:
btn = 0x4;
break;
case 0x2:
btn = 0x4;
break;
case 0x4:
btn = 0x2;
break;
case 0x8:
btn = 0x4;
break;
default:
break;
}
} else if(custom_btn_id == SUBGHZ_CUSTOM_BTN_LEFT) {
switch(original_btn_code) {
case 0x1:
btn = 0x8;
break;
case 0x2:
btn = 0x8;
break;
case 0x4:
btn = 0x8;
break;
case 0x8:
btn = 0x2;
break;
default:
break;
}
}
return btn;
}
void subghz_protocol_decoder_jarolift_get_string(void* context, FuriString* output) {
furi_assert(context);
SubGhzProtocolDecoderJarolift* instance = context;
subghz_protocol_jarolift_remote_controller(&instance->generic, instance->keystore);
// push protocol data to global variable
subghz_block_generic_global.cnt_is_available = true;
subghz_block_generic_global.cnt_length_bit = 16;
subghz_block_generic_global.current_cnt = instance->generic.cnt;
furi_string_cat_printf(
output,
"%s %dbit\r\n"
"Key:%0llX\r\n"
"Sn:%07lX Btn:%01X - %s\r\n"
"Cnt:%04lX Group:%04lX\r\n",
instance->generic.protocol_name,
instance->generic.data_count_bit,
instance->generic.data,
instance->generic.serial,
instance->generic.btn,
subghz_protocol_jarolift_get_button_name(instance->generic.btn),
instance->generic.cnt,
instance->generic.seed);
}

View File

@@ -0,0 +1,108 @@
#pragma once
#include "base.h"
#define SUBGHZ_PROTOCOL_JAROLIFT_NAME "Jarolift"
typedef struct SubGhzProtocolDecoderJarolift SubGhzProtocolDecoderJarolift;
typedef struct SubGhzProtocolEncoderJarolift SubGhzProtocolEncoderJarolift;
extern const SubGhzProtocolDecoder subghz_protocol_jarolift_decoder;
extern const SubGhzProtocolEncoder subghz_protocol_jarolift_encoder;
extern const SubGhzProtocol subghz_protocol_jarolift;
/**
* Allocate SubGhzProtocolEncoderJarolift.
* @param environment Pointer to a SubGhzEnvironment instance
* @return SubGhzProtocolEncoderJarolift* pointer to a SubGhzProtocolEncoderJarolift instance
*/
void* subghz_protocol_encoder_jarolift_alloc(SubGhzEnvironment* environment);
/**
* Free SubGhzProtocolEncoderJarolift.
* @param context Pointer to a SubGhzProtocolEncoderJarolift instance
*/
void subghz_protocol_encoder_jarolift_free(void* context);
/**
* Deserialize and generating an upload to send.
* @param context Pointer to a SubGhzProtocolEncoderJarolift instance
* @param flipper_format Pointer to a FlipperFormat instance
* @return true On success
*/
SubGhzProtocolStatus
subghz_protocol_encoder_jarolift_deserialize(void* context, FlipperFormat* flipper_format);
/**
* Forced transmission stop.
* @param context Pointer to a SubGhzProtocolEncoderJarolift instance
*/
void subghz_protocol_encoder_jarolift_stop(void* context);
/**
* Getting the level and duration of the upload to be loaded into DMA.
* @param context Pointer to a SubGhzProtocolEncoderJarolift instance
* @return LevelDuration
*/
LevelDuration subghz_protocol_encoder_jarolift_yield(void* context);
/**
* Allocate SubGhzProtocolDecoderJarolift.
* @param environment Pointer to a SubGhzEnvironment instance
* @return SubGhzProtocolDecoderJarolift* pointer to a SubGhzProtocolDecoderJarolift instance
*/
void* subghz_protocol_decoder_jarolift_alloc(SubGhzEnvironment* environment);
/**
* Free SubGhzProtocolDecoderJarolift.
* @param context Pointer to a SubGhzProtocolDecoderJarolift instance
*/
void subghz_protocol_decoder_jarolift_free(void* context);
/**
* Reset decoder SubGhzProtocolDecoderJarolift.
* @param context Pointer to a SubGhzProtocolDecoderJarolift instance
*/
void subghz_protocol_decoder_jarolift_reset(void* context);
/**
* Parse a raw sequence of levels and durations received from the air.
* @param context Pointer to a SubGhzProtocolDecoderJarolift instance
* @param level Signal level true-high false-low
* @param duration Duration of this level in, us
*/
void subghz_protocol_decoder_jarolift_feed(void* context, bool level, uint32_t duration);
/**
* Getting the hash sum of the last randomly received parcel.
* @param context Pointer to a SubGhzProtocolDecoderJarolift instance
* @return hash Hash sum
*/
uint8_t subghz_protocol_decoder_jarolift_get_hash_data(void* context);
/**
* Serialize data SubGhzProtocolDecoderJarolift.
* @param context Pointer to a SubGhzProtocolDecoderJarolift instance
* @param flipper_format Pointer to a FlipperFormat instance
* @param preset The modulation on which the signal was received, SubGhzRadioPreset
* @return status
*/
SubGhzProtocolStatus subghz_protocol_decoder_jarolift_serialize(
void* context,
FlipperFormat* flipper_format,
SubGhzRadioPreset* preset);
/**
* Deserialize data SubGhzProtocolDecoderJarolift.
* @param context Pointer to a SubGhzProtocolDecoderJarolift instance
* @param flipper_format Pointer to a FlipperFormat instance
* @return status
*/
SubGhzProtocolStatus
subghz_protocol_decoder_jarolift_deserialize(void* context, FlipperFormat* flipper_format);
/**
* Getting a textual representation of the received data.
* @param context Pointer to a SubGhzProtocolDecoderJarolift instance
* @param output Resulting text
*/
void subghz_protocol_decoder_jarolift_get_string(void* context, FuriString* output);

View File

@@ -26,6 +26,8 @@
#define KEELOQ_LEARNING_MAGIC_SERIAL_TYPE_2 7u
#define KEELOQ_LEARNING_MAGIC_SERIAL_TYPE_3 8u
// #define BENINCA_ARC_KEY_TYPE 9u -- RESERVED
#define KEELOQ_LEARNING_SIMPLE_KINGGATES 10u
#define KEELOQ_LEARNING_NORMAL_JAROLIFT 11u
/**
* Simple Learning Encrypt

View File

@@ -187,7 +187,7 @@ static bool subghz_protocol_kinggates_stylo_4k_gen_data(
uint64_t encrypt = 0;
for
M_EACH(manufacture_code, *subghz_keystore_get_data(instance->keystore), SubGhzKeyArray_t) {
if(strcmp(furi_string_get_cstr(manufacture_code->name), "Kingates_Stylo4k") == 0) {
if(manufacture_code->type == KEELOQ_LEARNING_SIMPLE_KINGGATES) {
// Simple Learning
encrypt = subghz_protocol_keeloq_common_encrypt(hop, manufacture_code->key);
encrypt = subghz_protocol_blocks_reverse_key(encrypt, 32);
@@ -220,7 +220,7 @@ bool subghz_protocol_kinggates_stylo_4k_create_data(
uint64_t encrypt = 0;
for
M_EACH(manufacture_code, *subghz_keystore_get_data(instance->keystore), SubGhzKeyArray_t) {
if(strcmp(furi_string_get_cstr(manufacture_code->name), "Kingates_Stylo4k") == 0) {
if(manufacture_code->type == KEELOQ_LEARNING_SIMPLE_KINGGATES) {
// Simple Learning
encrypt = subghz_protocol_keeloq_common_encrypt(decrypt, manufacture_code->key);
encrypt = subghz_protocol_blocks_reverse_key(encrypt, 32);
@@ -456,7 +456,6 @@ void subghz_protocol_decoder_kinggates_stylo_4k_feed(void* context, bool level,
subghz_protocol_kinggates_stylo_4k_const.te_delta * 2) {
instance->decoder.parser_step = KingGates_stylo_4kDecoderStepSaveDuration;
instance->decoder.decode_data = 0;
instance->generic.data_2 = 0;
instance->decoder.decode_count_bit = 0;
instance->header_count = 0;
}
@@ -476,7 +475,6 @@ void subghz_protocol_decoder_kinggates_stylo_4k_feed(void* context, bool level,
instance->decoder.parser_step = KingGates_stylo_4kDecoderStepReset;
instance->decoder.decode_data = 0;
instance->generic.data_2 = 0;
instance->decoder.decode_count_bit = 0;
instance->header_count = 0;
break;
@@ -561,7 +559,7 @@ static void subghz_protocol_kinggates_stylo_4k_remote_controller(
for
M_EACH(manufacture_code, *subghz_keystore_get_data(keystore), SubGhzKeyArray_t) {
if(manufacture_code->type == KEELOQ_LEARNING_SIMPLE) {
if(manufacture_code->type == KEELOQ_LEARNING_SIMPLE_KINGGATES) {
decrypt = subghz_protocol_keeloq_common_decrypt(hop, manufacture_code->key);
if(((decrypt >> 28) == instance->btn) && (((decrypt >> 24) & 0x0F) == 0x0C) &&
(((decrypt >> 16) & 0xFF) == (instance->serial & 0xFF))) {

View File

@@ -27,7 +27,7 @@ const SubGhzProtocol* const subghz_protocol_registry_items[] = {
&subghz_protocol_hay21, &subghz_protocol_revers_rb2,
&subghz_protocol_feron, &subghz_protocol_roger,
&subghz_protocol_elplast, &subghz_protocol_treadmill37,
&subghz_protocol_beninca_arc,
&subghz_protocol_beninca_arc, &subghz_protocol_jarolift,
};
const SubGhzProtocolRegistry subghz_protocol_registry = {

View File

@@ -55,3 +55,4 @@
#include "elplast.h"
#include "treadmill37.h"
#include "beninca_arc.h"
#include "jarolift.h"

View File

@@ -231,6 +231,24 @@ bool subghz_protocol_beninca_arc_create_data(
uint32_t cnt,
SubGhzRadioPreset* preset);
/**
* Key generation from simple data.
* @param context Pointer to a SubGhzProtocolEncoderJarolift instance
* @param flipper_format Pointer to a FlipperFormat instance
* @param serial Serial number, 24 bit
* @param btn Button number, 8 bit
* @param cnt Counter value, 16 bit
* @param preset Modulation, SubGhzRadioPreset
* @return true On success
*/
bool subghz_protocol_jarolift_create_data(
void* context,
FlipperFormat* flipper_format,
uint32_t serial,
uint8_t btn,
uint16_t cnt,
SubGhzRadioPreset* preset);
typedef struct SubGhzProtocolDecoderBinRAW SubGhzProtocolDecoderBinRAW;
void subghz_protocol_decoder_bin_raw_data_input_rssi(

View File

@@ -3725,6 +3725,7 @@ Function,+,subghz_protocol_encoder_raw_free,void,void*
Function,+,subghz_protocol_encoder_raw_stop,void,void*
Function,+,subghz_protocol_encoder_raw_yield,LevelDuration,void*
Function,+,subghz_protocol_faac_slh_create_data,_Bool,"void*, FlipperFormat*, uint32_t, uint8_t, uint32_t, uint32_t, const char*, SubGhzRadioPreset*"
Function,+,subghz_protocol_jarolift_create_data,_Bool,"void*, FlipperFormat*, uint32_t, uint8_t, uint16_t, SubGhzRadioPreset*"
Function,+,subghz_protocol_keeloq_bft_create_data,_Bool,"void*, FlipperFormat*, uint32_t, uint8_t, uint16_t, uint32_t, const char*, SubGhzRadioPreset*"
Function,+,subghz_protocol_keeloq_create_data,_Bool,"void*, FlipperFormat*, uint32_t, uint8_t, uint16_t, const char*, SubGhzRadioPreset*"
Function,+,subghz_protocol_kinggates_stylo_4k_create_data,_Bool,"void*, FlipperFormat*, uint32_t, uint8_t, uint16_t, SubGhzRadioPreset*"
1 entry status name type params
3725 Function + subghz_protocol_encoder_raw_stop void void*
3726 Function + subghz_protocol_encoder_raw_yield LevelDuration void*
3727 Function + subghz_protocol_faac_slh_create_data _Bool void*, FlipperFormat*, uint32_t, uint8_t, uint32_t, uint32_t, const char*, SubGhzRadioPreset*
3728 Function + subghz_protocol_jarolift_create_data _Bool void*, FlipperFormat*, uint32_t, uint8_t, uint16_t, SubGhzRadioPreset*
3729 Function + subghz_protocol_keeloq_bft_create_data _Bool void*, FlipperFormat*, uint32_t, uint8_t, uint16_t, uint32_t, const char*, SubGhzRadioPreset*
3730 Function + subghz_protocol_keeloq_create_data _Bool void*, FlipperFormat*, uint32_t, uint8_t, uint16_t, const char*, SubGhzRadioPreset*
3731 Function + subghz_protocol_kinggates_stylo_4k_create_data _Bool void*, FlipperFormat*, uint32_t, uint8_t, uint16_t, SubGhzRadioPreset*