Opis modelu działania bramki Z2S

Moderator: vajera

User avatar
vajera
Posts: 7290
Joined: Wed Oct 31, 2018 7:58 am
Location: Biedrusko
Has thanked: 289 times
Been thanked: 161 times

Post

  1. Bramka w trybie parowania (180 sekund od resetu albo 1x BOOT),
  2. urządzenie Zigbee wprowadzone w tryb parowania łączy się z siecią Zigbee utworzoną przez bramkę,
  3. bramka odpytuje urządzenie Zigbee o nazwę producenta i model - jeżeli rozpozna urządzenie na podstawie wpisu w bazie to wykonuje następujace kroki:
    1. wykonuje tzw. binding na wybranych klastrach,
    2. generuje kanał/kanały Supla odpowiadające urządzeniu Zigbee, np. DoorSensor -> Supla::Sensor::VirtualBinary,
    3. wiele urządzeń Zigbee wygeneruje kilka kanałów Supla, np. takie gniazdko z pomiarem energii -> przekaźnik + licznik energii elektrycznej.
Bramka Zigbee <=> SUPLA
Więcej informacji tutaj:
https://forum.supla.org/viewforum.php?f=127
FAQ https://forum.supla.org/viewtopic.php?t=17277
User avatar
vajera
Posts: 7290
Joined: Wed Oct 31, 2018 7:58 am
Location: Biedrusko
Has thanked: 289 times
Been thanked: 161 times

Post

Chciałem podzielić się z Wami przykładem tego, jak identyczna funkcjonalność potrafi różnić się skalą implementacji w przypadku różnych urządzeń Zigbee:

przykład dotyczy trzech głowic TRV, które pozwalają na bezpośrednie przesyłanie temperatury do głowicy (bez użycia kalibracji temperatury, wysyłana jest rzeczywista wartość pomiaru).

Głowica Bosch BTH-RA:

Code: Select all

_gateway->sendAttributeWrite(
        &_device, 
        ESP_ZB_ZCL_CLUSTER_ID_THERMOSTAT, 
        BOSCH_TRV_EXTERNAL_TEMPERATURE_INPUT_ID, 
        ESP_ZB_ZCL_ATTR_TYPE_S16, 
        2, &external_sensor_temperature,
        true,
        1, BOSCH_MANUFACTURER_CODE);
Głowica Sonoff TRV-ZB:

Code: Select all

_gateway->sendAttributeWrite(
        &_device, 
        SONOFF_CUSTOM_CLUSTER, 
        TRVZB_CMD_SET_TEMPERATURE_SENSOR_SELECT, 
        ESP_ZB_ZCL_ATTR_TYPE_U8, 1, &temperature_selector);

_gateway->sendAttributeWrite(
        &_device, 
        SONOFF_CUSTOM_CLUSTER, 
        TRVZB_CMD_SET_EXTERNAL_TEMPERATURE_INPUT, 
        ESP_ZB_ZCL_ATTR_TYPE_S16, 2, &external_sensor_temperature);
i teraz gwiazda wieczoru ;) Aqara E1 (kod podzieliłem na 4 części):

Code: Select all

typedef struct lumi_fff2_cmd_header_s {

  uint8_t cmd_start;
  uint8_t cmd_category;
  uint8_t cmd_size;
  uint8_t cmd_type;
  uint8_t counter;
  uint8_t integrity;
  uint8_t cmd_action;
  uint8_t data_type;
  uint8_t params_size;
} __attribute__((packed)) lumi_fff2_cmd_header_t;

typedef struct lumi_sensor_link_params_1_s {

  uint32_t timestamp;
  uint8_t  const_value_3d;
  uint8_t  link_id;
  esp_zb_ieee_addr_t device_address;
  esp_zb_ieee_addr_t sensor_address;
  uint8_t shared_const_value[4];
  uint8_t const_value_link_1[11];
  uint8_t const_common_value[7];
  uint8_t const_value_64;
  uint8_t const_end_65;
} __attribute__((packed)) lumi_sensor_link_params_1_t;

typedef struct lumi_sensor_link_params_2_s {

  uint32_t timestamp;
  uint8_t  const_value_3d;
  uint8_t  link_id;
  esp_zb_ieee_addr_t device_address;
  esp_zb_ieee_addr_t sensor_address;
  uint8_t const_value_link_2[18];
  uint8_t const_common_value[7];
  uint8_t const_value_04;
  uint8_t const_end_65;
} __attribute__((packed)) lumi_sensor_link_params_2_t;

typedef struct lumi_sensor_unlink_params_s {

  uint32_t timestamp;
  uint8_t  const_value_3d;
  uint8_t  link_id;
  esp_zb_ieee_addr_t device_address;
  uint8_t sensor_address[12];
} __attribute__((packed)) lumi_sensor_unlink_params_t;

typedef struct lumi_sensor_send_temperature_params_s {

  esp_zb_ieee_addr_t sensor_address;
  uint8_t const_value[4];
  float temperature_100;
} __attribute__((packed)) lumi_sensor_send_temperature_params_t;


static constexpr lumi_fff2_cmd_header_t 
  lumi_fff2_cmd_header_template PROGMEM = {
  
    .cmd_start    = 0xAA, 
    .cmd_category = 0x71, 
    .cmd_size     = 0x31, 
    .cmd_type     = 0x44, 
    .counter      = 0x10, 
    .integrity    = 0x60, //512 - sum(cmd_start..counter)
    .cmd_action   = 0x02, 
    .data_type    = 0x41, 
    .params_size  = 0x2E
};

static constexpr lumi_sensor_link_params_1_t 
  lumi_sensor_link_params_1_template PROGMEM = {

  .timestamp          = 0x00000000,
  .const_value_3d     = 0x3D,
  .link_id            = 04,
  .device_address     = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
  .sensor_address     = { 0x00, 0x15, 0x8D, 0x00, 0x01, 0x9D, 0x1B, 0x98 },//some static value - seems enough
  .shared_const_value = { 0x00, 0x01, 0x00, 0x55 },
  .const_value_link_1 = { 0x13, 0x0A, 0x02, 0x00, 0x00, 0x64,
                          0x04, 0xCE, 0xC2, 0xB6, 0xC8 },
  .const_common_value = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x3D },
  .const_value_64     = 0x64,
  .const_end_65       = 0x65,
};

static constexpr lumi_sensor_link_params_2_t 
  lumi_sensor_link_params_2_template PROGMEM = {

  .timestamp          = 0x00000000,
  .const_value_3d     = 0x3D,
  .link_id            = 05,
  .device_address     = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
  .sensor_address     = { 0x00, 0x15, 0x8D, 0x00, 0x01, 0x9D, 0x1B, 0x98 },//some static value - seems enough
  .const_value_link_2 = {0x08, 0x00, 0x07, 0xFD, 0x16, 0x0A, 0x02, 0x0A, 0xC9,
                         0xE8, 0xB1, 0xB8, 0xD4, 0xDA, 0xCF, 0xDF, 0xC0, 0xEB},	
  .const_common_value = {0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x3D},
  .const_value_04     = 0x04,
  .const_end_65       = 0x65,
};

static constexpr lumi_sensor_unlink_params_t 
  lumi_sensor_unlink_params_template PROGMEM = {

  .timestamp          = 0x00000000,
  .const_value_3d     = 0x3D,
  .link_id            = 05,
  .device_address     = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
  .sensor_address     = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                         0x00, 0x00, 0x00, 0x0}
};

static constexpr lumi_sensor_unlink_params_t 
lumi_sensor_unlink_params_2 PROGMEM = {

  .timestamp          = 0x00000000,
  .const_value_3d     = 0x3D,
  .link_id            = 04,
  .device_address     = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
  .sensor_address     = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                         0x00, 0x00, 0x00, 0x0}
};

static constexpr lumi_sensor_send_temperature_params_t 
  lumi_sensor_send_temperature_params_template PROGMEM = {

  .sensor_address    = { 0x00, 0x15, 0x8D, 0x00, 0x01, 0x9D, 0x1B, 0x98 },//some static value - seems enough
  .const_value       = {0x00, 0x01, 0x00, 0x55},
  .temperature_100   = -27500
};

Code: Select all

void buildLumiFFF2CmdHeader(uint8_t* fff2_cmd_data_buffer,
                            uint8_t counter,
                            uint8_t action,
                            uint8_t params_size) {

  memset(fff2_cmd_data_buffer, 0 , 0xAA);

  uint8_t fff2_header_size = sizeof(lumi_fff2_cmd_header_t);
  
  memcpy(fff2_cmd_data_buffer, 
         &lumi_fff2_cmd_header_template,
         fff2_header_size);

  lumi_fff2_cmd_header_t *lumi_fff2_cmd_header = 
    (lumi_fff2_cmd_header_t *)fff2_cmd_data_buffer;

  lumi_fff2_cmd_header->cmd_size = params_size + 3;
  lumi_fff2_cmd_header->counter = counter;
  lumi_fff2_cmd_header->cmd_action = action;
  lumi_fff2_cmd_header->params_size = params_size;

  uint16_t integrity = lumi_fff2_cmd_header->cmd_start +
                       lumi_fff2_cmd_header->cmd_category +
                       lumi_fff2_cmd_header->cmd_size +
                       lumi_fff2_cmd_header->cmd_type +
                       lumi_fff2_cmd_header->counter;
  integrity = 512 - integrity;
  lumi_fff2_cmd_header->integrity = integrity;
}

uint8_t buildLumiFFF2CmdLinkParams1(uint8_t* fff2_cmd_data_buffer,
                                    uint8_t counter,
                                    time_t timestamp,
                                    esp_zb_ieee_addr_t ieee_addr) {
  
  uint8_t fff2_header_size = sizeof(lumi_fff2_cmd_header_t);
  uint8_t lumi_sensor_link_params_1_size = 
    sizeof(lumi_sensor_link_params_1_t);

  *fff2_cmd_data_buffer = fff2_header_size + lumi_sensor_link_params_1_size;

  buildLumiFFF2CmdHeader(fff2_cmd_data_buffer + 1,
                         counter, 
                         LUMI_FFF2_CMD_ACTION_LINK_SENSOR,
                         lumi_sensor_link_params_1_size);
    
  memcpy(fff2_cmd_data_buffer + fff2_header_size + 1, 
         &lumi_sensor_link_params_1_template,
         lumi_sensor_link_params_1_size);

  lumi_sensor_link_params_1_t *lumi_sensor_link_params_1 = 
    (lumi_sensor_link_params_1_t*)(fff2_cmd_data_buffer + fff2_header_size + 1);
      
  lumi_sensor_link_params_1->timestamp  = timestamp;

  /*memcpy(lumi_sensor_link_params_1->device_address,
        ieee_addr, 
        sizeof(esp_zb_ieee_addr_t));
        */
  for (uint8_t i = 0; i < sizeof(esp_zb_ieee_addr_t); i++)
    lumi_sensor_link_params_1->device_address[i] = ieee_addr[7 - i];

  return fff2_header_size + lumi_sensor_link_params_1_size + 1;
}

uint8_t buildLumiFFF2CmdLinkParams2(uint8_t* fff2_cmd_data_buffer,
                                    uint8_t counter,
                                    time_t timestamp,
                                    esp_zb_ieee_addr_t ieee_addr) {

  uint8_t fff2_header_size = sizeof(lumi_fff2_cmd_header_t);
  uint8_t lumi_sensor_link_params_2_size = 
    sizeof(lumi_sensor_link_params_2_t);

  *fff2_cmd_data_buffer = fff2_header_size + lumi_sensor_link_params_2_size;

  buildLumiFFF2CmdHeader(fff2_cmd_data_buffer + 1, 
                         counter, 
                         LUMI_FFF2_CMD_ACTION_LINK_SENSOR,
                         lumi_sensor_link_params_2_size);
    
  memcpy(fff2_cmd_data_buffer + fff2_header_size + 1, 
         &lumi_sensor_link_params_2_template,
         lumi_sensor_link_params_2_size);

  lumi_sensor_link_params_2_t *lumi_sensor_link_params_2 = 
    (lumi_sensor_link_params_2_t*)(fff2_cmd_data_buffer + fff2_header_size + 1);
      
  lumi_sensor_link_params_2->timestamp  = timestamp;

  /*memcpy(lumi_sensor_link_params_2->device_address,
        ieee_addr, 
        sizeof(esp_zb_ieee_addr_t));*/

  for (uint8_t i = 0; i < sizeof(esp_zb_ieee_addr_t); i++)
    lumi_sensor_link_params_2->device_address[i] = ieee_addr[7 - i];
  
  return fff2_header_size + lumi_sensor_link_params_2_size + 1;
}

uint8_t buildLumiFFF2CmdUnlinkParams(uint8_t* fff2_cmd_data_buffer,
                                     uint8_t counter,
                                     time_t timestamp,
                                     uint8_t link_id,
                                     esp_zb_ieee_addr_t ieee_addr) {

  uint8_t fff2_header_size = sizeof(lumi_fff2_cmd_header_t);
  uint8_t lumi_sensor_unlink_params_size = 
    sizeof(lumi_sensor_unlink_params_t);

  *fff2_cmd_data_buffer = fff2_header_size + lumi_sensor_unlink_params_size;

  buildLumiFFF2CmdHeader(fff2_cmd_data_buffer + 1, 
                         counter, 
                         LUMI_FFF2_CMD_ACTION_UNLINK_SENSOR,
                         lumi_sensor_unlink_params_size);
    
  memcpy(fff2_cmd_data_buffer + fff2_header_size + 1, 
         &lumi_sensor_unlink_params_template,
         lumi_sensor_unlink_params_size);

  lumi_sensor_unlink_params_t *lumi_sensor_unlink_params = 
    (lumi_sensor_unlink_params_t*)(fff2_cmd_data_buffer + fff2_header_size + 1);
      
  lumi_sensor_unlink_params->timestamp  = timestamp;
  lumi_sensor_unlink_params->link_id    = link_id;

  /*memcpy(lumi_sensor_unlink_params->device_address,
        ieee_addr, 
        sizeof(esp_zb_ieee_addr_t));*/
  
  for (uint8_t i = 0; i < sizeof(esp_zb_ieee_addr_t); i++)
    lumi_sensor_unlink_params->device_address[i] = ieee_addr[7 - i];

  return fff2_header_size + lumi_sensor_unlink_params_size + 1;
}

float ReverseFloat( const float inFloat ) {
   float retVal;
   char *floatToConvert = ( char* ) & inFloat;
   char *returnFloat = ( char* ) & retVal;

   // swap the bytes into a temporary buffer
   returnFloat[0] = floatToConvert[3];
   returnFloat[1] = floatToConvert[2];
   returnFloat[2] = floatToConvert[1];
   returnFloat[3] = floatToConvert[0];

   return retVal;
}


uint8_t buildLumiFFF2CmdSendTemperatureParams(uint8_t* fff2_cmd_data_buffer,
                                              float temperature_100) {
  
  uint8_t fff2_header_size = sizeof(lumi_fff2_cmd_header_t);
  uint8_t lumi_sensor_send_temperature_params_size = 
    sizeof(lumi_sensor_send_temperature_params_t);

  *fff2_cmd_data_buffer = fff2_header_size + lumi_sensor_send_temperature_params_size;

  buildLumiFFF2CmdHeader(fff2_cmd_data_buffer + 1, 
                         0x10, 
                         LUMI_FFF2_CMD_ACTION_SEND_TEMPERATURE,
                         lumi_sensor_send_temperature_params_size);
    
  memcpy(fff2_cmd_data_buffer + fff2_header_size + 1, 
         &lumi_sensor_send_temperature_params_template,
         lumi_sensor_send_temperature_params_size);

  lumi_sensor_send_temperature_params_t *lumi_sensor_send_temperature_params = 
    (lumi_sensor_send_temperature_params_t*)(fff2_cmd_data_buffer + 
      fff2_header_size + 1);
      
  lumi_sensor_send_temperature_params->temperature_100  = 
    ReverseFloat(temperature_100);

  return fff2_header_size + lumi_sensor_send_temperature_params_size + 1;
}

Code: Select all

uint8_t fff2_cmd_data_buffer[0xAA];

      time_t timestamp = time(nullptr);

      if (trv_external_sensor_present) {
      
        uint8_t buffer_size =
          buildLumiFFF2CmdLinkParams1(fff2_cmd_data_buffer,
                                      0x12, 
                                      timestamp, 
                                      _device.ieee_addr);

        _gateway->sendAttributeWrite(
          &_device, 
          LUMI_CUSTOM_CLUSTER, 
          LUMI_CUSTOM_CLUSTER_FFF2_CMD_ID, 
          ESP_ZB_ZCL_ATTR_TYPE_OCTET_STRING, 
          buffer_size, &fff2_cmd_data_buffer,
          true,
          1, LUMI_MANUFACTURER_CODE);

        buffer_size =
          buildLumiFFF2CmdLinkParams2(fff2_cmd_data_buffer, 
                                      0x13,
                                      timestamp, 
                                      _device.ieee_addr);

        _gateway->sendAttributeWrite(
          &_device, 
          LUMI_CUSTOM_CLUSTER, 
          LUMI_CUSTOM_CLUSTER_FFF2_CMD_ID, 
          ESP_ZB_ZCL_ATTR_TYPE_OCTET_STRING, 
          buffer_size, &fff2_cmd_data_buffer,
          true,
          1, LUMI_MANUFACTURER_CODE);

        _gateway->sendAttributeRead(
        &_device, 
        ESP_ZB_ZCL_CLUSTER_ID_THERMOSTAT, 
        ESP_ZB_ZCL_ATTR_THERMOSTAT_LOCAL_TEMPERATURE_ID,
        false);
 

Code: Select all

uint8_t fff2_cmd_data_buffer[0xAA];

      time_t timestamp = time(nullptr);

      if ((_trv_temperature_sensor_type == 0) ||
          (_trv_temperature_sensor_type == 0xFF))
        sendTRVExternalSensorInput(true);
      
      uint8_t buffer_size = 
        buildLumiFFF2CmdSendTemperatureParams(fff2_cmd_data_buffer, 
                                              external_sensor_temperature);

      _gateway->sendAttributeWrite(
        &_device, 
        LUMI_CUSTOM_CLUSTER, 
        LUMI_CUSTOM_CLUSTER_FFF2_CMD_ID, 
        ESP_ZB_ZCL_ATTR_TYPE_OCTET_STRING, 
        buffer_size, &fff2_cmd_data_buffer,
        true,
        1, LUMI_MANUFACTURER_CODE);  

      _gateway->sendAttributeRead(
        &_device, 
        ESP_ZB_ZCL_CLUSTER_ID_THERMOSTAT, 
        ESP_ZB_ZCL_ATTR_THERMOSTAT_LOCAL_TEMPERATURE_ID,
        false);
Bramka Zigbee <=> SUPLA
Więcej informacji tutaj:
https://forum.supla.org/viewforum.php?f=127
FAQ https://forum.supla.org/viewtopic.php?t=17277

Return to “Bramka ZigBee”