Mixiaoxiao / Arduino-HomeKit-ESP8266

Native Apple HomeKit accessory implementation for the ESP8266 Arduino core.
MIT License
1.5k stars 279 forks source link

Thermostat giving error “Accessory out of compliance” when trying to pair #67

Closed daz closed 3 years ago

daz commented 3 years ago

All other accessories and examples I try work fine, but thermostat is giving me the error.

#include <homekit/homekit.h>
#include <homekit/characteristics.h>

void my_accessory_identify(homekit_value_t _value) {
  printf("accessory identify\n");
}

homekit_characteristic_t cha_current_heating_cooling_state = HOMEKIT_CHARACTERISTIC_(CURRENT_HEATING_COOLING_STATE, 0);
homekit_characteristic_t cha_target_heating_cooling_state = HOMEKIT_CHARACTERISTIC_(TARGET_HEATING_COOLING_STATE, 0);
homekit_characteristic_t cha_current_temperature = HOMEKIT_CHARACTERISTIC_(CURRENT_TEMPERATURE, 0);
homekit_characteristic_t cha_target_temperature = HOMEKIT_CHARACTERISTIC_(TARGET_TEMPERATURE, 0);
homekit_characteristic_t cha_temperature_display_units = HOMEKIT_CHARACTERISTIC_(TEMPERATURE_DISPLAY_UNITS, 0);

homekit_accessory_t *accessories[] = {
  HOMEKIT_ACCESSORY(.id=1, .category=homekit_accessory_category_thermostat, .services=(homekit_service_t*[]) {
    HOMEKIT_SERVICE(ACCESSORY_INFORMATION, .characteristics=(homekit_characteristic_t*[]) {
      HOMEKIT_CHARACTERISTIC(NAME, "Thermostat"),
      HOMEKIT_CHARACTERISTIC(MANUFACTURER, "Arduino HomeKit"),
      HOMEKIT_CHARACTERISTIC(SERIAL_NUMBER, "0123456"),
      HOMEKIT_CHARACTERISTIC(MODEL, "ESP8266/ESP32"),
      HOMEKIT_CHARACTERISTIC(FIRMWARE_REVISION, "1.0"),
      HOMEKIT_CHARACTERISTIC(IDENTIFY, my_accessory_identify),
      NULL
    }),
    HOMEKIT_SERVICE(THERMOSTAT, .primary=true, .characteristics=(homekit_characteristic_t*[]) {
      &cha_current_heating_cooling_state,
      &cha_target_heating_cooling_state,
      &cha_current_temperature,
      &cha_target_temperature,
      &cha_temperature_display_units,
      NULL
    }),
    NULL
  }),
  NULL
};

homekit_server_config_t config = {
  .accessories = accessories,
  .password = "111-11-111"
};

Any ideas?

waaraawa commented 3 years ago

Hi daz, You should make homekit can set cooling status, temperature and temperature unit. here is mine thermostat definitions. // Thermostat Service, HOMEKIT_SERVICE_THERMOSTAT homekit_characteristic_t h_thermo_c_st = HOMEKIT_CHARACTERISTIC_(CURRENT_HEATING_COOLING_STATE, 0); homekit_characteristic_t h_thermo_t_st = HOMEKIT_CHARACTERISTIC_(TARGET_HEATING_COOLING_STATE, 0, .getter = get_target_heating_cooling_state, .setter = set_target_heating_cooling_state); homekit_characteristic_t h_thermo_c_temp = HOMEKIT_CHARACTERISTIC_(CURRENT_TEMPERATURE, 0); homekit_characteristic_t h_thermo_t_temp = HOMEKIT_CHARACTERISTIC_(TARGET_TEMPERATURE, 0, .getter = get_target_temperature, .setter = set_target_temperature); homekit_characteristic_t h_thermo_temp_unit = HOMEKIT_CHARACTERISTIC_(TEMPERATURE_DISPLAY_UNITS, 0, .getter = get_temperature_display_unit, .setter = set_temperature_display_unit); homekit_characteristic_t h_thermo_c_humi = HOMEKIT_CHARACTERISTIC_(CURRENT_RELATIVE_HUMIDITY, 0); also you can check whole codes as following my code. https://github.com/waaraawa/Homekit_AQI_Desk/blob/master/homekit_config.c

BR

daz commented 3 years ago

When adding the setters/getters I'm still getting an error "Accessory is out of compliance".

Same error when I copy your code verbatim. But when I remove this section, it pairs:

            HOMEKIT_SERVICE(
                THERMOSTAT,
                .primary = true,
                .characteristics = (homekit_characteristic_t*[]) {
                    HOMEKIT_CHARACTERISTIC(NAME, "Air Conditioner"),
                    &h_thermo_c_st,
                    &h_thermo_t_st,
                    &h_thermo_c_temp,
                    &h_thermo_t_temp,
                    &h_thermo_temp_unit,
                    &h_thermo_c_humi,
                    NULL
                }
            ),
waaraawa commented 3 years ago

@daz If you can share your homekit_config.c file, I can review it but I don't know why with not enought info. I hope you review which info you should make get/set as characteristics.h

`/** Defines that the accessory contains a thermostat.

Required Characteristics:

daz commented 3 years ago

Okay I found the problem, the default target temperature variable was zero, which is out of range.

andynovak12 commented 2 years ago

Okay I found the problem, the default target temperature variable was zero, which is out of range.

I had a similar problem with the CURRENT_TEMPERATURE. However for me, the issue wasn't with the default value (I had it correctly set to 20).

My CURRENT_TEMPERATURE value was being overwritten by a temperature sensor's values, and at the time of pairing with Homekit, the sensor was unplugged and setting CURRENT_TEMPERATURE to a value outside of the "acceptable" temperature range defined by Apple.

Hopefully this helps someone in the future if they are ever in the situation I was in.

korgoth commented 1 year ago

Hey guys,

im struggling with creating a thermostat also - either it doesnt pair or im getting stuck on "updating" state.

Can someone provide a working example of how they setup their thermostat using this library?

Any help is much appreciated!

Currently i have the following code:

`#include <homekit/homekit.h>

include <homekit/characteristics.h>

void my_accessory_identify(homekit_value_t _value) { printf("accessory identify\n"); }

// celsius homekit_characteristic_t ac_display_units = HOMEKITCHARACTERISTIC(TEMPERATURE_DISPLAY_UNITS, 0);

// float; min 0, max 100, step 0.1, unit celsius homekit_characteristic_t ac_current_temp = HOMEKITCHARACTERISTIC(CURRENT_TEMPERATURE, 20);

// float; min 0, max 100, step 0.1, unit celsius homekit_characteristic_t ac_target_temp = HOMEKITCHARACTERISTIC(TARGET_TEMPERATURE, 20);

// 0: Off, 1: Heat, 2: Cool homekit_characteristic_t ac_current_state = HOMEKITCHARACTERISTIC(CURRENT_HEATING_COOLING_STATE, 0);

// 0: off, 1: heat, 2:cool, 3: auto homekit_characteristic_t ac_target_state = HOMEKITCHARACTERISTIC(CURRENT_HEATING_COOLING_STATE, 0);

homekit_accessory_t accessories[] = { HOMEKIT_ACCESSORY(.id=1, .category=homekit_accessory_category_thermostat, .services=(homekit_service_t[]) { HOMEKIT_SERVICE(ACCESSORY_INFORMATION, .characteristics=(homekit_characteristic_t[]) { HOMEKIT_CHARACTERISTIC(NAME, "AIR CON"), HOMEKIT_CHARACTERISTIC(MANUFACTURER, "korgoth"), HOMEKIT_CHARACTERISTIC(SERIAL_NUMBER, "0000002"), HOMEKIT_CHARACTERISTIC(MODEL, "ESP8266"), //HOMEKIT_CHARACTERISTIC(FIRMWARE_REVISION, "0.0.2"), HOMEKIT_CHARACTERISTIC(IDENTIFY, my_accessory_identify), NULL }), HOMEKIT_SERVICE(THERMOSTAT, .primary=true, .characteristics=(homekit_characteristic_t[]) { &ac_display_units, &ac_current_temp, &ac_target_temp, &ac_current_state, &ac_target_state, //&ac_active, //&ac_rotation_speed, NULL }), NULL }), NULL };

homekit_server_config_t config = { .accessories = accessories, .password = "123-45-678", };`


and for the sketch itself:

`#include

include

include "wifi_info.h"

include

include

define LOG_D(fmt, ...) printf_P(PSTR(fmt "\n") , ##__VA_ARGS__);

//temperature sensor output pin int temp_outputpin = A0;

// IR settings const uint16_t kIrLed = 14; // ESP8266 GPIO pin to use. Recommended: 4 (D2).

include

IRHitachiAc296 ac(kIrLed); // Set the GPIO to be used to sending the message

define MODE_AUTO kHitachiAc296Auto

define MODE_COOL kHitachiAc296Cool

define MODE_DRY kHitachiAc296Dehumidify

define MODE_HEAT kHitachiAc296Heat

define MODE_FAN kHitachiAc296Dehumidify;

define FAN_AUTO kHitachiAc296FanAuto

define FAN_MIN kHitachiAc296FanLow

define FAN_MED kHitachiAc296FanMedium

define FAN_HI kHitachiAc296FanHigh

// Globals bool queueCommand = false; void flipQueueCommand(bool newState) { Serial.write("Flipping queueCommand to %d\n", newState); queueCommand = newState; }

void setup() { Serial.begin(115200); wifi_connect(); // in wifi_info.h

homekit_storage_reset(); // to remove the previous HomeKit pairing storage when you first run this new HomeKit example

my_homekit_setup();

//ac_rotation_speed.value.int_value = 100; Serial.write("HomeKit setup complete. About to start ac.begin()\n"); ac.begin(); }

void loop() { drd.loop(); my_homekit_loop(); delay(10);

if (queueCommand) { Serial.write("Sending AC Command....\n"); ac.send(); flipQueueCommand(false); } }

// defined in my_accessory.c extern "C" homekit_server_config_t config;

extern "C" homekit_characteristic_t ac_display_units; extern "C" homekit_characteristic_t ac_current_temp; extern "C" homekit_characteristic_t ac_target_temp; extern "C" homekit_characteristic_t ac_current_state; extern "C" homekit_characteristic_t ac_target_state;

static uint32_t next_heap_millis = 0;

void target_temp_setter(const homekit_value_t value) { LOG_D("TARGET TEMP SETTER: Got value: %f", value.float_value); ac_target_temp.value = value; //sync value

ac.setMode(ac.getMode()); ac.setTemp(value.float_value); flipQueueCommand(true); }

void target_state_setter(const homekit_value_t value) { LOG_D("TARGET STATE SETTER: Got value INT: %d", value.int_value); ac_target_state.value = value; //sync value

switch(value.int_value) {
    case 0:
        ac.off();
    break;
    case 1:
        ac.setMode(MODE_HEAT);
    break;
    case 2:
        ac.setMode(MODE_COOL);
    break;
    case 3:
        ac.setMode(MODE_AUTO);
    break;
}

flipQueueCommand(true);

}

void current_state_setter(const homekit_value_t value) { LOG_D("CURRENT STATE SETTER NO OP: Got value INT: %d", value.int_value); ac_target_state.value = value; //sync value }

// --- End Setters

// --- GETTERS homekit_value_t current_state_getter() { LOG_D("CURRENT STATE GETTER CALLED! RESPONDING WITH: %d", ac_target_state.value.int_value); return HOMEKIT_UINT8(ac_current_state.value.int_value); }

homekit_value_t target_state_getter() { LOG_D("TARGET STATE GETTER CALLED! RESPONDING WITH: %d", ac_target_state.value.int_value); return HOMEKIT_UINT8(ac_target_state.value.int_value); }

homekit_value_t target_temp_getter() { LOG_D("TARGET TEMP GETTER CALLED! RESPONDING WITH: %f", ac_target_temp.value.float_value); return ac_target_temp.value; }

// END GETTERS

void my_homekit_setup() { Serial.write("starting my_homekit_setup\n");

//ac_active.setter = active_setter; ac_current_state.getter = current_state_getter; ac_current_state.setter = current_state_setter; ac_target_state.setter = target_state_setter; ac_target_state.getter = target_state_getter; ac_target_temp.setter = target_temp_setter; ac_target_temp.getter = target_temp_getter; //ac_rotation_speed.setter = rotation_speed_setter;

Serial.write("about to call arduino_homekit_setup\n"); arduino_homekit_setup(&config);

//report the switch value HERE to HomeKit if it is changed (e.g. by a physical button)

Serial.write("exiting my_homekit_setup\n"); }

void my_homekit_loop() { arduino_homekit_loop(); const uint32_t t = millis(); if (t > next_heap_millis) { next_heap_millis = t + 10 * 1000; my_homekit_report(); LOG_D("Free heap: %d, HomeKit clients: %d", ESP.getFreeHeap(), arduino_homekit_connected_clients_count());

} }

void my_homekit_report() { //report current temperature int analogValue = analogRead(temp_outputpin); float millivolts = (analogValue/1024.0) * 3300; //3300 is the voltage provided by NodeMCU float celsius = millivolts/10; //LOG_D("Current temperature: %.1f", celsius); ac_current_temp.value.float_value = celsius; homekit_characteristic_notify(&ac_current_temp, ac_current_temp.value);

} `

mateusmsantin commented 1 year ago

look it in your code: HOMEKIT_ACCESSORY(.id=1, .category=homekit_accessory_category_thermostat, .services=(homekit_service_t[]) { HOMEKIT_ACCESSORY(.id=1, .category=homekit_accessory_category_thermostat, .services=(homekit_service_t*[]) {