Parcourir la source

[FL-3092] SubGhz: add DOOYA protocol (#2178)

* SubGhz: add DOOYA protocol
* SubGhz: add unit_test DOOYA protocol
* SubGhz: fix protocol Dooya

Co-authored-by: Aleksandr Kutuzov <alleteam@gmail.com>
Skorpionm il y a 2 ans
Parent
commit
0afc4a8982

+ 16 - 1
applications/debug/unit_tests/subghz/subghz_test.c

@@ -13,7 +13,7 @@
 #define CAME_ATOMO_DIR_NAME EXT_PATH("subghz/assets/came_atomo")
 #define CAME_ATOMO_DIR_NAME EXT_PATH("subghz/assets/came_atomo")
 #define NICE_FLOR_S_DIR_NAME EXT_PATH("subghz/assets/nice_flor_s")
 #define NICE_FLOR_S_DIR_NAME EXT_PATH("subghz/assets/nice_flor_s")
 #define TEST_RANDOM_DIR_NAME EXT_PATH("unit_tests/subghz/test_random_raw.sub")
 #define TEST_RANDOM_DIR_NAME EXT_PATH("unit_tests/subghz/test_random_raw.sub")
-#define TEST_RANDOM_COUNT_PARSE 295
+#define TEST_RANDOM_COUNT_PARSE 300
 #define TEST_TIMEOUT 10000
 #define TEST_TIMEOUT 10000
 
 
 static SubGhzEnvironment* environment_handler;
 static SubGhzEnvironment* environment_handler;
@@ -612,6 +612,13 @@ MU_TEST(subghz_decoder_holtek_ht12x_test) {
         "Test decoder " SUBGHZ_PROTOCOL_HOLTEK_HT12X_NAME " error\r\n");
         "Test decoder " SUBGHZ_PROTOCOL_HOLTEK_HT12X_NAME " error\r\n");
 }
 }
 
 
+MU_TEST(subghz_decoder_dooya_test) {
+    mu_assert(
+        subghz_decoder_test(
+            EXT_PATH("unit_tests/subghz/dooya_raw.sub"), SUBGHZ_PROTOCOL_DOOYA_NAME),
+        "Test decoder " SUBGHZ_PROTOCOL_DOOYA_NAME " error\r\n");
+}
+
 //test encoders
 //test encoders
 MU_TEST(subghz_encoder_princeton_test) {
 MU_TEST(subghz_encoder_princeton_test) {
     mu_assert(
     mu_assert(
@@ -757,6 +764,12 @@ MU_TEST(subghz_encoder_holtek_ht12x_test) {
         "Test encoder " SUBGHZ_PROTOCOL_HOLTEK_HT12X_NAME " error\r\n");
         "Test encoder " SUBGHZ_PROTOCOL_HOLTEK_HT12X_NAME " error\r\n");
 }
 }
 
 
+MU_TEST(subghz_encoder_dooya_test) {
+    mu_assert(
+        subghz_encoder_test(EXT_PATH("unit_tests/subghz/dooya.sub")),
+        "Test encoder " SUBGHZ_PROTOCOL_DOOYA_NAME " error\r\n");
+}
+
 MU_TEST(subghz_random_test) {
 MU_TEST(subghz_random_test) {
     mu_assert(subghz_decode_random_test(TEST_RANDOM_DIR_NAME), "Random test error\r\n");
     mu_assert(subghz_decode_random_test(TEST_RANDOM_DIR_NAME), "Random test error\r\n");
 }
 }
@@ -803,6 +816,7 @@ MU_TEST_SUITE(subghz) {
     MU_RUN_TEST(subghz_decoder_ansonic_test);
     MU_RUN_TEST(subghz_decoder_ansonic_test);
     MU_RUN_TEST(subghz_decoder_smc5326_test);
     MU_RUN_TEST(subghz_decoder_smc5326_test);
     MU_RUN_TEST(subghz_decoder_holtek_ht12x_test);
     MU_RUN_TEST(subghz_decoder_holtek_ht12x_test);
+    MU_RUN_TEST(subghz_decoder_dooya_test);
 
 
     MU_RUN_TEST(subghz_encoder_princeton_test);
     MU_RUN_TEST(subghz_encoder_princeton_test);
     MU_RUN_TEST(subghz_encoder_came_test);
     MU_RUN_TEST(subghz_encoder_came_test);
@@ -828,6 +842,7 @@ MU_TEST_SUITE(subghz) {
     MU_RUN_TEST(subghz_encoder_ansonic_test);
     MU_RUN_TEST(subghz_encoder_ansonic_test);
     MU_RUN_TEST(subghz_encoder_smc5326_test);
     MU_RUN_TEST(subghz_encoder_smc5326_test);
     MU_RUN_TEST(subghz_encoder_holtek_ht12x_test);
     MU_RUN_TEST(subghz_encoder_holtek_ht12x_test);
+    MU_RUN_TEST(subghz_encoder_dooya_test);
 
 
     MU_RUN_TEST(subghz_random_test);
     MU_RUN_TEST(subghz_random_test);
     subghz_test_deinit();
     subghz_test_deinit();

+ 7 - 0
assets/unit_tests/subghz/dooya.sub

@@ -0,0 +1,7 @@
+Filetype: Flipper SubGhz Key File
+Version: 1
+Frequency: 433920000
+Preset: FuriHalSubGhzPresetOok650Async
+Protocol: Dooya
+Bit: 40
+Key: 00 00 00 E1 DC 03 05 11

Fichier diff supprimé car celui-ci est trop grand
+ 5 - 0
assets/unit_tests/subghz/dooya_raw.sub


Fichier diff supprimé car celui-ci est trop grand
+ 0 - 0
assets/unit_tests/subghz/test_random_raw.sub


+ 447 - 0
lib/subghz/protocols/dooya.c

@@ -0,0 +1,447 @@
+#include "dooya.h"
+#include "../blocks/const.h"
+#include "../blocks/decoder.h"
+#include "../blocks/encoder.h"
+#include "../blocks/generic.h"
+#include "../blocks/math.h"
+
+#define TAG "SubGhzProtocolDooya"
+
+#define DOYA_SINGLE_CHANNEL 0xFF
+
+static const SubGhzBlockConst subghz_protocol_dooya_const = {
+    .te_short = 366,
+    .te_long = 733,
+    .te_delta = 120,
+    .min_count_bit_for_found = 40,
+};
+
+struct SubGhzProtocolDecoderDooya {
+    SubGhzProtocolDecoderBase base;
+
+    SubGhzBlockDecoder decoder;
+    SubGhzBlockGeneric generic;
+};
+
+struct SubGhzProtocolEncoderDooya {
+    SubGhzProtocolEncoderBase base;
+
+    SubGhzProtocolBlockEncoder encoder;
+    SubGhzBlockGeneric generic;
+};
+
+typedef enum {
+    DooyaDecoderStepReset = 0,
+    DooyaDecoderStepFoundStartBit,
+    DooyaDecoderStepSaveDuration,
+    DooyaDecoderStepCheckDuration,
+} DooyaDecoderStep;
+
+const SubGhzProtocolDecoder subghz_protocol_dooya_decoder = {
+    .alloc = subghz_protocol_decoder_dooya_alloc,
+    .free = subghz_protocol_decoder_dooya_free,
+
+    .feed = subghz_protocol_decoder_dooya_feed,
+    .reset = subghz_protocol_decoder_dooya_reset,
+
+    .get_hash_data = subghz_protocol_decoder_dooya_get_hash_data,
+    .serialize = subghz_protocol_decoder_dooya_serialize,
+    .deserialize = subghz_protocol_decoder_dooya_deserialize,
+    .get_string = subghz_protocol_decoder_dooya_get_string,
+};
+
+const SubGhzProtocolEncoder subghz_protocol_dooya_encoder = {
+    .alloc = subghz_protocol_encoder_dooya_alloc,
+    .free = subghz_protocol_encoder_dooya_free,
+
+    .deserialize = subghz_protocol_encoder_dooya_deserialize,
+    .stop = subghz_protocol_encoder_dooya_stop,
+    .yield = subghz_protocol_encoder_dooya_yield,
+};
+
+const SubGhzProtocol subghz_protocol_dooya = {
+    .name = SUBGHZ_PROTOCOL_DOOYA_NAME,
+    .type = SubGhzProtocolTypeStatic,
+    .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_315 | SubGhzProtocolFlag_AM |
+            SubGhzProtocolFlag_Decodable | SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save |
+            SubGhzProtocolFlag_Send,
+
+    .decoder = &subghz_protocol_dooya_decoder,
+    .encoder = &subghz_protocol_dooya_encoder,
+};
+
+void* subghz_protocol_encoder_dooya_alloc(SubGhzEnvironment* environment) {
+    UNUSED(environment);
+    SubGhzProtocolEncoderDooya* instance = malloc(sizeof(SubGhzProtocolEncoderDooya));
+
+    instance->base.protocol = &subghz_protocol_dooya;
+    instance->generic.protocol_name = instance->base.protocol->name;
+
+    instance->encoder.repeat = 10;
+    instance->encoder.size_upload = 128;
+    instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration));
+    instance->encoder.is_running = false;
+    return instance;
+}
+
+void subghz_protocol_encoder_dooya_free(void* context) {
+    furi_assert(context);
+    SubGhzProtocolEncoderDooya* instance = context;
+    free(instance->encoder.upload);
+    free(instance);
+}
+
+/**
+ * Generating an upload from data.
+ * @param instance Pointer to a SubGhzProtocolEncoderDooya instance
+ * @return true On success
+ */
+static bool subghz_protocol_encoder_dooya_get_upload(SubGhzProtocolEncoderDooya* instance) {
+    furi_assert(instance);
+
+    size_t index = 0;
+    size_t size_upload = (instance->generic.data_count_bit * 2) + 2;
+    if(size_upload > instance->encoder.size_upload) {
+        FURI_LOG_E(TAG, "Size upload exceeds allocated encoder buffer.");
+        return false;
+    } else {
+        instance->encoder.size_upload = size_upload;
+    }
+
+    //Send header
+    if(bit_read(instance->generic.data, 0)) {
+        instance->encoder.upload[index++] = level_duration_make(
+            false,
+            (uint32_t)subghz_protocol_dooya_const.te_long * 12 +
+                subghz_protocol_dooya_const.te_long);
+    } else {
+        instance->encoder.upload[index++] = level_duration_make(
+            false,
+            (uint32_t)subghz_protocol_dooya_const.te_long * 12 +
+                subghz_protocol_dooya_const.te_short);
+    }
+
+    //Send start bit
+    instance->encoder.upload[index++] =
+        level_duration_make(true, (uint32_t)subghz_protocol_dooya_const.te_short * 13);
+    instance->encoder.upload[index++] =
+        level_duration_make(false, (uint32_t)subghz_protocol_dooya_const.te_long * 2);
+
+    //Send key data
+    for(uint8_t i = instance->generic.data_count_bit; 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_dooya_const.te_long);
+            instance->encoder.upload[index++] =
+                level_duration_make(false, (uint32_t)subghz_protocol_dooya_const.te_short);
+        } else {
+            //send bit 0
+            instance->encoder.upload[index++] =
+                level_duration_make(true, (uint32_t)subghz_protocol_dooya_const.te_short);
+            instance->encoder.upload[index++] =
+                level_duration_make(false, (uint32_t)subghz_protocol_dooya_const.te_long);
+        }
+    }
+    return true;
+}
+
+bool subghz_protocol_encoder_dooya_deserialize(void* context, FlipperFormat* flipper_format) {
+    furi_assert(context);
+    SubGhzProtocolEncoderDooya* instance = context;
+    bool res = false;
+    do {
+        if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) {
+            FURI_LOG_E(TAG, "Deserialize error");
+            break;
+        }
+        if(instance->generic.data_count_bit !=
+           subghz_protocol_dooya_const.min_count_bit_for_found) {
+            FURI_LOG_E(TAG, "Wrong number of bits in key");
+            break;
+        }
+        //optional parameter parameter
+        flipper_format_read_uint32(
+            flipper_format, "Repeat", (uint32_t*)&instance->encoder.repeat, 1);
+
+        if(!subghz_protocol_encoder_dooya_get_upload(instance)) break;
+        instance->encoder.is_running = true;
+
+        res = true;
+    } while(false);
+
+    return res;
+}
+
+void subghz_protocol_encoder_dooya_stop(void* context) {
+    SubGhzProtocolEncoderDooya* instance = context;
+    instance->encoder.is_running = false;
+}
+
+LevelDuration subghz_protocol_encoder_dooya_yield(void* context) {
+    SubGhzProtocolEncoderDooya* 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;
+}
+
+void* subghz_protocol_decoder_dooya_alloc(SubGhzEnvironment* environment) {
+    UNUSED(environment);
+    SubGhzProtocolDecoderDooya* instance = malloc(sizeof(SubGhzProtocolDecoderDooya));
+    instance->base.protocol = &subghz_protocol_dooya;
+    instance->generic.protocol_name = instance->base.protocol->name;
+    return instance;
+}
+
+void subghz_protocol_decoder_dooya_free(void* context) {
+    furi_assert(context);
+    SubGhzProtocolDecoderDooya* instance = context;
+    free(instance);
+}
+
+void subghz_protocol_decoder_dooya_reset(void* context) {
+    furi_assert(context);
+    SubGhzProtocolDecoderDooya* instance = context;
+    instance->decoder.parser_step = DooyaDecoderStepReset;
+}
+
+void subghz_protocol_decoder_dooya_feed(void* context, bool level, uint32_t duration) {
+    furi_assert(context);
+    SubGhzProtocolDecoderDooya* instance = context;
+
+    switch(instance->decoder.parser_step) {
+    case DooyaDecoderStepReset:
+        if((!level) && (DURATION_DIFF(duration, subghz_protocol_dooya_const.te_long * 12) <
+                        subghz_protocol_dooya_const.te_delta * 20)) {
+            instance->decoder.parser_step = DooyaDecoderStepFoundStartBit;
+        }
+        break;
+
+    case DooyaDecoderStepFoundStartBit:
+        if(!level) {
+            if(DURATION_DIFF(duration, subghz_protocol_dooya_const.te_long * 2) <
+               subghz_protocol_dooya_const.te_delta * 3) {
+                instance->decoder.parser_step = DooyaDecoderStepSaveDuration;
+                instance->decoder.decode_data = 0;
+                instance->decoder.decode_count_bit = 0;
+            } else {
+                instance->decoder.parser_step = DooyaDecoderStepReset;
+            }
+        } else if(
+            DURATION_DIFF(duration, subghz_protocol_dooya_const.te_short * 13) <
+            subghz_protocol_dooya_const.te_delta * 5) {
+            break;
+        } else {
+            instance->decoder.parser_step = DooyaDecoderStepReset;
+        }
+        break;
+
+    case DooyaDecoderStepSaveDuration:
+        if(level) {
+            instance->decoder.te_last = duration;
+            instance->decoder.parser_step = DooyaDecoderStepCheckDuration;
+        } else {
+            instance->decoder.parser_step = DooyaDecoderStepReset;
+        }
+        break;
+
+    case DooyaDecoderStepCheckDuration:
+        if(!level) {
+            if(duration >= (subghz_protocol_dooya_const.te_long * 4)) {
+                //add last bit
+                if(DURATION_DIFF(instance->decoder.te_last, subghz_protocol_dooya_const.te_short) <
+                   subghz_protocol_dooya_const.te_delta) {
+                    subghz_protocol_blocks_add_bit(&instance->decoder, 0);
+                } else if(
+                    DURATION_DIFF(instance->decoder.te_last, subghz_protocol_dooya_const.te_long) <
+                    subghz_protocol_dooya_const.te_delta * 2) {
+                    subghz_protocol_blocks_add_bit(&instance->decoder, 1);
+                } else {
+                    instance->decoder.parser_step = DooyaDecoderStepReset;
+                    break;
+                }
+                instance->decoder.parser_step = DooyaDecoderStepFoundStartBit;
+                if(instance->decoder.decode_count_bit ==
+                   subghz_protocol_dooya_const.min_count_bit_for_found) {
+                    instance->generic.data = 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);
+                }
+                break;
+            } else if(
+                (DURATION_DIFF(instance->decoder.te_last, subghz_protocol_dooya_const.te_short) <
+                 subghz_protocol_dooya_const.te_delta) &&
+                (DURATION_DIFF(duration, subghz_protocol_dooya_const.te_long) <
+                 subghz_protocol_dooya_const.te_delta * 2)) {
+                subghz_protocol_blocks_add_bit(&instance->decoder, 0);
+                instance->decoder.parser_step = DooyaDecoderStepSaveDuration;
+            } else if(
+                (DURATION_DIFF(instance->decoder.te_last, subghz_protocol_dooya_const.te_long) <
+                 subghz_protocol_dooya_const.te_delta * 2) &&
+                (DURATION_DIFF(duration, subghz_protocol_dooya_const.te_short) <
+                 subghz_protocol_dooya_const.te_delta)) {
+                subghz_protocol_blocks_add_bit(&instance->decoder, 1);
+                instance->decoder.parser_step = DooyaDecoderStepSaveDuration;
+            } else {
+                instance->decoder.parser_step = DooyaDecoderStepReset;
+            }
+        } else {
+            instance->decoder.parser_step = DooyaDecoderStepReset;
+        }
+        break;
+    }
+}
+
+/** 
+ * Analysis of received data
+ * @param instance Pointer to a SubGhzBlockGeneric* instance
+ */
+static void subghz_protocol_somfy_telis_check_remote_controller(SubGhzBlockGeneric* instance) {
+    /*
+ * 																serial       s/m  ch      key   	
+ * long press down   X * E1DC030533, 40b 			111000011101110000000011 0000 0101 0011 0011
+ * 
+ * short press down  3 * E1DC030533, 40b			111000011101110000000011 0000 0101 0011 0011
+ *                   3 * E1DC03053C, 40b 	        111000011101110000000011 0000 0101 0011 1100
+ * 	
+ * press stop        X * E1DC030555, 40b			111000011101110000000011 0000 0101 0101 0101
+ * 
+ * long press up     X * E1DC030511, 40b			111000011101110000000011 0000 0101 0001 0001
+ * 
+ * short press up    3 * E1DC030511, 40b			111000011101110000000011 0000 0101 0001 0001
+ *                   3 * E1DC03051E, 40b		    111000011101110000000011 0000 0101 0001 1110
+ *
+ * serial: 3 byte serial number
+ * s/m: single (b0000) / multi (b0001) channel console  
+ * ch: channel if single (always b0101) or multi 
+ * key: 0b00010001 - long press up
+ *      0b00011110 - short press up
+ *      0b00110011 - long press down
+ *      0b00111100 - short press down
+ *      0b01010101 - press stop 
+ *      0b01111001 - press up + down
+ *      0b10000000 - press up + stop
+ *      0b10000001 - press down + stop
+ *      0b11001100 - press P2
+ *      
+*/
+
+    instance->serial = (instance->data >> 16);
+    if((instance->data >> 12) & 0x0F) {
+        instance->cnt = (instance->data >> 8) & 0x0F;
+    } else {
+        instance->cnt = DOYA_SINGLE_CHANNEL;
+    }
+    instance->btn = instance->data & 0xFF;
+}
+
+uint8_t subghz_protocol_decoder_dooya_get_hash_data(void* context) {
+    furi_assert(context);
+    SubGhzProtocolDecoderDooya* instance = context;
+    return subghz_protocol_blocks_get_hash_data(
+        &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1);
+}
+
+bool subghz_protocol_decoder_dooya_serialize(
+    void* context,
+    FlipperFormat* flipper_format,
+    SubGhzRadioPreset* preset) {
+    furi_assert(context);
+    SubGhzProtocolDecoderDooya* instance = context;
+    return subghz_block_generic_serialize(&instance->generic, flipper_format, preset);
+}
+
+bool subghz_protocol_decoder_dooya_deserialize(void* context, FlipperFormat* flipper_format) {
+    furi_assert(context);
+    SubGhzProtocolDecoderDooya* instance = context;
+    bool ret = false;
+    do {
+        if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) {
+            break;
+        }
+        if(instance->generic.data_count_bit !=
+           subghz_protocol_dooya_const.min_count_bit_for_found) {
+            FURI_LOG_E(TAG, "Wrong number of bits in key");
+            break;
+        }
+        ret = true;
+    } while(false);
+    return ret;
+}
+
+/**
+ * Get button name.
+ * @param btn Button number, 8 bit
+ */
+static const char* subghz_protocol_dooya_get_name_button(uint8_t btn) {
+    const char* btn_name;
+    switch(btn) {
+    case 0b00010001:
+        btn_name = "Up_Long";
+        break;
+    case 0b00011110:
+        btn_name = "Up_Short";
+        break;
+    case 0b00110011:
+        btn_name = "Down_Long";
+        break;
+    case 0b00111100:
+        btn_name = "Down_Short";
+        break;
+    case 0b01010101:
+        btn_name = "Stop";
+        break;
+    case 0b01111001:
+        btn_name = "Up+Down";
+        break;
+    case 0b10000000:
+        btn_name = "Up+Stop";
+        break;
+    case 0b10000001:
+        btn_name = "Down+Stop";
+        break;
+    case 0b11001100:
+        btn_name = "P2";
+        break;
+    default:
+        btn_name = "Unknown";
+        break;
+    }
+    return btn_name;
+}
+
+void subghz_protocol_decoder_dooya_get_string(void* context, FuriString* output) {
+    furi_assert(context);
+    SubGhzProtocolDecoderDooya* instance = context;
+
+    subghz_protocol_somfy_telis_check_remote_controller(&instance->generic);
+
+    furi_string_cat_printf(
+        output,
+        "%s %dbit\r\n"
+        "Key:0x%010llX\r\n"
+        "Sn:0x%08lX\r\n"
+        "Btn:%s\r\n",
+        instance->generic.protocol_name,
+        instance->generic.data_count_bit,
+        instance->generic.data,
+        instance->generic.serial,
+        subghz_protocol_dooya_get_name_button(instance->generic.btn));
+    if(instance->generic.cnt == DOYA_SINGLE_CHANNEL) {
+        furi_string_cat_printf(output, "Ch:Single\r\n");
+    } else {
+        furi_string_cat_printf(output, "Ch:%lu\r\n", instance->generic.cnt);
+    }
+}

+ 107 - 0
lib/subghz/protocols/dooya.h

@@ -0,0 +1,107 @@
+#pragma once
+
+#include "base.h"
+
+#define SUBGHZ_PROTOCOL_DOOYA_NAME "Dooya"
+
+typedef struct SubGhzProtocolDecoderDooya SubGhzProtocolDecoderDooya;
+typedef struct SubGhzProtocolEncoderDooya SubGhzProtocolEncoderDooya;
+
+extern const SubGhzProtocolDecoder subghz_protocol_dooya_decoder;
+extern const SubGhzProtocolEncoder subghz_protocol_dooya_encoder;
+extern const SubGhzProtocol subghz_protocol_dooya;
+
+/**
+ * Allocate SubGhzProtocolEncoderDooya.
+ * @param environment Pointer to a SubGhzEnvironment instance
+ * @return SubGhzProtocolEncoderDooya* pointer to a SubGhzProtocolEncoderDooya instance
+ */
+void* subghz_protocol_encoder_dooya_alloc(SubGhzEnvironment* environment);
+
+/**
+ * Free SubGhzProtocolEncoderDooya.
+ * @param context Pointer to a SubGhzProtocolEncoderDooya instance
+ */
+void subghz_protocol_encoder_dooya_free(void* context);
+
+/**
+ * Deserialize and generating an upload to send.
+ * @param context Pointer to a SubGhzProtocolEncoderDooya instance
+ * @param flipper_format Pointer to a FlipperFormat instance
+ * @return true On success
+ */
+bool subghz_protocol_encoder_dooya_deserialize(void* context, FlipperFormat* flipper_format);
+
+/**
+ * Forced transmission stop.
+ * @param context Pointer to a SubGhzProtocolEncoderDooya instance
+ */
+void subghz_protocol_encoder_dooya_stop(void* context);
+
+/**
+ * Getting the level and duration of the upload to be loaded into DMA.
+ * @param context Pointer to a SubGhzProtocolEncoderDooya instance
+ * @return LevelDuration 
+ */
+LevelDuration subghz_protocol_encoder_dooya_yield(void* context);
+
+/**
+ * Allocate SubGhzProtocolDecoderDooya.
+ * @param environment Pointer to a SubGhzEnvironment instance
+ * @return SubGhzProtocolDecoderDooya* pointer to a SubGhzProtocolDecoderDooya instance
+ */
+void* subghz_protocol_decoder_dooya_alloc(SubGhzEnvironment* environment);
+
+/**
+ * Free SubGhzProtocolDecoderDooya.
+ * @param context Pointer to a SubGhzProtocolDecoderDooya instance
+ */
+void subghz_protocol_decoder_dooya_free(void* context);
+
+/**
+ * Reset decoder SubGhzProtocolDecoderDooya.
+ * @param context Pointer to a SubGhzProtocolDecoderDooya instance
+ */
+void subghz_protocol_decoder_dooya_reset(void* context);
+
+/**
+ * Parse a raw sequence of levels and durations received from the air.
+ * @param context Pointer to a SubGhzProtocolDecoderDooya instance
+ * @param level Signal level true-high false-low
+ * @param duration Duration of this level in, us
+ */
+void subghz_protocol_decoder_dooya_feed(void* context, bool level, uint32_t duration);
+
+/**
+ * Getting the hash sum of the last randomly received parcel.
+ * @param context Pointer to a SubGhzProtocolDecoderDooya instance
+ * @return hash Hash sum
+ */
+uint8_t subghz_protocol_decoder_dooya_get_hash_data(void* context);
+
+/**
+ * Serialize data SubGhzProtocolDecoderDooya.
+ * @param context Pointer to a SubGhzProtocolDecoderDooya instance
+ * @param flipper_format Pointer to a FlipperFormat instance
+ * @param preset The modulation on which the signal was received, SubGhzRadioPreset
+ * @return true On success
+ */
+bool subghz_protocol_decoder_dooya_serialize(
+    void* context,
+    FlipperFormat* flipper_format,
+    SubGhzRadioPreset* preset);
+
+/**
+ * Deserialize data SubGhzProtocolDecoderDooya.
+ * @param context Pointer to a SubGhzProtocolDecoderDooya instance
+ * @param flipper_format Pointer to a FlipperFormat instance
+ * @return true On success
+ */
+bool subghz_protocol_decoder_dooya_deserialize(void* context, FlipperFormat* flipper_format);
+
+/**
+ * Getting a textual representation of the received data.
+ * @param context Pointer to a SubGhzProtocolDecoderDooya instance
+ * @param output Resulting text
+ */
+void subghz_protocol_decoder_dooya_get_string(void* context, FuriString* output);

+ 2 - 1
lib/subghz/protocols/protocol_items.c

@@ -39,8 +39,9 @@ const SubGhzProtocol* subghz_protocol_registry_items[] = {
     &subghz_protocol_smc5326,
     &subghz_protocol_smc5326,
     &subghz_protocol_holtek_th12x,
     &subghz_protocol_holtek_th12x,
     &subghz_protocol_linear_delta3,
     &subghz_protocol_linear_delta3,
+    &subghz_protocol_dooya,
 };
 };
 
 
 const SubGhzProtocolRegistry subghz_protocol_registry = {
 const SubGhzProtocolRegistry subghz_protocol_registry = {
     .items = subghz_protocol_registry_items,
     .items = subghz_protocol_registry_items,
-    .size = COUNT_OF(subghz_protocol_registry_items)};
+    .size = COUNT_OF(subghz_protocol_registry_items)};

+ 1 - 0
lib/subghz/protocols/protocol_items.h

@@ -39,5 +39,6 @@
 #include "ansonic.h"
 #include "ansonic.h"
 #include "smc5326.h"
 #include "smc5326.h"
 #include "holtek_ht12x.h"
 #include "holtek_ht12x.h"
+#include "dooya.h"
 
 
 extern const SubGhzProtocolRegistry subghz_protocol_registry;
 extern const SubGhzProtocolRegistry subghz_protocol_registry;

Certains fichiers n'ont pas été affichés car il y a eu trop de fichiers modifiés dans ce diff